Path: blob/21.2-virgl/src/gallium/drivers/r300/compiler/radeon_emulate_branches.c
4574 views
/*1* Copyright 2009 Nicolai Hähnle <[email protected]>2*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* on the rights to use, copy, modify, merge, publish, distribute, sub7* license, and/or sell copies of the Software, and to permit persons to whom8* the Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* 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 AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,18* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR19* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE20* USE OR OTHER DEALINGS IN THE SOFTWARE. */2122#include "radeon_emulate_branches.h"2324#include <stdio.h>2526#include "radeon_compiler.h"27#include "radeon_dataflow.h"2829#define VERBOSE 03031#define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)323334struct proxy_info {35unsigned int Proxied:1;36unsigned int Index:RC_REGISTER_INDEX_BITS;37};3839struct register_proxies {40struct proxy_info Temporary[RC_REGISTER_MAX_INDEX];41};4243struct branch_info {44struct rc_instruction * If;45struct rc_instruction * Else;46};4748struct emulate_branch_state {49struct radeon_compiler * C;5051struct branch_info * Branches;52unsigned int BranchCount;53unsigned int BranchReserved;54};555657static void handle_if(struct emulate_branch_state * s, struct rc_instruction * inst)58{59struct branch_info * branch;60struct rc_instruction * inst_mov;6162memory_pool_array_reserve(&s->C->Pool, struct branch_info,63s->Branches, s->BranchCount, s->BranchReserved, 1);6465DBG("%s\n", __FUNCTION__);6667branch = &s->Branches[s->BranchCount++];68memset(branch, 0, sizeof(struct branch_info));69branch->If = inst;7071/* Make a safety copy of the decision register, because we will need72* it at ENDIF time and it might be overwritten in both branches. */73inst_mov = rc_insert_new_instruction(s->C, inst->Prev);74inst_mov->U.I.Opcode = RC_OPCODE_MOV;75inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;76inst_mov->U.I.DstReg.Index = rc_find_free_temporary(s->C);77inst_mov->U.I.DstReg.WriteMask = RC_MASK_X;78inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];7980inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;81inst->U.I.SrcReg[0].Index = inst_mov->U.I.DstReg.Index;82inst->U.I.SrcReg[0].Swizzle = 0;83inst->U.I.SrcReg[0].Abs = 0;84inst->U.I.SrcReg[0].Negate = 0;85}8687static void handle_else(struct emulate_branch_state * s, struct rc_instruction * inst)88{89struct branch_info * branch;9091if (!s->BranchCount) {92rc_error(s->C, "Encountered ELSE outside of branches");93return;94}9596DBG("%s\n", __FUNCTION__);9798branch = &s->Branches[s->BranchCount - 1];99branch->Else = inst;100}101102103struct state_and_proxies {104struct emulate_branch_state * S;105struct register_proxies * Proxies;106};107108static struct proxy_info * get_proxy_info(struct state_and_proxies * sap,109rc_register_file file, unsigned int index)110{111if (file == RC_FILE_TEMPORARY) {112return &sap->Proxies->Temporary[index];113} else {114return 0;115}116}117118static void scan_write(void * userdata, struct rc_instruction * inst,119rc_register_file file, unsigned int index, unsigned int comp)120{121struct state_and_proxies * sap = userdata;122struct proxy_info * proxy = get_proxy_info(sap, file, index);123124if (proxy && !proxy->Proxied) {125proxy->Proxied = 1;126proxy->Index = rc_find_free_temporary(sap->S->C);127}128}129130static void remap_proxy_function(void * userdata, struct rc_instruction * inst,131rc_register_file * pfile, unsigned int * pindex)132{133struct state_and_proxies * sap = userdata;134struct proxy_info * proxy = get_proxy_info(sap, *pfile, *pindex);135136if (proxy && proxy->Proxied) {137*pfile = RC_FILE_TEMPORARY;138*pindex = proxy->Index;139}140}141142/**143* Redirect all writes in the instruction range [begin, end) to proxy144* temporary registers.145*/146static void allocate_and_insert_proxies(struct emulate_branch_state * s,147struct register_proxies * proxies,148struct rc_instruction * begin,149struct rc_instruction * end)150{151struct state_and_proxies sap;152153sap.S = s;154sap.Proxies = proxies;155156for(struct rc_instruction * inst = begin; inst != end; inst = inst->Next) {157rc_for_all_writes_mask(inst, scan_write, &sap);158rc_remap_registers(inst, remap_proxy_function, &sap);159}160161for(unsigned int index = 0; index < RC_REGISTER_MAX_INDEX; ++index) {162if (proxies->Temporary[index].Proxied) {163struct rc_instruction * inst_mov = rc_insert_new_instruction(s->C, begin->Prev);164inst_mov->U.I.Opcode = RC_OPCODE_MOV;165inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;166inst_mov->U.I.DstReg.Index = proxies->Temporary[index].Index;167inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZW;168inst_mov->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;169inst_mov->U.I.SrcReg[0].Index = index;170}171}172}173174175static void inject_cmp(struct emulate_branch_state * s,176struct rc_instruction * inst_if,177struct rc_instruction * inst_endif,178rc_register_file file, unsigned int index,179struct proxy_info ifproxy,180struct proxy_info elseproxy)181{182struct rc_instruction * inst_cmp = rc_insert_new_instruction(s->C, inst_endif);183inst_cmp->U.I.Opcode = RC_OPCODE_CMP;184inst_cmp->U.I.DstReg.File = file;185inst_cmp->U.I.DstReg.Index = index;186inst_cmp->U.I.DstReg.WriteMask = RC_MASK_XYZW;187inst_cmp->U.I.SrcReg[0] = inst_if->U.I.SrcReg[0];188inst_cmp->U.I.SrcReg[0].Abs = 1;189inst_cmp->U.I.SrcReg[0].Negate = RC_MASK_XYZW;190inst_cmp->U.I.SrcReg[1].File = RC_FILE_TEMPORARY;191inst_cmp->U.I.SrcReg[1].Index = ifproxy.Proxied ? ifproxy.Index : index;192inst_cmp->U.I.SrcReg[2].File = RC_FILE_TEMPORARY;193inst_cmp->U.I.SrcReg[2].Index = elseproxy.Proxied ? elseproxy.Index : index;194}195196static void handle_endif(struct emulate_branch_state * s, struct rc_instruction * inst)197{198struct branch_info * branch;199struct register_proxies IfProxies;200struct register_proxies ElseProxies;201202if (!s->BranchCount) {203rc_error(s->C, "Encountered ENDIF outside of branches");204return;205}206207DBG("%s\n", __FUNCTION__);208209branch = &s->Branches[s->BranchCount - 1];210211memset(&IfProxies, 0, sizeof(IfProxies));212memset(&ElseProxies, 0, sizeof(ElseProxies));213214allocate_and_insert_proxies(s, &IfProxies, branch->If->Next, branch->Else ? branch->Else : inst);215216if (branch->Else)217allocate_and_insert_proxies(s, &ElseProxies, branch->Else->Next, inst);218219/* Insert the CMP instructions at the end. */220for(unsigned int index = 0; index < RC_REGISTER_MAX_INDEX; ++index) {221if (IfProxies.Temporary[index].Proxied || ElseProxies.Temporary[index].Proxied) {222inject_cmp(s, branch->If, inst, RC_FILE_TEMPORARY, index,223IfProxies.Temporary[index], ElseProxies.Temporary[index]);224}225}226227/* Remove all traces of the branch instructions */228rc_remove_instruction(branch->If);229if (branch->Else)230rc_remove_instruction(branch->Else);231rc_remove_instruction(inst);232233s->BranchCount--;234235if (VERBOSE) {236DBG("Program after ENDIF handling:\n");237rc_print_program(&s->C->Program);238}239}240241242struct remap_output_data {243unsigned int Output:RC_REGISTER_INDEX_BITS;244unsigned int Temporary:RC_REGISTER_INDEX_BITS;245};246247static void remap_output_function(void * userdata, struct rc_instruction * inst,248rc_register_file * pfile, unsigned int * pindex)249{250struct remap_output_data * data = userdata;251252if (*pfile == RC_FILE_OUTPUT && *pindex == data->Output) {253*pfile = RC_FILE_TEMPORARY;254*pindex = data->Temporary;255}256}257258259/**260* Output registers cannot be read from and so cannot be dealt with like261* temporary registers.262*263* We do the simplest thing: If an output registers is written within264* a branch, then *all* writes to this register are proxied to a265* temporary register, and a final MOV is appended to the end of266* the program.267*/268static void fix_output_writes(struct emulate_branch_state * s, struct rc_instruction * inst)269{270const struct rc_opcode_info * opcode;271272if (!s->BranchCount)273return;274275opcode = rc_get_opcode_info(inst->U.I.Opcode);276277if (!opcode->HasDstReg)278return;279280if (inst->U.I.DstReg.File == RC_FILE_OUTPUT) {281struct remap_output_data remap;282struct rc_instruction * inst_mov;283284remap.Output = inst->U.I.DstReg.Index;285remap.Temporary = rc_find_free_temporary(s->C);286287for(struct rc_instruction * inst = s->C->Program.Instructions.Next;288inst != &s->C->Program.Instructions;289inst = inst->Next) {290rc_remap_registers(inst, &remap_output_function, &remap);291}292293inst_mov = rc_insert_new_instruction(s->C, s->C->Program.Instructions.Prev);294inst_mov->U.I.Opcode = RC_OPCODE_MOV;295inst_mov->U.I.DstReg.File = RC_FILE_OUTPUT;296inst_mov->U.I.DstReg.Index = remap.Output;297inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZW;298inst_mov->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;299inst_mov->U.I.SrcReg[0].Index = remap.Temporary;300}301}302303/**304* Remove branch instructions; instead, execute both branches305* on different register sets and choose between their results306* using CMP instructions in place of the original ENDIF.307*/308void rc_emulate_branches(struct radeon_compiler *c, void *user)309{310struct emulate_branch_state s;311struct rc_instruction * ptr;312313memset(&s, 0, sizeof(s));314s.C = c;315316/* Untypical loop because we may remove the current instruction */317ptr = c->Program.Instructions.Next;318while(ptr != &c->Program.Instructions) {319struct rc_instruction * inst = ptr;320ptr = ptr->Next;321322if (inst->Type == RC_INSTRUCTION_NORMAL) {323switch(inst->U.I.Opcode) {324case RC_OPCODE_IF:325handle_if(&s, inst);326break;327case RC_OPCODE_ELSE:328handle_else(&s, inst);329break;330case RC_OPCODE_ENDIF:331handle_endif(&s, inst);332break;333default:334fix_output_writes(&s, inst);335break;336}337} else {338rc_error(c, "%s: unhandled instruction type\n", __FUNCTION__);339}340}341}342343344