diff options
-rw-r--r-- | clang/include/clang/CIR/CIRGenerator.h | 25 | ||||
-rw-r--r-- | clang/include/clang/CIR/MissingFeatures.h | 13 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp | 23 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 14 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 22 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.h | 25 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 90 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 8 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenerator.cpp | 46 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 4 | ||||
-rw-r--r-- | clang/test/CIR/CodeGen/inline-cxx-func.cpp | 70 | ||||
-rw-r--r-- | clang/test/CIR/CodeGen/member-functions.cpp | 6 |
14 files changed, 326 insertions, 23 deletions
diff --git a/clang/include/clang/CIR/CIRGenerator.h b/clang/include/clang/CIR/CIRGenerator.h index bb20fdf..8e49371 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 @@ public: ~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 @@ public: 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 3b82de8..65978e5 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 e368ccd..6cf4e5c 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 b5c33a2..107535e 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 @@ public: : 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 @@ public: /// 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 b33bb71c..36d5225 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -569,6 +569,8 @@ public: 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 bd67a34..b008ee9 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 0badde02..ef61aa7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -55,6 +55,15 @@ public: /// 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 @@ public: 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 0000000..fdd8b63 --- /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 9da7fcc..0ec5dce 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 726da5b..512009a 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 4c0aa9a..734f3cd 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 48ae2a0..c19312a 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -84,6 +84,10 @@ public: 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 0000000..31d0255 --- /dev/null +++ b/clang/test/CIR/CodeGen/inline-cxx-func.cpp @@ -0,0 +1,70 @@ +// 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; + } +}; + +// 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 3d7dc8c..c1d49ac 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: } |