Last active
February 26, 2016 22:03
-
-
Save nwellnhof/c95f7675d073f81a4b84 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Interface method stub. Needs a custom struct for arguments and return value | |
// of each interface method. | |
typedef void | |
(*cfish_interface_stub_t)(void *self, void *vargs, uint32_t *hash); | |
// Classes contain a fixed-size hashtable with pointers to interface method | |
// stubs. This table could also be moved in memory before the class struct | |
// to avoid overhead for classes that don't implement interfaces. | |
typedef struct { | |
... | |
cfish_interface_stub_t itable[ITABLE_SIZE]; | |
... | |
} cfish_Class; | |
// Method names are hashed using a fixed function. The hash is a static value | |
// known at compile time. | |
// NOTE: Comparing the symbol address isn't reliable across parcels. We'll need | |
// a struct containing the hash value and the method name (see below). | |
uint32_t PREFIX_Method_HASH = 0xCAFEBABE; | |
// Also a static value known at compile time. | |
#define PREFIX_Interface_Method_OFFSET \ | |
(offsetof(cfish_Class, itable) \ | |
+ (0xCAFEBABE % ITABLE_SIZE) * sizeof(cfish_interface_stub_t)) | |
// Argument struct for an interface method. | |
typedef struct { | |
int arg1; | |
void *arg2; | |
int retval; | |
} PREFIX_Interface_Method_arg_t; | |
// Interface method wrapper. Similar complexity as normal method wrappers. | |
// Argument mangling is a bit more expensive on x64-86 since arguments can't | |
// be passed in registers. | |
static CFISH_INLINE int | |
PREFIX_Interface_Method(Interface *self, int arg1, void *arg2) { | |
// Lookup interface method stub in itable. | |
cfish_interface_stub_t method = (cfish_interface_stub_t) | |
cfish_obj_method(self, PREFIX_Interface_Method_OFFSET); | |
// Set up argument struct. | |
PREFIX_Interface_Method_arg_t args; | |
args.arg1 = arg1; | |
args.arg2 = arg2; | |
// Call stub. | |
method(self, &args, &PREFIX_Method_HASH); | |
return args.retval; | |
} | |
// Simple interface method stub in case there are no collisions. | |
// In the common case, the overhead of an interface method call | |
// should be about twice the overhead of a class method call. | |
void | |
PREFIX_Class_Method_ISTUB(void *self, void *vargs, uint32_t *hash) { | |
PREFIX_Method_arg_t *args = (PREFIX_Method_arg_t)vargs; | |
// Methods can be called directly because the call to the stub goes | |
// through the class's itable, so the exact type of self is known. | |
// The stub can also be shared with subclasses that don't override | |
// the method. Alternatively, the method call could be dispatched | |
// dynamically, and the stub could be shared with all subclasses. | |
args->retval = PREFIX_Class_Method_IMP((Class*)self, args->arg1, | |
args->arg2); | |
} | |
// Interface method resolution stub in case of collisions. | |
// The hash values and collisions are known at compile time, so this code can | |
// be generated. | |
// Could also be shared with subclasses under certain conditions (probably | |
// hard to figure out). | |
void | |
PREFIX_Class_MRSTUB1(void *self, void *vargs, uint32_t *hash) { | |
// Switch statement with static values can be optimized by the compiler. | |
// Branch prediction should keep additional overhead negligible, at | |
// least in the monomorphic case. | |
switch (*hash) { | |
case 0xCAFEBABE: { | |
PREFIX_Method_arg_t *args = (PREFIX_Method_arg_t)vargs; | |
// Methods in the same parcel can be called directly. | |
args->retval = PREFIX_Class_Method_IMP((Class*)self, args->arg1, | |
args->arg2) | |
break; | |
} | |
case 0xDEADBEEF: | |
// Extremely rare case where two method names hash to the same | |
// value. Resolve by comparing address of hash value global. | |
// NOTE: This doesn't work across with classes and interfaces | |
// from different parcels. We have to compare the actual | |
// method names. | |
if (hash == PREFIX_Method2_HASH) { | |
PREFIX_Method2_arg_t *args = (PREFIX_Method2_arg_t)vargs; | |
// Methods in other parcels must go through vtable. | |
args->retval = PREFIX_Class_Method2((Class*)self, ...) | |
} | |
else { | |
... | |
} | |
break; | |
case ... | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment