aboutsummaryrefslogtreecommitdiff
path: root/backend/lib
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib')
-rw-r--r--backend/lib/AsmPrinter/AsmPrinter.cpp3
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt1
-rw-r--r--backend/lib/CodeGen/CompilationUnit.cpp39
-rw-r--r--backend/lib/CodeGen/InstructionDescriptor.cpp8
-rw-r--r--backend/lib/CodeGen/MachineFunction.cpp2
-rw-r--r--backend/lib/CodeGen/MachineFunctionCloner.cpp8
-rw-r--r--backend/lib/CodeGen/MachineMemOperand.cpp40
-rw-r--r--backend/lib/CodeGen/MachineOperand.cpp59
-rw-r--r--backend/lib/CodeGen/MachinePseudoExpansion.cpp22
-rw-r--r--backend/lib/CodeGen/MachineRegisterInfo.cpp1
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp58
-rw-r--r--backend/lib/MIR/EmbeddedIR.cpp121
-rw-r--r--backend/lib/MIR/LLVMConstantSyntax.cpp279
-rw-r--r--backend/lib/MIR/MIDebugSyntax.cpp179
-rw-r--r--backend/lib/MIR/MIParser.cpp425
-rw-r--r--backend/lib/MIR/MIPrinter.cpp351
-rw-r--r--backend/lib/MIR/MIRDataParser.cpp2
-rw-r--r--backend/lib/MIR/MIRDocumentParser.cpp46
-rw-r--r--backend/lib/MIR/MIRDocumentParser.h4
-rw-r--r--backend/lib/MIR/MIRDocumentPrinter.cpp261
-rw-r--r--backend/lib/MIR/MIRDocumentReferenceValidation.cpp18
-rw-r--r--backend/lib/MIR/MIRFrameParser.cpp12
-rw-r--r--backend/lib/MIR/MIRFunctionParser.cpp50
-rw-r--r--backend/lib/MIR/MIRFunctionRecordsParser.cpp68
-rw-r--r--backend/lib/MIR/MIRMetadataParser.cpp242
-rw-r--r--backend/lib/MIR/MIRMetadataTextParser.cpp10
-rw-r--r--backend/lib/MIR/MIRMetadataTextParser.h4
-rw-r--r--backend/lib/Support/TextDiagnostic.cpp7
-rw-r--r--backend/lib/Support/YAML.cpp40
-rw-r--r--backend/lib/Tooling/PipelineDriver.cpp2
30 files changed, 1739 insertions, 623 deletions
diff --git a/backend/lib/AsmPrinter/AsmPrinter.cpp b/backend/lib/AsmPrinter/AsmPrinter.cpp
index 49388e1..1f237af 100644
--- a/backend/lib/AsmPrinter/AsmPrinter.cpp
+++ b/backend/lib/AsmPrinter/AsmPrinter.cpp
@@ -402,7 +402,8 @@ Expected<void> AsmPrinter::emitFunction(const MachineFunction &Function) {
if (!Type)
return std::unexpected(Type.error());
}
- if (const auto Alignment = Function.attributes().alignment()) {
+ if (const auto Alignment = Function.attributes().alignment();
+ Alignment && Alignment->bytes() > 1) {
auto Aligned = Streamer->emitAlignment(Alignment->bytes());
if (!Aligned)
return std::unexpected(Aligned.error());
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt
index 15f1772..d25d164 100644
--- a/backend/lib/CodeGen/CMakeLists.txt
+++ b/backend/lib/CodeGen/CMakeLists.txt
@@ -88,6 +88,7 @@ set(
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GenericConstant.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/FrameIndexElimination.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InstructionDescriptor.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InlineAsm.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/LowLevelType.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineBasicBlockAttributes.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineCFIInfo.h"
diff --git a/backend/lib/CodeGen/CompilationUnit.cpp b/backend/lib/CodeGen/CompilationUnit.cpp
index 5ec54bc..a017851 100644
--- a/backend/lib/CodeGen/CompilationUnit.cpp
+++ b/backend/lib/CodeGen/CompilationUnit.cpp
@@ -22,13 +22,11 @@ CompilationUnit &CompilationUnit::operator=(CompilationUnit &&) noexcept = defau
CompilationUnit::~CompilationUnit() = default;
Expected<MachineFunction *> CompilationUnit::createMachineFunction(std::string Name) {
- if (Name.empty())
- return makeUnexpected(ErrorCode::InvalidArgument, "machine function name cannot be empty");
- if (FunctionNameToIndex.contains(Name)) {
+ if (!Name.empty() && FunctionNameToIndex.contains(Name)) {
return makeUnexpected(ErrorCode::InvalidArgument,
"machine function name is already present in the compilation unit");
}
- if (GlobalNameToIndex.contains(Name)) {
+ if (!Name.empty() && GlobalNameToIndex.contains(Name)) {
return makeUnexpected(ErrorCode::InvalidArgument,
"machine function name conflicts with a global object");
}
@@ -42,7 +40,8 @@ Expected<MachineFunction *> CompilationUnit::createMachineFunction(std::string N
MachineFunction *Result = Function->get();
const std::size_t Index = Functions.size();
- FunctionNameToIndex.emplace(std::string(Result->name()), Index);
+ if (!Result->name().empty())
+ FunctionNameToIndex.emplace(std::string(Result->name()), Index);
Functions.push_back(std::move(*Function));
return Result;
}
@@ -129,23 +128,20 @@ const GlobalObjectDesc *CompilationUnit::globalObject(std::string_view Name) con
}
Expected<void> CompilationUnit::verify() const {
- if (FunctionNameToIndex.size() != Functions.size()) {
- return makeUnexpected(ErrorCode::VerificationError,
- "compilation unit function name index has an inconsistent size");
- }
if (GlobalNameToIndex.size() != GlobalObjects.size()) {
return makeUnexpected(ErrorCode::VerificationError,
"compilation unit global object name index has an inconsistent size");
}
std::unordered_set<MachineFunctionStableID> StableIDs;
+ std::size_t NamedFunctionCount = 0;
for (std::size_t Index = 0; Index < Functions.size(); ++Index) {
const std::unique_ptr<MachineFunction> &Function = Functions[Index];
if (Function == nullptr) {
return makeUnexpected(ErrorCode::VerificationError,
"compilation unit contains a null machine function");
}
- if (Function->name().empty() || !Function->stableID().isValid()) {
+ if (!Function->stableID().isValid()) {
return makeUnexpected(ErrorCode::VerificationError,
"compilation unit contains an invalid machine function identity");
}
@@ -154,16 +150,23 @@ Expected<void> CompilationUnit::verify() const {
"compilation unit contains duplicate machine function StableIDs");
}
- const auto Position = FunctionNameToIndex.find(Function->name());
- if (Position == FunctionNameToIndex.end() || Position->second != Index) {
- return makeUnexpected(ErrorCode::VerificationError,
- "compilation unit function name index is inconsistent");
- }
- if (GlobalNameToIndex.contains(Function->name())) {
- return makeUnexpected(ErrorCode::VerificationError,
- "function name conflicts with a global object symbol");
+ if (!Function->name().empty()) {
+ ++NamedFunctionCount;
+ const auto Position = FunctionNameToIndex.find(Function->name());
+ if (Position == FunctionNameToIndex.end() || Position->second != Index) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "compilation unit function name index is inconsistent");
+ }
+ if (GlobalNameToIndex.contains(Function->name())) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "function name conflicts with a global object symbol");
+ }
}
}
+ if (FunctionNameToIndex.size() != NamedFunctionCount) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "compilation unit function name index has an inconsistent size");
+ }
for (std::size_t Index = 0; Index < GlobalObjects.size(); ++Index) {
const std::unique_ptr<GlobalObjectDesc> &Object = GlobalObjects[Index];
diff --git a/backend/lib/CodeGen/InstructionDescriptor.cpp b/backend/lib/CodeGen/InstructionDescriptor.cpp
index 48d4f10..fa00537 100644
--- a/backend/lib/CodeGen/InstructionDescriptor.cpp
+++ b/backend/lib/CodeGen/InstructionDescriptor.cpp
@@ -395,8 +395,12 @@ Expected<void> InstructionSet::validateExplicitOperands(const InstructionDescrip
}
const OperandDescriptor &OperandDesc = descriptorForOperand(Descriptor, *Shape, Index);
const auto Validation = validateOperand(OperandDesc, Operand, RegInfo);
- if (!Validation)
- return std::unexpected(Validation.error());
+ if (!Validation) {
+ return makeUnexpected(
+ Validation.error().code(),
+ "operand " + std::to_string(Index) + " of '" + Descriptor.Name + "': " +
+ std::string(Validation.error().message()));
+ }
if (Operand.isRegister()) {
if (isDefRole(OperandDesc.RegisterRole)) {
if (SawUse) {
diff --git a/backend/lib/CodeGen/MachineFunction.cpp b/backend/lib/CodeGen/MachineFunction.cpp
index 7f4c3a9..b870858 100644
--- a/backend/lib/CodeGen/MachineFunction.cpp
+++ b/backend/lib/CodeGen/MachineFunction.cpp
@@ -57,8 +57,6 @@ private:
Expected<std::unique_ptr<MachineFunction>>
MachineFunction::create(std::string Name, MachineFunctionStableID StableID) {
- if (Name.empty())
- return makeUnexpected(ErrorCode::InvalidArgument, "machine function name cannot be empty");
if (!StableID.isValid())
return makeUnexpected(ErrorCode::InvalidArgument, "machine function StableID must be valid");
return std::unique_ptr<MachineFunction>(new MachineFunction(std::move(Name), StableID));
diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp
index 0048194..1a66e66 100644
--- a/backend/lib/CodeGen/MachineFunctionCloner.cpp
+++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp
@@ -202,6 +202,14 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) {
if (!DestinationReg)
return std::unexpected(DestinationReg.error());
+ if (SourceInfo.Name) {
+ auto DestinationInfo =
+ State.Destination.registerInfo().virtualRegisterInfo(*DestinationReg);
+ if (!DestinationInfo)
+ return std::unexpected(DestinationInfo.error());
+ DestinationInfo->get().Name = SourceInfo.Name;
+ }
+
if (SourceInfo.Bank) {
auto SetResult =
State.Destination.registerInfo().setRegisterBank(*DestinationReg, *SourceInfo.Bank);
diff --git a/backend/lib/CodeGen/MachineMemOperand.cpp b/backend/lib/CodeGen/MachineMemOperand.cpp
index 95e7237..5f126fd 100644
--- a/backend/lib/CodeGen/MachineMemOperand.cpp
+++ b/backend/lib/CodeGen/MachineMemOperand.cpp
@@ -4,12 +4,22 @@
#include <algorithm>
#include <array>
+#include <bit>
#include <ranges>
#include <utility>
namespace rcc {
namespace {
+[[nodiscard]] std::uint64_t effectiveAlignment(std::uint64_t BaseAlignment,
+ std::int64_t Offset) noexcept {
+ if (Offset == 0)
+ return BaseAlignment;
+ const auto OffsetBits = static_cast<std::uint64_t>(Offset);
+ const std::uint64_t OffsetAlignment = std::uint64_t{1} << std::countr_zero(OffsetBits);
+ return std::min(BaseAlignment, OffsetAlignment);
+}
+
[[nodiscard]] bool isValidAtomicLoadOrdering(AtomicOrdering Ordering) noexcept {
return Ordering == AtomicOrdering::Unordered || Ordering == AtomicOrdering::Monotonic ||
Ordering == AtomicOrdering::Acquire || Ordering == AtomicOrdering::SequentiallyConsistent;
@@ -167,6 +177,17 @@ MemoryBase MemoryBase::irValue(std::string Name) {
return MemoryBase(Kind::IRValue, std::move(Name));
}
+Expected<MemoryBase> MemoryBase::irConstant(GenericConstantData Constant) {
+ auto Verification = Constant.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ if (Constant.Type.Kind != GenericConstantTypeKind::Pointer) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine memory IR constant base must have pointer type");
+ }
+ return MemoryBase(Kind::IRConstant, std::move(Constant));
+}
+
MemoryBase MemoryBase::stack() {
return MemoryBase(Kind::Stack, std::monostate{});
}
@@ -222,6 +243,12 @@ std::optional<std::string_view> MemoryBase::symbolName() const noexcept {
return std::get<std::string>(Value);
}
+const GenericConstantData *MemoryBase::constantValue() const noexcept {
+ if (BaseKind != Kind::IRConstant)
+ return nullptr;
+ return std::get_if<GenericConstantData>(&Value);
+}
+
Expected<MachineMemOperand> MachineMemOperand::create(MachineMemOperandDesc Description) {
const bool IsLoad = Description.Accesses.has(MemoryAccessFlag::Load);
const bool IsStore = Description.Accesses.has(MemoryAccessFlag::Store);
@@ -233,9 +260,16 @@ Expected<MachineMemOperand> MachineMemOperand::create(MachineMemOperandDesc Desc
return makeUnexpected(ErrorCode::InvalidArgument,
"machine memory operand address space must fit in 24 bits");
}
- if (Description.BaseAlign && Description.BaseAlign->bytes() < Description.Align.bytes()) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "machine memory operand base alignment cannot be smaller than alignment");
+ if (Description.BaseAlign) {
+ const std::uint64_t Effective =
+ effectiveAlignment(Description.BaseAlign->bytes(), Description.Offset);
+ if (Effective != Description.Align.bytes()) {
+ return makeUnexpected(
+ ErrorCode::InvalidArgument,
+ "machine memory operand alignment is inconsistent with base alignment and offset");
+ }
+ if (*Description.BaseAlign == Description.Align)
+ Description.BaseAlign.reset();
}
if (Description.Accesses.has(MemoryAccessFlag::Invariant) && (!IsLoad || IsStore)) {
return makeUnexpected(ErrorCode::InvalidArgument,
diff --git a/backend/lib/CodeGen/MachineOperand.cpp b/backend/lib/CodeGen/MachineOperand.cpp
index d1d152e..c81a7c3 100644
--- a/backend/lib/CodeGen/MachineOperand.cpp
+++ b/backend/lib/CodeGen/MachineOperand.cpp
@@ -121,31 +121,12 @@ Expected<IntegerImmediate> IntegerImmediate::parseDecimal(std::uint32_t BitWidth
Word = static_cast<std::uint64_t>(Product);
Carry = static_cast<std::uint64_t>(Product >> 64U);
}
- if (Carry != 0 || (Words.back() & ~integerHighWordMask(BitWidth)) != 0) {
- return makeUnexpected(ErrorCode::ParseError,
- "integer immediate does not fit its declared bit width");
- }
}
+ // LLVM integer literals are arbitrary precision and are extended or
+ // truncated to the declared bit width before becoming a constant.
+ Words.back() &= integerHighWordMask(BitWidth);
if (Negative) {
- if (std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; })) {
- return makeUnexpected(ErrorCode::ParseError,
- "negative zero is not a canonical integer immediate");
- }
- const std::uint32_t SignBit = BitWidth - 1U;
- const std::size_t SignWord = SignBit / 64U;
- const std::uint64_t SignMask = std::uint64_t{1} << (SignBit % 64U);
- bool Fits = Words[SignWord] <= SignMask;
- for (std::size_t Index = SignWord + 1; Index < Words.size(); ++Index)
- Fits = Fits && Words[Index] == 0;
- if (Words[SignWord] == SignMask) {
- for (std::size_t Index = 0; Index < SignWord; ++Index)
- Fits = Fits && Words[Index] == 0;
- }
- if (!Fits) {
- return makeUnexpected(ErrorCode::ParseError,
- "negative integer immediate does not fit its declared bit width");
- }
for (std::uint64_t &Word : Words)
Word = ~Word;
std::uint64_t Carry = 1;
@@ -186,6 +167,36 @@ std::string IntegerImmediate::toUnsignedDecimal() const {
return Digits;
}
+std::string IntegerImmediate::toSignedDecimal() const {
+ if (!isNegative())
+ return toUnsignedDecimal();
+
+ std::vector<std::uint64_t> Magnitude = Words;
+ for (std::uint64_t &Word : Magnitude)
+ Word = ~Word;
+ std::uint64_t Carry = 1;
+ for (std::uint64_t &Word : Magnitude) {
+ const std::uint64_t Previous = Word;
+ Word += Carry;
+ Carry = Carry != 0 && Word < Previous ? 1 : 0;
+ }
+ Magnitude.back() &= integerHighWordMask(BitWidth);
+
+ std::string Digits;
+ while (!std::ranges::all_of(Magnitude, [](std::uint64_t Word) { return Word == 0; })) {
+ unsigned __int128 Remainder = 0;
+ for (auto Iterator = Magnitude.rbegin(); Iterator != Magnitude.rend(); ++Iterator) {
+ const unsigned __int128 Dividend = (Remainder << 64U) | *Iterator;
+ *Iterator = static_cast<std::uint64_t>(Dividend / 10U);
+ Remainder = Dividend % 10U;
+ }
+ Digits.push_back(static_cast<char>('0' + static_cast<unsigned>(Remainder)));
+ }
+ std::ranges::reverse(Digits);
+ Digits.insert(Digits.begin(), '-');
+ return Digits;
+}
+
Expected<void> IntegerImmediate::verify() const {
return verifyIntegerImmediate(BitWidth, Words, ErrorCode::VerificationError);
}
@@ -324,10 +335,6 @@ Expected<MachineOperand> MachineOperand::globalSymbol(std::string Name, std::int
Expected<MachineOperand> MachineOperand::externalSymbol(std::string Name, std::int64_t Offset,
std::uint32_t TargetFlags) {
- if (Name.empty()) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "external symbol operand name cannot be empty");
- }
return MachineOperand(Kind::ExternalSymbol, ExternalSymbolOperand{std::move(Name), Offset},
TargetFlags);
}
diff --git a/backend/lib/CodeGen/MachinePseudoExpansion.cpp b/backend/lib/CodeGen/MachinePseudoExpansion.cpp
index 676804a..924c515 100644
--- a/backend/lib/CodeGen/MachinePseudoExpansion.cpp
+++ b/backend/lib/CodeGen/MachinePseudoExpansion.cpp
@@ -200,13 +200,20 @@ using BlockSizeSnapshot = std::vector<std::pair<MachineBlockStableID, std::uint6
return Error(Failure.code(), std::move(Prefix));
}
+[[nodiscard]] std::vector<MachineInstr *> layoutInstructions(MachineFunction &Function) {
+ std::vector<MachineInstr *> Result;
+ for (MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; Block = Block->next())
+ for (MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
+ Instruction = Instruction->next())
+ Result.push_back(Instruction);
+ return Result;
+}
+
[[nodiscard]] Expected<MachinePassResult>
prepareImplementation(MachineFunction &Function, const TargetDescription &Target,
const TargetPseudoExpansionInfo &Info, MachineActionReplay &Actions) {
bool Changed = false;
- for (MachineInstr *Instruction : Function.allInstructions()) {
- if (Instruction == nullptr || Instruction->parent() == nullptr)
- continue;
+ for (MachineInstr *Instruction : layoutInstructions(Function)) {
const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode());
if (Descriptor == nullptr)
return makeUnexpected(ErrorCode::VerificationError,
@@ -255,10 +262,7 @@ expandImplementation(MachineFunction &Function, const TargetDescription &Target,
SizesBefore = std::move(*Snapshot);
}
std::vector<MachineInstr *> Worklist;
- for (MachineInstr *Instruction : Function.allInstructions()) {
- if (Instruction != nullptr && Instruction->parent() != nullptr)
- Worklist.push_back(Instruction);
- }
+ Worklist = layoutInstructions(Function);
bool Changed = false;
for (MachineInstr *Instruction : Worklist) {
if (Instruction->parent() == nullptr)
@@ -325,9 +329,7 @@ expandImplementation(MachineFunction &Function, const TargetDescription &Target,
}
}
}
- for (MachineInstr *Instruction : Function.allInstructions()) {
- if (Instruction == nullptr || Instruction->parent() == nullptr)
- continue;
+ for (MachineInstr *Instruction : layoutInstructions(Function)) {
const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode());
if (Descriptor != nullptr && Descriptor->Flags.has(InstructionFlag::Pseudo) &&
Descriptor->Owner == ownerFor(Phase)) {
diff --git a/backend/lib/CodeGen/MachineRegisterInfo.cpp b/backend/lib/CodeGen/MachineRegisterInfo.cpp
index 7bd0ef3..2fe74d0 100644
--- a/backend/lib/CodeGen/MachineRegisterInfo.cpp
+++ b/backend/lib/CodeGen/MachineRegisterInfo.cpp
@@ -90,6 +90,7 @@ Expected<void> MachineRegisterInfo::eraseVirtualRegister(Register Reg) {
}
Info->get().IsTombstone = true;
+ Info->get().Name.reset();
Info->get().Type.reset();
Info->get().Bank.reset();
Info->get().RegClass.reset();
diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp
index be7462b..3d9fbdb 100644
--- a/backend/lib/CodeGen/MachineVerifier.cpp
+++ b/backend/lib/CodeGen/MachineVerifier.cpp
@@ -10,6 +10,7 @@
#include "rcc/CodeGen/MachineInstr.h"
#include "rcc/CodeGen/MachineLiveness.h"
#include "rcc/CodeGen/MachineMemOperand.h"
+#include "rcc/CodeGen/MachineMetadata.h"
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
@@ -28,6 +29,42 @@
namespace rcc {
namespace {
+[[nodiscard]] std::string formatDebugInstruction(const MachineInstr &Instruction,
+ const TargetDescription &Target) {
+ const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction.opcode());
+ std::string Result = Descriptor == nullptr ? "<unknown>" : Descriptor->Name;
+ for (std::size_t Index = 0; Index < Instruction.operandCount(); ++Index) {
+ if (Index == 0)
+ Result.push_back(' ');
+ else
+ Result.append(", ");
+ const MachineOperand &Operand = Instruction.operands()[Index];
+ if (const auto *Immediate = Operand.getIf<std::int64_t>()) {
+ Result.append(std::to_string(*Immediate));
+ continue;
+ }
+ if (const auto *RegisterValue = Operand.getIf<Register>()) {
+ if (RegisterValue->isNoRegister()) {
+ Result.append("$noreg");
+ continue;
+ }
+ if (RegisterValue->isVirtual()) {
+ Result.push_back('%');
+ Result.append(std::to_string(*RegisterValue->virtualIndex()));
+ continue;
+ }
+ const auto Physical = RegisterValue->physicalRegister();
+ const PhysicalRegisterDesc *Register =
+ Physical ? Target.registers().physicalRegister(*Physical) : nullptr;
+ Result.push_back('$');
+ Result.append(Register == nullptr ? RegisterValue->toString() : Register->Name);
+ continue;
+ }
+ Result.append("<operand>");
+ }
+ return Result;
+}
+
class FunctionVerifier final {
public:
FunctionVerifier(const MachineFunction &Function, const TargetDescription &Target,
@@ -35,7 +72,7 @@ public:
: Function(Function), Target(Target), Options(Options) {}
[[nodiscard]] Expected<void> run() {
- if (Function.name().empty() || !Function.stableID().isValid())
+ if (!Function.stableID().isValid())
return fail("function identity is invalid");
auto AttributeResult = Function.attributes().verify();
@@ -309,6 +346,14 @@ private:
if (Instruction.sourceLocation() && !Instruction.sourceLocation()->isValid()) {
return fail("instruction has an invalid source location");
}
+ const Opcode OpcodeValue = Instruction.opcode();
+ const bool IsDebugValue = OpcodeValue == opcode(GenericOpcode::DBG_VALUE) ||
+ OpcodeValue == opcode(GenericOpcode::DBG_VALUE_LIST);
+ if (IsDebugValue && Instruction.operandCount() == 4 && !Instruction.debugLocation()) {
+ return fail("Bad machine code: Missing DebugLoc for debug instruction\n"
+ "- instruction: " +
+ formatDebugInstruction(Instruction, Target));
+ }
if (Instruction.debugLocation()) {
auto DebugLocationResult = Instruction.debugLocation()->verify(Function.metadata());
if (!DebugLocationResult)
@@ -359,10 +404,6 @@ private:
Global != nullptr && Global->Name.empty()) {
return fail("global symbol operand has an empty name");
}
- if (const auto *External = Operand.getIf<ExternalSymbolOperand>();
- External != nullptr && External->Name.empty()) {
- return fail("external symbol operand has an empty name");
- }
if (const auto *Address = Operand.getIf<BlockAddressOperand>();
Address != nullptr && (Address->FunctionName.empty() || Address->BlockName.empty())) {
return fail("block-address operand has an incomplete name");
@@ -534,13 +575,6 @@ private:
return std::unexpected(Call.error());
}
- for (const MachinePrefetchTarget &Prefetch : Records.prefetchTargets()) {
- const std::uint64_t Key = (static_cast<std::uint64_t>(Prefetch.Block.BaseID) << 32U) |
- static_cast<std::uint64_t>(Prefetch.Block.CloneID);
- if (!LinkedBlockProfileIDs.contains(Key))
- return fail("prefetch target references an unknown bb_id");
- }
-
if (!Records.debugValueSubstitutions().empty() &&
!Function.attributes().has(MachineFunctionAttribute::DebugInstrRef)) {
return fail("debug value substitutions require DebugInstrRef");
diff --git a/backend/lib/MIR/EmbeddedIR.cpp b/backend/lib/MIR/EmbeddedIR.cpp
index 662faac..93cd534 100644
--- a/backend/lib/MIR/EmbeddedIR.cpp
+++ b/backend/lib/MIR/EmbeddedIR.cpp
@@ -18,6 +18,7 @@ struct ParsedIdentifier final {
std::string Name;
TextRange Range;
std::size_t End = 0;
+ bool IsNumericSlot = false;
};
[[nodiscard]] bool isHorizontalSpace(char Character) noexcept {
@@ -96,6 +97,18 @@ const EmbeddedIRFunction *EmbeddedIRReferenceIndex::function(std::string_view Na
return &Functions[Position->second];
}
+const EmbeddedIRFunction *
+EmbeddedIRReferenceIndex::unnamedFunction(std::size_t Ordinal) const noexcept {
+ for (const EmbeddedIRFunction &Function : Functions) {
+ if (!Function.isUnnamed())
+ continue;
+ if (Ordinal == 0)
+ return &Function;
+ --Ordinal;
+ }
+ return nullptr;
+}
+
const EmbeddedIRReference *
EmbeddedIRReferenceIndex::globalObject(std::string_view Name) const noexcept {
const auto Position = GlobalNameToIndex.find(Name);
@@ -152,6 +165,10 @@ public:
return failureResult();
}
+ validateLocalReferences();
+ if (Failed)
+ return failureResult();
+
EmbeddedIRIndexResult Result;
Result.Index.emplace(std::move(Index));
return Result;
@@ -209,6 +226,7 @@ private:
return std::nullopt;
std::string Name;
+ bool IsNumericSlot = false;
if (Line[Position] == '"') {
++Position;
while (Position < End && Line[Position] != '"') {
@@ -237,6 +255,7 @@ private:
}
++Position;
} else if (Line[Position] >= '0' && Line[Position] <= '9') {
+ IsNumericSlot = true;
const std::size_t NameBegin = Position;
while (Position < End && Line[Position] >= '0' && Line[Position] <= '9')
++Position;
@@ -254,6 +273,7 @@ private:
.Name = std::move(Name),
.Range = Index.Source.range(LineOffset + Begin, LineOffset + Position),
.End = Position,
+ .IsNumericSlot = IsNumericSlot,
};
}
@@ -464,6 +484,7 @@ private:
Function.Name = Name.Name;
Function.NameRange = Name.Range;
Function.IsDefinition = IsDefinition;
+ Function.IsUnnamed = Name.IsNumericSlot;
Index.Functions.push_back(std::move(Function));
Index.FunctionNameToIndex.emplace(Name.Name, IndexValue);
return IndexValue;
@@ -491,6 +512,9 @@ private:
if (!Name || Failed)
return;
const std::size_t Following = skipHorizontalSpace(Line, Name->End, End);
+ recordLocalReference(*Name, Index.Functions[ActiveFunction.value()]);
+ if (Failed)
+ return;
std::size_t Previous = IdentifierBegin;
while (Previous > 0 && isHorizontalSpace(Line[Previous - 1]))
--Previous;
@@ -537,6 +561,7 @@ private:
.Name = Name.Name,
.Range = Name.Range,
});
+ observeNumericSlot(Name, Function);
}
void parseActiveFunctionLine(std::string_view Line, std::size_t LineOffset, std::size_t End) {
@@ -587,12 +612,23 @@ private:
return;
}
EmbeddedIRFunction &Function = Index.Functions[ActiveFunction.value()];
+ const std::size_t PreviousBlockCount = Function.Blocks.size();
parseBlockLabel(Line, LineOffset, Begin, BodyEnd, Function);
if (Failed)
return;
+ const std::size_t FirstContent = skipHorizontalSpace(Line, Begin, BodyEnd);
+ if (Function.Blocks.empty() && Function.Blocks.size() == PreviousBlockCount &&
+ FirstContent < BodyEnd) {
+ addImplicitEntryBlock(LineOffset + FirstContent, Function);
+ if (Failed)
+ return;
+ }
parseLocalValueDefinition(Line, LineOffset, Begin, BodyEnd, Function);
if (Failed)
return;
+ parseLocalReferences(Line, LineOffset, Begin, BodyEnd, Function);
+ if (Failed)
+ return;
FunctionBraceDepth = Depth;
if (FunctionBraceDepth == 0)
@@ -624,9 +660,45 @@ private:
const std::size_t BlockIndex = Function.Blocks.size();
Function.BlockNameToIndex.emplace(Name->Name, BlockIndex);
Function.Blocks.push_back(EmbeddedIRReference{
- .Name = std::move(Name->Name),
+ .Name = Name->Name,
.Range = Name->Range,
});
+ observeNumericSlot(*Name, Function);
+ }
+
+ void addImplicitEntryBlock(std::size_t Offset, EmbeddedIRFunction &Function) {
+ const std::string Name = std::to_string(Function.NextUnnamedSlot++);
+ if (Function.BlockNameToIndex.contains(Name) || Function.ValueNameToIndex.contains(Name)) {
+ diagnose(Offset, Offset, "embedded LLVM IR unnamed entry block slot is already in use");
+ return;
+ }
+ const std::size_t BlockIndex = Function.Blocks.size();
+ Function.BlockNameToIndex.emplace(Name, BlockIndex);
+ Function.Blocks.push_back(EmbeddedIRReference{
+ .Name = Name,
+ .Range = Index.Source.range(Offset, Offset),
+ });
+ }
+
+ void observeNumericSlot(const ParsedIdentifier &Name, EmbeddedIRFunction &Function) {
+ if (!Name.IsNumericSlot)
+ return;
+ std::uint64_t Slot = 0;
+ for (const char Digit : Name.Name) {
+ const std::uint64_t Value = static_cast<std::uint64_t>(Digit - '0');
+ if (Slot > (std::numeric_limits<std::uint64_t>::max() - Value) / 10U) {
+ diagnose(Name.Range.Begin.Offset, Name.Range.End.Offset,
+ "embedded LLVM IR numeric slot is too large");
+ return;
+ }
+ Slot = Slot * 10U + Value;
+ }
+ if (Slot == std::numeric_limits<std::uint64_t>::max()) {
+ diagnose(Name.Range.Begin.Offset, Name.Range.End.Offset,
+ "embedded LLVM IR numeric slot is too large");
+ return;
+ }
+ Function.NextUnnamedSlot = std::max(Function.NextUnnamedSlot, Slot + 1U);
}
void parseLocalValueDefinition(std::string_view Line, std::size_t LineOffset, std::size_t Begin,
@@ -657,6 +729,53 @@ private:
});
}
+ void recordLocalReference(const ParsedIdentifier &Name, EmbeddedIRFunction &Function) {
+ Function.LocalReferences.push_back(EmbeddedIRReference{
+ .Name = Name.Name,
+ .Range = Name.Range,
+ });
+ }
+
+ void parseLocalReferences(std::string_view Line, std::size_t LineOffset, std::size_t Begin,
+ std::size_t End, EmbeddedIRFunction &Function) {
+ bool InQuote = false;
+ for (std::size_t Position = Begin; Position < End; ++Position) {
+ const char Character = Line[Position];
+ if (InQuote && Character == '\\' && Position + 2 < End &&
+ isHexDigit(Line[Position + 1]) && isHexDigit(Line[Position + 2])) {
+ Position += 2;
+ continue;
+ }
+ if (Character == '"') {
+ InQuote = !InQuote;
+ continue;
+ }
+ if (InQuote || Character != '%')
+ continue;
+ auto Name = parseIdentifier(Line, LineOffset, Position, End, '%');
+ if (!Name || Failed)
+ continue;
+ recordLocalReference(*Name, Function);
+ Position = Name->End - 1;
+ }
+ }
+
+ void validateLocalReferences() {
+ for (const EmbeddedIRFunction &Function : Index.Functions) {
+ if (!Function.IsDefinition)
+ continue;
+ for (const EmbeddedIRReference &Reference : Function.LocalReferences) {
+ if (Function.ValueNameToIndex.contains(Reference.Name) ||
+ Function.BlockNameToIndex.contains(Reference.Name) ||
+ Index.TypeNameToIndex.contains(Reference.Name))
+ continue;
+ diagnose(Reference.Range.Begin.Offset, Reference.Range.End.Offset,
+ "use of undefined value '%" + Reference.Name + "'");
+ return;
+ }
+ }
+ }
+
void parseGlobalObject(std::string_view Line, std::size_t LineOffset, std::size_t Position,
std::size_t End) {
auto Name = parseIdentifier(Line, LineOffset, Position, End, '@');
diff --git a/backend/lib/MIR/LLVMConstantSyntax.cpp b/backend/lib/MIR/LLVMConstantSyntax.cpp
index 8bc2c18..a2025dc 100644
--- a/backend/lib/MIR/LLVMConstantSyntax.cpp
+++ b/backend/lib/MIR/LLVMConstantSyntax.cpp
@@ -121,6 +121,17 @@ public:
Limbs.push_back(static_cast<std::uint32_t>(Carry));
}
+ [[nodiscard]] std::uint32_t divideSmall(std::uint32_t Divisor) {
+ std::uint64_t Remainder = 0;
+ for (std::size_t Index = Limbs.size(); Index != 0; --Index) {
+ const std::uint64_t Dividend = (Remainder << 32U) | Limbs[Index - 1U];
+ Limbs[Index - 1U] = static_cast<std::uint32_t>(Dividend / Divisor);
+ Remainder = Dividend % Divisor;
+ }
+ normalize();
+ return static_cast<std::uint32_t>(Remainder);
+ }
+
void addSmall(std::uint32_t Value) {
std::uint64_t Carry = Value;
std::size_t Index = 0;
@@ -159,6 +170,51 @@ public:
normalize();
}
+ [[nodiscard]] std::size_t countrZero() const noexcept {
+ std::size_t Result = 0;
+ for (const std::uint32_t Limb : Limbs) {
+ if (Limb == 0) {
+ Result += 32U;
+ continue;
+ }
+ return Result + static_cast<std::size_t>(std::countr_zero(Limb));
+ }
+ return Result;
+ }
+
+ void shiftRight(std::size_t Amount) noexcept {
+ if (Amount == 0 || isZero())
+ return;
+ const std::size_t LimbOffset = Amount / 32U;
+ if (LimbOffset >= Limbs.size()) {
+ Limbs.clear();
+ return;
+ }
+ if (LimbOffset != 0)
+ Limbs.erase(Limbs.begin(), Limbs.begin() + static_cast<std::ptrdiff_t>(LimbOffset));
+ const unsigned BitOffset = static_cast<unsigned>(Amount % 32U);
+ if (BitOffset != 0) {
+ std::uint32_t Carry = 0;
+ for (std::size_t Index = Limbs.size(); Index != 0; --Index) {
+ const std::uint32_t Current = Limbs[Index - 1U];
+ Limbs[Index - 1U] = (Current >> BitOffset) | Carry;
+ Carry = Current << (32U - BitOffset);
+ }
+ }
+ normalize();
+ }
+
+ [[nodiscard]] std::string toDecimal() const {
+ if (isZero())
+ return "0";
+ BigUnsigned Remaining = *this;
+ std::string Digits;
+ while (!Remaining.isZero())
+ Digits.push_back(static_cast<char>('0' + Remaining.divideSmall(10)));
+ std::ranges::reverse(Digits);
+ return Digits;
+ }
+
[[nodiscard]] BigUnsigned shiftedLeft(std::size_t Amount) const {
if (isZero() || Amount == 0)
return *this;
@@ -2382,6 +2438,225 @@ Expected<std::string> printType(const GenericConstantType &Type) {
return makeUnexpected(ErrorCode::VerificationError, "generic constant type is invalid");
}
+enum class PrintedFloatCategory : std::uint8_t {
+ Zero,
+ Finite,
+ Infinity,
+ QuietNaN,
+ SignalingNaN,
+};
+
+struct PrintedBinaryFloat final {
+ PrintedFloatCategory Category = PrintedFloatCategory::Zero;
+ bool Negative = false;
+ BigUnsigned Significand;
+ std::int32_t BinaryExponent = 0;
+ BigUnsigned NaNPayload;
+ std::uint32_t QuietNaNBit = 0;
+};
+
+[[nodiscard]] std::uint64_t extractFloatField(std::span<const std::uint64_t> Words,
+ std::uint32_t Begin, std::uint32_t Width) noexcept {
+ std::uint64_t Result = 0;
+ for (std::uint32_t Bit = 0; Bit < Width; ++Bit) {
+ if (hasFloatBit(Words, Begin + Bit))
+ Result |= std::uint64_t{1} << Bit;
+ }
+ return Result;
+}
+
+[[nodiscard]] BigUnsigned extractFloatBits(std::span<const std::uint64_t> Words,
+ std::uint32_t Begin, std::uint32_t Width) {
+ BigUnsigned Result;
+ for (std::uint32_t Bit = 0; Bit < Width; ++Bit) {
+ if (hasFloatBit(Words, Begin + Bit))
+ Result.setBit(Bit);
+ }
+ return Result;
+}
+
+[[nodiscard]] std::optional<PrintedBinaryFloat> decodePrintedFloat(const FloatingPointBits &Bits) {
+ std::optional<BinaryFloatFormat> Format = binaryFloatFormat(Bits.semantics());
+ std::span<const std::uint64_t> Words = Bits.words();
+ if (!Format)
+ return std::nullopt;
+
+ if (Bits.semantics() == FloatSemantics::PPCDoubleDouble ||
+ Bits.semantics() == FloatSemantics::PPCDoubleDoubleLegacy) {
+ if (Words.size() != 2 || Words[1] != 0)
+ return std::nullopt;
+ Format = binaryFloatFormat(FloatSemantics::IEEEDouble);
+ Words = Words.first(1);
+ }
+ if (!Format || Format->ExponentBitWidth == 0)
+ return std::nullopt;
+
+ PrintedBinaryFloat Result;
+ Result.Negative = hasFloatBit(Words, Format->StorageBitWidth - 1U);
+ const std::uint32_t StoredSignificandWidth =
+ Format->HasExplicitIntegerBit ? Format->Precision : Format->Precision - 1U;
+ const std::uint64_t Exponent =
+ extractFloatField(Words, StoredSignificandWidth, Format->ExponentBitWidth);
+ const std::uint64_t MaximumExponent = (std::uint64_t{1} << Format->ExponentBitWidth) - 1U;
+
+ if (Exponent == MaximumExponent) {
+ const std::uint32_t PayloadWidth = Format->Precision - 1U;
+ Result.NaNPayload = extractFloatBits(Words, 0, PayloadWidth);
+ if (Result.NaNPayload.isZero()) {
+ Result.Category = PrintedFloatCategory::Infinity;
+ return Result;
+ }
+ Result.QuietNaNBit = PayloadWidth - 1U;
+ Result.Category = hasFloatBit(Words, Result.QuietNaNBit) ? PrintedFloatCategory::QuietNaN
+ : PrintedFloatCategory::SignalingNaN;
+ return Result;
+ }
+
+ Result.Significand = extractFloatBits(Words, 0, StoredSignificandWidth);
+ if (Exponent != 0 && !Format->HasExplicitIntegerBit)
+ Result.Significand.setBit(Format->Precision - 1U);
+ if (Result.Significand.isZero()) {
+ Result.Category = PrintedFloatCategory::Zero;
+ return Result;
+ }
+
+ const std::int64_t Bias = 1 - static_cast<std::int64_t>(Format->MinExponent);
+ const std::int64_t UnbiasedExponent =
+ Exponent == 0 ? Format->MinExponent : static_cast<std::int64_t>(Exponent) - Bias;
+ Result.BinaryExponent = static_cast<std::int32_t>(UnbiasedExponent - (Format->Precision - 1U));
+ Result.Category = PrintedFloatCategory::Finite;
+ return Result;
+}
+
+[[nodiscard]] std::string printNaNPayload(BigUnsigned Payload, std::uint32_t QuietNaNBit) {
+ Payload.clearBit(QuietNaNBit);
+ const auto Width = static_cast<std::uint32_t>(std::max<std::size_t>(Payload.bitWidth(), 1U));
+ std::array<std::uint64_t, 2> Words{Payload.word(0), Payload.word(1)};
+ std::string Digits = hexadecimalWords(Words, Width);
+ const std::size_t FirstNonzero = Digits.find_first_not_of('0');
+ if (FirstNonzero == std::string::npos)
+ return "0x0";
+ Digits.erase(0, FirstNonzero);
+ return "0x" + Digits;
+}
+
+[[nodiscard]] std::string formatFiniteFloat(PrintedBinaryFloat Value) {
+ std::string Result;
+ if (Value.Negative)
+ Result.push_back('-');
+ if (Value.Category == PrintedFloatCategory::Zero) {
+ Result.append("0.000000e+00");
+ return Result;
+ }
+
+ const std::size_t TrailingZeros = Value.Significand.countrZero();
+ Value.Significand.shiftRight(TrailingZeros);
+ Value.BinaryExponent += static_cast<std::int32_t>(TrailingZeros);
+
+ std::int32_t DecimalExponent = 0;
+ if (Value.BinaryExponent >= 0) {
+ Value.Significand =
+ Value.Significand.shiftedLeft(static_cast<std::size_t>(Value.BinaryExponent));
+ } else {
+ DecimalExponent = Value.BinaryExponent;
+ for (std::int32_t Power = 0; Power < -Value.BinaryExponent; ++Power)
+ Value.Significand.multiplySmall(5);
+ }
+
+ std::string Digits = Value.Significand.toDecimal();
+ while (Digits.size() > 1U && Digits.back() == '0') {
+ Digits.pop_back();
+ ++DecimalExponent;
+ }
+
+ constexpr std::size_t FormatPrecision = 6;
+ if (Digits.size() > FormatPrecision) {
+ std::size_t Removed = Digits.size() - FormatPrecision;
+ const bool RoundUp = Digits[FormatPrecision] >= '5';
+ Digits.resize(FormatPrecision);
+ if (RoundUp) {
+ std::size_t Position = Digits.size();
+ while (Position != 0 && Digits[Position - 1U] == '9') {
+ Digits[Position - 1U] = '0';
+ --Position;
+ }
+ if (Position == 0)
+ Digits.insert(Digits.begin(), '1');
+ else
+ ++Digits[Position - 1U];
+ }
+ while (Digits.size() > 1U && Digits.back() == '0') {
+ Digits.pop_back();
+ ++Removed;
+ }
+ DecimalExponent += static_cast<std::int32_t>(Removed);
+ }
+
+ DecimalExponent += static_cast<std::int32_t>(Digits.size() - 1U);
+ Result.push_back(Digits.front());
+ Result.push_back('.');
+ Result.append(Digits.substr(1));
+ Result.append(7U - Digits.size(), '0');
+ Result.push_back('e');
+ Result.push_back(DecimalExponent < 0 ? '-' : '+');
+ const std::uint32_t Magnitude = static_cast<std::uint32_t>(
+ DecimalExponent < 0 ? -static_cast<std::int64_t>(DecimalExponent) : DecimalExponent);
+ std::string Exponent = std::to_string(Magnitude);
+ if (Exponent.size() < 2U)
+ Result.push_back('0');
+ Result.append(Exponent);
+ return Result;
+}
+
+[[nodiscard]] std::string exactFloatingText(const FloatingPointBits &Bits) {
+ return "f0x" + hexadecimalWords(Bits.words(), Bits.bitWidth());
+}
+
+[[nodiscard]] std::string canonicalFloatingText(const FloatingPointBits &Bits) {
+ auto Decoded = decodePrintedFloat(Bits);
+ if (!Decoded)
+ return exactFloatingText(Bits);
+
+ std::string Candidate;
+ switch (Decoded->Category) {
+ case PrintedFloatCategory::Zero:
+ case PrintedFloatCategory::Finite:
+ Candidate = formatFiniteFloat(std::move(*Decoded));
+ break;
+ case PrintedFloatCategory::Infinity:
+ Candidate = Decoded->Negative ? "-inf" : "+inf";
+ break;
+ case PrintedFloatCategory::QuietNaN: {
+ BigUnsigned Payload = Decoded->NaNPayload;
+ Payload.clearBit(Decoded->QuietNaNBit);
+ if (Payload.isZero()) {
+ Candidate = Decoded->Negative ? "-qnan" : "+qnan";
+ break;
+ }
+ Candidate = Decoded->Negative ? "-nan(" : "+nan(";
+ Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit));
+ Candidate.push_back(')');
+ break;
+ }
+ case PrintedFloatCategory::SignalingNaN:
+ Candidate = Decoded->Negative ? "-snan(" : "+snan(";
+ Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit));
+ Candidate.push_back(')');
+ break;
+ }
+
+ auto Type = GenericConstantType::floating(Bits.semantics());
+ if (!Type)
+ return exactFloatingText(Bits);
+ auto TypeText = printType(*Type);
+ if (!TypeText)
+ return exactFloatingText(Bits);
+ auto Parsed = parseLLVMConstant(*TypeText + " " + Candidate);
+ if (!Parsed || !Parsed->FloatingValue || *Parsed->FloatingValue != Bits)
+ return exactFloatingText(Bits);
+ return Candidate;
+}
+
[[nodiscard]] std::string_view
expressionOpcodeName(GenericConstantExpressionOpcode Opcode) noexcept {
switch (Opcode) {
@@ -2464,9 +2739,7 @@ Expected<std::string> printValue(const GenericConstantData &Value, bool IncludeT
if (!Value.FloatingValue)
return makeUnexpected(ErrorCode::VerificationError,
"floating constant is missing its floating payload");
- Result.append("f0x");
- Result.append(hexadecimalWords(Value.FloatingValue.value().words(),
- Value.FloatingValue.value().bitWidth()));
+ Result.append(canonicalFloatingText(*Value.FloatingValue));
return Result;
case GenericConstantKind::NullPointer:
Result.append("null");
diff --git a/backend/lib/MIR/MIDebugSyntax.cpp b/backend/lib/MIR/MIDebugSyntax.cpp
index fc08df6..e614a21 100644
--- a/backend/lib/MIR/MIDebugSyntax.cpp
+++ b/backend/lib/MIR/MIDebugSyntax.cpp
@@ -5,7 +5,10 @@
#include <array>
#include <charconv>
#include <cstdint>
+#include <optional>
#include <ranges>
+#include <span>
+#include <string>
#include <string_view>
#include <system_error>
#include <utility>
@@ -20,26 +23,8 @@ namespace {
return Error == std::errc{} && End == Text.data() + Text.size();
}
-} // namespace
-
-std::optional<std::uint64_t> parseDIExpressionOpcodeName(std::string_view Name) noexcept {
- const auto NumberedOpcode = [Name](std::string_view Prefix,
- std::uint64_t Base) -> std::optional<std::uint64_t> {
- if (!Name.starts_with(Prefix))
- return std::nullopt;
- std::uint64_t Index = 0;
- if (!parseIndex(Name.substr(Prefix.size()), Index) || Index > 31)
- return std::nullopt;
- return Base + Index;
- };
- if (auto Opcode = NumberedOpcode("DW_OP_lit", 0x30U))
- return Opcode;
- if (auto Opcode = NumberedOpcode("DW_OP_reg", 0x50U))
- return Opcode;
- if (auto Opcode = NumberedOpcode("DW_OP_breg", 0x70U))
- return Opcode;
-
- constexpr auto NamedOpcodes = std::to_array<std::pair<std::string_view, std::uint64_t>>({
+[[nodiscard]] const auto &namedOpcodes() {
+ static constexpr auto Opcodes = std::to_array<std::pair<std::string_view, std::uint64_t>>({
{"DW_OP_addr", 0x03},
{"DW_OP_deref", 0x06},
{"DW_OP_const1u", 0x08},
@@ -114,7 +99,104 @@ std::optional<std::uint64_t> parseDIExpressionOpcodeName(std::string_view Name)
{"DW_OP_LLVM_entry_value", 0x1003},
{"DW_OP_LLVM_implicit_pointer", 0x1004},
{"DW_OP_LLVM_arg", 0x1005},
+ {"DW_OP_LLVM_extract_bits_sext", 0x1006},
+ {"DW_OP_LLVM_extract_bits_zext", 0x1007},
});
+ return Opcodes;
+}
+
+[[nodiscard]] const auto &attributeEncodings() {
+ static constexpr auto Encodings = std::to_array<std::pair<std::string_view, std::uint64_t>>({
+ {"DW_ATE_address", 0x01},
+ {"DW_ATE_boolean", 0x02},
+ {"DW_ATE_complex_float", 0x03},
+ {"DW_ATE_float", 0x04},
+ {"DW_ATE_signed", 0x05},
+ {"DW_ATE_signed_char", 0x06},
+ {"DW_ATE_unsigned", 0x07},
+ {"DW_ATE_unsigned_char", 0x08},
+ {"DW_ATE_imaginary_float", 0x09},
+ {"DW_ATE_packed_decimal", 0x0A},
+ {"DW_ATE_numeric_string", 0x0B},
+ {"DW_ATE_edited", 0x0C},
+ {"DW_ATE_signed_fixed", 0x0D},
+ {"DW_ATE_unsigned_fixed", 0x0E},
+ {"DW_ATE_decimal_float", 0x0F},
+ {"DW_ATE_UTF", 0x10},
+ {"DW_ATE_UCS", 0x11},
+ {"DW_ATE_ASCII", 0x12},
+ });
+ return Encodings;
+}
+
+[[nodiscard]] std::optional<std::string> opcodeName(std::uint64_t Opcode) {
+ if (Opcode >= 0x30U && Opcode <= 0x4FU)
+ return "DW_OP_lit" + std::to_string(Opcode - 0x30U);
+ if (Opcode >= 0x50U && Opcode <= 0x6FU)
+ return "DW_OP_reg" + std::to_string(Opcode - 0x50U);
+ if (Opcode >= 0x70U && Opcode <= 0x8FU)
+ return "DW_OP_breg" + std::to_string(Opcode - 0x70U);
+ const auto &Opcodes = namedOpcodes();
+ const auto Found =
+ std::ranges::find_if(Opcodes, [Opcode](const auto &Entry) { return Entry.second == Opcode; });
+ if (Found == Opcodes.end())
+ return std::nullopt;
+ return std::string(Found->first);
+}
+
+[[nodiscard]] std::size_t opcodeSize(std::uint64_t Opcode) noexcept {
+ if (Opcode >= 0x70U && Opcode <= 0x8FU)
+ return 2;
+ switch (Opcode) {
+ case 0x92:
+ case 0x1000:
+ case 0x1001:
+ case 0x1006:
+ case 0x1007:
+ return 3;
+ case 0x10:
+ case 0x11:
+ case 0x23:
+ case 0x90:
+ case 0x94:
+ case 0x1002:
+ case 0x1003:
+ case 0x1005:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+[[nodiscard]] std::optional<std::string_view> attributeName(std::uint64_t Encoding) noexcept {
+ const auto &Encodings = attributeEncodings();
+ const auto Found = std::ranges::find_if(
+ Encodings, [Encoding](const auto &Entry) { return Entry.second == Encoding; });
+ if (Found == Encodings.end())
+ return std::nullopt;
+ return Found->first;
+}
+
+} // namespace
+
+std::optional<std::uint64_t> parseDIExpressionOpcodeName(std::string_view Name) noexcept {
+ const auto NumberedOpcode = [Name](std::string_view Prefix,
+ std::uint64_t Base) -> std::optional<std::uint64_t> {
+ if (!Name.starts_with(Prefix))
+ return std::nullopt;
+ std::uint64_t Index = 0;
+ if (!parseIndex(Name.substr(Prefix.size()), Index) || Index > 31)
+ return std::nullopt;
+ return Base + Index;
+ };
+ if (auto Opcode = NumberedOpcode("DW_OP_lit", 0x30U))
+ return Opcode;
+ if (auto Opcode = NumberedOpcode("DW_OP_reg", 0x50U))
+ return Opcode;
+ if (auto Opcode = NumberedOpcode("DW_OP_breg", 0x70U))
+ return Opcode;
+
+ const auto &NamedOpcodes = namedOpcodes();
const auto *const Found =
std::ranges::find_if(NamedOpcodes, [Name](const auto &Entry) { return Entry.first == Name; });
if (Found == NamedOpcodes.end())
@@ -122,4 +204,61 @@ std::optional<std::uint64_t> parseDIExpressionOpcodeName(std::string_view Name)
return Found->second;
}
+std::optional<std::uint64_t>
+parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept {
+ const auto &Encodings = attributeEncodings();
+ const auto Found =
+ std::ranges::find_if(Encodings, [Name](const auto &Entry) { return Entry.first == Name; });
+ if (Found == Encodings.end())
+ return std::nullopt;
+ return Found->second;
+}
+
+std::string invalidDIExpressionElementMessage(std::string_view Name) {
+ if (Name.starts_with("DW_OP_"))
+ return "invalid DWARF op '" + std::string(Name) + "'";
+ if (Name.starts_with("DW_ATE_"))
+ return "invalid DWARF attribute encoding '" + std::string(Name) + "'";
+ return "expected unsigned integer";
+}
+
+std::string formatDIExpression(std::span<const std::uint64_t> Elements) {
+ std::string Result = "!DIExpression(";
+ std::size_t Position = 0;
+ bool First = true;
+ while (Position < Elements.size()) {
+ const std::uint64_t Opcode = Elements[Position];
+ const auto Name = opcodeName(Opcode);
+ const std::size_t Size = opcodeSize(Opcode);
+ if (!Name || Position + Size > Elements.size()) {
+ Result = "!DIExpression(";
+ for (std::size_t Index = 0; Index < Elements.size(); ++Index) {
+ if (Index != 0)
+ Result.append(", ");
+ Result.append(std::to_string(Elements[Index]));
+ }
+ Result.push_back(')');
+ return Result;
+ }
+
+ if (!First)
+ Result.append(", ");
+ First = false;
+ Result.append(*Name);
+ for (std::size_t Argument = 1; Argument < Size; ++Argument) {
+ Result.append(", ");
+ if (Opcode == 0x1001U && Argument == 2U) {
+ if (const auto Encoding = attributeName(Elements[Position + Argument])) {
+ Result.append(*Encoding);
+ continue;
+ }
+ }
+ Result.append(std::to_string(Elements[Position + Argument]));
+ }
+ Position += Size;
+ }
+ Result.push_back(')');
+ return Result;
+}
+
} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIParser.cpp b/backend/lib/MIR/MIParser.cpp
index 483c2f2..01ecbf5 100644
--- a/backend/lib/MIR/MIParser.cpp
+++ b/backend/lib/MIR/MIParser.cpp
@@ -2,6 +2,7 @@
#include "rcc/MIR/MIParser.h"
+#include "rcc/CodeGen/InlineAsm.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCFGUpdater.h"
@@ -13,6 +14,7 @@
#include "rcc/CodeGen/TargetDescription.h"
#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
+#include "rcc/MIR/LLVMConstantSyntax.h"
#include "rcc/MIR/MIDebugSyntax.h"
#include "rcc/MIR/MILexer.h"
@@ -113,60 +115,6 @@ namespace {
Token.isKeyword("llvm_vector_register_mask");
}
-[[nodiscard]] std::optional<unsigned> hexDigit(char Character) noexcept {
- if (Character >= '0' && Character <= '9')
- return static_cast<unsigned>(Character - '0');
- if (Character >= 'a' && Character <= 'f')
- return static_cast<unsigned>(Character - 'a') + 10U;
- if (Character >= 'A' && Character <= 'F')
- return static_cast<unsigned>(Character - 'A') + 10U;
- return std::nullopt;
-}
-
-[[nodiscard]] bool parseHexWords(std::string_view Digits, std::uint32_t BitWidth,
- std::vector<std::uint64_t> &Words) {
- if (Digits.empty() || Digits.size() > (static_cast<std::size_t>(BitWidth) + 3U) / 4U)
- return false;
- Words.assign((static_cast<std::size_t>(BitWidth) + 63U) / 64U, 0);
- std::size_t BitOffset = 0;
- for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator) {
- const auto Digit = hexDigit(*Iterator);
- if (!Digit)
- return false;
- const std::size_t WordIndex = BitOffset / 64U;
- const unsigned Shift = static_cast<unsigned>(BitOffset % 64U);
- Words[WordIndex] |= static_cast<std::uint64_t>(*Digit) << Shift;
- BitOffset += 4U;
- }
- if (BitWidth % 64U != 0)
- Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U;
- return true;
-}
-
-[[nodiscard]] bool isZeroWords(std::span<const std::uint64_t> Words) noexcept {
- return std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; });
-}
-
-[[nodiscard]] bool magnitudeFitsSignedNegative(std::span<const std::uint64_t> Words,
- std::uint32_t BitWidth) noexcept {
- const std::size_t SignWord = (BitWidth - 1U) / 64U;
- const unsigned SignBit = (BitWidth - 1U) % 64U;
- for (std::size_t Index = SignWord + 1U; Index < Words.size(); ++Index) {
- if (Words[Index] != 0)
- return false;
- }
- const std::uint64_t SignMask = std::uint64_t{1} << SignBit;
- if ((Words[SignWord] & ~SignMask) != 0)
- return false;
- if ((Words[SignWord] & SignMask) == 0)
- return true;
- for (std::size_t Index = 0; Index < SignWord; ++Index) {
- if (Words[Index] != 0)
- return false;
- }
- return true;
-}
-
[[nodiscard]] Expected<IntegerImmediate> parseIntegerImmediateValue(std::uint32_t BitWidth,
std::string_view Spelling) {
if (BitWidth == 0 || BitWidth > IntegerImmediate::MaxBitWidth)
@@ -188,17 +136,13 @@ namespace {
Word = static_cast<std::uint64_t>(Product);
Carry = static_cast<std::uint64_t>(Product >> 64U);
}
- if (Carry != 0)
- return makeUnexpected(ErrorCode::ParseError, "typed integer does not fit its bit width");
- if (BitWidth % 64U != 0 && (Words.back() >> (BitWidth % 64U)) != 0) {
- return makeUnexpected(ErrorCode::ParseError, "typed integer does not fit its bit width");
- }
}
+ // LLVM parses the arbitrary-precision literal first and then truncates it to
+ // the declared integer width. Keep exactly that modulo-2^N bit pattern.
+ if (BitWidth % 64U != 0)
+ Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U;
if (Negative) {
- if (isZeroWords(Words) || !magnitudeFitsSignedNegative(Words, BitWidth))
- return makeUnexpected(ErrorCode::ParseError,
- "negative typed integer does not fit its bit width");
for (std::uint64_t &Word : Words)
Word = ~Word;
std::uint64_t Carry = 1;
@@ -213,51 +157,6 @@ namespace {
return IntegerImmediate::create(BitWidth, Words);
}
-[[nodiscard]] std::uint16_t floatToHalfBits(float Value) noexcept {
- const std::uint32_t Bits = std::bit_cast<std::uint32_t>(Value);
- const std::uint32_t Sign = (Bits >> 16U) & 0x8000U;
- const std::uint32_t Exponent = (Bits >> 23U) & 0xFFU;
- const std::uint32_t Fraction = Bits & 0x7FFFFFU;
- if (Exponent == 0xFFU) {
- const std::uint32_t Payload = Fraction == 0 ? 0 : std::max(1U, Fraction >> 13U);
- return static_cast<std::uint16_t>(Sign | 0x7C00U | Payload);
- }
-
- const int HalfExponent = static_cast<int>(Exponent) - 127 + 15;
- if (HalfExponent >= 31)
- return static_cast<std::uint16_t>(Sign | 0x7C00U);
- if (HalfExponent <= 0) {
- if (HalfExponent < -10)
- return static_cast<std::uint16_t>(Sign);
- const std::uint32_t Mantissa = Fraction | 0x800000U;
- const unsigned Shift = static_cast<unsigned>(14 - HalfExponent);
- std::uint32_t Rounded = Mantissa >> Shift;
- const std::uint32_t Remainder = Mantissa & ((std::uint32_t{1} << Shift) - 1U);
- const std::uint32_t Halfway = std::uint32_t{1} << (Shift - 1U);
- if (Remainder > Halfway || (Remainder == Halfway && (Rounded & 1U) != 0))
- ++Rounded;
- return static_cast<std::uint16_t>(Sign | Rounded);
- }
-
- std::uint32_t Rounded = Fraction >> 13U;
- const std::uint32_t Remainder = Fraction & 0x1FFFU;
- if (Remainder > 0x1000U || (Remainder == 0x1000U && (Rounded & 1U) != 0)) {
- ++Rounded;
- if (Rounded == 0x400U)
- return static_cast<std::uint16_t>(Sign |
- (static_cast<std::uint32_t>(HalfExponent + 1) << 10U));
- }
- return static_cast<std::uint16_t>(Sign | (static_cast<std::uint32_t>(HalfExponent) << 10U) |
- Rounded);
-}
-
-[[nodiscard]] std::uint16_t floatToBFloatBits(float Value) noexcept {
- std::uint32_t Bits = std::bit_cast<std::uint32_t>(Value);
- const std::uint32_t Bias = 0x7FFFU + ((Bits >> 16U) & 1U);
- Bits += Bias;
- return static_cast<std::uint16_t>(Bits >> 16U);
-}
-
[[nodiscard]] std::optional<std::uint64_t> minimumTypeSizeInBits(LLT Type) noexcept {
if (!Type.isValid() || Type.isToken())
return std::nullopt;
@@ -493,6 +392,8 @@ private:
[[nodiscard]] bool parseRegisterOperand(bool IsDef, ParsedOperand &Result);
[[nodiscard]] bool parseTargetFlags(std::uint32_t &Flags);
[[nodiscard]] bool parseOperand(bool IsDef, ParsedOperand &Result);
+ [[nodiscard]] bool parseInlineAsmExtraInfoOperand(ParsedOperand &Result);
+ [[nodiscard]] bool parseInlineAsmOperandFlag(ParsedOperand &Result);
[[nodiscard]] bool parseTypedIntegerOperand(ParsedOperand &Result);
[[nodiscard]] bool parseFloatingImmediateOperand(ParsedOperand &Result);
[[nodiscard]] bool parseMetadataReference(MachineMetadataReference &Result);
@@ -684,7 +585,8 @@ bool MIParser::Impl::parseScalarLowLevelType(const MIToken &TypeToken, LLT &Resu
std::uint64_t Value = 0;
if (!parseUnsignedDigits(Digits, Value))
- return fail(TypeToken, "low-level type suffix must be an unsigned decimal integer");
+ return fail(TypeToken,
+ "expected integers after 's'/'i'/'f'/'bf'/'p' type identifier");
Expected<LLT> Created = makeUnexpected(ErrorCode::ParseError, "invalid low-level type");
switch (Kind) {
@@ -739,40 +641,67 @@ bool MIParser::Impl::parseScalarLowLevelType(const MIToken &TypeToken, LLT &Resu
bool MIParser::Impl::parseLowLevelType(LLT &Result) {
const MIToken Begin = token();
+ const auto HasScalarPrefix = [](std::string_view Spelling) {
+ return Spelling.starts_with('s') || Spelling.starts_with('i') ||
+ Spelling.starts_with('f') || Spelling.starts_with("bf") ||
+ Spelling.starts_with('p');
+ };
if (at(MITokenKind::Identifier)) {
+ if (!HasScalarPrefix(token().stringValue())) {
+ return fail(Begin,
+ "expected tN, pA, <M x tN>, <M x pA>, <vscale x M x tN>, or "
+ "<vscale x M x pA> for GlobalISel type, where t = {'s', 'i', 'f', 'bf'}");
+ }
if (parseScalarLowLevelType(token(), Result))
return true;
advance();
return false;
}
- if (!consume(MITokenKind::Less))
- return fail(Begin, "expected a scalar, pointer, or vector low-level type");
+ if (!consume(MITokenKind::Less)) {
+ return fail(Begin,
+ "expected tN, pA, <M x tN>, <M x pA>, <vscale x M x tN>, or "
+ "<vscale x M x pA> for GlobalISel type, where t = {'s', 'i', 'f', 'bf'}");
+ }
bool Scalable = false;
if (at(MITokenKind::Identifier) && token().stringValue() == "vscale") {
Scalable = true;
advance();
- if (!at(MITokenKind::Identifier) || token().stringValue() != "x")
- return fail("expected 'x' after 'vscale' in a scalable vector type");
+ if (!at(MITokenKind::Identifier) || token().stringValue() != "x") {
+ return fail(
+ "expected <vscale x M x tN>, where t = {'s', 'i', 'f', 'bf', 'p'}");
+ }
advance();
}
+ const auto VectorError = [this, &Begin, Scalable] {
+ if (Scalable) {
+ return fail(Begin,
+ "expected <vscale x M x tN> for vector type, where t = "
+ "{'s', 'i', 'f', 'bf', 'p'}");
+ }
+ return fail(Begin,
+ "expected <M x tN> for vector type, where t = {'s', 'i', 'f', 'bf', 'p'}");
+ };
+
std::uint64_t ElementCount = 0;
if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), ElementCount))
- return fail("expected an unsigned vector element count");
+ return VectorError();
advance();
if (!at(MITokenKind::Identifier) || token().stringValue() != "x")
- return fail("expected 'x' after the vector element count");
+ return VectorError();
advance();
+ if (!at(MITokenKind::Identifier) || !HasScalarPrefix(token().stringValue()))
+ return VectorError();
LLT ElementType;
if (parseScalarLowLevelType(token(), ElementType))
return true;
if (ElementType.isToken())
return fail("token cannot be a vector element type");
advance();
- if (expect(MITokenKind::Greater, "'>' after the vector low-level type"))
- return true;
+ if (!consume(MITokenKind::Greater))
+ return VectorError();
Expected<LLT> Created = Scalable ? LLT::makeScalableVector(ElementCount, ElementType)
: LLT::makeFixedVector(ElementCount, ElementType);
@@ -821,6 +750,8 @@ bool MIParser::Impl::parseBlockAttributes(MachineBasicBlock *Block) {
} else if (at(MITokenKind::IRBlock) || at(MITokenKind::NamedIRBlock)) {
if (!Seen.insert("ir-block").second)
return fail("duplicate IR block attribute");
+ if (Context.HasIRBlock && !Context.HasIRBlock(symbolName(token())))
+ return fail(token(), "use of undefined IR block '" + std::string(token().spelling()) + "'");
if (Block != nullptr) {
auto SetName = Block->attributes().setIRBlockName(symbolName(token()));
if (!SetName)
@@ -915,7 +846,14 @@ bool MIParser::Impl::parseBlockDefinition(bool Create, std::uint32_t &ID) {
if (Blocks.size() >= Options.MaximumBlocks)
return fail(Label, "machine basic block count exceeds parser limit");
if (Blocks.contains(ID))
- return fail(Label, "redefinition of machine basic block #" + std::to_string(ID));
+ return fail(Label,
+ "redefinition of machine basic block with id #" + std::to_string(ID));
+ if (!Label.stringValue().empty() && Context.HasIRBlock &&
+ !Context.HasIRBlock(Label.stringValue())) {
+ return fail(Label, "basic block '" + std::string(Label.stringValue()) +
+ "' is not defined in the function '" +
+ std::string(Function.name()) + "'");
+ }
auto Created = Function.createBasicBlock();
if (!Created)
return importError(Label, Created.error());
@@ -1000,6 +938,12 @@ Register MIParser::Impl::getOrCreateVirtualRegister(const MIToken &Token) {
static_cast<void>(importError(Token, Created.error()));
return {};
}
+ auto Info = Function.registerInfo().virtualRegisterInfo(*Created);
+ if (!Info) {
+ static_cast<void>(importError(Token, Info.error()));
+ return {};
+ }
+ Info->get().Name = Name;
NamedVirtualRegisters.emplace(Name, *Created);
return *Created;
}
@@ -1345,96 +1289,22 @@ bool MIParser::Impl::parseFloatingImmediateOperand(ParsedOperand &Result) {
const auto Semantics = floatSemanticsForKeyword(Begin.stringValue());
if (!Semantics)
return fail(Begin, "unknown floating-point immediate type");
- const std::uint32_t BitWidth = getFloatSemanticsBitWidth(*Semantics).value_or(0);
advance();
if (!at(MITokenKind::FloatingPointLiteral) && !at(MITokenKind::HexLiteral))
return fail("expected a floating-point literal");
const MIToken Literal = token();
- const std::string_view Spelling = Literal.spelling();
- std::vector<std::uint64_t> Words;
- if (Spelling.starts_with("f0x") || Spelling.starts_with("f0X")) {
- if (!parseHexWords(Spelling.substr(3), BitWidth, Words))
- return fail(Literal, "raw floating-point literal does not fit its semantics");
- } else if (Spelling.starts_with("0x") || Spelling.starts_with("0X")) {
- std::string_view Digits = Spelling.substr(2);
- char Prefix = '\0';
- if (!Digits.empty() && !hexDigit(Digits.front())) {
- Prefix = Digits.front();
- Digits.remove_prefix(1);
- }
- char ExpectedPrefix = '\0';
- switch (*Semantics) {
- case FloatSemantics::IEEEHalf:
- ExpectedPrefix = 'H';
- break;
- case FloatSemantics::BFloat:
- ExpectedPrefix = 'R';
- break;
- case FloatSemantics::X87DoubleExtended:
- ExpectedPrefix = 'K';
- break;
- case FloatSemantics::IEEEQuad:
- ExpectedPrefix = 'L';
- break;
- case FloatSemantics::PPCDoubleDouble:
- case FloatSemantics::PPCDoubleDoubleLegacy:
- ExpectedPrefix = 'M';
- break;
- default:
- break;
- }
- if (ExpectedPrefix != '\0') {
- if (Prefix != ExpectedPrefix || !parseHexWords(Digits, BitWidth, Words))
- return fail(Literal, "floating-point hexadecimal prefix does not match its semantics");
- } else if (Prefix != '\0') {
- return fail(Literal, "unexpected floating-point hexadecimal prefix");
- } else if (*Semantics == FloatSemantics::IEEESingle) {
- std::uint64_t DoubleBits = 0;
- if (!parseHexadecimal(Spelling, DoubleBits))
- return fail(Literal, "invalid IEEE single hexadecimal literal");
- const float Value = static_cast<float>(std::bit_cast<double>(DoubleBits));
- Words = {std::bit_cast<std::uint32_t>(Value)};
- } else if (*Semantics == FloatSemantics::IEEEDouble) {
- std::uint64_t DoubleBits = 0;
- if (!parseHexadecimal(Spelling, DoubleBits))
- return fail(Literal, "invalid IEEE double hexadecimal literal");
- Words = {DoubleBits};
- } else {
- return fail(Literal, "floating-point literal requires its semantics prefix");
- }
- } else {
- const char *First = Spelling.data();
- const char *Last = First + Spelling.size();
- if (*Semantics == FloatSemantics::IEEEDouble) {
- double Value = 0;
- const auto Parsed = std::from_chars(First, Last, Value, std::chars_format::general);
- if (Parsed.ec != std::errc{} || Parsed.ptr != Last)
- return fail(Literal, "invalid IEEE double decimal literal");
- Words = {std::bit_cast<std::uint64_t>(Value)};
- } else if (*Semantics == FloatSemantics::IEEESingle || *Semantics == FloatSemantics::IEEEHalf ||
- *Semantics == FloatSemantics::BFloat) {
- float Value = 0;
- const auto Parsed = std::from_chars(First, Last, Value, std::chars_format::general);
- if (Parsed.ec != std::errc{} || Parsed.ptr != Last)
- return fail(Literal, "invalid floating-point decimal literal");
- if (*Semantics == FloatSemantics::IEEESingle)
- Words = {std::bit_cast<std::uint32_t>(Value)};
- else if (*Semantics == FloatSemantics::IEEEHalf)
- Words = {floatToHalfBits(Value)};
- else
- Words = {floatToBFloatBits(Value)};
- } else {
- return fail(Literal, "this floating-point semantics requires an exact hexadecimal literal");
- }
- }
-
- auto Bits = FloatingPointBits::create(*Semantics, Words);
- if (!Bits)
- return importError(Literal, Bits.error());
+ std::string ConstantText(Begin.stringValue());
+ ConstantText.push_back(' ');
+ ConstantText.append(Literal.spelling());
+ auto Constant = parseLLVMConstant(ConstantText);
+ if (!Constant)
+ return fail(Literal, std::string(Constant.error().message()));
+ if (!Constant->FloatingValue || Constant->FloatingValue->semantics() != *Semantics)
+ return fail(Literal, "floating-point literal produced the wrong semantics");
advance();
Result = ParsedOperand{
- MachineOperand::floatingImmediate(*Bits),
+ MachineOperand::floatingImmediate(*Constant->FloatingValue),
std::nullopt,
{Begin.range().Begin, token().range().Begin},
};
@@ -1485,10 +1355,10 @@ bool MIParser::Impl::parseMetadataOperand(ParsedOperand &Result) {
while (!at(MITokenKind::RightParen)) {
if (at(MITokenKind::Identifier)) {
const auto Opcode = parseDIExpressionOpcodeName(token().stringValue());
- if (!Opcode)
- return fail("unknown debug expression opcode '" + std::string(token().stringValue()) +
- "'");
- Elements.push_back(*Opcode);
+ const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue());
+ if (!Opcode && !Encoding)
+ return fail(invalidDIExpressionElementMessage(token().stringValue()));
+ Elements.push_back(Opcode ? *Opcode : *Encoding);
advance();
} else if (at(MITokenKind::IntegerLiteral)) {
std::int64_t Signed = 0;
@@ -1855,10 +1725,22 @@ bool MIParser::Impl::parseMemoryBase(MemoryBase &Result, std::int64_t &Offset) {
break;
case MITokenKind::IRValue:
case MITokenKind::NamedIRValue:
- case MITokenKind::QuotedIRValue:
Result = MemoryBase::irValue(symbolName(BaseToken));
advance();
break;
+ case MITokenKind::QuotedIRValue: {
+ LLVMConstantSyntaxOptions ConstantOptions;
+ ConstantOptions.DefaultPointerSizeInBits = Context.DefaultPointerSizeInBits;
+ auto Constant = parseLLVMConstant(BaseToken.stringValue(), ConstantOptions);
+ if (!Constant)
+ return importError(BaseToken, Constant.error());
+ auto Base = MemoryBase::irConstant(std::move(*Constant));
+ if (!Base)
+ return importError(BaseToken, Base.error());
+ Result = std::move(*Base);
+ advance();
+ break;
+ }
case MITokenKind::Keyword:
if (BaseToken.isKeyword("unknown-address")) {
Result = MemoryBase();
@@ -2021,6 +1903,7 @@ bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result) {
if (!Size)
return importError(Begin, Size.error());
Description.Size = *Size;
+ Description.MemoryType = Type;
} else {
return fail("expected a memory size, unknown-size, or memory low-level type");
}
@@ -2044,21 +1927,27 @@ bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result) {
advance();
if (parseMemoryBase(Description.Base, Description.Offset))
return true;
+ if (const GenericConstantData *Constant = Description.Base.constantValue())
+ Description.AddressSpace = Constant->Type.AddressSpace;
}
+ Alignment BaseAlignment = Description.Align;
std::unordered_set<std::string> SeenOptions;
while (consume(MITokenKind::Comma)) {
const std::string Name(token().spelling());
if (!SeenOptions.insert(Name).second)
return fail("duplicate machine memory operand option '" + Name + "'");
if (atKeyword("align")) {
- if (parseAlignment(Description.Align))
+ Alignment ParsedAlignment;
+ if (parseAlignment(ParsedAlignment))
return true;
+ if ((static_cast<std::uint64_t>(Description.Offset) & (ParsedAlignment.bytes() - 1U)) != 0) {
+ return fail("specified alignment is more aligned than offset");
+ }
+ BaseAlignment = ParsedAlignment;
} else if (atKeyword("basealign")) {
- Alignment BaseAlignment;
if (parseAlignment(BaseAlignment))
return true;
- Description.BaseAlign = BaseAlignment;
} else if (atKeyword("addrspace")) {
advance();
if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Description.AddressSpace) ||
@@ -2088,6 +1977,19 @@ bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result) {
return fail("unknown machine memory operand option '" + Name + "'");
}
}
+
+ std::uint64_t EffectiveAlignment = BaseAlignment.bytes();
+ if (Description.Offset != 0) {
+ const auto OffsetBits = static_cast<std::uint64_t>(Description.Offset);
+ const std::uint64_t OffsetAlignment = std::uint64_t{1} << std::countr_zero(OffsetBits);
+ EffectiveAlignment = std::min(EffectiveAlignment, OffsetAlignment);
+ }
+ Description.Align = *Alignment::create(EffectiveAlignment);
+ if (BaseAlignment != Description.Align)
+ Description.BaseAlign = BaseAlignment;
+ else
+ Description.BaseAlign.reset();
+
if (expect(MITokenKind::RightParen, "')' after the machine memory operand"))
return true;
@@ -2527,6 +2429,78 @@ bool MIParser::Impl::parseOperand(bool IsDef, ParsedOperand &Result) {
return false;
}
+bool MIParser::Impl::parseInlineAsmExtraInfoOperand(ParsedOperand &Result) {
+ const MIToken Begin = token();
+ InlineAsmExtraInfo ExtraInfo;
+ while (at(MITokenKind::Identifier)) {
+ const auto Bits = InlineAsmExtraInfo::namedFlagBits(token().stringValue());
+ if (!Bits) {
+ return fail("unknown inline asm extra info flag '" + std::string(token().stringValue()) +
+ "'");
+ }
+ ExtraInfo = InlineAsmExtraInfo(ExtraInfo.rawValue() | *Bits);
+ advance();
+ }
+ Result = ParsedOperand{
+ MachineOperand::immediate(static_cast<std::int64_t>(ExtraInfo.rawValue())),
+ std::nullopt,
+ {Begin.range().Begin, token().range().Begin},
+ };
+ return false;
+}
+
+bool MIParser::Impl::parseInlineAsmOperandFlag(ParsedOperand &Result) {
+ const MIToken Begin = token();
+ const std::string KindName(token().stringValue());
+ const auto Kind = InlineAsmOperandFlag::kindForName(KindName);
+ if (!Kind)
+ return fail("unknown inline asm operand kind '" + KindName + "'");
+ advance();
+
+ InlineAsmOperandFlag Flag(*Kind);
+ if (at(MITokenKind::Identifier) && token().stringValue() == "tiedto") {
+ advance();
+ if (!consume(MITokenKind::Colon))
+ return fail("expected ':' after 'tiedto'");
+ if (!at(MITokenKind::NamedRegister))
+ return fail("expected '$N' operand number after 'tiedto:'");
+ std::uint64_t OperandNumber = 0;
+ if (!parseUnsignedDecimal(token().stringValue(), OperandNumber) ||
+ OperandNumber > std::numeric_limits<std::uint32_t>::max() ||
+ !Flag.setMatchingOperand(static_cast<std::uint32_t>(OperandNumber))) {
+ return fail("invalid operand number in tiedto constraint");
+ }
+ advance();
+ } else if (consume(MITokenKind::Colon)) {
+ if (!at(MITokenKind::Identifier))
+ return fail("expected register class or memory constraint name after ':'");
+ const std::string Constraint(token().stringValue());
+ if (*Kind == InlineAsmOperandKind::Memory) {
+ const auto MemoryConstraint = InlineAsmOperandFlag::memoryConstraintForName(Constraint);
+ if (!MemoryConstraint)
+ return fail("unknown memory constraint '" + Constraint + "'");
+ if (!Flag.setMemoryConstraint(*MemoryConstraint))
+ return fail("invalid memory constraint encoding");
+ } else if (*Kind == InlineAsmOperandKind::RegisterDef ||
+ *Kind == InlineAsmOperandKind::RegisterUse ||
+ *Kind == InlineAsmOperandKind::RegisterDefEarlyClobber) {
+ const RegisterClassDesc *RegisterClass = Target.registers().registerClass(Constraint);
+ if (RegisterClass == nullptr)
+ return fail("unknown register class '" + Constraint + "'");
+ if (!Flag.setRegisterClass(RegisterClass->ID.value()))
+ return fail("register class cannot be encoded in an inline asm operand flag");
+ }
+ advance();
+ }
+
+ Result = ParsedOperand{
+ MachineOperand::immediate(static_cast<std::int64_t>(Flag.rawValue())),
+ std::nullopt,
+ {Begin.range().Begin, token().range().Begin},
+ };
+ return false;
+}
+
bool MIParser::Impl::parseBlockLiveIns(MachineBasicBlock &Block) {
advance();
if (expect(MITokenKind::Colon, "':' after liveins"))
@@ -2566,7 +2540,7 @@ bool MIParser::Impl::parseBlockSuccessors(MachineBasicBlock &Block) {
return false;
do {
if (!at(MITokenKind::MachineBasicBlock))
- return fail("expected a machine basic block successor");
+ return fail("expected a machine basic block reference");
MachineBasicBlock *Successor = blockFor(token());
if (Successor == nullptr)
return true;
@@ -2575,7 +2549,7 @@ bool MIParser::Impl::parseBlockSuccessors(MachineBasicBlock &Block) {
if (consume(MITokenKind::LeftParen)) {
std::uint32_t Weight = 0;
if (parseUnsigned32(token(), Weight))
- return fail("invalid successor probability weight");
+ return fail("expected unsigned integer");
Probability = BranchProbability::fromRaw(Weight);
advance();
if (expect(MITokenKind::RightParen, "')' after successor weight"))
@@ -2623,6 +2597,7 @@ bool MIParser::Impl::parseInstruction(MachineBasicBlock &Block, bool InBundle,
if (Descriptor == nullptr)
return fail("unknown machine instruction '" + std::string(OpcodeToken.stringValue()) + "'");
advance();
+ const bool IsInlineAsm = Descriptor->Name == "INLINEASM" || Descriptor->Name == "INLINEASM_BR";
while (!token().isNewlineOrEnd() && !at(MITokenKind::DoubleColon) &&
!at(MITokenKind::LeftBrace) && !atKeyword("debug-instr-number") &&
@@ -2633,7 +2608,16 @@ bool MIParser::Impl::parseInstruction(MachineBasicBlock &Block, bool InBundle,
if (Operands.size() >= Options.MaximumOperandsPerInstruction)
return fail("instruction operand count exceeds parser limit");
ParsedOperand Operand{MachineOperand::immediate(0), std::nullopt, {}};
- if (parseOperand(false, Operand))
+ const bool IsInlineAsmExtraInfo =
+ IsInlineAsm && Operands.size() == InlineAsmExtraInfoOperandIndex &&
+ at(MITokenKind::Identifier);
+ const bool IsInlineAsmOperandFlag =
+ IsInlineAsm && Operands.size() >= InlineAsmFirstConstraintOperandIndex &&
+ at(MITokenKind::Identifier);
+ if (IsInlineAsmExtraInfo
+ ? parseInlineAsmExtraInfoOperand(Operand)
+ : (IsInlineAsmOperandFlag ? parseInlineAsmOperandFlag(Operand)
+ : parseOperand(false, Operand)))
return true;
Operands.push_back(std::move(Operand));
if (token().isNewlineOrEnd() || at(MITokenKind::DoubleColon) || at(MITokenKind::LeftBrace) ||
@@ -2885,6 +2869,7 @@ bool MIParser::Impl::inferSuccessors() {
continue;
bool BlocksFallthrough = true;
+ std::vector<MachineBasicBlock *> Successors;
for (MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
Instruction = Instruction->next()) {
const InstructionDescriptor *Descriptor =
@@ -2894,17 +2879,25 @@ bool MIParser::Impl::inferSuccessors() {
if (Descriptor->Flags.has(InstructionFlag::Branch)) {
for (const MachineOperand &Operand : Instruction->operands()) {
if (const BasicBlockOperand *TargetBlock = Operand.getIf<BasicBlockOperand>()) {
- auto Added = Function.cfgUpdater().addSuccessor(*Block, *TargetBlock->Block);
- if (!Added)
- return importError(token(), Added.error());
+ if (std::ranges::find(Successors, TargetBlock->Block) == Successors.end())
+ Successors.push_back(TargetBlock->Block);
}
}
}
if (Descriptor->Flags.has(InstructionFlag::Barrier))
BlocksFallthrough = false;
}
- if (BlocksFallthrough && Block->next() != nullptr) {
- auto Added = Function.cfgUpdater().addSuccessor(*Block, *Block->next());
+ if (BlocksFallthrough && Block->next() != nullptr &&
+ std::ranges::find(Successors, Block->next()) == Successors.end())
+ Successors.push_back(Block->next());
+ if (Successors.empty())
+ continue;
+ const auto Probability =
+ BranchProbability::fromFraction(1U, static_cast<std::uint32_t>(Successors.size()));
+ if (!Probability)
+ return importError(token(), Probability.error());
+ for (MachineBasicBlock *Successor : Successors) {
+ auto Added = Function.cfgUpdater().addSuccessor(*Block, *Successor, *Probability);
if (!Added)
return importError(token(), Added.error());
}
diff --git a/backend/lib/MIR/MIPrinter.cpp b/backend/lib/MIR/MIPrinter.cpp
index 8ad6d6c..9e86b72 100644
--- a/backend/lib/MIR/MIPrinter.cpp
+++ b/backend/lib/MIR/MIPrinter.cpp
@@ -2,6 +2,7 @@
#include "rcc/MIR/MIPrinter.h"
+#include "rcc/CodeGen/InlineAsm.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCFIInfo.h"
@@ -14,6 +15,8 @@
#include "rcc/CodeGen/TargetDescription.h"
#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
+#include "rcc/MIR/LLVMConstantSyntax.h"
+#include "rcc/MIR/MIDebugSyntax.h"
#include <algorithm>
#include <array>
@@ -83,52 +86,6 @@ namespace {
return Result;
}
-[[nodiscard]] std::string unsignedDecimal(std::span<const std::uint64_t> InputWords) {
- std::vector<std::uint64_t> Words(InputWords.begin(), InputWords.end());
- while (!Words.empty() && Words.back() == 0)
- Words.pop_back();
- if (Words.empty())
- return "0";
-
- std::string Reversed;
- while (!Words.empty()) {
- std::uint64_t Remainder = 0;
- for (std::size_t Index = Words.size(); Index != 0; --Index) {
- const unsigned __int128 Dividend =
- (static_cast<unsigned __int128>(Remainder) << 64U) | Words[Index - 1U];
- Words[Index - 1U] = static_cast<std::uint64_t>(Dividend / 10U);
- Remainder = static_cast<std::uint64_t>(Dividend % 10U);
- }
- Reversed.push_back(static_cast<char>('0' + Remainder));
- while (!Words.empty() && Words.back() == 0)
- Words.pop_back();
- }
- std::ranges::reverse(Reversed);
- return Reversed;
-}
-
-[[nodiscard]] std::string_view floatKeyword(FloatSemantics Semantics) noexcept {
- switch (Semantics) {
- case FloatSemantics::IEEEHalf:
- return "half";
- case FloatSemantics::BFloat:
- return "bfloat";
- case FloatSemantics::IEEESingle:
- return "float";
- case FloatSemantics::IEEEDouble:
- return "double";
- case FloatSemantics::IEEEQuad:
- return "fp128";
- case FloatSemantics::PPCDoubleDouble:
- case FloatSemantics::PPCDoubleDoubleLegacy:
- return "ppc_fp128";
- case FloatSemantics::X87DoubleExtended:
- return "x86_fp80";
- default:
- return {};
- }
-}
-
[[nodiscard]] std::string_view integerPredicateName(IntegerPredicate Predicate) noexcept {
switch (Predicate) {
case IntegerPredicate::Equal:
@@ -314,9 +271,13 @@ private:
[[nodiscard]] Expected<void> initialize();
[[nodiscard]] Expected<void> printBlock(const MachineBasicBlock &Block);
[[nodiscard]] Expected<void> printInstruction(const MachineInstr &Instruction);
- [[nodiscard]] Expected<void> printOperand(const MachineOperand &Operand, bool IsExplicitDef);
+ [[nodiscard]] Expected<void> printOperand(const MachineOperand &Operand, bool IsExplicitDef,
+ bool PrintVirtualType = true);
+ [[nodiscard]] Expected<void> printInlineAsmExtraInfo(const MachineOperand &Operand);
+ [[nodiscard]] Expected<std::size_t>
+ printInlineAsmOperandFlag(const MachineOperand &Operand);
[[nodiscard]] Expected<void> printRegisterOperand(const MachineOperand &Operand,
- bool IsExplicitDef);
+ bool IsExplicitDef, bool PrintVirtualType);
[[nodiscard]] Expected<void> printTargetFlags(std::uint32_t Flags);
[[nodiscard]] Expected<void> printMetadataReference(MachineMetadataReference Reference);
[[nodiscard]] Expected<void> printDILocation(const MachineDILocation &Location,
@@ -437,11 +398,6 @@ Expected<void> BodyPrinter::appendPhysicalRegister(PhysicalRegister Register) {
void BodyPrinter::appendBlockReference(const MachineBasicBlock &Block) {
Output.append("%bb.");
Output.append(std::to_string(BlockIDs.at(&Block)));
- const auto &Name = Block.attributes().name();
- if (Name) {
- Output.push_back('.');
- Output.append(quoteName(*Name));
- }
}
void BodyPrinter::appendOffset(std::int64_t Offset) {
@@ -528,7 +484,7 @@ Expected<void> BodyPrinter::printDILocation(const MachineDILocation &Location,
}
Expected<void> BodyPrinter::printRegisterOperand(const MachineOperand &Operand,
- bool IsExplicitDef) {
+ bool IsExplicitDef, bool PrintVirtualType) {
const RegisterOperandFlags Flags = Operand.registerFlags();
if (Flags.has(RegisterOperandFlag::Implicit) && Flags.has(RegisterOperandFlag::Define)) {
Output.append("implicit-def ");
@@ -570,11 +526,19 @@ Expected<void> BodyPrinter::printRegisterOperand(const MachineOperand &Operand,
if (!Printed)
return Printed;
} else {
- auto VirtualIndex = virtualRegisterID(Reg);
- if (!VirtualIndex)
- return std::unexpected(VirtualIndex.error());
+ auto Info = Function.registerInfo().virtualRegisterInfo(Reg);
+ if (!Info)
+ return std::unexpected(Info.error());
+ const VirtualRegisterInfo &RegisterInfo = Info->get();
Output.push_back('%');
- Output.append(std::to_string(*VirtualIndex));
+ if (RegisterInfo.Name) {
+ Output.append(*RegisterInfo.Name);
+ } else {
+ auto VirtualIndex = virtualRegisterID(Reg);
+ if (!VirtualIndex)
+ return std::unexpected(VirtualIndex.error());
+ Output.append(std::to_string(*VirtualIndex));
+ }
if (Operand.subReg().value() != 0) {
const SubRegIndexDesc *SubReg = Target.registers().subRegIndex(Operand.subReg());
if (SubReg == nullptr)
@@ -584,28 +548,26 @@ Expected<void> BodyPrinter::printRegisterOperand(const MachineOperand &Operand,
Output.append(SubReg->Name);
}
- auto Info = Function.registerInfo().virtualRegisterInfo(Reg);
- if (!Info)
- return std::unexpected(Info.error());
- const VirtualRegisterInfo &RegisterInfo = Info->get();
- if (RegisterInfo.RegClass) {
+ const bool PrintRegisterConstraint =
+ IsExplicitDef || Function.registerInfo().getUniqueVRegDef(Reg) == nullptr;
+ if (PrintRegisterConstraint && RegisterInfo.RegClass) {
const auto Name = Target.registers().registerClassMIRName(*RegisterInfo.RegClass);
if (!Name)
return makeUnexpected(ErrorCode::VerificationError,
"virtual register class has no MIR name");
Output.push_back(':');
Output.append(*Name);
- } else if (RegisterInfo.Bank) {
+ } else if (PrintRegisterConstraint && RegisterInfo.Bank) {
const auto Name = Target.registers().registerBankMIRName(*RegisterInfo.Bank);
if (!Name)
return makeUnexpected(ErrorCode::VerificationError,
"virtual register bank has no MIR name");
Output.push_back(':');
Output.append(*Name);
- } else if (RegisterInfo.Type) {
+ } else if (PrintRegisterConstraint && RegisterInfo.Type) {
Output.append(":_");
}
- if (RegisterInfo.Type) {
+ if (PrintVirtualType && RegisterInfo.Type) {
auto Type = llvmMIRLowLevelType(*RegisterInfo.Type);
if (!Type)
return std::unexpected(Type.error());
@@ -801,9 +763,10 @@ Expected<void> BodyPrinter::printCFIInstruction(CFIInstructionIndex Index) {
return makeUnexpected(ErrorCode::InternalError, "CFI printer did not handle an operation");
}
-Expected<void> BodyPrinter::printOperand(const MachineOperand &Operand, bool IsExplicitDef) {
+Expected<void> BodyPrinter::printOperand(const MachineOperand &Operand, bool IsExplicitDef,
+ bool PrintVirtualType) {
if (Operand.isRegister())
- return printRegisterOperand(Operand, IsExplicitDef);
+ return printRegisterOperand(Operand, IsExplicitDef, PrintVirtualType);
if (auto Flags = printTargetFlags(Operand.targetFlags()); !Flags)
return Flags;
@@ -819,19 +782,21 @@ Expected<void> BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE
Output.push_back('i');
Output.append(std::to_string(Value.bitWidth()));
Output.push_back(' ');
- Output.append(unsignedDecimal(Value.words()));
+ if (Value.bitWidth() == 1U)
+ Output.append(Value.isZero() ? "false" : "true");
+ else
+ Output.append(Value.toSignedDecimal());
return {};
}
case MachineOperand::Kind::FloatingImmediate: {
const FloatingPointBits &Bits = Operand.getIf<FloatingImmediateOperand>()->Bits;
- const std::string_view Keyword = floatKeyword(Bits.semantics());
- if (Keyword.empty()) {
- return makeUnexpected(ErrorCode::Unsupported,
- "floating-point semantics has no LLVM MIR spelling");
- }
- Output.append(Keyword);
- Output.append(" f0x");
- Output.append(hexadecimalWords(Bits.words(), Bits.bitWidth()));
+ auto Constant = GenericConstantData::floating(Bits);
+ if (!Constant)
+ return std::unexpected(Constant.error());
+ auto Text = printLLVMConstant(*Constant);
+ if (!Text)
+ return std::unexpected(Text.error());
+ Output.append(*Text);
return {};
}
case MachineOperand::Kind::BasicBlock: {
@@ -952,14 +917,7 @@ Expected<void> BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE
return makeUnexpected(ErrorCode::VerificationError,
"metadata operand has no printable value");
}
- Output.append("!DIExpression(");
- const auto Elements = Expression->elements();
- for (std::size_t Index = 0; Index < Elements.size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- Output.append(std::to_string(Elements[Index]));
- }
- Output.push_back(')');
+ Output.append(formatDIExpression(Expression->elements()));
return {};
}
case MachineOperand::Kind::TargetIndex: {
@@ -1039,6 +997,68 @@ Expected<void> BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE
return makeUnexpected(ErrorCode::InternalError, "machine operand printer did not handle a kind");
}
+Expected<void> BodyPrinter::printInlineAsmExtraInfo(const MachineOperand &Operand) {
+ const auto *Value = Operand.getIf<std::int64_t>();
+ if (Value == nullptr || Operand.targetFlags() != 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "inline assembly extra info must be an unflagged immediate");
+ }
+ const InlineAsmExtraInfo ExtraInfo(static_cast<std::uint32_t>(*Value));
+ const std::vector<std::string_view> Names = ExtraInfo.names();
+ for (std::size_t Index = 0; Index < Names.size(); ++Index) {
+ if (Index != 0)
+ Output.push_back(' ');
+ Output.append(Names[Index]);
+ }
+ return {};
+}
+
+Expected<std::size_t> BodyPrinter::printInlineAsmOperandFlag(const MachineOperand &Operand) {
+ const auto *Value = Operand.getIf<std::int64_t>();
+ if (Value == nullptr || *Value < 0 ||
+ static_cast<std::uint64_t>(*Value) > std::numeric_limits<std::uint32_t>::max() ||
+ Operand.targetFlags() != 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "inline assembly operand flag must be an unflagged 32-bit immediate");
+ }
+
+ const InlineAsmOperandFlag Flag(static_cast<std::uint32_t>(*Value));
+ const std::string_view KindName = InlineAsmOperandFlag::kindName(Flag.kind());
+ if (KindName.empty() || Flag.operandCount() == 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "inline assembly operand flag has an invalid kind or operand count");
+ }
+ Output.append(KindName);
+
+ if (const auto MatchingOperand = Flag.matchingOperand()) {
+ Output.append(" tiedto:$");
+ Output.append(std::to_string(*MatchingOperand));
+ return Flag.operandCount();
+ }
+ if (Flag.kind() == InlineAsmOperandKind::Memory ||
+ Flag.kind() == InlineAsmOperandKind::Function) {
+ const auto Constraint = InlineAsmOperandFlag::memoryConstraintName(Flag.memoryConstraint());
+ if (Constraint) {
+ Output.push_back(':');
+ Output.append(*Constraint);
+ }
+ return Flag.operandCount();
+ }
+ if (Flag.isRegisterKind()) {
+ if (const auto RegisterClass = Flag.registerClass()) {
+ const auto Name =
+ Target.registers().registerClassMIRName(RegisterClassID::fromValue(*RegisterClass));
+ if (!Name) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "inline assembly operand flag references an unknown register class");
+ }
+ Output.push_back(':');
+ Output.append(*Name);
+ }
+ }
+ return Flag.operandCount();
+}
+
Expected<void> BodyPrinter::printMemoryBase(const MemoryBase &Base, std::int64_t Offset) {
switch (Base.kind()) {
case MemoryBase::Kind::Unknown:
@@ -1123,6 +1143,20 @@ Expected<void> BodyPrinter::printMemoryBase(const MemoryBase &Base, std::int64_t
Output.append(quoteName(*Name));
break;
}
+ case MemoryBase::Kind::IRConstant: {
+ const GenericConstantData *Constant = Base.constantValue();
+ if (Constant == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "IR-constant memory base has no constant value");
+ }
+ auto Printed = printLLVMConstant(*Constant);
+ if (!Printed)
+ return std::unexpected(Printed.error());
+ Output.push_back('`');
+ Output.append(*Printed);
+ Output.push_back('`');
+ break;
+ }
case MemoryBase::Kind::Stack:
Output.append("stack");
break;
@@ -1219,18 +1253,27 @@ Expected<void> BodyPrinter::printMemoryOperand(const MachineMemOperand &Operand)
}
Output.push_back(' ');
- switch (Description.Size.kind()) {
- case MemorySize::Kind::Unknown:
- Output.append("unknown-size");
- break;
- case MemorySize::Kind::Fixed:
- Output.append(std::to_string(Description.Size.minimumBytes()));
- break;
- case MemorySize::Kind::Scalable:
- Output.append("(<vscale x ");
- Output.append(std::to_string(Description.Size.minimumBytes()));
- Output.append(" x s8>)");
- break;
+ if (Description.MemoryType) {
+ auto Type = llvmMIRLowLevelType(*Description.MemoryType);
+ if (!Type)
+ return std::unexpected(Type.error());
+ Output.push_back('(');
+ Output.append(*Type);
+ Output.push_back(')');
+ } else {
+ switch (Description.Size.kind()) {
+ case MemorySize::Kind::Unknown:
+ Output.append("unknown-size");
+ break;
+ case MemorySize::Kind::Fixed:
+ Output.append(std::to_string(Description.Size.minimumBytes()));
+ break;
+ case MemorySize::Kind::Scalable:
+ Output.append("(<vscale x ");
+ Output.append(std::to_string(Description.Size.minimumBytes()));
+ Output.append(" x s8>)");
+ break;
+ }
}
const std::string_view Preposition =
@@ -1240,9 +1283,13 @@ Expected<void> BodyPrinter::printMemoryOperand(const MachineMemOperand &Operand)
return Printed;
}
- Output.append(", align ");
- Output.append(std::to_string(Description.Align.bytes()));
- if (Description.BaseAlign) {
+ if (!Description.Size.isKnown() ||
+ (Description.Size.minimumBytes() != 0 &&
+ Description.Align.bytes() != Description.Size.minimumBytes())) {
+ Output.append(", align ");
+ Output.append(std::to_string(Description.Align.bytes()));
+ }
+ if (Description.BaseAlign && *Description.BaseAlign != Description.Align) {
Output.append(", basealign ");
Output.append(std::to_string(Description.BaseAlign->bytes()));
}
@@ -1337,13 +1384,74 @@ Expected<void> BodyPrinter::printInstruction(const MachineInstr &Instruction) {
Output.append(Descriptor->Name);
bool HasPrintedTail = false;
+ const bool IsInlineAsm = Descriptor->Name == "INLINEASM" || Descriptor->Name == "INLINEASM_BR";
+ std::size_t InlineAsmPayloadRemaining = 0;
for (std::size_t Index = ExplicitDefs; Index < Instruction.operandCount(); ++Index) {
Output.append(HasPrintedTail ? ", " : " ");
HasPrintedTail = true;
- if (auto Printed = printOperand(*Instruction.operandAt(Index), false); !Printed) {
- return Printed;
+ const MachineOperand &Operand = *Instruction.operandAt(Index);
+ if (IsInlineAsm && Index == InlineAsmExtraInfoOperandIndex) {
+ auto Printed = printInlineAsmExtraInfo(Operand);
+ if (!Printed)
+ return Printed;
+ continue;
}
+ if (IsInlineAsm && Index >= InlineAsmFirstConstraintOperandIndex &&
+ InlineAsmPayloadRemaining == 0) {
+ auto OperandCount = printInlineAsmOperandFlag(Operand);
+ if (!OperandCount)
+ return std::unexpected(OperandCount.error());
+ InlineAsmPayloadRemaining = *OperandCount;
+ continue;
+ }
+ bool PrintVirtualType = true;
+ if (Descriptor->Flags.has(InstructionFlag::Generic) &&
+ !Descriptor->Flags.has(InstructionFlag::Phi) &&
+ Index < Descriptor->Operands.size()) {
+ const std::optional<OperandTypeConstraint> &UseConstraint =
+ Descriptor->Operands[Index].TypeConstraint;
+ if (UseConstraint) {
+ for (std::size_t DefIndex = 0; DefIndex < ExplicitDefs; ++DefIndex) {
+ if (DefIndex >= Descriptor->Operands.size())
+ break;
+ const std::optional<OperandTypeConstraint> &DefConstraint =
+ Descriptor->Operands[DefIndex].TypeConstraint;
+ if (DefConstraint && DefConstraint->EqualityGroup == UseConstraint->EqualityGroup) {
+ PrintVirtualType = false;
+ break;
+ }
+ }
+ } else if (const Register *UseRegister = Operand.getIf<Register>();
+ UseRegister != nullptr && UseRegister->isVirtual()) {
+ const auto UseInfo = Function.registerInfo().virtualRegisterInfo(*UseRegister);
+ for (std::size_t DefIndex = 0; DefIndex < ExplicitDefs && UseInfo &&
+ UseInfo->get().Type;
+ ++DefIndex) {
+ if (DefIndex >= Descriptor->Operands.size() ||
+ Descriptor->Operands[DefIndex].TypeConstraint)
+ continue;
+ const MachineOperand *DefOperand = Instruction.operandAt(DefIndex);
+ const Register *DefRegister =
+ DefOperand == nullptr ? nullptr : DefOperand->getIf<Register>();
+ if (DefRegister == nullptr || !DefRegister->isVirtual())
+ continue;
+ const auto DefInfo = Function.registerInfo().virtualRegisterInfo(*DefRegister);
+ if (DefInfo && DefInfo->get().Type == UseInfo->get().Type) {
+ PrintVirtualType = false;
+ break;
+ }
+ }
+ }
+ }
+ auto Printed = printOperand(Operand, false, PrintVirtualType);
+ if (!Printed)
+ return Printed;
+ if (IsInlineAsm && InlineAsmPayloadRemaining != 0)
+ --InlineAsmPayloadRemaining;
}
+ if (InlineAsmPayloadRemaining != 0)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "inline assembly operand flag has missing payload operands");
const auto BeginAttachment = [this, &HasPrintedTail](std::string_view Name) {
Output.append(HasPrintedTail ? ", " : " ");
@@ -1490,6 +1598,26 @@ Expected<void> BodyPrinter::printBlock(const MachineBasicBlock &Block) {
}
Output.append(":\n");
+ bool HasLineAttributes = false;
+ if (!Block.successors().empty()) {
+ Output.append(" successors: ");
+ for (std::size_t Index = 0; Index < Block.successors().size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ const MachineSuccessor &Successor = Block.successors()[Index];
+ if (Successor.Block == nullptr || !BlockIDs.contains(Successor.Block)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "successor references an unlinked block");
+ }
+ appendBlockReference(*Successor.Block);
+ Output.append("(0x");
+ Output.append(hexadecimalWords(
+ std::array{static_cast<std::uint64_t>(Successor.Probability.rawValue())}, 32));
+ Output.push_back(')');
+ }
+ Output.push_back('\n');
+ HasLineAttributes = true;
+ }
if (!Block.liveIns().empty()) {
Output.append(" liveins: ");
for (std::size_t Index = 0; Index < Block.liveIns().size(); ++Index) {
@@ -1504,24 +1632,11 @@ Expected<void> BodyPrinter::printBlock(const MachineBasicBlock &Block) {
}
}
Output.push_back('\n');
+ HasLineAttributes = true;
}
- if (!Block.successors().empty()) {
- Output.append(" successors: ");
- for (std::size_t Index = 0; Index < Block.successors().size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- const MachineSuccessor &Successor = Block.successors()[Index];
- if (Successor.Block == nullptr || !BlockIDs.contains(Successor.Block)) {
- return makeUnexpected(ErrorCode::VerificationError,
- "successor references an unlinked block");
- }
- appendBlockReference(*Successor.Block);
- Output.push_back('(');
- Output.append(std::to_string(Successor.Probability.rawValue()));
- Output.push_back(')');
- }
+
+ if (HasLineAttributes && Block.firstInstruction() != nullptr)
Output.push_back('\n');
- }
bool InBundle = false;
for (const MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr;
diff --git a/backend/lib/MIR/MIRDataParser.cpp b/backend/lib/MIR/MIRDataParser.cpp
index fc70f0f..7e04ace 100644
--- a/backend/lib/MIR/MIRDataParser.cpp
+++ b/backend/lib/MIR/MIRDataParser.cpp
@@ -203,7 +203,7 @@ bool MIRDocumentParser::reserveJumpTables(const yaml::Node &Root, MachineFunctio
Encoding = JumpTableEntryEncoding::Custom32;
EntrySize = 4;
} else {
- return fail(KindNode->range(), "unknown jump table kind '" + std::string(KindName) + "'");
+ return fail(KindNode->range(), "unknown enumerated scalar");
}
const yaml::Node *Entries = mappingValue(*JumpTable, "entries");
diff --git a/backend/lib/MIR/MIRDocumentParser.cpp b/backend/lib/MIR/MIRDocumentParser.cpp
index defcce9..ca2c6cd 100644
--- a/backend/lib/MIR/MIRDocumentParser.cpp
+++ b/backend/lib/MIR/MIRDocumentParser.cpp
@@ -49,8 +49,6 @@ using parser_detail::trim;
bool MIRDocumentParser::fail(const TextRange &Range, std::string Message, std::string Category) {
if (Result.Diagnostics.empty()) {
- if (ActiveFunction != nullptr)
- Message = "function '" + std::string(ActiveFunction->name()) + "': " + std::move(Message);
Result.Diagnostics.push_back(TextDiagnostic{
.Severity = DiagnosticSeverity::Error,
.Category = std::move(Category),
@@ -210,7 +208,14 @@ MIRDocumentParseResult MIRDocumentParser::run() {
std::string(Result.Source.name()) + ":embedded-ir", std::string(FirstRoot->scalar()));
if (!Embedded.succeeded()) {
const auto Diagnostics = Embedded.diagnostics();
- Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end());
+ if (Diagnostics.empty()) {
+ static_cast<void>(fail(FirstRoot->range(), "embedded LLVM IR indexing failed",
+ "mir-embedded-ir"));
+ } else {
+ const TextDiagnostic &Diagnostic = Diagnostics.front();
+ const TextRange Range = scalarDiagnosticRange(*FirstRoot, Diagnostic.Range);
+ static_cast<void>(fail(Range, Diagnostic.Message, Diagnostic.Category));
+ }
return std::move(Result);
}
Staging.EmbeddedIR = std::move(Embedded).takeIndex();
@@ -301,22 +306,36 @@ bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Docum
std::string_view{"prefetch-targets"},
std::string_view{"body"},
};
- if (validateKeys(Root, Keys, "machine function"))
- return true;
const yaml::Node *NameNode = mappingValue(Root, "name");
if (NameNode == nullptr)
- return fail(Root.range(), "machine function requires 'name'");
+ return fail(Root.range(), "missing required key 'name'");
+ if (validateKeys(Root, Keys, "machine function"))
+ return true;
std::string_view Name;
- if (scalar(*NameNode, Name, "machine function name") || Name.empty())
- return fail(NameNode->range(), "machine function name cannot be empty");
+ if (scalar(*NameNode, Name, "machine function name"))
+ return true;
+ const EmbeddedIRFunction *IRFunction = nullptr;
if (Document.EmbeddedIR) {
- const EmbeddedIRFunction *IRFunction = Document.EmbeddedIR->function(Name);
+ if (Name.empty()) {
+ std::size_t AnonymousOrdinal = 0;
+ for (std::size_t Index = 0; Index < Document.Unit.functionCount(); ++Index) {
+ const MachineFunction *Existing = Document.Unit.functionAt(Index);
+ if (Existing != nullptr && Existing->name().empty())
+ ++AnonymousOrdinal;
+ }
+ IRFunction = Document.EmbeddedIR->unnamedFunction(AnonymousOrdinal);
+ } else {
+ IRFunction = Document.EmbeddedIR->function(Name);
+ }
if (IRFunction == nullptr)
- return fail(NameNode->range(),
- "machine function has no corresponding embedded LLVM IR function");
+ return fail(NameNode->range(), "function '" + std::string(Name) +
+ "' isn't defined in the provided LLVM IR");
}
+ if (!Name.empty() && Document.Unit.function(Name) != nullptr)
+ return fail(NameNode->range(), "redefinition of machine function '" + std::string(Name) + "'");
+
auto Created = Document.Unit.createMachineFunction(std::string(Name));
if (!Created)
return importError(NameNode->range(), Created.error());
@@ -332,6 +351,11 @@ bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Docum
};
MIParserContext Context;
Context.DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits;
+ if (IRFunction != nullptr) {
+ Context.HasIRBlock = [IRFunction](std::string_view BlockName) {
+ return IRFunction->block(BlockName) != nullptr;
+ };
+ }
std::vector<PendingJumpTable> PendingJumpTables;
if (parseFunctionScalars(Root, Function, DocumentInfo) ||
diff --git a/backend/lib/MIR/MIRDocumentParser.h b/backend/lib/MIR/MIRDocumentParser.h
index 53a31b4..3fade39 100644
--- a/backend/lib/MIR/MIRDocumentParser.h
+++ b/backend/lib/MIR/MIRDocumentParser.h
@@ -97,8 +97,8 @@ private:
std::span<const PendingJumpTable> Pending);
[[nodiscard]] bool parseBody(const yaml::Node &Root, MachineFunction &Function,
MIParserContext &Context);
- [[nodiscard]] TextRange bodyDiagnosticRange(const yaml::Node &Body,
- const TextRange &BodyRange) const noexcept;
+ [[nodiscard]] TextRange scalarDiagnosticRange(const yaml::Node &Scalar,
+ const TextRange &ScalarRange) const noexcept;
[[nodiscard]] bool parseFunctionRecords(const yaml::Node &Root, MachineFunction &Function);
[[nodiscard]] bool parseTargetState(const yaml::Node &Root, MachineFunction &Function);
[[nodiscard]] bool convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination);
diff --git a/backend/lib/MIR/MIRDocumentPrinter.cpp b/backend/lib/MIR/MIRDocumentPrinter.cpp
index dba1ec7..5cc0173 100644
--- a/backend/lib/MIR/MIRDocumentPrinter.cpp
+++ b/backend/lib/MIR/MIRDocumentPrinter.cpp
@@ -17,6 +17,7 @@
#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
#include "rcc/MIR/LLVMConstantSyntax.h"
+#include "rcc/MIR/MIDebugSyntax.h"
#include "rcc/MIR/MIRDocument.h"
#include <algorithm>
@@ -38,16 +39,206 @@
namespace rcc::mir {
namespace {
+enum class YAMLQuoting : std::uint8_t {
+ None,
+ Single,
+ Double,
+};
+
+[[nodiscard]] bool isASCIIAlphanumeric(unsigned char Character) noexcept {
+ return (Character >= '0' && Character <= '9') || (Character >= 'A' && Character <= 'Z') ||
+ (Character >= 'a' && Character <= 'z');
+}
+
+[[nodiscard]] bool isYAMLNumeric(std::string_view Value) noexcept {
+ if (Value.empty() || Value == "+" || Value == "-")
+ return false;
+ if (Value == ".nan" || Value == ".NaN" || Value == ".NAN")
+ return true;
+
+ std::string_view Tail = Value;
+ if (Tail.front() == '+' || Tail.front() == '-')
+ Tail.remove_prefix(1);
+ if (Tail == ".inf" || Tail == ".Inf" || Tail == ".INF")
+ return true;
+ if (Value.starts_with("0o")) {
+ return Value.size() > 2U &&
+ Value.substr(2).find_first_not_of("01234567") == std::string_view::npos;
+ }
+ if (Value.starts_with("0x")) {
+ return Value.size() > 2U &&
+ Value.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string_view::npos;
+ }
+
+ if (Tail.empty() || Tail.front() == 'e' || Tail.front() == 'E')
+ return false;
+ std::size_t Position = 0;
+ if (Tail.front() == '.') {
+ ++Position;
+ if (Position == Tail.size() || Tail[Position] < '0' || Tail[Position] > '9')
+ return false;
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ } else {
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ if (Position == 0)
+ return false;
+ }
+
+ if (Position < Tail.size() && Tail[Position] == '.') {
+ ++Position;
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ }
+ if (Position == Tail.size())
+ return true;
+ if (Tail[Position] != 'e' && Tail[Position] != 'E')
+ return false;
+ ++Position;
+ if (Position < Tail.size() && (Tail[Position] == '+' || Tail[Position] == '-'))
+ ++Position;
+ const std::size_t ExponentBegin = Position;
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ return Position == Tail.size() && Position != ExponentBegin;
+}
+
+[[nodiscard]] YAMLQuoting yamlQuoting(std::string_view Value) noexcept {
+ if (Value.empty())
+ return YAMLQuoting::Single;
+
+ YAMLQuoting Result = YAMLQuoting::None;
+ const auto IsSpace = [](unsigned char Character) {
+ return Character == ' ' || Character == '\t' || Character == '\r' || Character == '\n' ||
+ Character == '\f' || Character == '\v';
+ };
+ if (IsSpace(static_cast<unsigned char>(Value.front())) ||
+ IsSpace(static_cast<unsigned char>(Value.back())) || Value == "null" || Value == "Null" ||
+ Value == "NULL" || Value == "~" || Value == "true" || Value == "True" || Value == "TRUE" ||
+ Value == "false" || Value == "False" || Value == "FALSE" || isYAMLNumeric(Value)) {
+ Result = YAMLQuoting::Single;
+ }
+
+ switch (Value.front()) {
+ case '-':
+ case '?':
+ case ':':
+ case '\\':
+ case ',':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '#':
+ case '&':
+ case '*':
+ case '!':
+ case '|':
+ case '>':
+ case '\'':
+ case '"':
+ case '%':
+ case '@':
+ case '`':
+ Result = YAMLQuoting::Single;
+ break;
+ default:
+ break;
+ }
+
+ for (const char RawCharacter : Value) {
+ const auto Character = static_cast<unsigned char>(RawCharacter);
+ if (isASCIIAlphanumeric(Character))
+ continue;
+ switch (Character) {
+ case '_':
+ case '-':
+ case '^':
+ case '.':
+ case ',':
+ case ' ':
+ case '\t':
+ continue;
+ case '\n':
+ case '\r':
+ case 0x7f:
+ return YAMLQuoting::Double;
+ default:
+ if (Character <= 0x1fU || (Character & 0x80U) != 0)
+ return YAMLQuoting::Double;
+ Result = YAMLQuoting::Single;
+ break;
+ }
+ }
+ return Result;
+}
+
[[nodiscard]] std::string yamlScalar(std::string_view Value) {
+ const YAMLQuoting Quoting = yamlQuoting(Value);
+ if (Quoting == YAMLQuoting::None)
+ return std::string(Value);
+
std::string Result;
Result.reserve(Value.size() + 2U);
- Result.push_back('\'');
+ const char Quote = Quoting == YAMLQuoting::Single ? '\'' : '"';
+ Result.push_back(Quote);
for (const char Character : Value) {
- Result.push_back(Character);
- if (Character == '\'')
- Result.push_back('\'');
+ if (Quoting == YAMLQuoting::Single) {
+ Result.push_back(Character);
+ if (Character == '\'')
+ Result.push_back('\'');
+ continue;
+ }
+ switch (Character) {
+ case '\\':
+ Result.append("\\\\");
+ break;
+ case '"':
+ Result.append("\\\"");
+ break;
+ case '\0':
+ Result.append("\\0");
+ break;
+ case '\a':
+ Result.append("\\a");
+ break;
+ case '\b':
+ Result.append("\\b");
+ break;
+ case '\t':
+ Result.append("\\t");
+ break;
+ case '\n':
+ Result.append("\\n");
+ break;
+ case '\v':
+ Result.append("\\v");
+ break;
+ case '\f':
+ Result.append("\\f");
+ break;
+ case '\r':
+ Result.append("\\r");
+ break;
+ case '\x1b':
+ Result.append("\\e");
+ break;
+ default: {
+ const auto Byte = static_cast<unsigned char>(Character);
+ if (Byte < 0x20U || Byte == 0x7fU) {
+ static constexpr std::string_view Hex = "0123456789ABCDEF";
+ Result.append("\\x");
+ Result.push_back(Hex[Byte >> 4U]);
+ Result.push_back(Hex[Byte & 0xfU]);
+ } else {
+ Result.push_back(Character);
+ }
+ break;
+ }
+ }
}
- Result.push_back('\'');
+ Result.push_back(Quote);
return Result;
}
@@ -396,14 +587,7 @@ Expected<std::string> MIRDocumentPrinter::metadataValue(const MachineMetadataVal
const MachineDIExpression *Expression = Value.expression();
if (Expression == nullptr)
return makeUnexpected(ErrorCode::VerificationError, "metadata value is not printable");
- std::string Text = "!DIExpression(";
- for (std::size_t Index = 0; Index < Expression->elements().size(); ++Index) {
- if (Index != 0)
- Text.append(", ");
- Text.append(std::to_string(Expression->elements()[Index]));
- }
- Text.push_back(')');
- return Text;
+ return formatDIExpression(Expression->elements());
}
Expected<std::string> MIRDocumentPrinter::diLocation(const MachineDILocation &Location,
@@ -679,24 +863,34 @@ Expected<void> MIRDocumentPrinter::printFrameInfo(const MachineFunction &Functio
Output.append(getMachineFramePointerPolicyName(State.FramePointerPolicy));
Output.push_back('\n');
}
- if (State.StackProtector) {
- auto Reference = frameReference(*State.StackProtector, Context);
- if (!Reference)
- return std::unexpected(Reference.error());
+ if (Options.Mode == MIPrintMode::Complete || State.StackProtector) {
+ std::string Reference;
+ if (State.StackProtector) {
+ auto PrintedReference = frameReference(*State.StackProtector, Context);
+ if (!PrintedReference)
+ return std::unexpected(PrintedReference.error());
+ Reference = std::move(*PrintedReference);
+ }
Output.append(" stackProtector: ");
- Output.append(yamlScalar(*Reference));
+ Output.append(yamlScalar(Reference));
Output.push_back('\n');
}
- if (State.FunctionContext) {
- auto Reference = frameReference(*State.FunctionContext, Context);
- if (!Reference)
- return std::unexpected(Reference.error());
+ if (Options.Mode == MIPrintMode::Complete || State.FunctionContext) {
+ std::string Reference;
+ if (State.FunctionContext) {
+ auto PrintedReference = frameReference(*State.FunctionContext, Context);
+ if (!PrintedReference)
+ return std::unexpected(PrintedReference.error());
+ Reference = std::move(*PrintedReference);
+ }
Output.append(" functionContext: ");
- Output.append(yamlScalar(*Reference));
+ Output.append(yamlScalar(Reference));
Output.push_back('\n');
}
- if (State.MaxCallFrameSize)
- Unsigned("maxCallFrameSize", *State.MaxCallFrameSize);
+ if (Options.Mode == MIPrintMode::Complete || State.MaxCallFrameSize) {
+ Unsigned("maxCallFrameSize",
+ State.MaxCallFrameSize.value_or(std::numeric_limits<std::uint32_t>::max()));
+ }
Unsigned("cvBytesOfCalleeSavedRegisters", State.CVBytesOfCalleeSavedRegisters);
Bool("hasOpaqueSPAdjustment", State.HasOpaqueSPAdjustment);
Bool("hasVAStart", State.HasVAStart);
@@ -1161,15 +1355,18 @@ Expected<void> MIRDocumentPrinter::printFunctionRecords(const MachineFunction &F
}
if (!Records.prefetchTargets().empty()) {
- Output.append("prefetch-targets:\n");
- for (const MachinePrefetchTarget &TargetValue : Records.prefetchTargets()) {
+ Output.append("prefetch-targets: [ ");
+ const auto Targets = Records.prefetchTargets();
+ for (std::size_t Index = 0; Index < Targets.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ const MachinePrefetchTarget &TargetValue = Targets[Index];
std::string Text = "bb_id " + std::to_string(TargetValue.Block.BaseID) + ", " +
std::to_string(TargetValue.Block.CloneID) + ", " +
std::to_string(TargetValue.CallsiteIndex);
- Output.append(" - ");
Output.append(yamlScalar(Text));
- Output.push_back('\n');
}
+ Output.append(" ]\n");
}
return {};
}
@@ -1188,9 +1385,9 @@ Expected<void> MIRDocumentPrinter::printFunction(const MachineFunction &Function
Output.append(yamlScalar(Function.name()));
Output.push_back('\n');
const auto Alignment = Function.attributes().alignment();
- if (Alignment) {
+ if (Options.Mode == MIPrintMode::Complete || Alignment) {
Output.append("alignment: ");
- Output.append(std::to_string(Alignment->bytes()));
+ Output.append(std::to_string(Alignment ? Alignment->bytes() : 1U));
Output.push_back('\n');
}
@@ -1244,6 +1441,7 @@ Expected<void> MIRDocumentPrinter::printFunction(const MachineFunction &Function
return std::unexpected(Body.error());
Output.append("body: |\n");
appendBlockScalar(Output, *Body, 2);
+ Output.append("...\n");
return {};
}
@@ -1281,6 +1479,7 @@ Expected<std::string> MIRDocumentPrinter::run() {
if (Options.EmitEmbeddedIR && Document.embeddedIR() != nullptr) {
Output.append("--- |\n");
appendBlockScalar(Output, Document.embeddedIR()->source().text(), 2);
+ Output.append("...\n");
}
for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
const MachineFunction *Function = Document.unit().functionAt(Index);
diff --git a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp
index a6dbb56..69b0dd1 100644
--- a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp
+++ b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp
@@ -27,7 +27,23 @@ Expected<void> verifyMIRDocumentReferences(const MIRDocument &Document,
const EmbeddedIRReferenceIndex *Embedded = UseEmbeddedIR ? Document.embeddedIR() : nullptr;
const EmbeddedIRFunction *IRFunction = nullptr;
if (Embedded != nullptr) {
- IRFunction = Embedded->function(Function.name());
+ if (Function.name().empty()) {
+ std::size_t AnonymousOrdinal = 0;
+ bool FoundFunction = false;
+ for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
+ const MachineFunction *Candidate = Document.unit().functionAt(Index);
+ if (Candidate == &Function) {
+ FoundFunction = true;
+ break;
+ }
+ if (Candidate != nullptr && Candidate->name().empty())
+ ++AnonymousOrdinal;
+ }
+ if (FoundFunction)
+ IRFunction = Embedded->unnamedFunction(AnonymousOrdinal);
+ } else {
+ IRFunction = Embedded->function(Function.name());
+ }
if (IRFunction == nullptr) {
return makeUnexpected(ErrorCode::VerificationError,
"machine function has no matching embedded LLVM IR function");
diff --git a/backend/lib/MIR/MIRFrameParser.cpp b/backend/lib/MIR/MIRFrameParser.cpp
index 76ace8f..9e21dfc 100644
--- a/backend/lib/MIR/MIRFrameParser.cpp
+++ b/backend/lib/MIR/MIRFrameParser.cpp
@@ -408,6 +408,13 @@ bool MIRDocumentParser::parseFrameState(const yaml::Node &Root, MachineFunction
for (const auto &[Key, Destination] : {std::pair{"stackProtector", &State.StackProtector},
std::pair{"functionContext", &State.FunctionContext}}) {
if (const yaml::Node *Value = mappingValue(*Frame, Key)) {
+ std::string_view Reference;
+ if (scalar(*Value, Reference, "frame index"))
+ return true;
+ if (Reference.empty()) {
+ Destination->reset();
+ continue;
+ }
FrameIndex Index;
if (parseFrameReference(*Value, Context, Index))
return true;
@@ -420,7 +427,10 @@ bool MIRDocumentParser::parseFrameState(const yaml::Node &Root, MachineFunction
Size > std::numeric_limits<std::uint32_t>::max()) {
return fail(Value->range(), "maximum call-frame size is outside the 32-bit unsigned range");
}
- State.MaxCallFrameSize = Size;
+ if (Size == std::numeric_limits<std::uint32_t>::max())
+ State.MaxCallFrameSize.reset();
+ else
+ State.MaxCallFrameSize = Size;
}
for (const auto &[Key, Destination] :
{std::pair{"cvBytesOfCalleeSavedRegisters", &State.CVBytesOfCalleeSavedRegisters},
diff --git a/backend/lib/MIR/MIRFunctionParser.cpp b/backend/lib/MIR/MIRFunctionParser.cpp
index 07cb2b0..a59e4bd 100644
--- a/backend/lib/MIR/MIRFunctionParser.cpp
+++ b/backend/lib/MIR/MIRFunctionParser.cpp
@@ -165,10 +165,9 @@ bool MIRDocumentParser::recomputeProperties(const yaml::Node &Root, MachineFunct
const auto ApplyComputed = [this, &Root, &Function](std::optional<bool> Explicit, bool Computed,
MachineFunctionProperty Property,
- std::string_view Name) -> bool {
+ std::string_view Conflict) -> bool {
if (Explicit && *Explicit && !Computed) {
- return fail(Root.range(), "explicit property '" + std::string(Name) +
- "' conflicts with the parsed machine function");
+ return fail(Root.range(), std::string(Function.name()) + std::string(Conflict));
}
if (Explicit.value_or(Computed))
Function.properties().set(Property);
@@ -176,15 +175,18 @@ bool MIRDocumentParser::recomputeProperties(const yaml::Node &Root, MachineFunct
Function.properties().clear(Property);
return false;
};
- if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs, "noPhis") ||
- ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA, "isSSA") ||
- ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs, "noVRegs")) {
+ if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs,
+ " has explicit property NoPhi, but contains at least one PHI") ||
+ ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA,
+ " has explicit property IsSSA, but is not valid SSA") ||
+ ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs,
+ " has explicit property NoVRegs, but contains virtual registers")) {
return true;
}
if (DocumentInfo.HasFakeUses && !*DocumentInfo.HasFakeUses && HasFakeUse) {
- return fail(Root.range(),
- "explicit property 'hasFakeUses: false' conflicts with a fake-use instruction");
+ return fail(Root.range(), std::string(Function.name()) +
+ " has explicit property hasFakeUses=false, but contains fake uses");
}
if (DocumentInfo.HasFakeUses.value_or(HasFakeUse))
Function.attributes().set(MachineFunctionAttribute::HasFakeUses);
@@ -302,7 +304,7 @@ bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction &
const auto Flag = Target.mirText().virtualRegisterFlag(Name);
if (!Flag)
return fail(FlagNode->range(),
- "unknown virtual-register flag '" + std::string(Name) + "'");
+ "use of undefined register flag '" + std::string(Name) + "'");
if ((Mask & *Flag) != 0)
return fail(FlagNode->range(), "duplicate virtual-register flag");
Mask = static_cast<std::uint8_t>(Mask | *Flag);
@@ -368,15 +370,23 @@ bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction &
return false;
}
-TextRange MIRDocumentParser::bodyDiagnosticRange(const yaml::Node &Body,
- const TextRange &BodyRange) const noexcept {
- if (Body.scalarStyle() != yaml::ScalarStyle::Literal)
- return Body.range();
+TextRange MIRDocumentParser::scalarDiagnosticRange(const yaml::Node &Scalar,
+ const TextRange &ScalarRange) const noexcept {
+ if (Scalar.scalarStyle() != yaml::ScalarStyle::Literal &&
+ Scalar.scalarStyle() != yaml::ScalarStyle::Folded) {
+ std::size_t ContentOffset = Scalar.range().Begin.Offset;
+ if (Scalar.scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
+ Scalar.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
+ ++ContentOffset;
+ }
+ return Result.Source.range(ContentOffset + ScalarRange.Begin.Offset,
+ ContentOffset + ScalarRange.End.Offset);
+ }
- const std::size_t FirstBodyLine = Body.range().Begin.Line + 1U;
- const std::size_t LastBodyLine = Body.range().End.Line;
+ const std::size_t FirstBodyLine = Scalar.range().Begin.Line + 1U;
+ const std::size_t LastBodyLine = Scalar.range().End.Line;
if (FirstBodyLine > LastBodyLine || FirstBodyLine == 0)
- return Body.range();
+ return Scalar.range();
std::size_t Indent = 0;
bool FoundContent = false;
@@ -392,9 +402,9 @@ TextRange MIRDocumentParser::bodyDiagnosticRange(const yaml::Node &Body,
break;
}
if (!FoundContent)
- return Body.range();
+ return Scalar.range();
- const TextPosition Fallback = Body.range().Begin;
+ const TextPosition Fallback = Scalar.range().Begin;
const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent,
Fallback](TextPosition Position) {
const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U;
@@ -407,7 +417,7 @@ TextRange MIRDocumentParser::bodyDiagnosticRange(const yaml::Node &Body,
const std::size_t Offset = Result.Source.lineOffsets()[SourceLine - 1U] + SourceColumn;
return Result.Source.positionAt(Offset);
};
- return {.Begin = MapPosition(BodyRange.Begin), .End = MapPosition(BodyRange.End)};
+ return {.Begin = MapPosition(ScalarRange.Begin), .End = MapPosition(ScalarRange.End)};
}
bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Function,
@@ -460,7 +470,7 @@ bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Funct
if (!Parser.diagnostics().empty()) {
const TextRange Range = Body == nullptr
? BodyRange
- : bodyDiagnosticRange(*Body, Parser.diagnostics().front().Range);
+ : scalarDiagnosticRange(*Body, Parser.diagnostics().front().Range);
return fail(Range, Parser.diagnostics().front().Message, "mir-body");
}
return importError(BodyRange, Parsed.error(), "mir-body");
diff --git a/backend/lib/MIR/MIRFunctionRecordsParser.cpp b/backend/lib/MIR/MIRFunctionRecordsParser.cpp
index dcd486e..aed6947 100644
--- a/backend/lib/MIR/MIRFunctionRecordsParser.cpp
+++ b/backend/lib/MIR/MIRFunctionRecordsParser.cpp
@@ -234,21 +234,63 @@ bool MIRDocumentParser::parseFunctionRecords(const yaml::Node &Root, MachineFunc
std::string_view Text;
if (TargetNode == nullptr || scalar(*TargetNode, Text, "prefetch target"))
return true;
- Text = trim(Text);
- if (!Text.starts_with("bb_id"))
- return fail(TargetNode->range(), "prefetch target must start with 'bb_id'");
- Text = trim(Text.substr(std::string_view("bb_id").size()));
+
+ std::size_t ContentOffset = TargetNode->range().Begin.Offset;
+ if (TargetNode->scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
+ TargetNode->scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
+ ++ContentOffset;
+ }
+ const auto ErrorRange = [this, ContentOffset](std::size_t Begin,
+ std::size_t End = 1U) {
+ return Result.Source.range(ContentOffset + Begin, ContentOffset + Begin + End);
+ };
+ const auto SkipSpace = [&Text](std::size_t &Cursor) {
+ while (Cursor < Text.size() && (Text[Cursor] == ' ' || Text[Cursor] == '\t'))
+ ++Cursor;
+ };
+ const auto ParseNumber = [&Text, &SkipSpace](std::size_t &Cursor,
+ std::uint32_t &Value) {
+ SkipSpace(Cursor);
+ const std::size_t Begin = Cursor;
+ while (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9')
+ ++Cursor;
+ return Begin != Cursor && parseUnsigned32(Text.substr(Begin, Cursor - Begin), Value);
+ };
+
+ std::size_t Cursor = 0;
+ SkipSpace(Cursor);
+ if (!Text.substr(Cursor).starts_with("bb_id") ||
+ (Cursor + std::string_view("bb_id").size() < Text.size() &&
+ Text[Cursor + std::string_view("bb_id").size()] != ' ' &&
+ Text[Cursor + std::string_view("bb_id").size()] != '\t')) {
+ return fail(ErrorRange(Cursor), "expected 'bb_id'");
+ }
+ Cursor += std::string_view("bb_id").size();
+
std::array<std::uint32_t, 3> Values{};
- for (std::size_t Index = 0; Index < Values.size(); ++Index) {
- const std::size_t Comma = Text.find(',');
- const std::string_view Part = Index + 1 == Values.size() ? Text : Text.substr(0, Comma);
- if ((Index + 1 != Values.size() && Comma == std::string_view::npos) ||
- !parseUnsigned32(trim(Part), Values[Index])) {
- return fail(TargetNode->range(), "prefetch target has invalid numeric fields");
- }
- if (Index + 1 != Values.size())
- Text = trim(Text.substr(Comma + 1));
+ if (!ParseNumber(Cursor, Values[0]))
+ return fail(ErrorRange(0), "expected unsigned integer");
+
+ SkipSpace(Cursor);
+ if (Cursor < Text.size() && (Text[Cursor] == ',' || Text[Cursor] == '.')) {
+ ++Cursor;
+ if (!ParseNumber(Cursor, Values[1]))
+ return fail(ErrorRange(0), "expected unsigned integer");
+ } else if (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9') {
+ if (!ParseNumber(Cursor, Values[1]))
+ return fail(ErrorRange(0), "expected unsigned integer");
}
+
+ SkipSpace(Cursor);
+ if (Cursor >= Text.size() || Text[Cursor] != ',')
+ return fail(ErrorRange(Cursor), "expected ','");
+ ++Cursor;
+ if (!ParseNumber(Cursor, Values[2]))
+ return fail(ErrorRange(0), "expected unsigned integer");
+ SkipSpace(Cursor);
+ if (Cursor != Text.size())
+ return fail(ErrorRange(0), "unexpected content after prefetch target");
+
auto AddResult = Function.records().addPrefetchTarget(
{.Block = {Values[0], Values[1]}, .CallsiteIndex = Values[2]});
if (!AddResult)
diff --git a/backend/lib/MIR/MIRMetadataParser.cpp b/backend/lib/MIR/MIRMetadataParser.cpp
index 6ce717b..911edb6 100644
--- a/backend/lib/MIR/MIRMetadataParser.cpp
+++ b/backend/lib/MIR/MIRMetadataParser.cpp
@@ -20,6 +20,7 @@
#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
#include "rcc/MIR/LLVMConstantSyntax.h"
+#include "rcc/MIR/MILexer.h"
#include <algorithm>
#include <array>
@@ -89,7 +90,8 @@ bool MIRDocumentParser::parseVariableDebugInfo(const yaml::Node &Entry, MachineF
MetadataTextParser Parser(std::string(Text), Resolve);
auto Value = Parser.parseValue();
if (!Value)
- return importError(ExpressionNode->range(), Value.error(), "mir-debug-metadata");
+ return importError(scalarDiagnosticRange(*ExpressionNode, Parser.diagnosticRange()),
+ Value.error(), "mir-debug-metadata");
if (Value->reference() != nullptr &&
Value->reference()->scope() != MachineMetadataScope::EmbeddedIR) {
return fail(ExpressionNode->range(),
@@ -148,132 +150,180 @@ bool MIRDocumentParser::parseMetadata(const yaml::Node &Root, MachineFunction &F
if (requireKind(*Metadata, yaml::NodeKind::Sequence, "machine metadata nodes"))
return true;
+ enum class ParsedElementKind : std::uint8_t {
+ Null,
+ Reference,
+ String,
+ };
+ struct ParsedElement final {
+ ParsedElementKind Kind = ParsedElementKind::Reference;
+ std::uint32_t Reference = 0;
+ std::string String;
+ TextRange Range;
+ };
struct Definition final {
const yaml::Node *Node = nullptr;
std::uint32_t Slot = 0;
bool IsDistinct = false;
- std::string_view Elements;
+ std::vector<ParsedElement> Elements;
};
std::vector<Definition> Definitions;
std::unordered_set<std::uint32_t> Slots;
+ const auto TranslateRange = [this](const yaml::Node &Node, const TextRange &Range) {
+ std::size_t ContentBegin = Node.range().Begin.Offset;
+ if (Node.scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
+ Node.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
+ ++ContentBegin;
+ }
+ return Result.Source.range(ContentBegin + Range.Begin.Offset, ContentBegin + Range.End.Offset);
+ };
+
for (const yaml::NodeID DefinitionID : Metadata->sequence()) {
const yaml::Node *DefinitionNode = node(DefinitionID);
std::string_view Text;
- if (DefinitionNode == nullptr || scalar(*DefinitionNode, Text, "machine metadata definition")) {
+ if (DefinitionNode == nullptr || scalar(*DefinitionNode, Text, "machine metadata definition"))
+ return true;
+
+ MILexer Lexer("machine-metadata", std::string(Text));
+ MIToken Token = Lexer.next();
+ const auto LexerFailed = [this, &Lexer, DefinitionNode, &TranslateRange,
+ &Token]() -> bool {
+ if (!Token.is(MITokenKind::Error))
+ return false;
+ const auto Diagnostics = Lexer.diagnostics();
+ const TextRange Range = Diagnostics.empty()
+ ? TranslateRange(*DefinitionNode, Token.range())
+ : TranslateRange(*DefinitionNode, Diagnostics.front().Range);
+ return fail(Range, Diagnostics.empty() ? "invalid machine metadata node"
+ : Diagnostics.front().Message);
+ };
+ const auto Advance = [&Lexer, &Token, &LexerFailed]() -> bool {
+ Token = Lexer.next();
+ return LexerFailed();
+ };
+ const auto FailAtToken = [this, DefinitionNode, &TranslateRange](const MIToken &At,
+ std::string Message) {
+ return fail(TranslateRange(*DefinitionNode, At.range()), std::move(Message));
+ };
+
+ if (LexerFailed())
+ return true;
+ if (!Token.is(MITokenKind::Exclaim))
+ return FailAtToken(Token, "expected a metadata node");
+ if (Advance())
return true;
- }
- Text = trim(Text);
- if (!Text.starts_with('!'))
- return fail(DefinitionNode->range(), "machine metadata definition must start with '!N'");
- const std::size_t Equals = Text.find('=');
- if (Equals == std::string_view::npos)
- return fail(DefinitionNode->range(), "machine metadata definition requires '='");
std::uint32_t Slot = 0;
- if (!parseUnsigned32(trim(Text.substr(1, Equals - 1)), Slot))
- return fail(DefinitionNode->range(), "machine metadata definition has an invalid slot");
- if (!Slots.insert(Slot).second)
- return fail(DefinitionNode->range(), "duplicate machine metadata slot");
- std::string_view Right = trim(Text.substr(Equals + 1));
- bool IsDistinct = false;
- if (Right.starts_with("distinct")) {
- Right = trim(Right.substr(std::string_view("distinct").size()));
- IsDistinct = true;
+ if (!Token.is(MITokenKind::IntegerLiteral) ||
+ !parseUnsigned32(Token.integerValue(), Slot)) {
+ return FailAtToken(Token, "expected metadata id after '!'");
+ }
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::Equal))
+ return FailAtToken(Token, "expected '='");
+ if (Advance())
+ return true;
+
+ const bool IsDistinct = Token.isKeyword("distinct");
+ if (IsDistinct && Advance())
+ return true;
+ if (!Token.is(MITokenKind::Exclaim))
+ return FailAtToken(Token, "expected a metadata node");
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::LeftBrace))
+ return FailAtToken(Token, "expected '{' here");
+ if (Advance())
+ return true;
+
+ std::vector<ParsedElement> Elements;
+ if (!Token.is(MITokenKind::RightBrace)) {
+ while (true) {
+ if (Token.hasStringValue() && Token.stringValue() == "null") {
+ Elements.push_back({.Kind = ParsedElementKind::Null,
+ .Reference = 0,
+ .String = {},
+ .Range = TranslateRange(*DefinitionNode, Token.range())});
+ } else {
+ if (!Token.is(MITokenKind::Exclaim))
+ return FailAtToken(Token, "expected '!' here");
+ const TextRange ReferenceRange = TranslateRange(*DefinitionNode, Token.range());
+ if (Advance())
+ return true;
+
+ if (Token.is(MITokenKind::StringConstant)) {
+ Elements.push_back({.Kind = ParsedElementKind::String,
+ .Reference = 0,
+ .String = std::string(Token.stringValue()),
+ .Range = TranslateRange(*DefinitionNode, Token.range())});
+ } else {
+ std::uint32_t Reference = 0;
+ if (!Token.is(MITokenKind::IntegerLiteral) ||
+ !parseUnsigned32(Token.integerValue(), Reference)) {
+ return FailAtToken(Token, "expected metadata id after '!'");
+ }
+ Elements.push_back({.Kind = ParsedElementKind::Reference,
+ .Reference = Reference,
+ .String = {},
+ .Range = ReferenceRange});
+ }
+ }
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::Comma))
+ break;
+ if (Advance())
+ return true;
+ }
}
- if (!Right.starts_with("!{") || !Right.ends_with('}'))
- return fail(DefinitionNode->range(), "machine metadata node must be a metadata tuple");
+ if (!Token.is(MITokenKind::RightBrace))
+ return FailAtToken(Token, "expected end of metadata node");
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::EndOfFile))
+ return FailAtToken(Token, "expected end of metadata node");
+ if (!Slots.insert(Slot).second)
+ return FailAtToken(Token, "Metadata id is already used");
+
Definitions.push_back({.Node = DefinitionNode,
.Slot = Slot,
.IsDistinct = IsDistinct,
- .Elements = Right.substr(2, Right.size() - 3)});
+ .Elements = std::move(Elements)});
Context.Metadata.push_back(
{.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)});
}
- auto ResolveReference = [this,
- &Slots](std::uint32_t Slot,
- const TextRange &Range) -> Expected<MachineMetadataElement> {
- MachineMetadataReference Reference;
- if (ActiveDocument->EmbeddedIR != std::nullopt &&
- ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) {
- Reference = MachineMetadataReference::embeddedIR(Slot);
- } else if (Slots.contains(Slot)) {
- Reference = MachineMetadataReference::machineFunction(Slot);
- } else {
- return makeUnexpected(ErrorCode::ParseError, "metadata tuple references undefined slot '!" +
- std::to_string(Slot) + "' at line " +
- std::to_string(Range.Begin.Line));
- }
- return MachineMetadataElement::reference(Reference);
- };
-
for (const Definition &Definition : Definitions) {
MachineMetadataNode NewNode{
.Slot = Definition.Slot,
.IsDistinct = Definition.IsDistinct,
.Elements = {},
};
- std::string_view Remaining = trim(Definition.Elements);
- while (!Remaining.empty()) {
- std::size_t End = 0;
- bool InString = false;
- bool Escaped = false;
- for (; End < Remaining.size(); ++End) {
- const char Character = Remaining[End];
- if (InString && Escaped) {
- Escaped = false;
- continue;
- }
- if (InString && Character == '\\') {
- Escaped = true;
- continue;
- }
- if (Character == '"') {
- InString = !InString;
- continue;
- }
- if (!InString && Character == ',')
- break;
+ for (const ParsedElement &Element : Definition.Elements) {
+ if (Element.Kind == ParsedElementKind::Null) {
+ NewNode.Elements.push_back(MachineMetadataElement::null());
+ continue;
+ }
+ if (Element.Kind == ParsedElementKind::String) {
+ NewNode.Elements.push_back(MachineMetadataElement::string(Element.String));
+ continue;
}
- if (InString)
- return fail(Definition.Node->range(), "unterminated machine metadata string");
- std::string_view Element = trim(Remaining.substr(0, End));
- if (Element.empty())
- return fail(Definition.Node->range(), "empty machine metadata tuple element");
- if (Element == "null") {
- NewNode.Elements.push_back(MachineMetadataElement::null());
- } else if (Element.starts_with('!') && Element.size() > 1 && Element[1] >= '0' &&
- Element[1] <= '9') {
- std::uint32_t ReferenceSlot = 0;
- if (!parseUnsigned32(Element.substr(1), ReferenceSlot))
- return fail(Definition.Node->range(), "invalid metadata tuple reference");
- auto Reference = ResolveReference(ReferenceSlot, Definition.Node->range());
- if (!Reference)
- return importError(Definition.Node->range(), Reference.error());
- NewNode.Elements.push_back(std::move(*Reference));
- } else if (Element.starts_with("!\"") && Element.ends_with('"')) {
- std::string Value;
- std::string_view Encoded = Element.substr(2, Element.size() - 3);
- for (std::size_t Index = 0; Index < Encoded.size(); ++Index) {
- if (Encoded[Index] == '\\' && Index + 1 < Encoded.size()) {
- const char EscapedCharacter = Encoded[++Index];
- if (EscapedCharacter != '\\' && EscapedCharacter != '"')
- return fail(Definition.Node->range(), "unsupported machine metadata string escape");
- Value.push_back(EscapedCharacter);
- } else {
- Value.push_back(Encoded[Index]);
- }
- }
- NewNode.Elements.push_back(MachineMetadataElement::string(std::move(Value)));
+ MachineMetadataReference Reference;
+ if (ActiveDocument->EmbeddedIR != std::nullopt &&
+ ActiveDocument->EmbeddedIR->metadata(std::to_string(Element.Reference)) != nullptr) {
+ Reference = MachineMetadataReference::embeddedIR(Element.Reference);
+ } else if (Slots.contains(Element.Reference)) {
+ Reference = MachineMetadataReference::machineFunction(Element.Reference);
} else {
- return fail(Definition.Node->range(), "unsupported machine metadata tuple element");
+ return fail(Element.Range,
+ "use of undefined metadata '!" + std::to_string(Element.Reference) + "'");
}
-
- if (End == Remaining.size())
- Remaining = {};
- else
- Remaining = trim(Remaining.substr(End + 1));
+ auto Resolved = MachineMetadataElement::reference(Reference);
+ if (!Resolved)
+ return importError(Element.Range, Resolved.error());
+ NewNode.Elements.push_back(std::move(*Resolved));
}
auto AddResult = Function.metadata().addNode(std::move(NewNode));
if (!AddResult)
diff --git a/backend/lib/MIR/MIRMetadataTextParser.cpp b/backend/lib/MIR/MIRMetadataTextParser.cpp
index 51b039f..7875b06 100644
--- a/backend/lib/MIR/MIRMetadataTextParser.cpp
+++ b/backend/lib/MIR/MIRMetadataTextParser.cpp
@@ -74,11 +74,11 @@ Expected<MachineDIExpression> MetadataTextParser::parseExpression() {
while (!at(MITokenKind::RightParen)) {
if (at(MITokenKind::Identifier)) {
const auto Opcode = parseDIExpressionOpcodeName(token().stringValue());
- if (!Opcode) {
- return makeUnexpected(ErrorCode::ParseError, "unknown DIExpression opcode '" +
- std::string(token().stringValue()) + "'");
- }
- Elements.push_back(*Opcode);
+ const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue());
+ if (!Opcode && !Encoding)
+ return makeUnexpected(ErrorCode::ParseError,
+ invalidDIExpressionElementMessage(token().stringValue()));
+ Elements.push_back(Opcode ? *Opcode : *Encoding);
advance();
} else if (at(MITokenKind::IntegerLiteral)) {
std::int64_t Signed = 0;
diff --git a/backend/lib/MIR/MIRMetadataTextParser.h b/backend/lib/MIR/MIRMetadataTextParser.h
index b8ebb12..032523d 100644
--- a/backend/lib/MIR/MIRMetadataTextParser.h
+++ b/backend/lib/MIR/MIRMetadataTextParser.h
@@ -35,6 +35,10 @@ public:
[[nodiscard]] Expected<MachineMetadataValue> parseValue();
[[nodiscard]] Expected<MachineDebugLocation> parseLocation();
+ [[nodiscard]] const TextRange &diagnosticRange() const noexcept {
+ return token().range();
+ }
+
private:
[[nodiscard]] const MIToken &token() const noexcept {
return Tokens[Position];
diff --git a/backend/lib/Support/TextDiagnostic.cpp b/backend/lib/Support/TextDiagnostic.cpp
index 1c027c3..e4c2dec 100644
--- a/backend/lib/Support/TextDiagnostic.cpp
+++ b/backend/lib/Support/TextDiagnostic.cpp
@@ -65,11 +65,8 @@ std::string formatTextDiagnostic(const TextDiagnostic &Diagnostic, const TextSou
const std::string_view SourceName =
Diagnostic.SourceName.empty() ? Source.name() : std::string_view(Diagnostic.SourceName);
- Output << SourceName << ':' << Position.Line << ':' << Position.Column << ": "
- << getDiagnosticSeverityName(Diagnostic.Severity);
- if (!Diagnostic.Category.empty())
- Output << '[' << Diagnostic.Category << ']';
- Output << ": " << Diagnostic.Message;
+ Output << getDiagnosticSeverityName(Diagnostic.Severity) << ": " << SourceName << ':'
+ << Position.Line << ':' << Position.Column << ": " << Diagnostic.Message;
const std::string_view SourceLine = Source.line(Position.Line);
if (SourceLine.empty())
diff --git a/backend/lib/Support/YAML.cpp b/backend/lib/Support/YAML.cpp
index 1d3ed2f..1d67114 100644
--- a/backend/lib/Support/YAML.cpp
+++ b/backend/lib/Support/YAML.cpp
@@ -120,9 +120,16 @@ public:
return failureResult();
}
if (markerAt(LineIndex) == DocumentMarker::End) {
- diagnose(Lines[LineIndex].Begin, Lines[LineIndex].ContentEnd,
- "document end marker has no open document");
- return failureResult();
+ const std::size_t EndRemainder = markerRemainder(LineIndex, 3);
+ if (EndRemainder < Lines[LineIndex].ContentEnd &&
+ Parsed.Source.text()[EndRemainder] != '#') {
+ diagnose(EndRemainder, Lines[LineIndex].ContentEnd,
+ "document end marker may only be followed by a comment");
+ return failureResult();
+ }
+ ++LineIndex;
+ skipBlankLines(LineIndex, Lines.size());
+ continue;
}
const std::size_t DocumentStartLine = LineIndex;
@@ -239,7 +246,8 @@ private:
return Result;
}
- void diagnose(std::size_t Begin, std::size_t End, std::string Message) {
+ void diagnose(std::size_t Begin, std::size_t End, std::string Message,
+ std::string SourceName = {}) {
if (Failed)
return;
Failed = true;
@@ -247,7 +255,8 @@ private:
.Severity = DiagnosticSeverity::Error,
.Category = "yaml",
.Message = std::move(Message),
- .SourceName = std::string(Parsed.Source.name()),
+ .SourceName = SourceName.empty() ? std::string(Parsed.Source.name())
+ : std::move(SourceName),
.Range = Parsed.Source.range(Begin, End),
});
}
@@ -321,6 +330,10 @@ private:
}
const std::size_t ContentStart = Lines[LineIndex].Begin + Indent;
+ if (isUnrecognizedPlainStart(Parsed.Source.text()[ContentStart])) {
+ diagnose(ContentStart, ContentStart + 1, "Unrecognized character while tokenizing.", "YAML");
+ return {};
+ }
if (isSequenceIndicator(ContentStart, Lines[LineIndex].ContentEnd))
return parseBlockSequence(LineIndex, Indent, Depth, LimitLine);
if (findBlockMappingColon(ContentStart, Lines[LineIndex].ContentEnd))
@@ -587,6 +600,10 @@ private:
return parseFlowMapping(Cursor, End, Depth);
if (Text[Cursor] == '\'' || Text[Cursor] == '"')
return parseQuotedScalar(Cursor, End);
+ if (isUnrecognizedPlainStart(Text[Cursor])) {
+ diagnose(Cursor, Cursor + 1, "Unrecognized character while tokenizing.", "YAML");
+ return {};
+ }
if (Text[Cursor] == '&' || Text[Cursor] == '*' || Text[Cursor] == '!' || Text[Cursor] == '%') {
diagnose(Cursor, Cursor + 1, "YAML anchors, aliases, tags, and directives are not supported");
return {};
@@ -768,6 +785,10 @@ private:
}
const std::size_t Begin = Cursor;
+ if (isUnrecognizedPlainStart(Text[Cursor])) {
+ diagnose(Cursor, Cursor + 1, "Unrecognized character while tokenizing.", "YAML");
+ return false;
+ }
if (Text[Cursor] == '\'' || Text[Cursor] == '"') {
ScalarStyle Style = ScalarStyle::Plain;
return parseQuotedText(Cursor, End, Key, Style, Range);
@@ -1049,6 +1070,11 @@ private:
return false;
}
+ if (isUnrecognizedPlainStart(Text[Begin])) {
+ diagnose(Begin, Begin + 1, "Unrecognized character while tokenizing.", "YAML");
+ return false;
+ }
+
if (Text[Begin] == '\'' || Text[Begin] == '"') {
std::size_t Cursor = Begin;
ScalarStyle Style = ScalarStyle::Plain;
@@ -1123,6 +1149,10 @@ private:
Parsed.Source.text()[Offset + 1] == '#';
}
+ [[nodiscard]] static bool isUnrecognizedPlainStart(char Character) noexcept {
+ return Character == '@' || Character == '`';
+ }
+
void skipFlowSpace(std::size_t &Cursor, std::size_t End) const noexcept {
const std::string_view Text = Parsed.Source.text();
while (Cursor < End) {
diff --git a/backend/lib/Tooling/PipelineDriver.cpp b/backend/lib/Tooling/PipelineDriver.cpp
index 330730e..e9a3d9d 100644
--- a/backend/lib/Tooling/PipelineDriver.cpp
+++ b/backend/lib/Tooling/PipelineDriver.cpp
@@ -84,6 +84,8 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio
"the default pipeline marker cannot be mixed with named passes");
}
for (const std::string &Name : Invocation.PassPipeline) {
+ if (Name == "none")
+ continue;
auto Pass = riscv::createCanonicalPipelinePass(Name);
if (!Pass)
return std::unexpected(Pass.error());