diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-01 04:50:21 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-01 04:50:21 +0800 |
| commit | 916b05ee8d79b22147c098cf52bba7fe86858f3f (patch) | |
| tree | d798384e44ccdce3ffe34d5d5171034ba36d13e2 /backend/lib | |
| parent | 81cf8b68eca2636574f3d0a9fa1cf0b428c163e9 (diff) | |
| download | rcc-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')
| -rw-r--r-- | backend/lib/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | backend/lib/CodeGen/CMakeLists.txt | 55 | ||||
| -rw-r--r-- | backend/lib/CodeGen/LowLevelType.cpp | 249 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunctionProperties.cpp | 88 | ||||
| -rw-r--r-- | backend/lib/CodeGen/Register.cpp | 56 |
5 files changed, 449 insertions, 0 deletions
diff --git a/backend/lib/CMakeLists.txt b/backend/lib/CMakeLists.txt index 13f7c7b..dfaff97 100644 --- a/backend/lib/CMakeLists.txt +++ b/backend/lib/CMakeLists.txt @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-3.0-or-later add_subdirectory(Support) +add_subdirectory(CodeGen) 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 |
