| /* |
| * 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); |
| } |
| } |