| /* |
| * Copyright 2009 Nicolai Hähnle <nhaehnle@gmail.com> |
| * |
| * 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 |
| * on 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 AUTHOR(S) AND/OR THEIR SUPPLIERS 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 "radeon_emulate_branches.h" |
| |
| #include <stdio.h> |
| |
| #include "radeon_compiler.h" |
| #include "radeon_dataflow.h" |
| |
| #define VERBOSE 0 |
| |
| #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0) |
| |
| |
| struct proxy_info { |
| unsigned int Proxied:1; |
| unsigned int Index:RC_REGISTER_INDEX_BITS; |
| }; |
| |
| struct register_proxies { |
| struct proxy_info Temporary[RC_REGISTER_MAX_INDEX]; |
| }; |
| |
| struct branch_info { |
| struct rc_instruction * If; |
| struct rc_instruction * Else; |
| }; |
| |
| struct emulate_branch_state { |
| struct radeon_compiler * C; |
| |
| struct branch_info * Branches; |
| unsigned int BranchCount; |
| unsigned int BranchReserved; |
| }; |
| |
| |
| static void handle_if(struct emulate_branch_state * s, struct rc_instruction * inst) |
| { |
| struct branch_info * branch; |
| struct rc_instruction * inst_mov; |
| |
| memory_pool_array_reserve(&s->C->Pool, struct branch_info, |
| s->Branches, s->BranchCount, s->BranchReserved, 1); |
| |
| DBG("%s\n", __FUNCTION__); |
| |
| branch = &s->Branches[s->BranchCount++]; |
| memset(branch, 0, sizeof(struct branch_info)); |
| branch->If = inst; |
| |
| /* Make a safety copy of the decision register, because we will need |
| * it at ENDIF time and it might be overwritten in both branches. */ |
| inst_mov = rc_insert_new_instruction(s->C, inst->Prev); |
| inst_mov->U.I.Opcode = RC_OPCODE_MOV; |
| inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY; |
| inst_mov->U.I.DstReg.Index = rc_find_free_temporary(s->C); |
| inst_mov->U.I.DstReg.WriteMask = RC_MASK_X; |
| inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0]; |
| |
| inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY; |
| inst->U.I.SrcReg[0].Index = inst_mov->U.I.DstReg.Index; |
| inst->U.I.SrcReg[0].Swizzle = 0; |
| inst->U.I.SrcReg[0].Abs = 0; |
| inst->U.I.SrcReg[0].Negate = 0; |
| } |
| |
| static void handle_else(struct emulate_branch_state * s, struct rc_instruction * inst) |
| { |
| struct branch_info * branch; |
| |
| if (!s->BranchCount) { |
| rc_error(s->C, "Encountered ELSE outside of branches"); |
| return; |
| } |
| |
| DBG("%s\n", __FUNCTION__); |
| |
| branch = &s->Branches[s->BranchCount - 1]; |
| branch->Else = inst; |
| } |
| |
| |
| struct state_and_proxies { |
| struct emulate_branch_state * S; |
| struct register_proxies * Proxies; |
| }; |
| |
| static struct proxy_info * get_proxy_info(struct state_and_proxies * sap, |
| rc_register_file file, unsigned int index) |
| { |
| if (file == RC_FILE_TEMPORARY) { |
| return &sap->Proxies->Temporary[index]; |
| } else { |
| return 0; |
| } |
| } |
| |
| static void scan_write(void * userdata, struct rc_instruction * inst, |
| rc_register_file file, unsigned int index, unsigned int comp) |
| { |
| struct state_and_proxies * sap = userdata; |
| struct proxy_info * proxy = get_proxy_info(sap, file, index); |
| |
| if (proxy && !proxy->Proxied) { |
| proxy->Proxied = 1; |
| proxy->Index = rc_find_free_temporary(sap->S->C); |
| } |
| } |
| |
| static void remap_proxy_function(void * userdata, struct rc_instruction * inst, |
| rc_register_file * pfile, unsigned int * pindex) |
| { |
| struct state_and_proxies * sap = userdata; |
| struct proxy_info * proxy = get_proxy_info(sap, *pfile, *pindex); |
| |
| if (proxy && proxy->Proxied) { |
| *pfile = RC_FILE_TEMPORARY; |
| *pindex = proxy->Index; |
| } |
| } |
| |
| /** |
| * Redirect all writes in the instruction range [begin, end) to proxy |
| * temporary registers. |
| */ |
| static void allocate_and_insert_proxies(struct emulate_branch_state * s, |
| struct register_proxies * proxies, |
| struct rc_instruction * begin, |
| struct rc_instruction * end) |
| { |
| struct state_and_proxies sap; |
| |
| sap.S = s; |
| sap.Proxies = proxies; |
| |
| for(struct rc_instruction * inst = begin; inst != end; inst = inst->Next) { |
| rc_for_all_writes_mask(inst, scan_write, &sap); |
| rc_remap_registers(inst, remap_proxy_function, &sap); |
| } |
| |
| for(unsigned int index = 0; index < RC_REGISTER_MAX_INDEX; ++index) { |
| if (proxies->Temporary[index].Proxied) { |
| struct rc_instruction * inst_mov = rc_insert_new_instruction(s->C, begin->Prev); |
| inst_mov->U.I.Opcode = RC_OPCODE_MOV; |
| inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY; |
| inst_mov->U.I.DstReg.Index = proxies->Temporary[index].Index; |
| inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZW; |
| inst_mov->U.I.SrcReg[0].File = RC_FILE_TEMPORARY; |
| inst_mov->U.I.SrcReg[0].Index = index; |
| } |
| } |
| } |
| |
| |
| static void inject_cmp(struct emulate_branch_state * s, |
| struct rc_instruction * inst_if, |
| struct rc_instruction * inst_endif, |
| rc_register_file file, unsigned int index, |
| struct proxy_info ifproxy, |
| struct proxy_info elseproxy) |
| { |
| struct rc_instruction * inst_cmp = rc_insert_new_instruction(s->C, inst_endif); |
| inst_cmp->U.I.Opcode = RC_OPCODE_CMP; |
| inst_cmp->U.I.DstReg.File = file; |
| inst_cmp->U.I.DstReg.Index = index; |
| inst_cmp->U.I.DstReg.WriteMask = RC_MASK_XYZW; |
| inst_cmp->U.I.SrcReg[0] = inst_if->U.I.SrcReg[0]; |
| inst_cmp->U.I.SrcReg[0].Abs = 1; |
| inst_cmp->U.I.SrcReg[0].Negate = RC_MASK_XYZW; |
| inst_cmp->U.I.SrcReg[1].File = RC_FILE_TEMPORARY; |
| inst_cmp->U.I.SrcReg[1].Index = ifproxy.Proxied ? ifproxy.Index : index; |
| inst_cmp->U.I.SrcReg[2].File = RC_FILE_TEMPORARY; |
| inst_cmp->U.I.SrcReg[2].Index = elseproxy.Proxied ? elseproxy.Index : index; |
| } |
| |
| static void handle_endif(struct emulate_branch_state * s, struct rc_instruction * inst) |
| { |
| struct branch_info * branch; |
| struct register_proxies IfProxies; |
| struct register_proxies ElseProxies; |
| |
| if (!s->BranchCount) { |
| rc_error(s->C, "Encountered ENDIF outside of branches"); |
| return; |
| } |
| |
| DBG("%s\n", __FUNCTION__); |
| |
| branch = &s->Branches[s->BranchCount - 1]; |
| |
| memset(&IfProxies, 0, sizeof(IfProxies)); |
| memset(&ElseProxies, 0, sizeof(ElseProxies)); |
| |
| allocate_and_insert_proxies(s, &IfProxies, branch->If->Next, branch->Else ? branch->Else : inst); |
| |
| if (branch->Else) |
| allocate_and_insert_proxies(s, &ElseProxies, branch->Else->Next, inst); |
| |
| /* Insert the CMP instructions at the end. */ |
| for(unsigned int index = 0; index < RC_REGISTER_MAX_INDEX; ++index) { |
| if (IfProxies.Temporary[index].Proxied || ElseProxies.Temporary[index].Proxied) { |
| inject_cmp(s, branch->If, inst, RC_FILE_TEMPORARY, index, |
| IfProxies.Temporary[index], ElseProxies.Temporary[index]); |
| } |
| } |
| |
| /* Remove all traces of the branch instructions */ |
| rc_remove_instruction(branch->If); |
| if (branch->Else) |
| rc_remove_instruction(branch->Else); |
| rc_remove_instruction(inst); |
| |
| s->BranchCount--; |
| |
| if (VERBOSE) { |
| DBG("Program after ENDIF handling:\n"); |
| rc_print_program(&s->C->Program); |
| } |
| } |
| |
| |
| struct remap_output_data { |
| unsigned int Output:RC_REGISTER_INDEX_BITS; |
| unsigned int Temporary:RC_REGISTER_INDEX_BITS; |
| }; |
| |
| static void remap_output_function(void * userdata, struct rc_instruction * inst, |
| rc_register_file * pfile, unsigned int * pindex) |
| { |
| struct remap_output_data * data = userdata; |
| |
| if (*pfile == RC_FILE_OUTPUT && *pindex == data->Output) { |
| *pfile = RC_FILE_TEMPORARY; |
| *pindex = data->Temporary; |
| } |
| } |
| |
| |
| /** |
| * Output registers cannot be read from and so cannot be dealt with like |
| * temporary registers. |
| * |
| * We do the simplest thing: If an output registers is written within |
| * a branch, then *all* writes to this register are proxied to a |
| * temporary register, and a final MOV is appended to the end of |
| * the program. |
| */ |
| static void fix_output_writes(struct emulate_branch_state * s, struct rc_instruction * inst) |
| { |
| const struct rc_opcode_info * opcode; |
| |
| if (!s->BranchCount) |
| return; |
| |
| opcode = rc_get_opcode_info(inst->U.I.Opcode); |
| |
| if (!opcode->HasDstReg) |
| return; |
| |
| if (inst->U.I.DstReg.File == RC_FILE_OUTPUT) { |
| struct remap_output_data remap; |
| struct rc_instruction * inst_mov; |
| |
| remap.Output = inst->U.I.DstReg.Index; |
| remap.Temporary = rc_find_free_temporary(s->C); |
| |
| for(struct rc_instruction * inst = s->C->Program.Instructions.Next; |
| inst != &s->C->Program.Instructions; |
| inst = inst->Next) { |
| rc_remap_registers(inst, &remap_output_function, &remap); |
| } |
| |
| inst_mov = rc_insert_new_instruction(s->C, s->C->Program.Instructions.Prev); |
| inst_mov->U.I.Opcode = RC_OPCODE_MOV; |
| inst_mov->U.I.DstReg.File = RC_FILE_OUTPUT; |
| inst_mov->U.I.DstReg.Index = remap.Output; |
| inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZW; |
| inst_mov->U.I.SrcReg[0].File = RC_FILE_TEMPORARY; |
| inst_mov->U.I.SrcReg[0].Index = remap.Temporary; |
| } |
| } |
| |
| /** |
| * Remove branch instructions; instead, execute both branches |
| * on different register sets and choose between their results |
| * using CMP instructions in place of the original ENDIF. |
| */ |
| void rc_emulate_branches(struct radeon_compiler *c, void *user) |
| { |
| struct emulate_branch_state s; |
| struct rc_instruction * ptr; |
| |
| memset(&s, 0, sizeof(s)); |
| s.C = c; |
| |
| /* Untypical loop because we may remove the current instruction */ |
| ptr = c->Program.Instructions.Next; |
| while(ptr != &c->Program.Instructions) { |
| struct rc_instruction * inst = ptr; |
| ptr = ptr->Next; |
| |
| if (inst->Type == RC_INSTRUCTION_NORMAL) { |
| switch(inst->U.I.Opcode) { |
| case RC_OPCODE_IF: |
| handle_if(&s, inst); |
| break; |
| case RC_OPCODE_ELSE: |
| handle_else(&s, inst); |
| break; |
| case RC_OPCODE_ENDIF: |
| handle_endif(&s, inst); |
| break; |
| default: |
| fix_output_writes(&s, inst); |
| break; |
| } |
| } else { |
| rc_error(c, "%s: unhandled instruction type\n", __FUNCTION__); |
| } |
| } |
| } |