174 lines · cpp
1//===--------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with4// LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===--------------------------------------------------------------------===//9//10// This file provides Itanium C++ ABI specific code11// that is used during LLVMIR lowering prepare.12//13//===--------------------------------------------------------------------===//14 15#include "LoweringPrepareCXXABI.h"16#include "mlir/IR/BuiltinAttributes.h"17#include "mlir/IR/Value.h"18#include "mlir/IR/ValueRange.h"19#include "clang/Basic/TargetInfo.h"20#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"21#include "clang/CIR/Dialect/IR/CIRAttrs.h"22#include "clang/CIR/Dialect/IR/CIRDataLayout.h"23#include "clang/CIR/Dialect/IR/CIRDialect.h"24#include "clang/CIR/MissingFeatures.h"25 26class LoweringPrepareItaniumCXXABI : public cir::LoweringPrepareCXXABI {27public:28 mlir::Value lowerDynamicCast(cir::CIRBaseBuilderTy &builder,29 clang::ASTContext &astCtx,30 cir::DynamicCastOp op) override;31};32 33cir::LoweringPrepareCXXABI *cir::LoweringPrepareCXXABI::createItaniumABI() {34 return new LoweringPrepareItaniumCXXABI();35}36 37static void buildBadCastCall(cir::CIRBaseBuilderTy &builder, mlir::Location loc,38 mlir::FlatSymbolRefAttr badCastFuncRef) {39 builder.createCallOp(loc, badCastFuncRef, cir::VoidType(),40 mlir::ValueRange{});41 // TODO(cir): Set the 'noreturn' attribute on the function.42 assert(!cir::MissingFeatures::opFuncNoReturn());43 cir::UnreachableOp::create(builder, loc);44 builder.clearInsertionPoint();45}46 47static mlir::Value48buildDynamicCastAfterNullCheck(cir::CIRBaseBuilderTy &builder,49 cir::DynamicCastOp op) {50 mlir::Location loc = op->getLoc();51 mlir::Value srcValue = op.getSrc();52 cir::DynamicCastInfoAttr castInfo = op.getInfo().value();53 54 // TODO(cir): consider address space55 assert(!cir::MissingFeatures::addressSpace());56 57 mlir::Value srcPtr = builder.createBitcast(srcValue, builder.getVoidPtrTy());58 cir::ConstantOp srcRtti = builder.getConstant(loc, castInfo.getSrcRtti());59 cir::ConstantOp destRtti = builder.getConstant(loc, castInfo.getDestRtti());60 cir::ConstantOp offsetHint =61 builder.getConstant(loc, castInfo.getOffsetHint());62 63 mlir::FlatSymbolRefAttr dynCastFuncRef = castInfo.getRuntimeFunc();64 mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint};65 66 mlir::Value castedPtr =67 builder68 .createCallOp(loc, dynCastFuncRef, builder.getVoidPtrTy(),69 dynCastFuncArgs)70 .getResult();71 72 assert(mlir::isa<cir::PointerType>(castedPtr.getType()) &&73 "the return value of __dynamic_cast should be a ptr");74 75 /// C++ [expr.dynamic.cast]p9:76 /// A failed cast to reference type throws std::bad_cast77 if (op.isRefCast()) {78 // Emit a cir.if that checks the casted value.79 mlir::Value castedValueIsNull = builder.createPtrIsNull(castedPtr);80 cir::IfOp::create(builder, loc, castedValueIsNull, false,81 [&](mlir::OpBuilder &, mlir::Location) {82 buildBadCastCall(builder, loc,83 castInfo.getBadCastFunc());84 });85 }86 87 // Note that castedPtr is a void*. Cast it to a pointer to the destination88 // type before return.89 return builder.createBitcast(castedPtr, op.getType());90}91 92static mlir::Value93buildDynamicCastToVoidAfterNullCheck(cir::CIRBaseBuilderTy &builder,94 clang::ASTContext &astCtx,95 cir::DynamicCastOp op) {96 mlir::Location loc = op.getLoc();97 bool vtableUsesRelativeLayout = op.getRelativeLayout();98 99 // TODO(cir): consider address space in this function.100 assert(!cir::MissingFeatures::addressSpace());101 102 mlir::Type vtableElemTy;103 uint64_t vtableElemAlign;104 if (vtableUsesRelativeLayout) {105 vtableElemTy = builder.getSIntNTy(32);106 vtableElemAlign = 4;107 } else {108 const auto &targetInfo = astCtx.getTargetInfo();109 auto ptrdiffTy = targetInfo.getPtrDiffType(clang::LangAS::Default);110 bool ptrdiffTyIsSigned = clang::TargetInfo::isTypeSigned(ptrdiffTy);111 uint64_t ptrdiffTyWidth = targetInfo.getTypeWidth(ptrdiffTy);112 113 vtableElemTy = cir::IntType::get(builder.getContext(), ptrdiffTyWidth,114 ptrdiffTyIsSigned);115 vtableElemAlign =116 llvm::divideCeil(targetInfo.getPointerAlign(clang::LangAS::Default), 8);117 }118 119 // Access vtable to get the offset from the given object to its containing120 // complete object.121 // TODO: Add a specialized operation to get the object offset?122 auto vptrTy = cir::VPtrType::get(builder.getContext());123 cir::PointerType vptrPtrTy = builder.getPointerTo(vptrTy);124 auto vptrPtr =125 cir::VTableGetVPtrOp::create(builder, loc, vptrPtrTy, op.getSrc());126 mlir::Value vptr = builder.createLoad(loc, vptrPtr);127 mlir::Value elementPtr =128 builder.createBitcast(vptr, builder.getPointerTo(vtableElemTy));129 mlir::Value minusTwo = builder.getSignedInt(loc, -2, 64);130 auto offsetToTopSlotPtr = cir::PtrStrideOp::create(131 builder, loc, builder.getPointerTo(vtableElemTy), elementPtr, minusTwo);132 mlir::Value offsetToTop =133 builder.createAlignedLoad(loc, offsetToTopSlotPtr, vtableElemAlign);134 135 // Add the offset to the given pointer to get the cast result.136 // Cast the input pointer to a uint8_t* to allow pointer arithmetic.137 cir::PointerType u8PtrTy = builder.getPointerTo(builder.getUIntNTy(8));138 mlir::Value srcBytePtr = builder.createBitcast(op.getSrc(), u8PtrTy);139 auto dstBytePtr =140 cir::PtrStrideOp::create(builder, loc, u8PtrTy, srcBytePtr, offsetToTop);141 // Cast the result to a void*.142 return builder.createBitcast(dstBytePtr, builder.getVoidPtrTy());143}144 145mlir::Value146LoweringPrepareItaniumCXXABI::lowerDynamicCast(cir::CIRBaseBuilderTy &builder,147 clang::ASTContext &astCtx,148 cir::DynamicCastOp op) {149 mlir::Location loc = op->getLoc();150 mlir::Value srcValue = op.getSrc();151 152 assert(!cir::MissingFeatures::emitTypeCheck());153 154 if (op.isRefCast())155 return buildDynamicCastAfterNullCheck(builder, op);156 157 mlir::Value srcValueIsNotNull = builder.createPtrToBoolCast(srcValue);158 return cir::TernaryOp::create(159 builder, loc, srcValueIsNotNull,160 [&](mlir::OpBuilder &, mlir::Location) {161 mlir::Value castedValue =162 op.isCastToVoid()163 ? buildDynamicCastToVoidAfterNullCheck(builder, astCtx,164 op)165 : buildDynamicCastAfterNullCheck(builder, op);166 builder.createYield(loc, castedValue);167 },168 [&](mlir::OpBuilder &, mlir::Location) {169 builder.createYield(170 loc, builder.getNullPtr(op.getType(), loc).getResult());171 })172 .getResult();173}174