@@ -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