-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[CIR] Add support for constructor aliases #145792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
andykaylor
wants to merge
1
commit into
llvm:main
Choose a base branch
from
andykaylor:cir-constructor-aliases
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
+300
−9
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis 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. Full diff: https://github.com/llvm/llvm-project/pull/145792.diff 7 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ef77c46b011f7..7e45fa464f9d4 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1772,7 +1772,8 @@ def FuncOp : CIR_Op<"func", [
OptionalAttr<StrAttr>:$sym_visibility,
UnitAttr:$comdat,
OptionalAttr<DictArrayAttr>:$arg_attrs,
- OptionalAttr<DictArrayAttr>:$res_attrs);
+ OptionalAttr<DictArrayAttr>:$res_attrs,
+ OptionalAttr<FlatSymbolRefAttr>:$aliasee);
let regions = (region AnyRegion:$body);
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 9e8944d1114b8..7009d6d7d702a 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -81,7 +81,6 @@ struct MissingFeatures {
static bool opFuncMultipleReturnVals() { return false; }
static bool opFuncAttributesForDefinition() { return false; }
static bool opFuncMaybeHandleStaticInExternC() { return false; }
- static bool opFuncGlobalAliases() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
static bool setFunctionAttributes() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index cd9096a0188a7..1044dfe507471 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -79,17 +79,102 @@ void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc,
}
}
+// Find out how to cirgen the complete destructor and constructor
+namespace {
+enum class StructorCIRGen { Emit, RAUW, Alias, COMDAT };
+}
+
+static StructorCIRGen getCIRGenToUse(CIRGenModule &cgm,
+ const CXXMethodDecl *md) {
+ if (!cgm.getCodeGenOpts().CXXCtorDtorAliases)
+ return StructorCIRGen::Emit;
+
+ // The complete and base structors are not equivalent if there are any virtual
+ // bases, so emit separate functions.
+ if (md->getParent()->getNumVBases()) {
+ // The return value is correct here, but other support for this is NYI.
+ cgm.errorNYI(md->getSourceRange(), "getCIRGenToUse: virtual bases");
+ return StructorCIRGen::Emit;
+ }
+
+ GlobalDecl aliasDecl;
+ if (const auto *dd = dyn_cast<CXXDestructorDecl>(md)) {
+ // The assignment is correct here, but other support for this is NYI.
+ cgm.errorNYI(md->getSourceRange(), "getCIRGenToUse: dtor");
+ aliasDecl = GlobalDecl(dd, Dtor_Complete);
+ } else {
+ const auto *cd = cast<CXXConstructorDecl>(md);
+ aliasDecl = GlobalDecl(cd, Ctor_Complete);
+ }
+
+ cir::GlobalLinkageKind linkage = cgm.getFunctionLinkage(aliasDecl);
+
+ if (cir::isDiscardableIfUnused(linkage))
+ return StructorCIRGen::RAUW;
+
+ // FIXME: Should we allow available_externally aliases?
+ if (!cir::isValidLinkage(linkage))
+ return StructorCIRGen::RAUW;
+
+ if (cir::isWeakForLinker(linkage)) {
+ // Only ELF and wasm support COMDATs with arbitrary names (C5/D5).
+ if (cgm.getTarget().getTriple().isOSBinFormatELF() ||
+ cgm.getTarget().getTriple().isOSBinFormatWasm())
+ return StructorCIRGen::COMDAT;
+ return StructorCIRGen::Emit;
+ }
+
+ return StructorCIRGen::Alias;
+}
+
+static void emitConstructorDestructorAlias(CIRGenModule &cgm,
+ GlobalDecl aliasDecl,
+ GlobalDecl targetDecl) {
+ cir::GlobalLinkageKind linkage = cgm.getFunctionLinkage(aliasDecl);
+
+ // Does this function alias already exists?
+ StringRef mangledName = cgm.getMangledName(aliasDecl);
+ auto globalValue = dyn_cast_or_null<cir::CIRGlobalValueInterface>(
+ cgm.getGlobalValue(mangledName));
+ if (globalValue && !globalValue.isDeclaration())
+ return;
+
+ auto entry = cast_or_null<cir::FuncOp>(cgm.getGlobalValue(mangledName));
+
+ // Retrieve aliasee info.
+ auto aliasee = cast<cir::FuncOp>(cgm.getAddrOfGlobal(targetDecl));
+
+ // Populate actual alias.
+ cgm.emitAliasForGlobal(mangledName, entry, aliasDecl, aliasee, linkage);
+}
+
void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) {
auto *md = cast<CXXMethodDecl>(gd.getDecl());
auto *cd = dyn_cast<CXXConstructorDecl>(md);
+ StructorCIRGen cirGenType = getCIRGenToUse(cgm, md);
+
if (!cd) {
cgm.errorNYI(md->getSourceRange(), "CXCABI emit destructor");
return;
}
- if (cgm.getCodeGenOpts().CXXCtorDtorAliases)
- cgm.errorNYI(md->getSourceRange(), "Ctor/Dtor aliases");
+ if (gd.getCtorType() == Ctor_Complete) {
+ GlobalDecl baseDecl = gd.getWithCtorType(Ctor_Base);
+
+ if (cirGenType == StructorCIRGen::Alias ||
+ cirGenType == StructorCIRGen::COMDAT) {
+ emitConstructorDestructorAlias(cgm, gd, baseDecl);
+ return;
+ }
+
+ if (cirGenType == StructorCIRGen::RAUW) {
+ StringRef mangledName = cgm.getMangledName(gd);
+ mlir::Operation *aliasee = cgm.getAddrOfGlobal(baseDecl);
+ cgm.addReplacement(mangledName, aliasee);
+ return;
+ }
+ }
auto fn = cgm.codegenCXXStructor(gd);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index f24bee44f26a7..1271f290d6193 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -888,6 +888,69 @@ void CIRGenModule::updateCompletedType(const TagDecl *td) {
genTypes.updateCompletedType(td);
}
+void CIRGenModule::addReplacement(StringRef name, mlir::Operation *op) {
+ replacements[name] = op;
+}
+
+void CIRGenModule::replacePointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF) {
+ std::optional<mlir::SymbolTable::UseRange> optionalUseRange =
+ oldF.getSymbolUses(theModule);
+ if (!optionalUseRange)
+ return;
+
+ for (const mlir::SymbolTable::SymbolUse &u : *optionalUseRange) {
+ // CallTryOp only shows up after FlattenCFG.
+ auto call = mlir::dyn_cast<cir::CallOp>(u.getUser());
+ if (!call)
+ continue;
+
+ mlir::OperandRange argOps = call.getArgs();
+ mlir::ArrayRef<mlir::Type> funcArgTypes =
+ newF.getFunctionType().getInputs();
+ // In the case of variadic functions, the call may have more arguments that
+ // the function type, so we can't use llvm::enumerate here.
+ for (unsigned i = 0; i < funcArgTypes.size(); i++) {
+ if (argOps[i].getType() == funcArgTypes[i])
+ continue;
+
+ // The purpose of this entire function is to insert bitcasts in the case
+ // where these types don't match, but I haven't seen a case where that
+ // happens.
+ errorNYI(call.getLoc(), "replace call with mismatched types");
+ }
+ }
+}
+
+void CIRGenModule::applyReplacements() {
+ for (auto &i : replacements) {
+ StringRef mangledName = i.first();
+ mlir::Operation *replacement = i.second;
+ mlir::Operation *entry = getGlobalValue(mangledName);
+ if (!entry)
+ continue;
+ assert(isa<cir::FuncOp>(entry) && "expected function");
+ auto oldF = cast<cir::FuncOp>(entry);
+ auto newF = dyn_cast<cir::FuncOp>(replacement);
+ if (!newF) {
+ // In classic codegen, this can be a global alias, a bitcast, or a GEP.
+ errorNYI(replacement->getLoc(), "replacement is not a function");
+ continue;
+ }
+
+ // LLVM has opaque pointer but CIR not. So we may have to handle these
+ // different pointer types when performing replacement.
+ replacePointerTypeArgs(oldF, newF);
+
+ // Replace old with new, but keep the old order.
+ if (oldF.replaceAllSymbolUses(newF.getSymNameAttr(), theModule).failed())
+ llvm_unreachable("internal error, cannot RAUW symbol");
+ if (newF) {
+ newF->moveBefore(oldF);
+ oldF->erase();
+ }
+ }
+}
+
// TODO(CIR): this could be a common method between LLVM codegen.
static bool isVarDeclStrongDefinition(const ASTContext &astContext,
CIRGenModule &cgm, const VarDecl *vd,
@@ -1797,11 +1860,52 @@ CIRGenModule::getGlobalVisibilityAttrFromDecl(const Decl *decl) {
void CIRGenModule::release() {
emitDeferred();
+ applyReplacements();
// There's a lot of code that is not implemented yet.
assert(!cir::MissingFeatures::cgmRelease());
}
+void CIRGenModule::emitAliasForGlobal(StringRef mangledName,
+ mlir::Operation *op, GlobalDecl aliasGD,
+ cir::FuncOp aliasee,
+ cir::GlobalLinkageKind linkage) {
+
+ auto *aliasFD = dyn_cast<FunctionDecl>(aliasGD.getDecl());
+ assert(aliasFD && "expected FunctionDecl");
+
+ // The aliasee function type is different from the alias one, this difference
+ // is specific to CIR because in LLVM the ptr types are already erased at this
+ // point.
+ const CIRGenFunctionInfo &fnInfo =
+ getTypes().arrangeCXXStructorDeclaration(aliasGD);
+ cir::FuncType fnType = getTypes().getFunctionType(fnInfo);
+
+ cir::FuncOp alias =
+ createCIRFunction(getLoc(aliasGD.getDecl()->getSourceRange()),
+ mangledName, fnType, aliasFD);
+ alias.setAliasee(aliasee.getName());
+ alias.setLinkage(linkage);
+ // Declarations cannot have public MLIR visibility, just mark them private
+ // but this really should have no meaning since CIR should not be using
+ // this information to derive linkage information.
+ mlir::SymbolTable::setSymbolVisibility(
+ alias, mlir::SymbolTable::Visibility::Private);
+
+ // Alias constructors and destructors are always unnamed_addr.
+ assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
+
+ // Switch any previous uses to the alias.
+ if (op) {
+ errorNYI(aliasFD->getSourceRange(), "emitAliasForGlobal: previous uses");
+ } else {
+ // Name already set by createCIRFunction
+ }
+
+ // Finally, set up the alias with its proper name and attributes.
+ setCommonAttributes(aliasGD, alias);
+}
+
mlir::Type CIRGenModule::convertType(QualType type) {
return genTypes.convertType(type);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 9f6a57c31d291..5a1eb9dea35c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -256,6 +256,10 @@ class CIRGenModule : public CIRGenTypeCache {
/// declarations are emitted lazily.
void emitGlobal(clang::GlobalDecl gd);
+ void emitAliasForGlobal(llvm::StringRef mangledName, mlir::Operation *op,
+ GlobalDecl aliasGD, cir::FuncOp aliasee,
+ cir::GlobalLinkageKind linkage);
+
mlir::Type convertType(clang::QualType type);
/// Set the visibility for the given global.
@@ -358,6 +362,8 @@ class CIRGenModule : public CIRGenTypeCache {
cir::GlobalLinkageKind getCIRLinkageVarDefinition(const VarDecl *vd,
bool isConstant);
+ void addReplacement(llvm::StringRef name, mlir::Operation *op);
+
/// Helpers to emit "not yet implemented" error diagnostics
DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef);
@@ -397,6 +403,17 @@ class CIRGenModule : public CIRGenTypeCache {
llvm::MapVector<clang::GlobalDecl, llvm::StringRef> mangledDeclNames;
llvm::StringMap<clang::GlobalDecl, llvm::BumpPtrAllocator> manglings;
+ // FIXME: should we use llvm::TrackingVH<mlir::Operation> here?
+ typedef llvm::StringMap<mlir::Operation *> ReplacementsTy;
+ ReplacementsTy replacements;
+ /// Call replaceAllUsesWith on all pairs in replacements.
+ void applyReplacements();
+
+ /// A helper function to replace all uses of OldF to NewF that replace
+ /// the type of pointer arguments. This is not needed to tradtional
+ /// pipeline since LLVM has opaque pointers but CIR not.
+ void replacePointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF);
+
void setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op);
};
} // namespace CIRGen
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 17157561357f9..d14fb6af5a988 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1419,13 +1419,17 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
}
// This function corresponds to `llvm::GlobalValue::isDeclaration` and should
-// have a similar implementation. We don't currently support aliases, ifuncs,
-// or materializable functions, but those should be handled here as they are
-// implemented.
+// have a similar implementation. We don't currently ifuncs or materializable
+// functions, but those should be handled here as they are implemented.
bool cir::FuncOp::isDeclaration() {
- assert(!cir::MissingFeatures::opFuncGlobalAliases());
assert(!cir::MissingFeatures::supportIFuncAttr());
- return getFunctionBody().empty();
+
+ std::optional<StringRef> aliasee = getAliasee();
+ if (!aliasee)
+ return getFunctionBody().empty();
+
+ // Aliases are always definitions.
+ return false;
}
mlir::Region *cir::FuncOp::getCallableRegion() {
@@ -1460,6 +1464,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
function_interface_impl::printFunctionSignature(
p, *this, fnType.getInputs(), fnType.isVarArg(), fnType.getReturnTypes());
+ if (std::optional<StringRef> aliaseeName = getAliasee()) {
+ p << " alias(";
+ p.printSymbolName(*aliaseeName);
+ p << ")";
+ }
+
// Print the body if this is not an external function.
Region &body = getOperation()->getRegion(0);
if (!body.empty()) {
diff --git a/clang/test/CIR/CodeGen/ctor-alias.cpp b/clang/test/CIR/CodeGen/ctor-alias.cpp
new file mode 100644
index 0000000000000..a20e206c6d714
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ctor-alias.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct B {
+ B();
+};
+B::B() {
+}
+
+// OGCG: @_ZN1BC1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BC2Ev
+
+// CHECK: cir.func{{.*}} @_ZN1BC2Ev(%arg0: !cir.ptr<!rec_B>
+// CHECK: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>, ["this", init]
+// CHECK: cir.store %arg0, %[[THIS_ADDR]]
+// CHECK: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
+
+// CHECK: cir.func{{.*}} private dso_local @_ZN1BC1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BC2Ev)
+
+// LLVM: define{{.*}} @_ZN1BC2Ev(ptr %[[THIS_ARG:.*]])
+// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+
+// This should be an alias, like the similar OGCG alias above, but that's not
+// implemented yet.
+// LLVM: declare dso_local void @_ZN1BC1Ev(ptr)
+
+// OGCG: define{{.*}} @_ZN1BC2Ev(ptr{{.*}} %[[THIS_ARG:.*]])
+// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+
+// The constructor in this cases is handled by RAUW rather than aliasing.
+struct Struk {
+ Struk() {}
+};
+
+void baz() {
+ Struk s;
+}
+
+// CHECK: cir.func{{.*}} @_ZN5StrukC2Ev(%arg0: !cir.ptr<!rec_Struk>
+// CHECK: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>>, ["this", init]
+// CHECK: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>>
+// CHECK: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_Struk>>, !cir.ptr<!rec_Struk>
+// CHECK: cir.return
+
+// CHECK-NOT: cir.func{{.*}} @_ZN5StrukC1Ev
+
+// CHECK: cir.func{{.*}} @_Z3bazv()
+// CHECK: %[[S_ADDR:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>, ["s", init]
+// CHECK: cir.call @_ZN5StrukC2Ev(%[[S_ADDR]]) : (!cir.ptr<!rec_Struk>) -> ()
+
+// LLVM: define linkonce_odr void @_ZN5StrukC2Ev(ptr %[[THIS_ARG]])
+// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+
+// LLVM: define{{.*}} void @_Z3bazv()
+// LLVM: %[[S_ADDR:.*]] = alloca %struct.Struk
+// LLVM: call void @_ZN5StrukC2Ev(ptr{{.*}} %[[S_ADDR]])
+
+// This function gets emitted before the constructor in OGCG.
+// OGCG: define{{.*}} void @_Z3bazv()
+// OGCG: %[[S_ADDR:.*]] = alloca %struct.Struk
+// OGCG: call void @_ZN5StrukC2Ev(ptr{{.*}} %[[S_ADDR]])
+
+// OGCG: define linkonce_odr void @_ZN5StrukC2Ev(ptr{{.*}} %[[THIS_ARG]])
+// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.