libjit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Libjit] Calling native functions that return 'long long' on x86


From: Eli Zaretskii
Subject: [Libjit] Calling native functions that return 'long long' on x86
Date: Wed, 05 Sep 2018 20:06:47 +0300

Hi,

I apologize for a longish message, but there's some background info
here that I think is important for understanding the issue at hand.

One of 32-bit x86 builds of Emacs uses 64-bit 'long long' data type
to represent Lisp objects; this allows, among other benefits, to
enlarge the limit on the maximum size of text-editing buffers.

That configuration fails to build with libjit on MS-Windows, although
the standard version, which uses 32-bit 'int' for Lisp objects, builds
and works just fine with the same source code on the same system.

After spending some time debugging the problem, I came to the
conclusion that using jit_insn_call_native to call a function that
returns a 64-bit 'long long' value fails because one of the 2 32-bit
registers used to return the 64-bit value is clobbered by subsequent
instructions.  Here's the actual example (disassembly by GDB):

     0x07d819ec:  push   %edx
     0x07d819ed:  push   %eax
     0x07d819ee:  push   %ebx
     0x07d819ef:  push   %ecx
     0x07d819f0:  call   0x122d933 <Fnth>
     0x07d819f5:  add    $0x10,%esp
     0x07d819f8:  mov    -0x18(%ebp),%ecx
     0x07d819fb:  mov    -0x14(%ebp),%ebx
     0x07d819fe:  mov    -0x8(%ebp),%esi
     0x07d81a01:  mov    -0x4(%ebp),%eax   <<<<<<<<<<<<<<<<<<<<<<<<<<
     0x07d81a04:  mov    -0x30(%ebp),%edi
     0x07d81a07:  mov    %edi,(%esi)
     0x07d81a09:  mov    -0x2c(%ebp),%edi
     0x07d81a0c:  mov    %edi,0x4(%esi)
     0x07d81a0f:  mov    %eax,-0x38(%ebp)
     0x07d81a12:  mov    %edx,-0x34(%ebp)
     0x07d81a15:  mov    %eax,0x8(%esi)
     0x07d81a18:  mov    %edx,0xc(%esi)

The function in question is Fnth.  It accepts 2 64-bit arguments,
which are correctly pushed onto the stack.  Stepping into Fnth, I see
that it works correctly and returns the correct value.  But then one
of the instructions (highlighted above) after the function returns
clobbers the EAX register before it could be used by the following
code.

In tracing this problem, I found that jit_insn_call_native calls
_jit_create_call_return_insns from jit-rules-x86.c to arrange for
retrieving the return value, and that one does this:

        /* Structure values must be flushed into the frame, and
           everything else ends up in a register */
        if(jit_type_is_struct(return_type) || jit_type_is_union(return_type))
        {
                if(!jit_insn_flush_struct(func, return_value))
                {
                        return 0;
                }
        }
        else if(return_type == jit_type_float32 ||
                        return_type == jit_type_float64 ||
                        return_type == jit_type_nfloat)
        {
                if(!jit_insn_return_reg(func, return_value, X86_REG_ST0))
                {
                        return 0;
                }
        }
        else if(return_type->kind != JIT_TYPE_VOID)
        {
                if(!jit_insn_return_reg(func, return_value, X86_REG_EAX))
                {
                        return 0;
                }
        }

This means that libjit thinks the return value in this case will be in
the EAX register, whereas it actually is in the EDX:EAX pair.

Is my analysis correct, and does this mean libjit currently lacks the
ability to support such functions in jit_insn_call_native?  I'm asking
because confusingly the clobbered register is EAX, not EDX, so maybe I
misunderstood something.  And if my conclusion is correct, would it be
hard to add support for this feature to libjit?

In case it matters, the native call is compiled like this:

  static void
  compile_binary (jit_function_t func, const char *name,
                  Lisp_Object (*callee) (Lisp_Object, Lisp_Object),
                  jit_value_t arg_and_dest, jit_value_t arg2)
  {
    jit_value_t args[2] = { arg_and_dest, arg2 };

    jit_value_t result = jit_insn_call_native (func, name, (void *) callee,
                                               binary_signature, args, 2,
                                               JIT_CALL_NOTHROW);
    jit_insn_store (func, arg_and_dest, result);
  }

Here, 'callee' is Fnth, and arg_and_dest is the location from which
the 1st argument comes and where we want to put the result.  Is this
the correct way of doing this?

Finally, if libjit indeed currently lacks the necessary support for
such return values on x86, would it work if I define the return type,
for the purposes of the function's signature, as an 8-byte struct or
union?  The code and the documentation seem to hint that libjit
supports passing small structs via registers, but I'm not sure I
understood correctly.

Thanks in advance for any help.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]