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

JIT: Use new ABI classifiers for GenTreeCall arguments #103537

Merged
merged 17 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
31 changes: 25 additions & 6 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ ABIPassingInformation::ABIPassingInformation(Compiler* comp, unsigned numSegment

if (numSegments > 1)
{
Segments = new (comp, CMK_ABI) ABIPassingSegment[numSegments];
m_segments = new (comp, CMK_ABI) ABIPassingSegment[numSegments];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason you can't always use the built in segment for segment[0] and allocate one less entry here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_segments and m_singleSegment is in a union currently. I didn't check whether it was overall worth it memory wise to separate those fields.. It would increase memory usage for the 1 segment case by a bit while decreasing memory usage in multiple segment cases by a bit more.

}
}

Expand All @@ -225,10 +225,10 @@ const ABIPassingSegment& ABIPassingInformation::Segment(unsigned index) const
assert(index < NumSegments);
if (NumSegments == 1)
{
return SingleSegment;
return m_singleSegment;
}

return Segments[index];
return m_segments[index];
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -265,6 +265,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 (Segment(i).IsPassedInRegister() && genIsValidFloatReg(Segment(i).GetRegister()))
{
return true;
}
}
return false;
}

//-----------------------------------------------------------------------------
// HasAnyStackSegment:
// Check if any part of this value is passed on the stack.
Expand Down Expand Up @@ -348,8 +367,8 @@ bool ABIPassingInformation::IsSplitAcrossRegistersAndStack() const
ABIPassingInformation ABIPassingInformation::FromSegment(Compiler* comp, const ABIPassingSegment& segment)
{
ABIPassingInformation info;
info.NumSegments = 1;
info.SingleSegment = segment;
info.NumSegments = 1;
info.m_singleSegment = segment;
return info;
}

Expand All @@ -371,7 +390,7 @@ ABIPassingInformation ABIPassingInformation::FromSegments(Compiler*
{
ABIPassingInformation info;
info.NumSegments = 2;
info.Segments = new (comp, CMK_ABI) ABIPassingSegment[2]{firstSegment, secondSegment};
info.m_segments = new (comp, CMK_ABI) ABIPassingSegment[2]{firstSegment, secondSegment};
return info;
}

Expand Down
8 changes: 6 additions & 2 deletions src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#pragma once

class ClassLayout;
enum class WellKnownArg : unsigned;

class ABIPassingSegment
{
regNumber m_register = REG_NA;
Expand Down Expand Up @@ -42,8 +45,8 @@ struct ABIPassingInformation
private:
union
{
ABIPassingSegment* Segments;
ABIPassingSegment SingleSegment;
ABIPassingSegment* m_segments;
ABIPassingSegment m_singleSegment;
};

public:
Expand Down Expand Up @@ -72,6 +75,7 @@ struct ABIPassingInformation
ABIPassingSegment& Segment(unsigned index);

bool HasAnyRegisterSegment() const;
bool HasAnyFloatingRegisterSegment() const;
bool HasAnyStackSegment() const;
bool HasExactlyOneRegisterSegment() const;
bool HasExactlyOneStackSegment() const;
Expand Down
88 changes: 5 additions & 83 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1258,87 +1258,6 @@ void CallArgABIInformation::SetHfaType(var_types type, unsigned hfaSlots)
}
}

//---------------------------------------------------------------
// SetByteSize: Set information related to this argument's size and alignment.
//
// Arguments:
// byteSize - The size in bytes of the argument.
// byteAlignment - The alignment in bytes of the argument.
// isStruct - Whether this arg is a struct.
// isFloatHfa - Whether this is a float HFA.
//
// Remarks:
// This function will determine how the argument size needs to be rounded. On
// most ABIs all arguments are rounded to stack pointer size, but Apple arm64
// ABI is an exception as it allows packing some small arguments into the
// same stack slot.
//
void CallArgABIInformation::SetByteSize(unsigned byteSize, unsigned byteAlignment, bool isStruct, bool isFloatHfa)
{
unsigned roundedByteSize;
if (compAppleArm64Abi())
{
// Only struct types need extension or rounding to pointer size, but HFA<float> does not.
if (isStruct && !isFloatHfa)
{
roundedByteSize = roundUp(byteSize, TARGET_POINTER_SIZE);
}
else
{
roundedByteSize = byteSize;
}
}
else
{
roundedByteSize = roundUp(byteSize, TARGET_POINTER_SIZE);
}

#if !defined(TARGET_ARM)
// Arm32 could have a struct with 8 byte alignment
// which rounded size % 8 is not 0.
assert(byteAlignment != 0);
assert(roundedByteSize % byteAlignment == 0);
#endif // TARGET_ARM

ByteSize = roundedByteSize;
ByteAlignment = byteAlignment;
}

//---------------------------------------------------------------
// SetMultiRegsNumw: Set the registers for a multi-reg arg using 'sequential' registers.
//
// Remarks:
// This assumes that `NumRegs` and the first reg num has already been set and
// determines how many sequential registers are necessary to pass the
// argument.
// Note that on ARM the registers set may skip odd float registers if the arg
// is a HFA of doubles, since double and float registers overlap.
void CallArgABIInformation::SetMultiRegNums()
{
#if FEATURE_MULTIREG_ARGS && !defined(UNIX_AMD64_ABI) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64)
if (NumRegs == 1)
{
return;
}

regNumber argReg = GetRegNum(0);
#ifdef TARGET_ARM
unsigned int regSize = (GetHfaType() == TYP_DOUBLE) ? 2 : 1;
#else
unsigned int regSize = 1;
#endif

if (NumRegs > MAX_ARG_REG_COUNT)
NO_WAY("Multireg argument exceeds the maximum length");

for (unsigned int regIndex = 1; regIndex < NumRegs; regIndex++)
{
argReg = (regNumber)(argReg + regSize);
SetRegNum(regIndex, argReg);
}
#endif // FEATURE_MULTIREG_ARGS && !defined(UNIX_AMD64_ABI) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64)
}

//---------------------------------------------------------------
// GetStackByteSize: Get the number of stack bytes used to pass this argument.
//
Expand Down Expand Up @@ -1468,7 +1387,7 @@ void CallArg::CheckIsStruct()
CallArgs::CallArgs()
: m_head(nullptr)
, m_lateHead(nullptr)
, m_nextStackByteOffset(0)
, m_argsStackSize(0)
#ifdef UNIX_X86_ABI
, m_stkSizeBytes(0)
, m_padStkAlign(0)
Expand Down Expand Up @@ -9817,7 +9736,7 @@ void CallArgs::InternalCopyFrom(Compiler* comp, CallArgs* other, CopyNodeFunc co
{
assert((m_head == nullptr) && (m_lateHead == nullptr));

m_nextStackByteOffset = other->m_nextStackByteOffset;
m_argsStackSize = other->m_argsStackSize;
m_hasThisPointer = other->m_hasThisPointer;
m_hasRetBuffer = other->m_hasRetBuffer;
m_isVarArgs = other->m_isVarArgs;
Expand Down Expand Up @@ -9845,6 +9764,7 @@ void CallArgs::InternalCopyFrom(Compiler* comp, CallArgs* other, CopyNodeFunc co
carg->m_isTmp = arg.m_isTmp;
carg->m_processed = arg.m_processed;
carg->AbiInfo = arg.AbiInfo;
carg->NewAbiInfo = arg.NewAbiInfo;
*tail = carg;
tail = &carg->m_next;
}
Expand Down Expand Up @@ -13174,6 +13094,8 @@ const char* Compiler::gtGetWellKnownArgNameForArgMsg(WellKnownArg arg)
return "swift error";
case WellKnownArg::SwiftSelf:
return "swift self";
case WellKnownArg::X86TailCallSpecialArg:
return "tail call";
default:
return nullptr;
}
Expand Down
19 changes: 4 additions & 15 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#define _GENTREE_H_
/*****************************************************************************/

#include "abi.h"
#include "vartype.h" // For "var_types"
#include "target.h" // For "regNumber"
#include "ssaconfig.h" // For "SsaConfig::RESERVED_SSA_NUM"
Expand Down Expand Up @@ -4480,6 +4481,7 @@ enum class WellKnownArg : unsigned
DispatchIndirectCallTarget,
SwiftError,
SwiftSelf,
X86TailCallSpecialArg,
};

#ifdef DEBUG
Expand All @@ -4492,12 +4494,10 @@ struct CallArgABIInformation
: NumRegs(0)
, ByteOffset(0)
, ByteSize(0)
, ByteAlignment(0)
#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
, StructFloatFieldType()
#endif
, ArgType(TYP_UNDEF)
, IsBackFilled(false)
, PassedByRef(false)
#if FEATURE_ARG_SPLIT
, m_isSplit(false)
Expand All @@ -4523,7 +4523,6 @@ struct CallArgABIInformation
unsigned NumRegs;
unsigned ByteOffset;
unsigned ByteSize;
unsigned ByteAlignment;
#if defined(UNIX_AMD64_ABI)
// Unix amd64 will split floating point types and integer types in structs
// between floating point and general purpose registers. Keep track of that
Expand All @@ -4541,9 +4540,6 @@ struct CallArgABIInformation
// that type. Note that if a struct is passed by reference, this will still
// be the struct type.
var_types ArgType : 5;
// True when the argument fills a register slot skipped due to alignment
// requirements of previous arguments.
bool IsBackFilled : 1;
// True iff the argument is passed by reference.
bool PassedByRef : 1;

Expand Down Expand Up @@ -4641,18 +4637,11 @@ struct CallArgABIInformation
#endif // TARGET_LOONGARCH64 || TARGET_RISCV64
}

void SetByteSize(unsigned byteSize, unsigned byteAlignment, bool isStruct, bool isFloatHfa);

// Get the number of bytes that this argument is occupying on the stack,
// including padding up to the target pointer size for platforms
// where a stack argument can't take less.
unsigned GetStackByteSize() const;

// Set the register numbers for a multireg argument.
// There's nothing to do on x64/Ux because the structDesc has already been used to set the
// register numbers.
void SetMultiRegNums();

// Return number of stack slots that this argument is taking.
// This value is not meaningful on Apple arm64 where multiple arguments can
// be passed in the same stack slot.
Expand Down Expand Up @@ -4755,6 +4744,7 @@ class CallArg

public:
CallArgABIInformation AbiInfo;
ABIPassingInformation NewAbiInfo;

CallArg(const NewCallArg& arg)
: CallArg()
Expand Down Expand Up @@ -4817,7 +4807,7 @@ class CallArgs
CallArg* m_head;
CallArg* m_lateHead;

unsigned m_nextStackByteOffset;
unsigned m_argsStackSize;
#ifdef UNIX_X86_ABI
// Number of stack bytes pushed before we start pushing these arguments.
unsigned m_stkSizeBytes;
Expand Down Expand Up @@ -4846,7 +4836,6 @@ class CallArgs
void AddedWellKnownArg(WellKnownArg arg);
void RemovedWellKnownArg(WellKnownArg arg);
regNumber GetCustomRegister(Compiler* comp, CorInfoCallConvExtension cc, WellKnownArg arg);
void SplitArg(CallArg* arg, unsigned numRegs, unsigned numSlots);
void SortArgs(Compiler* comp, GenTreeCall* call, CallArg** sortedArgs);

public:
Expand Down
8 changes: 2 additions & 6 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1721,7 +1721,8 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc,
template <typename Classifier>
void Compiler::lvaClassifyParameterABI(Classifier& classifier)
{
lvaParameterPassingInfo = new (this, CMK_LvaTable) ABIPassingInformation[info.compArgsCount];
lvaParameterPassingInfo =
info.compArgsCount == 0 ? nullptr : new (this, CMK_LvaTable) ABIPassingInformation[info.compArgsCount];

for (unsigned i = 0; i < info.compArgsCount; i++)
{
Expand Down Expand Up @@ -1768,11 +1769,6 @@ void Compiler::lvaClassifyParameterABI(Classifier& classifier)
//
void Compiler::lvaClassifyParameterABI()
{
if (info.compArgsCount == 0)
{
return;
}

ClassifierInfo cInfo;
cInfo.CallConv = info.compCallConv;
cInfo.IsVarArgs = info.compIsVarArgs;
Expand Down
7 changes: 5 additions & 2 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3448,10 +3448,13 @@ void Lowering::LowerCFGCall(GenTreeCall* call)
call->gtArgs.PushLateBack(targetArg);

// Set up ABI information for this arg.
targetArg->NewAbiInfo =
ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(REG_DISPATCH_INDIRECT_CALL_ADDR,
0, TARGET_POINTER_SIZE));
targetArg->AbiInfo.ArgType = callTarget->TypeGet();
targetArg->AbiInfo.SetRegNum(0, REG_DISPATCH_INDIRECT_CALL_ADDR);
targetArg->AbiInfo.NumRegs = 1;
targetArg->AbiInfo.SetByteSize(TARGET_POINTER_SIZE, TARGET_POINTER_SIZE, false, false);
targetArg->AbiInfo.NumRegs = 1;
targetArg->AbiInfo.ByteSize = TARGET_POINTER_SIZE;

// Lower the newly added args now that call is updated
LowerArg(call, targetArg, true /* late */);
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 @@ -602,14 +602,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
Loading