aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 13:43:50 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 13:43:50 +0000
commit06538905915b15bde6364459929fe89cf0b8de5c (patch)
tree1ec58f49ee4d990484001c13395aae2a8e5cd328 /lib
parent90bf56f456e22dcd98458eca14e0b152a5efb179 (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>
Diffstat (limited to 'lib')
-rw-r--r--lib/CodeGen/CABI/ClangABI.cc227
-rw-r--r--lib/CodeGen/CABI/Lowering.cc140
-rw-r--r--lib/CodeGen/FunctionGen.cc50
3 files changed, 393 insertions, 24 deletions
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) {