/*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014, 2015
* Wolfgang Bumiller
* Dale Weiler
*
va_end(ap);
}
-static bool irwarning(lex_ctx_t ctx, int warntype, const char *fmt, ...)
+static bool GMQCC_WARN irwarning(lex_ctx_t ctx, int warntype, const char *fmt, ...)
{
bool r;
va_list ap;
ir_builder* ir_builder_new(const char *modulename)
{
ir_builder* self;
+ size_t i;
self = (ir_builder*)mem_a(sizeof(*self));
if (!self)
self->nil = ir_value_var("nil", store_value, TYPE_NIL);
self->nil->cvq = CV_CONST;
+ for (i = 0; i != IR_MAX_VINSTR_TEMPS; ++i) {
+ /* we write to them, but they're not supposed to be used outside the IR, so
+ * let's not allow the generation of ir_instrs which use these.
+ * So it's a constant noexpr.
+ */
+ self->vinstr_temp[i] = ir_value_var("vinstr_temp", store_value, TYPE_NOEXPR);
+ self->vinstr_temp[i]->cvq = CV_CONST;
+ }
+
self->reserved_va_count = NULL;
+ self->coverage_func = NULL;
+
self->code = code_init();
return self;
ir_value_delete(self->fields[i]);
}
ir_value_delete(self->nil);
+ for (i = 0; i != IR_MAX_VINSTR_TEMPS; ++i) {
+ ir_value_delete(self->vinstr_temp[i]);
+ }
vec_free(self->fields);
vec_free(self->filenames);
vec_free(self->filestrings);
ir_block* bn = ir_block_new(self, label);
bn->context = ctx;
vec_push(self->blocks, bn);
+
+ if ((self->flags & IR_FLAG_BLOCK_COVERAGE) && self->owner->coverage_func)
+ (void)ir_block_create_call(bn, ctx, NULL, self->owner->coverage_func, false);
+
return bn;
}
(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_NEG_V) );
}
static bool ir_function_pass_peephole(ir_function *self)
if (!instr_is_operation(oper->opcode))
continue;
+ /* Don't change semantics of MUL_VF in engines where these may not alias. */
if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) {
if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1])
continue;
static bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing)
{
+ if (v && v->vtype == TYPE_NOEXPR) {
+ irerror(self->context, "tried to use a NOEXPR value");
+ return false;
+ }
+
if (self->_ops[op]) {
size_t idx;
if (writing && vec_ir_instr_find(self->_ops[op]->writes, self, &idx))
return self;
}
+/* helper function */
+static ir_value* ir_builder_imm_float(ir_builder *self, float value, bool add_to_list) {
+ ir_value *v = ir_value_var("#IMMEDIATE", store_global, TYPE_FLOAT);
+ v->flags |= IR_FLAG_ERASABLE;
+ v->hasvalue = true;
+ v->cvq = CV_CONST;
+ v->constval.vfloat = value;
+
+ vec_push(self->globals, v);
+ if (add_to_list)
+ vec_push(self->const_floats, v);
+ return v;
+}
+
ir_value* ir_value_vector_member(ir_value *self, unsigned int member)
{
char *name;
if (self->vtype == TYPE_STRING)
mem_d((void*)self->constval.vstring);
}
- for (i = 0; i < 3; ++i) {
- if (self->members[i])
- ir_value_delete(self->members[i]);
+ if (!(self->flags & IR_FLAG_SPLIT_VECTOR)) {
+ for (i = 0; i < 3; ++i) {
+ if (self->members[i])
+ ir_value_delete(self->members[i]);
+ }
}
vec_free(self->reads);
vec_free(self->writes);
return true;
}
+bool ir_block_create_state_op(ir_block *self, lex_ctx_t ctx, ir_value *frame, ir_value *think)
+{
+ ir_instr *in;
+ if (!ir_check_unreachable(self))
+ return false;
+
+ in = ir_instr_new(ctx, self, INSTR_STATE);
+ if (!in)
+ return false;
+
+ if (!ir_instr_op(in, 0, frame, false) ||
+ !ir_instr_op(in, 1, think, false))
+ {
+ ir_instr_delete(in);
+ return false;
+ }
+ vec_push(self->instr, in);
+ return true;
+}
+
static bool ir_block_create_store(ir_block *self, lex_ctx_t ctx, ir_value *target, ir_value *what)
{
int op = 0;
ir_instr *in;
if (!ir_check_unreachable(self))
return false;
+
self->final = true;
+
self->is_return = true;
in = ir_instr_new(ctx, self, INSTR_RETURN);
if (!in)
#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:
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:
+ case VINSTR_CROSS:
#if 0
case INSTR_DIV_VF:
case INSTR_MUL_IV:
ir_value *operand)
{
int ot = TYPE_FLOAT;
+ ir_value *minus_1 = NULL;
+ if (opcode == VINSTR_NEG_F || opcode == VINSTR_NEG_V)
+ minus_1 = ir_builder_imm_float(self->owner->owner, -1.0f, false);
switch (opcode) {
case INSTR_NOT_F:
case INSTR_NOT_V:
case INSTR_NOT_S:
case INSTR_NOT_ENT:
- case INSTR_NOT_FNC:
-#if 0
- case INSTR_NOT_I:
-#endif
+ case INSTR_NOT_FNC: /*
+ case INSTR_NOT_I: */
ot = TYPE_FLOAT;
break;
- /* QC doesn't have other unary operations. We expect extensions to fill
- * the above list, otherwise we assume out-type = in-type, eg for an
- * unary minus
- */
+ /* Negation is implemented as -1 * <operand> */
+ case VINSTR_NEG_F:
+ return ir_block_create_general_instr(self, ctx, label, INSTR_MUL_F, minus_1, operand, TYPE_FLOAT);
+ case VINSTR_NEG_V:
+ return ir_block_create_general_instr(self, ctx, label, INSTR_MUL_FV, minus_1, operand, TYPE_VECTOR);
+
default:
ot = operand->vtype;
break;
}
}
- 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_BITAND_VF ||
+ instr->opcode == VINSTR_BITOR_VF ||
+ instr->opcode == VINSTR_BITXOR ||
+ instr->opcode == VINSTR_BITXOR_VF ||
+ instr->opcode == VINSTR_BITXOR_V ||
+ instr->opcode == VINSTR_CROSS)
{
value = instr->_ops[2];
/* the float source will get an additional lifetime */
if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1))
*changed = true;
}
- else if (instr->opcode == INSTR_MUL_FV || instr->opcode == INSTR_LOAD_V)
+
+ if (instr->opcode == INSTR_MUL_FV ||
+ instr->opcode == INSTR_LOAD_V ||
+ instr->opcode == VINSTR_BITXOR ||
+ instr->opcode == VINSTR_BITXOR_VF ||
+ instr->opcode == VINSTR_BITXOR_V ||
+ instr->opcode == VINSTR_CROSS)
{
value = instr->_ops[1];
/* the float source will get an additional lifetime */
ir_block *onfalse;
size_t stidx;
size_t i;
+ int j;
block->generated = true;
block->code_start = vec_size(code->statements);
stmt.o2.s1 = 0;
stmt.o3.s1 = 0;
if (stmt.o1.s1 != 1)
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
/* no further instructions can be in this block */
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);
+ 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);
+ 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);
+
+ /* 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);
+ ++stmt.o1.s1;
+ ++stmt.o2.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+ ++stmt.o1.s1;
+ ++stmt.o2.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+
+ /* 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);
+ ++stmt.o1.s1;
+ ++stmt.o2.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+ ++stmt.o1.s1;
+ ++stmt.o2.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+
+ /* 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);
+ 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);
+ }
+ 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);
+
+ /* 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);
+ ++stmt.o1.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+ ++stmt.o1.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+
+ /* 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);
+ ++stmt.o1.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+ ++stmt.o1.s1;
+ ++stmt.o3.s1;
+ code_push_statement(code, &stmt, instr->context);
+
+ /* 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);
+ 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);
+ }
+ 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);
+
+ /* instruction generated */
+ continue;
+ }
+
+ if (instr->opcode == VINSTR_CROSS) {
+ stmt.opcode = INSTR_MUL_F;
+ for (j = 0; j < 3; ++j) {
+ stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + (j + 1) % 3;
+ stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + (j + 2) % 3;
+ stmt.o3.s1 = ir_value_code_addr(instr->_ops[0]) + j;
+ code_push_statement(code, &stmt, instr->context);
+ stmt.o1.s1 = ir_value_code_addr(instr->_ops[1]) + (j + 2) % 3;
+ stmt.o2.s1 = ir_value_code_addr(instr->_ops[2]) + (j + 1) % 3;
+ stmt.o3.s1 = ir_value_code_addr(func->owner->vinstr_temp[0]) + j;
+ code_push_statement(code, &stmt, instr->context);
+ }
+ 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);
+
+ /* instruction generated */
+ continue;
+ }
+
if (instr->opcode == VINSTR_COND) {
ontrue = instr->bops[0];
onfalse = instr->bops[1];
stmt.opcode = INSTR_IF;
stmt.o2.s1 = (ontrue->code_start) - vec_size(code->statements);
if (stmt.o2.s1 != 1)
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
}
if (onfalse->generated) {
stmt.opcode = INSTR_IFNOT;
stmt.o2.s1 = (onfalse->code_start) - vec_size(code->statements);
if (stmt.o2.s1 != 1)
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
}
if (!ontrue->generated) {
if (onfalse->generated)
ontrue = tmp;
}
stidx = vec_size(code->statements);
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
/* on false we jump, so add ontrue-path */
if (!gen_blocks_recursive(code, func, ontrue))
return false;
stmt.o2.s1 = 0;
stmt.o3.s1 = 0;
if (stmt.o1.s1 != 1)
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
return true;
}
else if (stidx+2 == vec_size(code->statements) && code->statements[stidx].o2.s1 == 1) {
stmt.opcode = type_store_instr[param->vtype];
stmt.o1.u1 = ir_value_code_addr(param);
stmt.o2.u1 = OFS_PARM0 + 3 * p;
- code_push_statement(code, &stmt, instr->context.line);
+
+ if (param->vtype == TYPE_VECTOR && (param->flags & IR_FLAG_SPLIT_VECTOR)) {
+ /* fetch 3 separate floats */
+ stmt.opcode = INSTR_STORE_F;
+ stmt.o1.u1 = ir_value_code_addr(param->members[0]);
+ code_push_statement(code, &stmt, instr->context);
+ stmt.o2.u1++;
+ stmt.o1.u1 = ir_value_code_addr(param->members[1]);
+ code_push_statement(code, &stmt, instr->context);
+ stmt.o2.u1++;
+ stmt.o1.u1 = ir_value_code_addr(param->members[2]);
+ code_push_statement(code, &stmt, instr->context);
+ }
+ else
+ code_push_statement(code, &stmt, instr->context);
}
/* Now handle extparams */
first = vec_size(instr->params);
stmt.opcode = type_store_instr[param->vtype];
stmt.o1.u1 = ir_value_code_addr(param);
stmt.o2.u1 = ir_value_code_addr(targetparam);
- code_push_statement(code, &stmt, instr->context.line);
+ if (param->vtype == TYPE_VECTOR && (param->flags & IR_FLAG_SPLIT_VECTOR)) {
+ /* fetch 3 separate floats */
+ stmt.opcode = INSTR_STORE_F;
+ stmt.o1.u1 = ir_value_code_addr(param->members[0]);
+ code_push_statement(code, &stmt, instr->context);
+ stmt.o2.u1++;
+ stmt.o1.u1 = ir_value_code_addr(param->members[1]);
+ code_push_statement(code, &stmt, instr->context);
+ stmt.o2.u1++;
+ stmt.o1.u1 = ir_value_code_addr(param->members[2]);
+ code_push_statement(code, &stmt, instr->context);
+ }
+ else
+ code_push_statement(code, &stmt, instr->context);
}
stmt.opcode = INSTR_CALL0 + vec_size(instr->params);
stmt.o1.u1 = ir_value_code_addr(instr->_ops[1]);
stmt.o2.u1 = 0;
stmt.o3.u1 = 0;
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
retvalue = instr->_ops[0];
if (retvalue && retvalue->store != store_return &&
stmt.o1.u1 = OFS_RETURN;
stmt.o2.u1 = ir_value_code_addr(retvalue);
stmt.o3.u1 = 0;
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
}
continue;
}
if (instr->opcode == INSTR_STATE) {
- irerror(block->context, "TODO: state instruction");
- return false;
+ stmt.opcode = instr->opcode;
+ if (instr->_ops[0])
+ stmt.o1.u1 = ir_value_code_addr(instr->_ops[0]);
+ if (instr->_ops[1])
+ stmt.o2.u1 = ir_value_code_addr(instr->_ops[1]);
+ stmt.o3.u1 = 0;
+ code_push_statement(code, &stmt, instr->context);
+ continue;
}
stmt.opcode = instr->opcode;
continue;
}
}
-
- code_push_statement(code, &stmt, instr->context.line);
+ code_push_statement(code, &stmt, instr->context);
}
return true;
}
retst->opcode = INSTR_DONE;
++opts_optimizationcount[OPTIM_VOID_RETURN];
} else {
+ lex_ctx_t last;
+
stmt.opcode = INSTR_DONE;
- stmt.o1.u1 = 0;
- stmt.o2.u1 = 0;
- stmt.o3.u1 = 0;
- code_push_statement(code, &stmt, vec_last(code->linenums));
+ stmt.o1.u1 = 0;
+ stmt.o2.u1 = 0;
+ stmt.o3.u1 = 0;
+ last.line = vec_last(code->linenums);
+ last.column = vec_last(code->columnnums);
+
+ code_push_statement(code, &stmt, last);
}
return true;
}
}
stmt.o1.u1 = ir_value_code_addr(ep);
stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
- code_push_statement(code, &stmt, self->context.line);
+ code_push_statement(code, &stmt, self->context);
}
return true;
if (i < 8) {
stmt.o1.u1 = OFS_PARM0 + 3*i;
stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
- code_push_statement(code, &stmt, self->context.line);
+ code_push_statement(code, &stmt, self->context);
continue;
}
ext = i - 8;
stmt.o1.u1 = ir_value_code_addr(ep);
stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
- code_push_statement(code, &stmt, self->context.line);
+ code_push_statement(code, &stmt, self->context);
}
return true;
irfun = global->constval.vfunc;
if (!irfun) {
if (global->cvq == CV_NONE) {
- irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
- "function `%s` has no body and in QC implicitly becomes a function-pointer", global->name);
+ if (irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER,
+ "function `%s` has no body and in QC implicitly becomes a function-pointer",
+ global->name))
+ {
+ /* Not bailing out just now. If this happens a lot you don't want to have
+ * to rerun gmqcc for each such function.
+ */
+
+ /* return false; */
+ }
}
/* this was a function pointer, don't generate code for those */
return true;
if (irfun->builtin)
return true;
+ /*
+ * If there is no definition and the thing is eraseable, we can ignore
+ * outputting the function to begin with.
+ */
+ if (global->flags & IR_FLAG_ERASABLE && irfun->code_function_def < 0) {
+ return true;
+ }
+
if (irfun->code_function_def < 0) {
irerror(irfun->context, "`%s`: IR global wasn't generated, failed to access function-def", irfun->name);
return false;
prog_section_def_t def;
bool pushdef = opts.optimizeoff;
+ /* we don't generate split-vectors */
+ if (global->vtype == TYPE_VECTOR && (global->flags & IR_FLAG_SPLIT_VECTOR))
+ return true;
+
def.type = global->vtype;
def.offset = vec_size(self->code->globals);
def.name = 0;
{
pushdef = true;
+ /*
+ * if we're eraseable and the function isn't referenced ignore outputting
+ * the function.
+ */
+ if (global->flags & IR_FLAG_ERASABLE && vec_size(global->reads) == 0) {
+ return true;
+ }
+
if (OPTS_OPTIMIZATION(OPTIM_STRIP_CONSTANT_NAMES) &&
!(global->flags & IR_FLAG_INCLUDE_DEF) &&
(global->name[0] == '#' || global->cvq == CV_CONST))
/* TODO: same as above but for entity-fields rather than globsl
*/
}
- else
- irwarning(global->context, WARN_VOID_VARIABLES, "unrecognized variable of type void `%s`",
- global->name);
+ else if(irwarning(global->context, WARN_VOID_VARIABLES, "unrecognized variable of type void `%s`",
+ global->name))
+ {
+ /* Not bailing out */
+ /* return false; */
+ }
/* I'd argue setting it to 0 is sufficient, but maybe some depend on knowing how far
* the system fields actually go? Though the engine knows this anyway...
* Maybe this could be an -foption
{
ir_value_code_setaddr(global, vec_size(self->code->globals));
if (global->hasvalue) {
+ if (global->cvq == CV_CONST && !vec_size(global->reads))
+ return true;
iptr = (int32_t*)&global->constval.ivec[0];
vec_push(self->code->globals, *iptr);
} else {
{
ir_value_code_setaddr(global, vec_size(self->code->globals));
if (global->hasvalue) {
- uint32_t load = code_genstring(self->code, global->constval.vstring);
+ uint32_t load;
+ if (global->cvq == CV_CONST && !vec_size(global->reads))
+ return true;
+ load = code_genstring(self->code, global->constval.vstring);
vec_push(self->code->globals, load);
} else {
vec_push(self->code->globals, 0);
return field->code.globaladdr >= 0;
}
+static void ir_builder_collect_reusables(ir_builder *builder) {
+ size_t i;
+ ir_value **reusables = NULL;
+ for (i = 0; i < vec_size(builder->globals); ++i) {
+ ir_value *value = builder->globals[i];
+ if (value->vtype != TYPE_FLOAT || !value->hasvalue)
+ continue;
+ if (value->cvq == CV_CONST || (value->name && value->name[0] == '#')) {
+ vec_push(reusables, value);
+ }
+ }
+ builder->const_floats = reusables;
+}
+
+static void ir_builder_split_vector(ir_builder *self, ir_value *vec) {
+ size_t i, count;
+ ir_value* found[3] = { NULL, NULL, NULL };
+
+ /* must not be written to */
+ if (vec_size(vec->writes))
+ return;
+ /* must not be trying to access individual members */
+ if (vec->members[0] || vec->members[1] || vec->members[2])
+ return;
+ /* should be actually used otherwise it won't be generated anyway */
+ count = vec_size(vec->reads);
+ if (!count)
+ return;
+
+ /* may only be used directly as function parameters, so if we find some other instruction cancel */
+ for (i = 0; i != count; ++i) {
+ /* we only split vectors if they're used directly as parameter to a call only! */
+ ir_instr *user = vec->reads[i];
+ if ((user->opcode < INSTR_CALL0 || user->opcode > INSTR_CALL8) && user->opcode != VINSTR_NRCALL)
+ return;
+ }
+
+ vec->flags |= IR_FLAG_SPLIT_VECTOR;
+
+ /* find existing floats making up the split */
+ count = vec_size(self->const_floats);
+ for (i = 0; i != count; ++i) {
+ ir_value *c = self->const_floats[i];
+ if (!found[0] && c->constval.vfloat == vec->constval.vvec.x)
+ found[0] = c;
+ if (!found[1] && c->constval.vfloat == vec->constval.vvec.y)
+ found[1] = c;
+ if (!found[2] && c->constval.vfloat == vec->constval.vvec.z)
+ found[2] = c;
+ if (found[0] && found[1] && found[2])
+ break;
+ }
+
+ /* generate floats for not yet found components */
+ if (!found[0])
+ found[0] = ir_builder_imm_float(self, vec->constval.vvec.x, true);
+ if (!found[1]) {
+ if (vec->constval.vvec.y == vec->constval.vvec.x)
+ found[1] = found[0];
+ else
+ found[1] = ir_builder_imm_float(self, vec->constval.vvec.y, true);
+ }
+ if (!found[2]) {
+ if (vec->constval.vvec.z == vec->constval.vvec.x)
+ found[2] = found[0];
+ else if (vec->constval.vvec.z == vec->constval.vvec.y)
+ found[2] = found[1];
+ else
+ found[2] = ir_builder_imm_float(self, vec->constval.vvec.z, true);
+ }
+
+ /* the .members array should be safe to use here. */
+ vec->members[0] = found[0];
+ vec->members[1] = found[1];
+ vec->members[2] = found[2];
+
+ /* register the readers for these floats */
+ count = vec_size(vec->reads);
+ for (i = 0; i != count; ++i) {
+ vec_push(found[0]->reads, vec->reads[i]);
+ vec_push(found[1]->reads, vec->reads[i]);
+ vec_push(found[2]->reads, vec->reads[i]);
+ }
+}
+
+static void ir_builder_split_vectors(ir_builder *self) {
+ size_t i, count = vec_size(self->globals);
+ for (i = 0; i != count; ++i) {
+ ir_value *v = self->globals[i];
+ if (v->vtype != TYPE_VECTOR || !v->name || v->name[0] != '#')
+ continue;
+ ir_builder_split_vector(self, self->globals[i]);
+ }
+}
+
bool ir_builder_generate(ir_builder *self, const char *filename)
{
prog_section_statement_t stmt;
size_t i;
char *lnofile = NULL;
+ if (OPTS_FLAG(SPLIT_VECTOR_PARAMETERS)) {
+ ir_builder_collect_reusables(self);
+ if (vec_size(self->const_floats) > 0)
+ ir_builder_split_vectors(self);
+ }
+
for (i = 0; i < vec_size(self->fields); ++i)
{
ir_builder_prepare_field(self->code, self->fields[i]);
vec_push(self->code->globals, 0);
vec_push(self->code->globals, 0);
+ /* generate virtual-instruction temps */
+ for (i = 0; i < IR_MAX_VINSTR_TEMPS; ++i) {
+ ir_value_code_setaddr(self->vinstr_temp[i], vec_size(self->code->globals));
+ vec_push(self->code->globals, 0);
+ vec_push(self->code->globals, 0);
+ vec_push(self->code->globals, 0);
+ }
+
/* generate global temps */
self->first_common_globaltemp = vec_size(self->code->globals);
for (i = 0; i < self->max_globaltemps; ++i) {
}
if (vec_size(self->code->globals) >= 65536) {
- irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle. Bailing out.");
+ irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle (%u). Bailing out.", (unsigned int)vec_size(self->code->globals));
return false;
}
/* DP errors if the last instruction is not an INSTR_DONE. */
if (vec_last(self->code->statements).opcode != INSTR_DONE)
{
+ lex_ctx_t last;
+
stmt.opcode = INSTR_DONE;
- stmt.o1.u1 = 0;
- stmt.o2.u1 = 0;
- stmt.o3.u1 = 0;
- code_push_statement(self->code, &stmt, vec_last(self->code->linenums));
+ stmt.o1.u1 = 0;
+ stmt.o2.u1 = 0;
+ stmt.o3.u1 = 0;
+ last.line = vec_last(self->code->linenums);
+ last.column = vec_last(self->code->columnnums);
+
+ code_push_statement(self->code, &stmt, last);
}
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY))
#define IND_BUFSZ 1024
-#ifdef _MSC_VER
-# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE)
-#endif
-
static const char *qc_opname(int op)
{
if (op < 0) return "<INVALID>";
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";
+ case VINSTR_CROSS: return "CROSS";
+ case VINSTR_NEG_F: return "NEG_F";
+ case VINSTR_NEG_V: return "NEG_V";
+ default: return "<UNK>";
}
}
return;
}
oprintf("%sfunction %s\n", ind, f->name);
- strncat(ind, "\t", IND_BUFSZ-1);
+ util_strncat(ind, "\t", IND_BUFSZ-1);
if (vec_size(f->locals))
{
oprintf("%s%i locals:\n", ind, (int)vec_size(f->locals));
{
size_t i;
oprintf("%s:%s\n", ind, b->label);
- strncat(ind, "\t", IND_BUFSZ-1);
+ util_strncat(ind, "\t", IND_BUFSZ-1);
if (b->instr && b->instr[0])
oprintf("%s (%i) [entry]\n", ind, (int)(b->instr[0]->eid-1));
return;
}
- strncat(ind, "\t", IND_BUFSZ-1);
+ util_strncat(ind, "\t", IND_BUFSZ-1);
if (in->_ops[0] && (in->_ops[1] || in->_ops[2])) {
ir_value_dump(in->_ops[0], oprintf);