dependency_wait |= next_2 ? next_2->dependencies : 0;
struct bifrost_header header = {
- .back_to_back = clause->back_to_back,
- .no_end_of_shader = (next_1 != NULL),
+ .flow_control =
+ (next_1 == NULL) ? BIFROST_FLOW_END :
+ (clause->back_to_back ? BIFROST_FLOW_NBTB : BIFROST_FLOW_NBTB_UNCONDITIONAL),
.terminate_discarded_threads = is_fragment,
.next_clause_prefetch = clause->next_clause_prefetch,
.staging_barrier = clause->staging_barrier,
default: return ".unknown";
}
}
+
+const char *
+bi_flow_control_name(enum bifrost_flow mode)
+{
+ switch (mode) {
+ case BIFROST_FLOW_END: return "eos";
+ case BIFROST_FLOW_NBTB_PC: return "nbb br_pc";
+ case BIFROST_FLOW_NBTB_UNCONDITIONAL: return "nbb r_uncond";
+ case BIFROST_FLOW_NBTB: return "nbb";
+ case BIFROST_FLOW_BTB_UNCONDITIONAL: return "bb r_uncond";
+ case BIFROST_FLOW_BTB_NONE: return "bb";
+ case BIFROST_FLOW_WE_UNCONDITIONAL: return "we r_uncond";
+ case BIFROST_FLOW_WE: return "we";
+ default: return "XXX";
+ }
+}
BIFROST_EXCEPTIONS_PRECISE_SQRT = 3,
};
+/* Describes clause flow control, with respect to control flow and branch
+ * reconvergence.
+ *
+ * Control flow may be considered back-to-back (execute clauses back-to-back),
+ * non-back-to-back (switch warps after clause before the next clause), write
+ * elision (back-to-back and elide register slot #3 write from the clause), or
+ * end of shader.
+ *
+ * Branch reconvergence may be disabled, enabled unconditionally, or enabled
+ * based on the program counter. A clause requires reconvergence if it has a
+ * successor that can be executed without first executing the clause itself.
+ * Separate iterations of a loop are treated separately here, so it is also the
+ * case for a loop exit where the iteration count is not warp-invariant.
+ *
+ */
+
+enum bifrost_flow {
+ /* End-of-shader */
+ BIFROST_FLOW_END = 0,
+
+ /* Non back-to-back, PC-encoded reconvergence */
+ BIFROST_FLOW_NBTB_PC = 1,
+
+ /* Non back-to-back, unconditional reconvergence */
+ BIFROST_FLOW_NBTB_UNCONDITIONAL = 2,
+
+ /* Non back-to-back, no reconvergence */
+ BIFROST_FLOW_NBTB = 3,
+
+ /* Back-to-back, unconditional reconvergence */
+ BIFROST_FLOW_BTB_UNCONDITIONAL = 4,
+
+ /* Back-to-back, no reconvergence */
+ BIFROST_FLOW_BTB_NONE = 5,
+
+ /* Write elision, unconditional reconvergence */
+ BIFROST_FLOW_WE_UNCONDITIONAL = 6,
+
+ /* Write elision, no reconvergence */
+ BIFROST_FLOW_WE = 7,
+};
+
struct bifrost_header {
/* Reserved */
unsigned zero1 : 5;
/* Floating-point excception handling mode */
enum bifrost_exceptions float_exceptions : 2;
- // true if the execution mask of the next clause is the same as the mask of
- // the current clause.
- unsigned back_to_back : 1;
- unsigned no_end_of_shader: 1;
- unsigned unk2 : 2;
+ /* Enum describing the flow control, which matters for handling
+ * divergence and reconvergence efficiently */
+ enum bifrost_flow flow_control : 3;
+
+ /* Reserved */
+ unsigned zero2 : 1;
/* Terminate discarded threads, rather than continuing execution. Set
* for fragment shaders for standard GL behaviour of DISCARD. */
if (header.staging_barrier)
fprintf(fp, "osrb ");
- if (!header.no_end_of_shader)
- fprintf(fp, "eos ");
-
- if (!header.back_to_back) {
- fprintf(fp, "nbb ");
- }
+ fprintf(fp, "%s ", bi_flow_control_name(header.flow_control));
if (header.suppress_inf)
fprintf(fp, "inf_suppress ");
fprintf(fp, "ftz_au ");
assert(!header.zero1);
+ assert(!header.zero2);
if (header.float_exceptions == BIFROST_EXCEPTIONS_DISABLED)
fprintf(fp, "fpe_ts ");
if (header.message_type)
fprintf(fp, "%s ", bi_message_type_name(header.next_message_type));
- if (header.unk2)
- fprintf(fp, "unk2 ");
-
if (header.terminate_discarded_threads)
fprintf(fp, "td ");
struct bifrost_header header;
memcpy((char *) &header, (char *) &header_bits, sizeof(struct bifrost_header));
dump_header(fp, header, verbose);
- if (!header.no_end_of_shader)
+ if (header.flow_control == BIFROST_FLOW_END)
stopbit = true;
fprintf(fp, "{\n");