blob: 5a067f8ec038385df188a3cd8e5ccd3fa2ad9097 [file] [log] [blame] [edit]
//===--------------------------------------------------------------------===//
//
// 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 file provides Itanium C++ ABI specific code
// that is used during LLVMIR lowering prepare.
//
//===--------------------------------------------------------------------===//
#include "LoweringPrepareCXXABI.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Value.h"
#include "mlir/IR/ValueRange.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/MissingFeatures.h"
class LoweringPrepareItaniumCXXABI : public cir::LoweringPrepareCXXABI {
public:
mlir::Value lowerDynamicCast(cir::CIRBaseBuilderTy &builder,
clang::ASTContext &astCtx,
cir::DynamicCastOp op) override;
};
cir::LoweringPrepareCXXABI *cir::LoweringPrepareCXXABI::createItaniumABI() {
return new LoweringPrepareItaniumCXXABI();
}
static void buildBadCastCall(cir::CIRBaseBuilderTy &builder, mlir::Location loc,
mlir::FlatSymbolRefAttr badCastFuncRef) {
builder.createCallOp(loc, badCastFuncRef, cir::VoidType(),
mlir::ValueRange{});
// TODO(cir): Set the 'noreturn' attribute on the function.
assert(!cir::MissingFeatures::opFuncNoReturn());
cir::UnreachableOp::create(builder, loc);
builder.clearInsertionPoint();
}
static mlir::Value
buildDynamicCastAfterNullCheck(cir::CIRBaseBuilderTy &builder,
cir::DynamicCastOp op) {
mlir::Location loc = op->getLoc();
mlir::Value srcValue = op.getSrc();
cir::DynamicCastInfoAttr castInfo = op.getInfo().value();
// TODO(cir): consider address space
assert(!cir::MissingFeatures::addressSpace());
mlir::Value srcPtr = builder.createBitcast(srcValue, builder.getVoidPtrTy());
cir::ConstantOp srcRtti = builder.getConstant(loc, castInfo.getSrcRtti());
cir::ConstantOp destRtti = builder.getConstant(loc, castInfo.getDestRtti());
cir::ConstantOp offsetHint =
builder.getConstant(loc, castInfo.getOffsetHint());
mlir::FlatSymbolRefAttr dynCastFuncRef = castInfo.getRuntimeFunc();
mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint};
mlir::Value castedPtr =
builder
.createCallOp(loc, dynCastFuncRef, builder.getVoidPtrTy(),
dynCastFuncArgs)
.getResult();
assert(mlir::isa<cir::PointerType>(castedPtr.getType()) &&
"the return value of __dynamic_cast should be a ptr");
/// C++ [expr.dynamic.cast]p9:
/// A failed cast to reference type throws std::bad_cast
if (op.isRefCast()) {
// Emit a cir.if that checks the casted value.
mlir::Value castedValueIsNull = builder.createPtrIsNull(castedPtr);
cir::IfOp::create(builder, loc, castedValueIsNull, false,
[&](mlir::OpBuilder &, mlir::Location) {
buildBadCastCall(builder, loc,
castInfo.getBadCastFunc());
});
}
// Note that castedPtr is a void*. Cast it to a pointer to the destination
// type before return.
return builder.createBitcast(castedPtr, op.getType());
}
static mlir::Value
buildDynamicCastToVoidAfterNullCheck(cir::CIRBaseBuilderTy &builder,
clang::ASTContext &astCtx,
cir::DynamicCastOp op) {
mlir::Location loc = op.getLoc();
bool vtableUsesRelativeLayout = op.getRelativeLayout();
// TODO(cir): consider address space in this function.
assert(!cir::MissingFeatures::addressSpace());
mlir::Type vtableElemTy;
uint64_t vtableElemAlign;
if (vtableUsesRelativeLayout) {
vtableElemTy = builder.getSIntNTy(32);
vtableElemAlign = 4;
} else {
const auto &targetInfo = astCtx.getTargetInfo();
auto ptrdiffTy = targetInfo.getPtrDiffType(clang::LangAS::Default);
bool ptrdiffTyIsSigned = clang::TargetInfo::isTypeSigned(ptrdiffTy);
uint64_t ptrdiffTyWidth = targetInfo.getTypeWidth(ptrdiffTy);
vtableElemTy = cir::IntType::get(builder.getContext(), ptrdiffTyWidth,
ptrdiffTyIsSigned);
vtableElemAlign =
llvm::divideCeil(targetInfo.getPointerAlign(clang::LangAS::Default), 8);
}
// Access vtable to get the offset from the given object to its containing
// complete object.
// TODO: Add a specialized operation to get the object offset?
auto vptrTy = cir::VPtrType::get(builder.getContext());
cir::PointerType vptrPtrTy = builder.getPointerTo(vptrTy);
auto vptrPtr =
cir::VTableGetVPtrOp::create(builder, loc, vptrPtrTy, op.getSrc());
mlir::Value vptr = builder.createLoad(loc, vptrPtr);
mlir::Value elementPtr =
builder.createBitcast(vptr, builder.getPointerTo(vtableElemTy));
mlir::Value minusTwo = builder.getSignedInt(loc, -2, 64);
auto offsetToTopSlotPtr = cir::PtrStrideOp::create(
builder, loc, builder.getPointerTo(vtableElemTy), elementPtr, minusTwo);
mlir::Value offsetToTop =
builder.createAlignedLoad(loc, offsetToTopSlotPtr, vtableElemAlign);
// Add the offset to the given pointer to get the cast result.
// Cast the input pointer to a uint8_t* to allow pointer arithmetic.
cir::PointerType u8PtrTy = builder.getPointerTo(builder.getUIntNTy(8));
mlir::Value srcBytePtr = builder.createBitcast(op.getSrc(), u8PtrTy);
auto dstBytePtr =
cir::PtrStrideOp::create(builder, loc, u8PtrTy, srcBytePtr, offsetToTop);
// Cast the result to a void*.
return builder.createBitcast(dstBytePtr, builder.getVoidPtrTy());
}
mlir::Value
LoweringPrepareItaniumCXXABI::lowerDynamicCast(cir::CIRBaseBuilderTy &builder,
clang::ASTContext &astCtx,
cir::DynamicCastOp op) {
mlir::Location loc = op->getLoc();
mlir::Value srcValue = op.getSrc();
assert(!cir::MissingFeatures::emitTypeCheck());
if (op.isRefCast())
return buildDynamicCastAfterNullCheck(builder, op);
mlir::Value srcValueIsNotNull = builder.createPtrToBoolCast(srcValue);
return cir::TernaryOp::create(
builder, loc, srcValueIsNotNull,
[&](mlir::OpBuilder &, mlir::Location) {
mlir::Value castedValue =
op.isCastToVoid()
? buildDynamicCastToVoidAfterNullCheck(builder, astCtx,
op)
: buildDynamicCastAfterNullCheck(builder, op);
builder.createYield(loc, castedValue);
},
[&](mlir::OpBuilder &, mlir::Location) {
builder.createYield(
loc, builder.getNullPtr(op.getType(), loc).getResult());
})
.getResult();
}