
Signed-off-by: Karol Herbst <kherbst@redhat.com> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net> Reviewed-by: Qiang Yu <yuq825@gmail.com>
420 lines
11 KiB
C
420 lines
11 KiB
C
/*
|
|
* Copyright (c) 2017 Lima Project
|
|
*
|
|
* 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, sub license,
|
|
* 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 NON-INFRINGEMENT. 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.
|
|
*
|
|
*/
|
|
|
|
#include "util/ralloc.h"
|
|
#include "compiler/nir/nir.h"
|
|
|
|
#include "gpir.h"
|
|
#include "lima_context.h"
|
|
|
|
|
|
static inline void *gpir_node_create_ssa(gpir_block *block, gpir_op op, nir_ssa_def *ssa)
|
|
{
|
|
int index = ssa->index;
|
|
gpir_node *node = gpir_node_create(block, op);
|
|
|
|
block->comp->var_nodes[index] = node;
|
|
snprintf(node->name, sizeof(node->name), "ssa%d", index);
|
|
list_addtail(&node->list, &block->node_list);
|
|
return node;
|
|
}
|
|
|
|
static inline void *gpir_node_create_reg(gpir_block *block, gpir_op op, nir_reg_dest *reg)
|
|
{
|
|
int index = reg->reg->index;
|
|
gpir_node *node = gpir_node_create(block, op);
|
|
gpir_store_node *store = gpir_node_create(block, gpir_op_store_reg);
|
|
|
|
snprintf(node->name, sizeof(node->name), "reg%d", index);
|
|
|
|
store->child = node;
|
|
gpir_node_add_dep(&store->node, node, GPIR_DEP_INPUT);
|
|
|
|
list_for_each_entry(gpir_reg, reg, &block->comp->reg_list, list) {
|
|
if (reg->index == index) {
|
|
store->reg = reg;
|
|
list_addtail(&store->reg_link, ®->defs_list);
|
|
break;
|
|
}
|
|
}
|
|
|
|
list_addtail(&node->list, &block->node_list);
|
|
list_addtail(&store->node.list, &block->node_list);
|
|
return node;
|
|
}
|
|
|
|
static void *gpir_node_create_dest(gpir_block *block, gpir_op op, nir_dest *dest)
|
|
{
|
|
if (dest->is_ssa)
|
|
return gpir_node_create_ssa(block, op, &dest->ssa);
|
|
else
|
|
return gpir_node_create_reg(block, op, &dest->reg);
|
|
}
|
|
|
|
static gpir_node *gpir_node_find(gpir_block *block, gpir_node *succ, nir_src *src)
|
|
{
|
|
gpir_node *pred;
|
|
|
|
if (src->is_ssa) {
|
|
pred = block->comp->var_nodes[src->ssa->index];
|
|
assert(pred);
|
|
}
|
|
else {
|
|
pred = gpir_node_create(block, gpir_op_load_reg);
|
|
list_addtail(&pred->list, &succ->list);
|
|
|
|
gpir_load_node *load = gpir_node_to_load(pred);
|
|
list_for_each_entry(gpir_reg, reg, &block->comp->reg_list, list) {
|
|
if (reg->index == src->reg.reg->index) {
|
|
load->reg = reg;
|
|
list_addtail(&load->reg_link, ®->uses_list);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pred;
|
|
}
|
|
|
|
static int nir_to_gpir_opcodes[nir_num_opcodes] = {
|
|
/* not supported */
|
|
[0 ... nir_last_opcode] = -1,
|
|
|
|
[nir_op_fmul] = gpir_op_mul,
|
|
[nir_op_fadd] = gpir_op_add,
|
|
[nir_op_fneg] = gpir_op_neg,
|
|
[nir_op_fnot] = gpir_op_not,
|
|
[nir_op_fmin] = gpir_op_min,
|
|
[nir_op_fmax] = gpir_op_max,
|
|
[nir_op_frcp] = gpir_op_rcp,
|
|
[nir_op_frsq] = gpir_op_rsqrt,
|
|
[nir_op_slt] = gpir_op_lt,
|
|
[nir_op_sge] = gpir_op_ge,
|
|
[nir_op_fcsel] = gpir_op_select,
|
|
[nir_op_ffloor] = gpir_op_floor,
|
|
[nir_op_fsign] = gpir_op_sign,
|
|
[nir_op_seq] = gpir_op_eq,
|
|
[nir_op_sne] = gpir_op_ne,
|
|
[nir_op_fand] = gpir_op_min,
|
|
[nir_op_for] = gpir_op_max,
|
|
[nir_op_fabs] = gpir_op_abs,
|
|
};
|
|
|
|
static bool gpir_emit_alu(gpir_block *block, nir_instr *ni)
|
|
{
|
|
nir_alu_instr *instr = nir_instr_as_alu(ni);
|
|
int op = nir_to_gpir_opcodes[instr->op];
|
|
|
|
if (op < 0) {
|
|
gpir_error("unsupported nir_op: %s\n", nir_op_infos[instr->op].name);
|
|
return false;
|
|
}
|
|
|
|
gpir_alu_node *node = gpir_node_create_dest(block, op, &instr->dest.dest);
|
|
if (unlikely(!node))
|
|
return false;
|
|
|
|
unsigned num_child = nir_op_infos[instr->op].num_inputs;
|
|
assert(num_child <= ARRAY_SIZE(node->children));
|
|
node->num_child = num_child;
|
|
|
|
for (int i = 0; i < num_child; i++) {
|
|
nir_alu_src *src = instr->src + i;
|
|
node->children_negate[i] = src->negate;
|
|
|
|
gpir_node *child = gpir_node_find(block, &node->node, &src->src);
|
|
node->children[i] = child;
|
|
|
|
gpir_node_add_dep(&node->node, child, GPIR_DEP_INPUT);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool gpir_emit_intrinsic(gpir_block *block, nir_instr *ni)
|
|
{
|
|
nir_intrinsic_instr *instr = nir_instr_as_intrinsic(ni);
|
|
|
|
switch (instr->intrinsic) {
|
|
case nir_intrinsic_load_input:
|
|
{
|
|
gpir_load_node *load =
|
|
gpir_node_create_dest(block, gpir_op_load_attribute, &instr->dest);
|
|
if (unlikely(!load))
|
|
return false;
|
|
|
|
load->index = nir_intrinsic_base(instr);
|
|
load->component = nir_intrinsic_component(instr);
|
|
|
|
return true;
|
|
}
|
|
case nir_intrinsic_load_uniform:
|
|
{
|
|
gpir_load_node *load =
|
|
gpir_node_create_dest(block, gpir_op_load_uniform, &instr->dest);
|
|
if (unlikely(!load))
|
|
return false;
|
|
|
|
int offset = nir_intrinsic_base(instr);
|
|
offset += (int)nir_src_as_float(instr->src[0]);
|
|
|
|
load->index = offset / 4;
|
|
load->component = offset % 4;
|
|
|
|
return true;
|
|
}
|
|
case nir_intrinsic_store_output:
|
|
{
|
|
gpir_store_node *store = gpir_node_create(block, gpir_op_store_varying);
|
|
if (unlikely(!store))
|
|
return false;
|
|
list_addtail(&store->node.list, &block->node_list);
|
|
|
|
store->index = nir_intrinsic_base(instr);
|
|
store->component = nir_intrinsic_component(instr);
|
|
|
|
gpir_node *child = gpir_node_find(block, &store->node, instr->src);
|
|
store->child = child;
|
|
gpir_node_add_dep(&store->node, child, GPIR_DEP_INPUT);
|
|
|
|
return true;
|
|
}
|
|
default:
|
|
gpir_error("unsupported nir_intrinsic_instr %d\n", instr->intrinsic);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool gpir_emit_load_const(gpir_block *block, nir_instr *ni)
|
|
{
|
|
nir_load_const_instr *instr = nir_instr_as_load_const(ni);
|
|
gpir_const_node *node =
|
|
gpir_node_create_ssa(block, gpir_op_const, &instr->def);
|
|
if (unlikely(!node))
|
|
return false;
|
|
|
|
assert(instr->def.bit_size == 32);
|
|
assert(instr->def.num_components == 1);
|
|
|
|
node->value.i = instr->value.i32[0];
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool gpir_emit_ssa_undef(gpir_block *block, nir_instr *ni)
|
|
{
|
|
gpir_error("nir_ssa_undef_instr not support\n");
|
|
return false;
|
|
}
|
|
|
|
static bool gpir_emit_tex(gpir_block *block, nir_instr *ni)
|
|
{
|
|
gpir_error("nir_jump_instr not support\n");
|
|
return false;
|
|
}
|
|
|
|
static bool gpir_emit_jump(gpir_block *block, nir_instr *ni)
|
|
{
|
|
gpir_error("nir_jump_instr not support\n");
|
|
return false;
|
|
}
|
|
|
|
static bool (*gpir_emit_instr[nir_instr_type_phi])(gpir_block *, nir_instr *) = {
|
|
[nir_instr_type_alu] = gpir_emit_alu,
|
|
[nir_instr_type_intrinsic] = gpir_emit_intrinsic,
|
|
[nir_instr_type_load_const] = gpir_emit_load_const,
|
|
[nir_instr_type_ssa_undef] = gpir_emit_ssa_undef,
|
|
[nir_instr_type_tex] = gpir_emit_tex,
|
|
[nir_instr_type_jump] = gpir_emit_jump,
|
|
};
|
|
|
|
static gpir_block *gpir_block_create(gpir_compiler *comp)
|
|
{
|
|
gpir_block *block = ralloc(comp, gpir_block);
|
|
if (!block)
|
|
return NULL;
|
|
|
|
list_inithead(&block->node_list);
|
|
list_inithead(&block->instr_list);
|
|
|
|
return block;
|
|
}
|
|
|
|
static bool gpir_emit_block(gpir_compiler *comp, nir_block *nblock)
|
|
{
|
|
gpir_block *block = gpir_block_create(comp);
|
|
if (!block)
|
|
return false;
|
|
|
|
list_addtail(&block->list, &comp->block_list);
|
|
block->comp = comp;
|
|
|
|
nir_foreach_instr(instr, nblock) {
|
|
assert(instr->type < nir_instr_type_phi);
|
|
if (!gpir_emit_instr[instr->type](block, instr))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool gpir_emit_if(gpir_compiler *comp, nir_if *nif)
|
|
{
|
|
gpir_error("if nir_cf_node not support\n");
|
|
return false;
|
|
}
|
|
|
|
static bool gpir_emit_loop(gpir_compiler *comp, nir_loop *nloop)
|
|
{
|
|
gpir_error("loop nir_cf_node not support\n");
|
|
return false;
|
|
}
|
|
|
|
static bool gpir_emit_function(gpir_compiler *comp, nir_function_impl *nfunc)
|
|
{
|
|
gpir_error("function nir_cf_node not support\n");
|
|
return false;
|
|
}
|
|
|
|
static bool gpir_emit_cf_list(gpir_compiler *comp, struct exec_list *list)
|
|
{
|
|
foreach_list_typed(nir_cf_node, node, node, list) {
|
|
bool ret;
|
|
|
|
switch (node->type) {
|
|
case nir_cf_node_block:
|
|
ret = gpir_emit_block(comp, nir_cf_node_as_block(node));
|
|
break;
|
|
case nir_cf_node_if:
|
|
ret = gpir_emit_if(comp, nir_cf_node_as_if(node));
|
|
break;
|
|
case nir_cf_node_loop:
|
|
ret = gpir_emit_loop(comp, nir_cf_node_as_loop(node));
|
|
break;
|
|
case nir_cf_node_function:
|
|
ret = gpir_emit_function(comp, nir_cf_node_as_function(node));
|
|
break;
|
|
default:
|
|
gpir_error("unknown NIR node type %d\n", node->type);
|
|
return false;
|
|
}
|
|
|
|
if (!ret)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
gpir_reg *gpir_create_reg(gpir_compiler *comp)
|
|
{
|
|
gpir_reg *reg = ralloc(comp, gpir_reg);
|
|
reg->index = comp->cur_reg++;
|
|
list_addtail(®->list, &comp->reg_list);
|
|
list_inithead(®->defs_list);
|
|
list_inithead(®->uses_list);
|
|
return reg;
|
|
}
|
|
|
|
static gpir_compiler *gpir_compiler_create(void *prog, unsigned num_reg, unsigned num_ssa)
|
|
{
|
|
gpir_compiler *comp = rzalloc(prog, gpir_compiler);
|
|
|
|
list_inithead(&comp->block_list);
|
|
list_inithead(&comp->reg_list);
|
|
|
|
for (int i = 0; i < num_reg; i++)
|
|
gpir_create_reg(comp);
|
|
|
|
comp->var_nodes = rzalloc_array(comp, gpir_node *, num_ssa);
|
|
comp->prog = prog;
|
|
return comp;
|
|
}
|
|
|
|
static int gpir_glsl_type_size(enum glsl_base_type type)
|
|
{
|
|
/* only support GLSL_TYPE_FLOAT */
|
|
assert(type == GLSL_TYPE_FLOAT);
|
|
return 4;
|
|
}
|
|
|
|
bool gpir_compile_nir(struct lima_vs_shader_state *prog, struct nir_shader *nir)
|
|
{
|
|
nir_function_impl *func = nir_shader_get_entrypoint(nir);
|
|
gpir_compiler *comp = gpir_compiler_create(prog, func->reg_alloc, func->ssa_alloc);
|
|
if (!comp)
|
|
return false;
|
|
|
|
comp->constant_base = nir->num_uniforms;
|
|
prog->uniform_pending_offset = nir->num_uniforms * 16;
|
|
|
|
if (!gpir_emit_cf_list(comp, &func->body))
|
|
goto err_out0;
|
|
|
|
gpir_node_print_prog_seq(comp);
|
|
gpir_node_print_prog_dep(comp);
|
|
|
|
if (!gpir_pre_rsched_lower_prog(comp))
|
|
goto err_out0;
|
|
|
|
if (!gpir_reduce_reg_pressure_schedule_prog(comp))
|
|
goto err_out0;
|
|
|
|
if (!gpir_post_rsched_lower_prog(comp))
|
|
goto err_out0;
|
|
|
|
if (!gpir_value_regalloc_prog(comp))
|
|
goto err_out0;
|
|
|
|
if (!gpir_physical_regalloc_prog(comp))
|
|
goto err_out0;
|
|
|
|
if (!gpir_schedule_prog(comp))
|
|
goto err_out0;
|
|
|
|
if (!gpir_codegen_prog(comp))
|
|
goto err_out0;
|
|
|
|
nir_foreach_variable(var, &nir->outputs) {
|
|
if (var->data.location == VARYING_SLOT_POS)
|
|
assert(var->data.driver_location == 0);
|
|
|
|
struct lima_varying_info *v = prog->varying + var->data.driver_location;
|
|
if (!v->components) {
|
|
v->component_size = gpir_glsl_type_size(glsl_get_base_type(var->type));
|
|
prog->num_varying++;
|
|
}
|
|
|
|
v->components += glsl_get_components(var->type);
|
|
}
|
|
|
|
ralloc_free(comp);
|
|
return true;
|
|
|
|
err_out0:
|
|
ralloc_free(comp);
|
|
return false;
|
|
}
|
|
|