noway_assert(compiler->compArgSize < 0x10000); // "ret" only has 2 byte operand
}
+
+#ifdef UNIX_X86_ABI
+ // The called function must remove hidden address argument from the stack before returning
+ // in case of struct returning according to cdecl calling convention on linux.
+ // Details: http://www.sco.com/developers/devspecs/abi386-4.pdf pages 40-43
+ if (compiler->info.compCallConv == CorInfoCallConvExtension::C && compiler->info.compRetBuffArg != BAD_VAR_NUM)
+ stkArgSize += TARGET_POINTER_SIZE;
+#endif // UNIX_X86_ABI
#endif // TARGET_X86
/* Return, popping our arguments (if any) */
node->gtFlags |= (GTF_CALL | GTF_GLOB_REF);
#ifdef UNIX_X86_ABI
- node->gtFlags |= GTF_CALL_POP_ARGS;
+ if (callType == CT_INDIRECT || callType == CT_HELPER)
+ node->gtFlags |= GTF_CALL_POP_ARGS;
#endif // UNIX_X86_ABI
for (GenTreeCall::Use& use : GenTreeCall::UseList(args))
{
return charsDisplayed;
}
+#ifdef TARGET_X86
+inline const char* GetCallConvName(CorInfoCallConvExtension callConv)
+{
+ switch (callConv)
+ {
+ case CorInfoCallConvExtension::Managed:
+ return "Managed";
+ case CorInfoCallConvExtension::C:
+ return "C";
+ case CorInfoCallConvExtension::Stdcall:
+ return "Stdcall";
+ case CorInfoCallConvExtension::Thiscall:
+ return "Thiscall";
+ case CorInfoCallConvExtension::Fastcall:
+ return "Fastcall";
+ case CorInfoCallConvExtension::CMemberFunction:
+ return "CMemberFunction";
+ case CorInfoCallConvExtension::StdcallMemberFunction:
+ return "StdcallMemberFunction";
+ case CorInfoCallConvExtension::FastcallMemberFunction:
+ return "FastcallMemberFunction";
+ default:
+ return "UnknownCallConv";
+ }
+}
+#endif // TARGET_X86
+
/*****************************************************************************/
void Compiler::gtDispNodeName(GenTree* tree)
{
gtfTypeBufWalk += SimpleSprintf_s(gtfTypeBufWalk, gtfTypeBuf, sizeof(gtfTypeBuf), " thiscall");
}
+#ifdef TARGET_X86
+ gtfTypeBufWalk += SimpleSprintf_s(gtfTypeBufWalk, gtfTypeBuf, sizeof(gtfTypeBuf), " %s",
+ GetCallConvName(tree->AsCall()->GetUnmanagedCallConv()));
+#endif // TARGET_X86
gtfType = gtfTypeBuf;
}
// Case of call returning a struct via hidden retbuf arg
CLANG_FORMAT_COMMENT_ANCHOR;
-#if defined(TARGET_WINDOWS) && !defined(TARGET_ARM)
+#if (defined(TARGET_WINDOWS) && !defined(TARGET_ARM)) || defined(UNIX_X86_ABI)
// Unmanaged instance methods on Windows need the retbuf arg after the first (this) parameter
if (srcCall->IsUnmanaged())
{
}
}
else
-#endif
+#endif // (defined(TARGET_WINDOWS) && !defined(TARGET_ARM)) || defined(UNIX_X86_ABI)
{
// insert the return value buffer into the argument list as first byref parameter
srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, srcCall->gtCallArgs);
GenTreeCall* call = gtNewIndCallNode(fptr, callRetTyp, nullptr, ilOffset);
call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT);
+#ifdef UNIX_X86_ABI
+ call->gtFlags &= ~GTF_CALL_POP_ARGS;
+#endif
return call;
}
noway_assert(arg != nullptr);
call->gtCallCookie = nullptr;
-#if defined(TARGET_X86)
- // x86 passes the cookie on the stack as the final argument to the call.
- GenTreeCall::Use** insertionPoint = &call->gtCallArgs;
- for (; *insertionPoint != nullptr; insertionPoint = &((*insertionPoint)->NextRef()))
- {
- }
- *insertionPoint = gtNewCallArgs(arg);
-#else // !defined(TARGET_X86)
- // All other architectures pass the cookie in a register.
+ // All architectures pass the cookie in a register.
call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs);
-#endif // defined(TARGET_X86)
nonStandardArgs.Add(arg, REG_PINVOKE_COOKIE_PARAM, NonStandardArgKind::PInvokeCookie);
numArgs++;
}
#ifdef UNIX_X86_ABI
// Add in the ret buff arg
- if (callHasRetBuffArg)
+ if (callHasRetBuffArg &&
+ call->unmgdCallConv != CorInfoCallConvExtension::C && // C and Stdcall calling conventions do not
+ call->unmgdCallConv != CorInfoCallConvExtension::Stdcall) // use registers to pass arguments.
maxRegArgs++;
#endif
}
//
bool Compiler::fgCanTailCallViaJitHelper()
{
-#ifndef TARGET_X86
- // On anything except X86 we have no faster mechanism available.
+#if !defined(TARGET_X86) || defined(UNIX_X86_ABI)
+ // On anything except windows X86 we have no faster mechanism available.
return false;
#else
// The JIT helper does not properly handle the case where localloc was used.
// The "regList" type is a small set of registerse
#ifdef TARGET_X86
-typedef TinyArray<unsigned short, regNumber, REGNUM_BITS> regList;
+typedef TinyArray<unsigned int, regNumber, REGNUM_BITS> regList;
#else
// The regList is unused for all other targets.
#endif // TARGET*
#define RBM_PINVOKE_TARGET_PARAM RBM_EAX
// GenericPInvokeCalliHelper cookie parameter
- #define REG_PINVOKE_COOKIE_PARAM REG_STK
+ #define REG_PINVOKE_COOKIE_PARAM REG_EBX
// IL stub's secret parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM)
#define REG_SECRET_STUB_PARAM REG_EAX
TinyArrayRef operator[](unsigned int n)
{
- assert((n + 1) * bits_per_element <= sizeof(itemType) * 8);
+ assert((n + 1) * bits_per_element <= sizeof(storageType) * 8);
return TinyArrayRef(&data, n);
}
// only use this for clearing it
switch (os)
{
case TargetOS.Windows:
+ case TargetOS.Linux:
Arg0 = Register.ECX;
Arg1 = Register.EDX;
Result = Register.EAX;
// Invoked for marshaling-required unmanaged CALLI calls as a stub.
// EAX - the unmanaged target
// ECX, EDX - arguments
-// [ESP + 4] - the VASigCookie
+// EBX - the VASigCookie
//
LEAF_ENTRY GenericPInvokeCalliHelper, _TEXT
- // save the target
- push eax
-
- // EAX <- VASigCookie
- mov eax, [esp + 8] // skip target and retaddr
-
- mov eax, [eax + VASigCookie__StubOffset]
- test eax, eax
+ cmp dword ptr [ebx + VASigCookie__StubOffset], 0
jz LOCAL_LABEL(GoCallCalliWorker)
- // ---------------------------------------
- push eax
-
- // stack layout at this point:
- //
- // | ... |
- // | stack arguments | ESP + 16
- // +----------------------+
- // | VASigCookie* | ESP + 12
- // +----------------------+
- // | return address | ESP + 8
- // +----------------------+
- // | CALLI target address | ESP + 4
- // +----------------------+
- // | stub entry point | ESP + 0
- // ------------------------
-
- // remove VASigCookie from the stack
- mov eax, [esp + 8]
- mov [esp + 12], eax
-
- // move stub entry point below the RA
- mov eax, [esp]
- mov [esp + 8], eax
-
- // load EAX with the target address
- pop eax
- pop eax
-
- // stack layout at this point:
- //
- // | ... |
- // | stack arguments | ESP + 8
- // +----------------------+
- // | return address | ESP + 4
- // +----------------------+
- // | stub entry point | ESP + 0
- // ------------------------
-
- // CALLI target address is in EAX
- ret
+ // Stub is already prepared, just jump to it
+ jmp dword ptr [ebx + VASigCookie__StubOffset]
LOCAL_LABEL(GoCallCalliWorker):
- // the target is on the stack and will become m_Datum of PInvokeCalliFrame
- // call the stub generating worker
- pop eax
-
//
- // target ptr in EAX, VASigCookie ptr in EDX
+ // call the stub generating worker
+ // target ptr in EAX, VASigCookie ptr in EBX
//
STUB_PROLOG
// save target
push eax
+ #define STACK_ALIGN_PADDING 4
+ sub esp, STACK_ALIGN_PADDING // pass stack aligned to 0x10
push eax // unmanaged target
- push dword ptr [esi + 4*7] // pVaSigCookie (first stack argument)
+ push ebx // pVaSigCookie (first stack argument)
push esi // pTransitionBlock
+ CHECK_STACK_ALIGNMENT
call C_FUNC(GenericPInvokeCalliStubWorker)
+ add esp, STACK_ALIGN_PADDING // restore alignment, callee pop args
+ #undef STACK_ALIGN_PADDING
// restore target
pop eax
; Invoked for marshaling-required unmanaged CALLI calls as a stub.
; EAX - the unmanaged target
; ECX, EDX - arguments
-; [ESP + 4] - the VASigCookie
+; EBX - the VASigCookie
;
_GenericPInvokeCalliHelper@0 proc public
- ; save the target
- push eax
-
- ; EAX <- VASigCookie
- mov eax, [esp + 8] ; skip target and retaddr
-
- mov eax, [eax + VASigCookie__StubOffset]
- test eax, eax
+ cmp dword ptr [ebx + VASigCookie__StubOffset], 0
jz GoCallCalliWorker
- ; ---------------------------------------
-
- push eax
-
- ; stack layout at this point:
- ;
- ; | ... |
- ; | stack arguments | ESP + 16
- ; +----------------------+
- ; | VASigCookie* | ESP + 12
- ; +----------------------+
- ; | return address | ESP + 8
- ; +----------------------+
- ; | CALLI target address | ESP + 4
- ; +----------------------+
- ; | stub entry point | ESP + 0
- ; ------------------------
-
- ; remove VASigCookie from the stack
- mov eax, [esp + 8]
- mov [esp + 12], eax
-
- ; move stub entry point below the RA
- mov eax, [esp]
- mov [esp + 8], eax
- ; load EAX with the target address
- pop eax
- pop eax
-
- ; stack layout at this point:
- ;
- ; | ... |
- ; | stack arguments | ESP + 8
- ; +----------------------+
- ; | return address | ESP + 4
- ; +----------------------+
- ; | stub entry point | ESP + 0
- ; ------------------------
-
- ; CALLI target address is in EAX
- ret
+ ; Stub is already prepared, just jump to it
+ jmp dword ptr [ebx + VASigCookie__StubOffset]
GoCallCalliWorker:
- ; the target is on the stack and will become m_Datum of PInvokeCalliFrame
- ; call the stub generating worker
- pop eax
-
;
- ; target ptr in EAX, VASigCookie ptr in EDX
+ ; call the stub generating worker
+ ; target ptr in EAX, VASigCookie ptr in EBX
;
STUB_PROLOG
push eax
push eax ; unmanaged target
- push dword ptr [esi + 4*7] ; pVaSigCookie (first stack argument)
+ push ebx ; pVaSigCookie (first stack argument)
push esi ; pTransitionBlock
call _GenericPInvokeCalliStubWorker@12
ret
LEAF_END JIT_WriteBarrierGroup, _TEXT
+// Pointer to JIT_WriteBarrierEAX
.data
.align 4
.global C_FUNC(JIT_WriteBarrierEAX_Loc)
addl $_GLOBAL_OFFSET_TABLE_+(2b-1b), %eax
.intel_syntax noprefix
mov eax, dword ptr [eax + C_FUNC(JIT_WriteBarrierEAX_Loc)@GOT]
+ mov eax, [eax]
xchg eax, dword ptr [esp]
ret
LEAF_END JIT_WriteBarrier_Callable, _TEXT
}
}
-#if defined HOST_X86
+#if defined HOST_X86 && !defined TARGET_UNIX
#pragma comment(linker, "/export:DoubleInt=_DoubleInt@4")
#endif
extern "C" DLL_EXPORT int DoubleInt(int a)
return DoubleIntImpl(a);
}
-#if defined HOST_X86
+#if defined HOST_X86 && !defined TARGET_UNIX
#pragma comment(linker, "/export:DoubleIntStdcall=_DoubleIntStdcall@4")
#endif
extern "C" DLL_EXPORT int __stdcall DoubleIntStdcall(int a)
}
}
-#if defined HOST_X86
+#if defined HOST_X86 && !defined TARGET_UNIX
#pragma comment(linker, "/export:ToUpper=_ToUpper@4")
#endif
extern "C" DLL_EXPORT WCHAR ToUpper(WCHAR a)
return ToUpperImpl(a);
}
-#if defined HOST_X86
+#if defined HOST_X86 && !defined TARGET_UNIX
#pragma comment(linker, "/export:ToUpperStdcall=_ToUpperStdcall@4")
#endif
extern "C" DLL_EXPORT WCHAR __stdcall ToUpperStdcall(WCHAR a)