zink/spirv: implement if-statements
authorErik Faye-Lund <erik.faye-lund@collabora.com>
Fri, 22 Mar 2019 14:10:25 +0000 (15:10 +0100)
committerErik Faye-Lund <erik.faye-lund@collabora.com>
Mon, 28 Oct 2019 08:51:43 +0000 (08:51 +0000)
Acked-by: Jordan Justen <jordan.l.justen@intel.com>
src/gallium/drivers/zink/nir_to_spirv/nir_to_spirv.c
src/gallium/drivers/zink/nir_to_spirv/spirv_builder.c
src/gallium/drivers/zink/nir_to_spirv/spirv_builder.h

index 099f78a12550ae7fd550fbc058c078d40605f129..9fc7aa94cd2e485ed373276843d79b569ae05efc 100644 (file)
@@ -1142,13 +1142,33 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
 static void
 start_block(struct ntv_context *ctx, SpvId label)
 {
-   assert(!ctx->block_started);
+   /* terminate previous block if needed */
+   if (ctx->block_started)
+      spirv_builder_emit_branch(&ctx->builder, label);
 
    /* start new block */
    spirv_builder_label(&ctx->builder, label);
    ctx->block_started = true;
 }
 
+static void
+branch(struct ntv_context *ctx, SpvId label)
+{
+   assert(ctx->block_started);
+   spirv_builder_emit_branch(&ctx->builder, label);
+   ctx->block_started = false;
+}
+
+static void
+branch_conditional(struct ntv_context *ctx, SpvId condition, SpvId then_id,
+                   SpvId else_id)
+{
+   assert(ctx->block_started);
+   spirv_builder_emit_branch_conditional(&ctx->builder, condition,
+                                         then_id, else_id);
+   ctx->block_started = false;
+}
+
 static void
 emit_block(struct ntv_context *ctx, struct nir_block *block)
 {
@@ -1189,6 +1209,50 @@ emit_block(struct ntv_context *ctx, struct nir_block *block)
    }
 }
 
+static void
+emit_cf_list(struct ntv_context *ctx, struct exec_list *list);
+
+static SpvId
+get_src_bool(struct ntv_context *ctx, nir_src *src)
+{
+   SpvId def = get_src_uint(ctx, src);
+   assert(nir_src_bit_size(*src) == 32);
+   unsigned num_components = nir_src_num_components(*src);
+   return uvec_to_bvec(ctx, def, num_components);
+}
+
+static void
+emit_if(struct ntv_context *ctx, nir_if *if_stmt)
+{
+   SpvId condition = get_src_bool(ctx, &if_stmt->condition);
+
+   SpvId header_id = spirv_builder_new_id(&ctx->builder);
+   SpvId then_id = block_label(ctx, nir_if_first_then_block(if_stmt));
+   SpvId endif_id = spirv_builder_new_id(&ctx->builder);
+   SpvId else_id = endif_id;
+
+   bool has_else = !exec_list_is_empty(&if_stmt->else_list);
+   if (has_else) {
+      assert(nir_if_first_else_block(if_stmt)->index < ctx->num_blocks);
+      else_id = block_label(ctx, nir_if_first_else_block(if_stmt));
+   }
+
+   /* create a header-block */
+   start_block(ctx, header_id);
+   spirv_builder_emit_selection_merge(&ctx->builder, endif_id,
+                                      SpvSelectionControlMaskNone);
+   branch_conditional(ctx, condition, then_id, else_id);
+
+   emit_cf_list(ctx, &if_stmt->then_list);
+
+   if (has_else) {
+      branch(ctx, endif_id);
+      emit_cf_list(ctx, &if_stmt->else_list);
+   }
+
+   start_block(ctx, endif_id);
+}
+
 static void
 emit_cf_list(struct ntv_context *ctx, struct exec_list *list)
 {
@@ -1199,7 +1263,7 @@ emit_cf_list(struct ntv_context *ctx, struct exec_list *list)
          break;
 
       case nir_cf_node_if:
-         unreachable("nir_cf_node_if not supported");
+         emit_if(ctx, nir_cf_node_as_if(node));
          break;
 
       case nir_cf_node_loop:
index daae3458dcf1cfcbba876fb18c6b3191ff6e95de..1a02ab0fe097bb94905f656da6b4e6584700d1ac 100644 (file)
@@ -432,6 +432,61 @@ spirv_builder_emit_vector_shuffle(struct spirv_builder *b, SpvId result_type,
    return result;
 }
 
+void
+spirv_builder_emit_branch(struct spirv_builder *b, SpvId label)
+{
+   spirv_buffer_prepare(&b->instructions, 2);
+   spirv_buffer_emit_word(&b->instructions, SpvOpBranch | (2 << 16));
+   spirv_buffer_emit_word(&b->instructions, label);
+}
+
+void
+spirv_builder_emit_selection_merge(struct spirv_builder *b, SpvId merge_block,
+                                   SpvSelectionControlMask selection_control)
+{
+   spirv_buffer_prepare(&b->instructions, 3);
+   spirv_buffer_emit_word(&b->instructions, SpvOpSelectionMerge | (3 << 16));
+   spirv_buffer_emit_word(&b->instructions, merge_block);
+   spirv_buffer_emit_word(&b->instructions, selection_control);
+}
+
+void
+spirv_builder_emit_branch_conditional(struct spirv_builder *b, SpvId condition,
+                                      SpvId true_label, SpvId false_label)
+{
+   spirv_buffer_prepare(&b->instructions, 4);
+   spirv_buffer_emit_word(&b->instructions, SpvOpBranchConditional | (4 << 16));
+   spirv_buffer_emit_word(&b->instructions, condition);
+   spirv_buffer_emit_word(&b->instructions, true_label);
+   spirv_buffer_emit_word(&b->instructions, false_label);
+}
+
+SpvId
+spirv_builder_emit_phi(struct spirv_builder *b, SpvId result_type,
+                       size_t num_vars, size_t *position)
+{
+   SpvId result = spirv_builder_new_id(b);
+
+   assert(num_vars > 0);
+   int words = 3 + 2 * num_vars;
+   spirv_buffer_prepare(&b->instructions, words);
+   spirv_buffer_emit_word(&b->instructions, SpvOpPhi | (words << 16));
+   spirv_buffer_emit_word(&b->instructions, result_type);
+   spirv_buffer_emit_word(&b->instructions, result);
+   *position = b->instructions.num_words;
+   for (int i = 0; i < 2 * num_vars; ++i)
+      spirv_buffer_emit_word(&b->instructions, 0);
+   return result;
+}
+
+void
+spirv_builder_set_phi_operand(struct spirv_builder *b, size_t position,
+                              size_t index, SpvId variable, SpvId parent)
+{
+   b->instructions.words[position + index * 2 + 0] = variable;
+   b->instructions.words[position + index * 2 + 1] = parent;
+}
+
 SpvId
 spirv_builder_emit_image_sample_implicit_lod(struct spirv_builder *b,
                                              SpvId result_type,
index ddc4bbf43288ff49a7731078dea3d2c8179a5192..2fecaf445f27d44a867078091d1a25a3caea29bc 100644 (file)
@@ -175,6 +175,26 @@ spirv_builder_emit_vector_shuffle(struct spirv_builder *b, SpvId result_type,
                                   const uint32_t components[],
                                   size_t num_components);
 
+void
+spirv_builder_emit_branch(struct spirv_builder *b, SpvId label);
+
+void
+spirv_builder_emit_selection_merge(struct spirv_builder *b, SpvId merge_block,
+                                   SpvSelectionControlMask selection_control);
+
+void
+spirv_builder_emit_branch_conditional(struct spirv_builder *b, SpvId condition,
+                                      SpvId true_label, SpvId false_label);
+
+SpvId
+spirv_builder_emit_phi(struct spirv_builder *b, SpvId result_type,
+                       size_t num_vars, size_t *position);
+
+void
+spirv_builder_set_phi_operand(struct spirv_builder *b, size_t position,
+                              size_t index, SpvId variable, SpvId parent);
+
+
 SpvId
 spirv_builder_emit_image_sample_implicit_lod(struct spirv_builder *b,
                                              SpvId result_type,