Skip to content

Commit

Permalink
[RISC-V] Fix struct types passed by floating-point calling convention (
Browse files Browse the repository at this point in the history
…#4)

* Classify stack arguments as single segment

* Bring back type fix-ups for structs passed according to hardware floating-point calling convention

* Use more strict reg masking condition for fixed refs like on other platforms because after dotnet#97368 we don't need an exception
  • Loading branch information
tomeksowi committed Jun 20, 2024
1 parent d37cba7 commit 9348fd1
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 24 deletions.
19 changes: 19 additions & 0 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct ABIPassingInformation
}

bool HasAnyRegisterSegment() const;
bool HasAnyFloatingRegisterSegment() const;
bool HasAnyStackSegment() const;
bool HasExactlyOneRegisterSegment() const;
bool HasExactlyOneStackSegment() const;
Expand Down
7 changes: 0 additions & 7 deletions src/coreclr/jit/lsrabuild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
27 changes: 27 additions & 0 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
40 changes: 23 additions & 17 deletions src/coreclr/jit/targetriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Expand Down

0 comments on commit 9348fd1

Please sign in to comment.