ac/nir: clean up handle_fs_outputs_post()
[mesa.git] / src / amd / common / ac_nir_to_llvm.c
index ae569d4c7fcba289026671dcabc3a139a817b8ed..550c12d249c7a885b3fe1bdc9b143d58e8acf2a1 100644 (file)
@@ -90,7 +90,6 @@ struct nir_to_llvm_context {
        LLVMValueRef ring_offsets;
        LLVMValueRef push_constants;
        LLVMValueRef view_index;
-       LLVMValueRef num_work_groups;
        LLVMValueRef tg_size;
 
        LLVMValueRef vertex_buffers;
@@ -780,7 +779,7 @@ static void create_function(struct nir_to_llvm_context *ctx,
 
                if (ctx->shader_info->info.cs.uses_grid_size) {
                        add_arg(&args, ARG_SGPR, ctx->ac.v3i32,
-                               &ctx->num_work_groups);
+                               &ctx->abi.num_work_groups);
                }
 
                for (int i = 0; i < 3; i++) {
@@ -4308,6 +4307,9 @@ static void visit_intrinsic(struct ac_nir_context *ctx,
                result = ctx->abi->base_vertex;
                break;
        }
+       case nir_intrinsic_load_local_group_size:
+               result = ctx->abi->load_local_group_size(ctx->abi);
+               break;
        case nir_intrinsic_load_vertex_id_zero_base: {
                result = ctx->abi->vertex_id;
                break;
@@ -4373,7 +4375,7 @@ static void visit_intrinsic(struct ac_nir_context *ctx,
                result = ctx->abi->instance_id;
                break;
        case nir_intrinsic_load_num_work_groups:
-               result = ctx->nctx->num_work_groups;
+               result = ctx->abi->num_work_groups;
                break;
        case nir_intrinsic_load_local_invocation_index:
                result = visit_load_local_invocation_index(ctx->nctx);
@@ -4436,6 +4438,9 @@ static void visit_intrinsic(struct ac_nir_context *ctx,
        case nir_intrinsic_image_size:
                result = visit_image_size(ctx, instr);
                break;
+       case nir_intrinsic_shader_clock:
+               result = ac_build_shader_clock(&ctx->ac);
+               break;
        case nir_intrinsic_discard:
        case nir_intrinsic_discard_if:
                emit_discard(ctx, instr);
@@ -5312,6 +5317,9 @@ handle_vs_input_decl(struct nir_to_llvm_context *ctx,
        int index = variable->data.location - VERT_ATTRIB_GENERIC0;
        int idx = variable->data.location;
        unsigned attrib_count = glsl_count_attribute_slots(variable->type, true);
+       uint8_t input_usage_mask =
+               ctx->shader_info->info.vs.input_usage_mask[variable->data.location];
+       unsigned num_channels = util_last_bit(input_usage_mask);
 
        variable->data.driver_location = idx * 4;
 
@@ -5336,7 +5344,9 @@ handle_vs_input_decl(struct nir_to_llvm_context *ctx,
                input = ac_build_buffer_load_format(&ctx->ac, t_list,
                                                    buffer_index,
                                                    ctx->ac.i32_0,
-                                                   4, false, true);
+                                                   num_channels, false, true);
+
+               input = ac_build_expand_to_vec4(&ctx->ac, input, num_channels);
 
                for (unsigned chan = 0; chan < 4; chan++) {
                        LLVMValueRef llvm_chan = LLVMConstInt(ctx->ac.i32, chan, false);
@@ -5728,27 +5738,6 @@ setup_shared(struct ac_nir_context *ctx,
        }
 }
 
-static LLVMValueRef
-emit_float_saturate(struct ac_llvm_context *ctx, LLVMValueRef v, float lo, float hi)
-{
-       v = ac_to_float(ctx, v);
-       v = emit_intrin_2f_param(ctx, "llvm.maxnum", ctx->f32, v, LLVMConstReal(ctx->f32, lo));
-       return emit_intrin_2f_param(ctx, "llvm.minnum", ctx->f32, v, LLVMConstReal(ctx->f32, hi));
-}
-
-
-static LLVMValueRef emit_pack_int16(struct nir_to_llvm_context *ctx,
-                                       LLVMValueRef src0, LLVMValueRef src1)
-{
-       LLVMValueRef const16 = LLVMConstInt(ctx->ac.i32, 16, false);
-       LLVMValueRef comp[2];
-
-       comp[0] = LLVMBuildAnd(ctx->builder, src0, LLVMConstInt(ctx->ac.i32, 65535, 0), "");
-       comp[1] = LLVMBuildAnd(ctx->builder, src1, LLVMConstInt(ctx->ac.i32, 65535, 0), "");
-       comp[1] = LLVMBuildShl(ctx->builder, comp[1], const16, "");
-       return LLVMBuildOr(ctx->builder, comp[0], comp[1], "");
-}
-
 /* Initialize arguments for the shader export intrinsic */
 static void
 si_llvm_init_export_args(struct nir_to_llvm_context *ctx,
@@ -5774,15 +5763,16 @@ si_llvm_init_export_args(struct nir_to_llvm_context *ctx,
        args->out[2] = LLVMGetUndef(ctx->ac.f32);
        args->out[3] = LLVMGetUndef(ctx->ac.f32);
 
-       if (!values)
-               return;
-
        if (ctx->stage == MESA_SHADER_FRAGMENT && target >= V_008DFC_SQ_EXP_MRT) {
-               LLVMValueRef val[4];
                unsigned index = target - V_008DFC_SQ_EXP_MRT;
                unsigned col_format = (ctx->options->key.fs.col_format >> (4 * index)) & 0xf;
                bool is_int8 = (ctx->options->key.fs.is_int8 >> index) & 1;
                bool is_int10 = (ctx->options->key.fs.is_int10 >> index) & 1;
+               unsigned chan;
+
+               LLVMValueRef (*packf)(struct ac_llvm_context *ctx, LLVMValueRef args[2]) = NULL;
+               LLVMValueRef (*packi)(struct ac_llvm_context *ctx, LLVMValueRef args[2],
+                                     unsigned bits, bool hi) = NULL;
 
                switch(col_format) {
                case V_028714_SPI_SHADER_ZERO:
@@ -5808,106 +5798,91 @@ si_llvm_init_export_args(struct nir_to_llvm_context *ctx,
                        break;
 
                case V_028714_SPI_SHADER_FP16_ABGR:
-                       args->compr = 1;
-
-                       for (unsigned chan = 0; chan < 2; chan++) {
-                               LLVMValueRef pack_args[2] = {
-                                       values[2 * chan],
-                                       values[2 * chan + 1]
-                               };
-                               LLVMValueRef packed;
-
-                               packed = ac_build_cvt_pkrtz_f16(&ctx->ac, pack_args);
-                               args->out[chan] = packed;
-                       }
+                       packf = ac_build_cvt_pkrtz_f16;
                        break;
 
                case V_028714_SPI_SHADER_UNORM16_ABGR:
-                       for (unsigned chan = 0; chan < 4; chan++) {
-                               val[chan] = ac_build_clamp(&ctx->ac, values[chan]);
-                               val[chan] = LLVMBuildFMul(ctx->builder, val[chan],
-                                                       LLVMConstReal(ctx->ac.f32, 65535), "");
-                               val[chan] = LLVMBuildFAdd(ctx->builder, val[chan],
-                                                       LLVMConstReal(ctx->ac.f32, 0.5), "");
-                               val[chan] = LLVMBuildFPToUI(ctx->builder, val[chan],
-                                                       ctx->ac.i32, "");
-                       }
-
-                       args->compr = 1;
-                       args->out[0] = emit_pack_int16(ctx, val[0], val[1]);
-                       args->out[1] = emit_pack_int16(ctx, val[2], val[3]);
+                       packf = ac_build_cvt_pknorm_u16;
                        break;
 
                case V_028714_SPI_SHADER_SNORM16_ABGR:
-                       for (unsigned chan = 0; chan < 4; chan++) {
-                               val[chan] = emit_float_saturate(&ctx->ac, values[chan], -1, 1);
-                               val[chan] = LLVMBuildFMul(ctx->builder, val[chan],
-                                                       LLVMConstReal(ctx->ac.f32, 32767), "");
-
-                               /* If positive, add 0.5, else add -0.5. */
-                               val[chan] = LLVMBuildFAdd(ctx->builder, val[chan],
-                                               LLVMBuildSelect(ctx->builder,
-                                                       LLVMBuildFCmp(ctx->builder, LLVMRealOGE,
-                                                               val[chan], ctx->ac.f32_0, ""),
-                                                       LLVMConstReal(ctx->ac.f32, 0.5),
-                                                       LLVMConstReal(ctx->ac.f32, -0.5), ""), "");
-                               val[chan] = LLVMBuildFPToSI(ctx->builder, val[chan], ctx->ac.i32, "");
-                       }
-
-                       args->compr = 1;
-                       args->out[0] = emit_pack_int16(ctx, val[0], val[1]);
-                       args->out[1] = emit_pack_int16(ctx, val[2], val[3]);
+                       packf = ac_build_cvt_pknorm_i16;
                        break;
 
-               case V_028714_SPI_SHADER_UINT16_ABGR: {
-                       LLVMValueRef max_rgb = LLVMConstInt(ctx->ac.i32,
-                                                           is_int8 ? 255 : is_int10 ? 1023 : 65535, 0);
-                       LLVMValueRef max_alpha = !is_int10 ? max_rgb : LLVMConstInt(ctx->ac.i32, 3, 0);
+               case V_028714_SPI_SHADER_UINT16_ABGR:
+                       packi = ac_build_cvt_pk_u16;
+                       break;
 
-                       for (unsigned chan = 0; chan < 4; chan++) {
-                               val[chan] = ac_to_integer(&ctx->ac, values[chan]);
-                               val[chan] = emit_minmax_int(&ctx->ac, LLVMIntULT, val[chan], chan == 3 ? max_alpha : max_rgb);
-                       }
+               case V_028714_SPI_SHADER_SINT16_ABGR:
+                       packi = ac_build_cvt_pk_i16;
+                       break;
 
-                       args->compr = 1;
-                       args->out[0] = emit_pack_int16(ctx, val[0], val[1]);
-                       args->out[1] = emit_pack_int16(ctx, val[2], val[3]);
+               default:
+               case V_028714_SPI_SHADER_32_ABGR:
+                       memcpy(&args->out[0], values, sizeof(values[0]) * 4);
                        break;
                }
 
-               case V_028714_SPI_SHADER_SINT16_ABGR: {
-                       LLVMValueRef max_rgb = LLVMConstInt(ctx->ac.i32,
-                                                           is_int8 ? 127 : is_int10 ? 511 : 32767, 0);
-                       LLVMValueRef min_rgb = LLVMConstInt(ctx->ac.i32,
-                                                           is_int8 ? -128 : is_int10 ? -512 : -32768, 0);
-                       LLVMValueRef max_alpha = !is_int10 ? max_rgb : ctx->ac.i32_1;
-                       LLVMValueRef min_alpha = !is_int10 ? min_rgb : LLVMConstInt(ctx->ac.i32, -2, 0);
+               /* Pack f16 or norm_i16/u16. */
+               if (packf) {
+                       for (chan = 0; chan < 2; chan++) {
+                               LLVMValueRef pack_args[2] = {
+                                       values[2 * chan],
+                                       values[2 * chan + 1]
+                               };
+                               LLVMValueRef packed;
 
-                       /* Clamp. */
-                       for (unsigned chan = 0; chan < 4; chan++) {
-                               val[chan] = ac_to_integer(&ctx->ac, values[chan]);
-                               val[chan] = emit_minmax_int(&ctx->ac, LLVMIntSLT, val[chan], chan == 3 ? max_alpha : max_rgb);
-                               val[chan] = emit_minmax_int(&ctx->ac, LLVMIntSGT, val[chan], chan == 3 ? min_alpha : min_rgb);
+                               packed = packf(&ctx->ac, pack_args);
+                               args->out[chan] = ac_to_float(&ctx->ac, packed);
                        }
-
-                       args->compr = 1;
-                       args->out[0] = emit_pack_int16(ctx, val[0], val[1]);
-                       args->out[1] = emit_pack_int16(ctx, val[2], val[3]);
-                       break;
+                       args->compr = 1; /* COMPR flag */
                }
 
-               default:
-               case V_028714_SPI_SHADER_32_ABGR:
-                       memcpy(&args->out[0], values, sizeof(values[0]) * 4);
-                       break;
+               /* Pack i16/u16. */
+               if (packi) {
+                       for (chan = 0; chan < 2; chan++) {
+                               LLVMValueRef pack_args[2] = {
+                                       ac_to_integer(&ctx->ac, values[2 * chan]),
+                                       ac_to_integer(&ctx->ac, values[2 * chan + 1])
+                               };
+                               LLVMValueRef packed;
+
+                               packed = packi(&ctx->ac, pack_args,
+                                              is_int8 ? 8 : is_int10 ? 10 : 16,
+                                              chan == 1);
+                               args->out[chan] = ac_to_float(&ctx->ac, packed);
+                       }
+                       args->compr = 1; /* COMPR flag */
                }
-       } else
-               memcpy(&args->out[0], values, sizeof(values[0]) * 4);
+               return;
+       }
+
+       memcpy(&args->out[0], values, sizeof(values[0]) * 4);
 
        for (unsigned i = 0; i < 4; ++i)
                args->out[i] = ac_to_float(&ctx->ac, args->out[i]);
 }
 
+static void
+radv_export_param(struct nir_to_llvm_context *ctx, unsigned index,
+                 LLVMValueRef *values)
+{
+       struct ac_export_args args;
+
+       si_llvm_init_export_args(ctx, values,
+                                V_008DFC_SQ_EXP_PARAM + index, &args);
+       ac_build_export(&ctx->ac, &args);
+}
+
+static LLVMValueRef
+radv_load_output(struct nir_to_llvm_context *ctx, unsigned index, unsigned chan)
+{
+       LLVMValueRef output =
+               ctx->nir->outputs[radeon_llvm_reg_index_soa(index, chan)];
+
+       return LLVMBuildLoad(ctx->builder, output, "");
+}
+
 static void
 handle_vs_outputs_post(struct nir_to_llvm_context *ctx,
                       bool export_prim_id,
@@ -5944,8 +5919,7 @@ handle_vs_outputs_post(struct nir_to_llvm_context *ctx,
 
                i = VARYING_SLOT_CLIP_DIST0;
                for (j = 0; j < ctx->num_output_clips + ctx->num_output_culls; j++)
-                       slots[j] = ac_to_float(&ctx->ac, LLVMBuildLoad(ctx->builder,
-                                                              ctx->nir->outputs[radeon_llvm_reg_index_soa(i, j)], ""));
+                       slots[j] = ac_to_float(&ctx->ac, radv_load_output(ctx, i, j));
 
                for (i = ctx->num_output_clips + ctx->num_output_culls; i < 8; i++)
                        slots[i] = LLVMGetUndef(ctx->ac.f32);
@@ -5967,27 +5941,23 @@ handle_vs_outputs_post(struct nir_to_llvm_context *ctx,
        LLVMValueRef pos_values[4] = {ctx->ac.f32_0, ctx->ac.f32_0, ctx->ac.f32_0, ctx->ac.f32_1};
        if (ctx->output_mask & (1ull << VARYING_SLOT_POS)) {
                for (unsigned j = 0; j < 4; j++)
-                       pos_values[j] = LLVMBuildLoad(ctx->builder,
-                                                ctx->nir->outputs[radeon_llvm_reg_index_soa(VARYING_SLOT_POS, j)], "");
+                       pos_values[j] = radv_load_output(ctx, VARYING_SLOT_POS, j);
        }
        si_llvm_init_export_args(ctx, pos_values, V_008DFC_SQ_EXP_POS, &pos_args[0]);
 
        if (ctx->output_mask & (1ull << VARYING_SLOT_PSIZ)) {
                outinfo->writes_pointsize = true;
-               psize_value = LLVMBuildLoad(ctx->builder,
-                                           ctx->nir->outputs[radeon_llvm_reg_index_soa(VARYING_SLOT_PSIZ, 0)], "");
+               psize_value = radv_load_output(ctx, VARYING_SLOT_PSIZ, 0);
        }
 
        if (ctx->output_mask & (1ull << VARYING_SLOT_LAYER)) {
                outinfo->writes_layer = true;
-               layer_value = LLVMBuildLoad(ctx->builder,
-                                           ctx->nir->outputs[radeon_llvm_reg_index_soa(VARYING_SLOT_LAYER, 0)], "");
+               layer_value = radv_load_output(ctx, VARYING_SLOT_LAYER, 0);
        }
 
        if (ctx->output_mask & (1ull << VARYING_SLOT_VIEWPORT)) {
                outinfo->writes_viewport_index = true;
-               viewport_index_value = LLVMBuildLoad(ctx->builder,
-                                                    ctx->nir->outputs[radeon_llvm_reg_index_soa(VARYING_SLOT_VIEWPORT, 0)], "");
+               viewport_index_value = radv_load_output(ctx, VARYING_SLOT_VIEWPORT, 0);
        }
 
        if (outinfo->writes_pointsize ||
@@ -6051,50 +6021,31 @@ handle_vs_outputs_post(struct nir_to_llvm_context *ctx,
                if (!(ctx->output_mask & (1ull << i)))
                        continue;
 
-               for (unsigned j = 0; j < 4; j++)
-                       values[j] = ac_to_float(&ctx->ac, LLVMBuildLoad(ctx->builder,
-                                               ctx->nir->outputs[radeon_llvm_reg_index_soa(i, j)], ""));
-
-               if (i == VARYING_SLOT_LAYER) {
-                       target = V_008DFC_SQ_EXP_PARAM + param_count;
-                       outinfo->vs_output_param_offset[VARYING_SLOT_LAYER] = param_count;
-                       param_count++;
-               } else if (i == VARYING_SLOT_PRIMITIVE_ID) {
-                       target = V_008DFC_SQ_EXP_PARAM + param_count;
-                       outinfo->vs_output_param_offset[VARYING_SLOT_PRIMITIVE_ID] = param_count;
-                       param_count++;
-               } else if (i >= VARYING_SLOT_VAR0) {
-                       outinfo->export_mask |= 1u << (i - VARYING_SLOT_VAR0);
-                       target = V_008DFC_SQ_EXP_PARAM + param_count;
-                       outinfo->vs_output_param_offset[i] = param_count;
-                       param_count++;
-               } else
+               if (i != VARYING_SLOT_LAYER &&
+                   i != VARYING_SLOT_PRIMITIVE_ID &&
+                   i < VARYING_SLOT_VAR0)
                        continue;
 
-               si_llvm_init_export_args(ctx, values, target, &args);
+               for (unsigned j = 0; j < 4; j++)
+                       values[j] = ac_to_float(&ctx->ac, radv_load_output(ctx, i, j));
 
-               if (target >= V_008DFC_SQ_EXP_POS &&
-                   target <= (V_008DFC_SQ_EXP_POS + 3)) {
-                       memcpy(&pos_args[target - V_008DFC_SQ_EXP_POS],
-                              &args, sizeof(args));
-               } else {
-                       ac_build_export(&ctx->ac, &args);
-               }
+               radv_export_param(ctx, param_count, values);
+
+               outinfo->vs_output_param_offset[i] = param_count++;
        }
 
        if (export_prim_id) {
                LLVMValueRef values[4];
-               target = V_008DFC_SQ_EXP_PARAM + param_count;
-               outinfo->vs_output_param_offset[VARYING_SLOT_PRIMITIVE_ID] = param_count;
-               param_count++;
 
                values[0] = ctx->vs_prim_id;
                ctx->shader_info->vs.vgpr_comp_cnt = MAX2(2,
                                                          ctx->shader_info->vs.vgpr_comp_cnt);
                for (unsigned j = 1; j < 4; j++)
                        values[j] = ctx->ac.f32_0;
-               si_llvm_init_export_args(ctx, values, target, &args);
-               ac_build_export(&ctx->ac, &args);
+
+               radv_export_param(ctx, param_count, values);
+
+               outinfo->vs_output_param_offset[VARYING_SLOT_PRIMITIVE_ID] = param_count++;
                outinfo->export_prim_id = true;
        }
 
@@ -6453,12 +6404,12 @@ handle_tcs_outputs_post(struct nir_to_llvm_context *ctx)
 
 static bool
 si_export_mrt_color(struct nir_to_llvm_context *ctx,
-                   LLVMValueRef *color, unsigned param, bool is_last,
+                   LLVMValueRef *color, unsigned index, bool is_last,
                    struct ac_export_args *args)
 {
        /* Export */
-       si_llvm_init_export_args(ctx, color, param,
-                                args);
+       si_llvm_init_export_args(ctx, color,
+                                V_008DFC_SQ_EXP_MRT + index, args);
 
        if (is_last) {
                args->valid_mask = 1; /* whether the EXEC mask is valid */
@@ -6490,45 +6441,52 @@ handle_fs_outputs_post(struct nir_to_llvm_context *ctx)
 
        for (unsigned i = 0; i < RADEON_LLVM_MAX_OUTPUTS; ++i) {
                LLVMValueRef values[4];
+               bool last = false;
 
                if (!(ctx->output_mask & (1ull << i)))
                        continue;
 
-               if (i == FRAG_RESULT_DEPTH) {
-                       ctx->shader_info->fs.writes_z = true;
-                       depth = ac_to_float(&ctx->ac, LLVMBuildLoad(ctx->builder,
-                                                           ctx->nir->outputs[radeon_llvm_reg_index_soa(i, 0)], ""));
-               } else if (i == FRAG_RESULT_STENCIL) {
-                       ctx->shader_info->fs.writes_stencil = true;
-                       stencil = ac_to_float(&ctx->ac, LLVMBuildLoad(ctx->builder,
-                                                             ctx->nir->outputs[radeon_llvm_reg_index_soa(i, 0)], ""));
-               } else if (i == FRAG_RESULT_SAMPLE_MASK) {
-                       ctx->shader_info->fs.writes_sample_mask = true;
-                       samplemask = ac_to_float(&ctx->ac, LLVMBuildLoad(ctx->builder,
-                                                                 ctx->nir->outputs[radeon_llvm_reg_index_soa(i, 0)], ""));
-               } else {
-                       bool last = false;
-                       for (unsigned j = 0; j < 4; j++)
-                               values[j] = ac_to_float(&ctx->ac, LLVMBuildLoad(ctx->builder,
-                                                                       ctx->nir->outputs[radeon_llvm_reg_index_soa(i, j)], ""));
+               if (i < FRAG_RESULT_DATA0)
+                       continue;
+
+               for (unsigned j = 0; j < 4; j++)
+                       values[j] = ac_to_float(&ctx->ac,
+                                               radv_load_output(ctx, i, j));
 
-                       if (!ctx->shader_info->fs.writes_z && !ctx->shader_info->fs.writes_stencil && !ctx->shader_info->fs.writes_sample_mask)
-                               last = ctx->output_mask <= ((1ull << (i + 1)) - 1);
+               if (!ctx->shader_info->info.ps.writes_z &&
+                   !ctx->shader_info->info.ps.writes_stencil &&
+                   !ctx->shader_info->info.ps.writes_sample_mask)
+                       last = ctx->output_mask <= ((1ull << (i + 1)) - 1);
 
-                       bool ret = si_export_mrt_color(ctx, values, V_008DFC_SQ_EXP_MRT + (i - FRAG_RESULT_DATA0), last, &color_args[index]);
-                       if (ret)
-                               index++;
-               }
+               bool ret = si_export_mrt_color(ctx, values,
+                                              i - FRAG_RESULT_DATA0,
+                                              last, &color_args[index]);
+               if (ret)
+                       index++;
        }
 
+       /* Process depth, stencil, samplemask. */
+       if (ctx->shader_info->info.ps.writes_z) {
+               depth = ac_to_float(&ctx->ac,
+                                   radv_load_output(ctx, FRAG_RESULT_DEPTH, 0));
+       }
+       if (ctx->shader_info->info.ps.writes_stencil) {
+               stencil = ac_to_float(&ctx->ac,
+                                     radv_load_output(ctx, FRAG_RESULT_STENCIL, 0));
+       }
+       if (ctx->shader_info->info.ps.writes_sample_mask) {
+               samplemask = ac_to_float(&ctx->ac,
+                                        radv_load_output(ctx, FRAG_RESULT_SAMPLE_MASK, 0));
+       }
+
+       /* Export PS outputs. */
        for (unsigned i = 0; i < index; i++)
                ac_build_export(&ctx->ac, &color_args[i]);
+
        if (depth || stencil || samplemask)
                radv_export_mrt_z(ctx, depth, stencil, samplemask);
-       else if (!index) {
-               si_export_mrt_color(ctx, NULL, V_008DFC_SQ_EXP_NULL, true, &color_args[0]);
-               ac_build_export(&ctx->ac, &color_args[0]);
-       }
+       else if (!index)
+               ac_build_export_null(&ctx->ac);
 }
 
 static void