aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-07 22:03:11 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-07 22:03:11 +0800
commit3e15bfd7b075209ec3b65d30d0611e060f6fbb77 (patch)
tree9a55310fddbb48a87f32bd964ff94374593f3f4c /backend
parente16de95dce32f077361577377d5c52fc4ad78339 (diff)
downloadrcc-project-3e15bfd7b075209ec3b65d30d0611e060f6fbb77.tar.gz
rcc-project-3e15bfd7b075209ec3b65d30d0611e060f6fbb77.tar.xz
[RISCV] Complete RV64GC instruction description
Diffstat (limited to 'backend')
-rw-r--r--backend/include/rcc/CodeGen/InstructionDescriptor.h49
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVTarget.h55
-rw-r--r--backend/lib/CodeGen/InstructionDescriptor.cpp215
-rw-r--r--backend/lib/Target/Description/TargetDescriptionGenerator.cpp1909
-rw-r--r--backend/lib/Target/Description/TargetDescriptionGenerator.h1
-rw-r--r--backend/lib/Target/RISCV/CMakeLists.txt34
-rw-r--r--backend/lib/Target/RISCV/RISCV.rccdesc2979
-rw-r--r--backend/lib/Target/RISCV/RISCVTarget.cpp61
-rw-r--r--backend/tools/DescriptionGenerator/DescriptionGeneratorMain.cpp22
-rw-r--r--backend/unittests/Target/DescriptionGeneratorTest.cpp125
-rw-r--r--backend/unittests/Target/RISCVTargetTest.cpp97
11 files changed, 5460 insertions, 87 deletions
diff --git a/backend/include/rcc/CodeGen/InstructionDescriptor.h b/backend/include/rcc/CodeGen/InstructionDescriptor.h
index 06e37d3..7cf598d 100644
--- a/backend/include/rcc/CodeGen/InstructionDescriptor.h
+++ b/backend/include/rcc/CodeGen/InstructionDescriptor.h
@@ -3,6 +3,7 @@
#ifndef RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H
#define RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H
+#include "rcc/CodeGen/LowLevelType.h"
#include "rcc/CodeGen/MachineIRTypes.h"
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/CodeGen/Register.h"
@@ -26,6 +27,11 @@ class MachineInstr;
class MachineMemOperand;
class MachineRegisterInfo;
+using ExplicitOperandVerifier = Expected<void> (*)(Opcode, std::span<const MachineOperand>,
+ const MachineRegisterInfo &);
+using InstructionSemanticVerifier = Expected<void> (*)(const MachineInstr &,
+ const MachineRegisterInfo &);
+
class OperandKindSet final {
public:
static_assert(static_cast<std::uint32_t>(MachineOperand::Kind::Count) <= 32);
@@ -61,11 +67,45 @@ private:
std::uint32_t Mask = 0;
};
+class LLTKindSet final {
+public:
+ static_assert(static_cast<std::uint32_t>(LLT::Kind::VectorPointer) < 16);
+
+ constexpr LLTKindSet() noexcept = default;
+
+ LLTKindSet(std::initializer_list<LLT::Kind> Kinds) noexcept;
+
+ [[nodiscard]] constexpr bool empty() const noexcept {
+ return Mask == 0;
+ }
+
+ [[nodiscard]] constexpr bool contains(LLT::Kind Kind) const noexcept {
+ const auto Index = static_cast<std::uint32_t>(Kind);
+ return Index < 16 && (Mask & (std::uint16_t{1} << Index)) != 0;
+ }
+
+ constexpr void add(LLT::Kind Kind) noexcept {
+ const auto Index = static_cast<std::uint32_t>(Kind);
+ if (Index < 16)
+ Mask |= std::uint16_t{1} << Index;
+ }
+
+ [[nodiscard]] constexpr std::uint16_t rawValue() const noexcept {
+ return Mask;
+ }
+
+ friend constexpr bool operator==(LLTKindSet Left, LLTKindSet Right) noexcept = default;
+
+private:
+ std::uint16_t Mask = 0;
+};
+
enum class RegisterOperandRole : std::uint8_t {
None,
Def,
Use,
DefUse,
+ Any,
};
enum class MemoryOperandRole : std::uint8_t {
@@ -76,6 +116,11 @@ enum class MemoryOperandRole : std::uint8_t {
Ordering,
};
+struct OperandTypeConstraint final {
+ LLTKindSet Kinds;
+ std::optional<std::uint16_t> EqualityGroup;
+};
+
enum class InstructionFlag : std::uint8_t {
Terminator,
Branch,
@@ -184,6 +229,7 @@ struct OperandDescriptor final {
std::optional<NamedOperandID> Name;
std::optional<TargetOperandTypeID> TargetOperandType;
std::uint32_t AllowedTargetFlags = 0;
+ std::optional<OperandTypeConstraint> TypeConstraint;
};
struct ImplicitRegisterDescriptor final {
@@ -201,6 +247,7 @@ struct InstructionDescriptor final {
std::vector<OperandDescriptor> Operands;
std::size_t NumExplicitDefs = 0;
bool Variadic = false;
+ std::size_t MinimumVariadicRepetitions = 0;
std::vector<ImplicitRegisterDescriptor> ImplicitDefs;
std::vector<ImplicitRegisterDescriptor> ImplicitUses;
bool AllowExtraImplicitOperands = false;
@@ -211,6 +258,8 @@ struct InstructionDescriptor final {
bool RequiresMemoryOperands = false;
PseudoOwner Owner = PseudoOwner::None;
PipelineStage Deadline = PipelineStage::Emission;
+ ExplicitOperandVerifier VerifyExplicitOperands = nullptr;
+ InstructionSemanticVerifier VerifyInstructionSemantics = nullptr;
};
struct ImmediatePredicate final {
diff --git a/backend/include/rcc/Target/RISCV/RISCVTarget.h b/backend/include/rcc/Target/RISCV/RISCVTarget.h
index 948e22d..ca0849d 100644
--- a/backend/include/rcc/Target/RISCV/RISCVTarget.h
+++ b/backend/include/rcc/Target/RISCV/RISCVTarget.h
@@ -3,20 +3,71 @@
#ifndef RCC_TARGET_RISCV_RISCVTARGET_H
#define RCC_TARGET_RISCV_RISCVTARGET_H
-#include "rcc/CodeGen/TargetMIRTextInfo.h"
-#include "rcc/CodeGen/TargetRegisterInfo.h"
+#include "rcc/CodeGen/TargetDescription.h"
#include "rcc/Support/Error.h"
+#include "rcc/Target/RISCV/RISCVInstructions.h"
#include "rcc/Target/RISCV/RISCVRegisters.h"
#include <cstdint>
#include <memory>
+#include <span>
+#include <string_view>
namespace rcc::riscv {
+struct SchedulingClassInfo final {
+ SchedulingClassID ID;
+ std::string_view Name;
+ std::uint32_t MicroOps = 0;
+ std::uint32_t Latency = 0;
+};
+
+struct EncodingCategoryInfo final {
+ EncodingCategoryID ID;
+ std::string_view Name;
+ std::uint32_t SizeInBytes = 0;
+};
+
+struct AssemblyFormatInfo final {
+ AssemblyFormatID ID;
+ std::string_view Name;
+};
+
+struct AssemblyInstructionInfo final {
+ Opcode InstructionOpcode;
+ std::string_view Mnemonic;
+ AssemblyFormatID Format;
+};
+
+struct CallingConventionInfo final {
+ CallingConvention ID;
+ std::string_view Name;
+ RegisterClassID IntegerRegisterClass;
+ RegisterClassID FloatingRegisterClass;
+ std::span<const PhysicalRegister> IntegerArgumentRegisters;
+ std::span<const PhysicalRegister> FloatingArgumentRegisters;
+ std::span<const PhysicalRegister> IntegerReturnRegisters;
+ std::span<const PhysicalRegister> FloatingReturnRegisters;
+ std::string_view PreservedMaskName;
+ std::uint32_t StackAlignmentBytes = 0;
+ std::uint32_t StackArgumentAlignmentBytes = 0;
+ std::uint32_t ShadowSpaceBytes = 0;
+ bool SupportsVarArgs = false;
+ bool SupportsTailCalls = false;
+};
+
[[nodiscard]] Expected<PhysicalRegister> physicalRegister(PhysicalRegisterID ID);
[[nodiscard]] Expected<TargetRegisterInfo> createRegisterInfo();
[[nodiscard]] Expected<TargetMIRTextInfo> createMIRTextInfo();
+[[nodiscard]] Expected<TargetDescription> createTargetDescription();
[[nodiscard]] std::shared_ptr<const RegisterMask> createCallPreservedMask();
+[[nodiscard]] std::span<const SchedulingClassInfo> schedulingClasses() noexcept;
+[[nodiscard]] std::span<const EncodingCategoryInfo> encodingCategories() noexcept;
+[[nodiscard]] std::span<const AssemblyFormatInfo> assemblyFormats() noexcept;
+[[nodiscard]] std::span<const AssemblyInstructionInfo> assemblyInstructions() noexcept;
+[[nodiscard]] const AssemblyInstructionInfo *assemblyInstruction(Opcode InstructionOpcode) noexcept;
+[[nodiscard]] std::span<const CallingConventionInfo> callingConventions() noexcept;
+[[nodiscard]] const CallingConventionInfo *callingConvention(CallingConvention ID) noexcept;
} // namespace rcc::riscv
diff --git a/backend/lib/CodeGen/InstructionDescriptor.cpp b/backend/lib/CodeGen/InstructionDescriptor.cpp
index 3d97440..57400d6 100644
--- a/backend/lib/CodeGen/InstructionDescriptor.cpp
+++ b/backend/lib/CodeGen/InstructionDescriptor.cpp
@@ -10,6 +10,7 @@
#include <cstddef>
#include <cstdint>
#include <string>
+#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -54,6 +55,8 @@ namespace {
}
bool SawOptional = false;
+ std::optional<std::size_t> VariadicBegin;
+ std::optional<std::size_t> VariadicEnd;
for (std::size_t Index = 0; Index < Descriptor.Operands.size(); ++Index) {
const OperandDescriptor &Operand = Descriptor.Operands[Index];
if (Operand.Kinds.empty()) {
@@ -75,19 +78,17 @@ namespace {
return makeUnexpected(ErrorCode::InvalidArgument,
"non-register operand descriptor has register constraints");
}
+ if (Operand.TypeConstraint) {
+ if (!isRegisterOnly(Operand.Kinds) || Operand.TypeConstraint->Kinds.empty() ||
+ Operand.TypeConstraint->Kinds.contains(LLT::Kind::Invalid)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "typed operand descriptor requires a nonempty register type set");
+ }
+ }
if (Operand.EarlyClobber && !isDefRole(Operand.RegisterRole)) {
return makeUnexpected(ErrorCode::InvalidArgument,
"early-clobber operand descriptor must define a register");
}
- if (Index < Descriptor.NumExplicitDefs && !isDefRole(Operand.RegisterRole)) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "canonical explicit defs must precede instruction uses");
- }
- if (Index >= Descriptor.NumExplicitDefs && isDefRole(Operand.RegisterRole)) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "register definition appears after the explicit def prefix");
- }
-
if (Descriptor.Variadic && Operand.Optional) {
return makeUnexpected(ErrorCode::InvalidArgument,
"variadic instruction descriptor cannot contain optional operands");
@@ -99,27 +100,61 @@ namespace {
"optional operand descriptors must form a trailing suffix");
}
- if (Operand.VariadicGroup &&
- (!Descriptor.Variadic || Index + 1 != Descriptor.Operands.size())) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "variadic operand group must be the final descriptor");
- }
if (Operand.VariadicGroup && (Operand.Optional || Operand.DefaultTiedOperand)) {
return makeUnexpected(ErrorCode::InvalidArgument,
"variadic operand descriptor cannot be optional or have a default tie");
}
+ if (Operand.VariadicGroup) {
+ if (!Descriptor.Variadic) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "non-variadic instruction descriptor has a variadic group");
+ }
+ if (!VariadicBegin)
+ VariadicBegin = Index;
+ VariadicEnd = Index + 1;
+ } else if (VariadicEnd) {
+ const bool LaterGroup =
+ std::ranges::any_of(Descriptor.Operands.begin() + static_cast<std::ptrdiff_t>(Index),
+ Descriptor.Operands.end(), [](const OperandDescriptor &Candidate) {
+ return Candidate.VariadicGroup;
+ });
+ if (LaterGroup) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "variadic operand group descriptors must be contiguous");
+ }
+ }
}
- if (Descriptor.Variadic &&
- (Descriptor.Operands.empty() || !Descriptor.Operands.back().VariadicGroup)) {
+ if (Descriptor.Variadic && !VariadicBegin) {
return makeUnexpected(ErrorCode::InvalidArgument,
- "variadic instruction descriptor requires a final variadic group");
+ "variadic instruction descriptor requires a variadic group");
}
- if (!Descriptor.Variadic &&
- std::ranges::any_of(Descriptor.Operands,
- [](const OperandDescriptor &Operand) { return Operand.VariadicGroup; })) {
+ if (!Descriptor.Variadic && Descriptor.MinimumVariadicRepetitions != 0) {
return makeUnexpected(ErrorCode::InvalidArgument,
- "non-variadic instruction descriptor has a variadic group");
+ "non-variadic instruction has a minimum variadic repetition count");
+ }
+
+ bool SawUse = false;
+ std::size_t MinimumDefs = 0;
+ for (std::size_t Index = 0; Index < Descriptor.Operands.size(); ++Index) {
+ const OperandDescriptor &Operand = Descriptor.Operands[Index];
+ const std::size_t Repetitions =
+ Operand.VariadicGroup ? Descriptor.MinimumVariadicRepetitions : 1;
+ for (std::size_t Repeat = 0; Repeat < Repetitions; ++Repeat) {
+ if (isDefRole(Operand.RegisterRole)) {
+ if (SawUse) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "register definition appears after an instruction use");
+ }
+ ++MinimumDefs;
+ } else {
+ SawUse = true;
+ }
+ }
+ }
+ if (Descriptor.NumExplicitDefs != MinimumDefs) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "instruction descriptor explicit def count does not match its shape");
}
for (std::size_t Index = 0; Index < Descriptor.Operands.size(); ++Index) {
@@ -207,11 +242,62 @@ namespace {
return {};
}
+struct OperandShape final {
+ std::size_t VariadicBegin = 0;
+ std::size_t VariadicEnd = 0;
+ std::size_t Repetitions = 0;
+};
+
+[[nodiscard]] Expected<OperandShape> resolveOperandShape(const InstructionDescriptor &Descriptor,
+ std::size_t OperandCount) {
+ if (!Descriptor.Variadic) {
+ std::size_t RequiredCount = Descriptor.Operands.size();
+ while (RequiredCount != 0 && Descriptor.Operands[RequiredCount - 1].Optional)
+ --RequiredCount;
+ if (OperandCount < RequiredCount || OperandCount > Descriptor.Operands.size()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction has an invalid explicit operand count");
+ }
+ return OperandShape{};
+ }
+
+ const auto Begin = std::ranges::find_if(
+ Descriptor.Operands, [](const OperandDescriptor &Operand) { return Operand.VariadicGroup; });
+ const auto End = std::ranges::find_if_not(
+ Begin, Descriptor.Operands.end(),
+ [](const OperandDescriptor &Operand) { return Operand.VariadicGroup; });
+ const std::size_t VariadicBegin =
+ static_cast<std::size_t>(std::distance(Descriptor.Operands.begin(), Begin));
+ const std::size_t VariadicEnd =
+ static_cast<std::size_t>(std::distance(Descriptor.Operands.begin(), End));
+ const std::size_t GroupSize = VariadicEnd - VariadicBegin;
+ const std::size_t FixedCount = Descriptor.Operands.size() - GroupSize;
+ if (GroupSize == 0 || OperandCount < FixedCount) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction has an invalid variadic operand count");
+ }
+ const std::size_t VariableCount = OperandCount - FixedCount;
+ if (VariableCount % GroupSize != 0 ||
+ VariableCount / GroupSize < Descriptor.MinimumVariadicRepetitions) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction has an incomplete or undersized variadic operand group");
+ }
+ return OperandShape{VariadicBegin, VariadicEnd, VariableCount / GroupSize};
+}
+
[[nodiscard]] const OperandDescriptor &descriptorForOperand(const InstructionDescriptor &Descriptor,
+ const OperandShape &Shape,
std::size_t Index) {
- if (Index < Descriptor.Operands.size())
+ if (!Descriptor.Variadic)
return Descriptor.Operands[Index];
- return Descriptor.Operands.back();
+ if (Index < Shape.VariadicBegin)
+ return Descriptor.Operands[Index];
+ const std::size_t GroupSize = Shape.VariadicEnd - Shape.VariadicBegin;
+ const std::size_t VariableCount = Shape.Repetitions * GroupSize;
+ if (Index < Shape.VariadicBegin + VariableCount) {
+ return Descriptor.Operands[Shape.VariadicBegin + (Index - Shape.VariadicBegin) % GroupSize];
+ }
+ return Descriptor.Operands[Shape.VariadicEnd + (Index - Shape.VariadicBegin - VariableCount)];
}
} // namespace
@@ -221,6 +307,11 @@ OperandKindSet::OperandKindSet(std::initializer_list<MachineOperand::Kind> Kinds
add(Kind);
}
+LLTKindSet::LLTKindSet(std::initializer_list<LLT::Kind> Kinds) noexcept {
+ for (const LLT::Kind Kind : Kinds)
+ add(Kind);
+}
+
InstructionFlags::InstructionFlags(std::initializer_list<InstructionFlag> Flags) noexcept {
for (const InstructionFlag Flag : Flags)
set(Flag);
@@ -290,31 +381,65 @@ const InstructionDescriptor *InstructionSet::descriptor(std::string_view Name) c
Expected<void> InstructionSet::validateExplicitOperands(const InstructionDescriptor &Descriptor,
std::span<const MachineOperand> Operands,
const MachineRegisterInfo &RegInfo) const {
- std::size_t RequiredCount = Descriptor.Operands.size();
- if (Descriptor.Variadic)
- --RequiredCount;
- else {
- while (RequiredCount != 0 && Descriptor.Operands[RequiredCount - 1].Optional)
- --RequiredCount;
- }
-
- if (Operands.size() < RequiredCount ||
- (!Descriptor.Variadic && Operands.size() > Descriptor.Operands.size())) {
- return makeUnexpected(ErrorCode::VerificationError,
- "instruction has an invalid explicit operand count");
- }
+ auto Shape = resolveOperandShape(Descriptor, Operands.size());
+ if (!Shape)
+ return std::unexpected(Shape.error());
+ bool SawUse = false;
+ std::unordered_map<std::uint16_t, LLT> EqualityGroups;
for (std::size_t Index = 0; Index < Operands.size(); ++Index) {
const MachineOperand &Operand = Operands[Index];
if (Operand.isImplicit()) {
return makeUnexpected(ErrorCode::VerificationError,
"explicit operand sequence contains an implicit register");
}
- const auto Validation =
- validateOperand(descriptorForOperand(Descriptor, Index), Operand, RegInfo);
+ const OperandDescriptor &OperandDesc = descriptorForOperand(Descriptor, *Shape, Index);
+ const auto Validation = validateOperand(OperandDesc, Operand, RegInfo);
if (!Validation)
return std::unexpected(Validation.error());
+ if (Operand.isRegister()) {
+ if (isDefRole(OperandDesc.RegisterRole)) {
+ if (SawUse) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction register definition appears after a use");
+ }
+ } else {
+ SawUse = true;
+ }
+ } else {
+ SawUse = true;
+ }
+
+ if (!OperandDesc.TypeConstraint)
+ continue;
+ const Register *Reg = Operand.getIf<Register>();
+ if (Reg == nullptr || !Reg->isVirtual()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "typed instruction operand must use a virtual register");
+ }
+ auto Info = RegInfo.virtualRegisterInfo(*Reg);
+ if (!Info)
+ return std::unexpected(Info.error());
+ if (!Info->get().Type) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "typed instruction operand virtual register has no LLT");
+ }
+ const LLT Type = *Info->get().Type;
+ if (!OperandDesc.TypeConstraint->Kinds.contains(Type.kind())) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction operand LLT kind is not permitted by its descriptor");
+ }
+ if (OperandDesc.TypeConstraint->EqualityGroup) {
+ const auto [Position, Inserted] =
+ EqualityGroups.emplace(*OperandDesc.TypeConstraint->EqualityGroup, Type);
+ if (!Inserted && Position->second != Type) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction operands in one LLT group have different types");
+ }
+ }
}
+ if (Descriptor.VerifyExplicitOperands != nullptr)
+ return Descriptor.VerifyExplicitOperands(Descriptor.InstructionOpcode, Operands, RegInfo);
return {};
}
@@ -472,7 +597,12 @@ Expected<void> InstructionSet::validateOperand(const OperandDescriptor &Descript
const bool IsUse = Operand.isUse();
const bool ExpectedDef = isDefRole(Descriptor.RegisterRole);
const bool ExpectedUse = isUseRole(Descriptor.RegisterRole);
- if (IsDef != ExpectedDef || IsUse != ExpectedUse) {
+ if (Descriptor.RegisterRole == RegisterOperandRole::Any) {
+ if (!IsDef && !IsUse) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "unclassified register operand must define or use its register");
+ }
+ } else if (IsDef != ExpectedDef || IsUse != ExpectedUse) {
return makeUnexpected(ErrorCode::VerificationError,
"register operand def/use role does not match its descriptor");
}
@@ -564,7 +694,12 @@ Expected<void> InstructionSet::validateInstruction(const MachineInstr &Instructi
auto ImplicitValidation = validateImplicitOperands(*Descriptor, ImplicitOperands);
if (!ImplicitValidation)
return std::unexpected(ImplicitValidation.error());
- return validateMemOperands(*Descriptor, Instruction.memOperands());
+ auto MemoryValidation = validateMemOperands(*Descriptor, Instruction.memOperands());
+ if (!MemoryValidation)
+ return std::unexpected(MemoryValidation.error());
+ if (Descriptor->VerifyInstructionSemantics != nullptr)
+ return Descriptor->VerifyInstructionSemantics(Instruction, RegInfo);
+ return {};
}
Expected<std::vector<MachineOperand>>
diff --git a/backend/lib/Target/Description/TargetDescriptionGenerator.cpp b/backend/lib/Target/Description/TargetDescriptionGenerator.cpp
index 259c9fe..3c5e817 100644
--- a/backend/lib/Target/Description/TargetDescriptionGenerator.cpp
+++ b/backend/lib/Target/Description/TargetDescriptionGenerator.cpp
@@ -6,8 +6,10 @@
#include <algorithm>
#include <array>
+#include <bit>
#include <cctype>
#include <charconv>
+#include <cstddef>
#include <cstdint>
#include <limits>
#include <optional>
@@ -92,6 +94,126 @@ struct SourceRegisterMask final {
TextRange Range;
};
+struct SourceMIROperandFlag final {
+ std::string Name;
+ std::uint32_t Value = 0;
+ TextRange Range;
+};
+
+struct SourceMIROperandFlags final {
+ std::uint32_t DirectMask = 0;
+ std::vector<SourceMIROperandFlag> Direct;
+ std::vector<SourceMIROperandFlag> Bitmask;
+ TextRange Range;
+};
+
+struct SourceCallingConvention final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::string IntegerRegisterClassSymbol;
+ std::string FloatingRegisterClassSymbol;
+ std::vector<std::string> IntegerArgumentRegisterSymbols;
+ std::vector<std::string> FloatingArgumentRegisterSymbols;
+ std::vector<std::string> IntegerReturnRegisterSymbols;
+ std::vector<std::string> FloatingReturnRegisterSymbols;
+ std::string PreservedMaskName;
+ std::uint32_t StackAlignmentBytes = 0;
+ std::uint32_t StackArgumentAlignmentBytes = 0;
+ std::uint32_t ShadowSpaceBytes = 0;
+ bool SupportsVarArgs = false;
+ bool SupportsTailCalls = false;
+ TextRange Range;
+};
+
+struct SourceNamedOperand final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ TextRange Range;
+};
+
+struct SourceImmediatePredicate final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::string Kind;
+ std::uint32_t Bits = 0;
+ std::uint64_t Alignment = 1;
+ bool NonZero = false;
+ std::vector<std::int64_t> AllowedValues;
+ TextRange Range;
+};
+
+struct SourceSchedulingClass final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::uint32_t MicroOps = 0;
+ std::uint32_t Latency = 0;
+ TextRange Range;
+};
+
+struct SourceEncodingCategory final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::uint32_t SizeInBytes = 0;
+ TextRange Range;
+};
+
+struct SourceAssemblyFormat final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ TextRange Range;
+};
+
+struct SourceOperand final {
+ std::vector<std::string> Kinds;
+ std::string RegisterRole = "none";
+ std::optional<std::string> RegisterClassSymbol;
+ std::optional<std::string> RegisterBankSymbol;
+ std::optional<std::string> ImmediatePredicateSymbol;
+ std::string MemoryRole = "none";
+ std::optional<std::uint32_t> DefaultTiedOperand;
+ bool EarlyClobber = false;
+ bool Optional = false;
+ bool VariadicGroup = false;
+ bool AllowSubReg = false;
+ std::optional<std::string> NameSymbol;
+ std::uint32_t AllowedTargetFlags = 0;
+ TextRange Range;
+};
+
+struct SourceImplicitRegister final {
+ std::string RegisterSymbol;
+ bool EarlyClobber = false;
+ bool Renamable = false;
+ TextRange Range;
+};
+
+struct SourceInstruction final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::string AssemblyMnemonic;
+ std::vector<SourceOperand> Operands;
+ std::uint32_t NumExplicitDefs = 0;
+ bool Variadic = false;
+ std::vector<SourceImplicitRegister> ImplicitDefs;
+ std::vector<SourceImplicitRegister> ImplicitUses;
+ bool AllowExtraImplicitOperands = false;
+ std::vector<std::string> Flags;
+ std::string SchedulingClassSymbol;
+ std::string EncodingCategorySymbol;
+ std::string AssemblyFormatSymbol;
+ bool RequiresMemoryOperands = false;
+ std::string Owner = "none";
+ std::string Deadline = "emission";
+ TextRange Range;
+};
+
struct DescriptionAST final {
SourceTarget Target;
std::vector<SourceRegisterUnit> RegisterUnits;
@@ -99,6 +221,14 @@ struct DescriptionAST final {
std::vector<SourceRegisterBank> RegisterBanks;
std::vector<SourceRegisterClass> RegisterClasses;
std::vector<SourceRegisterMask> RegisterMasks;
+ SourceMIROperandFlags MIROperandFlags;
+ std::vector<SourceCallingConvention> CallingConventions;
+ std::vector<SourceNamedOperand> NamedOperands;
+ std::vector<SourceImmediatePredicate> ImmediatePredicates;
+ std::vector<SourceSchedulingClass> SchedulingClasses;
+ std::vector<SourceEncodingCategory> EncodingCategories;
+ std::vector<SourceAssemblyFormat> AssemblyFormats;
+ std::vector<SourceInstruction> Instructions;
};
struct NormalizedRegisterUnitLane final {
@@ -147,6 +277,104 @@ struct NormalizedRegisterMask final {
std::vector<std::uint32_t> PreservedRegisters;
};
+struct NormalizedCallingConvention final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::uint32_t IntegerRegisterClass = 0;
+ std::uint32_t FloatingRegisterClass = 0;
+ std::vector<std::uint32_t> IntegerArgumentRegisters;
+ std::vector<std::uint32_t> FloatingArgumentRegisters;
+ std::vector<std::uint32_t> IntegerReturnRegisters;
+ std::vector<std::uint32_t> FloatingReturnRegisters;
+ std::string PreservedMaskName;
+ std::uint32_t StackAlignmentBytes = 0;
+ std::uint32_t StackArgumentAlignmentBytes = 0;
+ std::uint32_t ShadowSpaceBytes = 0;
+ bool SupportsVarArgs = false;
+ bool SupportsTailCalls = false;
+};
+
+struct NormalizedNamedOperand final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+};
+
+struct NormalizedImmediatePredicate final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ bool IsSigned = false;
+ std::uint32_t Bits = 0;
+ std::uint64_t Alignment = 1;
+ bool NonZero = false;
+ std::vector<std::int64_t> AllowedValues;
+};
+
+struct NormalizedSchedulingClass final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::uint32_t MicroOps = 0;
+ std::uint32_t Latency = 0;
+};
+
+struct NormalizedEncodingCategory final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::uint32_t SizeInBytes = 0;
+};
+
+struct NormalizedAssemblyFormat final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+};
+
+struct NormalizedOperand final {
+ std::vector<std::string> Kinds;
+ std::string RegisterRole;
+ std::optional<std::uint32_t> RegisterClass;
+ std::optional<std::uint32_t> RegisterBank;
+ std::optional<std::uint32_t> ImmediatePredicate;
+ std::string MemoryRole;
+ std::optional<std::uint32_t> DefaultTiedOperand;
+ bool EarlyClobber = false;
+ bool Optional = false;
+ bool VariadicGroup = false;
+ bool AllowSubReg = false;
+ std::optional<std::uint32_t> Name;
+ std::uint32_t AllowedTargetFlags = 0;
+};
+
+struct NormalizedImplicitRegister final {
+ std::uint32_t Register = 0;
+ bool EarlyClobber = false;
+ bool Renamable = false;
+};
+
+struct NormalizedInstruction final {
+ std::uint32_t ID = 0;
+ std::string Symbol;
+ std::string Name;
+ std::string AssemblyMnemonic;
+ std::vector<NormalizedOperand> Operands;
+ std::uint32_t NumExplicitDefs = 0;
+ bool Variadic = false;
+ std::vector<NormalizedImplicitRegister> ImplicitDefs;
+ std::vector<NormalizedImplicitRegister> ImplicitUses;
+ bool AllowExtraImplicitOperands = false;
+ std::vector<std::string> Flags;
+ std::uint32_t SchedulingClass = 0;
+ std::uint32_t EncodingCategory = 0;
+ std::uint32_t AssemblyFormat = 0;
+ bool RequiresMemoryOperands = false;
+ std::string Owner;
+ std::string Deadline;
+};
+
struct NormalizedTargetModel final {
SourceTarget Target;
std::vector<SourceRegisterUnit> RegisterUnits;
@@ -154,6 +382,14 @@ struct NormalizedTargetModel final {
std::vector<NormalizedRegisterBank> RegisterBanks;
std::vector<NormalizedRegisterClass> RegisterClasses;
std::vector<NormalizedRegisterMask> RegisterMasks;
+ SourceMIROperandFlags MIROperandFlags;
+ std::vector<NormalizedCallingConvention> CallingConventions;
+ std::vector<NormalizedNamedOperand> NamedOperands;
+ std::vector<NormalizedImmediatePredicate> ImmediatePredicates;
+ std::vector<NormalizedSchedulingClass> SchedulingClasses;
+ std::vector<NormalizedEncodingCategory> EncodingCategories;
+ std::vector<NormalizedAssemblyFormat> AssemblyFormats;
+ std::vector<NormalizedInstruction> Instructions;
};
[[nodiscard]] std::string lowerASCII(std::string_view Value) {
@@ -181,6 +417,21 @@ struct NormalizedTargetModel final {
});
}
+[[nodiscard]] bool isAssemblyMnemonic(std::string_view Value) noexcept {
+ if (Value.empty())
+ return false;
+ if (std::islower(static_cast<unsigned char>(Value.front())) == 0)
+ return false;
+ if (std::islower(static_cast<unsigned char>(Value.back())) == 0 &&
+ std::isdigit(static_cast<unsigned char>(Value.back())) == 0)
+ return false;
+ return std::ranges::all_of(Value, [](char Character) {
+ const unsigned char Code = static_cast<unsigned char>(Character);
+ return std::islower(Code) != 0 || std::isdigit(Code) != 0 || Character == '.' ||
+ Character == '_';
+ });
+}
+
[[nodiscard]] std::string upperIdentifier(std::string_view Value) {
std::string Result;
Result.reserve(Value.size());
@@ -239,7 +490,9 @@ public:
return std::nullopt;
if (!checkKeys(*Root,
{"schema", "target", "register_units", "registers", "register_banks",
- "register_classes", "register_masks"},
+ "register_classes", "register_masks", "mir_operand_flags", "named_operands",
+ "immediate_predicates", "scheduling_classes", "encoding_categories",
+ "assembly_formats", "calling_conventions", "instructions"},
"target description root")) {
return std::nullopt;
}
@@ -248,7 +501,7 @@ public:
if (SchemaNode == nullptr)
return std::nullopt;
const auto Schema = scalar(*SchemaNode, "target description schema");
- if (!Schema || *Schema != "rcc-target-description-v1") {
+ if (!Schema || *Schema != "rcc-target-description-v2") {
if (Schema)
fail(SchemaNode->range(), "unsupported target description schema");
return std::nullopt;
@@ -264,13 +517,40 @@ public:
const yaml::Node *ClassesNode =
requiredNode(*Root, "register_classes", "target description root");
const yaml::Node *MasksNode = requiredNode(*Root, "register_masks", "target description root");
+ const yaml::Node *MIROperandFlagsNode = optionalNode(*Root, "mir_operand_flags");
+ const yaml::Node *NamedOperandsNode =
+ requiredNode(*Root, "named_operands", "target description root");
+ const yaml::Node *ImmediatePredicatesNode =
+ requiredNode(*Root, "immediate_predicates", "target description root");
+ const yaml::Node *SchedulingClassesNode =
+ requiredNode(*Root, "scheduling_classes", "target description root");
+ const yaml::Node *EncodingCategoriesNode =
+ requiredNode(*Root, "encoding_categories", "target description root");
+ const yaml::Node *AssemblyFormatsNode =
+ requiredNode(*Root, "assembly_formats", "target description root");
+ const yaml::Node *CallingConventionsNode =
+ requiredNode(*Root, "calling_conventions", "target description root");
+ const yaml::Node *InstructionsNode =
+ requiredNode(*Root, "instructions", "target description root");
if (UnitsNode == nullptr || RegistersNode == nullptr || BanksNode == nullptr ||
- ClassesNode == nullptr || MasksNode == nullptr ||
+ ClassesNode == nullptr || MasksNode == nullptr || NamedOperandsNode == nullptr ||
+ ImmediatePredicatesNode == nullptr || SchedulingClassesNode == nullptr ||
+ EncodingCategoriesNode == nullptr || AssemblyFormatsNode == nullptr ||
+ CallingConventionsNode == nullptr || InstructionsNode == nullptr ||
!parseRegisterUnits(*UnitsNode, Result.RegisterUnits) ||
!parseRegisters(*RegistersNode, Result.Registers) ||
!parseRegisterBanks(*BanksNode, Result.RegisterBanks) ||
!parseRegisterClasses(*ClassesNode, Result.RegisterClasses) ||
- !parseRegisterMasks(*MasksNode, Result.RegisterMasks)) {
+ !parseRegisterMasks(*MasksNode, Result.RegisterMasks) ||
+ (MIROperandFlagsNode != nullptr &&
+ !parseMIROperandFlags(*MIROperandFlagsNode, Result.MIROperandFlags)) ||
+ !parseNamedOperands(*NamedOperandsNode, Result.NamedOperands) ||
+ !parseImmediatePredicates(*ImmediatePredicatesNode, Result.ImmediatePredicates) ||
+ !parseSchedulingClasses(*SchedulingClassesNode, Result.SchedulingClasses) ||
+ !parseEncodingCategories(*EncodingCategoriesNode, Result.EncodingCategories) ||
+ !parseAssemblyFormats(*AssemblyFormatsNode, Result.AssemblyFormats) ||
+ !parseCallingConventions(*CallingConventionsNode, Result.CallingConventions) ||
+ !parseInstructions(*InstructionsNode, Result.Instructions)) {
return std::nullopt;
}
return Result;
@@ -378,6 +658,21 @@ private:
return static_cast<std::uint32_t>(*Value);
}
+ [[nodiscard]] std::optional<std::int64_t> signedInteger(const yaml::Node &Node,
+ std::string_view Context) {
+ const auto Text = scalar(Node, Context);
+ if (!Text)
+ return std::nullopt;
+ std::int64_t Result = 0;
+ const auto [End, Error] =
+ std::from_chars(Text->data(), Text->data() + Text->size(), Result, 10);
+ if (Error != std::errc{} || End != Text->data() + Text->size()) {
+ fail(Node.range(), std::string(Context) + " must be a signed decimal integer");
+ return std::nullopt;
+ }
+ return Result;
+ }
+
[[nodiscard]] std::optional<bool> boolean(const yaml::Node &Node, std::string_view Context) {
const auto Text = scalar(Node, Context);
if (!Text)
@@ -407,6 +702,23 @@ private:
return Result;
}
+ [[nodiscard]] std::optional<std::vector<std::int64_t>>
+ signedIntegerSequence(const yaml::Node &Node, std::string_view Context) {
+ if (!requireSequence(&Node, Context))
+ return std::nullopt;
+ std::vector<std::int64_t> Result;
+ for (const yaml::NodeID Value : Node.sequence()) {
+ const yaml::Node *Element = Input.node(Value);
+ if (Element == nullptr)
+ return std::nullopt;
+ const auto Parsed = signedInteger(*Element, Context);
+ if (!Parsed)
+ return std::nullopt;
+ Result.push_back(*Parsed);
+ }
+ return Result;
+ }
+
[[nodiscard]] bool parseTarget(const yaml::Node &Node, SourceTarget &Result) {
if (!requireMapping(&Node, "target"))
return false;
@@ -684,6 +996,588 @@ private:
return true;
}
+ [[nodiscard]] bool parseMIROperandFlagSequence(const yaml::Node &Node, std::string_view Context,
+ std::vector<SourceMIROperandFlag> &Result) {
+ if (!requireSequence(&Node, Context))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, Context) || !checkKeys(*Element, {"name", "value"}, Context)) {
+ return false;
+ }
+ const auto Name = requiredScalar(*Element, "name", Context);
+ const yaml::Node *ValueNode = requiredNode(*Element, "value", Context);
+ if (!Name || ValueNode == nullptr)
+ return false;
+ const auto Value = unsignedInteger32(*ValueNode, std::string(Context) + " value");
+ if (!Value)
+ return false;
+ Result.push_back({.Name = *Name, .Value = *Value, .Range = Element->range()});
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool parseMIROperandFlags(const yaml::Node &Node, SourceMIROperandFlags &Result) {
+ if (!requireMapping(&Node, "MIR operand flags") ||
+ !checkKeys(Node, {"direct_mask", "direct", "bitmask"}, "MIR operand flags")) {
+ return false;
+ }
+ const yaml::Node *DirectMaskNode = requiredNode(Node, "direct_mask", "MIR operand flags");
+ const yaml::Node *DirectNode = requiredNode(Node, "direct", "MIR operand flags");
+ const yaml::Node *BitmaskNode = requiredNode(Node, "bitmask", "MIR operand flags");
+ if (DirectMaskNode == nullptr || DirectNode == nullptr || BitmaskNode == nullptr)
+ return false;
+ const auto DirectMask = unsignedInteger32(*DirectMaskNode, "MIR operand flag direct_mask");
+ if (!DirectMask ||
+ !parseMIROperandFlagSequence(*DirectNode, "direct MIR operand flag", Result.Direct) ||
+ !parseMIROperandFlagSequence(*BitmaskNode, "bitmask MIR operand flag", Result.Bitmask)) {
+ return false;
+ }
+ Result.DirectMask = *DirectMask;
+ Result.Range = Node.range();
+ return true;
+ }
+
+ [[nodiscard]] bool parseNamedOperands(const yaml::Node &Node,
+ std::vector<SourceNamedOperand> &Result) {
+ if (!requireSequence(&Node, "named_operands"))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, "named operand"))
+ return false;
+ if (!checkKeys(*Element, {"id", "symbol", "name"}, "named operand"))
+ return false;
+ const yaml::Node *IDNode = requiredNode(*Element, "id", "named operand");
+ const auto Symbol = requiredScalar(*Element, "symbol", "named operand");
+ const auto Name = requiredScalar(*Element, "name", "named operand");
+ if (IDNode == nullptr || !Symbol || !Name)
+ return false;
+ const auto ID = unsignedInteger32(*IDNode, "named operand id");
+ if (!ID)
+ return false;
+ Result.push_back({.ID = *ID, .Symbol = *Symbol, .Name = *Name, .Range = Element->range()});
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool parseImmediatePredicates(const yaml::Node &Node,
+ std::vector<SourceImmediatePredicate> &Result) {
+ if (!requireSequence(&Node, "immediate_predicates"))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, "immediate predicate"))
+ return false;
+ if (!checkKeys(
+ *Element,
+ {"id", "symbol", "name", "kind", "bits", "alignment", "nonzero", "allowed_values"},
+ "immediate predicate")) {
+ return false;
+ }
+ const yaml::Node *IDNode = requiredNode(*Element, "id", "immediate predicate");
+ const auto Symbol = requiredScalar(*Element, "symbol", "immediate predicate");
+ const auto Name = requiredScalar(*Element, "name", "immediate predicate");
+ const auto Kind = requiredScalar(*Element, "kind", "immediate predicate");
+ const yaml::Node *BitsNode = requiredNode(*Element, "bits", "immediate predicate");
+ if (IDNode == nullptr || !Symbol || !Name || !Kind || BitsNode == nullptr)
+ return false;
+ const auto ID = unsignedInteger32(*IDNode, "immediate predicate id");
+ const auto Bits = unsignedInteger32(*BitsNode, "immediate predicate bits");
+ if (!ID || !Bits)
+ return false;
+ SourceImmediatePredicate Predicate{
+ .ID = *ID,
+ .Symbol = *Symbol,
+ .Name = *Name,
+ .Kind = *Kind,
+ .Bits = *Bits,
+ .Alignment = 1,
+ .NonZero = false,
+ .AllowedValues = {},
+ .Range = Element->range(),
+ };
+ if (const yaml::Node *Alignment = optionalNode(*Element, "alignment")) {
+ const auto Value = unsignedInteger(*Alignment, "immediate predicate alignment");
+ if (!Value)
+ return false;
+ Predicate.Alignment = *Value;
+ }
+ if (const yaml::Node *NonZero = optionalNode(*Element, "nonzero")) {
+ const auto Value = boolean(*NonZero, "immediate predicate nonzero");
+ if (!Value)
+ return false;
+ Predicate.NonZero = *Value;
+ }
+ if (const yaml::Node *AllowedValues = optionalNode(*Element, "allowed_values")) {
+ const auto Values =
+ signedIntegerSequence(*AllowedValues, "immediate predicate allowed_values");
+ if (!Values || Values->empty()) {
+ if (Values)
+ fail(AllowedValues->range(), "immediate predicate allowed_values must be nonempty");
+ return false;
+ }
+ Predicate.AllowedValues = *Values;
+ }
+ Result.push_back(std::move(Predicate));
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool parseSchedulingClasses(const yaml::Node &Node,
+ std::vector<SourceSchedulingClass> &Result) {
+ if (!requireSequence(&Node, "scheduling_classes"))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, "scheduling class"))
+ return false;
+ if (!checkKeys(*Element, {"id", "symbol", "name", "micro_ops", "latency"},
+ "scheduling class")) {
+ return false;
+ }
+ const yaml::Node *IDNode = requiredNode(*Element, "id", "scheduling class");
+ const auto Symbol = requiredScalar(*Element, "symbol", "scheduling class");
+ const auto Name = requiredScalar(*Element, "name", "scheduling class");
+ const yaml::Node *MicroOpsNode = requiredNode(*Element, "micro_ops", "scheduling class");
+ const yaml::Node *LatencyNode = requiredNode(*Element, "latency", "scheduling class");
+ if (IDNode == nullptr || !Symbol || !Name || MicroOpsNode == nullptr ||
+ LatencyNode == nullptr)
+ return false;
+ const auto ID = unsignedInteger32(*IDNode, "scheduling class id");
+ const auto MicroOps = unsignedInteger32(*MicroOpsNode, "scheduling class micro_ops");
+ const auto Latency = unsignedInteger32(*LatencyNode, "scheduling class latency");
+ if (!ID || !MicroOps || !Latency)
+ return false;
+ Result.push_back({
+ .ID = *ID,
+ .Symbol = *Symbol,
+ .Name = *Name,
+ .MicroOps = *MicroOps,
+ .Latency = *Latency,
+ .Range = Element->range(),
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool parseEncodingCategories(const yaml::Node &Node,
+ std::vector<SourceEncodingCategory> &Result) {
+ if (!requireSequence(&Node, "encoding_categories"))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, "encoding category"))
+ return false;
+ if (!checkKeys(*Element, {"id", "symbol", "name", "size_bytes"}, "encoding category"))
+ return false;
+ const yaml::Node *IDNode = requiredNode(*Element, "id", "encoding category");
+ const auto Symbol = requiredScalar(*Element, "symbol", "encoding category");
+ const auto Name = requiredScalar(*Element, "name", "encoding category");
+ const yaml::Node *SizeNode = requiredNode(*Element, "size_bytes", "encoding category");
+ if (IDNode == nullptr || !Symbol || !Name || SizeNode == nullptr)
+ return false;
+ const auto ID = unsignedInteger32(*IDNode, "encoding category id");
+ const auto Size = unsignedInteger32(*SizeNode, "encoding category size_bytes");
+ if (!ID || !Size)
+ return false;
+ Result.push_back({
+ .ID = *ID,
+ .Symbol = *Symbol,
+ .Name = *Name,
+ .SizeInBytes = *Size,
+ .Range = Element->range(),
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool parseAssemblyFormats(const yaml::Node &Node,
+ std::vector<SourceAssemblyFormat> &Result) {
+ if (!requireSequence(&Node, "assembly_formats"))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, "assembly format"))
+ return false;
+ if (!checkKeys(*Element, {"id", "symbol", "name"}, "assembly format"))
+ return false;
+ const yaml::Node *IDNode = requiredNode(*Element, "id", "assembly format");
+ const auto Symbol = requiredScalar(*Element, "symbol", "assembly format");
+ const auto Name = requiredScalar(*Element, "name", "assembly format");
+ if (IDNode == nullptr || !Symbol || !Name)
+ return false;
+ const auto ID = unsignedInteger32(*IDNode, "assembly format id");
+ if (!ID)
+ return false;
+ Result.push_back({.ID = *ID, .Symbol = *Symbol, .Name = *Name, .Range = Element->range()});
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool parseCallingConventions(const yaml::Node &Node,
+ std::vector<SourceCallingConvention> &Result) {
+ if (!requireSequence(&Node, "calling_conventions"))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, "calling convention"))
+ return false;
+ if (!checkKeys(*Element,
+ {"id", "symbol", "name", "integer_register_class", "floating_register_class",
+ "integer_argument_registers", "floating_argument_registers",
+ "integer_return_registers", "floating_return_registers", "preserved_mask",
+ "stack_alignment_bytes", "stack_argument_alignment_bytes",
+ "shadow_space_bytes", "supports_varargs", "supports_tail_calls"},
+ "calling convention")) {
+ return false;
+ }
+ const yaml::Node *IDNode = requiredNode(*Element, "id", "calling convention");
+ const auto Symbol = requiredScalar(*Element, "symbol", "calling convention");
+ const auto Name = requiredScalar(*Element, "name", "calling convention");
+ const auto IntegerClass =
+ requiredScalar(*Element, "integer_register_class", "calling convention");
+ const auto FloatingClass =
+ requiredScalar(*Element, "floating_register_class", "calling convention");
+ const yaml::Node *IntegerArguments =
+ requiredNode(*Element, "integer_argument_registers", "calling convention");
+ const yaml::Node *FloatingArguments =
+ requiredNode(*Element, "floating_argument_registers", "calling convention");
+ const yaml::Node *IntegerReturns =
+ requiredNode(*Element, "integer_return_registers", "calling convention");
+ const yaml::Node *FloatingReturns =
+ requiredNode(*Element, "floating_return_registers", "calling convention");
+ const auto PreservedMask = requiredScalar(*Element, "preserved_mask", "calling convention");
+ const yaml::Node *StackAlignment =
+ requiredNode(*Element, "stack_alignment_bytes", "calling convention");
+ const yaml::Node *StackArgumentAlignment =
+ requiredNode(*Element, "stack_argument_alignment_bytes", "calling convention");
+ const yaml::Node *ShadowSpace =
+ requiredNode(*Element, "shadow_space_bytes", "calling convention");
+ const yaml::Node *SupportsVarArgs =
+ requiredNode(*Element, "supports_varargs", "calling convention");
+ const yaml::Node *SupportsTailCalls =
+ requiredNode(*Element, "supports_tail_calls", "calling convention");
+ if (IDNode == nullptr || !Symbol || !Name || !IntegerClass || !FloatingClass ||
+ IntegerArguments == nullptr || FloatingArguments == nullptr ||
+ IntegerReturns == nullptr || FloatingReturns == nullptr || !PreservedMask ||
+ StackAlignment == nullptr || StackArgumentAlignment == nullptr ||
+ ShadowSpace == nullptr || SupportsVarArgs == nullptr || SupportsTailCalls == nullptr) {
+ return false;
+ }
+ const auto ID = unsignedInteger32(*IDNode, "calling convention id");
+ const auto IntegerArgumentList =
+ scalarSequence(*IntegerArguments, "calling convention integer arguments");
+ const auto FloatingArgumentList =
+ scalarSequence(*FloatingArguments, "calling convention floating arguments");
+ const auto IntegerReturnList =
+ scalarSequence(*IntegerReturns, "calling convention integer returns");
+ const auto FloatingReturnList =
+ scalarSequence(*FloatingReturns, "calling convention floating returns");
+ const auto StackAlignmentValue =
+ unsignedInteger32(*StackAlignment, "calling convention stack alignment");
+ const auto StackArgumentAlignmentValue =
+ unsignedInteger32(*StackArgumentAlignment, "calling convention stack argument alignment");
+ const auto ShadowSpaceValue =
+ unsignedInteger32(*ShadowSpace, "calling convention shadow space");
+ const auto SupportsVarArgsValue = boolean(*SupportsVarArgs, "calling convention varargs");
+ const auto SupportsTailCallsValue =
+ boolean(*SupportsTailCalls, "calling convention tail calls");
+ if (!ID || !IntegerArgumentList || !FloatingArgumentList || !IntegerReturnList ||
+ !FloatingReturnList || !StackAlignmentValue || !StackArgumentAlignmentValue ||
+ !ShadowSpaceValue || !SupportsVarArgsValue || !SupportsTailCallsValue) {
+ return false;
+ }
+ Result.push_back({
+ .ID = *ID,
+ .Symbol = *Symbol,
+ .Name = *Name,
+ .IntegerRegisterClassSymbol = *IntegerClass,
+ .FloatingRegisterClassSymbol = *FloatingClass,
+ .IntegerArgumentRegisterSymbols = *IntegerArgumentList,
+ .FloatingArgumentRegisterSymbols = *FloatingArgumentList,
+ .IntegerReturnRegisterSymbols = *IntegerReturnList,
+ .FloatingReturnRegisterSymbols = *FloatingReturnList,
+ .PreservedMaskName = *PreservedMask,
+ .StackAlignmentBytes = *StackAlignmentValue,
+ .StackArgumentAlignmentBytes = *StackArgumentAlignmentValue,
+ .ShadowSpaceBytes = *ShadowSpaceValue,
+ .SupportsVarArgs = *SupportsVarArgsValue,
+ .SupportsTailCalls = *SupportsTailCallsValue,
+ .Range = Element->range(),
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] std::optional<SourceOperand> parseOperand(const yaml::Node &Node) {
+ if (!requireMapping(&Node, "instruction operand"))
+ return std::nullopt;
+ if (!checkKeys(Node,
+ {"kinds", "role", "register_class", "register_bank", "immediate_predicate",
+ "memory_role", "default_tied_operand", "early_clobber", "optional",
+ "variadic_group", "allow_subreg", "name", "allowed_target_flags"},
+ "instruction operand")) {
+ return std::nullopt;
+ }
+ const yaml::Node *KindsNode = requiredNode(Node, "kinds", "instruction operand");
+ if (KindsNode == nullptr)
+ return std::nullopt;
+ const auto Kinds = scalarSequence(*KindsNode, "instruction operand kinds");
+ if (!Kinds)
+ return std::nullopt;
+
+ SourceOperand Result;
+ Result.Kinds = *Kinds;
+ Result.Range = Node.range();
+ if (const yaml::Node *Role = optionalNode(Node, "role")) {
+ const auto Value = scalar(*Role, "instruction operand role");
+ if (!Value)
+ return std::nullopt;
+ Result.RegisterRole = *Value;
+ }
+ if (const yaml::Node *RegisterClass = optionalNode(Node, "register_class")) {
+ const auto Value = scalar(*RegisterClass, "instruction operand register_class");
+ if (!Value)
+ return std::nullopt;
+ Result.RegisterClassSymbol = *Value;
+ }
+ if (const yaml::Node *RegisterBank = optionalNode(Node, "register_bank")) {
+ const auto Value = scalar(*RegisterBank, "instruction operand register_bank");
+ if (!Value)
+ return std::nullopt;
+ Result.RegisterBankSymbol = *Value;
+ }
+ if (const yaml::Node *Predicate = optionalNode(Node, "immediate_predicate")) {
+ const auto Value = scalar(*Predicate, "instruction operand immediate_predicate");
+ if (!Value)
+ return std::nullopt;
+ Result.ImmediatePredicateSymbol = *Value;
+ }
+ if (const yaml::Node *MemoryRole = optionalNode(Node, "memory_role")) {
+ const auto Value = scalar(*MemoryRole, "instruction operand memory_role");
+ if (!Value)
+ return std::nullopt;
+ Result.MemoryRole = *Value;
+ }
+ if (const yaml::Node *Tie = optionalNode(Node, "default_tied_operand")) {
+ const auto Value = unsignedInteger32(*Tie, "instruction operand default_tied_operand");
+ if (!Value)
+ return std::nullopt;
+ Result.DefaultTiedOperand = *Value;
+ }
+ if (const yaml::Node *EarlyClobber = optionalNode(Node, "early_clobber")) {
+ const auto Value = boolean(*EarlyClobber, "instruction operand early_clobber");
+ if (!Value)
+ return std::nullopt;
+ Result.EarlyClobber = *Value;
+ }
+ if (const yaml::Node *Optional = optionalNode(Node, "optional")) {
+ const auto Value = boolean(*Optional, "instruction operand optional");
+ if (!Value)
+ return std::nullopt;
+ Result.Optional = *Value;
+ }
+ if (const yaml::Node *VariadicGroup = optionalNode(Node, "variadic_group")) {
+ const auto Value = boolean(*VariadicGroup, "instruction operand variadic_group");
+ if (!Value)
+ return std::nullopt;
+ Result.VariadicGroup = *Value;
+ }
+ if (const yaml::Node *AllowSubReg = optionalNode(Node, "allow_subreg")) {
+ const auto Value = boolean(*AllowSubReg, "instruction operand allow_subreg");
+ if (!Value)
+ return std::nullopt;
+ Result.AllowSubReg = *Value;
+ }
+ if (const yaml::Node *Name = optionalNode(Node, "name")) {
+ const auto Value = scalar(*Name, "instruction operand name");
+ if (!Value)
+ return std::nullopt;
+ Result.NameSymbol = *Value;
+ }
+ if (const yaml::Node *TargetFlags = optionalNode(Node, "allowed_target_flags")) {
+ const auto Value =
+ unsignedInteger32(*TargetFlags, "instruction operand allowed_target_flags");
+ if (!Value)
+ return std::nullopt;
+ Result.AllowedTargetFlags = *Value;
+ }
+ return Result;
+ }
+
+ [[nodiscard]] std::optional<std::vector<SourceOperand>> parseOperands(const yaml::Node &Node) {
+ if (!requireSequence(&Node, "instruction operands"))
+ return std::nullopt;
+ std::vector<SourceOperand> Result;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (Element == nullptr)
+ return std::nullopt;
+ const auto Operand = parseOperand(*Element);
+ if (!Operand)
+ return std::nullopt;
+ Result.push_back(*Operand);
+ }
+ return Result;
+ }
+
+ [[nodiscard]] std::optional<SourceImplicitRegister>
+ parseImplicitRegister(const yaml::Node &Node, std::string_view Context) {
+ if (!requireMapping(&Node, Context))
+ return std::nullopt;
+ if (!checkKeys(Node, {"register", "early_clobber", "renamable"}, Context))
+ return std::nullopt;
+ const auto Register = requiredScalar(Node, "register", Context);
+ if (!Register)
+ return std::nullopt;
+ SourceImplicitRegister Result{
+ .RegisterSymbol = *Register,
+ .Range = Node.range(),
+ };
+ if (const yaml::Node *EarlyClobber = optionalNode(Node, "early_clobber")) {
+ const auto Value = boolean(*EarlyClobber, std::string(Context) + " early_clobber");
+ if (!Value)
+ return std::nullopt;
+ Result.EarlyClobber = *Value;
+ }
+ if (const yaml::Node *Renamable = optionalNode(Node, "renamable")) {
+ const auto Value = boolean(*Renamable, std::string(Context) + " renamable");
+ if (!Value)
+ return std::nullopt;
+ Result.Renamable = *Value;
+ }
+ return Result;
+ }
+
+ [[nodiscard]] std::optional<std::vector<SourceImplicitRegister>>
+ parseImplicitRegisters(const yaml::Node &Node, std::string_view Context) {
+ if (!requireSequence(&Node, Context))
+ return std::nullopt;
+ std::vector<SourceImplicitRegister> Result;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (Element == nullptr)
+ return std::nullopt;
+ const auto Register = parseImplicitRegister(*Element, Context);
+ if (!Register)
+ return std::nullopt;
+ Result.push_back(*Register);
+ }
+ return Result;
+ }
+
+ [[nodiscard]] bool parseInstructions(const yaml::Node &Node,
+ std::vector<SourceInstruction> &Result) {
+ if (!requireSequence(&Node, "instructions"))
+ return false;
+ for (const yaml::NodeID ElementID : Node.sequence()) {
+ const yaml::Node *Element = Input.node(ElementID);
+ if (!requireMapping(Element, "instruction"))
+ return false;
+ if (!checkKeys(*Element,
+ {"id", "symbol", "name", "assembly_mnemonic", "operands", "num_explicit_defs",
+ "variadic", "implicit_defs", "implicit_uses", "allow_extra_implicit_operands",
+ "flags", "scheduling_class", "encoding_category", "assembly_format",
+ "requires_memory_operands", "owner", "deadline"},
+ "instruction")) {
+ return false;
+ }
+ const yaml::Node *IDNode = requiredNode(*Element, "id", "instruction");
+ const auto Symbol = requiredScalar(*Element, "symbol", "instruction");
+ const auto Name = requiredScalar(*Element, "name", "instruction");
+ const yaml::Node *OperandsNode = requiredNode(*Element, "operands", "instruction");
+ const yaml::Node *DefsNode = requiredNode(*Element, "num_explicit_defs", "instruction");
+ const yaml::Node *FlagsNode = requiredNode(*Element, "flags", "instruction");
+ if (IDNode == nullptr || !Symbol || !Name || OperandsNode == nullptr || DefsNode == nullptr ||
+ FlagsNode == nullptr) {
+ return false;
+ }
+ const auto ID = unsignedInteger32(*IDNode, "instruction id");
+ const auto Defs = unsignedInteger32(*DefsNode, "instruction num_explicit_defs");
+ const auto Operands = parseOperands(*OperandsNode);
+ const auto Flags = scalarSequence(*FlagsNode, "instruction flags");
+ if (!ID || !Defs || !Operands || !Flags)
+ return false;
+ SourceInstruction Instruction;
+ Instruction.ID = *ID;
+ Instruction.Symbol = *Symbol;
+ Instruction.Name = *Name;
+ Instruction.Operands = *Operands;
+ Instruction.NumExplicitDefs = *Defs;
+ Instruction.Flags = *Flags;
+ Instruction.Range = Element->range();
+ if (const yaml::Node *Mnemonic = optionalNode(*Element, "assembly_mnemonic")) {
+ const auto Value = scalar(*Mnemonic, "instruction assembly_mnemonic");
+ if (!Value)
+ return false;
+ Instruction.AssemblyMnemonic = *Value;
+ }
+ if (const yaml::Node *SchedulingClass = optionalNode(*Element, "scheduling_class")) {
+ const auto Value = scalar(*SchedulingClass, "instruction scheduling_class");
+ if (!Value)
+ return false;
+ Instruction.SchedulingClassSymbol = *Value;
+ }
+ if (const yaml::Node *EncodingCategory = optionalNode(*Element, "encoding_category")) {
+ const auto Value = scalar(*EncodingCategory, "instruction encoding_category");
+ if (!Value)
+ return false;
+ Instruction.EncodingCategorySymbol = *Value;
+ }
+ if (const yaml::Node *AssemblyFormat = optionalNode(*Element, "assembly_format")) {
+ const auto Value = scalar(*AssemblyFormat, "instruction assembly_format");
+ if (!Value)
+ return false;
+ Instruction.AssemblyFormatSymbol = *Value;
+ }
+ if (const yaml::Node *Variadic = optionalNode(*Element, "variadic")) {
+ const auto Value = boolean(*Variadic, "instruction variadic");
+ if (!Value)
+ return false;
+ Instruction.Variadic = *Value;
+ }
+ if (const yaml::Node *ImplicitDefs = optionalNode(*Element, "implicit_defs")) {
+ const auto Values = parseImplicitRegisters(*ImplicitDefs, "instruction implicit_defs");
+ if (!Values)
+ return false;
+ Instruction.ImplicitDefs = *Values;
+ }
+ if (const yaml::Node *ImplicitUses = optionalNode(*Element, "implicit_uses")) {
+ const auto Values = parseImplicitRegisters(*ImplicitUses, "instruction implicit_uses");
+ if (!Values)
+ return false;
+ Instruction.ImplicitUses = *Values;
+ }
+ if (const yaml::Node *AllowExtra = optionalNode(*Element, "allow_extra_implicit_operands")) {
+ const auto Value = boolean(*AllowExtra, "instruction allow_extra_implicit_operands");
+ if (!Value)
+ return false;
+ Instruction.AllowExtraImplicitOperands = *Value;
+ }
+ if (const yaml::Node *MemoryOperands = optionalNode(*Element, "requires_memory_operands")) {
+ const auto Value = boolean(*MemoryOperands, "instruction requires_memory_operands");
+ if (!Value)
+ return false;
+ Instruction.RequiresMemoryOperands = *Value;
+ }
+ if (const yaml::Node *Owner = optionalNode(*Element, "owner")) {
+ const auto Value = scalar(*Owner, "instruction owner");
+ if (!Value)
+ return false;
+ Instruction.Owner = *Value;
+ }
+ if (const yaml::Node *Deadline = optionalNode(*Element, "deadline")) {
+ const auto Value = scalar(*Deadline, "instruction deadline");
+ if (!Value)
+ return false;
+ Instruction.Deadline = *Value;
+ }
+ Result.push_back(std::move(Instruction));
+ }
+ return true;
+ }
+
[[nodiscard]] const yaml::Node *optionalNode(const yaml::Node &Mapping,
std::string_view Key) const noexcept {
const std::optional<yaml::NodeID> Value = Mapping.mappingValue(Key);
@@ -694,6 +1588,146 @@ private:
std::vector<TextDiagnostic> Diagnostics;
};
+[[nodiscard]] std::optional<std::string_view>
+operandKindEnumerator(std::string_view Value) noexcept {
+ static constexpr std::array<std::pair<std::string_view, std::string_view>, 22> Kinds = {{
+ {"register", "Register"},
+ {"immediate", "Immediate"},
+ {"integer_immediate", "IntegerImmediate"},
+ {"floating_immediate", "FloatingImmediate"},
+ {"basic_block", "BasicBlock"},
+ {"frame_index", "FrameIndex"},
+ {"constant_pool_index", "ConstantPoolIndex"},
+ {"jump_table_index", "JumpTableIndex"},
+ {"global_symbol", "GlobalSymbol"},
+ {"external_symbol", "ExternalSymbol"},
+ {"block_address", "BlockAddress"},
+ {"register_mask", "RegisterMask"},
+ {"live_out_register_mask", "LiveOutRegisterMask"},
+ {"metadata", "Metadata"},
+ {"target_index", "TargetIndex"},
+ {"mc_symbol", "MCSymbol"},
+ {"cfi_index", "CFIIndex"},
+ {"intrinsic", "Intrinsic"},
+ {"predicate", "Predicate"},
+ {"shuffle_mask", "ShuffleMask"},
+ {"debug_instr_ref", "DebugInstrRef"},
+ {"lane_mask", "LaneMask"},
+ }};
+ const auto Position = std::ranges::find(Kinds, Value, &decltype(Kinds)::value_type::first);
+ if (Position == Kinds.end())
+ return std::nullopt;
+ return Position->second;
+}
+
+[[nodiscard]] std::optional<std::string_view>
+registerRoleEnumerator(std::string_view Value) noexcept {
+ if (Value == "none")
+ return "None";
+ if (Value == "def")
+ return "Def";
+ if (Value == "use")
+ return "Use";
+ if (Value == "def_use")
+ return "DefUse";
+ return std::nullopt;
+}
+
+[[nodiscard]] std::optional<std::string_view>
+memoryRoleEnumerator(std::string_view Value) noexcept {
+ if (Value == "none")
+ return "None";
+ if (Value == "address")
+ return "Address";
+ if (Value == "value")
+ return "Value";
+ if (Value == "size")
+ return "Size";
+ if (Value == "ordering")
+ return "Ordering";
+ return std::nullopt;
+}
+
+[[nodiscard]] std::optional<std::string_view>
+instructionFlagEnumerator(std::string_view Value) noexcept {
+ static constexpr std::array<std::pair<std::string_view, std::string_view>, 23> Flags = {{
+ {"terminator", "Terminator"},
+ {"branch", "Branch"},
+ {"indirect_branch", "IndirectBranch"},
+ {"call", "Call"},
+ {"return", "Return"},
+ {"barrier", "Barrier"},
+ {"may_load", "MayLoad"},
+ {"may_store", "MayStore"},
+ {"has_side_effects", "HasSideEffects"},
+ {"pseudo", "Pseudo"},
+ {"meta", "Meta"},
+ {"allowed_pre_legalizer", "AllowedPreLegalizer"},
+ {"produces_code", "ProducesCode"},
+ {"commutable", "Commutable"},
+ {"rematerializable", "Rematerializable"},
+ {"predicable", "Predicable"},
+ {"not_duplicable", "NotDuplicable"},
+ {"convergent", "Convergent"},
+ {"unmodeled_side_effects", "UnmodeledSideEffects"},
+ {"phi", "Phi"},
+ {"fake_use", "FakeUse"},
+ {"generic", "Generic"},
+ {"terminal_emission", "TerminalEmission"},
+ }};
+ const auto Position = std::ranges::find(Flags, Value, &decltype(Flags)::value_type::first);
+ if (Position == Flags.end())
+ return std::nullopt;
+ return Position->second;
+}
+
+[[nodiscard]] std::optional<std::string_view>
+pseudoOwnerEnumerator(std::string_view Value) noexcept {
+ static constexpr std::array<std::pair<std::string_view, std::string_view>, 11> Owners = {{
+ {"none", "None"},
+ {"legalizer", "Legalizer"},
+ {"register_bank_select", "RegisterBankSelect"},
+ {"instruction_select", "InstructionSelect"},
+ {"phi_elimination", "PhiElimination"},
+ {"two_address_rewrite", "TwoAddressRewrite"},
+ {"register_allocator", "RegisterAllocator"},
+ {"pre_pei_expansion", "PrePEIExpansion"},
+ {"prologue_epilogue_insertion", "PrologueEpilogueInsertion"},
+ {"post_ra_expansion", "PostRAExpansion"},
+ {"finalization", "Finalization"},
+ }};
+ if (Value == "asm_printer")
+ return "AsmPrinter";
+ const auto Position = std::ranges::find(Owners, Value, &decltype(Owners)::value_type::first);
+ if (Position == Owners.end())
+ return std::nullopt;
+ return Position->second;
+}
+
+[[nodiscard]] std::optional<std::string_view>
+pipelineStageEnumerator(std::string_view Value) noexcept {
+ static constexpr std::array<std::pair<std::string_view, std::string_view>, 10> Stages = {{
+ {"machine_ir_input", "MachineIRInput"},
+ {"legalized", "Legalized"},
+ {"reg_bank_selected", "RegBankSelected"},
+ {"selected", "Selected"},
+ {"ssa_eliminated", "SSAEliminated"},
+ {"tied_ops_rewritten", "TiedOpsRewritten"},
+ {"register_allocated", "RegisterAllocated"},
+ {"frame_finalized", "FrameFinalized"},
+ {"post_ra_finalized", "PostRAFinalized"},
+ {"emission", "Emission"},
+ }};
+ const auto Position = std::ranges::find(Stages, Value, &decltype(Stages)::value_type::first);
+ if (Position == Stages.end())
+ return std::nullopt;
+ return Position->second;
+}
+
+[[nodiscard]] bool isPowerOfTwo(std::uint64_t Value) noexcept {
+ return Value != 0 && (Value & (Value - 1U)) == 0;
+}
+
class ModelNormalizer final {
public:
ModelNormalizer(const DescriptionAST &Input, std::string SourceName)
@@ -706,11 +1740,26 @@ public:
NormalizedTargetModel Result;
Result.Target = Input.Target;
Result.RegisterUnits = Input.RegisterUnits;
+ Result.MIROperandFlags = Input.MIROperandFlags;
if (!normalizeRegisterUnits() || !normalizeRegisters(Result) ||
!normalizeRegisterBanks(Result) || !normalizeRegisterClasses(Result) ||
- !normalizeRegisterMasks(Result)) {
+ !normalizeRegisterMasks(Result) || !normalizeMIROperandFlags(Result) ||
+ !normalizeCallingConventions(Result) || !normalizeNamedOperands(Result) ||
+ !normalizeImmediatePredicates(Result) || !normalizeSchedulingClasses(Result) ||
+ !normalizeEncodingCategories(Result) || !normalizeAssemblyFormats(Result) ||
+ !normalizeInstructions(Result)) {
return std::nullopt;
}
+ std::ranges::sort(Result.RegisterUnits, {}, &SourceRegisterUnit::ID);
+ std::ranges::sort(Result.Registers, {}, &NormalizedRegister::ID);
+ std::ranges::sort(Result.RegisterBanks, {}, &NormalizedRegisterBank::ID);
+ std::ranges::sort(Result.RegisterClasses, {}, &NormalizedRegisterClass::ID);
+ std::ranges::sort(Result.CallingConventions, {}, &NormalizedCallingConvention::ID);
+ std::ranges::sort(Result.NamedOperands, {}, &NormalizedNamedOperand::ID);
+ std::ranges::sort(Result.ImmediatePredicates, {}, &NormalizedImmediatePredicate::ID);
+ std::ranges::sort(Result.SchedulingClasses, {}, &NormalizedSchedulingClass::ID);
+ std::ranges::sort(Result.EncodingCategories, {}, &NormalizedEncodingCategory::ID);
+ std::ranges::sort(Result.AssemblyFormats, {}, &NormalizedAssemblyFormat::ID);
return Result;
}
@@ -946,12 +1995,597 @@ private:
return true;
}
+ [[nodiscard]] bool normalizeMIROperandFlags(NormalizedTargetModel &Result) {
+ const SourceMIROperandFlags &Flags = Result.MIROperandFlags;
+ if (Flags.Direct.empty() != (Flags.DirectMask == 0)) {
+ fail(Flags.Range, "direct MIR operand flags and their direct mask must be present together");
+ return false;
+ }
+
+ std::unordered_set<std::string> Names;
+ std::unordered_set<std::uint32_t> DirectValues;
+ for (const SourceMIROperandFlag &Flag : Flags.Direct) {
+ if (Flag.Name.empty() || Flag.Value == 0 || (Flag.Value & ~Flags.DirectMask) != 0 ||
+ !Names.insert(Flag.Name).second || !DirectValues.insert(Flag.Value).second) {
+ fail(Flag.Range,
+ "direct MIR operand flags require unique names and nonzero values inside the mask");
+ return false;
+ }
+ }
+
+ std::uint32_t BitmaskValues = 0;
+ for (const SourceMIROperandFlag &Flag : Flags.Bitmask) {
+ if (Flag.Name.empty() || !std::has_single_bit(Flag.Value) ||
+ (Flag.Value & Flags.DirectMask) != 0 || !Names.insert(Flag.Name).second ||
+ (BitmaskValues & Flag.Value) != 0) {
+ fail(Flag.Range,
+ "bitmask MIR operand flags require unique names and distinct bits outside the "
+ "direct mask");
+ return false;
+ }
+ BitmaskValues |= Flag.Value;
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeCallingConventions(NormalizedTargetModel &Result) {
+ if (Input.CallingConventions.empty()) {
+ fail(Input.Target.Range, "target description requires at least one calling convention");
+ return false;
+ }
+ std::unordered_set<std::uint32_t> IDs;
+ std::unordered_set<std::string> Symbols;
+ std::unordered_set<std::string> Names;
+ for (const SourceCallingConvention &Convention : Input.CallingConventions) {
+ const auto IntegerClass = ClassBySymbol.find(Convention.IntegerRegisterClassSymbol);
+ const auto FloatingClass = ClassBySymbol.find(Convention.FloatingRegisterClassSymbol);
+ const auto PreservedMask = std::ranges::find(
+ Result.RegisterMasks, Convention.PreservedMaskName, &NormalizedRegisterMask::Name);
+ if (Convention.ID == 0 || !isIdentifier(Convention.Symbol) || Convention.Name.empty() ||
+ IntegerClass == ClassBySymbol.end() || FloatingClass == ClassBySymbol.end() ||
+ PreservedMask == Result.RegisterMasks.end() || Convention.StackAlignmentBytes == 0 ||
+ Convention.StackArgumentAlignmentBytes == 0 ||
+ !isPowerOfTwo(Convention.StackAlignmentBytes) ||
+ !isPowerOfTwo(Convention.StackArgumentAlignmentBytes) ||
+ Convention.StackAlignmentBytes % Convention.StackArgumentAlignmentBytes != 0 ||
+ Convention.ShadowSpaceBytes % Convention.StackArgumentAlignmentBytes != 0 ||
+ !IDs.insert(Convention.ID).second || !Symbols.insert(Convention.Symbol).second ||
+ !Names.insert(Convention.Name).second) {
+ fail(
+ Convention.Range,
+ "calling conventions require unique IDs, symbols, names, declared register classes and "
+ "preserved masks, compatible power-of-two stack alignments, and aligned shadow space");
+ return false;
+ }
+ const auto IntegerClassDescription = std::ranges::find(
+ Result.RegisterClasses, IntegerClass->second, &NormalizedRegisterClass::ID);
+ const auto FloatingClassDescription = std::ranges::find(
+ Result.RegisterClasses, FloatingClass->second, &NormalizedRegisterClass::ID);
+ if (IntegerClassDescription == Result.RegisterClasses.end() ||
+ FloatingClassDescription == Result.RegisterClasses.end()) {
+ fail(Convention.Range, "calling convention register classes must resolve");
+ return false;
+ }
+ const auto normalizeRegisterList = [this,
+ &Convention](const std::vector<std::string> &Symbols,
+ const NormalizedRegisterClass &RegisterClass,
+ std::vector<std::uint32_t> &Output) {
+ std::unordered_set<std::uint32_t> Registers;
+ for (const std::string &Symbol : Symbols) {
+ const auto Register = RegisterBySymbol.find(Symbol);
+ if (Register == RegisterBySymbol.end() ||
+ !std::ranges::contains(RegisterClass.Registers, Register->second) ||
+ !Registers.insert(Register->second).second) {
+ fail(Convention.Range,
+ "calling convention register lists must contain distinct members of their class");
+ return false;
+ }
+ Output.push_back(Register->second);
+ }
+ return true;
+ };
+ NormalizedCallingConvention Normalized{
+ .ID = Convention.ID,
+ .Symbol = Convention.Symbol,
+ .Name = Convention.Name,
+ .IntegerRegisterClass = IntegerClass->second,
+ .FloatingRegisterClass = FloatingClass->second,
+ .IntegerArgumentRegisters = {},
+ .FloatingArgumentRegisters = {},
+ .IntegerReturnRegisters = {},
+ .FloatingReturnRegisters = {},
+ .PreservedMaskName = Convention.PreservedMaskName,
+ .StackAlignmentBytes = Convention.StackAlignmentBytes,
+ .StackArgumentAlignmentBytes = Convention.StackArgumentAlignmentBytes,
+ .ShadowSpaceBytes = Convention.ShadowSpaceBytes,
+ .SupportsVarArgs = Convention.SupportsVarArgs,
+ .SupportsTailCalls = Convention.SupportsTailCalls,
+ };
+ if (!normalizeRegisterList(Convention.IntegerArgumentRegisterSymbols,
+ *IntegerClassDescription, Normalized.IntegerArgumentRegisters) ||
+ !normalizeRegisterList(Convention.FloatingArgumentRegisterSymbols,
+ *FloatingClassDescription, Normalized.FloatingArgumentRegisters) ||
+ !normalizeRegisterList(Convention.IntegerReturnRegisterSymbols, *IntegerClassDescription,
+ Normalized.IntegerReturnRegisters) ||
+ !normalizeRegisterList(Convention.FloatingReturnRegisterSymbols,
+ *FloatingClassDescription, Normalized.FloatingReturnRegisters)) {
+ return false;
+ }
+ CallingConventionBySymbol.emplace(Convention.Symbol, Convention.ID);
+ Result.CallingConventions.push_back(std::move(Normalized));
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeNamedOperands(NormalizedTargetModel &Result) {
+ std::unordered_set<std::uint32_t> IDs;
+ std::unordered_set<std::string> Symbols;
+ std::unordered_set<std::string> Names;
+ for (const SourceNamedOperand &Operand : Input.NamedOperands) {
+ if (Operand.ID == 0 || !isIdentifier(Operand.Symbol) || Operand.Name.empty() ||
+ !IDs.insert(Operand.ID).second || !Symbols.insert(Operand.Symbol).second ||
+ !Names.insert(Operand.Name).second) {
+ fail(Operand.Range, "named operands require nonzero unique IDs, symbols, and names");
+ return false;
+ }
+ NamedOperandBySymbol.emplace(Operand.Symbol, Operand.ID);
+ Result.NamedOperands.push_back({
+ .ID = Operand.ID,
+ .Symbol = Operand.Symbol,
+ .Name = Operand.Name,
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeImmediatePredicates(NormalizedTargetModel &Result) {
+ std::unordered_set<std::uint32_t> IDs;
+ std::unordered_set<std::string> Symbols;
+ std::unordered_set<std::string> Names;
+ for (const SourceImmediatePredicate &Predicate : Input.ImmediatePredicates) {
+ const bool IsSigned = Predicate.Kind == "signed";
+ const bool IsUnsigned = Predicate.Kind == "unsigned";
+ if (Predicate.ID == 0 || !isIdentifier(Predicate.Symbol) || Predicate.Name.empty() ||
+ (!IsSigned && !IsUnsigned) || Predicate.Bits == 0 || Predicate.Bits > 63 ||
+ !isPowerOfTwo(Predicate.Alignment) || !IDs.insert(Predicate.ID).second ||
+ !Symbols.insert(Predicate.Symbol).second || !Names.insert(Predicate.Name).second) {
+ fail(Predicate.Range,
+ "immediate predicates require nonzero unique IDs, symbols, names, a signed or "
+ "unsigned kind, a width in [1, 63], and power-of-two alignment");
+ return false;
+ }
+ const std::int64_t PositiveLimit = std::int64_t{1} << (Predicate.Bits - 1U);
+ const std::int64_t Minimum = IsSigned ? -PositiveLimit : 0;
+ const std::int64_t Maximum =
+ IsSigned ? PositiveLimit - 1
+ : static_cast<std::int64_t>((std::uint64_t{1} << Predicate.Bits) - 1U);
+ std::unordered_set<std::int64_t> AllowedValues;
+ for (const std::int64_t Value : Predicate.AllowedValues) {
+ if (Value < Minimum || Value > Maximum || (Predicate.NonZero && Value == 0) ||
+ !AllowedValues.insert(Value).second) {
+ fail(Predicate.Range,
+ "immediate predicate allowed_values must be unique, in range, and satisfy nonzero");
+ return false;
+ }
+ }
+ ImmediatePredicateBySymbol.emplace(Predicate.Symbol, Predicate.ID);
+ Result.ImmediatePredicates.push_back({
+ .ID = Predicate.ID,
+ .Symbol = Predicate.Symbol,
+ .Name = Predicate.Name,
+ .IsSigned = IsSigned,
+ .Bits = Predicate.Bits,
+ .Alignment = Predicate.Alignment,
+ .NonZero = Predicate.NonZero,
+ .AllowedValues = Predicate.AllowedValues,
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeSchedulingClasses(NormalizedTargetModel &Result) {
+ if (Input.SchedulingClasses.empty()) {
+ fail(Input.Target.Range, "target description requires at least one scheduling class");
+ return false;
+ }
+ std::unordered_set<std::uint32_t> IDs;
+ std::unordered_set<std::string> Symbols;
+ std::unordered_set<std::string> Names;
+ for (const SourceSchedulingClass &Class : Input.SchedulingClasses) {
+ if (Class.ID == 0 || !isIdentifier(Class.Symbol) || Class.Name.empty() ||
+ Class.MicroOps == 0 || Class.Latency == 0 || !IDs.insert(Class.ID).second ||
+ !Symbols.insert(Class.Symbol).second || !Names.insert(Class.Name).second) {
+ fail(Class.Range,
+ "scheduling classes require nonzero unique IDs, symbols, names, micro-op counts, "
+ "and latencies");
+ return false;
+ }
+ SchedulingClassBySymbol.emplace(Class.Symbol, Class.ID);
+ Result.SchedulingClasses.push_back({
+ .ID = Class.ID,
+ .Symbol = Class.Symbol,
+ .Name = Class.Name,
+ .MicroOps = Class.MicroOps,
+ .Latency = Class.Latency,
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeEncodingCategories(NormalizedTargetModel &Result) {
+ if (Input.EncodingCategories.empty()) {
+ fail(Input.Target.Range, "target description requires at least one encoding category");
+ return false;
+ }
+ std::unordered_set<std::uint32_t> IDs;
+ std::unordered_set<std::string> Symbols;
+ std::unordered_set<std::string> Names;
+ for (const SourceEncodingCategory &Category : Input.EncodingCategories) {
+ if (Category.ID == 0 || !isIdentifier(Category.Symbol) || Category.Name.empty() ||
+ Category.SizeInBytes == 0 || !IDs.insert(Category.ID).second ||
+ !Symbols.insert(Category.Symbol).second || !Names.insert(Category.Name).second) {
+ fail(Category.Range,
+ "encoding categories require nonzero unique IDs, symbols, names, and sizes");
+ return false;
+ }
+ EncodingCategoryBySymbol.emplace(Category.Symbol, Category.ID);
+ Result.EncodingCategories.push_back({
+ .ID = Category.ID,
+ .Symbol = Category.Symbol,
+ .Name = Category.Name,
+ .SizeInBytes = Category.SizeInBytes,
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeAssemblyFormats(NormalizedTargetModel &Result) {
+ if (Input.AssemblyFormats.empty()) {
+ fail(Input.Target.Range, "target description requires at least one assembly format");
+ return false;
+ }
+ std::unordered_set<std::uint32_t> IDs;
+ std::unordered_set<std::string> Symbols;
+ std::unordered_set<std::string> Names;
+ for (const SourceAssemblyFormat &Format : Input.AssemblyFormats) {
+ if (Format.ID == 0 || !isIdentifier(Format.Symbol) || Format.Name.empty() ||
+ !IDs.insert(Format.ID).second || !Symbols.insert(Format.Symbol).second ||
+ !Names.insert(Format.Name).second) {
+ fail(Format.Range, "assembly formats require nonzero unique IDs, symbols, and names");
+ return false;
+ }
+ AssemblyFormatBySymbol.emplace(Format.Symbol, Format.ID);
+ Result.AssemblyFormats.push_back({
+ .ID = Format.ID,
+ .Symbol = Format.Symbol,
+ .Name = Format.Name,
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeOperand(const SourceOperand &InputOperand,
+ NormalizedOperand &Result) {
+ if (InputOperand.Kinds.empty()) {
+ fail(InputOperand.Range, "instruction operand requires at least one kind");
+ return false;
+ }
+ std::unordered_set<std::string> Kinds;
+ bool HasRegisterKind = false;
+ for (const std::string &Kind : InputOperand.Kinds) {
+ const auto Enumerator = operandKindEnumerator(Kind);
+ if (!Enumerator || !Kinds.insert(std::string(*Enumerator)).second) {
+ fail(InputOperand.Range, "instruction operand kinds must be known and unique");
+ return false;
+ }
+ HasRegisterKind |= *Enumerator == "Register";
+ Result.Kinds.push_back(std::string(*Enumerator));
+ }
+
+ const auto Role = registerRoleEnumerator(InputOperand.RegisterRole);
+ const auto MemoryRole = memoryRoleEnumerator(InputOperand.MemoryRole);
+ if (!Role || !MemoryRole || HasRegisterKind != (*Role != "None")) {
+ fail(InputOperand.Range,
+ "instruction operand register role and memory role must match its declared kinds");
+ return false;
+ }
+ Result.RegisterRole = std::string(*Role);
+ Result.MemoryRole = std::string(*MemoryRole);
+
+ const bool IsDefRole = *Role == "Def" || *Role == "DefUse";
+ const bool IsRegisterOnly = Result.Kinds.size() == 1 && Result.Kinds.front() == "Register";
+ if (IsDefRole && !IsRegisterOnly) {
+ fail(InputOperand.Range, "register definition operands must accept only registers");
+ return false;
+ }
+ if (!HasRegisterKind && (InputOperand.RegisterClassSymbol || InputOperand.RegisterBankSymbol ||
+ InputOperand.EarlyClobber || InputOperand.AllowSubReg)) {
+ fail(InputOperand.Range, "non-register operands cannot have register constraints");
+ return false;
+ }
+ if (InputOperand.EarlyClobber && !IsDefRole) {
+ fail(InputOperand.Range, "early-clobber operands must define a register");
+ return false;
+ }
+
+ if (InputOperand.RegisterClassSymbol) {
+ const auto Position = ClassBySymbol.find(*InputOperand.RegisterClassSymbol);
+ if (Position == ClassBySymbol.end()) {
+ fail(InputOperand.Range, "instruction operand names an unknown register class");
+ return false;
+ }
+ Result.RegisterClass = Position->second;
+ }
+ if (InputOperand.RegisterBankSymbol) {
+ const auto Position = BankBySymbol.find(*InputOperand.RegisterBankSymbol);
+ if (Position == BankBySymbol.end()) {
+ fail(InputOperand.Range, "instruction operand names an unknown register bank");
+ return false;
+ }
+ Result.RegisterBank = Position->second;
+ }
+ if (InputOperand.ImmediatePredicateSymbol) {
+ const auto Position = ImmediatePredicateBySymbol.find(*InputOperand.ImmediatePredicateSymbol);
+ if (Position == ImmediatePredicateBySymbol.end() || Result.Kinds.size() != 1 ||
+ Result.Kinds.front() != "Immediate") {
+ fail(InputOperand.Range,
+ "instruction immediate predicates must name a declaration and constrain an immediate");
+ return false;
+ }
+ Result.ImmediatePredicate = Position->second;
+ }
+ if (InputOperand.NameSymbol) {
+ const auto Position = NamedOperandBySymbol.find(*InputOperand.NameSymbol);
+ if (Position == NamedOperandBySymbol.end()) {
+ fail(InputOperand.Range, "instruction operand names an unknown named operand");
+ return false;
+ }
+ Result.Name = Position->second;
+ }
+
+ Result.DefaultTiedOperand = InputOperand.DefaultTiedOperand;
+ Result.EarlyClobber = InputOperand.EarlyClobber;
+ Result.Optional = InputOperand.Optional;
+ Result.VariadicGroup = InputOperand.VariadicGroup;
+ Result.AllowSubReg = InputOperand.AllowSubReg;
+ Result.AllowedTargetFlags = InputOperand.AllowedTargetFlags;
+ return true;
+ }
+
+ [[nodiscard]] bool
+ normalizeImplicitRegisters(const std::vector<SourceImplicitRegister> &InputRegisters, bool IsDef,
+ std::vector<NormalizedImplicitRegister> &Result) {
+ std::unordered_set<std::uint32_t> Registers;
+ for (const SourceImplicitRegister &Register : InputRegisters) {
+ const auto Position = RegisterBySymbol.find(Register.RegisterSymbol);
+ if (Position == RegisterBySymbol.end() || !Registers.insert(Position->second).second ||
+ (!IsDef && Register.EarlyClobber)) {
+ fail(Register.Range,
+ "implicit registers must be declared and unique; implicit uses cannot early-clobber");
+ return false;
+ }
+ Result.push_back({
+ .Register = Position->second,
+ .EarlyClobber = Register.EarlyClobber,
+ .Renamable = Register.Renamable,
+ });
+ }
+ return true;
+ }
+
+ [[nodiscard]] bool normalizeInstructions(NormalizedTargetModel &Result) {
+ if (Input.Instructions.empty()) {
+ fail(Input.Target.Range, "target description requires at least one instruction");
+ return false;
+ }
+ std::unordered_set<std::uint32_t> IDs;
+ std::unordered_set<std::string> Symbols;
+ std::unordered_set<std::string> Names;
+ for (const SourceInstruction &Instruction : Input.Instructions) {
+ const bool HasMnemonic = !Instruction.AssemblyMnemonic.empty();
+ const bool HasSchedulingClass = !Instruction.SchedulingClassSymbol.empty();
+ const bool HasEncodingCategory = !Instruction.EncodingCategorySymbol.empty();
+ const bool HasAssemblyFormat = !Instruction.AssemblyFormatSymbol.empty();
+ const bool HasAllMachineFacts =
+ HasMnemonic && HasSchedulingClass && HasEncodingCategory && HasAssemblyFormat;
+ const bool HasAnyMachineFact =
+ HasMnemonic || HasSchedulingClass || HasEncodingCategory || HasAssemblyFormat;
+ const bool IsPseudoSource =
+ std::ranges::find(Instruction.Flags, "pseudo") != Instruction.Flags.end();
+ const bool IsTerminalEmissionSource =
+ std::ranges::find(Instruction.Flags, "terminal_emission") != Instruction.Flags.end();
+ if (HasAnyMachineFact != HasAllMachineFacts ||
+ ((!IsPseudoSource || IsTerminalEmissionSource) && !HasAllMachineFacts)) {
+ fail(Instruction.Range,
+ "encoded instructions and terminal-emission pseudos require complete scheduling, "
+ "encoding, mnemonic, and assembly-format facts");
+ return false;
+ }
+ const auto SchedulingClass = SchedulingClassBySymbol.find(Instruction.SchedulingClassSymbol);
+ const auto EncodingCategory =
+ EncodingCategoryBySymbol.find(Instruction.EncodingCategorySymbol);
+ const auto AssemblyFormat = AssemblyFormatBySymbol.find(Instruction.AssemblyFormatSymbol);
+ const auto Owner = pseudoOwnerEnumerator(Instruction.Owner);
+ const auto Deadline = pipelineStageEnumerator(Instruction.Deadline);
+ if (Instruction.ID == 0 || !isIdentifier(Instruction.Symbol) || Instruction.Name.empty() ||
+ (HasMnemonic && !isAssemblyMnemonic(Instruction.AssemblyMnemonic)) ||
+ (HasSchedulingClass && SchedulingClass == SchedulingClassBySymbol.end()) ||
+ (HasEncodingCategory && EncodingCategory == EncodingCategoryBySymbol.end()) ||
+ (HasAssemblyFormat && AssemblyFormat == AssemblyFormatBySymbol.end()) || !Owner ||
+ !Deadline || !IDs.insert(Instruction.ID).second ||
+ !Symbols.insert(Instruction.Symbol).second || !Names.insert(Instruction.Name).second) {
+ fail(Instruction.Range,
+ "instructions require nonzero unique IDs, symbols, lowercase assembly mnemonics, and "
+ "declared format IDs");
+ return false;
+ }
+ if (Instruction.NumExplicitDefs > Instruction.Operands.size()) {
+ fail(Instruction.Range, "instruction explicit def count is outside its operand range");
+ return false;
+ }
+
+ NormalizedInstruction Normalized{
+ .ID = Instruction.ID,
+ .Symbol = Instruction.Symbol,
+ .Name = Instruction.Name,
+ .AssemblyMnemonic = Instruction.AssemblyMnemonic,
+ .Operands = {},
+ .NumExplicitDefs = Instruction.NumExplicitDefs,
+ .Variadic = Instruction.Variadic,
+ .ImplicitDefs = {},
+ .ImplicitUses = {},
+ .AllowExtraImplicitOperands = Instruction.AllowExtraImplicitOperands,
+ .Flags = {},
+ .SchedulingClass = HasSchedulingClass ? SchedulingClass->second : 0,
+ .EncodingCategory = HasEncodingCategory ? EncodingCategory->second : 0,
+ .AssemblyFormat = HasAssemblyFormat ? AssemblyFormat->second : 0,
+ .RequiresMemoryOperands = Instruction.RequiresMemoryOperands,
+ .Owner = std::string(*Owner),
+ .Deadline = std::string(*Deadline),
+ };
+ Normalized.Operands.reserve(Instruction.Operands.size());
+ bool SawOptional = false;
+ for (std::size_t Index = 0; Index < Instruction.Operands.size(); ++Index) {
+ NormalizedOperand Operand;
+ if (!normalizeOperand(Instruction.Operands[Index], Operand))
+ return false;
+ const bool IsDef = Operand.RegisterRole == "Def" || Operand.RegisterRole == "DefUse";
+ if ((Index < Instruction.NumExplicitDefs) != IsDef) {
+ fail(Instruction.Operands[Index].Range,
+ "instruction explicit register definitions must form the leading operand prefix");
+ return false;
+ }
+ if (Instruction.Variadic && Operand.Optional) {
+ fail(Instruction.Operands[Index].Range,
+ "variadic instructions cannot contain optional operands");
+ return false;
+ }
+ if (Operand.Optional)
+ SawOptional = true;
+ else if (SawOptional && !Operand.VariadicGroup) {
+ fail(Instruction.Operands[Index].Range,
+ "optional instruction operands must form a trailing suffix");
+ return false;
+ }
+ if (Operand.VariadicGroup &&
+ (!Instruction.Variadic || Index + 1 != Instruction.Operands.size())) {
+ fail(Instruction.Operands[Index].Range,
+ "a variadic operand group must be the final instruction operand");
+ return false;
+ }
+ if (Operand.VariadicGroup && (Operand.Optional || Operand.DefaultTiedOperand)) {
+ fail(Instruction.Operands[Index].Range,
+ "a variadic operand group cannot be optional or have a default tie");
+ return false;
+ }
+ Normalized.Operands.push_back(std::move(Operand));
+ }
+ if (Instruction.Variadic &&
+ (Normalized.Operands.empty() || !Normalized.Operands.back().VariadicGroup)) {
+ fail(Instruction.Range, "variadic instructions require a final variadic operand group");
+ return false;
+ }
+ if (!Instruction.Variadic &&
+ std::ranges::any_of(Normalized.Operands, [](const NormalizedOperand &Operand) {
+ return Operand.VariadicGroup;
+ })) {
+ fail(Instruction.Range, "non-variadic instructions cannot have a variadic operand group");
+ return false;
+ }
+
+ std::unordered_set<std::uint32_t> NamedOperands;
+ for (std::size_t Index = 0; Index < Normalized.Operands.size(); ++Index) {
+ const NormalizedOperand &Operand = Normalized.Operands[Index];
+ if (Operand.Name && !NamedOperands.insert(*Operand.Name).second) {
+ fail(Instruction.Operands[Index].Range,
+ "instruction operands must use distinct named operand IDs");
+ return false;
+ }
+ if (!Operand.DefaultTiedOperand)
+ continue;
+ const std::uint32_t OtherIndex = *Operand.DefaultTiedOperand;
+ if (OtherIndex >= Normalized.Operands.size() || OtherIndex == Index ||
+ !Normalized.Operands[OtherIndex].DefaultTiedOperand ||
+ *Normalized.Operands[OtherIndex].DefaultTiedOperand != Index) {
+ fail(Instruction.Operands[Index].Range,
+ "instruction default ties must be reciprocal and in range");
+ return false;
+ }
+ const NormalizedOperand &Other = Normalized.Operands[OtherIndex];
+ const bool IsRegisterOnly =
+ Operand.Kinds.size() == 1 && Operand.Kinds.front() == "Register";
+ const bool OtherIsRegisterOnly =
+ Other.Kinds.size() == 1 && Other.Kinds.front() == "Register";
+ const bool IsDefUsePair = (Operand.RegisterRole == "Def" && Other.RegisterRole == "Use") ||
+ (Operand.RegisterRole == "Use" && Other.RegisterRole == "Def");
+ if (Operand.Optional || Other.Optional || !IsRegisterOnly || !OtherIsRegisterOnly ||
+ !IsDefUsePair) {
+ fail(Instruction.Operands[Index].Range,
+ "instruction default ties must connect required register defs and uses");
+ return false;
+ }
+ }
+
+ std::unordered_set<std::string> Flags;
+ for (const std::string &Flag : Instruction.Flags) {
+ const auto Enumerator = instructionFlagEnumerator(Flag);
+ if (!Enumerator || !Flags.insert(std::string(*Enumerator)).second) {
+ fail(Instruction.Range, "instruction flags must be known and unique");
+ return false;
+ }
+ Normalized.Flags.push_back(std::string(*Enumerator));
+ }
+ const auto HasFlag = [&Normalized](std::string_view Flag) {
+ return std::ranges::find(Normalized.Flags, Flag) != Normalized.Flags.end();
+ };
+ const bool IsTerminator = HasFlag("Terminator");
+ if ((HasFlag("Branch") || HasFlag("Return") || HasFlag("Barrier") ||
+ HasFlag("IndirectBranch")) &&
+ !IsTerminator) {
+ fail(Instruction.Range, "branch, return, and barrier instructions must be terminators");
+ return false;
+ }
+ if (HasFlag("IndirectBranch") && !HasFlag("Branch")) {
+ fail(Instruction.Range, "indirect branch instructions must also be branches");
+ return false;
+ }
+ const bool IsPseudo = HasFlag("Pseudo");
+ const bool IsMeta = HasFlag("Meta");
+ if ((IsMeta && (!IsPseudo || HasFlag("ProducesCode"))) ||
+ (!IsPseudo && Normalized.Owner != "None") ||
+ (IsPseudo && !IsMeta && Normalized.Owner == "None") ||
+ (HasFlag("TerminalEmission") &&
+ (!IsPseudo || Normalized.Owner != "AsmPrinter" || !HasFlag("ProducesCode")))) {
+ fail(Instruction.Range, "instruction pseudo flags, owner, and deadline are inconsistent");
+ return false;
+ }
+ if (Normalized.RequiresMemoryOperands && !HasFlag("MayLoad") && !HasFlag("MayStore")) {
+ fail(Instruction.Range,
+ "memory operand requirements need a load or store instruction flag");
+ return false;
+ }
+ if (!normalizeImplicitRegisters(Instruction.ImplicitDefs, true, Normalized.ImplicitDefs) ||
+ !normalizeImplicitRegisters(Instruction.ImplicitUses, false, Normalized.ImplicitUses)) {
+ return false;
+ }
+ Result.Instructions.push_back(std::move(Normalized));
+ }
+ std::ranges::sort(Result.Instructions, {}, &NormalizedInstruction::ID);
+ return true;
+ }
+
const DescriptionAST &Input;
std::string SourceName;
std::unordered_map<std::string, std::uint32_t> UnitBySymbol;
std::unordered_map<std::string, std::uint32_t> RegisterBySymbol;
std::unordered_map<std::string, std::uint32_t> BankBySymbol;
std::unordered_map<std::string, std::uint32_t> ClassBySymbol;
+ std::unordered_map<std::string, std::uint32_t> NamedOperandBySymbol;
+ std::unordered_map<std::string, std::uint32_t> ImmediatePredicateBySymbol;
+ std::unordered_map<std::string, std::uint32_t> SchedulingClassBySymbol;
+ std::unordered_map<std::string, std::uint32_t> EncodingCategoryBySymbol;
+ std::unordered_map<std::string, std::uint32_t> AssemblyFormatBySymbol;
+ std::unordered_map<std::string, std::uint32_t> CallingConventionBySymbol;
std::vector<TextDiagnostic> Diagnostics;
};
@@ -1024,12 +2658,152 @@ private:
return Output.str();
}
+[[nodiscard]] std::string emitInstructionHeader(const NormalizedTargetModel &Model,
+ std::string_view SourceName) {
+ const std::string Guard = "RCC_TARGET_" + upperIdentifier(Model.Target.Namespace) + "_" +
+ upperIdentifier(Model.Target.Name) + "INSTRUCTIONS_H";
+ std::ostringstream Output;
+ Output << "// SPDX-License-Identifier: GPL-3.0-or-later\n\n";
+ Output << "// This file is generated by rcc-backend-descgen from ";
+ appendQuoted(Output, SourceName);
+ Output << ". Do not edit it manually.\n\n";
+ Output << "#ifndef " << Guard << "\n#define " << Guard << "\n\n";
+ Output << "#include <cstdint>\n\n";
+ Output << "namespace rcc::" << Model.Target.Namespace << " {\n\n";
+
+ Output << "enum class InstructionOpcode : std::uint32_t {\n Invalid = 0,\n";
+ for (const NormalizedInstruction &Instruction : Model.Instructions)
+ Output << " " << Instruction.Symbol << " = " << Instruction.ID << ",\n";
+ Output << "};\n\n";
+
+ Output << "enum class ImmediatePredicate : std::uint32_t {\n None = 0,\n";
+ for (const NormalizedImmediatePredicate &Predicate : Model.ImmediatePredicates)
+ Output << " " << Predicate.Symbol << " = " << Predicate.ID << ",\n";
+ Output << "};\n\n";
+
+ Output << "enum class NamedOperand : std::uint32_t {\n None = 0,\n";
+ for (const NormalizedNamedOperand &Operand : Model.NamedOperands)
+ Output << " " << Operand.Symbol << " = " << Operand.ID << ",\n";
+ Output << "};\n\n";
+
+ Output << "enum class SchedulingClass : std::uint32_t {\n None = 0,\n";
+ for (const NormalizedSchedulingClass &Class : Model.SchedulingClasses)
+ Output << " " << Class.Symbol << " = " << Class.ID << ",\n";
+ Output << "};\n\n";
+
+ Output << "enum class EncodingCategory : std::uint32_t {\n None = 0,\n";
+ for (const NormalizedEncodingCategory &Category : Model.EncodingCategories)
+ Output << " " << Category.Symbol << " = " << Category.ID << ",\n";
+ Output << "};\n\n";
+
+ Output << "enum class AssemblyFormat : std::uint32_t {\n None = 0,\n";
+ for (const NormalizedAssemblyFormat &Format : Model.AssemblyFormats)
+ Output << " " << Format.Symbol << " = " << Format.ID << ",\n";
+ Output << "};\n\n";
+
+ Output << "enum class CallingConvention : std::uint32_t {\n None = 0,\n";
+ for (const NormalizedCallingConvention &Convention : Model.CallingConventions)
+ Output << " " << Convention.Symbol << " = " << Convention.ID << ",\n";
+ Output << "};\n\n";
+
+ Output << "} // namespace rcc::" << Model.Target.Namespace << "\n\n";
+ Output << "#endif // " << Guard << "\n";
+ return Output.str();
+}
+
void emitRegisterIDList(std::ostream &Output, const NormalizedTargetModel &Model,
std::span<const std::uint32_t> IDs) {
for (const std::uint32_t ID : IDs)
Output << "PhysicalRegisterID::" << findRegister(Model, ID).Symbol << ", ";
}
+void emitPhysicalRegisterArray(std::ostream &Output, const NormalizedTargetModel &Model,
+ std::string_view Name, std::span<const std::uint32_t> IDs) {
+ Output << "const std::array<PhysicalRegister, " << IDs.size() << "> " << Name << " = {{\n";
+ for (const std::uint32_t ID : IDs)
+ Output << " physical(PhysicalRegisterID::" << findRegister(Model, ID).Symbol << "),\n";
+ Output << "}};\n";
+}
+
+void emitOperandDescriptor(std::ostream &Output, const NormalizedOperand &Operand) {
+ Output << " {.Kinds = OperandKindSet{";
+ for (const std::string &Kind : Operand.Kinds)
+ Output << "MachineOperand::Kind::" << Kind << ", ";
+ Output << "}, .RegisterRole = RegisterOperandRole::" << Operand.RegisterRole;
+ Output << ", .RegClass = ";
+ if (Operand.RegisterClass)
+ Output << "RegisterClassID::fromValue(" << *Operand.RegisterClass << ")";
+ else
+ Output << "std::nullopt";
+ Output << ", .RegBank = ";
+ if (Operand.RegisterBank)
+ Output << "RegisterBankID::fromValue(" << *Operand.RegisterBank << ")";
+ else
+ Output << "std::nullopt";
+ Output << ", .ImmediatePredicate = ";
+ if (Operand.ImmediatePredicate)
+ Output << "ImmediatePredicateID::fromValue(" << *Operand.ImmediatePredicate << ")";
+ else
+ Output << "std::nullopt";
+ Output << ", .MemoryRole = MemoryOperandRole::" << Operand.MemoryRole;
+ Output << ", .DefaultTiedOperand = ";
+ if (Operand.DefaultTiedOperand)
+ Output << "std::size_t{" << *Operand.DefaultTiedOperand << "}";
+ else
+ Output << "std::nullopt";
+ Output << ", .EarlyClobber = " << (Operand.EarlyClobber ? "true" : "false")
+ << ", .Optional = " << (Operand.Optional ? "true" : "false")
+ << ", .VariadicGroup = " << (Operand.VariadicGroup ? "true" : "false")
+ << ", .AllowSubReg = " << (Operand.AllowSubReg ? "true" : "false");
+ Output << ", .Name = ";
+ if (Operand.Name)
+ Output << "NamedOperandID::fromValue(" << *Operand.Name << ")";
+ else
+ Output << "std::nullopt";
+ Output << ", .TargetOperandType = std::nullopt, .AllowedTargetFlags = "
+ << Operand.AllowedTargetFlags << ", .TypeConstraint = std::nullopt},\n";
+}
+
+void emitImplicitRegisterList(std::ostream &Output, const NormalizedTargetModel &Model,
+ std::span<const NormalizedImplicitRegister> Registers) {
+ Output << "{";
+ for (const NormalizedImplicitRegister &Register : Registers) {
+ Output << "{physical(PhysicalRegisterID::" << findRegister(Model, Register.Register).Symbol
+ << "), " << (Register.EarlyClobber ? "true" : "false") << ", "
+ << (Register.Renamable ? "true" : "false") << "}, ";
+ }
+ Output << "}";
+}
+
+void emitImmediatePredicate(std::ostream &Output, const NormalizedImmediatePredicate &Predicate) {
+ const std::int64_t PositiveLimit = std::int64_t{1} << (Predicate.Bits - 1U);
+ const std::int64_t Minimum = Predicate.IsSigned ? -PositiveLimit : 0;
+ const std::int64_t Maximum =
+ Predicate.IsSigned
+ ? PositiveLimit - 1
+ : static_cast<std::int64_t>((std::uint64_t{1} << Predicate.Bits) - std::uint64_t{1});
+ Output << " {.ID = ImmediatePredicateID::fromValue(" << Predicate.ID << "), .Name = ";
+ appendQuoted(Output, Predicate.Name);
+ Output << ", .Predicate = [](const MachineOperand &Operand) {\n"
+ << " const auto *Value = Operand.getIf<std::int64_t>();\n"
+ << " return Value != nullptr && *Value >= " << Minimum
+ << " && *Value <= " << Maximum;
+ if (Predicate.Alignment != 1)
+ Output << " && (*Value % " << Predicate.Alignment << ") == 0";
+ if (Predicate.NonZero)
+ Output << " && *Value != 0";
+ if (!Predicate.AllowedValues.empty()) {
+ Output << " && (";
+ for (std::size_t Index = 0; Index < Predicate.AllowedValues.size(); ++Index) {
+ if (Index != 0)
+ Output << " || ";
+ Output << "*Value == " << Predicate.AllowedValues[Index];
+ }
+ Output << ")";
+ }
+ Output << ";\n }},\n";
+}
+
[[nodiscard]] std::string emitTargetTables(const NormalizedTargetModel &Model,
std::string_view SourceName) {
std::ostringstream Output;
@@ -1089,6 +2863,20 @@ void emitRegisterIDList(std::ostream &Output, const NormalizedTargetModel &Model
}
Output << " };\n}\n\n";
Output << "void appendGeneratedMIRTextInfo(TargetMIRTextInfoDesc &Description) {\n";
+ Output << " Description.DirectOperandFlagMask = " << Model.MIROperandFlags.DirectMask
+ << ";\n Description.DirectOperandFlags = {\n";
+ for (const SourceMIROperandFlag &Flag : Model.MIROperandFlags.Direct) {
+ Output << " {";
+ appendQuoted(Output, Flag.Name);
+ Output << ", " << Flag.Value << "},\n";
+ }
+ Output << " };\n Description.BitmaskOperandFlags = {\n";
+ for (const SourceMIROperandFlag &Flag : Model.MIROperandFlags.Bitmask) {
+ Output << " {";
+ appendQuoted(Output, Flag.Name);
+ Output << ", " << Flag.Value << "},\n";
+ }
+ Output << " };\n";
Output << " Description.RegisterMasks = {\n";
for (const NormalizedRegisterMask &Mask : Model.RegisterMasks) {
Output << " {";
@@ -1102,7 +2890,115 @@ void emitRegisterIDList(std::ostream &Output, const NormalizedTargetModel &Model
Output << " {physical(PhysicalRegisterID::" << Register.Symbol << "), "
<< Register.DwarfRegister << ", true},\n";
}
- Output << " };\n}\n";
+ Output << " };\n}\n\n";
+
+ Output
+ << "void appendGeneratedInstructionInfo(std::vector<rcc::InstructionDescriptor> "
+ "&Descriptors,\n"
+ << " std::vector<rcc::ImmediatePredicate> &Predicates) {\n"
+ << " Predicates = {\n";
+ for (const NormalizedImmediatePredicate &Predicate : Model.ImmediatePredicates)
+ emitImmediatePredicate(Output, Predicate);
+ Output << " };\n Descriptors = {\n";
+ for (const NormalizedInstruction &Instruction : Model.Instructions) {
+ Output << " {.InstructionOpcode = Opcode::fromValue(" << Instruction.ID << "), .Name = ";
+ appendQuoted(Output, Instruction.Name);
+ Output << ", .Operands = {\n";
+ for (const NormalizedOperand &Operand : Instruction.Operands)
+ emitOperandDescriptor(Output, Operand);
+ Output << " }, .NumExplicitDefs = " << Instruction.NumExplicitDefs
+ << ", .Variadic = " << (Instruction.Variadic ? "true" : "false")
+ << ", .MinimumVariadicRepetitions = 0, .ImplicitDefs = ";
+ emitImplicitRegisterList(Output, Model, Instruction.ImplicitDefs);
+ Output << ", .ImplicitUses = ";
+ emitImplicitRegisterList(Output, Model, Instruction.ImplicitUses);
+ Output << ", .AllowExtraImplicitOperands = "
+ << (Instruction.AllowExtraImplicitOperands ? "true" : "false")
+ << ", .Flags = InstructionFlags{";
+ for (const std::string &Flag : Instruction.Flags)
+ Output << "InstructionFlag::" << Flag << ", ";
+ Output << "}, .SchedulingClass = SchedulingClassID::fromValue(" << Instruction.SchedulingClass
+ << "), .EncodingCategory = EncodingCategoryID::fromValue("
+ << Instruction.EncodingCategory << "), .AssemblyFormat = AssemblyFormatID::fromValue("
+ << Instruction.AssemblyFormat << "), .RequiresMemoryOperands = "
+ << (Instruction.RequiresMemoryOperands ? "true" : "false")
+ << ", .Owner = PseudoOwner::" << Instruction.Owner
+ << ", .Deadline = PipelineStage::" << Instruction.Deadline
+ << ", .VerifyExplicitOperands = nullptr, "
+ ".VerifyInstructionSemantics = nullptr},\n";
+ }
+ Output << " };\n}\n\n";
+
+ Output << "constexpr std::array<SchedulingClassInfo, " << Model.SchedulingClasses.size()
+ << "> GeneratedSchedulingClasses = {{\n";
+ for (const NormalizedSchedulingClass &Class : Model.SchedulingClasses) {
+ Output << " {SchedulingClassID::fromValue(" << Class.ID << "), ";
+ appendQuoted(Output, Class.Name);
+ Output << ", " << Class.MicroOps << ", " << Class.Latency << "},\n";
+ }
+ Output << "}};\n\n";
+
+ Output << "constexpr std::array<EncodingCategoryInfo, " << Model.EncodingCategories.size()
+ << "> GeneratedEncodingCategories = {{\n";
+ for (const NormalizedEncodingCategory &Category : Model.EncodingCategories) {
+ Output << " {EncodingCategoryID::fromValue(" << Category.ID << "), ";
+ appendQuoted(Output, Category.Name);
+ Output << ", " << Category.SizeInBytes << "},\n";
+ }
+ Output << "}};\n\n";
+
+ Output << "constexpr std::array<AssemblyFormatInfo, " << Model.AssemblyFormats.size()
+ << "> GeneratedAssemblyFormats = {{\n";
+ for (const NormalizedAssemblyFormat &Format : Model.AssemblyFormats) {
+ Output << " {AssemblyFormatID::fromValue(" << Format.ID << "), ";
+ appendQuoted(Output, Format.Name);
+ Output << "},\n";
+ }
+ Output << "}};\n\n";
+
+ const auto AssemblyInstructionCount =
+ std::ranges::count_if(Model.Instructions, [](const NormalizedInstruction &Instruction) {
+ return !Instruction.AssemblyMnemonic.empty();
+ });
+ Output << "constexpr std::array<AssemblyInstructionInfo, " << AssemblyInstructionCount
+ << "> GeneratedAssemblyInstructions = {{\n";
+ for (const NormalizedInstruction &Instruction : Model.Instructions) {
+ if (Instruction.AssemblyMnemonic.empty())
+ continue;
+ Output << " {Opcode::fromValue(" << Instruction.ID << "), ";
+ appendQuoted(Output, Instruction.AssemblyMnemonic);
+ Output << ", AssemblyFormatID::fromValue(" << Instruction.AssemblyFormat << ")},\n";
+ }
+ Output << "}};\n\n";
+
+ for (const NormalizedCallingConvention &Convention : Model.CallingConventions) {
+ const std::string Prefix = "CC" + std::to_string(Convention.ID);
+ emitPhysicalRegisterArray(Output, Model, Prefix + "IntegerArguments",
+ Convention.IntegerArgumentRegisters);
+ emitPhysicalRegisterArray(Output, Model, Prefix + "FloatingArguments",
+ Convention.FloatingArgumentRegisters);
+ emitPhysicalRegisterArray(Output, Model, Prefix + "IntegerReturns",
+ Convention.IntegerReturnRegisters);
+ emitPhysicalRegisterArray(Output, Model, Prefix + "FloatingReturns",
+ Convention.FloatingReturnRegisters);
+ }
+ Output << "const std::array<CallingConventionInfo, " << Model.CallingConventions.size()
+ << "> GeneratedCallingConventions = {{\n";
+ for (const NormalizedCallingConvention &Convention : Model.CallingConventions) {
+ const std::string Prefix = "CC" + std::to_string(Convention.ID);
+ Output << " {CallingConvention::" << Convention.Symbol << ", ";
+ appendQuoted(Output, Convention.Name);
+ Output << ", RegisterClassID::fromValue(" << Convention.IntegerRegisterClass
+ << "), RegisterClassID::fromValue(" << Convention.FloatingRegisterClass << "), "
+ << Prefix << "IntegerArguments, " << Prefix << "FloatingArguments, " << Prefix
+ << "IntegerReturns, " << Prefix << "FloatingReturns, ";
+ appendQuoted(Output, Convention.PreservedMaskName);
+ Output << ", " << Convention.StackAlignmentBytes << ", "
+ << Convention.StackArgumentAlignmentBytes << ", " << Convention.ShadowSpaceBytes << ", "
+ << (Convention.SupportsVarArgs ? "true" : "false") << ", "
+ << (Convention.SupportsTailCalls ? "true" : "false") << "},\n";
+ }
+ Output << "}};\n";
return Output.str();
}
@@ -1141,6 +3037,7 @@ DescriptionGenerationResult generateDescription(std::string SourceName, std::str
.Output =
GeneratedDescription{
.RegisterHeader = emitRegisterHeader(*Model, DisplayName),
+ .InstructionHeader = emitInstructionHeader(*Model, DisplayName),
.TargetTables = emitTargetTables(*Model, DisplayName),
},
.Diagnostics = {},
diff --git a/backend/lib/Target/Description/TargetDescriptionGenerator.h b/backend/lib/Target/Description/TargetDescriptionGenerator.h
index 54cbf3c..c0d8ed5 100644
--- a/backend/lib/Target/Description/TargetDescriptionGenerator.h
+++ b/backend/lib/Target/Description/TargetDescriptionGenerator.h
@@ -14,6 +14,7 @@ namespace rcc::targetgen {
struct GeneratedDescription final {
std::string RegisterHeader;
+ std::string InstructionHeader;
std::string TargetTables;
};
diff --git a/backend/lib/Target/RISCV/CMakeLists.txt b/backend/lib/Target/RISCV/CMakeLists.txt
index 2c744c0..a77494a 100644
--- a/backend/lib/Target/RISCV/CMakeLists.txt
+++ b/backend/lib/Target/RISCV/CMakeLists.txt
@@ -2,20 +2,28 @@
set(RCCBackendRISCVDescription "${CMAKE_CURRENT_SOURCE_DIR}/RISCV.rccdesc")
set(
- RCCBackendRISCVGeneratedHeader
+ RCCBackendRISCVGeneratedRegisterHeader
"${RCC_BACKEND_BINARY_DIR}/generated/include/rcc/Target/RISCV/RISCVRegisters.h"
)
set(
+ RCCBackendRISCVGeneratedInstructionHeader
+ "${RCC_BACKEND_BINARY_DIR}/generated/include/rcc/Target/RISCV/RISCVInstructions.h"
+)
+set(
RCCBackendRISCVGeneratedTables
"${RCC_BACKEND_BINARY_DIR}/generated/lib/Target/RISCV/RISCVGenTargetDescription.inc"
)
add_custom_command(
- OUTPUT "${RCCBackendRISCVGeneratedHeader}" "${RCCBackendRISCVGeneratedTables}"
+ OUTPUT
+ "${RCCBackendRISCVGeneratedRegisterHeader}"
+ "${RCCBackendRISCVGeneratedInstructionHeader}"
+ "${RCCBackendRISCVGeneratedTables}"
COMMAND
"$<TARGET_FILE:RCCBackendDescriptionGenerator>"
--input "${RCCBackendRISCVDescription}"
- --header "${RCCBackendRISCVGeneratedHeader}"
+ --register-header "${RCCBackendRISCVGeneratedRegisterHeader}"
+ --instruction-header "${RCCBackendRISCVGeneratedInstructionHeader}"
--tables "${RCCBackendRISCVGeneratedTables}"
DEPENDS RCCBackendDescriptionGenerator "${RCCBackendRISCVDescription}"
COMMENT "Generating RCC RV64GC target tables"
@@ -24,18 +32,24 @@ add_custom_command(
add_custom_target(
RCCBackendRISCVGeneratedTablesTarget
- DEPENDS "${RCCBackendRISCVGeneratedHeader}" "${RCCBackendRISCVGeneratedTables}"
+ DEPENDS
+ "${RCCBackendRISCVGeneratedRegisterHeader}"
+ "${RCCBackendRISCVGeneratedInstructionHeader}"
+ "${RCCBackendRISCVGeneratedTables}"
)
set_source_files_properties(
- "${RCCBackendRISCVGeneratedHeader}" "${RCCBackendRISCVGeneratedTables}"
+ "${RCCBackendRISCVGeneratedRegisterHeader}"
+ "${RCCBackendRISCVGeneratedInstructionHeader}"
+ "${RCCBackendRISCVGeneratedTables}"
PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE
)
add_library(
RCCBackendRISCV
RISCVTarget.cpp
- "${RCCBackendRISCVGeneratedHeader}"
+ "${RCCBackendRISCVGeneratedRegisterHeader}"
+ "${RCCBackendRISCVGeneratedInstructionHeader}"
"${RCCBackendRISCVGeneratedTables}"
)
add_library(RCCBackend::RISCV ALIAS RCCBackendRISCV)
@@ -68,7 +82,9 @@ install(
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
)
install(
- FILES "${RCCBackendRISCVGeneratedHeader}"
+ FILES
+ "${RCCBackendRISCVGeneratedRegisterHeader}"
+ "${RCCBackendRISCVGeneratedInstructionHeader}"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rcc/Target/RISCV"
)
@@ -76,4 +92,6 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVTarget.h"
)
-rcc_backend_register_tidy_files("${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp")
+rcc_backend_register_tidy_files(
+ "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp"
+)
diff --git a/backend/lib/Target/RISCV/RISCV.rccdesc b/backend/lib/Target/RISCV/RISCV.rccdesc
index 54093e6..a8dfdde 100644
--- a/backend/lib/Target/RISCV/RISCV.rccdesc
+++ b/backend/lib/Target/RISCV/RISCV.rccdesc
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later
-schema: rcc-target-description-v1
+schema: rcc-target-description-v2
target:
namespace: riscv
name: RISCV
@@ -213,8 +213,2985 @@ register_classes:
lanes: 1
registers: [F8, F9, F10, F11, F12, F13, F14, F15]
allocation_order: [F10, F11, F12, F13, F14, F15, F8, F9]
+ -
+ id: 7
+ symbol: GPR_NX0
+ name: GPR_NX0
+ bank: GPR
+ size_bits: 64
+ spill_size_bits: 64
+ spill_alignment_bits: 64
+ lanes: 1
+ registers: [X1, X2, 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: 8
+ symbol: GPR_NX0_NX2
+ name: GPR_NX0_NX2
+ bank: GPR
+ size_bits: 64
+ spill_size_bits: 64
+ spill_alignment_bits: 64
+ 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]
register_masks:
-
name: CSR_ILP32D_LP64D
preserved: [X1, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, F8, F9, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27]
+
+mir_operand_flags:
+ direct_mask: 31
+ direct:
+ - { name: riscv-call, value: 1 }
+ bitmask: []
+
+calling_conventions:
+ -
+ id: 1
+ symbol: LP64D
+ name: lp64d
+ integer_register_class: GPR
+ floating_register_class: FPR64
+ integer_argument_registers: [X10, X11, X12, X13, X14, X15, X16, X17]
+ floating_argument_registers: [F10, F11, F12, F13, F14, F15, F16, F17]
+ integer_return_registers: [X10, X11]
+ floating_return_registers: [F10, F11]
+ preserved_mask: CSR_ILP32D_LP64D
+ stack_alignment_bytes: 16
+ stack_argument_alignment_bytes: 16
+ shadow_space_bytes: 0
+ supports_varargs: true
+ supports_tail_calls: true
+
+named_operands:
+ - { id: 1, symbol: RD, name: rd }
+ - { id: 2, symbol: RS1, name: rs1 }
+ - { id: 3, symbol: RS2, name: rs2 }
+ - { id: 4, symbol: IMM, name: imm }
+ - { id: 5, symbol: TARGET, name: target }
+ - { id: 6, symbol: CSR, name: csr }
+ - { id: 7, symbol: ZIMM, name: zimm }
+ - { id: 8, symbol: PRED, name: pred }
+ - { id: 9, symbol: SUCC, name: succ }
+ - { id: 10, symbol: FRD, name: frd }
+ - { id: 11, symbol: FRS1, name: frs1 }
+ - { id: 12, symbol: FRS2, name: frs2 }
+ - { id: 13, symbol: FRS3, name: frs3 }
+ - { id: 14, symbol: RM, name: rm }
+
+immediate_predicates:
+ - { id: 1, symbol: SIMM12, name: simm12, kind: signed, bits: 12 }
+ - { id: 2, symbol: SIMM20, name: simm20, kind: signed, bits: 20 }
+ - { id: 3, symbol: UIMM5, name: uimm5, kind: unsigned, bits: 5 }
+ - { id: 4, symbol: UIMM6, name: uimm6, kind: unsigned, bits: 6 }
+ - { id: 5, symbol: UIMM12, name: uimm12, kind: unsigned, bits: 12 }
+ - { id: 6, symbol: UIMM4, name: uimm4, kind: unsigned, bits: 4 }
+ - { id: 7, symbol: RM, name: rm, kind: unsigned, bits: 3, allowed_values: [0, 1, 2, 3, 4, 7] }
+ - { id: 8, symbol: C_UIMM10_NZ_A4, name: c-uimm10-nz-a4, kind: unsigned, bits: 10, alignment: 4, nonzero: true }
+ - { id: 9, symbol: C_UIMM7_A4, name: c-uimm7-a4, kind: unsigned, bits: 7, alignment: 4 }
+ - { id: 10, symbol: C_UIMM8_A8, name: c-uimm8-a8, kind: unsigned, bits: 8, alignment: 8 }
+ - { id: 11, symbol: C_UIMM8_A4, name: c-uimm8-a4, kind: unsigned, bits: 8, alignment: 4 }
+ - { id: 12, symbol: C_UIMM9_A8, name: c-uimm9-a8, kind: unsigned, bits: 9, alignment: 8 }
+ - { id: 13, symbol: C_SIMM6, name: c-simm6, kind: signed, bits: 6 }
+ - { id: 14, symbol: C_SIMM6_NZ, name: c-simm6-nz, kind: signed, bits: 6, nonzero: true }
+ - { id: 15, symbol: C_SIMM9_A2, name: c-simm9-a2, kind: signed, bits: 9, alignment: 2 }
+ - { id: 16, symbol: C_SIMM12_A2, name: c-simm12-a2, kind: signed, bits: 12, alignment: 2 }
+ - { id: 17, symbol: C_SIMM10_A16_NZ, name: c-simm10-a16-nz, kind: signed, bits: 10, alignment: 16, nonzero: true }
+
+scheduling_classes:
+ - { id: 1, symbol: ALU, name: alu, micro_ops: 1, latency: 1 }
+ - { id: 2, symbol: LOAD, name: load, micro_ops: 1, latency: 3 }
+ - { id: 3, symbol: STORE, name: store, micro_ops: 1, latency: 1 }
+ - { id: 4, symbol: BRANCH, name: branch, micro_ops: 1, latency: 1 }
+ - { id: 5, symbol: SYSTEM, name: system, micro_ops: 1, latency: 1 }
+ - { id: 6, symbol: MUL, name: mul, micro_ops: 1, latency: 3 }
+ - { id: 7, symbol: DIV, name: div, micro_ops: 1, latency: 20 }
+ - { id: 8, symbol: FENCE, name: fence, micro_ops: 1, latency: 1 }
+ - { id: 9, symbol: ATOMIC, name: atomic, micro_ops: 1, latency: 4 }
+ - { id: 10, symbol: FP_ADD, name: fp-add, micro_ops: 1, latency: 4 }
+ - { id: 11, symbol: FP_MUL, name: fp-mul, micro_ops: 1, latency: 4 }
+ - { id: 12, symbol: FP_DIV, name: fp-div, micro_ops: 1, latency: 20 }
+ - { id: 13, symbol: FP_SQRT, name: fp-sqrt, micro_ops: 1, latency: 20 }
+ - { id: 14, symbol: FP_CONVERT, name: fp-convert, micro_ops: 1, latency: 4 }
+ - { id: 15, symbol: COMPRESSED, name: compressed, micro_ops: 1, latency: 1 }
+
+encoding_categories:
+ - { id: 1, symbol: R, name: r, size_bytes: 4 }
+ - { id: 2, symbol: I, name: i, size_bytes: 4 }
+ - { id: 3, symbol: S, name: s, size_bytes: 4 }
+ - { id: 4, symbol: B, name: b, size_bytes: 4 }
+ - { id: 5, symbol: U, name: u, size_bytes: 4 }
+ - { id: 6, symbol: J, name: j, size_bytes: 4 }
+ - { id: 7, symbol: SYSTEM, name: system, size_bytes: 4 }
+ - { id: 8, symbol: AMO, name: amo, size_bytes: 4 }
+ - { id: 9, symbol: R4, name: r4, size_bytes: 4 }
+ - { id: 10, symbol: C, name: c, size_bytes: 2 }
+
+assembly_formats:
+ - { id: 1, symbol: RD_IMM, name: rd-imm }
+ - { id: 2, symbol: RD_RS1_IMM, name: rd-rs1-imm }
+ - { id: 3, symbol: RD_RS1_RS2, name: rd-rs1-rs2 }
+ - { id: 4, symbol: RD_OFFSET_RS1, name: rd-offset-rs1 }
+ - { id: 5, symbol: RS2_OFFSET_RS1, name: rs2-offset-rs1 }
+ - { id: 6, symbol: RS1_RS2_TARGET, name: rs1-rs2-target }
+ - { id: 7, symbol: RD_TARGET, name: rd-target }
+ - { id: 8, symbol: NONE, name: none }
+ - { id: 9, symbol: IMM_IMM, name: imm-imm }
+ - { id: 10, symbol: RD_CSR_RS1, name: rd-csr-rs1 }
+ - { id: 11, symbol: RD_CSR_IMM, name: rd-csr-imm }
+ - { id: 12, symbol: RD_PAREN_RS1, name: rd-paren-rs1 }
+ - { id: 13, symbol: RD_RS2_PAREN_RS1, name: rd-rs2-paren-rs1 }
+ - { id: 14, symbol: FRD_OFFSET_RS1, name: frd-offset-rs1 }
+ - { id: 15, symbol: FRS2_OFFSET_RS1, name: frs2-offset-rs1 }
+ - { id: 16, symbol: FRD_FRS1_FRS2_RM, name: frd-frs1-frs2-rm }
+ - { id: 17, symbol: FRD_FRS1_FRS2_FRS3_RM, name: frd-frs1-frs2-frs3-rm }
+ - { id: 18, symbol: FRD_FRS1_RM, name: frd-frs1-rm }
+ - { id: 19, symbol: FRD_FRS1_FRS2, name: frd-frs1-frs2 }
+ - { id: 20, symbol: RD_FRS1_RM, name: rd-frs1-rm }
+ - { id: 21, symbol: RD_FRS1, name: rd-frs1 }
+ - { id: 22, symbol: RD_FRS1_FRS2, name: rd-frs1-frs2 }
+ - { id: 23, symbol: FRD_RS1_RM, name: frd-rs1-rm }
+ - { id: 24, symbol: FRD_RS1, name: frd-rs1 }
+ - { id: 25, symbol: TARGET, name: target }
+ - { id: 26, symbol: RS1_TARGET, name: rs1-target }
+ - { id: 27, symbol: RS1, name: rs1 }
+ - { id: 28, symbol: RD_RS2, name: rd-rs2 }
+ - { id: 29, symbol: IMM_SP, name: imm-sp }
+ - { id: 30, symbol: RD_IMM_SP, name: rd-imm-sp }
+ - { id: 31, symbol: RD_OFFSET_SP, name: rd-offset-sp }
+ - { id: 32, symbol: RS2_OFFSET_SP, name: rs2-offset-sp }
+ - { id: 33, symbol: FRD_OFFSET_SP, name: frd-offset-sp }
+ - { id: 34, symbol: FRS2_OFFSET_SP, name: frs2-offset-sp }
+
+instructions:
+ -
+ id: 1024
+ symbol: LUI
+ name: LUI
+ assembly_mnemonic: lui
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: SIMM20, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: U
+ assembly_format: RD_IMM
+ -
+ id: 1025
+ symbol: AUIPC
+ name: AUIPC
+ assembly_mnemonic: auipc
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: SIMM20, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: U
+ assembly_format: RD_IMM
+ -
+ id: 1026
+ symbol: ADDI
+ name: ADDI
+ assembly_mnemonic: addi
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1042
+ symbol: SRLI
+ name: SRLI
+ assembly_mnemonic: srli
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: UIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1043
+ symbol: SRAI
+ name: SRAI
+ assembly_mnemonic: srai
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: UIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1044
+ symbol: ADDIW
+ name: ADDIW
+ assembly_mnemonic: addiw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1045
+ symbol: SLLIW
+ name: SLLIW
+ assembly_mnemonic: slliw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: UIMM5, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1046
+ symbol: SRLIW
+ name: SRLIW
+ assembly_mnemonic: srliw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: UIMM5, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1047
+ symbol: SRAIW
+ name: SRAIW
+ assembly_mnemonic: sraiw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: UIMM5, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1048
+ symbol: SLL
+ name: SLL
+ assembly_mnemonic: sll
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1049
+ symbol: SLT
+ name: SLT
+ assembly_mnemonic: slt
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1050
+ symbol: SLTU
+ name: SLTU
+ assembly_mnemonic: sltu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1051
+ symbol: XOR
+ name: XOR
+ assembly_mnemonic: xor
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1052
+ symbol: SRL
+ name: SRL
+ assembly_mnemonic: srl
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1053
+ symbol: SRA
+ name: SRA
+ assembly_mnemonic: sra
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1054
+ symbol: OR
+ name: OR
+ assembly_mnemonic: or
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1055
+ symbol: AND
+ name: AND
+ assembly_mnemonic: and
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1056
+ symbol: ADDW
+ name: ADDW
+ assembly_mnemonic: addw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1057
+ symbol: SUBW
+ name: SUBW
+ assembly_mnemonic: subw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1058
+ symbol: SLLW
+ name: SLLW
+ assembly_mnemonic: sllw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1059
+ symbol: SRLW
+ name: SRLW
+ assembly_mnemonic: srlw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1060
+ symbol: SRAW
+ name: SRAW
+ assembly_mnemonic: sraw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1061
+ symbol: LB
+ name: LB
+ assembly_mnemonic: lb
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1062
+ symbol: LH
+ name: LH
+ assembly_mnemonic: lh
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1063
+ symbol: LW
+ name: LW
+ assembly_mnemonic: lw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1064
+ symbol: LBU
+ name: LBU
+ assembly_mnemonic: lbu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1065
+ symbol: LHU
+ name: LHU
+ assembly_mnemonic: lhu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1066
+ symbol: LWU
+ name: LWU
+ assembly_mnemonic: lwu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1067
+ symbol: SB
+ name: SB
+ assembly_mnemonic: sb
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: S
+ assembly_format: RS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1068
+ symbol: SH
+ name: SH
+ assembly_mnemonic: sh
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: S
+ assembly_format: RS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1069
+ symbol: SW
+ name: SW
+ assembly_mnemonic: sw
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: S
+ assembly_format: RS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1070
+ symbol: BLT
+ name: BLT
+ assembly_mnemonic: blt
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: B
+ assembly_format: RS1_RS2_TARGET
+ -
+ id: 1071
+ symbol: BGE
+ name: BGE
+ assembly_mnemonic: bge
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: B
+ assembly_format: RS1_RS2_TARGET
+ -
+ id: 1072
+ symbol: BLTU
+ name: BLTU
+ assembly_mnemonic: bltu
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: B
+ assembly_format: RS1_RS2_TARGET
+ -
+ id: 1073
+ symbol: BGEU
+ name: BGEU
+ assembly_mnemonic: bgeu
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: B
+ assembly_format: RS1_RS2_TARGET
+ -
+ id: 1074
+ symbol: JALR
+ name: JALR
+ assembly_mnemonic: jalr
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [terminator, branch, indirect_branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1075
+ symbol: FENCE
+ name: FENCE
+ assembly_mnemonic: fence
+ operands:
+ - { kinds: [immediate], immediate_predicate: UIMM4, name: PRED }
+ - { kinds: [immediate], immediate_predicate: UIMM4, name: SUCC }
+ num_explicit_defs: 0
+ flags: [has_side_effects, produces_code]
+ scheduling_class: FENCE
+ encoding_category: I
+ assembly_format: IMM_IMM
+ -
+ id: 1076
+ symbol: FENCE_I
+ name: FENCE_I
+ assembly_mnemonic: fence.i
+ operands: []
+ num_explicit_defs: 0
+ flags: [has_side_effects, produces_code]
+ scheduling_class: FENCE
+ encoding_category: I
+ assembly_format: NONE
+ -
+ id: 1077
+ symbol: CSRRW
+ name: CSRRW
+ assembly_mnemonic: csrrw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ num_explicit_defs: 1
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: RD_CSR_RS1
+ -
+ id: 1078
+ symbol: CSRRS
+ name: CSRRS
+ assembly_mnemonic: csrrs
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ num_explicit_defs: 1
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: RD_CSR_RS1
+ -
+ id: 1079
+ symbol: CSRRC
+ name: CSRRC
+ assembly_mnemonic: csrrc
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ num_explicit_defs: 1
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: RD_CSR_RS1
+ -
+ id: 1080
+ symbol: CSRRWI
+ name: CSRRWI
+ assembly_mnemonic: csrrwi
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
+ - { kinds: [immediate], immediate_predicate: UIMM5, name: ZIMM }
+ num_explicit_defs: 1
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: RD_CSR_IMM
+ -
+ id: 1083
+ symbol: MUL
+ name: MUL
+ assembly_mnemonic: mul
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code, commutable]
+ scheduling_class: MUL
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1084
+ symbol: MULH
+ name: MULH
+ assembly_mnemonic: mulh
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code, commutable]
+ scheduling_class: MUL
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1085
+ symbol: MULHSU
+ name: MULHSU
+ assembly_mnemonic: mulhsu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: MUL
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1086
+ symbol: MULHU
+ name: MULHU
+ assembly_mnemonic: mulhu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code, commutable]
+ scheduling_class: MUL
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1087
+ symbol: DIV
+ name: DIV
+ assembly_mnemonic: div
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1088
+ symbol: DIVU
+ name: DIVU
+ assembly_mnemonic: divu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1089
+ symbol: REM
+ name: REM
+ assembly_mnemonic: rem
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1090
+ symbol: REMU
+ name: REMU
+ assembly_mnemonic: remu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1091
+ symbol: MULW
+ name: MULW
+ assembly_mnemonic: mulw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code, commutable]
+ scheduling_class: MUL
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1092
+ symbol: DIVW
+ name: DIVW
+ assembly_mnemonic: divw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1093
+ symbol: DIVUW
+ name: DIVUW
+ assembly_mnemonic: divuw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1094
+ symbol: REMW
+ name: REMW
+ assembly_mnemonic: remw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1095
+ symbol: REMUW
+ name: REMUW
+ assembly_mnemonic: remuw
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: DIV
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1081
+ symbol: CSRRSI
+ name: CSRRSI
+ assembly_mnemonic: csrrsi
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
+ - { kinds: [immediate], immediate_predicate: UIMM5, name: ZIMM }
+ num_explicit_defs: 1
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: RD_CSR_IMM
+ -
+ id: 1082
+ symbol: CSRRCI
+ name: CSRRCI
+ assembly_mnemonic: csrrci
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM12, name: CSR }
+ - { kinds: [immediate], immediate_predicate: UIMM5, name: ZIMM }
+ num_explicit_defs: 1
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: RD_CSR_IMM
+ -
+ id: 1027
+ symbol: ADD
+ name: ADD
+ assembly_mnemonic: add
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1028
+ symbol: SUB
+ name: SUB
+ assembly_mnemonic: sub
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: R
+ assembly_format: RD_RS1_RS2
+ -
+ id: 1029
+ symbol: LD
+ name: LD
+ assembly_mnemonic: ld
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1030
+ symbol: SD
+ name: SD
+ assembly_mnemonic: sd
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: S
+ assembly_format: RS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1031
+ symbol: BEQ
+ name: BEQ
+ assembly_mnemonic: beq
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: B
+ assembly_format: RS1_RS2_TARGET
+ -
+ id: 1032
+ symbol: BNE
+ name: BNE
+ assembly_mnemonic: bne
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS2 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: B
+ assembly_format: RS1_RS2_TARGET
+ -
+ id: 1033
+ symbol: JAL
+ name: JAL
+ assembly_mnemonic: jal
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 1
+ flags: [terminator, branch, produces_code]
+ scheduling_class: BRANCH
+ encoding_category: J
+ assembly_format: RD_TARGET
+ -
+ id: 1034
+ symbol: ECALL
+ name: ECALL
+ assembly_mnemonic: ecall
+ operands: []
+ num_explicit_defs: 0
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: NONE
+ -
+ id: 1035
+ symbol: EBREAK
+ name: EBREAK
+ assembly_mnemonic: ebreak
+ operands: []
+ num_explicit_defs: 0
+ flags: [has_side_effects, produces_code]
+ scheduling_class: SYSTEM
+ encoding_category: SYSTEM
+ assembly_format: NONE
+ -
+ id: 1036
+ symbol: SLTI
+ name: SLTI
+ assembly_mnemonic: slti
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1037
+ symbol: SLTIU
+ name: SLTIU
+ assembly_mnemonic: sltiu
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1038
+ symbol: XORI
+ name: XORI
+ assembly_mnemonic: xori
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1039
+ symbol: ORI
+ name: ORI
+ assembly_mnemonic: ori
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1040
+ symbol: ANDI
+ name: ANDI
+ assembly_mnemonic: andi
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1041
+ symbol: SLLI
+ name: SLLI
+ assembly_mnemonic: slli
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: UIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: ALU
+ encoding_category: I
+ assembly_format: RD_RS1_IMM
+ -
+ id: 1096
+ symbol: LR_W
+ name: LR_W
+ assembly_mnemonic: lr.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1097
+ symbol: SC_W
+ name: SC_W
+ assembly_mnemonic: sc.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1098
+ symbol: AMOSWAP_W
+ name: AMOSWAP_W
+ assembly_mnemonic: amoswap.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1118
+ symbol: FLW
+ name: FLW
+ assembly_mnemonic: flw
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, memory_role: value, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: FRD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1119
+ symbol: FSW
+ name: FSW
+ assembly_mnemonic: fsw
+ operands:
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, memory_role: value, name: FRS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: S
+ assembly_format: FRS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1120
+ symbol: FMADD_S
+ name: FMADD_S
+ assembly_mnemonic: fmadd.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1121
+ symbol: FMSUB_S
+ name: FMSUB_S
+ assembly_mnemonic: fmsub.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1122
+ symbol: FNMSUB_S
+ name: FNMSUB_S
+ assembly_mnemonic: fnmsub.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1123
+ symbol: FNMADD_S
+ name: FNMADD_S
+ assembly_mnemonic: fnmadd.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1124
+ symbol: FADD_S
+ name: FADD_S
+ assembly_mnemonic: fadd.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1125
+ symbol: FSUB_S
+ name: FSUB_S
+ assembly_mnemonic: fsub.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1126
+ symbol: FMUL_S
+ name: FMUL_S
+ assembly_mnemonic: fmul.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1127
+ symbol: FDIV_S
+ name: FDIV_S
+ assembly_mnemonic: fdiv.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_DIV
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1128
+ symbol: FSQRT_S
+ name: FSQRT_S
+ assembly_mnemonic: fsqrt.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_SQRT
+ encoding_category: R
+ assembly_format: FRD_FRS1_RM
+ -
+ id: 1129
+ symbol: FSGNJ_S
+ name: FSGNJ_S
+ assembly_mnemonic: fsgnj.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1130
+ symbol: FSGNJN_S
+ name: FSGNJN_S
+ assembly_mnemonic: fsgnjn.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1131
+ symbol: FSGNJX_S
+ name: FSGNJX_S
+ assembly_mnemonic: fsgnjx.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1132
+ symbol: FMIN_S
+ name: FMIN_S
+ assembly_mnemonic: fmin.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1133
+ symbol: FMAX_S
+ name: FMAX_S
+ assembly_mnemonic: fmax.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1134
+ symbol: FCVT_W_S
+ name: FCVT_W_S
+ assembly_mnemonic: fcvt.w.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1135
+ symbol: FCVT_WU_S
+ name: FCVT_WU_S
+ assembly_mnemonic: fcvt.wu.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1136
+ symbol: FCVT_L_S
+ name: FCVT_L_S
+ assembly_mnemonic: fcvt.l.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1137
+ symbol: FCVT_LU_S
+ name: FCVT_LU_S
+ assembly_mnemonic: fcvt.lu.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1138
+ symbol: FMV_X_W
+ name: FMV_X_W
+ assembly_mnemonic: fmv.x.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1
+ -
+ id: 1139
+ symbol: FEQ_S
+ name: FEQ_S
+ assembly_mnemonic: feq.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: RD_FRS1_FRS2
+ -
+ id: 1140
+ symbol: FLT_S
+ name: FLT_S
+ assembly_mnemonic: flt.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: RD_FRS1_FRS2
+ -
+ id: 1141
+ symbol: FLE_S
+ name: FLE_S
+ assembly_mnemonic: fle.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: RD_FRS1_FRS2
+ -
+ id: 1142
+ symbol: FCLASS_S
+ name: FCLASS_S
+ assembly_mnemonic: fclass.s
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1
+ -
+ id: 1143
+ symbol: FCVT_S_W
+ name: FCVT_S_W
+ assembly_mnemonic: fcvt.s.w
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1144
+ symbol: FCVT_S_WU
+ name: FCVT_S_WU
+ assembly_mnemonic: fcvt.s.wu
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1145
+ symbol: FCVT_S_L
+ name: FCVT_S_L
+ assembly_mnemonic: fcvt.s.l
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1146
+ symbol: FCVT_S_LU
+ name: FCVT_S_LU
+ assembly_mnemonic: fcvt.s.lu
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1147
+ symbol: FMV_W_X
+ name: FMV_W_X
+ assembly_mnemonic: fmv.w.x
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1
+ -
+ id: 1148
+ symbol: FLD
+ name: FLD
+ assembly_mnemonic: fld
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, memory_role: value, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: I
+ assembly_format: FRD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1149
+ symbol: FSD
+ name: FSD
+ assembly_mnemonic: fsd
+ operands:
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, memory_role: value, name: FRS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: SIMM12, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: S
+ assembly_format: FRS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1150
+ symbol: FMADD_D
+ name: FMADD_D
+ assembly_mnemonic: fmadd.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1151
+ symbol: FMSUB_D
+ name: FMSUB_D
+ assembly_mnemonic: fmsub.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1152
+ symbol: FNMSUB_D
+ name: FNMSUB_D
+ assembly_mnemonic: fnmsub.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1153
+ symbol: FNMADD_D
+ name: FNMADD_D
+ assembly_mnemonic: fnmadd.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS3 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R4
+ assembly_format: FRD_FRS1_FRS2_FRS3_RM
+ -
+ id: 1154
+ symbol: FADD_D
+ name: FADD_D
+ assembly_mnemonic: fadd.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1155
+ symbol: FSUB_D
+ name: FSUB_D
+ assembly_mnemonic: fsub.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1156
+ symbol: FMUL_D
+ name: FMUL_D
+ assembly_mnemonic: fmul.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_MUL
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1157
+ symbol: FDIV_D
+ name: FDIV_D
+ assembly_mnemonic: fdiv.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_DIV
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2_RM
+ -
+ id: 1158
+ symbol: FSQRT_D
+ name: FSQRT_D
+ assembly_mnemonic: fsqrt.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_SQRT
+ encoding_category: R
+ assembly_format: FRD_FRS1_RM
+ -
+ id: 1159
+ symbol: FSGNJ_D
+ name: FSGNJ_D
+ assembly_mnemonic: fsgnj.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1160
+ symbol: FSGNJN_D
+ name: FSGNJN_D
+ assembly_mnemonic: fsgnjn.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1161
+ symbol: FSGNJX_D
+ name: FSGNJX_D
+ assembly_mnemonic: fsgnjx.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1162
+ symbol: FMIN_D
+ name: FMIN_D
+ assembly_mnemonic: fmin.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1163
+ symbol: FMAX_D
+ name: FMAX_D
+ assembly_mnemonic: fmax.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: FRD_FRS1_FRS2
+ -
+ id: 1164
+ symbol: FCVT_W_D
+ name: FCVT_W_D
+ assembly_mnemonic: fcvt.w.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1165
+ symbol: FCVT_WU_D
+ name: FCVT_WU_D
+ assembly_mnemonic: fcvt.wu.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1166
+ symbol: FCVT_L_D
+ name: FCVT_L_D
+ assembly_mnemonic: fcvt.l.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1167
+ symbol: FCVT_LU_D
+ name: FCVT_LU_D
+ assembly_mnemonic: fcvt.lu.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1_RM
+ -
+ id: 1168
+ symbol: FMV_X_D
+ name: FMV_X_D
+ assembly_mnemonic: fmv.x.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1
+ -
+ id: 1169
+ symbol: FEQ_D
+ name: FEQ_D
+ assembly_mnemonic: feq.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: RD_FRS1_FRS2
+ -
+ id: 1170
+ symbol: FLT_D
+ name: FLT_D
+ assembly_mnemonic: flt.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: RD_FRS1_FRS2
+ -
+ id: 1171
+ symbol: FLE_D
+ name: FLE_D
+ assembly_mnemonic: fle.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_ADD
+ encoding_category: R
+ assembly_format: RD_FRS1_FRS2
+ -
+ id: 1172
+ symbol: FCLASS_D
+ name: FCLASS_D
+ assembly_mnemonic: fclass.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: RD_FRS1
+ -
+ id: 1173
+ symbol: FCVT_D_W
+ name: FCVT_D_W
+ assembly_mnemonic: fcvt.d.w
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1174
+ symbol: FCVT_D_WU
+ name: FCVT_D_WU
+ assembly_mnemonic: fcvt.d.wu
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1175
+ symbol: FCVT_D_L
+ name: FCVT_D_L
+ assembly_mnemonic: fcvt.d.l
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1176
+ symbol: FCVT_D_LU
+ name: FCVT_D_LU
+ assembly_mnemonic: fcvt.d.lu
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1_RM
+ -
+ id: 1177
+ symbol: FMV_D_X
+ name: FMV_D_X
+ assembly_mnemonic: fmv.d.x
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, name: RS1 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_RS1
+ -
+ id: 1178
+ symbol: FCVT_S_D
+ name: FCVT_S_D
+ assembly_mnemonic: fcvt.s.d
+ operands:
+ - { kinds: [register], role: def, register_class: FPR32, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_FRS1_RM
+ -
+ id: 1180
+ symbol: C_ADDI4SPN
+ name: C_ADDI4SPN
+ assembly_mnemonic: c.addi4spn
+ operands:
+ - { kinds: [register], role: def, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_UIMM10_NZ_A4, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM_SP
+ -
+ id: 1181
+ symbol: C_FLD
+ name: C_FLD
+ assembly_mnemonic: c.fld
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64C, register_bank: FPR, memory_role: value, name: FRD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM8_A8, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: C
+ assembly_format: FRD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1182
+ symbol: C_LW
+ name: C_LW
+ assembly_mnemonic: c.lw
+ operands:
+ - { kinds: [register], role: def, register_class: GPRC, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM7_A4, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: C
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1183
+ symbol: C_LD
+ name: C_LD
+ assembly_mnemonic: c.ld
+ operands:
+ - { kinds: [register], role: def, register_class: GPRC, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM8_A8, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: LOAD
+ encoding_category: C
+ assembly_format: RD_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1184
+ symbol: C_FSD
+ name: C_FSD
+ assembly_mnemonic: c.fsd
+ operands:
+ - { kinds: [register], role: use, register_class: FPR64C, register_bank: FPR, memory_role: value, name: FRS2 }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM8_A8, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: C
+ assembly_format: FRS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1185
+ symbol: C_SW
+ name: C_SW
+ assembly_mnemonic: c.sw
+ operands:
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM7_A4, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: C
+ assembly_format: RS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1186
+ symbol: C_SD
+ name: C_SD
+ assembly_mnemonic: c.sd
+ operands:
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, memory_role: address, name: RS1 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM8_A8, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ scheduling_class: STORE
+ encoding_category: C
+ assembly_format: RS2_OFFSET_RS1
+ requires_memory_operands: true
+ -
+ id: 1187
+ symbol: C_ADDI
+ name: C_ADDI
+ assembly_mnemonic: c.addi
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_SIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1188
+ symbol: C_ADDIW
+ name: C_ADDIW
+ assembly_mnemonic: c.addiw
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPR_NX0, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_SIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1189
+ symbol: C_LI
+ name: C_LI
+ assembly_mnemonic: c.li
+ operands:
+ - { kinds: [register], role: def, register_class: GPR_NX0, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_SIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1190
+ symbol: C_ADDI16SP
+ name: C_ADDI16SP
+ assembly_mnemonic: c.addi16sp
+ operands:
+ - { kinds: [immediate], immediate_predicate: C_SIMM10_A16_NZ, name: IMM }
+ num_explicit_defs: 0
+ flags: [produces_code]
+ implicit_defs: [{ register: X2 }]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: IMM_SP
+ -
+ id: 1191
+ symbol: C_LUI
+ name: C_LUI
+ assembly_mnemonic: c.lui
+ operands:
+ - { kinds: [register], role: def, register_class: GPR_NX0_NX2, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_SIMM6_NZ, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1192
+ symbol: C_SRLI
+ name: C_SRLI
+ assembly_mnemonic: c.srli
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1193
+ symbol: C_SRAI
+ name: C_SRAI
+ assembly_mnemonic: c.srai
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1194
+ symbol: C_ANDI
+ name: C_ANDI
+ assembly_mnemonic: c.andi
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_SIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1195
+ symbol: C_SUB
+ name: C_SUB
+ assembly_mnemonic: c.sub
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1196
+ symbol: C_XOR
+ name: C_XOR
+ assembly_mnemonic: c.xor
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1197
+ symbol: C_OR
+ name: C_OR
+ assembly_mnemonic: c.or
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1198
+ symbol: C_AND
+ name: C_AND
+ assembly_mnemonic: c.and
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1199
+ symbol: C_SUBW
+ name: C_SUBW
+ assembly_mnemonic: c.subw
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1200
+ symbol: C_ADDW
+ name: C_ADDW
+ assembly_mnemonic: c.addw
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPRC, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1201
+ symbol: C_J
+ name: C_J
+ assembly_mnemonic: c.j
+ operands:
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: TARGET
+ -
+ id: 1202
+ symbol: C_BEQZ
+ name: C_BEQZ
+ assembly_mnemonic: c.beqz
+ operands:
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS1 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RS1_TARGET
+ -
+ id: 1203
+ symbol: C_BNEZ
+ name: C_BNEZ
+ assembly_mnemonic: c.bnez
+ operands:
+ - { kinds: [register], role: use, register_class: GPRC, register_bank: GPR, name: RS1 }
+ - { kinds: [basic_block], name: TARGET }
+ num_explicit_defs: 0
+ flags: [terminator, branch, produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RS1_TARGET
+ -
+ id: 1204
+ symbol: C_SLLI
+ name: C_SLLI
+ assembly_mnemonic: c.slli
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPR_NX0, register_bank: GPR, name: RD }
+ - { kinds: [immediate], immediate_predicate: UIMM6, name: IMM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_IMM
+ -
+ id: 1205
+ symbol: C_FLDSP
+ name: C_FLDSP
+ assembly_mnemonic: c.fldsp
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, memory_role: value, name: FRD }
+ - { kinds: [immediate], immediate_predicate: C_UIMM9_A8, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: LOAD
+ encoding_category: C
+ assembly_format: FRD_OFFSET_SP
+ requires_memory_operands: true
+ -
+ id: 1206
+ symbol: C_LWSP
+ name: C_LWSP
+ assembly_mnemonic: c.lwsp
+ operands:
+ - { kinds: [register], role: def, register_class: GPR_NX0, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_UIMM8_A4, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: LOAD
+ encoding_category: C
+ assembly_format: RD_OFFSET_SP
+ requires_memory_operands: true
+ -
+ id: 1207
+ symbol: C_LDSP
+ name: C_LDSP
+ assembly_mnemonic: c.ldsp
+ operands:
+ - { kinds: [register], role: def, register_class: GPR_NX0, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [immediate], immediate_predicate: C_UIMM9_A8, name: IMM }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: LOAD
+ encoding_category: C
+ assembly_format: RD_OFFSET_SP
+ requires_memory_operands: true
+ -
+ id: 1208
+ symbol: C_JR
+ name: C_JR
+ assembly_mnemonic: c.jr
+ operands:
+ - { kinds: [register], role: use, register_class: GPR_NX0, register_bank: GPR, name: RS1 }
+ num_explicit_defs: 0
+ flags: [terminator, branch, indirect_branch, produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RS1
+ -
+ id: 1209
+ symbol: C_MV
+ name: C_MV
+ assembly_mnemonic: c.mv
+ operands:
+ - { kinds: [register], role: def, register_class: GPR_NX0, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR_NX0, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1210
+ symbol: C_EBREAK
+ name: C_EBREAK
+ assembly_mnemonic: c.ebreak
+ operands: []
+ num_explicit_defs: 0
+ flags: [has_side_effects, produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: NONE
+ -
+ id: 1211
+ symbol: C_JALR
+ name: C_JALR
+ assembly_mnemonic: c.jalr
+ operands:
+ - { kinds: [register], role: use, register_class: GPR_NX0, register_bank: GPR, name: RS1 }
+ num_explicit_defs: 0
+ flags: [terminator, branch, indirect_branch, produces_code]
+ implicit_defs: [{ register: X1 }]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RS1
+ -
+ id: 1212
+ symbol: C_ADD
+ name: C_ADD
+ assembly_mnemonic: c.add
+ operands:
+ - { kinds: [register], role: def_use, register_class: GPR_NX0, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR_NX0, register_bank: GPR, name: RS2 }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: COMPRESSED
+ encoding_category: C
+ assembly_format: RD_RS2
+ -
+ id: 1213
+ symbol: C_FSDSP
+ name: C_FSDSP
+ assembly_mnemonic: c.fsdsp
+ operands:
+ - { kinds: [register], role: use, register_class: FPR64, register_bank: FPR, memory_role: value, name: FRS2 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM9_A8, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: STORE
+ encoding_category: C
+ assembly_format: FRS2_OFFSET_SP
+ requires_memory_operands: true
+ -
+ id: 1214
+ symbol: C_SWSP
+ name: C_SWSP
+ assembly_mnemonic: c.swsp
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM8_A4, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: STORE
+ encoding_category: C
+ assembly_format: RS2_OFFSET_SP
+ requires_memory_operands: true
+ -
+ id: 1215
+ symbol: C_SDSP
+ name: C_SDSP
+ assembly_mnemonic: c.sdsp
+ operands:
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [immediate], immediate_predicate: C_UIMM9_A8, name: IMM }
+ num_explicit_defs: 0
+ flags: [may_store, produces_code]
+ implicit_uses: [{ register: X2 }]
+ scheduling_class: STORE
+ encoding_category: C
+ assembly_format: RS2_OFFSET_SP
+ requires_memory_operands: true
+ -
+ id: 1179
+ symbol: FCVT_D_S
+ name: FCVT_D_S
+ assembly_mnemonic: fcvt.d.s
+ operands:
+ - { kinds: [register], role: def, register_class: FPR64, register_bank: FPR, name: FRD }
+ - { kinds: [register], role: use, register_class: FPR32, register_bank: FPR, name: FRS1 }
+ - { kinds: [immediate], immediate_predicate: RM, name: RM }
+ num_explicit_defs: 1
+ flags: [produces_code]
+ scheduling_class: FP_CONVERT
+ encoding_category: R
+ assembly_format: FRD_FRS1_RM
+ -
+ id: 1099
+ symbol: AMOADD_W
+ name: AMOADD_W
+ assembly_mnemonic: amoadd.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1100
+ symbol: AMOXOR_W
+ name: AMOXOR_W
+ assembly_mnemonic: amoxor.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1101
+ symbol: AMOAND_W
+ name: AMOAND_W
+ assembly_mnemonic: amoand.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1102
+ symbol: AMOOR_W
+ name: AMOOR_W
+ assembly_mnemonic: amoor.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1103
+ symbol: AMOMIN_W
+ name: AMOMIN_W
+ assembly_mnemonic: amomin.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1104
+ symbol: AMOMAX_W
+ name: AMOMAX_W
+ assembly_mnemonic: amomax.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1105
+ symbol: AMOMINU_W
+ name: AMOMINU_W
+ assembly_mnemonic: amominu.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1106
+ symbol: AMOMAXU_W
+ name: AMOMAXU_W
+ assembly_mnemonic: amomaxu.w
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1107
+ symbol: LR_D
+ name: LR_D
+ assembly_mnemonic: lr.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1108
+ symbol: SC_D
+ name: SC_D
+ assembly_mnemonic: sc.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1109
+ symbol: AMOSWAP_D
+ name: AMOSWAP_D
+ assembly_mnemonic: amoswap.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1110
+ symbol: AMOADD_D
+ name: AMOADD_D
+ assembly_mnemonic: amoadd.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1111
+ symbol: AMOXOR_D
+ name: AMOXOR_D
+ assembly_mnemonic: amoxor.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1112
+ symbol: AMOAND_D
+ name: AMOAND_D
+ assembly_mnemonic: amoand.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1113
+ symbol: AMOOR_D
+ name: AMOOR_D
+ assembly_mnemonic: amoor.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1114
+ symbol: AMOMIN_D
+ name: AMOMIN_D
+ assembly_mnemonic: amomin.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1115
+ symbol: AMOMAX_D
+ name: AMOMAX_D
+ assembly_mnemonic: amomax.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1116
+ symbol: AMOMINU_D
+ name: AMOMINU_D
+ assembly_mnemonic: amominu.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1117
+ symbol: AMOMAXU_D
+ name: AMOMAXU_D
+ assembly_mnemonic: amomaxu.d
+ operands:
+ - { kinds: [register], role: def, register_class: GPR, register_bank: GPR, memory_role: value, name: RD }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: value, name: RS2 }
+ - { kinds: [register], role: use, register_class: GPR, register_bank: GPR, memory_role: address, name: RS1 }
+ num_explicit_defs: 1
+ flags: [may_load, may_store, produces_code]
+ scheduling_class: ATOMIC
+ encoding_category: AMO
+ assembly_format: RD_RS2_PAREN_RS1
+ requires_memory_operands: true
+ -
+ id: 1216
+ symbol: PseudoCC_COPY
+ name: PseudoCC_COPY
+ operands:
+ - { kinds: [register], role: def }
+ - { kinds: [register], role: use }
+ - { kinds: [immediate] }
+ num_explicit_defs: 1
+ flags: [pseudo, produces_code, allowed_pre_legalizer]
+ owner: instruction_select
+ deadline: selected
+ -
+ id: 1217
+ symbol: PseudoLOAD_STACK_ARG
+ name: PseudoLOAD_STACK_ARG
+ operands:
+ - { kinds: [register], role: def, memory_role: value }
+ - { kinds: [frame_index] }
+ - { kinds: [immediate, integer_immediate] }
+ - { kinds: [immediate] }
+ num_explicit_defs: 1
+ flags: [pseudo, produces_code, allowed_pre_legalizer, may_load]
+ requires_memory_operands: true
+ owner: instruction_select
+ deadline: selected
+ -
+ id: 1218
+ symbol: PseudoSTORE_STACK_ARG
+ name: PseudoSTORE_STACK_ARG
+ operands:
+ - { kinds: [register], role: use, memory_role: value }
+ - { kinds: [frame_index] }
+ - { kinds: [immediate, integer_immediate] }
+ - { kinds: [immediate] }
+ num_explicit_defs: 0
+ flags: [pseudo, produces_code, allowed_pre_legalizer, may_store]
+ requires_memory_operands: true
+ owner: instruction_select
+ deadline: selected
+ -
+ id: 1219
+ symbol: PseudoBYVAL_COPY
+ name: PseudoBYVAL_COPY
+ operands:
+ - { kinds: [register], role: def }
+ - { kinds: [register], role: use, memory_role: address }
+ - { kinds: [frame_index] }
+ - { kinds: [immediate, integer_immediate] }
+ - { kinds: [immediate, integer_immediate] }
+ num_explicit_defs: 1
+ flags: [pseudo, produces_code, allowed_pre_legalizer, may_load, may_store]
+ requires_memory_operands: true
+ owner: instruction_select
+ deadline: selected
+ -
+ id: 1220
+ symbol: ADJCALLSTACKDOWN
+ name: ADJCALLSTACKDOWN
+ operands:
+ - { kinds: [immediate, integer_immediate] }
+ - { kinds: [immediate, integer_immediate] }
+ num_explicit_defs: 0
+ flags: [pseudo, allowed_pre_legalizer, has_side_effects]
+ owner: prologue_epilogue_insertion
+ deadline: frame_finalized
+ -
+ id: 1221
+ symbol: ADJCALLSTACKUP
+ name: ADJCALLSTACKUP
+ operands:
+ - { kinds: [immediate, integer_immediate] }
+ - { kinds: [immediate, integer_immediate] }
+ num_explicit_defs: 0
+ flags: [pseudo, allowed_pre_legalizer, has_side_effects]
+ owner: prologue_epilogue_insertion
+ deadline: frame_finalized
+ -
+ id: 1222
+ symbol: PseudoCALL
+ name: PseudoCALL
+ operands:
+ - { kinds: [global_symbol, external_symbol, register], role: use, allowed_target_flags: 1 }
+ - { kinds: [immediate, integer_immediate] }
+ - { kinds: [register_mask] }
+ num_explicit_defs: 0
+ implicit_defs: [{ register: X1 }]
+ allow_extra_implicit_operands: true
+ flags: [pseudo, produces_code, allowed_pre_legalizer, call, may_load, may_store, has_side_effects, not_duplicable, unmodeled_side_effects]
+ owner: post_ra_expansion
+ deadline: post_ra_finalized
+ -
+ id: 1223
+ symbol: PseudoTAIL
+ name: PseudoTAIL
+ operands:
+ - { kinds: [global_symbol, external_symbol, register], role: use, allowed_target_flags: 1 }
+ - { kinds: [immediate, integer_immediate] }
+ - { kinds: [register_mask] }
+ num_explicit_defs: 0
+ allow_extra_implicit_operands: true
+ flags: [pseudo, produces_code, allowed_pre_legalizer, terminator, branch, call, barrier, may_load, may_store, has_side_effects, not_duplicable, unmodeled_side_effects]
+ owner: post_ra_expansion
+ deadline: post_ra_finalized
+ -
+ id: 1224
+ symbol: PseudoRET
+ name: PseudoRET
+ operands: []
+ num_explicit_defs: 0
+ implicit_uses: [{ register: X1 }]
+ allow_extra_implicit_operands: true
+ flags: [pseudo, produces_code, allowed_pre_legalizer, terminator, return, barrier]
+ owner: post_ra_expansion
+ deadline: post_ra_finalized
diff --git a/backend/lib/Target/RISCV/RISCVTarget.cpp b/backend/lib/Target/RISCV/RISCVTarget.cpp
index bbad827..8b3741a 100644
--- a/backend/lib/Target/RISCV/RISCVTarget.cpp
+++ b/backend/lib/Target/RISCV/RISCVTarget.cpp
@@ -2,11 +2,15 @@
#include "rcc/Target/RISCV/RISCVTarget.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+
+#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <memory>
+#include <span>
#include <utility>
#include <vector>
@@ -93,6 +97,27 @@ Expected<TargetMIRTextInfo> createMIRTextInfo() {
return TargetMIRTextInfo::create(std::move(Description), *Registers);
}
+Expected<TargetDescription> createTargetDescription() {
+ auto Registers = createRegisterInfo();
+ if (!Registers)
+ return std::unexpected(Registers.error());
+
+ std::vector<InstructionDescriptor> Descriptors;
+ std::vector<rcc::ImmediatePredicate> Predicates;
+ appendGeneratedInstructionInfo(Descriptors, Predicates);
+ auto GenericInfo = appendGenericInstructionInfo(Descriptors, Predicates);
+ if (!GenericInfo)
+ return std::unexpected(GenericInfo.error());
+ auto Instructions = InstructionSet::create(std::move(Descriptors), std::move(Predicates));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+
+ TargetMIRTextInfoDesc MIRText;
+ appendGeneratedMIRTextInfo(MIRText);
+ return TargetDescription::create(std::move(*Instructions), std::move(*Registers),
+ std::move(MIRText));
+}
+
std::shared_ptr<const RegisterMask> createCallPreservedMask() {
TargetMIRTextInfoDesc Description;
appendGeneratedMIRTextInfo(Description);
@@ -100,4 +125,40 @@ std::shared_ptr<const RegisterMask> createCallPreservedMask() {
return Description.RegisterMasks.front().Mask;
}
+std::span<const SchedulingClassInfo> schedulingClasses() noexcept {
+ return GeneratedSchedulingClasses;
+}
+
+std::span<const EncodingCategoryInfo> encodingCategories() noexcept {
+ return GeneratedEncodingCategories;
+}
+
+std::span<const AssemblyFormatInfo> assemblyFormats() noexcept {
+ return GeneratedAssemblyFormats;
+}
+
+std::span<const AssemblyInstructionInfo> assemblyInstructions() noexcept {
+ return GeneratedAssemblyInstructions;
+}
+
+const AssemblyInstructionInfo *assemblyInstruction(Opcode InstructionOpcode) noexcept {
+ for (const AssemblyInstructionInfo &Info : GeneratedAssemblyInstructions) {
+ if (Info.InstructionOpcode == InstructionOpcode)
+ return &Info;
+ }
+ return nullptr;
+}
+
+std::span<const CallingConventionInfo> callingConventions() noexcept {
+ return GeneratedCallingConventions;
+}
+
+const CallingConventionInfo *callingConvention(CallingConvention ID) noexcept {
+ for (const CallingConventionInfo &Info : GeneratedCallingConventions) {
+ if (Info.ID == ID)
+ return &Info;
+ }
+ return nullptr;
+}
+
} // namespace rcc::riscv
diff --git a/backend/tools/DescriptionGenerator/DescriptionGeneratorMain.cpp b/backend/tools/DescriptionGenerator/DescriptionGeneratorMain.cpp
index 11890f6..80b4767 100644
--- a/backend/tools/DescriptionGenerator/DescriptionGeneratorMain.cpp
+++ b/backend/tools/DescriptionGenerator/DescriptionGeneratorMain.cpp
@@ -14,14 +14,16 @@ namespace {
struct CommandLine final {
std::string InputPath;
- std::string HeaderPath;
+ std::string RegisterHeaderPath;
+ std::string InstructionHeaderPath;
std::string TablesPath;
};
[[nodiscard]] bool parseCommandLine(int ArgumentCount, char *Arguments[], CommandLine &Result) {
for (int Index = 1; Index < ArgumentCount; ++Index) {
const std::string_view Argument(Arguments[Index]);
- if (Argument == "--input" || Argument == "--header" || Argument == "--tables") {
+ if (Argument == "--input" || Argument == "--register-header" ||
+ Argument == "--instruction-header" || Argument == "--tables") {
if (Index + 1 >= ArgumentCount) {
std::cerr << "rcc-backend-descgen: missing value for " << Argument << '\n';
return false;
@@ -29,8 +31,10 @@ struct CommandLine final {
std::string *Destination = nullptr;
if (Argument == "--input")
Destination = &Result.InputPath;
- else if (Argument == "--header")
- Destination = &Result.HeaderPath;
+ else if (Argument == "--register-header")
+ Destination = &Result.RegisterHeaderPath;
+ else if (Argument == "--instruction-header")
+ Destination = &Result.InstructionHeaderPath;
else
Destination = &Result.TablesPath;
*Destination = Arguments[++Index];
@@ -39,9 +43,12 @@ struct CommandLine final {
std::cerr << "rcc-backend-descgen: unknown argument '" << Argument << "'\n";
return false;
}
- if (!Result.InputPath.empty() && !Result.HeaderPath.empty() && !Result.TablesPath.empty())
+ if (!Result.InputPath.empty() && !Result.RegisterHeaderPath.empty() &&
+ !Result.InstructionHeaderPath.empty() && !Result.TablesPath.empty()) {
return true;
- std::cerr << "usage: rcc-backend-descgen --input <file> --header <file> --tables <file>\n";
+ }
+ std::cerr << "usage: rcc-backend-descgen --input <file> --register-header <file> "
+ "--instruction-header <file> --tables <file>\n";
return false;
}
@@ -90,7 +97,8 @@ int main(int ArgumentCount, char *Arguments[]) {
return 1;
}
- return writeFile(Command.HeaderPath, Result.Output->RegisterHeader) &&
+ return writeFile(Command.RegisterHeaderPath, Result.Output->RegisterHeader) &&
+ writeFile(Command.InstructionHeaderPath, Result.Output->InstructionHeader) &&
writeFile(Command.TablesPath, Result.Output->TargetTables)
? 0
: 1;
diff --git a/backend/unittests/Target/DescriptionGeneratorTest.cpp b/backend/unittests/Target/DescriptionGeneratorTest.cpp
index 0ba52b2..d41d2d4 100644
--- a/backend/unittests/Target/DescriptionGeneratorTest.cpp
+++ b/backend/unittests/Target/DescriptionGeneratorTest.cpp
@@ -2,6 +2,7 @@
#include "TargetDescriptionGenerator.h"
+#include <cstddef>
#include <iostream>
#include <string>
#include <string_view>
@@ -26,7 +27,7 @@ private:
};
[[nodiscard]] std::string validDescription(std::string_view Aliases = "[acc]") {
- return std::string(R"(schema: rcc-target-description-v1
+ return std::string(R"(schema: rcc-target-description-v2
target:
namespace: tiny
name: Tiny
@@ -46,6 +47,25 @@ register_classes:
- { id: 1, symbol: GPR64, name: GPR64, bank: GPR, size_bits: 64, spill_size_bits: 64, spill_alignment_bits: 64, lanes: 1, registers: [R1], allocation_order: [R1] }
register_masks:
- { name: CSR_TINY, preserved: [R1] }
+mir_operand_flags:
+ direct_mask: 31
+ direct: [{ name: tiny-call, value: 1 }]
+ bitmask: [{ name: tiny-relax, value: 32 }]
+calling_conventions:
+ - { id: 1, symbol: TINY, name: tiny, integer_register_class: GPR64, floating_register_class: GPR64, integer_argument_registers: [R1], floating_argument_registers: [R1], integer_return_registers: [R1], floating_return_registers: [R1], preserved_mask: CSR_TINY, stack_alignment_bytes: 8, stack_argument_alignment_bytes: 8, shadow_space_bytes: 0, supports_varargs: true, supports_tail_calls: true }
+named_operands:
+ - { id: 1, symbol: RD, name: rd }
+ - { id: 2, symbol: IMM, name: imm }
+immediate_predicates:
+ - { id: 1, symbol: SIMM8, name: simm8, kind: signed, bits: 8 }
+scheduling_classes:
+ - { id: 1, symbol: ALU, name: alu, micro_ops: 1, latency: 1 }
+encoding_categories:
+ - { id: 1, symbol: I, name: i, size_bytes: 4 }
+assembly_formats:
+ - { id: 1, symbol: RD_IMM, name: rd-imm }
+instructions:
+ - { id: 1024, symbol: LOAD_IMM, name: LOAD_IMM, assembly_mnemonic: load.imm, operands: [{ kinds: [register], role: def, register_class: GPR64, register_bank: GPR, name: RD }, { kinds: [immediate], immediate_predicate: SIMM8, name: IMM, allowed_target_flags: 33 }], num_explicit_defs: 1, flags: [produces_code], scheduling_class: ALU, encoding_category: I, assembly_format: RD_IMM }
)";
}
@@ -62,14 +82,25 @@ void testGeneration(TestContext &Context) {
return;
Context.expect(First.Output->RegisterHeader == Second.Output->RegisterHeader &&
+ First.Output->InstructionHeader == Second.Output->InstructionHeader &&
First.Output->TargetTables == Second.Output->TargetTables,
"target description generation must be deterministic");
Context.expect(
First.Output->RegisterHeader.find("enum class PhysicalRegisterID") != std::string::npos &&
First.Output->RegisterHeader.find("R1 = 1") != std::string::npos &&
+ First.Output->InstructionHeader.find("enum class InstructionOpcode") !=
+ std::string::npos &&
+ First.Output->InstructionHeader.find("LOAD_IMM = 1024") != std::string::npos &&
First.Output->TargetTables.find("appendGeneratedRegisterInfo") != std::string::npos &&
+ First.Output->TargetTables.find("appendGeneratedInstructionInfo") != std::string::npos &&
+ First.Output->TargetTables.find("GeneratedAssemblyInstructions") != std::string::npos &&
+ First.Output->TargetTables.find("Description.DirectOperandFlagMask = 31") !=
+ std::string::npos &&
+ First.Output->TargetTables.find("\"tiny-call\", 1") != std::string::npos &&
+ First.Output->TargetTables.find("\"load.imm\"") != std::string::npos &&
First.Output->TargetTables.find("makePreservedMask") != std::string::npos,
- "generated output must contain typed register IDs and static target tables");
+ "generated output must contain typed target IDs, assembly mnemonics, and static target "
+ "tables");
}
void testSemanticRejection(TestContext &Context) {
@@ -82,30 +113,82 @@ void testSemanticRejection(TestContext &Context) {
std::string::npos,
"an ABI alias that collides with a canonical name must be rejected");
- const std::string UnknownUnit = R"(schema: rcc-target-description-v1
-target:
- namespace: tiny
- name: Tiny
- triple: tiny-unknown-none
- cpu: tiny-cpu
- abi: tiny-abi
- data_layout: e-p:64:64
- features: [base]
-register_units:
- - { id: 1, symbol: U_R1, name: r1.unit }
-registers:
- - { id: 1, symbol: R1, name: r1, encoding: 0, dwarf_register: 0, units: [{ unit: U_MISSING, lanes: 1 }] }
-register_banks:
- - { id: 1, symbol: GPR, name: GPR, maximum_size_bits: 64 }
-register_classes:
- - { id: 1, symbol: GPR64, name: GPR64, bank: GPR, size_bits: 64, spill_size_bits: 64, spill_alignment_bits: 64, lanes: 1, registers: [R1], allocation_order: [R1] }
-register_masks: []
-)";
+ std::string UnknownUnit = validDescription();
+ constexpr std::string_view UnitReference = "unit: U_R1";
+ const std::size_t UnitPosition = UnknownUnit.find(UnitReference);
+ if (UnitPosition != std::string::npos)
+ UnknownUnit.replace(UnitPosition, UnitReference.size(), "unit: U_MISSING");
const auto BadUnit = rcc::targetgen::generateDescription("bad-unit.rccdesc", UnknownUnit);
Context.expect(!BadUnit.succeeded() && !BadUnit.Diagnostics.empty() &&
BadUnit.Diagnostics.front().Message.find("declared units") !=
std::string::npos,
"a register unit reference must resolve during semantic validation");
+
+ std::string UnknownClass = validDescription();
+ constexpr std::string_view ClassReference = "register_class: GPR64";
+ const std::size_t ClassPosition = UnknownClass.find(ClassReference);
+ if (ClassPosition != std::string::npos)
+ UnknownClass.replace(ClassPosition, ClassReference.size(), "register_class: MISSING");
+ const auto BadClass =
+ rcc::targetgen::generateDescription("bad-instruction-class.rccdesc", UnknownClass);
+ Context.expect(!BadClass.succeeded() && !BadClass.Diagnostics.empty() &&
+ BadClass.Diagnostics.front().Message.find("register class") !=
+ std::string::npos,
+ "instruction register classes must resolve during semantic validation");
+
+ std::string BadAllowedValue = validDescription();
+ constexpr std::string_view Predicate = "kind: signed, bits: 8";
+ const std::size_t PredicatePosition = BadAllowedValue.find(Predicate);
+ if (PredicatePosition != std::string::npos) {
+ BadAllowedValue.replace(PredicatePosition, Predicate.size(),
+ "kind: signed, bits: 8, allowed_values: [128]");
+ }
+ const auto InvalidAllowedValue =
+ rcc::targetgen::generateDescription("bad-allowed-value.rccdesc", BadAllowedValue);
+ Context.expect(!InvalidAllowedValue.succeeded() && !InvalidAllowedValue.Diagnostics.empty() &&
+ InvalidAllowedValue.Diagnostics.front().Message.find("allowed_values") !=
+ std::string::npos,
+ "immediate predicates must reject allowed values outside their signed range");
+
+ std::string ContradictoryNonZero = validDescription();
+ if (PredicatePosition != std::string::npos) {
+ const std::size_t Position = ContradictoryNonZero.find(Predicate);
+ ContradictoryNonZero.replace(Position, Predicate.size(),
+ "kind: signed, bits: 8, nonzero: true, allowed_values: [0]");
+ }
+ const auto InvalidNonZero =
+ rcc::targetgen::generateDescription("bad-nonzero.rccdesc", ContradictoryNonZero);
+ Context.expect(!InvalidNonZero.succeeded() && !InvalidNonZero.Diagnostics.empty() &&
+ InvalidNonZero.Diagnostics.front().Message.find("allowed_values") !=
+ std::string::npos,
+ "nonzero immediate predicates must reject an explicit zero value");
+
+ std::string BadStackAlignment = validDescription();
+ constexpr std::string_view StackAlignment =
+ "stack_alignment_bytes: 8, stack_argument_alignment_bytes: 8";
+ const std::size_t StackPosition = BadStackAlignment.find(StackAlignment);
+ if (StackPosition != std::string::npos) {
+ BadStackAlignment.replace(StackPosition, StackAlignment.size(),
+ "stack_alignment_bytes: 8, stack_argument_alignment_bytes: 16");
+ }
+ const auto InvalidStackAlignment =
+ rcc::targetgen::generateDescription("bad-stack-alignment.rccdesc", BadStackAlignment);
+ Context.expect(!InvalidStackAlignment.succeeded() && !InvalidStackAlignment.Diagnostics.empty() &&
+ InvalidStackAlignment.Diagnostics.front().Message.find("stack alignments") !=
+ std::string::npos,
+ "calling conventions must reject incompatible stack alignments");
+
+ std::string BadMnemonic = validDescription();
+ constexpr std::string_view Mnemonic = "assembly_mnemonic: load.imm";
+ const std::size_t MnemonicPosition = BadMnemonic.find(Mnemonic);
+ if (MnemonicPosition != std::string::npos)
+ BadMnemonic.replace(MnemonicPosition, Mnemonic.size(), "assembly_mnemonic: .load");
+ const auto InvalidMnemonic =
+ rcc::targetgen::generateDescription("bad-mnemonic.rccdesc", BadMnemonic);
+ Context.expect(!InvalidMnemonic.succeeded() && !InvalidMnemonic.Diagnostics.empty() &&
+ InvalidMnemonic.Diagnostics.front().Message.find("assembly mnemonics") !=
+ std::string::npos,
+ "assembly mnemonics must use a canonical lowercase spelling");
}
} // namespace
diff --git a/backend/unittests/Target/RISCVTargetTest.cpp b/backend/unittests/Target/RISCVTargetTest.cpp
index 27a862d..98525e9 100644
--- a/backend/unittests/Target/RISCVTargetTest.cpp
+++ b/backend/unittests/Target/RISCVTargetTest.cpp
@@ -2,6 +2,9 @@
#include "rcc/Target/RISCV/RISCVTarget.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+
+#include <algorithm>
#include <cstdint>
#include <iostream>
#include <ranges>
@@ -47,8 +50,8 @@ void testRegisterFacts(TestContext &Context) {
Context.expect(
Registers->physicalRegisters().size() == 64 && Registers->registerUnits().size() == 64 &&
- Registers->registerClasses().size() == 6 && Registers->registerBanks().size() == 2,
- "RV64GC must expose the complete scalar integer and floating register files");
+ Registers->registerClasses().size() == 8 && Registers->registerBanks().size() == 2,
+ "RV64GC must expose the complete scalar, compressed, and restricted register classes");
Context.expect(Registers->physicalRegister("x0") != nullptr &&
Registers->physicalRegister("zero") == nullptr &&
Registers->physicalRegisterByAlternativeName("ZERO") != nullptr &&
@@ -71,6 +74,9 @@ void testRegisterFacts(TestContext &Context) {
std::ranges::find(GPR->AllocationOrder, X2) == GPR->AllocationOrder.end() &&
std::ranges::find(GPR->AllocationOrder, X5) != GPR->AllocationOrder.end(),
"the general-register allocation order must exclude statically reserved registers");
+ Context.expect(Registers->registerClass("gpr_nx0") != nullptr &&
+ Registers->registerClass("gpr_nx0_nx2") != nullptr,
+ "compressed instructions must have explicit nonzero register classes");
}
void testABIAndDebugFacts(TestContext &Context) {
@@ -93,10 +99,96 @@ void testABIAndDebugFacts(TestContext &Context) {
Context.expect(MIRText->registerMasks().size() == 1 &&
MIRText->registerMasks().front().Name == "csr_ilp32d_lp64d",
"RV64GC must expose its named LP64D call-preserved mask");
+ Context.expect(MIRText->directOperandFlagMask() == 31 &&
+ MIRText->directOperandFlag("riscv-call") == 1,
+ "RV64GC MIR target flags must expose the canonical direct-call spelling");
Context.expect(MIRText->dwarfRegister(X1) == 1,
"the return-address register must retain its DWARF register number");
Context.expect(MIRText->physicalRegister(63) == F31,
"the final floating register DWARF number must map to f31");
+
+ const auto Conventions = rcc::riscv::callingConventions();
+ const auto *LP64D = rcc::riscv::callingConvention(rcc::riscv::CallingConvention::LP64D);
+ Context.expect(
+ Conventions.size() == 1 && LP64D != nullptr && LP64D->Name == "lp64d" &&
+ LP64D->IntegerArgumentRegisters.size() == 8 &&
+ LP64D->FloatingArgumentRegisters.size() == 8 &&
+ LP64D->IntegerReturnRegisters.size() == 2 && LP64D->FloatingReturnRegisters.size() == 2 &&
+ LP64D->StackAlignmentBytes == 16 && LP64D->StackArgumentAlignmentBytes == 16 &&
+ LP64D->SupportsVarArgs && LP64D->SupportsTailCalls &&
+ LP64D->PreservedMaskName == "CSR_ILP32D_LP64D",
+ "LP64D calling-convention tables must expose register, stack, and tail-call facts");
+}
+
+void testInstructionFacts(TestContext &Context) {
+ auto Target = rcc::riscv::createTargetDescription();
+ Context.expect(Target.has_value(), "RV64GC target description must be structurally valid");
+ if (!Target) {
+ std::cerr << "target construction error: " << Target.error().message() << '\n';
+ return;
+ }
+
+ const auto *Addi = Target->instructionDescriptor(
+ rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::ADDI)));
+ const auto *Load = Target->instructionDescriptor(
+ rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::LD)));
+ const auto *Branch = Target->instructionDescriptor(
+ rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::BEQ)));
+ const auto *Floating = Target->instructionDescriptor(
+ rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::FADD_S)));
+ const auto *Atomic = Target->instructionDescriptor(
+ rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::LR_W)));
+ const auto *Compressed = Target->instructionDescriptor(rcc::Opcode::fromValue(
+ static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::C_ADDI16SP)));
+ const auto *GenericAdd = Target->instructionDescriptor(rcc::opcode(rcc::GenericOpcode::G_ADD));
+ const auto *PseudoCall = Target->instructionDescriptor(rcc::Opcode::fromValue(
+ static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::PseudoCALL)));
+ const auto *Assembly = rcc::riscv::assemblyInstruction(
+ rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::ADDI)));
+ const auto *FenceAssembly = rcc::riscv::assemblyInstruction(
+ rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::FENCE_I)));
+ Context.expect(
+ Target->instructions().descriptors().size() ==
+ 201 + rcc::genericInstructionDescriptors().size() &&
+ Addi != nullptr && Addi->Name == "ADDI" && Addi->NumExplicitDefs == 1 &&
+ Addi->Operands.size() == 3 && Addi->Operands.back().ImmediatePredicate &&
+ Load != nullptr && Load->Flags.has(rcc::InstructionFlag::MayLoad) &&
+ Load->RequiresMemoryOperands && Branch != nullptr &&
+ Branch->Flags.has(rcc::InstructionFlag::Terminator) &&
+ Branch->Flags.has(rcc::InstructionFlag::Branch) && Floating != nullptr &&
+ Floating->Operands.size() == 4 && Floating->Operands.back().ImmediatePredicate &&
+ Atomic != nullptr && Atomic->Flags.has(rcc::InstructionFlag::MayLoad) &&
+ Atomic->RequiresMemoryOperands && Compressed != nullptr &&
+ Compressed->ImplicitDefs.size() == 1 && Compressed->ImplicitUses.size() == 1 &&
+ GenericAdd != nullptr && GenericAdd->Flags.has(rcc::InstructionFlag::Generic) &&
+ PseudoCall != nullptr && PseudoCall->Flags.has(rcc::InstructionFlag::Pseudo) &&
+ PseudoCall->Flags.has(rcc::InstructionFlag::Call) &&
+ rcc::riscv::assemblyInstruction(PseudoCall->InstructionOpcode) == nullptr &&
+ Assembly != nullptr && Assembly->Mnemonic == "addi" &&
+ Assembly->Format == rcc::AssemblyFormatID::fromValue(static_cast<std::uint32_t>(
+ rcc::riscv::AssemblyFormat::RD_RS1_IMM)),
+ "generated instruction and assembly descriptors must drive the formal RV64GC target");
+
+ const auto Schedules = rcc::riscv::schedulingClasses();
+ const auto Encodings = rcc::riscv::encodingCategories();
+ const auto Formats = rcc::riscv::assemblyFormats();
+ Context.expect(Schedules.size() == 15 && Encodings.size() == 10 && Formats.size() == 34 &&
+ rcc::riscv::assemblyInstructions().size() == 192 && FenceAssembly != nullptr &&
+ FenceAssembly->Mnemonic == "fence.i" &&
+ std::ranges::any_of(Schedules,
+ [](const rcc::riscv::SchedulingClassInfo &Class) {
+ return Class.Name == "load" && Class.MicroOps == 1 &&
+ Class.Latency == 3;
+ }) &&
+ std::ranges::any_of(Encodings,
+ [](const rcc::riscv::EncodingCategoryInfo &Category) {
+ return Category.Name == "i" && Category.SizeInBytes == 4;
+ }) &&
+ std::ranges::any_of(Formats,
+ [](const rcc::riscv::AssemblyFormatInfo &Format) {
+ return Format.Name == "rd-rs1-imm";
+ }),
+ "generated scheduling, encoding, and assembly tables must preserve target facts");
}
} // namespace
@@ -105,5 +197,6 @@ int main() {
TestContext Context;
testRegisterFacts(Context);
testABIAndDebugFacts(Context);
+ testInstructionFacts(Context);
return Context.result();
}