mirror of
https://gitlab.winehq.org/wine/vkd3d.git
synced 2025-12-15 08:03:30 -08:00
vkd3d-shader/ir: Handle integer division by zero in vsir_program_lower_udiv().
This achieves two things:
- The GLSL backend no longer needs to handle this by itself. Likwise, the
MSL backend won't have to either.
- We no longer handle division by zero for DXIL UDiv and URem instructions,
which leave this undefined.
This commit is contained in:
Notes:
Henri Verbeet
2025-10-13 19:32:21 +02:00
Approved-by: Giovanni Mascellani (@giomasce) Approved-by: Henri Verbeet (@hverbeet) Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1775
@@ -1454,8 +1454,8 @@ static enum vkd3d_result vsir_program_lower_imul(struct vsir_program *program,
|
||||
static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
|
||||
struct vsir_program_iterator *it, struct vsir_transformation_context *ctx)
|
||||
{
|
||||
unsigned int count = 3, src0_id, src1_id, divisor_id;
|
||||
struct vkd3d_shader_instruction *udiv, *ins, *mov;
|
||||
unsigned int count = 2;
|
||||
|
||||
udiv = vsir_program_iterator_current(it);
|
||||
|
||||
@@ -1469,9 +1469,9 @@ static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
|
||||
}
|
||||
|
||||
if (udiv->dst[0].reg.type != VKD3DSPR_NULL)
|
||||
++count;
|
||||
count += 2;
|
||||
if (udiv->dst[1].reg.type != VKD3DSPR_NULL)
|
||||
++count;
|
||||
count += 2;
|
||||
|
||||
if (!vsir_program_iterator_insert_after(it, count))
|
||||
return VKD3D_ERROR_OUT_OF_MEMORY;
|
||||
@@ -1483,14 +1483,33 @@ static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
|
||||
return VKD3D_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mov->src[0] = udiv->src[0];
|
||||
dst_param_init_ssa(&mov->dst[0], program->ssa_count, udiv->src[0].reg.data_type, udiv->src[0].reg.dimension);
|
||||
src0_id = program->ssa_count++;
|
||||
dst_param_init_ssa(&mov->dst[0], src0_id, udiv->src[0].reg.data_type, udiv->src[0].reg.dimension);
|
||||
|
||||
mov = vsir_program_iterator_next(it);
|
||||
if (!(vsir_instruction_init_with_params(program, mov, &udiv->location, VSIR_OP_MOV, 1, 1)))
|
||||
return VKD3D_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mov->src[0] = udiv->src[1];
|
||||
dst_param_init_ssa(&mov->dst[0], program->ssa_count + 1, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
src1_id = program->ssa_count++;
|
||||
dst_param_init_ssa(&mov->dst[0], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
|
||||
mov = vsir_program_iterator_next(it);
|
||||
if (!(vsir_instruction_init_with_params(program, mov, &udiv->location, VSIR_OP_MOVC, 1, 3)))
|
||||
return VKD3D_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
src_param_init_ssa(&mov->src[0], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
src_param_init_ssa(&mov->src[1], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
vsir_register_init(&mov->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
|
||||
mov->src[2].reg.dimension = udiv->src[1].reg.dimension;
|
||||
mov->src[2].reg.u.immconst_u32[0] = 1;
|
||||
mov->src[2].reg.u.immconst_u32[1] = 1;
|
||||
mov->src[2].reg.u.immconst_u32[2] = 1;
|
||||
mov->src[2].reg.u.immconst_u32[3] = 1;
|
||||
if (mov->src[2].reg.dimension == VSIR_DIMENSION_VEC4)
|
||||
mov->src[2].swizzle = VKD3D_SHADER_NO_SWIZZLE;
|
||||
divisor_id = program->ssa_count++;
|
||||
dst_param_init_ssa(&mov->dst[0], divisor_id, mov->src[1].reg.data_type, mov->src[1].reg.dimension);
|
||||
|
||||
if (udiv->dst[0].reg.type != VKD3DSPR_NULL)
|
||||
{
|
||||
@@ -1501,11 +1520,30 @@ static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
|
||||
|
||||
ins->flags = udiv->flags;
|
||||
|
||||
src_param_init_ssa(&ins->src[0], program->ssa_count,
|
||||
udiv->src[0].reg.data_type, udiv->src[0].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[1], program->ssa_count + 1,
|
||||
udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[0], src0_id, udiv->src[0].reg.data_type, udiv->src[0].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[1], divisor_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
dst_param_init_ssa(&ins->dst[0], program->ssa_count, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
|
||||
/* Like its TPF equivalent, division by zero is well-defined for
|
||||
* VSIR_OP_UDIV, and returns UINT_MAX. Division by zero is undefined
|
||||
* for VSIR_OP_UDIV_SIMPLE and VSIR_OP_UREM, so handle it here. */
|
||||
ins = vsir_program_iterator_next(it);
|
||||
if (!(vsir_instruction_init_with_params(program, ins, &udiv->location, VSIR_OP_MOVC, 1, 3)))
|
||||
return VKD3D_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
src_param_init_ssa(&ins->src[0], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[1], program->ssa_count, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
vsir_register_init(&ins->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
|
||||
ins->src[2].reg.dimension = udiv->src[1].reg.dimension;
|
||||
ins->src[2].reg.u.immconst_u32[0] = UINT_MAX;
|
||||
ins->src[2].reg.u.immconst_u32[1] = UINT_MAX;
|
||||
ins->src[2].reg.u.immconst_u32[2] = UINT_MAX;
|
||||
ins->src[2].reg.u.immconst_u32[3] = UINT_MAX;
|
||||
if (ins->src[2].reg.dimension == VSIR_DIMENSION_VEC4)
|
||||
ins->src[2].swizzle = VKD3D_SHADER_NO_SWIZZLE;
|
||||
ins->dst[0] = udiv->dst[0];
|
||||
|
||||
++program->ssa_count;
|
||||
}
|
||||
|
||||
if (udiv->dst[1].reg.type != VKD3DSPR_NULL)
|
||||
@@ -1517,15 +1555,30 @@ static enum vkd3d_result vsir_program_lower_udiv(struct vsir_program *program,
|
||||
|
||||
ins->flags = udiv->flags;
|
||||
|
||||
src_param_init_ssa(&ins->src[0], program->ssa_count,
|
||||
udiv->src[0].reg.data_type, udiv->src[0].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[1], program->ssa_count + 1,
|
||||
udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[0], src0_id, udiv->src[0].reg.data_type, udiv->src[0].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[1], divisor_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
dst_param_init_ssa(&ins->dst[0], program->ssa_count, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
|
||||
ins = vsir_program_iterator_next(it);
|
||||
if (!(vsir_instruction_init_with_params(program, ins, &udiv->location, VSIR_OP_MOVC, 1, 3)))
|
||||
return VKD3D_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
src_param_init_ssa(&ins->src[0], src1_id, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
src_param_init_ssa(&ins->src[1], program->ssa_count, udiv->src[1].reg.data_type, udiv->src[1].reg.dimension);
|
||||
vsir_register_init(&ins->src[2].reg, VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0);
|
||||
ins->src[2].reg.dimension = udiv->src[1].reg.dimension;
|
||||
ins->src[2].reg.u.immconst_u32[0] = UINT_MAX;
|
||||
ins->src[2].reg.u.immconst_u32[1] = UINT_MAX;
|
||||
ins->src[2].reg.u.immconst_u32[2] = UINT_MAX;
|
||||
ins->src[2].reg.u.immconst_u32[3] = UINT_MAX;
|
||||
if (ins->src[2].reg.dimension == VSIR_DIMENSION_VEC4)
|
||||
ins->src[2].swizzle = VKD3D_SHADER_NO_SWIZZLE;
|
||||
ins->dst[0] = udiv->dst[1];
|
||||
|
||||
++program->ssa_count;
|
||||
}
|
||||
|
||||
vkd3d_shader_instruction_make_nop(udiv);
|
||||
program->ssa_count += 2;
|
||||
|
||||
return VKD3D_OK;
|
||||
}
|
||||
|
||||
@@ -7625,14 +7625,13 @@ static void spirv_compiler_emit_bool_cast(struct spirv_compiler *compiler,
|
||||
static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compiler *compiler,
|
||||
const struct vkd3d_shader_instruction *instruction)
|
||||
{
|
||||
uint32_t src_ids[SPIRV_MAX_SRC_COUNT], condition_id = 0, uint_max_id = 0;
|
||||
struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
|
||||
const struct vkd3d_shader_dst_param *dst = instruction->dst;
|
||||
const struct vkd3d_shader_src_param *src = instruction->src;
|
||||
unsigned int i, component_count;
|
||||
uint32_t src_ids[SPIRV_MAX_SRC_COUNT];
|
||||
uint32_t type_id, val_id;
|
||||
SpvOp op = SpvOpMax;
|
||||
bool check_zero;
|
||||
unsigned int i;
|
||||
|
||||
if (src->reg.data_type == VSIR_DATA_U64 && instruction->opcode == VSIR_OP_COUNTBITS)
|
||||
{
|
||||
@@ -7672,42 +7671,14 @@ static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compil
|
||||
return VKD3D_ERROR_INVALID_SHADER;
|
||||
}
|
||||
|
||||
/* SPIR-V doesn't mandate a behaviour when a denominator is zero,
|
||||
* so we have an explicit check. */
|
||||
switch (instruction->opcode)
|
||||
{
|
||||
case VSIR_OP_UDIV_SIMPLE:
|
||||
case VSIR_OP_UREM:
|
||||
check_zero = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
check_zero = false;
|
||||
break;
|
||||
}
|
||||
|
||||
VKD3D_ASSERT(instruction->dst_count == 1);
|
||||
VKD3D_ASSERT(instruction->src_count <= SPIRV_MAX_SRC_COUNT);
|
||||
if (check_zero)
|
||||
VKD3D_ASSERT(instruction->src_count == 2);
|
||||
|
||||
component_count = vsir_write_mask_component_count(dst[0].write_mask);
|
||||
type_id = spirv_compiler_get_type_id_for_dst(compiler, dst);
|
||||
|
||||
for (i = 0; i < instruction->src_count; ++i)
|
||||
src_ids[i] = spirv_compiler_emit_load_src(compiler, &src[i], dst->write_mask);
|
||||
|
||||
if (check_zero)
|
||||
{
|
||||
condition_id = spirv_compiler_emit_int_to_bool(compiler,
|
||||
VKD3D_SHADER_CONDITIONAL_OP_NZ, src[1].reg.data_type, component_count, src_ids[1]);
|
||||
|
||||
if (data_type_is_64_bit(dst[0].reg.data_type))
|
||||
uint_max_id = spirv_compiler_get_constant_uint64_vector(compiler, UINT64_MAX, component_count);
|
||||
else
|
||||
uint_max_id = spirv_compiler_get_constant_uint_vector(compiler, UINT_MAX, component_count);
|
||||
}
|
||||
|
||||
/* The SPIR-V specification states, "The resulting value is undefined if
|
||||
* Shift is greater than or equal to the bit width of the components of
|
||||
* Base." Direct3D applies only the lowest 5 bits of the shift.
|
||||
@@ -7728,9 +7699,6 @@ static enum vkd3d_result spirv_compiler_emit_alu_instruction(struct spirv_compil
|
||||
if (instruction->flags & VKD3DSI_PRECISE_XYZW)
|
||||
vkd3d_spirv_build_op_decorate(builder, val_id, SpvDecorationNoContraction, NULL, 0);
|
||||
|
||||
if (check_zero)
|
||||
val_id = vkd3d_spirv_build_op_select(builder, type_id, condition_id, val_id, uint_max_id);
|
||||
|
||||
spirv_compiler_emit_store_dst(compiler, dst, val_id);
|
||||
return VKD3D_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user