mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2024-11-23 20:33:05 +00:00
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!).
This commit is contained in:
parent
a32f59b256
commit
d3c7b6fb39
4 changed files with 164 additions and 3 deletions
2
Makefile
2
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
|
||||
|
|
4
lexer.c
4
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);
|
||||
|
|
1
lexer.h
1
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 },
|
||||
|
|
160
parser.c
160
parser.c
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
|
||||
#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), "<float>", 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;
|
||||
|
|
Loading…
Reference in a new issue