Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,12 @@ struct MissingFeatures {
static bool opFuncExtraAttrs() { return false; }
static bool opFuncMaybeHandleStaticInExternC() { return false; }
static bool opFuncMultipleReturnVals() { return false; }
static bool opFuncNoUnwind() { return false; }
static bool opFuncOperandBundles() { return false; }
static bool opFuncParameterAttributes() { return false; }
static bool opFuncReadOnly() { return false; }
static bool opFuncSection() { return false; }
static bool opFuncWillReturn() { return false; }
static bool setLLVMFunctionFEnvAttributes() { return false; }
static bool setFunctionAttributes() { return false; }

Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return getPointerTo(cir::VPtrType::get(getContext()));
}

cir::FuncType getFuncType(llvm::ArrayRef<mlir::Type> params, mlir::Type retTy,
bool isVarArg = false) {
return cir::FuncType::get(params, retTy, isVarArg);
}

/// Get a CIR record kind from a AST declaration tag.
cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
switch (kind) {
Expand Down Expand Up @@ -372,6 +377,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return cir::BinOp::create(*this, loc, cir::BinOpKind::Div, lhs, rhs);
}

mlir::Value createDynCast(mlir::Location loc, mlir::Value src,
cir::PointerType destType, bool isRefCast,
cir::DynamicCastInfoAttr info) {
auto castKind =
isRefCast ? cir::DynamicCastKind::Ref : cir::DynamicCastKind::Ptr;
return cir::DynamicCastOp::create(*this, loc, destType, castKind, src, info,
/*relative_layout=*/false);
}

Address createBaseClassAddr(mlir::Location loc, Address addr,
mlir::Type destType, unsigned offset,
bool assumeNotNull) {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ class CIRGenCXXABI {
Address thisAddr, const CXXRecordDecl *classDecl,
const CXXRecordDecl *baseClassDecl) = 0;

virtual mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
QualType srcRecordTy,
QualType destRecordTy,
cir::PointerType destCIRTy,
bool isRefCast, Address src) = 0;

public:
/// Similar to AddedStructorArgs, but only notes the number of additional
/// arguments.
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1185,10 +1185,16 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
case CK_BuiltinFnToFnPtr:
llvm_unreachable("builtin functions are handled elsewhere");

case CK_Dynamic: {
LValue lv = emitLValue(e->getSubExpr());
Address v = lv.getAddress();
const auto *dce = cast<CXXDynamicCastExpr>(e);
return makeNaturalAlignAddrLValue(emitDynamicCast(v, dce), e->getType());
}

// These are never l-values; just use the aggregate emission code.
case CK_NonAtomicToAtomic:
case CK_AtomicToNonAtomic:
case CK_Dynamic:
case CK_ToUnion:
case CK_BaseToDerived:
case CK_AddressSpaceConversion:
Expand Down
40 changes: 40 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -728,3 +728,43 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD,
// Emit the call to delete.
emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs);
}

mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
const CXXDynamicCastExpr *dce) {
mlir::Location loc = getLoc(dce->getSourceRange());

cgm.emitExplicitCastExprType(dce, this);
QualType destTy = dce->getTypeAsWritten();
QualType srcTy = dce->getSubExpr()->getType();

// C++ [expr.dynamic.cast]p7:
// If T is "pointer to cv void," then the result is a pointer to the most
// derived object pointed to by v.
bool isDynCastToVoid = destTy->isVoidPointerType();
bool isRefCast = destTy->isReferenceType();

QualType srcRecordTy;
QualType destRecordTy;
if (isDynCastToVoid) {
srcRecordTy = srcTy->getPointeeType();
// No destRecordTy.
} else if (const PointerType *destPTy = destTy->getAs<PointerType>()) {
srcRecordTy = srcTy->castAs<PointerType>()->getPointeeType();
destRecordTy = destPTy->getPointeeType();
} else {
srcRecordTy = srcTy;
destRecordTy = destTy->castAs<ReferenceType>()->getPointeeType();
}

assert(srcRecordTy->isRecordType() && "source type must be a record type!");
assert(!cir::MissingFeatures::emitTypeCheck());

if (dce->isAlwaysNull()) {
cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull");
return {};
}

auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy));
return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
destCirTy, isRefCast, thisAddr);
}
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1894,6 +1894,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
return v;
}

case CK_Dynamic: {
Address v = cgf.emitPointerWithAlignment(subExpr);
const auto *dce = cast<CXXDynamicCastExpr>(ce);
return cgf.emitDynamicCast(v, dce);
}
case CK_ArrayToPointerDecay:
return cgf.emitArrayToPointerDecay(subExpr).getPointer();

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,8 @@ class CIRGenFunction : public CIRGenTypeCache {

mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);

mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce);

/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
Expand Down
150 changes: 150 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
Address thisAddr, const CXXRecordDecl *classDecl,
const CXXRecordDecl *baseClassDecl) override;

// The traditional clang CodeGen emits calls to `__dynamic_cast` directly into
// LLVM in the `emitDynamicCastCall` function. In CIR, `dynamic_cast`
// expressions are lowered to `cir.dyn_cast` ops instead of calls to runtime
// functions. So during CIRGen we don't need the `emitDynamicCastCall`
// function that clang CodeGen has.
mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
QualType srcRecordTy, QualType destRecordTy,
cir::PointerType destCIRTy, bool isRefCast,
Address src) override;

/**************************** RTTI Uniqueness ******************************/
protected:
/// Returns true if the ABI requires RTTI type_info objects to be unique
Expand Down Expand Up @@ -1796,3 +1806,143 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
}
return vbaseOffset;
}

static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
// Prototype: void __cxa_bad_cast();

// TODO(cir): set the calling convention of the runtime function.
assert(!cir::MissingFeatures::opFuncCallingConv());

cir::FuncType fnTy =
cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy());
return cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_cast");
}

// TODO(cir): This could be shared with classic codegen.
static CharUnits computeOffsetHint(ASTContext &astContext,
const CXXRecordDecl *src,
const CXXRecordDecl *dst) {
CXXBasePaths paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
/*DetectVirtual=*/false);

// If Dst is not derived from Src we can skip the whole computation below and
// return that Src is not a public base of Dst. Record all inheritance paths.
if (!dst->isDerivedFrom(src, paths))
return CharUnits::fromQuantity(-2ULL);

unsigned numPublicPaths = 0;
CharUnits offset;

// Now walk all possible inheritance paths.
for (const CXXBasePath &path : paths) {
if (path.Access != AS_public) // Ignore non-public inheritance.
continue;

++numPublicPaths;

for (const CXXBasePathElement &pathElement : path) {
// If the path contains a virtual base class we can't give any hint.
// -1: no hint.
if (pathElement.Base->isVirtual())
return CharUnits::fromQuantity(-1ULL);

if (numPublicPaths > 1) // Won't use offsets, skip computation.
continue;

// Accumulate the base class offsets.
const ASTRecordLayout &L =
astContext.getASTRecordLayout(pathElement.Class);
offset += L.getBaseClassOffset(
pathElement.Base->getType()->getAsCXXRecordDecl());
}
}

// -2: Src is not a public base of Dst.
if (numPublicPaths == 0)
return CharUnits::fromQuantity(-2ULL);

// -3: Src is a multiple public base type but never a virtual base type.
if (numPublicPaths > 1)
return CharUnits::fromQuantity(-3ULL);

// Otherwise, the Src type is a unique public nonvirtual base type of Dst.
// Return the offset of Src from the origin of Dst.
return offset;
}

static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
// Prototype:
// void *__dynamic_cast(const void *sub,
// global_as const abi::__class_type_info *src,
// global_as const abi::__class_type_info *dst,
// std::ptrdiff_t src2dst_offset);

mlir::Type voidPtrTy = cgf.getBuilder().getVoidPtrTy();
mlir::Type rttiPtrTy = cgf.getBuilder().getUInt8PtrTy();
mlir::Type ptrDiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());

// TODO(cir): mark the function as nowind willreturn readonly.
assert(!cir::MissingFeatures::opFuncNoUnwind());
assert(!cir::MissingFeatures::opFuncWillReturn());
assert(!cir::MissingFeatures::opFuncReadOnly());

// TODO(cir): set the calling convention of the runtime function.
assert(!cir::MissingFeatures::opFuncCallingConv());

cir::FuncType FTy = cgf.getBuilder().getFuncType(
{voidPtrTy, rttiPtrTy, rttiPtrTy, ptrDiffTy}, voidPtrTy);
return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
}

static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf,
mlir::Location loc,
QualType srcRecordTy,
QualType destRecordTy) {
auto srcRtti = mlir::cast<cir::GlobalViewAttr>(
cgf.cgm.getAddrOfRTTIDescriptor(loc, srcRecordTy));
auto destRtti = mlir::cast<cir::GlobalViewAttr>(
cgf.cgm.getAddrOfRTTIDescriptor(loc, destRecordTy));

cir::FuncOp runtimeFuncOp = getItaniumDynamicCastFn(cgf);
cir::FuncOp badCastFuncOp = getBadCastFn(cgf);
auto runtimeFuncRef = mlir::FlatSymbolRefAttr::get(runtimeFuncOp);
auto badCastFuncRef = mlir::FlatSymbolRefAttr::get(badCastFuncOp);

const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl();
const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl();
CharUnits offsetHint = computeOffsetHint(cgf.getContext(), srcDecl, destDecl);

mlir::Type ptrdiffTy = cgf.convertType(cgf.getContext().getPointerDiffType());
auto offsetHintAttr = cir::IntAttr::get(ptrdiffTy, offsetHint.getQuantity());

return cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef,
badCastFuncRef, offsetHintAttr);
}

mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
mlir::Location loc,
QualType srcRecordTy,
QualType destRecordTy,
cir::PointerType destCIRTy,
bool isRefCast, Address src) {
bool isCastToVoid = destRecordTy.isNull();
assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference");

if (isCastToVoid) {
cgm.errorNYI(loc, "emitDynamicCastToVoid");
return {};
}

// If the destination is effectively final, the cast succeeds if and only
// if the dynamic type of the pointer is exactly the destination type.
if (destRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() &&
cgf.cgm.getCodeGenOpts().OptimizationLevel > 0) {
cgm.errorNYI(loc, "emitExactDynamicCast");
return {};
}

cir::DynamicCastInfoAttr castInfo =
emitDynamicCastInfo(cgf, loc, srcRecordTy, destRecordTy);
return cgf.getBuilder().createDynCast(loc, src.getPointer(), destCIRTy,
isRefCast, castInfo);
}
23 changes: 23 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2079,6 +2079,29 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
return fnOp;
}

cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
StringRef name, mlir::ArrayAttr,
[[maybe_unused]] bool isLocal,
bool assumeConvergent) {
if (assumeConvergent)
errorNYI("createRuntimeFunction: assumeConvergent");
if (isLocal)
errorNYI("createRuntimeFunction: local");

cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(),
/*forVtable=*/false);

if (entry) {
// TODO(cir): set the attributes of the function.
assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
assert(!cir::MissingFeatures::opFuncCallingConv());
assert(!cir::MissingFeatures::opGlobalDLLImportExport());
entry.setDSOLocal(true);
}

return entry;
}

mlir::SymbolTable::Visibility
CIRGenModule::getMLIRVisibility(cir::GlobalOp op) {
// MLIR doesn't accept public symbols declarations (only
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ class CIRGenModule : public CIRGenTypeCache {
cir::FuncType ty,
const clang::FunctionDecl *fd);

cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
mlir::ArrayAttr = {}, bool isLocal = false,
bool assumeConvergent = false);

static constexpr const char *builtinCoroId = "__builtin_coro_id";

/// Given a builtin id for a function like "__builtin_fabsf", return a
Expand Down
35 changes: 33 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenVTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,40 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) {
llvm_unreachable("Should not have been asked to emit this");
}
}
// -fapple-kext mode does not support weak linkage, so we must use
// internal linkage.
if (astContext.getLangOpts().AppleKext)
return cir::GlobalLinkageKind::InternalLinkage;

auto discardableODRLinkage = cir::GlobalLinkageKind::LinkOnceODRLinkage;
auto nonDiscardableODRLinkage = cir::GlobalLinkageKind::WeakODRLinkage;
if (rd->hasAttr<DLLExportAttr>()) {
// Cannot discard exported vtables.
discardableODRLinkage = nonDiscardableODRLinkage;
} else if (rd->hasAttr<DLLImportAttr>()) {
// Imported vtables are available externally.
discardableODRLinkage = cir::GlobalLinkageKind::AvailableExternallyLinkage;
nonDiscardableODRLinkage =
cir::GlobalLinkageKind::AvailableExternallyLinkage;
}

switch (rd->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
return discardableODRLinkage;

case TSK_ExplicitInstantiationDeclaration: {
errorNYI(rd->getSourceRange(),
"getVTableLinkage: explicit instantiation declaration");
return cir::GlobalLinkageKind::ExternalLinkage;
}

case TSK_ExplicitInstantiationDefinition:
return nonDiscardableODRLinkage;
}

errorNYI(rd->getSourceRange(), "getVTableLinkage: no key function");
return cir::GlobalLinkageKind::ExternalLinkage;
llvm_unreachable("Invalid TemplateSpecializationKind!");
}

cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *rd) {
Expand Down
Loading