Skip to content

Commit

Permalink
[ARM] Lower unsigned saturation to USAT
Browse files Browse the repository at this point in the history
Summary:
Implement lower of unsigned saturation on an interval [0, k] where k + 1 is a power of two using USAT instruction in a similar way to how [~k, k] is lowered using SSAT on ARM models that supports it.

Patch by Marten Svanfeldt

Reviewers: t.p.northover, pbarrio, eastig, SjoerdMeijer, javed.absar, fhahn

Reviewed By: fhahn

Subscribers: fhahn, aemerson, javed.absar, llvm-commits, kristof.beyls

Differential Revision: https://reviews.llvm.org/D41348

llvm-svn: 321164
  • Loading branch information
fhahn committed Dec 20, 2017
1 parent cd6be96 commit c3aa6d8
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 7 deletions.
35 changes: 28 additions & 7 deletions llvm/lib/Target/ARM/ARMISelLowering.cpp
Expand Up @@ -1246,6 +1246,7 @@ const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const {
case ARMISD::CMOV: return "ARMISD::CMOV";

case ARMISD::SSAT: return "ARMISD::SSAT";
case ARMISD::USAT: return "ARMISD::USAT";

case ARMISD::SRL_FLAG: return "ARMISD::SRL_FLAG";
case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG";
Expand Down Expand Up @@ -4196,7 +4197,7 @@ static bool isUpperSaturate(const SDValue LHS, const SDValue RHS,
((K == LHS && K == TrueVal) || (K == RHS && K == FalseVal)));
}

// Check if two chained conditionals could be converted into SSAT.
// Check if two chained conditionals could be converted into SSAT or USAT.
//
// SSAT can replace a set of two conditional selectors that bound a number to an
// interval of type [k, ~k] when k + 1 is a power of 2. Here are some examples:
Expand All @@ -4207,10 +4208,14 @@ static bool isUpperSaturate(const SDValue LHS, const SDValue RHS,
// x < k ? (x < -k ? -k : x) : k
// etc.
//
// USAT works similarily to SSAT but bounds on the interval [0, k] where k + 1 is
// a power of 2.
//
// It returns true if the conversion can be done, false otherwise.
// Additionally, the variable is returned in parameter V and the constant in K.
// Additionally, the variable is returned in parameter V, the constant in K and
// usat is set to true if the conditional represents an unsigned saturation
static bool isSaturatingConditional(const SDValue &Op, SDValue &V,
uint64_t &K) {
uint64_t &K, bool &usat) {
SDValue LHS1 = Op.getOperand(0);
SDValue RHS1 = Op.getOperand(1);
SDValue TrueVal1 = Op.getOperand(2);
Expand Down Expand Up @@ -4277,13 +4282,23 @@ static bool isSaturatingConditional(const SDValue &Op, SDValue &V,
int64_t Val1 = cast<ConstantSDNode>(*K1)->getSExtValue();
int64_t Val2 = cast<ConstantSDNode>(*K2)->getSExtValue();
int64_t PosVal = std::max(Val1, Val2);
int64_t NegVal = std::min(Val1, Val2);

if (((Val1 > Val2 && UpperCheckOp == &Op) ||
(Val1 < Val2 && UpperCheckOp == &Op2)) &&
Val1 == ~Val2 && isPowerOf2_64(PosVal + 1)) {
isPowerOf2_64(PosVal + 1)) {

// Handle the difference between USAT (unsigned) and SSAT (signed) saturation
if (Val1 == ~Val2)
usat = false;
else if (NegVal == 0)
usat = true;
else
return false;

V = V2;
K = (uint64_t)PosVal; // At this point, PosVal is guaranteed to be positive

return true;
}

Expand All @@ -4297,10 +4312,16 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {
// Try to convert two saturating conditional selects into a single SSAT
SDValue SatValue;
uint64_t SatConstant;
bool SatUSat;
if (((!Subtarget->isThumb() && Subtarget->hasV6Ops()) || Subtarget->isThumb2()) &&
isSaturatingConditional(Op, SatValue, SatConstant))
return DAG.getNode(ARMISD::SSAT, dl, VT, SatValue,
DAG.getConstant(countTrailingOnes(SatConstant), dl, VT));
isSaturatingConditional(Op, SatValue, SatConstant, SatUSat)) {
if (SatUSat)
return DAG.getNode(ARMISD::USAT, dl, VT, SatValue,
DAG.getConstant(countTrailingOnes(SatConstant), dl, VT));
else
return DAG.getNode(ARMISD::SSAT, dl, VT, SatValue,
DAG.getConstant(countTrailingOnes(SatConstant), dl, VT));
}

SDValue LHS = Op.getOperand(0);
SDValue RHS = Op.getOperand(1);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/ARM/ARMISelLowering.h
Expand Up @@ -87,6 +87,7 @@ class VectorType;
CMOV, // ARM conditional move instructions.

SSAT, // Signed saturation
USAT, // Unsigned saturation

BCC_i64,

Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/ARM/ARMInstrInfo.td
Expand Up @@ -139,6 +139,8 @@ def ARMcmov : SDNode<"ARMISD::CMOV", SDT_ARMCMov,

def ARMssatnoshift : SDNode<"ARMISD::SSAT", SDTIntSatNoShOp, []>;

def ARMusatnoshift : SDNode<"ARMISD::USAT", SDTIntSatNoShOp, []>;

def ARMbrcond : SDNode<"ARMISD::BRCOND", SDT_ARMBrcond,
[SDNPHasChain, SDNPInGlue, SDNPOutGlue]>;

Expand Down Expand Up @@ -3832,6 +3834,8 @@ def : ARMV6Pat<(int_arm_usat GPRnopc:$a, imm0_31:$pos),
(USAT imm0_31:$pos, GPRnopc:$a, 0)>;
def : ARMPat<(ARMssatnoshift GPRnopc:$Rn, imm0_31:$imm),
(SSAT imm0_31:$imm, GPRnopc:$Rn, 0)>;
def : ARMPat<(ARMusatnoshift GPRnopc:$Rn, imm0_31:$imm),
(USAT imm0_31:$imm, GPRnopc:$Rn, 0)>;
def : ARMV6Pat<(int_arm_ssat16 GPRnopc:$a, imm1_16:$pos),
(SSAT16 imm1_16:$pos, GPRnopc:$a)>;
def : ARMV6Pat<(int_arm_usat16 GPRnopc:$a, imm0_15:$pos),
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/ARM/ARMInstrThumb2.td
Expand Up @@ -2336,6 +2336,8 @@ def t2USAT16: T2SatI<(ins imm0_15:$sat_imm, rGPR:$Rn),

def : T2Pat<(ARMssatnoshift GPRnopc:$Rn, imm0_31:$imm),
(t2SSAT imm0_31:$imm, GPRnopc:$Rn, 0)>;
def : T2Pat<(ARMusatnoshift GPRnopc:$Rn, imm0_31:$imm),
(t2USAT imm0_31:$imm, GPRnopc:$Rn, 0)>;
def : T2Pat<(int_arm_ssat GPR:$a, imm1_32:$pos),
(t2SSAT imm1_32:$pos, GPR:$a, 0)>;
def : T2Pat<(int_arm_usat GPR:$a, imm0_31:$pos),
Expand Down
214 changes: 214 additions & 0 deletions llvm/test/CodeGen/ARM/usat.ll
@@ -0,0 +1,214 @@
; RUN: llc -mtriple=armv4t-eabi %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=V4T
; RUN: llc -mtriple=armv6-eabi %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=V6
; RUN: llc -mtriple=armv6t2-eabi %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=V6T2

; Check for several conditions that should result in USAT.
; For example, the base test is equivalent to
; x < 0 ? 0 : (x > k ? k : x) in C. All patterns that bound x
; to the interval [0, k] where k + 1 is a power of 2 can be
; transformed into USAT. At the end there are some tests
; checking that conditionals are not transformed if they don't
; match the right pattern.

;
; Base tests with different bit widths
;

; x < 0 ? 0 : (x > k ? k : x)
; 32-bit base test
define i32 @unsigned_sat_base_32bit(i32 %x) #0 {
; CHECK-LABEL: unsigned_sat_base_32bit:
; V6: usat r0, #23, r0
; V6T2: usat r0, #23, r0
; V4T-NOT: usat
entry:
%cmpLow = icmp slt i32 %x, 0
%cmpUp = icmp sgt i32 %x, 8388607
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %x
%saturateLow = select i1 %cmpLow, i32 0, i32 %saturateUp
ret i32 %saturateLow
}

; x < 0 ? 0 : (x > k ? k : x)
; 16-bit base test
define i16 @unsigned_sat_base_16bit(i16 %x) #0 {
; CHECK-LABEL: unsigned_sat_base_16bit:
; V6: usat r0, #11, r0
; V6T2: usat r0, #11, r0
; V4T-NOT: usat
entry:
%cmpLow = icmp slt i16 %x, 0
%cmpUp = icmp sgt i16 %x, 2047
%saturateUp = select i1 %cmpUp, i16 2047, i16 %x
%saturateLow = select i1 %cmpLow, i16 0, i16 %saturateUp
ret i16 %saturateLow
}

; x < 0 ? 0 : (x > k ? k : x)
; 8-bit base test
define i8 @unsigned_sat_base_8bit(i8 %x) #0 {
; CHECK-LABEL: unsigned_sat_base_8bit:
; V6: usat r0, #5, r0
; V6T2: usat r0, #5, r0
; V4T-NOT: usat
entry:
%cmpLow = icmp slt i8 %x, 0
%cmpUp = icmp sgt i8 %x, 31
%saturateUp = select i1 %cmpUp, i8 31, i8 %x
%saturateLow = select i1 %cmpLow, i8 0, i8 %saturateUp
ret i8 %saturateLow
}

;
; Tests where the conditionals that check for upper and lower bounds,
; or the < and > operators, are arranged in different ways. Only some
; of the possible combinations that lead to USAT are tested.
;
; x < 0 ? 0 : (x < k ? x : k)
define i32 @unsigned_sat_lower_upper_1(i32 %x) #0 {
; CHECK-LABEL: unsigned_sat_lower_upper_1:
; V6: usat r0, #23, r0
; V6T2: usat r0, #23, r0
; V4T-NOT: usat
entry:
%cmpLow = icmp slt i32 %x, 0
%cmpUp = icmp slt i32 %x, 8388607
%saturateUp = select i1 %cmpUp, i32 %x, i32 8388607
%saturateLow = select i1 %cmpLow, i32 0, i32 %saturateUp
ret i32 %saturateLow
}

; x > 0 ? (x > k ? k : x) : 0
define i32 @unsigned_sat_lower_upper_2(i32 %x) #0 {
; CHECK-LABEL: unsigned_sat_lower_upper_2:
; V6: usat r0, #23, r0
; V6T2: usat r0, #23, r0
; V4T-NOT: usat
entry:
%cmpLow = icmp sgt i32 %x, 0
%cmpUp = icmp sgt i32 %x, 8388607
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %x
%saturateLow = select i1 %cmpLow, i32 %saturateUp, i32 0
ret i32 %saturateLow
}

; x < k ? (x < 0 ? 0 : x) : k
define i32 @unsigned_sat_upper_lower_1(i32 %x) #0 {
; CHECK-LABEL: unsigned_sat_upper_lower_1:
; V6: usat r0, #23, r0
; V6T2: usat r0, #23, r0
; V4T-NOT: usat
entry:
%cmpUp = icmp slt i32 %x, 8388607
%cmpLow = icmp slt i32 %x, 0
%saturateLow = select i1 %cmpLow, i32 0, i32 %x
%saturateUp = select i1 %cmpUp, i32 %saturateLow, i32 8388607
ret i32 %saturateUp
}

; x > k ? k : (x < 0 ? 0 : x)
define i32 @unsigned_sat_upper_lower_2(i32 %x) #0 {
; CHECK-LABEL: unsigned_sat_upper_lower_2:
; V6: usat r0, #23, r0
; V6T2: usat r0, #23, r0
; V4T-NOT: usat
entry:
%cmpUp = icmp sgt i32 %x, 8388607
%cmpLow = icmp slt i32 %x, 0
%saturateLow = select i1 %cmpLow, i32 0, i32 %x
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

; k < x ? k : (x > 0 ? x : 0)
define i32 @unsigned_sat_upper_lower_3(i32 %x) #0 {
; CHECK-LABEL: unsigned_sat_upper_lower_3:
; V6: usat r0, #23, r0
; V6T2: usat r0, #23, r0
; V4T-NOT: usat
entry:
%cmpUp = icmp slt i32 8388607, %x
%cmpLow = icmp sgt i32 %x, 0
%saturateLow = select i1 %cmpLow, i32 %x, i32 0
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

;
; The following tests check for patterns that should not transform
; into USAT but are similar enough that could confuse the selector.
;
; x > k ? k : (x > 0 ? 0 : x)
; First condition upper-saturates, second doesn't lower-saturate.
define i32 @no_unsigned_sat_missing_lower(i32 %x) #0 {
; CHECK-LABEL: no_unsigned_sat_missing_lower
; CHECK-NOT: usat
entry:
%cmpUp = icmp sgt i32 %x, 8388607
%cmpLow = icmp sgt i32 %x, 0
%saturateLow = select i1 %cmpLow, i32 0, i32 %x
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

; x < k ? k : (x < 0 ? 0 : x)
; Second condition lower-saturates, first doesn't upper-saturate.
define i32 @no_unsigned_sat_missing_upper(i32 %x) #0 {
; CHECK-LABEL: no_unsigned_sat_missing_upper:
; CHECK-NOT: usat
entry:
%cmpUp = icmp slt i32 %x, 8388607
%cmpLow = icmp slt i32 %x, 0
%saturateLow = select i1 %cmpLow, i32 0, i32 %x
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

; Lower constant is different in the select and in the compare
define i32 @no_unsigned_sat_incorrect_constant(i32 %x) #0 {
; CHECK-LABEL: no_unsigned_sat_incorrect_constant:
; CHECK-NOT: usat
entry:
%cmpUp = icmp sgt i32 %x, 8388607
%cmpLow = icmp slt i32 %x, 0
%saturateLow = select i1 %cmpLow, i32 -1, i32 %x
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

; The interval is not [0, k]
define i32 @no_unsigned_sat_incorrect_interval(i32 %x) #0 {
; CHECK-LABEL: no_unsigned_sat_incorrect_interval:
; CHECK-NOT: usat
entry:
%cmpUp = icmp sgt i32 %x, 8388607
%cmpLow = icmp slt i32 %x, -4
%saturateLow = select i1 %cmpLow, i32 -4, i32 %x
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

; The returned value (y) is not the same as the tested value (x).
define i32 @no_unsigned_sat_incorrect_return(i32 %x, i32 %y) #0 {
; CHECK-LABEL: no_unsigned_sat_incorrect_return:
; CHECK-NOT: usat
entry:
%cmpUp = icmp sgt i32 %x, 8388607
%cmpLow = icmp slt i32 %x, 0
%saturateLow = select i1 %cmpLow, i32 0, i32 %y
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

; One of the values in a compare (y) is not the same as the rest
; of the compare and select values (x).
define i32 @no_unsigned_sat_incorrect_compare(i32 %x, i32 %y) #0 {
; CHECK-LABEL: no_unsigned_sat_incorrect_compare:
; CHECK-NOT: usat
entry:
%cmpUp = icmp sgt i32 %x, 8388607
%cmpLow = icmp slt i32 %y, 0
%saturateLow = select i1 %cmpLow, i32 0, i32 %x
%saturateUp = select i1 %cmpUp, i32 8388607, i32 %saturateLow
ret i32 %saturateUp
}

0 comments on commit c3aa6d8

Please sign in to comment.