/* * Copyright (C) 2009 Nicolai Haehnle. * Copyright 2010 Tom Stellard <tstellar@gmail.com> * * All Rights Reserved. * * 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, sublicense, 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 NONINFRINGEMENT. * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS 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_dataflow.h" #include "radeon_compiler.h" #include "radeon_compiler_util.h" #include "radeon_program.h" struct read_write_mask_data { void * UserData; rc_read_write_mask_fn Cb; }; static void reads_normal_callback( void * userdata, struct rc_instruction * fullinst, struct rc_src_register * src) { struct read_write_mask_data * cb_data = userdata; unsigned int refmask = 0; unsigned int chan; for(chan = 0; chan < 4; chan++) { refmask |= 1 << GET_SWZ(src->Swizzle, chan); } refmask &= RC_MASK_XYZW; if (refmask) { cb_data->Cb(cb_data->UserData, fullinst, src->File, src->Index, refmask); } if (refmask && src->RelAddr) { cb_data->Cb(cb_data->UserData, fullinst, RC_FILE_ADDRESS, 0, RC_MASK_X); } } static void pair_get_src_refmasks(unsigned int * refmasks, struct rc_pair_instruction * inst, unsigned int swz, unsigned int src) { if (swz >= 4) return; if (swz == RC_SWIZZLE_X || swz == RC_SWIZZLE_Y || swz == RC_SWIZZLE_Z) { if(src == RC_PAIR_PRESUB_SRC) { unsigned int i; int srcp_regs = rc_presubtract_src_reg_count( inst->RGB.Src[src].Index); for(i = 0; i < srcp_regs; i++) { refmasks[i] |= 1 << swz; } } else { refmasks[src] |= 1 << swz; } } if (swz == RC_SWIZZLE_W) { if (src == RC_PAIR_PRESUB_SRC) { unsigned int i; int srcp_regs = rc_presubtract_src_reg_count( inst->Alpha.Src[src].Index); for(i = 0; i < srcp_regs; i++) { refmasks[i] |= 1 << swz; } } else { refmasks[src] |= 1 << swz; } } } static void reads_pair(struct rc_instruction * fullinst, rc_read_write_mask_fn cb, void * userdata) { struct rc_pair_instruction * inst = &fullinst->U.P; unsigned int refmasks[3] = { 0, 0, 0 }; unsigned int arg; for(arg = 0; arg < 3; ++arg) { unsigned int chan; for(chan = 0; chan < 3; ++chan) { unsigned int swz_rgb = GET_SWZ(inst->RGB.Arg[arg].Swizzle, chan); unsigned int swz_alpha = GET_SWZ(inst->Alpha.Arg[arg].Swizzle, chan); pair_get_src_refmasks(refmasks, inst, swz_rgb, inst->RGB.Arg[arg].Source); pair_get_src_refmasks(refmasks, inst, swz_alpha, inst->Alpha.Arg[arg].Source); } } for(unsigned int src = 0; src < 3; ++src) { if (inst->RGB.Src[src].Used && (refmasks[src] & RC_MASK_XYZ)) cb(userdata, fullinst, inst->RGB.Src[src].File, inst->RGB.Src[src].Index, refmasks[src] & RC_MASK_XYZ); if (inst->Alpha.Src[src].Used && (refmasks[src] & RC_MASK_W)) cb(userdata, fullinst, inst->Alpha.Src[src].File, inst->Alpha.Src[src].Index, RC_MASK_W); } } static void pair_sub_for_all_args( struct rc_instruction * fullinst, struct rc_pair_sub_instruction * sub, rc_pair_read_arg_fn cb, void * userdata) { int i; const struct rc_opcode_info * info = rc_get_opcode_info(sub->Opcode); for(i = 0; i < info->NumSrcRegs; i++) { unsigned int src_type; src_type = rc_source_type_swz(sub->Arg[i].Swizzle); if (src_type == RC_SOURCE_NONE) continue; if (sub->Arg[i].Source == RC_PAIR_PRESUB_SRC) { unsigned int presub_type; unsigned int presub_src_count; struct rc_pair_instruction_source * src_array; unsigned int j; if (src_type & RC_SOURCE_RGB) { presub_type = fullinst-> U.P.RGB.Src[RC_PAIR_PRESUB_SRC].Index; src_array = fullinst->U.P.RGB.Src; } else { presub_type = fullinst-> U.P.Alpha.Src[RC_PAIR_PRESUB_SRC].Index; src_array = fullinst->U.P.Alpha.Src; } presub_src_count = rc_presubtract_src_reg_count(presub_type); for(j = 0; j < presub_src_count; j++) { cb(userdata, fullinst, &sub->Arg[i], &src_array[j]); } } else { struct rc_pair_instruction_source * src = rc_pair_get_src(&fullinst->U.P, &sub->Arg[i]); if (src) { cb(userdata, fullinst, &sub->Arg[i], src); } } } } /* This function calls the callback function (cb) for each source used by * the instruction. * */ void rc_for_all_reads_src( struct rc_instruction * inst, rc_read_src_fn cb, void * userdata) { const struct rc_opcode_info * opcode = rc_get_opcode_info(inst->U.I.Opcode); /* This function only works with normal instructions. */ if (inst->Type != RC_INSTRUCTION_NORMAL) { assert(0); return; } for(unsigned int src = 0; src < opcode->NumSrcRegs; ++src) { if (inst->U.I.SrcReg[src].File == RC_FILE_NONE) continue; if (inst->U.I.SrcReg[src].File == RC_FILE_PRESUB) { unsigned int i; unsigned int srcp_regs = rc_presubtract_src_reg_count( inst->U.I.PreSub.Opcode); for( i = 0; i < srcp_regs; i++) { cb(userdata, inst, &inst->U.I.PreSub.SrcReg[i]); } } else { cb(userdata, inst, &inst->U.I.SrcReg[src]); } } } /** * This function calls the callback function (cb) for each arg of the RGB and * alpha components. */ void rc_pair_for_all_reads_arg( struct rc_instruction * inst, rc_pair_read_arg_fn cb, void * userdata) { /* This function only works with pair instructions. */ if (inst->Type != RC_INSTRUCTION_PAIR) { assert(0); return; } pair_sub_for_all_args(inst, &inst->U.P.RGB, cb, userdata); pair_sub_for_all_args(inst, &inst->U.P.Alpha, cb, userdata); } /** * Calls a callback function for all register reads. * * This is conservative, i.e. if the same register is referenced multiple times, * the callback may also be called multiple times. * Also, the writemask of the instruction is not taken into account. */ void rc_for_all_reads_mask(struct rc_instruction * inst, rc_read_write_mask_fn cb, void * userdata) { if (inst->Type == RC_INSTRUCTION_NORMAL) { struct read_write_mask_data cb_data; cb_data.UserData = userdata; cb_data.Cb = cb; rc_for_all_reads_src(inst, reads_normal_callback, &cb_data); } else { reads_pair(inst, cb, userdata); } } static void writes_normal(struct rc_instruction * fullinst, rc_read_write_mask_fn cb, void * userdata) { struct rc_sub_instruction * inst = &fullinst->U.I; const struct rc_opcode_info * opcode = rc_get_opcode_info(inst->Opcode); if (opcode->HasDstReg && inst->DstReg.WriteMask) cb(userdata, fullinst, inst->DstReg.File, inst->DstReg.Index, inst->DstReg.WriteMask); if (inst->WriteALUResult) cb(userdata, fullinst, RC_FILE_SPECIAL, RC_SPECIAL_ALU_RESULT, RC_MASK_X); } static void writes_pair(struct rc_instruction * fullinst, rc_read_write_mask_fn cb, void * userdata) { struct rc_pair_instruction * inst = &fullinst->U.P; if (inst->RGB.WriteMask) cb(userdata, fullinst, RC_FILE_TEMPORARY, inst->RGB.DestIndex, inst->RGB.WriteMask); if (inst->Alpha.WriteMask) cb(userdata, fullinst, RC_FILE_TEMPORARY, inst->Alpha.DestIndex, RC_MASK_W); if (inst->WriteALUResult) cb(userdata, fullinst, RC_FILE_SPECIAL, RC_SPECIAL_ALU_RESULT, RC_MASK_X); } /** * Calls a callback function for all register writes in the instruction, * reporting writemasks to the callback function. * * \warning Does not report output registers for paired instructions! */ void rc_for_all_writes_mask(struct rc_instruction * inst, rc_read_write_mask_fn cb, void * userdata) { if (inst->Type == RC_INSTRUCTION_NORMAL) { writes_normal(inst, cb, userdata); } else { writes_pair(inst, cb, userdata); } } struct mask_to_chan_data { void * UserData; rc_read_write_chan_fn Fn; }; static void mask_to_chan_cb(void * data, struct rc_instruction * inst, rc_register_file file, unsigned int index, unsigned int mask) { struct mask_to_chan_data * d = data; for(unsigned int chan = 0; chan < 4; ++chan) { if (GET_BIT(mask, chan)) d->Fn(d->UserData, inst, file, index, chan); } } /** * Calls a callback function for all sourced register channels. * * This is conservative, i.e. channels may be called multiple times, * and the writemask of the instruction is not taken into account. */ void rc_for_all_reads_chan(struct rc_instruction * inst, rc_read_write_chan_fn cb, void * userdata) { struct mask_to_chan_data d; d.UserData = userdata; d.Fn = cb; rc_for_all_reads_mask(inst, &mask_to_chan_cb, &d); } /** * Calls a callback function for all written register channels. * * \warning Does not report output registers for paired instructions! */ void rc_for_all_writes_chan(struct rc_instruction * inst, rc_read_write_chan_fn cb, void * userdata) { struct mask_to_chan_data d; d.UserData = userdata; d.Fn = cb; rc_for_all_writes_mask(inst, &mask_to_chan_cb, &d); } static void remap_normal_instruction(struct rc_instruction * fullinst, rc_remap_register_fn cb, void * userdata) { struct rc_sub_instruction * inst = &fullinst->U.I; const struct rc_opcode_info * opcode = rc_get_opcode_info(inst->Opcode); unsigned int remapped_presub = 0; if (opcode->HasDstReg) { rc_register_file file = inst->DstReg.File; unsigned int index = inst->DstReg.Index; cb(userdata, fullinst, &file, &index); inst->DstReg.File = file; inst->DstReg.Index = index; } for(unsigned int src = 0; src < opcode->NumSrcRegs; ++src) { rc_register_file file = inst->SrcReg[src].File; unsigned int index = inst->SrcReg[src].Index; if (file == RC_FILE_PRESUB) { unsigned int i; unsigned int srcp_srcs = rc_presubtract_src_reg_count( inst->PreSub.Opcode); /* Make sure we only remap presubtract sources once in * case more than one source register reads the * presubtract result. */ if (remapped_presub) continue; for(i = 0; i < srcp_srcs; i++) { file = inst->PreSub.SrcReg[i].File; index = inst->PreSub.SrcReg[i].Index; cb(userdata, fullinst, &file, &index); inst->PreSub.SrcReg[i].File = file; inst->PreSub.SrcReg[i].Index = index; } remapped_presub = 1; } else { cb(userdata, fullinst, &file, &index); inst->SrcReg[src].File = file; inst->SrcReg[src].Index = index; } } } static void remap_pair_instruction(struct rc_instruction * fullinst, rc_remap_register_fn cb, void * userdata) { struct rc_pair_instruction * inst = &fullinst->U.P; if (inst->RGB.WriteMask) { rc_register_file file = RC_FILE_TEMPORARY; unsigned int index = inst->RGB.DestIndex; cb(userdata, fullinst, &file, &index); inst->RGB.DestIndex = index; } if (inst->Alpha.WriteMask) { rc_register_file file = RC_FILE_TEMPORARY; unsigned int index = inst->Alpha.DestIndex; cb(userdata, fullinst, &file, &index); inst->Alpha.DestIndex = index; } for(unsigned int src = 0; src < 3; ++src) { if (inst->RGB.Src[src].Used) { rc_register_file file = inst->RGB.Src[src].File; unsigned int index = inst->RGB.Src[src].Index; cb(userdata, fullinst, &file, &index); inst->RGB.Src[src].File = file; inst->RGB.Src[src].Index = index; } if (inst->Alpha.Src[src].Used) { rc_register_file file = inst->Alpha.Src[src].File; unsigned int index = inst->Alpha.Src[src].Index; cb(userdata, fullinst, &file, &index); inst->Alpha.Src[src].File = file; inst->Alpha.Src[src].Index = index; } } } /** * Remap all register accesses according to the given function. * That is, call the function \p cb for each referenced register (both read and written) * and update the given instruction \p inst accordingly * if it modifies its \ref pfile and \ref pindex contents. */ void rc_remap_registers(struct rc_instruction * inst, rc_remap_register_fn cb, void * userdata) { if (inst->Type == RC_INSTRUCTION_NORMAL) remap_normal_instruction(inst, cb, userdata); else remap_pair_instruction(inst, cb, userdata); } struct branch_write_mask { unsigned int IfWriteMask:4; unsigned int ElseWriteMask:4; unsigned int HasElse:1; }; union get_readers_read_cb { rc_read_src_fn I; rc_pair_read_arg_fn P; }; struct get_readers_callback_data { struct radeon_compiler * C; struct rc_reader_data * ReaderData; rc_read_src_fn ReadNormalCB; rc_pair_read_arg_fn ReadPairCB; rc_read_write_mask_fn WriteCB; rc_register_file DstFile; unsigned int DstIndex; unsigned int DstMask; unsigned int AliveWriteMask; /* For convenience, this is indexed starting at 1 */ struct branch_write_mask BranchMasks[R500_PFS_MAX_BRANCH_DEPTH_FULL + 1]; }; static struct rc_reader * add_reader( struct memory_pool * pool, struct rc_reader_data * data, struct rc_instruction * inst, unsigned int mask) { struct rc_reader * new; memory_pool_array_reserve(pool, struct rc_reader, data->Readers, data->ReaderCount, data->ReadersReserved, 1); new = &data->Readers[data->ReaderCount++]; new->Inst = inst; new->WriteMask = mask; return new; } static void add_reader_normal( struct memory_pool * pool, struct rc_reader_data * data, struct rc_instruction * inst, unsigned int mask, struct rc_src_register * src) { struct rc_reader * new = add_reader(pool, data, inst, mask); new->U.I.Src = src; } static void add_reader_pair( struct memory_pool * pool, struct rc_reader_data * data, struct rc_instruction * inst, unsigned int mask, struct rc_pair_instruction_arg * arg, struct rc_pair_instruction_source * src) { struct rc_reader * new = add_reader(pool, data, inst, mask); new->U.P.Src = src; new->U.P.Arg = arg; } static unsigned int get_readers_read_callback( struct get_readers_callback_data * cb_data, unsigned int has_rel_addr, rc_register_file file, unsigned int index, unsigned int swizzle) { unsigned int shared_mask, read_mask; if (has_rel_addr) { cb_data->ReaderData->Abort = 1; return RC_MASK_NONE; } shared_mask = rc_src_reads_dst_mask(file, index, swizzle, cb_data->DstFile, cb_data->DstIndex, cb_data->AliveWriteMask); if (shared_mask == RC_MASK_NONE) return shared_mask; /* If we make it this far, it means that this source reads from the * same register written to by d->ReaderData->Writer. */ read_mask = rc_swizzle_to_writemask(swizzle); if (cb_data->ReaderData->AbortOnRead & read_mask) { cb_data->ReaderData->Abort = 1; return shared_mask; } if (cb_data->ReaderData->LoopDepth > 0) { cb_data->ReaderData->AbortOnWrite |= (read_mask & cb_data->AliveWriteMask); } /* XXX The behavior in this case should be configurable. */ if ((read_mask & cb_data->AliveWriteMask) != read_mask) { cb_data->ReaderData->Abort = 1; return shared_mask; } return shared_mask; } static void get_readers_pair_read_callback( void * userdata, struct rc_instruction * inst, struct rc_pair_instruction_arg * arg, struct rc_pair_instruction_source * src) { unsigned int shared_mask; struct get_readers_callback_data * d = userdata; shared_mask = get_readers_read_callback(d, 0 /*Pair Instructions don't use RelAddr*/, src->File, src->Index, arg->Swizzle); if (shared_mask == RC_MASK_NONE) return; if (d->ReadPairCB) d->ReadPairCB(d->ReaderData, inst, arg, src); if (d->ReaderData->ExitOnAbort && d->ReaderData->Abort) return; add_reader_pair(&d->C->Pool, d->ReaderData, inst, shared_mask, arg, src); } /** * This function is used by rc_get_readers_normal() to determine whether inst * is a reader of userdata->ReaderData->Writer */ static void get_readers_normal_read_callback( void * userdata, struct rc_instruction * inst, struct rc_src_register * src) { struct get_readers_callback_data * d = userdata; unsigned int shared_mask; shared_mask = get_readers_read_callback(d, src->RelAddr, src->File, src->Index, src->Swizzle); if (shared_mask == RC_MASK_NONE) return; /* The callback function could potentially clear d->ReaderData->Abort, * so we need to call it before we return. */ if (d->ReadNormalCB) d->ReadNormalCB(d->ReaderData, inst, src); if (d->ReaderData->ExitOnAbort && d->ReaderData->Abort) return; add_reader_normal(&d->C->Pool, d->ReaderData, inst, shared_mask, src); } /** * This function is used by rc_get_readers_normal() to determine when * userdata->ReaderData->Writer is dead (i. e. All compontents of its * destination register have been overwritten by other instructions). */ static void get_readers_write_callback( void *userdata, struct rc_instruction * inst, rc_register_file file, unsigned int index, unsigned int mask) { struct get_readers_callback_data * d = userdata; if (index == d->DstIndex && file == d->DstFile) { unsigned int shared_mask = mask & d->DstMask; d->ReaderData->AbortOnRead &= ~shared_mask; d->AliveWriteMask &= ~shared_mask; if (d->ReaderData->AbortOnWrite & shared_mask) { d->ReaderData->Abort = 1; } } if(d->WriteCB) d->WriteCB(d->ReaderData, inst, file, index, mask); } static void push_branch_mask( struct get_readers_callback_data * d, unsigned int * branch_depth) { (*branch_depth)++; if (*branch_depth > R500_PFS_MAX_BRANCH_DEPTH_FULL) { d->ReaderData->Abort = 1; return; } d->BranchMasks[*branch_depth].IfWriteMask = d->AliveWriteMask; } static void pop_branch_mask( struct get_readers_callback_data * d, unsigned int * branch_depth) { struct branch_write_mask * masks = &d->BranchMasks[*branch_depth]; if (masks->HasElse) { /* Abort on read for components that were written in the IF * block. */ d->ReaderData->AbortOnRead |= masks->IfWriteMask & ~masks->ElseWriteMask; /* Abort on read for components that were written in the ELSE * block. */ d->ReaderData->AbortOnRead |= masks->ElseWriteMask & ~d->AliveWriteMask; d->AliveWriteMask = masks->IfWriteMask ^ ((masks->IfWriteMask ^ masks->ElseWriteMask) & (masks->IfWriteMask ^ d->AliveWriteMask)); } else { d->ReaderData->AbortOnRead |= masks->IfWriteMask & ~d->AliveWriteMask; d->AliveWriteMask = masks->IfWriteMask; } memset(masks, 0, sizeof(struct branch_write_mask)); (*branch_depth)--; } static void get_readers_for_single_write( void * userdata, struct rc_instruction * writer, rc_register_file dst_file, unsigned int dst_index, unsigned int dst_mask) { struct rc_instruction * tmp; unsigned int branch_depth = 0; struct rc_instruction * endloop = NULL; unsigned int abort_on_read_at_endloop = 0; struct get_readers_callback_data * d = userdata; d->ReaderData->Writer = writer; d->ReaderData->AbortOnRead = 0; d->ReaderData->AbortOnWrite = 0; d->ReaderData->LoopDepth = 0; d->ReaderData->InElse = 0; d->DstFile = dst_file; d->DstIndex = dst_index; d->DstMask = dst_mask; d->AliveWriteMask = dst_mask; memset(d->BranchMasks, 0, sizeof(d->BranchMasks)); if (!dst_mask) return; for(tmp = writer->Next; tmp != &d->C->Program.Instructions; tmp = tmp->Next){ rc_opcode opcode = rc_get_flow_control_inst(tmp); switch(opcode) { case RC_OPCODE_BGNLOOP: d->ReaderData->LoopDepth++; push_branch_mask(d, &branch_depth); break; case RC_OPCODE_ENDLOOP: if (d->ReaderData->LoopDepth > 0) { d->ReaderData->LoopDepth--; if (d->ReaderData->LoopDepth == 0) { d->ReaderData->AbortOnWrite = 0; } pop_branch_mask(d, &branch_depth); } else { /* Here we have reached an ENDLOOP without * seeing its BGNLOOP. These means that * the writer was written inside of a loop, * so it could have readers that are above it * (i.e. they have a lower IP). To find these * readers we jump to the BGNLOOP instruction * and check each instruction until we get * back to the writer. */ endloop = tmp; tmp = rc_match_endloop(tmp); if (!tmp) { rc_error(d->C, "Failed to match endloop.\n"); d->ReaderData->Abort = 1; return; } abort_on_read_at_endloop = d->ReaderData->AbortOnRead; d->ReaderData->AbortOnRead |= d->AliveWriteMask; continue; } break; case RC_OPCODE_IF: push_branch_mask(d, &branch_depth); break; case RC_OPCODE_ELSE: if (branch_depth == 0) { d->ReaderData->InElse = 1; } else { unsigned int temp_mask = d->AliveWriteMask; d->AliveWriteMask = d->BranchMasks[branch_depth].IfWriteMask; d->BranchMasks[branch_depth].ElseWriteMask = temp_mask; d->BranchMasks[branch_depth].HasElse = 1; } break; case RC_OPCODE_ENDIF: if (branch_depth == 0) { d->ReaderData->AbortOnRead = d->AliveWriteMask; d->ReaderData->InElse = 0; } else { pop_branch_mask(d, &branch_depth); } break; default: break; } if (d->ReaderData->InElse) continue; if (tmp->Type == RC_INSTRUCTION_NORMAL) { rc_for_all_reads_src(tmp, get_readers_normal_read_callback, d); } else { rc_pair_for_all_reads_arg(tmp, get_readers_pair_read_callback, d); } /* This can happen when we jump from an ENDLOOP to BGNLOOP */ if (tmp == writer) { tmp = endloop; endloop = NULL; d->ReaderData->AbortOnRead = abort_on_read_at_endloop; continue; } rc_for_all_writes_mask(tmp, get_readers_write_callback, d); if (d->ReaderData->ExitOnAbort && d->ReaderData->Abort) return; if (branch_depth == 0 && !d->AliveWriteMask) return; } } static void init_get_readers_callback_data( struct get_readers_callback_data * d, struct rc_reader_data * reader_data, struct radeon_compiler * c, rc_read_src_fn read_normal_cb, rc_pair_read_arg_fn read_pair_cb, rc_read_write_mask_fn write_cb) { reader_data->Abort = 0; reader_data->ReaderCount = 0; reader_data->ReadersReserved = 0; reader_data->Readers = NULL; d->C = c; d->ReaderData = reader_data; d->ReadNormalCB = read_normal_cb; d->ReadPairCB = read_pair_cb; d->WriteCB = write_cb; } /** * This function will create a list of readers via the rc_reader_data struct. * This function will abort (set the flag data->Abort) and return if it * encounters an instruction that reads from @param writer and also a different * instruction. Here are some examples: * * writer = instruction 0; * 0 MOV TEMP[0].xy, TEMP[1].xy * 1 MOV TEMP[0].zw, TEMP[2].xy * 2 MOV TEMP[3], TEMP[0] * The Abort flag will be set on instruction 2, because it reads values written * by instructions 0 and 1. * * writer = instruction 1; * 0 IF TEMP[0].x * 1 MOV TEMP[1], TEMP[2] * 2 ELSE * 3 MOV TEMP[1], TEMP[2] * 4 ENDIF * 5 MOV TEMP[3], TEMP[1] * The Abort flag will be set on instruction 5, because it could read from the * value written by either instruction 1 or 3, depending on the jump decision * made at instruction 0. * * writer = instruction 0; * 0 MOV TEMP[0], TEMP[1] * 2 BGNLOOP * 3 ADD TEMP[0], TEMP[0], none.1 * 4 ENDLOOP * The Abort flag will be set on instruction 3, because in the first iteration * of the loop it reads the value written by instruction 0 and in all other * iterations it reads the value written by instruction 3. * * @param read_cb This function will be called for for every instruction that * has been determined to be a reader of writer. * @param write_cb This function will be called for every instruction after * writer. */ void rc_get_readers( struct radeon_compiler * c, struct rc_instruction * writer, struct rc_reader_data * data, rc_read_src_fn read_normal_cb, rc_pair_read_arg_fn read_pair_cb, rc_read_write_mask_fn write_cb) { struct get_readers_callback_data d; init_get_readers_callback_data(&d, data, c, read_normal_cb, read_pair_cb, write_cb); rc_for_all_writes_mask(writer, get_readers_for_single_write, &d); } void rc_get_readers_sub( struct radeon_compiler * c, struct rc_instruction * writer, struct rc_pair_sub_instruction * sub_writer, struct rc_reader_data * data, rc_read_src_fn read_normal_cb, rc_pair_read_arg_fn read_pair_cb, rc_read_write_mask_fn write_cb) { struct get_readers_callback_data d; init_get_readers_callback_data(&d, data, c, read_normal_cb, read_pair_cb, write_cb); if (sub_writer->WriteMask) { get_readers_for_single_write(&d, writer, RC_FILE_TEMPORARY, sub_writer->DestIndex, sub_writer->WriteMask); } }