aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/CIR/CIRGenerator.h25
-rw-r--r--clang/include/clang/CIR/MissingFeatures.h13
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp23
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCXXABI.h14
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp2
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenFunction.cpp22
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenFunction.h25
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp90
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenModule.cpp8
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenerator.cpp46
-rw-r--r--clang/lib/CIR/CodeGen/CMakeLists.txt1
-rw-r--r--clang/lib/CIR/FrontendAction/CIRGenAction.cpp4
-rw-r--r--clang/test/CIR/CodeGen/inline-cxx-func.cpp70
-rw-r--r--clang/test/CIR/CodeGen/member-functions.cpp6
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 &params);
+ /// 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: }