diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-16 09:43:35 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-16 09:43:35 +0000 |
| commit | 90f691e1e1e5a3803c95b547071f340da5985c09 (patch) | |
| tree | 130aedc39a807c05bc0d4228cfa55771858ccfe9 /lib/CodeGen | |
| parent | 3dc3a15c86287b2788142b58e2512269cd5274c6 (diff) | |
refactor(types): separate native type registration and imports
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/NativeTypeSystem.cc | 224 | ||||
| -rw-r--r-- | lib/CodeGen/TypeSystem.cc | 217 |
2 files changed, 224 insertions, 217 deletions
diff --git a/lib/CodeGen/NativeTypeSystem.cc b/lib/CodeGen/NativeTypeSystem.cc new file mode 100644 index 0000000..a4fcee0 --- /dev/null +++ b/lib/CodeGen/NativeTypeSystem.cc @@ -0,0 +1,224 @@ +#include "cps/TypeSystem.h" +#include <utility> +#include <climits> +#include <limits> +#include <type_traits> +#include <sstream> +#include <cctype> + +using namespace llvm; +using namespace cps; + +namespace { + +std::string cTypeName(const std::string &Identity) { + return "C_TYPE(\"" + Identity + "\")"; +} + +// sizeof alone cannot distinguish padded x87 from IEEE quad or double-double. +template<typename T> Type *nativeFloatType(LLVMContext &C) { + constexpr int Digits = std::numeric_limits<T>::digits; + constexpr int Exponent = std::numeric_limits<T>::max_exponent; + static_assert(std::numeric_limits<T>::radix == 2, "Unsupported native C floating-point radix"); + static_assert((Digits == 24 && Exponent == 128) || + (Digits == 53 && Exponent == 1024) || + (Digits == 64 && Exponent == 16384) || + (Digits == 113 && Exponent == 16384) || + (Digits == 106 && Exponent == 1024), "Unsupported native C floating-point format"); + if constexpr (Digits == 24) return Type::getFloatTy(C); + if constexpr (Digits == 53) return Type::getDoubleTy(C); + if constexpr (Digits == 64) return Type::getX86_FP80Ty(C); + if constexpr (Digits == 113) return Type::getFP128Ty(C); + return Type::getPPC_FP128Ty(C); +} + +} // namespace + + +void TypeSystem::registerBuiltins() { + registerType("INTEGER", Type::getInt64Ty(Context), TypeKind::Integer); + registerType("REAL", Type::getDoubleTy(Context), TypeKind::Real); + registerType("BOOLEAN", Type::getInt1Ty(Context), TypeKind::Boolean); + registerType("STRING", PointerType::getUnqual(Context), TypeKind::String); + registerType("CHAR", Type::getInt8Ty(Context), TypeKind::Char); + registerType("VOID", Type::getVoidTy(Context), TypeKind::Void); + + // The compiler's native C/C++ ABI determines representation, not a table + // of assumed LP64/LLP64 widths. LLVM's addressable bytes are eight bits. + static_assert(CHAR_BIT == 8, "LLVM requires eight-bit addressable bytes"); + auto Integral = [&](auto Sample, const char *Name, unsigned Rank, + const char *InputFormat, const char *OutputFormat) { + using T = decltype(Sample); + TypeInfo Info; + Info.Name = cTypeName(Name); + Info.CIdentity = Name; + Info.Kind = TypeKind::CScalar; + Info.LLVMType = Type::getIntNTy(Context, sizeof(T) * CHAR_BIT); + Info.CRank = Rank; + Info.CSigned = std::is_signed<T>::value; + Info.CInputFormat = InputFormat; + Info.COutputFormat = OutputFormat; + Types.emplace(Info.Name, std::move(Info)); + }; + Integral(char{}, "char", 2, " %c", "%c"); + Integral(static_cast<signed char>(0), "signed char", 2, "%hhd", "%hhd"); + Integral(static_cast<unsigned char>(0), "unsigned char", 2, "%hhu", "%hhu"); + Integral(short{}, "short", 3, "%hd", "%hd"); + Integral(static_cast<unsigned short>(0), "unsigned short", 3, "%hu", "%hu"); + Integral(int{}, "int", 4, "%d", "%d"); + Integral(static_cast<unsigned int>(0), "unsigned int", 4, "%u", "%u"); + Integral(long{}, "long", 5, "%ld", "%ld"); + Integral(static_cast<unsigned long>(0), "unsigned long", 5, "%lu", "%lu"); + Integral(static_cast<long long>(0), "long long", 6, "%lld", "%lld"); + Integral(static_cast<unsigned long long>(0), "unsigned long long", 6, "%llu", "%llu"); + + auto Floating = [&](auto Sample, const char *Name, unsigned Rank, const char *Format) { + TypeInfo Info; + Info.Name = cTypeName(Name); + Info.CIdentity = Name; + Info.Kind = TypeKind::CScalar; + Info.LLVMType = nativeFloatType<decltype(Sample)>(Context); + Info.CRank = Rank; + Info.CInputFormat = Format; + Info.COutputFormat = Rank == 9 ? "%Lf" : "%f"; + Types.emplace(Info.Name, std::move(Info)); + }; + Floating(float{}, "float", 7, "%f"); + Floating(double{}, "double", 8, "%lf"); + Floating(static_cast<long double>(0), "long double", 9, "%Lf"); + + TypeInfo Bool; + Bool.Name = cTypeName("_Bool"); + Bool.CIdentity = "_Bool"; + Bool.Kind = TypeKind::CScalar; + Bool.LLVMType = Type::getInt1Ty(Context); + Bool.CRank = 1; + Types.emplace(Bool.Name, std::move(Bool)); + TypeInfo Void; + Void.Name = cTypeName("void"); + Void.CIdentity = "void"; + Void.Kind = TypeKind::CScalar; + Void.LLVMType = Type::getVoidTy(Context); + Types.emplace(Void.Name, std::move(Void)); + + std::string Err; + defineCAlias("C_INT", "int", Err); + defineCAlias("C_CHAR", "char", Err); + defineCAlias("C_STRING", "char*", Err); + defineCAlias("C_DOUBLE", "double", Err); +} + + +const TypeInfo *TypeSystem::promoteCScalar(const TypeInfo *Info) const { + if (!Info || Info->Kind != TypeKind::CScalar || Info->isVoid()) return nullptr; + if (Info->CRank >= 4) return Info; + const TypeInfo *Int = resolve(cTypeName("int")); + unsigned Width = Info->LLVMType->getIntegerBitWidth(); + unsigned IntWidth = Int->LLVMType->getIntegerBitWidth(); + return Width < IntWidth || (Width == IntWidth && Info->CSigned) + ? Int : resolve(cTypeName("unsigned int")); +} + +const TypeInfo *TypeSystem::commonCScalar(const TypeInfo *Left, const TypeInfo *Right) const { + const TypeInfo *L = promoteCScalar(Left); + const TypeInfo *R = promoteCScalar(Right); + if (!L || !R) return nullptr; + if (L->isReal() || R->isReal() || L->CSigned == R->CSigned) + return L->CRank >= R->CRank ? L : R; + const TypeInfo *S = L->CSigned ? L : R; + const TypeInfo *U = L->CSigned ? R : L; + if (U->CRank >= S->CRank) return U; + if (S->LLVMType->getIntegerBitWidth() > U->LLVMType->getIntegerBitWidth()) return S; + return resolve(cTypeName("unsigned " + S->CIdentity)); +} + +const TypeInfo *TypeSystem::importCType(const std::string &Expression, std::string &Err) { + // Whitespace is immaterial around stars; C's multiword base type is + // validated separately. Stars must all precede or all follow the base. + std::istringstream Input(Expression); + std::string Word, Text; + while (Input >> Word) { + if (!Text.empty()) Text += ' '; + Text += Word; + } + unsigned Depth = 0; + bool Prefix = !Text.empty() && Text.front() == '*'; + if (Prefix) { + while (!Text.empty() && (Text.front() == '*' || Text.front() == ' ')) { + if (Text.front() == '*') ++Depth; + Text.erase(Text.begin()); + } + } else { + while (!Text.empty() && (Text.back() == '*' || Text.back() == ' ')) { + if (Text.back() == '*') ++Depth; + Text.pop_back(); + } + } + std::istringstream BaseWords(Text); + std::map<std::string, unsigned> Counts; + bool Valid = !Text.empty(); + while (BaseWords >> Word) { + if (Word != "signed" && Word != "unsigned" && Word != "short" && + Word != "long" && Word != "int" && Word != "char" && + Word != "float" && Word != "double" && Word != "_Bool" && Word != "void") + Valid = false; + ++Counts[Word]; + } + for (const auto &Entry : Counts) + if (Entry.second > (Entry.first == "long" ? 2u : 1u)) Valid = false; + auto Has = [&](const char *Name) { return Counts.count(Name) != 0; }; + if (Has("signed") && Has("unsigned")) Valid = false; + std::string Base; + if (Has("void") || Has("_Bool") || Has("float")) { + if (Counts.size() != 1) Valid = false; + Base = Has("void") ? "void" : Has("_Bool") ? "_Bool" : "float"; + } else if (Has("double")) { + if (Counts.size() != (Has("long") ? 2u : 1u) || (Has("long") && Counts["long"] > 1)) + Valid = false; + Base = Has("long") ? "long double" : "double"; + } else if (Has("char")) { + if (Has("int") || Has("short") || Has("long")) Valid = false; + Base = Has("unsigned") ? "unsigned char" : Has("signed") ? "signed char" : "char"; + } else { + if (Has("short") && Has("long")) Valid = false; + Base = Has("short") ? "short" : Has("long") ? + (Counts["long"] == 2 ? "long long" : "long") : "int"; + if (Has("unsigned")) Base = "unsigned " + Base; + } + if (!Valid) { + Err = "Invalid or unsupported C type expression '" + Expression + "'"; + return nullptr; + } + const TypeInfo *Info = resolve(cTypeName(Base)); + for (unsigned I = 0; I < Depth; ++I) { + std::string Identity = Info->CIdentity + '*'; + const TypeInfo *Existing = resolve(cTypeName(Identity)); + if (Existing) { + Info = Existing; + continue; + } + TypeInfo Pointer; + Pointer.Name = cTypeName(Identity); + Pointer.CIdentity = Identity; + Pointer.Kind = TypeKind::CPointer; + Pointer.LLVMType = PointerType::getUnqual(Context); + Pointer.Pointer.PointeeTypeName = Info->Name; + auto Result = Types.emplace(Pointer.Name, std::move(Pointer)); + Info = &Result.first->second; + } + return Info; +} + +bool TypeSystem::defineCAlias(const std::string &Name, const std::string &Expression, + std::string &Err) { + if (Types.count(Name) || EnumConstants.count(Name)) { + Err = "Type name '" + Name + "' is already defined or reserved"; + return false; + } + const TypeInfo *C = importCType(Expression, Err); + if (!C) return false; + TypeInfo Alias = *C; + Alias.Name = Name; + Types.emplace(Name, std::move(Alias)); + return true; +} diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc index 9906050..a837c9a 100644 --- a/lib/CodeGen/TypeSystem.cc +++ b/lib/CodeGen/TypeSystem.cc @@ -2,40 +2,10 @@ #include "cps/TypeAST.h" #include "llvm/IR/Constants.h" #include <utility> -#include <climits> -#include <limits> -#include <type_traits> -#include <sstream> -#include <cctype> using namespace llvm; using namespace cps; -namespace { - -std::string cTypeName(const std::string &Identity) { - return "C_TYPE(\"" + Identity + "\")"; -} - -// sizeof alone cannot distinguish padded x87 from IEEE quad or double-double. -template<typename T> Type *nativeFloatType(LLVMContext &C) { - constexpr int Digits = std::numeric_limits<T>::digits; - constexpr int Exponent = std::numeric_limits<T>::max_exponent; - static_assert(std::numeric_limits<T>::radix == 2, "Unsupported native C floating-point radix"); - static_assert((Digits == 24 && Exponent == 128) || - (Digits == 53 && Exponent == 1024) || - (Digits == 64 && Exponent == 16384) || - (Digits == 113 && Exponent == 16384) || - (Digits == 106 && Exponent == 1024), "Unsupported native C floating-point format"); - if constexpr (Digits == 24) return Type::getFloatTy(C); - if constexpr (Digits == 53) return Type::getDoubleTy(C); - if constexpr (Digits == 64) return Type::getX86_FP80Ty(C); - if constexpr (Digits == 113) return Type::getFP128Ty(C); - return Type::getPPC_FP128Ty(C); -} - -} // namespace - bool TypeInfo::isC() const { return !CIdentity.empty(); } bool TypeInfo::sameType(const TypeInfo *Other) const { @@ -134,198 +104,11 @@ TypeSystem::TypeSystem(LLVMContext &Ctx) : Context(Ctx) { registerBuiltins(); } -void TypeSystem::registerBuiltins() { - registerType("INTEGER", Type::getInt64Ty(Context), TypeKind::Integer); - registerType("REAL", Type::getDoubleTy(Context), TypeKind::Real); - registerType("BOOLEAN", Type::getInt1Ty(Context), TypeKind::Boolean); - registerType("STRING", PointerType::getUnqual(Context), TypeKind::String); - registerType("CHAR", Type::getInt8Ty(Context), TypeKind::Char); - registerType("VOID", Type::getVoidTy(Context), TypeKind::Void); - - // The compiler's native C/C++ ABI determines representation, not a table - // of assumed LP64/LLP64 widths. LLVM's addressable bytes are eight bits. - static_assert(CHAR_BIT == 8, "LLVM requires eight-bit addressable bytes"); - auto Integral = [&](auto Sample, const char *Name, unsigned Rank, - const char *InputFormat, const char *OutputFormat) { - using T = decltype(Sample); - TypeInfo Info; - Info.Name = cTypeName(Name); - Info.CIdentity = Name; - Info.Kind = TypeKind::CScalar; - Info.LLVMType = Type::getIntNTy(Context, sizeof(T) * CHAR_BIT); - Info.CRank = Rank; - Info.CSigned = std::is_signed<T>::value; - Info.CInputFormat = InputFormat; - Info.COutputFormat = OutputFormat; - Types.emplace(Info.Name, std::move(Info)); - }; - Integral(char{}, "char", 2, " %c", "%c"); - Integral(static_cast<signed char>(0), "signed char", 2, "%hhd", "%hhd"); - Integral(static_cast<unsigned char>(0), "unsigned char", 2, "%hhu", "%hhu"); - Integral(short{}, "short", 3, "%hd", "%hd"); - Integral(static_cast<unsigned short>(0), "unsigned short", 3, "%hu", "%hu"); - Integral(int{}, "int", 4, "%d", "%d"); - Integral(static_cast<unsigned int>(0), "unsigned int", 4, "%u", "%u"); - Integral(long{}, "long", 5, "%ld", "%ld"); - Integral(static_cast<unsigned long>(0), "unsigned long", 5, "%lu", "%lu"); - Integral(static_cast<long long>(0), "long long", 6, "%lld", "%lld"); - Integral(static_cast<unsigned long long>(0), "unsigned long long", 6, "%llu", "%llu"); - - auto Floating = [&](auto Sample, const char *Name, unsigned Rank, const char *Format) { - TypeInfo Info; - Info.Name = cTypeName(Name); - Info.CIdentity = Name; - Info.Kind = TypeKind::CScalar; - Info.LLVMType = nativeFloatType<decltype(Sample)>(Context); - Info.CRank = Rank; - Info.CInputFormat = Format; - Info.COutputFormat = Rank == 9 ? "%Lf" : "%f"; - Types.emplace(Info.Name, std::move(Info)); - }; - Floating(float{}, "float", 7, "%f"); - Floating(double{}, "double", 8, "%lf"); - Floating(static_cast<long double>(0), "long double", 9, "%Lf"); - - TypeInfo Bool; - Bool.Name = cTypeName("_Bool"); - Bool.CIdentity = "_Bool"; - Bool.Kind = TypeKind::CScalar; - Bool.LLVMType = Type::getInt1Ty(Context); - Bool.CRank = 1; - Types.emplace(Bool.Name, std::move(Bool)); - TypeInfo Void; - Void.Name = cTypeName("void"); - Void.CIdentity = "void"; - Void.Kind = TypeKind::CScalar; - Void.LLVMType = Type::getVoidTy(Context); - Types.emplace(Void.Name, std::move(Void)); - - std::string Err; - defineCAlias("C_INT", "int", Err); - defineCAlias("C_CHAR", "char", Err); - defineCAlias("C_STRING", "char*", Err); - defineCAlias("C_DOUBLE", "double", Err); -} - bool TypeSystem::sameType(const std::string &Left, const std::string &Right) const { const TypeInfo *L = resolve(Left); return L && L->sameType(resolve(Right)); } -const TypeInfo *TypeSystem::promoteCScalar(const TypeInfo *Info) const { - if (!Info || Info->Kind != TypeKind::CScalar || Info->isVoid()) return nullptr; - if (Info->CRank >= 4) return Info; - const TypeInfo *Int = resolve(cTypeName("int")); - unsigned Width = Info->LLVMType->getIntegerBitWidth(); - unsigned IntWidth = Int->LLVMType->getIntegerBitWidth(); - return Width < IntWidth || (Width == IntWidth && Info->CSigned) - ? Int : resolve(cTypeName("unsigned int")); -} - -const TypeInfo *TypeSystem::commonCScalar(const TypeInfo *Left, const TypeInfo *Right) const { - const TypeInfo *L = promoteCScalar(Left); - const TypeInfo *R = promoteCScalar(Right); - if (!L || !R) return nullptr; - if (L->isReal() || R->isReal() || L->CSigned == R->CSigned) - return L->CRank >= R->CRank ? L : R; - const TypeInfo *S = L->CSigned ? L : R; - const TypeInfo *U = L->CSigned ? R : L; - if (U->CRank >= S->CRank) return U; - if (S->LLVMType->getIntegerBitWidth() > U->LLVMType->getIntegerBitWidth()) return S; - return resolve(cTypeName("unsigned " + S->CIdentity)); -} - -const TypeInfo *TypeSystem::importCType(const std::string &Expression, std::string &Err) { - // Whitespace is immaterial around stars; C's multiword base type is - // validated separately. Stars must all precede or all follow the base. - std::istringstream Input(Expression); - std::string Word, Text; - while (Input >> Word) { - if (!Text.empty()) Text += ' '; - Text += Word; - } - unsigned Depth = 0; - bool Prefix = !Text.empty() && Text.front() == '*'; - if (Prefix) { - while (!Text.empty() && (Text.front() == '*' || Text.front() == ' ')) { - if (Text.front() == '*') ++Depth; - Text.erase(Text.begin()); - } - } else { - while (!Text.empty() && (Text.back() == '*' || Text.back() == ' ')) { - if (Text.back() == '*') ++Depth; - Text.pop_back(); - } - } - std::istringstream BaseWords(Text); - std::map<std::string, unsigned> Counts; - bool Valid = !Text.empty(); - while (BaseWords >> Word) { - if (Word != "signed" && Word != "unsigned" && Word != "short" && - Word != "long" && Word != "int" && Word != "char" && - Word != "float" && Word != "double" && Word != "_Bool" && Word != "void") - Valid = false; - ++Counts[Word]; - } - for (const auto &Entry : Counts) - if (Entry.second > (Entry.first == "long" ? 2u : 1u)) Valid = false; - auto Has = [&](const char *Name) { return Counts.count(Name) != 0; }; - if (Has("signed") && Has("unsigned")) Valid = false; - std::string Base; - if (Has("void") || Has("_Bool") || Has("float")) { - if (Counts.size() != 1) Valid = false; - Base = Has("void") ? "void" : Has("_Bool") ? "_Bool" : "float"; - } else if (Has("double")) { - if (Counts.size() != (Has("long") ? 2u : 1u) || (Has("long") && Counts["long"] > 1)) - Valid = false; - Base = Has("long") ? "long double" : "double"; - } else if (Has("char")) { - if (Has("int") || Has("short") || Has("long")) Valid = false; - Base = Has("unsigned") ? "unsigned char" : Has("signed") ? "signed char" : "char"; - } else { - if (Has("short") && Has("long")) Valid = false; - Base = Has("short") ? "short" : Has("long") ? - (Counts["long"] == 2 ? "long long" : "long") : "int"; - if (Has("unsigned")) Base = "unsigned " + Base; - } - if (!Valid) { - Err = "Invalid or unsupported C type expression '" + Expression + "'"; - return nullptr; - } - const TypeInfo *Info = resolve(cTypeName(Base)); - for (unsigned I = 0; I < Depth; ++I) { - std::string Identity = Info->CIdentity + '*'; - const TypeInfo *Existing = resolve(cTypeName(Identity)); - if (Existing) { - Info = Existing; - continue; - } - TypeInfo Pointer; - Pointer.Name = cTypeName(Identity); - Pointer.CIdentity = Identity; - Pointer.Kind = TypeKind::CPointer; - Pointer.LLVMType = PointerType::getUnqual(Context); - Pointer.Pointer.PointeeTypeName = Info->Name; - auto Result = Types.emplace(Pointer.Name, std::move(Pointer)); - Info = &Result.first->second; - } - return Info; -} - -bool TypeSystem::defineCAlias(const std::string &Name, const std::string &Expression, - std::string &Err) { - if (Types.count(Name) || EnumConstants.count(Name)) { - Err = "Type name '" + Name + "' is already defined or reserved"; - return false; - } - const TypeInfo *C = importCType(Expression, Err); - if (!C) return false; - TypeInfo Alias = *C; - Alias.Name = Name; - Types.emplace(Name, std::move(Alias)); - return true; -} - const TypeInfo *TypeSystem::resolve(const std::string &TypeName) const { auto It = Types.find(TypeName); if (It == Types.end()) { |
