Skip to content

Commit 3e9fd49

Browse files
authored
[CIR] Add support for constructor aliases (#145792)
This change adds support for handling the -mconstructor-aliases option in CIR. Aliases are not yet correctly lowered to LLVM IR. That will be implemented in a future change.
1 parent 2557f99 commit 3e9fd49

File tree

9 files changed

+327
-9
lines changed

9 files changed

+327
-9
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1772,7 +1772,8 @@ def FuncOp : CIR_Op<"func", [
17721772
OptionalAttr<StrAttr>:$sym_visibility,
17731773
UnitAttr:$comdat,
17741774
OptionalAttr<DictArrayAttr>:$arg_attrs,
1775-
OptionalAttr<DictArrayAttr>:$res_attrs);
1775+
OptionalAttr<DictArrayAttr>:$res_attrs,
1776+
OptionalAttr<FlatSymbolRefAttr>:$aliasee);
17761777

17771778
let regions = (region AnyRegion:$body);
17781779

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ struct MissingFeatures {
8181
static bool opFuncMultipleReturnVals() { return false; }
8282
static bool opFuncAttributesForDefinition() { return false; }
8383
static bool opFuncMaybeHandleStaticInExternC() { return false; }
84-
static bool opFuncGlobalAliases() { return false; }
8584
static bool setLLVMFunctionFEnvAttributes() { return false; }
8685
static bool setFunctionAttributes() { return false; }
8786

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,102 @@ void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc,
7979
}
8080
}
8181

82+
// Find out how to cirgen the complete destructor and constructor
83+
namespace {
84+
enum class StructorCIRGen { Emit, RAUW, Alias, COMDAT };
85+
}
86+
87+
static StructorCIRGen getCIRGenToUse(CIRGenModule &cgm,
88+
const CXXMethodDecl *md) {
89+
if (!cgm.getCodeGenOpts().CXXCtorDtorAliases)
90+
return StructorCIRGen::Emit;
91+
92+
// The complete and base structors are not equivalent if there are any virtual
93+
// bases, so emit separate functions.
94+
if (md->getParent()->getNumVBases()) {
95+
// The return value is correct here, but other support for this is NYI.
96+
cgm.errorNYI(md->getSourceRange(), "getCIRGenToUse: virtual bases");
97+
return StructorCIRGen::Emit;
98+
}
99+
100+
GlobalDecl aliasDecl;
101+
if (const auto *dd = dyn_cast<CXXDestructorDecl>(md)) {
102+
// The assignment is correct here, but other support for this is NYI.
103+
cgm.errorNYI(md->getSourceRange(), "getCIRGenToUse: dtor");
104+
aliasDecl = GlobalDecl(dd, Dtor_Complete);
105+
} else {
106+
const auto *cd = cast<CXXConstructorDecl>(md);
107+
aliasDecl = GlobalDecl(cd, Ctor_Complete);
108+
}
109+
110+
cir::GlobalLinkageKind linkage = cgm.getFunctionLinkage(aliasDecl);
111+
112+
if (cir::isDiscardableIfUnused(linkage))
113+
return StructorCIRGen::RAUW;
114+
115+
// FIXME: Should we allow available_externally aliases?
116+
if (!cir::isValidLinkage(linkage))
117+
return StructorCIRGen::RAUW;
118+
119+
if (cir::isWeakForLinker(linkage)) {
120+
// Only ELF and wasm support COMDATs with arbitrary names (C5/D5).
121+
if (cgm.getTarget().getTriple().isOSBinFormatELF() ||
122+
cgm.getTarget().getTriple().isOSBinFormatWasm())
123+
return StructorCIRGen::COMDAT;
124+
return StructorCIRGen::Emit;
125+
}
126+
127+
return StructorCIRGen::Alias;
128+
}
129+
130+
static void emitConstructorDestructorAlias(CIRGenModule &cgm,
131+
GlobalDecl aliasDecl,
132+
GlobalDecl targetDecl) {
133+
cir::GlobalLinkageKind linkage = cgm.getFunctionLinkage(aliasDecl);
134+
135+
// Does this function alias already exists?
136+
StringRef mangledName = cgm.getMangledName(aliasDecl);
137+
auto globalValue = dyn_cast_or_null<cir::CIRGlobalValueInterface>(
138+
cgm.getGlobalValue(mangledName));
139+
if (globalValue && !globalValue.isDeclaration())
140+
return;
141+
142+
auto entry = cast_or_null<cir::FuncOp>(cgm.getGlobalValue(mangledName));
143+
144+
// Retrieve aliasee info.
145+
auto aliasee = cast<cir::FuncOp>(cgm.getAddrOfGlobal(targetDecl));
146+
147+
// Populate actual alias.
148+
cgm.emitAliasForGlobal(mangledName, entry, aliasDecl, aliasee, linkage);
149+
}
150+
82151
void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) {
83152
auto *md = cast<CXXMethodDecl>(gd.getDecl());
84153
auto *cd = dyn_cast<CXXConstructorDecl>(md);
85154

155+
StructorCIRGen cirGenType = getCIRGenToUse(cgm, md);
156+
86157
if (!cd) {
87158
cgm.errorNYI(md->getSourceRange(), "CXCABI emit destructor");
88159
return;
89160
}
90161

91-
if (cgm.getCodeGenOpts().CXXCtorDtorAliases)
92-
cgm.errorNYI(md->getSourceRange(), "Ctor/Dtor aliases");
162+
if (gd.getCtorType() == Ctor_Complete) {
163+
GlobalDecl baseDecl = gd.getWithCtorType(Ctor_Base);
164+
165+
if (cirGenType == StructorCIRGen::Alias ||
166+
cirGenType == StructorCIRGen::COMDAT) {
167+
emitConstructorDestructorAlias(cgm, gd, baseDecl);
168+
return;
169+
}
170+
171+
if (cirGenType == StructorCIRGen::RAUW) {
172+
StringRef mangledName = cgm.getMangledName(gd);
173+
mlir::Operation *aliasee = cgm.getAddrOfGlobal(baseDecl);
174+
cgm.addReplacement(mangledName, aliasee);
175+
return;
176+
}
177+
}
93178

94179
auto fn = cgm.codegenCXXStructor(gd);
95180

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,65 @@ void CIRGenModule::updateCompletedType(const TagDecl *td) {
888888
genTypes.updateCompletedType(td);
889889
}
890890

891+
void CIRGenModule::addReplacement(StringRef name, mlir::Operation *op) {
892+
replacements[name] = op;
893+
}
894+
895+
void CIRGenModule::replacePointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF) {
896+
std::optional<mlir::SymbolTable::UseRange> optionalUseRange =
897+
oldF.getSymbolUses(theModule);
898+
if (!optionalUseRange)
899+
return;
900+
901+
for (const mlir::SymbolTable::SymbolUse &u : *optionalUseRange) {
902+
// CallTryOp only shows up after FlattenCFG.
903+
auto call = mlir::dyn_cast<cir::CallOp>(u.getUser());
904+
if (!call)
905+
continue;
906+
907+
for (const auto [argOp, fnArgType] :
908+
llvm::zip(call.getArgs(), newF.getFunctionType().getInputs())) {
909+
if (argOp.getType() == fnArgType)
910+
continue;
911+
912+
// The purpose of this entire function is to insert bitcasts in the case
913+
// where these types don't match, but I haven't seen a case where that
914+
// happens.
915+
errorNYI(call.getLoc(), "replace call with mismatched types");
916+
}
917+
}
918+
}
919+
920+
void CIRGenModule::applyReplacements() {
921+
for (auto &i : replacements) {
922+
StringRef mangledName = i.first();
923+
mlir::Operation *replacement = i.second;
924+
mlir::Operation *entry = getGlobalValue(mangledName);
925+
if (!entry)
926+
continue;
927+
assert(isa<cir::FuncOp>(entry) && "expected function");
928+
auto oldF = cast<cir::FuncOp>(entry);
929+
auto newF = dyn_cast<cir::FuncOp>(replacement);
930+
if (!newF) {
931+
// In classic codegen, this can be a global alias, a bitcast, or a GEP.
932+
errorNYI(replacement->getLoc(), "replacement is not a function");
933+
continue;
934+
}
935+
936+
// LLVM has opaque pointer but CIR not. So we may have to handle these
937+
// different pointer types when performing replacement.
938+
replacePointerTypeArgs(oldF, newF);
939+
940+
// Replace old with new, but keep the old order.
941+
if (oldF.replaceAllSymbolUses(newF.getSymNameAttr(), theModule).failed())
942+
llvm_unreachable("internal error, cannot RAUW symbol");
943+
if (newF) {
944+
newF->moveBefore(oldF);
945+
oldF->erase();
946+
}
947+
}
948+
}
949+
891950
// TODO(CIR): this could be a common method between LLVM codegen.
892951
static bool isVarDeclStrongDefinition(const ASTContext &astContext,
893952
CIRGenModule &cgm, const VarDecl *vd,
@@ -1797,11 +1856,52 @@ CIRGenModule::getGlobalVisibilityAttrFromDecl(const Decl *decl) {
17971856

17981857
void CIRGenModule::release() {
17991858
emitDeferred();
1859+
applyReplacements();
18001860

18011861
// There's a lot of code that is not implemented yet.
18021862
assert(!cir::MissingFeatures::cgmRelease());
18031863
}
18041864

1865+
void CIRGenModule::emitAliasForGlobal(StringRef mangledName,
1866+
mlir::Operation *op, GlobalDecl aliasGD,
1867+
cir::FuncOp aliasee,
1868+
cir::GlobalLinkageKind linkage) {
1869+
1870+
auto *aliasFD = dyn_cast<FunctionDecl>(aliasGD.getDecl());
1871+
assert(aliasFD && "expected FunctionDecl");
1872+
1873+
// The aliasee function type is different from the alias one, this difference
1874+
// is specific to CIR because in LLVM the ptr types are already erased at this
1875+
// point.
1876+
const CIRGenFunctionInfo &fnInfo =
1877+
getTypes().arrangeCXXStructorDeclaration(aliasGD);
1878+
cir::FuncType fnType = getTypes().getFunctionType(fnInfo);
1879+
1880+
cir::FuncOp alias =
1881+
createCIRFunction(getLoc(aliasGD.getDecl()->getSourceRange()),
1882+
mangledName, fnType, aliasFD);
1883+
alias.setAliasee(aliasee.getName());
1884+
alias.setLinkage(linkage);
1885+
// Declarations cannot have public MLIR visibility, just mark them private
1886+
// but this really should have no meaning since CIR should not be using
1887+
// this information to derive linkage information.
1888+
mlir::SymbolTable::setSymbolVisibility(
1889+
alias, mlir::SymbolTable::Visibility::Private);
1890+
1891+
// Alias constructors and destructors are always unnamed_addr.
1892+
assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
1893+
1894+
// Switch any previous uses to the alias.
1895+
if (op) {
1896+
errorNYI(aliasFD->getSourceRange(), "emitAliasForGlobal: previous uses");
1897+
} else {
1898+
// Name already set by createCIRFunction
1899+
}
1900+
1901+
// Finally, set up the alias with its proper name and attributes.
1902+
setCommonAttributes(aliasGD, alias);
1903+
}
1904+
18051905
mlir::Type CIRGenModule::convertType(QualType type) {
18061906
return genTypes.convertType(type);
18071907
}

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ class CIRGenModule : public CIRGenTypeCache {
256256
/// declarations are emitted lazily.
257257
void emitGlobal(clang::GlobalDecl gd);
258258

259+
void emitAliasForGlobal(llvm::StringRef mangledName, mlir::Operation *op,
260+
GlobalDecl aliasGD, cir::FuncOp aliasee,
261+
cir::GlobalLinkageKind linkage);
262+
259263
mlir::Type convertType(clang::QualType type);
260264

261265
/// Set the visibility for the given global.
@@ -358,6 +362,8 @@ class CIRGenModule : public CIRGenTypeCache {
358362
cir::GlobalLinkageKind getCIRLinkageVarDefinition(const VarDecl *vd,
359363
bool isConstant);
360364

365+
void addReplacement(llvm::StringRef name, mlir::Operation *op);
366+
361367
/// Helpers to emit "not yet implemented" error diagnostics
362368
DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef);
363369

@@ -397,6 +403,17 @@ class CIRGenModule : public CIRGenTypeCache {
397403
llvm::MapVector<clang::GlobalDecl, llvm::StringRef> mangledDeclNames;
398404
llvm::StringMap<clang::GlobalDecl, llvm::BumpPtrAllocator> manglings;
399405

406+
// FIXME: should we use llvm::TrackingVH<mlir::Operation> here?
407+
typedef llvm::StringMap<mlir::Operation *> ReplacementsTy;
408+
ReplacementsTy replacements;
409+
/// Call replaceAllUsesWith on all pairs in replacements.
410+
void applyReplacements();
411+
412+
/// A helper function to replace all uses of OldF to NewF that replace
413+
/// the type of pointer arguments. This is not needed to tradtional
414+
/// pipeline since LLVM has opaque pointers but CIR not.
415+
void replacePointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF);
416+
400417
void setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op);
401418
};
402419
} // namespace CIRGen

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,11 +1403,27 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
14031403
state.addAttribute(getFunctionTypeAttrName(state.name),
14041404
TypeAttr::get(fnType));
14051405

1406+
bool hasAlias = false;
1407+
mlir::StringAttr aliaseeNameAttr = getAliaseeAttrName(state.name);
1408+
if (parser.parseOptionalKeyword("alias").succeeded()) {
1409+
if (parser.parseLParen().failed())
1410+
return failure();
1411+
mlir::StringAttr aliaseeAttr;
1412+
if (parser.parseOptionalSymbolName(aliaseeAttr).failed())
1413+
return failure();
1414+
state.addAttribute(aliaseeNameAttr, FlatSymbolRefAttr::get(aliaseeAttr));
1415+
if (parser.parseRParen().failed())
1416+
return failure();
1417+
hasAlias = true;
1418+
}
1419+
14061420
// Parse the optional function body.
14071421
auto *body = state.addRegion();
14081422
OptionalParseResult parseResult = parser.parseOptionalRegion(
14091423
*body, arguments, /*enableNameShadowing=*/false);
14101424
if (parseResult.has_value()) {
1425+
if (hasAlias)
1426+
return parser.emitError(loc, "function alias shall not have a body");
14111427
if (failed(*parseResult))
14121428
return failure();
14131429
// Function body was parsed, make sure its not empty.
@@ -1419,13 +1435,17 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
14191435
}
14201436

14211437
// This function corresponds to `llvm::GlobalValue::isDeclaration` and should
1422-
// have a similar implementation. We don't currently support aliases, ifuncs,
1423-
// or materializable functions, but those should be handled here as they are
1424-
// implemented.
1438+
// have a similar implementation. We don't currently ifuncs or materializable
1439+
// functions, but those should be handled here as they are implemented.
14251440
bool cir::FuncOp::isDeclaration() {
1426-
assert(!cir::MissingFeatures::opFuncGlobalAliases());
14271441
assert(!cir::MissingFeatures::supportIFuncAttr());
1428-
return getFunctionBody().empty();
1442+
1443+
std::optional<StringRef> aliasee = getAliasee();
1444+
if (!aliasee)
1445+
return getFunctionBody().empty();
1446+
1447+
// Aliases are always definitions.
1448+
return false;
14291449
}
14301450

14311451
mlir::Region *cir::FuncOp::getCallableRegion() {
@@ -1460,6 +1480,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
14601480
function_interface_impl::printFunctionSignature(
14611481
p, *this, fnType.getInputs(), fnType.isVarArg(), fnType.getReturnTypes());
14621482

1483+
if (std::optional<StringRef> aliaseeName = getAliasee()) {
1484+
p << " alias(";
1485+
p.printSymbolName(*aliaseeName);
1486+
p << ")";
1487+
}
1488+
14631489
// Print the body if this is not an external function.
14641490
Region &body = getOperation()->getRegion(0);
14651491
if (!body.empty()) {

0 commit comments

Comments
 (0)