panfrost/midgard: Implement "pipeline register" prepass
authorAlyssa Rosenzweig <alyssa@rosenzweig.io>
Thu, 23 May 2019 01:40:23 +0000 (01:40 +0000)
committerAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Tue, 4 Jun 2019 20:14:50 +0000 (20:14 +0000)
This prepass, run after scheduling but before RA, specializes to
pipeline registers where possible. It walks the IR, checking whether
sources are ever used outside of the immediate bundle in which they are
written. If they are not, they are rewritten to a pipeline register (r24
or r25), valid only within the bundle itself. This has theoretical
benefits for power consumption and register pressure (and performance by
extension). While this is tested to work, it's not clear how much of a
win it really is, especially without an out-of-order scheduler (yet!).

Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Reviewed-by: Ryan Houdek <Sonicadvance1@gmail.com>
src/gallium/drivers/panfrost/meson.build
src/gallium/drivers/panfrost/midgard/compiler.h
src/gallium/drivers/panfrost/midgard/midgard_ra_pipeline.c [new file with mode: 0644]
src/gallium/drivers/panfrost/midgard/midgard_schedule.c

index 297bdb341bd2b9b0a5d9e3e102c5601a7efa3f94..b65f027f2c4e0f0edcb08a84011b1b6c8038865f 100644 (file)
@@ -32,6 +32,7 @@ files_panfrost = files(
   'midgard/midgard_schedule.c',
   'midgard/midgard_emit.c',
   'midgard/midgard_ra.c',
+  'midgard/midgard_ra_pipeline.c',
   'midgard/midgard_liveness.c',
   'midgard/midgard_ops.c',
 
index 37ed2362263213072570cb923b26a2e3b80f2d1d..1191c5cc7b856a9abcbc613004f48ba79d8f8f3a 100644 (file)
@@ -430,6 +430,8 @@ struct ra_graph* allocate_registers(compiler_context *ctx);
 void install_registers(compiler_context *ctx, struct ra_graph *g);
 bool mir_is_live_after(compiler_context *ctx, midgard_block *block, midgard_instruction *start, int src);
 
+void mir_create_pipeline_registers(compiler_context *ctx);
+
 /* Final emission */
 
 void emit_binary_bundle(
diff --git a/src/gallium/drivers/panfrost/midgard/midgard_ra_pipeline.c b/src/gallium/drivers/panfrost/midgard/midgard_ra_pipeline.c
new file mode 100644 (file)
index 0000000..4de1b91
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 Alyssa Rosenzweig <alyssa@rosenzweig.io>
+ *
+ * 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.
+ */
+
+#include "compiler.h"
+
+/* Creates pipeline registers. This is a prepass run before the main register
+ * allocator but after scheduling, once bundles are created. It works by
+ * iterating the scheduled IR, checking if a value is ever used after the end
+ * of the current bundle. If it is not, it is promoted to a bundle-specific
+ * pipeline register.
+ *
+ * Pipeline registers are only written from the first two stages of the
+ * pipeline (vmul/sadd) lasting the duration of the bundle only. There are two
+ * 128-bit pipeline registers available (r24/r25). The upshot is that no actual
+ * register allocation is needed; we can _always_ promote a value to a pipeline
+ * register, liveness permitting. This greatly simplifies the logic of this
+ * passing, negating the need for a proper RA like work registers.
+ */
+
+static bool
+mir_pipeline_ins(
+                compiler_context *ctx,
+                midgard_block *block,
+                midgard_bundle *bundle, unsigned i,
+                unsigned pipeline_count)
+{
+        midgard_instruction *ins = bundle->instructions[i];
+        unsigned dest = ins->ssa_args.dest;
+
+        /* Check to make sure we're legal */
+
+        if (ins->compact_branch)
+                return false;
+
+        if ((dest < 0) || (dest >= SSA_FIXED_MINIMUM))
+                return false;
+
+        /* We want to know if we live after this bundle, so check if
+         * we're live after the last instruction of the bundle */
+
+        midgard_instruction *end = bundle->instructions[
+                bundle->instruction_count - 1];
+
+        if (mir_is_live_after(ctx, block, end, ins->ssa_args.dest))
+                return false;
+
+        /* We're only live in this bundle -- pipeline! */
+
+        mir_rewrite_index(ctx, dest, SSA_FIXED_REGISTER(24 + pipeline_count));
+
+        return true;
+}
+
+void
+mir_create_pipeline_registers(compiler_context *ctx)
+{
+        mir_foreach_block(ctx, block) {
+                mir_foreach_bundle_in_block(block, bundle) {
+                        if (!mir_is_alu_bundle(bundle)) continue;
+                        if (bundle->instruction_count < 2) continue;
+
+                        /* Only first 2 instructions could pipeline */
+                        bool succ = mir_pipeline_ins(ctx, block, bundle, 0, 0);
+                        mir_pipeline_ins(ctx, block, bundle, 1, succ);
+                }
+        }
+}
index 385b8bcdbc01c479063ed8e8125b3e54a6cce0f9..1a562af8142919dacbe3612a612c7171e1b9fb9f 100644 (file)
@@ -470,10 +470,14 @@ void
 schedule_program(compiler_context *ctx)
 {
         /* We run RA prior to scheduling */
-        struct ra_graph *g = allocate_registers(ctx);
-        install_registers(ctx, g);
 
         mir_foreach_block(ctx, block) {
                 schedule_block(ctx, block);
         }
+
+        /* Pipeline registers creation is a prepass before RA */
+        mir_create_pipeline_registers(ctx);
+
+        struct ra_graph *g = allocate_registers(ctx);
+        install_registers(ctx, g);
 }