Skip to content

Commit cf6a669

Browse files
Use signature types when building the ABI info
This will allow us to let "mismatched" struct types as call arguments, which in turn is expected to simplify and de-pessimize much of the code working around the need to keep precise handles on struct nodes. Credit to @jakobbotsch for being able to make this change.
1 parent 5c559f1 commit cf6a669

File tree

4 files changed

+46
-87
lines changed

4 files changed

+46
-87
lines changed

src/coreclr/jit/codegencommon.cpp

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6581,35 +6581,6 @@ bool Compiler::IsHfa(CORINFO_CLASS_HANDLE hClass)
65816581
return varTypeIsValidHfaType(GetHfaType(hClass));
65826582
}
65836583

6584-
bool Compiler::IsHfa(GenTree* tree)
6585-
{
6586-
if (GlobalJitOptions::compFeatureHfa)
6587-
{
6588-
return IsHfa(gtGetStructHandleIfPresent(tree));
6589-
}
6590-
else
6591-
{
6592-
return false;
6593-
}
6594-
}
6595-
6596-
var_types Compiler::GetHfaType(GenTree* tree)
6597-
{
6598-
if (GlobalJitOptions::compFeatureHfa)
6599-
{
6600-
return GetHfaType(gtGetStructHandleIfPresent(tree));
6601-
}
6602-
else
6603-
{
6604-
return TYP_UNDEF;
6605-
}
6606-
}
6607-
6608-
unsigned Compiler::GetHfaCount(GenTree* tree)
6609-
{
6610-
return GetHfaCount(gtGetStructHandle(tree));
6611-
}
6612-
66136584
var_types Compiler::GetHfaType(CORINFO_CLASS_HANDLE hClass)
66146585
{
66156586
if (GlobalJitOptions::compFeatureHfa)

src/coreclr/jit/compiler.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,11 +1882,6 @@ class Compiler
18821882
//
18831883

18841884
bool IsHfa(CORINFO_CLASS_HANDLE hClass);
1885-
bool IsHfa(GenTree* tree);
1886-
1887-
var_types GetHfaType(GenTree* tree);
1888-
unsigned GetHfaCount(GenTree* tree);
1889-
18901885
var_types GetHfaType(CORINFO_CLASS_HANDLE hClass);
18911886
unsigned GetHfaCount(CORINFO_CLASS_HANDLE hClass);
18921887

src/coreclr/jit/lower.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3814,18 +3814,18 @@ void Lowering::LowerCallStruct(GenTreeCall* call)
38143814

38153815
if (GlobalJitOptions::compFeatureHfa)
38163816
{
3817-
if (comp->IsHfa(call))
3817+
if (comp->IsHfa(call->gtRetClsHnd))
38183818
{
38193819
#if defined(TARGET_ARM64)
3820-
assert(comp->GetHfaCount(call) == 1);
3820+
assert(comp->GetHfaCount(call->gtRetClsHnd) == 1);
38213821
#elif defined(TARGET_ARM)
38223822
// ARM returns double in 2 float registers, but
38233823
// `call->HasMultiRegRetVal()` count double registers.
3824-
assert(comp->GetHfaCount(call) <= 2);
3824+
assert(comp->GetHfaCount(call->gtRetClsHnd) <= 2);
38253825
#else // !TARGET_ARM64 && !TARGET_ARM
38263826
NYI("Unknown architecture");
38273827
#endif // !TARGET_ARM64 && !TARGET_ARM
3828-
var_types hfaType = comp->GetHfaType(call);
3828+
var_types hfaType = comp->GetHfaType(call->gtRetClsHnd);
38293829
if (call->TypeIs(hfaType))
38303830
{
38313831
return;

src/coreclr/jit/morph.cpp

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2233,7 +2233,12 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
22332233
argx->gtType = TYP_I_IMPL;
22342234
}
22352235

2236-
// Setup any HFA information about 'argx'
2236+
// Note we must use the signature types for making ABI decisions. This is especially important for structs,
2237+
// where the "argx" node can legally have a type that is not ABI-compatible with the one in the signature.
2238+
const var_types argSigType = arg.GetSignatureType();
2239+
const CORINFO_CLASS_HANDLE argSigClass = arg.GetSignatureClassHandle();
2240+
2241+
// Setup any HFA information about the argument.
22372242
bool isHfaArg = false;
22382243
var_types hfaType = TYP_UNDEF;
22392244
unsigned hfaSlots = 0;
@@ -2245,7 +2250,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
22452250

22462251
if (GlobalJitOptions::compFeatureHfa)
22472252
{
2248-
hfaType = comp->GetHfaType(argx);
2253+
hfaType = comp->GetHfaType(argSigClass);
22492254
isHfaArg = varTypeIsValidHfaType(hfaType);
22502255

22512256
#if defined(TARGET_ARM64)
@@ -2258,7 +2263,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
22582263

22592264
if (isHfaArg)
22602265
{
2261-
hfaSlots = comp->GetHfaCount(argx);
2266+
hfaSlots = comp->GetHfaCount(argSigClass);
22622267

22632268
// If we have a HFA struct it's possible we transition from a method that originally
22642269
// only had integer types to now start having FP types. We have to communicate this
@@ -2272,10 +2277,11 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
22722277
const bool isFloatHfa = (hfaType == TYP_FLOAT);
22732278

22742279
#ifdef TARGET_ARM
2275-
passUsingFloatRegs = !callIsVararg && (isHfaArg || varTypeUsesFloatReg(argx)) && !comp->opts.compUseSoftFP;
2280+
passUsingFloatRegs =
2281+
!callIsVararg && (isHfaArg || varTypeUsesFloatReg(argSigType)) && !comp->opts.compUseSoftFP;
22762282
bool passUsingIntRegs = passUsingFloatRegs ? false : (intArgRegNum < MAX_REG_ARG);
22772283

2278-
// We don't use the "size" return value from InferOpSizeAlign().
2284+
// TODO-Bug: this should use the signature type/class instead of "argx".
22792285
comp->codeGen->InferOpSizeAlign(argx, &argAlignBytes);
22802286

22812287
argAlignBytes = roundUp(argAlignBytes, TARGET_POINTER_SIZE);
@@ -2303,11 +2309,11 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
23032309
#elif defined(TARGET_ARM64)
23042310

23052311
assert(!callIsVararg || !isHfaArg);
2306-
passUsingFloatRegs = !callIsVararg && (isHfaArg || varTypeUsesFloatReg(argx));
2312+
passUsingFloatRegs = !callIsVararg && (isHfaArg || varTypeUsesFloatReg(argSigType));
23072313

23082314
#elif defined(TARGET_AMD64)
23092315

2310-
passUsingFloatRegs = varTypeIsFloating(argx);
2316+
passUsingFloatRegs = varTypeIsFloating(argSigType);
23112317

23122318
#elif defined(TARGET_X86)
23132319

@@ -2316,7 +2322,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
23162322
#elif defined(TARGET_LOONGARCH64)
23172323

23182324
assert(!callIsVararg && !isHfaArg);
2319-
passUsingFloatRegs = varTypeUsesFloatReg(argx);
2325+
passUsingFloatRegs = varTypeUsesFloatReg(argSigType);
23202326
DWORD floatFieldFlags = STRUCT_NO_FLOAT_FIELD;
23212327

23222328
#else
@@ -2325,43 +2331,34 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
23252331

23262332
bool isBackFilled = false;
23272333
unsigned nextFltArgRegNum = fltArgRegNum; // This is the next floating-point argument register number to use
2334+
bool isStructArg = varTypeIsStruct(argSigType);
23282335
var_types structBaseType = TYP_STRUCT;
23292336
unsigned structSize = 0;
23302337
bool passStructByRef = false;
23312338

2332-
bool isStructArg;
2333-
GenTree* actualArg = argx->gtEffectiveVal(true /* Commas only */);
2334-
23352339
//
23362340
// Figure out the size of the argument. This is either in number of registers, or number of
23372341
// TARGET_POINTER_SIZE stack slots, or the sum of these if the argument is split between the registers and
23382342
// the stack.
23392343
//
2340-
isStructArg = varTypeIsStruct(argx);
2341-
// Note that we internally in the JIT can change some struct args to
2342-
// primitive args (e.g. OBJ<struct>(x) -> IND<TYP_LONG>(x)). Similarly,
2343-
// the ABI type can also change from struct to primitive (e.g. a 8-byte
2344-
// struct passed in a register). So isStructArg may be false even if
2345-
// the signature type was (or is) a struct, however only in cases where
2346-
// it does not matter.
2347-
CORINFO_CLASS_HANDLE objClass = NO_CLASS_HANDLE;
2344+
23482345
if (isStructArg)
23492346
{
2350-
objClass = comp->gtGetStructHandle(argx);
2351-
if (argx->TypeGet() == TYP_STRUCT)
2347+
GenTree* actualArg = argx->gtEffectiveVal(true /* Commas only */);
2348+
2349+
// Here we look at "actualArg" to avoid calling "getClassSize".
2350+
if (actualArg->TypeGet() == TYP_STRUCT)
23522351
{
2353-
// For TYP_STRUCT arguments we must have an OBJ, LCL_VAR or MKREFANY
23542352
switch (actualArg->OperGet())
23552353
{
23562354
case GT_OBJ:
2357-
structSize = actualArg->AsObj()->GetLayout()->GetSize();
2358-
assert(structSize == comp->info.compCompHnd->getClassSize(objClass));
2355+
structSize = actualArg->AsObj()->Size();
23592356
break;
23602357
case GT_LCL_VAR:
23612358
structSize = comp->lvaGetDesc(actualArg->AsLclVarCommon())->lvExactSize;
23622359
break;
23632360
case GT_MKREFANY:
2364-
structSize = comp->info.compCompHnd->getClassSize(objClass);
2361+
structSize = comp->info.compCompHnd->getClassSize(argSigClass);
23652362
break;
23662363
default:
23672364
BADCODE("illegal argument tree: cannot determine size for ABI handling");
@@ -2370,28 +2367,29 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
23702367
}
23712368
else
23722369
{
2373-
structSize = genTypeSize(argx);
2374-
assert(structSize == comp->info.compCompHnd->getClassSize(objClass));
2370+
structSize = genTypeSize(actualArg);
23752371
}
2372+
2373+
assert(structSize = comp->info.compCompHnd->getClassSize(argSigClass));
23762374
}
23772375
#if defined(TARGET_AMD64)
23782376
#ifdef UNIX_AMD64_ABI
23792377
if (!isStructArg)
23802378
{
23812379
size = 1; // On AMD64, all primitives fit in a single (64-bit) 'slot'
2382-
byteSize = genTypeSize(arg.GetSignatureType());
2380+
byteSize = genTypeSize(argSigType);
23832381
}
23842382
else
23852383
{
23862384
size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE;
23872385
byteSize = structSize;
2388-
comp->eeGetSystemVAmd64PassStructInRegisterDescriptor(objClass, &structDesc);
2386+
comp->eeGetSystemVAmd64PassStructInRegisterDescriptor(argSigClass, &structDesc);
23892387
}
23902388
#else // !UNIX_AMD64_ABI
23912389
size = 1; // On AMD64 Windows, all args fit in a single (64-bit) 'slot'
23922390
if (!isStructArg)
23932391
{
2394-
byteSize = genTypeSize(arg.GetSignatureType());
2392+
byteSize = genTypeSize(argSigType);
23952393
}
23962394

23972395
#endif // UNIX_AMD64_ABI
@@ -2402,9 +2400,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
24022400
{
24032401
// HFA structs are passed by value in multiple registers.
24042402
// The "size" in registers may differ the size in pointer-sized units.
2405-
CORINFO_CLASS_HANDLE structHnd = comp->gtGetStructHandle(argx);
2406-
size = comp->GetHfaCount(structHnd);
2407-
byteSize = comp->info.compCompHnd->getClassSize(structHnd);
2403+
size = hfaSlots;
2404+
byteSize = structSize;
24082405
}
24092406
else
24102407
{
@@ -2420,13 +2417,13 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
24202417
size = 1;
24212418
}
24222419
}
2423-
// Note that there are some additional rules for multireg structs.
2420+
// Note that there are some additional rules for multireg structs on ARM64.
24242421
// (i.e they cannot be split between registers and the stack)
24252422
}
24262423
else
24272424
{
24282425
size = 1; // Otherwise, all primitive types fit in a single (64-bit) 'slot'
2429-
byteSize = genTypeSize(arg.GetSignatureType());
2426+
byteSize = genTypeSize(argSigType);
24302427
}
24312428
#elif defined(TARGET_ARM) || defined(TARGET_X86)
24322429
if (isStructArg)
@@ -2438,8 +2435,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
24382435
{
24392436
// The typical case.
24402437
// Long/double type argument(s) will be modified as needed in Lowering.
2441-
size = genTypeStSz(argx->gtType);
2442-
byteSize = genTypeSize(arg.GetSignatureType());
2438+
size = genTypeStSz(argSigType);
2439+
byteSize = genTypeSize(argSigType);
24432440
}
24442441
#else
24452442
#error Unsupported or unset target architecture
@@ -2451,14 +2448,14 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
24512448
assert(structSize != 0);
24522449

24532450
Compiler::structPassingKind howToPassStruct;
2454-
structBaseType = comp->getArgTypeForStruct(objClass, &howToPassStruct, callIsVararg, structSize);
2451+
structBaseType = comp->getArgTypeForStruct(argSigClass, &howToPassStruct, callIsVararg, structSize);
24552452
passStructByRef = (howToPassStruct == Compiler::SPK_ByReference);
24562453
#if defined(TARGET_LOONGARCH64)
24572454
if (!passStructByRef)
24582455
{
24592456
assert((howToPassStruct == Compiler::SPK_ByValue) || (howToPassStruct == Compiler::SPK_PrimitiveType));
24602457

2461-
floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(objClass);
2458+
floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(argSigClass);
24622459

24632460
passUsingFloatRegs = (floatFieldFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) ? true : false;
24642461
comp->compFloatingPointUsed |= passUsingFloatRegs;
@@ -2469,8 +2466,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
24692466
// for "struct { float, float }", and retyping to a primitive here will cause the
24702467
// multi-reg morphing to not kick in (the struct in question needs to be passed in
24712468
// two FP registers). Here is just keep "structBaseType" as "TYP_STRUCT".
2472-
// TODO-LoongArch64: fix "getPrimitiveTypeForStruct" or use the ABI information in
2473-
// the arg entry instead of calling it here.
2469+
// TODO-LoongArch64: fix "getPrimitiveTypeForStruct".
24742470
structBaseType = TYP_STRUCT;
24752471
}
24762472

@@ -2529,7 +2525,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
25292525
// Arm64 Apple has a special ABI for passing small size arguments on stack,
25302526
// bytes are aligned to 1-byte, shorts to 2-byte, int/float to 4-byte, etc.
25312527
// It means passing 8 1-byte arguments on stack can take as small as 8 bytes.
2532-
argAlignBytes = comp->eeGetArgSizeAlignment(arg.GetSignatureType(), isFloatHfa);
2528+
argAlignBytes = comp->eeGetArgSizeAlignment(argSigType, isFloatHfa);
25332529
}
25342530

25352531
#ifdef TARGET_LOONGARCH64
@@ -2541,11 +2537,11 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
25412537
bool isRegArg = false;
25422538
regNumber nonStdRegNum = REG_NA;
25432539

2544-
if (isRegParamType(genActualType(argx->TypeGet()))
2540+
if (isRegParamType(genActualType(argSigType))
25452541
#ifdef UNIX_AMD64_ABI
25462542
&& (!isStructArg || structDesc.passedInRegisters)
25472543
#elif defined(TARGET_X86)
2548-
|| (isStructArg && comp->isTrivialPointerSizedStruct(objClass))
2544+
|| (isStructArg && comp->isTrivialPointerSizedStruct(argSigClass))
25492545
#endif
25502546
)
25512547
{
@@ -3001,12 +2997,9 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
30012997
#endif
30022998
}
30032999

3004-
if (GlobalJitOptions::compFeatureHfa)
3000+
if (isHfaArg)
30053001
{
3006-
if (isHfaArg)
3007-
{
3008-
arg.AbiInfo.SetHfaType(hfaType, hfaSlots);
3009-
}
3002+
arg.AbiInfo.SetHfaType(hfaType, hfaSlots);
30103003
}
30113004

30123005
arg.AbiInfo.SetMultiRegNums();

0 commit comments

Comments
 (0)