/* 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]); } } }
static void presub_nop(struct rc_instruction * emitted) { int prev_rgb_index, prev_alpha_index, i, num_src; /* We don't need a nop if the previous instruction is a TEX. */ if (emitted->Prev->Type != RC_INSTRUCTION_PAIR) { return; } if (emitted->Prev->U.P.RGB.WriteMask) prev_rgb_index = emitted->Prev->U.P.RGB.DestIndex; else prev_rgb_index = -1; if (emitted->Prev->U.P.Alpha.WriteMask) prev_alpha_index = emitted->Prev->U.P.Alpha.DestIndex; else prev_alpha_index = 1; /* Check the previous rgb instruction */ if (emitted->U.P.RGB.Src[RC_PAIR_PRESUB_SRC].Used) { num_src = rc_presubtract_src_reg_count( emitted->U.P.RGB.Src[RC_PAIR_PRESUB_SRC].Index); for (i = 0; i < num_src; i++) { unsigned int index = emitted->U.P.RGB.Src[i].Index; if (emitted->U.P.RGB.Src[i].File == RC_FILE_TEMPORARY && (index == prev_rgb_index || index == prev_alpha_index)) { emitted->Prev->U.P.Nop = 1; return; } } } /* Check the previous alpha instruction. */ if (!emitted->U.P.Alpha.Src[RC_PAIR_PRESUB_SRC].Used) return; num_src = rc_presubtract_src_reg_count( emitted->U.P.Alpha.Src[RC_PAIR_PRESUB_SRC].Index); for (i = 0; i < num_src; i++) { unsigned int index = emitted->U.P.Alpha.Src[i].Index; if(emitted->U.P.Alpha.Src[i].File == RC_FILE_TEMPORARY && (index == prev_rgb_index || index == prev_alpha_index)) { emitted->Prev->U.P.Nop = 1; return; } } }
static void mark_used_presub(struct rc_pair_sub_instruction * sub) { if (sub->Src[RC_PAIR_PRESUB_SRC].Used) { unsigned int presub_reg_count = rc_presubtract_src_reg_count( sub->Src[RC_PAIR_PRESUB_SRC].Index); unsigned int i; for (i = 0; i < presub_reg_count; i++) { sub->Src[i].Used = 1; } } }
static void pair_foreach_source_callback( struct rc_pair_instruction * pair, void * data, rc_pair_foreach_src_fn cb, unsigned int swz, unsigned int src) { /* swz > 3 means that the swizzle is either not used, or a constant * swizzle (e.g. 0, 1, 0.5). */ if(swz > 3) return; if(swz == RC_SWIZZLE_W) { if (src == RC_PAIR_PRESUB_SRC) { unsigned int i; unsigned int src_count = rc_presubtract_src_reg_count( pair->Alpha.Src[RC_PAIR_PRESUB_SRC].Index); for(i = 0; i < src_count; i++) { cb(data, &pair->Alpha.Src[i]); } } else { cb(data, &pair->Alpha.Src[src]); } } else { if (src == RC_PAIR_PRESUB_SRC) { unsigned int i; unsigned int src_count = rc_presubtract_src_reg_count( pair->RGB.Src[RC_PAIR_PRESUB_SRC].Index); for(i = 0; i < src_count; i++) { cb(data, &pair->RGB.Src[i]); } } else { cb(data, &pair->RGB.Src[src]); } } }
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 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 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); } } } }
static int destructive_merge_instructions( struct rc_pair_instruction * rgb, struct rc_pair_instruction * alpha) { const struct rc_opcode_info * opcode; assert(rgb->Alpha.Opcode == RC_OPCODE_NOP); assert(alpha->RGB.Opcode == RC_OPCODE_NOP); /* Presubtract registers need to be merged first so that registers * needed by the presubtract operation can be placed in src0 and/or * src1. */ /* Merge the rgb presubtract registers. */ const struct rc_opcode_info * rgb_info = rc_get_opcode_info(rgb->RGB.Opcode); if (alpha->RGB.Src[RC_PAIR_PRESUB_SRC].Used) { unsigned int srcp_src; unsigned int srcp_regs; if (rgb->RGB.Src[RC_PAIR_PRESUB_SRC].Used) return 0; srcp_regs = rc_presubtract_src_reg_count( alpha->RGB.Src[RC_PAIR_PRESUB_SRC].Index); for(srcp_src = 0; srcp_src < srcp_regs; srcp_src++) { unsigned int arg; int free_source; unsigned int one_way = 0; struct radeon_pair_instruction_source srcp = alpha->RGB.Src[srcp_src]; struct radeon_pair_instruction_source temp; /* 2nd arg of 1 means this is an rgb source. * 3rd arg of 0 means this is not an alpha source. */ free_source = rc_pair_alloc_source(rgb, 1, 0, srcp.File, srcp.Index); /* If free_source < 0 then there are no free source * slots. */ if (free_source < 0) return 0; temp = rgb->RGB.Src[srcp_src]; rgb->RGB.Src[srcp_src] = rgb->RGB.Src[free_source]; /* srcp needs src0 and src1 to be the same */ if (free_source < srcp_src) { if (!temp.Used) continue; free_source = rc_pair_alloc_source(rgb, 1, 0, srcp.File, srcp.Index); one_way = 1; } else { rgb->RGB.Src[free_source] = temp; } /* If free_source == srcp_src, then the presubtract * source is already in the correct place. */ if (free_source == srcp_src) continue; /* Shuffle the sources, so we can put the * presubtract source in the correct place. */ for (arg = 0; arg < rgb_info->NumSrcRegs; arg++) { /*If this arg does not read from an rgb source, * do nothing. */ if (rc_source_type_that_arg_reads( rgb->RGB.Arg[arg].Source, rgb->RGB.Arg[arg].Swizzle, 3) != RC_PAIR_SOURCE_RGB) { continue; } if (rgb->RGB.Arg[arg].Source == srcp_src) rgb->RGB.Arg[arg].Source = free_source; /* We need to do this just in case register * is one of the sources already, but in the * wrong spot. */ else if(rgb->RGB.Arg[arg].Source == free_source && !one_way) { rgb->RGB.Arg[arg].Source = srcp_src; } } } } /* Merge the alpha presubtract registers */ if (alpha->Alpha.Src[RC_PAIR_PRESUB_SRC].Used) { unsigned int srcp_src; unsigned int srcp_regs; if(rgb->Alpha.Src[RC_PAIR_PRESUB_SRC].Used) return 0; srcp_regs = rc_presubtract_src_reg_count( alpha->Alpha.Src[RC_PAIR_PRESUB_SRC].Index); for(srcp_src = 0; srcp_src < srcp_regs; srcp_src++) { unsigned int arg; int free_source; unsigned int one_way = 0; struct radeon_pair_instruction_source srcp = alpha->Alpha.Src[srcp_src]; struct radeon_pair_instruction_source temp; /* 2nd arg of 0 means this is not an rgb source. * 3rd arg of 1 means this is an alpha source. */ free_source = rc_pair_alloc_source(rgb, 0, 1, srcp.File, srcp.Index); /* If free_source < 0 then there are no free source * slots. */ if (free_source < 0) return 0; temp = rgb->Alpha.Src[srcp_src]; rgb->Alpha.Src[srcp_src] = rgb->Alpha.Src[free_source]; /* srcp needs src0 and src1 to be the same. */ if (free_source < srcp_src) { if (!temp.Used) continue; free_source = rc_pair_alloc_source(rgb, 0, 1, temp.File, temp.Index); one_way = 1; } else { rgb->Alpha.Src[free_source] = temp; } /* If free_source == srcp_src, then the presubtract * source is already in the correct place. */ if (free_source == srcp_src) continue; /* Shuffle the sources, so we can put the * presubtract source in the correct place. */ for(arg = 0; arg < rgb_info->NumSrcRegs; arg++) { /*If this arg does not read from an alpha * source, do nothing. */ if (rc_source_type_that_arg_reads( rgb->RGB.Arg[arg].Source, rgb->RGB.Arg[arg].Swizzle, 3) != RC_PAIR_SOURCE_ALPHA) { continue; } if (rgb->RGB.Arg[arg].Source == srcp_src) rgb->RGB.Arg[arg].Source = free_source; else if (rgb->RGB.Arg[arg].Source == free_source && !one_way) { rgb->RGB.Arg[arg].Source = srcp_src; } } } } /* Copy alpha args into rgb */ opcode = rc_get_opcode_info(alpha->Alpha.Opcode); for(unsigned int arg = 0; arg < opcode->NumSrcRegs; ++arg) { unsigned int srcrgb = 0; unsigned int srcalpha = 0; unsigned int oldsrc = alpha->Alpha.Arg[arg].Source; rc_register_file file = 0; unsigned int index = 0; if (alpha->Alpha.Arg[arg].Swizzle < 3) { srcrgb = 1; file = alpha->RGB.Src[oldsrc].File; index = alpha->RGB.Src[oldsrc].Index; } else if (alpha->Alpha.Arg[arg].Swizzle < 4) { srcalpha = 1; file = alpha->Alpha.Src[oldsrc].File; index = alpha->Alpha.Src[oldsrc].Index; } int source = rc_pair_alloc_source(rgb, srcrgb, srcalpha, file, index); if (source < 0) return 0; rgb->Alpha.Arg[arg].Source = source; rgb->Alpha.Arg[arg].Swizzle = alpha->Alpha.Arg[arg].Swizzle; rgb->Alpha.Arg[arg].Abs = alpha->Alpha.Arg[arg].Abs; rgb->Alpha.Arg[arg].Negate = alpha->Alpha.Arg[arg].Negate; } /* Copy alpha opcode into rgb */ rgb->Alpha.Opcode = alpha->Alpha.Opcode; rgb->Alpha.DestIndex = alpha->Alpha.DestIndex; rgb->Alpha.WriteMask = alpha->Alpha.WriteMask; rgb->Alpha.OutputWriteMask = alpha->Alpha.OutputWriteMask; rgb->Alpha.DepthWriteMask = alpha->Alpha.DepthWriteMask; rgb->Alpha.Saturate = alpha->Alpha.Saturate; /* Merge ALU result writing */ if (alpha->WriteALUResult) { if (rgb->WriteALUResult) return 0; rgb->WriteALUResult = alpha->WriteALUResult; rgb->ALUResultCompare = alpha->ALUResultCompare; } return 1; }
/* This is a helper function for destructive_merge_instructions(). It helps * merge presubtract sources from two instructions and makes sure the * presubtract sources end up in the correct spot. This function assumes that * dst_full is an rgb instruction, meaning that it has a vector instruction(rgb) * but no scalar instruction (alpha). * @return 0 if merging the presubtract sources fails. * @retrun 1 if merging the presubtract sources succeeds. */ static int merge_presub_sources( struct rc_pair_instruction * dst_full, struct rc_pair_sub_instruction src, unsigned int type) { unsigned int srcp_src, srcp_regs, is_rgb, is_alpha; struct rc_pair_sub_instruction * dst_sub; const struct rc_opcode_info * info; assert(dst_full->Alpha.Opcode == RC_OPCODE_NOP); switch(type) { case RC_SOURCE_RGB: is_rgb = 1; is_alpha = 0; dst_sub = &dst_full->RGB; break; case RC_SOURCE_ALPHA: is_rgb = 0; is_alpha = 1; dst_sub = &dst_full->Alpha; break; default: assert(0); return 0; } info = rc_get_opcode_info(dst_full->RGB.Opcode); if (dst_sub->Src[RC_PAIR_PRESUB_SRC].Used) return 0; srcp_regs = rc_presubtract_src_reg_count( src.Src[RC_PAIR_PRESUB_SRC].Index); for(srcp_src = 0; srcp_src < srcp_regs; srcp_src++) { unsigned int arg; int free_source; unsigned int one_way = 0; struct rc_pair_instruction_source srcp = src.Src[srcp_src]; struct rc_pair_instruction_source temp; free_source = rc_pair_alloc_source(dst_full, is_rgb, is_alpha, srcp.File, srcp.Index); /* If free_source < 0 then there are no free source * slots. */ if (free_source < 0) return 0; temp = dst_sub->Src[srcp_src]; dst_sub->Src[srcp_src] = dst_sub->Src[free_source]; /* srcp needs src0 and src1 to be the same */ if (free_source < srcp_src) { if (!temp.Used) continue; free_source = rc_pair_alloc_source(dst_full, is_rgb, is_alpha, temp.File, temp.Index); if (free_source < 0) return 0; one_way = 1; } else { dst_sub->Src[free_source] = temp; } /* If free_source == srcp_src, then the presubtract * source is already in the correct place. */ if (free_source == srcp_src) continue; /* Shuffle the sources, so we can put the * presubtract source in the correct place. */ for(arg = 0; arg < info->NumSrcRegs; arg++) { /*If this arg does not read from an rgb source, * do nothing. */ if (!(rc_source_type_swz(dst_full->RGB.Arg[arg].Swizzle, 3) & type)) { continue; } if (dst_full->RGB.Arg[arg].Source == srcp_src) dst_full->RGB.Arg[arg].Source = free_source; /* We need to do this just in case register * is one of the sources already, but in the * wrong spot. */ else if(dst_full->RGB.Arg[arg].Source == free_source && !one_way) { dst_full->RGB.Arg[arg].Source = srcp_src; } } } return 1; }
/** * Return the source slot where we installed the given register access, * or -1 if no slot was free anymore. */ int rc_pair_alloc_source(struct rc_pair_instruction *pair, unsigned int rgb, unsigned int alpha, rc_register_file file, unsigned int index) { int candidate = -1; int candidate_quality = -1; unsigned int alpha_used = 0; unsigned int rgb_used = 0; int i; if ((!rgb && !alpha) || file == RC_FILE_NONE) return 0; /* Make sure only one presubtract operation is used per instruction. */ if (file == RC_FILE_PRESUB) { if (rgb && pair->RGB.Src[RC_PAIR_PRESUB_SRC].Used && index != pair->RGB.Src[RC_PAIR_PRESUB_SRC].Index) { return -1; } if (alpha && pair->Alpha.Src[RC_PAIR_PRESUB_SRC].Used && index != pair->Alpha.Src[RC_PAIR_PRESUB_SRC].Index) { return -1; } } for(i = 0; i < 3; ++i) { int q = 0; if (rgb) { if (pair->RGB.Src[i].Used) { if (pair->RGB.Src[i].File != file || pair->RGB.Src[i].Index != index) { rgb_used++; continue; } q++; } } if (alpha) { if (pair->Alpha.Src[i].Used) { if (pair->Alpha.Src[i].File != file || pair->Alpha.Src[i].Index != index) { alpha_used++; continue; } q++; } } if (q > candidate_quality) { candidate_quality = q; candidate = i; } } if (file == RC_FILE_PRESUB) { candidate = RC_PAIR_PRESUB_SRC; } else if (candidate < 0 || (rgb && rgb_used > 2) || (alpha && alpha_used > 2)) { return -1; } /* candidate >= 0 */ if (rgb) { pair->RGB.Src[candidate].Used = 1; pair->RGB.Src[candidate].File = file; pair->RGB.Src[candidate].Index = index; if (candidate == RC_PAIR_PRESUB_SRC) { /* For registers with the RC_FILE_PRESUB file, * the index stores the presubtract op. */ int src_regs = rc_presubtract_src_reg_count(index); for(i = 0; i < src_regs; i++) { pair->RGB.Src[i].Used = 1; } } } if (alpha) { pair->Alpha.Src[candidate].Used = 1; pair->Alpha.Src[candidate].File = file; pair->Alpha.Src[candidate].Index = index; if (candidate == RC_PAIR_PRESUB_SRC) { /* For registers with the RC_FILE_PRESUB file, * the index stores the presubtract op. */ int src_regs = rc_presubtract_src_reg_count(index); for(i=0; i < src_regs; i++) { pair->Alpha.Src[i].Used = 1; } } } return candidate; }