Merge branch 'divVerent/submit/vector-bitops' of git://git.xonotic.org/xonotic/gmqcc into cooking

This commit is contained in:
Dale Weiler 2013-08-26 12:36:17 -04:00
commit 454234ef5f
10 changed files with 338 additions and 103 deletions

80
fold.c
View file

@ -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
View file

@ -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
View file

@ -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
View file

@ -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

View file

@ -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");
}

View file

@ -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'

View file

@ -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");
}

View file

@ -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'

View file

@ -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");
}

View file

@ -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'