Implement indirect VSD calls for x86.
authorPat Gavlin <pagavlin@microsoft.com>
Mon, 12 Sep 2016 21:23:29 +0000 (14:23 -0700)
committerPat Gavlin <pagavlin@microsoft.com>
Fri, 16 Sep 2016 17:33:40 +0000 (10:33 -0700)
commitf7d8a4a7dbd9ce5b2981d7c9632ac108b2dcef93
treed365d4b518e0112c9c4393dfdca8715c40280597
parent832a7498bf7aff89a70800c5f02517866f93dc1f
Implement indirect VSD calls for x86.

Indirect VSD calls on x86 require not only that the address of the
VSD indirection cell is passed to the stub in EAX, but also that the
call instruction is
a) preceeded by a 3-byte NOP, and
b) exactly `call [eax]`.

On x64, these types of calls only require that the indirection cell
address is passed in R11 (i.e. they do not require the generation of
a specific call instruction encoding). The RyuJIT IR is therefore
able to represent such calls succinctly as something like:

    t72 =    lclVar    ref    V04 loc1         u:3 (last use) $240

           /--*  t72    ref
    t295 = *  putarg_reg ref

    t202 =    lclVar    long   V09 tmp4         u:4 $382

           /--*  t202   long
    t296 = *  putarg_reg long

    t106 =    lclVar    long   V09 tmp4         u:4 (last use) $382

           /--*  t106   long
    t297 = *  indir     long

           /--*  t295   ref    this in rcx
           +--*  t296   long   arg1 in r11
           +--*  t297   long   calli tgt
    t107 = *  call ind stub ref    $24a

In this form, the address of the indirection cell is in the lclVar
`tmp4`, which is then used by both a `putarg_reg` to move the
argument into R11 and by the indirection that generates the call
target. Because there are a relatively large number of registers on
x64, this works out nicely: the address of the indirection cell is
frequently allocated to R11, few extraneous copies are required,
and the code generator produces `call [r11]` for the call instruction.

Unfortunately, the situation is not so straightforward on x86: not
only must code generator must both pass the address of the indirection
cell in EAX and produce the specific call form mentioned earlier,
but there are also far fewer available registers. As a result, the
address of the indirection cell is infrequently allocated to EAX and
(barring an implicit understanding in the code generator that a
previous putarg_reg has placed the address of the indirection cell
into EAX) requires a redundant `mov eax, ...` before the call.

Ideally, we would be able to store the address of the indirection cell
to a local with a very short lifetime and pre-allocate that local to
EAX, but the IR does not have that capability, and adding it now
seems to be prohibitively expensive. Instead, this change omits the
`putarg_reg` used to put the the indirection cell address into the
required register on other platforms and simply uses the `calli tgt`
operand to the call to represent both the non-standard argument and
the call target:

    t40 =    lclVar    ref    V04 loc1         u:3 $1c0

           /--*  t40    ref
    t280 = *  putarg_reg ref

    t70 =    lclVar    int    V06 loc3         u:4 (last use) $2c1

           /--*  t70    int
    t281 = *  indir     int

           /--*  t280   ref    this in ecx
           +--*  t281   int    calli tgt
    t71 = *  call ind stub ref    $1c6

Lowering then marks the indirection as contained and sets the
destination candidates for its operand to EAX.
src/jit/codegenxarch.cpp
src/jit/lower.cpp
src/jit/lowerxarch.cpp
src/jit/morph.cpp
src/jit/target.h