diff --git a/src/coreclr/jit/abi.cpp b/src/coreclr/jit/abi.cpp index 524eedb48ee4d..d007e82577339 100644 --- a/src/coreclr/jit/abi.cpp +++ b/src/coreclr/jit/abi.cpp @@ -206,6 +206,25 @@ bool ABIPassingInformation::HasAnyRegisterSegment() const return false; } +//----------------------------------------------------------------------------- +// HasAnyFloatingRegisterSegment: +// Check if any part of this value is passed in a floating-point register. +// +// Return Value: +// True if so. +// +bool ABIPassingInformation::HasAnyFloatingRegisterSegment() const +{ + for (unsigned i = 0; i < NumSegments; i++) + { + if (Segments[i].IsPassedInRegister() && genIsValidFloatReg(Segments[i].GetRegister())) + { + return true; + } + } + return false; +} + //----------------------------------------------------------------------------- // HasAnyStackSegment: // Check if any part of this value is passed on the stack. diff --git a/src/coreclr/jit/abi.h b/src/coreclr/jit/abi.h index 4172844c4d553..2d2690f159795 100644 --- a/src/coreclr/jit/abi.h +++ b/src/coreclr/jit/abi.h @@ -64,6 +64,7 @@ struct ABIPassingInformation } bool HasAnyRegisterSegment() const; + bool HasAnyFloatingRegisterSegment() const; bool HasAnyStackSegment() const; bool HasExactlyOneRegisterSegment() const; bool HasExactlyOneStackSegment() const; diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index e130d9fc600cf..831d5f3e80396 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -603,14 +603,7 @@ RefPosition* LinearScan::newRefPosition(Interval* theInterval, regNumber physicalReg = genRegNumFromMask(mask, theInterval->registerType); RefPosition* pos = newRefPosition(physicalReg, theLocation, RefTypeFixedReg, nullptr, mask); assert(theInterval != nullptr); -#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // The LoongArch64's ABI which the float args maybe passed by integer register - // when no float register left but free integer register. - assert((regType(theInterval->registerType) == FloatRegisterType) || - (allRegs(theInterval->registerType) & mask) != 0); -#else assert((allRegs(theInterval->registerType) & mask) != 0); -#endif } RefPosition* newRP = newRefPositionRaw(theLocation, theTreeNode, theRefType); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index d3ac281b3ec66..0e416a8952784 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2126,6 +2126,33 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call Compiler::structPassingKind howToPassStruct; var_types structBaseType = comp->getArgTypeForStruct(argSigClass, &howToPassStruct, IsVarArgs(), argLayout->GetSize()); +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + if (arg.NewAbiInfo.HasAnyFloatingRegisterSegment()) + { + // Struct passed according to hardware floating-point calling convention + assert(arg.NewAbiInfo.NumSegments <= 2); + assert(!arg.NewAbiInfo.HasAnyStackSegment()); + if (arg.NewAbiInfo.NumSegments == 2) + { + // On LoongArch64, "getPrimitiveTypeForStruct" will incorrectly return "TYP_LONG" + // for "struct { float, float }", and retyping to a primitive here will cause the + // multi-reg morphing to not kick in (the struct in question needs to be passed in + // two FP registers). Here is just keep "structBaseType" as "TYP_STRUCT". + // TODO-LoongArch64: fix "getPrimitiveTypeForStruct". + structBaseType = TYP_STRUCT; + } + else + { + assert(arg.NewAbiInfo.NumSegments == 1); + structBaseType = arg.NewAbiInfo.Segments[0].GetRegisterType(); + } + + for (unsigned i = 0; i < arg.NewAbiInfo.NumSegments; ++i) + { + arg.AbiInfo.StructFloatFieldType[i] = arg.NewAbiInfo.Segments[i].GetRegisterType(); + } + } +#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) arg.AbiInfo.PassedByRef = howToPassStruct == Compiler::SPK_ByReference; arg.AbiInfo.ArgType = structBaseType == TYP_UNKNOWN ? argx->TypeGet() : structBaseType; diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index ea4c99872b794..4df767d8cbfcc 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -138,32 +138,38 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, else { // Integer calling convention - auto passSlot = [this](unsigned offset, unsigned size) -> ABIPassingSegment { + auto passOnStack = [this](unsigned offset, unsigned size) -> ABIPassingSegment { assert(size > 0); - assert(size <= TARGET_POINTER_SIZE); - if (m_intRegs.Count() > 0) + assert(size <= 2 * TARGET_POINTER_SIZE); + assert((m_stackArgSize % TARGET_POINTER_SIZE) == 0); + ABIPassingSegment seg = ABIPassingSegment::OnStack(m_stackArgSize, offset, size); + m_stackArgSize += (size > TARGET_POINTER_SIZE) ? (2 * TARGET_POINTER_SIZE) : TARGET_POINTER_SIZE; + return seg; + }; + + if (m_intRegs.Count() > 0) + { + if (passedSize <= TARGET_POINTER_SIZE) { - return ABIPassingSegment::InRegister(m_intRegs.Dequeue(), offset, size); + ABIPassingSegment seg = ABIPassingSegment::InRegister(m_intRegs.Dequeue(), 0, passedSize); + return ABIPassingInformation::FromSegment(comp, seg); } else { - assert((m_stackArgSize % TARGET_POINTER_SIZE) == 0); - ABIPassingSegment seg = ABIPassingSegment::OnStack(m_stackArgSize, offset, size); - m_stackArgSize += TARGET_POINTER_SIZE; - return seg; + assert(varTypeIsStruct(type)); + unsigned int tailSize = passedSize - TARGET_POINTER_SIZE; + + ABIPassingSegment head = ABIPassingSegment::InRegister(m_intRegs.Dequeue(), 0, TARGET_POINTER_SIZE); + ABIPassingSegment tail = + (m_intRegs.Count() > 0) + ? ABIPassingSegment::InRegister(m_intRegs.Dequeue(), TARGET_POINTER_SIZE, tailSize) + : passOnStack(TARGET_POINTER_SIZE, tailSize); + return {2, new (comp, CMK_ABI) ABIPassingSegment[2]{head, tail}}; } - }; - - if (passedSize <= TARGET_POINTER_SIZE) - { - return ABIPassingInformation::FromSegment(comp, passSlot(0, passedSize)); } else { - assert(varTypeIsStruct(type)); - return {2, new (comp, CMK_ABI) - ABIPassingSegment[2]{passSlot(0, TARGET_POINTER_SIZE), - passSlot(TARGET_POINTER_SIZE, passedSize - TARGET_POINTER_SIZE)}}; + return ABIPassingInformation::FromSegment(comp, passOnStack(0, passedSize)); } } }