summaryrefslogtreecommitdiff
path: root/ir_to_mesa.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ir_to_mesa.cpp')
-rw-r--r--ir_to_mesa.cpp548
1 files changed, 548 insertions, 0 deletions
diff --git a/ir_to_mesa.cpp b/ir_to_mesa.cpp
new file mode 100644
index 0000000000..5cbd451b21
--- /dev/null
+++ b/ir_to_mesa.cpp
@@ -0,0 +1,548 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file ir_to_mesa.cpp
+ *
+ * Translates the IR to ARB_fragment_program text if possible,
+ * printing the result
+ *
+ * The code generation is performed using monoburg. Because monoburg
+ * produces a single C file with the definitions of the node types in
+ * it, this file is included from the monoburg output.
+ */
+
+/* Quiet compiler warnings due to monoburg not marking functions defined
+ * in the header as inline.
+ */
+#define g_new
+#define g_error
+#include "mesa_codegen.h"
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_print_visitor.h"
+#include "ir_expression_flattening.h"
+#include "glsl_types.h"
+
+#include "shader/prog_instruction.h"
+
+ir_to_mesa_src_reg ir_to_mesa_undef = {
+ PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP
+};
+
+ir_to_mesa_instruction *
+ir_to_mesa_emit_op3(struct mbtree *tree, enum prog_opcode op,
+ ir_to_mesa_dst_reg dst,
+ ir_to_mesa_src_reg src0,
+ ir_to_mesa_src_reg src1,
+ ir_to_mesa_src_reg src2)
+{
+ ir_to_mesa_instruction *inst = new ir_to_mesa_instruction();
+
+ inst->op = op;
+ inst->dst_reg = dst;
+ inst->src_reg[0] = src0;
+ inst->src_reg[1] = src1;
+ inst->src_reg[2] = src2;
+
+ tree->v->instructions.push_tail(inst);
+
+ return inst;
+}
+
+
+ir_to_mesa_instruction *
+ir_to_mesa_emit_op2(struct mbtree *tree, enum prog_opcode op,
+ ir_to_mesa_dst_reg dst,
+ ir_to_mesa_src_reg src0,
+ ir_to_mesa_src_reg src1)
+{
+ return ir_to_mesa_emit_op3(tree, op, dst, src0, src1, ir_to_mesa_undef);
+}
+
+ir_to_mesa_instruction *
+ir_to_mesa_emit_op1(struct mbtree *tree, enum prog_opcode op,
+ ir_to_mesa_dst_reg dst,
+ ir_to_mesa_src_reg src0)
+{
+ return ir_to_mesa_emit_op3(tree, op,
+ dst, src0, ir_to_mesa_undef, ir_to_mesa_undef);
+}
+
+struct mbtree *
+ir_to_mesa_visitor::create_tree(int op, struct mbtree *left, struct mbtree *right)
+{
+ struct mbtree *tree = (struct mbtree *)calloc(sizeof(struct mbtree), 1);
+
+ tree->op = op;
+ tree->left = left;
+ tree->right = right;
+ tree->v = this;
+ tree->src_reg.swizzle = SWIZZLE_XYZW;
+
+ return tree;
+}
+
+const char *
+produce_swizzle(int8_t *swizzle, const char *reg_name,
+ const char **swizzle_reg_name)
+{
+ if (swizzle[0] == 0 &&
+ swizzle[1] == 1 &&
+ swizzle[2] == 2 &&
+ swizzle[3] == 3)
+ {
+ *swizzle_reg_name = reg_name;
+ } else {
+ char swizzle_letters[4] = { 'x', 'y', 'z', 'w' };
+ char *temp;
+ asprintf(&temp, "%s.%c%c%c%c",
+ reg_name,
+ swizzle_letters[swizzle[0]],
+ swizzle_letters[swizzle[1]],
+ swizzle_letters[swizzle[2]],
+ swizzle_letters[swizzle[3]]);
+ *swizzle_reg_name = temp;
+ }
+ return *swizzle_reg_name;
+}
+
+/**
+ * In the initial pass of codegen, we assign temporary numbers to
+ * intermediate results. (not SSA -- variable assignments will reuse
+ * storage). Actual register allocation for the Mesa VM occurs in a
+ * pass over the Mesa IR later.
+ */
+void
+ir_to_mesa_visitor::get_temp(struct mbtree *tree)
+{
+ tree->src_reg.file = PROGRAM_TEMPORARY;
+ tree->src_reg.index = this->next_temp++;
+}
+
+void
+ir_to_mesa_visitor::get_temp_for_var(ir_variable *var, struct mbtree *tree)
+{
+ temp_entry *entry;
+
+ foreach_iter(exec_list_iterator, iter, this->variable_storage) {
+ entry = (temp_entry *)iter.get();
+
+ if (entry->var == var) {
+ tree->src_reg.file = entry->file;
+ tree->src_reg.index = entry->index;
+ return;
+ }
+ }
+
+ entry = new temp_entry(var, PROGRAM_TEMPORARY, this->next_temp++);
+ this->variable_storage.push_tail(entry);
+
+ tree->src_reg.file = entry->file;
+ tree->src_reg.index = entry->index;
+}
+
+static void
+reduce(struct mbtree *t, int goal)
+{
+ struct mbtree *kids[10];
+ int rule = mono_burg_rule((MBState *)t->state, goal);
+ const uint16_t *nts = mono_burg_nts[rule];
+ int i;
+
+ mono_burg_kids (t, rule, kids);
+
+ for (i = 0; nts[i]; i++) {
+ reduce(kids[i], nts[i]);
+ }
+
+ if (t->left) {
+ if (mono_burg_func[rule]) {
+ mono_burg_func[rule](t, NULL);
+ } else {
+ printf("no code for rules %s\n", mono_burg_rule_string[rule]);
+ exit(1);
+ }
+ } else {
+ if (mono_burg_func[rule]) {
+ printf("unused code for rule %s\n", mono_burg_rule_string[rule]);
+ exit(1);
+ }
+ }
+}
+
+void
+ir_to_mesa_visitor::visit(ir_variable *ir)
+{
+ (void)ir;
+}
+
+void
+ir_to_mesa_visitor::visit(ir_loop *ir)
+{
+ (void)ir;
+
+ printf("Can't support loops, should be flattened before here\n");
+ exit(1);
+}
+
+void
+ir_to_mesa_visitor::visit(ir_loop_jump *ir)
+{
+ (void) ir;
+ printf("Can't support loops, should be flattened before here\n");
+ exit(1);
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_function_signature *ir)
+{
+ assert(0);
+ (void)ir;
+}
+
+void
+ir_to_mesa_visitor::visit(ir_function *ir)
+{
+ /* Ignore function bodies other than main() -- we shouldn't see calls to
+ * them since they should all be inlined before we get to ir_to_mesa.
+ */
+ if (strcmp(ir->name, "main") == 0) {
+ const ir_function_signature *sig;
+ exec_list empty;
+
+ sig = ir->matching_signature(&empty);
+
+ assert(sig);
+
+ foreach_iter(exec_list_iterator, iter, sig->body) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+
+ ir->accept(this);
+ }
+ }
+}
+
+void
+ir_to_mesa_visitor::visit(ir_expression *ir)
+{
+ unsigned int operand;
+ struct mbtree *op[2];
+ const glsl_type *vec4_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 4, 1);
+ 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);
+
+ for (operand = 0; operand < ir->get_num_operands(); operand++) {
+ this->result = NULL;
+ ir->operands[operand]->accept(this);
+ if (!this->result) {
+ ir_print_visitor v;
+ printf("Failed to get tree for expression operand:\n");
+ ir->operands[operand]->accept(&v);
+ exit(1);
+ }
+ op[operand] = this->result;
+ }
+
+ this->result = NULL;
+
+ switch (ir->operation) {
+ case ir_binop_add:
+ this->result = this->create_tree(MB_TERM_add_vec4_vec4, op[0], op[1]);
+ break;
+ case ir_binop_sub:
+ this->result = this->create_tree(MB_TERM_sub_vec4_vec4, op[0], op[1]);
+ break;
+ case ir_binop_mul:
+ this->result = this->create_tree(MB_TERM_mul_vec4_vec4, op[0], op[1]);
+ break;
+ case ir_binop_div:
+ this->result = this->create_tree(MB_TERM_div_vec4_vec4, op[0], op[1]);
+ break;
+ case ir_binop_dot:
+ if (ir->operands[0]->type == vec4_type) {
+ assert(ir->operands[1]->type == vec4_type);
+ this->result = this->create_tree(MB_TERM_dp4_vec4_vec4, op[0], op[1]);
+ } else if (ir->operands[0]->type == vec3_type) {
+ assert(ir->operands[1]->type == vec3_type);
+ this->result = this->create_tree(MB_TERM_dp3_vec4_vec4, op[0], op[1]);
+ } else if (ir->operands[0]->type == vec2_type) {
+ assert(ir->operands[1]->type == vec2_type);
+ this->result = this->create_tree(MB_TERM_dp2_vec4_vec4, op[0], op[1]);
+ }
+ break;
+ case ir_unop_sqrt:
+ this->result = this->create_tree(MB_TERM_sqrt_vec4, op[0], op[1]);
+ break;
+ default:
+ break;
+ }
+ if (!this->result) {
+ ir_print_visitor v;
+ printf("Failed to get tree for expression:\n");
+ ir->accept(&v);
+ exit(1);
+ }
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_swizzle *ir)
+{
+ struct mbtree *tree;
+ int i;
+ int swizzle[4];
+
+ /* FINISHME: Handle swizzles on the left side of an assignment. */
+
+ ir->val->accept(this);
+ assert(this->result);
+
+ tree = this->create_tree(MB_TERM_swizzle_vec4, this->result, NULL);
+
+ for (i = 0; i < 4; i++) {
+ if (i < ir->type->vector_elements) {
+ switch (i) {
+ case 0:
+ swizzle[i] = ir->mask.x;
+ break;
+ case 1:
+ swizzle[i] = ir->mask.y;
+ break;
+ case 2:
+ swizzle[i] = ir->mask.z;
+ break;
+ case 3:
+ swizzle[i] = ir->mask.w;
+ break;
+ }
+ } else {
+ /* If the type is smaller than a vec4, replicate the last
+ * channel out.
+ */
+ swizzle[i] = ir->type->vector_elements - 1;
+ }
+ }
+
+ tree->src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0],
+ swizzle[1],
+ swizzle[2],
+ swizzle[3]);
+
+ this->result = tree;
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_dereference_variable *ir)
+{
+ struct mbtree *tree;
+ int size_swizzles[4] = {
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X),
+ };
+
+ ir_variable *var = ir->var->as_variable();
+
+ /* By the time we make it to this stage, matric`es should be broken down
+ * to vectors.
+ */
+ assert(!var->type->is_matrix());
+
+ tree = this->create_tree(MB_TERM_reference_vec4, NULL, NULL);
+
+ if (strncmp(var->name, "gl_", 3) == 0) {
+ if (strcmp(var->name, "gl_FragColor") == 0) {
+ tree->src_reg.file = PROGRAM_INPUT;
+ tree->src_reg.index = FRAG_ATTRIB_COL0;
+ } else {
+ assert(0);
+ }
+ } else {
+ this->get_temp_for_var(var, tree);
+ }
+
+ /* If the type is smaller than a vec4, replicate the last channel out. */
+ tree->src_reg.swizzle = size_swizzles[ir->type->vector_elements - 1];
+
+ this->result = tree;
+}
+
+void
+ir_to_mesa_visitor::visit(ir_dereference_array *ir)
+{
+ struct mbtree *tree;
+ int size_swizzles[4] = {
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X),
+ };
+
+ ir_variable *var = ir->array->as_variable();
+ ir_constant *index = ir->array_index->constant_expression_value();
+ char *name;
+
+ assert(var);
+ assert(index);
+ assert(strcmp(var->name, "gl_TexCoord") == 0);
+
+ asprintf(&name, "fragment.texcoord[%d]", index->value.i[0]);
+ tree = this->create_tree(MB_TERM_reference_vec4, NULL, NULL);
+ tree->reg_name = name;
+
+ /* If the type is smaller than a vec4, replicate the last channel out. */
+ tree->src_reg.swizzle = size_swizzles[ir->type->vector_elements - 1];
+
+ this->result = tree;
+}
+
+void
+ir_to_mesa_visitor::visit(ir_dereference_record *ir)
+{
+ (void)ir;
+ assert(0);
+}
+
+void
+ir_to_mesa_visitor::visit(ir_assignment *ir)
+{
+ struct mbtree *l, *r, *t;
+
+ ir->lhs->accept(this);
+ l = this->result;
+ ir->rhs->accept(this);
+ r = this->result;
+ assert(l);
+ assert(r);
+
+ assert(!ir->condition);
+
+ t = this->create_tree(MB_TERM_assign, l, r);
+ mono_burg_label(t, NULL);
+ reduce(t, MB_NTERM_stmt);
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_constant *ir)
+{
+ struct mbtree *tree;
+
+ assert(!ir->type->is_matrix());
+
+ tree = this->create_tree(MB_TERM_reference_vec4, NULL, NULL);
+
+ assert(ir->type->base_type == GLSL_TYPE_FLOAT);
+
+ /* FINISHME: This will end up being _mesa_add_unnamed_constant,
+ * which handles sharing values and sharing channels of vec4
+ * constants for small values.
+ */
+ /* FINISHME: Do something with the constant values for now.
+ */
+ tree->src_reg.file = PROGRAM_CONSTANT;
+ tree->src_reg.index = this->next_constant++;
+ tree->src_reg.swizzle = SWIZZLE_NOOP;
+
+ this->result = tree;
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_call *ir)
+{
+ printf("Can't support call to %s\n", ir->callee_name());
+ exit(1);
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_texture *ir)
+{
+ assert(0);
+
+ ir->coordinate->accept(this);
+}
+
+void
+ir_to_mesa_visitor::visit(ir_return *ir)
+{
+ assert(0);
+
+ ir->get_value()->accept(this);
+}
+
+
+void
+ir_to_mesa_visitor::visit(ir_if *ir)
+{
+ (void)ir;
+ printf("Can't support conditionals, should be flattened before here.\n");
+ exit(1);
+}
+
+static struct prog_src_register
+mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg)
+{
+ struct prog_src_register mesa_reg;
+
+ mesa_reg.File = reg.file;
+ mesa_reg.Index = reg.index;
+
+ return mesa_reg;
+}
+
+void
+do_ir_to_mesa(exec_list *instructions)
+{
+ ir_to_mesa_visitor v;
+ struct prog_instruction *mesa_instructions, *mesa_inst;
+
+ visit_exec_list(instructions, &v);
+
+ int num_instructions = 0;
+ foreach_iter(exec_list_iterator, iter, v.instructions) {
+ num_instructions++;
+ }
+
+ mesa_instructions =
+ (struct prog_instruction *)calloc(num_instructions,
+ sizeof(*mesa_instructions));
+
+ mesa_inst = mesa_instructions;
+ foreach_iter(exec_list_iterator, iter, v.instructions) {
+ ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get();
+ mesa_inst->Opcode = inst->op;
+ mesa_inst->DstReg.File = inst->dst_reg.file;
+ mesa_inst->DstReg.Index = inst->dst_reg.index;
+ 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++;
+ }
+}