Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Copyright 2009 Nicolai Hhnle <nhaehnle (at) gmail.com>
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * on the rights to use, copy, modify, merge, publish, distribute, sub
      8  * license, and/or sell copies of the Software, and to permit persons to whom
      9  * the Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     18  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
     19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     21  * USE OR OTHER DEALINGS IN THE SOFTWARE. */
     22 
     23 #include "radeon_emulate_branches.h"
     24 
     25 #include <stdio.h>
     26 
     27 #include "radeon_compiler.h"
     28 #include "radeon_dataflow.h"
     29 
     30 #define VERBOSE 0
     31 
     32 #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)
     33 
     34 
     35 struct proxy_info {
     36 	unsigned int Proxied:1;
     37 	unsigned int Index:RC_REGISTER_INDEX_BITS;
     38 };
     39 
     40 struct register_proxies {
     41 	struct proxy_info Temporary[RC_REGISTER_MAX_INDEX];
     42 };
     43 
     44 struct branch_info {
     45 	struct rc_instruction * If;
     46 	struct rc_instruction * Else;
     47 };
     48 
     49 struct emulate_branch_state {
     50 	struct radeon_compiler * C;
     51 
     52 	struct branch_info * Branches;
     53 	unsigned int BranchCount;
     54 	unsigned int BranchReserved;
     55 };
     56 
     57 
     58 static void handle_if(struct emulate_branch_state * s, struct rc_instruction * inst)
     59 {
     60 	struct branch_info * branch;
     61 	struct rc_instruction * inst_mov;
     62 
     63 	memory_pool_array_reserve(&s->C->Pool, struct branch_info,
     64 			s->Branches, s->BranchCount, s->BranchReserved, 1);
     65 
     66 	DBG("%s\n", __FUNCTION__);
     67 
     68 	branch = &s->Branches[s->BranchCount++];
     69 	memset(branch, 0, sizeof(struct branch_info));
     70 	branch->If = inst;
     71 
     72 	/* Make a safety copy of the decision register, because we will need
     73 	 * it at ENDIF time and it might be overwritten in both branches. */
     74 	inst_mov = rc_insert_new_instruction(s->C, inst->Prev);
     75 	inst_mov->U.I.Opcode = RC_OPCODE_MOV;
     76 	inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
     77 	inst_mov->U.I.DstReg.Index = rc_find_free_temporary(s->C);
     78 	inst_mov->U.I.DstReg.WriteMask = RC_MASK_X;
     79 	inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
     80 
     81 	inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
     82 	inst->U.I.SrcReg[0].Index = inst_mov->U.I.DstReg.Index;
     83 	inst->U.I.SrcReg[0].Swizzle = 0;
     84 	inst->U.I.SrcReg[0].Abs = 0;
     85 	inst->U.I.SrcReg[0].Negate = 0;
     86 }
     87 
     88 static void handle_else(struct emulate_branch_state * s, struct rc_instruction * inst)
     89 {
     90 	struct branch_info * branch;
     91 
     92 	if (!s->BranchCount) {
     93 		rc_error(s->C, "Encountered ELSE outside of branches");
     94 		return;
     95 	}
     96 
     97 	DBG("%s\n", __FUNCTION__);
     98 
     99 	branch = &s->Branches[s->BranchCount - 1];
    100 	branch->Else = inst;
    101 }
    102 
    103 
    104 struct state_and_proxies {
    105 	struct emulate_branch_state * S;
    106 	struct register_proxies * Proxies;
    107 };
    108 
    109 static struct proxy_info * get_proxy_info(struct state_and_proxies * sap,
    110 			rc_register_file file, unsigned int index)
    111 {
    112 	if (file == RC_FILE_TEMPORARY) {
    113 		return &sap->Proxies->Temporary[index];
    114 	} else {
    115 		return 0;
    116 	}
    117 }
    118 
    119 static void scan_write(void * userdata, struct rc_instruction * inst,
    120 		rc_register_file file, unsigned int index, unsigned int comp)
    121 {
    122 	struct state_and_proxies * sap = userdata;
    123 	struct proxy_info * proxy = get_proxy_info(sap, file, index);
    124 
    125 	if (proxy && !proxy->Proxied) {
    126 		proxy->Proxied = 1;
    127 		proxy->Index = rc_find_free_temporary(sap->S->C);
    128 	}
    129 }
    130 
    131 static void remap_proxy_function(void * userdata, struct rc_instruction * inst,
    132 		rc_register_file * pfile, unsigned int * pindex)
    133 {
    134 	struct state_and_proxies * sap = userdata;
    135 	struct proxy_info * proxy = get_proxy_info(sap, *pfile, *pindex);
    136 
    137 	if (proxy && proxy->Proxied) {
    138 		*pfile = RC_FILE_TEMPORARY;
    139 		*pindex = proxy->Index;
    140 	}
    141 }
    142 
    143 /**
    144  * Redirect all writes in the instruction range [begin, end) to proxy
    145  * temporary registers.
    146  */
    147 static void allocate_and_insert_proxies(struct emulate_branch_state * s,
    148 		struct register_proxies * proxies,
    149 		struct rc_instruction * begin,
    150 		struct rc_instruction * end)
    151 {
    152 	struct state_and_proxies sap;
    153 
    154 	sap.S = s;
    155 	sap.Proxies = proxies;
    156 
    157 	for(struct rc_instruction * inst = begin; inst != end; inst = inst->Next) {
    158 		rc_for_all_writes_mask(inst, scan_write, &sap);
    159 		rc_remap_registers(inst, remap_proxy_function, &sap);
    160 	}
    161 
    162 	for(unsigned int index = 0; index < RC_REGISTER_MAX_INDEX; ++index) {
    163 		if (proxies->Temporary[index].Proxied) {
    164 			struct rc_instruction * inst_mov = rc_insert_new_instruction(s->C, begin->Prev);
    165 			inst_mov->U.I.Opcode = RC_OPCODE_MOV;
    166 			inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
    167 			inst_mov->U.I.DstReg.Index = proxies->Temporary[index].Index;
    168 			inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZW;
    169 			inst_mov->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
    170 			inst_mov->U.I.SrcReg[0].Index = index;
    171 		}
    172 	}
    173 }
    174 
    175 
    176 static void inject_cmp(struct emulate_branch_state * s,
    177 		struct rc_instruction * inst_if,
    178 		struct rc_instruction * inst_endif,
    179 		rc_register_file file, unsigned int index,
    180 		struct proxy_info ifproxy,
    181 		struct proxy_info elseproxy)
    182 {
    183 	struct rc_instruction * inst_cmp = rc_insert_new_instruction(s->C, inst_endif);
    184 	inst_cmp->U.I.Opcode = RC_OPCODE_CMP;
    185 	inst_cmp->U.I.DstReg.File = file;
    186 	inst_cmp->U.I.DstReg.Index = index;
    187 	inst_cmp->U.I.DstReg.WriteMask = RC_MASK_XYZW;
    188 	inst_cmp->U.I.SrcReg[0] = inst_if->U.I.SrcReg[0];
    189 	inst_cmp->U.I.SrcReg[0].Abs = 1;
    190 	inst_cmp->U.I.SrcReg[0].Negate = RC_MASK_XYZW;
    191 	inst_cmp->U.I.SrcReg[1].File = RC_FILE_TEMPORARY;
    192 	inst_cmp->U.I.SrcReg[1].Index = ifproxy.Proxied ? ifproxy.Index : index;
    193 	inst_cmp->U.I.SrcReg[2].File = RC_FILE_TEMPORARY;
    194 	inst_cmp->U.I.SrcReg[2].Index = elseproxy.Proxied ? elseproxy.Index : index;
    195 }
    196 
    197 static void handle_endif(struct emulate_branch_state * s, struct rc_instruction * inst)
    198 {
    199 	struct branch_info * branch;
    200 	struct register_proxies IfProxies;
    201 	struct register_proxies ElseProxies;
    202 
    203 	if (!s->BranchCount) {
    204 		rc_error(s->C, "Encountered ENDIF outside of branches");
    205 		return;
    206 	}
    207 
    208 	DBG("%s\n", __FUNCTION__);
    209 
    210 	branch = &s->Branches[s->BranchCount - 1];
    211 
    212 	memset(&IfProxies, 0, sizeof(IfProxies));
    213 	memset(&ElseProxies, 0, sizeof(ElseProxies));
    214 
    215 	allocate_and_insert_proxies(s, &IfProxies, branch->If->Next, branch->Else ? branch->Else : inst);
    216 
    217 	if (branch->Else)
    218 		allocate_and_insert_proxies(s, &ElseProxies, branch->Else->Next, inst);
    219 
    220 	/* Insert the CMP instructions at the end. */
    221 	for(unsigned int index = 0; index < RC_REGISTER_MAX_INDEX; ++index) {
    222 		if (IfProxies.Temporary[index].Proxied || ElseProxies.Temporary[index].Proxied) {
    223 			inject_cmp(s, branch->If, inst, RC_FILE_TEMPORARY, index,
    224 					IfProxies.Temporary[index], ElseProxies.Temporary[index]);
    225 		}
    226 	}
    227 
    228 	/* Remove all traces of the branch instructions */
    229 	rc_remove_instruction(branch->If);
    230 	if (branch->Else)
    231 		rc_remove_instruction(branch->Else);
    232 	rc_remove_instruction(inst);
    233 
    234 	s->BranchCount--;
    235 
    236 	if (VERBOSE) {
    237 		DBG("Program after ENDIF handling:\n");
    238 		rc_print_program(&s->C->Program);
    239 	}
    240 }
    241 
    242 
    243 struct remap_output_data {
    244 	unsigned int Output:RC_REGISTER_INDEX_BITS;
    245 	unsigned int Temporary:RC_REGISTER_INDEX_BITS;
    246 };
    247 
    248 static void remap_output_function(void * userdata, struct rc_instruction * inst,
    249 		rc_register_file * pfile, unsigned int * pindex)
    250 {
    251 	struct remap_output_data * data = userdata;
    252 
    253 	if (*pfile == RC_FILE_OUTPUT && *pindex == data->Output) {
    254 		*pfile = RC_FILE_TEMPORARY;
    255 		*pindex = data->Temporary;
    256 	}
    257 }
    258 
    259 
    260 /**
    261  * Output registers cannot be read from and so cannot be dealt with like
    262  * temporary registers.
    263  *
    264  * We do the simplest thing: If an output registers is written within
    265  * a branch, then *all* writes to this register are proxied to a
    266  * temporary register, and a final MOV is appended to the end of
    267  * the program.
    268  */
    269 static void fix_output_writes(struct emulate_branch_state * s, struct rc_instruction * inst)
    270 {
    271 	const struct rc_opcode_info * opcode;
    272 
    273 	if (!s->BranchCount)
    274 		return;
    275 
    276 	opcode = rc_get_opcode_info(inst->U.I.Opcode);
    277 
    278 	if (!opcode->HasDstReg)
    279 		return;
    280 
    281 	if (inst->U.I.DstReg.File == RC_FILE_OUTPUT) {
    282 		struct remap_output_data remap;
    283 		struct rc_instruction * inst_mov;
    284 
    285 		remap.Output = inst->U.I.DstReg.Index;
    286 		remap.Temporary = rc_find_free_temporary(s->C);
    287 
    288 		for(struct rc_instruction * inst = s->C->Program.Instructions.Next;
    289 		    inst != &s->C->Program.Instructions;
    290 		    inst = inst->Next) {
    291 			rc_remap_registers(inst, &remap_output_function, &remap);
    292 		}
    293 
    294 		inst_mov = rc_insert_new_instruction(s->C, s->C->Program.Instructions.Prev);
    295 		inst_mov->U.I.Opcode = RC_OPCODE_MOV;
    296 		inst_mov->U.I.DstReg.File = RC_FILE_OUTPUT;
    297 		inst_mov->U.I.DstReg.Index = remap.Output;
    298 		inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZW;
    299 		inst_mov->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
    300 		inst_mov->U.I.SrcReg[0].Index = remap.Temporary;
    301 	}
    302 }
    303 
    304 /**
    305  * Remove branch instructions; instead, execute both branches
    306  * on different register sets and choose between their results
    307  * using CMP instructions in place of the original ENDIF.
    308  */
    309 void rc_emulate_branches(struct radeon_compiler *c, void *user)
    310 {
    311 	struct emulate_branch_state s;
    312 	struct rc_instruction * ptr;
    313 
    314 	memset(&s, 0, sizeof(s));
    315 	s.C = c;
    316 
    317 	/* Untypical loop because we may remove the current instruction */
    318 	ptr = c->Program.Instructions.Next;
    319 	while(ptr != &c->Program.Instructions) {
    320 		struct rc_instruction * inst = ptr;
    321 		ptr = ptr->Next;
    322 
    323 		if (inst->Type == RC_INSTRUCTION_NORMAL) {
    324 			switch(inst->U.I.Opcode) {
    325 			case RC_OPCODE_IF:
    326 				handle_if(&s, inst);
    327 				break;
    328 			case RC_OPCODE_ELSE:
    329 				handle_else(&s, inst);
    330 				break;
    331 			case RC_OPCODE_ENDIF:
    332 				handle_endif(&s, inst);
    333 				break;
    334 			default:
    335 				fix_output_writes(&s, inst);
    336 				break;
    337 			}
    338 		} else {
    339 			rc_error(c, "%s: unhandled instruction type\n", __FUNCTION__);
    340 		}
    341 	}
    342 }
    343