Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move return buffer handling from interop to the JIT #39294

Merged
merged 82 commits into from
Dec 3, 2020
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
c17d786
Turn off interop-level system to enable thiscall handling
jkoritzinsky Jul 7, 2020
8eecd35
Generate a return buffer for instance method callis.
jkoritzinsky Jul 7, 2020
c06a040
On Windows non-arm32, unmanaged instance methods have their hidden re…
jkoritzinsky Jul 7, 2020
9448965
Teach the JIT to generate a return buffer for reverse P/Invokes for t…
jkoritzinsky Jul 8, 2020
7a26d40
Emit the native `this` argument before the return buffer when needed.
jkoritzinsky Jul 8, 2020
a75f746
Remove isInstanceMethod support in the interop subsystem now that the…
jkoritzinsky Jul 8, 2020
4f09a4f
Add reverse P/Invoke tests for ThisCall.
jkoritzinsky Jul 8, 2020
e4aaf91
Fix x86 build.
jkoritzinsky Jul 8, 2020
3d66e53
Add enum test.
jkoritzinsky Jul 8, 2020
4eee458
Remove interop-specific handling of x86 thiscall and get non trivial-…
jkoritzinsky Jul 8, 2020
a7e0b52
Remove bashing the return type to int.
jkoritzinsky Jul 9, 2020
8eb6846
Don't unwrap single primitive field structs on x86 any more. RyuJIT s…
jkoritzinsky Jul 10, 2020
110c2ae
Pass the retbuf arg on the stack as the first argument (inserted last…
jkoritzinsky Jul 10, 2020
96336f5
Remove extra newline
jkoritzinsky Jul 13, 2020
48e3fce
Enable returning 8-byte structs in multiple registers on x86 and add …
jkoritzinsky Jul 13, 2020
b83c60a
Since the x86 stub linker path emulates the stdcall calling conventio…
jkoritzinsky Jul 14, 2020
f3bdf58
Remove unused code for manual return buffers from the interop subsystem.
jkoritzinsky Jul 14, 2020
e7b8369
Merge branch 'master' of https://github.com/dotnet/runtime into retbu…
jkoritzinsky Jul 14, 2020
5324b7e
Apply format patch.
jkoritzinsky Jul 14, 2020
3c60ca2
Don't check IsUmanagedValueTypeReturnedByRef unless the methodtable i…
jkoritzinsky Jul 14, 2020
f23c291
Merge branch 'master' of https://github.com/dotnet/runtime into retbu…
jkoritzinsky Jul 15, 2020
0a6790b
Fix HasRetBuffArg check for ARM and ARM64
jkoritzinsky Jul 16, 2020
925ec02
Merge branch 'master' into retbuf-move-to-jit
jkoritzinsky Sep 14, 2020
47553e5
Pass calling convention to getReturnTypeForStruct.
jkoritzinsky Sep 30, 2020
779708a
Merge commit '957fdb9f966a041a394ad2a7840cbfdb9782fafe' into retbuf-m…
jkoritzinsky Sep 30, 2020
4b8870b
Propogate calling convention through the JIT so it can determine stru…
jkoritzinsky Oct 1, 2020
6c56bc8
Remove now-unused code for multireg struct returns in the x86 reverse…
jkoritzinsky Oct 1, 2020
3897473
Treat RuntimeArgumentHandle and RuntimeMethodHandleInternal as ELEMEN…
jkoritzinsky Oct 2, 2020
426b0f3
Treat RuntimeFieldHandleInternal as an ELEMENT_TYPE_I since it is use…
jkoritzinsky Oct 3, 2020
cc73336
Apply format patch.
jkoritzinsky Oct 3, 2020
a825b8a
Use enum value instead of 0.
jkoritzinsky Oct 3, 2020
9c0cbb1
Update comments.
jkoritzinsky Oct 3, 2020
63507b7
Fix stack imbalance in x86 ThisCall reverse tests where native has a …
jkoritzinsky Oct 5, 2020
b72dcb4
Merge branch 'master' of https://github.com/dotnet/runtime into retbu…
jkoritzinsky Oct 5, 2020
1262c27
Specify source register for copy instruction so the correct instructi…
jkoritzinsky Oct 7, 2020
ef2adac
Fix instance call P/Invoke's on Windows ARM64.
jkoritzinsky Oct 7, 2020
5c2a9bc
Fix the reverse P/Invoke case for instance methods on ARM64.
jkoritzinsky Oct 8, 2020
dd44e32
Update src/coreclr/src/vm/dllimportcallback.cpp
jkoritzinsky Oct 8, 2020
1056c44
Fix Linux x64 crossgen failure.
jkoritzinsky Oct 8, 2020
a0218a1
Merge branches 'retbuf-move-to-jit' and 'retbuf-move-to-jit' of githu…
jkoritzinsky Oct 8, 2020
47bf6aa
Fix typo.
jkoritzinsky Oct 8, 2020
a1bb21c
For x86 reverse P/Invokes where the return is an 8-byte struct, handl…
jkoritzinsky Oct 9, 2020
254063b
Flip EAX/EDX slots in the x86 reverse P/Invoke stub. Now that they ma…
jkoritzinsky Oct 9, 2020
71adacb
Apply format patch.
jkoritzinsky Oct 12, 2020
195713b
Disable test on Win ARM32 to avoid blocking CI.
jkoritzinsky Oct 12, 2020
8833b06
Restore pointer-sized trivial struct normalization, this time on-dema…
jkoritzinsky Oct 14, 2020
7e2c1f9
Ensure we normalize in ArgIterator.
jkoritzinsky Oct 14, 2020
382113f
Explicitly validate size for this optimization.
jkoritzinsky Oct 14, 2020
bd4022a
Fix typo
jkoritzinsky Oct 14, 2020
c4acc94
Merge branch 'master' of github.com:dotnet/runtime into retbuf-move-t…
jkoritzinsky Oct 14, 2020
1694da8
Fix infinite loop in negative case in VM impl of normalization algorithm
jkoritzinsky Oct 15, 2020
5dd9031
Fix promoting fields of structs that are passed in register into a ca…
jkoritzinsky Oct 16, 2020
7182ad0
Fixed passing a trivial struct in registers to a fast tail call (that…
jkoritzinsky Oct 16, 2020
e329242
Fix formatting
jkoritzinsky Oct 19, 2020
94f6a1d
Update crossgen2 to the "JIT does the struct normalization" plan.
jkoritzinsky Oct 19, 2020
cfce1d7
Merge branch 'master' of github.com:dotnet/runtime into retbuf-move-t…
jkoritzinsky Oct 23, 2020
0368407
Fix license headers.
jkoritzinsky Oct 27, 2020
a4286f3
Remove the old validation that relates only to the old x86 struct nor…
jkoritzinsky Oct 27, 2020
bae4165
Apply suggestions from code review
jkoritzinsky Oct 28, 2020
77d6bf6
Add more comments and clean up the code
jkoritzinsky Oct 28, 2020
9e965f8
introduce a CORINFO_UNMANAGED_CALLCONV_MANAGED enum member to represe…
jkoritzinsky Oct 28, 2020
c28925e
Fix formatting
jkoritzinsky Oct 28, 2020
e5212cb
Apply suggestions from code review
jkoritzinsky Oct 30, 2020
e020165
PR feedback.
jkoritzinsky Nov 2, 2020
90cfcef
Fix comparison.
jkoritzinsky Nov 2, 2020
41b21a5
fix callconv calculation.
jkoritzinsky Nov 2, 2020
f6eeddf
Fix build break.
jkoritzinsky Nov 2, 2020
71c9032
Use a union with the tailCalInfo pointer to store the unmanaged calli…
jkoritzinsky Nov 2, 2020
b334b37
Fix formatting.
jkoritzinsky Nov 3, 2020
28b1d48
Update src/coreclr/src/jit/importer.cpp
jkoritzinsky Nov 23, 2020
aab5ca0
Collapse identical code paths. Clean up comments
jkoritzinsky Nov 23, 2020
96081a0
Add some comments around CorInfoUnmanagedCallConv
jkoritzinsky Nov 24, 2020
0ef4948
CALLCONV_MANAGED=CALLCONV_UNKNOWN jit local
jkoritzinsky Nov 24, 2020
6ee4a82
PR feedback.
jkoritzinsky Nov 30, 2020
3c89d74
Use new HasFixedRetBufArg method.
jkoritzinsky Nov 30, 2020
2ce4270
Fix formatting.
jkoritzinsky Nov 30, 2020
fb00bc9
Fix compMethodReturnsRetBufAddr
jkoritzinsky Nov 30, 2020
8f9478a
Create CorInfoCallConvExtension enum instead of reusing CorInfoUnmana…
jkoritzinsky Nov 30, 2020
68d4081
Merge branch 'master' of github.com:dotnet/runtime into retbuf-move-t…
jkoritzinsky Nov 30, 2020
e26c36d
Fix the unreachable code warnings.
jkoritzinsky Dec 1, 2020
c1040aa
Fix formatting
jkoritzinsky Dec 1, 2020
fa2f2aa
Use FEATURE_MULTIREG_RET instead of TARGET_* for XARCH case.
jkoritzinsky Dec 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/coreclr/src/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -734,15 +734,21 @@ inline bool IsCallerPop(CorInfoCallConv callConv)
}
#endif // UNIX_X86_ABI

// Represents the calling conventions supported with the extensible calling convention syntax
// as well as the metadata-encoded calling conventions and the managed calling convention.
enum CorInfoUnmanagedCallConv
{
// These correspond to CorUnmanagedCallingConvention

CORINFO_UNMANAGED_CALLCONV_UNKNOWN,
CORINFO_UNMANAGED_CALLCONV_C,
CORINFO_UNMANAGED_CALLCONV_STDCALL,
CORINFO_UNMANAGED_CALLCONV_THISCALL,
CORINFO_UNMANAGED_CALLCONV_FASTCALL
CORINFO_UNMANAGED_CALLCONV_FASTCALL,
// This represents the managed calling convention. We need some way to represent the
// managed calling convention here since we use this type within the JIT for choosing
// how to home return values and arguments.
CORINFO_UNMANAGED_CALLCONV_MANAGED
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UNMANAGED MANAGED looks confusing to me, why do we need this and why can't we use CorInfoCallConv in the Jit source and delete compMethodInfoGetUnmanagedCallConv?

Update: I looked that it was a replacement for CORINFO_UNMANAGED_CALLCONV_UNKNOWN, but I recommend to try to use CorInfoCallConv instead in the jit source code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of using CorInfoCallConv for two reasons:

  1. It represents significantly more than just method calling conventions.
  2. It doesn't enable us to extend the list of supported calling conventions easily. We added the CORINFO_CALLCONV_UNMANAGED calling convention to represent an "extensible" calling convention model that isn't directly represented by bits in metadata, so I wanted to use an enum that we could extend easily to add support for new calling conventions. The CorInfoCallConv enum doesn't have space to extend to add new unmanaged calling conventions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sandreenko would you prefer if I made a new enum to use instead of extending the CorInfoUnmanagedCallingConvention enum for this use case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment just above this says

	    // These correspond to CorUnmanagedCallingConvention

which is no longer true. Perhaps update that comment too? And I think it's also worth explaining what the newly added convention means, as the name sounds contradictory.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some comments to explain the new extended usage of this enum. Let me know what you think!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can CORINFO_UNMANAGED_CALLCONV_MANAGED be defined as CORINFO_UNMANAGED_CALLCONV_UNKNOWN? It would make it possible to have the definition to be local to the JIT.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone and done that.

// New calling conventions supported with the extensible calling convention encoding go here.
};

// These are returned from getMethodOptions
Expand Down
17 changes: 12 additions & 5 deletions src/coreclr/src/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10065,15 +10065,15 @@ void CodeGen::genVzeroupperIfNeeded(bool check256bitOnly /* = true*/)
// Return Value:
// true if type is returned in multiple registers, false otherwise.
//
bool Compiler::IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass)
bool Compiler::IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass, CorInfoUnmanagedCallConv callConv)
{
if (hClass == NO_CLASS_HANDLE)
{
return false;
}

structPassingKind howToReturnStruct;
var_types returnType = getReturnTypeForStruct(hClass, &howToReturnStruct);
var_types returnType = getReturnTypeForStruct(hClass, callConv, &howToReturnStruct);

#ifdef TARGET_ARM64
return (varTypeIsStruct(returnType) && (howToReturnStruct != SPK_PrimitiveType));
Expand Down Expand Up @@ -11529,7 +11529,11 @@ void CodeGen::genReturn(GenTree* treeNode)
}
else // we must have a struct return type
{
retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass);
CorInfoUnmanagedCallConv callConv =
compiler->compMethodInfoGetUnmanagedCallConv(compiler->info.compMethodInfo);

retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass,
callConv);
}
regCount = retTypeDesc.GetReturnRegCount();
}
Expand Down Expand Up @@ -11654,7 +11658,9 @@ void CodeGen::genStructReturn(GenTree* treeNode)
if (actualOp1->OperIs(GT_LCL_VAR))
{
varDsc = compiler->lvaGetDesc(actualOp1->AsLclVar()->GetLclNum());
retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd());
retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(),
compiler->compMethodInfoGetUnmanagedCallConv(
compiler->info.compMethodInfo));
assert(varDsc->lvIsMultiRegRet);
}
else
Expand Down Expand Up @@ -11834,7 +11840,8 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode)
hasRegs = true;
if (varReg != reg)
{
inst_RV_RV(ins_Copy(type), varReg, reg, type);
// We may need a cross register-file copy here.
inst_RV_RV(ins_Copy(reg, type), varReg, reg, type);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}
fieldVarDsc->SetRegNum(varReg);
}
Expand Down
24 changes: 22 additions & 2 deletions src/coreclr/src/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
}
else // we must have a struct return type
{
retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass);
retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass,
compiler->compMethodInfoGetUnmanagedCallConv(
compiler->info.compMethodInfo));
}

const unsigned regCount = retTypeDesc.GetReturnRegCount();
Expand Down Expand Up @@ -5709,12 +5711,30 @@ void CodeGen::genJmpMethod(GenTree* jmp)
#endif // !defined(UNIX_AMD64_ABI)
{
// Register argument
CLANG_FORMAT_COMMENT_ANCHOR;
#ifdef TARGET_X86
noway_assert(
isRegParamType(genActualType(varDsc->TypeGet())) ||
(varTypeIsStruct(varDsc->TypeGet()) && compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd())));
#else
noway_assert(isRegParamType(genActualType(varDsc->TypeGet())));
#endif // TARGET_X86

// Is register argument already in the right register?
// If not load it from its stack location.
var_types loadType = varDsc->lvaArgType();
regNumber argReg = varDsc->GetArgReg(); // incoming arg register

#ifdef TARGET_X86
if (varTypeIsStruct(varDsc->TypeGet()))
{
assert(compiler->isTrivialPointerSizedStruct(varDsc->GetStructHnd()));
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}
// Treat trivial pointer-sized structs as a pointer sized primitive
// for the purposes of registers.
loadType = TYP_I_IMPL;
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
#endif

regNumber argReg = varDsc->GetArgReg(); // incoming arg register

if (varDsc->GetRegNum() != argReg)
{
Expand Down
156 changes: 130 additions & 26 deletions src/coreclr/src/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,55 @@ bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd)
}
#endif // ARM_SOFTFP

#ifdef TARGET_X86
//---------------------------------------------------------------------------
// isTrivialPointerSizedStruct:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: I know you don't want to change the managed calling convention here but could you please clarify which decisions are temporary here. For example. on x64 we pass 8 bytes struct in a reg even if it has several fields, but we restrict x86 to 1 field, is it a temporary solution?
Allowing all structs <= pointer size to be passed as register would simplify the checks in this method and decrease the number of JIT-EE calls.

The same question about 8 byte struct, would they be passed as regs in managed calling conv in the future changes on x86?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these decisions can be changed in a future PR if we're willing to take the R2R compat break. To my knowledge, changing the calling convention breaks ready-to-run compatibility, so I didn't want to do that in this PR if I could avoid it.

// Check if the given struct type contains only one pointer-sized integer value type
//
// Arguments:
// clsHnd - the handle for the struct type.
//
// Return Value:
// true if the given struct type contains only one pointer-sized integer value type,
// false otherwise.
//
bool Compiler::isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) const
{
assert(info.compCompHnd->isValueClass(clsHnd));
if (info.compCompHnd->getClassSize(clsHnd) != TARGET_POINTER_SIZE)
{
return false;
}
for (;;)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of back and forth with the EE; would it make sense to do all this work over on the jit interface side?

(I'm guessing we rarely iterate in this loop, but still...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's very unlikely we'll iterate through this loop more than once in my experience, but I can move this to the other side of the ee-jit interface if you think that's better for perf. I was trying to avoid the hassle of versioning the jit-ee interface, but I'm fine doing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be fine if you just measured how often we have to iterate (over say PMI FX) and just confirmed it is truly rare.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've run the PMI FX diff as you recommended and confirmed that in the whole framework we run the loop body at most twice.

We run the loop body twice in 557 cases (551 cases are in Roslyn, the remaining 6 are in System.Reflection.Metadata), and we run the loop body only once in 102592 cases.

So, we run the loop body only once in 0.5% of cases if we include the Roslyn assemblies in Core_Root, or approximately 0.005% of cases if we exclude the Roslyn assemblies.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we only make it past the first checks in the function (the pointer-sized check and the first "is value type with one field" check) 19% of the time.

{
// all of class chain must be of value type and must have only one field
if (!info.compCompHnd->isValueClass(clsHnd) || info.compCompHnd->getClassNumInstanceFields(clsHnd) != 1)
{
return false;
}

CORINFO_CLASS_HANDLE* pClsHnd = &clsHnd;
CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(clsHnd, 0);
CorInfoType fieldType = info.compCompHnd->getFieldType(fldHnd, pClsHnd);

var_types vt = JITtype2varType(fieldType);

if (fieldType == CORINFO_TYPE_VALUECLASS)
{
clsHnd = *pClsHnd;
}
else if (varTypeIsI(vt) && !varTypeIsGC(vt))
{
return true;
}
else
{
return false;
}
}
}
#endif // TARGET_X86

//-----------------------------------------------------------------------------
// getPrimitiveTypeForStruct:
// Get the "primitive" type that is is used for a struct
Expand Down Expand Up @@ -693,7 +742,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
assert(structSize != 0);

// Determine if we can pass the struct as a primitive type.
// Note that on x86 we never pass structs as primitive types (unless the VM unwraps them for us).
// Note that on x86 we only pass specific pointer-sized structs as primitives that the VM used to unwrap.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will soon forget what VM used to unwrap, maybe delete an outdated reference and say something like:
// Note that on Windows we only pass specific pointer-sized structs that satisfy isTrivialPointerSizedStruct checks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the body says:

On Unix x86, always use a return buffer for structures.

Is it still true? What does this method return for a trivial struct on x86 unix?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The native calling conventions always uses a return buffer for structures, even non-trivial ones, on x86 unix. For the managed calling convention, we'll continue to support the current system (4-byte structs returned in registers).

#ifndef TARGET_X86
#ifdef UNIX_AMD64_ABI

Expand Down Expand Up @@ -728,7 +777,11 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
// and also examine the clsHnd to see if it is an HFA of count one
useType = getPrimitiveTypeForStruct(structSize, clsHnd, isVarArg);
}

#else
if (isTrivialPointerSizedStruct(clsHnd))
{
useType = TYP_I_IMPL;
}
#endif // !TARGET_X86

// Did we change this struct type into a simple "primitive" type?
Expand Down Expand Up @@ -876,6 +929,8 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
//
// Arguments:
// clsHnd - the handle for the struct type
// callConv - the calling convention of the function
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
// that returns this struct.
// wbReturnStruct - An "out" argument with information about how
// the struct is to be returned
// structSize - the size of the struct type,
Expand Down Expand Up @@ -910,9 +965,10 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
// Whenever this method's return value is TYP_STRUCT it always means
// that multiple registers are used to return this struct.
//
var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
structPassingKind* wbReturnStruct /* = nullptr */,
unsigned structSize /* = 0 */)
var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
CorInfoUnmanagedCallConv callConv,
structPassingKind* wbReturnStruct /* = nullptr */,
unsigned structSize /* = 0 */)
{
var_types useType = TYP_UNKNOWN;
structPassingKind howToReturnStruct = SPK_Unknown; // We must change this before we return
Expand Down Expand Up @@ -950,10 +1006,32 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
{
// Return classification is not always size based...
canReturnInRegister = structDesc.passedInRegisters;
if (!canReturnInRegister)
{
assert(structDesc.eightByteCount == 0);
howToReturnStruct = SPK_ByReference;
useType = TYP_UNKNOWN;
}
}

#endif // UNIX_AMD64_ABI
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved

#ifdef UNIX_X86_ABI
if (callConv != CORINFO_UNMANAGED_CALLCONV_MANAGED)
{
canReturnInRegister = false;
howToReturnStruct = SPK_ByReference;
useType = TYP_UNKNOWN;
}
#elif defined(TARGET_WINDOWS) && !defined(TARGET_ARM)
if (callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL)
{
canReturnInRegister = false;
howToReturnStruct = SPK_ByReference;
useType = TYP_UNKNOWN;
}
#endif

// Check for cases where a small struct is returned in a register
// via a primitive type.
//
Expand Down Expand Up @@ -1008,7 +1086,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
// If so, we should have already set howToReturnStruct, too.
assert(howToReturnStruct != SPK_Unknown);
}
else // We can't replace the struct with a "primitive" type
else if (canReturnInRegister) // We can't replace the struct with a "primitive" type
{
// See if we can return this struct by value, possibly in multiple registers
// or if we should return it using a return buffer register
Expand All @@ -1031,24 +1109,13 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,

#ifdef UNIX_AMD64_ABI

// The case of (structDesc.eightByteCount == 1) should have already been handled
if (structDesc.eightByteCount > 1)
{
// setup wbPassType and useType indicate that this is returned by value in multiple registers
howToReturnStruct = SPK_ByValue;
useType = TYP_STRUCT;
assert(structDesc.passedInRegisters == true);
}
else
{
assert(structDesc.eightByteCount == 0);
// Otherwise we return this struct using a return buffer
// setup wbPassType and useType indicate that this is return using a return buffer register
// (reference to a return buffer)
howToReturnStruct = SPK_ByReference;
useType = TYP_UNKNOWN;
assert(structDesc.passedInRegisters == false);
}
// The cases of (structDesc.eightByteCount == 1) and (structDesc.eightByteCount == 0)
// should have already been handled
assert(structDesc.eightByteCount > 1);
// setup wbPassType and useType indicate that this is returned by value in multiple registers
howToReturnStruct = SPK_ByValue;
useType = TYP_STRUCT;
assert(structDesc.passedInRegisters == true);

#elif defined(TARGET_ARM64)

Expand All @@ -1071,8 +1138,26 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
howToReturnStruct = SPK_ByReference;
useType = TYP_UNKNOWN;
}
#elif defined(TARGET_X86)

#elif defined(TARGET_ARM) || defined(TARGET_X86)
// Only 8-byte structs are return in multiple registers.
// We also only support multireg struct returns on x86 to match the native calling convention.
// So return 8-byte structs only when the calling convention is a native calling convention.
if (structSize == MAX_RET_MULTIREG_BYTES && callConv != CORINFO_UNMANAGED_CALLCONV_MANAGED)
{
// setup wbPassType and useType indicate that this is return by value in multiple registers
howToReturnStruct = SPK_ByValue;
useType = TYP_STRUCT;
}
else
{
// Otherwise we return this struct using a return buffer
// setup wbPassType and useType indicate that this is returned using a return buffer register
// (reference to a return buffer)
howToReturnStruct = SPK_ByReference;
useType = TYP_UNKNOWN;
}
#elif defined(TARGET_ARM)

// Otherwise we return this struct using a return buffer
// setup wbPassType and useType indicate that this is returned using a return buffer register
Expand Down Expand Up @@ -1975,6 +2060,22 @@ unsigned Compiler::compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd)
return sigSize;
}

bool Compiler::callConvIsInstanceMethodCallConv(CorInfoUnmanagedCallConv callConv)
{
return callConv == CORINFO_UNMANAGED_CALLCONV_THISCALL;
}

CorInfoUnmanagedCallConv Compiler::compMethodInfoGetUnmanagedCallConv(CORINFO_METHOD_INFO* mthInfo)
{
CorInfoCallConv callConv = mthInfo->args.getCallConv();
if (callConv == CORINFO_CALLCONV_DEFAULT || callConv == CORINFO_CALLCONV_VARARG)
{
// Both the default and the varargs calling conventions represent a managed callconv.
return CORINFO_UNMANAGED_CALLCONV_MANAGED;
}
return (CorInfoUnmanagedCallConv)callConv;
}

#ifdef DEBUG
static bool DidComponentUnitTests = false;

Expand Down Expand Up @@ -2209,7 +2310,7 @@ void Compiler::compSetProcessor()
#elif defined(TARGET_ARM64)
info.genCPU = CPU_ARM64;
#elif defined(TARGET_AMD64)
info.genCPU = CPU_X64;
info.genCPU = CPU_X64;
#elif defined(TARGET_X86)
if (jitFlags.IsSet(JitFlags::JIT_FLAG_TARGET_P4))
info.genCPU = CPU_X86_PENTIUM_4;
Expand Down Expand Up @@ -5976,6 +6077,9 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
case CORINFO_CALLCONV_NATIVEVARARG:
info.compIsVarArgs = true;
break;
case CORINFO_CALLCONV_C:
case CORINFO_CALLCONV_STDCALL:
case CORINFO_CALLCONV_THISCALL:
case CORINFO_CALLCONV_DEFAULT:
info.compIsVarArgs = false;
break;
Expand Down
Loading