From 7dcebf232ae7acbfe610357508ac63788c7bd123 Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Mon, 26 Aug 2013 11:40:43 +0200 Subject: [PATCH] XOR! --- gmqcc.h | 9 ++-- ir.c | 106 ++++++++++++++++++++++++++++++++++++++++++----- parser.c | 109 +++---------------------------------------------- tests/xor.qc | 6 +++ tests/xor.tmpl | 2 + 5 files changed, 116 insertions(+), 116 deletions(-) diff --git a/gmqcc.h b/gmqcc.h index 13ce4f6..f7e9502 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -719,10 +719,13 @@ enum { */ VINSTR_NRCALL, /* Emulated instructions. */ - VINSTR_BITAND_VV, - VINSTR_BITOR_VV, + VINSTR_BITXOR, + VINSTR_BITAND_V, + VINSTR_BITOR_V, + VINSTR_BITXOR_V, VINSTR_BITAND_VF, - VINSTR_BITOR_VF + VINSTR_BITOR_VF, + VINSTR_BITXOR_VF }; /* TODO: elide */ diff --git a/ir.c b/ir.c index 94f49c2..e60dfbd 100644 --- a/ir.c +++ b/ir.c @@ -613,10 +613,13 @@ static bool instr_is_operation(uint16_t op) (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) || (op >= INSTR_AND && op <= INSTR_BITOR) || (op >= INSTR_CALL0 && op <= INSTR_CALL8) || - (op == VINSTR_BITAND_VV) || + (op == VINSTR_BITXOR) || + (op == VINSTR_BITAND_V) || (op == VINSTR_BITAND_VF) || - (op == VINSTR_BITOR_VV) || - (op == VINSTR_BITOR_VF)); + (op == VINSTR_BITOR_V) || + (op == VINSTR_BITOR_VF) || + (op == VINSTR_BITXOR_V) || + (op == VINSTR_BITXOR_VF)); } static bool ir_function_pass_peephole(ir_function *self) @@ -653,11 +656,17 @@ static bool ir_function_pass_peephole(ir_function *self) continue; } + /* Emulated bitxor cannot deal with aliased inputs. */ + if (oper->opcode == VINSTR_BITXOR && oper->_ops[2]->memberof == oper->_ops[1]) + continue; + /* Emulated bitand/bitor for vector+float cannot deal with aliased inputs. */ if (oper->opcode == VINSTR_BITAND_VF && oper->_ops[2]->memberof == oper->_ops[1]) continue; if (oper->opcode == VINSTR_BITOR_VF && oper->_ops[2]->memberof == oper->_ops[1]) continue; + if (oper->opcode == VINSTR_BITXOR_VF && oper->_ops[2]->memberof == oper->_ops[1]) + continue; value = oper->_ops[0]; @@ -1781,6 +1790,7 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx, #endif case INSTR_BITAND: case INSTR_BITOR: + case VINSTR_BITXOR: #if 0 case INSTR_SUB_S: /* -- offset of string as float */ case INSTR_MUL_IF: @@ -1817,10 +1827,12 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx, case INSTR_SUB_V: case INSTR_MUL_VF: case INSTR_MUL_FV: - case VINSTR_BITAND_VV: - case VINSTR_BITOR_VV: + case VINSTR_BITAND_V: + case VINSTR_BITOR_V: + case VINSTR_BITXOR_V: case VINSTR_BITAND_VF: case VINSTR_BITOR_VF: + case VINSTR_BITXOR_VF: #if 0 case INSTR_DIV_VF: case INSTR_MUL_IV: @@ -2520,8 +2532,11 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed) * same source and destination operand otherwise, as the engine may * read the source multiple times. */ if (instr->opcode == INSTR_MUL_VF || + instr->opcode == VINSTR_BITXOR || instr->opcode == VINSTR_BITAND_VF || - instr->opcode == VINSTR_BITOR_VF) + instr->opcode == VINSTR_BITOR_VF || + instr->opcode == VINSTR_BITXOR_VF || + instr->opcode == VINSTR_BITXOR_V) { value = instr->_ops[2]; /* the float source will get an additional lifetime */ @@ -2786,6 +2801,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc ir_block *onfalse; size_t stidx; size_t i; + int j; block->generated = true; block->code_start = vec_size(code->statements); @@ -2818,7 +2834,28 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc return true; } - if (instr->opcode == VINSTR_BITAND_VV) { + if (instr->opcode == VINSTR_BITXOR) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context.line); + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + code_push_statement(code, &stmt, instr->context.line); + stmt.opcode = INSTR_SUB_F; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]); + stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context.line); + + /* instruction generated */ + continue; + } + + if (instr->opcode == VINSTR_BITAND_V) { stmt.opcode = INSTR_BITAND; stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); @@ -2837,7 +2874,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc continue; } - if (instr->opcode == VINSTR_BITOR_VV) { + if (instr->opcode == VINSTR_BITOR_V) { stmt.opcode = INSTR_BITOR; stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); @@ -2856,6 +2893,29 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc continue; } + if (instr->opcode == VINSTR_BITXOR_V) { + for (j = 0; j < 3; ++j) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + j; + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j; + code_push_statement(code, &stmt, instr->context.line); + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + j; + stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j; + code_push_statement(code, &stmt, instr->context.line); + } + stmt.opcode = INSTR_SUB_V; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]); + stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context.line); + + /* instruction generated */ + continue; + } + if (instr->opcode == VINSTR_BITAND_VF) { stmt.opcode = INSTR_BITAND; stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]); @@ -2890,6 +2950,29 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc continue; } + if (instr->opcode == VINSTR_BITXOR_VF) { + for (j = 0; j < 3; ++j) { + stmt.opcode = INSTR_BITOR; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j; + code_push_statement(code, &stmt, instr->context.line); + stmt.opcode = INSTR_BITAND; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + j; + stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]); + stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j; + code_push_statement(code, &stmt, instr->context.line); + } + stmt.opcode = INSTR_SUB_V; + stmt.o1.s1 = ir_value_code_addr(instr->_ops[0]); + stmt.o2.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]); + stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]); + code_push_statement(code, &stmt, instr->context.line); + + /* instruction generated */ + continue; + } + if (instr->opcode == VINSTR_COND) { ontrue = instr->bops[0]; onfalse = instr->bops[1]; @@ -3856,10 +3939,13 @@ static const char *qc_opname(int op) case VINSTR_PHI: return "PHI"; case VINSTR_JUMP: return "JUMP"; case VINSTR_COND: return "COND"; - case VINSTR_BITAND_VV: return "BITAND_VV"; - case VINSTR_BITOR_VV: return "BITOR_VV"; + case VINSTR_BITXOR: return "BITXOR"; + case VINSTR_BITAND_V: return "BITAND_V"; + case VINSTR_BITOR_V: return "BITOR_V"; + case VINSTR_BITXOR_V: return "BITXOR_V"; case VINSTR_BITAND_VF: return "BITAND_VF"; case VINSTR_BITOR_VF: return "BITOR_VF"; + case VINSTR_BITXOR_VF: return "BITXOR_VF"; default: return ""; } } diff --git a/parser.c b/parser.c index 05aac15..1d94e92 100644 --- a/parser.c +++ b/parser.c @@ -623,6 +623,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid1('|'): case opid1('&'): + case opid1('^'): if ( !(exprs[0]->vtype == TYPE_FLOAT && exprs[1]->vtype == TYPE_FLOAT) && !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) && !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR)) @@ -640,127 +641,29 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) */ if (exprs[0]->vtype == TYPE_FLOAT) { out = (ast_expression*)ast_binary_new(ctx, - (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), + (op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), exprs[0], exprs[1]); } else { /* - * The first is a vector: vector is allowed to xor with vector and + * The first is a vector: vector is allowed to bitop with vector and * with scalar, branch here for the second operand. */ if (exprs[1]->vtype == TYPE_VECTOR) { /* - * Xor all the values of the vector components against the + * Bitop all the values of the vector components against the * vectors components in question. */ out = (ast_expression*)ast_binary_new(ctx, - (op->id == opid1('|') ? VINSTR_BITOR_VV : VINSTR_BITAND_VV), + (op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V), exprs[0], exprs[1]); } else { out = (ast_expression*)ast_binary_new(ctx, - (op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF), + (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF), exprs[0], exprs[1]); } } } break; - case opid1('^'): - /* - * Okay lets designate what the hell is an acceptable use - * of the ^ operator. In many vector processing units, XOR - * is allowed to be used on vectors, but only if the first - * operand is a vector, the second operand can be a float - * or vector. It's never legal for the first operand to be - * a float, and then the following operand to be a vector. - * Further more, the only time it is legal to do XOR otherwise - * is when both operand are floats. This nicely crafted if - * statement catches them all. - * - * In the event that the first operand is a vector, two - * possible situations can arise, thus, each element of - * vector A (operand A) is exclusive-ORed with the corresponding - * element of vector B (operand B), If B is scalar, the - * scalar value is first replicated for each element. - * - * The QCVM itself lacks a BITXOR instruction. Thus emulating - * the mathematics of it is required. The following equation - * is used: (LHS | RHS) & ~(LHS & RHS). However, due to the - * QCVM also lacking a BITNEG instruction, we need to emulate - * ~FOO with -1 - FOO, the whole process becoming this nicely - * crafted expression: (LHS | RHS) & (-1 - (LHS & RHS)). - * - * When A is not scalar, this process is repeated for all - * components of vector A with the value in operand B, - * only if operand B is scalar. When A is not scalar, and B - * is also not scalar, this process is repeated for all - * components of the vector A with the components of vector B. - * Finally when A is scalar and B is scalar, this process is - * simply used once for A and B being LHS and RHS respectfully. - * - * Yes the semantics are a bit strange (no pun intended). - * But then again BITXOR is strange itself, consdering it's - * commutative, assocative, and elements of the BITXOR operation - * are their own inverse. - */ - if ( !(exprs[0]->vtype == TYPE_FLOAT && exprs[1]->vtype == TYPE_FLOAT) && - !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) && - !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR)) - { - compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s", - type_name[exprs[0]->vtype], - type_name[exprs[1]->vtype]); - return false; - } - - if (!(out = fold_op(parser->fold, op, exprs))) { - /* - * IF the first expression is float, the following will be too - * since scalar ^ vector is not allowed. - */ - if (exprs[0]->vtype == TYPE_FLOAT) { - ast_binary *expr = ast_binary_new( - ctx, - INSTR_SUB_F, - (ast_expression*)parser->fold->imm_float[2], - (ast_expression*)ast_binary_new( - ctx, - INSTR_BITAND, - exprs[0], - exprs[1] - ) - ); - expr->refs = AST_REF_NONE; - - out = (ast_expression*) - ast_binary_new( - ctx, - INSTR_BITAND, - (ast_expression*)ast_binary_new( - ctx, - INSTR_BITOR, - exprs[0], - exprs[1] - ), - (ast_expression*)expr - ); - } else { - /* - * The first is a vector: vector is allowed to xor with vector and - * with scalar, branch here for the second operand. - */ - if (exprs[1]->vtype == TYPE_VECTOR) { - /* - * Xor all the values of the vector components against the - * vectors components in question. - */ - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector"); - return false; - } else { - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float"); - return false; - } - } - } - break; case opid2('<','<'): case opid2('>','>'): diff --git a/tests/xor.qc b/tests/xor.qc index 41d02e9..cb79972 100644 --- a/tests/xor.qc +++ b/tests/xor.qc @@ -38,6 +38,12 @@ void main() { // vector ^ vector // vector ^ float // are legal in constant expressions (currently) + vector v1 = '5 2 5'; + vector v2 = '3 10 3'; + + print("vv: ", vtos(v1 ^ v2), "\n"); + print("vf: ", vtos(v1 ^ 10), "\n"); + const vector v3 = '5 2 5' ^ '3 10 3'; const vector v4 = '5 2 5' ^ 10; diff --git a/tests/xor.tmpl b/tests/xor.tmpl index 5394f3d..2f0ff10 100644 --- a/tests/xor.tmpl +++ b/tests/xor.tmpl @@ -10,4 +10,6 @@ M: assocative M: inverse M: vv: '6 8 6' M: vf: '15 8 15' +M: vv: '6 8 6' +M: vf: '15 8 15' M: 100:200 swapped is: 200:100 -- 2.39.2