diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-17 10:32:30 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-17 10:32:30 +0000 |
| commit | 377a1fd2eadd2a70cd27f13e2fee6e96c8706a51 (patch) | |
| tree | c05665f26b3434359cf4ae7720e8f9111a322522 /lib/CodeGen | |
| parent | f5246db6d045ffb86d6717e1b1c37f03bcb2b75b (diff) | |
feat(codegen): overload global functions and procedures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 69 | ||||
| -rw-r--r-- | lib/CodeGen/ConstantGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/Designator.cc | 5 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 5 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 67 | ||||
| -rw-r--r-- | lib/CodeGen/GlobalGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/ValueTypeGen.cc | 15 |
8 files changed, 113 insertions, 54 deletions
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index b81fe2b..9f59a4d 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -160,7 +160,7 @@ bool CodeGen::isVisible(size_t Owner) const { void CodeGen::selectUnit(size_t Unit) { CurrentUnit = Unit; DiagnosticSource = Units->size() > 1 ? (*Units)[Unit].Path : ""; - FuncGen->setCurrentUnit(Unit); + FuncGen->setCurrentUnit(Unit, (*Units)[Unit].Path); } void CodeGen::importSymbols() { @@ -187,15 +187,17 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) { }); FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); }); - IdentifierMap<size_t> Definitions; + struct Definition { size_t Unit; bool IsFunction; bool IsOverload; }; + IdentifierMap<Definition> Definitions; std::optional<size_t> CodeUnit; FunctionDefAST *Entry = nullptr; size_t EntryUnit = 0; - auto Reserve = [&](const std::string &Name) { - auto [It, Added] = Definitions.emplace(Name, CurrentUnit); - if (!Added && It->second != CurrentUnit) + auto Reserve = [&](const std::string &Name, bool IsFunction = false, bool IsOverload = false) { + auto [It, Added] = Definitions.emplace(Name, Definition{CurrentUnit, IsFunction, IsOverload}); + bool Compatible = IsFunction && IsOverload && It->second.IsFunction && It->second.IsOverload; + if (!Added && It->second.Unit != CurrentUnit && !Compatible) reportError("Duplicate definition %s in %s and %s", Name.c_str(), - Sources[It->second].Path.c_str(), Sources[CurrentUnit].Path.c_str()); + Sources[It->second.Unit].Path.c_str(), Sources[CurrentUnit].Path.c_str()); }; for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); @@ -203,7 +205,7 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) { SaveAndRestore<int> Location(DiagnosticLine, S->getLine()); std::string TypeName; if (auto *F = dynamic_cast<FunctionDefAST*>(S.get())) { - Reserve(F->getProto()->getName()); + Reserve(F->getProto()->getName(), true, F->getProto()->isOverload()); if (sameIdentifier(F->getProto()->getName(), "MAIN")) { if (Entry) reportError("Multiple MAIN definitions in %s and %s", Sources[EntryUnit].Path.c_str(), Sources[I].Path.c_str()); @@ -326,7 +328,7 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) { if (!Builder->GetInsertBlock()->hasTerminator()) { Value *Status = ConstantInt::get(*TheContext, APInt(32, 0)); - if (Entry) { + if (Entry && !HadError) { Value *Result = Exceptions->emitCall(TheModule->getFunction("__cps_main"), {}, "main_result"); Status = Builder->CreateTrunc(Result, Type::getInt32Ty(*TheContext)); } @@ -356,16 +358,13 @@ void CodeGen::print(raw_ostream &Output) { // Collects the arguments for a call to a user FUNCTION/PROCEDURE, dispatching // on the *declared* signature: BYREF parameters take the variable's alloca, // BYVAL parameters are evaluated and coerced to the declared parameter type. -bool CodeGen::marshalCallArgs(const std::string &Callee, +bool CodeGen::marshalCallArgs(const FuncSig &Signature, const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line, std::vector<Value*> &Out) { SaveAndRestore<int> Location(DiagnosticLine, Line ? Line : DiagnosticLine); - const FuncSig *Sig = FuncGen->getSignature(Callee); - if (!Sig) { - reportError("Call to undefined function %s", Callee.c_str()); - return false; - } + const FuncSig *Sig = &Signature; + const std::string &Callee = Sig->Name; if (ArgExprs.size() != Sig->Params.size()) { reportError("Incorrect # arguments passed to %s", Callee.c_str()); return false; @@ -424,3 +423,45 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, } return true; } + +// Resolve using static source types only; argument evaluation belongs to marshalling. +const FuncSig *CodeGen::resolveCall(const std::string &Name, + const std::vector<std::unique_ptr<ExprAST>> &Args) { + auto Overloads = FuncGen->getOverloads(Name); + if (Overloads.empty()) { + reportError("Call to undefined function %s", Name.c_str()); + return nullptr; + } + if (Overloads.size() == 1 && !Overloads.front()->IsOverload) return Overloads.front(); + struct ArgumentType { const TypeInfo *Type; int Rank; }; + std::vector<ArgumentType> Actual; + std::string Key; + for (const auto &Arg : Args) { + const ArrayMetadata *Array = nullptr; + if (auto *Var = dynamic_cast<VariableExprAST *>(Arg.get())) { + const SymbolInfo *Symbol = getSymbolInfo(Var->getName()); + if (Symbol && Symbol->IsArray) Array = Arrays->getMetadata(Var->getName()); + } + const TypeInfo *Type = Array ? Types->resolve(Array->ElementTypeName) : getExprTypeInfo(Arg.get()); + int Rank = Array ? Array->Rank : 0; + Actual.push_back({Type, Rank}); + if (!Key.empty()) Key += ", "; + if (Rank) Key += "ARRAY" + std::to_string(Rank) + " OF "; + Key += Type ? Type->Name : "?"; + } + for (const FuncSig *Sig : Overloads) { + if (Sig->Params.size() != Actual.size()) continue; + bool Match = true; + for (size_t I = 0; I < Actual.size(); ++I) { + const ParamSig &P = Sig->Params[I]; + if (Actual[I].Rank != (P.IsArray ? P.Rank : 0) || !Actual[I].Type || + !Actual[I].Type->sameType(Types->resolve(P.TypeName))) { + Match = false; + break; + } + } + if (Match) return Sig; + } + reportError("No matching overload for %s(%s)", Name.c_str(), Key.c_str()); + return nullptr; +} diff --git a/lib/CodeGen/ConstantGen.cc b/lib/CodeGen/ConstantGen.cc index 6e1ec57..fc5d178 100644 --- a/lib/CodeGen/ConstantGen.cc +++ b/lib/CodeGen/ConstantGen.cc @@ -60,7 +60,7 @@ void CodeGen::emitConstantStmt(ConstantStmtAST *Stmt) { return; } if (ConstantScopes.back().ID == 0 && - (FuncGen->getSignature(Name) || isBuiltinName(Name))) { + (FuncGen->hasFunction(Name) || isBuiltinName(Name))) { reportError("Constant %s conflicts with a function name", Name.c_str()); return; } diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc index b31492b..540059d 100644 --- a/lib/CodeGen/Designator.cc +++ b/lib/CodeGen/Designator.cc @@ -97,8 +97,9 @@ Value *CodeGen::emitMethodCall(const std::string &Class, Value *Receiver, return nullptr; } std::vector<Value*> Args = {Receiver}; - if (!marshalCallArgs(Method->getLinkName(), ArgExprs, Line, Args)) return nullptr; - return FuncGen->emitCall(Method->getLinkName(), Args); + const FuncSig *Sig = resolveCall(Method->getLinkName(), ArgExprs); + if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args)) return nullptr; + return FuncGen->emitCall(Sig->LinkName, Args); } // Resolves a designator (or legacy variable/array-access node) to the address diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc index ba5e329..b667e6a 100644 --- a/lib/CodeGen/ExpressionGen.cc +++ b/lib/CodeGen/ExpressionGen.cc @@ -299,8 +299,9 @@ Value *CodeGen::emitCall(const std::string &Callee, } std::vector<Value*> Args; - if (!marshalCallArgs(Name, ArgExprs, Line, Args)) return nullptr; - return FuncGen->emitCall(Name, Args); + const FuncSig *Sig = resolveCall(Name, ArgExprs); + if (!Sig || !marshalCallArgs(*Sig, ArgExprs, Line, Args)) return nullptr; + return FuncGen->emitCall(Sig->LinkName, Args); } Value *CodeGen::emitAllocationSize(ExprAST *Expr) { diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index d985bce..b4b37d3 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -104,13 +104,28 @@ void FunctionGen::createArgumentAllocas(Function *F, const std::vector<ParamDecl } } -const FuncSig *FunctionGen::getSignature(const std::string &Name) const { +std::vector<const FuncSig *> FunctionGen::getOverloads(const std::string &Name) const { + std::vector<const FuncSig *> Result; auto It = Signatures.find(sameIdentifier(Name, "MAIN") ? "__cps_main" : Name); - return It == Signatures.end() || (Visible && !Visible(It->second.OwnerUnit)) - ? nullptr : &It->second; + if (It != Signatures.end()) + for (const FuncSig &Sig : It->second) + if (!Visible || Visible(Sig.OwnerUnit)) Result.push_back(&Sig); + return Result; +} + +const FuncSig *FunctionGen::getSignatureByLink(const std::string &Name) const { + auto It = LinkIndex.find(Name); + if (It == LinkIndex.end()) return nullptr; + return &Signatures.at(It->second.first)[It->second.second]; } Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { + auto Registered = PrototypeLinks.find(Proto); + if (Registered != PrototypeLinks.end()) return Module.getFunction(Registered->second); + if (Proto->isOverload() && Proto->getOwnerClass().empty() && sameIdentifier(Proto->getName(), "MAIN")) { + reportError("MAIN cannot be declared OVERLOAD"); + return nullptr; + } if (Proto->getOwnerClass().empty()) { if (isReservedRuntimeName(Proto->getName())) { reportError("'%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name", @@ -137,6 +152,8 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { FuncSig Sig; Sig.OwnerUnit = CurrentUnit; Sig.Name = Proto->getLinkName(); + Sig.IsOverload = Proto->isOverload(); + Sig.Prototype = Proto; Sig.ReturnTypeName = Proto->getReturnType(); std::vector<Type*> ArgTypes; @@ -167,29 +184,38 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { } ArgTypes.push_back(T); } + if (!Sig.ParamKey.empty()) Sig.ParamKey += ","; + if (P.IsArray) Sig.ParamKey += "ARRAY" + std::to_string(P.Rank) + " OF "; + Sig.ParamKey += Info ? (Info->isC() ? "C:" + Info->CIdentity : Info->Name) : P.TypeName; Sig.Params.push_back(std::move(PS)); } Type *RetType = getLLVMType(Proto->getReturnType()); FunctionType *FT = FunctionType::get(RetType, ArgTypes, false); - const FuncSig *Existing = getSignature(Proto->getLinkName()); - Function *F = Module.getFunction(Existing ? Existing->Name : Proto->getLinkName()); - if (F && F->getFunctionType() != FT) { - reportError("Function %s conflicts with an existing declaration of a different type", - Proto->getName().c_str()); - return nullptr; - } - if (!F) { - F = Function::Create(FT, - Proto->getOwnerClass().empty() && sameIdentifier(Proto->getName(), "MAIN") - ? Function::InternalLinkage : Function::ExternalLinkage, - Proto->getLinkName(), &Module); + auto &Overloads = Signatures[Sig.Name]; + for (const FuncSig &Existing : Overloads) { + if (!Sig.IsOverload || !Existing.IsOverload) { + reportError("Function %s cannot be redefined. Multiple definition of %s; every overload must be declared OVERLOAD", + Proto->getName().c_str(), Proto->getName().c_str()); + return nullptr; + } + if (sameIdentifier(Existing.ParamKey, Sig.ParamKey)) { + if (Existing.OwnerUnit != CurrentUnit) + reportError("Duplicate definition %s in %s and %s", Sig.Name.c_str(), + UnitPaths[Existing.OwnerUnit].c_str(), UnitPaths[CurrentUnit].c_str()); + else reportError("Function %s cannot be redefined", Proto->getName().c_str()); + return nullptr; + } } + Sig.LinkName = Sig.Name + (Sig.IsOverload ? "(" + Sig.ParamKey + ")" : ""); + Function *F = Function::Create(FT, + Proto->getOwnerClass().empty() && sameIdentifier(Proto->getName(), "MAIN") + ? Function::InternalLinkage : Function::ExternalLinkage, + Sig.LinkName, &Module); Exceptions.prepareFunction(F); - - // Keep the first registration: on a duplicate definition the first one - // wins and the second is rejected by emitFunctionDef. - Signatures.emplace(Proto->getLinkName(), std::move(Sig)); + PrototypeLinks.emplace(Proto, Sig.LinkName); + LinkIndex.emplace(Sig.LinkName, std::make_pair(Sig.Name, Overloads.size())); + Overloads.push_back(std::move(Sig)); Function::arg_iterator AI = F->arg_begin(); if (!Proto->getOwnerClass().empty()) (AI++)->setName("this"); @@ -280,8 +306,7 @@ static Value *GenerateCall(llvm::Module &Module, } llvm::Value *FunctionGen::emitCall(const std::string &Callee, const std::vector<llvm::Value*> &Args) { - const FuncSig *Sig = getSignature(Callee); - return GenerateCall(Module, Exceptions, Sig ? Sig->Name : Callee, Args, HadError, DiagnosticLine); + return GenerateCall(Module, Exceptions, Callee, Args, HadError, DiagnosticLine); } void FunctionGen::emitReturn(ReturnStmtAST *Ret, llvm::Value *RetVal) { diff --git a/lib/CodeGen/GlobalGen.cc b/lib/CodeGen/GlobalGen.cc index 659ae85..46b6f0c 100644 --- a/lib/CodeGen/GlobalGen.cc +++ b/lib/CodeGen/GlobalGen.cc @@ -34,7 +34,7 @@ void CodeGen::runGlobalPrePass(const SourceUnit &Unit, int Phase) { continue; } if (Exports[CurrentUnit].count(Name) || Types->resolve(Name) || - Types->lookupEnumConstant(Name) || FuncGen->getSignature(Name) || + Types->lookupEnumConstant(Name) || FuncGen->hasFunction(Name) || isBuiltinName(Name)) { reportError("Global %s conflicts with an existing definition", Name.c_str()); continue; diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc index 1a9c285..8783cde 100644 --- a/lib/CodeGen/StatementGen.cc +++ b/lib/CodeGen/StatementGen.cc @@ -100,7 +100,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) { if (auto *Ret = dynamic_cast<ReturnStmtAST*>(Stmt)) { Function *F = Builder->GetInsertBlock()->getParent(); const FuncSig *Sig = F->getName() == "main" ? nullptr - : FuncGen->getSignature(F->getName().str()); + : FuncGen->getSignatureByLink(F->getName().str()); // The generated wrapper has no signature; user MAIN uses __cps_main. if (!Sig) { reportError("RETURN is only allowed inside a FUNCTION or PROCEDURE"); diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc index 5e926cf..d2bba03 100644 --- a/lib/CodeGen/ValueTypeGen.cc +++ b/lib/CodeGen/ValueTypeGen.cc @@ -8,7 +8,7 @@ using namespace cps; using namespace cps::detail; -const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { +const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) { if (!Expr) return nullptr; if (dynamic_cast<IntegerExprAST*>(Expr)) return resolveType("INTEGER"); @@ -129,20 +129,11 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { if (const BuiltinInfo *B = findBuiltin(Name)) { return resolveType(B->ReturnTypeName); } - if (const FuncSig *Sig = FuncGen->getSignature(Name)) { + if (const FuncSig *Sig = resolveCall(Name, Call->getArgs())) { return resolveType(Sig->ReturnTypeName); } - Function *CalleeF = TheModule->getFunction(Name); - if (!CalleeF) return resolveType("INTEGER"); - - Type *RetTy = CalleeF->getReturnType(); - if (RetTy->isVoidTy()) return resolveType("VOID"); - if (RetTy->isDoubleTy()) return resolveType("REAL"); - if (RetTy->isIntegerTy(1)) return resolveType("BOOLEAN"); - if (RetTy->isIntegerTy(8)) return resolveType("CHAR"); - if (RetTy->isPointerTy()) return resolveType("STRING"); - return resolveType("INTEGER"); + return nullptr; } return nullptr; |
