2 * Copyright (C) 2012, 2013
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is furnished to do
10 * so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * Provides all the "intrinsics" / "builtins" for GMQCC. These can do
28 * a few things, they can provide fall back implementations for math
29 * functions if the definitions don't exist for some given engine. Or
30 * then can determine definitions for existing builtins, and simply
31 * wrap back to them instead. This is like a "portable" intrface that
32 * is entered when -fintrin is used (causing all existing builtins to
33 * be ignored by the compiler and instead interface through here.
35 #define intrin_ctx(I) parser_ctx((I)->parser)
37 static GMQCC_INLINE ast_function *intrin_value(intrin_t *intrin, ast_value **out, const char *name, qcint_t vtype) {
38 ast_value *value = NULL;
39 ast_function *func = NULL;
43 util_snprintf(buffer, sizeof(buffer), "__builtin_%s", name);
44 util_snprintf(stype, sizeof(stype), "<%s>", type_name[vtype]);
46 value = ast_value_new(intrin_ctx(intrin), buffer, TYPE_FUNCTION);
47 value->intrinsic = true;
48 value->expression.next = (ast_expression*)ast_value_new(intrin_ctx(intrin), stype, vtype);
49 func = ast_function_new(intrin_ctx(intrin), buffer, value);
50 value->expression.flags |= AST_FLAG_ERASEABLE;
56 static GMQCC_INLINE void intrin_reg(intrin_t *intrin, ast_value *const value, ast_function *const func) {
57 vec_push(intrin->parser->functions, func);
58 vec_push(intrin->parser->globals, (ast_expression*)value);
61 #define QC_M_E 2.71828182845905f
63 static ast_expression *intrin_pow (intrin_t *intrin) {
65 * float pow(float x, float y) {
78 ast_value *value = NULL;
79 ast_value *arg1 = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
80 ast_value *arg2 = ast_value_new(intrin_ctx(intrin), "y", TYPE_FLOAT);
81 ast_value *local = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
82 ast_block *body = ast_block_new(intrin_ctx(intrin));
83 ast_block *l1b = ast_block_new(intrin_ctx(intrin)); /* loop 1 body */
84 ast_block *l2b = ast_block_new(intrin_ctx(intrin)); /* loop 2 body */
85 ast_loop *loop1 = NULL;
86 ast_loop *loop2 = NULL;
87 ast_function *func = intrin_value(intrin, &value, "pow", TYPE_FLOAT);
90 vec_push(value->expression.params, arg1);
91 vec_push(value->expression.params, arg2);
94 vec_push(body->locals, local);
96 /* assignment to local of value 1.0f */
98 (ast_expression*)ast_store_new (
101 (ast_expression*)local,
102 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
108 (ast_expression*)ast_binstore_new (
112 (ast_expression*)arg2,
113 (ast_expression*)fold_constgen_float(intrin->parser->fold, 0.25f)
119 (ast_expression*)ast_binstore_new (
123 (ast_expression*)arg1,
124 (ast_expression*)arg1
129 loop2 = ast_loop_new (
132 (ast_expression*)ast_binary_new (
135 (ast_expression*)arg2,
136 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
145 /* push nested loop into loop expressions */
146 vec_push(l1b->exprs, (ast_expression*)loop2);
150 (ast_expression*)ast_binstore_new (
154 (ast_expression*)arg2,
155 (ast_expression*)intrin->fold->imm_float[1] /* 1 == 1.0f */
160 (ast_expression*)ast_binstore_new (
164 (ast_expression*)local,
165 (ast_expression*)arg1
170 loop1 = ast_loop_new (
173 (ast_expression*)ast_binary_new (
176 (ast_expression*)arg2,
177 (ast_expression*)intrin->fold->imm_float[0] /* 0 == 0.0f */
186 /* push the loop1 into the body for the function */
187 vec_push(body->exprs, (ast_expression*)loop1);
190 vec_push(body->exprs,
191 (ast_expression*)ast_return_new (
193 (ast_expression*)local
197 /* push block and register intrin for codegen */
198 vec_push(func->blocks, body);
200 intrin_reg(intrin, value, func);
202 return (ast_expression*)value;
205 static ast_expression *intrin_mod(intrin_t *intrin) {
207 * float mod(float a, float b) {
209 * float sign = (div < 0.0f) ? -1 : 1;
210 * return a - b * sign * floor(sign * div);
213 ast_value *value = NULL;
214 ast_call *call = ast_call_new (intrin_ctx(intrin), intrin_func(intrin, "floor"));
215 ast_value *a = ast_value_new(intrin_ctx(intrin), "a", TYPE_FLOAT);
216 ast_value *b = ast_value_new(intrin_ctx(intrin), "b", TYPE_FLOAT);
217 ast_value *div = ast_value_new(intrin_ctx(intrin), "div", TYPE_FLOAT);
218 ast_value *sign = ast_value_new(intrin_ctx(intrin), "sign", TYPE_FLOAT);
219 ast_block *body = ast_block_new(intrin_ctx(intrin));
220 ast_function *func = intrin_value(intrin, &value, "mod", TYPE_FLOAT);
222 vec_push(value->expression.params, a);
223 vec_push(value->expression.params, b);
225 vec_push(body->locals, div);
226 vec_push(body->locals, sign);
229 vec_push(body->exprs,
230 (ast_expression*)ast_store_new(
233 (ast_expression*)div,
234 (ast_expression*)ast_binary_new(
243 /* sign = (div < 0.0f) ? -1 : 1; */
244 vec_push(body->exprs,
245 (ast_expression*)ast_store_new(
248 (ast_expression*)sign,
249 (ast_expression*)ast_ternary_new(
251 (ast_expression*)ast_binary_new(
254 (ast_expression*)div,
255 (ast_expression*)intrin->fold->imm_float[0]
257 (ast_expression*)intrin->fold->imm_float[2],
258 (ast_expression*)intrin->fold->imm_float[1]
263 /* floor(sign * div) */
264 vec_push(call->params,
265 (ast_expression*)ast_binary_new(
268 (ast_expression*)sign,
273 /* return a - b * sign * <call> */
274 vec_push(body->exprs,
275 (ast_expression*)ast_return_new(
277 (ast_expression*)ast_binary_new(
281 (ast_expression*)ast_binary_new(
285 (ast_expression*)ast_binary_new(
288 (ast_expression*)sign,
289 (ast_expression*)call
296 vec_push(func->blocks, body); /* {{{ body }}} */
297 intrin_reg(intrin, value, func);
299 return (ast_expression*)value;
302 static ast_expression *intrin_exp(intrin_t *intrin) {
304 * float exp(float x) {
305 * return pow(QC_M_E, x);
308 ast_value *value = NULL;
309 ast_call *call = ast_call_new (intrin_ctx(intrin), intrin_func(intrin, "pow"));
310 ast_value *arg1 = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
311 ast_block *body = ast_block_new(intrin_ctx(intrin));
312 ast_function *func = intrin_value(intrin, &value, "exp", TYPE_FLOAT);
314 /* push arguments for params to call */
315 vec_push(call->params, (ast_expression*)fold_constgen_float(intrin->fold, QC_M_E));
316 vec_push(call->params, (ast_expression*)arg1);
318 /* return pow(QC_M_E, x) */
319 vec_push(body->exprs,
320 (ast_expression*)ast_return_new(
322 (ast_expression*)call
326 vec_push(value->expression.params, arg1); /* float x (for param) */
328 vec_push(func->blocks, body); /* {{{ body }}} */
330 intrin_reg(intrin, value, func);
332 return (ast_expression*)value;
335 static ast_expression *intrin_isnan(intrin_t *intrin) {
337 * float isnan(float x) {
341 * return (x != local);
344 ast_value *value = NULL;
345 ast_value *arg1 = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
346 ast_value *local = ast_value_new(intrin_ctx(intrin), "local", TYPE_FLOAT);
347 ast_block *body = ast_block_new(intrin_ctx(intrin));
348 ast_function *func = intrin_value(intrin, &value, "isnan", TYPE_FLOAT);
350 vec_push(body->locals, local);
351 vec_push(body->exprs,
352 (ast_expression*)ast_store_new(
355 (ast_expression*)local,
356 (ast_expression*)arg1
360 vec_push(body->exprs,
361 (ast_expression*)ast_return_new(
363 (ast_expression*)ast_binary_new(
366 (ast_expression*)arg1,
367 (ast_expression*)local
372 vec_push(value->expression.params, arg1);
373 vec_push(func->blocks, body);
375 intrin_reg(intrin, value, func);
377 return (ast_expression*)value;
380 static ast_expression *intrin_fabs(intrin_t *intrin) {
382 * float fabs(float x) {
383 * return x < 0 ? -x : x;
386 ast_value *value = NULL;
387 ast_value *arg1 = ast_value_new(intrin_ctx(intrin), "x", TYPE_FLOAT);
388 ast_block *body = ast_block_new(intrin_ctx(intrin));
389 ast_function *func = intrin_value(intrin, &value, "fabs", TYPE_FLOAT);
391 vec_push(body->exprs,
392 (ast_expression*)ast_return_new(
394 (ast_expression*)ast_ternary_new(
396 (ast_expression*)ast_binary_new(
399 (ast_expression*)arg1,
400 (ast_expression*)intrin->fold->imm_float[0]
402 (ast_expression*)ast_binary_new(
405 (ast_expression*)intrin->fold->imm_float[0],
406 (ast_expression*)arg1
408 (ast_expression*)arg1
413 vec_push(value->expression.params, arg1);
414 vec_push(func->blocks, body);
416 intrin_reg(intrin, value, func);
418 return (ast_expression*)value;
422 * TODO: make static (and handle ast_type_string) here for the builtin
423 * instead of in SYA parse close.
425 ast_expression *intrin_debug_typestring(intrin_t *intrin) {
427 return (ast_expression*)0x1;
430 static const intrin_func_t intrinsics[] = {
431 {&intrin_exp, "__builtin_exp", "exp", 1},
432 {&intrin_mod, "__builtin_mod", "mod", 2},
433 {&intrin_pow, "__builtin_pow", "pow", 2},
434 {&intrin_isnan, "__builtin_isnan", "isnan", 1},
435 {&intrin_fabs, "__builtin_fabs", "fabs", 1},
436 {&intrin_debug_typestring, "__builtin_debug_typestring", "", 0}
439 static void intrin_error(intrin_t *intrin, const char *fmt, ...) {
442 vcompile_error(intrin->parser->lex->tok.ctx, fmt, ap);
447 intrin_t *intrin_init(parser_t *parser) {
448 intrin_t *intrin = (intrin_t*)mem_a(sizeof(intrin_t));
451 intrin->parser = parser;
452 intrin->fold = parser->fold;
453 intrin->intrinsics = NULL;
454 intrin->generated = NULL;
456 vec_append(intrin->intrinsics, GMQCC_ARRAY_COUNT(intrinsics), intrinsics);
458 /* populate with null pointers for tracking generation */
459 for (i = 0; i < GMQCC_ARRAY_COUNT(intrinsics); i++)
460 vec_push(intrin->generated, NULL);
465 void intrin_cleanup(intrin_t *intrin) {
466 vec_free(intrin->intrinsics);
467 vec_free(intrin->generated);
471 ast_expression *intrin_fold(intrin_t *intrin, ast_value *value, ast_expression **exprs) {
473 if (!value || !value->name)
475 for (i = 0; i < vec_size(intrin->intrinsics); i++)
476 if (!strcmp(value->name, intrin->intrinsics[i].name))
477 return (vec_size(exprs) != intrin->intrinsics[i].args)
479 : fold_intrin(intrin->fold, value->name + 10, exprs);
483 static GMQCC_INLINE ast_expression *intrin_func_try(intrin_t *intrin, size_t offset, const char *compare) {
485 for (i = 0; i < vec_size(intrin->intrinsics); i++) {
486 if (strcmp(*(char **)((char *)&intrin->intrinsics[i] + offset), compare))
488 if (intrin->generated[i])
489 return intrin->generated[i];
490 return intrin->generated[i] = intrin->intrinsics[i].intrin(intrin);
495 ast_expression *intrin_func(intrin_t *intrin, const char *name) {
497 ast_expression *find;
499 /* try current first */
500 if ((find = parser_find_global(intrin->parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
501 for (i = 0; i < vec_size(intrin->parser->functions); ++i)
502 if (((ast_value*)find)->name && !strcmp(intrin->parser->functions[i]->name, ((ast_value*)find)->name) && intrin->parser->functions[i]->builtin < 0)
504 /* try name second */
505 if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, name), name)))
507 /* try alias third */
508 if ((find = intrin_func_try(intrin, offsetof(intrin_func_t, alias), name)))
511 intrin_error(intrin, "need function: `%s` compiler depends on it", name);