+static void lp_exec_endswitch(struct lp_exec_mask *mask,
+ struct lp_build_tgsi_context * bld_base)
+{
+ LLVMBuilderRef builder = mask->bld->gallivm->builder;
+ struct function_ctx *ctx = func_ctx(mask);
+
+ if (ctx->switch_stack_size > LP_MAX_TGSI_NESTING) {
+ ctx->switch_stack_size--;
+ return;
+ }
+
+ /* check if there's deferred default if so do it now */
+ if (ctx->switch_pc && !ctx->switch_in_default) {
+ LLVMValueRef prevmask, defaultmask;
+ unsigned tmp_pc;
+ prevmask = ctx->switch_stack[ctx->switch_stack_size - 1].switch_mask;
+ defaultmask = LLVMBuildNot(builder, ctx->switch_mask_default, "sw_default_mask");
+ mask->switch_mask = LLVMBuildAnd(builder, prevmask, defaultmask, "sw_mask");
+ ctx->switch_in_default = true;
+
+ lp_exec_mask_update(mask);
+
+ assert(bld_base->instructions[ctx->switch_pc - 1].Instruction.Opcode ==
+ TGSI_OPCODE_DEFAULT);
+
+ tmp_pc = bld_base->pc;
+ bld_base->pc = ctx->switch_pc;
+ /*
+ * re-purpose switch_pc to point to here again, since we stop execution of
+ * the deferred default after next break.
+ */
+ ctx->switch_pc = tmp_pc - 1;
+
+ return;
+ }
+
+ else if (ctx->switch_pc && ctx->switch_in_default) {
+ assert(bld_base->pc == ctx->switch_pc + 1);
+ }
+
+ ctx->switch_stack_size--;
+ mask->switch_mask = ctx->switch_stack[ctx->switch_stack_size].switch_mask;
+ ctx->switch_val = ctx->switch_stack[ctx->switch_stack_size].switch_val;
+ ctx->switch_mask_default = ctx->switch_stack[ctx->switch_stack_size].switch_mask_default;
+ ctx->switch_in_default = ctx->switch_stack[ctx->switch_stack_size].switch_in_default;
+ ctx->switch_pc = ctx->switch_stack[ctx->switch_stack_size].switch_pc;
+
+ ctx->break_type = ctx->break_type_stack[ctx->loop_stack_size + ctx->switch_stack_size];
+
+ lp_exec_mask_update(mask);
+}
+
+static void lp_exec_case(struct lp_exec_mask *mask,
+ LLVMValueRef caseval)
+{
+ LLVMBuilderRef builder = mask->bld->gallivm->builder;
+ struct function_ctx *ctx = func_ctx(mask);
+
+ LLVMValueRef casemask, prevmask;
+
+ if (ctx->switch_stack_size > LP_MAX_TGSI_NESTING) {
+ return;
+ }
+
+ /* skipping case mask evaluation here is NOT optional (not in all cases anyway). */
+ if (!ctx->switch_in_default) {
+ prevmask = ctx->switch_stack[ctx->switch_stack_size - 1].switch_mask;
+ casemask = lp_build_cmp(mask->bld, PIPE_FUNC_EQUAL, caseval, ctx->switch_val);
+ ctx->switch_mask_default = LLVMBuildOr(builder, casemask,
+ ctx->switch_mask_default, "sw_default_mask");
+ casemask = LLVMBuildOr(builder, casemask, mask->switch_mask, "");
+ mask->switch_mask = LLVMBuildAnd(builder, casemask, prevmask, "sw_mask");
+
+ lp_exec_mask_update(mask);
+ }
+}
+
+/*
+ * Analyse default statement in a switch.
+ * \return true if default is last statement, false otherwise
+ * \param default_pc_start contains pc of instruction to jump to
+ * if default wasn't last but there's no
+ * fallthrough into default.
+ */
+static boolean default_analyse_is_last(struct lp_exec_mask *mask,
+ struct lp_build_tgsi_context * bld_base,
+ int *default_pc_start)
+{
+ unsigned pc = bld_base->pc;
+ struct function_ctx *ctx = func_ctx(mask);
+ unsigned curr_switch_stack = ctx->switch_stack_size;
+
+ if (ctx->switch_stack_size > LP_MAX_TGSI_NESTING) {
+ return false;
+ }
+
+ /* skip over case statements which are together with default */
+ while (bld_base->instructions[pc].Instruction.Opcode == TGSI_OPCODE_CASE) {
+ pc++;
+ }
+
+ while (pc != -1 && pc < bld_base->num_instructions) {
+ unsigned opcode = bld_base->instructions[pc].Instruction.Opcode;
+ switch (opcode) {
+ case TGSI_OPCODE_CASE:
+ if (curr_switch_stack == ctx->switch_stack_size) {
+ *default_pc_start = pc - 1;
+ return false;
+ }
+ break;
+ case TGSI_OPCODE_SWITCH:
+ curr_switch_stack++;
+ break;
+ case TGSI_OPCODE_ENDSWITCH:
+ if (curr_switch_stack == ctx->switch_stack_size) {
+ *default_pc_start = pc - 1;
+ return true;
+ }
+ curr_switch_stack--;
+ break;
+ }
+ pc++;
+ }
+ /* should never arrive here */
+ assert(0);
+ return true;
+}
+
+static void lp_exec_default(struct lp_exec_mask *mask,
+ struct lp_build_tgsi_context * bld_base)
+{
+ LLVMBuilderRef builder = mask->bld->gallivm->builder;
+ struct function_ctx *ctx = func_ctx(mask);
+
+ int default_exec_pc;
+ boolean default_is_last;
+
+ if (ctx->switch_stack_size > LP_MAX_TGSI_NESTING) {
+ return;
+ }
+
+ /*
+ * This is a messy opcode, because it may not be always at the end and
+ * there can be fallthrough in and out of it.
+ */
+
+ default_is_last = default_analyse_is_last(mask, bld_base, &default_exec_pc);
+ /*
+ * If it is last statement in switch (note that case statements appearing
+ * "at the same time" as default don't change that) everything is just fine,
+ * update switch mask and go on. This means we can handle default with
+ * fallthrough INTO it without overhead, if it is last.
+ */
+ if (default_is_last) {
+ LLVMValueRef prevmask, defaultmask;
+ prevmask = ctx->switch_stack[ctx->switch_stack_size - 1].switch_mask;
+ defaultmask = LLVMBuildNot(builder, ctx->switch_mask_default, "sw_default_mask");
+ defaultmask = LLVMBuildOr(builder, defaultmask, mask->switch_mask, "");
+ mask->switch_mask = LLVMBuildAnd(builder, prevmask, defaultmask, "sw_mask");
+ ctx->switch_in_default = true;
+
+ lp_exec_mask_update(mask);
+ }
+ else {
+ /*
+ * Technically, "case" immediately before default isn't really a
+ * fallthrough, however we still have to count them as such as we
+ * already have updated the masks.
+ * If that happens in practice could add a switch optimizer pass
+ * which just gets rid of all case statements appearing together with
+ * default (or could do switch analysis at switch start time instead).
+ */
+ unsigned opcode = bld_base->instructions[bld_base->pc - 1].Instruction.Opcode;
+ boolean ft_into = (opcode != TGSI_OPCODE_BRK &&
+ opcode != TGSI_OPCODE_SWITCH);
+ /*
+ * If it is not last statement and there was no fallthrough into it,
+ * we record the PC and continue execution at next case (again, those
+ * case encountered at the same time don't count). At endswitch
+ * time, we update switchmask, and go back executing the code we skipped
+ * until the next break (possibly re-executing some code with changed mask
+ * if there was a fallthrough out of default).
+ * Finally, if it is not last statement and there was a fallthrough into it,
+ * do the same as with the former case, except instead of skipping the code
+ * just execute it without updating the mask, then go back and re-execute.
+ */
+ ctx->switch_pc = bld_base->pc;
+ if (!ft_into) {
+ bld_base->pc = default_exec_pc;
+ }
+ }
+}
+
+
+/* stores val into an address pointed to by dst_ptr.