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
emitLibraryCall(CIRGenFunction & cgf,const FunctionDecl * fd,const CallExpr * e,mlir::Operation * calleeValue)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(e->getCallee()->getType(), callee, e, ReturnValueSlot());
35 }
36
37 template <typename Op>
emitBuiltinBitOp(CIRGenFunction & cgf,const CallExpr * e,bool poisonZero=false)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(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
emitBuiltinExpr(const GlobalDecl & gd,unsigned builtinID,const CallExpr * e,ReturnValueSlot returnValue)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, 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(0)->HasSideEffects(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(2);
189 [[maybe_unused]] bool evalSucceeded =
190 probArg->EvaluateAsFloat(probability, cgm.getASTContext());
191 assert(evalSucceeded &&
192 "probability should be able to evaluate as float");
193 bool loseInfo = false; // ignored
194 probability.convert(llvm::APFloat::IEEEdouble(),
195 llvm::RoundingMode::Dynamic, &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(e->getType());
233 }
234
235 /// Given a builtin id for a function like "__builtin_fabsf", return a Function*
236 /// for "fabsf".
getBuiltinLibFunction(const FunctionDecl * fd,unsigned builtinID)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(builtinID).substr(10);
248
249 GlobalDecl d(fd);
250 mlir::Type type = convertType(fd->getType());
251 return getOrCreateCIRFunction(name, type, d, /*forVTable=*/false);
252 }
253
emitCheckedArgForAssume(const Expr * e)254 mlir::Value CIRGenFunction::emitCheckedArgForAssume(const Expr *e) {
255 mlir::Value argValue = evaluateExprAsBool(e);
256 if (!sanOpts.has(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