aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen/MachineVerifierTargetTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/unittests/CodeGen/MachineVerifierTargetTest.cpp')
-rw-r--r--backend/unittests/CodeGen/MachineVerifierTargetTest.cpp485
1 files changed, 485 insertions, 0 deletions
diff --git a/backend/unittests/CodeGen/MachineVerifierTargetTest.cpp b/backend/unittests/CodeGen/MachineVerifierTargetTest.cpp
new file mode 100644
index 0000000..3eb926d
--- /dev/null
+++ b/backend/unittests/CodeGen/MachineVerifierTargetTest.cpp
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/LowLevelType.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineIRBuilder.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineOperand.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+#include "rcc/CodeGen/MachineVerifier.h"
+#include "rcc/CodeGen/RegisterMask.h"
+#include "rcc/CodeGen/TargetDescription.h"
+
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace {
+
+constexpr std::uint32_t DefGPR64Opcode = 1;
+constexpr std::uint32_t DefGPR32Opcode = 2;
+constexpr std::uint32_t EarlyClobberOpcode = 3;
+constexpr std::uint32_t TiedAnyOpcode = 4;
+constexpr std::uint32_t CallOpcode = 5;
+constexpr std::uint32_t RenamableImplicitOpcode = 6;
+
+class TestContext final {
+public:
+ void expect(bool Condition, std::string_view Message) {
+ if (Condition)
+ return;
+ std::cerr << Message << '\n';
+ ++FailureCount;
+ }
+
+ [[nodiscard]] int result() const noexcept {
+ return FailureCount == 0 ? 0 : 1;
+ }
+
+private:
+ int FailureCount = 0;
+};
+
+struct FunctionFixture final {
+ std::unique_ptr<rcc::MachineFunction> Function;
+ rcc::MachineBasicBlock *Block = nullptr;
+};
+
+[[nodiscard]] rcc::PhysicalRegister phys(std::uint32_t Number) {
+ return *rcc::PhysicalRegister::create(Number);
+}
+
+[[nodiscard]] rcc::OperandDescriptor
+registerOperand(rcc::RegisterOperandRole Role,
+ std::optional<rcc::RegisterClassID> Class = std::nullopt,
+ std::optional<rcc::RegisterBankID> Bank = std::nullopt, bool AllowSubReg = false,
+ bool EarlyClobber = false) {
+ rcc::OperandDescriptor Operand;
+ Operand.Kinds = {rcc::MachineOperand::Kind::Register};
+ Operand.RegisterRole = Role;
+ Operand.RegClass = Class;
+ Operand.RegBank = Bank;
+ Operand.AllowSubReg = AllowSubReg;
+ Operand.EarlyClobber = EarlyClobber;
+ return Operand;
+}
+
+[[nodiscard]] rcc::InstructionDescriptor instruction(std::uint32_t OpcodeValue, std::string Name) {
+ rcc::InstructionDescriptor Instruction;
+ Instruction.InstructionOpcode = rcc::Opcode::fromValue(OpcodeValue);
+ Instruction.Name = std::move(Name);
+ Instruction.Flags = {rcc::InstructionFlag::ProducesCode};
+ return Instruction;
+}
+
+[[nodiscard]] rcc::Expected<rcc::TargetDescription> createTarget() {
+ auto DefGPR64 = instruction(DefGPR64Opcode, "TEST_DEF_GPR64");
+ DefGPR64.Operands.push_back(registerOperand(rcc::RegisterOperandRole::Def,
+ rcc::test::TestRegisterClass::GPR64,
+ rcc::test::TestRegisterBank::GPR, true));
+ DefGPR64.NumExplicitDefs = 1;
+
+ auto DefGPR32 = instruction(DefGPR32Opcode, "TEST_DEF_GPR32");
+ DefGPR32.Operands.push_back(registerOperand(rcc::RegisterOperandRole::Def,
+ rcc::test::TestRegisterClass::GPR32,
+ rcc::test::TestRegisterBank::GPR, true));
+ DefGPR32.NumExplicitDefs = 1;
+
+ auto EarlyClobber = instruction(EarlyClobberOpcode, "TEST_EARLY_CLOBBER");
+ EarlyClobber.Operands.push_back(registerOperand(rcc::RegisterOperandRole::Def,
+ rcc::test::TestRegisterClass::GPR64,
+ rcc::test::TestRegisterBank::GPR, false, true));
+ EarlyClobber.Operands.push_back(registerOperand(rcc::RegisterOperandRole::Use,
+ rcc::test::TestRegisterClass::GPR64,
+ rcc::test::TestRegisterBank::GPR));
+ EarlyClobber.NumExplicitDefs = 1;
+
+ auto TiedAny = instruction(TiedAnyOpcode, "TEST_TIED_ANY");
+ auto TiedDef = registerOperand(rcc::RegisterOperandRole::Def);
+ auto TiedUse = registerOperand(rcc::RegisterOperandRole::Use);
+ TiedDef.DefaultTiedOperand = 1;
+ TiedUse.DefaultTiedOperand = 0;
+ TiedAny.Operands.push_back(TiedDef);
+ TiedAny.Operands.push_back(TiedUse);
+ TiedAny.NumExplicitDefs = 1;
+
+ auto Call = instruction(CallOpcode, "TEST_CALL");
+ rcc::OperandDescriptor Mask;
+ Mask.Kinds = {rcc::MachineOperand::Kind::RegisterMask};
+ Call.Operands.push_back(Mask);
+ Call.Flags.set(rcc::InstructionFlag::Call);
+ Call.Flags.set(rcc::InstructionFlag::HasSideEffects);
+
+ auto RenamableImplicit = instruction(RenamableImplicitOpcode, "TEST_RENAMABLE_IMPLICIT");
+ RenamableImplicit.ImplicitDefs.push_back({phys(rcc::test::TestRegister::G0), false, true});
+
+ std::vector<rcc::InstructionDescriptor> Descriptors;
+ Descriptors.push_back(std::move(DefGPR64));
+ Descriptors.push_back(std::move(DefGPR32));
+ Descriptors.push_back(std::move(EarlyClobber));
+ Descriptors.push_back(std::move(TiedAny));
+ Descriptors.push_back(std::move(Call));
+ Descriptors.push_back(std::move(RenamableImplicit));
+ auto Instructions = rcc::InstructionSet::create(std::move(Descriptors));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ return rcc::test::createTestTargetDescription(std::move(*Instructions));
+}
+
+[[nodiscard]] FunctionFixture createFunction(TestContext &Context, std::string Name,
+ std::uint64_t ID) {
+ auto Function =
+ rcc::MachineFunction::create(std::move(Name), rcc::MachineFunctionStableID::fromValue(ID));
+ Context.expect(Function.has_value(), "target verifier function must be created");
+ if (!Function)
+ return {};
+
+ auto Block = (*Function)->createBasicBlock();
+ Context.expect(Block && (*Function)->appendBlock(**Block).has_value(),
+ "target verifier block must be linked");
+ if (!Block)
+ return {};
+ return FunctionFixture{std::move(*Function), *Block};
+}
+
+[[nodiscard]] rcc::Expected<void> classify(rcc::MachineRegisterInfo &RegInfo, rcc::Register Reg,
+ rcc::RegisterBankID Bank, rcc::RegisterClassID Class) {
+ auto BankResult = RegInfo.setRegisterBank(Reg, Bank);
+ if (!BankResult)
+ return std::unexpected(BankResult.error());
+ return RegInfo.setRegisterClass(Reg, Class);
+}
+
+[[nodiscard]] rcc::Expected<rcc::MachineInstr *>
+appendInstruction(FunctionFixture &Fixture, std::uint32_t OpcodeValue,
+ std::vector<rcc::MachineOperand> Operands,
+ std::optional<rcc::MachineOperandTie> Tie = std::nullopt) {
+ auto Instruction = Fixture.Function->createInstruction(rcc::Opcode::fromValue(OpcodeValue));
+ if (!Instruction)
+ return std::unexpected(Instruction.error());
+ for (rcc::MachineOperand &Operand : Operands) {
+ auto AddResult = (*Instruction)->addOperand(std::move(Operand));
+ if (!AddResult)
+ return std::unexpected(AddResult.error());
+ }
+ if (Tie) {
+ auto TieResult = (*Instruction)->tieOperands(Tie->DefOperand, Tie->UseOperand);
+ if (!TieResult)
+ return std::unexpected(TieResult.error());
+ }
+ auto AppendResult = Fixture.Block->appendInstruction(**Instruction);
+ if (!AppendResult)
+ return std::unexpected(AppendResult.error());
+ return *Instruction;
+}
+
+void testValidTargetFacts(TestContext &Context, const rcc::TargetDescription &Target) {
+ auto Fixture = createFunction(Context, "valid_target_facts", 1);
+ if (!Fixture.Function)
+ return;
+
+ auto Type = rcc::LLT::makeInteger(64);
+ auto Reg = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ if (!Reg)
+ return;
+ Context.expect(classify(Fixture.Function->registerInfo(), *Reg, rcc::test::TestRegisterBank::GPR,
+ rcc::test::TestRegisterClass::GPR64Alloc)
+ .has_value(),
+ "valid target verifier vreg must be classified");
+
+ const auto G0 = phys(rcc::test::TestRegister::G0);
+ const auto G1 = phys(rcc::test::TestRegister::G1);
+ Context.expect(Fixture.Function->registerInfo().addLiveIn(G0, *Reg).has_value() &&
+ Fixture.Function->registerInfo()
+ .reservePhysicalRegister(phys(rcc::test::TestRegister::StackPointer))
+ .has_value() &&
+ Fixture.Block->addLiveIn(G0, rcc::LaneMask::fromRaw(0x3)).has_value(),
+ "valid target live-in and reserved register facts must be recorded");
+
+ rcc::MachineIRBuilder Builder(*Fixture.Function, Target);
+ Context.expect(Builder.setInsertPoint(*Fixture.Block).has_value(),
+ "valid target builder insertion point must be set");
+ auto Def = Builder.buildInstr(rcc::Opcode::fromValue(DefGPR64Opcode));
+ Context.expect(Def && Def->addDef(*Reg).has_value() && Def->build(),
+ "register subclass must satisfy a target operand superclass");
+
+ auto Early = Builder.buildInstr(rcc::Opcode::fromValue(EarlyClobberOpcode));
+ Context.expect(
+ Early &&
+ Early->addDef(rcc::Register::fromPhysical(G0), {rcc::RegisterOperandFlag::EarlyClobber})
+ .has_value() &&
+ Early->addUse(rcc::Register::fromPhysical(G1)).has_value() && Early->build(),
+ "disjoint physical registers must satisfy early-clobber");
+
+ auto Call = Builder.buildInstr(rcc::Opcode::fromValue(CallOpcode));
+ Context.expect(Call &&
+ Call->addRegisterMask(rcc::test::createTestCallPreservedMask()).has_value() &&
+ Call->build(),
+ "valid target call mask must build");
+ Context.expect(rcc::MachineVerifier::verify(*Fixture.Function, Target).has_value(),
+ "complete valid target facts must pass MachineVerifier");
+}
+
+void testSubRegisterProjection(TestContext &Context, const rcc::TargetDescription &Target) {
+ auto Fixture = createFunction(Context, "subregister_projection", 2);
+ if (!Fixture.Function)
+ return;
+
+ auto Type = rcc::LLT::makeInteger(64);
+ auto Reg = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ if (!Reg)
+ return;
+ Context.expect(classify(Fixture.Function->registerInfo(), *Reg, rcc::test::TestRegisterBank::GPR,
+ rcc::test::TestRegisterClass::GPR64)
+ .has_value(),
+ "subregister source vreg must be classified");
+
+ rcc::MachineIRBuilder Builder(*Fixture.Function, Target);
+ Context.expect(Builder.setInsertPoint(*Fixture.Block).has_value(),
+ "subregister builder insertion point must be set");
+ auto Def = Builder.buildInstr(rcc::Opcode::fromValue(DefGPR32Opcode));
+ Context.expect(Def && Def->addDef(*Reg, {}, rcc::test::TestSubReg::Low32).has_value() &&
+ Def->build(),
+ "projected GPR32Low class must satisfy the GPR32 operand class");
+ Context.expect(rcc::MachineVerifier::verify(*Fixture.Function, Target).has_value(),
+ "valid virtual subregister projection must verify");
+}
+
+void testUnknownAndMalformedTargetFacts(TestContext &Context,
+ const rcc::TargetDescription &Target) {
+ {
+ auto Fixture = createFunction(Context, "unknown_physreg", 3);
+ auto Operand = rcc::MachineOperand::registerOperand(rcc::Register::fromPhysical(phys(99)),
+ {rcc::RegisterOperandFlag::Define});
+ if (Fixture.Function && Operand) {
+ std::vector<rcc::MachineOperand> Operands;
+ Operands.push_back(std::move(*Operand));
+ Context.expect(appendInstruction(Fixture, DefGPR64Opcode, std::move(Operands)) &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject unknown physical registers");
+ }
+ }
+ {
+ auto Fixture = createFunction(Context, "physical_subreg", 4);
+ auto Operand = rcc::MachineOperand::registerOperand(
+ rcc::Register::fromPhysical(phys(rcc::test::TestRegister::G0)),
+ {rcc::RegisterOperandFlag::Define}, rcc::test::TestSubReg::Low32);
+ if (Fixture.Function && Operand) {
+ std::vector<rcc::MachineOperand> Operands;
+ Operands.push_back(std::move(*Operand));
+ Context.expect(appendInstruction(Fixture, DefGPR64Opcode, std::move(Operands)) &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject physical operands with SubRegIndex");
+ }
+ }
+ {
+ auto Fixture = createFunction(Context, "unknown_bank", 5);
+ auto Type = rcc::LLT::makeInteger(64);
+ if (!Fixture.Function || !Type)
+ return;
+ auto Reg = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ Context.expect(Reg &&
+ Fixture.Function->registerInfo()
+ .setRegisterBank(*Reg, rcc::RegisterBankID::fromValue(99))
+ .has_value() &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject unknown virtual register bank IDs");
+ }
+ {
+ auto Fixture = createFunction(Context, "wide_bank_value", 6);
+ auto Type = rcc::LLT::makeInteger(128);
+ if (!Fixture.Function || !Type)
+ return;
+ auto Reg = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ Context.expect(Reg &&
+ Fixture.Function->registerInfo()
+ .setRegisterBank(*Reg, rcc::test::TestRegisterBank::GPR)
+ .has_value() &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject LLTs wider than their register bank");
+ }
+ {
+ auto Fixture = createFunction(Context, "bad_function_livein", 7);
+ Context.expect(Fixture.Function &&
+ Fixture.Function->registerInfo().addLiveIn(phys(99)).has_value() &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject unknown function physical live-ins");
+ }
+ {
+ auto Fixture = createFunction(Context, "bad_block_lanes", 8);
+ Context.expect(
+ Fixture.Function &&
+ Fixture.Block
+ ->addLiveIn(phys(rcc::test::TestRegister::G0), rcc::LaneMask::fromRaw(0x10))
+ .has_value() &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject target-invalid block live-in lanes");
+ }
+ {
+ auto Fixture = createFunction(Context, "bad_allocation_hint", 9);
+ auto Type = rcc::LLT::makeInteger(64);
+ auto Reg = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ Context.expect(Reg &&
+ classify(Fixture.Function->registerInfo(), *Reg,
+ rcc::test::TestRegisterBank::FPR,
+ rcc::test::TestRegisterClass::FPR64)
+ .has_value() &&
+ Fixture.Function->registerInfo()
+ .addAllocationHint(*Reg, phys(rcc::test::TestRegister::G0))
+ .has_value() &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject allocation hints outside a vreg class");
+ }
+ {
+ auto Fixture = createFunction(Context, "reserved_allocation_hint", 14);
+ auto Type = rcc::LLT::makeInteger(64);
+ auto Reg = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ const auto G0 = phys(rcc::test::TestRegister::G0);
+ Context.expect(Reg &&
+ classify(Fixture.Function->registerInfo(), *Reg,
+ rcc::test::TestRegisterBank::GPR,
+ rcc::test::TestRegisterClass::GPR64)
+ .has_value() &&
+ Fixture.Function->registerInfo().addAllocationHint(*Reg, G0).has_value() &&
+ Fixture.Function->registerInfo().reservePhysicalRegister(G0).has_value() &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject allocation hints that overlap reserved registers");
+ }
+ {
+ auto Fixture = createFunction(Context, "incompatible_function_livein", 15);
+ auto Type = rcc::LLT::makeInteger(64);
+ auto Reg = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ Context.expect(
+ Reg &&
+ classify(Fixture.Function->registerInfo(), *Reg, rcc::test::TestRegisterBank::FPR,
+ rcc::test::TestRegisterClass::FPR64)
+ .has_value() &&
+ Fixture.Function->registerInfo()
+ .addLiveIn(phys(rcc::test::TestRegister::G0), *Reg)
+ .has_value() &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject a function live-in outside its initial vreg constraints");
+ }
+}
+
+void testRegMaskEarlyClobberAndTies(TestContext &Context, const rcc::TargetDescription &Target) {
+ {
+ auto Fixture = createFunction(Context, "bad_regmask", 10);
+ auto Mask = std::make_shared<const rcc::RegisterMask>(
+ std::vector<std::uint64_t>{std::uint64_t{1} << 63U});
+ auto Operand = rcc::MachineOperand::registerMask(std::move(Mask));
+ if (Fixture.Function && Operand) {
+ std::vector<rcc::MachineOperand> Operands;
+ Operands.push_back(std::move(*Operand));
+ Context.expect(appendInstruction(Fixture, CallOpcode, std::move(Operands)) &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject RegMask bits for unknown registers");
+ }
+ }
+ {
+ auto Fixture = createFunction(Context, "early_overlap", 11);
+ const auto G0 = phys(rcc::test::TestRegister::G0);
+ rcc::MachineIRBuilder Builder(*Fixture.Function, Target);
+ Context.expect(Builder.setInsertPoint(*Fixture.Block).has_value(),
+ "early-clobber builder insertion point must be set");
+ auto Pending = Builder.buildInstr(rcc::Opcode::fromValue(EarlyClobberOpcode));
+ Context.expect(
+ Pending &&
+ Pending
+ ->addDef(rcc::Register::fromPhysical(G0), {rcc::RegisterOperandFlag::EarlyClobber})
+ .has_value() &&
+ Pending->addUse(rcc::Register::fromPhysical(G0)).has_value() && !Pending->build() &&
+ Fixture.Function->allInstructions().empty(),
+ "Builder must reject overlapping early-clobber before publication");
+
+ auto Def = rcc::MachineOperand::registerOperand(
+ rcc::Register::fromPhysical(G0),
+ {rcc::RegisterOperandFlag::Define, rcc::RegisterOperandFlag::EarlyClobber});
+ auto Use = rcc::MachineOperand::registerOperand(rcc::Register::fromPhysical(G0),
+ {rcc::RegisterOperandFlag::Use});
+ if (Def && Use) {
+ std::vector<rcc::MachineOperand> Operands;
+ Operands.push_back(std::move(*Def));
+ Operands.push_back(std::move(*Use));
+ Context.expect(appendInstruction(Fixture, EarlyClobberOpcode, std::move(Operands)) &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject overlapping early-clobber");
+ }
+ }
+ {
+ auto Fixture = createFunction(Context, "incompatible_tie", 12);
+ auto Type = rcc::LLT::makeInteger(64);
+ auto GPR = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ auto FPR = Fixture.Function->registerInfo().createVirtualRegister(*Type);
+ if (!GPR || !FPR)
+ return;
+ Context.expect(classify(Fixture.Function->registerInfo(), *GPR,
+ rcc::test::TestRegisterBank::GPR, rcc::test::TestRegisterClass::GPR64)
+ .has_value() &&
+ classify(Fixture.Function->registerInfo(), *FPR,
+ rcc::test::TestRegisterBank::FPR,
+ rcc::test::TestRegisterClass::FPR64)
+ .has_value(),
+ "incompatible tied vregs must have individually valid classes");
+
+ auto Def = rcc::MachineOperand::registerOperand(*GPR, {rcc::RegisterOperandFlag::Define});
+ auto Use = rcc::MachineOperand::registerOperand(*FPR, {rcc::RegisterOperandFlag::Use});
+ if (Def && Use) {
+ std::vector<rcc::MachineOperand> Operands;
+ Operands.push_back(std::move(*Def));
+ Operands.push_back(std::move(*Use));
+ Context.expect(appendInstruction(Fixture, TiedAnyOpcode, std::move(Operands),
+ rcc::MachineOperandTie{0, 1}) &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject tied classes without a physical intersection");
+ }
+ }
+ {
+ auto Fixture = createFunction(Context, "reserved_renamable", 13);
+ const auto G0 = phys(rcc::test::TestRegister::G0);
+ Context.expect(Fixture.Function->registerInfo().reservePhysicalRegister(G0).has_value(),
+ "reserved renamable test register must be reserved");
+
+ rcc::MachineIRBuilder Builder(*Fixture.Function, Target);
+ Context.expect(Builder.setInsertPoint(*Fixture.Block).has_value(),
+ "reserved renamable builder insertion point must be set");
+ auto Pending = Builder.buildInstr(rcc::Opcode::fromValue(RenamableImplicitOpcode));
+ Context.expect(Pending && !Pending->build(),
+ "Builder must reject a renamable reserved implicit register");
+
+ const rcc::InstructionDescriptor *Descriptor =
+ Target.instructionDescriptor(rcc::Opcode::fromValue(RenamableImplicitOpcode));
+ if (Descriptor == nullptr)
+ return;
+ auto Implicits = Target.createDefaultImplicitOperands(*Descriptor);
+ if (Implicits) {
+ Context.expect(appendInstruction(Fixture, RenamableImplicitOpcode, std::move(*Implicits)) &&
+ !rcc::MachineVerifier::verify(*Fixture.Function, Target),
+ "MachineVerifier must reject a renamable reserved implicit register");
+ }
+ }
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ auto Target = createTarget();
+ Context.expect(Target.has_value(), "target-aware verifier test target must be valid");
+ if (!Target)
+ return Context.result();
+
+ testValidTargetFacts(Context, *Target);
+ testSubRegisterProjection(Context, *Target);
+ testUnknownAndMalformedTargetFacts(Context, *Target);
+ testRegMaskEarlyClobberAndTies(Context, *Target);
+ return Context.result();
+}