blob: e59a1fdb837cb82063f19e116f87e37e69a0743c [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 C++ code generation of classes
//
//===----------------------------------------------------------------------===//
#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/CIR/MissingFeatures.h"
using namespace clang;
using namespace clang::CIRGen;
/// Checks whether the given constructor is a valid subject for the
/// complete-to-base constructor delegation optimization, i.e. emitting the
/// complete constructor as a simple call to the base constructor.
bool CIRGenFunction::isConstructorDelegationValid(
const CXXConstructorDecl *ctor) {
// Currently we disable the optimization for classes with virtual bases
// because (1) the address of parameter variables need to be consistent across
// all initializers but (2) the delegate function call necessarily creates a
// second copy of the parameter variable.
//
// The limiting example (purely theoretical AFAIK):
// struct A { A(int &c) { c++; } };
// struct A : virtual A {
// B(int count) : A(count) { printf("%d\n", count); }
// };
// ...although even this example could in principle be emitted as a delegation
// since the address of the parameter doesn't escape.
if (ctor->getParent()->getNumVBases())
return false;
// We also disable the optimization for variadic functions because it's
// impossible to "re-pass" varargs.
if (ctor->getType()->castAs<FunctionProtoType>()->isVariadic())
return false;
// FIXME: Decide if we can do a delegation of a delegating constructor.
if (ctor->isDelegatingConstructor())
return false;
return true;
}
/// This routine generates necessary code to initialize base classes and
/// non-static data members belonging to this constructor.
void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
CXXCtorType ctorType,
FunctionArgList &args) {
if (cd->isDelegatingConstructor())
return emitDelegatingCXXConstructorCall(cd, args);
if (cd->getNumCtorInitializers() != 0) {
// There's much more to do here.
cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: any initializer");
return;
}
}
Address CIRGenFunction::loadCXXThisAddress() {
assert(curFuncDecl && "loading 'this' without a func declaration?");
assert(isa<CXXMethodDecl>(curFuncDecl));
// Lazily compute CXXThisAlignment.
if (cxxThisAlignment.isZero()) {
// Just use the best known alignment for the parent.
// TODO: if we're currently emitting a complete-object ctor/dtor, we can
// always use the complete-object alignment.
auto rd = cast<CXXMethodDecl>(curFuncDecl)->getParent();
cxxThisAlignment = cgm.getClassPointerAlignment(rd);
}
return Address(loadCXXThis(), cxxThisAlignment);
}
void CIRGenFunction::emitDelegateCXXConstructorCall(
const CXXConstructorDecl *ctor, CXXCtorType ctorType,
const FunctionArgList &args, SourceLocation loc) {
CallArgList delegateArgs;
FunctionArgList::const_iterator i = args.begin(), e = args.end();
assert(i != e && "no parameters to constructor");
// this
Address thisAddr = loadCXXThisAddress();
delegateArgs.add(RValue::get(thisAddr.getPointer()), (*i)->getType());
++i;
// FIXME: The location of the VTT parameter in the parameter list is specific
// to the Itanium ABI and shouldn't be hardcoded here.
if (cgm.getCXXABI().needsVTTParameter(curGD)) {
cgm.errorNYI(loc, "emitDelegateCXXConstructorCall: VTT parameter");
return;
}
// Explicit arguments.
for (; i != e; ++i) {
const VarDecl *param = *i;
// FIXME: per-argument source location
emitDelegateCallArg(delegateArgs, param, loc);
}
assert(!cir::MissingFeatures::sanitizers());
emitCXXConstructorCall(ctor, ctorType, /*ForVirtualBase=*/false,
/*Delegating=*/true, thisAddr, delegateArgs, loc);
}
void CIRGenFunction::emitDelegatingCXXConstructorCall(
const CXXConstructorDecl *ctor, const FunctionArgList &args) {
assert(ctor->isDelegatingConstructor());
Address thisPtr = loadCXXThisAddress();
assert(!cir::MissingFeatures::objCGC());
assert(!cir::MissingFeatures::sanitizers());
AggValueSlot aggSlot = AggValueSlot::forAddr(
thisPtr, Qualifiers(), AggValueSlot::IsDestructed,
AggValueSlot::IsNotAliased, AggValueSlot::MayOverlap,
AggValueSlot::IsNotZeroed);
emitAggExpr(ctor->init_begin()[0]->getInit(), aggSlot);
const CXXRecordDecl *classDecl = ctor->getParent();
if (cgm.getLangOpts().Exceptions && !classDecl->hasTrivialDestructor()) {
cgm.errorNYI(ctor->getSourceRange(),
"emitDelegatingCXXConstructorCall: exception");
return;
}
}
Address CIRGenFunction::getAddressOfBaseClass(
Address value, const CXXRecordDecl *derived,
llvm::iterator_range<CastExpr::path_const_iterator> path,
bool nullCheckValue, SourceLocation loc) {
assert(!path.empty() && "Base path should not be empty!");
if ((*path.begin())->isVirtual()) {
// The implementation here is actually complete, but let's flag this
// as an error until the rest of the virtual base class support is in place.
cgm.errorNYI(loc, "getAddrOfBaseClass: virtual base");
return Address::invalid();
}
// Compute the static offset of the ultimate destination within its
// allocating subobject (the virtual base, if there is one, or else
// the "complete" object that we see).
CharUnits nonVirtualOffset =
cgm.computeNonVirtualBaseClassOffset(derived, path);
// Get the base pointer type.
mlir::Type baseValueTy = convertType((path.end()[-1])->getType());
assert(!cir::MissingFeatures::addressSpace());
// The if statement here is redundant now, but it will be needed when we add
// support for virtual base classes.
// If there is no virtual base, use cir.base_class_addr. It takes care of
// the adjustment and the null pointer check.
if (nonVirtualOffset.isZero()) {
assert(!cir::MissingFeatures::sanitizers());
return builder.createBaseClassAddr(getLoc(loc), value, baseValueTy, 0,
/*assumeNotNull=*/true);
}
assert(!cir::MissingFeatures::sanitizers());
// Apply the offset
value = builder.createBaseClassAddr(getLoc(loc), value, baseValueTy,
nonVirtualOffset.getQuantity(),
/*assumeNotNull=*/true);
// Cast to the destination type.
value = value.withElementType(builder, baseValueTy);
return value;
}
void CIRGenFunction::emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
clang::CXXCtorType type,
bool forVirtualBase,
bool delegating,
AggValueSlot thisAVS,
const clang::CXXConstructExpr *e) {
CallArgList args;
Address thisAddr = thisAVS.getAddress();
QualType thisType = d->getThisType();
mlir::Value thisPtr = thisAddr.getPointer();
assert(!cir::MissingFeatures::addressSpace());
args.add(RValue::get(thisPtr), thisType);
// In LLVM Codegen: If this is a trivial constructor, just emit what's needed.
// If this is a union copy constructor, we must emit a memcpy, because the AST
// does not model that copy.
assert(!cir::MissingFeatures::isMemcpyEquivalentSpecialMember());
const FunctionProtoType *fpt = d->getType()->castAs<FunctionProtoType>();
assert(!cir::MissingFeatures::opCallArgEvaluationOrder());
emitCallArgs(args, fpt, e->arguments(), e->getConstructor(),
/*ParamsToSkip=*/0);
assert(!cir::MissingFeatures::sanitizers());
emitCXXConstructorCall(d, type, forVirtualBase, delegating, thisAddr, args,
e->getExprLoc());
}
void CIRGenFunction::emitCXXConstructorCall(
const CXXConstructorDecl *d, CXXCtorType type, bool forVirtualBase,
bool delegating, Address thisAddr, CallArgList &args, SourceLocation loc) {
const CXXRecordDecl *crd = d->getParent();
// If this is a call to a trivial default constructor:
// In LLVM: do nothing.
// In CIR: emit as a regular call, other later passes should lower the
// ctor call into trivial initialization.
assert(!cir::MissingFeatures::isTrivialCtorOrDtor());
assert(!cir::MissingFeatures::isMemcpyEquivalentSpecialMember());
bool passPrototypeArgs = true;
// Check whether we can actually emit the constructor before trying to do so.
if (d->getInheritedConstructor()) {
cgm.errorNYI(d->getSourceRange(),
"emitCXXConstructorCall: inherited constructor");
return;
}
// Insert any ABI-specific implicit constructor arguments.
assert(!cir::MissingFeatures::implicitConstructorArgs());
// Emit the call.
auto calleePtr = cgm.getAddrOfCXXStructor(GlobalDecl(d, type));
const CIRGenFunctionInfo &info = cgm.getTypes().arrangeCXXConstructorCall(
args, d, type, passPrototypeArgs);
CIRGenCallee callee = CIRGenCallee::forDirect(calleePtr, GlobalDecl(d, type));
cir::CIRCallOpInterface c;
emitCall(info, callee, ReturnValueSlot(), args, &c, getLoc(loc));
if (cgm.getCodeGenOpts().OptimizationLevel != 0 && !crd->isDynamicClass() &&
type != Ctor_Base && cgm.getCodeGenOpts().StrictVTablePointers)
cgm.errorNYI(d->getSourceRange(), "vtable assumption loads");
}