[clang] [CIR] Inheriting Constructor/inheriting ctor inlining lowering (PR #191467)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 10 10:02:19 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Erich Keane (erichkeane)
<details>
<summary>Changes</summary>
In cases with inheritance/vertual tables/etc, we need to generate a series of constructors to delegate to. There are a handful slightly different cases where we need to generate these/generate calls to these, so this patch does that lowering.
The test check-lines are a bit confusing thanks to the ordering differences between declarations. However the LLVM/OGCG lines are copy pasted (plus minor attribute differences), with the exception of the call to a delegated constructor.
One thing of note here: There is a difference in behavior with the delegated constructor, which is called out in the test in a comment. Classic codegen has a bug where it correctly creates the declaration without arguments (since this constructor is only for initializing the vtable pointers, arguments aren't necessary). However, when classic-codegen creates the call, it doesn't omit them.
This isn't a problem there, however in CIR, this causes us to fail the verifier, so this fixes that in CIR, but leaves it alone in OGCG.
---
Patch is 26.51 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/191467.diff
7 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenCall.cpp (+11-3)
- (modified) clang/lib/CIR/CodeGen/CIRGenClass.cpp (+130-6)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp (+5-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+12-6)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+66)
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+6)
- (added) clang/test/CIR/CodeGen/inherited-ctors.cpp (+182)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 876fef687b477..4dc24113f71b9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -736,9 +736,8 @@ CIRGenTypes::arrangeCXXStructorDeclaration(GlobalDecl gd) {
if (auto *cd = dyn_cast<CXXConstructorDecl>(md)) {
// A base class inheriting constructor doesn't get forwarded arguments
// needed to construct a virtual base (or base class thereof)
- if (cd->getInheritedConstructor())
- cgm.errorNYI(cd->getSourceRange(),
- "arrangeCXXStructorDeclaration: inheriting constructor");
+ if (auto inherited = cd->getInheritedConstructor())
+ passParams = inheritingCtorHasParams(inherited, gd.getCtorType());
}
CanQual<FunctionProtoType> fpt = getFormalType(md);
@@ -1427,3 +1426,12 @@ void CIRGenFunction::emitCallArgs(
std::reverse(args.begin() + callArgsStart, args.end());
}
}
+
+bool CIRGenTypes::inheritingCtorHasParams(
+ const InheritedConstructor &inherited, CXXCtorType type) {
+ // Parameters are unnecessary if we're constructing a base class subobject
+ // and the inherited constructor lives in a virtual base.
+ return type == Ctor_Complete ||
+ !inherited.getShadowDecl()->constructsVirtualBase() ||
+ !getASTContext().getTargetInfo().getCXXABI().hasConstructorVariants();
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 748d450d6dc10..bf0e28ae30ba5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -1344,14 +1344,134 @@ void CIRGenFunction::emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
assert(!cir::MissingFeatures::opCallArgEvaluationOrder());
- emitCallArgs(args, fpt, e->arguments(), e->getConstructor(),
- /*ParamsToSkip=*/0);
+ if (auto inherited = d->getInheritedConstructor();
+ !inherited || cgm.getTypes().inheritingCtorHasParams(inherited, type))
+ emitCallArgs(args, fpt, e->arguments(), e->getConstructor(),
+ /*ParamsToSkip=*/0);
assert(!cir::MissingFeatures::sanitizers());
emitCXXConstructorCall(d, type, forVirtualBase, delegating, thisAddr, args,
e->getExprLoc());
}
+static bool canEmitDelegateCallArgs(CIRGenModule &cgm, ASTContext &ctx,
+ const CXXConstructorDecl *d,
+ CXXCtorType type) {
+ // We can't forward a variadic call.
+ if (d->isVariadic())
+ return false;
+
+ if (ctx.getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
+ // If the parameters are callee-cleanup, it's not safe to forward.
+ if (llvm::any_of(d->parameters(), [&ctx](const ParmVarDecl *param) {
+ return param->needsDestruction(ctx);
+ }))
+ return false;
+
+ // FIXME(CIR): It isn't clear to me that this is the right answer here,
+ // classic-codegen decides the answer is 'false' if there is an inalloca
+ // argument. What our default should be in this case, or what we want to do.
+ // When we get an understanding of what the the calling-convention code
+ // needs here, we should be able to replace this with either a 'return
+ // false' or 'return true'.
+ cgm.errorNYI(d->getSourceRange(),
+ "canEmitDelegateCallArgs: args-destroyed-L-to-R in callee");
+ }
+
+ return true;
+}
+
+void CIRGenFunction::emitInheritedCXXConstructorCall(
+ const CXXConstructorDecl *d, bool forVirtualBase, Address thisAddr,
+ bool inheritedFromVBase, const CXXInheritedCtorInitExpr *e) {
+
+ CallArgList ctorArgs;
+ CallArg thisArg(RValue::get(getAsNaturalPointerTo(
+ thisAddr, d->getThisType()->getPointeeType())),
+ d->getThisType());
+
+ if (inheritedFromVBase &&
+ cgm.getTarget().getCXXABI().hasConstructorVariants()) {
+ cgm.errorNYI(
+ e->getSourceRange(),
+ "emitInheritedCXXConstructorCall inheritedFromVBasewith ctor variants");
+ return;
+ } else if (!cxxInheritedCtorInitExprArgs.empty()) {
+ // The inheriting constructor was inlined; just inject its arguments.
+ assert(cxxInheritedCtorInitExprArgs.size() >= d->getNumParams() &&
+ "wrong number of parameters for inherited constructor call");
+ ctorArgs = cxxInheritedCtorInitExprArgs;
+ ctorArgs[0] = thisArg;
+ } else {
+ ctorArgs.push_back(thisArg);
+ const auto *outerCtor = cast<CXXConstructorDecl>(curCodeDecl);
+ assert(outerCtor->getNumParams() == d->getNumParams());
+ assert(!outerCtor->isVariadic() && "should have been inlined");
+
+ for (const ParmVarDecl *param : outerCtor->parameters()) {
+ assert(getContext().hasSameUnqualifiedType(
+ outerCtor->getParamDecl(param->getFunctionScopeIndex())->getType(),
+ param->getType()));
+ emitDelegateCallArg(ctorArgs, param, e->getLocation());
+
+ if (param->hasAttr<PassObjectSizeAttr>())
+ cgm.errorNYI(
+ e->getLocation(),
+ "emitInheritedCXXConstructorCall: pass object size attr argument");
+ }
+ }
+
+ emitCXXConstructorCall(d, Ctor_Base, forVirtualBase, /*delegating=*/false,
+ thisAddr, ctorArgs, e->getLocation());
+}
+
+void CIRGenFunction::emitInlinedInheritingCXXConstructorCall(
+ SourceLocation loc, const CXXConstructorDecl *d, CXXCtorType ctorType,
+ bool forVirtualBase, bool delegating, CallArgList &args) {
+ GlobalDecl gd(d, ctorType);
+ assert(!cir::MissingFeatures::generateDebugInfo());
+ assert(!cir::MissingFeatures::runCleanupsScope());
+ InlinedInheritingConstructorScope scope(*this, gd);
+
+ // Save the arguments to be passed to the inherited constructor.
+ cxxInheritedCtorInitExprArgs = args;
+
+ FunctionArgList params;
+ QualType retTy = buildFunctionArgList(gd, params);
+
+ cgm.getCXXABI().addImplicitConstructorArgs(*this, d, ctorType, forVirtualBase,
+ delegating, args);
+
+ // Emit a simplified prolog. We only need to emit the implicit params.
+ assert(args.size() >= params.size() && "too few arguments for call");
+ for (auto [idx, arg, parm] :
+ llvm::zip_longest(llvm::index_range{0, args.size()}, args, params)) {
+ if (idx < params.size() && isa<ImplicitParamDecl>(*parm)) {
+ mlir::Location parmLoc = getLoc((*parm)->getSourceRange());
+ RValue argVal = arg->getRValue(*this, parmLoc);
+
+ LValue allocaVal = makeAddrLValue(
+ createTempAlloca(convertType((*parm)->getType()),
+ getContext().getDeclAlign(*parm), parmLoc),
+ (*parm)->getType());
+
+ emitStoreThroughLValue(argVal, allocaVal, /*isInit=*/true);
+
+ setAddrOfLocalVar((*parm), allocaVal.getAddress());
+ }
+ }
+
+ // FIXME(cir): it isn't clear what it takes to get here with a constructor?
+ // Leave as an NYI until we come across a reproducer.
+ if (!retTy->isVoidType())
+ cgm.errorNYI(d->getSourceRange(),
+ "emitInlinedInheritingCXXConstructorCall: non-void return");
+
+ cgm.getCXXABI().emitInstanceFunctionProlog(loc, *this);
+ cxxThisValue = cxxabiThisValue;
+ emitCtorPrologue(d, ctorType, params);
+}
+
void CIRGenFunction::emitCXXConstructorCall(
const CXXConstructorDecl *d, CXXCtorType type, bool forVirtualBase,
bool delegating, Address thisAddr, CallArgList &args, SourceLocation loc) {
@@ -1370,10 +1490,14 @@ void CIRGenFunction::emitCXXConstructorCall(
bool passPrototypeArgs = true;
// Check whether we can actually emit the constructor before trying to do so.
- if (d->getInheritedConstructor()) {
- cgm.errorNYI(d->getSourceRange(),
- "emitCXXConstructorCall: inherited constructor");
- return;
+ if (auto inherited = d->getInheritedConstructor()) {
+ passPrototypeArgs = getTypes().inheritingCtorHasParams(inherited, type);
+ if (passPrototypeArgs &&
+ !canEmitDelegateCallArgs(cgm, cgm.getASTContext(), d, type)) {
+ emitInlinedInheritingCXXConstructorCall(loc, d, type, forVirtualBase,
+ delegating, args);
+ return;
+ }
}
// Insert any ABI-specific implicit constructor arguments.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index ffce8a6bf86a7..21a6f9209bb17 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -501,8 +501,11 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
Visit(dae->getExpr());
}
void VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *e) {
- cgf.cgm.errorNYI(e->getSourceRange(),
- "AggExprEmitter: VisitCXXInheritedCtorInitExpr");
+ AggValueSlot slot =
+ ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
+ cgf.emitInheritedCXXConstructorCall(e->getConstructor(),
+ e->constructsVBase(), slot.getAddress(),
+ e->inheritedFromVBase(), e);
}
/// Emit the initializer for a std::initializer_list initialized with a
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index f7f9331060956..fcfbeb809371e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -990,13 +990,19 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
cgm.getCXXABI().buildThisParam(*this, args);
}
+ bool passedParams = true;
if (const auto *cd = dyn_cast<CXXConstructorDecl>(fd))
- if (cd->getInheritedConstructor())
- cgm.errorNYI(fd->getSourceRange(),
- "buildFunctionArgList: inherited constructor");
-
- for (auto *param : fd->parameters())
- args.push_back(param);
+ if (auto inherited = cd->getInheritedConstructor())
+ passedParams =
+ getTypes().inheritingCtorHasParams(inherited, gd.getCtorType());
+
+ if (passedParams) {
+ for (auto *param : fd->parameters()) {
+ args.push_back(param);
+ if (param->hasAttr<PassObjectSizeAttr>())
+ cgm.errorNYI(param->getSourceRange(), "pass-object-size attribute");
+ }
+ }
if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)))
cgm.getCXXABI().addImplicitStructorParams(*this, retTy, args);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 88c7996eab569..612fee560f8ee 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -137,11 +137,16 @@ class CIRGenFunction : public CIRGenTypeCache {
/// expression.
Address cxxDefaultInitExprThis = Address::invalid();
+ /// The values of function arguments to use when evaluating
+ /// CXXInheritedCtorInitExprs within this context.
+ CallArgList cxxInheritedCtorInitExprArgs;
+
// Holds the Decl for the current outermost non-closure context
const clang::Decl *curFuncDecl = nullptr;
/// This is the inner-most code context, which includes blocks.
const clang::Decl *curCodeDecl = nullptr;
const CIRGenFunctionInfo *curFnInfo = nullptr;
+ QualType fnRetTy;
/// The current function or global initializer that is generated code for.
/// This is usually a cir::FuncOp, but it can also be a cir::GlobalOp for
@@ -1564,6 +1569,15 @@ class CIRGenFunction : public CIRGenTypeCache {
bool delegating, Address thisAddr,
CallArgList &args, clang::SourceLocation loc);
+ void emitInheritedCXXConstructorCall(const CXXConstructorDecl *d,
+ bool forVirtualBase, Address thisAddr,
+ bool inheritedFromVBase,
+ const CXXInheritedCtorInitExpr *e);
+
+ void emitInlinedInheritingCXXConstructorCall(
+ SourceLocation loc, const CXXConstructorDecl *d, CXXCtorType ctorType,
+ bool forVirtualBase, bool delegating, CallArgList &args);
+
void emitCXXDeleteExpr(const CXXDeleteExpr *e);
void emitCXXDestructorCall(const CXXDestructorDecl *dd, CXXDtorType type,
@@ -2395,6 +2409,58 @@ class CIRGenFunction : public CIRGenTypeCache {
private:
QualType getVarArgType(const Expr *arg);
+
+ class InlinedInheritingConstructorScope {
+ public:
+ InlinedInheritingConstructorScope(CIRGenFunction &cgf, GlobalDecl gd)
+ : cgf(cgf), oldCurGD(cgf.curGD), oldCurFuncDecl(cgf.curFuncDecl),
+ oldCurCodeDecl(cgf.curCodeDecl),
+ oldCxxabiThisDecl(cgf.cxxabiThisDecl),
+ oldCxxThisValue(cgf.cxxThisValue),
+ oldCxxThisAlignment(cgf.cxxThisAlignment),
+ oldReturnValue(cgf.returnValue), oldFnRetTy(cgf.fnRetTy),
+ oldCxxInheritedCtorInitExprArgs(
+ std::move(cgf.cxxInheritedCtorInitExprArgs)) {
+ cgf.curGD = gd;
+ cgf.curFuncDecl = cast<CXXConstructorDecl>(gd.getDecl());
+ cgf.curCodeDecl = cgf.curFuncDecl;
+ cgf.cxxabiThisDecl = nullptr;
+ cgf.cxxabiThisValue = nullptr;
+ cgf.cxxThisValue = nullptr;
+ cgf.cxxThisAlignment = CharUnits();
+ cgf.returnValue = Address::invalid();
+ cgf.fnRetTy = QualType();
+ cgf.cxxInheritedCtorInitExprArgs.clear();
+ // FIXME: at one point when we wnat to call one of these, we'll need
+ // CXXInheritedCtorInitExprArgs here too.
+ }
+ ~InlinedInheritingConstructorScope() {
+ cgf.curGD = oldCurGD;
+ cgf.curFuncDecl = oldCurFuncDecl;
+ cgf.curCodeDecl = oldCurCodeDecl;
+ cgf.cxxabiThisDecl = oldCxxabiThisDecl;
+ cgf.cxxabiThisValue = oldCxxabiThisValue;
+ cgf.cxxThisValue = oldCxxThisValue;
+ cgf.cxxThisAlignment = oldCxxThisAlignment;
+ cgf.returnValue = oldReturnValue;
+ cgf.fnRetTy = oldFnRetTy;
+ cgf.cxxInheritedCtorInitExprArgs =
+ std::move(oldCxxInheritedCtorInitExprArgs);
+ }
+
+ private:
+ CIRGenFunction &cgf;
+ GlobalDecl oldCurGD;
+ const Decl *oldCurFuncDecl;
+ const Decl *oldCurCodeDecl;
+ ImplicitParamDecl *oldCxxabiThisDecl;
+ mlir::Value oldCxxabiThisValue;
+ mlir::Value oldCxxThisValue;
+ clang::CharUnits oldCxxThisAlignment;
+ Address oldReturnValue;
+ QualType oldFnRetTy;
+ CallArgList oldCxxInheritedCtorInitExprArgs;
+ };
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index 172b348ac78cf..c881b1b6e69e4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -29,6 +29,7 @@ class ASTContext;
class FunctionType;
class GlobalDecl;
class QualType;
+class TargetInfo;
class Type;
} // namespace clang
@@ -132,6 +133,11 @@ class CIRGenTypes {
cir::FuncType getFunctionType(clang::GlobalDecl gd);
+ /// Determine if a C++ inheriting constructor should have parameters matching
+ /// those of its inherited constructor.
+ bool inheritingCtorHasParams(const InheritedConstructor &inherited,
+ CXXCtorType type);
+
// The arrangement methods are split into three families:
// - those meant to drive the signature and prologue/epilogue
// of a function declaration or definition,
diff --git a/clang/test/CIR/CodeGen/inherited-ctors.cpp b/clang/test/CIR/CodeGen/inherited-ctors.cpp
new file mode 100644
index 0000000000000..158bc9f11b94a
--- /dev/null
+++ b/clang/test/CIR/CodeGen/inherited-ctors.cpp
@@ -0,0 +1,182 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct Base {
+ Base(int i);
+ Base(float, ...);
+};
+
+struct Derived : Base {
+ using Base::Base;
+};
+
+struct VirtDerived : virtual Base {
+ using Base::Base;
+};
+
+struct VirtualDelegatingCtor : VirtDerived {
+ VirtualDelegatingCtor(int x) : Base(x), VirtDerived(x){}
+};
+
+void emitDelegateCallArgs() {
+ // ONLY PassPrototypeArgs
+ Derived canEmitDelegateCallArgs{1};
+}
+
+void cannotEmitDelegateCallArgs() {
+ // Inside of the PassPrototypeArgs && !canEmitDelegateCallArgs
+ Derived cannotEmitDelgateCallArgs{1.1f,2,3.0};
+}
+void fallsthrough() {
+ // !PassPrototypeArgs
+ VirtualDelegatingCtor noInheritingCtorHasParams{1};
+}
+
+
+// LLVM and OGCG check labels are identical other than the 1 difference called out (and ordering).
+// CIR-LABEL: cir.func private @_ZN4BaseC2Ei(!cir.ptr<!rec_Base>{{.*}}, !s32i{{.*}}) special_member<#cir.cxx_ctor<!rec_Base, custom>>
+// LLVM-LABEL: declare void @_ZN4BaseC2Ei(ptr {{.*}}, i32 {{.*}})
+//
+//
+// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN7DerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_Derived>{{.*}}, %{{.*}}: !s32i{{.*}}) special_member<#cir.cxx_ctor<!rec_Derived, custom>>
+// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["this", init]
+// CIR: %[[INT_ALLOCA:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["", init]
+// CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived>
+// CIR: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[THIS_LOAD]] : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
+// CIR: %[[INT:.*]] = cir.load align(4) %[[INT_ALLOCA]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.call @_ZN4BaseC2Ei(%[[BASE_ADDR]], %[[INT]]) : (!cir.ptr<!rec_Base>{{.*}}, !s32i{{.*}}) -> ()
+//
+// LLVM-LABEL: define linkonce_odr void @_ZN7DerivedCI24BaseEi(ptr {{.*}}, i32 {{.*}})
+// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[INT_ALLOCA:.*]] = alloca i32
+// LLVM: %[[BASE_ADDR:.*]] = load ptr, ptr %[[THIS_ALLOCA]]
+// LLVM: %[[INT:.*]] = load i32, ptr %[[INT_ALLOCA]]
+// LLVM: call void @_ZN4BaseC2Ei(ptr {{.*}}%[[BASE_ADDR]], i32 {{.*}}[[INT]])
+//
+//
+// CIR-LABEL: cir.func no_inline dso_local @_Z20emitDelegateCallArgsv()
+// CIR: cir.call @_ZN7DerivedCI14BaseEi(%{{.*}}, %{{.*}}) : (!cir.ptr<!rec_Derived>{{.*}}, !s32i{{.*}}) -> ()
+// LLVM-LABEL: define dso_local void @_Z20emitDelegateCallArgsv()
+// LLVM: call void @_ZN7DerivedCI14BaseEi(ptr {{.*}}, i32 {{.*}}1)
+//
+// CIR-LABEL: cir.func private @_ZN4BaseC2Efz(!cir.ptr<!rec_Base>{{.*}}, !cir.float{{.*}}, ...) special_member<#cir.cxx_ctor<!rec_Base, custom>>
+// LLVM-LABEL: declare void @_ZN4BaseC2Efz(ptr {{.*}}, float {{.*}}, ...)
+//
+// CIR-LABEL: cir.func no_inline dso_local @_Z26cannotEmitDelegateCallArgsv()
+// CIR: %[[TMP_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["tmp", init]
+// CIR: %[[FP_1_1:.*]] = cir.const #cir.fp<1.1{{.*}}> : !cir.float
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i
+// CIR: %[[THREE:.*]] = cir.const #cir.fp<3.0{{.*}}> : !cir.double
+// CIR: %[[LOAD_DERIVED:.*]] = cir.load %[[TMP_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived>
+// CIR: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[LOAD_DERIVED]] : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
+// CIR: cir.call @_ZN4BaseC2Efz(%[[BASE_ADDR]], %[[FP_1_1]], %[[TWO]], %[[THREE]]) : (!cir.ptr<!rec_Base>{{.*}}, !cir.float{{.*}}, !s32i{{.*}}, !cir.double{{.*}}) -> ()
+//
+// LLVM-LABEL: define dso_local void @_Z26cannotEmitDelegateCallArgsv()
+// LLVM: %[[TMP_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[TMP_LOAD:.*]] = load ptr, ptr %[[TMP_ALLOCA]]
+// LLVM: call void (ptr, float, ...) @_ZN4BaseC2Efz(ptr {{.*}}%[[TMP_LOAD]], float {{.*}}0x3FF19999A{{.*}}, i32 {{.*}}2, double {{.*}}3.000000e+00)
+//
+// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN11VirtDerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_VirtDerived> {{.*}}, %{{.*}}: !cir.ptr<!cir.ptr<!void>>{{.*}}) special_member<#cir.cxx_ctor<!rec_VirtDerived, custom>>
+// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtDerived>, !cir.ptr<!cir.ptr<!rec_VirtDerived>>, ["this", init] {alignment = 8 : i64}
+// CIR: %[[VTT_ALLOCA:.]] = cir.alloca !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!cir.ptr<!cir.ptr<!void>>>, ["vtt", init] {alignment = 8 : i64}
+// CIR: %[[THIS:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/191467
More information about the cfe-commits
mailing list