diff --git a/ast.c b/ast.c index 3f5f02b..ad85e4f 100644 --- a/ast.c +++ b/ast.c @@ -515,7 +515,7 @@ ast_unary* ast_unary_new(lex_ctx_t ctx, int op, ast_instantiate(ast_unary, ctx, ast_unary_delete); ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_unary_codegen); - self->op = op; + self->op = op; self->operand = expr; if (ast_istype(expr, ast_unary) && OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) { @@ -530,10 +530,13 @@ ast_unary* ast_unary_new(lex_ctx_t ctx, int op, ast_propagate_effects(self, expr); - if (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) { + if (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) { self->expression.vtype = TYPE_FLOAT; - } else + } else if (op >= VINSTR_NEG_F && op <= VINSTR_NEG_V) { + self->expression.vtype = TYPE_FLOAT; + } else { compile_error(ctx, "cannot determine type of unary operation %s", util_instr_str[op]); + } return self; } diff --git a/gmqcc.h b/gmqcc.h index d8ad374..9848f20 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -712,6 +712,7 @@ enum { VINSTR_PHI, VINSTR_JUMP, VINSTR_COND, + /* A never returning CALL. * Creating this causes IR blocks to be marked as 'final'. * No-Return-Call @@ -726,7 +727,9 @@ enum { VINSTR_BITXOR, VINSTR_BITXOR_V, VINSTR_BITXOR_VF, - VINSTR_CROSS + VINSTR_CROSS, + VINSTR_NEG_F, + VINSTR_NEG_V }; /* TODO: elide */ diff --git a/ir.c b/ir.c index f92d552..49d4b2f 100644 --- a/ir.c +++ b/ir.c @@ -1878,16 +1878,22 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx, case INSTR_NOT_V: case INSTR_NOT_S: case INSTR_NOT_ENT: - case INSTR_NOT_FNC: -#if 0 - case INSTR_NOT_I: -#endif + case INSTR_NOT_FNC: /* + case INSTR_NOT_I: */ ot = TYPE_FLOAT; break; - /* QC doesn't have other unary operations. We expect extensions to fill - * the above list, otherwise we assume out-type = in-type, eg for an - * unary minus + + /* + * Negation for virtual instructions is emulated with 0-value. Thankfully + * the operand for 0 already exists so we just source it from here. */ + case VINSTR_NEG_F: + return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_F, NULL, operand, ot); + break; + case VINSTR_NEG_V: + return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, NULL, operand, ot); + break; + default: ot = operand->vtype; break; diff --git a/parser.c b/parser.c index bfc8be4..7d4698e 100644 --- a/parser.c +++ b/parser.c @@ -466,22 +466,18 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) break; case opid2('-','P'): if (!(out = fold_op(parser->fold, op, exprs))) { - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, - (ast_expression*)parser->fold->imm_float[0], - exprs[0]); - break; - case TYPE_VECTOR: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, - (ast_expression*)parser->fold->imm_vector[0], - exprs[0]); - break; - default: - compile_error(ctx, "invalid types used in expression: cannot negate type %s", - type_name[exprs[0]->vtype]); + if (exprs[0]->vtype != TYPE_FLOAT && + exprs[0]->vtype != TYPE_VECTOR) { + compile_error(ctx, "invalid types used in unary expression: cannot negate type %s", + type_name[exprs[0]->vtype]); return false; } + /* + * TYPE_VECTOR = TYPE_FLOAT+1, + * VINSTR_NEG_V = VINSTR_NEG_F+1, + * thus (VINSTR_NEG_F-TYPE_FLOAT) + TYPE_* = VINSTR_NEG_*. + */ + out = (ast_expression*)ast_unary_new(ctx, (VINSTR_NEG_F-TYPE_FLOAT) + exprs[0]->vtype, exprs[0]); } break;