@@ -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.setNoProtoAttr (oldFn.getNoProtoAttr ());
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" );
@@ -1729,6 +1783,34 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
17291783 invalidLoc ? theModule->getLoc () : getLoc (funcDecl->getSourceRange ()),
17301784 mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
17311785
1786+ // If we already created a function with the same mangled name (but different
1787+ // type) before, take its name and add it to the list of functions to be
1788+ // replaced with F at the end of CodeGen.
1789+ //
1790+ // This happens if there is a prototype for a function (e.g. "int f()") and
1791+ // then a definition of a different type (e.g. "int f(int x)").
1792+ if (entry) {
1793+
1794+ // Fetch a generic symbol-defining operation and its uses.
1795+ auto symbolOp = mlir::cast<mlir::SymbolOpInterface>(entry);
1796+
1797+ // TODO(cir): When can this symbol be something other than a function?
1798+ if (!isa<cir::FuncOp>(entry))
1799+ errorNYI (d->getSourceRange (), " getOrCreateCIRFunction: non-FuncOp" );
1800+
1801+ // This might be an implementation of a function without a prototype, in
1802+ // which case, try to do special replacement of calls which match the new
1803+ // prototype. The really key thing here is that we also potentially drop
1804+ // arguments from the call site so as to make a direct call, which makes the
1805+ // inliner happier and suppresses a number of optimizer warnings (!) about
1806+ // dropping arguments.
1807+ if (symbolOp.getSymbolUses (symbolOp->getParentOp ()))
1808+ replaceUsesOfNonProtoTypeWithRealFunction (entry, funcOp);
1809+
1810+ // Obliterate no-proto declaration.
1811+ entry->erase ();
1812+ }
1813+
17321814 if (d)
17331815 setFunctionAttributes (gd, funcOp, /* isIncompleteFunction=*/ false , isThunk);
17341816
@@ -1805,7 +1887,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
18051887 func = builder.create <cir::FuncOp>(loc, name, funcType);
18061888
18071889 assert (!cir::MissingFeatures::opFuncAstDeclAttr ());
1808- assert (!cir::MissingFeatures::opFuncNoProto ());
1890+
1891+ if (funcDecl && !funcDecl->hasPrototype ())
1892+ func.setNoProtoAttr (builder.getUnitAttr ());
18091893
18101894 assert (func.isDeclaration () && " expected empty body" );
18111895
0 commit comments