cmd/compile: redo flag constant ops for arm

Encode the flag results in an auxint field instead of having
one opcode per flag state. This helps us handle the new *noov
branches in a unified manner.

This is only for arm, arm64 is in a subsequent CL.

We could extend to other architectures as well, athough it would
only be cleanup, no behavioral change.

Update #39505

Change-Id: Ia46cea596faad540d1496c5915ab1274571543f0
Reviewed-on: https://go-review.googlesource.com/c/go/+/238077
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Keith Randall 2020-06-15 14:43:02 -07:00
parent 377c1536f5
commit 40ef1faabc
15 changed files with 841 additions and 1213 deletions

View file

@ -115,6 +115,7 @@ var knownFormats = map[string]string{
"cmd/compile/internal/ssa.Sym %v": "", "cmd/compile/internal/ssa.Sym %v": "",
"cmd/compile/internal/ssa.ValAndOff %s": "", "cmd/compile/internal/ssa.ValAndOff %s": "",
"cmd/compile/internal/ssa.domain %v": "", "cmd/compile/internal/ssa.domain %v": "",
"cmd/compile/internal/ssa.flagConstant %s": "",
"cmd/compile/internal/ssa.posetNode %v": "", "cmd/compile/internal/ssa.posetNode %v": "",
"cmd/compile/internal/ssa.posetTestOp %v": "", "cmd/compile/internal/ssa.posetTestOp %v": "",
"cmd/compile/internal/ssa.rbrank %d": "", "cmd/compile/internal/ssa.rbrank %d": "",

View file

@ -857,12 +857,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.AGETCALLERPC) p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpARMFlagEQ, case ssa.OpARMFlagConstant:
ssa.OpARMFlagLT_ULT, v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
ssa.OpARMFlagLT_UGT,
ssa.OpARMFlagGT_ULT,
ssa.OpARMFlagGT_UGT:
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
case ssa.OpARMInvertFlags: case ssa.OpARMInvertFlags:
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
case ssa.OpClobber: case ssa.OpClobber:

View file

@ -185,6 +185,11 @@ func checkFunc(f *Func) {
f.Fatalf("bad type %T for S390XRotateParams in %v", v.Aux, v) f.Fatalf("bad type %T for S390XRotateParams in %v", v.Aux, v)
} }
canHaveAux = true canHaveAux = true
case auxFlagConstant:
if v.AuxInt < 0 || v.AuxInt > 15 {
f.Fatalf("bad FlagConstant AuxInt value for %v", v)
}
canHaveAuxInt = true
default: default:
f.Fatalf("unknown aux type for %s", v.Op) f.Fatalf("unknown aux type for %s", v.Op)
} }

View file

@ -0,0 +1,31 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64
#include "textflag.h"
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24
MOVQ x+0(FP), AX
ADDQ y+8(FP), AX
PUSHFQ
POPQ AX
MOVQ AX, ret+16(FP)
RET
TEXT ·asmSubFlags(SB),NOSPLIT,$0-24
MOVQ x+0(FP), AX
SUBQ y+8(FP), AX
PUSHFQ
POPQ AX
MOVQ AX, ret+16(FP)
RET
TEXT ·asmAndFlags(SB),NOSPLIT,$0-24
MOVQ x+0(FP), AX
ANDQ y+8(FP), AX
PUSHFQ
POPQ AX
MOVQ AX, ret+16(FP)
RET

View file

@ -0,0 +1,32 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm64
#include "textflag.h"
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24
MOVD x+0(FP), R0
MOVD y+8(FP), R1
CMN R0, R1
WORD $0xd53b4200 // MOVD NZCV, R0
MOVD R0, ret+16(FP)
RET
TEXT ·asmSubFlags(SB),NOSPLIT,$0-24
MOVD x+0(FP), R0
MOVD y+8(FP), R1
CMP R1, R0
WORD $0xd53b4200 // MOVD NZCV, R0
MOVD R0, ret+16(FP)
RET
TEXT ·asmAndFlags(SB),NOSPLIT,$0-24
MOVD x+0(FP), R0
MOVD y+8(FP), R1
TST R1, R0
WORD $0xd53b4200 // MOVD NZCV, R0
BIC $0x30000000, R0 // clear C, V bits, as TST does not change those flags
MOVD R0, ret+16(FP)
RET

View file

@ -0,0 +1,108 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64 arm64
package ssa
// This file tests the functions addFlags64 and subFlags64 by comparing their
// results to what the chip calculates.
import (
"runtime"
"testing"
)
func TestAddFlagsNative(t *testing.T) {
var numbers = []int64{
1, 0, -1,
2, -2,
1<<63 - 1, -1 << 63,
}
coverage := map[flagConstant]bool{}
for _, x := range numbers {
for _, y := range numbers {
a := addFlags64(x, y)
b := flagRegister2flagConstant(asmAddFlags(x, y), false)
if a != b {
t.Errorf("asmAdd diff: x=%x y=%x got=%s want=%s\n", x, y, a, b)
}
coverage[a] = true
}
}
if len(coverage) != 9 { // TODO: can we cover all outputs?
t.Errorf("coverage too small, got %d want 9", len(coverage))
}
}
func TestSubFlagsNative(t *testing.T) {
var numbers = []int64{
1, 0, -1,
2, -2,
1<<63 - 1, -1 << 63,
}
coverage := map[flagConstant]bool{}
for _, x := range numbers {
for _, y := range numbers {
a := subFlags64(x, y)
b := flagRegister2flagConstant(asmSubFlags(x, y), true)
if a != b {
t.Errorf("asmSub diff: x=%x y=%x got=%s want=%s\n", x, y, a, b)
}
coverage[a] = true
}
}
if len(coverage) != 7 { // TODO: can we cover all outputs?
t.Errorf("coverage too small, got %d want 7", len(coverage))
}
}
func TestAndFlagsNative(t *testing.T) {
var numbers = []int64{
1, 0, -1,
2, -2,
1<<63 - 1, -1 << 63,
}
coverage := map[flagConstant]bool{}
for _, x := range numbers {
for _, y := range numbers {
a := logicFlags64(x & y)
b := flagRegister2flagConstant(asmAndFlags(x, y), false)
if a != b {
t.Errorf("asmAnd diff: x=%x y=%x got=%s want=%s\n", x, y, a, b)
}
coverage[a] = true
}
}
if len(coverage) != 3 {
t.Errorf("coverage too small, got %d want 3", len(coverage))
}
}
func asmAddFlags(x, y int64) int
func asmSubFlags(x, y int64) int
func asmAndFlags(x, y int64) int
func flagRegister2flagConstant(x int, sub bool) flagConstant {
var fcb flagConstantBuilder
switch runtime.GOARCH {
case "amd64":
fcb.Z = x>>6&1 != 0
fcb.N = x>>7&1 != 0
fcb.C = x>>0&1 != 0
if sub {
// Convert from amd64-sense to arm-sense
fcb.C = !fcb.C
}
fcb.V = x>>11&1 != 0
case "arm64":
fcb.Z = x>>30&1 != 0
fcb.N = x>>31&1 != 0
fcb.C = x>>29&1 != 0
fcb.V = x>>28&1 != 0
default:
panic("unsupported architecture: " + runtime.GOARCH)
}
return fcb.encode()
}

View file

@ -609,89 +609,59 @@
(Select1 (CALLudiv x (MOVWconst [c]))) && isPowerOfTwo(c) -> (ANDconst [c-1] x) (Select1 (CALLudiv x (MOVWconst [c]))) && isPowerOfTwo(c) -> (ANDconst [c-1] x)
// constant comparisons // constant comparisons
(CMPconst (MOVWconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ) (CMPconst (MOVWconst [x]) [y]) => (FlagConstant [subFlags32(x,y)])
(CMPconst (MOVWconst [x]) [y]) && int32(x)<int32(y) && uint32(x)<uint32(y) -> (FlagLT_ULT) (CMNconst (MOVWconst [x]) [y]) => (FlagConstant [addFlags32(x,y)])
(CMPconst (MOVWconst [x]) [y]) && int32(x)<int32(y) && uint32(x)>uint32(y) -> (FlagLT_UGT) (TSTconst (MOVWconst [x]) [y]) => (FlagConstant [logicFlags32(x&y)])
(CMPconst (MOVWconst [x]) [y]) && int32(x)>int32(y) && uint32(x)<uint32(y) -> (FlagGT_ULT) (TEQconst (MOVWconst [x]) [y]) => (FlagConstant [logicFlags32(x^y)])
(CMPconst (MOVWconst [x]) [y]) && int32(x)>int32(y) && uint32(x)>uint32(y) -> (FlagGT_UGT)
(CMNconst (MOVWconst [x]) [y]) && int32(x)==int32(-y) -> (FlagEQ)
(CMNconst (MOVWconst [x]) [y]) && int32(x)<int32(-y) && uint32(x)<uint32(-y) -> (FlagLT_ULT)
(CMNconst (MOVWconst [x]) [y]) && int32(x)<int32(-y) && uint32(x)>uint32(-y) -> (FlagLT_UGT)
(CMNconst (MOVWconst [x]) [y]) && int32(x)>int32(-y) && uint32(x)<uint32(-y) -> (FlagGT_ULT)
(CMNconst (MOVWconst [x]) [y]) && int32(x)>int32(-y) && uint32(x)>uint32(-y) -> (FlagGT_UGT)
(TSTconst (MOVWconst [x]) [y]) && int32(x&y)==0 -> (FlagEQ)
(TSTconst (MOVWconst [x]) [y]) && int32(x&y)<0 -> (FlagLT_UGT)
(TSTconst (MOVWconst [x]) [y]) && int32(x&y)>0 -> (FlagGT_UGT)
(TEQconst (MOVWconst [x]) [y]) && int32(x^y)==0 -> (FlagEQ)
(TEQconst (MOVWconst [x]) [y]) && int32(x^y)<0 -> (FlagLT_UGT)
(TEQconst (MOVWconst [x]) [y]) && int32(x^y)>0 -> (FlagGT_UGT)
// other known comparisons // other known comparisons
(CMPconst (MOVBUreg _) [c]) && 0xff < c -> (FlagLT_ULT) (CMPconst (MOVBUreg _) [c]) && 0xff < c => (FlagConstant [subFlags32(0,1)])
(CMPconst (MOVHUreg _) [c]) && 0xffff < c -> (FlagLT_ULT) (CMPconst (MOVHUreg _) [c]) && 0xffff < c => (FlagConstant [subFlags32(0,1)])
(CMPconst (ANDconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT_ULT) (CMPconst (ANDconst _ [m]) [n]) && 0 <= m && m < n => (FlagConstant [subFlags32(0,1)])
(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n) -> (FlagLT_ULT) (CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n) => (FlagConstant [subFlags32(0,1)])
// absorb flag constants into branches // absorb flag constants into branches
(EQ (FlagEQ) yes no) -> (First yes no) (EQ (FlagConstant [fc]) yes no) && fc.eq() => (First yes no)
(EQ (FlagLT_ULT) yes no) -> (First no yes) (EQ (FlagConstant [fc]) yes no) && !fc.eq() => (First no yes)
(EQ (FlagLT_UGT) yes no) -> (First no yes)
(EQ (FlagGT_ULT) yes no) -> (First no yes)
(EQ (FlagGT_UGT) yes no) -> (First no yes)
(NE (FlagEQ) yes no) -> (First no yes) (NE (FlagConstant [fc]) yes no) && fc.ne() => (First yes no)
(NE (FlagLT_ULT) yes no) -> (First yes no) (NE (FlagConstant [fc]) yes no) && !fc.ne() => (First no yes)
(NE (FlagLT_UGT) yes no) -> (First yes no)
(NE (FlagGT_ULT) yes no) -> (First yes no)
(NE (FlagGT_UGT) yes no) -> (First yes no)
(LT (FlagEQ) yes no) -> (First no yes) (LT (FlagConstant [fc]) yes no) && fc.lt() => (First yes no)
(LT (FlagLT_ULT) yes no) -> (First yes no) (LT (FlagConstant [fc]) yes no) && !fc.lt() => (First no yes)
(LT (FlagLT_UGT) yes no) -> (First yes no)
(LT (FlagGT_ULT) yes no) -> (First no yes)
(LT (FlagGT_UGT) yes no) -> (First no yes)
(LE (FlagEQ) yes no) -> (First yes no) (LE (FlagConstant [fc]) yes no) && fc.le() => (First yes no)
(LE (FlagLT_ULT) yes no) -> (First yes no) (LE (FlagConstant [fc]) yes no) && !fc.le() => (First no yes)
(LE (FlagLT_UGT) yes no) -> (First yes no)
(LE (FlagGT_ULT) yes no) -> (First no yes)
(LE (FlagGT_UGT) yes no) -> (First no yes)
(GT (FlagEQ) yes no) -> (First no yes) (GT (FlagConstant [fc]) yes no) && fc.gt() => (First yes no)
(GT (FlagLT_ULT) yes no) -> (First no yes) (GT (FlagConstant [fc]) yes no) && !fc.gt() => (First no yes)
(GT (FlagLT_UGT) yes no) -> (First no yes)
(GT (FlagGT_ULT) yes no) -> (First yes no)
(GT (FlagGT_UGT) yes no) -> (First yes no)
(GE (FlagEQ) yes no) -> (First yes no) (GE (FlagConstant [fc]) yes no) && fc.ge() => (First yes no)
(GE (FlagLT_ULT) yes no) -> (First no yes) (GE (FlagConstant [fc]) yes no) && !fc.ge() => (First no yes)
(GE (FlagLT_UGT) yes no) -> (First no yes)
(GE (FlagGT_ULT) yes no) -> (First yes no)
(GE (FlagGT_UGT) yes no) -> (First yes no)
(ULT (FlagEQ) yes no) -> (First no yes) (ULT (FlagConstant [fc]) yes no) && fc.ult() => (First yes no)
(ULT (FlagLT_ULT) yes no) -> (First yes no) (ULT (FlagConstant [fc]) yes no) && !fc.ult() => (First no yes)
(ULT (FlagLT_UGT) yes no) -> (First no yes)
(ULT (FlagGT_ULT) yes no) -> (First yes no)
(ULT (FlagGT_UGT) yes no) -> (First no yes)
(ULE (FlagEQ) yes no) -> (First yes no) (ULE (FlagConstant [fc]) yes no) && fc.ule() => (First yes no)
(ULE (FlagLT_ULT) yes no) -> (First yes no) (ULE (FlagConstant [fc]) yes no) && !fc.ule() => (First no yes)
(ULE (FlagLT_UGT) yes no) -> (First no yes)
(ULE (FlagGT_ULT) yes no) -> (First yes no)
(ULE (FlagGT_UGT) yes no) -> (First no yes)
(UGT (FlagEQ) yes no) -> (First no yes) (UGT (FlagConstant [fc]) yes no) && fc.ugt() => (First yes no)
(UGT (FlagLT_ULT) yes no) -> (First no yes) (UGT (FlagConstant [fc]) yes no) && !fc.ugt() => (First no yes)
(UGT (FlagLT_UGT) yes no) -> (First yes no)
(UGT (FlagGT_ULT) yes no) -> (First no yes)
(UGT (FlagGT_UGT) yes no) -> (First yes no)
(UGE (FlagEQ) yes no) -> (First yes no) (UGE (FlagConstant [fc]) yes no) && fc.uge() => (First yes no)
(UGE (FlagLT_ULT) yes no) -> (First no yes) (UGE (FlagConstant [fc]) yes no) && !fc.uge() => (First no yes)
(UGE (FlagLT_UGT) yes no) -> (First yes no)
(UGE (FlagGT_ULT) yes no) -> (First no yes) (LTnoov (FlagConstant [fc]) yes no) && fc.ltNoov() => (First yes no)
(UGE (FlagGT_UGT) yes no) -> (First yes no) (LTnoov (FlagConstant [fc]) yes no) && !fc.ltNoov() => (First no yes)
(LEnoov (FlagConstant [fc]) yes no) && fc.leNoov() => (First yes no)
(LEnoov (FlagConstant [fc]) yes no) && !fc.leNoov() => (First no yes)
(GTnoov (FlagConstant [fc]) yes no) && fc.gtNoov() => (First yes no)
(GTnoov (FlagConstant [fc]) yes no) && !fc.gtNoov() => (First no yes)
(GEnoov (FlagConstant [fc]) yes no) && fc.geNoov() => (First yes no)
(GEnoov (FlagConstant [fc]) yes no) && !fc.geNoov() => (First no yes)
// absorb InvertFlags into branches // absorb InvertFlags into branches
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no) (LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
@ -710,65 +680,16 @@
(GTnoov (InvertFlags cmp) yes no) => (LTnoov cmp yes no) (GTnoov (InvertFlags cmp) yes no) => (LTnoov cmp yes no)
// absorb flag constants into boolean values // absorb flag constants into boolean values
(Equal (FlagEQ)) -> (MOVWconst [1]) (Equal (FlagConstant [fc])) => (MOVWconst [b2i32(fc.eq())])
(Equal (FlagLT_ULT)) -> (MOVWconst [0]) (NotEqual (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ne())])
(Equal (FlagLT_UGT)) -> (MOVWconst [0]) (LessThan (FlagConstant [fc])) => (MOVWconst [b2i32(fc.lt())])
(Equal (FlagGT_ULT)) -> (MOVWconst [0]) (LessThanU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ult())])
(Equal (FlagGT_UGT)) -> (MOVWconst [0]) (LessEqual (FlagConstant [fc])) => (MOVWconst [b2i32(fc.le())])
(LessEqualU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ule())])
(NotEqual (FlagEQ)) -> (MOVWconst [0]) (GreaterThan (FlagConstant [fc])) => (MOVWconst [b2i32(fc.gt())])
(NotEqual (FlagLT_ULT)) -> (MOVWconst [1]) (GreaterThanU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ugt())])
(NotEqual (FlagLT_UGT)) -> (MOVWconst [1]) (GreaterEqual (FlagConstant [fc])) => (MOVWconst [b2i32(fc.ge())])
(NotEqual (FlagGT_ULT)) -> (MOVWconst [1]) (GreaterEqualU (FlagConstant [fc])) => (MOVWconst [b2i32(fc.uge())])
(NotEqual (FlagGT_UGT)) -> (MOVWconst [1])
(LessThan (FlagEQ)) -> (MOVWconst [0])
(LessThan (FlagLT_ULT)) -> (MOVWconst [1])
(LessThan (FlagLT_UGT)) -> (MOVWconst [1])
(LessThan (FlagGT_ULT)) -> (MOVWconst [0])
(LessThan (FlagGT_UGT)) -> (MOVWconst [0])
(LessThanU (FlagEQ)) -> (MOVWconst [0])
(LessThanU (FlagLT_ULT)) -> (MOVWconst [1])
(LessThanU (FlagLT_UGT)) -> (MOVWconst [0])
(LessThanU (FlagGT_ULT)) -> (MOVWconst [1])
(LessThanU (FlagGT_UGT)) -> (MOVWconst [0])
(LessEqual (FlagEQ)) -> (MOVWconst [1])
(LessEqual (FlagLT_ULT)) -> (MOVWconst [1])
(LessEqual (FlagLT_UGT)) -> (MOVWconst [1])
(LessEqual (FlagGT_ULT)) -> (MOVWconst [0])
(LessEqual (FlagGT_UGT)) -> (MOVWconst [0])
(LessEqualU (FlagEQ)) -> (MOVWconst [1])
(LessEqualU (FlagLT_ULT)) -> (MOVWconst [1])
(LessEqualU (FlagLT_UGT)) -> (MOVWconst [0])
(LessEqualU (FlagGT_ULT)) -> (MOVWconst [1])
(LessEqualU (FlagGT_UGT)) -> (MOVWconst [0])
(GreaterThan (FlagEQ)) -> (MOVWconst [0])
(GreaterThan (FlagLT_ULT)) -> (MOVWconst [0])
(GreaterThan (FlagLT_UGT)) -> (MOVWconst [0])
(GreaterThan (FlagGT_ULT)) -> (MOVWconst [1])
(GreaterThan (FlagGT_UGT)) -> (MOVWconst [1])
(GreaterThanU (FlagEQ)) -> (MOVWconst [0])
(GreaterThanU (FlagLT_ULT)) -> (MOVWconst [0])
(GreaterThanU (FlagLT_UGT)) -> (MOVWconst [1])
(GreaterThanU (FlagGT_ULT)) -> (MOVWconst [0])
(GreaterThanU (FlagGT_UGT)) -> (MOVWconst [1])
(GreaterEqual (FlagEQ)) -> (MOVWconst [1])
(GreaterEqual (FlagLT_ULT)) -> (MOVWconst [0])
(GreaterEqual (FlagLT_UGT)) -> (MOVWconst [0])
(GreaterEqual (FlagGT_ULT)) -> (MOVWconst [1])
(GreaterEqual (FlagGT_UGT)) -> (MOVWconst [1])
(GreaterEqualU (FlagEQ)) -> (MOVWconst [1])
(GreaterEqualU (FlagLT_ULT)) -> (MOVWconst [0])
(GreaterEqualU (FlagLT_UGT)) -> (MOVWconst [1])
(GreaterEqualU (FlagGT_ULT)) -> (MOVWconst [0])
(GreaterEqualU (FlagGT_UGT)) -> (MOVWconst [1])
// absorb InvertFlags into boolean values // absorb InvertFlags into boolean values
(Equal (InvertFlags x)) -> (Equal x) (Equal (InvertFlags x)) -> (Equal x)
@ -783,26 +704,17 @@
(GreaterEqualU (InvertFlags x)) -> (LessEqualU x) (GreaterEqualU (InvertFlags x)) -> (LessEqualU x)
// absorb flag constants into conditional instructions // absorb flag constants into conditional instructions
(CMOVWLSconst _ (FlagEQ) [c]) -> (MOVWconst [c]) (CMOVWLSconst _ (FlagConstant [fc]) [c]) && fc.ule() => (MOVWconst [c])
(CMOVWLSconst _ (FlagLT_ULT) [c]) -> (MOVWconst [c]) (CMOVWLSconst x (FlagConstant [fc]) [c]) && fc.ugt() => x
(CMOVWLSconst x (FlagLT_UGT)) -> x
(CMOVWLSconst _ (FlagGT_ULT) [c]) -> (MOVWconst [c])
(CMOVWLSconst x (FlagGT_UGT)) -> x
(CMOVWHSconst _ (FlagEQ) [c]) -> (MOVWconst [c]) (CMOVWHSconst _ (FlagConstant [fc]) [c]) && fc.uge() => (MOVWconst [c])
(CMOVWHSconst x (FlagLT_ULT)) -> x (CMOVWHSconst x (FlagConstant [fc]) [c]) && fc.ult() => x
(CMOVWHSconst _ (FlagLT_UGT) [c]) -> (MOVWconst [c])
(CMOVWHSconst x (FlagGT_ULT)) -> x
(CMOVWHSconst _ (FlagGT_UGT) [c]) -> (MOVWconst [c])
(CMOVWLSconst x (InvertFlags flags) [c]) -> (CMOVWHSconst x flags [c]) (CMOVWLSconst x (InvertFlags flags) [c]) -> (CMOVWHSconst x flags [c])
(CMOVWHSconst x (InvertFlags flags) [c]) -> (CMOVWLSconst x flags [c]) (CMOVWHSconst x (InvertFlags flags) [c]) -> (CMOVWLSconst x flags [c])
(SRAcond x _ (FlagEQ)) -> (SRAconst x [31]) (SRAcond x _ (FlagConstant [fc])) && fc.uge() => (SRAconst x [31])
(SRAcond x y (FlagLT_ULT)) -> (SRA x y) (SRAcond x y (FlagConstant [fc])) && fc.ult() => (SRA x y)
(SRAcond x _ (FlagLT_UGT)) -> (SRAconst x [31])
(SRAcond x y (FlagGT_ULT)) -> (SRA x y)
(SRAcond x _ (FlagGT_UGT)) -> (SRAconst x [31])
// remove redundant *const ops // remove redundant *const ops
(ADDconst [0] x) -> x (ADDconst [0] x) -> x

View file

@ -550,18 +550,12 @@ func init() {
{name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go). {name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
{name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r0, r1}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go). {name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r0, r1}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
// Constant flag values. For any comparison, there are 5 possible // Constant flag value.
// outcomes: the three from the signed total order (<,==,>) and the // Note: there's an "unordered" outcome for floating-point
// three from the unsigned total order. The == cases overlap.
// Note: there's a sixth "unordered" outcome for floating-point
// comparisons, but we don't use such a beast yet. // comparisons, but we don't use such a beast yet.
// These ops are for temporary use by rewrite rules. They // This op is for temporary use by rewrite rules. It
// cannot appear in the generated assembly. // cannot appear in the generated assembly.
{name: "FlagEQ"}, // equal {name: "FlagConstant", aux: "FlagConstant"},
{name: "FlagLT_ULT"}, // signed < and unsigned <
{name: "FlagLT_UGT"}, // signed < and unsigned >
{name: "FlagGT_UGT"}, // signed > and unsigned <
{name: "FlagGT_ULT"}, // signed > and unsigned >
// (InvertFlags (CMP a b)) == (CMP b a) // (InvertFlags (CMP a b)) == (CMP b a)
// InvertFlags is a pseudo-op which can't appear in assembly output. // InvertFlags is a pseudo-op which can't appear in assembly output.

View file

@ -1423,7 +1423,7 @@ func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxi
func opHasAuxInt(op opData) bool { func opHasAuxInt(op opData) bool {
switch op.aux { switch op.aux {
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "TypSize", "ARM64BitField": case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant":
return true return true
} }
return false return false
@ -1818,6 +1818,8 @@ func (op opData) auxIntType() string {
return "int64" return "int64"
case "CCop": case "CCop":
return "Op" return "Op"
case "FlagConstant":
return "flagConstant"
default: default:
return "invalid" return "invalid"
} }

View file

@ -77,6 +77,7 @@ const (
auxInt128 // auxInt represents a 128-bit integer. Always 0. auxInt128 // auxInt represents a 128-bit integer. Always 0.
auxFloat32 // auxInt is a float32 (encoded with math.Float64bits) auxFloat32 // auxInt is a float32 (encoded with math.Float64bits)
auxFloat64 // auxInt is a float64 (encoded with math.Float64bits) auxFloat64 // auxInt is a float64 (encoded with math.Float64bits)
auxFlagConstant // auxInt is a flagConstant
auxString // aux is a string auxString // aux is a string
auxSym // aux is a symbol (a *gc.Node for locals, an *obj.LSym for globals, or nil for none) auxSym // aux is a symbol (a *gc.Node for locals, an *obj.LSym for globals, or nil for none)
auxSymOff // aux is a symbol, auxInt is an offset auxSymOff // aux is a symbol, auxInt is an offset

View file

@ -1283,11 +1283,7 @@ const (
OpARMLoweredPanicExtendA OpARMLoweredPanicExtendA
OpARMLoweredPanicExtendB OpARMLoweredPanicExtendB
OpARMLoweredPanicExtendC OpARMLoweredPanicExtendC
OpARMFlagEQ OpARMFlagConstant
OpARMFlagLT_ULT
OpARMFlagLT_UGT
OpARMFlagGT_UGT
OpARMFlagGT_ULT
OpARMInvertFlags OpARMInvertFlags
OpARMLoweredWB OpARMLoweredWB
@ -16911,29 +16907,10 @@ var opcodeTable = [...]opInfo{
}, },
}, },
{ {
name: "FlagEQ", name: "FlagConstant",
argLen: 0, auxType: auxFlagConstant,
reg: regInfo{}, argLen: 0,
}, reg: regInfo{},
{
name: "FlagLT_ULT",
argLen: 0,
reg: regInfo{},
},
{
name: "FlagLT_UGT",
argLen: 0,
reg: regInfo{},
},
{
name: "FlagGT_UGT",
argLen: 0,
reg: regInfo{},
},
{
name: "FlagGT_ULT",
argLen: 0,
reg: regInfo{},
}, },
{ {
name: "InvertFlags", name: "InvertFlags",

View file

@ -511,6 +511,14 @@ func b2i(b bool) int64 {
return 0 return 0
} }
// b2i32 translates a boolean value to 0 or 1.
func b2i32(b bool) int32 {
if b {
return 1
}
return 0
}
// shiftIsBounded reports whether (left/right) shift Value v is known to be bounded. // shiftIsBounded reports whether (left/right) shift Value v is known to be bounded.
// A shift is bounded if it is shifting by less than the width of the shifted value. // A shift is bounded if it is shifting by less than the width of the shifted value.
func shiftIsBounded(v *Value) bool { func shiftIsBounded(v *Value) bool {
@ -616,6 +624,9 @@ func auxIntToInt128(x int64) int128 {
} }
return 0 return 0
} }
func auxIntToFlagConstant(x int64) flagConstant {
return flagConstant(x)
}
func boolToAuxInt(b bool) int64 { func boolToAuxInt(b bool) int64 {
if b { if b {
@ -653,6 +664,9 @@ func int128ToAuxInt(x int128) int64 {
} }
return 0 return 0
} }
func flagConstantToAuxInt(x flagConstant) int64 {
return int64(x)
}
func auxToString(i interface{}) string { func auxToString(i interface{}) string {
return i.(string) return i.(string)
@ -1473,3 +1487,170 @@ func sequentialAddresses(x, y *Value, n int64) bool {
} }
return false return false
} }
// flagConstant represents the result of a compile-time comparison.
// The sense of these flags does not necessarily represent the hardware's notion
// of a flags register - these are just a compile-time construct.
// We happen to match the semantics to those of arm/arm64.
// Note that these semantics differ from x86: the carry flag has the opposite
// sense on a subtraction!
// On amd64, C=1 represents a borrow, e.g. SBB on amd64 does x - y - C.
// On arm64, C=0 represents a borrow, e.g. SBC on arm64 does x - y - ^C.
// (because it does x + ^y + C).
// See https://en.wikipedia.org/wiki/Carry_flag#Vs._borrow_flag
type flagConstant uint8
// N reports whether the result of an operation is negative (high bit set).
func (fc flagConstant) N() bool {
return fc&1 != 0
}
// Z reports whether the result of an operation is 0.
func (fc flagConstant) Z() bool {
return fc&2 != 0
}
// C reports whether an unsigned add overflowed (carry), or an
// unsigned subtract did not underflow (borrow).
func (fc flagConstant) C() bool {
return fc&4 != 0
}
// V reports whether a signed operation overflowed or underflowed.
func (fc flagConstant) V() bool {
return fc&8 != 0
}
func (fc flagConstant) eq() bool {
return fc.Z()
}
func (fc flagConstant) ne() bool {
return !fc.Z()
}
func (fc flagConstant) lt() bool {
return fc.N() != fc.V()
}
func (fc flagConstant) le() bool {
return fc.Z() || fc.lt()
}
func (fc flagConstant) gt() bool {
return !fc.Z() && fc.ge()
}
func (fc flagConstant) ge() bool {
return fc.N() == fc.V()
}
func (fc flagConstant) ult() bool {
return !fc.C()
}
func (fc flagConstant) ule() bool {
return fc.Z() || fc.ult()
}
func (fc flagConstant) ugt() bool {
return !fc.Z() && fc.uge()
}
func (fc flagConstant) uge() bool {
return fc.C()
}
func (fc flagConstant) ltNoov() bool {
return fc.lt() && !fc.V()
}
func (fc flagConstant) leNoov() bool {
return fc.le() && !fc.V()
}
func (fc flagConstant) gtNoov() bool {
return fc.gt() && !fc.V()
}
func (fc flagConstant) geNoov() bool {
return fc.ge() && !fc.V()
}
func (fc flagConstant) String() string {
return fmt.Sprintf("N=%v,Z=%v,C=%v,V=%v", fc.N(), fc.Z(), fc.C(), fc.V())
}
type flagConstantBuilder struct {
N bool
Z bool
C bool
V bool
}
func (fcs flagConstantBuilder) encode() flagConstant {
var fc flagConstant
if fcs.N {
fc |= 1
}
if fcs.Z {
fc |= 2
}
if fcs.C {
fc |= 4
}
if fcs.V {
fc |= 8
}
return fc
}
// Note: addFlags(x,y) != subFlags(x,-y) in some situations:
// - the results of the C flag are different
// - the results of the V flag when y==minint are different
// addFlags64 returns the flags that would be set from computing x+y.
func addFlags64(x, y int64) flagConstant {
var fcb flagConstantBuilder
fcb.Z = x+y == 0
fcb.N = x+y < 0
fcb.C = uint64(x+y) < uint64(x)
fcb.V = x >= 0 && y >= 0 && x+y < 0 || x < 0 && y < 0 && x+y >= 0
return fcb.encode()
}
// subFlags64 returns the flags that would be set from computing x-y.
func subFlags64(x, y int64) flagConstant {
var fcb flagConstantBuilder
fcb.Z = x-y == 0
fcb.N = x-y < 0
fcb.C = uint64(y) <= uint64(x) // This code follows the arm carry flag model.
fcb.V = x >= 0 && y < 0 && x-y < 0 || x < 0 && y >= 0 && x-y >= 0
return fcb.encode()
}
// addFlags32 returns the flags that would be set from computing x+y.
func addFlags32(x, y int32) flagConstant {
var fcb flagConstantBuilder
fcb.Z = x+y == 0
fcb.N = x+y < 0
fcb.C = uint32(x+y) < uint32(x)
fcb.V = x >= 0 && y >= 0 && x+y < 0 || x < 0 && y < 0 && x+y >= 0
return fcb.encode()
}
// subFlags32 returns the flags that would be set from computing x-y.
func subFlags32(x, y int32) flagConstant {
var fcb flagConstantBuilder
fcb.Z = x-y == 0
fcb.N = x-y < 0
fcb.C = uint32(y) <= uint32(x) // This code follows the arm carry flag model.
fcb.V = x >= 0 && y < 0 && x-y < 0 || x < 0 && y >= 0 && x-y >= 0
return fcb.encode()
}
// logicFlags64 returns flags set to the sign/zeroness of x.
// C and V are set to false.
func logicFlags64(x int64) flagConstant {
var fcb flagConstantBuilder
fcb.Z = x == 0
fcb.N = x < 0
return fcb.encode()
}
// logicFlags32 returns flags set to the sign/zeroness of x.
// C and V are set to false.
func logicFlags32(x int32) flagConstant {
var fcb flagConstantBuilder
fcb.Z = x == 0
fcb.N = x < 0
return fcb.encode()
}

File diff suppressed because it is too large Load diff

View file

@ -27,3 +27,12 @@ func TestMoveSmall(t *testing.T) {
} }
} }
} }
func TestSubFlags(t *testing.T) {
if !subFlags32(0, 1).lt() {
t.Errorf("subFlags32(0,1).lt() returned false")
}
if !subFlags32(0, 1).ult() {
t.Errorf("subFlags32(0,1).ult() returned false")
}
}

View file

@ -206,6 +206,8 @@ func (v *Value) auxString() string {
return fmt.Sprintf(" {%s}", v.Aux.(Op)) return fmt.Sprintf(" {%s}", v.Aux.(Op))
case auxS390XCCMask, auxS390XRotateParams: case auxS390XCCMask, auxS390XRotateParams:
return fmt.Sprintf(" {%v}", v.Aux) return fmt.Sprintf(" {%v}", v.Aux)
case auxFlagConstant:
return fmt.Sprintf("[%s]", flagConstant(v.AuxInt))
} }
return "" return ""
} }