panfrost/midgard: Implement integer downsize ops
authorAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Mon, 1 Jul 2019 22:26:22 +0000 (15:26 -0700)
committerAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Wed, 10 Jul 2019 13:12:03 +0000 (06:12 -0700)
Oh, dear. No turning back now.

We begin implementing non-32-bit types, using downsizing integer type
conversions as the initial instructions. We implement them naively as
type-converting moves; substantially more efficient operation is
possible by copypropping the type conversion modifier, but this
optimization is not implemented here.

Size converting modifiers on Midgard allow an instruction to write to a
destination 1/2 the size, or to read from a source 1/2 the size. If we
need an extreme conversion (32-bit to 8-bit, for instance), multiple
type converting ops are chained together, which here is handled via an
algebraic pass.

Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
src/gallium/drivers/panfrost/midgard/midgard_compile.c
src/gallium/drivers/panfrost/midgard/midgard_nir_algebraic.py

index 2c304d9066e1eff212b5f20f5db03aca38ad5ce5..109ed010d38b2ce7592b26b55394134ddce86916 100644 (file)
@@ -703,6 +703,29 @@ nir_is_fzero_constant(nir_src src)
         return true;
 }
 
+/* Analyze the sizes of inputs/outputs to determine which reg mode to run an
+ * ALU instruction in. Right now, we just consider the size of the first
+ * argument. This will fail for upconverting, for instance (TODO) */
+
+static midgard_reg_mode
+reg_mode_for_nir(nir_alu_instr *instr)
+{
+        unsigned src_bitsize = nir_src_bit_size(instr->src[0].src);
+
+        switch (src_bitsize) {
+                case 8:
+                        return midgard_reg_mode_8;
+                case 16:
+                        return midgard_reg_mode_16;
+                case 32:
+                        return midgard_reg_mode_32;
+                case 64:
+                        return midgard_reg_mode_64;
+                default:
+                        unreachable("Invalid bit size");
+        }
+}
+
 static void
 emit_alu(compiler_context *ctx, nir_alu_instr *instr)
 {
@@ -728,6 +751,12 @@ emit_alu(compiler_context *ctx, nir_alu_instr *instr)
 
         unsigned broadcast_swizzle = 0;
 
+        /* Do we need a destination override? Used for inline
+         * type conversion */
+
+        midgard_dest_override dest_override =
+                midgard_dest_override_none;
+
         switch (instr->op) {
                 ALU_CASE(fadd, fadd);
                 ALU_CASE(fmul, fmul);
@@ -824,6 +853,20 @@ emit_alu(compiler_context *ctx, nir_alu_instr *instr)
                 ALU_CASE(fneg, fmov);
                 ALU_CASE(fsat, fmov);
 
+        /* For size conversion, we use a move. Ideally though we would squash
+         * these ops together; maybe that has to happen after in NIR as part of
+         * propagation...? An earlier algebraic pass ensured we step down by
+         * only / exactly one size */
+
+        case nir_op_u2u8:
+        case nir_op_u2u16:
+        case nir_op_i2i8:
+        case nir_op_i2i16: {
+                op = midgard_alu_op_imov;
+                dest_override = midgard_dest_override_lower;
+                break;
+        }
+
         /* For greater-or-equal, we lower to less-or-equal and flip the
          * arguments */
 
@@ -958,8 +1001,8 @@ emit_alu(compiler_context *ctx, nir_alu_instr *instr)
 
         midgard_vector_alu alu = {
                 .op = op,
-                .reg_mode = midgard_reg_mode_32,
-                .dest_override = midgard_dest_override_none,
+                .reg_mode = reg_mode_for_nir(instr),
+                .dest_override = dest_override,
                 .outmod = outmod,
 
                 /* Writemask only valid for non-SSA NIR */
@@ -2066,6 +2109,10 @@ mir_nontrivial_outmod(midgard_instruction *ins)
         bool is_int = midgard_is_integer_op(ins->alu.op);
         unsigned mod = ins->alu.outmod;
 
+        /* Type conversion is a sort of outmod */
+        if (ins->alu.dest_override != midgard_dest_override_none)
+                return true;
+
         if (is_int)
                 return mod != midgard_outmod_int_wrap;
         else
index df0caa2664029b7fcc4e8e84a7cc3a679b648953..b05c193e507877fca5b178f731358dfcf15edb48 100644 (file)
@@ -44,6 +44,18 @@ algebraic_late = [
     (('b32csel', a, 0, 'b@32'), ('iand', ('inot', a), b)),
 ]
 
+
+# Midgard is able to type convert down by only one "step" per instruction; if
+# NIR wants more than one step, we need to break up into multiple instructions
+
+converts = [
+    (('i2i8', 'a@32'), ('i2i8', ('i2i16', a))),
+    (('u2u8', 'a@32'), ('u2u8', ('u2u16', a))),
+
+    (('i2i32', 'a@8'), ('i2i32', ('i2i16', a))),
+    (('u2u32', 'a@8'), ('u2u32', ('u2u16', a)))
+]
+
 # Midgard scales fsin/fcos arguments by pi.
 # Pass must be run only once, after the main loop
 
@@ -66,7 +78,7 @@ def run():
     print('#include "midgard_nir.h"')
 
     print(nir_algebraic.AlgebraicPass("midgard_nir_lower_algebraic_late",
-                                      algebraic_late).render())
+                                      algebraic_late + converts).render())
 
     print(nir_algebraic.AlgebraicPass("midgard_nir_scale_trig",
                                       scale_trig).render())