diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-22 13:51:44 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-22 13:51:44 +0000 |
| commit | 5eccd90b47b327917ad0e5858c79ca8fab27cd74 (patch) | |
| tree | 56e5c4110560679047e8813ab221742a0ab739f6 /lib/CodeGen | |
| parent | e3cc6c585a15fe4478dd2312bf5d390896729a31 (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/CodeGen')
| -rw-r--r-- | lib/CodeGen/CallGen.cc | 35 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 5 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 37 |
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; } |
