#include "brw_wm.h"
}
#include "brw_fs.h"
+#include "brw_dead_control_flow.h"
#include "main/uniforms.h"
#include "brw_fs_live_variables.h"
#include "glsl/glsl_types.h"
ALU3(MAD)
ALU2(ADDC)
ALU2(SUBB)
+ALU2(SEL)
/** Gen4 predicated IF. */
fs_inst *
return inst;
}
-/** Gen6+ IF with embedded comparison. */
+/** Gen6 IF with embedded comparison. */
fs_inst *
fs_visitor::IF(fs_reg src0, fs_reg src1, uint32_t condition)
{
- assert(brw->gen >= 6);
+ assert(brw->gen == 6);
fs_inst *inst = new(mem_ctx) fs_inst(BRW_OPCODE_IF,
reg_null_d, src0, src1);
inst->conditional_mod = condition;
this->imm.u = u;
}
-/** Fixed brw_reg Immediate value constructor. */
+/** Fixed brw_reg. */
fs_reg::fs_reg(struct brw_reg fixed_hw_reg)
{
init();
return type == BRW_REGISTER_TYPE_F ? imm.f == 1.0 : imm.i == 1;
}
+bool
+fs_reg::is_null() const
+{
+ return file == HW_REG &&
+ fixed_hw_reg.file == BRW_ARCHITECTURE_REGISTER_FILE &&
+ fixed_hw_reg.nr == BRW_ARF_NULL;
+}
+
bool
fs_reg::is_valid_3src() const
{
assert(force_uncompressed_stack >= 0);
}
-void
-fs_visitor::push_force_sechalf()
-{
- force_sechalf_stack++;
-}
-
-void
-fs_visitor::pop_force_sechalf()
-{
- force_sechalf_stack--;
- assert(force_sechalf_stack >= 0);
-}
-
/**
* Returns true if the instruction has a flag that means it won't
* update an entire destination register.
return 1;
}
+bool
+fs_inst::reads_flag()
+{
+ return predicate;
+}
+
+bool
+fs_inst::writes_flag()
+{
+ return (conditional_mod && opcode != BRW_OPCODE_SEL) ||
+ opcode == FS_OPCODE_MOV_DISPATCH_TO_FLAGS;
+}
+
/**
* Returns how many MRFs an FS opcode will write over.
*
case FS_OPCODE_FB_WRITE:
return 2;
case FS_OPCODE_UNIFORM_PULL_CONSTANT_LOAD:
- case FS_OPCODE_UNSPILL:
+ case SHADER_OPCODE_GEN4_SCRATCH_READ:
return 1;
case FS_OPCODE_VARYING_PULL_CONSTANT_LOAD:
return inst->mlen;
- case FS_OPCODE_SPILL:
+ case SHADER_OPCODE_GEN4_SCRATCH_WRITE:
return 2;
case SHADER_OPCODE_UNTYPED_ATOMIC:
case SHADER_OPCODE_UNTYPED_SURFACE_READ:
return reg;
}
+void
+fs_visitor::compute_sample_position(fs_reg dst, fs_reg int_sample_pos)
+{
+ assert(dst.type == BRW_REGISTER_TYPE_F);
+
+ if (c->key.compute_pos_offset) {
+ /* Convert int_sample_pos to floating point */
+ emit(MOV(dst, int_sample_pos));
+ /* Scale to the range [0, 1] */
+ emit(MUL(dst, dst, fs_reg(1 / 16.0f)));
+ }
+ else {
+ /* From ARB_sample_shading specification:
+ * "When rendering to a non-multisample buffer, or if multisample
+ * rasterization is disabled, gl_SamplePosition will always be
+ * (0.5, 0.5).
+ */
+ emit(MOV(dst, fs_reg(0.5f)));
+ }
+}
+
+fs_reg *
+fs_visitor::emit_samplepos_setup(ir_variable *ir)
+{
+ assert(brw->gen >= 6);
+ assert(ir->type == glsl_type::vec2_type);
+
+ this->current_annotation = "compute sample position";
+ fs_reg *reg = new(this->mem_ctx) fs_reg(this, ir->type);
+ fs_reg pos = *reg;
+ fs_reg int_sample_x = fs_reg(this, glsl_type::int_type);
+ fs_reg int_sample_y = fs_reg(this, glsl_type::int_type);
+
+ /* WM will be run in MSDISPMODE_PERSAMPLE. So, only one of SIMD8 or SIMD16
+ * mode will be enabled.
+ *
+ * From the Ivy Bridge PRM, volume 2 part 1, page 344:
+ * R31.1:0 Position Offset X/Y for Slot[3:0]
+ * R31.3:2 Position Offset X/Y for Slot[7:4]
+ * .....
+ *
+ * The X, Y sample positions come in as bytes in thread payload. So, read
+ * the positions using vstride=16, width=8, hstride=2.
+ */
+ struct brw_reg sample_pos_reg =
+ stride(retype(brw_vec1_grf(c->sample_pos_reg, 0),
+ BRW_REGISTER_TYPE_B), 16, 8, 2);
+
+ emit(MOV(int_sample_x, fs_reg(sample_pos_reg)));
+ if (dispatch_width == 16) {
+ int_sample_x.sechalf = true;
+ fs_inst *inst = emit(MOV(int_sample_x,
+ fs_reg(suboffset(sample_pos_reg, 16))));
+ inst->force_sechalf = true;
+ int_sample_x.sechalf = false;
+ }
+ /* Compute gl_SamplePosition.x */
+ compute_sample_position(pos, int_sample_x);
+ pos.reg_offset++;
+ emit(MOV(int_sample_y, fs_reg(suboffset(sample_pos_reg, 1))));
+ if (dispatch_width == 16) {
+ int_sample_y.sechalf = true;
+ fs_inst *inst = emit(MOV(int_sample_y,
+ fs_reg(suboffset(sample_pos_reg, 17))));
+ inst->force_sechalf = true;
+ int_sample_y.sechalf = false;
+ }
+ /* Compute gl_SamplePosition.y */
+ compute_sample_position(pos, int_sample_y);
+ return reg;
+}
+
+fs_reg *
+fs_visitor::emit_sampleid_setup(ir_variable *ir)
+{
+ assert(brw->gen >= 6);
+
+ this->current_annotation = "compute sample id";
+ fs_reg *reg = new(this->mem_ctx) fs_reg(this, ir->type);
+
+ if (c->key.compute_sample_id) {
+ fs_reg t1 = fs_reg(this, glsl_type::int_type);
+ fs_reg t2 = fs_reg(this, glsl_type::int_type);
+ t2.type = BRW_REGISTER_TYPE_UW;
+
+ /* The PS will be run in MSDISPMODE_PERSAMPLE. For example with
+ * 8x multisampling, subspan 0 will represent sample N (where N
+ * is 0, 2, 4 or 6), subspan 1 will represent sample 1, 3, 5 or
+ * 7. We can find the value of N by looking at R0.0 bits 7:6
+ * ("Starting Sample Pair Index (SSPI)") and multiplying by two
+ * (since samples are always delivered in pairs). That is, we
+ * compute 2*((R0.0 & 0xc0) >> 6) == (R0.0 & 0xc0) >> 5. Then
+ * we need to add N to the sequence (0, 0, 0, 0, 1, 1, 1, 1) in
+ * case of SIMD8 and sequence (0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
+ * 2, 3, 3, 3, 3) in case of SIMD16. We compute this sequence by
+ * populating a temporary variable with the sequence (0, 1, 2, 3),
+ * and then reading from it using vstride=1, width=4, hstride=0.
+ * These computations hold good for 4x multisampling as well.
+ */
+ emit(BRW_OPCODE_AND, t1,
+ fs_reg(retype(brw_vec1_grf(0, 0), BRW_REGISTER_TYPE_D)),
+ fs_reg(brw_imm_d(0xc0)));
+ emit(BRW_OPCODE_SHR, t1, t1, fs_reg(5));
+ /* This works for both SIMD8 and SIMD16 */
+ emit(MOV(t2, brw_imm_v(0x3210)));
+ /* This special instruction takes care of setting vstride=1,
+ * width=4, hstride=0 of t2 during an ADD instruction.
+ */
+ emit(FS_OPCODE_SET_SAMPLE_ID, *reg, t1, t2);
+ } else {
+ /* As per GL_ARB_sample_shading specification:
+ * "When rendering to a non-multisample buffer, or if multisample
+ * rasterization is disabled, gl_SampleID will always be zero."
+ */
+ emit(BRW_OPCODE_MOV, *reg, fs_reg(0));
+ }
+
+ return reg;
+}
+
fs_reg
fs_visitor::fix_math_operand(fs_reg src)
{
break;
}
break;
+ case BRW_OPCODE_OR:
+ if (inst->src[0].equals(inst->src[1])) {
+ inst->opcode = BRW_OPCODE_MOV;
+ inst->src[1] = reg_undef;
+ progress = true;
+ break;
+ }
+ break;
+ case BRW_OPCODE_SEL:
+ if (inst->saturate && inst->src[1].file == IMM) {
+ switch (inst->conditional_mod) {
+ case BRW_CONDITIONAL_LE:
+ case BRW_CONDITIONAL_L:
+ switch (inst->src[1].type) {
+ case BRW_REGISTER_TYPE_F:
+ if (inst->src[1].imm.f >= 1.0f) {
+ inst->opcode = BRW_OPCODE_MOV;
+ inst->src[1] = reg_undef;
+ progress = true;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case BRW_CONDITIONAL_GE:
+ case BRW_CONDITIONAL_G:
+ switch (inst->src[1].type) {
+ case BRW_REGISTER_TYPE_F:
+ if (inst->src[1].imm.f <= 0.0f) {
+ inst->opcode = BRW_OPCODE_MOV;
+ inst->src[1] = reg_undef;
+ inst->conditional_mod = BRW_CONDITIONAL_NONE;
+ progress = true;
+ }
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
default:
break;
}
foreach_list_safe(node, &this->instructions) {
fs_inst *inst = (fs_inst *)node;
- if (inst->dst.file == GRF) {
+ if (inst->dst.file == GRF && !inst->has_side_effects()) {
bool dead = true;
for (int i = 0; i < inst->regs_written; i++) {
get_dead_code_hash_entry(ht, inst->dst.reg,
inst->dst.reg_offset);
- if (inst->is_partial_write()) {
- /* For a partial write, we can't remove any previous dead code
- * candidate, since we're just modifying their result, but we can
- * be dead code eliminiated ourselves.
- */
- if (entry) {
- entry->data = inst;
+ if (entry) {
+ if (inst->is_partial_write()) {
+ /* For a partial write, we can't remove any previous dead code
+ * candidate, since we're just modifying their result.
+ */
} else {
- insert_dead_code_hash(ht, inst->dst.reg, inst->dst.reg_offset,
- inst);
- }
- } else {
- if (entry) {
/* We're completely updating a channel, and there was a
* previous write to the channel that wasn't read. Kill it!
*/
fs_inst *inst = (fs_inst *)entry->data;
inst->remove();
progress = true;
- _mesa_hash_table_remove(ht, entry);
}
+ _mesa_hash_table_remove(ht, entry);
+ }
+
+ if (!inst->has_side_effects())
insert_dead_code_hash(ht, inst->dst.reg, inst->dst.reg_offset,
inst);
- }
}
}
}
}
/**
- * Implements a second type of register coalescing: This one checks if
- * the two regs involved in a raw move don't interfere, in which case
- * they can both by stored in the same place and the MOV removed.
+ * Implements register coalescing: Checks if the two registers involved in a
+ * raw move don't interfere, in which case they can both be stored in the same
+ * place and the MOV removed.
*/
bool
-fs_visitor::register_coalesce_2()
+fs_visitor::register_coalesce()
{
bool progress = false;
int var_from = live_intervals->var_from_reg(&inst->src[0]);
int var_to = live_intervals->var_from_reg(&inst->dst);
- if (live_intervals->vars_interfere(var_from, var_to))
+ if (live_intervals->vars_interfere(var_from, var_to) &&
+ !inst->dst.equals(inst->src[0]))
continue;
int reg_from = inst->src[0].reg;
return progress;
}
-bool
-fs_visitor::register_coalesce()
-{
- bool progress = false;
- int if_depth = 0;
- int loop_depth = 0;
-
- foreach_list_safe(node, &this->instructions) {
- fs_inst *inst = (fs_inst *)node;
-
- /* Make sure that we dominate the instructions we're going to
- * scan for interfering with our coalescing, or we won't have
- * scanned enough to see if anything interferes with our
- * coalescing. We don't dominate the following instructions if
- * we're in a loop or an if block.
- */
- switch (inst->opcode) {
- case BRW_OPCODE_DO:
- loop_depth++;
- break;
- case BRW_OPCODE_WHILE:
- loop_depth--;
- break;
- case BRW_OPCODE_IF:
- if_depth++;
- break;
- case BRW_OPCODE_ENDIF:
- if_depth--;
- break;
- default:
- break;
- }
- if (loop_depth || if_depth)
- continue;
-
- if (inst->opcode != BRW_OPCODE_MOV ||
- inst->is_partial_write() ||
- inst->saturate ||
- inst->dst.file != GRF || (inst->src[0].file != GRF &&
- inst->src[0].file != UNIFORM)||
- inst->dst.type != inst->src[0].type)
- continue;
-
- bool has_source_modifiers = (inst->src[0].abs ||
- inst->src[0].negate ||
- inst->src[0].smear != -1 ||
- inst->src[0].file == UNIFORM);
-
- /* Found a move of a GRF to a GRF. Let's see if we can coalesce
- * them: check for no writes to either one until the exit of the
- * program.
- */
- bool interfered = false;
-
- for (fs_inst *scan_inst = (fs_inst *)inst->next;
- !scan_inst->is_tail_sentinel();
- scan_inst = (fs_inst *)scan_inst->next) {
- if (scan_inst->dst.file == GRF) {
- if (scan_inst->overwrites_reg(inst->dst) ||
- scan_inst->overwrites_reg(inst->src[0])) {
- interfered = true;
- break;
- }
- }
-
- if (has_source_modifiers) {
- for (int i = 0; i < 3; i++) {
- if (scan_inst->src[i].file == GRF &&
- scan_inst->src[i].reg == inst->dst.reg &&
- scan_inst->src[i].reg_offset == inst->dst.reg_offset &&
- inst->dst.type != scan_inst->src[i].type)
- {
- interfered = true;
- break;
- }
- }
- }
-
-
- /* The gen6 MATH instruction can't handle source modifiers or
- * unusual register regions, so avoid coalescing those for
- * now. We should do something more specific.
- */
- if (has_source_modifiers && !can_do_source_mods(scan_inst)) {
- interfered = true;
- break;
- }
-
- if (scan_inst->mlen > 0 && scan_inst->base_mrf == -1 &&
- scan_inst->src[0].file == GRF &&
- scan_inst->src[0].reg == inst->dst.reg) {
- interfered = true;
- break;
- }
-
- /* The accumulator result appears to get used for the
- * conditional modifier generation. When negating a UD
- * value, there is a 33rd bit generated for the sign in the
- * accumulator value, so now you can't check, for example,
- * equality with a 32-bit value. See piglit fs-op-neg-uint.
- */
- if (scan_inst->conditional_mod &&
- inst->src[0].negate &&
- inst->src[0].type == BRW_REGISTER_TYPE_UD) {
- interfered = true;
- break;
- }
- }
- if (interfered) {
- continue;
- }
-
- /* Rewrite the later usage to point at the source of the move to
- * be removed.
- */
- for (fs_inst *scan_inst = inst;
- !scan_inst->is_tail_sentinel();
- scan_inst = (fs_inst *)scan_inst->next) {
- for (int i = 0; i < 3; i++) {
- if (scan_inst->src[i].file == GRF &&
- scan_inst->src[i].reg == inst->dst.reg &&
- scan_inst->src[i].reg_offset == inst->dst.reg_offset) {
- fs_reg new_src = inst->src[0];
- if (scan_inst->src[i].abs) {
- new_src.negate = 0;
- new_src.abs = 1;
- }
- new_src.negate ^= scan_inst->src[i].negate;
- new_src.sechalf = scan_inst->src[i].sechalf;
- scan_inst->src[i] = new_src;
- }
- }
- }
-
- inst->remove();
- progress = true;
- }
-
- if (progress)
- invalidate_live_intervals();
-
- return progress;
-}
-
-
bool
fs_visitor::compute_to_mrf()
{
if (inst->saturate)
printf(".sat");
if (inst->conditional_mod) {
- printf(".cmod");
+ printf("%s", conditional_modifier[inst->conditional_mod]);
if (!inst->predicate &&
(brw->gen < 5 || (inst->opcode != BRW_OPCODE_SEL &&
inst->opcode != BRW_OPCODE_IF &&
printf("***u%d***", inst->dst.reg);
break;
case HW_REG:
- printf("hw_reg%d", inst->dst.fixed_hw_reg.nr);
+ if (inst->dst.fixed_hw_reg.file == BRW_ARCHITECTURE_REGISTER_FILE) {
+ switch (inst->dst.fixed_hw_reg.nr) {
+ case BRW_ARF_NULL:
+ printf("null");
+ break;
+ case BRW_ARF_ADDRESS:
+ printf("a0.%d", inst->dst.fixed_hw_reg.subnr);
+ break;
+ case BRW_ARF_ACCUMULATOR:
+ printf("acc%d", inst->dst.fixed_hw_reg.subnr);
+ break;
+ case BRW_ARF_FLAG:
+ printf("f%d.%d", inst->dst.fixed_hw_reg.nr & 0xf,
+ inst->dst.fixed_hw_reg.subnr);
+ break;
+ default:
+ printf("arf%d.%d", inst->dst.fixed_hw_reg.nr & 0xf,
+ inst->dst.fixed_hw_reg.subnr);
+ break;
+ }
+ } else {
+ printf("hw_reg%d", inst->dst.fixed_hw_reg.nr);
+ }
if (inst->dst.fixed_hw_reg.subnr)
printf("+%d", inst->dst.fixed_hw_reg.subnr);
break;
printf("???");
break;
}
- printf(", ");
+ printf(":%s, ", reg_encoding[inst->dst.type]);
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < 3 && inst->src[i].file != BAD_FILE; i++) {
if (inst->src[i].negate)
printf("-");
if (inst->src[i].abs)
printf("-");
if (inst->src[i].fixed_hw_reg.abs)
printf("|");
- printf("hw_reg%d", inst->src[i].fixed_hw_reg.nr);
+ if (inst->src[i].fixed_hw_reg.file == BRW_ARCHITECTURE_REGISTER_FILE) {
+ switch (inst->src[i].fixed_hw_reg.nr) {
+ case BRW_ARF_NULL:
+ printf("null");
+ break;
+ case BRW_ARF_ADDRESS:
+ printf("a0.%d", inst->src[i].fixed_hw_reg.subnr);
+ break;
+ case BRW_ARF_ACCUMULATOR:
+ printf("acc%d", inst->src[i].fixed_hw_reg.subnr);
+ break;
+ case BRW_ARF_FLAG:
+ printf("f%d.%d", inst->src[i].fixed_hw_reg.nr & 0xf,
+ inst->src[i].fixed_hw_reg.subnr);
+ break;
+ default:
+ printf("arf%d.%d", inst->src[i].fixed_hw_reg.nr & 0xf,
+ inst->src[i].fixed_hw_reg.subnr);
+ break;
+ }
+ } else {
+ printf("hw_reg%d", inst->src[i].fixed_hw_reg.nr);
+ }
if (inst->src[i].fixed_hw_reg.subnr)
printf("+%d", inst->src[i].fixed_hw_reg.subnr);
if (inst->src[i].fixed_hw_reg.abs)
if (inst->src[i].abs)
printf("|");
- if (i < 3)
+ if (inst->src[i].file != IMM) {
+ printf(":%s", reg_encoding[inst->src[i].type]);
+ }
+
+ if (i < 2 && inst->src[i + 1].file != BAD_FILE)
printf(", ");
}
c->nr_payload_regs++;
}
}
+
+ c->prog_data.uses_pos_offset = c->key.compute_pos_offset;
/* R31: MSAA position offsets. */
+ if (c->prog_data.uses_pos_offset) {
+ c->sample_pos_reg = c->nr_payload_regs;
+ c->nr_payload_regs++;
+ }
+
/* R32-: bary for 32-pixel. */
/* R58-59: interp W for 32-pixel. */
{
uint32_t next_binding_table_offset = 0;
+ /* If there are no color regions, we still perform an FB write to a null
+ * renderbuffer, which we place at surface index 0.
+ */
c->prog_data.binding_table.render_target_start = next_binding_table_offset;
- next_binding_table_offset += c->key.nr_color_regions;
+ next_binding_table_offset += MAX2(c->key.nr_color_regions, 1);
assign_common_binding_table_offsets(next_binding_table_offset);
}
{
sanity_param_count = fp->Base.Parameters->NumParameters;
uint32_t orig_nr_params = c->prog_data.nr_params;
+ bool allocated_without_spills;
assign_binding_table_offsets();
/* We handle discards by keeping track of the still-live pixels in f0.1.
* Initialize it with the dispatched pixels.
*/
- if (fp->UsesKill) {
+ if (fp->UsesKill || c->key.alpha_test_func) {
fs_inst *discard_init = emit(FS_OPCODE_MOV_DISPATCH_TO_FLAGS);
discard_init->flag_subreg = 1;
}
emit(FS_OPCODE_PLACEHOLDER_HALT);
+ if (c->key.alpha_test_func)
+ emit_alpha_test();
+
emit_fb_writes();
split_virtual_grfs();
move_uniform_array_access_to_pull_constants();
+ remove_dead_constants();
setup_pull_constants();
bool progress;
progress = opt_algebraic() || progress;
progress = opt_cse() || progress;
progress = opt_copy_propagate() || progress;
+ progress = opt_peephole_sel() || progress;
+ progress = opt_peephole_predicated_break() || progress;
progress = dead_code_eliminate() || progress;
progress = dead_code_eliminate_local() || progress;
- progress = register_coalesce() || progress;
- progress = register_coalesce_2() || progress;
+ progress = dead_control_flow_eliminate(this) || progress;
+ progress = register_coalesce() || progress;
progress = compute_to_mrf() || progress;
} while (progress);
- remove_dead_constants();
-
- schedule_instructions(false);
-
lower_uniform_pull_constant_loads();
assign_curb_setup();
assign_urb_setup();
- if (0) {
- /* Debug of register spilling: Go spill everything. */
- for (int i = 0; i < virtual_grf_count; i++) {
- spill_reg(i);
- }
+ static enum instruction_scheduler_mode pre_modes[] = {
+ SCHEDULE_PRE,
+ SCHEDULE_PRE_NON_LIFO,
+ SCHEDULE_PRE_LIFO,
+ };
+
+ /* Try each scheduling heuristic to see if it can successfully register
+ * allocate without spilling. They should be ordered by decreasing
+ * performance but increasing likelihood of allocating.
+ */
+ for (unsigned i = 0; i < ARRAY_SIZE(pre_modes); i++) {
+ schedule_instructions(pre_modes[i]);
+
+ if (0) {
+ assign_regs_trivial();
+ allocated_without_spills = true;
+ } else {
+ allocated_without_spills = assign_regs(false);
+ }
+ if (allocated_without_spills)
+ break;
}
- if (0)
- assign_regs_trivial();
- else {
- while (!assign_regs()) {
- if (failed)
- break;
- }
+ if (!allocated_without_spills) {
+ /* We assume that any spilling is worse than just dropping back to
+ * SIMD8. There's probably actually some intermediate point where
+ * SIMD16 with a couple of spills is still better.
+ */
+ if (dispatch_width == 16) {
+ fail("Failure to register allocate. Reduce number of "
+ "live scalar values to avoid this.");
+ }
+
+ /* Since we're out of heuristics, just go spill registers until we
+ * get an allocation.
+ */
+ while (!assign_regs(true)) {
+ if (failed)
+ break;
+ }
}
}
assert(force_uncompressed_stack == 0);
- assert(force_sechalf_stack == 0);
/* This must come after all optimization and register allocation, since
* it inserts dead code that happens to have side effects, and it does
if (failed)
return false;
- schedule_instructions(true);
+ if (!allocated_without_spills)
+ schedule_instructions(SCHEDULE_POST);
if (dispatch_width == 8) {
c->prog_data.reg_blocks = brw_register_blocks(grf_used);