From 749df8dec23b8b8e4085a81beeff17692c41b294 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Mon, 16 Oct 2023 15:41:17 +1000 Subject: [PATCH] vkd3d-shader/dxil: Implement the DXIL BINOP instruction. --- libs/vkd3d-shader/dxil.c | 228 ++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + .../hlsl/arithmetic-float-uniform.shader_test | 14 +- tests/hlsl/cbuffer.shader_test | 6 +- tests/hlsl/const.shader_test | 2 +- tests/hlsl/cross.shader_test | 4 +- tests/hlsl/dot.shader_test | 2 +- tests/hlsl/expr-indexing.shader_test | 2 +- tests/hlsl/lerp.shader_test | 2 +- tests/hlsl/loop.shader_test | 10 +- tests/hlsl/math.shader_test | 2 +- tests/hlsl/object-references.shader_test | 2 +- tests/hlsl/reflect.shader_test | 2 +- tests/hlsl/writemask-assignop-0.shader_test | 2 +- tests/hlsl/writemask-assignop-1.shader_test | 2 +- 15 files changed, 256 insertions(+), 25 deletions(-) diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 213f8703..e0e242cb 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -242,6 +242,45 @@ enum dxil_shader_properties_tag SHADER_PROPERTIES_ENTRY_ROOT_SIG = 12, }; +enum dxil_binop_code +{ + BINOP_ADD = 0, + BINOP_SUB = 1, + BINOP_MUL = 2, + BINOP_UDIV = 3, + BINOP_SDIV = 4, + BINOP_UREM = 5, + BINOP_SREM = 6, + BINOP_SHL = 7, + BINOP_LSHR = 8, + BINOP_ASHR = 9, + BINOP_AND = 10, + BINOP_OR = 11, + BINOP_XOR = 12 +}; + +enum dxil_fast_fp_flags +{ + FP_ALLOW_UNSAFE_ALGEBRA = 0x1, + FP_NO_NAN = 0x2, + FP_NO_INF = 0x4, + FP_NO_SIGNED_ZEROS = 0x8, + FP_ALLOW_RECIPROCAL = 0x10, +}; + +enum dxil_overflowing_binop_flags +{ + /* Operation is known to never overflow. */ + OB_NO_UNSIGNED_WRAP = 0x1, + OB_NO_SIGNED_WRAP = 0x2, +}; + +enum dxil_possibly_exact_binop_flags +{ + /* "A udiv or sdiv instruction, which can be marked as "exact", indicating that no bits are destroyed." */ + PEB_EXACT = 0x1, +}; + enum dx_intrinsic_opcode { DX_LOAD_INPUT = 4, @@ -1463,6 +1502,11 @@ static inline bool sm6_type_is_integer(const struct sm6_type *type) return type->class == TYPE_CLASS_INTEGER; } +static bool sm6_type_is_bool_i16_i32_i64(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_INTEGER && (type->u.width == 1 || type->u.width >= 16); +} + static bool sm6_type_is_bool(const struct sm6_type *type) { return type->class == TYPE_CLASS_INTEGER && type->u.width == 1; @@ -2634,6 +2678,187 @@ static struct sm6_block *sm6_block_create() return block; } +static enum vkd3d_shader_opcode map_binary_op(uint64_t code, const struct sm6_type *type_a, + const struct sm6_type *type_b, struct sm6_parser *sm6) +{ + bool is_int = sm6_type_is_bool_i16_i32_i64(type_a); + bool is_bool = sm6_type_is_bool(type_a); + enum vkd3d_shader_opcode op; + bool is_valid; + + if (!is_int && !sm6_type_is_floating_point(type_a)) + { + WARN("Argument type %u is not bool, int16/32/64 or floating point.\n", type_a->class); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "An argument to a binary operation is not bool, int16/32/64 or floating point."); + return VKD3DSIH_INVALID; + } + if (type_a != type_b) + { + WARN("Type mismatch, type %u width %u vs type %u width %u.\n", type_a->class, + type_a->u.width, type_b->class, type_b->u.width); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "Type mismatch in binary operation arguments."); + } + + switch (code) + { + case BINOP_ADD: + case BINOP_SUB: + /* NEG is applied later for subtraction. */ + op = is_int ? VKD3DSIH_IADD : VKD3DSIH_ADD; + is_valid = !is_bool; + break; + case BINOP_AND: + op = VKD3DSIH_AND; + is_valid = is_int; + break; + case BINOP_ASHR: + op = VKD3DSIH_ISHR; + is_valid = is_int && !is_bool; + break; + case BINOP_LSHR: + op = VKD3DSIH_USHR; + is_valid = is_int && !is_bool; + break; + case BINOP_MUL: + op = is_int ? VKD3DSIH_UMUL : VKD3DSIH_MUL; + is_valid = !is_bool; + break; + case BINOP_OR: + op = VKD3DSIH_OR; + is_valid = is_int; + break; + case BINOP_SDIV: + op = is_int ? VKD3DSIH_IDIV : VKD3DSIH_DIV; + is_valid = !is_bool; + break; + case BINOP_SREM: + op = is_int ? VKD3DSIH_IDIV : VKD3DSIH_FREM; + is_valid = !is_bool; + break; + case BINOP_SHL: + op = VKD3DSIH_ISHL; + is_valid = is_int && !is_bool; + break; + case BINOP_UDIV: + case BINOP_UREM: + op = VKD3DSIH_UDIV; + is_valid = is_int && !is_bool; + break; + case BINOP_XOR: + op = VKD3DSIH_XOR; + is_valid = is_int; + break; + default: + FIXME("Unhandled binary op %#"PRIx64".\n", code); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "Binary operation %#"PRIx64" is unhandled.", code); + return VKD3DSIH_INVALID; + } + + if (!is_valid) + { + WARN("Invalid operation %u for type %u, width %u.\n", op, type_a->class, type_a->u.width); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_OPERATION, + "Binary operation %u is invalid on type class %u, width %u.", op, type_a->class, type_a->u.width); + } + + return op; +} + +static void sm6_parser_emit_binop(struct sm6_parser *sm6, const struct dxil_record *record, + struct vkd3d_shader_instruction *ins, struct sm6_value *dst) +{ + struct vkd3d_shader_src_param *src_params; + enum vkd3d_shader_opcode handler_idx; + const struct sm6_value *a, *b; + unsigned int i = 0; + uint64_t code; + + a = sm6_parser_get_value_by_ref(sm6, record, NULL, &i); + b = sm6_parser_get_value_by_ref(sm6, record, a->type, &i); + if (!a || !b) + return; + + if (!dxil_record_validate_operand_count(record, i + 1, i + 2, sm6)) + return; + + code = record->operands[i++]; + if ((handler_idx = map_binary_op(code, a->type, b->type, sm6)) == VKD3DSIH_INVALID) + return; + + vsir_instruction_init(ins, &sm6->p.location, handler_idx); + + if (record->operand_count > i && record->operands[i]) + { + uint64_t flags = record->operands[i]; + bool silence_warning = false; + + switch (handler_idx) + { + case VKD3DSIH_ADD: + case VKD3DSIH_MUL: + case VKD3DSIH_DIV: + case VKD3DSIH_FREM: + if (!(flags & FP_ALLOW_UNSAFE_ALGEBRA)) + ins->flags |= VKD3DSI_PRECISE_X; + flags &= ~FP_ALLOW_UNSAFE_ALGEBRA; + /* SPIR-V FPFastMathMode is only available in the Kernel executon model. */ + silence_warning = !(flags & ~(FP_NO_NAN | FP_NO_INF | FP_NO_SIGNED_ZEROS | FP_ALLOW_RECIPROCAL)); + break; + case VKD3DSIH_IADD: + case VKD3DSIH_UMUL: + case VKD3DSIH_ISHL: + silence_warning = !(flags & ~(OB_NO_UNSIGNED_WRAP | OB_NO_SIGNED_WRAP)); + break; + case VKD3DSIH_ISHR: + case VKD3DSIH_USHR: + case VKD3DSIH_IDIV: + case VKD3DSIH_UDIV: + silence_warning = !(flags & ~PEB_EXACT); + break; + default: + break; + } + /* The above flags are very common and cause warning spam. */ + if (flags && silence_warning) + { + TRACE("Ignoring flags %#"PRIx64".\n", flags); + } + else if (flags) + { + WARN("Ignoring flags %#"PRIx64".\n", flags); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS, + "Ignoring flags %#"PRIx64" for a binary operation.", flags); + } + } + + src_params = instruction_src_params_alloc(ins, 2, sm6); + src_param_init_from_value(&src_params[0], a); + src_param_init_from_value(&src_params[1], b); + if (code == BINOP_SUB) + src_params[1].modifiers = VKD3DSPSM_NEG; + + dst->type = a->type; + + if (handler_idx == VKD3DSIH_UMUL || handler_idx == VKD3DSIH_UDIV || handler_idx == VKD3DSIH_IDIV) + { + struct vkd3d_shader_dst_param *dst_params = instruction_dst_params_alloc(ins, 2, sm6); + unsigned int index = code != BINOP_UDIV && code != BINOP_SDIV; + + dst_param_init(&dst_params[0]); + dst_param_init(&dst_params[1]); + register_init_ssa_scalar(&dst_params[index].reg, a->type, sm6); + vsir_register_init(&dst_params[index ^ 1].reg, VKD3DSPR_NULL, VKD3D_DATA_UNUSED, 0); + dst->u.reg = dst_params[index].reg; + } + else + { + instruction_dst_param_init_ssa_scalar(ins, sm6); + } +} + static void sm6_parser_emit_dx_cbuffer_load(struct sm6_parser *sm6, struct sm6_block *code_block, enum dx_intrinsic_opcode op, const struct sm6_value **operands, struct vkd3d_shader_instruction *ins) { @@ -3233,6 +3458,9 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const record = block->records[i]; switch (record->code) { + case FUNC_CODE_INST_BINOP: + sm6_parser_emit_binop(sm6, record, ins, dst); + break; case FUNC_CODE_INST_CALL: sm6_parser_emit_call(sm6, record, code_block, ins, dst); break; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 3420f5d8..ab730011 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -192,6 +192,7 @@ enum vkd3d_shader_error VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH = 8305, VKD3D_SHADER_WARNING_DXIL_ENTRY_POINT_MISMATCH = 8306, VKD3D_SHADER_WARNING_DXIL_INVALID_MASK = 8307, + VKD3D_SHADER_WARNING_DXIL_INVALID_OPERATION = 8308, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED = 9000, VKD3D_SHADER_ERROR_VSIR_INVALID_HANDLER = 9001, diff --git a/tests/hlsl/arithmetic-float-uniform.shader_test b/tests/hlsl/arithmetic-float-uniform.shader_test index 8aaca621..4812d053 100644 --- a/tests/hlsl/arithmetic-float-uniform.shader_test +++ b/tests/hlsl/arithmetic-float-uniform.shader_test @@ -10,7 +10,7 @@ float4 main() : SV_TARGET [test] uniform 0 float4 5.0 15.0 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (20.0, -10.0, 75.0, 0.33333333) 1 [pixel shader] @@ -25,7 +25,7 @@ float4 main() : SV_TARGET [test] uniform 0 float4 5.0 15.0 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (5.0, 5.0, -5.0, 3.0) 1 [pixel shader] @@ -40,7 +40,7 @@ float4 main() : SV_TARGET [test] uniform 0 float4 42.0 5.0 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (2.0, -2.0, 2.0, -2.0) 16 [pixel shader] @@ -55,7 +55,7 @@ float4 main() : SV_TARGET [test] uniform 0 float4 45.0 5.0 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (0.0, 0.0, 0.0, 0.0) [pixel shader] @@ -69,8 +69,8 @@ float4 main() : sv_target [test] uniform 0 float4 5.0 -42.1 4.0 45.0 uniform 4 float4 15.0 -5.0 4.1 5.0 -todo(sm>=6) draw quad -probe all rgba (5.0, -2.1, 4.0, 0.0) 4 +draw quad +probe all rgba (5.0, -2.1, 4.0, 0.0) 6 [require] % Infinities are not allowed in SM1 @@ -88,5 +88,5 @@ float4 main() : SV_TARGET [test] uniform 0 float4 1.0 0.0 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (1e99, 1e99, 1e99, 1e99) diff --git a/tests/hlsl/cbuffer.shader_test b/tests/hlsl/cbuffer.shader_test index dbb49ea7..b4dc01ed 100644 --- a/tests/hlsl/cbuffer.shader_test +++ b/tests/hlsl/cbuffer.shader_test @@ -176,7 +176,7 @@ float4 main() : sv_target uniform 0 float4 1.0 2.0 3.0 4.0 uniform 4 float4 5.0 6.0 7.0 8.0 uniform 8 float4 9.0 10.0 11.0 12.0 -todo(sm>=6) draw quad +draw quad probe all rgba (509, 610, 711, 812) @@ -204,7 +204,7 @@ uniform 0 float4 0.0 1.0 2.0 3.0 uniform 4 float4 4.0 5.0 6.0 7.0 uniform 8 float4 8.0 9.0 10.0 11.0 uniform 12 float4 12.0 13.0 14.0 15.0 -todo(sm>=6) draw quad +draw quad probe all rgba (12468.0, 13509.0, 14010.0, 15011.0) @@ -326,7 +326,7 @@ float4 main() : sv_target uniform 0 float 1.0 uniform 1 float 2.0 uniform 4 float4 5.0 6.0 7.0 8.0 -todo(sm>=6) draw quad +draw quad probe all rgba (512.0, 612.0, 712.0, 812.0) diff --git a/tests/hlsl/const.shader_test b/tests/hlsl/const.shader_test index 17427c38..9541203d 100644 --- a/tests/hlsl/const.shader_test +++ b/tests/hlsl/const.shader_test @@ -10,7 +10,7 @@ float4 main() : sv_target [test] uniform 0 float4 1.0 2.0 3.0 4.0 uniform 4 float4 0.1 0.2 0.3 0.4 -todo(sm>=6) draw quad +draw quad probe all rgba (1.1, 2.2, 3.3, 4.4) [pixel shader fail(sm<6)] diff --git a/tests/hlsl/cross.shader_test b/tests/hlsl/cross.shader_test index b84e2eac..22f373a6 100644 --- a/tests/hlsl/cross.shader_test +++ b/tests/hlsl/cross.shader_test @@ -12,7 +12,7 @@ float4 main() : sv_target [test] uniform 0 float4 1 -2 3 4 uniform 4 float4 10 100 1000 10000 -todo(sm>=6) draw quad +draw quad probe all rgba (-2300, -970, 120, 0) @@ -29,5 +29,5 @@ float4 main() : sv_target [test] uniform 0 float4 1 -2 3 4 -todo(sm>=6) draw quad +draw quad probe all rgba (-20, 8, 12, 3.5) diff --git a/tests/hlsl/dot.shader_test b/tests/hlsl/dot.shader_test index bb71919c..c620e5fa 100644 --- a/tests/hlsl/dot.shader_test +++ b/tests/hlsl/dot.shader_test @@ -71,7 +71,7 @@ float4 main() : SV_TARGET % Account for both the SM1 and SM4 uniform layout uniform 0 float4 2.0 3.0 0.0 0.0 uniform 4 float4 3.0 0.0 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (6.0, 6.0, 6.0, 6.0) [pixel shader] diff --git a/tests/hlsl/expr-indexing.shader_test b/tests/hlsl/expr-indexing.shader_test index 1c598816..c11fa654 100644 --- a/tests/hlsl/expr-indexing.shader_test +++ b/tests/hlsl/expr-indexing.shader_test @@ -9,7 +9,7 @@ float4 main() : sv_target [test] uniform 0 float4 1.0 2.0 3.0 4.0 uniform 4 float4 5.0 6.0 7.0 8.0 -todo(sm>=6) draw quad +draw quad probe all rgba (8.0, 8.0, 8.0, 8.0) diff --git a/tests/hlsl/lerp.shader_test b/tests/hlsl/lerp.shader_test index 27c45fe7..413bfe83 100644 --- a/tests/hlsl/lerp.shader_test +++ b/tests/hlsl/lerp.shader_test @@ -12,7 +12,7 @@ float4 main() : SV_TARGET uniform 0 float4 2.0 3.0 4.0 5.0 uniform 4 float4 0.0 -10.0 10.0 100.0 uniform 8 float4 0.0 1.0 -1.0 0.75 -todo(sm>=6) draw quad +draw quad probe all rgba (2.0, -10.0, -2.0, 76.25) [require] diff --git a/tests/hlsl/loop.shader_test b/tests/hlsl/loop.shader_test index c15e4879..35a30359 100644 --- a/tests/hlsl/loop.shader_test +++ b/tests/hlsl/loop.shader_test @@ -1,3 +1,5 @@ +% TODO: dxcompiler emits no loops for any of these test shaders. + [pixel shader] float a; @@ -16,7 +18,7 @@ float4 main() : sv_target [test] uniform 0 float 5.0 -todo(sm>=6) draw quad +draw quad probe all rgba (50.0, 50.0, 50.0, 50.0) @@ -39,7 +41,7 @@ float4 main() : sv_target [test] uniform 0 float 4.0 -todo(sm>=6) draw quad +draw quad probe all rgba (20.0, 20.0, 20.0, 20.0) [pixel shader] @@ -68,7 +70,7 @@ float4 main() : sv_target [test] uniform 0 float 4.0 -todo(sm>=6) draw quad +draw quad probe all rgba (409.1, 409.1, 409.1, 409.1) [pixel shader] @@ -98,7 +100,7 @@ float4 main() : sv_target [test] uniform 0 float 4.0 -todo(sm>=6) draw quad +draw quad probe all rgba (410.1, 410.1, 410.1, 410.1) % loop attribute by itself diff --git a/tests/hlsl/math.shader_test b/tests/hlsl/math.shader_test index 8f059227..15f57933 100644 --- a/tests/hlsl/math.shader_test +++ b/tests/hlsl/math.shader_test @@ -14,5 +14,5 @@ float4 main() : SV_TARGET [test] uniform 0 float4 2.5 0.3 0.2 0.7 uniform 4 float4 0.1 1.5 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (-12.43, 9.833333, 1.6, 35.0) 1 diff --git a/tests/hlsl/object-references.shader_test b/tests/hlsl/object-references.shader_test index ff405559..c857f388 100644 --- a/tests/hlsl/object-references.shader_test +++ b/tests/hlsl/object-references.shader_test @@ -202,7 +202,7 @@ float4 main() : sv_target [test] uniform 0 float 10.0 -todo(sm>=6) draw quad +draw quad probe (0, 0) rgba (11.0, 12.0, 13.0, 11.0) diff --git a/tests/hlsl/reflect.shader_test b/tests/hlsl/reflect.shader_test index 808b4b77..25890086 100644 --- a/tests/hlsl/reflect.shader_test +++ b/tests/hlsl/reflect.shader_test @@ -62,7 +62,7 @@ float4 main() : sv_target [test] uniform 0 float4 0.5 0.0 0.0 0.0 uniform 4 float4 0.6 0.0 0.0 0.0 -todo(sm>=6) draw quad +draw quad probe all rgba (0.14, 0.14, 0.14, 0.14) 4 [pixel shader] diff --git a/tests/hlsl/writemask-assignop-0.shader_test b/tests/hlsl/writemask-assignop-0.shader_test index fa8ecc2e..374a38bb 100644 --- a/tests/hlsl/writemask-assignop-0.shader_test +++ b/tests/hlsl/writemask-assignop-0.shader_test @@ -11,5 +11,5 @@ float4 main() : SV_target [test] uniform 0 float4 0.0303 0.08 0.07 0.0202 -todo(sm>=6) draw quad +draw quad probe all rgba (-0.4697, -0.02, 0.57, 0.3202) 2 diff --git a/tests/hlsl/writemask-assignop-1.shader_test b/tests/hlsl/writemask-assignop-1.shader_test index 3bebcce6..61993257 100644 --- a/tests/hlsl/writemask-assignop-1.shader_test +++ b/tests/hlsl/writemask-assignop-1.shader_test @@ -12,5 +12,5 @@ float4 main() : SV_target [test] uniform 0 float4 0.0303 0.08 0.07 0.0202 -todo(sm>=6) draw quad +draw quad probe all rgba (0.5697, -0.08, -0.27, -0.4202)