Path: blob/21.2-virgl/src/gallium/drivers/lima/ir/pp/node_to_instr.c
4574 views
/*1* Copyright (c) 2017 Lima Project2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sub license,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the11* next paragraph) shall be included in all copies or substantial portions12* of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER20* DEALINGS IN THE SOFTWARE.21*22*/2324#include "ppir.h"252627static bool create_new_instr(ppir_block *block, ppir_node *node)28{29ppir_instr *instr = ppir_instr_create(block);30if (unlikely(!instr))31return false;3233if (!ppir_instr_insert_node(instr, node))34return false;3536return true;37}3839/*40* If a node has a pipeline dest, schedule it in the same instruction as its41* successor.42* Since it has a pipeline dest, it must have only one successor and since we43* schedule nodes backwards, its successor must have already been scheduled.44* Load varyings can't output to a pipeline register but are also potentially45* trivial to insert and save an instruction if they have a single successor.46*/47static bool ppir_do_node_to_instr_try_insert(ppir_block *block, ppir_node *node)48{49ppir_dest *dest = ppir_node_get_dest(node);5051if (dest && dest->type == ppir_target_pipeline) {52assert(ppir_node_has_single_src_succ(node));53ppir_node *succ = ppir_node_first_succ(node);54assert(succ);55assert(succ->instr);5657return ppir_instr_insert_node(succ->instr, node);58}5960switch (node->type) {61case ppir_node_type_load:62break;63default:64return false;65}6667if (!ppir_node_has_single_src_succ(node))68return false;6970ppir_node *succ = ppir_node_first_succ(node);71assert(succ);72assert(succ->instr);7374return ppir_instr_insert_node(succ->instr, node);75}7677static bool ppir_do_one_node_to_instr(ppir_block *block, ppir_node *node)78{79switch (node->type) {80case ppir_node_type_alu:81{82/* don't create an instr for undef node */83if (node->op == ppir_op_undef)84break;8586/* merge pred mul and succ add in the same instr can save a reg87* by using pipeline reg ^vmul/^fmul */88ppir_alu_node *alu = ppir_node_to_alu(node);89if (alu->dest.type == ppir_target_ssa &&90ppir_node_has_single_succ(node)) {91ppir_node *succ = ppir_node_first_succ(node);92if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_VEC_ADD) {93node->instr_pos = PPIR_INSTR_SLOT_ALU_VEC_MUL;94ppir_instr_insert_mul_node(succ, node);95}96else if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_SCL_ADD &&97alu->dest.ssa.num_components == 1) {98node->instr_pos = PPIR_INSTR_SLOT_ALU_SCL_MUL;99ppir_instr_insert_mul_node(succ, node);100}101}102103/* can't inserted to any existing instr, create one */104if (!node->instr && !create_new_instr(block, node))105return false;106107break;108}109case ppir_node_type_load:110case ppir_node_type_load_texture:111{112if (!create_new_instr(block, node))113return false;114115/* load varying output can be a register, it doesn't need a mov */116switch (node->op) {117case ppir_op_load_varying:118case ppir_op_load_coords:119case ppir_op_load_coords_reg:120case ppir_op_load_fragcoord:121case ppir_op_load_pointcoord:122case ppir_op_load_frontface:123return true;124default:125break;126}127128/* Load cannot be pipelined, likely slot is already taken. Create a mov */129assert(ppir_node_has_single_src_succ(node));130ppir_dest *dest = ppir_node_get_dest(node);131assert(dest->type == ppir_target_pipeline);132ppir_pipeline pipeline_reg = dest->pipeline;133134/* Turn dest back to SSA, so we can update predecessors */135ppir_node *succ = ppir_node_first_succ(node);136137/* Single succ can still have multiple references to this node */138for (int i = 0; i < ppir_node_get_src_num(succ); i++) {139ppir_src *src = ppir_node_get_src(succ, i);140if (src && src->node == node) {141/* Can consume uniforms directly */142dest->type = ppir_target_ssa;143dest->ssa.index = -1;144ppir_node_target_assign(src, node);145}146}147148ppir_node *move = ppir_node_insert_mov(node);149if (unlikely(!move))150return false;151152ppir_src *mov_src = ppir_node_get_src(move, 0);153mov_src->type = dest->type = ppir_target_pipeline;154mov_src->pipeline = dest->pipeline = pipeline_reg;155156ppir_debug("node_to_instr create move %d for load %d\n",157move->index, node->index);158159if (!ppir_instr_insert_node(node->instr, move))160return false;161162break;163}164case ppir_node_type_const: {165/* Const cannot be pipelined, too many consts in the instruction.166* Create a mov. */167168ppir_node *move = ppir_node_insert_mov(node);169if (!create_new_instr(block, move))170return false;171172ppir_debug("node_to_instr create move %d for const %d\n",173move->index, node->index);174175ppir_dest *dest = ppir_node_get_dest(node);176ppir_src *mov_src = ppir_node_get_src(move, 0);177178/* update succ from ^const to ssa mov output */179ppir_dest *move_dest = ppir_node_get_dest(move);180move_dest->type = ppir_target_ssa;181ppir_node *succ = ppir_node_first_succ(move);182ppir_node_replace_child(succ, node, move);183184mov_src->type = dest->type = ppir_target_pipeline;185mov_src->pipeline = dest->pipeline = ppir_pipeline_reg_const0;186187if (!ppir_instr_insert_node(move->instr, node))188return false;189190break;191}192case ppir_node_type_store:193{194if (node->op == ppir_op_store_temp) {195if (!create_new_instr(block, node))196return false;197break;198}199break;200}201case ppir_node_type_discard:202if (!create_new_instr(block, node))203return false;204node->instr->is_end = true;205break;206case ppir_node_type_branch:207if (!create_new_instr(block, node))208return false;209break;210default:211return false;212}213214return true;215}216217static unsigned int ppir_node_score(ppir_node *node)218{219/* preferentially expand nodes in later instruction slots first, so220* nodes for earlier slots (which are more likely pipelineable) get221* added to the ready list. */222unsigned int late_slot = 0;223int *slots = ppir_op_infos[node->op].slots;224if (slots)225for (int i = 0; slots[i] != PPIR_INSTR_SLOT_END; i++)226late_slot = MAX2(late_slot, slots[i]);227228/* to untie, favour nodes with pipelines for earlier expansion.229* increase that for nodes with chained pipelines */230unsigned int pipeline = 0;231ppir_node *n = node;232ppir_dest *dest = ppir_node_get_dest(n);233while (dest && dest->type == ppir_target_pipeline) {234pipeline++;235assert(ppir_node_has_single_src_succ(n));236n = ppir_node_first_succ(n);237dest = ppir_node_get_dest(n);238}239assert(pipeline < 4);240241return (late_slot << 2 | pipeline);242}243244static ppir_node *ppir_ready_list_pick_best(ppir_block *block,245struct list_head *ready_list)246{247unsigned int best_score = 0;248ppir_node *best = NULL;249250list_for_each_entry(ppir_node, node, ready_list, sched_list) {251unsigned int score = ppir_node_score(node);252if (!best || score > best_score) {253best = node;254best_score = score;255}256}257258assert(best);259return best;260}261262static bool ppir_do_node_to_instr(ppir_block *block, ppir_node *root)263{264struct list_head ready_list;265list_inithead(&ready_list);266list_addtail(&root->sched_list, &ready_list);267268while (!list_is_empty(&ready_list)) {269ppir_node *node = ppir_ready_list_pick_best(block, &ready_list);270list_del(&node->sched_list);271272/* first try pipeline sched, if that didn't succeed try normal sched */273if (!ppir_do_node_to_instr_try_insert(block, node))274if (!ppir_do_one_node_to_instr(block, node))275return false;276277if (node->is_end)278node->instr->is_end = true;279280ppir_node_foreach_pred(node, dep) {281ppir_node *pred = dep->pred;282bool ready = true;283284/* pred may already have been processed by a previous node */285if (pred->instr)286continue;287288/* insert pred only when all its successors have been inserted */289ppir_node_foreach_succ(pred, dep) {290ppir_node *succ = dep->succ;291if (!succ->instr) {292ready = false;293break;294}295}296297if (ready)298list_addtail(&pred->sched_list, &ready_list);299}300}301302return true;303}304305static bool ppir_create_instr_from_node(ppir_compiler *comp)306{307list_for_each_entry(ppir_block, block, &comp->block_list, list) {308list_for_each_entry(ppir_node, node, &block->node_list, list) {309if (ppir_node_is_root(node)) {310if (!ppir_do_node_to_instr(block, node))311return false;312}313}314}315316return true;317}318319static void ppir_build_instr_dependency(ppir_compiler *comp)320{321list_for_each_entry(ppir_block, block, &comp->block_list, list) {322list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {323for (int i = 0; i < PPIR_INSTR_SLOT_NUM; i++) {324ppir_node *node = instr->slots[i];325if (node) {326ppir_node_foreach_pred(node, dep) {327ppir_node *pred = dep->pred;328if (pred->instr && pred->instr != instr)329ppir_instr_add_dep(instr, pred->instr);330}331}332}333}334}335}336337bool ppir_node_to_instr(ppir_compiler *comp)338{339if (!ppir_create_instr_from_node(comp))340return false;341ppir_instr_print_list(comp);342343ppir_build_instr_dependency(comp);344ppir_instr_print_dep(comp);345346return true;347}348349350