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-2020 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) 96 return BUILTIN.unimp; 97 if (md.packages.length != 1) 98 return BUILTIN.unimp; 99 100 const id1 = (*md.packages)[0]; 101 if (id1 != Id.core && id1 != Id.std) 102 return BUILTIN.unimp; 103 const id3 = fd.ident; 104 105 if (id1 == Id.core && id2 == Id.bitop) 106 { 107 if (id3 == Id.bsf) return BUILTIN.bsf; 108 if (id3 == Id.bsr) return BUILTIN.bsr; 109 if (id3 == Id.bswap) return BUILTIN.bswap; 110 if (id3 == Id._popcnt) return BUILTIN.popcnt; 111 return BUILTIN.unimp; 112 } 113 114 // Math 115 if (id3 == Id.sin) return BUILTIN.sin; 116 if (id3 == Id.cos) return BUILTIN.cos; 117 if (id3 == Id.tan) return BUILTIN.tan; 118 if (id3 == Id.atan2) return BUILTIN.unimp; // N.B unimplmeneted 119 120 if (id3 == Id._sqrt) return BUILTIN.sqrt; 121 if (id3 == Id.fabs) return BUILTIN.fabs; 122 123 if (id3 == Id.exp) return BUILTIN.exp; 124 if (id3 == Id.expm1) return BUILTIN.expm1; 125 if (id3 == Id.exp2) return BUILTIN.exp2; 126 if (id3 == Id.yl2x) return CTFloat.yl2x_supported ? BUILTIN.yl2x : BUILTIN.unimp; 127 if (id3 == Id.yl2xp1) return CTFloat.yl2xp1_supported ? BUILTIN.yl2xp1 : BUILTIN.unimp; 128 129 if (id3 == Id.log) return BUILTIN.log; 130 if (id3 == Id.log2) return BUILTIN.log2; 131 if (id3 == Id.log10) return BUILTIN.log10; 132 133 if (id3 == Id.ldexp) return BUILTIN.ldexp; 134 if (id3 == Id.round) return BUILTIN.round; 135 if (id3 == Id.floor) return BUILTIN.floor; 136 if (id3 == Id.ceil) return BUILTIN.ceil; 137 if (id3 == Id.trunc) return BUILTIN.trunc; 138 139 if (id3 == Id.fmin) return BUILTIN.fmin; 140 if (id3 == Id.fmax) return BUILTIN.fmax; 141 if (id3 == Id.fma) return BUILTIN.fma; 142 if (id3 == Id.copysign) return BUILTIN.copysign; 143 144 if (id3 == Id.isnan) return BUILTIN.isnan; 145 if (id3 == Id.isInfinity) return BUILTIN.isinfinity; 146 if (id3 == Id.isfinite) return BUILTIN.isfinite; 147 148 // Only match pow(fp,fp) where fp is a floating point type 149 if (id3 == Id._pow) 150 { 151 if ((*fd.parameters)[0].type.isfloating() && 152 (*fd.parameters)[1].type.isfloating()) 153 return BUILTIN.pow; 154 return BUILTIN.unimp; 155 } 156 157 if (id3 != Id.toPrec) 158 return BUILTIN.unimp; 159 const(char)* me = mangleExact(fd); 160 final switch (me["_D4core4math__T6toPrecHT".length]) 161 { 162 case 'd': return BUILTIN.toPrecDouble; 163 case 'e': return BUILTIN.toPrecReal; 164 case 'f': return BUILTIN.toPrecFloat; 165 } 166 } 167 168 Expression eval_unimp(Loc loc, FuncDeclaration fd, Expressions* arguments) 169 { 170 return null; 171 } 172 173 Expression eval_sin(Loc loc, FuncDeclaration fd, Expressions* arguments) 174 { 175 Expression arg0 = (*arguments)[0]; 176 assert(arg0.op == TOK.float64); 177 return new RealExp(loc, CTFloat.sin(arg0.toReal()), arg0.type); 178 } 179 180 Expression eval_cos(Loc loc, FuncDeclaration fd, Expressions* arguments) 181 { 182 Expression arg0 = (*arguments)[0]; 183 assert(arg0.op == TOK.float64); 184 return new RealExp(loc, CTFloat.cos(arg0.toReal()), arg0.type); 185 } 186 187 Expression eval_tan(Loc loc, FuncDeclaration fd, Expressions* arguments) 188 { 189 Expression arg0 = (*arguments)[0]; 190 assert(arg0.op == TOK.float64); 191 return new RealExp(loc, CTFloat.tan(arg0.toReal()), arg0.type); 192 } 193 194 Expression eval_sqrt(Loc loc, FuncDeclaration fd, Expressions* arguments) 195 { 196 Expression arg0 = (*arguments)[0]; 197 assert(arg0.op == TOK.float64); 198 return new RealExp(loc, CTFloat.sqrt(arg0.toReal()), arg0.type); 199 } 200 201 Expression eval_fabs(Loc loc, FuncDeclaration fd, Expressions* arguments) 202 { 203 Expression arg0 = (*arguments)[0]; 204 assert(arg0.op == TOK.float64); 205 return new RealExp(loc, CTFloat.fabs(arg0.toReal()), arg0.type); 206 } 207 208 Expression eval_ldexp(Loc loc, FuncDeclaration fd, Expressions* arguments) 209 { 210 Expression arg0 = (*arguments)[0]; 211 assert(arg0.op == TOK.float64); 212 Expression arg1 = (*arguments)[1]; 213 assert(arg1.op == TOK.int64); 214 return new RealExp(loc, CTFloat.ldexp(arg0.toReal(), cast(int) arg1.toInteger()), arg0.type); 215 } 216 217 Expression eval_log(Loc loc, FuncDeclaration fd, Expressions* arguments) 218 { 219 Expression arg0 = (*arguments)[0]; 220 assert(arg0.op == TOK.float64); 221 return new RealExp(loc, CTFloat.log(arg0.toReal()), arg0.type); 222 } 223 224 Expression eval_log2(Loc loc, FuncDeclaration fd, Expressions* arguments) 225 { 226 Expression arg0 = (*arguments)[0]; 227 assert(arg0.op == TOK.float64); 228 return new RealExp(loc, CTFloat.log2(arg0.toReal()), arg0.type); 229 } 230 231 Expression eval_log10(Loc loc, FuncDeclaration fd, Expressions* arguments) 232 { 233 Expression arg0 = (*arguments)[0]; 234 assert(arg0.op == TOK.float64); 235 return new RealExp(loc, CTFloat.log10(arg0.toReal()), arg0.type); 236 } 237 238 Expression eval_exp(Loc loc, FuncDeclaration fd, Expressions* arguments) 239 { 240 Expression arg0 = (*arguments)[0]; 241 assert(arg0.op == TOK.float64); 242 return new RealExp(loc, CTFloat.exp(arg0.toReal()), arg0.type); 243 } 244 245 Expression eval_expm1(Loc loc, FuncDeclaration fd, Expressions* arguments) 246 { 247 Expression arg0 = (*arguments)[0]; 248 assert(arg0.op == TOK.float64); 249 return new RealExp(loc, CTFloat.expm1(arg0.toReal()), arg0.type); 250 } 251 252 Expression eval_exp2(Loc loc, FuncDeclaration fd, Expressions* arguments) 253 { 254 Expression arg0 = (*arguments)[0]; 255 assert(arg0.op == TOK.float64); 256 return new RealExp(loc, CTFloat.exp2(arg0.toReal()), arg0.type); 257 } 258 259 Expression eval_round(Loc loc, FuncDeclaration fd, Expressions* arguments) 260 { 261 Expression arg0 = (*arguments)[0]; 262 assert(arg0.op == TOK.float64); 263 return new RealExp(loc, CTFloat.round(arg0.toReal()), arg0.type); 264 } 265 266 Expression eval_floor(Loc loc, FuncDeclaration fd, Expressions* arguments) 267 { 268 Expression arg0 = (*arguments)[0]; 269 assert(arg0.op == TOK.float64); 270 return new RealExp(loc, CTFloat.floor(arg0.toReal()), arg0.type); 271 } 272 273 Expression eval_ceil(Loc loc, FuncDeclaration fd, Expressions* arguments) 274 { 275 Expression arg0 = (*arguments)[0]; 276 assert(arg0.op == TOK.float64); 277 return new RealExp(loc, CTFloat.ceil(arg0.toReal()), arg0.type); 278 } 279 280 Expression eval_trunc(Loc loc, FuncDeclaration fd, Expressions* arguments) 281 { 282 Expression arg0 = (*arguments)[0]; 283 assert(arg0.op == TOK.float64); 284 return new RealExp(loc, CTFloat.trunc(arg0.toReal()), arg0.type); 285 } 286 287 Expression eval_copysign(Loc loc, FuncDeclaration fd, Expressions* arguments) 288 { 289 Expression arg0 = (*arguments)[0]; 290 assert(arg0.op == TOK.float64); 291 Expression arg1 = (*arguments)[1]; 292 assert(arg1.op == TOK.float64); 293 return new RealExp(loc, CTFloat.copysign(arg0.toReal(), arg1.toReal()), arg0.type); 294 } 295 296 Expression eval_pow(Loc loc, FuncDeclaration fd, Expressions* arguments) 297 { 298 Expression arg0 = (*arguments)[0]; 299 assert(arg0.op == TOK.float64); 300 Expression arg1 = (*arguments)[1]; 301 assert(arg1.op == TOK.float64); 302 return new RealExp(loc, CTFloat.pow(arg0.toReal(), arg1.toReal()), arg0.type); 303 } 304 305 Expression eval_fmin(Loc loc, FuncDeclaration fd, Expressions* arguments) 306 { 307 Expression arg0 = (*arguments)[0]; 308 assert(arg0.op == TOK.float64); 309 Expression arg1 = (*arguments)[1]; 310 assert(arg1.op == TOK.float64); 311 return new RealExp(loc, CTFloat.fmin(arg0.toReal(), arg1.toReal()), arg0.type); 312 } 313 314 Expression eval_fmax(Loc loc, FuncDeclaration fd, Expressions* arguments) 315 { 316 Expression arg0 = (*arguments)[0]; 317 assert(arg0.op == TOK.float64); 318 Expression arg1 = (*arguments)[1]; 319 assert(arg1.op == TOK.float64); 320 return new RealExp(loc, CTFloat.fmax(arg0.toReal(), arg1.toReal()), arg0.type); 321 } 322 323 Expression eval_fma(Loc loc, FuncDeclaration fd, Expressions* arguments) 324 { 325 Expression arg0 = (*arguments)[0]; 326 assert(arg0.op == TOK.float64); 327 Expression arg1 = (*arguments)[1]; 328 assert(arg1.op == TOK.float64); 329 Expression arg2 = (*arguments)[2]; 330 assert(arg2.op == TOK.float64); 331 return new RealExp(loc, CTFloat.fma(arg0.toReal(), arg1.toReal(), arg2.toReal()), arg0.type); 332 } 333 334 Expression eval_isnan(Loc loc, FuncDeclaration fd, Expressions* arguments) 335 { 336 Expression arg0 = (*arguments)[0]; 337 assert(arg0.op == TOK.float64); 338 return IntegerExp.createBool(CTFloat.isNaN(arg0.toReal())); 339 } 340 341 Expression eval_isinfinity(Loc loc, FuncDeclaration fd, Expressions* arguments) 342 { 343 Expression arg0 = (*arguments)[0]; 344 assert(arg0.op == TOK.float64); 345 return IntegerExp.createBool(CTFloat.isInfinity(arg0.toReal())); 346 } 347 348 Expression eval_isfinite(Loc loc, FuncDeclaration fd, Expressions* arguments) 349 { 350 Expression arg0 = (*arguments)[0]; 351 assert(arg0.op == TOK.float64); 352 const value = !CTFloat.isNaN(arg0.toReal()) && !CTFloat.isInfinity(arg0.toReal()); 353 return IntegerExp.createBool(value); 354 } 355 356 Expression eval_bsf(Loc loc, FuncDeclaration fd, Expressions* arguments) 357 { 358 Expression arg0 = (*arguments)[0]; 359 assert(arg0.op == TOK.int64); 360 uinteger_t n = arg0.toInteger(); 361 if (n == 0) 362 error(loc, "`bsf(0)` is undefined"); 363 return new IntegerExp(loc, core.bitop.bsf(n), Type.tint32); 364 } 365 366 Expression eval_bsr(Loc loc, FuncDeclaration fd, Expressions* arguments) 367 { 368 Expression arg0 = (*arguments)[0]; 369 assert(arg0.op == TOK.int64); 370 uinteger_t n = arg0.toInteger(); 371 if (n == 0) 372 error(loc, "`bsr(0)` is undefined"); 373 return new IntegerExp(loc, core.bitop.bsr(n), Type.tint32); 374 } 375 376 Expression eval_bswap(Loc loc, FuncDeclaration fd, Expressions* arguments) 377 { 378 Expression arg0 = (*arguments)[0]; 379 assert(arg0.op == TOK.int64); 380 uinteger_t n = arg0.toInteger(); 381 TY ty = arg0.type.toBasetype().ty; 382 if (ty == Tint64 || ty == Tuns64) 383 return new IntegerExp(loc, core.bitop.bswap(cast(ulong) n), arg0.type); 384 else 385 return new IntegerExp(loc, core.bitop.bswap(cast(uint) n), arg0.type); 386 } 387 388 Expression eval_popcnt(Loc loc, FuncDeclaration fd, Expressions* arguments) 389 { 390 Expression arg0 = (*arguments)[0]; 391 assert(arg0.op == TOK.int64); 392 uinteger_t n = arg0.toInteger(); 393 return new IntegerExp(loc, core.bitop.popcnt(n), Type.tint32); 394 } 395 396 Expression eval_yl2x(Loc loc, FuncDeclaration fd, Expressions* arguments) 397 { 398 Expression arg0 = (*arguments)[0]; 399 assert(arg0.op == TOK.float64); 400 Expression arg1 = (*arguments)[1]; 401 assert(arg1.op == TOK.float64); 402 const x = arg0.toReal(); 403 const y = arg1.toReal(); 404 real_t result = CTFloat.zero; 405 CTFloat.yl2x(&x, &y, &result); 406 return new RealExp(loc, result, arg0.type); 407 } 408 409 Expression eval_yl2xp1(Loc loc, FuncDeclaration fd, Expressions* arguments) 410 { 411 Expression arg0 = (*arguments)[0]; 412 assert(arg0.op == TOK.float64); 413 Expression arg1 = (*arguments)[1]; 414 assert(arg1.op == TOK.float64); 415 const x = arg0.toReal(); 416 const y = arg1.toReal(); 417 real_t result = CTFloat.zero; 418 CTFloat.yl2xp1(&x, &y, &result); 419 return new RealExp(loc, result, arg0.type); 420 } 421 422 Expression eval_toPrecFloat(Loc loc, FuncDeclaration fd, Expressions* arguments) 423 { 424 Expression arg0 = (*arguments)[0]; 425 float f = cast(real)arg0.toReal(); 426 return new RealExp(loc, real_t(f), Type.tfloat32); 427 } 428 429 Expression eval_toPrecDouble(Loc loc, FuncDeclaration fd, Expressions* arguments) 430 { 431 Expression arg0 = (*arguments)[0]; 432 double d = cast(real)arg0.toReal(); 433 return new RealExp(loc, real_t(d), Type.tfloat64); 434 } 435 436 Expression eval_toPrecReal(Loc loc, FuncDeclaration fd, Expressions* arguments) 437 { 438 Expression arg0 = (*arguments)[0]; 439 return new RealExp(loc, arg0.toReal(), Type.tfloat80); 440 }