Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
35 changes: 30 additions & 5 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4626,8 +4626,13 @@ bool Compiler::impIsImplicitTailCallCandidate(
// In the multi-reg case, we we force IR to be one of the following:
Comment thread
AndyAyersMS marked this conversation as resolved.
Outdated
// GT_RETURN(LCL_VAR) or GT_RETURN(CALL). If op is anything other than
// a lclvar or call, it is assigned to a temp, which is then returned.
// In the non-multireg case, the two special helpers with "fake" return
// buffers are handled ("GETFIELDSTRUCT" and "UNBOX_NULLABLE").
// In the non-multireg case, calls that return via a return buffer are
// handled. This covers the two special helpers with "fake" return buffers
// ("GETFIELDSTRUCT" and "UNBOX_NULLABLE"), as well as inline candidates
// whose ABI requires a real return buffer. The latter reach here when an
Comment thread
AndyAyersMS marked this conversation as resolved.
Outdated
// intrinsic such as "Unsafe.BitCast" forwards the candidate's GT_RET_EXPR
// as the return value of a method whose own return type does not use a
// return buffer.
//
// Arguments:
// op - the return value
Expand All @@ -4643,12 +4648,32 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op)
JITDUMP("\nimpFixupStructReturnType: retyping\n");
DISPTREE(op);

if (op->IsCall() && op->AsCall()->ShouldHaveRetBufArg())
GenTreeCall* retBufCall = nullptr;

if (op->IsCall())
{
retBufCall = op->AsCall();
}
else if (op->OperIs(GT_RET_EXPR))
{
// This must be one of those 'special' helpers that don't really have a return buffer, but instead
// use it as a way to keep the trees cleaner with fewer address-taken temps. Well now we have to
// The placeholder was created during this importation, so it cannot have been
// substituted yet; only "gtSubstExpr" can form chains of GT_RET_EXPRs, and those
// are walked later by "UpdateInlineReturnExpressionPlaceHolder".
assert(op->AsRetExpr()->gtSubstExpr == nullptr);
retBufCall = op->AsRetExpr()->gtInlineCandidate;
}

if ((retBufCall != nullptr) && retBufCall->ShouldHaveRetBufArg())
{
// This is either one of those 'special' helpers that don't really have a return buffer,
// but instead use it as a way to keep the trees cleaner with fewer address-taken temps,
// or a call that genuinely returns via a return buffer. Either way we now have to
// materialize the return buffer as an address-taken temp. Then we can return the temp.
//
// Note we store "op" itself rather than "retBufCall": for a GT_RET_EXPR this lets
// "impStoreStruct" insert the return buffer argument into the candidate and prune the
// placeholder, which is what establishes the argument the inliner later expects.
Comment thread
AndyAyersMS marked this conversation as resolved.
Outdated
//
unsigned tmpNum = lvaGrabTemp(true DEBUGARG("pseudo return buffer"));

// No need to spill anything as we're about to return.
Expand Down
38 changes: 38 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_130135/Runtime_130135.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Runtime_130135;

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using Xunit;

public static class Runtime_130135
{
[Fact]
public static void TestEntryPoint()
{
V2<int> result = Unbox(GetV2());

Assert.Equal(1, result.X);
Assert.Equal(2, result.Y);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static V2<int> GetV2() =>
Unsafe.BitCast<Vector64<int>, V2<int>>(CreateVector());

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector64<int> CreateVector() => Vector64.Create(1, 2);

[MethodImpl(MethodImplOptions.NoInlining)]
private static V2<int> Unbox(object value) => (V2<int>)value;

[StructLayout(LayoutKind.Sequential)]
private struct V2<T>(T x, T y)
{
public T X = x;
public T Y = y;
}
}
1 change: 1 addition & 0 deletions src/tests/JIT/Regression/Regression_ro_2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
<Compile Include="JitBlue\Runtime_129970\Runtime_129970.cs" />
<Compile Include="JitBlue\Runtime_129971\Runtime_129971.cs" />
<Compile Include="JitBlue\Runtime_129972\Runtime_129972.cs" />
<Compile Include="JitBlue\Runtime_130135\Runtime_130135.cs" />
<Compile Include="JitBlue\Runtime_130189\Runtime_130189.cs" />
<Compile Include="JitBlue\Runtime_130216\Runtime_130216.cs" />
<Compile Include="JitBlue\Runtime_130248\Runtime_130248.cs" />
Expand Down
Loading