| ; 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 |
| } |