@@ -1103,6 +1103,60 @@ cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator(
1103
1103
return cir::GlobalLinkageKind::ExternalLinkage;
1104
1104
}
1105
1105
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
+
1106
1160
cir::GlobalLinkageKind
1107
1161
CIRGenModule::getCIRLinkageVarDefinition (const VarDecl *vd, bool isConstant) {
1108
1162
assert (!isConstant && " constant variables NYI" );
@@ -1701,8 +1755,7 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
1701
1755
// Lookup the entry, lazily creating it if necessary.
1702
1756
mlir::Operation *entry = getGlobalValue (mangledName);
1703
1757
if (entry) {
1704
- if (!isa<cir::FuncOp>(entry))
1705
- errorNYI (d->getSourceRange (), " getOrCreateCIRFunction: non-FuncOp" );
1758
+ assert (mlir::isa<cir::FuncOp>(entry));
1706
1759
1707
1760
assert (!cir::MissingFeatures::weakRefReference ());
1708
1761
@@ -1738,6 +1791,30 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
1738
1791
invalidLoc ? theModule->getLoc () : getLoc (funcDecl->getSourceRange ()),
1739
1792
mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
1740
1793
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
+
1741
1818
if (d)
1742
1819
setFunctionAttributes (gd, funcOp, /* isIncompleteFunction=*/ false , isThunk);
1743
1820
@@ -1814,7 +1891,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
1814
1891
func = builder.create <cir::FuncOp>(loc, name, funcType);
1815
1892
1816
1893
assert (!cir::MissingFeatures::opFuncAstDeclAttr ());
1817
- assert (!cir::MissingFeatures::opFuncNoProto ());
1894
+
1895
+ if (funcDecl && !funcDecl->hasPrototype ())
1896
+ func.setNoProto (true );
1818
1897
1819
1898
assert (func.isDeclaration () && " expected empty body" );
1820
1899
0 commit comments