[all-commits] [llvm/llvm-project] 729f3b: [ARM] Select ssat for saturating clamps with a non...
KavinSai-synthara via All-commits
all-commits at lists.llvm.org
Wed Jun 24 07:39:04 PDT 2026
Branch: refs/heads/main
Home: https://github.com/llvm/llvm-project
Commit: 729f3bfbec19065c33593b95db251321dadb0e9c
https://github.com/llvm/llvm-project/commit/729f3bfbec19065c33593b95db251321dadb0e9c
Author: KavinSai-synthara <kavin.sai at synthara.ai>
Date: 2026-06-24 (Wed, 24 Jun 2026)
Changed paths:
M llvm/lib/Target/ARM/ARMISelLowering.cpp
A llvm/test/CodeGen/ARM/ssat-offset.ll
Log Message:
-----------
[ARM] Select ssat for saturating clamps with a non-zero offset (#203854)
### Summary
On ARM/Thumb2, a saturating clamp to a signed range that is not centered
on zero is *not* matched to `ssat`, even though it can be potentially
matched to `ssat`. The backend instead emits a long explicit
compare/select clamp.
This pattern is extremely common in quantized ML code: an int8
requantize is `clamp(accumulator + output_zero_point, -128, 127)`, and
whenever the output zero_point != 0, the instruction count regress from
one ssat to ~10 instructions.
The root cause is that InstCombine canonically **sinks the constant out
of the min/max** — `clamp(X + C, -128, 127)` becomes `clamp(X, -128-C,
127-C) + C` — and the backend's SSAT matcher
(`PerformMinMaxToSatCombine`) only handles a clamp whose bounds already
form a saturation range. The offset of the form (`[-128-C, 127-C]`,
width 256 = 2⁸) falls through to a generic clamp.
#### Reproducer
```c
#include <stdint.h>
int8_t requant_offset(int32_t acc) { // output zero-point = 96
int32_t v = (acc >> 8) + 96;
if (v < -128) v = -128;
if (v > 127) v = 127;
return (int8_t)v;
}
int8_t requant_zero(int32_t acc) { // output zero-point = 0 (for contrast)
int32_t v = (acc >> 8);
if (v < -128) v = -128;
if (v > 127) v = 127;
return (int8_t)v;
}
```
```
clang -O2 --target=thumbv7em-none-eabihf -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -S issue.c
```
**Actual**
`requant_offset` (zero-point 96):
```asm
asrs r1, r0, #8
cmn.w r1, #224
mvn r1, #223
it gt
asrgt r1, r0, #8
cmp r1, #31
it ge
movge r1, #31
add.w r0, r1, #96
sxtb r0, r0
```
`requant_zero` (zero-point 0):
```asm
ssat r0, #8, r0, asr #8
```
#### Expected
`requant_offset` should select `ssat`, e.g.:
```asm
movs r1, #96
add.w r0, r1, r0, asr #8
ssat r0, #8, r0
subs r0, #96
```
#### Why this is sound
A clamp whose width (hi − lo + 1) is a power of two is just an `ssat`
shifted off zero by constant C.
```
clamp(X, lo, hi) == ssat_k(X − C) + C, C = lo + 2^(k-1)
```
e.g. `clamp(X, −224, 31)` == `clamp(X+96, (-225+96), (31+96)) - 96` ==
`ssat8(X + 96) − 96`
Assisted-by: Claude
To unsubscribe from these emails, change your notification settings at https://github.com/llvm/llvm-project/settings/notifications
More information about the All-commits
mailing list