1 /**
2  * Implement CTFE for intrinsic (builtin) functions.
3  *
4  * Currently includes functions from `std.math`, `core.math` and `core.bitop`.
5  *
6  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/builtin.d, _builtin.d)
10  * Documentation:  https://dlang.org/phobos/dmd_builtin.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/builtin.d
12  */
13 
14 module dmd.builtin;
15 
16 import core.stdc.math;
17 import core.stdc.string;
18 import dmd.arraytypes;
19 import dmd.dmangle;
20 import dmd.errors;
21 import dmd.expression;
22 import dmd.func;
23 import dmd.globals;
24 import dmd.mtype;
25 import dmd.root.ctfloat;
26 import dmd.root.stringtable;
27 import dmd.tokens;
28 import dmd.id;
29 static import core.bitop;
30 
31 /**********************************
32  * Determine if function is a builtin one that we can
33  * evaluate at compile time.
34  */
35 public extern (C++) BUILTIN isBuiltin(FuncDeclaration fd)
36 {
37     if (fd.builtin == BUILTIN.unknown)
38     {
39         fd.builtin = determine_builtin(fd);
40     }
41     return fd.builtin;
42 }
43 
44 /**************************************
45  * Evaluate builtin function.
46  * Return result; NULL if cannot evaluate it.
47  */
48 public extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments)
49 {
50     if (fd.builtin == BUILTIN.unimp)
51         return null;
52 
53     switch (fd.builtin)
54     {
55         foreach(e; __traits(allMembers, BUILTIN))
56         {
57             static if (e == "unknown")
58                 case BUILTIN.unknown: assert(false);
59             else
60                 mixin("case BUILTIN."~e~": return eval_"~e~"(loc, fd, arguments);");
61         }
62         default: assert(0);
63     }
64 }
65 
66 private:
67 
68 /**
69  * Handler for evaluating builtins during CTFE.
70  *
71  * Params:
72  *  loc = The call location, for error reporting.
73  *  fd = The callee declaration, e.g. to disambiguate between different overloads
74  *       in a single handler (LDC).
75  *  arguments = The function call arguments.
76  * Returns:
77  *  An Expression containing the return value of the call.
78  */
79 
80 BUILTIN determine_builtin(FuncDeclaration func)
81 {
82     auto fd = func.toAliasFunc();
83     if (fd.isDeprecated())
84         return BUILTIN.unimp;
85     auto m = fd.getModule();
86     if (!m || !m.md)
87         return BUILTIN.unimp;
88     const md = m.md;
89     const id2 = md.id;
90 
91     // Look for core.math, core.bitop and std.math
92     if (id2 != Id.math && id2 != Id.bitop)
93         return BUILTIN.unimp;
94 
95     if (md.packages.length != 1)
96         return BUILTIN.unimp;
97 
98     const id1 = md.packages[0];
99     if (id1 != Id.core && id1 != Id.std)
100         return BUILTIN.unimp;
101     const id3 = fd.ident;
102 
103     if (id1 == Id.core && id2 == Id.bitop)
104     {
105         if (id3 == Id.bsf)     return BUILTIN.bsf;
106         if (id3 == Id.bsr)     return BUILTIN.bsr;
107         if (id3 == Id.bswap)   return BUILTIN.bswap;
108         if (id3 == Id._popcnt) return BUILTIN.popcnt;
109         return BUILTIN.unimp;
110     }
111 
112     // Math
113     if (id3 == Id.sin)   return BUILTIN.sin;
114     if (id3 == Id.cos)   return BUILTIN.cos;
115     if (id3 == Id.tan)   return BUILTIN.tan;
116     if (id3 == Id.atan2) return BUILTIN.unimp; // N.B unimplmeneted
117 
118     if (id3 == Id._sqrt) return BUILTIN.sqrt;
119     if (id3 == Id.fabs)  return BUILTIN.fabs;
120 
121     if (id3 == Id.exp)    return BUILTIN.exp;
122     if (id3 == Id.expm1)  return BUILTIN.expm1;
123     if (id3 == Id.exp2)   return BUILTIN.exp2;
124     if (id3 == Id.yl2x)   return CTFloat.yl2x_supported ? BUILTIN.yl2x : BUILTIN.unimp;
125     if (id3 == Id.yl2xp1) return CTFloat.yl2xp1_supported ? BUILTIN.yl2xp1 : BUILTIN.unimp;
126 
127     if (id3 == Id.log)   return BUILTIN.log;
128     if (id3 == Id.log2)  return BUILTIN.log2;
129     if (id3 == Id.log10) return BUILTIN.log10;
130 
131     if (id3 == Id.ldexp) return BUILTIN.ldexp;
132     if (id3 == Id.round) return BUILTIN.round;
133     if (id3 == Id.floor) return BUILTIN.floor;
134     if (id3 == Id.ceil)  return BUILTIN.ceil;
135     if (id3 == Id.trunc) return BUILTIN.trunc;
136 
137     if (id3 == Id.fmin)     return BUILTIN.fmin;
138     if (id3 == Id.fmax)     return BUILTIN.fmax;
139     if (id3 == Id.fma)      return BUILTIN.fma;
140     if (id3 == Id.copysign) return BUILTIN.copysign;
141 
142     if (id3 == Id.isnan)      return BUILTIN.isnan;
143     if (id3 == Id.isInfinity) return BUILTIN.isinfinity;
144     if (id3 == Id.isfinite)   return BUILTIN.isfinite;
145 
146     // Only match pow(fp,fp) where fp is a floating point type
147     if (id3 == Id._pow)
148     {
149         if ((*fd.parameters)[0].type.isfloating() &&
150             (*fd.parameters)[1].type.isfloating())
151             return BUILTIN.pow;
152         return BUILTIN.unimp;
153     }
154 
155     if (id3 != Id.toPrec)
156         return BUILTIN.unimp;
157     const(char)* me = mangleExact(fd);
158     final switch (me["_D4core4math__T6toPrecHT".length])
159     {
160         case 'd': return BUILTIN.toPrecDouble;
161         case 'e': return BUILTIN.toPrecReal;
162         case 'f': return BUILTIN.toPrecFloat;
163     }
164 }
165 
166 Expression eval_unimp(Loc loc, FuncDeclaration fd, Expressions* arguments)
167 {
168     return null;
169 }
170 
171 Expression eval_sin(Loc loc, FuncDeclaration fd, Expressions* arguments)
172 {
173     Expression arg0 = (*arguments)[0];
174     assert(arg0.op == TOK.float64);
175     return new RealExp(loc, CTFloat.sin(arg0.toReal()), arg0.type);
176 }
177 
178 Expression eval_cos(Loc loc, FuncDeclaration fd, Expressions* arguments)
179 {
180     Expression arg0 = (*arguments)[0];
181     assert(arg0.op == TOK.float64);
182     return new RealExp(loc, CTFloat.cos(arg0.toReal()), arg0.type);
183 }
184 
185 Expression eval_tan(Loc loc, FuncDeclaration fd, Expressions* arguments)
186 {
187     Expression arg0 = (*arguments)[0];
188     assert(arg0.op == TOK.float64);
189     return new RealExp(loc, CTFloat.tan(arg0.toReal()), arg0.type);
190 }
191 
192 Expression eval_sqrt(Loc loc, FuncDeclaration fd, Expressions* arguments)
193 {
194     Expression arg0 = (*arguments)[0];
195     assert(arg0.op == TOK.float64);
196     return new RealExp(loc, CTFloat.sqrt(arg0.toReal()), arg0.type);
197 }
198 
199 Expression eval_fabs(Loc loc, FuncDeclaration fd, Expressions* arguments)
200 {
201     Expression arg0 = (*arguments)[0];
202     assert(arg0.op == TOK.float64);
203     return new RealExp(loc, CTFloat.fabs(arg0.toReal()), arg0.type);
204 }
205 
206 Expression eval_ldexp(Loc loc, FuncDeclaration fd, Expressions* arguments)
207 {
208     Expression arg0 = (*arguments)[0];
209     assert(arg0.op == TOK.float64);
210     Expression arg1 = (*arguments)[1];
211     assert(arg1.op == TOK.int64);
212     return new RealExp(loc, CTFloat.ldexp(arg0.toReal(), cast(int) arg1.toInteger()), arg0.type);
213 }
214 
215 Expression eval_log(Loc loc, FuncDeclaration fd, Expressions* arguments)
216 {
217     Expression arg0 = (*arguments)[0];
218     assert(arg0.op == TOK.float64);
219     return new RealExp(loc, CTFloat.log(arg0.toReal()), arg0.type);
220 }
221 
222 Expression eval_log2(Loc loc, FuncDeclaration fd, Expressions* arguments)
223 {
224     Expression arg0 = (*arguments)[0];
225     assert(arg0.op == TOK.float64);
226     return new RealExp(loc, CTFloat.log2(arg0.toReal()), arg0.type);
227 }
228 
229 Expression eval_log10(Loc loc, FuncDeclaration fd, Expressions* arguments)
230 {
231     Expression arg0 = (*arguments)[0];
232     assert(arg0.op == TOK.float64);
233     return new RealExp(loc, CTFloat.log10(arg0.toReal()), arg0.type);
234 }
235 
236 Expression eval_exp(Loc loc, FuncDeclaration fd, Expressions* arguments)
237 {
238     Expression arg0 = (*arguments)[0];
239     assert(arg0.op == TOK.float64);
240     return new RealExp(loc, CTFloat.exp(arg0.toReal()), arg0.type);
241 }
242 
243 Expression eval_expm1(Loc loc, FuncDeclaration fd, Expressions* arguments)
244 {
245     Expression arg0 = (*arguments)[0];
246     assert(arg0.op == TOK.float64);
247     return new RealExp(loc, CTFloat.expm1(arg0.toReal()), arg0.type);
248 }
249 
250 Expression eval_exp2(Loc loc, FuncDeclaration fd, Expressions* arguments)
251 {
252     Expression arg0 = (*arguments)[0];
253     assert(arg0.op == TOK.float64);
254     return new RealExp(loc, CTFloat.exp2(arg0.toReal()), arg0.type);
255 }
256 
257 Expression eval_round(Loc loc, FuncDeclaration fd, Expressions* arguments)
258 {
259     Expression arg0 = (*arguments)[0];
260     assert(arg0.op == TOK.float64);
261     return new RealExp(loc, CTFloat.round(arg0.toReal()), arg0.type);
262 }
263 
264 Expression eval_floor(Loc loc, FuncDeclaration fd, Expressions* arguments)
265 {
266     Expression arg0 = (*arguments)[0];
267     assert(arg0.op == TOK.float64);
268     return new RealExp(loc, CTFloat.floor(arg0.toReal()), arg0.type);
269 }
270 
271 Expression eval_ceil(Loc loc, FuncDeclaration fd, Expressions* arguments)
272 {
273     Expression arg0 = (*arguments)[0];
274     assert(arg0.op == TOK.float64);
275     return new RealExp(loc, CTFloat.ceil(arg0.toReal()), arg0.type);
276 }
277 
278 Expression eval_trunc(Loc loc, FuncDeclaration fd, Expressions* arguments)
279 {
280     Expression arg0 = (*arguments)[0];
281     assert(arg0.op == TOK.float64);
282     return new RealExp(loc, CTFloat.trunc(arg0.toReal()), arg0.type);
283 }
284 
285 Expression eval_copysign(Loc loc, FuncDeclaration fd, Expressions* arguments)
286 {
287     Expression arg0 = (*arguments)[0];
288     assert(arg0.op == TOK.float64);
289     Expression arg1 = (*arguments)[1];
290     assert(arg1.op == TOK.float64);
291     return new RealExp(loc, CTFloat.copysign(arg0.toReal(), arg1.toReal()), arg0.type);
292 }
293 
294 Expression eval_pow(Loc loc, FuncDeclaration fd, Expressions* arguments)
295 {
296     Expression arg0 = (*arguments)[0];
297     assert(arg0.op == TOK.float64);
298     Expression arg1 = (*arguments)[1];
299     assert(arg1.op == TOK.float64);
300     return new RealExp(loc, CTFloat.pow(arg0.toReal(), arg1.toReal()), arg0.type);
301 }
302 
303 Expression eval_fmin(Loc loc, FuncDeclaration fd, Expressions* arguments)
304 {
305     Expression arg0 = (*arguments)[0];
306     assert(arg0.op == TOK.float64);
307     Expression arg1 = (*arguments)[1];
308     assert(arg1.op == TOK.float64);
309     return new RealExp(loc, CTFloat.fmin(arg0.toReal(), arg1.toReal()), arg0.type);
310 }
311 
312 Expression eval_fmax(Loc loc, FuncDeclaration fd, Expressions* arguments)
313 {
314     Expression arg0 = (*arguments)[0];
315     assert(arg0.op == TOK.float64);
316     Expression arg1 = (*arguments)[1];
317     assert(arg1.op == TOK.float64);
318     return new RealExp(loc, CTFloat.fmax(arg0.toReal(), arg1.toReal()), arg0.type);
319 }
320 
321 Expression eval_fma(Loc loc, FuncDeclaration fd, Expressions* arguments)
322 {
323     Expression arg0 = (*arguments)[0];
324     assert(arg0.op == TOK.float64);
325     Expression arg1 = (*arguments)[1];
326     assert(arg1.op == TOK.float64);
327     Expression arg2 = (*arguments)[2];
328     assert(arg2.op == TOK.float64);
329     return new RealExp(loc, CTFloat.fma(arg0.toReal(), arg1.toReal(), arg2.toReal()), arg0.type);
330 }
331 
332 Expression eval_isnan(Loc loc, FuncDeclaration fd, Expressions* arguments)
333 {
334     Expression arg0 = (*arguments)[0];
335     assert(arg0.op == TOK.float64);
336     return IntegerExp.createBool(CTFloat.isNaN(arg0.toReal()));
337 }
338 
339 Expression eval_isinfinity(Loc loc, FuncDeclaration fd, Expressions* arguments)
340 {
341     Expression arg0 = (*arguments)[0];
342     assert(arg0.op == TOK.float64);
343     return IntegerExp.createBool(CTFloat.isInfinity(arg0.toReal()));
344 }
345 
346 Expression eval_isfinite(Loc loc, FuncDeclaration fd, Expressions* arguments)
347 {
348     Expression arg0 = (*arguments)[0];
349     assert(arg0.op == TOK.float64);
350     const value = !CTFloat.isNaN(arg0.toReal()) && !CTFloat.isInfinity(arg0.toReal());
351     return IntegerExp.createBool(value);
352 }
353 
354 Expression eval_bsf(Loc loc, FuncDeclaration fd, Expressions* arguments)
355 {
356     Expression arg0 = (*arguments)[0];
357     assert(arg0.op == TOK.int64);
358     uinteger_t n = arg0.toInteger();
359     if (n == 0)
360         error(loc, "`bsf(0)` is undefined");
361     return new IntegerExp(loc, core.bitop.bsf(n), Type.tint32);
362 }
363 
364 Expression eval_bsr(Loc loc, FuncDeclaration fd, Expressions* arguments)
365 {
366     Expression arg0 = (*arguments)[0];
367     assert(arg0.op == TOK.int64);
368     uinteger_t n = arg0.toInteger();
369     if (n == 0)
370         error(loc, "`bsr(0)` is undefined");
371     return new IntegerExp(loc, core.bitop.bsr(n), Type.tint32);
372 }
373 
374 Expression eval_bswap(Loc loc, FuncDeclaration fd, Expressions* arguments)
375 {
376     Expression arg0 = (*arguments)[0];
377     assert(arg0.op == TOK.int64);
378     uinteger_t n = arg0.toInteger();
379     TY ty = arg0.type.toBasetype().ty;
380     if (ty == Tint64 || ty == Tuns64)
381         return new IntegerExp(loc, core.bitop.bswap(cast(ulong) n), arg0.type);
382     else
383         return new IntegerExp(loc, core.bitop.bswap(cast(uint) n), arg0.type);
384 }
385 
386 Expression eval_popcnt(Loc loc, FuncDeclaration fd, Expressions* arguments)
387 {
388     Expression arg0 = (*arguments)[0];
389     assert(arg0.op == TOK.int64);
390     uinteger_t n = arg0.toInteger();
391     return new IntegerExp(loc, core.bitop.popcnt(n), Type.tint32);
392 }
393 
394 Expression eval_yl2x(Loc loc, FuncDeclaration fd, Expressions* arguments)
395 {
396     Expression arg0 = (*arguments)[0];
397     assert(arg0.op == TOK.float64);
398     Expression arg1 = (*arguments)[1];
399     assert(arg1.op == TOK.float64);
400     const x = arg0.toReal();
401     const y = arg1.toReal();
402     real_t result = CTFloat.zero;
403     CTFloat.yl2x(&x, &y, &result);
404     return new RealExp(loc, result, arg0.type);
405 }
406 
407 Expression eval_yl2xp1(Loc loc, FuncDeclaration fd, Expressions* arguments)
408 {
409     Expression arg0 = (*arguments)[0];
410     assert(arg0.op == TOK.float64);
411     Expression arg1 = (*arguments)[1];
412     assert(arg1.op == TOK.float64);
413     const x = arg0.toReal();
414     const y = arg1.toReal();
415     real_t result = CTFloat.zero;
416     CTFloat.yl2xp1(&x, &y, &result);
417     return new RealExp(loc, result, arg0.type);
418 }
419 
420 Expression eval_toPrecFloat(Loc loc, FuncDeclaration fd, Expressions* arguments)
421 {
422     Expression arg0 = (*arguments)[0];
423     float f = cast(real)arg0.toReal();
424     return new RealExp(loc, real_t(f), Type.tfloat32);
425 }
426 
427 Expression eval_toPrecDouble(Loc loc, FuncDeclaration fd, Expressions* arguments)
428 {
429     Expression arg0 = (*arguments)[0];
430     double d = cast(real)arg0.toReal();
431     return new RealExp(loc, real_t(d), Type.tfloat64);
432 }
433 
434 Expression eval_toPrecReal(Loc loc, FuncDeclaration fd, Expressions* arguments)
435 {
436     Expression arg0 = (*arguments)[0];
437     return new RealExp(loc, arg0.toReal(), Type.tfloat80);
438 }
439 
440 // These built-ins are reserved for GDC and LDC.
441 Expression eval_gcc(Loc, FuncDeclaration, Expressions*)
442 {
443     assert(0);
444 }
445 
446 Expression eval_llvm(Loc, FuncDeclaration, Expressions*)
447 {
448     assert(0);
449 }