aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-27 17:13:36 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-27 17:13:36 +0800
commit3b14bd4d5324b5a280c1ee5567b5526b59e60c5b (patch)
tree1881fc10785a8c195c9b1cdb2ddde6c310f63ab7
parentdd477bc887546b00144ea6c0a3e03ad366661636 (diff)
downloadrcc-project-3b14bd4d5324b5a280c1ee5567b5526b59e60c5b.tar.gz
rcc-project-3b14bd4d5324b5a280c1ee5567b5526b59e60c5b.tar.xz
[GlobalISel] Generate floating memory selection
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h1
-rw-r--r--backend/include/rcc/TableGen/Record.h3
-rw-r--r--backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp101
-rw-r--r--backend/lib/TableGen/Parser.cpp29
-rw-r--r--backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp95
-rw-r--r--backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc56
-rw-r--r--backend/utils/TableGen/Common/CodeGenSelection.cpp38
-rw-r--r--backend/utils/TableGen/Common/CodeGenTarget.h3
-rw-r--r--backend/utils/TableGen/GlobalISelEmitter.cpp12
9 files changed, 179 insertions, 159 deletions
diff --git a/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h b/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h
index d8bdc1a..f191db2 100644
--- a/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h
+++ b/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h
@@ -49,6 +49,7 @@ enum class GIMatchTableOpcode : std::uint8_t {
CheckOperandDefOperandConstantRanges,
CaptureDefOperand,
CreateTemporary,
+ CreateTemporaryType,
MutateOpcode,
BuildInstruction,
RenderOperand,
diff --git a/backend/include/rcc/TableGen/Record.h b/backend/include/rcc/TableGen/Record.h
index f3c7d12..525d63f 100644
--- a/backend/include/rcc/TableGen/Record.h
+++ b/backend/include/rcc/TableGen/Record.h
@@ -348,7 +348,8 @@ struct SourceSelectionDefUsePredicate final {
struct SourceSelectionTemporary final {
std::string Symbol;
- std::uint32_t TypeFromOperand = 0;
+ std::optional<std::uint32_t> TypeFromOperand;
+ std::optional<SourceSelectionTypePredicate> Type;
TextRange Range;
};
diff --git a/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp b/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp
index 39699c2..88fcc24 100644
--- a/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp
+++ b/backend/lib/CodeGen/GlobalISel/GIMatchTableExecutor.cpp
@@ -17,6 +17,7 @@
#include <charconv>
#include <cstddef>
#include <cstdint>
+#include <iterator>
#include <limits>
#include <optional>
#include <ranges>
@@ -293,7 +294,7 @@ struct MatchedRule final {
struct Temporary final {
std::uint32_t ID = 0;
- std::uint32_t TypeFromOperand = 0;
+ LowLevelType Type;
};
struct RenderedOperand final {
@@ -342,6 +343,10 @@ struct MatchedRule final {
[ID](const MatchedRule::CapturedOperand &Capture) { return Capture.ID == ID; });
}
+[[nodiscard]] bool hasRootInstruction(const MatchedRule &Rule) {
+ return std::ranges::any_of(Rule.Instructions, &MatchedRule::Instruction::ReplacesRoot);
+}
+
[[nodiscard]] const MatchedRule::CapturedOperand *
findCapturedOperand(const std::vector<MatchedRule::CapturedOperand> &Captures, std::uint32_t ID) {
const auto Position = std::ranges::find(Captures, ID, &MatchedRule::CapturedOperand::ID);
@@ -565,12 +570,28 @@ matchRule(const GIMatchTable &Table, const GIMatchTableRule &Rule, const Machine
Applying = true;
Result.Temporaries.push_back({
.ID = Step.Value,
- .TypeFromOperand = Step.OperandIndex,
+ .Type = **Type,
});
break;
}
+ case GIMatchTableOpcode::CreateTemporaryType: {
+ if (Complete || Step.Value == 0 || hasTemporary(Result, Step.Value) ||
+ Step.Auxiliary != static_cast<std::uint32_t>(GIMatchTypeKind::Scalar) ||
+ Step.Immediate <= 0 ||
+ static_cast<std::uint64_t>(Step.Immediate) > LowLevelType::MaxBitWidth) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "generated match table has an invalid explicit temporary type");
+ }
+ auto Type =
+ LowLevelType::makeCategoryNeutralScalar(static_cast<std::uint64_t>(Step.Immediate));
+ if (!Type)
+ return std::unexpected(Type.error());
+ Applying = true;
+ Result.Temporaries.push_back({.ID = Step.Value, .Type = *Type});
+ break;
+ }
case GIMatchTableOpcode::MutateOpcode:
- if (Complete || Step.Value == 0 || !Result.Instructions.empty()) {
+ if (Complete || Step.Value == 0 || hasRootInstruction(Result)) {
return makeUnexpected(ErrorCode::VerificationError,
"generated match table has an invalid root mutation");
}
@@ -582,7 +603,7 @@ matchRule(const GIMatchTable &Table, const GIMatchTableRule &Rule, const Machine
});
break;
case GIMatchTableOpcode::BuildInstruction:
- if (Complete || Step.Value == 0 || Result.Instructions.empty()) {
+ if (Complete || Step.Value == 0) {
return makeUnexpected(ErrorCode::VerificationError,
"generated match table has an invalid instruction action");
}
@@ -736,7 +757,7 @@ matchRule(const GIMatchTable &Table, const GIMatchTableRule &Rule, const Machine
if (!Complete ||
(Table.Kind == GIMatchTableKind::Selection &&
- (Result.Instructions.empty() || !Result.Instructions.front().ReplacesRoot)) ||
+ (Result.Instructions.empty() || !hasRootInstruction(Result))) ||
(Table.Kind == GIMatchTableKind::Combiner && Result.Instructions.empty() &&
!Result.ReplaceRootCapture) ||
(Table.Kind == GIMatchTableKind::Combiner && !Result.Instructions.empty() &&
@@ -1134,6 +1155,13 @@ template <typename Finalizer, typename ComplexRenderer>
}
return {};
}
+ const auto RootPosition =
+ std::ranges::find(Rule.Instructions, true, &MatchedRule::Instruction::ReplacesRoot);
+ if (RootPosition == Rule.Instructions.end())
+ return makeUnexpected(ErrorCode::VerificationError,
+ "generated match table selection has no root instruction");
+ const std::size_t RootIndex =
+ static_cast<std::size_t>(std::distance(Rule.Instructions.begin(), RootPosition));
const MachineInstrFlags InputFlags = Instruction.flags();
const bool InputMayRaiseFPException =
InputDescriptor->Flags.has(InstructionFlag::MayRaiseFPException);
@@ -1156,31 +1184,7 @@ template <typename Finalizer, typename ComplexRenderer>
std::vector<MaterializedTemporary> Temporaries;
Temporaries.reserve(Rule.Temporaries.size());
for (const MatchedRule::Temporary &Temporary : Rule.Temporaries) {
- const MachineOperand *SourceOperand = Instruction.operandAt(Temporary.TypeFromOperand);
- const Register *Source = SourceOperand == nullptr ? nullptr : SourceOperand->getIf<Register>();
- if (Source == nullptr || !Source->isVirtual()) {
- auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries);
- if (!Cleanup)
- return std::unexpected(Cleanup.error());
- return makeUnexpected(ErrorCode::VerificationError,
- "generated match table temporary has an invalid type source");
- }
- auto SourceInfo = Function.registerInfo().virtualRegisterInfo(*Source);
- if (!SourceInfo) {
- auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries);
- if (!Cleanup)
- return std::unexpected(Cleanup.error());
- return std::unexpected(SourceInfo.error());
- }
- const std::optional<LowLevelType> Type = SourceInfo->get().Type;
- if (!Type) {
- auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries);
- if (!Cleanup)
- return std::unexpected(Cleanup.error());
- return makeUnexpected(ErrorCode::VerificationError,
- "generated match table temporary type source has no type");
- }
- auto Reg = Function.registerInfo().createVirtualRegister(*Type);
+ auto Reg = Function.registerInfo().createVirtualRegister(Temporary.Type);
if (!Reg) {
auto Cleanup = eraseTemporaries(Function.registerInfo(), Temporaries);
if (!Cleanup)
@@ -1272,7 +1276,7 @@ template <typename Finalizer, typename ComplexRenderer>
return std::unexpected(Cleanup.error());
return std::unexpected(Operand.error());
}
- if (InstructionIndex == 0 &&
+ if (InstructionIndex == RootIndex &&
Rendered.SourceKind == MatchedRule::RenderedOperand::Kind::RootOperand) {
if (SourceToOutput[Rendered.Value] &&
Instruction.operandAt(Rendered.Value)->tiedOperandIndex()) {
@@ -1365,30 +1369,38 @@ template <typename Finalizer, typename ComplexRenderer>
ComplexAnchor = *Complete;
}
- MachineInstr *Anchor = &Instruction;
- for (std::size_t Index = 1; Index < Rule.Instructions.size(); ++Index) {
- auto Point = Builder.setInsertPointAfter(*Anchor);
+ const auto BuildAction = [&](std::size_t Index, MachineInstr *&Anchor) -> Expected<void> {
+ auto Point = Anchor == nullptr ? Builder.setInsertPointBefore(Instruction)
+ : Builder.setInsertPointAfter(*Anchor);
if (!Point)
- return Rollback(Point.error());
+ return std::unexpected(Point.error());
auto Pending = Builder.buildInstr(Rule.Instructions[Index].ResultOpcode);
if (!Pending)
- return Rollback(Pending.error());
+ return std::unexpected(Pending.error());
for (MachineOperand &Operand : RenderedInstructions[Index]) {
auto Added = Pending->addOperand(std::move(Operand));
if (!Added)
- return Rollback(Added.error());
+ return std::unexpected(Added.error());
}
auto Built = Pending->build();
if (!Built)
- return Rollback(Built.error());
+ return std::unexpected(Built.error());
AddedInstructions.push_back(*Built);
auto Finalized = Finalize(**Built, InputFlags, InputMayRaiseFPException);
if (!Finalized)
- return Rollback(Finalized.error());
+ return std::unexpected(Finalized.error());
Anchor = *Built;
+ return {};
+ };
+
+ MachineInstr *BeforeAnchor = ComplexAnchor;
+ for (std::size_t Index = 0; Index < RootIndex; ++Index) {
+ auto Built = BuildAction(Index, BeforeAnchor);
+ if (!Built)
+ return Rollback(Built.error());
}
- const MatchedRule::Instruction &RootAction = Rule.Instructions.front();
+ const MatchedRule::Instruction &RootAction = Rule.Instructions[RootIndex];
RootModified = true;
auto SetOpcode = Instruction.setOpcode(RootAction.ResultOpcode);
if (!SetOpcode)
@@ -1398,7 +1410,7 @@ template <typename Finalizer, typename ComplexRenderer>
if (!Erased)
return Rollback(Erased.error());
}
- for (MachineOperand &Operand : RenderedInstructions.front()) {
+ for (MachineOperand &Operand : RenderedInstructions[RootIndex]) {
auto Added = Instruction.addOperand(std::move(Operand));
if (!Added)
return Rollback(Added.error());
@@ -1419,6 +1431,13 @@ template <typename Finalizer, typename ComplexRenderer>
return Rollback(Tied.error());
}
+ MachineInstr *AfterAnchor = &Instruction;
+ for (std::size_t Index = RootIndex + 1U; Index < Rule.Instructions.size(); ++Index) {
+ auto Built = BuildAction(Index, AfterAnchor);
+ if (!Built)
+ return Rollback(Built.error());
+ }
+
for (const Register Reg : Rule.EraseOperandDefinitions) {
MachineInstr *Definition = Function.registerInfo().getVRegDef(Reg);
if (Definition == nullptr || Definition == &Instruction ||
diff --git a/backend/lib/TableGen/Parser.cpp b/backend/lib/TableGen/Parser.cpp
index 67c7a43..a4e2dfe 100644
--- a/backend/lib/TableGen/Parser.cpp
+++ b/backend/lib/TableGen/Parser.cpp
@@ -1553,20 +1553,35 @@ private:
for (const yaml::NodeID ElementID : Node.sequence()) {
const yaml::Node *Element = Input.node(ElementID);
if (!requireMapping(Element, "selection temporary") ||
- !checkKeys(*Element, {"id", "type_from_operand"}, "selection temporary")) {
+ !checkKeys(*Element, {"id", "type_from_operand", "type"}, "selection temporary")) {
return false;
}
const auto Symbol = requiredScalar(*Element, "id", "selection temporary");
- const yaml::Node *TypeNode =
- requiredNode(*Element, "type_from_operand", "selection temporary");
- if (!Symbol || TypeNode == nullptr)
+ const yaml::Node *TypeFromOperandNode = optionalNode(*Element, "type_from_operand");
+ const yaml::Node *TypeNode = optionalNode(*Element, "type");
+ if (!Symbol)
return false;
- const auto TypeFromOperand = unsignedInteger32(*TypeNode, "selection temporary type operand");
- if (!TypeFromOperand)
+ if ((TypeFromOperandNode == nullptr) == (TypeNode == nullptr)) {
+ fail(Element->range(),
+ "selection temporary requires exactly one of type_from_operand and type");
return false;
+ }
+ std::optional<std::uint32_t> TypeFromOperand;
+ std::optional<SourceSelectionTypePredicate> Type;
+ if (TypeFromOperandNode != nullptr) {
+ TypeFromOperand =
+ unsignedInteger32(*TypeFromOperandNode, "selection temporary type operand");
+ if (!TypeFromOperand)
+ return false;
+ } else {
+ Type = parseSelectionTypePredicate(*TypeNode, "selection temporary type");
+ if (!Type)
+ return false;
+ }
Result.push_back({
.Symbol = *Symbol,
- .TypeFromOperand = *TypeFromOperand,
+ .TypeFromOperand = TypeFromOperand,
+ .Type = std::move(Type),
.Range = Element->range(),
});
}
diff --git a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 6486f5c..f87bb78 100644
--- a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -146,14 +146,6 @@ operandType(const MachineFunction &Function, const MachineInstr &Instruction, st
(Type.isPointer() && Type.scalarBitWidth() == 64);
}
-[[nodiscard]] constexpr RegisterBankID fprBank() noexcept {
- return RegisterBankID::fromValue(static_cast<std::uint32_t>(RegisterBank::FPR));
-}
-
-[[nodiscard]] Expected<void> appendImmediate(MachineInstr &Instruction, std::int64_t Value) {
- return Instruction.addOperand(MachineOperand::immediate(Value));
-}
-
[[nodiscard]] Expected<void> insertPhysicalRegister(MachineInstr &Instruction, std::size_t Index,
PhysicalRegister Reg,
RegisterOperandFlag Flag) {
@@ -577,10 +569,6 @@ public:
const std::optional<LowLevelType> Type = resultType(Function, Instruction);
switch (Generic) {
- case GenericOpcode::G_FPEXTLOAD:
- return selectFloatExtLoad(Instruction, Function, Target);
- case GenericOpcode::G_FPTRUNCSTORE:
- return selectFloatTruncStore(Instruction, Function, Target);
case GenericOpcode::G_CONSTANT:
return selectIntegerConstant(Instruction, Function, Target, Type);
case GenericOpcode::G_FCONSTANT:
@@ -1158,89 +1146,6 @@ private:
return Instruction.setOpcode(instructionOpcode(InstructionOpcode::FENCE));
}
- [[nodiscard]] Expected<void> selectFloatExtLoad(MachineInstr &Instruction,
- MachineFunction &Function,
- const TargetDescription &Target) const {
- const MachineOperand *DestinationOperand = Instruction.operandAt(0);
- const Register *Destination =
- DestinationOperand == nullptr ? nullptr : DestinationOperand->getIf<Register>();
- if (Destination == nullptr)
- return makeUnexpected(ErrorCode::VerificationError,
- "RISC-V floating extension load has no destination register");
- const Register DestinationRegister = *Destination;
- auto F32 = LowLevelType::makeCategoryNeutralScalar(32);
- if (!F32)
- return std::unexpected(F32.error());
- auto Temporary = createTemporary(Function, *F32, Target, fprBank());
- if (!Temporary)
- return std::unexpected(Temporary.error());
- auto SetDestination = Instruction.operandAt(0)->setRegister(*Temporary);
- if (!SetDestination)
- return std::unexpected(SetDestination.error());
- auto Immediate = appendImmediate(Instruction, 0);
- if (!Immediate)
- return std::unexpected(Immediate.error());
- auto SetOpcode = Instruction.setOpcode(instructionOpcode(InstructionOpcode::FLW));
- if (!SetOpcode)
- return std::unexpected(SetOpcode.error());
- MachineIRBuilder Builder(Function, Target);
- auto Conversion = buildImmediateAfter(Builder, Instruction, InstructionOpcode::FCVT_D_S,
- DestinationRegister, *Temporary, 0);
- if (!Conversion)
- return std::unexpected(Conversion.error());
- return {};
- }
-
- [[nodiscard]] Expected<void> selectFloatTruncStore(MachineInstr &Instruction,
- MachineFunction &Function,
- const TargetDescription &Target) const {
- const MachineOperand *ValueOperand = Instruction.operandAt(0);
- const Register *Value = ValueOperand == nullptr ? nullptr : ValueOperand->getIf<Register>();
- if (Value == nullptr)
- return makeUnexpected(ErrorCode::VerificationError,
- "RISC-V floating truncation store has no value register");
- const Register ValueRegister = *Value;
- auto F32 = LowLevelType::makeCategoryNeutralScalar(32);
- if (!F32)
- return std::unexpected(F32.error());
- auto Temporary = createTemporary(Function, *F32, Target, fprBank());
- if (!Temporary)
- return std::unexpected(Temporary.error());
- MachineIRBuilder Builder(Function, Target);
- auto Point = Builder.setInsertPointBefore(Instruction);
- if (!Point)
- return std::unexpected(Point.error());
- auto RegisterConstraints =
- constrainBuilderRegisters(Function, Target, {*Temporary, ValueRegister});
- if (!RegisterConstraints)
- return std::unexpected(RegisterConstraints.error());
- auto Conversion = Builder.buildInstr(instructionOpcode(InstructionOpcode::FCVT_S_D));
- if (!Conversion)
- return std::unexpected(Conversion.error());
- auto Def = Conversion->addDef(*Temporary);
- if (!Def)
- return std::unexpected(Def.error());
- auto Use = Conversion->addUse(ValueRegister);
- if (!Use)
- return std::unexpected(Use.error());
- auto Rounding = Conversion->addImmediate(0);
- if (!Rounding)
- return std::unexpected(Rounding.error());
- auto Built = Conversion->build();
- if (!Built)
- return std::unexpected(Built.error());
- auto Constraint = constrainSelectedInstruction(**Built, Function, Target);
- if (!Constraint)
- return std::unexpected(Constraint.error());
- auto SetValue = Instruction.operandAt(0)->setRegister(*Temporary);
- if (!SetValue)
- return std::unexpected(SetValue.error());
- auto Immediate = appendImmediate(Instruction, 0);
- if (!Immediate)
- return std::unexpected(Immediate.error());
- return Instruction.setOpcode(instructionOpcode(InstructionOpcode::FSW));
- }
-
[[nodiscard]] Expected<void> selectIntegerConstant(MachineInstr &Instruction,
MachineFunction &Function,
const TargetDescription &Target,
diff --git a/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc b/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc
index a5d9107..940da64 100644
--- a/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc
+++ b/backend/lib/Target/RISCV/RISCVGISelFloating.rccdesc
@@ -795,3 +795,59 @@ selection_rules:
- { complex: select_condition_code, operand: 1 }
- { operand: 2 }
- { operand: 3 }
+ -
+ id: select_g_fpextload_s32_to_s64
+ priority: 300
+ match:
+ opcode: G_FPEXTLOAD
+ result_type: { kind: scalar, bits: 64 }
+ memory: { index: 0, bytes: 4 }
+ operand_types:
+ - { index: 1, kind: pointer, bits: 64 }
+ operand_banks:
+ - { index: 0, bank: FPR }
+ - { index: 1, bank: GPR }
+ apply:
+ temporaries:
+ - { id: loaded, type: { kind: scalar, bits: 32 } }
+ instructions:
+ - opcode: FLW
+ root: true
+ operands:
+ - { temporary: loaded, flags: [define] }
+ - { complex: addr_reg_imm_base, operand: 1 }
+ - { complex: addr_reg_imm_offset, operand: 1 }
+ - opcode: FCVT_D_S
+ root: false
+ operands:
+ - { operand: 0 }
+ - { temporary: loaded, flags: [use] }
+ - { immediate: 0 }
+ -
+ id: select_g_fptruncstore_s64_to_s32
+ priority: 300
+ match:
+ opcode: G_FPTRUNCSTORE
+ memory: { index: 0, bytes: 4 }
+ operand_types:
+ - { index: 0, kind: scalar, bits: 64 }
+ - { index: 1, kind: pointer, bits: 64 }
+ operand_banks:
+ - { index: 0, bank: FPR }
+ - { index: 1, bank: GPR }
+ apply:
+ temporaries:
+ - { id: rounded, type: { kind: scalar, bits: 32 } }
+ instructions:
+ - opcode: FCVT_S_D
+ root: false
+ operands:
+ - { temporary: rounded, flags: [define] }
+ - { operand: 0 }
+ - { immediate: 7 }
+ - opcode: FSW
+ root: true
+ operands:
+ - { temporary: rounded, flags: [use] }
+ - { complex: addr_reg_imm_base, operand: 1 }
+ - { complex: addr_reg_imm_offset, operand: 1 }
diff --git a/backend/utils/TableGen/Common/CodeGenSelection.cpp b/backend/utils/TableGen/Common/CodeGenSelection.cpp
index 5093f67..077a95f 100644
--- a/backend/utils/TableGen/Common/CodeGenSelection.cpp
+++ b/backend/utils/TableGen/Common/CodeGenSelection.cpp
@@ -481,21 +481,36 @@ bool CodeGenTarget::normalizeSelectionRules(NormalizedTargetModel &Result) {
for (const SourceSelectionTemporary &Temporary : Rule.Temporaries) {
if (!isIdentifier(Temporary.Symbol) ||
!TemporaryIDs.emplace(Temporary.Symbol, TemporaryIDs.size() + 1U).second ||
- (MatchInstruction != nullptr &&
- !isRegisterOperand(*MatchInstruction, Temporary.TypeFromOperand))) {
- fail(Temporary.Range,
- "selection temporary requires a unique identifier and a register type source");
+ (Temporary.TypeFromOperand.has_value() == Temporary.Type.has_value())) {
+ fail(Temporary.Range, "selection temporary requires a unique identifier and one type");
return false;
}
- Normalized.Temporaries.push_back({
+ NormalizedSelectionTemporary NormalizedTemporary{
.ID = TemporaryIDs.at(Temporary.Symbol),
- .TypeFromOperand = Temporary.TypeFromOperand,
- });
+ .TypeFromOperand = std::nullopt,
+ .Type = std::nullopt,
+ };
+ if (Temporary.TypeFromOperand) {
+ if (MatchInstruction != nullptr &&
+ !isRegisterOperand(*MatchInstruction, *Temporary.TypeFromOperand)) {
+ fail(Temporary.Range,
+ "selection temporary type source must be a matched register operand");
+ return false;
+ }
+ NormalizedTemporary.TypeFromOperand = *Temporary.TypeFromOperand;
+ } else {
+ auto Type = NormalizeType(*Temporary.Type);
+ if (!Type || Type->Kind != NormalizedSelectionTypeKind::Scalar) {
+ fail(Temporary.Range, "selection temporary explicit type must be a scalar");
+ return false;
+ }
+ NormalizedTemporary.Type = *Type;
+ }
+ Normalized.Temporaries.push_back(std::move(NormalizedTemporary));
}
bool HasRootAction = false;
- for (std::size_t ActionIndex = 0; ActionIndex < Rule.Actions.size(); ++ActionIndex) {
- const SourceSelectionAction &Action = Rule.Actions[ActionIndex];
+ for (const SourceSelectionAction &Action : Rule.Actions) {
const auto ResultOpcode = resolveOpcode(Result, Action.ResultOpcodeSymbol);
const NormalizedInstruction *ResultInstruction =
findInstructionBySymbol(Result, Action.ResultOpcodeSymbol);
@@ -509,9 +524,8 @@ bool CodeGenTarget::normalizeSelectionRules(NormalizedTargetModel &Result) {
"selection instruction opcode must name a declared target instruction or core pseudo");
return false;
}
- if (Action.ReplacesRoot == HasRootAction || (Action.ReplacesRoot && ActionIndex != 0)) {
- fail(Action.Range,
- "selection instructions require exactly one root replacement as their first action");
+ if (Action.ReplacesRoot && HasRootAction) {
+ fail(Action.Range, "selection instructions require exactly one root replacement");
return false;
}
HasRootAction |= Action.ReplacesRoot;
diff --git a/backend/utils/TableGen/Common/CodeGenTarget.h b/backend/utils/TableGen/Common/CodeGenTarget.h
index 43c7383..2ab6db1 100644
--- a/backend/utils/TableGen/Common/CodeGenTarget.h
+++ b/backend/utils/TableGen/Common/CodeGenTarget.h
@@ -298,7 +298,8 @@ struct NormalizedSelectionDefUsePredicate final {
struct NormalizedSelectionTemporary final {
std::uint32_t ID = 0;
- std::uint32_t TypeFromOperand = 0;
+ std::optional<std::uint32_t> TypeFromOperand;
+ std::optional<NormalizedSelectionTypePredicate> Type;
};
enum class NormalizedSelectionImmediateTransform : std::uint8_t {
diff --git a/backend/utils/TableGen/GlobalISelEmitter.cpp b/backend/utils/TableGen/GlobalISelEmitter.cpp
index 092969d..3ce9411 100644
--- a/backend/utils/TableGen/GlobalISelEmitter.cpp
+++ b/backend/utils/TableGen/GlobalISelEmitter.cpp
@@ -162,8 +162,16 @@ std::string emitGlobalISel(const NormalizedTargetModel &Model, std::string_view
emitInstruction(Output, "CaptureDefOperand", DefUse.RootOperandIndex, Capture.ID,
std::to_string(Capture.OperandIndex));
}
- for (const NormalizedSelectionTemporary &Temporary : Rule.Temporaries)
- emitInstruction(Output, "CreateTemporary", Temporary.TypeFromOperand, Temporary.ID);
+ for (const NormalizedSelectionTemporary &Temporary : Rule.Temporaries) {
+ if (Temporary.TypeFromOperand) {
+ emitInstruction(Output, "CreateTemporary", *Temporary.TypeFromOperand, Temporary.ID);
+ continue;
+ }
+ emitInstruction(Output, "CreateTemporaryType", 0, Temporary.ID,
+ "static_cast<std::uint32_t>(GIMatchTypeKind::" +
+ std::string(typeKindName(Temporary.Type->Kind)) + ")",
+ Temporary.Type->Bits);
+ }
for (const NormalizedSelectionAction &Action : Rule.Actions) {
emitInstruction(Output, Action.ReplacesRoot ? "MutateOpcode" : "BuildInstruction", 0,
Action.ResultOpcode);