aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Munday <mike.munday@ibm.com>2020-02-17 03:43:33 -0800
committerMichael Munday <mike.munday@ibm.com>2020-04-22 20:11:06 +0000
commitab7a65f2837b693f015f47572b6bf2c8f1062288 (patch)
tree707bff72fe84b42a903f06fa897a4ba2817b8228 /src
parent81df5e69fc189af44459a4c6520b1c99d0210a92 (diff)
downloadgo-ab7a65f2837b693f015f47572b6bf2c8f1062288.tar.xz
cmd/compile: clean up codegen for branch-on-carry on s390x
This CL optimizes code that uses a carry from a function such as bits.Add64 as the condition in an if statement. For example: x, c := bits.Add64(a, b, 0) if c != 0 { panic("overflow") } Rather than converting the carry into a 0 or a 1 value and using that as an input to a comparison instruction the carry flag is now used as the input to a conditional branch directly. This typically removes an ADD LOGICAL WITH CARRY instruction when user code is doing overflow detection and is closer to the code that a user would expect to generate. Change-Id: I950431270955ab72f1b5c6db873b6abe769be0da Reviewed-on: https://go-review.googlesource.com/c/go/+/219757 Run-TryBot: Michael Munday <mike.munday@ibm.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
Diffstat (limited to 'src')
-rw-r--r--src/cmd/compile/internal/ssa/gen/S390X.rules14
-rw-r--r--src/cmd/compile/internal/ssa/rewriteS390X.go460
-rw-r--r--src/cmd/internal/obj/s390x/condition_code.go6
-rw-r--r--src/math/bits/bits_test.go124
4 files changed, 604 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules
index 7e713303af..61ba4ac38e 100644
--- a/src/cmd/compile/internal/ssa/gen/S390X.rules
+++ b/src/cmd/compile/internal/ssa/gen/S390X.rules
@@ -1218,6 +1218,20 @@
(SUBE x y (Select1 (SUBC (MOVDconst [0]) (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) c))))))
-> (SUBE x y c)
+// branch on carry
+(C(G|LG)IJ {s390x.Equal} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0]) => (BRC {s390x.NoCarry} carry)
+(C(G|LG)IJ {s390x.Equal} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [1]) => (BRC {s390x.Carry} carry)
+(C(G|LG)IJ {s390x.LessOrGreater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0]) => (BRC {s390x.Carry} carry)
+(C(G|LG)IJ {s390x.LessOrGreater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [1]) => (BRC {s390x.NoCarry} carry)
+(C(G|LG)IJ {s390x.Greater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0]) => (BRC {s390x.Carry} carry)
+
+// branch on borrow
+(C(G|LG)IJ {s390x.Equal} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0]) => (BRC {s390x.NoBorrow} borrow)
+(C(G|LG)IJ {s390x.Equal} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [1]) => (BRC {s390x.Borrow} borrow)
+(C(G|LG)IJ {s390x.LessOrGreater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0]) => (BRC {s390x.Borrow} borrow)
+(C(G|LG)IJ {s390x.LessOrGreater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [1]) => (BRC {s390x.NoBorrow} borrow)
+(C(G|LG)IJ {s390x.Greater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0]) => (BRC {s390x.Borrow} borrow)
+
// fused multiply-add
(Select0 (F(ADD|SUB) (FMUL y z) x)) -> (FM(ADD|SUB) x y z)
(Select0 (F(ADDS|SUBS) (FMULS y z) x)) -> (FM(ADDS|SUBS) x y z)
diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go
index 01b654ac95..a7f6b3cd9c 100644
--- a/src/cmd/compile/internal/ssa/rewriteS390X.go
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -19678,6 +19678,236 @@ func rewriteBlockS390X(b *Block) bool {
b.swapSuccessors()
return true
}
+ // match: (CGIJ {s390x.Equal} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0])
+ // result: (BRC {s390x.NoCarry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.NoCarry)
+ return true
+ }
+ // match: (CGIJ {s390x.Equal} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [1])
+ // result: (BRC {s390x.Carry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.Carry)
+ return true
+ }
+ // match: (CGIJ {s390x.LessOrGreater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0])
+ // result: (BRC {s390x.Carry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.Carry)
+ return true
+ }
+ // match: (CGIJ {s390x.LessOrGreater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [1])
+ // result: (BRC {s390x.NoCarry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.NoCarry)
+ return true
+ }
+ // match: (CGIJ {s390x.Greater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0])
+ // result: (BRC {s390x.Carry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Greater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.Carry)
+ return true
+ }
+ // match: (CGIJ {s390x.Equal} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0])
+ // result: (BRC {s390x.NoBorrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.NoBorrow)
+ return true
+ }
+ // match: (CGIJ {s390x.Equal} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [1])
+ // result: (BRC {s390x.Borrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.Borrow)
+ return true
+ }
+ // match: (CGIJ {s390x.LessOrGreater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0])
+ // result: (BRC {s390x.Borrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.Borrow)
+ return true
+ }
+ // match: (CGIJ {s390x.LessOrGreater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [1])
+ // result: (BRC {s390x.NoBorrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.NoBorrow)
+ return true
+ }
+ // match: (CGIJ {s390x.Greater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0])
+ // result: (BRC {s390x.Borrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToInt8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Greater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.Borrow)
+ return true
+ }
case BlockS390XCGRJ:
// match: (CGRJ {c} x (MOVDconst [y]) yes no)
// cond: is8Bit(y)
@@ -19993,6 +20223,236 @@ func rewriteBlockS390X(b *Block) bool {
b.swapSuccessors()
return true
}
+ // match: (CLGIJ {s390x.Equal} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0])
+ // result: (BRC {s390x.NoCarry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.NoCarry)
+ return true
+ }
+ // match: (CLGIJ {s390x.Equal} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [1])
+ // result: (BRC {s390x.Carry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.Carry)
+ return true
+ }
+ // match: (CLGIJ {s390x.LessOrGreater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0])
+ // result: (BRC {s390x.Carry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.Carry)
+ return true
+ }
+ // match: (CLGIJ {s390x.LessOrGreater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [1])
+ // result: (BRC {s390x.NoCarry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.NoCarry)
+ return true
+ }
+ // match: (CLGIJ {s390x.Greater} (Select0 (ADDE (MOVDconst [0]) (MOVDconst [0]) carry)) [0])
+ // result: (BRC {s390x.Carry} carry)
+ for b.Controls[0].Op == OpSelect0 {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpS390XADDE {
+ break
+ }
+ carry := v_0_0.Args[2]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_1 := v_0_0.Args[1]
+ if v_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Greater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, carry)
+ b.Aux = s390xCCMaskToAux(s390x.Carry)
+ return true
+ }
+ // match: (CLGIJ {s390x.Equal} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0])
+ // result: (BRC {s390x.NoBorrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.NoBorrow)
+ return true
+ }
+ // match: (CLGIJ {s390x.Equal} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [1])
+ // result: (BRC {s390x.Borrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.Equal {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.Borrow)
+ return true
+ }
+ // match: (CLGIJ {s390x.LessOrGreater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0])
+ // result: (BRC {s390x.Borrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.Borrow)
+ return true
+ }
+ // match: (CLGIJ {s390x.LessOrGreater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [1])
+ // result: (BRC {s390x.NoBorrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 1 || auxToS390xCCMask(b.Aux) != s390x.LessOrGreater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.NoBorrow)
+ return true
+ }
+ // match: (CLGIJ {s390x.Greater} (NEG (Select0 (SUBE (MOVDconst [0]) (MOVDconst [0]) borrow))) [0])
+ // result: (BRC {s390x.Borrow} borrow)
+ for b.Controls[0].Op == OpS390XNEG {
+ v_0 := b.Controls[0]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpSelect0 {
+ break
+ }
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpS390XSUBE {
+ break
+ }
+ borrow := v_0_0_0.Args[2]
+ v_0_0_0_0 := v_0_0_0.Args[0]
+ if v_0_0_0_0.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_0.AuxInt) != 0 {
+ break
+ }
+ v_0_0_0_1 := v_0_0_0.Args[1]
+ if v_0_0_0_1.Op != OpS390XMOVDconst || auxIntToInt64(v_0_0_0_1.AuxInt) != 0 || auxIntToUint8(b.AuxInt) != 0 || auxToS390xCCMask(b.Aux) != s390x.Greater {
+ break
+ }
+ b.resetWithControl(BlockS390XBRC, borrow)
+ b.Aux = s390xCCMaskToAux(s390x.Borrow)
+ return true
+ }
case BlockS390XCLGRJ:
// match: (CLGRJ {c} x (MOVDconst [y]) yes no)
// cond: isU8Bit(y)
diff --git a/src/cmd/internal/obj/s390x/condition_code.go b/src/cmd/internal/obj/s390x/condition_code.go
index a112911a32..764fc5bc6a 100644
--- a/src/cmd/internal/obj/s390x/condition_code.go
+++ b/src/cmd/internal/obj/s390x/condition_code.go
@@ -50,6 +50,12 @@ const (
// 4-bit mask
Always CCMask = Equal | Less | Greater | Unordered
+
+ // useful aliases
+ Carry CCMask = GreaterOrUnordered
+ NoCarry CCMask = LessOrEqual
+ Borrow CCMask = NoCarry
+ NoBorrow CCMask = Carry
)
// Inverse returns the complement of the condition code mask.
diff --git a/src/math/bits/bits_test.go b/src/math/bits/bits_test.go
index c0f43093d9..23b4539fcd 100644
--- a/src/math/bits/bits_test.go
+++ b/src/math/bits/bits_test.go
@@ -806,6 +806,130 @@ func TestAddSubUint64(t *testing.T) {
}
}
+func TestAdd64OverflowPanic(t *testing.T) {
+ // Test that 64-bit overflow panics fire correctly.
+ // These are designed to improve coverage of compiler intrinsics.
+ tests := []func(uint64, uint64) uint64{
+ func(a, b uint64) uint64 {
+ x, c := Add64(a, b, 0)
+ if c > 0 {
+ panic("overflow")
+ }
+ return x
+ },
+ func(a, b uint64) uint64 {
+ x, c := Add64(a, b, 0)
+ if c != 0 {
+ panic("overflow")
+ }
+ return x
+ },
+ func(a, b uint64) uint64 {
+ x, c := Add64(a, b, 0)
+ if c == 1 {
+ panic("overflow")
+ }
+ return x
+ },
+ func(a, b uint64) uint64 {
+ x, c := Add64(a, b, 0)
+ if c != 1 {
+ return x
+ }
+ panic("overflow")
+ },
+ func(a, b uint64) uint64 {
+ x, c := Add64(a, b, 0)
+ if c == 0 {
+ return x
+ }
+ panic("overflow")
+ },
+ }
+ for _, test := range tests {
+ shouldPanic := func(f func()) {
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatalf("expected panic")
+ }
+ }()
+ f()
+ }
+
+ // overflow
+ shouldPanic(func() { test(_M64, 1) })
+ shouldPanic(func() { test(1, _M64) })
+ shouldPanic(func() { test(_M64, _M64) })
+
+ // no overflow
+ test(_M64, 0)
+ test(0, 0)
+ test(1, 1)
+ }
+}
+
+func TestSub64OverflowPanic(t *testing.T) {
+ // Test that 64-bit overflow panics fire correctly.
+ // These are designed to improve coverage of compiler intrinsics.
+ tests := []func(uint64, uint64) uint64{
+ func(a, b uint64) uint64 {
+ x, c := Sub64(a, b, 0)
+ if c > 0 {
+ panic("overflow")
+ }
+ return x
+ },
+ func(a, b uint64) uint64 {
+ x, c := Sub64(a, b, 0)
+ if c != 0 {
+ panic("overflow")
+ }
+ return x
+ },
+ func(a, b uint64) uint64 {
+ x, c := Sub64(a, b, 0)
+ if c == 1 {
+ panic("overflow")
+ }
+ return x
+ },
+ func(a, b uint64) uint64 {
+ x, c := Sub64(a, b, 0)
+ if c != 1 {
+ return x
+ }
+ panic("overflow")
+ },
+ func(a, b uint64) uint64 {
+ x, c := Sub64(a, b, 0)
+ if c == 0 {
+ return x
+ }
+ panic("overflow")
+ },
+ }
+ for _, test := range tests {
+ shouldPanic := func(f func()) {
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatalf("expected panic")
+ }
+ }()
+ f()
+ }
+
+ // overflow
+ shouldPanic(func() { test(0, 1) })
+ shouldPanic(func() { test(1, _M64) })
+ shouldPanic(func() { test(_M64-1, _M64) })
+
+ // no overflow
+ test(_M64, 0)
+ test(0, 0)
+ test(1, 1)
+ }
+}
+
func TestMulDiv(t *testing.T) {
testMul := func(msg string, f func(x, y uint) (hi, lo uint), x, y, hi, lo uint) {
hi1, lo1 := f(x, y)