mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-02-12 14:45:52 +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
68
fold.c
68
fold.c
|
@ -73,6 +73,38 @@ static GMQCC_INLINE vec3_t vec3_neg(vec3_t a) {
|
||||||
return out;
|
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) {
|
static GMQCC_INLINE vec3_t vec3_xor(vec3_t a, vec3_t b) {
|
||||||
vec3_t out;
|
vec3_t out;
|
||||||
out.x = (qcfloat_t)(((qcint_t)a.x) ^ ((qcint_t)b.x));
|
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;
|
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) {
|
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);
|
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_float (fold, -1.0f);
|
||||||
|
|
||||||
(void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.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;
|
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) {
|
static GMQCC_INLINE ast_expression *fold_op_bor(fold_t *fold, ast_value *a, ast_value *b) {
|
||||||
|
if (isfloat(a)) {
|
||||||
if (fold_can_2(a, 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))));
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast_value *b) {
|
static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast_value *b) {
|
||||||
|
if (isfloat(a)) {
|
||||||
if (fold_can_2(a, 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))));
|
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;
|
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) {
|
static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
|
||||||
|
if (isfloat(a)) {
|
||||||
if (fold_can_1(a))
|
if (fold_can_1(a))
|
||||||
return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
gmqcc.h
11
gmqcc.h
|
@ -717,7 +717,16 @@ enum {
|
||||||
* Creating this causes IR blocks to be marked as 'final'.
|
* Creating this causes IR blocks to be marked as 'final'.
|
||||||
* No-Return-Call
|
* 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 */
|
/* TODO: elide */
|
||||||
|
|
180
ir.c
180
ir.c
|
@ -612,7 +612,8 @@ static bool instr_is_operation(uint16_t op)
|
||||||
(op == INSTR_ADDRESS) ||
|
(op == INSTR_ADDRESS) ||
|
||||||
(op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) ||
|
(op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) ||
|
||||||
(op >= INSTR_AND && op <= INSTR_BITOR) ||
|
(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)
|
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))
|
if (!instr_is_operation(oper->opcode))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* Old engine's mul for vector+float cannot deal with aliased inputs. */
|
||||||
if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) {
|
if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) {
|
||||||
if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1])
|
if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1])
|
||||||
continue;
|
continue;
|
||||||
|
@ -648,6 +650,18 @@ static bool ir_function_pass_peephole(ir_function *self)
|
||||||
continue;
|
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];
|
value = oper->_ops[0];
|
||||||
|
|
||||||
/* only do it for SSA values */
|
/* only do it for SSA values */
|
||||||
|
@ -1770,6 +1784,7 @@ ir_value* ir_block_create_binop(ir_block *self, lex_ctx_t ctx,
|
||||||
#endif
|
#endif
|
||||||
case INSTR_BITAND:
|
case INSTR_BITAND:
|
||||||
case INSTR_BITOR:
|
case INSTR_BITOR:
|
||||||
|
case VINSTR_BITXOR:
|
||||||
#if 0
|
#if 0
|
||||||
case INSTR_SUB_S: /* -- offset of string as float */
|
case INSTR_SUB_S: /* -- offset of string as float */
|
||||||
case INSTR_MUL_IF:
|
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_SUB_V:
|
||||||
case INSTR_MUL_VF:
|
case INSTR_MUL_VF:
|
||||||
case INSTR_MUL_FV:
|
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
|
#if 0
|
||||||
case INSTR_DIV_VF:
|
case INSTR_DIV_VF:
|
||||||
case INSTR_MUL_IV:
|
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];
|
value = instr->_ops[2];
|
||||||
/* the float source will get an additional lifetime */
|
/* 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;
|
ir_block *onfalse;
|
||||||
size_t stidx;
|
size_t stidx;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
int j;
|
||||||
|
|
||||||
block->generated = true;
|
block->generated = true;
|
||||||
block->code_start = vec_size(code->statements);
|
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;
|
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) {
|
if (instr->opcode == VINSTR_COND) {
|
||||||
ontrue = instr->bops[0];
|
ontrue = instr->bops[0];
|
||||||
onfalse = instr->bops[1];
|
onfalse = instr->bops[1];
|
||||||
|
@ -3764,6 +3933,13 @@ static const char *qc_opname(int op)
|
||||||
case VINSTR_PHI: return "PHI";
|
case VINSTR_PHI: return "PHI";
|
||||||
case VINSTR_JUMP: return "JUMP";
|
case VINSTR_JUMP: return "JUMP";
|
||||||
case VINSTR_COND: return "COND";
|
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>";
|
default: return "<UNK>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
117
parser.c
117
parser.c
|
@ -623,55 +623,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
||||||
|
|
||||||
case opid1('|'):
|
case opid1('|'):
|
||||||
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('^'):
|
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) &&
|
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_FLOAT) &&
|
||||||
!(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR))
|
!(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.
|
* since scalar ^ vector is not allowed.
|
||||||
*/
|
*/
|
||||||
if (exprs[0]->vtype == TYPE_FLOAT) {
|
if (exprs[0]->vtype == TYPE_FLOAT) {
|
||||||
ast_binary *expr = ast_binary_new(
|
out = (ast_expression*)ast_binary_new(ctx,
|
||||||
ctx,
|
(op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
|
||||||
INSTR_SUB_F,
|
exprs[0], exprs[1]);
|
||||||
(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 {
|
} 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.
|
* with scalar, branch here for the second operand.
|
||||||
*/
|
*/
|
||||||
if (exprs[1]->vtype == TYPE_VECTOR) {
|
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.
|
* vectors components in question.
|
||||||
*/
|
*/
|
||||||
compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
|
out = (ast_expression*)ast_binary_new(ctx,
|
||||||
return false;
|
(op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
|
||||||
|
exprs[0], exprs[1]);
|
||||||
} else {
|
} else {
|
||||||
compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
|
out = (ast_expression*)ast_binary_new(ctx,
|
||||||
return false;
|
(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;
|
break;
|
||||||
case opid2('&','='):
|
case opid2('&','='):
|
||||||
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[0], ty1, sizeof(ty1));
|
||||||
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
|
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
|
||||||
compile_error(ctx, "invalid types used in expression: %s and %s",
|
compile_error(ctx, "invalid types used in expression: %s and %s",
|
||||||
|
@ -1110,8 +1043,13 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
||||||
assignop = type_storep_instr[exprs[0]->vtype];
|
assignop = type_storep_instr[exprs[0]->vtype];
|
||||||
else
|
else
|
||||||
assignop = type_store_instr[exprs[0]->vtype];
|
assignop = type_store_instr[exprs[0]->vtype];
|
||||||
|
if (exprs[0]->vtype == TYPE_FLOAT)
|
||||||
out = (ast_expression*)ast_binstore_new(ctx, assignop,
|
out = (ast_expression*)ast_binstore_new(ctx, assignop,
|
||||||
(op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
|
(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]);
|
exprs[0], exprs[1]);
|
||||||
break;
|
break;
|
||||||
case opid3('&','~','='):
|
case opid3('&','~','='):
|
||||||
|
@ -1119,7 +1057,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
|
||||||
* But QC has no bitwise-not, so we implement it as
|
* But QC has no bitwise-not, so we implement it as
|
||||||
* a -= a & (b);
|
* 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[0], ty1, sizeof(ty1));
|
||||||
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
|
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
|
||||||
compile_error(ctx, "invalid types used in expression: %s and %s",
|
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];
|
assignop = type_storep_instr[exprs[0]->vtype];
|
||||||
else
|
else
|
||||||
assignop = type_store_instr[exprs[0]->vtype];
|
assignop = type_store_instr[exprs[0]->vtype];
|
||||||
|
if (exprs[0]->vtype == TYPE_FLOAT)
|
||||||
out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
|
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)
|
if (!out)
|
||||||
return false;
|
return false;
|
||||||
if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
|
if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
|
||||||
compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
|
compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
|
||||||
}
|
}
|
||||||
|
if (exprs[0]->vtype == TYPE_FLOAT)
|
||||||
asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
|
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;
|
asbinstore->keep_dest = true;
|
||||||
out = (ast_expression*)asbinstore;
|
out = (ast_expression*)asbinstore;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case opid2('~', 'P'):
|
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));
|
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
|
||||||
compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
|
compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!(out = fold_op(parser->fold, op, exprs)))
|
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]);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
#undef NotSameType
|
#undef NotSameType
|
||||||
|
|
|
@ -3,12 +3,18 @@ void main() {
|
||||||
float b; b = 1;
|
float b; b = 1;
|
||||||
float c; c = 1;
|
float c; c = 1;
|
||||||
float d; d = 1;
|
float d; d = 1;
|
||||||
|
vector e; e = '1 1 1';
|
||||||
|
vector f; f = '1 1 1';
|
||||||
|
|
||||||
a &~= 1; // 0
|
a &~= 1; // 0
|
||||||
b &= ~1; // 0
|
b &= ~1; // 0
|
||||||
c &= ~d; // 0
|
c &= ~d; // 0
|
||||||
|
f &~= e; // '0 0 0'
|
||||||
|
e &= ~e; // '0 0 0'
|
||||||
|
|
||||||
print("a: ", ftos(a), "\nb: ",
|
print("a: ", ftos(a), "\nb: ",
|
||||||
ftos(b), "\nc: ",
|
ftos(b), "\nc: ",
|
||||||
ftos(c), "\n");
|
ftos(c), "\n");
|
||||||
|
print("e: ", vtos(e), "\n");
|
||||||
|
print("f: ", vtos(f), "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,3 +7,5 @@ E: $null
|
||||||
M: a: 0
|
M: a: 0
|
||||||
M: b: 0
|
M: b: 0
|
||||||
M: c: 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), "\n");
|
||||||
print(vtos(v / 2), "\n");
|
print(vtos(v / 2), "\n");
|
||||||
print(vtos(v), "\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: '4 8 16'
|
||||||
M: '2 4 8'
|
M: '2 4 8'
|
||||||
M: '4 8 16'
|
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 ^ vector
|
||||||
// vector ^ float
|
// vector ^ float
|
||||||
// are legal in constant expressions (currently)
|
// 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 v3 = '5 2 5' ^ '3 10 3';
|
||||||
const vector v4 = '5 2 5' ^ 10;
|
const vector v4 = '5 2 5' ^ 10;
|
||||||
|
|
||||||
|
@ -49,4 +55,12 @@ void main() {
|
||||||
float swap_y = 200;
|
float swap_y = 200;
|
||||||
vector swaps = swap(swap_x, swap_y);
|
vector swaps = swap(swap_x, swap_y);
|
||||||
print("100:200 swapped is: ", ftos(swaps.x), ":", ftos(swaps.y), "\n");
|
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: inverse
|
||||||
M: vv: '6 8 6'
|
M: vv: '6 8 6'
|
||||||
M: vf: '15 8 15'
|
M: vf: '15 8 15'
|
||||||
|
M: vv: '6 8 6'
|
||||||
|
M: vf: '15 8 15'
|
||||||
M: 100:200 swapped is: 200:100
|
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