aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 13:51:44 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 13:51:44 +0000
commit5eccd90b47b327917ad0e5858c79ca8fab27cd74 (patch)
tree56e5c4110560679047e8813ab221742a0ab739f6 /lib
parente3cc6c585a15fe4478dd2312bf5d390896729a31 (diff)
feat(codegen): pass C-struct records by value as variadic arguments
A variadic EXTERN call is now arranged by clang per call, from the fixed parameter count and the promoted argument types. C-struct records pass unchanged in variadic positions, as C11 6.5.2.2p6-7 and 7.16.1.1p2 allow; a record that is not a C struct names the offending field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/CodeGen/CallGen.cc35
-rw-r--r--lib/CodeGen/ExpressionGen.cc5
-rw-r--r--lib/CodeGen/FunctionGen.cc37
3 files changed, 57 insertions, 20 deletions
diff --git a/lib/CodeGen/CallGen.cc b/lib/CodeGen/CallGen.cc
index b25ca12..a5567b7 100644
--- a/lib/CodeGen/CallGen.cc
+++ b/lib/CodeGen/CallGen.cc
@@ -23,15 +23,17 @@ using namespace cps;
bool CodeGen::marshalCallArgs(const FuncSig &Signature,
const std::vector<std::unique_ptr<ExprAST>> &ArgExprs,
int Line,
- std::vector<Value*> &Out) {
+ std::vector<Value*> &Out,
+ std::vector<const TypeInfo *> *Variadic) {
std::vector<ExprAST*> Args;
for (const auto &Arg : ArgExprs) Args.push_back(Arg.get());
- return marshalCallArgs(Signature, Args, Line, Out);
+ return marshalCallArgs(Signature, Args, Line, Out, Variadic);
}
bool CodeGen::marshalCallArgs(const FuncSig &Signature,
const std::vector<ExprAST*> &ArgExprs,
- int Line, std::vector<Value*> &Out) {
+ int Line, std::vector<Value*> &Out,
+ std::vector<const TypeInfo *> *Variadic) {
SaveAndRestore<int> Location(DiagnosticLine, Line ? Line : DiagnosticLine);
const FuncSig *Sig = &Signature;
const std::string &Callee = Sig->Name;
@@ -43,9 +45,11 @@ bool CodeGen::marshalCallArgs(const FuncSig &Signature,
std::vector<size_t> Transfers;
for (size_t i = 0; i < ArgExprs.size(); ++i) {
if (i >= Sig->Params.size()) {
- Value *V = emitVariadicArgument(ArgExprs[i], Callee, i + 1);
+ const TypeInfo *Promoted = nullptr;
+ Value *V = emitVariadicArgument(ArgExprs[i], Callee, i + 1, Promoted);
if (!V) return false;
Out.push_back(V);
+ if (Variadic) Variadic->push_back(Promoted);
continue;
}
const ParamSig &P = Sig->Params[i];
@@ -126,20 +130,33 @@ bool CodeGen::marshalCallArgs(const FuncSig &Signature,
// C default argument promotions for the variadic part of an EXTERN call; only
// C values are accepted, matching the explicit-cast rule for C/pseudocode.
-Value *CodeGen::emitVariadicArgument(ExprAST *Expr, const std::string &Callee, size_t Position) {
+// C structs and pointers to them pass unchanged (C11 6.5.2.2p6-7, 7.16.1.1p2).
+Value *CodeGen::emitVariadicArgument(ExprAST *Expr, const std::string &Callee, size_t Position,
+ const TypeInfo *&Promoted) {
const TypeInfo *Info = isWholeArrayVar(Expr) ? nullptr : getExprTypeInfo(Expr);
- // Pointers to C structs pass unchanged; records by value are not supported yet.
- if (!Info || Info->isRecord() || !Types->isExternType(Info)) {
+ const TypeInfo *Record = Info && Info->Kind == TypeKind::Pointer
+ ? Types->resolve(Info->Pointer.PointeeTypeName) : Info;
+ std::string Why;
+ if (Record && Record->isRecord() && !Record->IsClass && !Types->isCStruct(Record, &Why)) {
+ reportError("Variadic argument %zu to %s uses record %s, which is not a C struct: %s",
+ Position, Callee.c_str(), Record->Name.c_str(), Why.c_str());
+ return nullptr;
+ }
+ if (!Info || !Types->isExternType(Info)) {
reportError("Variadic argument %zu to %s must be a C value; cast it to a C type", Position, Callee.c_str());
return nullptr;
}
+ Promoted = Info;
Value *V = emitExpr(Expr);
if (!V || Info->Kind != TypeKind::CScalar) return V;
if (Info->isReal()) {
const TypeInfo *Double = resolveType("C_DOUBLE");
- return Info->CRank < Double->CRank ? convertNumericValue(V, Info, Double) : V;
+ if (Info->CRank >= Double->CRank) return V;
+ Promoted = Double;
+ return convertNumericValue(V, Info, Double);
}
- return convertNumericValue(V, Info, Types->promoteCScalar(Info));
+ Promoted = Types->promoteCScalar(Info);
+ return convertNumericValue(V, Info, Promoted);
}
// Resolve using static source types only; argument evaluation belongs to marshalling.
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index ece055a..d7a4ba7 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -231,9 +231,10 @@ Value *CodeGen::emitCall(const std::string &Callee,
}
std::vector<Value*> Args;
+ std::vector<const TypeInfo *> Variadic;
const FuncSig *Sig = resolveCall(Name, ArgExprs, Explicit);
- if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args)) return nullptr;
- return FuncGen->emitCall(*Sig, Args);
+ if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args, &Variadic)) return nullptr;
+ return FuncGen->emitCall(*Sig, Args, Variadic);
}
Value *CodeGen::emitAllocationSize(ExprAST *Expr) {
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index b79698e..6895945 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -470,21 +470,40 @@ llvm::Value *FunctionGen::emitCall(const std::string &Callee, const std::vector<
return Exceptions.emitCall(CalleeF, Args, "calltmp");
}
-llvm::Value *FunctionGen::emitCall(const FuncSig &Sig, const std::vector<llvm::Value*> &Args) {
+llvm::Value *FunctionGen::emitCall(const FuncSig &Sig, const std::vector<llvm::Value*> &Args,
+ const std::vector<const TypeInfo *> &Variadic) {
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;
+ // A call with variadic arguments is arranged with their promoted types.
+ const CABISignature *ABISig = &Sig.ExternABI;
+ CABISignature CallABI;
+ if (!Variadic.empty()) {
+ std::vector<const TypeInfo *> ArgTypes;
+ for (const ParamSig &P : Sig.Params) ArgTypes.push_back(Types.resolve(P.TypeName));
+ ArgTypes.insert(ArgTypes.end(), Variadic.begin(), Variadic.end());
+ const TypeInfo *Ret = sameIdentifier(Sig.ReturnTypeName, "VOID")
+ ? nullptr : Types.resolve(Sig.ReturnTypeName);
+ std::string Err;
+ if (!ABI.arrange(Ret, ArgTypes, Sig.Params.size(), true, CallABI, Err)) {
+ reportError("Internal: cannot arrange the call to %s: %s", Sig.Name.c_str(), Err.c_str());
+ return nullptr;
+ }
+ ABISig = &CallABI;
+ }
+ if (Args.size() != ABISig->Args.size()) {
+ reportError("Internal: the call to %s has %zu arguments but %zu are arranged", Sig.Name.c_str(),
+ Args.size(), ABISig->Args.size());
+ return nullptr;
+ }
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());
+ std::vector<Value *> IRArgs = lowerCABIArguments(Builder, *ABISig, Args, SRet);
Value *Call = Sig.ExternType->getReturnType()->isVoidTy()
? Exceptions.emitCall(Callee, IRArgs) : Exceptions.emitCall(Callee, IRArgs, "calltmp");
- cast<CallBase>(Call)->setAttributes(Sig.ExternAttrs);
- cast<CallBase>(Call)->setCallingConv(Sig.ExternABI.CallingConv);
- Value *Result = lowerCABIResult(Builder, Sig.ExternABI, Call, SRet);
+ cast<CallBase>(Call)->setAttributes(ABISig == &Sig.ExternABI
+ ? Sig.ExternAttrs : getCABIAttributes(Context, CallABI));
+ cast<CallBase>(Call)->setCallingConv(ABISig->CallingConv);
+ Value *Result = lowerCABIResult(Builder, *ABISig, Call, SRet);
return Result ? Result : Call;
}