mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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:
parent
377c1536f5
commit
40ef1faabc
15 changed files with 841 additions and 1213 deletions
|
|
@ -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": "",
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
src/cmd/compile/internal/ssa/flags_amd64_test.s
Normal file
31
src/cmd/compile/internal/ssa/flags_amd64_test.s
Normal 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
|
||||||
32
src/cmd/compile/internal/ssa/flags_arm64_test.s
Normal file
32
src/cmd/compile/internal/ssa/flags_arm64_test.s
Normal 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
|
||||||
108
src/cmd/compile/internal/ssa/flags_test.go
Normal file
108
src/cmd/compile/internal/ssa/flags_test.go
Normal 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()
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue