https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/142484
This change upstreams the code to support emitting inline C++ function definitions, including the ASTConsumer handler for inline definitions and the code to load the 'this' pointer. This necessitates introducing the Itanium CXXABI class. No other CXXABI subclasses are supported at this time. The Itanium CXXABI is used for AppleARM64, which will require its own handler for a few special cases (such as array cookies) later. >From 7e09fb18fc370e66b8d346bb50d11bedd564c1a5 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Mon, 2 Jun 2025 13:33:34 -0700 Subject: [PATCH] [CIR] Support inline C++ member function definitions This change upstreams the code to support emitting inline C++ function definitions, including the ASTConsumer handler for inline definitions and the code to load the 'this' pointer. This necessitates introducing the Itanium CXXABI class. No other CXXABI subclasses are supported at this time. The Itanium CXXABI is used for AppleARM64, which will require its own handler for a few special cases (such as array cookies) later. --- clang/include/clang/CIR/CIRGenerator.h | 25 ++++++ clang/include/clang/CIR/MissingFeatures.h | 13 ++- clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp | 23 +++-- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 14 +++ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 + clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 22 ++++- clang/lib/CIR/CodeGen/CIRGenFunction.h | 25 ++++++ clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 90 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 8 -- clang/lib/CIR/CodeGen/CIRGenerator.cpp | 46 +++++++++- clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 4 + clang/test/CIR/CodeGen/inline-cxx-func.cpp | 72 +++++++++++++++ clang/test/CIR/CodeGen/member-functions.cpp | 6 +- 14 files changed, 328 insertions(+), 23 deletions(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp create mode 100644 clang/test/CIR/CodeGen/inline-cxx-func.cpp diff --git a/clang/include/clang/CIR/CIRGenerator.h b/clang/include/clang/CIR/CIRGenerator.h index bb20fdf72693b..8e49371e99020 100644 --- a/clang/include/clang/CIR/CIRGenerator.h +++ b/clang/include/clang/CIR/CIRGenerator.h @@ -43,10 +43,32 @@ class CIRGenerator : public clang::ASTConsumer { const clang::CodeGenOptions &codeGenOpts; + unsigned handlingTopLevelDecls; + + /// Use this when emitting decls to block re-entrant decl emission. It will + /// emit all deferred decls on scope exit. Set EmitDeferred to false if decl + /// emission must be deferred longer, like at the end of a tag definition. + struct HandlingTopLevelDeclRAII { + CIRGenerator &self; + bool emitDeferred; + HandlingTopLevelDeclRAII(CIRGenerator &self, bool emitDeferred = true) + : self{self}, emitDeferred{emitDeferred} { + ++self.handlingTopLevelDecls; + } + ~HandlingTopLevelDeclRAII() { + unsigned Level = --self.handlingTopLevelDecls; + if (Level == 0 && emitDeferred) + self.emitDeferredDecls(); + } + }; + protected: std::unique_ptr<mlir::MLIRContext> mlirContext; std::unique_ptr<clang::CIRGen::CIRGenModule> cgm; +private: + llvm::SmallVector<clang::FunctionDecl *, 8> deferredInlineMemberFuncDefs; + public: CIRGenerator(clang::DiagnosticsEngine &diags, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, @@ -54,6 +76,7 @@ class CIRGenerator : public clang::ASTConsumer { ~CIRGenerator() override; void Initialize(clang::ASTContext &astContext) override; bool HandleTopLevelDecl(clang::DeclGroupRef group) override; + void HandleInlineFunctionDefinition(clang::FunctionDecl *d) override; void CompleteTentativeDefinition(clang::VarDecl *d) override; mlir::ModuleOp getModule() const; @@ -61,6 +84,8 @@ class CIRGenerator : public clang::ASTConsumer { const mlir::MLIRContext &getMLIRContext() const { return *mlirContext; }; bool verifyModule() const; + + void emitDeferredDecls(); }; } // namespace cir diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 3b82de882953c..65978e51a23e9 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -137,8 +137,15 @@ struct MissingFeatures { static bool recordZeroInit() { return false; } static bool zeroSizeRecordMembers() { return false; } - // Misc + // CXXABI static bool cxxABI() { return false; } + static bool cxxabiThisAlignment() { return false; } + static bool cxxabiUseARMMethodPtrABI() { return false; } + static bool cxxabiUseARMGuardVarABI() { return false; } + static bool cxxabiAppleARM64CXXABI() { return false; } + static bool cxxabiStructorImplicitParam() { return false; } + + // Misc static bool cirgenABIInfo() { return false; } static bool abiArgInfo() { return false; } static bool tryEmitAsConstant() { return false; } @@ -187,7 +194,6 @@ struct MissingFeatures { static bool typeChecks() { return false; } static bool lambdaFieldToName() { return false; } static bool updateCompletedType() { return false; } - static bool targetSpecificCXXABI() { return false; } static bool moduleNameHash() { return false; } static bool constantFoldSwitchStatement() { return false; } static bool cudaSupport() { return false; } @@ -196,13 +202,12 @@ struct MissingFeatures { static bool constEmitterVectorILE() { return false; } static bool needsGlobalCtorDtor() { return false; } static bool emitTypeCheck() { return false; } - static bool cxxabiThisDecl() { return false; } - static bool cxxabiThisAlignment() { return false; } static bool writebacks() { return false; } static bool cleanupsToDeactivate() { return false; } static bool stackBase() { return false; } static bool deferredDecls() { return false; } static bool setTargetAttributes() { return false; } + static bool coverageMapping() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp index e368ccd006bb8..6cf4e5c658fb6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp @@ -33,13 +33,24 @@ void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf, &cgm.getASTContext().Idents.get("this"), md->getThisType(), ImplicitParamKind::CXXThis); params.push_back(thisDecl); - - // Classic codegen save thisDecl in CodeGenFunction::CXXABIThisDecl, but it - // doesn't seem to be needed in CIRGen. - assert(!cir::MissingFeatures::cxxabiThisDecl()); + cgf.cxxabiThisDecl = thisDecl; // Classic codegen computes the alignment of thisDecl and saves it in - // CodeGenFunction::CXXABIThisAlignment, but it doesn't seem to be needed in - // CIRGen. + // CodeGenFunction::CXXABIThisAlignment, but it is only used in emitTypeCheck + // in CodeGenFunction::StartFunction(). assert(!cir::MissingFeatures::cxxabiThisAlignment()); } + +mlir::Value CIRGenCXXABI::loadIncomingCXXThis(CIRGenFunction &cgf) { + ImplicitParamDecl *vd = getThisDecl(cgf); + Address addr = cgf.getAddrOfLocalVar(vd); + return cgf.getBuilder().create<cir::LoadOp>( + cgf.getLoc(vd->getLocation()), addr.getElementType(), addr.getPointer()); +} + +void CIRGenCXXABI::setCXXABIThisValue(CIRGenFunction &cgf, + mlir::Value thisPtr) { + /// Initialize the 'this' slot. + assert(getThisDecl(cgf) && "no 'this' variable for function"); + cgf.cxxabiThisValue = thisPtr; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index b5c33a29442c7..107535ebc7275 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H #include "CIRGenCall.h" +#include "CIRGenFunction.h" #include "CIRGenModule.h" #include "clang/AST/Mangle.h" @@ -34,7 +35,17 @@ class CIRGenCXXABI { : cgm(cgm), mangleContext(cgm.getASTContext().createMangleContext()) {} virtual ~CIRGenCXXABI(); + void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr); + public: + clang::ImplicitParamDecl *getThisDecl(CIRGenFunction &cgf) { + return cgf.cxxabiThisDecl; + } + + /// Emit the ABI-specific prolog for the function + virtual void emitInstanceFunctionProlog(SourceLocation Loc, + CIRGenFunction &cgf) = 0; + /// Get the type of the implicit "this" parameter used by a method. May return /// zero if no specific type is applicable, e.g. if the ABI expects the "this" /// parameter to point to some artificial offset in a complete object due to @@ -47,6 +58,9 @@ class CIRGenCXXABI { /// Build a parameter variable suitable for 'this'. void buildThisParam(CIRGenFunction &cgf, FunctionArgList ¶ms); + /// Loads the incoming C++ this pointer as it was passed by the caller. + mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf); + /// Returns true if the given constructor or destructor is one of the kinds /// that the ABI says returns 'this' (only applies when called non-virtually /// for destructors). diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index b33bb71c99c90..36d52251a0106 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -569,6 +569,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value VisitUnaryLNot(const UnaryOperator *e); + mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); } + /// Emit a conversion from the specified type to the specified destination /// type, both of which are CIR scalar types. /// TODO: do we need ScalarConversionOpts here? Should be done in another diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index bd67a3407d749..b008ee9b472a1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -343,7 +343,9 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, curFn = fn; - const auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl()); + const Decl *d = gd.getDecl(); + const auto *fd = dyn_cast_or_null<FunctionDecl>(d); + curFuncDecl = d->getNonClosureContext(); mlir::Block *entryBB = &fn.getBlocks().front(); builder.setInsertionPointToStart(entryBB); @@ -385,6 +387,24 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, if (!returnType->isVoidType()) emitAndUpdateRetAlloca(returnType, getLoc(fd->getBody()->getEndLoc()), getContext().getTypeAlignInChars(returnType)); + + if (isa_and_nonnull<CXXMethodDecl>(d) && + cast<CXXMethodDecl>(d)->isInstance()) { + cgm.getCXXABI().emitInstanceFunctionProlog(loc, *this); + + const auto *md = cast<CXXMethodDecl>(d); + if (md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call) { + cgm.errorNYI(loc, "lambda call operator"); + } else { + // Not in a lambda; just use 'this' from the method. + // FIXME: Should we generate a new load for each use of 'this'? The fast + // register allocator would be happier... + cxxThisValue = cxxabiThisValue; + } + + assert(!cir::MissingFeatures::sanitizers()); + assert(!cir::MissingFeatures::emitTypeCheck()); + } } void CIRGenFunction::finishFunction(SourceLocation endLoc) {} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 0badde024b166..ef61aa7f4ee6d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -55,6 +55,15 @@ class CIRGenFunction : public CIRGenTypeCache { /// The compiler-generated variable that holds the return value. std::optional<mlir::Value> fnRetAlloca; + /// CXXThisDecl - When generating code for a C++ member function, + /// this will hold the implicit 'this' declaration. + ImplicitParamDecl *cxxabiThisDecl = nullptr; + mlir::Value cxxabiThisValue = nullptr; + mlir::Value cxxThisValue = nullptr; + + // Holds the Decl for the current outermost non-closure context + const clang::Decl *curFuncDecl = nullptr; + /// The function for which code is currently being generated. cir::FuncOp curFn; @@ -307,6 +316,22 @@ class CIRGenFunction : public CIRGenTypeCache { return LValue::makeAddr(addr, ty, baseInfo); } + /// Return the address of a local variable. + Address getAddrOfLocalVar(const clang::VarDecl *vd) { + auto it = localDeclMap.find(vd); + assert(it != localDeclMap.end() && + "Invalid argument to getAddrOfLocalVar(), no decl!"); + return it->second; + } + + /// Load the value for 'this'. This function is only valid while generating + /// code for an C++ member function. + /// FIXME(cir): this should return a mlir::Value! + mlir::Value loadCXXThis() { + assert(cxxThisValue && "no 'this' value for this function"); + return cxxThisValue; + } + /// Get an appropriate 'undef' rvalue for the given type. /// TODO: What's the equivalent for MLIR? Currently we're only using this for /// void types so it just returns RValue::get(nullptr) but it'll need diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp new file mode 100644 index 0000000000000..fdd8b63fb6da0 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This provides C++ code generation targeting the Itanium C++ ABI. The class +// in this file generates structures that follow the Itanium C++ ABI, which is +// documented at: +// https://itanium-cxx-abi.github.io/cxx-abi/abi.html +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// +// It also supports the closely-related ARM ABI, documented at: +// https://developer.arm.com/documentation/ihi0041/g/ +// +//===----------------------------------------------------------------------===// + +#include "CIRGenCXXABI.h" +#include "CIRGenFunction.h" + +#include "clang/AST/GlobalDecl.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace clang::CIRGen; + +namespace { + +class CIRGenItaniumCXXABI : public CIRGenCXXABI { +public: + CIRGenItaniumCXXABI(CIRGenModule &cgm) : CIRGenCXXABI(cgm) { + assert(!cir::MissingFeatures::cxxabiUseARMMethodPtrABI()); + assert(!cir::MissingFeatures::cxxabiUseARMGuardVarABI()); + } + + void emitInstanceFunctionProlog(SourceLocation Loc, + CIRGenFunction &CGF) override; +}; + +} // namespace + +void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc, + CIRGenFunction &cgf) { + // Naked functions have no prolog. + if (cgf.curFuncDecl && cgf.curFuncDecl->hasAttr<NakedAttr>()) { + cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(), + "emitInstanceFunctionProlog: Naked"); + } + + /// Initialize the 'this' slot. In the Itanium C++ ABI, no prologue + /// adjustments are required, because they are all handled by thunks. + setCXXABIThisValue(cgf, loadIncomingCXXThis(cgf)); + + /// Classic codegen has code here to initialize the 'vtt' slot if + // getStructorImplicitParamDecl(cgf) returns a non-null value, but in the + // current implementation (of classic codegen) it never does. + assert(!cir::MissingFeatures::cxxabiStructorImplicitParam()); + + /// If this is a function that the ABI specifies returns 'this', initialize + /// the return slot to this' at the start of the function. + /// + /// Unlike the setting of return types, this is done within the ABI + /// implementation instead of by clients of CIRGenCXXBI because: + /// 1) getThisValue is currently protected + /// 2) in theory, an ABI could implement 'this' returns some other way; + /// HasThisReturn only specifies a contract, not the implementation + if (hasThisReturn(cgf.curGD)) { + cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(), + "emitInstanceFunctionProlog: hasThisReturn"); + } +} + +CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) { + switch (cgm.getASTContext().getCXXABIKind()) { + case TargetCXXABI::GenericItanium: + case TargetCXXABI::GenericAArch64: + return new CIRGenItaniumCXXABI(cgm); + + case TargetCXXABI::AppleARM64: + // The general Itanium ABI will do until we implement something that + // requires special handling. + assert(!cir::MissingFeatures::cxxabiAppleARM64CXXABI()); + return new CIRGenItaniumCXXABI(cgm); + + default: + llvm_unreachable("bad or NYI ABI kind"); + } +} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 9da7fcc0f89d5..0ec5dcec99281 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -55,14 +55,6 @@ static CIRGenCXXABI *createCXXABI(CIRGenModule &cgm) { llvm_unreachable("invalid C++ ABI kind"); } -namespace clang::CIRGen { -// TODO(cir): Implement target-specific CIRGenCXXABIs -CIRGenCXXABI *CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) { - assert(!cir::MissingFeatures::targetSpecificCXXABI()); - return new CIRGenCXXABI(cgm); -} -} // namespace clang::CIRGen - CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, clang::ASTContext &astContext, const clang::CodeGenOptions &cgo, diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp index 726da5b013264..512009affa806 100644 --- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -28,8 +28,12 @@ void CIRGenerator::anchor() {} CIRGenerator::CIRGenerator(clang::DiagnosticsEngine &diags, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs, const CodeGenOptions &cgo) - : diags(diags), fs(std::move(vfs)), codeGenOpts{cgo} {} -CIRGenerator::~CIRGenerator() = default; + : diags(diags), fs(std::move(vfs)), codeGenOpts{cgo}, + handlingTopLevelDecls{0} {} +CIRGenerator::~CIRGenerator() { + // There should normally not be any leftover inline method definitions. + assert(deferredInlineMemberFuncDefs.empty() || diags.hasErrorOccurred()); +} void CIRGenerator::Initialize(ASTContext &astContext) { using namespace llvm; @@ -57,12 +61,50 @@ bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) { if (diags.hasUnrecoverableErrorOccurred()) return true; + HandlingTopLevelDeclRAII handlingDecl(*this); + for (Decl *decl : group) cgm->emitTopLevelDecl(decl); return true; } +void CIRGenerator::HandleInlineFunctionDefinition(FunctionDecl *d) { + if (diags.hasErrorOccurred()) + return; + + assert(d->doesThisDeclarationHaveABody()); + + // We may want to emit this definition. However, that decision might be + // based on computing the linkage, and we have to defer that in case we are + // inside of something that will chagne the method's final linkage, e.g. + // typedef struct { + // void bar(); + // void foo() { bar(); } + // } A; + deferredInlineMemberFuncDefs.push_back(d); + + // Provide some coverage mapping even for methods that aren't emitted. + // Don't do this for templated classes though, as they may not be + // instantiable. + assert(!cir::MissingFeatures::coverageMapping()); +} + +void CIRGenerator::emitDeferredDecls() { + if (deferredInlineMemberFuncDefs.empty()) + return; + + // Emit any deferred inline method definitions. Note that more deferred + // methods may be added during this loop, since ASTConsumer callbacks can be + // invoked if AST inspection results in declarations being added. Therefore, + // we use an index to loop over the deferredInlineMemberFuncDefs rather than + // a range. + HandlingTopLevelDeclRAII handlingDecls(*this); + for (unsigned i = 0; i != deferredInlineMemberFuncDefs.size(); ++i) + cgm->emitTopLevelDecl(deferredInlineMemberFuncDefs[i]); + deferredInlineMemberFuncDefs.clear(); +} + void CIRGenerator::CompleteTentativeDefinition(VarDecl *d) { if (diags.hasErrorOccurred()) return; diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 4c0aa9a3d0a64..734f3cdca399a 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -19,6 +19,7 @@ add_clang_library(clangCIR CIRGenExprConstant.cpp CIRGenExprScalar.cpp CIRGenFunction.cpp + CIRGenItaniumCXXABI.cpp CIRGenModule.cpp CIRGenOpenACCClause.cpp CIRGenRecordLayoutBuilder.cpp diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 48ae2a05df700..c19312ae245dd 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -84,6 +84,10 @@ class CIRGenConsumer : public clang::ASTConsumer { return true; } + void HandleInlineFunctionDefinition(FunctionDecl *D) override { + Gen->HandleInlineFunctionDefinition(D); + } + void HandleTranslationUnit(ASTContext &C) override { Gen->HandleTranslationUnit(C); diff --git a/clang/test/CIR/CodeGen/inline-cxx-func.cpp b/clang/test/CIR/CodeGen/inline-cxx-func.cpp new file mode 100644 index 0000000000000..43019369b21c6 --- /dev/null +++ b/clang/test/CIR/CodeGen/inline-cxx-func.cpp @@ -0,0 +1,72 @@ +// 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 S { + int Member; + + int InlineFunc() { + return Member; + } + + int OutlineFunc(); +}; + +// CIR: !rec_S = !cir.record<struct "S" {!s32i}> +// LLVM: %struct.S = type { i32 } +// OGCG: %struct.S = type { i32 } + +// CIR: cir.func @_ZN1S10InlineFuncEv(%arg0: !cir.ptr<!rec_S> {{.*}}) -> !s32i +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["this", init] +// CIR: %[[RET_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] +// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>> +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S> +// CIR: %[[MEMBER_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "Member"} : !cir.ptr<!rec_S> -> !cir.ptr<!s32i> +// CIR: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.store %[[MEMBER]], %[[RET_ADDR]] : !s32i, !cir.ptr<!s32i> +// CIR: %[[RET_VAL:.*]] = cir.load %[[RET_ADDR]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.return %[[RET_VAL]] : !s32i + +// LLVM: define{{.*}} i32 @_ZN1S10InlineFuncEv(ptr %[[ARG0:.*]]) +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr, i64 1, align 8 +// LLVM: %[[RET_ADDR:.*]] = alloca i32, i64 1, align 4 +// LLVM: store ptr %[[ARG0]], ptr %[[THIS_ADDR]], align 8 +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// LLVM: %[[MEMBER_ADDR:.*]] = getelementptr %struct.S, ptr %[[THIS]], i32 0, i32 0 +// LLVM: %[[MEMBER:.*]] = load i32, ptr %[[MEMBER_ADDR]], align 4 +// LLVM: store i32 %[[MEMBER]], ptr %[[RET_ADDR]], align 4 +// LLVM: %[[RET_VAL:.*]] = load i32, ptr %[[RET_ADDR]], align 4 +// LLVM: ret i32 %[[RET_VAL]] + +// The inlined function is defined after use() in OGCG + +void use() { + S s; + s.InlineFunc(); +} + +// CIR: cir.func @_Z3usev() +// CIR: %[[S_ADDR:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s"] +// CIR: %[[RET_VAL:.*]] = cir.call @_ZN1S10InlineFuncEv(%[[S_ADDR]]) : (!cir.ptr<!rec_S>) -> !s32i +// CIR: cir.return + +// LLVM: define{{.*}} void @_Z3usev() +// LLVM: %[[S_ADDR:.*]] = alloca %struct.S +// LLVM: %[[RET_VAL:.*]] = call i32 @_ZN1S10InlineFuncEv(ptr %[[S_ADDR]]) +// LLVM: ret void + +// OGCG: define{{.*}} void @_Z3usev() +// OGCG: %[[S_ADDR:.*]] = alloca %struct.S +// OGCG: %[[RET_VAL:.*]] = call{{.*}} i32 @_ZN1S10InlineFuncEv(ptr{{.*}} %[[S_ADDR]]) +// OGCG: ret void + +// OGCG: define{{.*}} i32 @_ZN1S10InlineFuncEv(ptr{{.*}} %[[ARG0:.*]]) +// OGCG: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// OGCG: store ptr %[[ARG0]], ptr %[[THIS_ADDR]], align 8 +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// OGCG: %[[MEMBER_ADDR:.*]] = getelementptr inbounds nuw %struct.S, ptr %[[THIS]], i32 0, i32 0 +// OGCG: %[[MEMBER:.*]] = load i32, ptr %[[MEMBER_ADDR]], align 4 +// OGCG: ret i32 %[[MEMBER]] diff --git a/clang/test/CIR/CodeGen/member-functions.cpp b/clang/test/CIR/CodeGen/member-functions.cpp index 3d7dc8c4184a1..c1d49ac4d8f3a 100644 --- a/clang/test/CIR/CodeGen/member-functions.cpp +++ b/clang/test/CIR/CodeGen/member-functions.cpp @@ -13,6 +13,7 @@ void C::f() {} // CIR: cir.func @_ZN1C1fEv(%[[THIS_ARG:.*]]: !cir.ptr<!rec_C> // CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["this", init] // CIR: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] : !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>> +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C> // CIR: cir.return // CIR: } @@ -23,8 +24,9 @@ void C::f2(int a, int b) {} // CIR-NEXT: %[[A_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] // CIR-NEXT: %[[B_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init] // CIR-NEXT: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] : !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>> -// CIR-NEXT: cir.store %[[A_ARG]], %[[A_ADDR]] : !s32i, !cir.ptr<!s32i> loc(#loc12) -// CIR-NEXT: cir.store %[[B_ARG]], %[[B_ADDR]] : !s32i, !cir.ptr<!s32i> loc(#loc12) +// CIR-NEXT: cir.store %[[A_ARG]], %[[A_ADDR]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: cir.store %[[B_ARG]], %[[B_ADDR]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C> // CIR-NEXT: cir.return // CIR-NEXT: } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits