From 06538905915b15bde6364459929fe89cf0b8de5c Mon Sep 17 00:00:00 2001 From: _ov4 Date: Thu, 22 Oct 2026 13:43:50 +0000 Subject: feat(codegen): arrange EXTERN signatures with clang's C ABI Every EXTERN "C" signature is now arranged by clang's CodeGen (arrangeFreeFunctionCall) for the host triple and applied by one generic lowering routine, replacing the hand-written signext/zeroext rule. clangCodeGen is linked statically into CPSCodeGen. Records still cross only through pointer types, so generated code is unchanged. Co-Authored-By: Claude Opus 5.5 --- include/cps/CABI.h | 76 +++++++++++++++++++++++++++++++++++++++++++++++ include/cps/FunctionGen.h | 6 +++- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 include/cps/CABI.h (limited to 'include') diff --git a/include/cps/CABI.h b/include/cps/CABI.h new file mode 100644 index 0000000..ba776e9 --- /dev/null +++ b/include/cps/CABI.h @@ -0,0 +1,76 @@ +#pragma once +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/IRBuilder.h" +#include +#include +#include +#include + +namespace cps { + +struct TypeInfo; +class TypeSystem; + +// One EXTERN "C" parameter or result as clang's ABIArgInfo arranges it. Only +// the kinds our targets use exist here; ClangABI reports any other kind as an +// internal error. +struct CABIArg { + enum class Kind { Direct, Extend, Indirect, Ignore }; + Kind K = Kind::Direct; + llvm::Type *ValueType = nullptr; // the pseudocode value; void for VOID + llvm::Type *CoerceType = nullptr; // Direct, Extend: the IR type passed + unsigned Offset = 0; // Direct, Extend: byte offset of the coerced part + bool Flatten = false; // Direct: a struct CoerceType passed element by element + unsigned DirectAlign = 0; // Direct, Extend: stack alignment, if any + llvm::Attribute::AttrKind Extension = llvm::Attribute::None; // Extend: SExt, ZExt or NoExt + bool ByVal = false; // Indirect: byval, otherwise a pointer to a copy + uint64_t Align = 0; // Indirect: alignment of the memory in bytes + bool InReg = false; +}; + +// An arranged EXTERN signature. Args holds the Fixed prototype parameters and, +// for one variadic call, the promoted variadic arguments after them. +struct CABISignature { + CABIArg Ret; + std::vector Args; + size_t Fixed = 0; + bool Variadic = false; + unsigned CallingConv = 0; +}; + +// Arranges signatures for the host C ABI with clang's CodeGen. The clang +// context is created on the first call; ClangABI.cc is the only file that +// includes clang headers. +class ClangABI { +public: + ClangABI(llvm::LLVMContext &Context, const TypeSystem &Types); + ~ClangABI(); + // Ret is nullptr for VOID; the first Fixed of Args are the prototype's + // parameters. On failure Err holds the reason for an internal error. + bool arrange(const TypeInfo *Ret, const std::vector &Args, size_t Fixed, + bool Variadic, CABISignature &Sig, std::string &Err); + +private: + struct State; + llvm::LLVMContext &Context; + const TypeSystem &Types; + std::unique_ptr S; +}; + +// Generic lowering of an arranged signature (Lowering.cc), without +// per-architecture code. +// The declared function type, from the fixed parameters. +llvm::FunctionType *getCABIFunctionType(const CABISignature &Sig); +// The call attributes, for every argument in Sig. +llvm::AttributeList getCABIAttributes(llvm::LLVMContext &C, const CABISignature &Sig); +// The IR arguments for one pseudocode value per Sig.Args entry. SRet receives +// the result memory when the result is returned indirectly. +std::vector lowerCABIArguments(llvm::IRBuilder<> &B, const CABISignature &Sig, + llvm::ArrayRef Args, llvm::Value *&SRet); +// The pseudocode result of Call; nullptr for VOID. +llvm::Value *lowerCABIResult(llvm::IRBuilder<> &B, const CABISignature &Sig, llvm::Value *Call, + llvm::Value *SRet); + +} // namespace cps diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h index 1b4a07a..8b63d67 100644 --- a/include/cps/FunctionGen.h +++ b/include/cps/FunctionGen.h @@ -1,4 +1,5 @@ #pragma once +#include "cps/CABI.h" #include "cps/Identifier.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" @@ -45,9 +46,11 @@ struct FuncSig { std::vector Params; size_t OwnerUnit = 0; // EXTERN "C": called through its declared C type, which may differ from a - // runtime declaration of the same libc symbol. + // runtime declaration of the same libc symbol. ExternABI is clang's + // arrangement of the fixed parameters; ExternType and ExternAttrs lower it. bool IsExternal = false; bool IsVariadic = false; + CABISignature ExternABI; llvm::FunctionType *ExternType = nullptr; llvm::AttributeList ExternAttrs; }; @@ -83,6 +86,7 @@ private: std::function BeforeCall; ArrayParamBinder BindArrayParam; std::function RegisterCopy; + ClangABI ABI{Context, Types}; // clang context created on the first EXTERN void createArgumentAllocas(llvm::Function *F, const std::vector &Params, bool IsMethod); llvm::Type *getLLVMType(const std::string &TypeName); -- cgit v1.3.1-4-g156e