static int resultSize(ResultType resultType) { switch (resultType.nativeType()) { case SCHAR: case UCHAR: case SSHORT: case USHORT: case SINT: case UINT: case SLONG: case ULONG: case POINTER: return 4; case SLONG_LONG: case ULONG_LONG: return 8; case FLOAT: case DOUBLE: return 16; case VOID: return 0; default: throw new IllegalArgumentException("invalid return type " + resultType); } }
@Override void compile( long function, String name, ResultType resultType, ParameterType[] parameterTypes, Class resultClass, Class[] parameterClasses, CallingConvention convention, boolean saveErrno) { int psize = 0; for (ParameterType t : parameterTypes) { psize += parameterSize(t); } int rsize = resultSize(resultType); // // JNI functions all look like: // foo(JNIEnv* env, jobject self, arg...) // We need to align the stack to 16 bytes, then copy all the old args // into the new parameter space. // It already has 4 bytes pushed (the return address) so we need to account for that. // final int stackadj = align(Math.max(psize, rsize) + 4, 16) - 4; Assembler a = new Assembler(X86_32); a.sub(esp, imm(stackadj)); // copy and convert the parameters from the orig stack to the new location for (int i = 0, srcoff = 0, dstoff = 0; i < parameterTypes.length; i++) { int srcParameterSize = parameterSize(parameterClasses[i]); int dstParameterSize = parameterSize(parameterTypes[i]); int disp = stackadj + 4 + 8 + srcoff; switch (parameterTypes[i].nativeType()) { case SCHAR: case SSHORT: a.movsx(eax, ptr(esp, disp, parameterTypes[i].nativeType())); break; case UCHAR: case USHORT: a.movzx(eax, ptr(esp, disp, parameterTypes[i].nativeType())); break; default: a.mov(eax, dword_ptr(esp, disp)); break; } a.mov(dword_ptr(esp, dstoff), eax); if (dstParameterSize > 4) { if (parameterTypes[i].nativeType() == NativeType.SLONG_LONG && long.class != parameterClasses[i]) { // sign extend from int.class -> long long a.sar(eax, imm(31)); } else if (parameterTypes[i].nativeType() == NativeType.ULONG_LONG && long.class != parameterClasses[i]) { // zero extend from int.class -> unsigned long long a.mov(dword_ptr(esp, dstoff + 4), imm(0)); } else { a.mov(eax, dword_ptr(esp, disp + 4)); } a.mov(dword_ptr(esp, dstoff + 4), eax); } dstoff += dstParameterSize; srcoff += srcParameterSize; } // Call to the actual native function a.call(imm(function & 0xffffffffL)); if (saveErrno) { int save = 0; switch (resultType.nativeType()) { case FLOAT: a.fstp(dword_ptr(esp, save)); break; case DOUBLE: a.fstp(qword_ptr(esp, save)); break; case SLONG_LONG: case ULONG_LONG: a.mov(dword_ptr(esp, save), eax); a.mov(dword_ptr(esp, save + 4), edx); break; case VOID: // No need to save for void values break; default: a.mov(dword_ptr(esp, save), eax); } // Save the errno in a thread-local variable a.call(imm(errnoFunctionAddress & 0xffffffffL)); // Retrieve return value and put it back in the appropriate return register switch (resultType.nativeType()) { case FLOAT: a.fld(dword_ptr(esp, save)); break; case DOUBLE: a.fld(qword_ptr(esp, save)); break; case SCHAR: a.movsx(eax, byte_ptr(esp, save)); break; case UCHAR: a.movzx(eax, byte_ptr(esp, save)); break; case SSHORT: a.movsx(eax, word_ptr(esp, save)); break; case USHORT: a.movzx(eax, word_ptr(esp, save)); break; case SLONG_LONG: case ULONG_LONG: a.mov(eax, dword_ptr(esp, save)); a.mov(edx, dword_ptr(esp, save + 4)); break; case VOID: // No need to save for void values break; default: a.mov(eax, dword_ptr(esp, save)); } } else { switch (resultType.nativeType()) { case SCHAR: a.movsx(eax, al); break; case UCHAR: a.movzx(eax, al); break; case SSHORT: a.movsx(eax, ax); break; case USHORT: a.movzx(eax, ax); break; } } if (long.class == resultClass) { // sign or zero extend the result to 64 bits switch (resultType.nativeType()) { case SCHAR: case SSHORT: case SINT: case SLONG: // sign extend eax to edx:eax a.mov(edx, eax); a.sar(edx, imm(31)); break; case UCHAR: case USHORT: case UINT: case ULONG: case POINTER: a.mov(edx, imm(0)); break; } } // Restore esp to the original position and return a.add(esp, imm(stackadj)); a.ret(); stubs.add(new Stub(name, sig(resultClass, parameterClasses), a)); }
boolean canCompile( ResultType returnType, ParameterType[] parameterTypes, CallingConvention convention) { switch (returnType.nativeType()) { case VOID: case SCHAR: case UCHAR: case SSHORT: case USHORT: case SINT: case UINT: case SLONG: case ULONG: case SLONG_LONG: case ULONG_LONG: case FLOAT: case DOUBLE: case POINTER: break; default: return false; } // There is only one calling convention; SYSV, so abort if someone tries to use stdcall if (convention != CallingConvention.DEFAULT) { return false; } int fCount = 0; int iCount = 0; for (ParameterType t : parameterTypes) { switch (t.nativeType()) { case SCHAR: case UCHAR: case SSHORT: case USHORT: case SINT: case UINT: case SLONG: case ULONG: case SLONG_LONG: case ULONG_LONG: case POINTER: ++iCount; break; case FLOAT: case DOUBLE: ++fCount; break; default: // Fail on anything else return false; } } // We can only safely compile methods with up to 6 integer and 8 floating point parameters return true; }