diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c
index a8b92a1e12c225f63ca6b86a6e3b5c968b53fff3..610d907d9815330b327396021903d2146f4e0d9f 100644
--- a/libs/vkd3d-shader/ir.c
+++ b/libs/vkd3d-shader/ir.c
@@ -17,6 +17,7 @@
  */
 
 #include "vkd3d_shader_private.h"
+#include "vkd3d_types.h"
 
 bool vsir_program_init(struct vsir_program *program, const struct vkd3d_shader_version *version, unsigned int reserve)
 {
@@ -56,19 +57,6 @@ static void vkd3d_shader_instruction_make_nop(struct vkd3d_shader_instruction *i
     vsir_instruction_init(ins, &location, VKD3DSIH_NOP);
 }
 
-static void remove_dcl_temps(struct vsir_program *program)
-{
-    unsigned int i;
-
-    for (i = 0; i < program->instructions.count; ++i)
-    {
-        struct vkd3d_shader_instruction *ins = &program->instructions.elements[i];
-
-        if (ins->handler_idx == VKD3DSIH_DCL_TEMPS)
-            vkd3d_shader_instruction_make_nop(ins);
-    }
-}
-
 static bool vsir_instruction_init_with_params(struct vsir_program *program,
         struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location,
         enum vkd3d_shader_opcode handler_idx, unsigned int dst_count, unsigned int src_count)
@@ -94,86 +82,163 @@ static bool vsir_instruction_init_with_params(struct vsir_program *program,
     return true;
 }
 
-static enum vkd3d_result vsir_program_lower_texkills(struct vsir_program *program)
+static enum vkd3d_result vsir_program_lower_texkill(struct vsir_program *program,
+        struct vkd3d_shader_instruction *texkill, unsigned int *tmp_idx)
 {
+    const unsigned int components_read = 3 + (program->shader_version.major >= 2);
     struct vkd3d_shader_instruction_array *instructions = &program->instructions;
-    struct vkd3d_shader_instruction *texkill_ins, *ins;
-    unsigned int components_read = 3 + (program->shader_version.major >= 2);
-    unsigned int tmp_idx = ~0u;
-    unsigned int i, k;
+    size_t pos = texkill - instructions->elements;
+    struct vkd3d_shader_instruction *ins;
+    unsigned int j;
 
-    for (i = 0; i < instructions->count; ++i)
-    {
-        texkill_ins = &instructions->elements[i];
+    if (!shader_instruction_array_insert_at(instructions, pos + 1, components_read + 1))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
 
-        if (texkill_ins->handler_idx != VKD3DSIH_TEXKILL)
-            continue;
+    if (*tmp_idx == ~0u)
+        *tmp_idx = program->temp_count++;
 
-        if (!shader_instruction_array_insert_at(instructions, i + 1, components_read + 1))
-            return VKD3D_ERROR_OUT_OF_MEMORY;
+    /* tmp = ins->dst[0] < 0  */
+
+    ins = &instructions->elements[pos + 1];
+    if (!vsir_instruction_init_with_params(program, ins, &texkill->location, VKD3DSIH_LTO, 1, 2))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
+    ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
+    ins->dst[0].reg.idx[0].offset = *tmp_idx;
+    ins->dst[0].write_mask = VKD3DSP_WRITEMASK_ALL;
 
-        if (tmp_idx == ~0u)
-            tmp_idx = program->temp_count++;
+    ins->src[0].reg = texkill->dst[0].reg;
+    ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
+    vsir_register_init(&ins->src[1].reg, VKD3DSPR_IMMCONST, VKD3D_DATA_FLOAT, 0);
+    ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4;
+    ins->src[1].reg.u.immconst_f32[0] = 0.0f;
+    ins->src[1].reg.u.immconst_f32[1] = 0.0f;
+    ins->src[1].reg.u.immconst_f32[2] = 0.0f;
+    ins->src[1].reg.u.immconst_f32[3] = 0.0f;
 
-        /* tmp = ins->dst[0] < 0  */
+    /* tmp.x = tmp.x || tmp.y */
+    /* tmp.x = tmp.x || tmp.z */
+    /* tmp.x = tmp.x || tmp.w, if sm >= 2.0 */
 
-        ins = &instructions->elements[i + 1];
-        if (!vsir_instruction_init_with_params(program, ins, &texkill_ins->location, VKD3DSIH_LTO, 1, 2))
+    for (j = 1; j < components_read; ++j)
+    {
+        ins = &instructions->elements[pos + 1 + j];
+        if (!(vsir_instruction_init_with_params(program, ins, &texkill->location, VKD3DSIH_OR, 1, 2)))
             return VKD3D_ERROR_OUT_OF_MEMORY;
 
         vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
         ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
-        ins->dst[0].reg.idx[0].offset = tmp_idx;
-        ins->dst[0].write_mask = VKD3DSP_WRITEMASK_ALL;
+        ins->dst[0].reg.idx[0].offset = *tmp_idx;
+        ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0;
 
-        ins->src[0].reg = texkill_ins->dst[0].reg;
-        ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE;
-        vsir_register_init(&ins->src[1].reg, VKD3DSPR_IMMCONST, VKD3D_DATA_FLOAT, 0);
+        vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
+        ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
+        ins->src[0].reg.idx[0].offset = *tmp_idx;
+        ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
+        vsir_register_init(&ins->src[1].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
         ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4;
-        ins->src[1].reg.u.immconst_f32[0] = 0.0f;
-        ins->src[1].reg.u.immconst_f32[1] = 0.0f;
-        ins->src[1].reg.u.immconst_f32[2] = 0.0f;
-        ins->src[1].reg.u.immconst_f32[3] = 0.0f;
+        ins->src[1].reg.idx[0].offset = *tmp_idx;
+        ins->src[1].swizzle = vkd3d_shader_create_swizzle(j, j, j, j);
+    }
 
-        /* tmp.x = tmp.x || tmp.y */
-        /* tmp.x = tmp.x || tmp.z */
-        /* tmp.x = tmp.x || tmp.w, if sm >= 2.0 */
+    /* discard_nz tmp.x */
 
-        for (k = 1; k < components_read; ++k)
-        {
-            ins = &instructions->elements[i + 1 + k];
-            if (!(vsir_instruction_init_with_params(program, ins, &texkill_ins->location, VKD3DSIH_OR, 1, 2)))
-                return VKD3D_ERROR_OUT_OF_MEMORY;
+    ins = &instructions->elements[pos + 1 + components_read];
+    if (!(vsir_instruction_init_with_params(program, ins, &texkill->location, VKD3DSIH_DISCARD, 0, 1)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+    ins->flags = VKD3D_SHADER_CONDITIONAL_OP_NZ;
 
-            vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
-            ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4;
-            ins->dst[0].reg.idx[0].offset = tmp_idx;
-            ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0;
-
-            vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
-            ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
-            ins->src[0].reg.idx[0].offset = tmp_idx;
-            ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
-            vsir_register_init(&ins->src[1].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
-            ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4;
-            ins->src[1].reg.idx[0].offset = tmp_idx;
-            ins->src[1].swizzle = vkd3d_shader_create_swizzle(k, k, k, k);
-        }
+    vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
+    ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
+    ins->src[0].reg.idx[0].offset = *tmp_idx;
+    ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
 
-        /* discard_nz tmp.x */
+    /* Make the original instruction no-op */
+    vkd3d_shader_instruction_make_nop(texkill);
 
-        ins = &instructions->elements[i + 1 + components_read];
-        if (!(vsir_instruction_init_with_params(program, ins, &texkill_ins->location, VKD3DSIH_DISCARD, 0, 1)))
-            return VKD3D_ERROR_OUT_OF_MEMORY;
-        ins->flags = VKD3D_SHADER_CONDITIONAL_OP_NZ;
+    return VKD3D_OK;
+}
 
-        vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1);
-        ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4;
-        ins->src[0].reg.idx[0].offset = tmp_idx;
-        ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X);
+/* The Shader Model 5 Assembly documentation states: "If components of a mad
+ * instruction are tagged as precise, the hardware must execute a mad instruction
+ * or the exact equivalent, and it cannot split it into a multiply followed by an add."
+ * But DXIL.rst states the opposite: "Floating point multiply & add. This operation is
+ * not fused for "precise" operations."
+ * Windows drivers seem to conform with the latter, for SM 4-5 and SM 6. */
+static enum vkd3d_result vsir_program_lower_precise_mad(struct vsir_program *program,
+        struct vkd3d_shader_instruction *mad, unsigned int *tmp_idx)
+{
+    struct vkd3d_shader_instruction_array *instructions = &program->instructions;
+    struct vkd3d_shader_instruction *mul_ins, *add_ins;
+    size_t pos = mad - instructions->elements;
+    struct vkd3d_shader_dst_param *mul_dst;
 
-        /* Make the original instruction no-op */
-        vkd3d_shader_instruction_make_nop(texkill_ins);
+    if (!(mad->flags & VKD3DSI_PRECISE_XYZW))
+        return VKD3D_OK;
+
+    if (!shader_instruction_array_insert_at(instructions, pos + 1, 1))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    if (*tmp_idx == ~0u)
+        *tmp_idx = program->temp_count++;
+
+    mul_ins = &instructions->elements[pos];
+    add_ins = &instructions->elements[pos + 1];
+
+    mul_ins->handler_idx = VKD3DSIH_MUL;
+    mul_ins->src_count = 2;
+
+    if (!(vsir_instruction_init_with_params(program, add_ins, &mul_ins->location, VKD3DSIH_ADD, 1, 2)))
+        return VKD3D_ERROR_OUT_OF_MEMORY;
+
+    add_ins->flags = mul_ins->flags & VKD3DSI_PRECISE_XYZW;
+
+    mul_dst = mul_ins->dst;
+    *add_ins->dst = *mul_dst;
+
+    mul_dst->modifiers = 0;
+    vsir_register_init(&mul_dst->reg, VKD3DSPR_TEMP, mul_ins->src[0].reg.data_type, 1);
+    mul_dst->reg.dimension = add_ins->dst->reg.dimension;
+    mul_dst->reg.idx[0].offset = *tmp_idx;
+
+    add_ins->src[0].reg = mul_dst->reg;
+    add_ins->src[0].swizzle = vsir_swizzle_from_writemask(mul_dst->write_mask);
+    add_ins->src[0].modifiers = 0;
+    add_ins->src[1] = mul_ins->src[2];
+
+    return VKD3D_OK;
+}
+
+static enum vkd3d_result vsir_program_lower_instructions(struct vsir_program *program)
+{
+    struct vkd3d_shader_instruction_array *instructions = &program->instructions;
+    unsigned int tmp_idx = ~0u, i;
+    enum vkd3d_result ret;
+
+    for (i = 0; i < instructions->count; ++i)
+    {
+        struct vkd3d_shader_instruction *ins = &instructions->elements[i];
+
+        switch (ins->handler_idx)
+        {
+            case VKD3DSIH_TEXKILL:
+                if ((ret = vsir_program_lower_texkill(program, ins, &tmp_idx)) < 0)
+                    return ret;
+                break;
+
+            case VKD3DSIH_MAD:
+                if ((ret = vsir_program_lower_precise_mad(program, ins, &tmp_idx)) < 0)
+                    return ret;
+                break;
+
+            case VKD3DSIH_DCL_TEMPS:
+                vkd3d_shader_instruction_make_nop(ins);
+                break;
+
+            default:
+                break;
+        }
     }
 
     return VKD3D_OK;
@@ -5749,9 +5814,7 @@ enum vkd3d_result vsir_program_normalise(struct vsir_program *program, uint64_t
 {
     enum vkd3d_result result = VKD3D_OK;
 
-    remove_dcl_temps(program);
-
-    if ((result = vsir_program_lower_texkills(program)) < 0)
+    if ((result = vsir_program_lower_instructions(program)) < 0)
         return result;
 
     if (program->shader_version.major >= 6)
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index b07a7bff75ec32f9ea9a67d6ff472891f8a1545a..f401fd7a3ff34793707f2ea09209d2d8cedbe999 100644
--- a/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -1761,6 +1761,31 @@ static inline unsigned int vkd3d_compact_swizzle(uint32_t swizzle, uint32_t writ
     return compacted_swizzle;
 }
 
+static inline uint32_t vsir_swizzle_from_writemask(unsigned int writemask)
+{
+    static const unsigned int swizzles[16] =
+    {
+        0,
+        VKD3D_SHADER_SWIZZLE(X, X, X, X),
+        VKD3D_SHADER_SWIZZLE(Y, Y, Y, Y),
+        VKD3D_SHADER_SWIZZLE(X, Y, X, X),
+        VKD3D_SHADER_SWIZZLE(Z, Z, Z, Z),
+        VKD3D_SHADER_SWIZZLE(X, Z, X, X),
+        VKD3D_SHADER_SWIZZLE(Y, Z, X, X),
+        VKD3D_SHADER_SWIZZLE(X, Y, Z, X),
+        VKD3D_SHADER_SWIZZLE(W, W, W, W),
+        VKD3D_SHADER_SWIZZLE(X, W, X, X),
+        VKD3D_SHADER_SWIZZLE(Y, W, X, X),
+        VKD3D_SHADER_SWIZZLE(X, Y, W, X),
+        VKD3D_SHADER_SWIZZLE(Z, W, X, X),
+        VKD3D_SHADER_SWIZZLE(X, Z, W, X),
+        VKD3D_SHADER_SWIZZLE(Y, Z, W, X),
+        VKD3D_SHADER_SWIZZLE(X, Y, Z, W),
+    };
+
+    return swizzles[writemask & 0xf];
+}
+
 struct vkd3d_struct
 {
     enum vkd3d_shader_structure_type type;
diff --git a/tests/hlsl/arithmetic-float-uniform.shader_test b/tests/hlsl/arithmetic-float-uniform.shader_test
index 61957f2bbbe56f5b42cd474e689b0306242d0690..a49d3f00a4d4cc3d563befdd15171ec30a7a7f43 100644
--- a/tests/hlsl/arithmetic-float-uniform.shader_test
+++ b/tests/hlsl/arithmetic-float-uniform.shader_test
@@ -121,7 +121,7 @@ uniform 0 float4 1.00000007 -42.1 4.0 45.0
 uniform 4 float4 1.625 -5.0 4.125 5.0
 uniform 8 float4 1.00000007 -1.0 0.5 -0.5
 todo(sm<6) draw quad
-todo probe all rgba (2.62500048, 209.5, 17.0, 224.5)
+probe all rgba (2.62500048, 209.5, 17.0, 224.5)
 
 [require]
 shader model >= 5.0