Skip to content

Commit a0c515a

Browse files
authored
[CIR] Upstream support for C++ member function calls (#140290)
This change adds the support needed to handle a C++ member function call, including arranging the function type with an argument added for the 'this' parameter. It was necessary to introduce the class to handle the CXXABI, but at this time no target-specific subclasses have been added.
1 parent 0b4cfd1 commit a0c515a

File tree

14 files changed

+514
-19
lines changed

14 files changed

+514
-19
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ struct MissingFeatures {
9090
static bool opCallArgEvaluationOrder() { return false; }
9191
static bool opCallCallConv() { return false; }
9292
static bool opCallSideEffect() { return false; }
93-
static bool opCallChainCall() { return false; }
9493
static bool opCallNoPrototypeFunc() { return false; }
9594
static bool opCallMustTail() { return false; }
9695
static bool opCallIndirect() { return false; }
@@ -107,6 +106,13 @@ struct MissingFeatures {
107106
static bool opCallLandingPad() { return false; }
108107
static bool opCallContinueBlock() { return false; }
109108

109+
// FnInfoOpts -- This is used to track whether calls are chain calls or
110+
// instance methods. Classic codegen uses chain call to track and extra free
111+
// register for x86 and uses instance method as a condition for a thunk
112+
// generation special case. It's not clear that we need either of these in
113+
// pre-lowering CIR codegen.
114+
static bool opCallFnInfoOpts() { return false; }
115+
110116
// ScopeOp handling
111117
static bool opScopeCleanupRegion() { return false; }
112118

@@ -189,6 +195,12 @@ struct MissingFeatures {
189195
static bool constEmitterArrayILE() { return false; }
190196
static bool constEmitterVectorILE() { return false; }
191197
static bool needsGlobalCtorDtor() { return false; }
198+
static bool emitTypeCheck() { return false; }
199+
static bool cxxabiThisDecl() { return false; }
200+
static bool cxxabiThisAlignment() { return false; }
201+
static bool writebacks() { return false; }
202+
static bool cleanupsToDeactivate() { return false; }
203+
static bool stackBase() { return false; }
192204

193205
// Missing types
194206
static bool dataMemberType() { return false; }
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This provides an abstract class for C++ code generation. Concrete subclasses
10+
// of this implement code generation for specific C++ ABIs.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "CIRGenCXXABI.h"
15+
#include "CIRGenFunction.h"
16+
17+
#include "clang/AST/Decl.h"
18+
#include "clang/AST/GlobalDecl.h"
19+
20+
using namespace clang;
21+
using namespace clang::CIRGen;
22+
23+
CIRGenCXXABI::~CIRGenCXXABI() {}
24+
25+
void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf,
26+
FunctionArgList &params) {
27+
const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl());
28+
29+
// FIXME: I'm not entirely sure I like using a fake decl just for code
30+
// generation. Maybe we can come up with a better way?
31+
auto *thisDecl =
32+
ImplicitParamDecl::Create(cgm.getASTContext(), nullptr, md->getLocation(),
33+
&cgm.getASTContext().Idents.get("this"),
34+
md->getThisType(), ImplicitParamKind::CXXThis);
35+
params.push_back(thisDecl);
36+
37+
// Classic codegen save thisDecl in CodeGenFunction::CXXABIThisDecl, but it
38+
// doesn't seem to be needed in CIRGen.
39+
assert(!cir::MissingFeatures::cxxabiThisDecl());
40+
41+
// Classic codegen computes the alignment of thisDecl and saves it in
42+
// CodeGenFunction::CXXABIThisAlignment, but it doesn't seem to be needed in
43+
// CIRGen.
44+
assert(!cir::MissingFeatures::cxxabiThisAlignment());
45+
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H
1515
#define LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H
1616

17+
#include "CIRGenCall.h"
1718
#include "CIRGenModule.h"
1819

1920
#include "clang/AST/Mangle.h"
@@ -31,9 +32,33 @@ class CIRGenCXXABI {
3132
// implemented.
3233
CIRGenCXXABI(CIRGenModule &cgm)
3334
: cgm(cgm), mangleContext(cgm.getASTContext().createMangleContext()) {}
34-
~CIRGenCXXABI();
35+
virtual ~CIRGenCXXABI();
3536

3637
public:
38+
/// Get the type of the implicit "this" parameter used by a method. May return
39+
/// zero if no specific type is applicable, e.g. if the ABI expects the "this"
40+
/// parameter to point to some artificial offset in a complete object due to
41+
/// vbases being reordered.
42+
virtual const clang::CXXRecordDecl *
43+
getThisArgumentTypeForMethod(const clang::CXXMethodDecl *md) {
44+
return md->getParent();
45+
}
46+
47+
/// Build a parameter variable suitable for 'this'.
48+
void buildThisParam(CIRGenFunction &cgf, FunctionArgList &params);
49+
50+
/// Returns true if the given constructor or destructor is one of the kinds
51+
/// that the ABI says returns 'this' (only applies when called non-virtually
52+
/// for destructors).
53+
///
54+
/// There currently is no way to indicate if a destructor returns 'this' when
55+
/// called virtually, and CIR generation does not support this case.
56+
virtual bool hasThisReturn(clang::GlobalDecl gd) const { return false; }
57+
58+
virtual bool hasMostDerivedReturn(clang::GlobalDecl gd) const {
59+
return false;
60+
}
61+
3762
/// Gets the mangle context.
3863
clang::MangleContext &getMangleContext() { return *mangleContext; }
3964
};
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
//===--- CIRGenExprCXX.cpp - Emit CIR Code for C++ expressions ------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This contains code dealing with code generation of C++ expressions
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CIRGenCXXABI.h"
14+
#include "CIRGenFunction.h"
15+
16+
#include "clang/AST/DeclCXX.h"
17+
#include "clang/AST/ExprCXX.h"
18+
#include "clang/CIR/MissingFeatures.h"
19+
20+
using namespace clang;
21+
using namespace clang::CIRGen;
22+
23+
namespace {
24+
struct MemberCallInfo {
25+
RequiredArgs reqArgs;
26+
// Number of prefix arguments for the call. Ignores the `this` pointer.
27+
unsigned prefixSize;
28+
};
29+
} // namespace
30+
31+
static MemberCallInfo commonBuildCXXMemberOrOperatorCall(
32+
CIRGenFunction &cgf, const CXXMethodDecl *md, mlir::Value thisPtr,
33+
mlir::Value implicitParam, QualType implicitParamTy, const CallExpr *ce,
34+
CallArgList &args, CallArgList *rtlArgs) {
35+
assert(ce == nullptr || isa<CXXMemberCallExpr>(ce) ||
36+
isa<CXXOperatorCallExpr>(ce));
37+
assert(md->isInstance() &&
38+
"Trying to emit a member or operator call expr on a static method!");
39+
40+
// Push the this ptr.
41+
const CXXRecordDecl *rd =
42+
cgf.cgm.getCXXABI().getThisArgumentTypeForMethod(md);
43+
args.add(RValue::get(thisPtr), cgf.getTypes().deriveThisType(rd, md));
44+
45+
// If there is an implicit parameter (e.g. VTT), emit it.
46+
if (implicitParam) {
47+
args.add(RValue::get(implicitParam), implicitParamTy);
48+
}
49+
50+
const auto *fpt = md->getType()->castAs<FunctionProtoType>();
51+
RequiredArgs required =
52+
RequiredArgs::getFromProtoWithExtraSlots(fpt, args.size());
53+
unsigned prefixSize = args.size() - 1;
54+
55+
// Add the rest of the call args
56+
if (rtlArgs) {
57+
// Special case: if the caller emitted the arguments right-to-left already
58+
// (prior to emitting the *this argument), we're done. This happens for
59+
// assignment operators.
60+
args.addFrom(*rtlArgs);
61+
} else if (ce) {
62+
// Special case: skip first argument of CXXOperatorCall (it is "this").
63+
unsigned argsToSkip = isa<CXXOperatorCallExpr>(ce) ? 1 : 0;
64+
cgf.emitCallArgs(args, fpt, drop_begin(ce->arguments(), argsToSkip),
65+
ce->getDirectCallee());
66+
} else {
67+
assert(
68+
fpt->getNumParams() == 0 &&
69+
"No CallExpr specified for function with non-zero number of arguments");
70+
}
71+
72+
// return {required, prefixSize};
73+
return {required, prefixSize};
74+
}
75+
76+
RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
77+
const CallExpr *ce, const CXXMethodDecl *md, ReturnValueSlot returnValue,
78+
bool hasQualifier, NestedNameSpecifier *qualifier, bool isArrow,
79+
const Expr *base) {
80+
assert(isa<CXXMemberCallExpr>(ce) || isa<CXXOperatorCallExpr>(ce));
81+
82+
if (md->isVirtual()) {
83+
cgm.errorNYI(ce->getSourceRange(),
84+
"emitCXXMemberOrOperatorMemberCallExpr: virtual call");
85+
return RValue::get(nullptr);
86+
}
87+
88+
bool trivialForCodegen =
89+
md->isTrivial() || (md->isDefaulted() && md->getParent()->isUnion());
90+
bool trivialAssignment =
91+
trivialForCodegen &&
92+
(md->isCopyAssignmentOperator() || md->isMoveAssignmentOperator()) &&
93+
!md->getParent()->mayInsertExtraPadding();
94+
(void)trivialAssignment;
95+
96+
// C++17 demands that we evaluate the RHS of a (possibly-compound) assignment
97+
// operator before the LHS.
98+
CallArgList rtlArgStorage;
99+
CallArgList *rtlArgs = nullptr;
100+
if (auto *oce = dyn_cast<CXXOperatorCallExpr>(ce)) {
101+
cgm.errorNYI(oce->getSourceRange(),
102+
"emitCXXMemberOrOperatorMemberCallExpr: operator call");
103+
return RValue::get(nullptr);
104+
}
105+
106+
LValue thisPtr;
107+
if (isArrow) {
108+
LValueBaseInfo baseInfo;
109+
assert(!cir::MissingFeatures::opTBAA());
110+
Address thisValue = emitPointerWithAlignment(base, &baseInfo);
111+
thisPtr = makeAddrLValue(thisValue, base->getType(), baseInfo);
112+
} else {
113+
thisPtr = emitLValue(base);
114+
}
115+
116+
if (const CXXConstructorDecl *ctor = dyn_cast<CXXConstructorDecl>(md)) {
117+
cgm.errorNYI(ce->getSourceRange(),
118+
"emitCXXMemberOrOperatorMemberCallExpr: constructor call");
119+
return RValue::get(nullptr);
120+
}
121+
122+
if (trivialForCodegen) {
123+
if (isa<CXXDestructorDecl>(md))
124+
return RValue::get(nullptr);
125+
126+
if (trivialAssignment) {
127+
cgm.errorNYI(ce->getSourceRange(),
128+
"emitCXXMemberOrOperatorMemberCallExpr: trivial assignment");
129+
return RValue::get(nullptr);
130+
} else {
131+
assert(md->getParent()->mayInsertExtraPadding() &&
132+
"unknown trivial member function");
133+
}
134+
}
135+
136+
// Compute the function type we're calling
137+
const CXXMethodDecl *calleeDecl = md;
138+
const CIRGenFunctionInfo *fInfo = nullptr;
139+
if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl)) {
140+
cgm.errorNYI(ce->getSourceRange(),
141+
"emitCXXMemberOrOperatorMemberCallExpr: destructor call");
142+
return RValue::get(nullptr);
143+
} else {
144+
fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl);
145+
}
146+
147+
mlir::Type ty = cgm.getTypes().getFunctionType(*fInfo);
148+
149+
assert(!cir::MissingFeatures::sanitizers());
150+
assert(!cir::MissingFeatures::emitTypeCheck());
151+
152+
if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl)) {
153+
cgm.errorNYI(ce->getSourceRange(),
154+
"emitCXXMemberOrOperatorMemberCallExpr: destructor call");
155+
return RValue::get(nullptr);
156+
}
157+
158+
assert(!cir::MissingFeatures::sanitizers());
159+
if (getLangOpts().AppleKext) {
160+
cgm.errorNYI(ce->getSourceRange(),
161+
"emitCXXMemberOrOperatorMemberCallExpr: AppleKext");
162+
return RValue::get(nullptr);
163+
}
164+
CIRGenCallee callee =
165+
CIRGenCallee::forDirect(cgm.getAddrOfFunction(md, ty), GlobalDecl(md));
166+
167+
return emitCXXMemberOrOperatorCall(
168+
calleeDecl, callee, returnValue, thisPtr.getPointer(),
169+
/*ImplicitParam=*/nullptr, QualType(), ce, rtlArgs);
170+
}
171+
172+
RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
173+
const CXXMethodDecl *md, const CIRGenCallee &callee,
174+
ReturnValueSlot returnValue, mlir::Value thisPtr, mlir::Value implicitParam,
175+
QualType implicitParamTy, const CallExpr *ce, CallArgList *rtlArgs) {
176+
const auto *fpt = md->getType()->castAs<FunctionProtoType>();
177+
CallArgList args;
178+
MemberCallInfo callInfo = commonBuildCXXMemberOrOperatorCall(
179+
*this, md, thisPtr, implicitParam, implicitParamTy, ce, args, rtlArgs);
180+
auto &fnInfo = cgm.getTypes().arrangeCXXMethodCall(
181+
args, fpt, callInfo.reqArgs, callInfo.prefixSize);
182+
assert((ce || currSrcLoc) && "expected source location");
183+
mlir::Location loc = ce ? getLoc(ce->getExprLoc()) : *currSrcLoc;
184+
assert(!cir::MissingFeatures::opCallMustTail());
185+
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
186+
}

0 commit comments

Comments
 (0)