| 1 | //===----------------------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This contains code to emit Builtin calls as CIR or a function call to be |
| 10 | // later resolved. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "CIRGenCall.h" |
| 15 | #include "CIRGenConstantEmitter.h" |
| 16 | #include "CIRGenFunction.h" |
| 17 | #include "CIRGenModule.h" |
| 18 | #include "CIRGenValue.h" |
| 19 | #include "mlir/IR/BuiltinAttributes.h" |
| 20 | #include "mlir/IR/Value.h" |
| 21 | #include "mlir/Support/LLVM.h" |
| 22 | #include "clang/AST/Expr.h" |
| 23 | #include "clang/AST/GlobalDecl.h" |
| 24 | #include "clang/CIR/MissingFeatures.h" |
| 25 | #include "llvm/Support/ErrorHandling.h" |
| 26 | |
| 27 | using namespace clang; |
| 28 | using namespace clang::CIRGen; |
| 29 | using namespace llvm; |
| 30 | |
| 31 | static RValue emitLibraryCall(CIRGenFunction &cgf, const FunctionDecl *fd, |
| 32 | const CallExpr *e, mlir::Operation *calleeValue) { |
| 33 | CIRGenCallee callee = CIRGenCallee::forDirect(calleeValue, GlobalDecl(fd)); |
| 34 | return cgf.emitCall(calleeTy: e->getCallee()->getType(), callee, e, returnValue: ReturnValueSlot()); |
| 35 | } |
| 36 | |
| 37 | template <typename Op> |
| 38 | static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e, |
| 39 | bool poisonZero = false) { |
| 40 | assert(!cir::MissingFeatures::builtinCheckKind()); |
| 41 | |
| 42 | mlir::Value arg = cgf.emitScalarExpr(e->getArg(Arg: 0)); |
| 43 | CIRGenBuilderTy &builder = cgf.getBuilder(); |
| 44 | |
| 45 | Op op; |
| 46 | if constexpr (std::is_same_v<Op, cir::BitClzOp> || |
| 47 | std::is_same_v<Op, cir::BitCtzOp>) |
| 48 | op = builder.create<Op>(cgf.getLoc(e->getSourceRange()), arg, poisonZero); |
| 49 | else |
| 50 | op = builder.create<Op>(cgf.getLoc(e->getSourceRange()), arg); |
| 51 | |
| 52 | mlir::Value result = op.getResult(); |
| 53 | mlir::Type exprTy = cgf.convertType(e->getType()); |
| 54 | if (exprTy != result.getType()) |
| 55 | result = builder.createIntCast(result, exprTy); |
| 56 | |
| 57 | return RValue::get(result); |
| 58 | } |
| 59 | |
| 60 | RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, |
| 61 | const CallExpr *e, |
| 62 | ReturnValueSlot returnValue) { |
| 63 | mlir::Location loc = getLoc(e->getSourceRange()); |
| 64 | |
| 65 | // See if we can constant fold this builtin. If so, don't emit it at all. |
| 66 | // TODO: Extend this handling to all builtin calls that we can constant-fold. |
| 67 | Expr::EvalResult result; |
| 68 | if (e->isPRValue() && e->EvaluateAsRValue(Result&: result, Ctx: cgm.getASTContext()) && |
| 69 | !result.hasSideEffects()) { |
| 70 | if (result.Val.isInt()) |
| 71 | return RValue::get(builder.getConstInt(loc, result.Val.getInt())); |
| 72 | if (result.Val.isFloat()) { |
| 73 | // Note: we are using result type of CallExpr to determine the type of |
| 74 | // the constant. Classic codegen uses the result value to determine the |
| 75 | // type. We feel it should be Ok to use expression type because it is |
| 76 | // hard to imagine a builtin function evaluates to a value that |
| 77 | // over/underflows its own defined type. |
| 78 | mlir::Type type = convertType(e->getType()); |
| 79 | return RValue::get(builder.getConstFP(loc, type, result.Val.getFloat())); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | const FunctionDecl *fd = gd.getDecl()->getAsFunction(); |
| 84 | |
| 85 | assert(!cir::MissingFeatures::builtinCallF128()); |
| 86 | |
| 87 | // If the builtin has been declared explicitly with an assembler label, |
| 88 | // disable the specialized emitting below. Ideally we should communicate the |
| 89 | // rename in IR, or at least avoid generating the intrinsic calls that are |
| 90 | // likely to get lowered to the renamed library functions. |
| 91 | unsigned builtinIDIfNoAsmLabel = fd->hasAttr<AsmLabelAttr>() ? 0 : builtinID; |
| 92 | |
| 93 | assert(!cir::MissingFeatures::builtinCallMathErrno()); |
| 94 | assert(!cir::MissingFeatures::builtinCall()); |
| 95 | |
| 96 | switch (builtinIDIfNoAsmLabel) { |
| 97 | default: |
| 98 | break; |
| 99 | |
| 100 | case Builtin::BI__assume: |
| 101 | case Builtin::BI__builtin_assume: { |
| 102 | if (e->getArg(Arg: 0)->HasSideEffects(Ctx: getContext())) |
| 103 | return RValue::get(nullptr); |
| 104 | |
| 105 | mlir::Value argValue = emitCheckedArgForAssume(e->getArg(0)); |
| 106 | builder.create<cir::AssumeOp>(loc, argValue); |
| 107 | return RValue::get(nullptr); |
| 108 | } |
| 109 | |
| 110 | case Builtin::BI__builtin_complex: { |
| 111 | mlir::Value real = emitScalarExpr(e->getArg(0)); |
| 112 | mlir::Value imag = emitScalarExpr(e->getArg(1)); |
| 113 | mlir::Value complex = builder.createComplexCreate(loc, real, imag); |
| 114 | return RValue::get(complex); |
| 115 | } |
| 116 | |
| 117 | case Builtin::BI__builtin_creal: |
| 118 | case Builtin::BI__builtin_crealf: |
| 119 | case Builtin::BI__builtin_creall: |
| 120 | case Builtin::BIcreal: |
| 121 | case Builtin::BIcrealf: |
| 122 | case Builtin::BIcreall: { |
| 123 | mlir::Value complex = emitComplexExpr(e->getArg(0)); |
| 124 | mlir::Value real = builder.createComplexReal(loc, complex); |
| 125 | return RValue::get(real); |
| 126 | } |
| 127 | |
| 128 | case Builtin::BI__builtin_cimag: |
| 129 | case Builtin::BI__builtin_cimagf: |
| 130 | case Builtin::BI__builtin_cimagl: |
| 131 | case Builtin::BIcimag: |
| 132 | case Builtin::BIcimagf: |
| 133 | case Builtin::BIcimagl: { |
| 134 | mlir::Value complex = emitComplexExpr(e->getArg(0)); |
| 135 | mlir::Value imag = builder.createComplexImag(loc, complex); |
| 136 | return RValue::get(imag); |
| 137 | } |
| 138 | |
| 139 | case Builtin::BI__builtin_clrsb: |
| 140 | case Builtin::BI__builtin_clrsbl: |
| 141 | case Builtin::BI__builtin_clrsbll: |
| 142 | return emitBuiltinBitOp<cir::BitClrsbOp>(*this, e); |
| 143 | |
| 144 | case Builtin::BI__builtin_ctzs: |
| 145 | case Builtin::BI__builtin_ctz: |
| 146 | case Builtin::BI__builtin_ctzl: |
| 147 | case Builtin::BI__builtin_ctzll: |
| 148 | case Builtin::BI__builtin_ctzg: |
| 149 | assert(!cir::MissingFeatures::builtinCheckKind()); |
| 150 | return emitBuiltinBitOp<cir::BitCtzOp>(*this, e, /*poisonZero=*/true); |
| 151 | |
| 152 | case Builtin::BI__builtin_clzs: |
| 153 | case Builtin::BI__builtin_clz: |
| 154 | case Builtin::BI__builtin_clzl: |
| 155 | case Builtin::BI__builtin_clzll: |
| 156 | case Builtin::BI__builtin_clzg: |
| 157 | assert(!cir::MissingFeatures::builtinCheckKind()); |
| 158 | return emitBuiltinBitOp<cir::BitClzOp>(*this, e, /*poisonZero=*/true); |
| 159 | |
| 160 | case Builtin::BI__builtin_parity: |
| 161 | case Builtin::BI__builtin_parityl: |
| 162 | case Builtin::BI__builtin_parityll: |
| 163 | return emitBuiltinBitOp<cir::BitParityOp>(*this, e); |
| 164 | |
| 165 | case Builtin::BI__lzcnt16: |
| 166 | case Builtin::BI__lzcnt: |
| 167 | case Builtin::BI__lzcnt64: |
| 168 | assert(!cir::MissingFeatures::builtinCheckKind()); |
| 169 | return emitBuiltinBitOp<cir::BitClzOp>(*this, e, /*poisonZero=*/false); |
| 170 | |
| 171 | case Builtin::BI__popcnt16: |
| 172 | case Builtin::BI__popcnt: |
| 173 | case Builtin::BI__popcnt64: |
| 174 | case Builtin::BI__builtin_popcount: |
| 175 | case Builtin::BI__builtin_popcountl: |
| 176 | case Builtin::BI__builtin_popcountll: |
| 177 | case Builtin::BI__builtin_popcountg: |
| 178 | return emitBuiltinBitOp<cir::BitPopcountOp>(*this, e); |
| 179 | |
| 180 | case Builtin::BI__builtin_expect: |
| 181 | case Builtin::BI__builtin_expect_with_probability: { |
| 182 | mlir::Value argValue = emitScalarExpr(e->getArg(0)); |
| 183 | mlir::Value expectedValue = emitScalarExpr(e->getArg(1)); |
| 184 | |
| 185 | mlir::FloatAttr probAttr; |
| 186 | if (builtinIDIfNoAsmLabel == Builtin::BI__builtin_expect_with_probability) { |
| 187 | llvm::APFloat probability(0.0); |
| 188 | const Expr *probArg = e->getArg(Arg: 2); |
| 189 | [[maybe_unused]] bool evalSucceeded = |
| 190 | probArg->EvaluateAsFloat(Result&: probability, Ctx: cgm.getASTContext()); |
| 191 | assert(evalSucceeded && |
| 192 | "probability should be able to evaluate as float" ); |
| 193 | bool loseInfo = false; // ignored |
| 194 | probability.convert(ToSemantics: llvm::APFloat::IEEEdouble(), |
| 195 | RM: llvm::RoundingMode::Dynamic, losesInfo: &loseInfo); |
| 196 | probAttr = mlir::FloatAttr::get(mlir::Float64Type::get(&getMLIRContext()), |
| 197 | probability); |
| 198 | } |
| 199 | |
| 200 | auto result = builder.create<cir::ExpectOp>( |
| 201 | loc, argValue.getType(), argValue, expectedValue, probAttr); |
| 202 | return RValue::get(result); |
| 203 | } |
| 204 | |
| 205 | case Builtin::BI__builtin_bswap16: |
| 206 | case Builtin::BI__builtin_bswap32: |
| 207 | case Builtin::BI__builtin_bswap64: |
| 208 | case Builtin::BI_byteswap_ushort: |
| 209 | case Builtin::BI_byteswap_ulong: |
| 210 | case Builtin::BI_byteswap_uint64: { |
| 211 | mlir::Value arg = emitScalarExpr(e->getArg(0)); |
| 212 | return RValue::get(builder.create<cir::ByteSwapOp>(loc, arg)); |
| 213 | } |
| 214 | |
| 215 | case Builtin::BI__builtin_bitreverse8: |
| 216 | case Builtin::BI__builtin_bitreverse16: |
| 217 | case Builtin::BI__builtin_bitreverse32: |
| 218 | case Builtin::BI__builtin_bitreverse64: { |
| 219 | mlir::Value arg = emitScalarExpr(e->getArg(0)); |
| 220 | return RValue::get(builder.create<cir::BitReverseOp>(loc, arg)); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | // If this is an alias for a lib function (e.g. __builtin_sin), emit |
| 225 | // the call using the normal call path, but using the unmangled |
| 226 | // version of the function name. |
| 227 | if (getContext().BuiltinInfo.isLibFunction(builtinID)) |
| 228 | return emitLibraryCall(*this, fd, e, |
| 229 | cgm.getBuiltinLibFunction(fd, builtinID)); |
| 230 | |
| 231 | cgm.errorNYI(e->getSourceRange(), "unimplemented builtin call" ); |
| 232 | return getUndefRValue(ty: e->getType()); |
| 233 | } |
| 234 | |
| 235 | /// Given a builtin id for a function like "__builtin_fabsf", return a Function* |
| 236 | /// for "fabsf". |
| 237 | cir::FuncOp CIRGenModule::getBuiltinLibFunction(const FunctionDecl *fd, |
| 238 | unsigned builtinID) { |
| 239 | assert(astContext.BuiltinInfo.isLibFunction(builtinID)); |
| 240 | |
| 241 | // Get the name, skip over the __builtin_ prefix (if necessary). We may have |
| 242 | // to build this up so provide a small stack buffer to handle the vast |
| 243 | // majority of names. |
| 244 | llvm::SmallString<64> name; |
| 245 | |
| 246 | assert(!cir::MissingFeatures::asmLabelAttr()); |
| 247 | name = astContext.BuiltinInfo.getName(ID: builtinID).substr(pos: 10); |
| 248 | |
| 249 | GlobalDecl d(fd); |
| 250 | mlir::Type type = convertType(fd->getType()); |
| 251 | return getOrCreateCIRFunction(name, type, d, /*forVTable=*/false); |
| 252 | } |
| 253 | |
| 254 | mlir::Value CIRGenFunction::emitCheckedArgForAssume(const Expr *e) { |
| 255 | mlir::Value argValue = evaluateExprAsBool(e); |
| 256 | if (!sanOpts.has(K: SanitizerKind::Builtin)) |
| 257 | return argValue; |
| 258 | |
| 259 | assert(!cir::MissingFeatures::sanitizers()); |
| 260 | cgm.errorNYI(e->getSourceRange(), |
| 261 | "emitCheckedArgForAssume: sanitizers are NYI" ); |
| 262 | return {}; |
| 263 | } |
| 264 | |