summaryrefslogtreecommitdiff
path: root/ir_to_mesa.cpp
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2010-04-29 09:02:09 -0700
committerEric Anholt <eric@anholt.net>2010-06-24 15:05:19 -0700
commit84771df82ed2ed8718013795089edd38cf5bd84d (patch)
tree44ef28d26546dc7375a22c357f350acc40b77a69 /ir_to_mesa.cpp
parent9290e0dd28e646c3dc810e0a6405582f8bf643b6 (diff)
ir_to_mesa: Start building GLSL IR to Mesa IR conversion.
There are major missing pieces here. Most operations aren't supported. Matrices need to be broken down to vector ops before we get here. Scalar operations (RSQ, RCP) are handled incorrectly. Arrays and structures are not even considered.
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++;
+ }
+}