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