aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/FunctionGen.cc
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 15:06:44 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 15:06:44 +0000
commitdc33cd2faf6fa52486c02e86038c8f8c07c0fe27 (patch)
treed474619d75743399e7a324c8ae5ba04056333e0b /lib/CodeGen/FunctionGen.cc
parenta29cb23c5517fd4f8f5524fdf38b67ff5fe4a902 (diff)
feat(codegen): declare and call EXTERN C functions
Variadic arguments must be C values and receive C default argument promotions; narrow integer parameters carry sign/zero extension. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/FunctionGen.cc')
-rw-r--r--lib/CodeGen/FunctionGen.cc101
1 files changed, 100 insertions, 1 deletions
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index b4b37d3..3937e01 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -127,7 +127,7 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
return nullptr;
}
if (Proto->getOwnerClass().empty()) {
- if (isReservedRuntimeName(Proto->getName())) {
+ if (!Proto->isExternal() && isReservedRuntimeName(Proto->getName())) {
reportError("'%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name",
Proto->getName().c_str());
return nullptr;
@@ -231,6 +231,96 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
return F;
}
+// 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.
+bool FunctionGen::emitExternPrototype(PrototypeAST *Proto) {
+ if (PrototypeLinks.count(Proto)) return true;
+ const std::string &Name = Proto->getName();
+ if (sameIdentifier(Name, "MAIN")) {
+ reportError("MAIN cannot be declared EXTERN");
+ return false;
+ }
+ if (CodeGen::isBuiltinName(Name)) {
+ reportError("'%s' is a built-in function and cannot be redefined", Name.c_str());
+ return false;
+ }
+ if (Types.lookupEnumConstant(Name) || Types.resolve(Name)) {
+ reportError("'%s' is already a type or enum value name", Name.c_str());
+ 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;
+ };
+
+ FuncSig Sig;
+ Sig.OwnerUnit = CurrentUnit;
+ Sig.Name = Name;
+ Sig.LinkName = Name;
+ Sig.Prototype = Proto;
+ Sig.ReturnTypeName = Proto->getReturnType();
+ Sig.IsExternal = true;
+ Sig.IsVariadic = Proto->isVariadic();
+
+ std::vector<Type *> ArgTypes;
+ AttrBuilder RetAttrs(Context);
+ std::vector<AttributeSet> ParamAttrs;
+ for (size_t I = 0; I < Proto->getParams().size(); ++I) {
+ const ParamDecl &P = Proto->getParams()[I];
+ const TypeInfo *Info = Types.resolve(P.TypeName);
+ if (!Info) {
+ reportError("Unknown type %s", P.TypeName.c_str());
+ return false;
+ }
+ if (!Info->isC() || Info->isVoid()) {
+ reportError("EXTERN parameter %zu of %s must be a non-void C type (got %s)",
+ 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));
+ ParamSig PS;
+ PS.TypeName = P.TypeName;
+ Sig.Params.push_back(std::move(PS));
+ }
+ Type *RetType = Type::getVoidTy(Context);
+ if (!sameIdentifier(Proto->getReturnType(), "VOID")) {
+ const TypeInfo *Info = Types.resolve(Proto->getReturnType());
+ if (!Info) {
+ reportError("Unknown type %s", Proto->getReturnType().c_str());
+ return false;
+ }
+ if (!Info->isC() || Info->isVoid()) {
+ reportError("EXTERN_FUNCTION %s must return a non-void C type (got %s)",
+ Name.c_str(), Proto->getReturnType().c_str());
+ return false;
+ }
+ RetType = Info->LLVMType;
+ if (Attribute::AttrKind K = Extension(Info); K != Attribute::None) RetAttrs.addAttribute(K);
+ }
+
+ auto &Overloads = Signatures[Sig.Name];
+ if (!Overloads.empty()) {
+ if (Overloads.front().OwnerUnit != CurrentUnit)
+ reportError("Duplicate definition %s in %s and %s", Name.c_str(),
+ UnitPaths[Overloads.front().OwnerUnit].c_str(), UnitPaths[CurrentUnit].c_str());
+ else reportError("Function %s cannot be redefined", 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);
+ PrototypeLinks.emplace(Proto, Name);
+ Overloads.push_back(std::move(Sig));
+ return true;
+}
+
Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST,
const std::function<void(StmtAST*)> &StmtEmitter, unsigned ScopeID) {
PrototypeAST *Proto = FuncAST->getProto();
@@ -309,6 +399,15 @@ llvm::Value *FunctionGen::emitCall(const std::string &Callee, const std::vector<
return GenerateCall(Module, Exceptions, Callee, Args, HadError, DiagnosticLine);
}
+llvm::Value *FunctionGen::emitCall(const FuncSig &Sig, const std::vector<llvm::Value*> &Args) {
+ if (!Sig.IsExternal) return emitCall(Sig.LinkName, Args);
+ FunctionCallee Callee(Sig.ExternType, Module.getNamedValue(Sig.LinkName));
+ Value *Call = Sig.ExternType->getReturnType()->isVoidTy()
+ ? Exceptions.emitCall(Callee, Args) : Exceptions.emitCall(Callee, Args, "calltmp");
+ cast<CallBase>(Call)->setAttributes(Sig.ExternAttrs);
+ return Call;
+}
+
void FunctionGen::emitReturn(ReturnStmtAST *Ret, llvm::Value *RetVal) {
Instruction *CatchCleanup = Exceptions.emitReturnCleanup();
Instruction *RetInst = RetVal ? Builder.CreateRet(RetVal) : Builder.CreateRetVoid();