mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-02-01 04:10:42 +00:00
Merge branch 'divVerent/submit/vector-bitops' of git://git.xonotic.org/xonotic/gmqcc into cooking
This commit is contained in:
commit
454234ef5f
10 changed files with 338 additions and 103 deletions
80
fold.c
80
fold.c
|
@ -73,6 +73,38 @@ static GMQCC_INLINE vec3_t vec3_neg(vec3_t a) {
|
|||
return out;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE vec3_t vec3_or(vec3_t a, vec3_t b) {
|
||||
vec3_t out;
|
||||
out.x = (qcfloat_t)(((qcint_t)a.x) | ((qcint_t)b.x));
|
||||
out.y = (qcfloat_t)(((qcint_t)a.y) | ((qcint_t)b.y));
|
||||
out.z = (qcfloat_t)(((qcint_t)a.z) | ((qcint_t)b.z));
|
||||
return out;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE vec3_t vec3_orvf(vec3_t a, qcfloat_t b) {
|
||||
vec3_t out;
|
||||
out.x = (qcfloat_t)(((qcint_t)a.x) | ((qcint_t)b));
|
||||
out.y = (qcfloat_t)(((qcint_t)a.y) | ((qcint_t)b));
|
||||
out.z = (qcfloat_t)(((qcint_t)a.z) | ((qcint_t)b));
|
||||
return out;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE vec3_t vec3_and(vec3_t a, vec3_t b) {
|
||||
vec3_t out;
|
||||
out.x = (qcfloat_t)(((qcint_t)a.x) & ((qcint_t)b.x));
|
||||
out.y = (qcfloat_t)(((qcint_t)a.y) & ((qcint_t)b.y));
|
||||
out.z = (qcfloat_t)(((qcint_t)a.z) & ((qcint_t)b.z));
|
||||
return out;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE vec3_t vec3_andvf(vec3_t a, qcfloat_t b) {
|
||||
vec3_t out;
|
||||
out.x = (qcfloat_t)(((qcint_t)a.x) & ((qcint_t)b));
|
||||
out.y = (qcfloat_t)(((qcint_t)a.y) & ((qcint_t)b));
|
||||
out.z = (qcfloat_t)(((qcint_t)a.z) & ((qcint_t)b));
|
||||
return out;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE vec3_t vec3_xor(vec3_t a, vec3_t b) {
|
||||
vec3_t out;
|
||||
out.x = (qcfloat_t)(((qcint_t)a.x) ^ ((qcint_t)b.x));
|
||||
|
@ -89,6 +121,14 @@ static GMQCC_INLINE vec3_t vec3_xorvf(vec3_t a, qcfloat_t b) {
|
|||
return out;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE vec3_t vec3_not(vec3_t a) {
|
||||
vec3_t out;
|
||||
out.x = (qcfloat_t)(~((qcint_t)a.x));
|
||||
out.y = (qcfloat_t)(~((qcint_t)a.y));
|
||||
out.z = (qcfloat_t)(~((qcint_t)a.z));
|
||||
return out;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE qcfloat_t vec3_mulvv(vec3_t a, vec3_t b) {
|
||||
return (a.x * b.x + a.y * b.y + a.z * b.z);
|
||||
}
|
||||
|
@ -184,6 +224,7 @@ fold_t *fold_init(parser_t *parser) {
|
|||
(void)fold_constgen_float (fold, -1.0f);
|
||||
|
||||
(void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
|
||||
(void)fold_constgen_vector(fold, vec3_create(-1.0f, -1.0f, -1.0f));
|
||||
|
||||
return fold;
|
||||
}
|
||||
|
@ -444,14 +485,34 @@ static GMQCC_INLINE ast_expression *fold_op_mod(fold_t *fold, ast_value *a, ast_
|
|||
}
|
||||
|
||||
static GMQCC_INLINE ast_expression *fold_op_bor(fold_t *fold, ast_value *a, ast_value *b) {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
|
||||
if (isfloat(a)) {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
|
||||
} else {
|
||||
if (isvector(b)) {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_vector(fold, vec3_or(fold_immvalue_vector(a), fold_immvalue_vector(b)));
|
||||
} else {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_vector(fold, vec3_orvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast_value *b) {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
|
||||
if (isfloat(a)) {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
|
||||
} else {
|
||||
if (isvector(b)) {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_vector(fold, vec3_and(fold_immvalue_vector(a), fold_immvalue_vector(b)));
|
||||
} else {
|
||||
if (fold_can_2(a, b))
|
||||
return fold_constgen_vector(fold, vec3_andvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -537,8 +598,15 @@ static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_
|
|||
}
|
||||
|
||||
static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
|
||||
if (fold_can_1(a))
|
||||
return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
|
||||
if (isfloat(a)) {
|
||||
if (fold_can_1(a))
|
||||
return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
|
||||
} else {
|
||||
if (isvector(a)) {
|
||||
if (fold_can_1(a))
|
||||
return fold_constgen_vector(fold, vec3_not(fold_immvalue_vector(a)));
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
11
gmqcc.h
11
gmqcc.h
|
@ -717,7 +717,16 @@ enum {
|
|||
* Creating this causes IR blocks to be marked as 'final'.
|
||||
* No-Return-Call
|
||||
*/
|
||||
VINSTR_NRCALL
|
||||
VINSTR_NRCALL,
|
||||
|
||||
/* Emulated instructions. */
|
||||
VINSTR_BITAND_V, /* BITAND_V must be the first emulated bitop */
|
||||
VINSTR_BITAND_VF,
|
||||
VINSTR_BITOR_V,
|
||||
VINSTR_BITOR_VF,
|
||||
VINSTR_BITXOR,
|
||||
VINSTR_BITXOR_V,
|
||||
VINSTR_BITXOR_VF /* BITXOR_VF must be the last emulated bitop */
|
||||
};
|
||||
|
||||
/* TODO: elide */
|
||||
|
|
190
ir.c
190
ir.c
|
@ -612,7 +612,8 @@ static bool instr_is_operation(uint16_t op)
|
|||
(op == INSTR_ADDRESS) ||
|
||||
(op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) ||
|
||||
(op >= INSTR_AND && op <= INSTR_BITOR) ||
|
||||
(op >= INSTR_CALL0 && op <= INSTR_CALL8) );
|
||||
(op >= INSTR_CALL0 && op <= INSTR_CALL8) ||
|
||||
(op >= VINSTR_BITAND_V && op <= VINSTR_BITXOR_VF) );
|
||||
}
|
||||
|
||||
static bool ir_function_pass_peephole(ir_function *self)
|
||||
|
@ -641,6 +642,7 @@ static bool ir_function_pass_peephole(ir_function *self)
|
|||
if (!instr_is_operation(oper->opcode))
|
||||
continue;
|
||||
|
||||
/* Old engine's mul for vector+float cannot deal with aliased inputs. */
|
||||
if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) {
|
||||
if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1])
|
||||
continue;
|
||||
|
@ -648,6 +650,18 @@ 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];
|
||||
|
||||
/* only do it for SSA values */
|
||||
|
@ -1770,6 +1784,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:
|
||||
|
@ -1806,6 +1821,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_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:
|
||||
|
@ -2501,7 +2522,15 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
|
|||
}
|
||||
}
|
||||
|
||||
if (instr->opcode == INSTR_MUL_VF)
|
||||
/* These operations need a special case as they can break when using
|
||||
* 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_BITXOR_VF ||
|
||||
instr->opcode == VINSTR_BITXOR_V)
|
||||
{
|
||||
value = instr->_ops[2];
|
||||
/* the float source will get an additional lifetime */
|
||||
|
@ -2766,6 +2795,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);
|
||||
|
@ -2798,6 +2828,145 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
|
|||
return true;
|
||||
}
|
||||
|
||||
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]);
|
||||
stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
++stmt.o1.s1;
|
||||
++stmt.o2.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
++stmt.o1.s1;
|
||||
++stmt.o2.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
|
||||
/* instruction generated */
|
||||
continue;
|
||||
}
|
||||
|
||||
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]);
|
||||
stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]);
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
++stmt.o1.s1;
|
||||
++stmt.o2.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
++stmt.o1.s1;
|
||||
++stmt.o2.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
|
||||
/* instruction generated */
|
||||
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]);
|
||||
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.o1.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
++stmt.o1.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
|
||||
/* instruction generated */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (instr->opcode == VINSTR_BITOR_VF) {
|
||||
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.o1.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
++stmt.o1.s1;
|
||||
++stmt.o3.s1;
|
||||
code_push_statement(code, &stmt, instr->context.line);
|
||||
|
||||
/* instruction generated */
|
||||
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];
|
||||
|
@ -3760,11 +3929,18 @@ static const char *qc_opname(int op)
|
|||
if (op < VINSTR_END)
|
||||
return util_instr_str[op];
|
||||
switch (op) {
|
||||
case VINSTR_END: return "END";
|
||||
case VINSTR_PHI: return "PHI";
|
||||
case VINSTR_JUMP: return "JUMP";
|
||||
case VINSTR_COND: return "COND";
|
||||
default: return "<UNK>";
|
||||
case VINSTR_END: return "END";
|
||||
case VINSTR_PHI: return "PHI";
|
||||
case VINSTR_JUMP: return "JUMP";
|
||||
case VINSTR_COND: return "COND";
|
||||
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 "<UNK>";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
127
parser.c
127
parser.c
|
@ -623,55 +623,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
|
||||
case opid1('|'):
|
||||
case opid1('&'):
|
||||
if (NotSameType(TYPE_FLOAT)) {
|
||||
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)))
|
||||
out = (ast_expression*)ast_binary_new(ctx,
|
||||
(op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
|
||||
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))
|
||||
|
@ -688,46 +640,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
* 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
|
||||
);
|
||||
out = (ast_expression*)ast_binary_new(ctx,
|
||||
(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.
|
||||
*/
|
||||
compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
|
||||
return false;
|
||||
out = (ast_expression*)ast_binary_new(ctx,
|
||||
(op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
|
||||
exprs[0], exprs[1]);
|
||||
} else {
|
||||
compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
|
||||
return false;
|
||||
out = (ast_expression*)ast_binary_new(ctx,
|
||||
(op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
|
||||
exprs[0], exprs[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1096,7 +1028,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
break;
|
||||
case opid2('&','='):
|
||||
case opid2('|','='):
|
||||
if (NotSameType(TYPE_FLOAT)) {
|
||||
case opid2('^','='):
|
||||
if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) {
|
||||
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 expression: %s and %s",
|
||||
|
@ -1110,16 +1043,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
assignop = type_storep_instr[exprs[0]->vtype];
|
||||
else
|
||||
assignop = type_store_instr[exprs[0]->vtype];
|
||||
out = (ast_expression*)ast_binstore_new(ctx, assignop,
|
||||
(op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
|
||||
exprs[0], exprs[1]);
|
||||
if (exprs[0]->vtype == TYPE_FLOAT)
|
||||
out = (ast_expression*)ast_binstore_new(ctx, assignop,
|
||||
(op->id == opid2('^','=') ? VINSTR_BITXOR : op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
|
||||
exprs[0], exprs[1]);
|
||||
else
|
||||
out = (ast_expression*)ast_binstore_new(ctx, assignop,
|
||||
(op->id == opid2('^','=') ? VINSTR_BITXOR_V : op->id == opid2('&','=') ? VINSTR_BITAND_V : VINSTR_BITOR_V),
|
||||
exprs[0], exprs[1]);
|
||||
break;
|
||||
case opid3('&','~','='):
|
||||
/* This is like: a &= ~(b);
|
||||
* But QC has no bitwise-not, so we implement it as
|
||||
* a -= a & (b);
|
||||
*/
|
||||
if (NotSameType(TYPE_FLOAT)) {
|
||||
if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) {
|
||||
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 expression: %s and %s",
|
||||
|
@ -1130,25 +1068,36 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
|||
assignop = type_storep_instr[exprs[0]->vtype];
|
||||
else
|
||||
assignop = type_store_instr[exprs[0]->vtype];
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
|
||||
if (exprs[0]->vtype == TYPE_FLOAT)
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
|
||||
else
|
||||
out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
|
||||
if (!out)
|
||||
return false;
|
||||
if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
|
||||
compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
|
||||
}
|
||||
asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
|
||||
if (exprs[0]->vtype == TYPE_FLOAT)
|
||||
asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
|
||||
else
|
||||
asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_V, exprs[0], out);
|
||||
asbinstore->keep_dest = true;
|
||||
out = (ast_expression*)asbinstore;
|
||||
break;
|
||||
|
||||
case opid2('~', 'P'):
|
||||
if (exprs[0]->vtype != TYPE_FLOAT) {
|
||||
if (exprs[0]->vtype != TYPE_FLOAT && exprs[0]->vtype != TYPE_VECTOR) {
|
||||
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
|
||||
compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
|
||||
return false;
|
||||
}
|
||||
if (!(out = fold_op(parser->fold, op, exprs)))
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
|
||||
if (!(out = fold_op(parser->fold, op, exprs))) {
|
||||
if (exprs[0]->vtype == TYPE_FLOAT) {
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
|
||||
} else {
|
||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#undef NotSameType
|
||||
|
|
|
@ -3,12 +3,18 @@ void main() {
|
|||
float b; b = 1;
|
||||
float c; c = 1;
|
||||
float d; d = 1;
|
||||
vector e; e = '1 1 1';
|
||||
vector f; f = '1 1 1';
|
||||
|
||||
a &~= 1; // 0
|
||||
b &= ~1; // 0
|
||||
c &= ~d; // 0
|
||||
f &~= e; // '0 0 0'
|
||||
e &= ~e; // '0 0 0'
|
||||
|
||||
print("a: ", ftos(a), "\nb: ",
|
||||
ftos(b), "\nc: ",
|
||||
ftos(c), "\n");
|
||||
print("e: ", vtos(e), "\n");
|
||||
print("f: ", vtos(f), "\n");
|
||||
}
|
||||
|
|
|
@ -7,3 +7,5 @@ E: $null
|
|||
M: a: 0
|
||||
M: b: 0
|
||||
M: c: 0
|
||||
M: e: '0 0 0'
|
||||
M: f: '0 0 0'
|
||||
|
|
|
@ -4,4 +4,8 @@ void main(vector v) {
|
|||
print(vtos(v), "\n");
|
||||
print(vtos(v / 2), "\n");
|
||||
print(vtos(v), "\n");
|
||||
print(vtos(v | 16), "\n");
|
||||
print(vtos(v & 16), "\n");
|
||||
print(vtos(v | '25 42 51'), "\n");
|
||||
print(vtos(v & '25 42 51'), "\n");
|
||||
}
|
||||
|
|
|
@ -7,3 +7,7 @@ M: '8 16 32'
|
|||
M: '4 8 16'
|
||||
M: '2 4 8'
|
||||
M: '4 8 16'
|
||||
M: '20 24 16'
|
||||
M: '0 0 16'
|
||||
M: '29 42 51'
|
||||
M: '0 8 16'
|
||||
|
|
14
tests/xor.qc
14
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;
|
||||
|
||||
|
@ -49,4 +55,12 @@ void main() {
|
|||
float swap_y = 200;
|
||||
vector swaps = swap(swap_x, swap_y);
|
||||
print("100:200 swapped is: ", ftos(swaps.x), ":", ftos(swaps.y), "\n");
|
||||
|
||||
// good olde xor swap test too
|
||||
vector swap_u = '1 2 3';
|
||||
vector swap_v = '4 5 6';
|
||||
swap_u ^= swap_v;
|
||||
swap_v ^= swap_u;
|
||||
swap_u ^= swap_v;
|
||||
print("'1 2 3':'4 5 6' swapped is: ", vtos(swap_u), ":", vtos(swap_v), "\n");
|
||||
}
|
||||
|
|
|
@ -10,4 +10,7 @@ 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
|
||||
M: '1 2 3':'4 5 6' swapped is: '4 5 6':'1 2 3'
|
||||
|
|
Loading…
Reference in a new issue