@@ -1103,6 +1103,60 @@ cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator(
11031103 return cir::GlobalLinkageKind::ExternalLinkage;
11041104}
11051105
1106+ // / This function is called when we implement a function with no prototype, e.g.
1107+ // / "int foo() {}". If there are existing call uses of the old function in the
1108+ // / module, this adjusts them to call the new function directly.
1109+ // /
1110+ // / This is not just a cleanup: the always_inline pass requires direct calls to
1111+ // / functions to be able to inline them. If there is a bitcast in the way, it
1112+ // / won't inline them. Instcombine normally deletes these calls, but it isn't
1113+ // / run at -O0.
1114+ void CIRGenModule::replaceUsesOfNonProtoTypeWithRealFunction (
1115+ mlir::Operation *old, cir::FuncOp newFn) {
1116+ // If we're redefining a global as a function, don't transform it.
1117+ auto oldFn = mlir::dyn_cast<cir::FuncOp>(old);
1118+ if (!oldFn)
1119+ return ;
1120+
1121+ // TODO(cir): this RAUW ignores the features below.
1122+ assert (!cir::MissingFeatures::opFuncExceptions ());
1123+ assert (!cir::MissingFeatures::opFuncParameterAttributes ());
1124+ assert (!cir::MissingFeatures::opFuncOperandBundles ());
1125+ if (oldFn->getAttrs ().size () <= 1 )
1126+ errorNYI (old->getLoc (),
1127+ " replaceUsesOfNonProtoTypeWithRealFunction: Attribute forwarding" );
1128+
1129+ // Mark new function as originated from a no-proto declaration.
1130+ newFn.setNoProto (oldFn.getNoProto ());
1131+
1132+ // Iterate through all calls of the no-proto function.
1133+ std::optional<mlir::SymbolTable::UseRange> symUses =
1134+ oldFn.getSymbolUses (oldFn->getParentOp ());
1135+ for (const mlir::SymbolTable::SymbolUse &use : symUses.value ()) {
1136+ mlir::OpBuilder::InsertionGuard guard (builder);
1137+
1138+ if (auto noProtoCallOp = mlir::dyn_cast<cir::CallOp>(use.getUser ())) {
1139+ builder.setInsertionPoint (noProtoCallOp);
1140+
1141+ // Patch call type with the real function type.
1142+ cir::CallOp realCallOp = builder.createCallOp (
1143+ noProtoCallOp.getLoc (), newFn, noProtoCallOp.getOperands ());
1144+
1145+ // Replace old no proto call with fixed call.
1146+ noProtoCallOp.replaceAllUsesWith (realCallOp);
1147+ noProtoCallOp.erase ();
1148+ } else if (auto getGlobalOp =
1149+ mlir::dyn_cast<cir::GetGlobalOp>(use.getUser ())) {
1150+ // Replace type
1151+ getGlobalOp.getAddr ().setType (
1152+ cir::PointerType::get (newFn.getFunctionType ()));
1153+ } else {
1154+ errorNYI (use.getUser ()->getLoc (),
1155+ " replaceUsesOfNonProtoTypeWithRealFunction: unexpected use" );
1156+ }
1157+ }
1158+ }
1159+
11061160cir::GlobalLinkageKind
11071161CIRGenModule::getCIRLinkageVarDefinition (const VarDecl *vd, bool isConstant) {
11081162 assert (!isConstant && " constant variables NYI" );
@@ -1701,8 +1755,7 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
17011755 // Lookup the entry, lazily creating it if necessary.
17021756 mlir::Operation *entry = getGlobalValue (mangledName);
17031757 if (entry) {
1704- if (!isa<cir::FuncOp>(entry))
1705- errorNYI (d->getSourceRange (), " getOrCreateCIRFunction: non-FuncOp" );
1758+ assert (mlir::isa<cir::FuncOp>(entry));
17061759
17071760 assert (!cir::MissingFeatures::weakRefReference ());
17081761
@@ -1738,6 +1791,30 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
17381791 invalidLoc ? theModule->getLoc () : getLoc (funcDecl->getSourceRange ()),
17391792 mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
17401793
1794+ // If we already created a function with the same mangled name (but different
1795+ // type) before, take its name and add it to the list of functions to be
1796+ // replaced with F at the end of CodeGen.
1797+ //
1798+ // This happens if there is a prototype for a function (e.g. "int f()") and
1799+ // then a definition of a different type (e.g. "int f(int x)").
1800+ if (entry) {
1801+
1802+ // Fetch a generic symbol-defining operation and its uses.
1803+ auto symbolOp = mlir::cast<mlir::SymbolOpInterface>(entry);
1804+
1805+ // This might be an implementation of a function without a prototype, in
1806+ // which case, try to do special replacement of calls which match the new
1807+ // prototype. The really key thing here is that we also potentially drop
1808+ // arguments from the call site so as to make a direct call, which makes the
1809+ // inliner happier and suppresses a number of optimizer warnings (!) about
1810+ // dropping arguments.
1811+ if (symbolOp.getSymbolUses (symbolOp->getParentOp ()))
1812+ replaceUsesOfNonProtoTypeWithRealFunction (entry, funcOp);
1813+
1814+ // Obliterate no-proto declaration.
1815+ entry->erase ();
1816+ }
1817+
17411818 if (d)
17421819 setFunctionAttributes (gd, funcOp, /* isIncompleteFunction=*/ false , isThunk);
17431820
@@ -1814,7 +1891,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
18141891 func = builder.create <cir::FuncOp>(loc, name, funcType);
18151892
18161893 assert (!cir::MissingFeatures::opFuncAstDeclAttr ());
1817- assert (!cir::MissingFeatures::opFuncNoProto ());
1894+
1895+ if (funcDecl && !funcDecl->hasPrototype ())
1896+ func.setNoProto (true );
18181897
18191898 assert (func.isDeclaration () && " expected empty body" );
18201899
0 commit comments