1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
|
// SPDX-License-Identifier: GPL-3.0-or-later
#include "RISCV.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineIRBuilder.h"
#include "rcc/CodeGen/MachineInstr.h"
#include "rcc/CodeGen/MachineMemOperand.h"
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/Target/RISCV/RISCVInstructions.h"
#include "rcc/Target/RISCV/RISCVRegisters.h"
#include "rcc/Target/RISCV/RISCVTarget.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <utility>
namespace rcc::riscv {
namespace {
struct SpillForm final {
InstructionOpcode Load;
InstructionOpcode Store;
std::uint64_t SizeInBytes;
};
[[nodiscard]] constexpr Opcode targetOpcode(InstructionOpcode Value) noexcept {
return Opcode::fromValue(static_cast<std::uint32_t>(Value));
}
[[nodiscard]] constexpr RegisterClassID registerClassID(RegisterClass Value) noexcept {
return RegisterClassID::fromValue(static_cast<std::uint32_t>(Value));
}
[[nodiscard]] constexpr std::optional<SpillForm> spillForm(RegisterClassID Class) noexcept {
if (Class == registerClassID(RegisterClass::GPR) ||
Class == registerClassID(RegisterClass::GPRC) ||
Class == registerClassID(RegisterClass::GPR_NX0) ||
Class == registerClassID(RegisterClass::GPR_NX0_NX2)) {
return SpillForm{InstructionOpcode::LD, InstructionOpcode::SD, 8};
}
if (Class == registerClassID(RegisterClass::FPR64) ||
Class == registerClassID(RegisterClass::FPR64C)) {
return SpillForm{InstructionOpcode::FLD, InstructionOpcode::FSD, 8};
}
if (Class == registerClassID(RegisterClass::FPR32) ||
Class == registerClassID(RegisterClass::FPR32C)) {
return SpillForm{InstructionOpcode::FLW, InstructionOpcode::FSW, 4};
}
return std::nullopt;
}
[[nodiscard]] Expected<const MachineMemOperand *>
createStackMemoryOperand(MachineIRBuilder &Builder, FrameIndex StackSlot, MemoryAccessFlag Access,
std::uint64_t SizeInBytes, std::uint64_t AlignmentInBytes) {
auto Size = MemorySize::fixed(SizeInBytes);
if (!Size)
return std::unexpected(Size.error());
auto Align = Alignment::create(AlignmentInBytes);
if (!Align)
return std::unexpected(Align.error());
MachineMemOperandDesc Description;
Description.Accesses = {Access};
Description.Size = *Size;
Description.Align = *Align;
Description.Base = MemoryBase::frameIndex(StackSlot);
return Builder.function().createMemOperand(std::move(Description));
}
[[nodiscard]] bool isCalleeSaved(PhysicalRegister Reg) noexcept {
const std::uint32_t Number = Reg.number();
const std::array GPRs{
PhysicalRegisterID::X8, PhysicalRegisterID::X9, PhysicalRegisterID::X18,
PhysicalRegisterID::X19, PhysicalRegisterID::X20, PhysicalRegisterID::X21,
PhysicalRegisterID::X22, PhysicalRegisterID::X23, PhysicalRegisterID::X24,
PhysicalRegisterID::X25, PhysicalRegisterID::X26, PhysicalRegisterID::X27,
};
const std::array FPRs{
PhysicalRegisterID::F8_D, PhysicalRegisterID::F9_D, PhysicalRegisterID::F18_D,
PhysicalRegisterID::F19_D, PhysicalRegisterID::F20_D, PhysicalRegisterID::F21_D,
PhysicalRegisterID::F22_D, PhysicalRegisterID::F23_D, PhysicalRegisterID::F24_D,
PhysicalRegisterID::F25_D, PhysicalRegisterID::F26_D, PhysicalRegisterID::F27_D,
PhysicalRegisterID::F8_F, PhysicalRegisterID::F9_F, PhysicalRegisterID::F18_F,
PhysicalRegisterID::F19_F, PhysicalRegisterID::F20_F, PhysicalRegisterID::F21_F,
PhysicalRegisterID::F22_F, PhysicalRegisterID::F23_F, PhysicalRegisterID::F24_F,
PhysicalRegisterID::F25_F, PhysicalRegisterID::F26_F, PhysicalRegisterID::F27_F,
};
const auto Matches = [Number](PhysicalRegisterID ID) {
return Number == static_cast<std::uint32_t>(ID);
};
return std::ranges::any_of(GPRs, Matches) || std::ranges::any_of(FPRs, Matches);
}
[[nodiscard]] bool isCompressedRegister(PhysicalRegister Reg) noexcept {
const std::uint32_t Number = Reg.number();
const std::uint32_t X8 = static_cast<std::uint32_t>(PhysicalRegisterID::X8);
const std::uint32_t X15 = static_cast<std::uint32_t>(PhysicalRegisterID::X15);
const std::uint32_t F8D = static_cast<std::uint32_t>(PhysicalRegisterID::F8_D);
const std::uint32_t F15D = static_cast<std::uint32_t>(PhysicalRegisterID::F15_D);
const std::uint32_t F8F = static_cast<std::uint32_t>(PhysicalRegisterID::F8_F);
const std::uint32_t F15F = static_cast<std::uint32_t>(PhysicalRegisterID::F15_F);
return (Number >= X8 && Number <= X15) || (Number >= F8D && Number <= F15D) ||
(Number >= F8F && Number <= F15F);
}
class RISCVRegisterAllocationInfo final : public TargetRegisterAllocationInfo {
public:
[[nodiscard]] bool
isReservedForFunction(const MachineFunction &Function, PhysicalRegister Reg,
const TargetDescription &Target) const noexcept override {
std::uint64_t RequiredAlignment = 16;
RequiredAlignment = std::max(
RequiredAlignment, Function.frameInfo().state().MaxAlignment.value_or(Alignment{}).bytes());
for (const StackObject &Object : Function.frameInfo().objects()) {
if (!Object.isTombstone() && !Object.description()->IsDead) {
RequiredAlignment = std::max(RequiredAlignment, Object.description()->Align.bytes());
}
}
const bool RequiresRealignment = RequiredAlignment > 16;
if (Reg.number() == static_cast<std::uint32_t>(PhysicalRegisterID::X9))
return RequiresRealignment;
if (Reg.number() != static_cast<std::uint32_t>(PhysicalRegisterID::X8))
return false;
const MachineFrameState &State = Function.frameInfo().state();
bool HasCalls = State.HasCalls;
for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr && !HasCalls;
Block = Block->next()) {
for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
Instruction = Instruction->next()) {
const InstructionDescriptor *Descriptor =
Target.instructionDescriptor(Instruction->opcode());
if (Descriptor != nullptr && Descriptor->Flags.has(InstructionFlag::Call)) {
HasCalls = true;
break;
}
}
}
const bool HasVariableObject =
std::ranges::any_of(Function.frameInfo().objects(), [](const StackObject &Object) {
return !Object.isTombstone() && !Object.description()->IsDead && Object.isVariableSized();
});
if (State.IsFrameAddressTaken || State.HasOpaqueSPAdjustment || HasVariableObject ||
RequiresRealignment)
return true;
switch (State.FramePointerPolicy) {
case MachineFramePointerPolicy::All:
case MachineFramePointerPolicy::Reserved:
return true;
case MachineFramePointerPolicy::NonLeaf:
case MachineFramePointerPolicy::NonLeafNoReserve:
return HasCalls;
case MachineFramePointerPolicy::None:
case MachineFramePointerPolicy::Count:
return false;
}
return false;
}
[[nodiscard]] Expected<void> validateSpillReload(RegisterClassID Class,
const TargetDescription &Target) const override {
const RegisterClassDesc *Description = Target.registers().registerClass(Class);
const auto Form = spillForm(Class);
if (!Form) {
return makeUnexpected(ErrorCode::Unsupported,
"RV64GC register class has no scalar spill/reload form");
}
if (Description == nullptr || !Description->Allocatable ||
Description->SpillSizeInBits != Form->SizeInBytes * 8U ||
Description->SpillAlignmentInBits == 0 || Description->SpillAlignmentInBits % 8U != 0) {
return makeUnexpected(ErrorCode::InvalidArgument,
"RV64GC register class has inconsistent spill properties");
}
for (const InstructionOpcode OpcodeValue : {Form->Load, Form->Store}) {
const InstructionDescriptor *Instruction =
Target.instructionDescriptor(targetOpcode(OpcodeValue));
const InstructionFlag MemoryFlag =
OpcodeValue == Form->Load ? InstructionFlag::MayLoad : InstructionFlag::MayStore;
if (Instruction == nullptr || Instruction->Operands.size() != 3 ||
!Instruction->Operands[1].Kinds.contains(MachineOperand::Kind::FrameIndex) ||
!Instruction->Flags.has(MemoryFlag)) {
return makeUnexpected(ErrorCode::InternalError,
"RV64GC spill instruction descriptor is incomplete");
}
}
return {};
}
[[nodiscard]] Expected<MachineInstr *> emitReload(MachineIRBuilder &Builder, Register Destination,
RegisterClassID Class, FrameIndex StackSlot,
LaneMask Lanes) const override {
auto Validated = validateSpillReload(Class, Builder.target());
if (!Validated)
return std::unexpected(Validated.error());
const RegisterClassDesc *Description = Builder.target().registers().registerClass(Class);
if (Lanes.empty() || !Lanes.intersects(Description->Lanes)) {
return makeUnexpected(ErrorCode::InvalidArgument,
"RV64GC reload requires a nonempty class lane mask");
}
const auto FormValue = spillForm(Class);
if (!FormValue) {
return makeUnexpected(ErrorCode::InternalError,
"RV64GC reload lost its validated spill form");
}
const SpillForm Form = FormValue.value();
auto Memory =
createStackMemoryOperand(Builder, StackSlot, MemoryAccessFlag::Load, Form.SizeInBytes,
Description->SpillAlignmentInBits / 8U);
if (!Memory)
return std::unexpected(Memory.error());
auto Pending = Builder.buildInstr(targetOpcode(Form.Load));
if (!Pending)
return std::unexpected(Pending.error());
if (auto Result = Pending->addDef(Destination); !Result)
return std::unexpected(Result.error());
if (auto Result = Pending->addFrameIndex(StackSlot); !Result)
return std::unexpected(Result.error());
if (auto Result = Pending->addImmediate(0); !Result)
return std::unexpected(Result.error());
if (auto Result = Pending->addMemOperand(**Memory); !Result)
return std::unexpected(Result.error());
return Pending->build();
}
[[nodiscard]] Expected<MachineInstr *> emitSpill(MachineIRBuilder &Builder, Register Source,
RegisterClassID Class, FrameIndex StackSlot,
LaneMask Lanes) const override {
auto Validated = validateSpillReload(Class, Builder.target());
if (!Validated)
return std::unexpected(Validated.error());
const RegisterClassDesc *Description = Builder.target().registers().registerClass(Class);
if (Lanes.empty() || !Lanes.intersects(Description->Lanes)) {
return makeUnexpected(ErrorCode::InvalidArgument,
"RV64GC spill requires a nonempty class lane mask");
}
const auto FormValue = spillForm(Class);
if (!FormValue) {
return makeUnexpected(ErrorCode::InternalError, "RV64GC spill lost its validated spill form");
}
const SpillForm Form = FormValue.value();
auto Memory =
createStackMemoryOperand(Builder, StackSlot, MemoryAccessFlag::Store, Form.SizeInBytes,
Description->SpillAlignmentInBits / 8U);
if (!Memory)
return std::unexpected(Memory.error());
auto Pending = Builder.buildInstr(targetOpcode(Form.Store));
if (!Pending)
return std::unexpected(Pending.error());
if (auto Result = Pending->addUse(Source); !Result)
return std::unexpected(Result.error());
if (auto Result = Pending->addFrameIndex(StackSlot); !Result)
return std::unexpected(Result.error());
if (auto Result = Pending->addImmediate(0); !Result)
return std::unexpected(Result.error());
if (auto Result = Pending->addMemOperand(**Memory); !Result)
return std::unexpected(Result.error());
return Pending->build();
}
[[nodiscard]] Expected<bool> isRematerializable(const MachineInstr &Instruction,
Register VirtualReg, const MachineFunction &,
const TargetDescription &) const override {
const MachineOperand *Definition = Instruction.operandAt(0);
const Register *DefinedReg = Definition == nullptr ? nullptr : Definition->getIf<Register>();
if (DefinedReg == nullptr || *DefinedReg != VirtualReg || !Definition->isDef())
return false;
if (Instruction.opcode() == targetOpcode(InstructionOpcode::LUI))
return Instruction.operandCount() == 2 && Instruction.operandAt(1) != nullptr;
if (Instruction.opcode() != targetOpcode(InstructionOpcode::ADDI) &&
Instruction.opcode() != targetOpcode(InstructionOpcode::ADDIW)) {
return false;
}
if (Instruction.operandCount() != 3)
return false;
const MachineOperand *Source = Instruction.operandAt(1);
const Register *SourceReg = Source == nullptr ? nullptr : Source->getIf<Register>();
const auto Zero = physicalRegister(PhysicalRegisterID::X0);
return Zero && SourceReg != nullptr && *SourceReg == Register::fromPhysical(*Zero) &&
Source->isUse();
}
[[nodiscard]] Expected<MachineInstr *>
rematerialize(MachineIRBuilder &Builder, const MachineInstr &Original, Register Destination,
Register OriginalRegister, const TargetDescription &Target) const override {
auto Accepted = isRematerializable(Original, OriginalRegister, Builder.function(), Target);
if (!Accepted)
return std::unexpected(Accepted.error());
if (!*Accepted) {
return makeUnexpected(ErrorCode::InvalidArgument,
"RV64GC instruction is not rematerializable");
}
auto Pending = Builder.buildInstr(Original.opcode());
if (!Pending)
return std::unexpected(Pending.error());
const MachineOperand *OriginalDef = Original.operandAt(0);
if (auto Result = Pending->addDef(Destination, {}, {}, OriginalDef->targetFlags()); !Result)
return std::unexpected(Result.error());
for (std::size_t Index = 1; Index < Original.operandCount(); ++Index) {
const MachineOperand *Operand = Original.operandAt(Index);
if (Operand == nullptr) {
return makeUnexpected(ErrorCode::InternalError,
"RV64GC rematerialization found a missing operand");
}
if (auto Result = Pending->addOperand(Operand->cloneDetached()); !Result)
return std::unexpected(Result.error());
}
return Pending->build();
}
[[nodiscard]] std::uint64_t physicalRegisterCost(const MachineFunction &, Register,
PhysicalRegister Reg, RegisterClassID,
bool) const noexcept override {
if (Reg.number() == static_cast<std::uint32_t>(PhysicalRegisterID::X1))
return 16;
if (isCalleeSaved(Reg))
return 4;
return isCompressedRegister(Reg) ? 0 : 1;
}
[[nodiscard]] std::uint64_t spillCost(const MachineFunction &, const LiveInterval &,
RegisterClassID Class) const noexcept override {
const auto Form = spillForm(Class);
return Form ? std::max<std::uint64_t>(1, Form->SizeInBytes / 4U) : 1;
}
};
} // namespace
const TargetRegisterAllocationInfo ®isterAllocationInfo() noexcept {
static const RISCVRegisterAllocationInfo Info;
return Info;
}
} // namespace rcc::riscv
|