aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 04:50:21 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 04:50:21 +0800
commit916b05ee8d79b22147c098cf52bba7fe86858f3f (patch)
treed798384e44ccdce3ffe34d5d5171034ba36d13e2 /backend/lib/CodeGen
parent81cf8b68eca2636574f3d0a9fa1cf0b428c163e9 (diff)
downloadrcc-project-916b05ee8d79b22147c098cf52bba7fe86858f3f.tar.gz
rcc-project-916b05ee8d79b22147c098cf52bba7fe86858f3f.tar.xz
[CodeGen] Add core machine type primitives
Introduce an independent 64-bit extended LLT representation that preserves exact floating-point semantics and supports fixed and scalable vectors. Add opaque physical and virtual registers, strongly typed monotonic stable IDs, and atomic MachineFunctionProperties transitions. Export the primitives through RCCBackend::CodeGen, cover their boundary and failure cases, record the frozen invariants, and normalize the architecture document permissions.
Diffstat (limited to 'backend/lib/CodeGen')
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt55
-rw-r--r--backend/lib/CodeGen/LowLevelType.cpp249
-rw-r--r--backend/lib/CodeGen/MachineFunctionProperties.cpp88
-rw-r--r--backend/lib/CodeGen/Register.cpp56
4 files changed, 448 insertions, 0 deletions
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt
new file mode 100644
index 0000000..f5b5b23
--- /dev/null
+++ b/backend/lib/CodeGen/CMakeLists.txt
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+set(
+ RCCBackendCodeGenSources
+ LowLevelType.cpp
+ MachineFunctionProperties.cpp
+ Register.cpp
+)
+set(
+ RCCBackendCodeGenHeaders
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/LowLevelType.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFunctionProperties.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/Register.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/StableID.h"
+)
+
+add_library(RCCBackendCodeGen ${RCCBackendCodeGenSources})
+add_library(RCCBackend::CodeGen ALIAS RCCBackendCodeGen)
+
+rcc_backend_configure_target(RCCBackendCodeGen)
+
+target_include_directories(
+ RCCBackendCodeGen
+ PUBLIC
+ "$<BUILD_INTERFACE:${RCC_BACKEND_SOURCE_DIR}/include>"
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+)
+target_link_libraries(RCCBackendCodeGen PUBLIC RCCBackend::Support)
+
+set_target_properties(
+ RCCBackendCodeGen
+ PROPERTIES
+ EXPORT_NAME CodeGen
+ OUTPUT_NAME RCCBackendCodeGen
+)
+
+install(
+ TARGETS RCCBackendCodeGen
+ EXPORT RCCBackendTargets
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
+
+rcc_backend_register_format_files(
+ "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionProperties.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Register.cpp"
+ ${RCCBackendCodeGenHeaders}
+)
+rcc_backend_register_tidy_files(
+ "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionProperties.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Register.cpp"
+)
diff --git a/backend/lib/CodeGen/LowLevelType.cpp b/backend/lib/CodeGen/LowLevelType.cpp
new file mode 100644
index 0000000..a276e02
--- /dev/null
+++ b/backend/lib/CodeGen/LowLevelType.cpp
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/LowLevelType.h"
+
+#include <array>
+#include <string>
+
+namespace rcc {
+namespace {
+
+struct FloatSemanticsInfo final {
+ std::uint32_t BitWidth;
+ std::string_view Name;
+ std::string_view Spelling;
+};
+
+constexpr std::array FloatSemanticsTable{
+ FloatSemanticsInfo{16, "IEEE half", "f16"},
+ FloatSemanticsInfo{16, "bfloat", "bf16"},
+ FloatSemanticsInfo{32, "IEEE single", "f32"},
+ FloatSemanticsInfo{64, "IEEE double", "f64"},
+ FloatSemanticsInfo{128, "IEEE quad", "f128"},
+ FloatSemanticsInfo{128, "PPC double-double", "ppcf128"},
+ FloatSemanticsInfo{128, "legacy PPC double-double", "ppcf128-legacy"},
+ FloatSemanticsInfo{8, "float8 E5M2", "f8e5m2"},
+ FloatSemanticsInfo{8, "float8 E5M2 FNUZ", "f8e5m2fnuz"},
+ FloatSemanticsInfo{8, "float8 E4M3", "f8e4m3"},
+ FloatSemanticsInfo{8, "float8 E4M3 FN", "f8e4m3fn"},
+ FloatSemanticsInfo{8, "float8 E4M3 FNUZ", "f8e4m3fnuz"},
+ FloatSemanticsInfo{8, "float8 E4M3 B11 FNUZ", "f8e4m3b11fnuz"},
+ FloatSemanticsInfo{8, "float8 E3M4", "f8e3m4"},
+ FloatSemanticsInfo{19, "TensorFloat-32", "tf32"},
+ FloatSemanticsInfo{8, "float8 E8M0 FNU", "f8e8m0fnu"},
+ FloatSemanticsInfo{6, "float6 E3M2 FN", "f6e3m2fn"},
+ FloatSemanticsInfo{6, "float6 E2M3 FN", "f6e2m3fn"},
+ FloatSemanticsInfo{4, "float4 E2M1 FN", "f4e2m1fn"},
+ FloatSemanticsInfo{80, "x87 double-extended", "x86fp80"},
+};
+
+static_assert(FloatSemanticsTable.size() == static_cast<std::size_t>(FloatSemantics::Count));
+
+[[nodiscard]] const FloatSemanticsInfo *getFloatSemanticsInfo(FloatSemantics Semantics) noexcept {
+ const auto Index = static_cast<std::size_t>(Semantics);
+ if (Index >= FloatSemanticsTable.size())
+ return nullptr;
+ return &FloatSemanticsTable[Index];
+}
+
+[[nodiscard]] LLT::Kind getVectorKind(LLT ElementType) noexcept {
+ switch (ElementType.kind()) {
+ case LLT::Kind::AnyScalar:
+ return LLT::Kind::VectorAny;
+ case LLT::Kind::Integer:
+ return LLT::Kind::VectorInteger;
+ case LLT::Kind::Float:
+ return LLT::Kind::VectorFloat;
+ case LLT::Kind::Pointer:
+ return LLT::Kind::VectorPointer;
+ case LLT::Kind::Invalid:
+ case LLT::Kind::Token:
+ case LLT::Kind::VectorAny:
+ case LLT::Kind::VectorInteger:
+ case LLT::Kind::VectorFloat:
+ case LLT::Kind::VectorPointer:
+ return LLT::Kind::Invalid;
+ }
+
+ return LLT::Kind::Invalid;
+}
+
+} // namespace
+
+bool isKnownFloatSemantics(FloatSemantics Semantics) noexcept {
+ return getFloatSemanticsInfo(Semantics) != nullptr;
+}
+
+std::optional<std::uint32_t> getFloatSemanticsBitWidth(FloatSemantics Semantics) noexcept {
+ const auto *Info = getFloatSemanticsInfo(Semantics);
+ if (Info == nullptr)
+ return std::nullopt;
+ return Info->BitWidth;
+}
+
+std::string_view getFloatSemanticsName(FloatSemantics Semantics) noexcept {
+ const auto *Info = getFloatSemanticsInfo(Semantics);
+ if (Info == nullptr)
+ return "unknown";
+ return Info->Name;
+}
+
+std::string_view getFloatSemanticsSpelling(FloatSemantics Semantics) noexcept {
+ const auto *Info = getFloatSemanticsInfo(Semantics);
+ if (Info == nullptr)
+ return "invalid-float";
+ return Info->Spelling;
+}
+
+Expected<LLT> LLT::makeScalar(Kind TypeKind, std::uint64_t BitWidth) {
+ if (BitWidth == 0 || BitWidth > MaxBitWidth) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "LLT bit width must be in the range [1, 65535]");
+ }
+
+ return LLT(encode(TypeKind, static_cast<std::uint32_t>(BitWidth), 0, 0, false));
+}
+
+Expected<LLT> LLT::makeAnyScalar(std::uint64_t BitWidth) {
+ return makeScalar(Kind::AnyScalar, BitWidth);
+}
+
+Expected<LLT> LLT::makeInteger(std::uint64_t BitWidth) {
+ return makeScalar(Kind::Integer, BitWidth);
+}
+
+Expected<LLT> LLT::makeFloat(FloatSemantics Semantics) {
+ const auto BitWidth = getFloatSemanticsBitWidth(Semantics);
+ if (!BitWidth) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "unknown floating-point semantics");
+ }
+
+ return LLT(encode(Kind::Float, *BitWidth, 0, static_cast<std::uint32_t>(Semantics), false));
+}
+
+Expected<LLT> LLT::makePointer(std::uint64_t AddressSpace, std::uint64_t BitWidth) {
+ if (BitWidth == 0 || BitWidth > MaxBitWidth) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "LLT pointer bit width must be in the range [1, 65535]");
+ }
+ if (AddressSpace > MaxAddressSpace) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "LLT address space must fit in 24 bits");
+ }
+
+ return LLT(encode(Kind::Pointer, static_cast<std::uint32_t>(BitWidth), 0,
+ static_cast<std::uint32_t>(AddressSpace), false));
+}
+
+Expected<LLT> LLT::makeFixedVector(std::uint64_t ElementCount, LLT ElementType) {
+ return makeVector(ElementCount, false, ElementType);
+}
+
+Expected<LLT> LLT::makeScalableVector(std::uint64_t MinimumElementCount, LLT ElementType) {
+ return makeVector(MinimumElementCount, true, ElementType);
+}
+
+Expected<LLT> LLT::makeVector(std::uint64_t ElementCount, bool IsScalable, LLT ElementType) {
+ if (ElementCount == 0 || ElementCount > MaxElementCount) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "LLT vector element count must be in the range [1, 65535]");
+ }
+ if (!IsScalable && ElementCount == 1) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "a fixed LLT vector must contain at least two elements");
+ }
+
+ const Kind VectorKind = getVectorKind(ElementType);
+ if (VectorKind == Kind::Invalid) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "LLT vector elements must be non-vector value types");
+ }
+
+ std::uint32_t Auxiliary = 0;
+ if (ElementType.isPointer())
+ Auxiliary = *ElementType.addressSpace();
+ else if (ElementType.isFloat())
+ Auxiliary = static_cast<std::uint32_t>(*ElementType.floatSemantics());
+
+ return LLT(encode(VectorKind, ElementType.scalarBitWidth(),
+ static_cast<std::uint32_t>(ElementCount), Auxiliary, IsScalable));
+}
+
+std::optional<std::uint32_t> LLT::addressSpace() const noexcept {
+ if (!isPointerOrPointerVector())
+ return std::nullopt;
+ return auxiliaryValue();
+}
+
+std::optional<FloatSemantics> LLT::floatSemantics() const noexcept {
+ if (!isFloatOrFloatVector())
+ return std::nullopt;
+ return static_cast<FloatSemantics>(auxiliaryValue());
+}
+
+std::optional<LLT> LLT::scalarElementType() const noexcept {
+ switch (kind()) {
+ case Kind::VectorAny:
+ return LLT(encode(Kind::AnyScalar, scalarBitWidth(), 0, 0, false));
+ case Kind::VectorInteger:
+ return LLT(encode(Kind::Integer, scalarBitWidth(), 0, 0, false));
+ case Kind::VectorFloat:
+ return LLT(encode(Kind::Float, scalarBitWidth(), 0, auxiliaryValue(), false));
+ case Kind::VectorPointer:
+ return LLT(encode(Kind::Pointer, scalarBitWidth(), 0, auxiliaryValue(), false));
+ case Kind::Invalid:
+ case Kind::Token:
+ case Kind::AnyScalar:
+ case Kind::Integer:
+ case Kind::Float:
+ case Kind::Pointer:
+ return std::nullopt;
+ }
+
+ return std::nullopt;
+}
+
+std::string LLT::toString() const {
+ if (!isValid())
+ return "LLT_invalid";
+ if (isToken())
+ return "s0";
+
+ std::string ScalarSpelling;
+ switch (kind()) {
+ case Kind::AnyScalar:
+ case Kind::VectorAny:
+ ScalarSpelling = "s" + std::to_string(scalarBitWidth());
+ break;
+ case Kind::Integer:
+ case Kind::VectorInteger:
+ ScalarSpelling = "i" + std::to_string(scalarBitWidth());
+ break;
+ case Kind::Float:
+ case Kind::VectorFloat:
+ ScalarSpelling =
+ std::string(getFloatSemanticsSpelling(static_cast<FloatSemantics>(auxiliaryValue())));
+ break;
+ case Kind::Pointer:
+ case Kind::VectorPointer:
+ ScalarSpelling =
+ "p" + std::to_string(auxiliaryValue()) + ":" + std::to_string(scalarBitWidth());
+ break;
+ case Kind::Invalid:
+ case Kind::Token:
+ break;
+ }
+
+ if (!isVector())
+ return ScalarSpelling;
+
+ std::string Result = "<";
+ if (isScalableVector())
+ Result += "vscale x ";
+ Result += std::to_string(minimumElementCount());
+ Result += " x ";
+ Result += ScalarSpelling;
+ Result += ">";
+ return Result;
+}
+
+} // namespace rcc
diff --git a/backend/lib/CodeGen/MachineFunctionProperties.cpp b/backend/lib/CodeGen/MachineFunctionProperties.cpp
new file mode 100644
index 0000000..55a4ca3
--- /dev/null
+++ b/backend/lib/CodeGen/MachineFunctionProperties.cpp
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineFunctionProperties.h"
+
+#include <array>
+#include <string>
+
+namespace rcc {
+namespace {
+
+constexpr std::array PropertyNames{
+ std::string_view{"IsSSA"},
+ std::string_view{"NoPHIs"},
+ std::string_view{"TracksLiveness"},
+ std::string_view{"Legalized"},
+ std::string_view{"RegBankSelected"},
+ std::string_view{"Selected"},
+ std::string_view{"TiedOpsRewritten"},
+ std::string_view{"NoVRegs"},
+ std::string_view{"FrameFinalized"},
+ std::string_view{"NoFrameIndices"},
+ std::string_view{"NoExecutablePseudos"},
+};
+
+static_assert(PropertyNames.size() == static_cast<std::size_t>(MachineFunctionProperty::Count));
+
+} // namespace
+
+bool isKnownMachineFunctionProperty(MachineFunctionProperty Property) noexcept {
+ return static_cast<std::size_t>(Property) < PropertyNames.size();
+}
+
+std::string_view getMachineFunctionPropertyName(MachineFunctionProperty Property) noexcept {
+ const auto Index = static_cast<std::size_t>(Property);
+ if (Index >= PropertyNames.size())
+ return "Unknown";
+ return PropertyNames[Index];
+}
+
+MachineFunctionProperties::MachineFunctionProperties(
+ std::initializer_list<MachineFunctionProperty> Properties) noexcept {
+ for (const MachineFunctionProperty Property : Properties)
+ set(Property);
+}
+
+Expected<MachineFunctionProperties> MachineFunctionProperties::fromRaw(std::uint64_t RawValue) {
+ if ((RawValue & ~ValidMask) != 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine function property mask contains unknown bits");
+ }
+ return MachineFunctionProperties(RawValue);
+}
+
+Expected<void> MachineFunctionProperties::applyTransition(MachineFunctionProperties Required,
+ MachineFunctionProperties Sets,
+ MachineFunctionProperties Clears) {
+ if (Sets.intersects(Clears)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "a machine function property transition cannot set and clear the "
+ "same property");
+ }
+
+ const MachineFunctionProperties Missing = Required.missingFrom(*this);
+ if (!Missing.empty()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "missing required machine function properties: " + Missing.toString());
+ }
+
+ Mask = (Mask | Sets.Mask) & ~Clears.Mask;
+ return {};
+}
+
+std::string MachineFunctionProperties::toString() const {
+ if (empty())
+ return "none";
+
+ std::string Result;
+ for (std::size_t Index = 0; Index < PropertyNames.size(); ++Index) {
+ if ((Mask & (std::uint64_t{1} << Index)) == 0)
+ continue;
+ if (!Result.empty())
+ Result += ", ";
+ Result += PropertyNames[Index];
+ }
+ return Result;
+}
+
+} // namespace rcc
diff --git a/backend/lib/CodeGen/Register.cpp b/backend/lib/CodeGen/Register.cpp
new file mode 100644
index 0000000..0af26c7
--- /dev/null
+++ b/backend/lib/CodeGen/Register.cpp
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/Register.h"
+
+#include <string>
+
+namespace rcc {
+namespace {
+
+constexpr std::uint64_t MaximumRegisterIndex = (std::uint64_t{1} << 31) - 1;
+
+} // namespace
+
+Expected<PhysicalRegister> PhysicalRegister::create(std::uint64_t Number) {
+ if (Number == 0 || Number > MaximumRegisterIndex) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "physical register number must be in the range [1, 2147483647]");
+ }
+ return PhysicalRegister(static_cast<std::uint32_t>(Number));
+}
+
+Expected<Register> Register::makePhysical(std::uint64_t Number) {
+ auto PhysReg = PhysicalRegister::create(Number);
+ if (!PhysReg)
+ return std::unexpected(PhysReg.error());
+ return fromPhysical(*PhysReg);
+}
+
+Expected<Register> Register::makeVirtual(std::uint64_t Index) {
+ if (Index > MaximumRegisterIndex) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "virtual register index must fit in 31 bits");
+ }
+ return Register(VirtualBit | static_cast<std::uint32_t>(Index));
+}
+
+std::optional<PhysicalRegister> Register::physicalRegister() const noexcept {
+ if (!isPhysical())
+ return std::nullopt;
+ return PhysicalRegister(OpaqueValue);
+}
+
+std::optional<std::uint32_t> Register::virtualIndex() const noexcept {
+ if (!isVirtual())
+ return std::nullopt;
+ return OpaqueValue & IndexMask;
+}
+
+std::string Register::toString() const {
+ if (isNoRegister())
+ return "noreg";
+ if (isVirtual())
+ return "%" + std::to_string(OpaqueValue & IndexMask);
+ return "$physreg" + std::to_string(OpaqueValue);
+}
+
+} // namespace rcc