Skip to content

Widen return values in CallTailCallTarget stubs #55740

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 13 additions & 12 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8272,10 +8272,9 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig
// void DispatchTailCalls(void* callersRetAddrSlot, void* callTarget, void* retValue)

// Add return value arg.
GenTree* retValArg;
GenTree* retVal = nullptr;
unsigned int newRetLcl = BAD_VAR_NUM;
GenTree* copyToRetBufNode = nullptr;
GenTree* retValArg;
GenTree* retVal = nullptr;
GenTree* copyToRetBufNode = nullptr;

if (origCall->HasRetBufArg())
{
Expand Down Expand Up @@ -8326,24 +8325,26 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig
else if (origCall->gtType != TYP_VOID)
{
JITDUMP("Creating a new temp for the return value\n");
newRetLcl = lvaGrabTemp(false DEBUGARG("Return value for tail call dispatcher"));
unsigned int newRetLcl = lvaGrabTemp(false DEBUGARG("Return value for tail call dispatcher"));
if (varTypeIsStruct(origCall->gtType))
{
lvaSetStruct(newRetLcl, origCall->gtRetClsHnd, false);
}
else
{
// Since we pass a reference to the return value to the dispatcher
// we need to use the real return type so we can normalize it on
// load when we return it.
lvaTable[newRetLcl].lvType = (var_types)origCall->gtReturnType;
// It is the responsibility of the call-tail-call-target stub to
// normalize the return value in the same way that we would do it
// when it is stored. It is not possible for us to normalize it as
// we don't know which eventual tailcaller may have written it, and
// the type of that tailcaller could potentially be narrower than
// this call. See TailCallHelp::NeedsReturnWidening.
lvaTable[newRetLcl].lvType = origCall->gtType;
}

lvaSetVarAddrExposed(newRetLcl);

retValArg =
gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(newRetLcl, genActualType(lvaTable[newRetLcl].lvType)));
retVal = gtNewLclvNode(newRetLcl, genActualType(lvaTable[newRetLcl].lvType));
retValArg = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(newRetLcl, lvaTable[newRetLcl].lvType));
retVal = gtNewLclvNode(newRetLcl, lvaTable[newRetLcl].lvType);

if (varTypeIsStruct(origCall->gtType))
{
Expand Down
37 changes: 36 additions & 1 deletion src/coreclr/vm/tailcallhelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,28 @@ TypeHandle TailCallHelp::NormalizeSigType(TypeHandle tyHnd)
return tyHnd;
}

// The managed calling convention requires that return types are widened up to 4 bytes.
// This needs to be mirrored when storing the return value.
bool TailCallHelp::NeedsReturnWidening(TypeHandle tyHnd, bool* isSigned)
{
CorElementType ety = tyHnd.GetSignatureCorElementType();
switch (ety)
{
case ELEMENT_TYPE_I1:
case ELEMENT_TYPE_I2:
*isSigned = true;
return true;
case ELEMENT_TYPE_BOOLEAN:
case ELEMENT_TYPE_CHAR:
case ELEMENT_TYPE_U1:
case ELEMENT_TYPE_U2:
*isSigned = false;
return true;
default:
return false;
}
}

bool TailCallHelp::GenerateGCDescriptor(
MethodDesc* pTargetMD, const ArgBufferLayout& layout, GCRefMapBuilder* builder)
{
Expand Down Expand Up @@ -579,7 +601,20 @@ MethodDesc* TailCallHelp::CreateCallTargetStub(const TailCallInfo& info)

pCode->EmitLDARG(ARG_RET_VAL);
pCode->EmitLDLOC(resultLcl);
EmitStoreTyHnd(pCode, info.RetTyHnd);
bool isSigned;
if (NeedsReturnWidening(info.RetTyHnd, &isSigned))
{
if (isSigned)
pCode->EmitCONV_I4();
else
pCode->EmitCONV_U4();
Copy link
Member Author

Choose a reason for hiding this comment

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

Is this even needed?

Copy link
Member

Choose a reason for hiding this comment

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

It should not be. EmitLDLOC should do the appropriate widening implicitly.


pCode->EmitSTIND_I4();
}
else
{
EmitStoreTyHnd(pCode, info.RetTyHnd);
}
}

pCode->EmitRET();
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/tailcallhelp.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class TailCallHelp
MetaSig& callSiteSig, MethodDesc* calleeMD,
bool storeTarget, bool thisArgByRef, bool hasInstArg, ArgBufferLayout* layout);
static TypeHandle NormalizeSigType(TypeHandle tyHnd);
static bool NeedsReturnWidening(TypeHandle tyHnd, bool* isSigned);
static bool GenerateGCDescriptor(MethodDesc* pTargetMD, const ArgBufferLayout& values, GCRefMapBuilder* builder);

static MethodDesc* CreateStoreArgsStub(TailCallInfo& info);
Expand Down
21 changes: 21 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_55253/Runtime_55253.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Runtime_55253
{
static int Main()
{
int errors = 0;
if (AsInt32() != -1)
errors |= 1;
if (AsUInt32() != 255)
errors |= 2;

return 100 + errors;
}

static uint AsUInt32() => AsUInt16();
static uint AsUInt16() => AsUInt8();
static uint AsUInt8() => 255;

static int AsInt32() => AsInt16();
static short AsInt16() => AsInt8();
static sbyte AsInt8() => -1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>False</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
<PropertyGroup>
<CLRTestBatchPreCommands><![CDATA[
$(CLRTestBatchPreCommands)
set DOTNET_TailcallStress=1
]]></CLRTestBatchPreCommands>
<BashCLRTestPreCommands><![CDATA[
$(BashCLRTestPreCommands)
export DOTNET_TailcallStress=1
]]></BashCLRTestPreCommands>
</PropertyGroup>
</Project>