extern "C" {
#include "main/mtypes.h"
#include "shader/prog_instruction.h"
+#include "shader/prog_optimize.h"
#include "shader/prog_print.h"
#include "shader/program.h"
#include "shader/prog_uniform.h"
int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */
GLuint swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */
int negate; /**< NEGATE_XYZW mask from mesa */
- bool reladdr; /**< Register index should be offset by address reg. */
+ /** Register index should be offset by the integer in this reg. */
+ ir_to_mesa_src_reg *reladdr;
} ir_to_mesa_src_reg;
typedef struct ir_to_mesa_dst_reg {
int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */
int writemask; /**< Bitfield of WRITEMASK_[XYZW] */
GLuint cond_mask:4;
+ /** Register index should be offset by the integer in this reg. */
+ ir_to_mesa_src_reg *reladdr;
} ir_to_mesa_dst_reg;
extern ir_to_mesa_src_reg ir_to_mesa_undef;
GLboolean cond_update;
int sampler; /**< sampler index */
int tex_target; /**< One of TEXTURE_*_INDEX */
+ GLboolean tex_shadow;
+
+ class function_entry *function; /* Set on OPCODE_CAL or OPCODE_BGNSUB */
};
-class temp_entry : public exec_node {
+class variable_storage : public exec_node {
public:
- temp_entry(ir_variable *var, int file, int index)
+ variable_storage(ir_variable *var, int file, int index)
: file(file), index(index), var(var)
{
/* empty */
ir_variable *var; /* variable that maps to this, if any */
};
+class function_entry : public exec_node {
+public:
+ ir_function_signature *sig;
+
+ /**
+ * identifier of this function signature used by the program.
+ *
+ * At the point that Mesa instructions for function calls are
+ * generated, we don't know the address of the first instruction of
+ * the function body. So we make the BranchTarget that is called a
+ * small integer and rewrite them during set_branchtargets().
+ */
+ int sig_id;
+
+ /**
+ * Pointer to first instruction of the function body.
+ *
+ * Set during function body emits after main() is processed.
+ */
+ ir_to_mesa_instruction *bgn_inst;
+
+ /**
+ * Index of the first instruction of the function body in actual
+ * Mesa IR.
+ *
+ * Set after convertion from ir_to_mesa_instruction to prog_instruction.
+ */
+ int inst;
+
+ /** Storage for the return value. */
+ ir_to_mesa_src_reg return_reg;
+};
+
class ir_to_mesa_visitor : public ir_visitor {
public:
ir_to_mesa_visitor();
+ function_entry *current_function;
+
GLcontext *ctx;
struct gl_program *prog;
int next_temp;
- temp_entry *find_variable_storage(ir_variable *var);
+ variable_storage *find_variable_storage(ir_variable *var);
+
+ function_entry *get_function_signature(ir_function_signature *sig);
ir_to_mesa_src_reg get_temp(const glsl_type *type);
+ void reladdr_to_temp(ir_instruction *ir,
+ ir_to_mesa_src_reg *reg, int *num_reladdr);
struct ir_to_mesa_src_reg src_reg_for_float(float val);
struct ir_to_mesa_src_reg result;
- /** List of temp_entry */
- exec_list variable_storage;
+ /** List of variable_storage */
+ exec_list variables;
+
+ /** List of function_entry */
+ exec_list function_signatures;
+ int next_signature_id;
/** List of ir_to_mesa_instruction */
exec_list instructions;
+ ir_to_mesa_instruction *ir_to_mesa_emit_op0(ir_instruction *ir,
+ enum prog_opcode op);
+
ir_to_mesa_instruction *ir_to_mesa_emit_op1(ir_instruction *ir,
enum prog_opcode op,
ir_to_mesa_dst_reg dst,
ir_to_mesa_src_reg src0,
ir_to_mesa_src_reg src1);
+ GLboolean try_emit_mad(ir_expression *ir,
+ int mul_operand);
+
int *sampler_map;
int sampler_map_size;
};
ir_to_mesa_src_reg ir_to_mesa_undef = {
- PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, NEGATE_NONE, false,
+ PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, NEGATE_NONE, NULL,
};
ir_to_mesa_dst_reg ir_to_mesa_undef_dst = {
- PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP
+ PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, COND_TR, NULL,
};
ir_to_mesa_dst_reg ir_to_mesa_address_reg = {
- PROGRAM_ADDRESS, 0, WRITEMASK_X
+ PROGRAM_ADDRESS, 0, WRITEMASK_X, COND_TR, NULL
};
static int swizzle_for_size(int size)
return size_swizzles[size - 1];
}
-/* This list should match up with builtin_variables.h */
-static const struct {
- const char *name;
- int file;
- int index;
-} builtin_var_to_mesa_reg[] = {
- /* core_vs */
- {"gl_Position", PROGRAM_OUTPUT, VERT_RESULT_HPOS},
- {"gl_PointSize", PROGRAM_OUTPUT, VERT_RESULT_PSIZ},
-
- /* core_fs */
- {"gl_FragCoord", PROGRAM_INPUT, FRAG_ATTRIB_WPOS},
- {"gl_FrontFacing", PROGRAM_INPUT, FRAG_ATTRIB_FACE},
- {"gl_FragColor", PROGRAM_OUTPUT, FRAG_ATTRIB_COL0},
- {"gl_FragDepth", PROGRAM_OUTPUT, FRAG_RESULT_DEPTH},
-
- /* 110_deprecated_fs */
- {"gl_Color", PROGRAM_INPUT, FRAG_ATTRIB_COL0},
- {"gl_SecondaryColor", PROGRAM_INPUT, FRAG_ATTRIB_COL1},
- {"gl_FogFragCoord", PROGRAM_INPUT, FRAG_ATTRIB_FOGC},
- {"gl_TexCoord", PROGRAM_INPUT, FRAG_ATTRIB_TEX0}, /* array */
-
- /* 110_deprecated_vs */
- {"gl_Vertex", PROGRAM_INPUT, VERT_ATTRIB_POS},
- {"gl_Normal", PROGRAM_INPUT, VERT_ATTRIB_NORMAL},
- {"gl_Color", PROGRAM_INPUT, VERT_ATTRIB_COLOR0},
- {"gl_SecondaryColor", PROGRAM_INPUT, VERT_ATTRIB_COLOR1},
- {"gl_MultiTexCoord0", PROGRAM_INPUT, VERT_ATTRIB_TEX0},
- {"gl_MultiTexCoord1", PROGRAM_INPUT, VERT_ATTRIB_TEX1},
- {"gl_MultiTexCoord2", PROGRAM_INPUT, VERT_ATTRIB_TEX2},
- {"gl_MultiTexCoord3", PROGRAM_INPUT, VERT_ATTRIB_TEX3},
- {"gl_MultiTexCoord4", PROGRAM_INPUT, VERT_ATTRIB_TEX4},
- {"gl_MultiTexCoord5", PROGRAM_INPUT, VERT_ATTRIB_TEX5},
- {"gl_MultiTexCoord6", PROGRAM_INPUT, VERT_ATTRIB_TEX6},
- {"gl_MultiTexCoord7", PROGRAM_INPUT, VERT_ATTRIB_TEX7},
- {"gl_TexCoord", PROGRAM_OUTPUT, VERT_RESULT_TEX0}, /* array */
- {"gl_FogCoord", PROGRAM_INPUT, VERT_RESULT_FOGC},
- /*{"gl_ClipVertex", PROGRAM_OUTPUT, VERT_ATTRIB_FOGC},*/ /* FINISHME */
- {"gl_FrontColor", PROGRAM_OUTPUT, VERT_RESULT_COL0},
- {"gl_BackColor", PROGRAM_OUTPUT, VERT_RESULT_BFC0},
- {"gl_FrontSecondaryColor", PROGRAM_OUTPUT, VERT_RESULT_COL1},
- {"gl_BackSecondaryColor", PROGRAM_OUTPUT, VERT_RESULT_BFC1},
- {"gl_FogFragCoord", PROGRAM_OUTPUT, VERT_RESULT_FOGC},
-
- /* 130_vs */
- /*{"gl_VertexID", PROGRAM_INPUT, VERT_ATTRIB_FOGC},*/ /* FINISHME */
-
- {"gl_FragData", PROGRAM_OUTPUT, FRAG_RESULT_DATA0}, /* array */
-};
-
ir_to_mesa_instruction *
ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir,
enum prog_opcode op,
ir_to_mesa_src_reg src2)
{
ir_to_mesa_instruction *inst = new(mem_ctx) ir_to_mesa_instruction();
+ int num_reladdr = 0;
+
+ /* If we have to do relative addressing, we want to load the ARL
+ * reg directly for one of the regs, and preload the other reladdr
+ * sources into temps.
+ */
+ num_reladdr += dst.reladdr != NULL;
+ num_reladdr += src0.reladdr != NULL;
+ num_reladdr += src1.reladdr != NULL;
+ num_reladdr += src2.reladdr != NULL;
+
+ reladdr_to_temp(ir, &src2, &num_reladdr);
+ reladdr_to_temp(ir, &src1, &num_reladdr);
+ reladdr_to_temp(ir, &src0, &num_reladdr);
+
+ if (dst.reladdr) {
+ ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg,
+ *dst.reladdr);
+
+ num_reladdr--;
+ }
+ assert(num_reladdr == 0);
inst->op = op;
inst->dst_reg = dst;
inst->src_reg[2] = src2;
inst->ir = ir;
+ inst->function = NULL;
+
this->instructions.push_tail(inst);
return inst;
src0, ir_to_mesa_undef, ir_to_mesa_undef);
}
+ir_to_mesa_instruction *
+ir_to_mesa_visitor::ir_to_mesa_emit_op0(ir_instruction *ir,
+ enum prog_opcode op)
+{
+ return ir_to_mesa_emit_op3(ir, op, ir_to_mesa_undef_dst,
+ ir_to_mesa_undef,
+ ir_to_mesa_undef,
+ ir_to_mesa_undef);
+}
+
void
ir_to_mesa_visitor::map_sampler(int location, int sampler)
{
dst_reg.index = reg.index;
dst_reg.writemask = WRITEMASK_XYZW;
dst_reg.cond_mask = COND_TR;
+ dst_reg.reladdr = reg.reladdr;
return dst_reg;
}
+inline ir_to_mesa_src_reg
+ir_to_mesa_src_reg_from_dst(ir_to_mesa_dst_reg reg)
+{
+ ir_to_mesa_src_reg src_reg;
+
+ src_reg.file = reg.file;
+ src_reg.index = reg.index;
+ src_reg.swizzle = SWIZZLE_XYZW;
+ src_reg.negate = 0;
+ src_reg.reladdr = reg.reladdr;
+
+ return src_reg;
+}
+
/**
* Emits Mesa scalar opcodes to produce unique answers across channels.
*
src_reg.file = PROGRAM_CONSTANT;
src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters,
&val, 1, &src_reg.swizzle);
+ src_reg.reladdr = NULL;
+ src_reg.negate = 0;
return src_reg;
}
case GLSL_TYPE_FLOAT:
case GLSL_TYPE_BOOL:
if (type->is_matrix()) {
- return 4; /* FINISHME: Not all matrices are 4x4. */
+ return type->matrix_columns;
} else {
/* Regardless of size of vector, it gets a vec4. This is bad
* packing for things like floats, but otherwise arrays become a
src_reg.file = PROGRAM_TEMPORARY;
src_reg.index = next_temp;
- src_reg.reladdr = false;
+ src_reg.reladdr = NULL;
next_temp += type_size(type);
for (i = 0; i < type->vector_elements; i++)
return src_reg;
}
-temp_entry *
+variable_storage *
ir_to_mesa_visitor::find_variable_storage(ir_variable *var)
{
- temp_entry *entry;
+ variable_storage *entry;
- foreach_iter(exec_list_iterator, iter, this->variable_storage) {
- entry = (temp_entry *)iter.get();
+ foreach_iter(exec_list_iterator, iter, this->variables) {
+ entry = (variable_storage *)iter.get();
if (entry->var == var)
return entry;
assert(!ir->increment);
assert(!ir->counter);
- ir_to_mesa_emit_op1(NULL, OPCODE_BGNLOOP,
- ir_to_mesa_undef_dst, ir_to_mesa_undef);
-
+ ir_to_mesa_emit_op0(NULL, OPCODE_BGNLOOP);
visit_exec_list(&ir->body_instructions, this);
-
- ir_to_mesa_emit_op1(NULL, OPCODE_ENDLOOP,
- ir_to_mesa_undef_dst, ir_to_mesa_undef);
+ ir_to_mesa_emit_op0(NULL, OPCODE_ENDLOOP);
}
void
{
switch (ir->mode) {
case ir_loop_jump::jump_break:
- ir_to_mesa_emit_op1(NULL, OPCODE_BRK,
- ir_to_mesa_undef_dst, ir_to_mesa_undef);
+ ir_to_mesa_emit_op0(NULL, OPCODE_BRK);
break;
case ir_loop_jump::jump_continue:
- ir_to_mesa_emit_op1(NULL, OPCODE_CONT,
- ir_to_mesa_undef_dst, ir_to_mesa_undef);
+ ir_to_mesa_emit_op0(NULL, OPCODE_CONT);
break;
}
}
}
}
+GLboolean
+ir_to_mesa_visitor::try_emit_mad(ir_expression *ir, int mul_operand)
+{
+ int nonmul_operand = 1 - mul_operand;
+ ir_to_mesa_src_reg a, b, c;
+
+ ir_expression *expr = ir->operands[mul_operand]->as_expression();
+ if (!expr || expr->operation != ir_binop_mul)
+ return false;
+
+ expr->operands[0]->accept(this);
+ a = this->result;
+ expr->operands[1]->accept(this);
+ b = this->result;
+ ir->operands[nonmul_operand]->accept(this);
+ c = this->result;
+
+ this->result = get_temp(ir->type);
+ ir_to_mesa_emit_op3(ir, OPCODE_MAD,
+ ir_to_mesa_dst_reg_from_src(this->result), a, b, c);
+
+ return true;
+}
+
+void
+ir_to_mesa_visitor::reladdr_to_temp(ir_instruction *ir,
+ ir_to_mesa_src_reg *reg, int *num_reladdr)
+{
+ if (!reg->reladdr)
+ return;
+
+ ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, *reg->reladdr);
+
+ if (*num_reladdr != 1) {
+ ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type);
+
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV,
+ ir_to_mesa_dst_reg_from_src(temp), *reg);
+ *reg = temp;
+ }
+
+ (*num_reladdr)--;
+}
+
void
ir_to_mesa_visitor::visit(ir_expression *ir)
{
const glsl_type *vec3_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 3, 1);
const glsl_type *vec2_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 2, 1);
+ /* Quick peephole: Emit OPCODE_MAD(a, b, c) instead of ADD(MUL(a, b), c)
+ */
+ if (ir->operation == ir_binop_add) {
+ if (try_emit_mad(ir, 1))
+ return;
+ if (try_emit_mad(ir, 0))
+ return;
+ }
+
for (operand = 0; operand < ir->get_num_operands(); operand++) {
this->result.file = PROGRAM_UNDEFINED;
ir->operands[operand]->accept(this);
}
op[operand] = this->result;
- /* Only expression implemented for matrices yet */
- assert(!ir->operands[operand]->type->is_matrix() ||
- ir->operation == ir_binop_mul);
+ /* Matrix expression operands should have been broken down to vector
+ * operations already.
+ */
+ assert(!ir->operands[operand]->type->is_matrix());
}
this->result.file = PROGRAM_UNDEFINED;
case ir_unop_abs:
ir_to_mesa_emit_op1(ir, OPCODE_ABS, result_dst, op[0]);
break;
+ case ir_unop_sign:
+ ir_to_mesa_emit_op1(ir, OPCODE_SSG, result_dst, op[0]);
+ break;
+ case ir_unop_rcp:
+ ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, op[0]);
+ break;
case ir_unop_exp:
- ir_to_mesa_emit_scalar_op1(ir, OPCODE_EXP, result_dst, op[0]);
+ ir_to_mesa_emit_scalar_op2(ir, OPCODE_POW, result_dst,
+ src_reg_for_float(M_E), op[0]);
break;
case ir_unop_exp2:
ir_to_mesa_emit_scalar_op1(ir, OPCODE_EX2, result_dst, op[0]);
case ir_binop_sub:
ir_to_mesa_emit_op2(ir, OPCODE_SUB, result_dst, op[0], op[1]);
break;
+
case ir_binop_mul:
- if (ir->operands[0]->type->is_matrix() &&
- !ir->operands[1]->type->is_matrix()) {
- if (ir->operands[1]->type->is_scalar()) {
- ir_to_mesa_dst_reg dst_column = result_dst;
- ir_to_mesa_src_reg src_column = op[0];
- for (int i = 0; i < ir->operands[0]->type->matrix_columns; i++) {
- ir_to_mesa_emit_op2(ir, OPCODE_MUL,
- dst_column, src_column, op[1]);
- dst_column.index++;
- src_column.index++;
- }
- } else {
- ir_to_mesa_src_reg src_column = op[0];
- ir_to_mesa_src_reg src_chan = op[1];
- assert(!ir->operands[1]->type->is_matrix() ||
- !"FINISHME: matrix * matrix");
- for (int i = 0; i < ir->operands[0]->type->matrix_columns; i++) {
- src_chan.swizzle = MAKE_SWIZZLE4(i, i, i, i);
- if (i == 0) {
- ir_to_mesa_emit_op2(ir, OPCODE_MUL,
- result_dst, src_column, src_chan);
- } else {
- ir_to_mesa_emit_op3(ir, OPCODE_MAD,
- result_dst, src_column, src_chan,
- result_src);
- }
- src_column.index++;
- }
- }
- } else {
- assert(!ir->operands[0]->type->is_matrix());
- assert(!ir->operands[1]->type->is_matrix());
- ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]);
- }
+ ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]);
break;
case ir_binop_div:
- ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, op[1]);
- ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], result_src);
+ assert(!"not reached: should be handled by ir_div_to_mul_rcp");
+ case ir_binop_mod:
+ assert(!"ir_binop_mod should have been converted to b * fract(a/b)");
break;
case ir_binop_less:
op[0], op[1]);
}
break;
+
+ case ir_binop_cross:
+ ir_to_mesa_emit_op2(ir, OPCODE_XPD, result_dst, op[0], op[1]);
+ break;
+
case ir_unop_sqrt:
ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]);
ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, result_src);
ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]);
break;
case ir_unop_f2b:
+ case ir_unop_i2b:
ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst,
result_src, src_reg_for_float(0.0));
break;
case ir_unop_floor:
ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]);
break;
+ case ir_unop_fract:
+ ir_to_mesa_emit_op1(ir, OPCODE_FRC, result_dst, op[0]);
+ break;
+
case ir_binop_min:
ir_to_mesa_emit_op2(ir, OPCODE_MIN, result_dst, op[0], op[1]);
break;
case ir_binop_bit_or:
assert(!"GLSL 1.30 features unsupported");
break;
-
- default:
- ir_print_visitor v;
- printf("Failed to get tree for expression:\n");
- ir->accept(&v);
- exit(1);
- break;
}
this->result = result_src;
this->result = src_reg;
}
-static temp_entry *
-get_builtin_matrix_ref(void *mem_ctx, struct gl_program *prog, ir_variable *var)
+static int
+add_matrix_ref(struct gl_program *prog, int *tokens)
+{
+ int base_pos = -1;
+ int i;
+
+ /* Add a ref for each column. It looks like the reason we do
+ * it this way is that _mesa_add_state_reference doesn't work
+ * for things that aren't vec4s, so the tokens[2]/tokens[3]
+ * range has to be equal.
+ */
+ for (i = 0; i < 4; i++) {
+ tokens[2] = i;
+ tokens[3] = i;
+ int pos = _mesa_add_state_reference(prog->Parameters,
+ (gl_state_index *)tokens);
+ if (base_pos == -1)
+ base_pos = pos;
+ else
+ assert(base_pos + i == pos);
+ }
+
+ return base_pos;
+}
+
+static variable_storage *
+get_builtin_matrix_ref(void *mem_ctx, struct gl_program *prog, ir_variable *var,
+ ir_rvalue *array_index)
{
/*
* NOTE: The ARB_vertex_program extension specified that matrices get
};
unsigned int i;
- temp_entry *entry;
+ variable_storage *entry;
/* C++ gets angry when we try to use an int as a gl_state_index, so we use
* ints for gl_state_index. Make sure they're compatible.
for (i = 0; i < Elements(matrices); i++) {
if (strcmp(var->name, matrices[i].name) == 0) {
- int j;
- int last_pos = -1, base_pos = -1;
int tokens[STATE_LENGTH];
+ int base_pos = -1;
tokens[0] = matrices[i].matrix;
- tokens[1] = 0; /* array index! */
tokens[4] = matrices[i].modifier;
-
- /* Add a ref for each column. It looks like the reason we do
- * it this way is that _mesa_add_state_reference doesn't work
- * for things that aren't vec4s, so the tokens[2]/tokens[3]
- * range has to be equal.
- */
- for (j = 0; j < 4; j++) {
- tokens[2] = j;
- tokens[3] = j;
- int pos = _mesa_add_state_reference(prog->Parameters,
- (gl_state_index *)tokens);
- assert(last_pos == -1 || last_pos == base_pos + j);
- if (base_pos == -1)
- base_pos = pos;
+ if (matrices[i].matrix == STATE_TEXTURE_MATRIX) {
+ ir_constant *index = array_index->constant_expression_value();
+ if (index) {
+ tokens[1] = index->value.i[0];
+ base_pos = add_matrix_ref(prog, tokens);
+ } else {
+ for (i = 0; i < var->type->length; i++) {
+ tokens[1] = i;
+ int pos = add_matrix_ref(prog, tokens);
+ if (base_pos == -1)
+ base_pos = pos;
+ else
+ assert(base_pos + (int)i * 4 == pos);
+ }
+ }
+ } else {
+ tokens[1] = 0; /* unused array index */
+ base_pos = add_matrix_ref(prog, tokens);
}
+ tokens[4] = matrices[i].modifier;
- entry = new(mem_ctx) temp_entry(var,
- PROGRAM_STATE_VAR,
- base_pos);
+ entry = new(mem_ctx) variable_storage(var,
+ PROGRAM_STATE_VAR,
+ base_pos);
return entry;
}
ir_to_mesa_visitor::visit(ir_dereference_variable *ir)
{
ir_to_mesa_src_reg src_reg;
- temp_entry *entry = find_variable_storage(ir->var);
- unsigned int i, loc;
- bool var_in;
+ variable_storage *entry = find_variable_storage(ir->var);
+ unsigned int loc;
if (!entry) {
switch (ir->var->mode) {
case ir_var_uniform:
- entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, ir->var);
+ entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, ir->var,
+ NULL);
if (entry)
break;
ir->var->type->gl_type);
map_sampler(ir->var->location, sampler);
- entry = new(mem_ctx) temp_entry(ir->var, PROGRAM_SAMPLER, sampler);
- this->variable_storage.push_tail(entry);
+ entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_SAMPLER,
+ sampler);
+ this->variables.push_tail(entry);
break;
}
*/
this->prog->Parameters->Parameters[loc].Used = GL_TRUE;
- entry = new(mem_ctx) temp_entry(ir->var, PROGRAM_UNIFORM, loc);
- this->variable_storage.push_tail(entry);
+ entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_UNIFORM, loc);
+ this->variables.push_tail(entry);
break;
case ir_var_in:
case ir_var_out:
case ir_var_inout:
- var_in = (ir->var->mode == ir_var_in ||
- ir->var->mode == ir_var_inout);
-
- for (i = 0; i < ARRAY_SIZE(builtin_var_to_mesa_reg); i++) {
- bool in = builtin_var_to_mesa_reg[i].file == PROGRAM_INPUT;
-
- if (strcmp(ir->var->name, builtin_var_to_mesa_reg[i].name) == 0 &&
- !(var_in ^ in))
- break;
- }
- if (i != ARRAY_SIZE(builtin_var_to_mesa_reg)) {
- entry = new(mem_ctx) temp_entry(ir->var,
- builtin_var_to_mesa_reg[i].file,
- builtin_var_to_mesa_reg[i].index);
- break;
- }
-
- /* If no builtin, then it's a user-generated varying
- * (FINISHME: or a function argument!)
- */
- /* The linker-assigned location is VERT_RESULT_* or FRAG_ATTRIB*
+ /* The linker assigns locations for varyings and attributes,
+ * including deprecated builtins (like gl_Color), user-assign
+ * generic attributes (glBindVertexLocation), and
+ * user-defined varyings.
+ *
+ * FINISHME: We would hit this path for function arguments. Fix!
*/
assert(ir->var->location != -1);
- if (var_in) {
- entry = new(mem_ctx) temp_entry(ir->var,
- PROGRAM_INPUT,
- ir->var->location);
+ if (ir->var->mode == ir_var_in ||
+ ir->var->mode == ir_var_inout) {
+ entry = new(mem_ctx) variable_storage(ir->var,
+ PROGRAM_INPUT,
+ ir->var->location);
if (this->prog->Target == GL_VERTEX_PROGRAM_ARB &&
ir->var->location >= VERT_ATTRIB_GENERIC0) {
ir->var->location - VERT_ATTRIB_GENERIC0);
}
} else {
- entry = new(mem_ctx) temp_entry(ir->var,
- PROGRAM_OUTPUT,
- ir->var->location);
+ entry = new(mem_ctx) variable_storage(ir->var,
+ PROGRAM_OUTPUT,
+ ir->var->location);
}
break;
case ir_var_auto:
- entry = new(mem_ctx) temp_entry(ir->var, PROGRAM_TEMPORARY,
- this->next_temp);
- this->variable_storage.push_tail(entry);
+ case ir_var_temporary:
+ entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_TEMPORARY,
+ this->next_temp);
+ this->variables.push_tail(entry);
next_temp += type_size(ir->var->type);
break;
src_reg.index = entry->index;
/* If the type is smaller than a vec4, replicate the last channel out. */
src_reg.swizzle = swizzle_for_size(ir->var->type->vector_elements);
- src_reg.reladdr = false;
+ src_reg.reladdr = NULL;
src_reg.negate = 0;
this->result = src_reg;
{
ir_constant *index;
ir_to_mesa_src_reg src_reg;
+ ir_dereference_variable *deref_var = ir->array->as_dereference_variable();
+ int element_size = type_size(ir->type);
index = ir->array_index->constant_expression_value();
- /* By the time we make it to this stage, matrices should be broken down
- * to vectors.
- */
- assert(!ir->type->is_matrix());
+ if (deref_var && strncmp(deref_var->var->name,
+ "gl_TextureMatrix",
+ strlen("gl_TextureMatrix")) == 0) {
+ ir_to_mesa_src_reg src_reg;
+ struct variable_storage *entry;
- ir->array->accept(this);
- src_reg = this->result;
+ entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, deref_var->var,
+ ir->array_index);
+ assert(entry);
- if (src_reg.file == PROGRAM_INPUT ||
- src_reg.file == PROGRAM_OUTPUT) {
- assert(index); /* FINISHME: Handle variable indexing of builtins. */
+ src_reg.file = entry->file;
+ src_reg.index = entry->index;
+ src_reg.swizzle = swizzle_for_size(ir->type->vector_elements);
+ src_reg.negate = 0;
- src_reg.index += index->value.i[0];
- } else {
if (index) {
- src_reg.index += index->value.i[0];
+ src_reg.reladdr = NULL;
} else {
- ir_to_mesa_src_reg array_base = this->result;
- /* Variable index array dereference. It eats the "vec4" of the
- * base of the array and an index that offsets the Mesa register
- * index.
- */
+ ir_to_mesa_src_reg index_reg = get_temp(glsl_type::float_type);
+
ir->array_index->accept(this);
+ ir_to_mesa_emit_op2(ir, OPCODE_MUL,
+ ir_to_mesa_dst_reg_from_src(index_reg),
+ this->result, src_reg_for_float(element_size));
- /* FINISHME: This doesn't work when we're trying to do the LHS
- * of an assignment.
- */
- src_reg.reladdr = true;
- ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg,
- this->result);
-
- this->result = get_temp(ir->type);
- ir_to_mesa_emit_op1(ir, OPCODE_MOV,
- ir_to_mesa_dst_reg_from_src(this->result),
- src_reg);
+ src_reg.reladdr = talloc(mem_ctx, ir_to_mesa_src_reg);
+ memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg));
+ }
+
+ this->result = src_reg;
+ return;
+ }
+
+ ir->array->accept(this);
+ src_reg = this->result;
+
+ if (index) {
+ src_reg.index += index->value.i[0] * element_size;
+ } else {
+ ir_to_mesa_src_reg array_base = this->result;
+ /* Variable index array dereference. It eats the "vec4" of the
+ * base of the array and an index that offsets the Mesa register
+ * index.
+ */
+ ir->array_index->accept(this);
+
+ ir_to_mesa_src_reg index_reg;
+
+ if (element_size == 1) {
+ index_reg = this->result;
+ } else {
+ index_reg = get_temp(glsl_type::float_type);
+
+ ir_to_mesa_emit_op2(ir, OPCODE_MUL,
+ ir_to_mesa_dst_reg_from_src(index_reg),
+ this->result, src_reg_for_float(element_size));
}
+
+ src_reg.reladdr = talloc(mem_ctx, ir_to_mesa_src_reg);
+ memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg));
}
/* If the type is smaller than a vec4, replicate the last channel out. */
* those, then go use ir_dereference to handle the rest.
*/
static struct ir_to_mesa_dst_reg
-get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v)
+get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v,
+ ir_to_mesa_src_reg *r)
{
struct ir_to_mesa_dst_reg dst_reg;
- ir_dereference *deref;
ir_swizzle *swiz;
+ ir_dereference_array *deref_array = ir->as_dereference_array();
+ /* This should have been handled by ir_vec_index_to_cond_assign */
+ if (deref_array) {
+ assert(!deref_array->array->type->is_vector());
+ }
+
/* Use the rvalue deref handler for the most part. We'll ignore
* swizzles in it and write swizzles using writemask, though.
*/
ir->accept(v);
dst_reg = ir_to_mesa_dst_reg_from_src(v->result);
- if ((deref = ir->as_dereference())) {
- ir_dereference_array *deref_array = ir->as_dereference_array();
- assert(!deref_array || deref_array->array->type->is_array());
+ if ((swiz = ir->as_swizzle())) {
+ int swizzles[4] = {
+ swiz->mask.x,
+ swiz->mask.y,
+ swiz->mask.z,
+ swiz->mask.w
+ };
+ int new_r_swizzle[4];
+ int orig_r_swizzle = r->swizzle;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ new_r_swizzle[i] = GET_SWZ(orig_r_swizzle, 0);
+ }
- ir->accept(v);
- } else if ((swiz = ir->as_swizzle())) {
dst_reg.writemask = 0;
- if (swiz->mask.num_components >= 1)
- dst_reg.writemask |= (1 << swiz->mask.x);
- if (swiz->mask.num_components >= 2)
- dst_reg.writemask |= (1 << swiz->mask.y);
- if (swiz->mask.num_components >= 3)
- dst_reg.writemask |= (1 << swiz->mask.z);
- if (swiz->mask.num_components >= 4)
- dst_reg.writemask |= (1 << swiz->mask.w);
+ for (i = 0; i < 4; i++) {
+ if (i < swiz->mask.num_components) {
+ dst_reg.writemask |= 1 << swizzles[i];
+ new_r_swizzle[swizzles[i]] = GET_SWZ(orig_r_swizzle, i);
+ }
+ }
+
+ r->swizzle = MAKE_SWIZZLE4(new_r_swizzle[0],
+ new_r_swizzle[1],
+ new_r_swizzle[2],
+ new_r_swizzle[3]);
}
return dst_reg;
{
struct ir_to_mesa_dst_reg l;
struct ir_to_mesa_src_reg r;
+ int i;
- assert(!ir->lhs->type->is_matrix());
assert(!ir->lhs->type->is_array());
assert(ir->lhs->type->base_type != GLSL_TYPE_STRUCT);
- l = get_assignment_lhs(ir->lhs, this);
-
ir->rhs->accept(this);
r = this->result;
+
+ l = get_assignment_lhs(ir->lhs, this, &r);
+
assert(l.file != PROGRAM_UNDEFINED);
assert(r.file != PROGRAM_UNDEFINED);
if (ir->condition) {
- ir_constant *condition_constant;
+ ir_to_mesa_src_reg condition;
- condition_constant = ir->condition->constant_expression_value();
+ ir->condition->accept(this);
+ condition = this->result;
- assert(condition_constant && condition_constant->value.b[0]);
+ /* We use the OPCODE_CMP (a < 0 ? b : c) for conditional moves,
+ * and the condition we produced is 0.0 or 1.0. By flipping the
+ * sign, we can choose which value OPCODE_CMP produces without
+ * an extra computing the condition.
+ */
+ condition.negate = ~condition.negate;
+ for (i = 0; i < type_size(ir->lhs->type); i++) {
+ ir_to_mesa_emit_op3(ir, OPCODE_CMP, l,
+ condition, r, ir_to_mesa_src_reg_from_dst(l));
+ l.index++;
+ r.index++;
+ }
+ } else {
+ for (i = 0; i < type_size(ir->lhs->type); i++) {
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+ l.index++;
+ r.index++;
+ }
}
-
- ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
}
GLfloat *values = stack_vals;
unsigned int i;
- if (ir->type->is_matrix() || ir->type->is_array()) {
- assert(!"FINISHME: array/matrix constants");
+ if (ir->type->is_array()) {
+ ir->print();
+ printf("\n");
+ assert(!"FINISHME: array constants");
+ }
+
+ if (ir->type->is_matrix()) {
+ /* Unfortunately, 4 floats is all we can get into
+ * _mesa_add_unnamed_constant. So, make a temp to store the
+ * matrix and move each constant value into it. If we get
+ * lucky, copy propagation will eliminate the extra moves.
+ */
+ ir_to_mesa_src_reg mat = get_temp(glsl_type::vec4_type);
+ ir_to_mesa_dst_reg mat_column = ir_to_mesa_dst_reg_from_src(mat);
+
+ for (i = 0; i < ir->type->matrix_columns; i++) {
+ src_reg.file = PROGRAM_CONSTANT;
+
+ assert(ir->type->base_type == GLSL_TYPE_FLOAT);
+ values = &ir->value.f[i * ir->type->vector_elements];
+
+ src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters,
+ values,
+ ir->type->vector_elements,
+ &src_reg.swizzle);
+ src_reg.reladdr = NULL;
+ src_reg.negate = 0;
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, mat_column, src_reg);
+
+ mat_column.index++;
+ }
+
+ this->result = mat;
}
src_reg.file = PROGRAM_CONSTANT;
src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters,
values, ir->type->vector_elements,
&src_reg.swizzle);
- src_reg.reladdr = false;
+ src_reg.reladdr = NULL;
src_reg.negate = 0;
this->result = src_reg;
}
+function_entry *
+ir_to_mesa_visitor::get_function_signature(ir_function_signature *sig)
+{
+ function_entry *entry;
+
+ foreach_iter(exec_list_iterator, iter, this->function_signatures) {
+ entry = (function_entry *)iter.get();
+
+ if (entry->sig == sig)
+ return entry;
+ }
+
+ entry = talloc(mem_ctx, function_entry);
+ entry->sig = sig;
+ entry->sig_id = this->next_signature_id++;
+ entry->bgn_inst = NULL;
+
+ /* Allocate storage for all the parameters. */
+ foreach_iter(exec_list_iterator, iter, sig->parameters) {
+ ir_variable *param = (ir_variable *)iter.get();
+ variable_storage *storage;
+
+ storage = find_variable_storage(param);
+ assert(!storage);
+
+ storage = new(mem_ctx) variable_storage(param, PROGRAM_TEMPORARY,
+ this->next_temp);
+ this->variables.push_tail(storage);
+
+ this->next_temp += type_size(param->type);
+ break;
+ }
+
+ if (sig->return_type) {
+ entry->return_reg = get_temp(sig->return_type);
+ } else {
+ entry->return_reg = ir_to_mesa_undef;
+ }
+
+ this->function_signatures.push_tail(entry);
+ return entry;
+}
void
ir_to_mesa_visitor::visit(ir_call *ir)
{
- printf("Can't support call to %s\n", ir->callee_name());
- exit(1);
+ ir_to_mesa_instruction *call_inst;
+ ir_function_signature *sig = ir->get_callee();
+ function_entry *entry = get_function_signature(sig);
+ int i;
+
+ /* Process in parameters. */
+ exec_list_iterator sig_iter = sig->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+ ir_variable *param = (ir_variable *)sig_iter.get();
+
+ if (param->mode == ir_var_in ||
+ param->mode == ir_var_inout) {
+ variable_storage *storage = find_variable_storage(param);
+ assert(storage);
+
+ param_rval->accept(this);
+ ir_to_mesa_src_reg r = this->result;
+
+ ir_to_mesa_dst_reg l;
+ l.file = storage->file;
+ l.index = storage->index;
+ l.reladdr = NULL;
+ l.writemask = WRITEMASK_XYZW;
+ l.cond_mask = COND_TR;
+
+ for (i = 0; i < type_size(param->type); i++) {
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+ l.index++;
+ r.index++;
+ }
+ }
+
+ sig_iter.next();
+ }
+ assert(!sig_iter.has_next());
+
+ /* Emit call instruction */
+ call_inst = ir_to_mesa_emit_op1(ir, OPCODE_CAL,
+ ir_to_mesa_undef_dst, ir_to_mesa_undef);
+ call_inst->function = entry;
+
+ /* Process out parameters. */
+ sig_iter = sig->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+ ir_variable *param = (ir_variable *)sig_iter.get();
+
+ if (param->mode == ir_var_out ||
+ param->mode == ir_var_inout) {
+ variable_storage *storage = find_variable_storage(param);
+ assert(storage);
+
+ ir_to_mesa_src_reg r;
+ r.file = storage->file;
+ r.index = storage->index;
+ r.reladdr = NULL;
+ r.swizzle = SWIZZLE_NOOP;
+ r.negate = 0;
+
+ param_rval->accept(this);
+ ir_to_mesa_dst_reg l = ir_to_mesa_dst_reg_from_src(this->result);
+
+ for (i = 0; i < type_size(param->type); i++) {
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+ l.index++;
+ r.index++;
+ }
+ }
+
+ sig_iter.next();
+ }
+ assert(!sig_iter.has_next());
+
+ /* Process return value. */
+ this->result = entry->return_reg;
}
void
ir_to_mesa_visitor::visit(ir_texture *ir)
{
- ir_to_mesa_src_reg result_src, coord;
- ir_to_mesa_dst_reg result_dst, lod_info;
+ ir_to_mesa_src_reg result_src, coord, lod_info = { 0 }, projector;
+ ir_to_mesa_dst_reg result_dst, coord_dst;
ir_to_mesa_instruction *inst = NULL;
+ prog_opcode opcode = OPCODE_NOP;
ir->coordinate->accept(this);
- coord = this->result;
+
+ /* Put our coords in a temp. We'll need to modify them for shadow,
+ * projection, or LOD, so the only case we'd use it as is is if
+ * we're doing plain old texturing. Mesa IR optimization should
+ * handle cleaning up our mess in that case.
+ */
+ coord = get_temp(glsl_type::vec4_type);
+ coord_dst = ir_to_mesa_dst_reg_from_src(coord);
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst,
+ this->result);
+
+ if (ir->projector) {
+ ir->projector->accept(this);
+ projector = this->result;
+ }
/* Storage for our result. Ideally for an assignment we'd be using
* the actual storage for the result here, instead.
switch (ir->op) {
case ir_tex:
- inst = ir_to_mesa_emit_op1(ir, OPCODE_TEX, result_dst, coord);
+ opcode = OPCODE_TEX;
break;
case ir_txb:
- /* Mesa IR stores bias in the last channel of the coords. */
- lod_info = ir_to_mesa_dst_reg_from_src(coord);
- lod_info.writemask = WRITEMASK_W;
+ opcode = OPCODE_TXB;
ir->lod_info.bias->accept(this);
- ir_to_mesa_emit_op1(ir, OPCODE_MOV, lod_info, this->result);
-
- inst = ir_to_mesa_emit_op1(ir, OPCODE_TXB, result_dst, coord);
+ lod_info = this->result;
break;
case ir_txl:
- /* Mesa IR stores lod in the last channel of the coords. */
- lod_info = ir_to_mesa_dst_reg_from_src(coord);
- lod_info.writemask = WRITEMASK_W;
+ opcode = OPCODE_TXL;
ir->lod_info.lod->accept(this);
- ir_to_mesa_emit_op1(ir, OPCODE_MOV, lod_info, this->result);
-
- inst = ir_to_mesa_emit_op1(ir, OPCODE_TXL, result_dst, coord);
+ lod_info = this->result;
break;
case ir_txd:
case ir_txf:
break;
}
+ if (ir->projector) {
+ if (opcode == OPCODE_TEX) {
+ /* Slot the projector in as the last component of the coord. */
+ coord_dst.writemask = WRITEMASK_W;
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, projector);
+ coord_dst.writemask = WRITEMASK_XYZW;
+ opcode = OPCODE_TXP;
+ } else {
+ ir_to_mesa_src_reg coord_w = coord;
+ coord_w.swizzle = SWIZZLE_WWWW;
+
+ /* For the other TEX opcodes there's no projective version
+ * since the last slot is taken up by lod info. Do the
+ * projective divide now.
+ */
+ coord_dst.writemask = WRITEMASK_W;
+ ir_to_mesa_emit_op1(ir, OPCODE_RCP, coord_dst, projector);
+
+ coord_dst.writemask = WRITEMASK_XYZ;
+ ir_to_mesa_emit_op2(ir, OPCODE_MUL, coord_dst, coord, coord_w);
+
+ coord_dst.writemask = WRITEMASK_XYZW;
+ coord.swizzle = SWIZZLE_XYZW;
+ }
+ }
+
+ if (ir->shadow_comparitor) {
+ /* Slot the shadow value in as the second to last component of the
+ * coord.
+ */
+ ir->shadow_comparitor->accept(this);
+ coord_dst.writemask = WRITEMASK_Z;
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, this->result);
+ coord_dst.writemask = WRITEMASK_XYZW;
+ }
+
+ if (opcode == OPCODE_TXL || opcode == OPCODE_TXB) {
+ /* Mesa IR stores lod or lod bias in the last channel of the coords. */
+ coord_dst.writemask = WRITEMASK_W;
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, lod_info);
+ coord_dst.writemask = WRITEMASK_XYZW;
+ }
+
+ inst = ir_to_mesa_emit_op1(ir, opcode, result_dst, coord);
+
+ if (ir->shadow_comparitor)
+ inst->tex_shadow = GL_TRUE;
+
ir_dereference_variable *sampler = ir->sampler->as_dereference_variable();
assert(sampler); /* FINISHME: sampler arrays */
/* generate the mapping, remove when we generate storage at
assert(!"FINISHME: other texture targets");
}
- assert(!ir->projector); /* FINISHME */
- assert(!ir->shadow_comparitor); /* FINISHME */
-
this->result = result_src;
}
void
ir_to_mesa_visitor::visit(ir_return *ir)
{
- assert(0);
+ assert(current_function);
- ir->get_value()->accept(this);
+ if (ir->get_value()) {
+ ir_to_mesa_dst_reg l;
+ int i;
+
+ ir->get_value()->accept(this);
+ ir_to_mesa_src_reg r = this->result;
+
+ l = ir_to_mesa_dst_reg_from_src(current_function->return_reg);
+
+ for (i = 0; i < type_size(current_function->sig->return_type); i++) {
+ ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+ l.index++;
+ r.index++;
+ }
+ }
+
+ ir_to_mesa_emit_op0(ir, OPCODE_RET);
}
void
ir_to_mesa_visitor::visit(ir_discard *ir)
{
- assert(0);
+ assert(ir->condition == NULL); /* FINISHME */
- ir->condition->accept(this);
+ ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV);
}
void
}
cond_inst->cond_update = GL_TRUE;
- if_inst = ir_to_mesa_emit_op1(ir->condition,
- OPCODE_IF, ir_to_mesa_undef_dst,
- ir_to_mesa_undef);
+ if_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_IF);
if_inst->dst_reg.cond_mask = COND_NE;
} else {
if_inst = ir_to_mesa_emit_op1(ir->condition,
visit_exec_list(&ir->then_instructions, this);
if (!ir->else_instructions.is_empty()) {
- else_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ELSE,
- ir_to_mesa_undef_dst,
- ir_to_mesa_undef);
+ else_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_ELSE);
visit_exec_list(&ir->else_instructions, this);
}
{
result.file = PROGRAM_UNDEFINED;
next_temp = 1;
+ next_signature_id = 1;
sampler_map = NULL;
sampler_map_size = 0;
+ current_function = NULL;
}
static struct prog_src_register
assert(reg.index < (1 << INST_INDEX_BITS) - 1);
mesa_reg.Index = reg.index;
mesa_reg.Swizzle = reg.swizzle;
- mesa_reg.RelAddr = reg.reladdr;
+ mesa_reg.RelAddr = reg.reladdr != NULL;
mesa_reg.Negate = reg.negate;
mesa_reg.Abs = 0;
}
static void
-set_branchtargets(struct prog_instruction *mesa_instructions,
+set_branchtargets(ir_to_mesa_visitor *v,
+ struct prog_instruction *mesa_instructions,
int num_instructions)
{
- int if_count = 0, loop_count;
+ int if_count = 0, loop_count = 0;
int *if_stack, *loop_stack;
int if_stack_pos = 0, loop_stack_pos = 0;
int i, j;
/* The loop ends point at each other. */
mesa_instructions[i].BranchTarget = loop_stack[loop_stack_pos];
mesa_instructions[loop_stack[loop_stack_pos]].BranchTarget = i;
+ break;
+ case OPCODE_CAL:
+ foreach_iter(exec_list_iterator, iter, v->function_signatures) {
+ function_entry *entry = (function_entry *)iter.get();
+
+ if (entry->sig_id == mesa_instructions[i].BranchTarget) {
+ mesa_instructions[i].BranchTarget = entry->inst;
+ break;
+ }
+ }
+ break;
default:
break;
}
}
}
+static void
+mark_input(struct gl_program *prog,
+ int index,
+ GLboolean reladdr)
+{
+ prog->InputsRead |= BITFIELD64_BIT(index);
+ int i;
+
+ if (reladdr) {
+ if (index >= FRAG_ATTRIB_TEX0 && index <= FRAG_ATTRIB_TEX7) {
+ for (i = 0; i < 8; i++) {
+ prog->InputsRead |= BITFIELD64_BIT(FRAG_ATTRIB_TEX0 + i);
+ }
+ } else {
+ assert(!"FINISHME: Mark InputsRead for varying arrays");
+ }
+ }
+}
+
+static void
+mark_output(struct gl_program *prog,
+ int index,
+ GLboolean reladdr)
+{
+ prog->OutputsWritten |= BITFIELD64_BIT(index);
+ int i;
+
+ if (reladdr) {
+ if (index >= VERT_RESULT_TEX0 && index <= VERT_RESULT_TEX7) {
+ for (i = 0; i < 8; i++) {
+ prog->OutputsWritten |= BITFIELD64_BIT(FRAG_ATTRIB_TEX0 + i);
+ }
+ } else {
+ assert(!"FINISHME: Mark OutputsWritten for varying arrays");
+ }
+ }
+}
+
static void
count_resources(struct gl_program *prog)
{
switch (inst->DstReg.File) {
case PROGRAM_OUTPUT:
- prog->OutputsWritten |= BITFIELD64_BIT(inst->DstReg.Index);
+ mark_output(prog, inst->DstReg.Index, inst->DstReg.RelAddr);
break;
case PROGRAM_INPUT:
- prog->InputsRead |= BITFIELD64_BIT(inst->DstReg.Index);
+ mark_input(prog, inst->DstReg.Index, inst->DstReg.RelAddr);
break;
default:
break;
for (reg = 0; reg < _mesa_num_inst_src_regs(inst->Opcode); reg++) {
switch (inst->SrcReg[reg].File) {
case PROGRAM_OUTPUT:
- prog->OutputsWritten |= BITFIELD64_BIT(inst->SrcReg[reg].Index);
+ mark_output(prog, inst->SrcReg[reg].Index,
+ inst->SrcReg[reg].RelAddr);
break;
case PROGRAM_INPUT:
- prog->InputsRead |= BITFIELD64_BIT(inst->SrcReg[reg].Index);
+ mark_input(prog, inst->SrcReg[reg].Index, inst->SrcReg[reg].RelAddr);
break;
default:
break;
}
struct gl_program *
-get_mesa_program(GLcontext *ctx, void *mem_ctx, struct gl_shader *shader)
+get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program,
+ struct gl_shader *shader)
{
+ void *mem_ctx = shader_program;
ir_to_mesa_visitor v;
struct prog_instruction *mesa_instructions, *mesa_inst;
ir_instruction **mesa_instruction_annotation;
int i;
struct gl_program *prog;
GLenum target;
+ GLboolean progress;
switch (shader->Type) {
case GL_VERTEX_SHADER: target = GL_VERTEX_PROGRAM_ARB; break;
default: assert(!"should not be reached"); break;
}
+ validate_ir_tree(shader->ir);
+
prog = ctx->Driver.NewProgram(ctx, target, 1);
if (!prog)
return NULL;
v.prog = prog;
v.mem_ctx = talloc_new(NULL);
+
+ /* Emit Mesa IR for main(). */
visit_exec_list(shader->ir, &v);
- v.ir_to_mesa_emit_op1(NULL, OPCODE_END,
- ir_to_mesa_undef_dst, ir_to_mesa_undef);
+ v.ir_to_mesa_emit_op0(NULL, OPCODE_END);
+
+ /* Now emit bodies for any functions that were used. */
+ do {
+ progress = GL_FALSE;
+
+ foreach_iter(exec_list_iterator, iter, v.function_signatures) {
+ function_entry *entry = (function_entry *)iter.get();
+
+ if (!entry->bgn_inst) {
+ v.current_function = entry;
+
+ entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_BGNSUB);
+ entry->bgn_inst->function = entry;
+
+ visit_exec_list(&entry->sig->body, &v);
+
+ entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_RET);
+ entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_ENDSUB);
+ progress = GL_TRUE;
+ }
+ }
+ } while (progress);
prog->NumTemporaries = v.next_temp;
mesa_inst->DstReg.Index = inst->dst_reg.index;
mesa_inst->DstReg.CondMask = inst->dst_reg.cond_mask;
mesa_inst->DstReg.WriteMask = inst->dst_reg.writemask;
+ mesa_inst->DstReg.RelAddr = inst->dst_reg.reladdr != NULL;
mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src_reg[0]);
mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src_reg[1]);
mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src_reg[2]);
mesa_inst->TexSrcUnit = inst->sampler;
mesa_inst->TexSrcTarget = inst->tex_target;
+ mesa_inst->TexShadow = inst->tex_shadow;
mesa_instruction_annotation[i] = inst->ir;
+ if (ctx->Shader.EmitNoIfs && mesa_inst->Opcode == OPCODE_IF) {
+ shader_program->InfoLog =
+ talloc_asprintf_append(shader_program->InfoLog,
+ "Couldn't flatten if statement\n");
+ shader_program->LinkStatus = false;
+ }
+
+ if (mesa_inst->Opcode == OPCODE_BGNSUB)
+ inst->function->inst = i;
+ else if (mesa_inst->Opcode == OPCODE_CAL)
+ mesa_inst->BranchTarget = inst->function->sig_id; /* rewritten later */
+
mesa_inst++;
i++;
}
- set_branchtargets(mesa_instructions, num_instructions);
+ set_branchtargets(&v, mesa_instructions, num_instructions);
if (0) {
print_program(mesa_instructions, mesa_instruction_annotation,
num_instructions);
_mesa_reference_program(ctx, &shader->Program, prog);
+ if ((ctx->Shader.Flags & GLSL_NO_OPT) == 0) {
+ _mesa_optimize_program(ctx, prog);
+ }
+
return prog;
}
extern "C" {
-static void
-steal_memory(ir_instruction *ir, void *new_ctx)
-{
- talloc_steal(new_ctx, ir);
-}
-
void
_mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader)
{
- struct _mesa_glsl_parse_state *state;
-
- state = talloc_zero(shader, struct _mesa_glsl_parse_state);
- switch (shader->Type) {
- case GL_VERTEX_SHADER: state->target = vertex_shader; break;
- case GL_FRAGMENT_SHADER: state->target = fragment_shader; break;
- case GL_GEOMETRY_SHADER: state->target = geometry_shader; break;
- }
-
- state->scanner = NULL;
- state->translation_unit.make_empty();
- state->symbols = new(shader) glsl_symbol_table;
- state->info_log = talloc_strdup(shader, "");
- state->error = false;
- state->temp_index = 0;
- state->loop_or_switch_nesting = NULL;
- state->ARB_texture_rectangle_enable = true;
-
- state->Const.MaxDrawBuffers = ctx->Const.MaxDrawBuffers;
+ struct _mesa_glsl_parse_state *state =
+ new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader);
const char *source = shader->Source;
- state->error = preprocess(state, &source, &state->info_log);
+ state->error = preprocess(state, &source, &state->info_log,
+ &ctx->Extensions);
if (!state->error) {
_mesa_glsl_lexer_ctor(state, source);
if (!state->error && !state->translation_unit.is_empty())
_mesa_ast_to_hir(shader->ir, state);
- /* Optimization passes */
if (!state->error && !shader->ir->is_empty()) {
+ validate_ir_tree(shader->ir);
+
+ /* Lowering */
+ do_mat_op_to_vec(shader->ir);
+ do_mod_to_fract(shader->ir);
+ do_div_to_mul_rcp(shader->ir);
+
+ /* Optimization passes */
bool progress;
do {
progress = false;
progress = do_dead_code_unlinked(state, shader->ir) || progress;
progress = do_constant_variable_unlinked(shader->ir) || progress;
progress = do_constant_folding(shader->ir) || progress;
+ progress = do_if_return(shader->ir) || progress;
+ if (ctx->Shader.EmitNoIfs)
+ progress = do_if_to_cond_assign(shader->ir) || progress;
+
progress = do_vec_index_to_swizzle(shader->ir) || progress;
+ /* Do this one after the previous to let the easier pass handle
+ * constant vector indexing.
+ */
+ progress = do_vec_index_to_cond_assign(shader->ir) || progress;
+
progress = do_swizzle_swizzle(shader->ir) || progress;
} while (progress);
+
+ validate_ir_tree(shader->ir);
}
shader->symbols = state->symbols;
shader->CompileStatus = !state->error;
shader->InfoLog = state->info_log;
+ shader->Version = state->language_version;
+ memcpy(shader->builtins_to_link, state->builtins_to_link,
+ sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link);
+ shader->num_builtins_to_link = state->num_builtins_to_link;
/* Retain any live IR, but trash the rest. */
- foreach_list(node, shader->ir) {
- visit_tree((ir_instruction *) node, steal_memory, shader);
- }
+ reparent_ir(shader->ir, shader);
talloc_free(state);
}
prog->Uniforms = _mesa_new_uniform_list();
}
- prog->LinkStatus = prog->LinkStatus;
-
- /* FINISHME: This should use the linker-generated code */
if (prog->LinkStatus) {
- for (i = 0; i < prog->NumShaders; i++) {
+ for (i = 0; i < prog->_NumLinkedShaders; i++) {
struct gl_program *linked_prog;
linked_prog = get_mesa_program(ctx, prog,
- prog->Shaders[i]);
+ prog->_LinkedShaders[i]);
count_resources(linked_prog);
link_uniforms_to_shared_uniform_list(prog->Uniforms, linked_prog);
- switch (prog->Shaders[i]->Type) {
+ switch (prog->_LinkedShaders[i]->Type) {
case GL_VERTEX_SHADER:
_mesa_reference_vertprog(ctx, &prog->VertexProgram,
(struct gl_vertex_program *)linked_prog);