diff options
author | Marek Olšák <[email protected]> | 2016-10-18 15:20:22 +0200 |
---|---|---|
committer | Marek Olšák <[email protected]> | 2016-10-19 22:21:46 +0200 |
commit | 3ec9975555d1cc5365413ad9062f412904f944a3 (patch) | |
tree | b4114b1d6728a39be7c1997c090bf30259d1ac81 /src/gallium/drivers/radeonsi/si_shader.c | |
parent | 041da0ae81d021b00ae65647338e739664f0d505 (diff) |
radeonsi: eliminate trivial constant VS outputs
These constant value VS PARAM exports:
- 0,0,0,0
- 0,0,0,1
- 1,1,1,0
- 1,1,1,1
can be loaded into PS inputs using the DEFAULT_VAL field, and the VS exports
can be removed from the IR to save export & parameter memory.
After LLVM optimizations, analyze the IR to see which exports are equal to
the ones listed above (or undef) and remove them if they are.
Targeted use cases:
- All DX9 eON ports always clear 10 VS outputs to 0.0 even if most of them
are unused by PS (such as Witcher 2 below).
- VS output arrays with unused elements that the GLSL compiler can't
eliminate (such as Batman below).
The shader-db deltas are quite interesting:
(not from upstream si-report.py, it won't be upstreamed)
PERCENTAGE DELTAS Shaders PARAM exports (affected only)
batman_arkham_origins 589 -67.17 %
bioshock-infinite 1769 -0.47 %
dirt-showdown 548 -2.68 %
dota2 1747 -3.36 %
f1-2015 776 -4.94 %
left_4_dead_2 1762 -0.07 %
metro_2033_redux 2670 -0.43 %
portal 474 -0.22 %
talos_principle 324 -3.63 %
warsow 176 -2.20 %
witcher2 1040 -73.78 %
----------------------------------------
All affected 991 -65.37 % ... 9681 -> 3353
----------------------------------------
Total 26725 -10.82 % ... 58490 -> 52162
v2: treat Undef as both 0 and 1
Reviewed-by: Nicolai Hähnle <[email protected]> (v1)
Tested-by: Edmondo Tommasina <[email protected]> (v1)
Diffstat (limited to 'src/gallium/drivers/radeonsi/si_shader.c')
-rw-r--r-- | src/gallium/drivers/radeonsi/si_shader.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/src/gallium/drivers/radeonsi/si_shader.c b/src/gallium/drivers/radeonsi/si_shader.c index 6a42a8f5dd9..a810d9ae8fc 100644 --- a/src/gallium/drivers/radeonsi/si_shader.c +++ b/src/gallium/drivers/radeonsi/si_shader.c @@ -6523,6 +6523,159 @@ static void si_init_shader_ctx(struct si_shader_context *ctx, bld_base->op_actions[TGSI_OPCODE_BARRIER].emit = si_llvm_emit_barrier; } +/* Return true if the PARAM export has been eliminated. */ +static bool si_eliminate_const_output(struct si_shader_context *ctx, + LLVMValueRef inst, unsigned offset) +{ + struct si_shader *shader = ctx->shader; + unsigned num_outputs = shader->selector->info.num_outputs; + unsigned i, default_val; /* SPI_PS_INPUT_CNTL_i.DEFAULT_VAL */ + bool is_zero[4] = {}, is_one[4] = {}; + + for (i = 0; i < 4; i++) { + LLVMBool loses_info; + LLVMValueRef p = LLVMGetOperand(inst, 5 + i); + if (!LLVMIsConstant(p)) + return false; + + /* It's a constant expression. Undef outputs are eliminated too. */ + if (LLVMIsUndef(p)) { + is_zero[i] = true; + is_one[i] = true; + } else { + double a = LLVMConstRealGetDouble(p, &loses_info); + + if (a == 0) + is_zero[i] = true; + else if (a == 1) + is_one[i] = true; + else + return false; /* other constant */ + } + } + + /* Only certain combinations of 0 and 1 can be eliminated. */ + if (is_zero[0] && is_zero[1] && is_zero[2]) + default_val = is_zero[3] ? 0 : 1; + else if (is_one[0] && is_one[1] && is_one[2]) + default_val = is_zero[3] ? 2 : 3; + else + return false; + + /* The PARAM export can be represented as DEFAULT_VAL. Kill it. */ + LLVMInstructionEraseFromParent(inst); + + /* Change OFFSET to DEFAULT_VAL. */ + for (i = 0; i < num_outputs; i++) { + if (shader->info.vs_output_param_offset[i] == offset) { + shader->info.vs_output_param_offset[i] = + EXP_PARAM_DEFAULT_VAL_0000 + default_val; + break; + } + } + return true; +} + +struct si_vs_exports { + unsigned num; + unsigned offset[SI_MAX_VS_OUTPUTS]; + LLVMValueRef inst[SI_MAX_VS_OUTPUTS]; +}; + +static void si_eliminate_const_vs_outputs(struct si_shader_context *ctx) +{ + struct si_shader *shader = ctx->shader; + struct tgsi_shader_info *info = &shader->selector->info; + LLVMBasicBlockRef bb; + struct si_vs_exports exports; + bool removed_any = false; + + exports.num = 0; + + if ((ctx->type == PIPE_SHADER_VERTEX && + (shader->key.vs.as_es || shader->key.vs.as_ls)) || + (ctx->type == PIPE_SHADER_TESS_EVAL && shader->key.tes.as_es)) + return; + + /* Process all LLVM instructions. */ + bb = LLVMGetFirstBasicBlock(ctx->main_fn); + while (bb) { + LLVMValueRef inst = LLVMGetFirstInstruction(bb); + + while (inst) { + LLVMValueRef cur = inst; + inst = LLVMGetNextInstruction(inst); + + if (LLVMGetInstructionOpcode(cur) != LLVMCall) + continue; + + LLVMValueRef callee = LLVMGetCalledValue(cur); + LLVMValueKind kind = LLVMGetValueKind(callee); + + if (kind != LLVMFunctionValueKind) + continue; + + const char *name = LLVMGetValueName(callee); + unsigned num_args = LLVMCountParams(callee); + + /* Check if this is an export instruction. */ + if (num_args != 9 || strcmp(name, "llvm.SI.export")) + continue; + + LLVMValueRef arg = LLVMGetOperand(cur, 3); + unsigned target = LLVMConstIntGetZExtValue(arg); + + if (target < V_008DFC_SQ_EXP_PARAM) + continue; + + target -= V_008DFC_SQ_EXP_PARAM; + + /* Eliminate constant value PARAM exports. */ + if (si_eliminate_const_output(ctx, cur, target)) { + removed_any = true; + } else { + exports.offset[exports.num] = target; + exports.inst[exports.num] = cur; + exports.num++; + } + } + bb = LLVMGetNextBasicBlock(bb); + } + + /* Remove holes in export memory due to removed PARAM exports. + * This is done by renumbering all PARAM exports. + */ + if (removed_any) { + ubyte current_offset[SI_MAX_VS_OUTPUTS]; + unsigned new_count = 0; + unsigned out, i; + + /* Make a copy of the offsets. We need the old version while + * we are modifying some of them. */ + assert(sizeof(current_offset) == + sizeof(shader->info.vs_output_param_offset)); + memcpy(current_offset, shader->info.vs_output_param_offset, + sizeof(current_offset)); + + for (i = 0; i < exports.num; i++) { + unsigned offset = exports.offset[i]; + + for (out = 0; out < info->num_outputs; out++) { + if (current_offset[out] != offset) + continue; + + LLVMSetOperand(exports.inst[i], 3, + LLVMConstInt(ctx->i32, + V_008DFC_SQ_EXP_PARAM + new_count, 0)); + shader->info.vs_output_param_offset[out] = new_count; + new_count++; + break; + } + } + shader->info.nr_param_exports = new_count; + } +} + int si_compile_tgsi_shader(struct si_screen *sscreen, LLVMTargetMachineRef tm, struct si_shader *shader, @@ -6546,6 +6699,9 @@ int si_compile_tgsi_shader(struct si_screen *sscreen, si_init_shader_ctx(&ctx, sscreen, shader, tm); ctx.is_monolithic = is_monolithic; + memset(shader->info.vs_output_param_offset, 0xff, + sizeof(shader->info.vs_output_param_offset)); + shader->info.uses_instanceid = sel->info.uses_instanceid; bld_base = &ctx.soa.bld_base; @@ -6630,6 +6786,10 @@ int si_compile_tgsi_shader(struct si_screen *sscreen, si_llvm_finalize_module(&ctx, r600_extra_shader_checks(&sscreen->b, ctx.type)); + /* Post-optimization transformations. */ + si_eliminate_const_vs_outputs(&ctx); + + /* Compile to bytecode. */ r = si_compile_llvm(sscreen, &shader->binary, &shader->config, tm, mod, debug, ctx.type, "TGSI shader"); if (r) { |