redundant-abs
Status: shipped (Phase 2) — see CHANGELOG.
What it detects
Calls to abs(expr) where the enclosed expression is statically provable to be non-negative. The rule currently recognises three sub-patterns:
abs(x * x)— a value squared is always non-negative for real floats.abs(dot(v, v))— the self-dot-product of any vector equals the sum of squared components, which is non-negative.abs(saturate(expr))— the output ofsaturateis clamped to [0, 1] and is therefore non-negative by definition.
The rule does not fire on abs(x * y) when x and y are distinct expressions, nor on abs(dot(u, v)) for distinct vectors, since those products can be negative. The match is structural: only the specific patterns listed above trigger the diagnostic.
Why it matters on a GPU
abs in HLSL is not a free operation when applied to an arbitrary VGPR value. On AMD RDNA, RDNA 2, and RDNA 3, abs can be encoded as a source modifier bit on an instruction that reads the value — similar to how neg works — but only when the abs is the direct input to another ALU instruction that supports the modifier. When abs(expr) is used as a standalone expression (assigned to a variable, returned, or passed to a function), and the compiler cannot fold it into the consuming instruction, it must emit an explicit instruction. The typical lowering is v_max_f32 dst, src, -src (which computes max(x, -x)), occupying a full VALU slot.
On NVIDIA Turing and Ada Lovelace, the equivalent is a real FP32 instruction rather than a free modifier in all cases where the absolute value is not directly consumed by a fused operation. On Intel Xe-HPG, abs similarly requires a dedicated instruction when it cannot be absorbed into the source modifier of the next op. In all cases, applying abs to a value that is already proven non-negative emits a real op that computes max(x, -x) and obtains exactly x — the instruction does work that produces no new information.
The three matched patterns are the most common sites in GPU shader code. x * x appears in squared-distance calculations, Fresnel terms, and moment shadow map variance computations. dot(v, v) appears in squared-length tests and normalization guards. abs(saturate(x)) appears when authors defensively wrap a saturate result in abs to guard against compiler-introduced negative zeros or when code is copied from a context where the input was not clamped. Removing the abs in each case eliminates the wasted instruction and, in the saturate case, removes a read-after-write dependency that can block instruction scheduling.
Examples
Bad
// From tests/fixtures/phase2/redundant.hlsl, lines 34-37
// HIT(redundant-abs): x*x is non-negative.
float abs_of_square(float x) {
return abs(x * x);
}
// From tests/fixtures/phase2/redundant.hlsl, lines 39-42
// HIT(redundant-abs): dot(v,v) is non-negative.
float abs_of_dot_self(float3 v) {
return abs(dot(v, v));
}
// From tests/fixtures/phase2/redundant.hlsl, lines 44-47
// HIT(redundant-abs): saturate output is non-negative.
float abs_of_saturate(float x) {
return abs(saturate(x));
}Good
// After machine-applicable fix — abs dropped in each case:
float abs_of_square(float x) {
return x * x;
}
float abs_of_dot_self(float3 v) {
return dot(v, v);
}
float abs_of_saturate(float x) {
return saturate(x);
}Options
none
Fix availability
machine-applicable — Dropping abs from any of the three matched patterns is a pure textual substitution with no observable semantic change. For x * x: IEEE 754 guarantees that a finite float squared is non-negative (negative zero squares to positive zero). For dot(v, v): the result is a sum of squares, always non-negative. For saturate(expr): the output is clamped to [0, 1]. shader-clippy fix applies it automatically.
See also
- Related rule: redundant-saturate — detects
saturate(saturate(x))and is related becauseabs(saturate(x))is another form of redundant wrapping - Related rule: clamp01-to-saturate — replaces
clamp(x, 0.0, 1.0)withsaturate(x), after which this rule may fire on theabswrapper - HLSL intrinsic reference:
abs,saturate,dotin the DirectX HLSL Intrinsics documentation - Companion blog post: saturate-redundancy overview
© 2026 NelCit, CC-BY-4.0.