llvmpipe: Proper control flow builders.
[mesa.git] / src / gallium / drivers / llvmpipe / lp_state_fs.c
index a9b2d48244614dc31560a0e5f1a79dd6c680575d..2e60da60cd1477506073b669eaa6b8c953aceb91 100644 (file)
 #include "tgsi/tgsi_scan.h"
 #include "tgsi/tgsi_parse.h"
 #include "lp_bld_type.h"
+#include "lp_bld_const.h"
 #include "lp_bld_conv.h"
+#include "lp_bld_intr.h"
 #include "lp_bld_logic.h"
 #include "lp_bld_depth.h"
+#include "lp_bld_interp.h"
 #include "lp_bld_tgsi.h"
 #include "lp_bld_alpha.h"
 #include "lp_bld_blend.h"
 #include "lp_context.h"
 #include "lp_state.h"
 #include "lp_quad.h"
+#include "lp_tex_sample.h"
 
 
 static const unsigned char quad_offset_x[4] = {0, 1, 0, 1};
 static const unsigned char quad_offset_y[4] = {0, 0, 1, 1};
 
 
-/**
- * Generate the position vectors.
- *
- * TODO: This should be called only once per fragment pipeline, for the first
- * quad, and the neighboring quad positions obtained by additions.
- *
- * Parameter x, y are the integer values with the quad upper left coordinates.
+/*
+ * Derive from the quad's upper left scalar coordinates the coordinates for
+ * all other quad pixels
  */
 static void
-generate_pos(LLVMBuilderRef builder,
-             LLVMValueRef x,
-             LLVMValueRef y,
-             LLVMValueRef a0_ptr,
-             LLVMValueRef dadx_ptr,
-             LLVMValueRef dady_ptr,
-             LLVMValueRef *pos)
+generate_pos0(LLVMBuilderRef builder,
+              LLVMValueRef x,
+              LLVMValueRef y,
+              LLVMValueRef *x0,
+              LLVMValueRef *y0)
 {
    LLVMTypeRef int_elem_type = LLVMInt32Type();
    LLVMTypeRef int_vec_type = LLVMVectorType(int_elem_type, QUAD_SIZE);
@@ -111,14 +109,8 @@ generate_pos(LLVMBuilderRef builder,
    LLVMTypeRef vec_type = LLVMVectorType(elem_type, QUAD_SIZE);
    LLVMValueRef x_offsets[QUAD_SIZE];
    LLVMValueRef y_offsets[QUAD_SIZE];
-   unsigned chan;
    unsigned i;
 
-   /*
-    * Derive from the quad's upper left scalar coordinates the coordinates for
-    * all other quad pixels
-    */
-
    x = lp_build_broadcast(builder, int_vec_type, x);
    y = lp_build_broadcast(builder, int_vec_type, y);
 
@@ -130,33 +122,8 @@ generate_pos(LLVMBuilderRef builder,
    x = LLVMBuildAdd(builder, x, LLVMConstVector(x_offsets, QUAD_SIZE), "");
    y = LLVMBuildAdd(builder, y, LLVMConstVector(y_offsets, QUAD_SIZE), "");
 
-   x = LLVMBuildSIToFP(builder, x, vec_type, "");
-   y = LLVMBuildSIToFP(builder, y, vec_type, "");
-
-   pos[0] = x;
-   pos[1] = y;
-
-   /* 
-    * Calculate z and w from the interpolation factors.
-    */
-
-   for(chan = 2; chan < NUM_CHANNELS; ++chan) {
-      LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), chan, 0);
-      LLVMValueRef a0   = LLVMBuildLoad(builder, LLVMBuildGEP(builder, a0_ptr,   &index, 1, ""), "");
-      LLVMValueRef dadx = LLVMBuildLoad(builder, LLVMBuildGEP(builder, dadx_ptr, &index, 1, ""), "");
-      LLVMValueRef dady = LLVMBuildLoad(builder, LLVMBuildGEP(builder, dady_ptr, &index, 1, ""), "");
-      LLVMValueRef res;
-      a0   = lp_build_broadcast(builder, vec_type, a0);
-      dadx = lp_build_broadcast(builder, vec_type, dadx);
-      dady = lp_build_broadcast(builder, vec_type, dady);
-      res = a0;
-      res = LLVMBuildAdd(builder, res, LLVMBuildMul(builder, dadx, x, ""), "");
-      res = LLVMBuildAdd(builder, res, LLVMBuildMul(builder, dady, y, ""), "");
-      pos[chan] = res;
-   }
-
-   for(chan = 0; chan < NUM_CHANNELS; ++chan)
-      lp_build_name(pos[chan], "pos.%c", "xyzw"[chan]);
+   *x0 = LLVMBuildSIToFP(builder, x, vec_type, "");
+   *y0 = LLVMBuildSIToFP(builder, y, vec_type, "");
 }
 
 
@@ -164,9 +131,8 @@ generate_pos(LLVMBuilderRef builder,
  * Generate the depth test.
  */
 static void
-generate_depth(struct llvmpipe_context *lp,
-               LLVMBuilderRef builder,
-               const struct pipe_depth_state *state,
+generate_depth(LLVMBuilderRef builder,
+               const struct lp_fragment_shader_variant_key *key,
                union lp_type src_type,
                struct lp_build_mask_context *mask,
                LLVMValueRef src,
@@ -175,10 +141,10 @@ generate_depth(struct llvmpipe_context *lp,
    const struct util_format_description *format_desc;
    union lp_type dst_type;
 
-   if(!lp->framebuffer.zsbuf)
+   if(!key->depth.enabled)
       return;
 
-   format_desc = util_format_description(lp->framebuffer.zsbuf->format);
+   format_desc = util_format_description(key->zsbuf_format);
    assert(format_desc);
 
    /* Pick the depth type. */
@@ -198,7 +164,7 @@ generate_depth(struct llvmpipe_context *lp,
 #endif
 
    lp_build_depth_test(builder,
-                       state,
+                       &key->depth,
                        dst_type,
                        format_desc,
                        mask,
@@ -216,24 +182,22 @@ generate_fs(struct llvmpipe_context *lp,
             const struct lp_fragment_shader_variant_key *key,
             LLVMBuilderRef builder,
             union lp_type type,
+            LLVMValueRef context_ptr,
             unsigned i,
-            LLVMValueRef x,
-            LLVMValueRef y,
-            LLVMValueRef a0_ptr,
-            LLVMValueRef dadx_ptr,
-            LLVMValueRef dady_ptr,
-            LLVMValueRef consts_ptr,
+            const struct lp_build_interp_soa_context *interp,
+            struct lp_build_sampler_soa *sampler,
             LLVMValueRef *pmask,
             LLVMValueRef *color,
-            LLVMValueRef depth_ptr,
-            LLVMValueRef samplers_ptr)
+            LLVMValueRef depth_ptr)
 {
    const struct tgsi_token *tokens = shader->base.tokens;
    LLVMTypeRef elem_type;
    LLVMTypeRef vec_type;
    LLVMTypeRef int_vec_type;
-   LLVMValueRef pos[NUM_CHANNELS];
+   LLVMValueRef consts_ptr;
    LLVMValueRef outputs[PIPE_MAX_SHADER_OUTPUTS][NUM_CHANNELS];
+   LLVMValueRef z = interp->pos[2];
+   struct lp_build_flow_context *flow;
    struct lp_build_mask_context mask;
    boolean early_depth_test;
    unsigned attrib;
@@ -243,27 +207,59 @@ generate_fs(struct llvmpipe_context *lp,
    vec_type = lp_build_vec_type(type);
    int_vec_type = lp_build_int_vec_type(type);
 
-   generate_pos(builder, x, y, a0_ptr, dadx_ptr, dady_ptr, pos);
+   consts_ptr = lp_jit_context_constants(builder, context_ptr);
 
-   lp_build_mask_begin(&mask, builder, type, *pmask);
+   flow = lp_build_flow_create(builder);
+
+   memset(outputs, 0, sizeof outputs);
+
+   lp_build_flow_scope_begin(flow);
+
+   /* Declare the color and z variables */
+   for (attrib = 0; attrib < shader->info.num_outputs; ++attrib) {
+      for(chan = 0; chan < NUM_CHANNELS; ++chan) {
+         boolean declare = FALSE;
+         switch (shader->info.output_semantic_name[attrib]) {
+         case TGSI_SEMANTIC_COLOR:
+            declare = TRUE;
+            break;
+         case TGSI_SEMANTIC_POSITION:
+            if(chan == 2)
+               declare = TRUE;
+            break;
+         }
+         if(declare) {
+            outputs[attrib][chan] = LLVMGetUndef(vec_type);
+            lp_build_flow_scope_declare(flow, &outputs[attrib][chan]);
+         }
+      }
+   }
+
+   lp_build_mask_begin(&mask, flow, type, *pmask);
 
    early_depth_test =
-      lp->depth_stencil->depth.enabled &&
-      lp->framebuffer.zsbuf &&
-      !lp->depth_stencil->alpha.enabled &&
-      !lp->fs->info.uses_kill &&
-      !lp->fs->info.writes_z;
+      key->depth.enabled &&
+      !key->alpha.enabled &&
+      !shader->info.uses_kill &&
+      !shader->info.writes_z;
 
    if(early_depth_test)
-      generate_depth(lp, builder, &key->depth,
-                          type, &mask,
-                          pos[2], depth_ptr);
-
-   memset(outputs, 0, sizeof outputs);
+      generate_depth(builder, key,
+                     type, &mask,
+                     z, depth_ptr);
 
    lp_build_tgsi_soa(builder, tokens, type, &mask,
-                     pos, a0_ptr, dadx_ptr, dady_ptr,
-                     consts_ptr, outputs, samplers_ptr);
+                     consts_ptr, interp->pos, interp->inputs,
+                     outputs, sampler);
+
+   if(!early_depth_test)
+      generate_depth(builder, key,
+                     type, &mask,
+                     z, depth_ptr);
+
+   lp_build_mask_end(&mask);
+
+   lp_build_flow_scope_end(flow);
 
    for (attrib = 0; attrib < shader->info.num_outputs; ++attrib) {
       for(chan = 0; chan < NUM_CHANNELS; ++chan) {
@@ -279,10 +275,14 @@ generate_fs(struct llvmpipe_context *lp,
 
                   /* Alpha test */
                   /* XXX: should the alpha reference value be passed separately? */
-                  if(cbuf == 0 && chan == 3)
+                  if(cbuf == 0 && chan == 3) {
+                     LLVMValueRef alpha = outputs[attrib][chan];
+                     LLVMValueRef alpha_ref_value;
+                     alpha_ref_value = lp_jit_context_alpha_ref_value(builder, context_ptr);
+                     alpha_ref_value = lp_build_broadcast(builder, vec_type, alpha_ref_value);
                      lp_build_alpha_test(builder, &key->alpha, type,
-                                         &mask,
-                                         outputs[attrib][chan]);
+                                         &mask, alpha, alpha_ref_value);
+                  }
 
                   if(cbuf == 0)
                      color[chan] = outputs[attrib][chan];
@@ -292,19 +292,14 @@ generate_fs(struct llvmpipe_context *lp,
 
             case TGSI_SEMANTIC_POSITION:
                if(chan == 2)
-                  pos[2] = outputs[attrib][chan];
+                  z = outputs[attrib][chan];
                break;
             }
          }
       }
    }
 
-   if(!early_depth_test)
-      generate_depth(lp, builder, &key->depth,
-                          type, &mask,
-                          pos[2], depth_ptr);
-
-   lp_build_mask_end(&mask);
+   lp_build_flow_destroy(flow);
 
    *pmask = mask.value;
 
@@ -318,14 +313,15 @@ static void
 generate_blend(const struct pipe_blend_state *blend,
                LLVMBuilderRef builder,
                union lp_type type,
+               LLVMValueRef context_ptr,
                LLVMValueRef mask,
                LLVMValueRef *src,
-               LLVMValueRef const_ptr,
                LLVMValueRef dst_ptr)
 {
    struct lp_build_context bld;
    LLVMTypeRef vec_type;
    LLVMTypeRef int_vec_type;
+   LLVMValueRef const_ptr;
    LLVMValueRef con[4];
    LLVMValueRef dst[4];
    LLVMValueRef res[4];
@@ -336,13 +332,13 @@ generate_blend(const struct pipe_blend_state *blend,
 
    lp_build_context_init(&bld, builder, type);
 
+   const_ptr = lp_jit_context_blend_color(builder, context_ptr);
+   const_ptr = LLVMBuildBitCast(builder, const_ptr,
+                                LLVMPointerType(vec_type, 0), "");
+
    for(chan = 0; chan < 4; ++chan) {
       LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), chan, 0);
-
-      if(const_ptr)
-         con[chan] = LLVMBuildLoad(builder, LLVMBuildGEP(builder, const_ptr, &index, 1, ""), "");
-      else
-         con[chan] = LLVMGetUndef(vec_type); /* FIXME */
+      con[chan] = LLVMBuildLoad(builder, LLVMBuildGEP(builder, const_ptr, &index, 1, ""), "");
 
       dst[chan] = LLVMBuildLoad(builder, LLVMBuildGEP(builder, dst_ptr, &index, 1, ""), "");
 
@@ -378,25 +374,27 @@ generate_fragment(struct llvmpipe_context *lp,
    LLVMTypeRef fs_int_vec_type;
    LLVMTypeRef blend_vec_type;
    LLVMTypeRef blend_int_vec_type;
-   LLVMTypeRef arg_types[10];
+   LLVMTypeRef arg_types[9];
    LLVMTypeRef func_type;
+   LLVMValueRef context_ptr;
    LLVMValueRef x;
    LLVMValueRef y;
    LLVMValueRef a0_ptr;
    LLVMValueRef dadx_ptr;
    LLVMValueRef dady_ptr;
-   LLVMValueRef consts_ptr;
    LLVMValueRef mask_ptr;
    LLVMValueRef color_ptr;
    LLVMValueRef depth_ptr;
-   LLVMValueRef samplers_ptr;
    LLVMBasicBlockRef block;
    LLVMBuilderRef builder;
+   LLVMValueRef x0;
+   LLVMValueRef y0;
+   struct lp_build_sampler_soa *sampler;
+   struct lp_build_interp_soa_context interp;
    LLVMValueRef fs_mask[LP_MAX_VECTOR_LENGTH];
    LLVMValueRef fs_out_color[NUM_CHANNELS][LP_MAX_VECTOR_LENGTH];
    LLVMValueRef blend_mask;
    LLVMValueRef blend_in_color[NUM_CHANNELS];
-   LLVMValueRef fetch_texel;
    unsigned num_fs;
    unsigned i;
    unsigned chan;
@@ -453,7 +451,7 @@ generate_fragment(struct llvmpipe_context *lp,
 
    /* 
     * Generate the function prototype. Any change here must be reflected in
-    * lp_state.h's lp_shader_fs_func function pointer type, and vice-versa.
+    * lp_jit.h's lp_jit_frag_func function pointer type, and vice-versa.
     */
 
    fs_elem_type = lp_build_elem_type(fs_type);
@@ -463,16 +461,15 @@ generate_fragment(struct llvmpipe_context *lp,
    blend_vec_type = lp_build_vec_type(blend_type);
    blend_int_vec_type = lp_build_int_vec_type(blend_type);
 
-   arg_types[0] = LLVMInt32Type();                     /* x */
-   arg_types[1] = LLVMInt32Type();                     /* y */
-   arg_types[2] = LLVMPointerType(fs_elem_type, 0);    /* a0 */
-   arg_types[3] = LLVMPointerType(fs_elem_type, 0);    /* dadx */
-   arg_types[4] = LLVMPointerType(fs_elem_type, 0);    /* dady */
-   arg_types[5] = LLVMPointerType(fs_elem_type, 0);    /* consts */
+   arg_types[0] = screen->context_ptr_type;            /* context */
+   arg_types[1] = LLVMInt32Type();                     /* x */
+   arg_types[2] = LLVMInt32Type();                     /* y */
+   arg_types[3] = LLVMPointerType(fs_elem_type, 0);    /* a0 */
+   arg_types[4] = LLVMPointerType(fs_elem_type, 0);    /* dadx */
+   arg_types[5] = LLVMPointerType(fs_elem_type, 0);    /* dady */
    arg_types[6] = LLVMPointerType(fs_int_vec_type, 0); /* mask */
    arg_types[7] = LLVMPointerType(blend_vec_type, 0);  /* color */
    arg_types[8] = LLVMPointerType(fs_int_vec_type, 0); /* depth */
-   arg_types[9] = LLVMPointerType(LLVMInt8Type(), 0);  /* samplers */
 
    func_type = LLVMFunctionType(LLVMVoidType(), arg_types, Elements(arg_types), 0);
 
@@ -482,27 +479,25 @@ generate_fragment(struct llvmpipe_context *lp,
       if(LLVMGetTypeKind(arg_types[i]) == LLVMPointerTypeKind)
          LLVMAddAttribute(LLVMGetParam(variant->function, i), LLVMNoAliasAttribute);
 
-   x            = LLVMGetParam(variant->function, 0);
-   y            = LLVMGetParam(variant->function, 1);
-   a0_ptr       = LLVMGetParam(variant->function, 2);
-   dadx_ptr     = LLVMGetParam(variant->function, 3);
-   dady_ptr     = LLVMGetParam(variant->function, 4);
-   consts_ptr   = LLVMGetParam(variant->function, 5);
+   context_ptr  = LLVMGetParam(variant->function, 0);
+   x            = LLVMGetParam(variant->function, 1);
+   y            = LLVMGetParam(variant->function, 2);
+   a0_ptr       = LLVMGetParam(variant->function, 3);
+   dadx_ptr     = LLVMGetParam(variant->function, 4);
+   dady_ptr     = LLVMGetParam(variant->function, 5);
    mask_ptr     = LLVMGetParam(variant->function, 6);
    color_ptr    = LLVMGetParam(variant->function, 7);
    depth_ptr    = LLVMGetParam(variant->function, 8);
-   samplers_ptr = LLVMGetParam(variant->function, 9);
 
+   lp_build_name(context_ptr, "context");
    lp_build_name(x, "x");
    lp_build_name(y, "y");
    lp_build_name(a0_ptr, "a0");
    lp_build_name(dadx_ptr, "dadx");
    lp_build_name(dady_ptr, "dady");
-   lp_build_name(consts_ptr, "consts");
    lp_build_name(mask_ptr, "mask");
    lp_build_name(color_ptr, "color");
    lp_build_name(depth_ptr, "depth");
-   lp_build_name(samplers_ptr, "samplers");
 
    /*
     * Function body
@@ -512,39 +507,48 @@ generate_fragment(struct llvmpipe_context *lp,
    builder = LLVMCreateBuilder();
    LLVMPositionBuilderAtEnd(builder, block);
 
+   generate_pos0(builder, x, y, &x0, &y0);
+
+   lp_build_interp_soa_init(&interp, shader->base.tokens, builder, fs_type,
+                            a0_ptr, dadx_ptr, dady_ptr,
+                            x0, y0, 2, 0);
+
+#if 0
+   /* C texture sampling */
+   sampler = lp_c_sampler_soa_create(context_ptr);
+#else
+   /* code generated texture sampling */
+   sampler = lp_llvm_sampler_soa_create(key->sampler, context_ptr);
+#endif
+
    for(i = 0; i < num_fs; ++i) {
       LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), i, 0);
       LLVMValueRef out_color[NUM_CHANNELS];
-      LLVMValueRef x_i;
       LLVMValueRef depth_ptr_i;
 
-      /* TODO: Reuse position interpolation */
-      x_i = LLVMBuildAdd(builder, x, LLVMConstInt(LLVMInt32Type(), 2*i, 0), "");
+      if(i != 0)
+         lp_build_interp_soa_update(&interp);
 
       fs_mask[i] = LLVMBuildLoad(builder, LLVMBuildGEP(builder, mask_ptr, &index, 1, ""), "");
       depth_ptr_i = LLVMBuildGEP(builder, depth_ptr, &index, 1, "");
 
-      generate_fs(lp,
-                  shader,
-                  key,
+      generate_fs(lp, shader, key,
                   builder,
                   fs_type,
+                  context_ptr,
                   i,
-                  x_i,
-                  y,
-                  a0_ptr,
-                  dadx_ptr,
-                  dady_ptr,
-                  consts_ptr,
+                  &interp,
+                  sampler,
                   &fs_mask[i],
                   out_color,
-                  depth_ptr_i,
-                  samplers_ptr);
+                  depth_ptr_i);
 
       for(chan = 0; chan < NUM_CHANNELS; ++chan)
          fs_out_color[chan][i] = out_color[chan];
    }
 
+   sampler->destroy(sampler);
+
    /* 
     * Convert the fs's output color and mask to fit to the blending type. 
     */
@@ -554,6 +558,7 @@ generate_fragment(struct llvmpipe_context *lp,
                     fs_out_color[chan], num_fs,
                     &blend_in_color[chan], 1);
       lp_build_name(blend_in_color[chan], "color.%c", "rgba"[chan]);
+
    }
 
    lp_build_conv_mask(builder, fs_type, blend_type,
@@ -567,9 +572,9 @@ generate_fragment(struct llvmpipe_context *lp,
    generate_blend(&key->blend,
                   builder,
                   blend_type,
+                  context_ptr,
                   blend_mask,
                   blend_in_color,
-                  NULL /* FIXME: blend_const_color */,
                   color_ptr);
 
    LLVMBuildRetVoid(builder);
@@ -592,19 +597,7 @@ generate_fragment(struct llvmpipe_context *lp,
       abort();
    }
 
-   /* Tell where the fetch_texel function is, if the shader refers to it.
-    * TODO: this should be done elsewhere.
-    */
-   fetch_texel = LLVMGetNamedFunction(screen->module, "fetch_texel");
-   if(fetch_texel) {
-      static boolean first_time = TRUE;
-      if(first_time) {
-         LLVMAddGlobalMapping(screen->engine, fetch_texel, lp_build_tgsi_fetch_texel_soa);
-         first_time = FALSE;
-      }
-   }
-
-   variant->jit_function = (lp_shader_fs_func)LLVMGetPointerToGlobal(screen->engine, variant->function);
+   variant->jit_function = (lp_jit_frag_func)LLVMGetPointerToGlobal(screen->engine, variant->function);
 
 #ifdef DEBUG
    lp_disassemble(variant->jit_function);
@@ -697,6 +690,41 @@ llvmpipe_set_constant_buffer(struct pipe_context *pipe,
 }
 
 
+/**
+ * We need to generate several variants of the fragment pipeline to match
+ * all the combinations of the contributing state atoms.
+ *
+ * TODO: there is actually no reason to tie this to context state -- the
+ * generated code could be cached globally in the screen.
+ */
+static void
+make_variant_key(struct llvmpipe_context *lp,
+                 struct lp_fragment_shader *shader,
+                 struct lp_fragment_shader_variant_key *key)
+{
+   unsigned i;
+
+   memset(key, 0, sizeof *key);
+
+   if(lp->framebuffer.zsbuf &&
+      lp->depth_stencil->depth.enabled) {
+      key->zsbuf_format = lp->framebuffer.zsbuf->format;
+      memcpy(&key->depth, &lp->depth_stencil->depth, sizeof key->depth);
+   }
+
+   key->alpha.enabled = lp->depth_stencil->alpha.enabled;
+   if(key->alpha.enabled)
+      key->alpha.func = lp->depth_stencil->alpha.func;
+   /* alpha.ref_value is passed in jit_context */
+
+   memcpy(&key->blend, lp->blend, sizeof key->blend);
+
+   for(i = 0; i < PIPE_MAX_SAMPLERS; ++i)
+      if(shader->info.file_mask[TGSI_FILE_SAMPLER] & (1 << i))
+         lp_sampler_static_state(&key->sampler[i], lp->texture[i], lp->sampler[i]);
+}
+
+
 void 
 llvmpipe_update_fs(struct llvmpipe_context *lp)
 {
@@ -704,17 +732,7 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
    struct lp_fragment_shader_variant_key key;
    struct lp_fragment_shader_variant *variant;
 
-   /* We need to generate several variants of the fragment pipeline to match
-    * all the combinations of the contributing state atoms.
-    *
-    * TODO: there is actually no reason to tie this to context state -- the
-    * generated code could be cached globally in the screen.
-    */
-
-   memset(&key, 0, sizeof key);
-   memcpy(&key.depth, &lp->depth_stencil->depth, sizeof &key.depth);
-   memcpy(&key.alpha, &lp->depth_stencil->alpha, sizeof &key.alpha);
-   memcpy(&key.blend, &lp->blend->base, sizeof &key.blend);
+   make_variant_key(lp, shader, &key);
 
    variant = shader->variants;
    while(variant) {