| /* |
| * Copyright (C) 2019 Collabora, Ltd. |
| * |
| * 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 AUTHORS OR COPYRIGHT HOLDERS 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. |
| */ |
| |
| /* Midgard has some accelerated support for perspective projection on the |
| * load/store pipes. So the first perspective projection pass looks for |
| * lowered/open-coded perspective projection of the form "fmul (A.xyz, |
| * frcp(A.w))" or "fmul (A.xy, frcp(A.z))" and rewrite with a native |
| * perspective division opcode (on the load/store pipe). Caveats apply: the |
| * frcp should be used only once to make this optimization worthwhile. And the |
| * source of the frcp ought to be a varying to make it worthwhile... |
| * |
| * The second pass in this file is a step #2 of sorts: fusing that load/store |
| * projection into a varying load instruction (they can be done together |
| * implicitly). This depends on the combination pass. Again caveat: the vary |
| * should only be used once to make this worthwhile. |
| */ |
| |
| #include "compiler.h" |
| |
| bool |
| midgard_opt_combine_projection(compiler_context *ctx, midgard_block *block) |
| { |
| bool progress = false; |
| |
| mir_foreach_instr_in_block_safe(block, ins) { |
| /* First search for fmul */ |
| if (ins->type != TAG_ALU_4) continue; |
| if (ins->alu.op != midgard_alu_op_fmul) continue; |
| |
| /* TODO: Flip */ |
| |
| /* Check the swizzles */ |
| |
| midgard_vector_alu_src src1 = |
| vector_alu_from_unsigned(ins->alu.src1); |
| |
| midgard_vector_alu_src src2 = |
| vector_alu_from_unsigned(ins->alu.src2); |
| |
| if (!mir_is_simple_swizzle(src1.swizzle, ins->mask)) continue; |
| if (src2.swizzle != SWIZZLE_XXXX) continue; |
| |
| /* Awesome, we're the right form. Now check where src2 is from */ |
| unsigned frcp = ins->ssa_args.src[1]; |
| unsigned to = ins->ssa_args.dest; |
| |
| if (frcp & IS_REG) continue; |
| if (to & IS_REG) continue; |
| |
| bool frcp_found = false; |
| unsigned frcp_component = 0; |
| unsigned frcp_from = 0; |
| |
| mir_foreach_instr_in_block_safe(block, sub) { |
| if (sub->ssa_args.dest != frcp) continue; |
| |
| midgard_vector_alu_src s = |
| vector_alu_from_unsigned(sub->alu.src1); |
| |
| frcp_component = s.swizzle & 3; |
| frcp_from = sub->ssa_args.src[0]; |
| |
| frcp_found = |
| (sub->type == TAG_ALU_4) && |
| (sub->alu.op == midgard_alu_op_frcp); |
| break; |
| } |
| |
| if (!frcp_found) continue; |
| if (frcp_component != COMPONENT_W && frcp_component != COMPONENT_Z) continue; |
| if (!mir_single_use(ctx, frcp)) continue; |
| |
| /* Heuristic: check if the frcp is from a single-use varying */ |
| |
| bool ok = false; |
| |
| /* One for frcp and one for fmul */ |
| if (mir_use_count(ctx, frcp_from) > 2) continue; |
| |
| mir_foreach_instr_in_block_safe(block, v) { |
| if (v->ssa_args.dest != frcp_from) continue; |
| if (v->type != TAG_LOAD_STORE_4) break; |
| if (!OP_IS_LOAD_VARY_F(v->load_store.op)) break; |
| |
| ok = true; |
| break; |
| } |
| |
| if (!ok) |
| continue; |
| |
| /* Nice, we got the form spot on. Let's convert! */ |
| |
| midgard_instruction accel = { |
| .type = TAG_LOAD_STORE_4, |
| .mask = ins->mask, |
| .ssa_args = { |
| .dest = to, |
| .src = { frcp_from, -1, -1 }, |
| }, |
| .load_store = { |
| .op = frcp_component == COMPONENT_W ? |
| midgard_op_ldst_perspective_division_w : |
| midgard_op_ldst_perspective_division_z, |
| .swizzle = SWIZZLE_XYZW, |
| .arg_1 = 0x20 |
| } |
| }; |
| |
| mir_insert_instruction_before(ins, accel); |
| mir_remove_instruction(ins); |
| |
| progress |= true; |
| } |
| |
| return progress; |
| } |
| |
| bool |
| midgard_opt_varying_projection(compiler_context *ctx, midgard_block *block) |
| { |
| bool progress = false; |
| |
| mir_foreach_instr_in_block_safe(block, ins) { |
| /* Search for a projection */ |
| if (ins->type != TAG_LOAD_STORE_4) continue; |
| if (!OP_IS_PROJECTION(ins->load_store.op)) continue; |
| |
| unsigned vary = ins->ssa_args.src[0]; |
| unsigned to = ins->ssa_args.dest; |
| |
| if (vary & IS_REG) continue; |
| if (to & IS_REG) continue; |
| if (!mir_single_use(ctx, vary)) continue; |
| |
| /* Check for a varying source. If we find it, we rewrite */ |
| |
| bool rewritten = false; |
| |
| mir_foreach_instr_in_block_safe(block, v) { |
| if (v->ssa_args.dest != vary) continue; |
| if (v->type != TAG_LOAD_STORE_4) break; |
| if (!OP_IS_LOAD_VARY_F(v->load_store.op)) break; |
| |
| /* We found it, so rewrite it to project. Grab the |
| * modifier */ |
| |
| unsigned param = v->load_store.varying_parameters; |
| midgard_varying_parameter p; |
| memcpy(&p, ¶m, sizeof(p)); |
| |
| if (p.modifier != midgard_varying_mod_none) |
| break; |
| |
| bool projects_w = |
| ins->load_store.op == midgard_op_ldst_perspective_division_w; |
| |
| p.modifier = projects_w ? |
| midgard_varying_mod_perspective_w : |
| midgard_varying_mod_perspective_z; |
| |
| /* Aliasing rules are annoying */ |
| memcpy(¶m, &p, sizeof(p)); |
| v->load_store.varying_parameters = param; |
| |
| /* Use the new destination */ |
| v->ssa_args.dest = to; |
| |
| rewritten = true; |
| break; |
| } |
| |
| if (rewritten) |
| mir_remove_instruction(ins); |
| |
| progress |= rewritten; |
| } |
| |
| return progress; |
| } |