From: Michael Munday Date: Mon, 17 Feb 2020 11:43:33 +0000 (-0800) Subject: cmd/compile: clean up codegen for branch-on-carry on s390x X-Git-Tag: go1.15beta1~440 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ab7a65f2837b693f015f47572b6bf2c8f1062288;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- 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) diff --git a/test/codegen/mathbits.go b/test/codegen/mathbits.go index 8bd6242b1e..942605de55 100644 --- a/test/codegen/mathbits.go +++ b/test/codegen/mathbits.go @@ -472,6 +472,69 @@ func Add64M(p, q, r *[3]uint64) { r[2], c = bits.Add64(p[2], q[2], c) } +func Add64PanicOnOverflowEQ(a, b uint64) uint64 { + r, c := bits.Add64(a, b, 0) + // s390x:"BRC\t[$]3,",-"ADDE" + if c == 1 { + panic("overflow") + } + return r +} + +func Add64PanicOnOverflowNE(a, b uint64) uint64 { + r, c := bits.Add64(a, b, 0) + // s390x:"BRC\t[$]3,",-"ADDE" + if c != 0 { + panic("overflow") + } + return r +} + +func Add64PanicOnOverflowGT(a, b uint64) uint64 { + r, c := bits.Add64(a, b, 0) + // s390x:"BRC\t[$]3,",-"ADDE" + if c > 0 { + panic("overflow") + } + return r +} + +func Add64MPanicOnOverflowEQ(a, b [2]uint64) [2]uint64 { + var r [2]uint64 + var c uint64 + r[0], c = bits.Add64(a[0], b[0], c) + r[1], c = bits.Add64(a[1], b[1], c) + // s390x:"BRC\t[$]3," + if c == 1 { + panic("overflow") + } + return r +} + +func Add64MPanicOnOverflowNE(a, b [2]uint64) [2]uint64 { + var r [2]uint64 + var c uint64 + r[0], c = bits.Add64(a[0], b[0], c) + r[1], c = bits.Add64(a[1], b[1], c) + // s390x:"BRC\t[$]3," + if c != 0 { + panic("overflow") + } + return r +} + +func Add64MPanicOnOverflowGT(a, b [2]uint64) [2]uint64 { + var r [2]uint64 + var c uint64 + r[0], c = bits.Add64(a[0], b[0], c) + r[1], c = bits.Add64(a[1], b[1], c) + // s390x:"BRC\t[$]3," + if c > 0 { + panic("overflow") + } + return r +} + // --------------- // // bits.Sub* // // --------------- // @@ -552,6 +615,69 @@ func Sub64M(p, q, r *[3]uint64) { r[2], c = bits.Sub64(p[2], q[2], c) } +func Sub64PanicOnOverflowEQ(a, b uint64) uint64 { + r, b := bits.Sub64(a, b, 0) + // s390x:"BRC\t[$]12,",-"ADDE",-"SUBE" + if b == 1 { + panic("overflow") + } + return r +} + +func Sub64PanicOnOverflowNE(a, b uint64) uint64 { + r, b := bits.Sub64(a, b, 0) + // s390x:"BRC\t[$]12,",-"ADDE",-"SUBE" + if b != 0 { + panic("overflow") + } + return r +} + +func Sub64PanicOnOverflowGT(a, b uint64) uint64 { + r, b := bits.Sub64(a, b, 0) + // s390x:"BRC\t[$]12,",-"ADDE",-"SUBE" + if b > 0 { + panic("overflow") + } + return r +} + +func Sub64MPanicOnOverflowEQ(a, b [2]uint64) [2]uint64 { + var r [2]uint64 + var c uint64 + r[0], c = bits.Sub64(a[0], b[0], c) + r[1], c = bits.Sub64(a[1], b[1], c) + // s390x:"BRC\t[$]12," + if c == 1 { + panic("overflow") + } + return r +} + +func Sub64MPanicOnOverflowNE(a, b [2]uint64) [2]uint64 { + var r [2]uint64 + var c uint64 + r[0], c = bits.Sub64(a[0], b[0], c) + r[1], c = bits.Sub64(a[1], b[1], c) + // s390x:"BRC\t[$]12," + if c != 0 { + panic("overflow") + } + return r +} + +func Sub64MPanicOnOverflowGT(a, b [2]uint64) [2]uint64 { + var r [2]uint64 + var c uint64 + r[0], c = bits.Sub64(a[0], b[0], c) + r[1], c = bits.Sub64(a[1], b[1], c) + // s390x:"BRC\t[$]12," + if c > 0 { + panic("overflow") + } + return r +} + // --------------- // // bits.Mul* // // --------------- //