From d3c7b6fb3997256722ec0ea66bad733f0729ebba Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Thu, 7 Mar 2013 21:31:19 +0000 Subject: [PATCH] Implement exponentiation operator `**` as well as __builtin_pow (used for exponentiation operator). Use of exponentiation operator with constants results in const folded (precomputed at compile time exponentiation), otherwise runtime exponentiation with some clever loops (slow!). --- Makefile | 2 +- lexer.c | 4 +- lexer.h | 1 + parser.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 410a97d..fb17d6b 100644 --- a/Makefile +++ b/Makefile @@ -147,7 +147,7 @@ $(QCVM): $(OBJ_X) $(CC) -o $@ $^ $(CFLAGS) -lm $(GMQCC): $(OBJ_C) $(OBJ_D) - $(CC) -o $@ $^ $(CFLAGS) + $(CC) -o $@ $^ $(CFLAGS) -lm $(TESTSUITE): $(OBJ_T) $(CC) -o $@ $^ $(CFLAGS) -lm diff --git a/lexer.c b/lexer.c index 5f23952..4235613 100644 --- a/lexer.c +++ b/lexer.c @@ -1232,7 +1232,7 @@ int lex_do(lex_file *lex) /* case '+': case '-': - */ + */ case '*': case '/': case '<': @@ -1352,7 +1352,7 @@ int lex_do(lex_file *lex) lex_tokench(lex, ch); nextch = lex_getch(lex); - if (nextch == '=') { + if (nextch == '=' || nextch == '*') { lex_tokench(lex, nextch); } else lex_ungetch(lex, nextch); diff --git a/lexer.h b/lexer.h index 9724a7b..e560f6d 100644 --- a/lexer.h +++ b/lexer.h @@ -185,6 +185,7 @@ static const oper_info c_operators[] = { { "%", 2, opid1('%'), ASSOC_LEFT, 13, 0 }, { "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 }, + { "**", 2, opid2('*', '*'), ASSOC_LEFT, 12, 0 }, { "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 }, { "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 }, diff --git a/parser.c b/parser.c index 681207c..b97d92c 100644 --- a/parser.c +++ b/parser.c @@ -23,6 +23,7 @@ */ #include #include +#include #include "gmqcc.h" #include "lexer.h" @@ -119,6 +120,7 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname); static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname); static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef); +static ast_expression *parser_builtin_pow(parser_t *); static void parseerror(parser_t *parser, const char *fmt, ...) { @@ -1071,6 +1073,23 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]); break; + case opid2('*', '*'): + if (NotSameType(TYPE_FLOAT)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in exponentiation: %s and %s", + ty1, ty2); + + return false; + } + + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1))); + } else { + out = parser_builtin_pow(parser); + } + break; + case opid3('<','=','>'): /* -1, 0, or 1 */ if (NotSameType(TYPE_FLOAT)) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -1830,6 +1849,8 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { var = (ast_expression*)intrinsic_debug_typestring; } + if (!strcmp(parser_tokval(parser), "__builtin_pow")) + var = parser_builtin_pow(parser); if (!var) { char *correct = NULL; @@ -3294,6 +3315,145 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * return true; } +ast_expression *parser_builtin_pow(parser_t *parser) { + /* + * float __builtin_pow(float x, float y) { + * float value = 1.0f; + * while (y > 0) { + * while (!(y&1)) { + * y *= 2; + * x *= x; + * } + * y = y - 1; + * value = x * value; + * } + * return value; + * } + */ + static ast_function *pow_func = NULL; + static ast_value *pow_func_val = NULL; + if (!pow_func) { + ast_value *pow_arguments[2]; + ast_value *pow_value = ast_value_new (parser_ctx(parser), "value", TYPE_FLOAT); + ast_block *pow_body = ast_block_new (parser_ctx(parser)); + ast_block *pow_loop_body = ast_block_new (parser_ctx(parser)); + ast_block *pow_loop_nest_body = ast_block_new (parser_ctx(parser)); + ast_loop *pow_loop = NULL; + ast_loop *pow_loop_nest = NULL; + + pow_arguments[0] = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + pow_arguments[1] = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + pow_func_val = ast_value_new (parser_ctx(parser), "__builtin_pow", TYPE_FUNCTION); + pow_func_val->expression.next = (ast_expression*)ast_value_new(parser_ctx(parser), "", TYPE_FLOAT); + + vec_push(pow_func_val->expression.params, pow_arguments[0]); + vec_push(pow_func_val->expression.params, pow_arguments[1]); + + pow_func = ast_function_new(parser_ctx(parser), "__builtin_pow", pow_func_val); + + /* float value; */ + vec_push(pow_body->locals, pow_value); + /* value = 1.0f; */ + vec_push(pow_body->exprs, + (ast_expression*)ast_store_new( + parser_ctx(parser), + INSTR_STORE_F, + (ast_expression*)pow_value, + (ast_expression*)parser_const_float_1(parser) + ) + ); + + /* y >>= 2 */ + vec_push(pow_loop_nest_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float(parser, 0.25f) + ) + ); + vec_push(pow_loop_nest_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)pow_arguments[0], + (ast_expression*)pow_arguments[0] + ) + ); + + /* while (!(y&1)) */ + pow_loop_nest = ast_loop_new ( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_AND, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float_1(parser) + ), + true, + NULL, + false, + NULL, + (ast_expression*)pow_loop_nest_body + ); + + vec_push(pow_loop_body->exprs, (ast_expression*)pow_loop_nest); + vec_push(pow_loop_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_SUB_F, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float_1(parser) + ) + ); + vec_push(pow_loop_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)pow_value, + (ast_expression*)pow_arguments[0] + ) + ); + + /* while (y > 0) { */ + pow_loop = ast_loop_new( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_GT, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float_0(parser) + ), + false, + NULL, + false, + NULL, + (ast_expression*)pow_loop_body + ); + /* } */ + vec_push(pow_body->exprs, (ast_expression*)pow_loop); + /* return value; */ + vec_push(pow_body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)pow_value + ) + ); + + vec_push(pow_func->blocks, pow_body); + vec_push(parser->globals, (ast_expression*)pow_func_val); + vec_push(parser->functions, pow_func); + } + + return (ast_expression*)pow_func_val; +} + /* parse computed goto sides */ static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) { ast_expression *on_true;