blob: d3888baea5d5e00bc63185a97c44e986ac61c34e [file] [log] [blame]
//===--- CIRGenExprCXX.cpp - Emit CIR Code for C++ expressions ------------===//
//
// 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 contains code dealing with code generation of C++ expressions
//
//===----------------------------------------------------------------------===//
#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/CIR/MissingFeatures.h"
using namespace clang;
using namespace clang::CIRGen;
namespace {
struct MemberCallInfo {
RequiredArgs reqArgs;
// Number of prefix arguments for the call. Ignores the `this` pointer.
unsigned prefixSize;
};
} // namespace
static MemberCallInfo commonBuildCXXMemberOrOperatorCall(
CIRGenFunction &cgf, const CXXMethodDecl *md, mlir::Value thisPtr,
mlir::Value implicitParam, QualType implicitParamTy, const CallExpr *ce,
CallArgList &args, CallArgList *rtlArgs) {
assert(ce == nullptr || isa<CXXMemberCallExpr>(ce) ||
isa<CXXOperatorCallExpr>(ce));
assert(md->isInstance() &&
"Trying to emit a member or operator call expr on a static method!");
// Push the this ptr.
const CXXRecordDecl *rd =
cgf.cgm.getCXXABI().getThisArgumentTypeForMethod(md);
args.add(RValue::get(thisPtr), cgf.getTypes().deriveThisType(rd, md));
// If there is an implicit parameter (e.g. VTT), emit it.
if (implicitParam) {
args.add(RValue::get(implicitParam), implicitParamTy);
}
const auto *fpt = md->getType()->castAs<FunctionProtoType>();
RequiredArgs required =
RequiredArgs::getFromProtoWithExtraSlots(fpt, args.size());
unsigned prefixSize = args.size() - 1;
// Add the rest of the call args
if (rtlArgs) {
// Special case: if the caller emitted the arguments right-to-left already
// (prior to emitting the *this argument), we're done. This happens for
// assignment operators.
args.addFrom(*rtlArgs);
} else if (ce) {
// Special case: skip first argument of CXXOperatorCall (it is "this").
unsigned argsToSkip = isa<CXXOperatorCallExpr>(ce) ? 1 : 0;
cgf.emitCallArgs(args, fpt, drop_begin(ce->arguments(), argsToSkip),
ce->getDirectCallee());
} else {
assert(
fpt->getNumParams() == 0 &&
"No CallExpr specified for function with non-zero number of arguments");
}
// return {required, prefixSize};
return {required, prefixSize};
}
RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
const CallExpr *ce, const CXXMethodDecl *md, ReturnValueSlot returnValue,
bool hasQualifier, NestedNameSpecifier *qualifier, bool isArrow,
const Expr *base) {
assert(isa<CXXMemberCallExpr>(ce) || isa<CXXOperatorCallExpr>(ce));
if (md->isVirtual()) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: virtual call");
return RValue::get(nullptr);
}
bool trivialForCodegen =
md->isTrivial() || (md->isDefaulted() && md->getParent()->isUnion());
bool trivialAssignment =
trivialForCodegen &&
(md->isCopyAssignmentOperator() || md->isMoveAssignmentOperator()) &&
!md->getParent()->mayInsertExtraPadding();
(void)trivialAssignment;
// C++17 demands that we evaluate the RHS of a (possibly-compound) assignment
// operator before the LHS.
CallArgList rtlArgStorage;
CallArgList *rtlArgs = nullptr;
if (auto *oce = dyn_cast<CXXOperatorCallExpr>(ce)) {
if (oce->isAssignmentOp()) {
cgm.errorNYI(
oce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: assignment operator");
}
}
LValue thisPtr;
if (isArrow) {
LValueBaseInfo baseInfo;
assert(!cir::MissingFeatures::opTBAA());
Address thisValue = emitPointerWithAlignment(base, &baseInfo);
thisPtr = makeAddrLValue(thisValue, base->getType(), baseInfo);
} else {
thisPtr = emitLValue(base);
}
if (isa<CXXConstructorDecl>(md)) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: constructor call");
return RValue::get(nullptr);
}
if (trivialForCodegen) {
if (isa<CXXDestructorDecl>(md))
return RValue::get(nullptr);
if (trivialAssignment) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: trivial assignment");
return RValue::get(nullptr);
}
assert(md->getParent()->mayInsertExtraPadding() &&
"unknown trivial member function");
}
// Compute the function type we're calling
const CXXMethodDecl *calleeDecl = md;
const CIRGenFunctionInfo *fInfo = nullptr;
if (isa<CXXDestructorDecl>(calleeDecl)) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: destructor call");
return RValue::get(nullptr);
}
fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl);
mlir::Type ty = cgm.getTypes().getFunctionType(*fInfo);
assert(!cir::MissingFeatures::sanitizers());
assert(!cir::MissingFeatures::emitTypeCheck());
if (isa<CXXDestructorDecl>(calleeDecl)) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: destructor call");
return RValue::get(nullptr);
}
assert(!cir::MissingFeatures::sanitizers());
if (getLangOpts().AppleKext) {
cgm.errorNYI(ce->getSourceRange(),
"emitCXXMemberOrOperatorMemberCallExpr: AppleKext");
return RValue::get(nullptr);
}
CIRGenCallee callee =
CIRGenCallee::forDirect(cgm.getAddrOfFunction(md, ty), GlobalDecl(md));
return emitCXXMemberOrOperatorCall(
calleeDecl, callee, returnValue, thisPtr.getPointer(),
/*ImplicitParam=*/nullptr, QualType(), ce, rtlArgs);
}
RValue
CIRGenFunction::emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e,
const CXXMethodDecl *md,
ReturnValueSlot returnValue) {
assert(md->isInstance() &&
"Trying to emit a member call expr on a static method!");
return emitCXXMemberOrOperatorMemberCallExpr(
e, md, returnValue, /*HasQualifier=*/false, /*Qualifier=*/nullptr,
/*IsArrow=*/false, e->getArg(0));
}
RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
const CXXMethodDecl *md, const CIRGenCallee &callee,
ReturnValueSlot returnValue, mlir::Value thisPtr, mlir::Value implicitParam,
QualType implicitParamTy, const CallExpr *ce, CallArgList *rtlArgs) {
const auto *fpt = md->getType()->castAs<FunctionProtoType>();
CallArgList args;
MemberCallInfo callInfo = commonBuildCXXMemberOrOperatorCall(
*this, md, thisPtr, implicitParam, implicitParamTy, ce, args, rtlArgs);
auto &fnInfo = cgm.getTypes().arrangeCXXMethodCall(
args, fpt, callInfo.reqArgs, callInfo.prefixSize);
assert((ce || currSrcLoc) && "expected source location");
mlir::Location loc = ce ? getLoc(ce->getExprLoc()) : *currSrcLoc;
assert(!cir::MissingFeatures::opCallMustTail());
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
}