From: Giovanni Mascellani gmascellani@codeweavers.com
--- libs/vkd3d-shader/ir.c | 234 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+)
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index b96e0b92c..b7d174fca 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -2632,6 +2632,237 @@ fail: return VKD3D_ERROR_OUT_OF_MEMORY; }
+static const struct vkd3d_shader_src_param *materialize_ssa_to_temps_compute_source(struct vkd3d_shader_instruction *ins, unsigned int label) +{ + unsigned int i; + + assert(ins->handler_idx == VKD3DSIH_PHI); + + for (i = 0; i < ins->src_count; i += 2) + { + if (label_from_src_param(&ins->src[i + 1]) == label) + return &ins->src[i]; + } + + vkd3d_unreachable(); +} + +static void materialize_ssa_to_temps_process_src_param(struct vkd3d_shader_parser *parser, struct vkd3d_shader_src_param *src); + +/* This is idempotent: it can be safely applied more than once on the + * same register. */ +static void materialize_ssa_to_temps_process_reg(struct vkd3d_shader_parser *parser, struct vkd3d_shader_register *reg) +{ + unsigned int i; + + if (reg->type == VKD3DSPR_SSA) + { + reg->type = VKD3DSPR_TEMP; + reg->idx[0].offset += parser->program.temp_count; + } + + for (i = 0; i < reg->idx_count; ++i) + if (reg->idx[i].rel_addr) + materialize_ssa_to_temps_process_src_param(parser, reg->idx[i].rel_addr); +} + +static void materialize_ssa_to_temps_process_dst_param(struct vkd3d_shader_parser *parser, struct vkd3d_shader_dst_param *dst) +{ + materialize_ssa_to_temps_process_reg(parser, &dst->reg); +} + +static void materialize_ssa_to_temps_process_src_param(struct vkd3d_shader_parser *parser, struct vkd3d_shader_src_param *src) +{ + materialize_ssa_to_temps_process_reg(parser, &src->reg); +} + +static bool materialize_ssa_to_temps_synthesize_mov(struct vkd3d_shader_parser *parser, + struct vkd3d_shader_instruction **instructions, size_t *ins_count, const struct vkd3d_shader_location *loc, + const struct vkd3d_shader_dst_param *dest, const struct vkd3d_shader_src_param *cond, + const struct vkd3d_shader_src_param *source, bool invert) +{ + struct vkd3d_shader_src_param *src; + struct vkd3d_shader_dst_param *dst; + + if (!vsir_instruction_init_with_params(parser, &(*instructions)[*ins_count], loc, + cond ? VKD3DSIH_MOVC : VKD3DSIH_MOV, 1, cond ? 3 : 1)) + return false; + + dst = (*instructions)[*ins_count].dst; + src = (*instructions)[*ins_count].src; + + dst[0] = *dest; + materialize_ssa_to_temps_process_dst_param(parser, &dst[0]); + + assert(dst[0].write_mask == VKD3DSP_WRITEMASK_0); + assert(dst[0].modifiers == 0); + assert(dst[0].shift == 0); + + if (cond) + { + src[0] = *cond; + src[1 + !!invert] = *source; + memset(&src[2 - !!invert], 0, sizeof(src[2 - !!invert])); + src[2 - !!invert].reg = dst[0].reg; + materialize_ssa_to_temps_process_src_param(parser, &src[1]); + materialize_ssa_to_temps_process_src_param(parser, &src[2]); + } + else + { + src[0] = *source; + materialize_ssa_to_temps_process_src_param(parser, &src[0]); + } + + ++*ins_count; + + return true; +} + +static enum vkd3d_result materialize_ssa_to_temps(struct vkd3d_shader_parser *parser) +{ + struct vkd3d_shader_instruction *instructions = NULL; + size_t ins_capacity = 0, ins_count = 0, i; + struct materialize_ssa_to_temps_block_data + { + size_t phi_begin; + size_t phi_count; + } *block_index = NULL; + unsigned int current_label = 0; + + if (!vkd3d_array_reserve((void **)&instructions, &ins_capacity, parser->program.instructions.count, sizeof(*instructions))) + { + ERR("Failed to allocate instructions.\n"); + goto fail; + } + + if (!(block_index = vkd3d_calloc(parser->program.block_count, sizeof(*block_index)))) + { + ERR("Failed to allocate block index.\n"); + goto fail; + } + + for (i = 0; i < parser->program.instructions.count; ++i) + { + struct vkd3d_shader_instruction *ins = &parser->program.instructions.elements[i]; + + switch (ins->handler_idx) + { + case VKD3DSIH_LABEL: + current_label = label_from_src_param(&ins->src[0]); + break; + + case VKD3DSIH_PHI: + assert(current_label != 0); + assert(i != 0); + if (block_index[current_label - 1].phi_begin == 0) + block_index[current_label - 1].phi_begin = i; + block_index[current_label - 1].phi_count += 1; + break; + + default: + current_label = 0; + break; + } + } + + for (i = 0; i < parser->program.instructions.count; ++i) + { + struct vkd3d_shader_instruction *ins = &parser->program.instructions.elements[i]; + size_t j; + + for (j = 0; j < ins->dst_count; ++j) + materialize_ssa_to_temps_process_dst_param(parser, &ins->dst[j]); + + for (j = 0; j < ins->src_count; ++j) + materialize_ssa_to_temps_process_src_param(parser, &ins->src[j]); + + switch (ins->handler_idx) + { + case VKD3DSIH_LABEL: + current_label = label_from_src_param(&ins->src[0]); + break; + + case VKD3DSIH_BRANCH: + { + if (vsir_register_is_label(&ins->src[0].reg)) + { + const struct materialize_ssa_to_temps_block_data *data = &block_index[label_from_src_param(&ins->src[0]) - 1]; + + if (!vkd3d_array_reserve((void **)&instructions, &ins_capacity, ins_count + data->phi_count, sizeof(*instructions))) + { + ERR("Failed to allocate instructions.\n"); + goto fail; + } + + for (j = data->phi_begin; j < data->phi_begin + data->phi_count; ++j) + { + materialize_ssa_to_temps_synthesize_mov(parser, &instructions, &ins_count, &ins->location, + &parser->program.instructions.elements[j].dst[0], NULL, + materialize_ssa_to_temps_compute_source(&parser->program.instructions.elements[j], current_label), false); + } + } + else + { + struct materialize_ssa_to_temps_block_data *data_true = &block_index[label_from_src_param(&ins->src[1]) - 1], + *data_false = &block_index[label_from_src_param(&ins->src[2]) - 1]; + const struct vkd3d_shader_src_param *cond = &ins->src[0]; + + if (!vkd3d_array_reserve((void **)&instructions, &ins_capacity, + ins_count + data_true->phi_count + data_false->phi_count, sizeof(*instructions))) + { + ERR("Failed to allocate instructions.\n"); + goto fail; + } + + for (j = data_true->phi_begin; j < data_true->phi_begin + data_true->phi_count; ++j) + { + materialize_ssa_to_temps_synthesize_mov(parser, &instructions, &ins_count, &ins->location, + &parser->program.instructions.elements[j].dst[0], cond, + materialize_ssa_to_temps_compute_source(&parser->program.instructions.elements[j], current_label), false); + } + + for (j = data_false->phi_begin; j < data_false->phi_begin + data_false->phi_count; ++j) + { + materialize_ssa_to_temps_synthesize_mov(parser, &instructions, &ins_count, &ins->location, + &parser->program.instructions.elements[j].dst[0], cond, + materialize_ssa_to_temps_compute_source(&parser->program.instructions.elements[j], current_label), true); + } + } + break; + } + + case VKD3DSIH_PHI: + continue; + + default: + break; + } + + if (!vkd3d_array_reserve((void **)&instructions, &ins_capacity, ins_count + 1, sizeof(*instructions))) + { + ERR("Failed to allocate instructions.\n"); + goto fail; + } + + instructions[ins_count++] = *ins; + } + + vkd3d_free(parser->program.instructions.elements); + vkd3d_free(block_index); + parser->program.instructions.elements = instructions; + parser->program.instructions.capacity = ins_capacity; + parser->program.instructions.count = ins_count; + parser->program.temp_count += parser->program.ssa_count; + parser->program.ssa_count = 0; + + return VKD3D_OK; + +fail: + vkd3d_free(instructions); + vkd3d_free(block_index); + return VKD3D_ERROR_OUT_OF_MEMORY; +} + enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, const struct vkd3d_shader_compile_info *compile_info) { @@ -2647,6 +2878,9 @@ enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, { if ((result = lower_switch_to_if_ladder(parser)) < 0) return result; + + if ((result = materialize_ssa_to_temps(parser)) < 0) + return result; } else {