| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: llc -mtriple=thumbv7em-none-eabi %s -o - | FileCheck %s | |
| ; Tests for recovering SSAT from an "offset" clamp: a clamp(X, Lo, Hi) whose | |
| ; width Width = Hi - Lo + 1 is a power of two but whose bounds are not centered | |
| ; on zero. PerformMinMaxToSatCombine rewrites clamp(X, Lo, Hi) into | |
| ; ssat(X - Center) + Center, with Center = Lo + Width/2, guarded so that | |
| ; X - Center cannot signed-overflow. | |
| ; A bare clamp to [-224, 31] (width 256). X is range-limited by the ashr, so | |
| ; X + 96 cannot overflow: clamp[-224,31] -> ssat8(X + 96) - 96. | |
| define i32 @offset_clamp_bare(i32 %x) { | |
| ; CHECK-LABEL: offset_clamp_bare: | |
| ; CHECK: @ %bb.0: | |
| ; CHECK-NEXT: movs r1, #96 | |
| ; CHECK-NEXT: add.w r0, r1, r0, asr #8 | |
| ; CHECK-NEXT: ssat r0, #8, r0 | |
| ; CHECK-NEXT: subs r0, #96 | |
| ; CHECK-NEXT: bx lr | |
| %s = ashr i32 %x, 8 | |
| %lo = call i32 @llvm.smax.i32(i32 %s, i32 -224) | |
| %hi = call i32 @llvm.smin.i32(i32 %lo, i32 31) | |
| ret i32 %hi | |
| } | |
| ; An already-symmetric clamp [-128, 127] is still a bare ssat. | |
| define i32 @symmetric_clamp(i32 %x) { | |
| ; CHECK-LABEL: symmetric_clamp: | |
| ; CHECK: @ %bb.0: | |
| ; CHECK-NEXT: ssat r0, #8, r0 | |
| ; CHECK-NEXT: bx lr | |
| %lo = call i32 @llvm.smax.i32(i32 %x, i32 -128) | |
| %hi = call i32 @llvm.smin.i32(i32 %lo, i32 127) | |
| ret i32 %hi | |
| } | |
| ; An unsigned clamp [0, 255] is still a usat. | |
| define i32 @unsigned_clamp(i32 %x) { | |
| ; CHECK-LABEL: unsigned_clamp: | |
| ; CHECK: @ %bb.0: | |
| ; CHECK-NEXT: usat r0, #8, r0 | |
| ; CHECK-NEXT: bx lr | |
| %lo = call i32 @llvm.smax.i32(i32 %x, i32 0) | |
| %hi = call i32 @llvm.smin.i32(i32 %lo, i32 255) | |
| ret i32 %hi | |
| } | |
| ; X is unconstrained, so X + 96 may signed-overflow and the rewrite is unsound. | |
| ; Must stay an explicit clamp (no ssat). | |
| define i32 @offset_clamp_may_overflow(i32 %x) { | |
| ; CHECK-LABEL: offset_clamp_may_overflow: | |
| ; CHECK: @ %bb.0: | |
| ; CHECK-NEXT: cmn.w r0, #224 | |
| ; CHECK-NEXT: it le | |
| ; CHECK-NEXT: mvnle r0, #223 | |
| ; CHECK-NEXT: cmp r0, #31 | |
| ; CHECK-NEXT: it ge | |
| ; CHECK-NEXT: movge r0, #31 | |
| ; CHECK-NEXT: bx lr | |
| %lo = call i32 @llvm.smax.i32(i32 %x, i32 -224) | |
| %hi = call i32 @llvm.smin.i32(i32 %lo, i32 31) | |
| ret i32 %hi | |
| } | |
| ; Width 151 ([-100, 50]) is not a power of two. No fold. | |
| define i32 @offset_clamp_non_pow2_width(i32 %x) { | |
| ; CHECK-LABEL: offset_clamp_non_pow2_width: | |
| ; CHECK: @ %bb.0: | |
| ; CHECK-NEXT: mvn r1, #99 | |
| ; CHECK-NEXT: asrs r2, r0, #8 | |
| ; CHECK-NEXT: cmn.w r2, #100 | |
| ; CHECK-NEXT: it gt | |
| ; CHECK-NEXT: asrgt r1, r0, #8 | |
| ; CHECK-NEXT: cmp r1, #50 | |
| ; CHECK-NEXT: it ge | |
| ; CHECK-NEXT: movge r1, #50 | |
| ; CHECK-NEXT: mov r0, r1 | |
| ; CHECK-NEXT: bx lr | |
| %s = ashr i32 %x, 8 | |
| %lo = call i32 @llvm.smax.i32(i32 %s, i32 -100) | |
| %hi = call i32 @llvm.smin.i32(i32 %lo, i32 50) | |
| ret i32 %hi | |
| } | |
| ; Reversed bounds are a degenerate clamp. The modular APInt width would look | |
| ; like a power of two, but this must not become an offset ssat. | |
| define i32 @offset_clamp_degenerate_bounds(i32 %x) { | |
| ; CHECK-LABEL: offset_clamp_degenerate_bounds: | |
| ; CHECK: @ %bb.0: | |
| ; CHECK-NEXT: mov.w r0, #-2147483648 | |
| ; CHECK-NEXT: bx lr | |
| %s = ashr i32 %x, 24 | |
| %lo = call i32 @llvm.smax.i32(i32 %s, i32 1) | |
| %hi = call i32 @llvm.smin.i32(i32 %lo, i32 -2147483648) | |
| ret i32 %hi | |
| } |