
Ideally this would be hooked up by ir_print_visitor dumping into a string that we could include as prog_instruction->Comment when in debug mode, and not try keeping ir_instruction trees around after conversion to Mesa. The ir_print_visitor isn't set up to do that for us today.
555 lines
14 KiB
C++
555 lines
14 KiB
C++
/*
|
|
* 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"
|
|
|
|
extern "C" {
|
|
#include "shader/prog_instruction.h"
|
|
#include "shader/prog_print.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;
|
|
inst->ir = tree->ir;
|
|
|
|
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,
|
|
ir_instruction *ir,
|
|
struct mbtree *left, struct mbtree *right)
|
|
{
|
|
struct mbtree *tree = (struct mbtree *)calloc(sizeof(struct mbtree), 1);
|
|
|
|
assert(ir);
|
|
|
|
tree->op = op;
|
|
tree->left = left;
|
|
tree->right = right;
|
|
tree->v = this;
|
|
tree->src_reg.swizzle = SWIZZLE_XYZW;
|
|
tree->ir = ir;
|
|
|
|
return tree;
|
|
}
|
|
|
|
/**
|
|
* 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, ir, op[0], op[1]);
|
|
break;
|
|
case ir_binop_sub:
|
|
this->result = this->create_tree(MB_TERM_sub_vec4_vec4, ir, op[0], op[1]);
|
|
break;
|
|
case ir_binop_mul:
|
|
this->result = this->create_tree(MB_TERM_mul_vec4_vec4, ir, op[0], op[1]);
|
|
break;
|
|
case ir_binop_div:
|
|
this->result = this->create_tree(MB_TERM_div_vec4_vec4, ir, 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,
|
|
ir, 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,
|
|
ir, 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,
|
|
ir, op[0], op[1]);
|
|
}
|
|
break;
|
|
case ir_unop_sqrt:
|
|
this->result = this->create_tree(MB_TERM_sqrt_vec4, ir, 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, ir, 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_X, SWIZZLE_X, SWIZZLE_X),
|
|
MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y),
|
|
MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z),
|
|
MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W),
|
|
};
|
|
|
|
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, ir, NULL, NULL);
|
|
tree->src_reg.file = PROGRAM_INPUT;
|
|
tree->src_reg.index = FRAG_ATTRIB_TEX0 + index->value.i[0];
|
|
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, ir, 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, ir, 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;
|
|
assert(reg.index < (1 << INST_INDEX_BITS) - 1);
|
|
mesa_reg.Index = reg.index;
|
|
mesa_reg.Swizzle = reg.swizzle;
|
|
|
|
return mesa_reg;
|
|
}
|
|
|
|
void
|
|
do_ir_to_mesa(exec_list *instructions)
|
|
{
|
|
ir_to_mesa_visitor v;
|
|
struct prog_instruction *mesa_instructions, *mesa_inst;
|
|
ir_instruction *last_ir = NULL;
|
|
|
|
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();
|
|
|
|
if (last_ir != inst->ir) {
|
|
ir_print_visitor print;
|
|
inst->ir->accept(&print);
|
|
printf("\n");
|
|
last_ir = inst->ir;
|
|
}
|
|
|
|
mesa_inst->Opcode = inst->op;
|
|
mesa_inst->DstReg.File = inst->dst_reg.file;
|
|
mesa_inst->DstReg.Index = inst->dst_reg.index;
|
|
mesa_inst->DstReg.CondMask = COND_TR;
|
|
mesa_inst->DstReg.WriteMask = WRITEMASK_XYZW;
|
|
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_print_instruction(mesa_inst);
|
|
|
|
mesa_inst++;
|
|
}
|
|
}
|