aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-12 01:07:48 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-12 01:07:48 +0800
commit7819df7189a54fa5b8df657d10285f1873628d25 (patch)
treed2f3d851aa00656e20df861a6f29e6423eeca944
parentdeb6a9e08397083a8d23cac4cfba94345419b443 (diff)
downloadrcc-project-7819df7189a54fa5b8df657d10285f1873628d25.tar.gz
rcc-project-7819df7189a54fa5b8df657d10285f1873628d25.tar.xz
[RISCV] Model FFLAGS accesses
-rw-r--r--backend/lib/CodeGen/PostRAScheduler.cpp11
-rw-r--r--backend/lib/Target/RISCV/RISCV.rccdesc39
-rw-r--r--backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp51
-rw-r--r--backend/unittests/Target/RISCVLateCodeGenTest.cpp20
-rw-r--r--backend/unittests/Target/RISCVTargetTest.cpp15
5 files changed, 129 insertions, 7 deletions
diff --git a/backend/lib/CodeGen/PostRAScheduler.cpp b/backend/lib/CodeGen/PostRAScheduler.cpp
index 026f763..204f05e 100644
--- a/backend/lib/CodeGen/PostRAScheduler.cpp
+++ b/backend/lib/CodeGen/PostRAScheduler.cpp
@@ -130,6 +130,13 @@ void removeUnits(RegisterUnitLaneSet &Live, const std::vector<PhysicalUnitAccess
Live.remove(Unit.Unit, Unit.Lanes);
}
+[[nodiscard]] bool isReservedPhysicalRegister(Register Reg, const MachineFunction &Function,
+ const TargetDescription &Target) noexcept {
+ const std::optional<PhysicalRegister> Physical = Reg.physicalRegister();
+ return Physical && (Function.registerInfo().isReserved(*Physical) ||
+ Target.registers().isReservedRegister(*Physical));
+}
+
[[nodiscard]] OperandFlagUpdate &getFlagUpdate(std::vector<OperandFlagUpdate> &Updates,
MachineOperand &Operand) {
const auto Position = std::ranges::find_if(
@@ -175,7 +182,7 @@ buildBlockLivenessRepairPlan(MachineBasicBlock &Block, const TargetDescription &
auto Units = physicalOperandUnits(*Operand, Target);
if (!Units)
return std::unexpected(Units.error());
- if (!intersects(Live, *Units))
+ if (!isReservedPhysicalRegister(*Reg, Block.function(), Target) && !intersects(Live, *Units))
Update.Flags.set(RegisterOperandFlag::Dead);
}
@@ -218,6 +225,8 @@ buildBlockLivenessRepairPlan(MachineBasicBlock &Block, const TargetDescription &
const Register *Reg = Operand->getIf<Register>();
if (Reg == nullptr || Reg->isNoRegister())
continue;
+ if (isReservedPhysicalRegister(*Reg, Block.function(), Target))
+ continue;
auto Units = physicalOperandUnits(*Operand, Target);
if (!Units)
return std::unexpected(Units.error());
diff --git a/backend/lib/Target/RISCV/RISCV.rccdesc b/backend/lib/Target/RISCV/RISCV.rccdesc
index 9943724..c589424 100644
--- a/backend/lib/Target/RISCV/RISCV.rccdesc
+++ b/backend/lib/Target/RISCV/RISCV.rccdesc
@@ -75,6 +75,7 @@ register_units:
- { id: 62, symbol: U_F29, name: f29.unit }
- { id: 63, symbol: U_F30, name: f30.unit }
- { id: 64, symbol: U_F31, name: f31.unit }
+ - { id: 65, symbol: U_FFLAGS, name: fflags.unit }
registers:
- { id: 1, symbol: X0, name: x0, encoding: 0, dwarf_register: 0, units: [{ unit: U_X0, lanes: 1 }], constant: true, reserved: true, aliases: [zero] }
@@ -173,10 +174,12 @@ registers:
- { id: 94, symbol: F29_F, name: f29_f, encoding: 29, dwarf_register: 61, units: [{ unit: U_F29, lanes: 1 }] }
- { id: 95, symbol: F30_F, name: f30_f, encoding: 30, dwarf_register: 62, units: [{ unit: U_F30, lanes: 1 }] }
- { id: 96, symbol: F31_F, name: f31_f, encoding: 31, dwarf_register: 63, units: [{ unit: U_F31, lanes: 1 }] }
+ - { id: 97, symbol: FFLAGS, name: fflags, encoding: 0, dwarf_register: 65, units: [{ unit: U_FFLAGS, lanes: 1 }], artificial: true, reserved: true }
register_banks:
- { id: 1, symbol: GPR, name: GPRB, maximum_size_bits: 64 }
- { id: 2, symbol: FPR, name: FPRB, maximum_size_bits: 64 }
+ - { id: 3, symbol: CSR, name: CSRB, maximum_size_bits: 32 }
register_classes:
-
@@ -267,6 +270,18 @@ register_classes:
lanes: 1
registers: [X1, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31]
allocation_order: [X5, X6, X7, X28, X29, X30, X31, X10, X11, X12, X13, X14, X15, X16, X17, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X1]
+ -
+ id: 9
+ symbol: FFLAGS
+ name: FFLAGS
+ bank: CSR
+ size_bits: 32
+ spill_size_bits: 32
+ spill_alignment_bits: 32
+ lanes: 1
+ registers: [FFLAGS]
+ allocation_order: []
+ allocatable: false
register_masks:
-
@@ -1007,6 +1022,7 @@ instructions:
- { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
- { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
num_explicit_defs: 1
+ allow_extra_implicit_operands: true
flags: [has_side_effects, produces_code]
scheduling_class: SYSTEM
encoding_category: SYSTEM
@@ -1022,6 +1038,7 @@ instructions:
- { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
- { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
num_explicit_defs: 1
+ allow_extra_implicit_operands: true
flags: [has_side_effects, produces_code]
scheduling_class: SYSTEM
encoding_category: SYSTEM
@@ -3520,6 +3537,28 @@ instructions:
owner: instruction_select
deadline: selected
-
+ id: 1245
+ symbol: ReadFFLAGS
+ name: ReadFFLAGS
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR }
+ num_explicit_defs: 1
+ implicit_uses: [{ register: FFLAGS }]
+ flags: [pseudo, produces_code, allowed_pre_legalizer, has_side_effects]
+ owner: post_ra_expansion
+ deadline: post_ra_finalized
+ -
+ id: 1246
+ symbol: WriteFFLAGS
+ name: WriteFFLAGS
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR }
+ num_explicit_defs: 0
+ implicit_defs: [{ register: FFLAGS }]
+ flags: [pseudo, produces_code, allowed_pre_legalizer, has_side_effects]
+ owner: post_ra_expansion
+ deadline: post_ra_finalized
+ -
id: 1216
symbol: PseudoCC_COPY
name: PseudoCC_COPY
diff --git a/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp
index ae5e51d..d0de28e 100644
--- a/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp
+++ b/backend/lib/Target/RISCV/RISCVPseudoExpansion.cpp
@@ -334,6 +334,53 @@ copyRegisters(const MachineInstr &Instruction) {
return {};
}
+[[nodiscard]] Expected<bool> expandFFLAGSAccess(MachineInstr &Instruction,
+ MachineFunction &Function,
+ const TargetDescription &Target, bool IsRead) {
+ const MachineOperand *ValueOperand = Instruction.operandAt(0);
+ const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>();
+ if (Value == nullptr || !Value->isPhysical()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "RISC-V FFLAGS pseudo retains a nonphysical value register");
+ }
+ auto Zero = physical(PhysicalRegisterID::X0);
+ if (!Zero)
+ return std::unexpected(Zero.error());
+
+ MachineIRBuilder Builder(Function, Target);
+ auto Point = Builder.setInsertPointBefore(Instruction);
+ if (!Point)
+ return std::unexpected(Point.error());
+ auto Pending = Builder.buildInstr(
+ targetOpcode(IsRead ? InstructionOpcode::CSRRS : InstructionOpcode::CSRRW));
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ Expected<void> Added = IsRead ? Pending->addOperand(ValueOperand->cloneDetached())
+ : Pending->addDef(Register::fromPhysical(*Zero));
+ if (!Added)
+ return std::unexpected(Added.error());
+ Added = Pending->addImmediate(1);
+ if (!Added)
+ return std::unexpected(Added.error());
+ Added = IsRead ? Pending->addUse(Register::fromPhysical(*Zero))
+ : Pending->addOperand(ValueOperand->cloneDetached());
+ if (!Added)
+ return std::unexpected(Added.error());
+ auto Implicit = copyExtraImplicitOperands(Instruction, 1, *Pending, std::nullopt, std::nullopt);
+ if (!Implicit)
+ return std::unexpected(Implicit.error());
+ auto Replacement = Pending->build();
+ if (!Replacement)
+ return std::unexpected(Replacement.error());
+ auto Decoration = copyCommonDecoration(Instruction, **Replacement, true, true, true);
+ if (!Decoration)
+ return std::unexpected(Decoration.error());
+ auto Removed = removeOldInstruction(Instruction);
+ if (!Removed)
+ return std::unexpected(Removed.error());
+ return true;
+}
+
[[nodiscard]] Expected<bool> expandCall(MachineInstr &Instruction, MachineFunction &Function,
const TargetDescription &Target, bool IsTail) {
MachineBasicBlock *Block = Instruction.parent();
@@ -534,6 +581,10 @@ public:
return expandReturn(Instruction, Function, Target);
if (Instruction.opcode() == targetOpcode(InstructionOpcode::PseudoBR))
return expandBranch(Instruction, Function, Target);
+ if (Instruction.opcode() == targetOpcode(InstructionOpcode::ReadFFLAGS))
+ return expandFFLAGSAccess(Instruction, Function, Target, true);
+ if (Instruction.opcode() == targetOpcode(InstructionOpcode::WriteFFLAGS))
+ return expandFFLAGSAccess(Instruction, Function, Target, false);
return false;
}
};
diff --git a/backend/unittests/Target/RISCVLateCodeGenTest.cpp b/backend/unittests/Target/RISCVLateCodeGenTest.cpp
index e8ac9de..4b11140 100644
--- a/backend/unittests/Target/RISCVLateCodeGenTest.cpp
+++ b/backend/unittests/Target/RISCVLateCodeGenTest.cpp
@@ -1129,6 +1129,18 @@ void testPostRAPseudoExpansionAndFinalization(TestContext &Context,
Context.expect(false, "post-RA cross-bank copy construction must succeed");
return;
}
+ auto ReadFFLAGS = Builder.buildInstr(opcode(rcc::riscv::InstructionOpcode::ReadFFLAGS));
+ if (!ReadFFLAGS || !ReadFFLAGS->addDef(physical(rcc::riscv::PhysicalRegisterID::X12)) ||
+ !ReadFFLAGS->build()) {
+ Context.expect(false, "ReadFFLAGS pseudo construction must succeed");
+ return;
+ }
+ auto WriteFFLAGS = Builder.buildInstr(opcode(rcc::riscv::InstructionOpcode::WriteFFLAGS));
+ if (!WriteFFLAGS || !WriteFFLAGS->addUse(physical(rcc::riscv::PhysicalRegisterID::X12)) ||
+ !WriteFFLAGS->build()) {
+ Context.expect(false, "WriteFFLAGS pseudo construction must succeed");
+ return;
+ }
auto Call = Builder.buildInstr(opcode(rcc::riscv::InstructionOpcode::PseudoCALL));
auto Preserved = rcc::riscv::createCallPreservedMask(rcc::riscv::CallingConvention::LP64D);
if (!Call || !Call->addExternalSymbol("callee", 0, 1) || !Call->addRegisterMask(Preserved) ||
@@ -1154,16 +1166,20 @@ void testPostRAPseudoExpansionAndFinalization(TestContext &Context,
std::size_t CrossMoveCount = 0;
std::size_t CallCount = 0;
std::size_t ReturnCount = 0;
+ std::size_t CSRRSCount = 0;
+ std::size_t CSRRWCount = 0;
for (rcc::MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
Instruction = Instruction->next()) {
ADDICount += Instruction->opcode() == opcode(rcc::riscv::InstructionOpcode::ADDI);
CrossMoveCount += Instruction->opcode() == opcode(rcc::riscv::InstructionOpcode::FMV_D_X);
CallCount += Instruction->opcode() == opcode(rcc::riscv::InstructionOpcode::JALR_CALL);
ReturnCount += Instruction->opcode() == opcode(rcc::riscv::InstructionOpcode::JALR_RET);
+ CSRRSCount += Instruction->opcode() == opcode(rcc::riscv::InstructionOpcode::CSRRS);
+ CSRRWCount += Instruction->opcode() == opcode(rcc::riscv::InstructionOpcode::CSRRW);
}
Context.expect(Expanded && Expanded->Changed && ADDICount == 1 && CrossMoveCount == 1 &&
- CallCount == 1 && ReturnCount == 1,
- "post-RA expansion must lower copies, calls, and returns to real RV64GC code");
+ CallCount == 1 && ReturnCount == 1 && CSRRSCount == 1 && CSRRWCount == 1,
+ "post-RA expansion must lower copies, calls, returns, and FFLAGS accesses");
if (!Expanded)
return;
diff --git a/backend/unittests/Target/RISCVTargetTest.cpp b/backend/unittests/Target/RISCVTargetTest.cpp
index 680d606..b4b3648 100644
--- a/backend/unittests/Target/RISCVTargetTest.cpp
+++ b/backend/unittests/Target/RISCVTargetTest.cpp
@@ -49,11 +49,12 @@ void testRegisterFacts(TestContext &Context) {
const rcc::PhysicalRegister X8 = registerID(rcc::riscv::PhysicalRegisterID::X8);
const rcc::PhysicalRegister F31D = registerID(rcc::riscv::PhysicalRegisterID::F31_D);
const rcc::PhysicalRegister F31F = registerID(rcc::riscv::PhysicalRegisterID::F31_F);
+ const rcc::PhysicalRegister FFLAGS = registerID(rcc::riscv::PhysicalRegisterID::FFLAGS);
Context.expect(
- Registers->physicalRegisters().size() == 96 && Registers->registerUnits().size() == 64 &&
- Registers->registerClasses().size() == 8 && Registers->registerBanks().size() == 2,
- "RV64GC must expose the complete scalar, compressed, and restricted register classes");
+ Registers->physicalRegisters().size() == 97 && Registers->registerUnits().size() == 65 &&
+ Registers->registerClasses().size() == 9 && Registers->registerBanks().size() == 3,
+ "RV64GC must expose scalar, compressed, restricted, and status register classes");
Context.expect(Registers->physicalRegister("x0") != nullptr &&
Registers->physicalRegister("zero") == nullptr &&
Registers->physicalRegisterByAlternativeName("ZERO") != nullptr &&
@@ -77,6 +78,12 @@ void testRegisterFacts(TestContext &Context) {
Registers->regsOverlap(F31D, F31F) &&
Registers->subRegister(F31D, rcc::SubRegIndex::fromValue(1)) == F31F,
"FPR32 and FPR64 MIR views must retain encoding, overlap, and subregister facts");
+ Context.expect(Registers->physicalRegister("fflags") != nullptr &&
+ Registers->physicalRegister("fflags")->Reg == FFLAGS &&
+ Registers->isReservedRegister(FFLAGS) &&
+ Registers->registerClass("fflags") != nullptr &&
+ !Registers->registerClass("fflags")->Allocatable,
+ "FFLAGS must be a reserved nonallocatable status register");
const auto *GPR = Registers->registerClass("gpr");
Context.expect(
@@ -149,7 +156,7 @@ void testABIAndDebugFacts(TestContext &Context) {
void testInstructionFacts(TestContext &Context) {
constexpr std::size_t TargetInstructionCount =
- static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::Select_GPR_Using_CC_GPR) -
+ static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::WriteFFLAGS) -
static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::LUI) + 1U;
auto Target = rcc::riscv::createTargetDescription();
Context.expect(Target.has_value(), "RV64GC target description must be structurally valid");