Skip to content

Commit 10e9b79

Browse files
committed
add new ABI classifier.
1 parent 9068070 commit 10e9b79

File tree

3 files changed

+234
-2
lines changed

3 files changed

+234
-2
lines changed

src/coreclr/jit/abi.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ struct ABIPassingInformation
4848
// multiple register segments and a struct segment.
4949
// - On Windows x64, all parameters always fit into one stack slot or
5050
// register, and thus always have NumSegments == 1
51-
// - On RISC-V, structs can be split out over 2 segments, each can be an integer/float register or a stack slot
51+
// - On loongarch64/riscv64, structs can be passed in two registers or
52+
// can be split out over register and stack, giving
53+
// multiple register segments and a struct segment.
5254
unsigned NumSegments = 0;
5355
ABIPassingSegment* Segments = nullptr;
5456

@@ -202,6 +204,22 @@ class RiscV64Classifier
202204
WellKnownArg wellKnownParam);
203205
};
204206

207+
class LoongArch64Classifier
208+
{
209+
const ClassifierInfo& m_info;
210+
RegisterQueue m_intRegs;
211+
RegisterQueue m_floatRegs;
212+
unsigned m_stackArgSize = 0;
213+
214+
public:
215+
LoongArch64Classifier(const ClassifierInfo& info);
216+
217+
ABIPassingInformation Classify(Compiler* comp,
218+
var_types type,
219+
ClassLayout* structLayout,
220+
WellKnownArg wellKnownParam);
221+
};
222+
205223
#if defined(TARGET_X86)
206224
typedef X86Classifier PlatformClassifier;
207225
#elif defined(WINDOWS_AMD64_ABI)
@@ -214,6 +232,8 @@ typedef Arm64Classifier PlatformClassifier;
214232
typedef Arm32Classifier PlatformClassifier;
215233
#elif defined(TARGET_RISCV64)
216234
typedef RiscV64Classifier PlatformClassifier;
235+
#elif defined(TARGET_LOONGARCH64)
236+
typedef LoongArch64Classifier PlatformClassifier;
217237
#endif
218238

219239
#ifdef SWIFT_SUPPORT

src/coreclr/jit/lclvars.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1805,7 +1805,7 @@ void Compiler::lvaClassifyParameterABI()
18051805
else
18061806
#endif
18071807
#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || \
1808-
defined(TARGET_RISCV64)
1808+
defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64)
18091809
{
18101810
PlatformClassifier classifier(cInfo);
18111811
lvaClassifyParameterABI(classifier);

src/coreclr/jit/targetloongarch64.cpp

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,216 @@ const regNumber fltArgRegs [] = {REG_F0, REG_F1, REG_F2, REG_F3, REG_F4, REG_F5,
2424
const regMaskTP fltArgMasks[] = {RBM_F0, RBM_F1, RBM_F2, RBM_F3, RBM_F4, RBM_F5, RBM_F6, RBM_F7 };
2525
// clang-format on
2626

27+
//-----------------------------------------------------------------------------
28+
// LoongArch64Classifier:
29+
// Construct a new instance of the LoongArch64 ABI classifier.
30+
//
31+
// Parameters:
32+
// info - Info about the method being classified.
33+
//
34+
LoongArch64Classifier::LoongArch64Classifier(const ClassifierInfo& info)
35+
: m_info(info)
36+
, m_intRegs(intArgRegs, ArrLen(intArgRegs))
37+
, m_floatRegs(fltArgRegs, ArrLen(fltArgRegs))
38+
{
39+
}
40+
41+
//-----------------------------------------------------------------------------
42+
// Classify:
43+
// Classify a parameter for the LoongArch64 ABI.
44+
//
45+
// Parameters:
46+
// comp - Compiler instance
47+
// type - The type of the parameter
48+
// structLayout - The layout of the struct. Expected to be non-null if
49+
// varTypeIsStruct(type) is true.
50+
// wellKnownParam - Well known type of the parameter (if it may affect its ABI classification)
51+
//
52+
// Returns:
53+
// Classification information for the parameter.
54+
//
55+
ABIPassingInformation LoongArch64Classifier::Classify(Compiler* comp,
56+
var_types type,
57+
ClassLayout* structLayout,
58+
WellKnownArg wellKnownParam)
59+
{
60+
assert(!m_info.IsVarArgs);
61+
62+
unsigned passedSize;
63+
unsigned slots = 0;
64+
var_types argRegTypeInStruct1 = TYP_UNKNOWN;
65+
var_types argRegTypeInStruct2 = TYP_UNKNOWN;
66+
67+
bool canPassArgInRegisters = false;
68+
if (varTypeIsStruct(type))
69+
{
70+
passedSize = structLayout->GetSize();
71+
if (passedSize > MAX_PASS_MULTIREG_BYTES)
72+
{
73+
slots = 1; // Passed by implicit byref
74+
passedSize = TARGET_POINTER_SIZE;
75+
canPassArgInRegisters = m_intRegs.Count() > 0;
76+
}
77+
else
78+
{
79+
assert(!structLayout->IsBlockLayout());
80+
81+
uint32_t floatFlags;
82+
CORINFO_CLASS_HANDLE typeHnd = structLayout->GetClassHandle();
83+
84+
floatFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd);
85+
86+
if ((floatFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) != 0)
87+
{
88+
if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
89+
{
90+
assert(passedSize <= TARGET_POINTER_SIZE);
91+
92+
slots = 1;
93+
canPassArgInRegisters = m_floatRegs.Count() > 0;
94+
95+
argRegTypeInStruct1 = (passedSize == 8) ? TYP_DOUBLE : TYP_FLOAT;
96+
}
97+
else if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0)
98+
{
99+
slots = 2;
100+
canPassArgInRegisters = m_floatRegs.Count() >= 2;
101+
102+
argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
103+
argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
104+
}
105+
else if ((floatFlags & STRUCT_FLOAT_FIELD_FIRST) != 0)
106+
{
107+
slots = 1;
108+
canPassArgInRegisters = (m_floatRegs.Count() > 0) && (m_intRegs.Count() > 0);
109+
110+
argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
111+
argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT;
112+
}
113+
else if ((floatFlags & STRUCT_FLOAT_FIELD_SECOND) != 0)
114+
{
115+
slots = 1;
116+
canPassArgInRegisters = (m_floatRegs.Count() > 0) && (m_intRegs.Count() > 0);
117+
118+
argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT;
119+
argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
120+
}
121+
122+
assert((slots == 1) || (slots == 2));
123+
124+
if (!canPassArgInRegisters)
125+
{
126+
m_floatRegs.Clear();
127+
slots = (passedSize + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE;
128+
// On LoongArch64, if there aren't any remaining floating-point registers to pass the argument,
129+
// integer registers (if any) are used instead.
130+
canPassArgInRegisters = m_intRegs.Count() >= slots;
131+
132+
argRegTypeInStruct1 = TYP_UNKNOWN;
133+
argRegTypeInStruct2 = TYP_UNKNOWN;
134+
}
135+
}
136+
else
137+
{
138+
slots = (passedSize + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE;
139+
canPassArgInRegisters = m_intRegs.Count() >= slots;
140+
}
141+
142+
if (!canPassArgInRegisters && (slots == 2))
143+
{
144+
// Here a struct-arg which needs two registers but only one integer register available,
145+
// it has to be split.
146+
if (m_intRegs.Count() > 0)
147+
{
148+
canPassArgInRegisters = true;
149+
}
150+
}
151+
}
152+
}
153+
else
154+
{
155+
assert(genTypeSize(type) <= TARGET_POINTER_SIZE);
156+
157+
slots = 1;
158+
passedSize = genTypeSize(type);
159+
if (varTypeIsFloating(type))
160+
{
161+
canPassArgInRegisters = m_floatRegs.Count() > 0;
162+
if (!canPassArgInRegisters)
163+
{
164+
m_floatRegs.Clear();
165+
canPassArgInRegisters = m_intRegs.Count() > 0;
166+
}
167+
}
168+
else
169+
{
170+
canPassArgInRegisters = m_intRegs.Count() > 0;
171+
}
172+
}
173+
174+
ABIPassingInformation info;
175+
if (canPassArgInRegisters)
176+
{
177+
info.NumSegments = slots;
178+
info.Segments = new (comp, CMK_ABI) ABIPassingSegment[slots];
179+
if (argRegTypeInStruct1 != TYP_UNKNOWN)
180+
{
181+
RegisterQueue* regs = varTypeIsFloating(argRegTypeInStruct1) ? &m_floatRegs : &m_intRegs;
182+
assert(regs->Count() > 0);
183+
184+
passedSize = genTypeSize(argRegTypeInStruct1);
185+
info.Segments[0] = ABIPassingSegment::InRegister(regs->Dequeue(), 0, passedSize);
186+
187+
if (argRegTypeInStruct2 != TYP_UNKNOWN)
188+
{
189+
unsigned slotSize = genTypeSize(argRegTypeInStruct2);
190+
191+
regs = varTypeIsFloating(argRegTypeInStruct2) ? &m_floatRegs : &m_intRegs;
192+
assert(regs->Count() > 0);
193+
194+
passedSize = max(passedSize, slotSize);
195+
info.Segments[1] = ABIPassingSegment::InRegister(regs->Dequeue(), passedSize, slotSize);
196+
}
197+
}
198+
else
199+
{
200+
RegisterQueue* regs = varTypeIsFloating(type) ? &m_floatRegs : &m_intRegs;
201+
unsigned slotSize = min(passedSize, (unsigned)TARGET_POINTER_SIZE);
202+
info.Segments[0] = ABIPassingSegment::InRegister(regs->Dequeue(), 0, slotSize);
203+
if (slots == 2)
204+
{
205+
assert(varTypeIsStruct(type));
206+
assert(passedSize > TARGET_POINTER_SIZE);
207+
unsigned tailSize = passedSize - slotSize;
208+
if (m_intRegs.Count() > 0)
209+
{
210+
info.Segments[1] = ABIPassingSegment::InRegister(m_intRegs.Dequeue(), slotSize, tailSize);
211+
}
212+
else
213+
{
214+
assert(m_intRegs.Count() == 0);
215+
assert(m_stackArgSize == 0);
216+
info.Segments[1] = ABIPassingSegment::OnStack(0, TARGET_POINTER_SIZE, tailSize);
217+
m_stackArgSize += TARGET_POINTER_SIZE;
218+
}
219+
}
220+
}
221+
}
222+
else
223+
{
224+
assert((m_stackArgSize % TARGET_POINTER_SIZE) == 0);
225+
226+
info = ABIPassingInformation::FromSegment(comp, ABIPassingSegment::OnStack(m_stackArgSize, 0, passedSize));
227+
228+
m_stackArgSize += roundUp(passedSize, TARGET_POINTER_SIZE);
229+
230+
// As soon as we pass something on stack we cannot go back and
231+
// enregister something else.
232+
// The float had been cleared before and only integer type go here.
233+
m_intRegs.Clear();
234+
}
235+
236+
return info;
237+
}
238+
27239
#endif // TARGET_LOONGARCH64

0 commit comments

Comments
 (0)