https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/191467
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. >From 17e78b9189d51317646b71955d2d3fc45790ac65 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Thu, 2 Apr 2026 15:55:35 -0700 Subject: [PATCH] [CIR] Inheriting Constructor/inheriting ctor inlining lowering 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. --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 14 +- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 136 ++++++++++++- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 7 +- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 18 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 66 +++++++ clang/lib/CIR/CodeGen/CIRGenTypes.h | 6 + clang/test/CIR/CodeGen/inherited-ctors.cpp | 182 ++++++++++++++++++ 7 files changed, 412 insertions(+), 17 deletions(-) create mode 100644 clang/test/CIR/CodeGen/inherited-ctors.cpp 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<!cir.ptr<!rec_VirtDerived>>, !cir.ptr<!rec_VirtDerived> +// CIR: %[[VTT:.*]] = cir.load align(8) %[[VTT_ALLOCA]] : !cir.ptr<!cir.ptr<!cir.ptr<!void>>>, !cir.ptr<!cir.ptr<!void>> +// CIR: %[[VTT_ADDR:.*]] = cir.vtt.address_point %[[VTT]] : !cir.ptr<!cir.ptr<!void>>, offset = 0 -> !cir.ptr<!cir.ptr<!void>> +// CIR: %[[VTT_ADDR_CAST:.*]] = cir.cast bitcast %[[VTT_ADDR]] : !cir.ptr<!cir.ptr<!void>> -> !cir.ptr<!cir.vptr> +// CIR: %[[VTT_ADDR_LOAD:.*]] = cir.load align(8) %[[VTT_ADDR_CAST]] : !cir.ptr<!cir.vptr>, !cir.vptr +// CIR: %[[VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_VirtDerived> -> !cir.ptr<!cir.vptr> +// CIR: cir.store align(8) %[[VTT_ADDR_LOAD]], %[[VPTR]] : !cir.vptr, !cir.ptr<!cir.vptr> + +// LLVM-LABEL: define linkonce_odr void @_ZN11VirtDerivedCI24BaseEi(ptr {{.*}}, ptr {{.*}}) +// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr +// LLVM: %[[VTT_ALLOCA:.*]] = alloca ptr +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// LLVM: %[[VTT:.*]] = load ptr, ptr %[[VTT_ALLOCA]] +// LLVM: %[[VTT_ADDR_LOAD:.*]] = load ptr, ptr %[[VTT]] +// LLVM: store ptr %[[VTT_ADDR_LOAD]], ptr %[[THIS]] +// +// +// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN21VirtualDelegatingCtorC1Ei(%{{.*}}: !cir.ptr<!rec_VirtualDelegatingCtor> {{.*}}, %{{.*}}: !s32i {{.*}}) special_member<#cir.cxx_ctor<!rec_VirtualDelegatingCtor, custom>> +// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtualDelegatingCtor>, !cir.ptr<!cir.ptr<!rec_VirtualDelegatingCtor>>, ["this", init] {alignment = 8 : i64} +// CIR: %[[X_ALLOCA:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64} +// CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtualDelegatingCtor>>, !cir.ptr<!rec_VirtualDelegatingCtor> +// CIR: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[THIS_LOAD]] : !cir.ptr<!rec_VirtualDelegatingCtor> nonnull [0] -> !cir.ptr<!rec_Base> +// CIR: %[[X_LOAD:.*]] = cir.load align(4) %[[X_ALLOCA]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.call @_ZN4BaseC2Ei(%[[BASE_ADDR]], %[[X_LOAD]]) : (!cir.ptr<!rec_Base> {{.*}}, !s32i {{{.*}}) -> () +// +// LLVM-LABEL: define linkonce_odr void @_ZN21VirtualDelegatingCtorC1Ei(ptr {{.*}}, i32 {{.*}}) +// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr +// LLVM: %[[X_ALLOCA:.*]] = alloca i32 +// LLVM: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// LLVM: %[[X_LOAD:.*]] = load i32, ptr %[[X_ALLOCA]] +// LLVM: call void @_ZN4BaseC2Ei(ptr {{.*}}%[[THIS_LOAD]], i32 {{.*}}%[[X_LOAD]]) + +// Note: Due to an innocuous bug in LLVM-IR codegen, this line is different. +// LLVM-IR codegen emits this with 3 arguments, despite the 3rd not being used +// in the body, and not being included in the declaration/definition of this +// function. CIR cannot reproduce this, as we have a verifier that checks that +// the arg counts match. +// CIR: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[THIS_LOAD]] : !cir.ptr<!rec_VirtualDelegatingCtor> nonnull [0] -> !cir.ptr<!rec_VirtDerived> +// CIR: %[[ADDR_PT:.*]] = cir.vtt.address_point @_ZTT21VirtualDelegatingCtor, offset = 1 -> !cir.ptr<!cir.ptr<!void>> +// CIR: cir.call @_ZN11VirtDerivedCI24BaseEi(%[[BASE_ADDR]], %[[ADDR_PT]]) : (!cir.ptr<!rec_VirtDerived>{{.*}}, !cir.ptr<!cir.ptr<!void>>{{.*}}) -> () +// CIR: %[[ADDR_PT:.*]] = cir.vtable.address_point(@_ZTV21VirtualDelegatingCtor, address_point = <index = 0, offset = 3>) : !cir.vptr +// CIR: %[[VPTR:.*]] = cir.vtable.get_vptr %[[THIS_LOAD]] : !cir.ptr<!rec_VirtualDelegatingCtor> -> !cir.ptr<!cir.vptr> +// CIR: cir.store align(8) %[[ADDR_PT]], %[[VPTR]] : !cir.vptr, !cir.ptr<!cir.vptr> + +// LLVM: call void @_ZN11VirtDerivedCI24BaseEi(ptr {{.*}}%[[THIS_LOAD]], ptr {{.*}}(i8, ptr @_ZTT21VirtualDelegatingCtor, i64 8)) +// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV21VirtualDelegatingCtor, i64 24), ptr %[[THIS_LOAD]] +// + +// CIR-LABEL: cir.func no_inline dso_local @_Z12fallsthroughv() +// CIR: cir.call @_ZN21VirtualDelegatingCtorC1Ei(%{{.*}}, %{{.*}}) : (!cir.ptr<!rec_VirtualDelegatingCtor> {{.*}}, !s32i {{.*}}) -> () +// LLVM-LABEL: define dso_local void @_Z12fallsthroughv() +// LLVM: call void @_ZN21VirtualDelegatingCtorC1Ei(ptr {{.*}}, i32 {{.*}}1) + + +// OGCG-LABEL: define dso_local void @_Z20emitDelegateCallArgsv() +// OGCG: call void @_ZN7DerivedCI14BaseEi(ptr {{.*}}, i32 {{.*}}1) +// +// OGCG-LABEL: define linkonce_odr void @_ZN7DerivedCI14BaseEi(ptr {{.*}}, i32 {{.*}}) +// OGCG: call void @_ZN7DerivedCI24BaseEi(ptr {{.*}}, i32 {{.*}}) +// +// OGCG-LABEL: define dso_local void @_Z26cannotEmitDelegateCallArgsv() +// OGCG: %[[TMP_ALLOCA:.*]] = alloca ptr +// OGCG: %[[TMP_LOAD:.*]] = load ptr, ptr %[[TMP_ALLOCA]] +// OGCG: call void (ptr, float, ...) @_ZN4BaseC2Efz(ptr {{.*}}%[[TMP_LOAD]], float {{.*}}0x3FF19999A{{.*}}, i32 {{.*}}2, double {{.*}}3.000000e+00) +// +// OGCG-LABEL: declare void @_ZN4BaseC2Efz(ptr {{.*}}, float {{.*}}, ...) +// +// OGCG-LABEL: define dso_local void @_Z12fallsthroughv() +// OGCG: call void @_ZN21VirtualDelegatingCtorC1Ei(ptr {{.*}}, i32 {{.*}}1) +// +// OGCG-LABEL: define linkonce_odr void @_ZN21VirtualDelegatingCtorC1Ei(ptr {{.*}}, i32 {{.*}}) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: %[[X_ALLOCA:.*]] = alloca i32 +// OGCG: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[X_LOAD:.*]] = load i32, ptr %[[X_ALLOCA]] +// OGCG: call void @_ZN4BaseC2Ei(ptr {{.*}}%[[THIS_LOAD]], i32 {{.*}}%[[X_LOAD]]) +// Note: see the note above for the CIR/LLVM-IR difference here. +// OGCG: %[[X_LOAD:.*]] = load i32, ptr %[[X_ALLOCA]] +// OGCG: call void @_ZN11VirtDerivedCI24BaseEi(ptr {{.*}}%[[THIS_LOAD]], ptr {{.*}}(i8, ptr @_ZTT21VirtualDelegatingCtor, i64 8), i32{{.*}}%[[X_LOAD]]) +// OGCG: store ptr getelementptr inbounds inrange(-24, 0) ({ [3 x ptr] }, ptr @_ZTV21VirtualDelegatingCtor, i32 0, i32 0, i32 3), ptr %[[THIS_LOAD]] +// +// OGCG-LABEL: define linkonce_odr void @_ZN7DerivedCI24BaseEi(ptr {{.*}}, i32 {{.*}}) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: %[[INT_ALLOCA:.*]] = alloca i32 +// OGCG: %[[BASE_ADDR:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[INT:.*]] = load i32, ptr %[[INT_ALLOCA]] +// OGCG: call void @_ZN4BaseC2Ei(ptr {{.*}}%[[BASE_ADDR]], i32 {{.*}}[[INT]]) +// +// OGCG-LABEL: declare void @_ZN4BaseC2Ei(ptr {{.*}}, i32 {{.*}}) + +// OGCG-LABEL: define linkonce_odr void @_ZN11VirtDerivedCI24BaseEi(ptr {{.*}}, ptr {{.*}}) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: %[[VTT_ALLOCA:.*]] = alloca ptr +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[VTT:.*]] = load ptr, ptr %[[VTT_ALLOCA]] +// OGCG: %[[VTT_ADDR_LOAD:.*]] = load ptr, ptr %[[VTT]] +// OGCG: store ptr %[[VTT_ADDR_LOAD]], ptr %[[THIS]] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
