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

Retype calls as SIMD when applicable. #53578

Merged
merged 7 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
9 changes: 1 addition & 8 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6500,12 +6500,6 @@ GenTree* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSE
// Make an exception for implicit by-ref parameters during global morph, since
// their lvType has been updated to byref but their appearances have not yet all
// been rewritten and so may have struct type still.
// Also, make an exception for retyping of a lclVar to a SIMD or scalar type of the same
// size as the struct if it has a single field This can happen when we retype the lhs
// of a call assignment.
// TODO-1stClassStructs: When we stop "lying" about the types for ABI purposes, we
// should be able to remove this exception and handle the assignment mismatch in
// Lowering.
LclVarDsc* varDsc = lvaGetDesc(lnum);

bool simd12ToSimd16Widening = false;
Expand All @@ -6514,8 +6508,7 @@ GenTree* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSE
simd12ToSimd16Widening = (type == TYP_SIMD16) && (varDsc->lvType == TYP_SIMD12);
#endif
assert((type == varDsc->lvType) || simd12ToSimd16Widening ||
(lvaIsImplicitByRefLocal(lnum) && fgGlobalMorph && (varDsc->lvType == TYP_BYREF)) ||
((varDsc->lvType == TYP_STRUCT) && (genTypeSize(type) == varDsc->lvExactSize)));
(lvaIsImplicitByRefLocal(lnum) && fgGlobalMorph && (varDsc->lvType == TYP_BYREF)));
}
GenTree* node = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs));

Expand Down
113 changes: 42 additions & 71 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9489,9 +9489,10 @@ var_types Compiler::impImportJitTestLabelMark(int numArgs)
#endif // DEBUG

//-----------------------------------------------------------------------------------
// impFixupCallStructReturn: For a call node that returns a struct type either
// adjust the return type to an enregisterable type, or set the flag to indicate
// struct return via retbuf arg.
// impFixupCallStructReturn: For a call node that returns a struct do one of the following:
// - set the flag to indicate struct return via retbuf arg;
// - adjust the return type to a SIMD type if it is returned in 1 reg;
// - spill call result into a temp if it is returned into 2 registers or more and not tail call or inline candidate.
//
// Arguments:
// call - GT_CALL GenTree node
Expand All @@ -9511,92 +9512,62 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN

#if FEATURE_MULTIREG_RET
call->InitializeStructReturnType(this, retClsHnd, call->GetUnmanagedCallConv());
#endif // FEATURE_MULTIREG_RET

#ifdef UNIX_AMD64_ABI

// Not allowed for FEATURE_CORCLR which is the only SKU available for System V OSs.
assert(!call->IsVarargs() && "varargs not allowed for System V OSs.");

const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
const unsigned retRegCount = retTypeDesc->GetReturnRegCount();
if (retRegCount == 0)
{
// struct not returned in registers i.e returned via hiddden retbuf arg.
call->gtCallMoreFlags |= GTF_CALL_M_RETBUFFARG;
}
else if (retRegCount == 1)
{
return call;
}
else
{
// must be a struct returned in two registers
assert(retRegCount == 2);

if ((!call->CanTailCall()) && (!call->IsInlineCandidate()))
{
// Force a call returning multi-reg struct to be always of the IR form
// tmp = call
//
// No need to assign a multi-reg struct to a local var if:
// - It is a tail call or
// - The call is marked for in-lining later
return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->GetUnmanagedCallConv()));
}
}

#else // not UNIX_AMD64_ABI
#else // !FEATURE_MULTIREG_RET
const unsigned retRegCount = 1;
#endif // !FEATURE_MULTIREG_RET

// Check for TYP_STRUCT type that wraps a primitive type
// Such structs are returned using a single register
// and we change the return type on those calls here.
//
structPassingKind howToReturnStruct;
var_types returnType;

returnType = getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct);
var_types returnType = getReturnTypeForStruct(retClsHnd, call->GetUnmanagedCallConv(), &howToReturnStruct);

if (howToReturnStruct == SPK_ByReference)
{
assert(returnType == TYP_UNKNOWN);
call->gtCallMoreFlags |= GTF_CALL_M_RETBUFFARG;
return call;
}
else
{

#if !FEATURE_MULTIREG_RET
return call;
#else // FEATURE_MULTIREG_RET
const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
const unsigned retRegCount = retTypeDesc->GetReturnRegCount();
assert(retRegCount != 0);
if (retRegCount == 1)
if (retRegCount == 1)
{
// Recognize SIMD types as we do for LCL_VARs,
// note it could be not the ABI specific type, for example, on x64 we can set 'TYP_SIMD8`
// for `System.Numerics.Vector2` here but lower will change it to long as ABI dictates.
var_types simdReturnType = impNormStructType(call->gtRetClsHnd);
if (simdReturnType != call->TypeGet())
{
return call;
assert(varTypeIsSIMD(simdReturnType));
JITDUMP("changing the type of a single reg call [%06u] from %s to %s\n", dspTreeID(call),
varTypeName(call->TypeGet()), varTypeName(returnType));
call->ChangeType(simdReturnType);
}
return call;
}

assert(returnType == TYP_STRUCT);
assert((howToReturnStruct == SPK_ByValueAsHfa) || (howToReturnStruct == SPK_ByValue));

assert(call->gtReturnType == returnType);
#if FEATURE_MULTIREG_RET
assert(returnType == TYP_STRUCT);
assert(call->gtReturnType == returnType);
assert((howToReturnStruct == SPK_ByValueAsHfa) || (howToReturnStruct == SPK_ByValue));

assert(retRegCount >= 2);
if ((!call->CanTailCall()) && (!call->IsInlineCandidate()))
{
// Force a call returning multi-reg struct to be always of the IR form
// tmp = call
//
// No need to assign a multi-reg struct to a local var if:
// - It is a tail call or
// - The call is marked for in-lining later
return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->GetUnmanagedCallConv()));
}
#endif // FEATURE_MULTIREG_RET
}
#ifdef UNIX_AMD64_ABI
// must be a struct returned in two registers
assert(retRegCount == 2);
#else // not UNIX_AMD64_ABI
assert(retRegCount >= 2);
#endif // not UNIX_AMD64_ABI

if (!call->CanTailCall() && !call->IsInlineCandidate())
{
// Force a call returning multi-reg struct to be always of the IR form
// tmp = call
//
// No need to assign a multi-reg struct to a local var if:
// - It is a tail call or
// - The call is marked for in-lining later
return impAssignMultiRegTypeToVar(call, retClsHnd DEBUGARG(call->GetUnmanagedCallConv()));
}
return call;
#endif // FEATURE_MULTIREG_RET
}

/*****************************************************************************
Expand Down
106 changes: 106 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_52864/Runtime_52864.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Runtime.CompilerServices;

using Point = System.Numerics.Vector2;

namespace Runtime_52864
{
public class test
{
static Point checkA;
static Point checkB;
static Point checkC;
static int returnVal;

public const int DefaultSeed = 20010415;
public static int Seed = Environment.GetEnvironmentVariable("CORECLR_SEED") switch
{
string seedStr when seedStr.Equals("random", StringComparison.OrdinalIgnoreCase) => new Random().Next(),
string seedStr when int.TryParse(seedStr, out int envSeed) => envSeed,
_ => DefaultSeed
};

[MethodImpl(MethodImplOptions.NoInlining)]
static void check(Point a, Point b, Point c)
{
if (a != checkA)
{
Console.WriteLine($"A doesn't match. Should be {checkA} but is {a}");
returnVal = -1;
}
if (b != checkB)
{
Console.WriteLine($"B doesn't match. Should be {checkB} but is {b}");
returnVal = -1;
}
if (c != checkC)
{
Console.WriteLine($"C doesn't match. Should be {checkC} but is {c}");
returnVal = -1;
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void FailureCase(List<Point> p)
{
Point p1 = p[0];
Point p2 = p.Last();

check(p1, p[1], p2);
check(p1, p[1], p2);
check(p1, p[1], p2);
}

static Point NextPoint(Random random)
{
return new Point(
(float)random.NextDouble(),
(float)random.NextDouble()
);
}

static int Main()
{
returnVal = 100;
Random random = new Random(Seed);

for (int i = 0; i < 50; i++)
{
List<Point> p = new List<Point>();

checkA = NextPoint(random);
p.Add(checkA);
checkB = NextPoint(random);
p.Add(checkB);
checkC = NextPoint(random);
p.Add(checkC);

FailureCase(p);

Thread.Sleep(15);
}

for (int i = 0; i < 50; i++)
{
List<Point> p = new List<Point>();

checkA = NextPoint(random);
p.Add(checkA);
checkB = NextPoint(random);
p.Add(checkB);
checkC = NextPoint(random);
p.Add(checkC);

FailureCase(p);
}

return returnVal;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<CLRTestPriority>1</CLRTestPriority>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup>
<CLRTestBatchPreCommands><![CDATA[
$(CLRTestBatchPreCommands)
set COMPlus_TieredPGO=1
set COMPlus_TC_QuickJitForLoops=1
]]></CLRTestBatchPreCommands>
<BashCLRTestPreCommands><![CDATA[
$(BashCLRTestPreCommands)
export COMPlus_TieredPGO=1
export COMPlus_TC_QuickJitForLoops=1
]]></BashCLRTestPreCommands>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>