[LLVMdev] Hash Table Virtual Calls with Conflict Resolution Stubs

Stephen Cross scross at scross.co.uk
Mon Jun 1 16:20:55 PDT 2015


Hi Sanjoy,

Thanks for your response.

> Assuming you have full control over your environment [...]

Just to answer this, I do have control over the calling convention, so comments
about the implementation choices are definitely in scope. Having said that, I'm
keen not to design the calling convention in order to satisfy current compiler
implementation limitations.

> Not using %rax for %method_id frees it up for use as %dest, for instance,
> so using %rdi for %method_id may not actually be a bad idea in
> practice.

Yes, using eax was just an example. The problem with using rdi is that the code
then has to shift all the values up one register, as well as inducing
an unnecessary
stack allocation. On an individual basis this is a minor cost, but
this is a core
language feature which may be invoked a huge number of times in a program.

After your mention of eax, I investigated other registers and discovered that a
viable solution already appears to exist in LLVM in the form of the
'nest' parameter
attribute. On x86_64 this passes a pointer in r10, which satisfies the use case
extremely well.

I've therefore realised that a very similar use case to this is
already well known
to LLVM in the form of nested functions and the static chain pointer.
As far as I
can tell, the registers used are:

* x86 : ecx
* x86_64 : r10
* ARM (32-bit and 64-bit) : r0/x0 (seems to treat it as a normal argument)

(I haven't investigated the other architectures yet.)

So x86 and x86_64 are excellent. I'm curious about the implementation of nested
functions on ARM, since they seem to be treating the static chain pointer as a
normal argument (or is it just that 'nest' is ignored on ARM?). As mentioned
above this is very costly because it means all the arguments have to be shifted
by one position; it would be more efficient if a callee-save register was used.

In any case, I expect to make a proposal at some point soon for either extending
'nest' or adding a new attribute (I'm thinking it could be 'outofband') that
allows passing an arbitrary-typed argument via an out-of-band
mechanism. Following
the established norms for nested functions, this would use ecx on x86,
r10 on x86_64
and a callee-save register on ARM. What do you think?

> I'm not sure I understand what you mean by "forward a return value",

My thinking was that the i32 return value might affect the correctness
of the call,
since we may in fact be returning significantly different types (e.g. a large
struct). Having said that, given that we're calling with the correct prototype
and returning the value in a function with the correct prototype it
seems hard to
imagine a code generator choking on this.

Presumably optimisation passes might still have trouble with this, but
it looks like
the new 'thunk' function attribute is designed for this case. Could
anyone explain
in detail the purpose of the 'thunk' attribute and what led to its introduction?
(The IR documentation is slightly vague.)

> but running the following:

Yes, this is right and thanks for pointing this out. In an actual conflict
resolution stub we need to pass arbitrary arguments of unknown type,
whereas I've
found casting non-varargs functions sometimes leads LLVM to zero all
the arguments.
Fortunately it looks like LLVM 3.6+ allows forwarding varargs which means this
works smoothly.

Kind regards,
Stephen



More information about the llvm-dev mailing list