Skip to content

Commit

Permalink
[Sparc] Fix double-float fabs and fneg on little endian CPUs.
Browse files Browse the repository at this point in the history
The SparcV8 fneg and fabs instructions interestingly come only in a
single-float variant. Since the sign bit is always the topmost bit no
matter what size float it is, you simply operate on the high
subregister, as if it were a single float.

However, the layout of double-floats in the float registers is reversed
on little-endian CPUs, so that the high bits are in the second
subregister, rather than the first.

Thus, this expansion must check the endianness to use the correct
subregister.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@267489 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
jyknight committed Apr 25, 2016
1 parent 426203a commit 90cc544
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 89 deletions.
40 changes: 28 additions & 12 deletions lib/Target/Sparc/SparcISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2649,24 +2649,29 @@ static SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG,
return RetAddr;
}

static SDValue LowerF64Op(SDValue Op, SelectionDAG &DAG, unsigned opcode)
static SDValue LowerF64Op(SDValue SrcReg64, SDLoc dl, SelectionDAG &DAG, unsigned opcode)
{
SDLoc dl(Op);

assert(Op.getValueType() == MVT::f64 && "LowerF64Op called on non-double!");
assert(SrcReg64.getValueType() == MVT::f64 && "LowerF64Op called on non-double!");
assert(opcode == ISD::FNEG || opcode == ISD::FABS);

// Lower fneg/fabs on f64 to fneg/fabs on f32.
// fneg f64 => fneg f32:sub_even, fmov f32:sub_odd.
// fabs f64 => fabs f32:sub_even, fmov f32:sub_odd.

SDValue SrcReg64 = Op.getOperand(0);
// Note: in little-endian, the floating-point value is stored in the
// registers are in the opposite order, so the subreg with the sign
// bit is the highest-numbered (odd), rather than the
// lowest-numbered (even).

SDValue Hi32 = DAG.getTargetExtractSubreg(SP::sub_even, dl, MVT::f32,
SrcReg64);
SDValue Lo32 = DAG.getTargetExtractSubreg(SP::sub_odd, dl, MVT::f32,
SrcReg64);

Hi32 = DAG.getNode(opcode, dl, MVT::f32, Hi32);
if (DAG.getDataLayout().isLittleEndian())
Lo32 = DAG.getNode(opcode, dl, MVT::f32, Lo32);
else
Hi32 = DAG.getNode(opcode, dl, MVT::f32, Hi32);

SDValue DstReg64 = SDValue(DAG.getMachineNode(TargetOpcode::IMPLICIT_DEF,
dl, MVT::f64), 0);
Expand Down Expand Up @@ -2810,24 +2815,35 @@ static SDValue LowerFNEGorFABS(SDValue Op, SelectionDAG &DAG, bool isV9) {
assert((Op.getOpcode() == ISD::FNEG || Op.getOpcode() == ISD::FABS)
&& "invalid opcode");

SDLoc dl(Op);

if (Op.getValueType() == MVT::f64)
return LowerF64Op(Op, DAG, Op.getOpcode());
return LowerF64Op(Op.getOperand(0), dl, DAG, Op.getOpcode());
if (Op.getValueType() != MVT::f128)
return Op;

// Lower fabs/fneg on f128 to fabs/fneg on f64
// fabs/fneg f128 => fabs/fneg f64:sub_even64, fmov f64:sub_odd64
// (As with LowerF64Op, on little-endian, we need to negate the odd
// subreg)

SDLoc dl(Op);
SDValue SrcReg128 = Op.getOperand(0);
SDValue Hi64 = DAG.getTargetExtractSubreg(SP::sub_even64, dl, MVT::f64,
SrcReg128);
SDValue Lo64 = DAG.getTargetExtractSubreg(SP::sub_odd64, dl, MVT::f64,
SrcReg128);
if (isV9)
Hi64 = DAG.getNode(Op.getOpcode(), dl, MVT::f64, Hi64);
else
Hi64 = LowerF64Op(Hi64, DAG, Op.getOpcode());

if (DAG.getDataLayout().isLittleEndian()) {
if (isV9)
Lo64 = DAG.getNode(Op.getOpcode(), dl, MVT::f64, Lo64);
else
Lo64 = LowerF64Op(Lo64, dl, DAG, Op.getOpcode());
} else {
if (isV9)
Hi64 = DAG.getNode(Op.getOpcode(), dl, MVT::f64, Hi64);
else
Hi64 = LowerF64Op(Hi64, dl, DAG, Op.getOpcode());
}

SDValue DstReg128 = SDValue(DAG.getMachineNode(TargetOpcode::IMPLICIT_DEF,
dl, MVT::f128), 0);
Expand Down
9 changes: 6 additions & 3 deletions test/CodeGen/SPARC/float.ll
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
; RUN: llc -march=sparc < %s | FileCheck %s -check-prefix=V8
; RUN: llc -march=sparc < %s | FileCheck %s -check-prefix=V8 -check-prefix=V8-BE
; RUN: llc -march=sparcel < %s | FileCheck %s -check-prefix=V8 -check-prefix=V8-EL
; RUN: llc -march=sparc -O0 < %s | FileCheck %s -check-prefix=V8-UNOPT
; RUN: llc -march=sparc -mattr=v9 < %s | FileCheck %s -check-prefix=V9
; RUN: llc -mtriple=sparc64-unknown-linux < %s | FileCheck %s -check-prefix=SPARC64

; V8-LABEL: test_neg:
; V8: call get_double
; V8: fnegs %f0, %f0
; V8-BE: fnegs %f0, %f0
; V8-EL: fnegs %f1, %f1

; V8-UNOPT-LABEL: test_neg:
; V8-UNOPT: fnegs
Expand All @@ -27,7 +29,8 @@ entry:
}

; V8-LABEL: test_abs:
; V8: fabss %f0, %f0
; V8-BE: fabss %f0, %f0
; V8-EL: fabss %f1, %f1

; V8-UNOPT-LABEL: test_abs:
; V8-UNOPT: fabss
Expand Down
117 changes: 43 additions & 74 deletions test/CodeGen/SPARC/fp128.ll
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
; RUN: llc < %s -march=sparc -mattr=hard-quad-float | FileCheck %s --check-prefix=HARD
; RUN: llc < %s -march=sparc -mattr=-hard-quad-float | FileCheck %s --check-prefix=SOFT
; RUN: llc < %s -march=sparc -mattr=hard-quad-float | FileCheck %s --check-prefix=CHECK --check-prefix=HARD --check-prefix=BE
; RUN: llc < %s -march=sparcel -mattr=hard-quad-float | FileCheck %s --check-prefix=CHECK --check-prefix=HARD --check-prefix=EL
; RUN: llc < %s -march=sparc -mattr=-hard-quad-float | FileCheck %s --check-prefix=CHECK --check-prefix=SOFT --check-prefix=BE
; RUN: llc < %s -march=sparcel -mattr=-hard-quad-float | FileCheck %s --check-prefix=CHECK --check-prefix=SOFT --check-prefix=EL


; HARD-LABEL: f128_ops
; HARD: ldd
; HARD: ldd
; HARD: ldd
; HARD: ldd
; CHECK-LABEL: f128_ops:
; CHECK: ldd
; CHECK: ldd
; CHECK: ldd
; CHECK: ldd
; HARD: faddq [[R0:.+]], [[R1:.+]], [[R2:.+]]
; HARD: fsubq [[R2]], [[R3:.+]], [[R4:.+]]
; HARD: fmulq [[R4]], [[R5:.+]], [[R6:.+]]
; HARD: fdivq [[R6]], [[R2]]
; HARD: std
; HARD: std

; SOFT-LABEL: f128_ops
; SOFT: ldd
; SOFT: ldd
; SOFT: ldd
; SOFT: ldd
; SOFT: call _Q_add
; SOFT: call _Q_sub
; SOFT: call _Q_mul
; SOFT: call _Q_div
; SOFT: std
; SOFT: std
; CHECK: std
; CHECK: std

define void @f128_ops(fp128* noalias sret %scalar.result, fp128* byval %a, fp128* byval %b, fp128* byval %c, fp128* byval %d) {
entry:
Expand All @@ -40,19 +34,12 @@ entry:
ret void
}

; HARD-LABEL: f128_spill
; HARD: std %f{{.+}}, [%[[S0:.+]]]
; HARD: std %f{{.+}}, [%[[S1:.+]]]
; HARD-DAG: ldd [%[[S0]]], %f{{.+}}
; HARD-DAG: ldd [%[[S1]]], %f{{.+}}
; HARD: jmp {{%[oi]7}}+12

; SOFT-LABEL: f128_spill
; SOFT: std %f{{.+}}, [%[[S0:.+]]]
; SOFT: std %f{{.+}}, [%[[S1:.+]]]
; SOFT-DAG: ldd [%[[S0]]], %f{{.+}}
; SOFT-DAG: ldd [%[[S1]]], %f{{.+}}
; SOFT: jmp {{%[oi]7}}+12
; CHECK-LABEL: f128_spill:
; CHECK: std %f{{.+}}, [%[[S0:.+]]]
; CHECK: std %f{{.+}}, [%[[S1:.+]]]
; CHECK-DAG: ldd [%[[S0]]], %f{{.+}}
; CHECK-DAG: ldd [%[[S1]]], %f{{.+}}
; CHECK: jmp {{%[oi]7}}+12

define void @f128_spill(fp128* noalias sret %scalar.result, fp128* byval %a) {
entry:
Expand All @@ -62,11 +49,9 @@ entry:
ret void
}

; HARD-LABEL: f128_compare
; CHECK-LABEL: f128_compare:
; HARD: fcmpq
; HARD-NEXT: nop

; SOFT-LABEL: f128_compare
; SOFT: _Q_cmp

define i32 @f128_compare(fp128* byval %f0, fp128* byval %f1, i32 %a, i32 %b) {
Expand All @@ -78,11 +63,9 @@ entry:
ret i32 %ret
}

; HARD-LABEL: f128_compare2
; HARD: fcmpq
; HARD: fb{{ule|g}}

; SOFT-LABEL: f128_compare2
; CHECK-LABEL: f128_compare2:
; HARD: fcmpq
; HARD: fb{{ule|g}}
; SOFT: _Q_cmp
; SOFT: cmp

Expand All @@ -99,11 +82,11 @@ entry:
}


; HARD-LABEL: f128_abs
; HARD: fabss

; SOFT-LABEL: f128_abs
; SOFT: fabss
; CHECK-LABEL: f128_abs:
; CHECK: ldd [%o0], %f0
; CHECK: ldd [%o0+8], %f2
; BE: fabss %f0, %f0
; EL: fabss %f3, %f3

define void @f128_abs(fp128* noalias sret %scalar.result, fp128* byval %a) {
entry:
Expand All @@ -115,10 +98,8 @@ entry:

declare fp128 @llvm.fabs.f128(fp128) nounwind readonly

; HARD-LABEL: int_to_f128
; CHECK-LABEL: int_to_f128:
; HARD: fitoq

; SOFT-LABEL: int_to_f128
; SOFT: _Q_itoq

define void @int_to_f128(fp128* noalias sret %scalar.result, i32 %i) {
Expand All @@ -128,17 +109,12 @@ entry:
ret void
}

; HARD-LABEL: fp128_unaligned
; HARD: ldub
; HARD: faddq
; HARD: stb
; HARD: ret

; SOFT-LABEL: fp128_unaligned
; SOFT: ldub
; CHECK-LABEL: fp128_unaligned:
; CHECK: ldub
; HARD: faddq
; SOFT: call _Q_add
; SOFT: stb
; SOFT: ret
; CHECK: stb
; CHECK: ret

define void @fp128_unaligned(fp128* %a, fp128* %b, fp128* %c) {
entry:
Expand All @@ -149,10 +125,8 @@ entry:
ret void
}

; HARD-LABEL: uint_to_f128
; CHECK-LABEL: uint_to_f128:
; HARD: fdtoq

; SOFT-LABEL: uint_to_f128
; SOFT: _Q_utoq

define void @uint_to_f128(fp128* noalias sret %scalar.result, i32 %i) {
Expand All @@ -162,11 +136,9 @@ entry:
ret void
}

; HARD-LABEL: f128_to_i32
; CHECK-LABEL: f128_to_i32:
; HARD: fqtoi
; HARD: fqtoi

; SOFT-LABEL: f128_to_i32
; SOFT: call _Q_qtou
; SOFT: call _Q_qtoi

Expand All @@ -181,13 +153,11 @@ entry:
ret i32 %4
}

; HARD-LABEL: test_itoq_qtoi
; CHECK-LABEL: test_itoq_qtoi
; HARD-DAG: call _Q_lltoq
; HARD-DAG: call _Q_qtoll
; HARD-DAG: fitoq
; HARD-DAG: fqtoi

; SOFT-LABEL: test_itoq_qtoi
; SOFT-DAG: call _Q_lltoq
; SOFT-DAG: call _Q_qtoll
; SOFT-DAG: call _Q_itoq
Expand All @@ -209,15 +179,11 @@ entry:
ret void
}

; HARD-LABEL: test_utoq_qtou
; HARD-DAG: call _Q_ulltoq
; HARD-DAG: call _Q_qtoull
; CHECK-LABEL: test_utoq_qtou:
; CHECK-DAG: call _Q_ulltoq
; CHECK-DAG: call _Q_qtoull
; HARD-DAG: fdtoq
; HARD-DAG: fqtoi

; SOFT-LABEL: test_utoq_qtou
; SOFT-DAG: call _Q_ulltoq
; SOFT-DAG: call _Q_qtoull
; SOFT-DAG: call _Q_utoq
; SOFT-DAG: call _Q_qtou

Expand All @@ -237,8 +203,11 @@ entry:
ret void
}

; SOFT-LABEL: f128_neg
; SOFT: fnegs
; CHECK-LABEL: f128_neg:
; CHECK: ldd [%o0], %f0
; CHECK: ldd [%o0+8], %f2
; BE: fnegs %f0, %f0
; EL: fnegs %f3, %f3

define void @f128_neg(fp128* noalias sret %scalar.result, fp128* byval %a) {
entry:
Expand Down

0 comments on commit 90cc544

Please sign in to comment.