//===--------------------------------------------------------------------===// // // 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(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(); }