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