diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-12 02:14:52 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-12 02:14:52 +0800 |
| commit | 16b269164d072f3c0be713decc977c5c5f44f5e8 (patch) | |
| tree | 5c33b79939edd899da6a72c4f22017f99dfd2444 /backend/unittests/Target/RISCVCodeGenTest.cpp | |
| parent | ed9afd0d9e2ecb202872b6ea4ebe5690860f9b84 (diff) | |
| download | rcc-project-16b269164d072f3c0be713decc977c5c5f44f5e8.tar.gz rcc-project-16b269164d072f3c0be713decc977c5c5f44f5e8.tar.xz | |
[RISCV] Legalize G_VAARG
Diffstat (limited to 'backend/unittests/Target/RISCVCodeGenTest.cpp')
| -rw-r--r-- | backend/unittests/Target/RISCVCodeGenTest.cpp | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/backend/unittests/Target/RISCVCodeGenTest.cpp b/backend/unittests/Target/RISCVCodeGenTest.cpp index 4f675a4..97728ae 100644 --- a/backend/unittests/Target/RISCVCodeGenTest.cpp +++ b/backend/unittests/Target/RISCVCodeGenTest.cpp @@ -1538,6 +1538,94 @@ void testVACopyLegalization(TestContext &Context) { "va_copy expansion must attach complete pointer-sized load/store memory operands"); } +void testVAArgLegalization(TestContext &Context) { + auto Target = rcc::riscv::createTargetDescription(); + Context.expect(Target.has_value(), "RV64GC target must be available for va_arg legalization"); + if (!Target) + return; + auto Function = + rcc::MachineFunction::create("riscv_vaarg", rcc::MachineFunctionStableID::fromValue(103)); + if (!Function) + return; + auto Block = (*Function)->createBasicBlock(); + auto Pointer = rcc::LLT::makePointer(0, 64); + auto I64 = rcc::LLT::makeInteger(64); + if (!Block || !Pointer || !I64 || !(*Function)->appendBlock(**Block)) + return; + auto Destination = (*Function)->registerInfo().createVirtualRegister(*I64); + auto ListPointer = (*Function)->registerInfo().createVirtualRegister(*Pointer); + auto Sink = (*Function)->registerInfo().createVirtualRegister(*I64); + if (!Destination || !ListPointer || !Sink) + return; + + rcc::MachineIRBuilder Builder(**Function, *Target); + if (!Builder.setInsertPoint(**Block)) + return; + auto Pending = Builder.buildInstr(rcc::opcode(rcc::GenericOpcode::G_VAARG)); + if (!Pending || !Pending->addDef(*Destination) || !Pending->addUse(*ListPointer) || + !Pending->addImmediate(4)) { + return; + } + auto Built = Pending->build(); + if (!Built) + return; + auto Freeze = Builder.buildInstr(rcc::opcode(rcc::GenericOpcode::G_FREEZE)); + if (!Freeze || !Freeze->addDef(*Sink) || !Freeze->addUse(*Destination) || !Freeze->build()) + return; + + Context.expect(Target->validateInstruction(**Built, (*Function)->registerInfo()).has_value(), + "canonical G_VAARG without MMOs must satisfy the generic descriptor"); + auto Action = rcc::riscv::legalizerInfo().classify(**Built, **Function, *Target); + Context.expect(Action && *Action == rcc::LegalizationAction::Custom, + "RV64GC must custom-legalize G_VAARG"); + if (!Action) + return; + auto Lowered = rcc::riscv::legalizerInfo().lower(**Built, **Function, *Target, *Action); + Context.expect(Lowered.has_value(), "RV64GC va_arg legalization must succeed"); + if (!Lowered) + return; + + std::vector<rcc::MachineInstr *> Instructions; + for (rcc::MachineInstr *Instruction = (*Function)->firstBlock()->firstInstruction(); + Instruction != nullptr; Instruction = Instruction->next()) + Instructions.push_back(Instruction); + constexpr std::array ExpectedOpcodes = { + rcc::GenericOpcode::G_LOAD, rcc::GenericOpcode::G_CONSTANT, + rcc::GenericOpcode::G_PTR_ADD, rcc::GenericOpcode::G_CONSTANT, + rcc::GenericOpcode::G_PTRMASK, rcc::GenericOpcode::G_CONSTANT, + rcc::GenericOpcode::G_PTR_ADD, rcc::GenericOpcode::G_STORE, + rcc::GenericOpcode::G_LOAD, rcc::GenericOpcode::G_FREEZE, + }; + bool OpcodeSequenceMatches = Instructions.size() == ExpectedOpcodes.size(); + for (std::size_t Index = 0; OpcodeSequenceMatches && Index < Instructions.size(); ++Index) + OpcodeSequenceMatches = Instructions[Index]->opcode() == rcc::opcode(ExpectedOpcodes[Index]); + Context.expect((*Built)->parent() == nullptr && OpcodeSequenceMatches, + "va_arg must expand into aligned list access and a live result load"); + if (!OpcodeSequenceMatches) + return; + + const auto *Bias = Instructions[1]->operandAt(1)->getIf<rcc::IntegerImmediateOperand>(); + const auto *Mask = Instructions[3]->operandAt(1)->getIf<rcc::IntegerImmediateOperand>(); + const auto *Increment = Instructions[5]->operandAt(1)->getIf<rcc::IntegerImmediateOperand>(); + Context.expect(Bias != nullptr && Bias->Value.toSignedDecimal() == "3" && Mask != nullptr && + Mask->Value.toSignedDecimal() == "-4" && Increment != nullptr && + Increment->Value.toSignedDecimal() == "8", + "va_arg alignment and allocation constants must follow the RV64 ABI"); + + const auto ListLoadMemory = Instructions[0]->memOperands(); + const auto ListStoreMemory = Instructions[7]->memOperands(); + const auto ValueLoadMemory = Instructions[8]->memOperands(); + Context.expect(ListLoadMemory.size() == 1 && ListStoreMemory.size() == 1 && + ValueLoadMemory.size() == 1 && ListLoadMemory.front()->isLoad() && + !ListLoadMemory.front()->isStore() && ListStoreMemory.front()->isStore() && + !ListStoreMemory.front()->isLoad() && ValueLoadMemory.front()->isLoad() && + !ValueLoadMemory.front()->isStore() && + ListLoadMemory.front()->description().MemoryType == *Pointer && + ListStoreMemory.front()->description().MemoryType == *Pointer && + ValueLoadMemory.front()->description().MemoryType == *I64, + "va_arg expansion must attach typed MMOs to every concrete memory access"); +} + void testMemoryOffsetSelection(TestContext &Context) { auto Target = rcc::riscv::createTargetDescription(); Context.expect(Target.has_value(), "RV64GC target must be available for memory selection"); @@ -1622,6 +1710,7 @@ int main() { testAtomicSelection(Context); testUnsupportedTypes(Context); testVACopyLegalization(Context); + testVAArgLegalization(Context); testMemoryOffsetSelection(Context); return Context.result(); } |
