diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-22 13:43:50 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-22 13:43:50 +0000 |
| commit | 06538905915b15bde6364459929fe89cf0b8de5c (patch) | |
| tree | 1ec58f49ee4d990484001c13395aae2a8e5cd328 | |
| parent | 90bf56f456e22dcd98458eca14e0b152a5efb179 (diff) | |
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 <noreply@anthropic.com>
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | cmake/NativeCompiler.cmake | 5 | ||||
| -rw-r--r-- | include/cps/CABI.h | 76 | ||||
| -rw-r--r-- | include/cps/FunctionGen.h | 6 | ||||
| -rw-r--r-- | lib/CodeGen/CABI/ClangABI.cc | 227 | ||||
| -rw-r--r-- | lib/CodeGen/CABI/Lowering.cc | 140 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 50 |
7 files changed, 480 insertions, 26 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e1cf41b..8207da4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,8 @@ add_library(CPSCodeGen STATIC lib/CodeGen/MemoryHandler.cc lib/CodeGen/FileHandler.cc lib/CodeGen/FileRuntimeIO.cc + lib/CodeGen/CABI/ClangABI.cc + lib/CodeGen/CABI/Lowering.cc ) llvm_map_components_to_libnames(llvm_libs core support native passes object) diff --git a/cmake/NativeCompiler.cmake b/cmake/NativeCompiler.cmake index e0b4b3f..5ff49cb 100644 --- a/cmake/NativeCompiler.cmake +++ b/cmake/NativeCompiler.cmake @@ -19,7 +19,7 @@ else() set(cpsc_linker_libraries lldELF) endif() -foreach(cpsc_native_library clangDriver ${cpsc_linker_libraries}) +foreach(cpsc_native_library clangDriver clangCodeGen ${cpsc_linker_libraries}) get_target_property(cpsc_native_type ${cpsc_native_library} TYPE) if(NOT cpsc_native_type STREQUAL "STATIC_LIBRARY") message(FATAL_ERROR "Native output requires static ${cpsc_native_library}; found ${cpsc_native_type}. See doc/build.md for static dependency setup.") @@ -75,6 +75,9 @@ target_sources(cpsc PRIVATE tools/driver/NativeCompiler.cc) target_include_directories(cpsc PRIVATE ${CLANG_INCLUDE_DIRS} ${LLD_INCLUDE_DIRS} "${CMAKE_BINARY_DIR}/native-runtime") target_link_libraries(cpsc PRIVATE clangDriver ${cpsc_linker_libraries}) +# EXTERN signatures use clang's C ABI code (lib/CodeGen/CABI/ClangABI.cc). +target_include_directories(CPSCodeGen PRIVATE ${CLANG_INCLUDE_DIRS}) +target_link_libraries(CPSCodeGen PRIVATE clangCodeGen clangDriver) if(MINGW AND TARGET LibXml2::LibXml2) # LLD's COFF manifest support reaches libxml2 through LLVMWindowsManifest. # An explicit .dll.a in that interface bypasses the driver's -static flag. 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 <cstdint> +#include <memory> +#include <string> +#include <vector> + +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<CABIArg> 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<const TypeInfo *> &Args, size_t Fixed, + bool Variadic, CABISignature &Sig, std::string &Err); + +private: + struct State; + llvm::LLVMContext &Context; + const TypeSystem &Types; + std::unique_ptr<State> 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<llvm::Value *> lowerCABIArguments(llvm::IRBuilder<> &B, const CABISignature &Sig, + llvm::ArrayRef<llvm::Value *> 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<ParamSig> 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<void(const FuncSig &)> BeforeCall; ArrayParamBinder BindArrayParam; std::function<void(llvm::Value *, const std::string &, int)> RegisterCopy; + ClangABI ABI{Context, Types}; // clang context created on the first EXTERN void createArgumentAllocas(llvm::Function *F, const std::vector<ParamDecl> &Params, bool IsMethod); llvm::Type *getLLVMType(const std::string &TypeName); diff --git a/lib/CodeGen/CABI/ClangABI.cc b/lib/CodeGen/CABI/ClangABI.cc new file mode 100644 index 0000000..3757a94 --- /dev/null +++ b/lib/CodeGen/CABI/ClangABI.cc @@ -0,0 +1,227 @@ +// The only file that talks to clang. Keep it that way: clang::CodeGen clashes +// with cps::CodeGen, clang is built without RTTI (so never subclass its +// classes here), and the CodeGenerator must be destroyed before the ASTContext. +#include "cps/CABI.h" +#include "cps/TypeSystem.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/CodeGen/CodeGenABITypes.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Driver/CreateInvocationFromArgs.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/TargetParser/Host.h" +#include <map> + +using namespace cps; +using clang::CodeGen::ABIArgInfo; + +// Members are destroyed in reverse order, so Gen goes before AST. +struct ClangABI::State { + const TypeSystem *Types = nullptr; + clang::DiagnosticOptions DiagOpts; + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> Diags; + std::shared_ptr<clang::CompilerInvocation> Invocation; + llvm::IntrusiveRefCntPtr<clang::FileManager> Files; + std::unique_ptr<clang::SourceManager> Sources; + llvm::IntrusiveRefCntPtr<clang::TargetInfo> Target; + std::unique_ptr<clang::IdentifierTable> Idents; + clang::SelectorTable Selectors; + clang::Builtin::Context Builtins; + std::unique_ptr<clang::ASTContext> AST; + std::unique_ptr<clang::CodeGenerator> Gen; + std::map<const TypeInfo *, clang::CanQualType> Records; + + // Target and language options come from a driver invocation for the host + // triple, as the C compiler would use them (e.g. Apple's darwinpcs and the + // signedness of char). + bool init(llvm::LLVMContext &Context, std::string &Err) { + Diags = llvm::makeIntrusiveRefCnt<clang::DiagnosticsEngine>( + clang::DiagnosticIDs::create(), DiagOpts, new clang::IgnoringDiagConsumer()); + std::string Triple = llvm::sys::getProcessTriple(); + std::string TargetArg = "--target=" + Triple; + const char *Args[] = {"clang", TargetArg.c_str(), "-fsyntax-only", "-x", "c", "cps-abi.c"}; + clang::CreateInvocationOptions Opts; + Opts.Diags = Diags; + Invocation = clang::createInvocation(Args, Opts); + if (Invocation) Target = clang::TargetInfo::CreateTargetInfo(*Diags, Invocation->getTargetOpts()); + if (!Target) { + Err = "cannot create a clang C ABI context for " + Triple; + return false; + } + clang::LangOptions &Lang = Invocation->getLangOpts(); + Target->adjust(*Diags, Lang, nullptr); + auto VFS = llvm::vfs::getRealFileSystem(); + Files = llvm::makeIntrusiveRefCnt<clang::FileManager>(clang::FileSystemOptions(), VFS); + Sources = std::make_unique<clang::SourceManager>(*Diags, *Files); + Idents = std::make_unique<clang::IdentifierTable>(Lang); + AST = std::make_unique<clang::ASTContext>(Lang, *Sources, *Idents, Selectors, Builtins, + clang::TU_Complete); + AST->InitBuiltinTypes(*Target); + Gen = clang::CreateLLVMCodeGen(*Diags, "cps-abi", VFS, Invocation->getHeaderSearchOpts(), + Invocation->getPreprocessorOpts(), + Invocation->getCodeGenOpts(), Context); + Gen->Initialize(*AST); + return true; + } + + // C scalars map by name and every pointer to void *; records are built + // once and checked against cpsc's layout. + bool map(const TypeInfo *Info, clang::CanQualType &Out, std::string &Err) { + using Member = clang::CanQualType clang::ASTContext::*; + static const std::map<std::string, Member> Scalars = { + {"_Bool", &clang::ASTContext::BoolTy}, + {"char", &clang::ASTContext::CharTy}, + {"signed char", &clang::ASTContext::SignedCharTy}, + {"unsigned char", &clang::ASTContext::UnsignedCharTy}, + {"short", &clang::ASTContext::ShortTy}, + {"unsigned short", &clang::ASTContext::UnsignedShortTy}, + {"int", &clang::ASTContext::IntTy}, + {"unsigned int", &clang::ASTContext::UnsignedIntTy}, + {"long", &clang::ASTContext::LongTy}, + {"unsigned long", &clang::ASTContext::UnsignedLongTy}, + {"long long", &clang::ASTContext::LongLongTy}, + {"unsigned long long", &clang::ASTContext::UnsignedLongLongTy}, + {"float", &clang::ASTContext::FloatTy}, + {"double", &clang::ASTContext::DoubleTy}, + {"long double", &clang::ASTContext::LongDoubleTy}, + }; + if (Info && (Info->Kind == TypeKind::CPointer || Info->Kind == TypeKind::Pointer)) { + Out = AST->VoidPtrTy; + return true; + } + if (Info && Info->Kind == TypeKind::CScalar) { + auto It = Scalars.find(Info->CIdentity); + if (It != Scalars.end()) { + Out = (*AST).*(It->second); + return true; + } + } + if (!Info || !Info->isRecord()) { + Err = "no C type for " + (Info ? Info->Name : std::string("an unknown type")); + return false; + } + auto Cached = Records.find(Info); + if (Cached != Records.end()) { + Out = Cached->second; + return true; + } + clang::TranslationUnitDecl *Unit = AST->getTranslationUnitDecl(); + auto *Record = clang::RecordDecl::Create(*AST, clang::TagTypeKind::Struct, Unit, {}, {}, + &AST->Idents.get(Info->Name)); + Record->startDefinition(); + for (const auto &[Name, Field] : Info->Record.Fields) { + clang::CanQualType FieldType; + if (!map(Types->resolve(Field.TypeName), FieldType, Err)) return false; + auto *Decl = clang::FieldDecl::Create(*AST, Record, {}, {}, &AST->Idents.get(Name), + FieldType, nullptr, nullptr, false, + clang::ICIS_NoInit); + Decl->setAccess(clang::AS_public); + Record->addDecl(Decl); + } + Record->completeDefinition(); + Unit->addDecl(Record); + Out = AST->getCanonicalTagType(Record); + uint64_t CSize = AST->getTypeSizeInChars(Out).getQuantity(); + uint64_t Size = Gen->GetModule()->getDataLayout().getTypeAllocSize(Info->LLVMType); + if (CSize != Size) { + Err = "record " + Info->Name + " has " + std::to_string(Size) + " bytes in cpsc but " + + std::to_string(CSize) + " in C"; + return false; + } + Records.emplace(Info, Out); + return true; + } +}; + +namespace { + +bool translate(const ABIArgInfo &I, llvm::Type *ValueType, const std::string &What, CABIArg &Out, + std::string &Err) { + Out.ValueType = ValueType; + const char *Unsupported = nullptr; + switch (I.getKind()) { + case ABIArgInfo::Direct: + case ABIArgInfo::Extend: + Out.K = I.isDirect() ? CABIArg::Kind::Direct : CABIArg::Kind::Extend; + Out.CoerceType = I.getCoerceToType(); + Out.Offset = I.getDirectOffset(); + Out.DirectAlign = I.getDirectAlign(); + Out.Flatten = I.isDirect() && I.getCanBeFlattened() && llvm::isa<llvm::StructType>(Out.CoerceType); + if (I.isExtend()) + Out.Extension = I.isSignExt() ? llvm::Attribute::SExt + : I.isZeroExt() ? llvm::Attribute::ZExt : llvm::Attribute::NoExt; + Out.InReg = I.getInReg(); + break; + case ABIArgInfo::Indirect: + Out.K = CABIArg::Kind::Indirect; + Out.ByVal = I.getIndirectByVal(); + Out.Align = I.getIndirectAlign().getQuantity(); + Out.InReg = I.getInReg(); + break; + case ABIArgInfo::Ignore: + Out.K = CABIArg::Kind::Ignore; + break; + case ABIArgInfo::IndirectAliased: Unsupported = "IndirectAliased"; break; + case ABIArgInfo::Expand: Unsupported = "Expand"; break; + case ABIArgInfo::CoerceAndExpand: Unsupported = "CoerceAndExpand"; break; + case ABIArgInfo::InAlloca: Unsupported = "InAlloca"; break; + case ABIArgInfo::TargetSpecific: Unsupported = "TargetSpecific"; break; + } + if (!Unsupported && I.getPaddingType()) Unsupported = "with a padding type"; + if (Unsupported) { + Err = "unsupported C ABI kind " + std::string(Unsupported) + " for " + What; + return false; + } + return true; +} + +} // namespace + +ClangABI::ClangABI(llvm::LLVMContext &Context, const TypeSystem &Types) + : Context(Context), Types(Types) {} + +ClangABI::~ClangABI() = default; + +bool ClangABI::arrange(const TypeInfo *Ret, const std::vector<const TypeInfo *> &Args, size_t Fixed, + bool Variadic, CABISignature &Sig, std::string &Err) { + if (!S) { + auto Fresh = std::make_unique<State>(); + Fresh->Types = &Types; + if (!Fresh->init(Context, Err)) return false; + S = std::move(Fresh); + } + clang::CanQualType RetType = S->AST->VoidTy; + if (Ret && !S->map(Ret, RetType, Err)) return false; + std::vector<clang::CanQualType> ArgTypes(Args.size()); + for (size_t I = 0; I < Args.size(); ++I) + if (!S->map(Args[I], ArgTypes[I], Err)) return false; + clang::CodeGen::RequiredArgs Required = Variadic + ? clang::CodeGen::RequiredArgs(Fixed) : clang::CodeGen::RequiredArgs::All; + const clang::CodeGen::CGFunctionInfo &Info = clang::CodeGen::arrangeFreeFunctionCall( + S->Gen->CGM(), RetType, ArgTypes, clang::FunctionType::ExtInfo(), Required); + + Sig = CABISignature(); + Sig.Fixed = Fixed; + Sig.Variadic = Variadic; + Sig.CallingConv = Info.getEffectiveCallingConvention(); + if (!translate(Info.getReturnInfo(), Ret ? Ret->LLVMType : llvm::Type::getVoidTy(Context), + "the result", Sig.Ret, Err)) + return false; + Sig.Args.resize(Args.size()); + for (size_t I = 0; I < Args.size(); ++I) + if (!translate(Info.arguments()[I].info, Args[I]->LLVMType, + (I < Fixed ? "parameter " : "variadic argument ") + std::to_string(I + 1), + Sig.Args[I], Err)) + return false; + return true; +} diff --git a/lib/CodeGen/CABI/Lowering.cc b/lib/CodeGen/CABI/Lowering.cc new file mode 100644 index 0000000..32305ef --- /dev/null +++ b/lib/CodeGen/CABI/Lowering.cc @@ -0,0 +1,140 @@ +// Applies a clang-arranged CABISignature with no per-architecture code. Sizes +// are never computed here: the module has no DataLayout until emission, so a +// coerced value goes through memory that holds both the value and its coerced +// form. +#include "cps/CABI.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" + +using namespace llvm; +using namespace cps; + +namespace { + +bool isDirect(const CABIArg &A) { + return A.K == CABIArg::Kind::Direct || A.K == CABIArg::Kind::Extend; +} + +// The IR values one argument occupies: none, one, or a flattened struct's elements. +unsigned countIRArgs(const CABIArg &A) { + if (A.K == CABIArg::Kind::Ignore) return 0; + return A.Flatten ? cast<StructType>(A.CoerceType)->getNumElements() : 1; +} + +void appendIRTypes(const CABIArg &A, std::vector<Type *> &Out) { + if (A.K == CABIArg::Kind::Indirect) Out.push_back(PointerType::getUnqual(A.ValueType->getContext())); + else if (A.Flatten) for (Type *Element : cast<StructType>(A.CoerceType)->elements()) Out.push_back(Element); + else if (isDirect(A)) Out.push_back(A.CoerceType); +} + +AllocaInst *entryAlloca(IRBuilder<> &B, Type *Ty, uint64_t MinAlign, const Twine &Name) { + Function *F = B.GetInsertBlock()->getParent(); + IRBuilder<> Entry(&F->getEntryBlock(), F->getEntryBlock().begin()); + AllocaInst *Memory = Entry.CreateAlloca(Ty, nullptr, Name); + if (MinAlign > Memory->getAlign().value()) Memory->setAlignment(Align(MinAlign)); + return Memory; +} + +// Memory for the value at offset 0 and its coerced form at A.Offset; the +// struct is at least as large and as aligned as either. +struct CoercionSlot { + AllocaInst *Value; + llvm::Value *Coerced; + Align CoercedAlign; +}; + +CoercionSlot coercionSlot(IRBuilder<> &B, const CABIArg &A) { + LLVMContext &C = A.ValueType->getContext(); + auto *Ty = StructType::get(C, {A.ValueType, ArrayType::get(Type::getInt8Ty(C), A.Offset), A.CoerceType}); + AllocaInst *Memory = entryAlloca(B, Ty, 0, "c.coerce"); + Value *Coerced = B.CreateConstInBoundsGEP1_32(Type::getInt8Ty(C), Memory, A.Offset); + return {Memory, Coerced, commonAlignment(Memory->getAlign(), A.Offset)}; +} + +AttributeSet argumentAttributes(LLVMContext &C, const CABIArg &A) { + AttrBuilder Attrs(C); + if (A.K == CABIArg::Kind::Extend) Attrs.addAttribute(A.Extension); + if (A.InReg) Attrs.addAttribute(Attribute::InReg); + if (isDirect(A) && A.DirectAlign) Attrs.addStackAlignmentAttr(A.DirectAlign); + if (A.K == CABIArg::Kind::Indirect && A.ByVal) { + Attrs.addByValAttr(A.ValueType); + Attrs.addAlignmentAttr(A.Align); + } + return AttributeSet::get(C, Attrs); +} + +} // namespace + +FunctionType *cps::getCABIFunctionType(const CABISignature &Sig) { + LLVMContext &C = Sig.Ret.ValueType->getContext(); + std::vector<Type *> Params; + if (Sig.Ret.K == CABIArg::Kind::Indirect) Params.push_back(PointerType::getUnqual(C)); + for (size_t I = 0; I < Sig.Fixed; ++I) appendIRTypes(Sig.Args[I], Params); + Type *Ret = isDirect(Sig.Ret) ? Sig.Ret.CoerceType : Type::getVoidTy(C); + return FunctionType::get(Ret, Params, Sig.Variadic); +} + +AttributeList cps::getCABIAttributes(LLVMContext &C, const CABISignature &Sig) { + AttrBuilder RetAttrs(C); + std::vector<AttributeSet> Params; + if (Sig.Ret.K == CABIArg::Kind::Indirect) { + AttrBuilder SRet(C); + SRet.addStructRetAttr(Sig.Ret.ValueType); + SRet.addAlignmentAttr(Sig.Ret.Align); + if (Sig.Ret.InReg) SRet.addAttribute(Attribute::InReg); + Params.push_back(AttributeSet::get(C, SRet)); + } else if (isDirect(Sig.Ret)) { + if (Sig.Ret.K == CABIArg::Kind::Extend) RetAttrs.addAttribute(Sig.Ret.Extension); + if (Sig.Ret.InReg) RetAttrs.addAttribute(Attribute::InReg); + } + for (const CABIArg &A : Sig.Args) + Params.insert(Params.end(), countIRArgs(A), argumentAttributes(C, A)); + return AttributeList::get(C, AttributeSet(), AttributeSet::get(C, RetAttrs), Params); +} + +std::vector<Value *> cps::lowerCABIArguments(IRBuilder<> &B, const CABISignature &Sig, + ArrayRef<Value *> Args, Value *&SRet) { + std::vector<Value *> Out; + SRet = nullptr; + if (Sig.Ret.K == CABIArg::Kind::Indirect) { + SRet = entryAlloca(B, Sig.Ret.ValueType, Sig.Ret.Align, "c.result"); + Out.push_back(SRet); + } + for (size_t I = 0; I < Args.size(); ++I) { + const CABIArg &A = Sig.Args[I]; + Value *V = Args[I]; + if (A.K == CABIArg::Kind::Ignore) continue; + if (A.K == CABIArg::Kind::Indirect) { + // byval or not, the callee receives memory of its own. + AllocaInst *Copy = entryAlloca(B, A.ValueType, A.Align, "c.arg"); + B.CreateStore(V, Copy); + Out.push_back(Copy); + continue; + } + if (!A.Flatten && !A.Offset && A.CoerceType == V->getType()) { + Out.push_back(V); + continue; + } + CoercionSlot Slot = coercionSlot(B, A); + B.CreateStore(V, Slot.Value); + if (!A.Flatten) { + Out.push_back(B.CreateAlignedLoad(A.CoerceType, Slot.Coerced, Slot.CoercedAlign)); + continue; + } + auto *Struct = cast<StructType>(A.CoerceType); + for (unsigned E = 0; E < Struct->getNumElements(); ++E) + Out.push_back(B.CreateLoad(Struct->getElementType(E), B.CreateStructGEP(Struct, Slot.Coerced, E))); + } + return Out; +} + +Value *cps::lowerCABIResult(IRBuilder<> &B, const CABISignature &Sig, Value *Call, Value *SRet) { + const CABIArg &R = Sig.Ret; + if (R.ValueType->isVoidTy()) return nullptr; + if (R.K == CABIArg::Kind::Ignore) return Constant::getNullValue(R.ValueType); + if (R.K == CABIArg::Kind::Indirect) return B.CreateLoad(R.ValueType, SRet); + if (!R.Offset && R.CoerceType == R.ValueType) return Call; + CoercionSlot Slot = coercionSlot(B, R); + B.CreateAlignedStore(Call, Slot.Coerced, Slot.CoercedAlign); + return B.CreateLoad(R.ValueType, Slot.Value); +} diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 1cbf19f..2cf6da3 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -274,7 +274,7 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { } // The prototype names the exact C symbol; parameters and the result must be C -// types. Narrow integers carry the C ABI's sign/zero extension attributes. +// types. clang arranges the signature for the host C ABI. bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { if (PrototypeLinks.count(Proto)) return true; const std::string &Name = Proto->getName(); @@ -291,13 +291,6 @@ bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { return false; } - auto Extension = [](const TypeInfo *Info) { - if (Info->Kind != TypeKind::CScalar || !Info->LLVMType->isIntegerTy() || - Info->LLVMType->getIntegerBitWidth() >= 32) - return Attribute::None; - return Info->CSigned ? Attribute::SExt : Attribute::ZExt; - }; - // Records cross the C boundary only as C structs, for now through a // pointer type. What names the parameter or return type in diagnostics. auto CheckRecord = [&](const TypeInfo *Info, const std::string &What) { @@ -327,9 +320,7 @@ bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { Sig.IsExternal = true; Sig.IsVariadic = Proto->isVariadic(); - std::vector<Type *> ArgTypes; - AttrBuilder RetAttrs(Context); - std::vector<AttributeSet> ParamAttrs; + std::vector<const TypeInfo *> ArgTypes; for (size_t I = 0; I < Proto->getParams().size(); ++I) { const ParamDecl &P = Proto->getParams()[I]; const TypeInfo *Info = Types.resolve(P.TypeName); @@ -344,15 +335,12 @@ bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { I + 1, Name.c_str(), P.TypeName.c_str()); return false; } - ArgTypes.push_back(Info->LLVMType); - AttrBuilder B(Context); - if (Attribute::AttrKind K = Extension(Info); K != Attribute::None) B.addAttribute(K); - ParamAttrs.push_back(AttributeSet::get(Context, B)); + ArgTypes.push_back(Info); ParamSig PS; PS.TypeName = P.TypeName; Sig.Params.push_back(std::move(PS)); } - Type *RetType = Type::getVoidTy(Context); + const TypeInfo *RetType = nullptr; if (!sameIdentifier(Proto->getReturnType(), "VOID")) { const TypeInfo *Info = Types.resolve(Proto->getReturnType()); if (!Info) { @@ -365,8 +353,7 @@ bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { Name.c_str(), Proto->getReturnType().c_str()); return false; } - RetType = Info->LLVMType; - if (Attribute::AttrKind K = Extension(Info); K != Attribute::None) RetAttrs.addAttribute(K); + RetType = Info; } // Repeated identical EXTERN declarations, as in C, share one symbol; each @@ -388,6 +375,7 @@ bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { reportError("Conflicting EXTERN declarations of %s", Name.c_str()); return false; } + Sig.ExternABI = First.ExternABI; Sig.ExternType = First.ExternType; Sig.ExternAttrs = First.ExternAttrs; PrototypeLinks.emplace(Proto, Name); @@ -401,10 +389,16 @@ bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) { else reportError("EXTERN %s conflicts with FUNCTION/PROCEDURE %s", Name.c_str(), Name.c_str()); return false; } - Sig.ExternType = FunctionType::get(RetType, ArgTypes, Proto->isVariadic()); - Sig.ExternAttrs = AttributeList::get(Context, AttributeSet(), - AttributeSet::get(Context, RetAttrs), ParamAttrs); - Module.getOrInsertFunction(Name, Sig.ExternType); + std::string Err; + if (!ABI.arrange(RetType, ArgTypes, ArgTypes.size(), Sig.IsVariadic, Sig.ExternABI, Err)) { + reportError("Internal: cannot arrange EXTERN %s: %s", Name.c_str(), Err.c_str()); + return false; + } + Sig.ExternType = getCABIFunctionType(Sig.ExternABI); + Sig.ExternAttrs = getCABIAttributes(Context, Sig.ExternABI); + FunctionCallee Declared = Module.getOrInsertFunction(Name, Sig.ExternType); + if (auto *F = dyn_cast<Function>(Declared.getCallee())) + F->setCallingConv(Sig.ExternABI.CallingConv); PrototypeLinks.emplace(Proto, Name); Overloads.push_back(std::move(Sig)); return true; @@ -485,10 +479,18 @@ llvm::Value *FunctionGen::emitCall(const FuncSig &Sig, const std::vector<llvm::V if (BeforeCall) BeforeCall(Sig); if (!Sig.IsExternal) return emitCall(Sig.LinkName, Args); FunctionCallee Callee(Sig.ExternType, Module.getNamedValue(Sig.LinkName)); + // Variadic arguments arrive promoted and pass as they are. + size_t Fixed = Sig.ExternABI.Fixed; + Value *SRet = nullptr; + std::vector<Value *> IRArgs = lowerCABIArguments( + Builder, Sig.ExternABI, ArrayRef<Value *>(Args).take_front(Fixed), SRet); + IRArgs.insert(IRArgs.end(), Args.begin() + Fixed, Args.end()); Value *Call = Sig.ExternType->getReturnType()->isVoidTy() - ? Exceptions.emitCall(Callee, Args) : Exceptions.emitCall(Callee, Args, "calltmp"); + ? Exceptions.emitCall(Callee, IRArgs) : Exceptions.emitCall(Callee, IRArgs, "calltmp"); cast<CallBase>(Call)->setAttributes(Sig.ExternAttrs); - return Call; + cast<CallBase>(Call)->setCallingConv(Sig.ExternABI.CallingConv); + Value *Result = lowerCABIResult(Builder, Sig.ExternABI, Call, SRet); + return Result ? Result : Call; } void FunctionGen::emitReturn(llvm::Value *RetVal) { |
