From c8285bb501eb9581af930a9ccd0ad8f791ea2ab2 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Fri, 5 Jun 2015 13:04:29 -0400 Subject: [dev.ssa] cmd/compile/internal/ssa: add missing copyright notices Change-Id: I9d4e0f3e9afc9920ee0d77b0073ac8597c7c048f Reviewed-on: https://go-review.googlesource.com/10756 Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/stackalloc.go | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index ab686470be..dd55d96ccc 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -1,3 +1,7 @@ +// Copyright 2015 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. + package ssa import "log" -- cgit v1.3 From 0dca7351e9d51bdaf980e1256ec41af8cb1b9747 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sat, 6 Jun 2015 16:03:33 -0700 Subject: [dev.ssa] cmd/compile/internal/ssa: autogenerate opcodes Revamp autogeneration. Get rid of gogenerate commands, they are more trouble than they are worth. (If the code won't compile, gogenerate doesn't work.) Generate opcode enums & tables. This means we only have to specify opcodes in one place instead of two. Add arch prefixes to opcodes so they will be globally unique. Change-Id: I175d0a89b701b2377bbe699f3756731b7c9f5a9f Reviewed-on: https://go-review.googlesource.com/10812 Reviewed-by: Alan Donovan --- src/cmd/compile/internal/gc/ssa.go | 40 +- src/cmd/compile/internal/ssa/block.go | 30 +- src/cmd/compile/internal/ssa/blockkind_string.go | 32 - src/cmd/compile/internal/ssa/config.go | 8 +- src/cmd/compile/internal/ssa/gen/AMD64.rules | 100 ++ src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 161 +++ src/cmd/compile/internal/ssa/gen/README | 7 + src/cmd/compile/internal/ssa/gen/generic.rules | 49 + src/cmd/compile/internal/ssa/gen/genericOps.go | 104 ++ src/cmd/compile/internal/ssa/gen/main.go | 146 +++ src/cmd/compile/internal/ssa/gen/rulegen.go | 480 +++++++++ src/cmd/compile/internal/ssa/lower.go | 4 +- src/cmd/compile/internal/ssa/lowerAmd64.go | 1090 -------------------- src/cmd/compile/internal/ssa/lowergeneric.go | 289 ++++++ src/cmd/compile/internal/ssa/op.go | 176 +--- src/cmd/compile/internal/ssa/opGen.go | 916 ++++++++++++++++ src/cmd/compile/internal/ssa/op_string.go | 32 - src/cmd/compile/internal/ssa/opamd64.go | 196 ---- src/cmd/compile/internal/ssa/opt.go | 5 +- src/cmd/compile/internal/ssa/regalloc.go | 42 +- src/cmd/compile/internal/ssa/rewriteAMD64.go | 1090 ++++++++++++++++++++ src/cmd/compile/internal/ssa/rewritegeneric.go | 424 ++++++++ src/cmd/compile/internal/ssa/rulegen/generic.rules | 49 - .../compile/internal/ssa/rulegen/lower_amd64.rules | 100 -- src/cmd/compile/internal/ssa/rulegen/rulegen.go | 458 -------- src/cmd/compile/internal/ssa/stackalloc.go | 9 +- src/cmd/compile/internal/ssa/value.go | 7 +- 27 files changed, 3843 insertions(+), 2201 deletions(-) delete mode 100644 src/cmd/compile/internal/ssa/blockkind_string.go create mode 100644 src/cmd/compile/internal/ssa/gen/AMD64.rules create mode 100644 src/cmd/compile/internal/ssa/gen/AMD64Ops.go create mode 100644 src/cmd/compile/internal/ssa/gen/README create mode 100644 src/cmd/compile/internal/ssa/gen/generic.rules create mode 100644 src/cmd/compile/internal/ssa/gen/genericOps.go create mode 100644 src/cmd/compile/internal/ssa/gen/main.go create mode 100644 src/cmd/compile/internal/ssa/gen/rulegen.go delete mode 100644 src/cmd/compile/internal/ssa/lowerAmd64.go create mode 100644 src/cmd/compile/internal/ssa/lowergeneric.go create mode 100644 src/cmd/compile/internal/ssa/opGen.go delete mode 100644 src/cmd/compile/internal/ssa/op_string.go delete mode 100644 src/cmd/compile/internal/ssa/opamd64.go create mode 100644 src/cmd/compile/internal/ssa/rewriteAMD64.go create mode 100644 src/cmd/compile/internal/ssa/rewritegeneric.go delete mode 100644 src/cmd/compile/internal/ssa/rulegen/generic.rules delete mode 100644 src/cmd/compile/internal/ssa/rulegen/lower_amd64.rules delete mode 100644 src/cmd/compile/internal/ssa/rulegen/rulegen.go (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index fd1c30edee..d017a981d4 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -607,7 +607,7 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { func genValue(v *ssa.Value) { switch v.Op { - case ssa.OpADDQ: + case ssa.OpAMD64ADDQ: // TODO: use addq instead of leaq if target is in the right register. p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM @@ -616,7 +616,7 @@ func genValue(v *ssa.Value) { p.From.Index = regnum(v.Args[1]) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpADDQconst: + case ssa.OpAMD64ADDQconst: // TODO: use addq instead of leaq if target is in the right register. p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM @@ -624,7 +624,7 @@ func genValue(v *ssa.Value) { p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpMULQconst: + case ssa.OpAMD64MULQconst: // TODO: this isn't right. doasm fails on it. I don't think obj // has ever been taught to compile imul $c, r1, r2. p := Prog(x86.AIMULQ) @@ -634,7 +634,7 @@ func genValue(v *ssa.Value) { p.From3.Reg = regnum(v.Args[0]) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpSUBQconst: + case ssa.OpAMD64SUBQconst: // This code compensates for the fact that the register allocator // doesn't understand 2-address instructions yet. TODO: fix that. x := regnum(v.Args[0]) @@ -652,7 +652,7 @@ func genValue(v *ssa.Value) { p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = r - case ssa.OpSHLQconst: + case ssa.OpAMD64SHLQconst: x := regnum(v.Args[0]) r := regnum(v) if x != r { @@ -668,7 +668,7 @@ func genValue(v *ssa.Value) { p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = r - case ssa.OpLEAQ: + case ssa.OpAMD64LEAQ: p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) @@ -677,46 +677,46 @@ func genValue(v *ssa.Value) { p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpCMPQ: + case ssa.OpAMD64CMPQ: p := Prog(x86.ACMPQ) p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[0]) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v.Args[1]) - case ssa.OpCMPQconst: + case ssa.OpAMD64CMPQconst: p := Prog(x86.ACMPQ) p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[0]) p.To.Type = obj.TYPE_CONST p.To.Offset = v.Aux.(int64) - case ssa.OpTESTB: + case ssa.OpAMD64TESTB: p := Prog(x86.ATESTB) p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[0]) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v.Args[1]) - case ssa.OpMOVQconst: + case ssa.OpAMD64MOVQconst: x := regnum(v) p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_CONST p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = x - case ssa.OpMOVQload: + case ssa.OpAMD64MOVQload: p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpMOVBload: + case ssa.OpAMD64MOVBload: p := Prog(x86.AMOVB) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpMOVQloadidx8: + case ssa.OpAMD64MOVQloadidx8: p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) @@ -725,7 +725,7 @@ func genValue(v *ssa.Value) { p.From.Index = regnum(v.Args[1]) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpMOVQstore: + case ssa.OpAMD64MOVQstore: p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[1]) @@ -775,7 +775,7 @@ func genValue(v *ssa.Value) { case ssa.OpArg: // memory arg needs no code // TODO: only mem arg goes here. - case ssa.OpLEAQglobal: + case ssa.OpAMD64LEAQglobal: g := v.Aux.(ssa.GlobalOffset) p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM @@ -812,7 +812,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { p.To.Type = obj.TYPE_BRANCH branches = append(branches, branch{p, b.Succs[0]}) } - case ssa.BlockEQ: + case ssa.BlockAMD64EQ: if b.Succs[0] == next { p := Prog(x86.AJNE) p.To.Type = obj.TYPE_BRANCH @@ -829,7 +829,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { q.To.Type = obj.TYPE_BRANCH branches = append(branches, branch{q, b.Succs[1]}) } - case ssa.BlockNE: + case ssa.BlockAMD64NE: if b.Succs[0] == next { p := Prog(x86.AJEQ) p.To.Type = obj.TYPE_BRANCH @@ -846,7 +846,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { q.To.Type = obj.TYPE_BRANCH branches = append(branches, branch{q, b.Succs[1]}) } - case ssa.BlockLT: + case ssa.BlockAMD64LT: if b.Succs[0] == next { p := Prog(x86.AJGE) p.To.Type = obj.TYPE_BRANCH @@ -863,7 +863,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { q.To.Type = obj.TYPE_BRANCH branches = append(branches, branch{q, b.Succs[1]}) } - case ssa.BlockULT: + case ssa.BlockAMD64ULT: if b.Succs[0] == next { p := Prog(x86.AJCC) p.To.Type = obj.TYPE_BRANCH @@ -880,7 +880,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { q.To.Type = obj.TYPE_BRANCH branches = append(branches, branch{q, b.Succs[1]}) } - case ssa.BlockUGT: + case ssa.BlockAMD64UGT: if b.Succs[0] == next { p := Prog(x86.AJLS) p.To.Type = obj.TYPE_BRANCH diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go index 899d69bc32..85d73bb9b8 100644 --- a/src/cmd/compile/internal/ssa/block.go +++ b/src/cmd/compile/internal/ssa/block.go @@ -4,10 +4,7 @@ package ssa -import ( - "fmt" - "strings" -) +import "fmt" // Block represents a basic block in the control flow graph of a function. type Block struct { @@ -50,29 +47,6 @@ type Block struct { // Call mem [nopanic, panic] (control opcode should be OpCall or OpStaticCall) type BlockKind int32 -// block kind ranges -const ( - blockInvalid BlockKind = 0 - blockGenericBase = 1 + 100*iota - blockAMD64Base - block386Base - - blockMax // sentinel -) - -// generic block kinds -const ( - blockGenericStart BlockKind = blockGenericBase + iota - - BlockExit // no successors. There should only be 1 of these. - BlockPlain // a single successor - BlockIf // 2 successors, if control goto Succs[0] else goto Succs[1] - BlockCall // 2 successors, normal return and panic - // TODO(khr): BlockPanic for the built-in panic call, has 1 edge to the exit block -) - -//go:generate stringer -type=BlockKind - // short form print func (b *Block) String() string { return fmt.Sprintf("b%d", b.ID) @@ -80,7 +54,7 @@ func (b *Block) String() string { // long form print func (b *Block) LongString() string { - s := strings.TrimPrefix(b.Kind.String(), "Block") + s := b.Kind.String() if b.Control != nil { s += fmt.Sprintf(" %s", b.Control) } diff --git a/src/cmd/compile/internal/ssa/blockkind_string.go b/src/cmd/compile/internal/ssa/blockkind_string.go deleted file mode 100644 index 60c820c871..0000000000 --- a/src/cmd/compile/internal/ssa/blockkind_string.go +++ /dev/null @@ -1,32 +0,0 @@ -// generated by stringer -type=BlockKind; DO NOT EDIT - -package ssa - -import "fmt" - -const ( - _BlockKind_name_0 = "blockInvalid" - _BlockKind_name_1 = "blockGenericStartBlockExitBlockPlainBlockIfBlockCall" - _BlockKind_name_2 = "blockAMD64StartBlockEQBlockNEBlockLTBlockLEBlockGTBlockGEBlockULTBlockULEBlockUGTBlockUGE" -) - -var ( - _BlockKind_index_0 = [...]uint8{0, 12} - _BlockKind_index_1 = [...]uint8{0, 17, 26, 36, 43, 52} - _BlockKind_index_2 = [...]uint8{0, 15, 22, 29, 36, 43, 50, 57, 65, 73, 81, 89} -) - -func (i BlockKind) String() string { - switch { - case i == 0: - return _BlockKind_name_0 - case 101 <= i && i <= 105: - i -= 101 - return _BlockKind_name_1[_BlockKind_index_1[i]:_BlockKind_index_1[i+1]] - case 201 <= i && i <= 211: - i -= 201 - return _BlockKind_name_2[_BlockKind_index_2[i]:_BlockKind_index_2[i+1]] - default: - return fmt.Sprintf("BlockKind(%d)", i) - } -} diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 7c5e07e12a..db2d80a7c4 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -30,12 +30,12 @@ func NewConfig(arch string, fe Frontend) *Config { switch arch { case "amd64": c.ptrSize = 8 - c.lowerBlock = lowerBlockAMD64 - c.lowerValue = lowerValueAMD64 + c.lowerBlock = rewriteBlockAMD64 + c.lowerValue = rewriteValueAMD64 case "386": c.ptrSize = 4 - c.lowerBlock = lowerBlockAMD64 - c.lowerValue = lowerValueAMD64 // TODO(khr): full 32-bit support + c.lowerBlock = rewriteBlockAMD64 + c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support default: log.Fatalf("arch %s not implemented", arch) } diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules new file mode 100644 index 0000000000..c4ff744421 --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -0,0 +1,100 @@ +// Copyright 2015 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. + +// x86 register conventions: +// - Integer types live in the low portion of registers. +// Upper portions are correctly extended. +// - Boolean types use the low-order byte of a register. Upper bytes are junk. +// - We do not use AH,BH,CH,DH registers. +// - Floating-point types will live in the low natural slot of an sse2 register. +// Unused portions are junk. + +// These are the lowerings themselves +(Add x y) && (is64BitInt(t) || isPtr(t)) -> (ADDQ x y) +(Add x y) && is32BitInt(t) -> (ADDL x y) + +(Sub x y) && is64BitInt(t) -> (SUBQ x y) + +(Mul x y) && is64BitInt(t) -> (MULQ x y) +(Lsh x y) && is64BitInt(t) -> (SHLQ x y) // TODO: check y>63 +(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y)) + +(Load ptr mem) && t.IsBoolean() -> (MOVBload [int64(0)] ptr mem) +(Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload [int64(0)] ptr mem) +(Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore [int64(0)] ptr val mem) + +// checks +(IsNonNil p) -> (SETNE (TESTQ p p)) +(IsInBounds idx len) -> (SETB (CMPQ idx len)) + +(Move [size] dst src mem) -> (REPMOVSB dst src (Const [size.(int64)]) mem) + +(OffPtr [off] ptr) -> (ADDQconst [off] ptr) + +(Const [val]) && is64BitInt(t) -> (MOVQconst [val]) + +// block rewrites +(If (SETL cmp) yes no) -> (LT cmp yes no) +(If (SETNE cmp) yes no) -> (NE cmp yes no) +(If (SETB cmp) yes no) -> (ULT cmp yes no) +(If cond yes no) && cond.Op == OpAMD64MOVBload -> (NE (TESTB cond cond) yes no) + +// Rules below here apply some simple optimizations after lowering. +// TODO: Should this be a separate pass? + +// global loads/stores +(Global [sym]) -> (LEAQglobal [GlobalOffset{sym,0}]) + +// fold constants into instructions +(ADDQ x (MOVQconst [c])) -> (ADDQconst [c] x) // TODO: restrict c to int32 range? +(ADDQ (MOVQconst [c]) x) -> (ADDQconst [c] x) +(SUBQ x (MOVQconst [c])) -> (SUBQconst x [c]) +(SUBQ (MOVQconst [c]) x) -> (NEGQ (SUBQconst x [c])) +(MULQ x (MOVQconst [c])) && c.(int64) == int64(int32(c.(int64))) -> (MULQconst [c] x) +(MULQ (MOVQconst [c]) x) -> (MULQconst [c] x) +(SHLQ x (MOVQconst [c])) -> (SHLQconst [c] x) +(CMPQ x (MOVQconst [c])) -> (CMPQconst x [c]) +(CMPQ (MOVQconst [c]) x) -> (InvertFlags (CMPQconst x [c])) + +// strength reduction +// TODO: do this a lot more generically +(MULQconst [c] x) && c.(int64) == 8 -> (SHLQconst [int64(3)] x) +(MULQconst [c] x) && c.(int64) == 64 -> (SHLQconst [int64(5)] x) + +// fold add/shift into leaq +(ADDQ x (SHLQconst [shift] y)) && shift.(int64) == 3 -> (LEAQ8 [int64(0)] x y) +(ADDQconst [c] (LEAQ8 [d] x y)) -> (LEAQ8 [addOff(c, d)] x y) + +// reverse ordering of compare instruction +(SETL (InvertFlags x)) -> (SETG x) +(SETG (InvertFlags x)) -> (SETL x) + +// fold constants into memory operations +// Note that this is not always a good idea because if not all the uses of +// the ADDQconst get eliminated, we still have to compute the ADDQconst and we now +// have potentially two live values (ptr and (ADDQconst [off] ptr)) instead of one. +// Nevertheless, let's do it! +(MOVQload [off1] (ADDQconst [off2] ptr) mem) -> (MOVQload [addOff(off1, off2)] ptr mem) +(MOVQstore [off1] (ADDQconst [off2] ptr) val mem) -> (MOVQstore [addOff(off1, off2)] ptr val mem) + +// indexed loads and stores +(MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) +(MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) + +(MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) +(MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) + +(ADDQconst [off] x) && off.(int64) == 0 -> (Copy x) + +// Absorb InvertFlags into branches. +(LT (InvertFlags cmp) yes no) -> (GT cmp yes no) +(GT (InvertFlags cmp) yes no) -> (LT cmp yes no) +(LE (InvertFlags cmp) yes no) -> (GE cmp yes no) +(GE (InvertFlags cmp) yes no) -> (LE cmp yes no) +(ULT (InvertFlags cmp) yes no) -> (UGT cmp yes no) +(UGT (InvertFlags cmp) yes no) -> (ULT cmp yes no) +(ULE (InvertFlags cmp) yes no) -> (UGE cmp yes no) +(UGE (InvertFlags cmp) yes no) -> (ULE cmp yes no) +(EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no) +(NE (InvertFlags cmp) yes no) -> (NE cmp yes no) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go new file mode 100644 index 0000000000..38d1e87575 --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -0,0 +1,161 @@ +// Copyright 2015 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. + +package main + +import "strings" + +// copied from ../../amd64/reg.go +var regNamesAMD64 = []string{ + ".AX", + ".CX", + ".DX", + ".BX", + ".SP", + ".BP", + ".SI", + ".DI", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".X0", + ".X1", + ".X2", + ".X3", + ".X4", + ".X5", + ".X6", + ".X7", + ".X8", + ".X9", + ".X10", + ".X11", + ".X12", + ".X13", + ".X14", + ".X15", + + // pseudo-registers + ".FP", + ".FLAGS", +} + +func init() { + // Make map from reg names to reg integers. + if len(regNamesAMD64) > 64 { + panic("too many registers") + } + num := map[string]int{} + for i, name := range regNamesAMD64 { + if name[0] != '.' { + panic("register name " + name + " does not start with '.'") + } + num[name[1:]] = i + } + buildReg := func(s string) regMask { + m := regMask(0) + for _, r := range strings.Split(s, " ") { + if n, ok := num[r]; ok { + m |= regMask(1) << uint(n) + continue + } + panic("register " + r + " not found") + } + return m + } + + gp := buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15") + gpsp := gp | buildReg("SP FP") + gp01 := regInfo{[]regMask{}, 0, []regMask{gp}} + gp11 := regInfo{[]regMask{gpsp}, 0, []regMask{gp}} + gp21 := regInfo{[]regMask{gpsp, gpsp}, 0, []regMask{gp}} + gp21shift := regInfo{[]regMask{gpsp, buildReg("CX")}, 0, []regMask{gp}} + gp2flags := regInfo{[]regMask{gpsp, gpsp}, 0, []regMask{buildReg("FLAGS")}} + gp1flags := regInfo{[]regMask{gpsp}, 0, []regMask{buildReg("FLAGS")}} + gpload := regInfo{[]regMask{gpsp, 0}, 0, []regMask{gp}} + gploadidx := regInfo{[]regMask{gpsp, gpsp, 0}, 0, []regMask{gp}} + gpstore := regInfo{[]regMask{gpsp, gpsp, 0}, 0, nil} + gpstoreidx := regInfo{[]regMask{gpsp, gpsp, gpsp, 0}, 0, nil} + flagsgp := regInfo{[]regMask{buildReg("FLAGS")}, 0, []regMask{gp}} + + // Suffixes encode the bit width of various instructions. + // Q = 64 bit, L = 32 bit, W = 16 bit, B = 8 bit + + // TODO: 2-address instructions. Mark ops as needing matching input/output regs. + var AMD64ops = []opData{ + {name: "ADDQ", reg: gp21}, // arg0 + arg1 + {name: "ADDQconst", reg: gp11}, // arg0 + aux.(int64) + {name: "SUBQ", reg: gp21}, // arg0 - arg1 + {name: "SUBQconst", reg: gp11}, // arg0 - aux.(int64) + {name: "MULQ", reg: gp21}, // arg0 * arg1 + {name: "MULQconst", reg: gp11}, // arg0 * aux.(int64) + {name: "SHLQ", reg: gp21shift}, // arg0 << arg1, shift amount is mod 64 + {name: "SHLQconst", reg: gp11}, // arg0 << aux.(int64), shift amount 0-63 + {name: "NEGQ", reg: gp11}, // -arg0 + + {name: "CMPQ", reg: gp2flags}, // arg0 compare to arg1 + {name: "CMPQconst", reg: gp1flags}, // arg0 compare to aux.(int64) + {name: "TESTQ", reg: gp2flags}, // (arg0 & arg1) compare to 0 + {name: "TESTB", reg: gp2flags}, // (arg0 & arg1) compare to 0 + + {name: "SETEQ", reg: flagsgp}, // extract == condition from arg0 + {name: "SETNE", reg: flagsgp}, // extract != condition from arg0 + {name: "SETL", reg: flagsgp}, // extract signed < condition from arg0 + {name: "SETG", reg: flagsgp}, // extract signed > condition from arg0 + {name: "SETGE", reg: flagsgp}, // extract signed >= condition from arg0 + {name: "SETB", reg: flagsgp}, // extract unsigned < condition from arg0 + + {name: "MOVQconst", reg: gp01}, // aux.(int64) + {name: "LEAQ", reg: gp21}, // arg0 + arg1 + aux.(int64) + {name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + aux.(int64) + {name: "LEAQ4", reg: gp21}, // arg0 + 4*arg1 + aux.(int64) + {name: "LEAQ8", reg: gp21}, // arg0 + 8*arg1 + aux.(int64) + {name: "LEAQglobal", reg: gp01}, // no args. address of aux.(GlobalOffset) + + {name: "MOVBload", reg: gpload}, // load byte from arg0+aux.(int64). arg1=mem + {name: "MOVBQZXload", reg: gpload}, // ditto, extend to uint64 + {name: "MOVBQSXload", reg: gpload}, // ditto, extend to int64 + {name: "MOVQload", reg: gpload}, // load 8 bytes from arg0+aux.(int64). arg1=mem + {name: "MOVQloadidx8", reg: gploadidx}, // load 8 bytes from arg0+8*arg1+aux.(int64). arg2=mem + {name: "MOVBstore", reg: gpstore}, // store byte in arg1 to arg0+aux.(int64). arg2=mem + {name: "MOVQstore", reg: gpstore}, // store 8 bytes in arg1 to arg0+aux.(int64). arg2=mem + {name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+aux.(int64). arg3=mem + + // Load/store from global. Same as the above loads, but arg0 is missing and + // aux is a GlobalOffset instead of an int64. + {name: "MOVQloadglobal"}, // Load from aux.(GlobalOffset). arg0 = memory + {name: "MOVQstoreglobal"}, // store arg0 to aux.(GlobalOffset). arg1=memory, returns memory. + + {name: "REPMOVSB", reg: regInfo{[]regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")}, buildReg("DI SI CX"), nil}}, // move arg2 bytes from arg1 to arg0. arg3=mem, returns memory + + {name: "ADDL", reg: gp21}, // arg0+arg1 + + // (InvertFlags (CMPQ a b)) == (CMPQ b a) + // So if we want (SETL (CMPQ a b)) but we can't do that because a is a constant, + // then we do (SETL (InvertFlags (CMPQ b a))) instead. + // Rewrites will convert this to (SETG (CMPQ b a)). + // InvertFlags is a pseudo-op which can't appear in assembly output. + {name: "InvertFlags"}, // reverse direction of arg0 + } + + var AMD64blocks = []blockData{ + {name: "EQ"}, + {name: "NE"}, + {name: "LT"}, + {name: "LE"}, + {name: "GT"}, + {name: "GE"}, + {name: "ULT"}, + {name: "ULE"}, + {name: "UGT"}, + {name: "UGE"}, + } + + archs = append(archs, arch{"AMD64", AMD64ops, AMD64blocks}) +} diff --git a/src/cmd/compile/internal/ssa/gen/README b/src/cmd/compile/internal/ssa/gen/README new file mode 100644 index 0000000000..6731b970b3 --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/README @@ -0,0 +1,7 @@ +// Copyright 2015 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. + +This package generates opcode tables, rewrite rules, etc. for the ssa compiler. +Run it with: + go run *.go diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules new file mode 100644 index 0000000000..b01952f402 --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -0,0 +1,49 @@ +// Copyright 2015 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. + +// values are specified using the following format: +// (op [aux] arg0 arg1 ...) +// the type and aux fields are optional +// on the matching side +// - the types and aux fields must match if they are specified. +// on the generated side +// - the type of the top-level expression is the same as the one on the left-hand side. +// - the type of any subexpressions must be specified explicitly. +// - aux will be nil if not specified. + +// blocks are specified using the following format: +// (kind controlvalue succ0 succ1 ...) +// controlvalue must be "nil" or a value expression +// succ* fields must be variables +// For now, the generated successors must be a permutation of the matched successors. + +// constant folding +(Add (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)+d.(int64)}]) +(Mul (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)*d.(int64)}]) +(IsInBounds (Const [c]) (Const [d])) -> (Const [inBounds(c.(int64),d.(int64))]) + +// tear apart slices +// TODO: anything that generates a slice needs to go in here. +(SlicePtr (Load ptr mem)) -> (Load ptr mem) +(SliceLen (Load ptr mem)) -> (Load (Add ptr (Const [int64(config.ptrSize)])) mem) +(SliceCap (Load ptr mem)) -> (Load (Add ptr (Const [int64(config.ptrSize*2)])) mem) + +// indexing operations +// Note: bounds check has already been done +(ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex ptr idx) mem) +(PtrIndex ptr idx) -> (Add ptr (Mul idx (Const [t.Elem().Size()]))) + +// big-object moves +// TODO: fix size +(Store dst (Load src mem) mem) && t.Size() > 8 -> (Move [t.Size()] dst src mem) + +// string ops +(Const [s]) && t.IsString() -> (StringMake (OffPtr [2*config.ptrSize] (Global [config.fe.StringSym(s.(string))])) (Const [int64(len(s.(string)))])) // TODO: ptr +(Load ptr mem) && t.IsString() -> (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) +(StringPtr (StringMake ptr _)) -> ptr +(StringLen (StringMake _ len)) -> len +(Store dst str mem) && str.Type.IsString() -> (Store (OffPtr [config.ptrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) + +(If (Const [c]) yes no) && c.(bool) -> (Plain nil yes) +(If (Const [c]) yes no) && !c.(bool) -> (Plain nil no) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go new file mode 100644 index 0000000000..e8c3cbeb8a --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -0,0 +1,104 @@ +// Copyright 2015 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. + +package main + +var genericOps = []opData{ + // 2-input arithmetic + // Types must be consistent with Go typing. Add, for example, must take two values + // of the same type and produces that same type. + {name: "Add"}, // arg0 + arg1 + {name: "Sub"}, // arg0 - arg1 + {name: "Mul"}, // arg0 * arg1 + {name: "Lsh"}, // arg0 << arg1 + {name: "Rsh"}, // arg0 >> arg1 (signed/unsigned depending on signedness of type) + + // 2-input comparisons + {name: "Less"}, // arg0 < arg1 + + // Data movement + {name: "Phi"}, // select an argument based on which predecessor block we came from + {name: "Copy"}, // output = arg0 + + // constants. Constant values are stored in the aux field. + // booleans have a bool aux field, strings have a string aux + // field, and so on. All integer types store their value + // in the aux field as an int64 (including int, uint64, etc.). + // We could store int8 as an int8, but that won't work for int, + // as it may be different widths on the host and target. + {name: "Const"}, + + // Constant-like things + {name: "Arg"}, // address of a function parameter/result. Memory input is an arg called ".mem". aux is a string (TODO: make it something other than a string?) + {name: "Global"}, // the address of a global variable aux.(*gc.Sym) + {name: "SP"}, // stack pointer + {name: "FP"}, // frame pointer + {name: "Func"}, // entry address of a function + + // Memory operations + {name: "Load"}, // Load from arg0+aux.(int64). arg1=memory + {name: "Store"}, // Store arg1 to arg0+aux.(int64). arg2=memory. Returns memory. + {name: "Move"}, // arg0=destptr, arg1=srcptr, arg2=mem, aux.(int64)=size. Returns memory. + + // Function calls. Arguments to the call have already been written to the stack. + // Return values appear on the stack. The method receiver, if any, is treated + // as a phantom first argument. + {name: "Call"}, // arg0=code pointer, arg1=context ptr, arg2=memory. Returns memory. + {name: "StaticCall"}, // call function aux.(*gc.Sym), arg0=memory. Returns memory. + + // Conversions + {name: "Convert"}, // convert arg0 to another type + {name: "ConvNop"}, // interpret arg0 as another type + + // Safety checks + {name: "IsNonNil"}, // arg0 != nil + {name: "IsInBounds"}, // 0 <= arg0 < arg1 + + // Indexing operations + {name: "ArrayIndex"}, // arg0=array, arg1=index. Returns a[i] + {name: "PtrIndex"}, // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type + {name: "OffPtr"}, // arg0 + aux.(int64) (arg0 and result are pointers) + + // Slices + {name: "SliceMake"}, // arg0=ptr, arg1=len, arg2=cap + {name: "SlicePtr"}, // ptr(arg0) + {name: "SliceLen"}, // len(arg0) + {name: "SliceCap"}, // cap(arg0) + + // Strings + {name: "StringMake"}, // arg0=ptr, arg1=len + {name: "StringPtr"}, // ptr(arg0) + {name: "StringLen"}, // len(arg0) + + // Spill&restore ops for the register allocator. These are + // semantically identical to OpCopy; they do not take/return + // stores like regular memory ops do. We can get away without memory + // args because we know there is no aliasing of spill slots on the stack. + // TODO: remove these, make them arch-specific ops stored + // in the fields of Config instead. + {name: "StoreReg8"}, + {name: "LoadReg8"}, + + // Used during ssa construction. Like Copy, but the arg has not been specified yet. + {name: "FwdRef"}, +} + +// kind control successors +// ------------------------------------------ +// Exit return mem [] +// Plain nil [next] +// If a boolean Value [then, else] +// Call mem [nopanic, panic] (control opcode should be OpCall or OpStaticCall) + +var genericBlocks = []blockData{ + {name: "Exit"}, // no successors. There should only be 1 of these. + {name: "Plain"}, // a single successor + {name: "If"}, // 2 successors, if control goto Succs[0] else goto Succs[1] + {name: "Call"}, // 2 successors, normal return and panic + // TODO(khr): BlockPanic for the built-in panic call, has 1 edge to the exit block +} + +func init() { + archs = append(archs, arch{"generic", genericOps, genericBlocks}) +} diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go new file mode 100644 index 0000000000..56b47bd99e --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/main.go @@ -0,0 +1,146 @@ +// Copyright 2015 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. + +// The gen command generates Go code (in the parent directory) for all +// the architecture-specific opcodes, blocks, and rewrites. + +package main + +import ( + "bytes" + "fmt" + "go/format" + "io/ioutil" + "log" +) + +type arch struct { + name string + ops []opData + blocks []blockData +} + +type opData struct { + name string + reg regInfo +} + +type blockData struct { + name string +} + +type regInfo struct { + inputs []regMask + clobbers regMask + outputs []regMask +} + +type regMask uint64 + +var archs []arch + +func main() { + genOp() + genLower() +} +func genOp() { + w := new(bytes.Buffer) + fmt.Fprintf(w, "// autogenerated: do not edit!\n") + fmt.Fprintf(w, "// generated from gen/*Ops.go\n") + fmt.Fprintln(w, "package ssa") + + // generate Block* declarations + fmt.Fprintln(w, "const (") + fmt.Fprintln(w, "blockInvalid BlockKind = iota") + for _, a := range archs { + fmt.Fprintln(w) + for _, d := range a.blocks { + fmt.Fprintf(w, "Block%s%s\n", a.Name(), d.name) + } + } + fmt.Fprintln(w, ")") + + // generate block kind string method + fmt.Fprintln(w, "var blockString = [...]string{") + fmt.Fprintln(w, "blockInvalid:\"BlockInvalid\",") + for _, a := range archs { + fmt.Fprintln(w) + for _, b := range a.blocks { + fmt.Fprintf(w, "Block%s%s:\"%s\",\n", a.Name(), b.name, b.name) + } + } + fmt.Fprintln(w, "}") + fmt.Fprintln(w, "func (k BlockKind) String() string {return blockString[k]}") + + // generate Op* declarations + fmt.Fprintln(w, "const (") + fmt.Fprintln(w, "OpInvalid Op = iota") + for _, a := range archs { + fmt.Fprintln(w) + for _, v := range a.ops { + fmt.Fprintf(w, "Op%s%s\n", a.Name(), v.name) + } + } + fmt.Fprintln(w, ")") + + // generate OpInfo table + fmt.Fprintln(w, "var opcodeTable = [...]opInfo{") + fmt.Fprintln(w, " { name: \"OpInvalid\" },") + for _, a := range archs { + fmt.Fprintln(w) + for _, v := range a.ops { + fmt.Fprintln(w, "{") + fmt.Fprintf(w, "name:\"%s\",\n", v.name) + fmt.Fprintln(w, "reg:regInfo{") + fmt.Fprintln(w, "inputs: []regMask{") + for _, r := range v.reg.inputs { + fmt.Fprintf(w, "%d,\n", r) + } + fmt.Fprintln(w, "},") + fmt.Fprintf(w, "clobbers: %d,\n", v.reg.clobbers) + fmt.Fprintln(w, "outputs: []regMask{") + for _, r := range v.reg.outputs { + fmt.Fprintf(w, "%d,\n", r) + } + fmt.Fprintln(w, "},") + fmt.Fprintln(w, "},") + if a.name == "generic" { + fmt.Fprintln(w, "generic:true,") + } + fmt.Fprintln(w, "},") + } + } + fmt.Fprintln(w, "}") + + // generate op string method + fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }") + + // gofmt result + b := w.Bytes() + var err error + b, err = format.Source(b) + if err != nil { + panic(err) + } + + err = ioutil.WriteFile("../opGen.go", b, 0666) + if err != nil { + log.Fatalf("can't write output: %v\n", err) + } +} + +// Name returns the name of the architecture for use in Op* and Block* enumerations. +func (a arch) Name() string { + s := a.name + if s == "generic" { + s = "" + } + return s +} + +func genLower() { + for _, a := range archs { + genRules(a) + } +} diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go new file mode 100644 index 0000000000..5edf178a8a --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -0,0 +1,480 @@ +// Copyright 2015 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. + +// This program generates Go code that applies rewrite rules to a Value. +// The generated code implements a function of type func (v *Value) bool +// which returns true iff if did something. +// Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html + +package main + +import ( + "bufio" + "bytes" + "crypto/md5" + "fmt" + "go/format" + "io" + "io/ioutil" + "log" + "os" + "sort" + "strings" +) + +// rule syntax: +// sexpr [&& extra conditions] -> sexpr +// +// sexpr are s-expressions (lisp-like parenthesized groupings) +// sexpr ::= (opcode sexpr*) +// | variable +// | [aux] +// | +// | {code} +// +// aux ::= variable | {code} +// type ::= variable | {code} +// variable ::= some token +// opcode ::= one of the opcodes from ../op.go (without the Op prefix) + +// extra conditions is just a chunk of Go that evaluates to a boolean. It may use +// variables declared in the matching sexpr. The variable "v" is predefined to be +// the value matched by the entire rule. + +// If multiple rules match, the first one in file order is selected. + +func genRules(arch arch) { + // Open input file. + text, err := os.Open(arch.name + ".rules") + if err != nil { + log.Fatalf("can't read rule file: %v", err) + } + + // oprules contains a list of rules for each block and opcode + blockrules := map[string][]string{} + oprules := map[string][]string{} + + // read rule file + scanner := bufio.NewScanner(text) + for scanner.Scan() { + line := scanner.Text() + if i := strings.Index(line, "//"); i >= 0 { + // Remove comments. Note that this isn't string safe, so + // it will truncate lines with // inside strings. Oh well. + line = line[:i] + } + line = strings.TrimSpace(line) + if line == "" { + continue + } + op := strings.Split(line, " ")[0][1:] + if isBlock(op, arch) { + blockrules[op] = append(blockrules[op], line) + } else { + oprules[op] = append(oprules[op], line) + } + } + if err := scanner.Err(); err != nil { + log.Fatalf("scanner failed: %v\n", err) + } + + // Start output buffer, write header. + w := new(bytes.Buffer) + fmt.Fprintf(w, "// autogenerated from gen/%s.rules: do not edit!\n", arch.name) + fmt.Fprintln(w, "// generated with: cd gen; go run *.go") + fmt.Fprintln(w, "package ssa") + fmt.Fprintf(w, "func rewriteValue%s(v *Value, config *Config) bool {\n", arch.name) + + // generate code for each rule + fmt.Fprintf(w, "switch v.Op {\n") + var ops []string + for op := range oprules { + ops = append(ops, op) + } + sort.Strings(ops) + for _, op := range ops { + fmt.Fprintf(w, "case %s:\n", opName(op, arch)) + for _, rule := range oprules[op] { + // Note: we use a hash to identify the rule so that its + // identity is invariant to adding/removing rules elsewhere + // in the rules file. This is useful to squash spurious + // diffs that would occur if we used rule index. + rulehash := fmt.Sprintf("%02x", md5.Sum([]byte(rule))) + + // split at -> + s := strings.Split(rule, "->") + if len(s) != 2 { + log.Fatalf("no arrow in rule %s", rule) + } + lhs := strings.TrimSpace(s[0]) + result := strings.TrimSpace(s[1]) + + // split match into matching part and additional condition + match := lhs + cond := "" + if i := strings.Index(match, "&&"); i >= 0 { + cond = strings.TrimSpace(match[i+2:]) + match = strings.TrimSpace(match[:i]) + } + + fmt.Fprintf(w, "// match: %s\n", match) + fmt.Fprintf(w, "// cond: %s\n", cond) + fmt.Fprintf(w, "// result: %s\n", result) + + fail := fmt.Sprintf("{\ngoto end%s\n}\n", rulehash) + + fmt.Fprintf(w, "{\n") + genMatch(w, arch, match, fail) + + if cond != "" { + fmt.Fprintf(w, "if !(%s) %s", cond, fail) + } + + genResult(w, arch, result) + fmt.Fprintf(w, "return true\n") + + fmt.Fprintf(w, "}\n") + fmt.Fprintf(w, "goto end%s\n", rulehash) // use label + fmt.Fprintf(w, "end%s:;\n", rulehash) + } + } + fmt.Fprintf(w, "}\n") + fmt.Fprintf(w, "return false\n") + fmt.Fprintf(w, "}\n") + + // Generate block rewrite function. + fmt.Fprintf(w, "func rewriteBlock%s(b *Block) bool {\n", arch.name) + fmt.Fprintf(w, "switch b.Kind {\n") + ops = nil + for op := range blockrules { + ops = append(ops, op) + } + sort.Strings(ops) + for _, op := range ops { + fmt.Fprintf(w, "case %s:\n", blockName(op, arch)) + for _, rule := range blockrules[op] { + rulehash := fmt.Sprintf("%02x", md5.Sum([]byte(rule))) + // split at -> + s := strings.Split(rule, "->") + if len(s) != 2 { + log.Fatalf("no arrow in rule %s", rule) + } + lhs := strings.TrimSpace(s[0]) + result := strings.TrimSpace(s[1]) + + // split match into matching part and additional condition + match := lhs + cond := "" + if i := strings.Index(match, "&&"); i >= 0 { + cond = strings.TrimSpace(match[i+2:]) + match = strings.TrimSpace(match[:i]) + } + + fmt.Fprintf(w, "// match: %s\n", match) + fmt.Fprintf(w, "// cond: %s\n", cond) + fmt.Fprintf(w, "// result: %s\n", result) + + fail := fmt.Sprintf("{\ngoto end%s\n}\n", rulehash) + + fmt.Fprintf(w, "{\n") + s = split(match[1 : len(match)-1]) // remove parens, then split + + // check match of control value + if s[1] != "nil" { + fmt.Fprintf(w, "v := b.Control\n") + genMatch0(w, arch, s[1], "v", fail, map[string]string{}, false) + } + + // assign successor names + succs := s[2:] + for i, a := range succs { + if a != "_" { + fmt.Fprintf(w, "%s := b.Succs[%d]\n", a, i) + } + } + + if cond != "" { + fmt.Fprintf(w, "if !(%s) %s", cond, fail) + } + + // Rule matches. Generate result. + t := split(result[1 : len(result)-1]) // remove parens, then split + newsuccs := t[2:] + + // Check if newsuccs is a subset of succs. + m := map[string]bool{} + for _, succ := range succs { + if m[succ] { + log.Fatalf("can't have a repeat successor name %s in %s", succ, rule) + } + m[succ] = true + } + for _, succ := range newsuccs { + if !m[succ] { + log.Fatalf("unknown successor %s in %s", succ, rule) + } + delete(m, succ) + } + + // Modify predecessor lists for no-longer-reachable blocks + for succ := range m { + fmt.Fprintf(w, "removePredecessor(b, %s)\n", succ) + } + + fmt.Fprintf(w, "b.Kind = %s\n", blockName(t[0], arch)) + if t[1] == "nil" { + fmt.Fprintf(w, "b.Control = nil\n") + } else { + fmt.Fprintf(w, "b.Control = %s\n", genResult0(w, arch, t[1], new(int), false)) + } + if len(newsuccs) < len(succs) { + fmt.Fprintf(w, "b.Succs = b.Succs[:%d]\n", len(newsuccs)) + } + for i, a := range newsuccs { + fmt.Fprintf(w, "b.Succs[%d] = %s\n", i, a) + } + + fmt.Fprintf(w, "return true\n") + + fmt.Fprintf(w, "}\n") + fmt.Fprintf(w, "goto end%s\n", rulehash) // use label + fmt.Fprintf(w, "end%s:;\n", rulehash) + } + } + fmt.Fprintf(w, "}\n") + fmt.Fprintf(w, "return false\n") + fmt.Fprintf(w, "}\n") + + // gofmt result + b := w.Bytes() + b, err = format.Source(b) + if err != nil { + panic(err) + } + + // Write to file + err = ioutil.WriteFile("../rewrite"+arch.name+".go", b, 0666) + if err != nil { + log.Fatalf("can't write output: %v\n", err) + } +} + +func genMatch(w io.Writer, arch arch, match, fail string) { + genMatch0(w, arch, match, "v", fail, map[string]string{}, true) +} + +func genMatch0(w io.Writer, arch arch, match, v, fail string, m map[string]string, top bool) { + if match[0] != '(' { + if x, ok := m[match]; ok { + // variable already has a definition. Check whether + // the old definition and the new definition match. + // For example, (add x x). Equality is just pointer equality + // on Values (so cse is important to do before lowering). + fmt.Fprintf(w, "if %s != %s %s", v, x, fail) + return + } + // remember that this variable references the given value + if match == "_" { + return + } + m[match] = v + fmt.Fprintf(w, "%s := %s\n", match, v) + return + } + + // split body up into regions. Split by spaces/tabs, except those + // contained in () or {}. + s := split(match[1 : len(match)-1]) // remove parens, then split + + // check op + if !top { + fmt.Fprintf(w, "if %s.Op != %s %s", v, opName(s[0], arch), fail) + } + + // check type/aux/args + argnum := 0 + for _, a := range s[1:] { + if a[0] == '<' { + // type restriction + t := a[1 : len(a)-1] // remove <> + if t[0] == '{' { + // code. We must match the results of this code. + fmt.Fprintf(w, "if %s.Type != %s %s", v, t[1:len(t)-1], fail) + } else { + // variable + if u, ok := m[t]; ok { + // must match previous variable + fmt.Fprintf(w, "if %s.Type != %s %s", v, u, fail) + } else { + m[t] = v + ".Type" + fmt.Fprintf(w, "%s := %s.Type\n", t, v) + } + } + } else if a[0] == '[' { + // aux restriction + x := a[1 : len(a)-1] // remove [] + if x[0] == '{' { + // code + fmt.Fprintf(w, "if %s.Aux != %s %s", v, x[1:len(x)-1], fail) + } else { + // variable + if y, ok := m[x]; ok { + fmt.Fprintf(w, "if %s.Aux != %s %s", v, y, fail) + } else { + m[x] = v + ".Aux" + fmt.Fprintf(w, "%s := %s.Aux\n", x, v) + } + } + } else if a[0] == '{' { + fmt.Fprintf(w, "if %s.Args[%d] != %s %s", v, argnum, a[1:len(a)-1], fail) + argnum++ + } else { + // variable or sexpr + genMatch0(w, arch, a, fmt.Sprintf("%s.Args[%d]", v, argnum), fail, m, false) + argnum++ + } + } +} + +func genResult(w io.Writer, arch arch, result string) { + genResult0(w, arch, result, new(int), true) +} +func genResult0(w io.Writer, arch arch, result string, alloc *int, top bool) string { + if result[0] != '(' { + // variable + if top { + fmt.Fprintf(w, "v.Op = %s.Op\n", result) + fmt.Fprintf(w, "v.Aux = %s.Aux\n", result) + fmt.Fprintf(w, "v.resetArgs()\n") + fmt.Fprintf(w, "v.AddArgs(%s.Args...)\n", result) + } + return result + } + + s := split(result[1 : len(result)-1]) // remove parens, then split + var v string + var hasType bool + if top { + v = "v" + fmt.Fprintf(w, "v.Op = %s\n", opName(s[0], arch)) + fmt.Fprintf(w, "v.Aux = nil\n") + fmt.Fprintf(w, "v.resetArgs()\n") + hasType = true + } else { + v = fmt.Sprintf("v%d", *alloc) + *alloc++ + fmt.Fprintf(w, "%s := v.Block.NewValue(%s, TypeInvalid, nil)\n", v, opName(s[0], arch)) + } + for _, a := range s[1:] { + if a[0] == '<' { + // type restriction + t := a[1 : len(a)-1] // remove <> + if t[0] == '{' { + t = t[1 : len(t)-1] // remove {} + } + fmt.Fprintf(w, "%s.Type = %s\n", v, t) + hasType = true + } else if a[0] == '[' { + // aux restriction + x := a[1 : len(a)-1] // remove [] + if x[0] == '{' { + x = x[1 : len(x)-1] // remove {} + } + fmt.Fprintf(w, "%s.Aux = %s\n", v, x) + } else if a[0] == '{' { + fmt.Fprintf(w, "%s.AddArg(%s)\n", v, a[1:len(a)-1]) + } else { + // regular argument (sexpr or variable) + x := genResult0(w, arch, a, alloc, false) + fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x) + } + } + if !hasType { + log.Fatalf("sub-expression %s must have a type", result) + } + return v +} + +func split(s string) []string { + var r []string + +outer: + for s != "" { + d := 0 // depth of ({[< + var open, close byte // opening and closing markers ({[< or )}]> + nonsp := false // found a non-space char so far + for i := 0; i < len(s); i++ { + switch { + case d == 0 && s[i] == '(': + open, close = '(', ')' + d++ + case d == 0 && s[i] == '<': + open, close = '<', '>' + d++ + case d == 0 && s[i] == '[': + open, close = '[', ']' + d++ + case d == 0 && s[i] == '{': + open, close = '{', '}' + d++ + case d == 0 && (s[i] == ' ' || s[i] == '\t'): + if nonsp { + r = append(r, strings.TrimSpace(s[:i])) + s = s[i:] + continue outer + } + case d > 0 && s[i] == open: + d++ + case d > 0 && s[i] == close: + d-- + default: + nonsp = true + } + } + if d != 0 { + panic("imbalanced expression: " + s) + } + if nonsp { + r = append(r, strings.TrimSpace(s)) + } + break + } + return r +} + +// isBlock returns true if this op is a block opcode. +func isBlock(name string, arch arch) bool { + for _, b := range genericBlocks { + if b.name == name { + return true + } + } + for _, b := range arch.blocks { + if b.name == name { + return true + } + } + return false +} + +// opName converts from an op name specified in a rule file to an Op enum. +// if the name matches a generic op, returns "Op" plus the specified name. +// Otherwise, returns "Op" plus arch name plus op name. +func opName(name string, arch arch) string { + for _, op := range genericOps { + if op.name == name { + return "Op" + name + } + } + return "Op" + arch.name + name +} + +func blockName(name string, arch arch) string { + for _, b := range genericBlocks { + if b.name == name { + return "Block" + name + } + } + return "Block" + arch.name + name +} diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index ebed4f2607..2ca1db784e 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -6,8 +6,6 @@ package ssa import "log" -//go:generate go run rulegen/rulegen.go rulegen/lower_amd64.rules lowerBlockAMD64 lowerValueAMD64 lowerAmd64.go - // convert to machine-dependent ops func lower(f *Func) { // repeat rewrites until we find no more rewrites @@ -16,7 +14,7 @@ func lower(f *Func) { // Check for unlowered opcodes, fail if we find one. for _, b := range f.Blocks { for _, v := range b.Values { - if v.Op < OpGenericEnd && v.Op != OpFP && v.Op != OpSP && v.Op != OpArg && v.Op != OpCopy && v.Op != OpPhi { + if opcodeTable[v.Op].generic && v.Op != OpFP && v.Op != OpSP && v.Op != OpArg && v.Op != OpCopy && v.Op != OpPhi { log.Panicf("%s not lowered", v.LongString()) } } diff --git a/src/cmd/compile/internal/ssa/lowerAmd64.go b/src/cmd/compile/internal/ssa/lowerAmd64.go deleted file mode 100644 index 6b5ff3e39f..0000000000 --- a/src/cmd/compile/internal/ssa/lowerAmd64.go +++ /dev/null @@ -1,1090 +0,0 @@ -// autogenerated from rulegen/lower_amd64.rules: do not edit! -// generated with: go run rulegen/rulegen.go rulegen/lower_amd64.rules lowerBlockAMD64 lowerValueAMD64 lowerAmd64.go -package ssa - -func lowerValueAMD64(v *Value, config *Config) bool { - switch v.Op { - case OpADDQ: - // match: (ADDQ x (MOVQconst [c])) - // cond: - // result: (ADDQconst [c] x) - { - x := v.Args[0] - if v.Args[1].Op != OpMOVQconst { - goto endacffd55e74ee0ff59ad58a18ddfc9973 - } - c := v.Args[1].Aux - v.Op = OpADDQconst - v.Aux = nil - v.resetArgs() - v.Aux = c - v.AddArg(x) - return true - } - goto endacffd55e74ee0ff59ad58a18ddfc9973 - endacffd55e74ee0ff59ad58a18ddfc9973: - ; - // match: (ADDQ (MOVQconst [c]) x) - // cond: - // result: (ADDQconst [c] x) - { - if v.Args[0].Op != OpMOVQconst { - goto end7166f476d744ab7a51125959d3d3c7e2 - } - c := v.Args[0].Aux - x := v.Args[1] - v.Op = OpADDQconst - v.Aux = nil - v.resetArgs() - v.Aux = c - v.AddArg(x) - return true - } - goto end7166f476d744ab7a51125959d3d3c7e2 - end7166f476d744ab7a51125959d3d3c7e2: - ; - // match: (ADDQ x (SHLQconst [shift] y)) - // cond: shift.(int64) == 3 - // result: (LEAQ8 [int64(0)] x y) - { - x := v.Args[0] - if v.Args[1].Op != OpSHLQconst { - goto endaf4f724e1e17f2b116d336c07da0165d - } - shift := v.Args[1].Aux - y := v.Args[1].Args[0] - if !(shift.(int64) == 3) { - goto endaf4f724e1e17f2b116d336c07da0165d - } - v.Op = OpLEAQ8 - v.Aux = nil - v.resetArgs() - v.Aux = int64(0) - v.AddArg(x) - v.AddArg(y) - return true - } - goto endaf4f724e1e17f2b116d336c07da0165d - endaf4f724e1e17f2b116d336c07da0165d: - ; - case OpADDQconst: - // match: (ADDQconst [c] (LEAQ8 [d] x y)) - // cond: - // result: (LEAQ8 [addOff(c, d)] x y) - { - c := v.Aux - if v.Args[0].Op != OpLEAQ8 { - goto ende2cc681c9abf9913288803fb1b39e639 - } - d := v.Args[0].Aux - x := v.Args[0].Args[0] - y := v.Args[0].Args[1] - v.Op = OpLEAQ8 - v.Aux = nil - v.resetArgs() - v.Aux = addOff(c, d) - v.AddArg(x) - v.AddArg(y) - return true - } - goto ende2cc681c9abf9913288803fb1b39e639 - ende2cc681c9abf9913288803fb1b39e639: - ; - // match: (ADDQconst [off] x) - // cond: off.(int64) == 0 - // result: (Copy x) - { - off := v.Aux - x := v.Args[0] - if !(off.(int64) == 0) { - goto endfa1c7cc5ac4716697e891376787f86ce - } - v.Op = OpCopy - v.Aux = nil - v.resetArgs() - v.AddArg(x) - return true - } - goto endfa1c7cc5ac4716697e891376787f86ce - endfa1c7cc5ac4716697e891376787f86ce: - ; - case OpAdd: - // match: (Add x y) - // cond: (is64BitInt(t) || isPtr(t)) - // result: (ADDQ x y) - { - t := v.Type - x := v.Args[0] - y := v.Args[1] - if !(is64BitInt(t) || isPtr(t)) { - goto endf031c523d7dd08e4b8e7010a94cd94c9 - } - v.Op = OpADDQ - v.Aux = nil - v.resetArgs() - v.AddArg(x) - v.AddArg(y) - return true - } - goto endf031c523d7dd08e4b8e7010a94cd94c9 - endf031c523d7dd08e4b8e7010a94cd94c9: - ; - // match: (Add x y) - // cond: is32BitInt(t) - // result: (ADDL x y) - { - t := v.Type - x := v.Args[0] - y := v.Args[1] - if !(is32BitInt(t)) { - goto end35a02a1587264e40cf1055856ff8445a - } - v.Op = OpADDL - v.Aux = nil - v.resetArgs() - v.AddArg(x) - v.AddArg(y) - return true - } - goto end35a02a1587264e40cf1055856ff8445a - end35a02a1587264e40cf1055856ff8445a: - ; - case OpCMPQ: - // match: (CMPQ x (MOVQconst [c])) - // cond: - // result: (CMPQconst x [c]) - { - x := v.Args[0] - if v.Args[1].Op != OpMOVQconst { - goto end32ef1328af280ac18fa8045a3502dae9 - } - c := v.Args[1].Aux - v.Op = OpCMPQconst - v.Aux = nil - v.resetArgs() - v.AddArg(x) - v.Aux = c - return true - } - goto end32ef1328af280ac18fa8045a3502dae9 - end32ef1328af280ac18fa8045a3502dae9: - ; - // match: (CMPQ (MOVQconst [c]) x) - // cond: - // result: (InvertFlags (CMPQconst x [c])) - { - if v.Args[0].Op != OpMOVQconst { - goto endf8ca12fe79290bc82b11cfa463bc9413 - } - c := v.Args[0].Aux - x := v.Args[1] - v.Op = OpInvertFlags - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(OpCMPQconst, TypeInvalid, nil) - v0.Type = TypeFlags - v0.AddArg(x) - v0.Aux = c - v.AddArg(v0) - return true - } - goto endf8ca12fe79290bc82b11cfa463bc9413 - endf8ca12fe79290bc82b11cfa463bc9413: - ; - case OpConst: - // match: (Const [val]) - // cond: is64BitInt(t) - // result: (MOVQconst [val]) - { - t := v.Type - val := v.Aux - if !(is64BitInt(t)) { - goto end7f5c5b34093fbc6860524cb803ee51bf - } - v.Op = OpMOVQconst - v.Aux = nil - v.resetArgs() - v.Aux = val - return true - } - goto end7f5c5b34093fbc6860524cb803ee51bf - end7f5c5b34093fbc6860524cb803ee51bf: - ; - case OpGlobal: - // match: (Global [sym]) - // cond: - // result: (LEAQglobal [GlobalOffset{sym,0}]) - { - sym := v.Aux - v.Op = OpLEAQglobal - v.Aux = nil - v.resetArgs() - v.Aux = GlobalOffset{sym, 0} - return true - } - goto end3a3c76fac0e2e53c0e1c60b9524e6f1c - end3a3c76fac0e2e53c0e1c60b9524e6f1c: - ; - case OpIsInBounds: - // match: (IsInBounds idx len) - // cond: - // result: (SETB (CMPQ idx len)) - { - idx := v.Args[0] - len := v.Args[1] - v.Op = OpSETB - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(OpCMPQ, TypeInvalid, nil) - v0.Type = TypeFlags - v0.AddArg(idx) - v0.AddArg(len) - v.AddArg(v0) - return true - } - goto endb51d371171154c0f1613b687757e0576 - endb51d371171154c0f1613b687757e0576: - ; - case OpIsNonNil: - // match: (IsNonNil p) - // cond: - // result: (SETNE (TESTQ p p)) - { - p := v.Args[0] - v.Op = OpSETNE - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(OpTESTQ, TypeInvalid, nil) - v0.Type = TypeFlags - v0.AddArg(p) - v0.AddArg(p) - v.AddArg(v0) - return true - } - goto endff508c3726edfb573abc6128c177e76c - endff508c3726edfb573abc6128c177e76c: - ; - case OpLess: - // match: (Less x y) - // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) - // result: (SETL (CMPQ x y)) - { - x := v.Args[0] - y := v.Args[1] - if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { - goto endcecf13a952d4c6c2383561c7d68a3cf9 - } - v.Op = OpSETL - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(OpCMPQ, TypeInvalid, nil) - v0.Type = TypeFlags - v0.AddArg(x) - v0.AddArg(y) - v.AddArg(v0) - return true - } - goto endcecf13a952d4c6c2383561c7d68a3cf9 - endcecf13a952d4c6c2383561c7d68a3cf9: - ; - case OpLoad: - // match: (Load ptr mem) - // cond: t.IsBoolean() - // result: (MOVBload [int64(0)] ptr mem) - { - t := v.Type - ptr := v.Args[0] - mem := v.Args[1] - if !(t.IsBoolean()) { - goto end73f21632e56c3614902d3c29c82dc4ea - } - v.Op = OpMOVBload - v.Aux = nil - v.resetArgs() - v.Aux = int64(0) - v.AddArg(ptr) - v.AddArg(mem) - return true - } - goto end73f21632e56c3614902d3c29c82dc4ea - end73f21632e56c3614902d3c29c82dc4ea: - ; - // match: (Load ptr mem) - // cond: (is64BitInt(t) || isPtr(t)) - // result: (MOVQload [int64(0)] ptr mem) - { - t := v.Type - ptr := v.Args[0] - mem := v.Args[1] - if !(is64BitInt(t) || isPtr(t)) { - goto end581ce5a20901df1b8143448ba031685b - } - v.Op = OpMOVQload - v.Aux = nil - v.resetArgs() - v.Aux = int64(0) - v.AddArg(ptr) - v.AddArg(mem) - return true - } - goto end581ce5a20901df1b8143448ba031685b - end581ce5a20901df1b8143448ba031685b: - ; - case OpLsh: - // match: (Lsh x y) - // cond: is64BitInt(t) - // result: (SHLQ x y) - { - t := v.Type - x := v.Args[0] - y := v.Args[1] - if !(is64BitInt(t)) { - goto end9f05c9539e51db6ad557989e0c822e9b - } - v.Op = OpSHLQ - v.Aux = nil - v.resetArgs() - v.AddArg(x) - v.AddArg(y) - return true - } - goto end9f05c9539e51db6ad557989e0c822e9b - end9f05c9539e51db6ad557989e0c822e9b: - ; - case OpMOVQload: - // match: (MOVQload [off1] (ADDQconst [off2] ptr) mem) - // cond: - // result: (MOVQload [addOff(off1, off2)] ptr mem) - { - off1 := v.Aux - if v.Args[0].Op != OpADDQconst { - goto end843d29b538c4483b432b632e5666d6e3 - } - off2 := v.Args[0].Aux - ptr := v.Args[0].Args[0] - mem := v.Args[1] - v.Op = OpMOVQload - v.Aux = nil - v.resetArgs() - v.Aux = addOff(off1, off2) - v.AddArg(ptr) - v.AddArg(mem) - return true - } - goto end843d29b538c4483b432b632e5666d6e3 - end843d29b538c4483b432b632e5666d6e3: - ; - // match: (MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) - // cond: - // result: (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) - { - off1 := v.Aux - if v.Args[0].Op != OpLEAQ8 { - goto end02f5ad148292c46463e7c20d3b821735 - } - off2 := v.Args[0].Aux - ptr := v.Args[0].Args[0] - idx := v.Args[0].Args[1] - mem := v.Args[1] - v.Op = OpMOVQloadidx8 - v.Aux = nil - v.resetArgs() - v.Aux = addOff(off1, off2) - v.AddArg(ptr) - v.AddArg(idx) - v.AddArg(mem) - return true - } - goto end02f5ad148292c46463e7c20d3b821735 - end02f5ad148292c46463e7c20d3b821735: - ; - case OpMOVQloadidx8: - // match: (MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) - // cond: - // result: (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) - { - off1 := v.Aux - if v.Args[0].Op != OpADDQconst { - goto ende81e44bcfb11f90916ccb440c590121f - } - off2 := v.Args[0].Aux - ptr := v.Args[0].Args[0] - idx := v.Args[1] - mem := v.Args[2] - v.Op = OpMOVQloadidx8 - v.Aux = nil - v.resetArgs() - v.Aux = addOff(off1, off2) - v.AddArg(ptr) - v.AddArg(idx) - v.AddArg(mem) - return true - } - goto ende81e44bcfb11f90916ccb440c590121f - ende81e44bcfb11f90916ccb440c590121f: - ; - case OpMOVQstore: - // match: (MOVQstore [off1] (ADDQconst [off2] ptr) val mem) - // cond: - // result: (MOVQstore [addOff(off1, off2)] ptr val mem) - { - off1 := v.Aux - if v.Args[0].Op != OpADDQconst { - goto end2108c693a43c79aed10b9246c39c80aa - } - off2 := v.Args[0].Aux - ptr := v.Args[0].Args[0] - val := v.Args[1] - mem := v.Args[2] - v.Op = OpMOVQstore - v.Aux = nil - v.resetArgs() - v.Aux = addOff(off1, off2) - v.AddArg(ptr) - v.AddArg(val) - v.AddArg(mem) - return true - } - goto end2108c693a43c79aed10b9246c39c80aa - end2108c693a43c79aed10b9246c39c80aa: - ; - // match: (MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) - // cond: - // result: (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) - { - off1 := v.Aux - if v.Args[0].Op != OpLEAQ8 { - goto endce1db8c8d37c8397c500a2068a65c215 - } - off2 := v.Args[0].Aux - ptr := v.Args[0].Args[0] - idx := v.Args[0].Args[1] - val := v.Args[1] - mem := v.Args[2] - v.Op = OpMOVQstoreidx8 - v.Aux = nil - v.resetArgs() - v.Aux = addOff(off1, off2) - v.AddArg(ptr) - v.AddArg(idx) - v.AddArg(val) - v.AddArg(mem) - return true - } - goto endce1db8c8d37c8397c500a2068a65c215 - endce1db8c8d37c8397c500a2068a65c215: - ; - case OpMOVQstoreidx8: - // match: (MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) - // cond: - // result: (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) - { - off1 := v.Aux - if v.Args[0].Op != OpADDQconst { - goto end01c970657b0fdefeab82458c15022163 - } - off2 := v.Args[0].Aux - ptr := v.Args[0].Args[0] - idx := v.Args[1] - val := v.Args[2] - mem := v.Args[3] - v.Op = OpMOVQstoreidx8 - v.Aux = nil - v.resetArgs() - v.Aux = addOff(off1, off2) - v.AddArg(ptr) - v.AddArg(idx) - v.AddArg(val) - v.AddArg(mem) - return true - } - goto end01c970657b0fdefeab82458c15022163 - end01c970657b0fdefeab82458c15022163: - ; - case OpMULQ: - // match: (MULQ x (MOVQconst [c])) - // cond: c.(int64) == int64(int32(c.(int64))) - // result: (MULQconst [c] x) - { - x := v.Args[0] - if v.Args[1].Op != OpMOVQconst { - goto ende8c09b194fcde7d9cdc69f2deff86304 - } - c := v.Args[1].Aux - if !(c.(int64) == int64(int32(c.(int64)))) { - goto ende8c09b194fcde7d9cdc69f2deff86304 - } - v.Op = OpMULQconst - v.Aux = nil - v.resetArgs() - v.Aux = c - v.AddArg(x) - return true - } - goto ende8c09b194fcde7d9cdc69f2deff86304 - ende8c09b194fcde7d9cdc69f2deff86304: - ; - // match: (MULQ (MOVQconst [c]) x) - // cond: - // result: (MULQconst [c] x) - { - if v.Args[0].Op != OpMOVQconst { - goto endc6e18d6968175d6e58eafa6dcf40c1b8 - } - c := v.Args[0].Aux - x := v.Args[1] - v.Op = OpMULQconst - v.Aux = nil - v.resetArgs() - v.Aux = c - v.AddArg(x) - return true - } - goto endc6e18d6968175d6e58eafa6dcf40c1b8 - endc6e18d6968175d6e58eafa6dcf40c1b8: - ; - case OpMULQconst: - // match: (MULQconst [c] x) - // cond: c.(int64) == 8 - // result: (SHLQconst [int64(3)] x) - { - c := v.Aux - x := v.Args[0] - if !(c.(int64) == 8) { - goto end7e16978c56138324ff2abf91fd6d94d4 - } - v.Op = OpSHLQconst - v.Aux = nil - v.resetArgs() - v.Aux = int64(3) - v.AddArg(x) - return true - } - goto end7e16978c56138324ff2abf91fd6d94d4 - end7e16978c56138324ff2abf91fd6d94d4: - ; - // match: (MULQconst [c] x) - // cond: c.(int64) == 64 - // result: (SHLQconst [int64(5)] x) - { - c := v.Aux - x := v.Args[0] - if !(c.(int64) == 64) { - goto end2c7a02f230e4b311ac3a4e22f70a4f08 - } - v.Op = OpSHLQconst - v.Aux = nil - v.resetArgs() - v.Aux = int64(5) - v.AddArg(x) - return true - } - goto end2c7a02f230e4b311ac3a4e22f70a4f08 - end2c7a02f230e4b311ac3a4e22f70a4f08: - ; - case OpMove: - // match: (Move [size] dst src mem) - // cond: - // result: (REPMOVSB dst src (Const [size.(int64)]) mem) - { - size := v.Aux - dst := v.Args[0] - src := v.Args[1] - mem := v.Args[2] - v.Op = OpREPMOVSB - v.Aux = nil - v.resetArgs() - v.AddArg(dst) - v.AddArg(src) - v0 := v.Block.NewValue(OpConst, TypeInvalid, nil) - v0.Type = TypeUInt64 - v0.Aux = size.(int64) - v.AddArg(v0) - v.AddArg(mem) - return true - } - goto end48909259b265a6bb2a076bc2c2dc7d1f - end48909259b265a6bb2a076bc2c2dc7d1f: - ; - case OpMul: - // match: (Mul x y) - // cond: is64BitInt(t) - // result: (MULQ x y) - { - t := v.Type - x := v.Args[0] - y := v.Args[1] - if !(is64BitInt(t)) { - goto endfab0d598f376ecba45a22587d50f7aff - } - v.Op = OpMULQ - v.Aux = nil - v.resetArgs() - v.AddArg(x) - v.AddArg(y) - return true - } - goto endfab0d598f376ecba45a22587d50f7aff - endfab0d598f376ecba45a22587d50f7aff: - ; - case OpOffPtr: - // match: (OffPtr [off] ptr) - // cond: - // result: (ADDQconst [off] ptr) - { - off := v.Aux - ptr := v.Args[0] - v.Op = OpADDQconst - v.Aux = nil - v.resetArgs() - v.Aux = off - v.AddArg(ptr) - return true - } - goto end0429f947ee7ac49ff45a243e461a5290 - end0429f947ee7ac49ff45a243e461a5290: - ; - case OpSETG: - // match: (SETG (InvertFlags x)) - // cond: - // result: (SETL x) - { - if v.Args[0].Op != OpInvertFlags { - goto endf7586738694c9cd0b74ae28bbadb649f - } - x := v.Args[0].Args[0] - v.Op = OpSETL - v.Aux = nil - v.resetArgs() - v.AddArg(x) - return true - } - goto endf7586738694c9cd0b74ae28bbadb649f - endf7586738694c9cd0b74ae28bbadb649f: - ; - case OpSETL: - // match: (SETL (InvertFlags x)) - // cond: - // result: (SETG x) - { - if v.Args[0].Op != OpInvertFlags { - goto ende33160cd86b9d4d3b77e02fb4658d5d3 - } - x := v.Args[0].Args[0] - v.Op = OpSETG - v.Aux = nil - v.resetArgs() - v.AddArg(x) - return true - } - goto ende33160cd86b9d4d3b77e02fb4658d5d3 - ende33160cd86b9d4d3b77e02fb4658d5d3: - ; - case OpSHLQ: - // match: (SHLQ x (MOVQconst [c])) - // cond: - // result: (SHLQconst [c] x) - { - x := v.Args[0] - if v.Args[1].Op != OpMOVQconst { - goto endcca412bead06dc3d56ef034a82d184d6 - } - c := v.Args[1].Aux - v.Op = OpSHLQconst - v.Aux = nil - v.resetArgs() - v.Aux = c - v.AddArg(x) - return true - } - goto endcca412bead06dc3d56ef034a82d184d6 - endcca412bead06dc3d56ef034a82d184d6: - ; - case OpSUBQ: - // match: (SUBQ x (MOVQconst [c])) - // cond: - // result: (SUBQconst x [c]) - { - x := v.Args[0] - if v.Args[1].Op != OpMOVQconst { - goto end5a74a63bd9ad15437717c6df3b25eebb - } - c := v.Args[1].Aux - v.Op = OpSUBQconst - v.Aux = nil - v.resetArgs() - v.AddArg(x) - v.Aux = c - return true - } - goto end5a74a63bd9ad15437717c6df3b25eebb - end5a74a63bd9ad15437717c6df3b25eebb: - ; - // match: (SUBQ (MOVQconst [c]) x) - // cond: - // result: (NEGQ (SUBQconst x [c])) - { - t := v.Type - if v.Args[0].Op != OpMOVQconst { - goto end78e66b6fc298684ff4ac8aec5ce873c9 - } - c := v.Args[0].Aux - x := v.Args[1] - v.Op = OpNEGQ - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(OpSUBQconst, TypeInvalid, nil) - v0.Type = t - v0.AddArg(x) - v0.Aux = c - v.AddArg(v0) - return true - } - goto end78e66b6fc298684ff4ac8aec5ce873c9 - end78e66b6fc298684ff4ac8aec5ce873c9: - ; - case OpStore: - // match: (Store ptr val mem) - // cond: (is64BitInt(val.Type) || isPtr(val.Type)) - // result: (MOVQstore [int64(0)] ptr val mem) - { - ptr := v.Args[0] - val := v.Args[1] - mem := v.Args[2] - if !(is64BitInt(val.Type) || isPtr(val.Type)) { - goto end9680b43f504bc06f9fab000823ce471a - } - v.Op = OpMOVQstore - v.Aux = nil - v.resetArgs() - v.Aux = int64(0) - v.AddArg(ptr) - v.AddArg(val) - v.AddArg(mem) - return true - } - goto end9680b43f504bc06f9fab000823ce471a - end9680b43f504bc06f9fab000823ce471a: - ; - case OpSub: - // match: (Sub x y) - // cond: is64BitInt(t) - // result: (SUBQ x y) - { - t := v.Type - x := v.Args[0] - y := v.Args[1] - if !(is64BitInt(t)) { - goto ende6ef29f885a8ecf3058212bb95917323 - } - v.Op = OpSUBQ - v.Aux = nil - v.resetArgs() - v.AddArg(x) - v.AddArg(y) - return true - } - goto ende6ef29f885a8ecf3058212bb95917323 - ende6ef29f885a8ecf3058212bb95917323: - } - return false -} -func lowerBlockAMD64(b *Block) bool { - switch b.Kind { - case BlockEQ: - // match: (BlockEQ (InvertFlags cmp) yes no) - // cond: - // result: (BlockEQ cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto endea853c6aba26aace57cc8951d332ebe9 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockEQ - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto endea853c6aba26aace57cc8951d332ebe9 - endea853c6aba26aace57cc8951d332ebe9: - ; - case BlockGE: - // match: (BlockGE (InvertFlags cmp) yes no) - // cond: - // result: (BlockLE cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto end608065f88da8bcb570f716698fd7c5c7 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockLE - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end608065f88da8bcb570f716698fd7c5c7 - end608065f88da8bcb570f716698fd7c5c7: - ; - case BlockGT: - // match: (BlockGT (InvertFlags cmp) yes no) - // cond: - // result: (BlockLT cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto ende1758ce91e7231fd66db6bb988856b14 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockLT - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto ende1758ce91e7231fd66db6bb988856b14 - ende1758ce91e7231fd66db6bb988856b14: - ; - case BlockIf: - // match: (BlockIf (SETL cmp) yes no) - // cond: - // result: (BlockLT cmp yes no) - { - v := b.Control - if v.Op != OpSETL { - goto endc6a5d98127b4b8aff782f6981348c864 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockLT - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto endc6a5d98127b4b8aff782f6981348c864 - endc6a5d98127b4b8aff782f6981348c864: - ; - // match: (BlockIf (SETNE cmp) yes no) - // cond: - // result: (BlockNE cmp yes no) - { - v := b.Control - if v.Op != OpSETNE { - goto end49bd2f760f561c30c85c3342af06753b - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockNE - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end49bd2f760f561c30c85c3342af06753b - end49bd2f760f561c30c85c3342af06753b: - ; - // match: (BlockIf (SETB cmp) yes no) - // cond: - // result: (BlockULT cmp yes no) - { - v := b.Control - if v.Op != OpSETB { - goto end4754c856495bfc5769799890d639a627 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockULT - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end4754c856495bfc5769799890d639a627 - end4754c856495bfc5769799890d639a627: - ; - // match: (BlockIf cond yes no) - // cond: cond.Op == OpMOVBload - // result: (BlockNE (TESTB cond cond) yes no) - { - v := b.Control - cond := v - yes := b.Succs[0] - no := b.Succs[1] - if !(cond.Op == OpMOVBload) { - goto end3a3c83af305cf35c49cb10183b4c6425 - } - b.Kind = BlockNE - v0 := v.Block.NewValue(OpTESTB, TypeInvalid, nil) - v0.Type = TypeFlags - v0.AddArg(cond) - v0.AddArg(cond) - b.Control = v0 - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end3a3c83af305cf35c49cb10183b4c6425 - end3a3c83af305cf35c49cb10183b4c6425: - ; - case BlockLE: - // match: (BlockLE (InvertFlags cmp) yes no) - // cond: - // result: (BlockGE cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto end6e761e611859351c15da0d249c3771f7 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockGE - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end6e761e611859351c15da0d249c3771f7 - end6e761e611859351c15da0d249c3771f7: - ; - case BlockLT: - // match: (BlockLT (InvertFlags cmp) yes no) - // cond: - // result: (BlockGT cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto endb269f9644dffd5a416ba236545ee2524 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockGT - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto endb269f9644dffd5a416ba236545ee2524 - endb269f9644dffd5a416ba236545ee2524: - ; - case BlockNE: - // match: (BlockNE (InvertFlags cmp) yes no) - // cond: - // result: (BlockNE cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto endc41d56a60f8ab211baa2bf0360b7b286 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockNE - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto endc41d56a60f8ab211baa2bf0360b7b286 - endc41d56a60f8ab211baa2bf0360b7b286: - ; - case BlockUGE: - // match: (BlockUGE (InvertFlags cmp) yes no) - // cond: - // result: (BlockULE cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto end9ae511e4f4e81005ae1f3c1e5941ba3c - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockULE - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end9ae511e4f4e81005ae1f3c1e5941ba3c - end9ae511e4f4e81005ae1f3c1e5941ba3c: - ; - case BlockUGT: - // match: (BlockUGT (InvertFlags cmp) yes no) - // cond: - // result: (BlockULT cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto end073724a0ca0ec030715dd33049b647e9 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockULT - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end073724a0ca0ec030715dd33049b647e9 - end073724a0ca0ec030715dd33049b647e9: - ; - case BlockULE: - // match: (BlockULE (InvertFlags cmp) yes no) - // cond: - // result: (BlockUGE cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto end2f53a6da23ace14fb1b9b9896827e62d - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockUGE - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto end2f53a6da23ace14fb1b9b9896827e62d - end2f53a6da23ace14fb1b9b9896827e62d: - ; - case BlockULT: - // match: (BlockULT (InvertFlags cmp) yes no) - // cond: - // result: (BlockUGT cmp yes no) - { - v := b.Control - if v.Op != OpInvertFlags { - goto endbceb44a1ad6c53fb33710fc88be6a679 - } - cmp := v.Args[0] - yes := b.Succs[0] - no := b.Succs[1] - b.Kind = BlockUGT - b.Control = cmp - b.Succs[0] = yes - b.Succs[1] = no - return true - } - goto endbceb44a1ad6c53fb33710fc88be6a679 - endbceb44a1ad6c53fb33710fc88be6a679: - } - return false -} diff --git a/src/cmd/compile/internal/ssa/lowergeneric.go b/src/cmd/compile/internal/ssa/lowergeneric.go new file mode 100644 index 0000000000..1ac276ad66 --- /dev/null +++ b/src/cmd/compile/internal/ssa/lowergeneric.go @@ -0,0 +1,289 @@ +// autogenerated from generic.rules: do not edit! +// generated with: go run rulegen/rulegen.go +package ssa + +func lowerValuegeneric(v *Value) bool { + switch v.Op { + case OpAdd: + // match: (Add (Const [c]) (Const [d])) + // cond: is64BitInt(t) + // result: (Const [{c.(int64)+d.(int64)}]) + { + t := v.Type + if v.Args[0].Op != OpConst { + goto end8d047ed0ae9537b840adc79ea82c6e05 + } + c := v.Args[0].Aux + if v.Args[1].Op != OpConst { + goto end8d047ed0ae9537b840adc79ea82c6e05 + } + d := v.Args[1].Aux + if !(is64BitInt(t)) { + goto end8d047ed0ae9537b840adc79ea82c6e05 + } + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = c.(int64) + d.(int64) + return true + } + goto end8d047ed0ae9537b840adc79ea82c6e05 + end8d047ed0ae9537b840adc79ea82c6e05: + ; + case OpArrayIndex: + // match: (ArrayIndex (Load ptr mem) idx) + // cond: + // result: (Load (PtrIndex ptr idx) mem) + { + if v.Args[0].Op != OpLoad { + goto end3809f4c52270a76313e4ea26e6f0b753 + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + idx := v.Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpPtrIndex, TypeInvalid, nil) + v0.Type = ptr.Type.Elem().Elem().PtrTo() + v0.AddArg(ptr) + v0.AddArg(idx) + v.AddArg(v0) + v.AddArg(mem) + return true + } + goto end3809f4c52270a76313e4ea26e6f0b753 + end3809f4c52270a76313e4ea26e6f0b753: + ; + case OpIsInBounds: + // match: (IsInBounds (Const [c]) (Const [d])) + // cond: + // result: (Const [inBounds(c.(int64),d.(int64))]) + { + if v.Args[0].Op != OpConst { + goto enddbd1a394d9b71ee64335361b8384865c + } + c := v.Args[0].Aux + if v.Args[1].Op != OpConst { + goto enddbd1a394d9b71ee64335361b8384865c + } + d := v.Args[1].Aux + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = inBounds(c.(int64), d.(int64)) + return true + } + goto enddbd1a394d9b71ee64335361b8384865c + enddbd1a394d9b71ee64335361b8384865c: + ; + case OpMul: + // match: (Mul (Const [c]) (Const [d])) + // cond: is64BitInt(t) + // result: (Const [{c.(int64)*d.(int64)}]) + { + t := v.Type + if v.Args[0].Op != OpConst { + goto end776610f88cf04f438242d76ed2b14f1c + } + c := v.Args[0].Aux + if v.Args[1].Op != OpConst { + goto end776610f88cf04f438242d76ed2b14f1c + } + d := v.Args[1].Aux + if !(is64BitInt(t)) { + goto end776610f88cf04f438242d76ed2b14f1c + } + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = c.(int64) * d.(int64) + return true + } + goto end776610f88cf04f438242d76ed2b14f1c + end776610f88cf04f438242d76ed2b14f1c: + ; + case OpPtrIndex: + // match: (PtrIndex ptr idx) + // cond: + // result: (Add ptr (Mul idx (Const [t.Elem().Size()]))) + { + t := v.Type + ptr := v.Args[0] + idx := v.Args[1] + v.Op = OpAdd + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v0 := v.Block.NewValue(OpMul, TypeInvalid, nil) + v0.Type = v.Block.Func.Config.Uintptr + v0.AddArg(idx) + v1 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v1.Type = v.Block.Func.Config.Uintptr + v1.Aux = t.Elem().Size() + v0.AddArg(v1) + v.AddArg(v0) + return true + } + goto end383c68c41e72d22ef00c4b7b0fddcbb8 + end383c68c41e72d22ef00c4b7b0fddcbb8: + ; + case OpSliceCap: + // match: (SliceCap (Load ptr mem)) + // cond: + // result: (Load (Add ptr (Const [int64(v.Block.Func.Config.ptrSize*2)])) mem) + { + if v.Args[0].Op != OpLoad { + goto endbf1d4db93c4664ed43be3f73afb4dfa3 + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAdd, TypeInvalid, nil) + v0.Type = ptr.Type + v0.AddArg(ptr) + v1 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v1.Type = v.Block.Func.Config.Uintptr + v1.Aux = int64(v.Block.Func.Config.ptrSize * 2) + v0.AddArg(v1) + v.AddArg(v0) + v.AddArg(mem) + return true + } + goto endbf1d4db93c4664ed43be3f73afb4dfa3 + endbf1d4db93c4664ed43be3f73afb4dfa3: + ; + case OpSliceLen: + // match: (SliceLen (Load ptr mem)) + // cond: + // result: (Load (Add ptr (Const [int64(v.Block.Func.Config.ptrSize)])) mem) + { + if v.Args[0].Op != OpLoad { + goto end9190b1ecbda4c5dd6d3e05d2495fb297 + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAdd, TypeInvalid, nil) + v0.Type = ptr.Type + v0.AddArg(ptr) + v1 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v1.Type = v.Block.Func.Config.Uintptr + v1.Aux = int64(v.Block.Func.Config.ptrSize) + v0.AddArg(v1) + v.AddArg(v0) + v.AddArg(mem) + return true + } + goto end9190b1ecbda4c5dd6d3e05d2495fb297 + end9190b1ecbda4c5dd6d3e05d2495fb297: + ; + case OpSlicePtr: + // match: (SlicePtr (Load ptr mem)) + // cond: + // result: (Load ptr mem) + { + if v.Args[0].Op != OpLoad { + goto end459613b83f95b65729d45c2ed663a153 + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(mem) + return true + } + goto end459613b83f95b65729d45c2ed663a153 + end459613b83f95b65729d45c2ed663a153: + ; + case OpStore: + // match: (Store dst (Load src mem) mem) + // cond: t.Size() > 8 + // result: (Move [t.Size()] dst src mem) + { + dst := v.Args[0] + if v.Args[1].Op != OpLoad { + goto end324ffb6d2771808da4267f62c854e9c8 + } + t := v.Args[1].Type + src := v.Args[1].Args[0] + mem := v.Args[1].Args[1] + if v.Args[2] != v.Args[1].Args[1] { + goto end324ffb6d2771808da4267f62c854e9c8 + } + if !(t.Size() > 8) { + goto end324ffb6d2771808da4267f62c854e9c8 + } + v.Op = OpMove + v.Aux = nil + v.resetArgs() + v.Aux = t.Size() + v.AddArg(dst) + v.AddArg(src) + v.AddArg(mem) + return true + } + goto end324ffb6d2771808da4267f62c854e9c8 + end324ffb6d2771808da4267f62c854e9c8: + } + return false +} +func lowerBlockgeneric(b *Block) bool { + switch b.Kind { + case BlockIf: + // match: (BlockIf (Const [c]) yes no) + // cond: c.(bool) + // result: (BlockPlain nil yes) + { + v := b.Control + if v.Op != OpConst { + goto endbe39807508a6192b4022c7293eb6e114 + } + c := v.Aux + yes := b.Succs[0] + no := b.Succs[1] + if !(c.(bool)) { + goto endbe39807508a6192b4022c7293eb6e114 + } + removePredecessor(b, no) + b.Kind = BlockPlain + b.Control = nil + b.Succs = b.Succs[:1] + b.Succs[0] = yes + return true + } + goto endbe39807508a6192b4022c7293eb6e114 + endbe39807508a6192b4022c7293eb6e114: + ; + // match: (BlockIf (Const [c]) yes no) + // cond: !c.(bool) + // result: (BlockPlain nil no) + { + v := b.Control + if v.Op != OpConst { + goto end69ac35957ebe0a77a5ef5103c1f79fbf + } + c := v.Aux + yes := b.Succs[0] + no := b.Succs[1] + if !(!c.(bool)) { + goto end69ac35957ebe0a77a5ef5103c1f79fbf + } + removePredecessor(b, yes) + b.Kind = BlockPlain + b.Control = nil + b.Succs = b.Succs[:1] + b.Succs[0] = no + return true + } + goto end69ac35957ebe0a77a5ef5103c1f79fbf + end69ac35957ebe0a77a5ef5103c1f79fbf: + } + return false +} diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index c8bd3d2f3a..19a3fddd49 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -12,95 +12,11 @@ import ( // An Op encodes the specific operation that a Value performs. // Opcodes' semantics can be modified by the type and aux fields of the Value. // For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type. -// Semantics of each op are described below. -// -// Ops come in two flavors, architecture-independent and architecture-dependent. -// Architecture-independent opcodes appear in this file. -// Architecture-dependent opcodes appear in op{arch}.go files. +// Semantics of each op are described in the opcode files in gen/*Ops.go. +// There is one file for generic (architecture-independent) ops and one file +// for each architecture. type Op int32 -// Opcode ranges, a generic one and one for each architecture. -const ( - opInvalid Op = 0 - opGenericBase = 1 + 1000*iota - opAMD64Base - op386Base - - opMax // sentinel -) - -// Generic opcodes -const ( - opGenericStart Op = opGenericBase + iota - - // 2-input arithmetic - OpAdd // arg0 + arg1 - OpSub // arg0 - arg1 - OpMul // arg0 * arg1 - OpLsh // arg0 << arg1 - OpRsh // arg0 >> arg1 (signed/unsigned depending on signedness of type) - - // 2-input comparisons - OpLess // arg0 < arg1 - - // constants. Constant values are stored in the aux field. - // booleans have a bool aux field, strings have a string aux - // field, and so on. All integer types store their value - // in the aux field as an int64 (including int, uint64, etc.). - // We could store int8 as an int8, but that won't work for int, - // as it may be different widths on the host and target. - OpConst - - OpArg // address of a function parameter/result. Memory input is an arg called ".mem". aux is a string (TODO: make it something other than a string?) - OpGlobal // the address of a global variable aux.(*gc.Sym) - OpFunc // entry address of a function - OpFP // frame pointer - OpSP // stack pointer - - OpCopy // output = arg0 - OpMove // arg0=destptr, arg1=srcptr, arg2=mem, aux.(int64)=size. Returns memory. - OpPhi // select an argument based on which predecessor block we came from - - OpSliceMake // arg0=ptr, arg1=len, arg2=cap - OpSlicePtr // ptr(arg0) - OpSliceLen // len(arg0) - OpSliceCap // cap(arg0) - - OpStringMake // arg0=ptr, arg1=len - OpStringPtr // ptr(arg0) - OpStringLen // len(arg0) - - OpLoad // Load from arg0. arg1=memory - OpStore // Store arg1 to arg0. arg2=memory. Returns memory. - OpArrayIndex // arg0=array, arg1=index. Returns a[i] - OpPtrIndex // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type - OpIsNonNil // arg0 != nil - OpIsInBounds // 0 <= arg0 < arg1 - - // function calls. Arguments to the call have already been written to the stack. - // Return values appear on the stack. The method receiver, if any, is treated - // as a phantom first argument. - OpCall // arg0=code pointer, arg1=context ptr, arg2=memory. Returns memory. - OpStaticCall // call function aux.(*gc.Sym), arg0=memory. Returns memory. - - OpConvert // convert arg0 to another type - OpConvNop // interpret arg0 as another type - - OpOffPtr // arg0 + aux.(int64) (arg0 and result are pointers) - - // spill&restore ops for the register allocator. These are - // semantically identical to OpCopy; they do not take/return - // stores like regular memory ops do. We can get away without memory - // args because we know there is no aliasing of spill slots on the stack. - OpStoreReg8 - OpLoadReg8 - - // used during ssa construction. Like OpCopy, but the arg has not been specified yet. - OpFwdRef - - OpGenericEnd -) - // GlobalOffset represents a fixed offset within a global variable type GlobalOffset struct { Global interface{} // holds a *gc.Sym @@ -121,86 +37,14 @@ func (g GlobalOffset) String() string { return fmt.Sprintf("%v+%d", g.Global, g.Offset) } -//go:generate stringer -type=Op - type opInfo struct { - flags int32 - - // returns a reg constraint for the instruction. [0] gives a reg constraint - // for each input, [1] gives a reg constraint for each output. (Values have - // exactly one output for now) - reg [2][]regMask + name string + reg regInfo + generic bool // this is a generic (arch-independent) opcode } -const ( - // possible properties of opcodes - OpFlagCommutative int32 = 1 << iota -) - -// Opcodes that represent the input Go program -var genericTable = map[Op]opInfo{ - // the unknown op is used only during building and should not appear in a - // fully formed ssa representation. - - OpAdd: {flags: OpFlagCommutative}, - OpSub: {}, - OpMul: {flags: OpFlagCommutative}, - OpLess: {}, - - OpConst: {}, // aux matches the type (e.g. bool, int64 float64) - OpArg: {}, // aux is the name of the input variable. Currently only ".mem" is used - OpGlobal: {}, // address of a global variable - OpFunc: {}, - OpCopy: {}, - OpPhi: {}, - - OpConvNop: {}, // aux is the type to convert to - - /* - // build and take apart slices - {name: "slicemake"}, // (ptr,len,cap) -> slice - {name: "sliceptr"}, // pointer part of slice - {name: "slicelen"}, // length part of slice - {name: "slicecap"}, // capacity part of slice - - // build and take apart strings - {name: "stringmake"}, // (ptr,len) -> string - {name: "stringptr"}, // pointer part of string - {name: "stringlen"}, // length part of string - - // operations on arrays/slices/strings - {name: "slice"}, // (s, i, j) -> s[i:j] - {name: "index"}, // (mem, ptr, idx) -> val - {name: "indexaddr"}, // (ptr, idx) -> ptr - - // loads & stores - {name: "load"}, // (mem, check, ptr) -> val - {name: "store"}, // (mem, check, ptr, val) -> mem - - // checks - {name: "checknil"}, // (mem, ptr) -> check - {name: "checkbound"}, // (mem, idx, len) -> check - - // functions - {name: "call"}, - - // builtins - {name: "len"}, - {name: "convert"}, - - // tuples - {name: "tuple"}, // build a tuple out of its arguments - {name: "extract"}, // aux is an int64. Extract that index out of a tuple - {name: "extractsuffix"}, // aux is an int64. Slice a tuple with [aux:] - - */ -} - -// table of opcodes, indexed by opcode ID -var opcodeTable [opMax]opInfo - -func init() { - for op, info := range genericTable { - opcodeTable[op] = info - } +type regInfo struct { + inputs []regMask + clobbers regMask + outputs []regMask // NOTE: values can only have 1 output for now. } diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go new file mode 100644 index 0000000000..604f096152 --- /dev/null +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -0,0 +1,916 @@ +// autogenerated: do not edit! +// generated from gen/*Ops.go +package ssa + +const ( + blockInvalid BlockKind = iota + + BlockAMD64EQ + BlockAMD64NE + BlockAMD64LT + BlockAMD64LE + BlockAMD64GT + BlockAMD64GE + BlockAMD64ULT + BlockAMD64ULE + BlockAMD64UGT + BlockAMD64UGE + + BlockExit + BlockPlain + BlockIf + BlockCall +) + +var blockString = [...]string{ + blockInvalid: "BlockInvalid", + + BlockAMD64EQ: "EQ", + BlockAMD64NE: "NE", + BlockAMD64LT: "LT", + BlockAMD64LE: "LE", + BlockAMD64GT: "GT", + BlockAMD64GE: "GE", + BlockAMD64ULT: "ULT", + BlockAMD64ULE: "ULE", + BlockAMD64UGT: "UGT", + BlockAMD64UGE: "UGE", + + BlockExit: "Exit", + BlockPlain: "Plain", + BlockIf: "If", + BlockCall: "Call", +} + +func (k BlockKind) String() string { return blockString[k] } + +const ( + OpInvalid Op = iota + + OpAMD64ADDQ + OpAMD64ADDQconst + OpAMD64SUBQ + OpAMD64SUBQconst + OpAMD64MULQ + OpAMD64MULQconst + OpAMD64SHLQ + OpAMD64SHLQconst + OpAMD64NEGQ + OpAMD64CMPQ + OpAMD64CMPQconst + OpAMD64TESTQ + OpAMD64TESTB + OpAMD64SETEQ + OpAMD64SETNE + OpAMD64SETL + OpAMD64SETG + OpAMD64SETGE + OpAMD64SETB + OpAMD64MOVQconst + OpAMD64LEAQ + OpAMD64LEAQ2 + OpAMD64LEAQ4 + OpAMD64LEAQ8 + OpAMD64LEAQglobal + OpAMD64MOVBload + OpAMD64MOVBQZXload + OpAMD64MOVBQSXload + OpAMD64MOVQload + OpAMD64MOVQloadidx8 + OpAMD64MOVBstore + OpAMD64MOVQstore + OpAMD64MOVQstoreidx8 + OpAMD64MOVQloadglobal + OpAMD64MOVQstoreglobal + OpAMD64REPMOVSB + OpAMD64ADDL + OpAMD64InvertFlags + + OpAdd + OpSub + OpMul + OpLsh + OpRsh + OpLess + OpPhi + OpCopy + OpConst + OpArg + OpGlobal + OpSP + OpFP + OpFunc + OpLoad + OpStore + OpMove + OpCall + OpStaticCall + OpConvert + OpConvNop + OpIsNonNil + OpIsInBounds + OpArrayIndex + OpPtrIndex + OpOffPtr + OpSliceMake + OpSlicePtr + OpSliceLen + OpSliceCap + OpStringMake + OpStringPtr + OpStringLen + OpStoreReg8 + OpLoadReg8 + OpFwdRef +) + +var opcodeTable = [...]opInfo{ + {name: "OpInvalid"}, + + { + name: "ADDQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "ADDQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SUBQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SUBQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MULQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MULQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SHLQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 2, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SHLQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "NEGQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "CMPQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 8589934592, + }, + }, + }, + { + name: "CMPQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 8589934592, + }, + }, + }, + { + name: "TESTQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 8589934592, + }, + }, + }, + { + name: "TESTB", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 8589934592, + }, + }, + }, + { + name: "SETEQ", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SETNE", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SETL", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SETG", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SETGE", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SETB", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MOVQconst", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "LEAQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "LEAQ2", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "LEAQ4", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "LEAQ8", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "LEAQglobal", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MOVBload", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MOVBQZXload", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MOVBQSXload", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MOVQload", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MOVQloadidx8", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "MOVBstore", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{}, + }, + }, + { + name: "MOVQstore", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{}, + }, + }, + { + name: "MOVQstoreidx8", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + 4295032831, + 0, + }, + clobbers: 0, + outputs: []regMask{}, + }, + }, + { + name: "MOVQloadglobal", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + }, + { + name: "MOVQstoreglobal", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + }, + { + name: "REPMOVSB", + reg: regInfo{ + inputs: []regMask{ + 128, + 64, + 2, + }, + clobbers: 194, + outputs: []regMask{}, + }, + }, + { + name: "ADDL", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "InvertFlags", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + }, + + { + name: "Add", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Mul", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Lsh", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Phi", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Copy", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Const", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Arg", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Global", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "SP", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "FP", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Func", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Load", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Store", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Move", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Call", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "StaticCall", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Convert", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "ConvNop", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "IsNonNil", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "IsInBounds", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "ArrayIndex", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "PtrIndex", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "OffPtr", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "SliceMake", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "SlicePtr", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "SliceLen", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "SliceCap", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "StringMake", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "StringPtr", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "StringLen", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "StoreReg8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "LoadReg8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "FwdRef", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, +} + +func (o Op) String() string { return opcodeTable[o].name } diff --git a/src/cmd/compile/internal/ssa/op_string.go b/src/cmd/compile/internal/ssa/op_string.go deleted file mode 100644 index 2005d332ab..0000000000 --- a/src/cmd/compile/internal/ssa/op_string.go +++ /dev/null @@ -1,32 +0,0 @@ -// generated by stringer -type=Op; DO NOT EDIT - -package ssa - -import "fmt" - -const ( - _Op_name_0 = "opInvalid" - _Op_name_1 = "opGenericStartOpAddOpSubOpMulOpLshOpRshOpLessOpConstOpArgOpGlobalOpFuncOpFPOpSPOpCopyOpMoveOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpLoadOpStoreOpArrayIndexOpPtrIndexOpIsNonNilOpIsInBoundsOpCallOpStaticCallOpConvertOpConvNopOpOffPtrOpStoreReg8OpLoadReg8OpFwdRefOpGenericEnd" - _Op_name_2 = "opAMD64startOpADDQOpADDQconstOpSUBQOpSUBQconstOpMULQOpMULQconstOpSHLQOpSHLQconstOpNEGQOpADDLOpCMPQOpCMPQconstOpTESTQOpTESTBOpSETEQOpSETNEOpSETLOpSETGOpSETGEOpSETBOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLEAQglobalOpMOVBloadOpMOVBQZXloadOpMOVBQSXloadOpMOVQloadOpMOVQstoreOpMOVQloadidx8OpMOVQstoreidx8OpMOVQloadglobalOpMOVQstoreglobalOpMOVQconstOpREPMOVSB" -) - -var ( - _Op_index_0 = [...]uint8{0, 9} - _Op_index_1 = [...]uint16{0, 14, 19, 24, 29, 34, 39, 45, 52, 57, 65, 71, 75, 79, 85, 91, 96, 107, 117, 127, 137, 149, 160, 171, 177, 184, 196, 206, 216, 228, 234, 246, 255, 264, 272, 283, 293, 301, 313} - _Op_index_2 = [...]uint16{0, 12, 18, 29, 35, 46, 52, 63, 69, 80, 86, 92, 98, 109, 116, 123, 130, 137, 143, 149, 156, 162, 175, 181, 188, 195, 202, 214, 224, 237, 250, 260, 271, 285, 300, 316, 333, 344, 354} -) - -func (i Op) String() string { - switch { - case i == 0: - return _Op_name_0 - case 1001 <= i && i <= 1038: - i -= 1001 - return _Op_name_1[_Op_index_1[i]:_Op_index_1[i+1]] - case 2001 <= i && i <= 2038: - i -= 2001 - return _Op_name_2[_Op_index_2[i]:_Op_index_2[i+1]] - default: - return fmt.Sprintf("Op(%d)", i) - } -} diff --git a/src/cmd/compile/internal/ssa/opamd64.go b/src/cmd/compile/internal/ssa/opamd64.go deleted file mode 100644 index 665f087b6e..0000000000 --- a/src/cmd/compile/internal/ssa/opamd64.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2015 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. - -package ssa - -// amd64-specific opcodes - -const ( - blockAMD64Start BlockKind = blockAMD64Base + iota - - BlockEQ - BlockNE - BlockLT - BlockLE - BlockGT - BlockGE - BlockULT - BlockULE - BlockUGT - BlockUGE -) - -const ( - opAMD64start Op = opAMD64Base + iota - - // Suffixes encode the bit width of various instructions. - // Q = 64 bit, L = 32 bit, W = 16 bit, B = 8 bit - - // arithmetic - OpADDQ // arg0 + arg1 - OpADDQconst // arg + aux.(int64) - OpSUBQ // arg0 - arg1 - OpSUBQconst // arg - aux.(int64) - OpMULQ // arg0 * arg1 - OpMULQconst // arg * aux.(int64) - OpSHLQ // arg0 << arg1 - OpSHLQconst // arg << aux.(int64) - OpNEGQ // -arg - OpADDL // arg0 + arg1 - - // Flags value generation. - // We pretend the flags type is an opaque thing that comparisons generate - // and from which we can extract boolean conditions like <, ==, etc. - OpCMPQ // arg0 compare to arg1 - OpCMPQconst // arg0 compare to aux.(int64) - OpTESTQ // (arg0 & arg1) compare to 0 - OpTESTB // (arg0 & arg1) compare to 0 - - // These opcodes extract a particular boolean condition from a flags value. - OpSETEQ // extract == condition from arg0 - OpSETNE // extract != condition from arg0 - OpSETL // extract signed < condition from arg0 - OpSETG // extract signed > condition from arg0 - OpSETGE // extract signed >= condition from arg0 - OpSETB // extract unsigned < condition from arg0 - - // InvertFlags reverses the direction of a flags type interpretation: - // (InvertFlags (CMPQ a b)) == (CMPQ b a) - // So if we want (SETL (CMPQ a b)) but we can't do that because a is a constant, - // then we do (SETL (InvertFlags (CMPQ b a))) instead. - // Rewrites will convert this to (SETG (CMPQ b a)). - // InvertFlags is a pseudo-op which can't appear in assembly output. - OpInvertFlags // reverse direction of arg0 - - OpLEAQ // arg0 + arg1 + aux.(int64) - OpLEAQ2 // arg0 + 2*arg1 + aux.(int64) - OpLEAQ4 // arg0 + 4*arg1 + aux.(int64) - OpLEAQ8 // arg0 + 8*arg1 + aux.(int64) - OpLEAQglobal // no args. address of aux.(GlobalOffset) - - // Load/store from general address - OpMOVBload // Load from arg0+aux.(int64). arg1=memory - OpMOVBQZXload - OpMOVBQSXload - OpMOVQload - OpMOVQstore // Store arg1 to arg0+aux.(int64). arg2=memory, returns memory. - OpMOVQloadidx8 // Load from arg0+arg1*8+aux.(int64). arg2=memory - OpMOVQstoreidx8 // Store arg2 to arg0+arg1*8+aux.(int64). arg3=memory, returns memory. - - // Load/store from global. Same as the above loads, but arg0 is missing and aux is a GlobalOffset instead of an int64. - OpMOVQloadglobal // arg0 = memory - OpMOVQstoreglobal // store arg0. arg1=memory, returns memory. - - // materialize a constant into a register - OpMOVQconst // (takes no arguments) - - // move memory - OpREPMOVSB // arg0=destptr, arg1=srcptr, arg2=len, arg3=mem -) - -type regMask uint64 - -var regsAMD64 = [...]string{ - "AX", - "CX", - "DX", - "BX", - "SP", - "BP", - "SI", - "DI", - "R8", - "R9", - "R10", - "R11", - "R12", - "R13", - "R14", - "R15", - - // pseudo registers - "FP", - "FLAGS", - "OVERWRITE0", // the same register as the first input -} - -var gp regMask = 0x1ffff // all integer registers including SP&FP -var gpout regMask = 0xffef // integer registers not including SP&FP -var cx regMask = 1 << 1 -var si regMask = 1 << 6 -var di regMask = 1 << 7 -var flags regMask = 1 << 17 - -var ( - // gp = general purpose (integer) registers - gp21 = [2][]regMask{{gp, gp}, {gpout}} // 2 input, 1 output - gp11 = [2][]regMask{{gp}, {gpout}} // 1 input, 1 output - gp01 = [2][]regMask{{}, {gpout}} // 0 input, 1 output - shift = [2][]regMask{{gp, cx}, {gpout}} // shift operations - gp2_flags = [2][]regMask{{gp, gp}, {flags}} // generate flags from 2 gp regs - gp1_flags = [2][]regMask{{gp}, {flags}} // generate flags from 1 gp reg - - gpload = [2][]regMask{{gp, 0}, {gpout}} - gploadidx = [2][]regMask{{gp, gp, 0}, {gpout}} - gpstore = [2][]regMask{{gp, gp, 0}, {0}} - gpstoreidx = [2][]regMask{{gp, gp, gp, 0}, {0}} - - gpload_stack = [2][]regMask{{0}, {gpout}} - gpstore_stack = [2][]regMask{{gp, 0}, {0}} -) - -// Opcodes that appear in an output amd64 program -var amd64Table = map[Op]opInfo{ - OpADDQ: {flags: OpFlagCommutative, reg: gp21}, // TODO: overwrite - OpADDQconst: {reg: gp11}, // aux = int64 constant to add - OpSUBQ: {reg: gp21}, - OpSUBQconst: {reg: gp11}, - OpMULQ: {reg: gp21}, - OpMULQconst: {reg: gp11}, - OpSHLQ: {reg: gp21}, - OpSHLQconst: {reg: gp11}, - - OpCMPQ: {reg: gp2_flags}, // compute arg[0]-arg[1] and produce flags - OpCMPQconst: {reg: gp1_flags}, - OpTESTQ: {reg: gp2_flags}, - OpTESTB: {reg: gp2_flags}, - - OpLEAQ: {flags: OpFlagCommutative, reg: gp21}, // aux = int64 constant to add - OpLEAQ2: {}, - OpLEAQ4: {}, - OpLEAQ8: {}, - OpLEAQglobal: {reg: gp01}, - - // loads and stores - OpMOVBload: {reg: gpload}, - OpMOVQload: {reg: gpload}, - OpMOVQstore: {reg: gpstore}, - OpMOVQloadidx8: {reg: gploadidx}, - OpMOVQstoreidx8: {reg: gpstoreidx}, - - OpMOVQconst: {reg: gp01}, - - OpStaticCall: {}, - - OpCopy: {reg: gp11}, // TODO: make arch-specific - OpConvNop: {reg: gp11}, // TODO: make arch-specific. Or get rid of this altogether. - - // convert from flags back to boolean - OpSETL: {}, - - // ops for spilling of registers - // unlike regular loads & stores, these take no memory argument. - // They are just like OpCopy but we use them during register allocation. - // TODO: different widths, float - OpLoadReg8: {}, - OpStoreReg8: {}, - - OpREPMOVSB: {reg: [2][]regMask{{di, si, cx, 0}, {0}}}, // TODO: record that si/di/cx are clobbered -} - -func init() { - for op, info := range amd64Table { - opcodeTable[op] = info - } -} diff --git a/src/cmd/compile/internal/ssa/opt.go b/src/cmd/compile/internal/ssa/opt.go index 81c1dfcc02..6e91fd7da3 100644 --- a/src/cmd/compile/internal/ssa/opt.go +++ b/src/cmd/compile/internal/ssa/opt.go @@ -5,9 +5,6 @@ package ssa // machine-independent optimization - -//go:generate go run rulegen/rulegen.go rulegen/generic.rules genericBlockRules genericValueRules generic.go - func opt(f *Func) { - applyRewrite(f, genericBlockRules, genericValueRules) + applyRewrite(f, rewriteBlockgeneric, rewriteValuegeneric) } diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 8da969b660..839008445c 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -20,8 +20,10 @@ func setloc(home []Location, v *Value, loc Location) []Location { type register uint +type regMask uint64 + // TODO: make arch-dependent -var numRegs register = 32 +var numRegs register = 64 var registers = [...]Register{ Register{0, "AX"}, @@ -40,12 +42,26 @@ var registers = [...]Register{ Register{13, "R13"}, Register{14, "R14"}, Register{15, "R15"}, + Register{16, "X0"}, + Register{17, "X1"}, + Register{18, "X2"}, + Register{19, "X3"}, + Register{20, "X4"}, + Register{21, "X5"}, + Register{22, "X6"}, + Register{23, "X7"}, + Register{24, "X8"}, + Register{25, "X9"}, + Register{26, "X10"}, + Register{27, "X11"}, + Register{28, "X12"}, + Register{29, "X13"}, + Register{30, "X14"}, + Register{31, "X15"}, + Register{32, "FP"}, // pseudo-register, actually a constant offset from SP + Register{33, "FLAGS"}, - // TODO X0, ... // TODO: make arch-dependent - Register{16, "FP"}, // pseudo-register, actually a constant offset from SP - Register{17, "FLAGS"}, - Register{18, "OVERWRITE"}, } // countRegs returns the number of set bits in the register mask. @@ -98,7 +114,7 @@ func regalloc(f *Func) { home = setloc(home, v, ®isters[4]) // TODO: arch-dependent case OpFP: fp = v - home = setloc(home, v, ®isters[16]) // TODO: arch-dependent + home = setloc(home, v, ®isters[32]) // TODO: arch-dependent } } @@ -135,7 +151,7 @@ func regalloc(f *Func) { // TODO: hack: initialize fixed registers regs[4] = regInfo{sp, sp, false} - regs[16] = regInfo{fp, fp, false} + regs[32] = regInfo{fp, fp, false} var used regMask // has a 1 for each non-nil entry in regs var dirty regMask // has a 1 for each dirty entry in regs @@ -155,8 +171,12 @@ func regalloc(f *Func) { // - definition of v. c will be identical to v but will live in // a register. v will be modified into a spill of c. regspec := opcodeTable[v.Op].reg - inputs := regspec[0] - outputs := regspec[1] + if v.Op == OpCopy || v.Op == OpConvNop { + // TODO: make this less of a hack + regspec = opcodeTable[OpAMD64ADDQconst].reg + } + inputs := regspec.inputs + outputs := regspec.outputs if len(inputs) == 0 && len(outputs) == 0 { // No register allocation required (or none specified yet) b.Values = append(b.Values, v) @@ -177,7 +197,7 @@ func regalloc(f *Func) { // nospill contains registers that we can't spill because // we already set them up for use by the current instruction. var nospill regMask - nospill |= 0x10010 // SP and FP can't be spilled (TODO: arch-specific) + nospill |= 0x100000010 // SP and FP can't be spilled (TODO: arch-specific) // Move inputs into registers for _, o := range order { @@ -278,6 +298,8 @@ func regalloc(f *Func) { nospill |= regMask(1) << r } + // TODO: do any clobbering + // pick a register for v itself. if len(outputs) > 1 { panic("can't do multi-output yet") diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go new file mode 100644 index 0000000000..d49245ad3a --- /dev/null +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -0,0 +1,1090 @@ +// autogenerated from gen/AMD64.rules: do not edit! +// generated with: cd gen; go run *.go +package ssa + +func rewriteValueAMD64(v *Value, config *Config) bool { + switch v.Op { + case OpAMD64ADDQ: + // match: (ADDQ x (MOVQconst [c])) + // cond: + // result: (ADDQconst [c] x) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto endacffd55e74ee0ff59ad58a18ddfc9973 + } + c := v.Args[1].Aux + v.Op = OpAMD64ADDQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto endacffd55e74ee0ff59ad58a18ddfc9973 + endacffd55e74ee0ff59ad58a18ddfc9973: + ; + // match: (ADDQ (MOVQconst [c]) x) + // cond: + // result: (ADDQconst [c] x) + { + if v.Args[0].Op != OpAMD64MOVQconst { + goto end7166f476d744ab7a51125959d3d3c7e2 + } + c := v.Args[0].Aux + x := v.Args[1] + v.Op = OpAMD64ADDQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto end7166f476d744ab7a51125959d3d3c7e2 + end7166f476d744ab7a51125959d3d3c7e2: + ; + // match: (ADDQ x (SHLQconst [shift] y)) + // cond: shift.(int64) == 3 + // result: (LEAQ8 [int64(0)] x y) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64SHLQconst { + goto endaf4f724e1e17f2b116d336c07da0165d + } + shift := v.Args[1].Aux + y := v.Args[1].Args[0] + if !(shift.(int64) == 3) { + goto endaf4f724e1e17f2b116d336c07da0165d + } + v.Op = OpAMD64LEAQ8 + v.Aux = nil + v.resetArgs() + v.Aux = int64(0) + v.AddArg(x) + v.AddArg(y) + return true + } + goto endaf4f724e1e17f2b116d336c07da0165d + endaf4f724e1e17f2b116d336c07da0165d: + ; + case OpAMD64ADDQconst: + // match: (ADDQconst [c] (LEAQ8 [d] x y)) + // cond: + // result: (LEAQ8 [addOff(c, d)] x y) + { + c := v.Aux + if v.Args[0].Op != OpAMD64LEAQ8 { + goto ende2cc681c9abf9913288803fb1b39e639 + } + d := v.Args[0].Aux + x := v.Args[0].Args[0] + y := v.Args[0].Args[1] + v.Op = OpAMD64LEAQ8 + v.Aux = nil + v.resetArgs() + v.Aux = addOff(c, d) + v.AddArg(x) + v.AddArg(y) + return true + } + goto ende2cc681c9abf9913288803fb1b39e639 + ende2cc681c9abf9913288803fb1b39e639: + ; + // match: (ADDQconst [off] x) + // cond: off.(int64) == 0 + // result: (Copy x) + { + off := v.Aux + x := v.Args[0] + if !(off.(int64) == 0) { + goto endfa1c7cc5ac4716697e891376787f86ce + } + v.Op = OpCopy + v.Aux = nil + v.resetArgs() + v.AddArg(x) + return true + } + goto endfa1c7cc5ac4716697e891376787f86ce + endfa1c7cc5ac4716697e891376787f86ce: + ; + case OpAdd: + // match: (Add x y) + // cond: (is64BitInt(t) || isPtr(t)) + // result: (ADDQ x y) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is64BitInt(t) || isPtr(t)) { + goto endf031c523d7dd08e4b8e7010a94cd94c9 + } + v.Op = OpAMD64ADDQ + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto endf031c523d7dd08e4b8e7010a94cd94c9 + endf031c523d7dd08e4b8e7010a94cd94c9: + ; + // match: (Add x y) + // cond: is32BitInt(t) + // result: (ADDL x y) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is32BitInt(t)) { + goto end35a02a1587264e40cf1055856ff8445a + } + v.Op = OpAMD64ADDL + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto end35a02a1587264e40cf1055856ff8445a + end35a02a1587264e40cf1055856ff8445a: + ; + case OpAMD64CMPQ: + // match: (CMPQ x (MOVQconst [c])) + // cond: + // result: (CMPQconst x [c]) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto end32ef1328af280ac18fa8045a3502dae9 + } + c := v.Args[1].Aux + v.Op = OpAMD64CMPQconst + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.Aux = c + return true + } + goto end32ef1328af280ac18fa8045a3502dae9 + end32ef1328af280ac18fa8045a3502dae9: + ; + // match: (CMPQ (MOVQconst [c]) x) + // cond: + // result: (InvertFlags (CMPQconst x [c])) + { + if v.Args[0].Op != OpAMD64MOVQconst { + goto endf8ca12fe79290bc82b11cfa463bc9413 + } + c := v.Args[0].Aux + x := v.Args[1] + v.Op = OpAMD64InvertFlags + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAMD64CMPQconst, TypeInvalid, nil) + v0.Type = TypeFlags + v0.AddArg(x) + v0.Aux = c + v.AddArg(v0) + return true + } + goto endf8ca12fe79290bc82b11cfa463bc9413 + endf8ca12fe79290bc82b11cfa463bc9413: + ; + case OpConst: + // match: (Const [val]) + // cond: is64BitInt(t) + // result: (MOVQconst [val]) + { + t := v.Type + val := v.Aux + if !(is64BitInt(t)) { + goto end7f5c5b34093fbc6860524cb803ee51bf + } + v.Op = OpAMD64MOVQconst + v.Aux = nil + v.resetArgs() + v.Aux = val + return true + } + goto end7f5c5b34093fbc6860524cb803ee51bf + end7f5c5b34093fbc6860524cb803ee51bf: + ; + case OpGlobal: + // match: (Global [sym]) + // cond: + // result: (LEAQglobal [GlobalOffset{sym,0}]) + { + sym := v.Aux + v.Op = OpAMD64LEAQglobal + v.Aux = nil + v.resetArgs() + v.Aux = GlobalOffset{sym, 0} + return true + } + goto end3a3c76fac0e2e53c0e1c60b9524e6f1c + end3a3c76fac0e2e53c0e1c60b9524e6f1c: + ; + case OpIsInBounds: + // match: (IsInBounds idx len) + // cond: + // result: (SETB (CMPQ idx len)) + { + idx := v.Args[0] + len := v.Args[1] + v.Op = OpAMD64SETB + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAMD64CMPQ, TypeInvalid, nil) + v0.Type = TypeFlags + v0.AddArg(idx) + v0.AddArg(len) + v.AddArg(v0) + return true + } + goto endb51d371171154c0f1613b687757e0576 + endb51d371171154c0f1613b687757e0576: + ; + case OpIsNonNil: + // match: (IsNonNil p) + // cond: + // result: (SETNE (TESTQ p p)) + { + p := v.Args[0] + v.Op = OpAMD64SETNE + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAMD64TESTQ, TypeInvalid, nil) + v0.Type = TypeFlags + v0.AddArg(p) + v0.AddArg(p) + v.AddArg(v0) + return true + } + goto endff508c3726edfb573abc6128c177e76c + endff508c3726edfb573abc6128c177e76c: + ; + case OpLess: + // match: (Less x y) + // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) + // result: (SETL (CMPQ x y)) + { + x := v.Args[0] + y := v.Args[1] + if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { + goto endcecf13a952d4c6c2383561c7d68a3cf9 + } + v.Op = OpAMD64SETL + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAMD64CMPQ, TypeInvalid, nil) + v0.Type = TypeFlags + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto endcecf13a952d4c6c2383561c7d68a3cf9 + endcecf13a952d4c6c2383561c7d68a3cf9: + ; + case OpLoad: + // match: (Load ptr mem) + // cond: t.IsBoolean() + // result: (MOVBload [int64(0)] ptr mem) + { + t := v.Type + ptr := v.Args[0] + mem := v.Args[1] + if !(t.IsBoolean()) { + goto end73f21632e56c3614902d3c29c82dc4ea + } + v.Op = OpAMD64MOVBload + v.Aux = nil + v.resetArgs() + v.Aux = int64(0) + v.AddArg(ptr) + v.AddArg(mem) + return true + } + goto end73f21632e56c3614902d3c29c82dc4ea + end73f21632e56c3614902d3c29c82dc4ea: + ; + // match: (Load ptr mem) + // cond: (is64BitInt(t) || isPtr(t)) + // result: (MOVQload [int64(0)] ptr mem) + { + t := v.Type + ptr := v.Args[0] + mem := v.Args[1] + if !(is64BitInt(t) || isPtr(t)) { + goto end581ce5a20901df1b8143448ba031685b + } + v.Op = OpAMD64MOVQload + v.Aux = nil + v.resetArgs() + v.Aux = int64(0) + v.AddArg(ptr) + v.AddArg(mem) + return true + } + goto end581ce5a20901df1b8143448ba031685b + end581ce5a20901df1b8143448ba031685b: + ; + case OpLsh: + // match: (Lsh x y) + // cond: is64BitInt(t) + // result: (SHLQ x y) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is64BitInt(t)) { + goto end9f05c9539e51db6ad557989e0c822e9b + } + v.Op = OpAMD64SHLQ + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto end9f05c9539e51db6ad557989e0c822e9b + end9f05c9539e51db6ad557989e0c822e9b: + ; + case OpAMD64MOVQload: + // match: (MOVQload [off1] (ADDQconst [off2] ptr) mem) + // cond: + // result: (MOVQload [addOff(off1, off2)] ptr mem) + { + off1 := v.Aux + if v.Args[0].Op != OpAMD64ADDQconst { + goto end843d29b538c4483b432b632e5666d6e3 + } + off2 := v.Args[0].Aux + ptr := v.Args[0].Args[0] + mem := v.Args[1] + v.Op = OpAMD64MOVQload + v.Aux = nil + v.resetArgs() + v.Aux = addOff(off1, off2) + v.AddArg(ptr) + v.AddArg(mem) + return true + } + goto end843d29b538c4483b432b632e5666d6e3 + end843d29b538c4483b432b632e5666d6e3: + ; + // match: (MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) + // cond: + // result: (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) + { + off1 := v.Aux + if v.Args[0].Op != OpAMD64LEAQ8 { + goto end02f5ad148292c46463e7c20d3b821735 + } + off2 := v.Args[0].Aux + ptr := v.Args[0].Args[0] + idx := v.Args[0].Args[1] + mem := v.Args[1] + v.Op = OpAMD64MOVQloadidx8 + v.Aux = nil + v.resetArgs() + v.Aux = addOff(off1, off2) + v.AddArg(ptr) + v.AddArg(idx) + v.AddArg(mem) + return true + } + goto end02f5ad148292c46463e7c20d3b821735 + end02f5ad148292c46463e7c20d3b821735: + ; + case OpAMD64MOVQloadidx8: + // match: (MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) + // cond: + // result: (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) + { + off1 := v.Aux + if v.Args[0].Op != OpAMD64ADDQconst { + goto ende81e44bcfb11f90916ccb440c590121f + } + off2 := v.Args[0].Aux + ptr := v.Args[0].Args[0] + idx := v.Args[1] + mem := v.Args[2] + v.Op = OpAMD64MOVQloadidx8 + v.Aux = nil + v.resetArgs() + v.Aux = addOff(off1, off2) + v.AddArg(ptr) + v.AddArg(idx) + v.AddArg(mem) + return true + } + goto ende81e44bcfb11f90916ccb440c590121f + ende81e44bcfb11f90916ccb440c590121f: + ; + case OpAMD64MOVQstore: + // match: (MOVQstore [off1] (ADDQconst [off2] ptr) val mem) + // cond: + // result: (MOVQstore [addOff(off1, off2)] ptr val mem) + { + off1 := v.Aux + if v.Args[0].Op != OpAMD64ADDQconst { + goto end2108c693a43c79aed10b9246c39c80aa + } + off2 := v.Args[0].Aux + ptr := v.Args[0].Args[0] + val := v.Args[1] + mem := v.Args[2] + v.Op = OpAMD64MOVQstore + v.Aux = nil + v.resetArgs() + v.Aux = addOff(off1, off2) + v.AddArg(ptr) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto end2108c693a43c79aed10b9246c39c80aa + end2108c693a43c79aed10b9246c39c80aa: + ; + // match: (MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) + // cond: + // result: (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) + { + off1 := v.Aux + if v.Args[0].Op != OpAMD64LEAQ8 { + goto endce1db8c8d37c8397c500a2068a65c215 + } + off2 := v.Args[0].Aux + ptr := v.Args[0].Args[0] + idx := v.Args[0].Args[1] + val := v.Args[1] + mem := v.Args[2] + v.Op = OpAMD64MOVQstoreidx8 + v.Aux = nil + v.resetArgs() + v.Aux = addOff(off1, off2) + v.AddArg(ptr) + v.AddArg(idx) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto endce1db8c8d37c8397c500a2068a65c215 + endce1db8c8d37c8397c500a2068a65c215: + ; + case OpAMD64MOVQstoreidx8: + // match: (MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) + // cond: + // result: (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) + { + off1 := v.Aux + if v.Args[0].Op != OpAMD64ADDQconst { + goto end01c970657b0fdefeab82458c15022163 + } + off2 := v.Args[0].Aux + ptr := v.Args[0].Args[0] + idx := v.Args[1] + val := v.Args[2] + mem := v.Args[3] + v.Op = OpAMD64MOVQstoreidx8 + v.Aux = nil + v.resetArgs() + v.Aux = addOff(off1, off2) + v.AddArg(ptr) + v.AddArg(idx) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto end01c970657b0fdefeab82458c15022163 + end01c970657b0fdefeab82458c15022163: + ; + case OpAMD64MULQ: + // match: (MULQ x (MOVQconst [c])) + // cond: c.(int64) == int64(int32(c.(int64))) + // result: (MULQconst [c] x) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto ende8c09b194fcde7d9cdc69f2deff86304 + } + c := v.Args[1].Aux + if !(c.(int64) == int64(int32(c.(int64)))) { + goto ende8c09b194fcde7d9cdc69f2deff86304 + } + v.Op = OpAMD64MULQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto ende8c09b194fcde7d9cdc69f2deff86304 + ende8c09b194fcde7d9cdc69f2deff86304: + ; + // match: (MULQ (MOVQconst [c]) x) + // cond: + // result: (MULQconst [c] x) + { + if v.Args[0].Op != OpAMD64MOVQconst { + goto endc6e18d6968175d6e58eafa6dcf40c1b8 + } + c := v.Args[0].Aux + x := v.Args[1] + v.Op = OpAMD64MULQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto endc6e18d6968175d6e58eafa6dcf40c1b8 + endc6e18d6968175d6e58eafa6dcf40c1b8: + ; + case OpAMD64MULQconst: + // match: (MULQconst [c] x) + // cond: c.(int64) == 8 + // result: (SHLQconst [int64(3)] x) + { + c := v.Aux + x := v.Args[0] + if !(c.(int64) == 8) { + goto end7e16978c56138324ff2abf91fd6d94d4 + } + v.Op = OpAMD64SHLQconst + v.Aux = nil + v.resetArgs() + v.Aux = int64(3) + v.AddArg(x) + return true + } + goto end7e16978c56138324ff2abf91fd6d94d4 + end7e16978c56138324ff2abf91fd6d94d4: + ; + // match: (MULQconst [c] x) + // cond: c.(int64) == 64 + // result: (SHLQconst [int64(5)] x) + { + c := v.Aux + x := v.Args[0] + if !(c.(int64) == 64) { + goto end2c7a02f230e4b311ac3a4e22f70a4f08 + } + v.Op = OpAMD64SHLQconst + v.Aux = nil + v.resetArgs() + v.Aux = int64(5) + v.AddArg(x) + return true + } + goto end2c7a02f230e4b311ac3a4e22f70a4f08 + end2c7a02f230e4b311ac3a4e22f70a4f08: + ; + case OpMove: + // match: (Move [size] dst src mem) + // cond: + // result: (REPMOVSB dst src (Const [size.(int64)]) mem) + { + size := v.Aux + dst := v.Args[0] + src := v.Args[1] + mem := v.Args[2] + v.Op = OpAMD64REPMOVSB + v.Aux = nil + v.resetArgs() + v.AddArg(dst) + v.AddArg(src) + v0 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v0.Type = TypeUInt64 + v0.Aux = size.(int64) + v.AddArg(v0) + v.AddArg(mem) + return true + } + goto end48909259b265a6bb2a076bc2c2dc7d1f + end48909259b265a6bb2a076bc2c2dc7d1f: + ; + case OpMul: + // match: (Mul x y) + // cond: is64BitInt(t) + // result: (MULQ x y) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is64BitInt(t)) { + goto endfab0d598f376ecba45a22587d50f7aff + } + v.Op = OpAMD64MULQ + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto endfab0d598f376ecba45a22587d50f7aff + endfab0d598f376ecba45a22587d50f7aff: + ; + case OpOffPtr: + // match: (OffPtr [off] ptr) + // cond: + // result: (ADDQconst [off] ptr) + { + off := v.Aux + ptr := v.Args[0] + v.Op = OpAMD64ADDQconst + v.Aux = nil + v.resetArgs() + v.Aux = off + v.AddArg(ptr) + return true + } + goto end0429f947ee7ac49ff45a243e461a5290 + end0429f947ee7ac49ff45a243e461a5290: + ; + case OpAMD64SETG: + // match: (SETG (InvertFlags x)) + // cond: + // result: (SETL x) + { + if v.Args[0].Op != OpAMD64InvertFlags { + goto endf7586738694c9cd0b74ae28bbadb649f + } + x := v.Args[0].Args[0] + v.Op = OpAMD64SETL + v.Aux = nil + v.resetArgs() + v.AddArg(x) + return true + } + goto endf7586738694c9cd0b74ae28bbadb649f + endf7586738694c9cd0b74ae28bbadb649f: + ; + case OpAMD64SETL: + // match: (SETL (InvertFlags x)) + // cond: + // result: (SETG x) + { + if v.Args[0].Op != OpAMD64InvertFlags { + goto ende33160cd86b9d4d3b77e02fb4658d5d3 + } + x := v.Args[0].Args[0] + v.Op = OpAMD64SETG + v.Aux = nil + v.resetArgs() + v.AddArg(x) + return true + } + goto ende33160cd86b9d4d3b77e02fb4658d5d3 + ende33160cd86b9d4d3b77e02fb4658d5d3: + ; + case OpAMD64SHLQ: + // match: (SHLQ x (MOVQconst [c])) + // cond: + // result: (SHLQconst [c] x) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto endcca412bead06dc3d56ef034a82d184d6 + } + c := v.Args[1].Aux + v.Op = OpAMD64SHLQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto endcca412bead06dc3d56ef034a82d184d6 + endcca412bead06dc3d56ef034a82d184d6: + ; + case OpAMD64SUBQ: + // match: (SUBQ x (MOVQconst [c])) + // cond: + // result: (SUBQconst x [c]) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto end5a74a63bd9ad15437717c6df3b25eebb + } + c := v.Args[1].Aux + v.Op = OpAMD64SUBQconst + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.Aux = c + return true + } + goto end5a74a63bd9ad15437717c6df3b25eebb + end5a74a63bd9ad15437717c6df3b25eebb: + ; + // match: (SUBQ (MOVQconst [c]) x) + // cond: + // result: (NEGQ (SUBQconst x [c])) + { + t := v.Type + if v.Args[0].Op != OpAMD64MOVQconst { + goto end78e66b6fc298684ff4ac8aec5ce873c9 + } + c := v.Args[0].Aux + x := v.Args[1] + v.Op = OpAMD64NEGQ + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAMD64SUBQconst, TypeInvalid, nil) + v0.Type = t + v0.AddArg(x) + v0.Aux = c + v.AddArg(v0) + return true + } + goto end78e66b6fc298684ff4ac8aec5ce873c9 + end78e66b6fc298684ff4ac8aec5ce873c9: + ; + case OpStore: + // match: (Store ptr val mem) + // cond: (is64BitInt(val.Type) || isPtr(val.Type)) + // result: (MOVQstore [int64(0)] ptr val mem) + { + ptr := v.Args[0] + val := v.Args[1] + mem := v.Args[2] + if !(is64BitInt(val.Type) || isPtr(val.Type)) { + goto end9680b43f504bc06f9fab000823ce471a + } + v.Op = OpAMD64MOVQstore + v.Aux = nil + v.resetArgs() + v.Aux = int64(0) + v.AddArg(ptr) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto end9680b43f504bc06f9fab000823ce471a + end9680b43f504bc06f9fab000823ce471a: + ; + case OpSub: + // match: (Sub x y) + // cond: is64BitInt(t) + // result: (SUBQ x y) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is64BitInt(t)) { + goto ende6ef29f885a8ecf3058212bb95917323 + } + v.Op = OpAMD64SUBQ + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto ende6ef29f885a8ecf3058212bb95917323 + ende6ef29f885a8ecf3058212bb95917323: + } + return false +} +func rewriteBlockAMD64(b *Block) bool { + switch b.Kind { + case BlockAMD64EQ: + // match: (EQ (InvertFlags cmp) yes no) + // cond: + // result: (EQ cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto end6b8e9afc73b1c4d528f31a60d2575fae + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64EQ + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end6b8e9afc73b1c4d528f31a60d2575fae + end6b8e9afc73b1c4d528f31a60d2575fae: + ; + case BlockAMD64GE: + // match: (GE (InvertFlags cmp) yes no) + // cond: + // result: (LE cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto end0610f000a6988ee8310307ec2ea138f8 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64LE + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end0610f000a6988ee8310307ec2ea138f8 + end0610f000a6988ee8310307ec2ea138f8: + ; + case BlockAMD64GT: + // match: (GT (InvertFlags cmp) yes no) + // cond: + // result: (LT cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto endf60c0660b6a8aa9565c97fc87f04eb34 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64LT + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto endf60c0660b6a8aa9565c97fc87f04eb34 + endf60c0660b6a8aa9565c97fc87f04eb34: + ; + case BlockIf: + // match: (If (SETL cmp) yes no) + // cond: + // result: (LT cmp yes no) + { + v := b.Control + if v.Op != OpAMD64SETL { + goto ende4d36879bb8e1bd8facaa8c91ba99dcc + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64LT + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto ende4d36879bb8e1bd8facaa8c91ba99dcc + ende4d36879bb8e1bd8facaa8c91ba99dcc: + ; + // match: (If (SETNE cmp) yes no) + // cond: + // result: (NE cmp yes no) + { + v := b.Control + if v.Op != OpAMD64SETNE { + goto end5ff1403aaf7b543bc454177ab584e4f5 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64NE + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end5ff1403aaf7b543bc454177ab584e4f5 + end5ff1403aaf7b543bc454177ab584e4f5: + ; + // match: (If (SETB cmp) yes no) + // cond: + // result: (ULT cmp yes no) + { + v := b.Control + if v.Op != OpAMD64SETB { + goto end04935012db9defeafceef8175f803ea2 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64ULT + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end04935012db9defeafceef8175f803ea2 + end04935012db9defeafceef8175f803ea2: + ; + // match: (If cond yes no) + // cond: cond.Op == OpAMD64MOVBload + // result: (NE (TESTB cond cond) yes no) + { + v := b.Control + cond := v + yes := b.Succs[0] + no := b.Succs[1] + if !(cond.Op == OpAMD64MOVBload) { + goto end7e22019fb0effc80f85c05ea30bdb5d9 + } + b.Kind = BlockAMD64NE + v0 := v.Block.NewValue(OpAMD64TESTB, TypeInvalid, nil) + v0.Type = TypeFlags + v0.AddArg(cond) + v0.AddArg(cond) + b.Control = v0 + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end7e22019fb0effc80f85c05ea30bdb5d9 + end7e22019fb0effc80f85c05ea30bdb5d9: + ; + case BlockAMD64LE: + // match: (LE (InvertFlags cmp) yes no) + // cond: + // result: (GE cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto end0d49d7d087fe7578e8015cf13dae37e3 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64GE + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end0d49d7d087fe7578e8015cf13dae37e3 + end0d49d7d087fe7578e8015cf13dae37e3: + ; + case BlockAMD64LT: + // match: (LT (InvertFlags cmp) yes no) + // cond: + // result: (GT cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto end6a408cde0fee0ae7b7da0443c8d902bf + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64GT + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end6a408cde0fee0ae7b7da0443c8d902bf + end6a408cde0fee0ae7b7da0443c8d902bf: + ; + case BlockAMD64NE: + // match: (NE (InvertFlags cmp) yes no) + // cond: + // result: (NE cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto end713001aba794e50b582fbff930e110af + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64NE + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end713001aba794e50b582fbff930e110af + end713001aba794e50b582fbff930e110af: + ; + case BlockAMD64UGE: + // match: (UGE (InvertFlags cmp) yes no) + // cond: + // result: (ULE cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto ende3e4ddc183ca1a46598b11c2d0d13966 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64ULE + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto ende3e4ddc183ca1a46598b11c2d0d13966 + ende3e4ddc183ca1a46598b11c2d0d13966: + ; + case BlockAMD64UGT: + // match: (UGT (InvertFlags cmp) yes no) + // cond: + // result: (ULT cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto end49818853af2e5251175d06c62768cae7 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64ULT + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end49818853af2e5251175d06c62768cae7 + end49818853af2e5251175d06c62768cae7: + ; + case BlockAMD64ULE: + // match: (ULE (InvertFlags cmp) yes no) + // cond: + // result: (UGE cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto endd6698aac0d67261293b558c95ea17b4f + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64UGE + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto endd6698aac0d67261293b558c95ea17b4f + endd6698aac0d67261293b558c95ea17b4f: + ; + case BlockAMD64ULT: + // match: (ULT (InvertFlags cmp) yes no) + // cond: + // result: (UGT cmp yes no) + { + v := b.Control + if v.Op != OpAMD64InvertFlags { + goto end35105dbc9646f02577167e45ae2f2fd2 + } + cmp := v.Args[0] + yes := b.Succs[0] + no := b.Succs[1] + b.Kind = BlockAMD64UGT + b.Control = cmp + b.Succs[0] = yes + b.Succs[1] = no + return true + } + goto end35105dbc9646f02577167e45ae2f2fd2 + end35105dbc9646f02577167e45ae2f2fd2: + } + return false +} diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go new file mode 100644 index 0000000000..e9552e68f3 --- /dev/null +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -0,0 +1,424 @@ +// autogenerated from gen/generic.rules: do not edit! +// generated with: cd gen; go run *.go +package ssa + +func rewriteValuegeneric(v *Value, config *Config) bool { + switch v.Op { + case OpAdd: + // match: (Add (Const [c]) (Const [d])) + // cond: is64BitInt(t) + // result: (Const [{c.(int64)+d.(int64)}]) + { + t := v.Type + if v.Args[0].Op != OpConst { + goto end8d047ed0ae9537b840adc79ea82c6e05 + } + c := v.Args[0].Aux + if v.Args[1].Op != OpConst { + goto end8d047ed0ae9537b840adc79ea82c6e05 + } + d := v.Args[1].Aux + if !(is64BitInt(t)) { + goto end8d047ed0ae9537b840adc79ea82c6e05 + } + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = c.(int64) + d.(int64) + return true + } + goto end8d047ed0ae9537b840adc79ea82c6e05 + end8d047ed0ae9537b840adc79ea82c6e05: + ; + case OpArrayIndex: + // match: (ArrayIndex (Load ptr mem) idx) + // cond: + // result: (Load (PtrIndex ptr idx) mem) + { + if v.Args[0].Op != OpLoad { + goto end3809f4c52270a76313e4ea26e6f0b753 + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + idx := v.Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpPtrIndex, TypeInvalid, nil) + v0.Type = ptr.Type.Elem().Elem().PtrTo() + v0.AddArg(ptr) + v0.AddArg(idx) + v.AddArg(v0) + v.AddArg(mem) + return true + } + goto end3809f4c52270a76313e4ea26e6f0b753 + end3809f4c52270a76313e4ea26e6f0b753: + ; + case OpConst: + // match: (Const [s]) + // cond: t.IsString() + // result: (StringMake (OffPtr [2*config.ptrSize] (Global [config.fe.StringSym(s.(string))])) (Const [int64(len(s.(string)))])) + { + t := v.Type + s := v.Aux + if !(t.IsString()) { + goto end8442aa5b3f4e5b840055475883110372 + } + v.Op = OpStringMake + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpOffPtr, TypeInvalid, nil) + v0.Type = TypeBytePtr + v0.Aux = 2 * config.ptrSize + v1 := v.Block.NewValue(OpGlobal, TypeInvalid, nil) + v1.Type = TypeBytePtr + v1.Aux = config.fe.StringSym(s.(string)) + v0.AddArg(v1) + v.AddArg(v0) + v2 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v2.Type = config.Uintptr + v2.Aux = int64(len(s.(string))) + v.AddArg(v2) + return true + } + goto end8442aa5b3f4e5b840055475883110372 + end8442aa5b3f4e5b840055475883110372: + ; + case OpIsInBounds: + // match: (IsInBounds (Const [c]) (Const [d])) + // cond: + // result: (Const [inBounds(c.(int64),d.(int64))]) + { + if v.Args[0].Op != OpConst { + goto enddbd1a394d9b71ee64335361b8384865c + } + c := v.Args[0].Aux + if v.Args[1].Op != OpConst { + goto enddbd1a394d9b71ee64335361b8384865c + } + d := v.Args[1].Aux + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = inBounds(c.(int64), d.(int64)) + return true + } + goto enddbd1a394d9b71ee64335361b8384865c + enddbd1a394d9b71ee64335361b8384865c: + ; + case OpLoad: + // match: (Load ptr mem) + // cond: t.IsString() + // result: (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) + { + t := v.Type + ptr := v.Args[0] + mem := v.Args[1] + if !(t.IsString()) { + goto endd0afd003b70d726a1c5bbaf51fe06182 + } + v.Op = OpStringMake + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpLoad, TypeInvalid, nil) + v0.Type = TypeBytePtr + v0.AddArg(ptr) + v0.AddArg(mem) + v.AddArg(v0) + v1 := v.Block.NewValue(OpLoad, TypeInvalid, nil) + v1.Type = config.Uintptr + v2 := v.Block.NewValue(OpOffPtr, TypeInvalid, nil) + v2.Type = TypeBytePtr + v2.Aux = config.ptrSize + v2.AddArg(ptr) + v1.AddArg(v2) + v1.AddArg(mem) + v.AddArg(v1) + return true + } + goto endd0afd003b70d726a1c5bbaf51fe06182 + endd0afd003b70d726a1c5bbaf51fe06182: + ; + case OpMul: + // match: (Mul (Const [c]) (Const [d])) + // cond: is64BitInt(t) + // result: (Const [{c.(int64)*d.(int64)}]) + { + t := v.Type + if v.Args[0].Op != OpConst { + goto end776610f88cf04f438242d76ed2b14f1c + } + c := v.Args[0].Aux + if v.Args[1].Op != OpConst { + goto end776610f88cf04f438242d76ed2b14f1c + } + d := v.Args[1].Aux + if !(is64BitInt(t)) { + goto end776610f88cf04f438242d76ed2b14f1c + } + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = c.(int64) * d.(int64) + return true + } + goto end776610f88cf04f438242d76ed2b14f1c + end776610f88cf04f438242d76ed2b14f1c: + ; + case OpPtrIndex: + // match: (PtrIndex ptr idx) + // cond: + // result: (Add ptr (Mul idx (Const [t.Elem().Size()]))) + { + t := v.Type + ptr := v.Args[0] + idx := v.Args[1] + v.Op = OpAdd + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v0 := v.Block.NewValue(OpMul, TypeInvalid, nil) + v0.Type = config.Uintptr + v0.AddArg(idx) + v1 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v1.Type = config.Uintptr + v1.Aux = t.Elem().Size() + v0.AddArg(v1) + v.AddArg(v0) + return true + } + goto end88c7c383675420d1581daeb899039fa8 + end88c7c383675420d1581daeb899039fa8: + ; + case OpSliceCap: + // match: (SliceCap (Load ptr mem)) + // cond: + // result: (Load (Add ptr (Const [int64(config.ptrSize*2)])) mem) + { + if v.Args[0].Op != OpLoad { + goto endc871dcd9a720b4290c9cae78fe147c8a + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAdd, TypeInvalid, nil) + v0.Type = ptr.Type + v0.AddArg(ptr) + v1 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v1.Type = config.Uintptr + v1.Aux = int64(config.ptrSize * 2) + v0.AddArg(v1) + v.AddArg(v0) + v.AddArg(mem) + return true + } + goto endc871dcd9a720b4290c9cae78fe147c8a + endc871dcd9a720b4290c9cae78fe147c8a: + ; + case OpSliceLen: + // match: (SliceLen (Load ptr mem)) + // cond: + // result: (Load (Add ptr (Const [int64(config.ptrSize)])) mem) + { + if v.Args[0].Op != OpLoad { + goto end1eec05e44f5fc8944e7c176f98a74d92 + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpAdd, TypeInvalid, nil) + v0.Type = ptr.Type + v0.AddArg(ptr) + v1 := v.Block.NewValue(OpConst, TypeInvalid, nil) + v1.Type = config.Uintptr + v1.Aux = int64(config.ptrSize) + v0.AddArg(v1) + v.AddArg(v0) + v.AddArg(mem) + return true + } + goto end1eec05e44f5fc8944e7c176f98a74d92 + end1eec05e44f5fc8944e7c176f98a74d92: + ; + case OpSlicePtr: + // match: (SlicePtr (Load ptr mem)) + // cond: + // result: (Load ptr mem) + { + if v.Args[0].Op != OpLoad { + goto end459613b83f95b65729d45c2ed663a153 + } + ptr := v.Args[0].Args[0] + mem := v.Args[0].Args[1] + v.Op = OpLoad + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(mem) + return true + } + goto end459613b83f95b65729d45c2ed663a153 + end459613b83f95b65729d45c2ed663a153: + ; + case OpStore: + // match: (Store dst (Load src mem) mem) + // cond: t.Size() > 8 + // result: (Move [t.Size()] dst src mem) + { + dst := v.Args[0] + if v.Args[1].Op != OpLoad { + goto end324ffb6d2771808da4267f62c854e9c8 + } + t := v.Args[1].Type + src := v.Args[1].Args[0] + mem := v.Args[1].Args[1] + if v.Args[2] != v.Args[1].Args[1] { + goto end324ffb6d2771808da4267f62c854e9c8 + } + if !(t.Size() > 8) { + goto end324ffb6d2771808da4267f62c854e9c8 + } + v.Op = OpMove + v.Aux = nil + v.resetArgs() + v.Aux = t.Size() + v.AddArg(dst) + v.AddArg(src) + v.AddArg(mem) + return true + } + goto end324ffb6d2771808da4267f62c854e9c8 + end324ffb6d2771808da4267f62c854e9c8: + ; + // match: (Store dst str mem) + // cond: str.Type.IsString() + // result: (Store (OffPtr [config.ptrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) + { + dst := v.Args[0] + str := v.Args[1] + mem := v.Args[2] + if !(str.Type.IsString()) { + goto end410559d97aed8018f820cd88723de442 + } + v.Op = OpStore + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(OpOffPtr, TypeInvalid, nil) + v0.Type = TypeBytePtr + v0.Aux = config.ptrSize + v0.AddArg(dst) + v.AddArg(v0) + v1 := v.Block.NewValue(OpStringLen, TypeInvalid, nil) + v1.Type = config.Uintptr + v1.AddArg(str) + v.AddArg(v1) + v2 := v.Block.NewValue(OpStore, TypeInvalid, nil) + v2.Type = TypeMem + v2.AddArg(dst) + v3 := v.Block.NewValue(OpStringPtr, TypeInvalid, nil) + v3.Type = TypeBytePtr + v3.AddArg(str) + v2.AddArg(v3) + v2.AddArg(mem) + v.AddArg(v2) + return true + } + goto end410559d97aed8018f820cd88723de442 + end410559d97aed8018f820cd88723de442: + ; + case OpStringLen: + // match: (StringLen (StringMake _ len)) + // cond: + // result: len + { + if v.Args[0].Op != OpStringMake { + goto end0d922460b7e5ca88324034f4bd6c027c + } + len := v.Args[0].Args[1] + v.Op = len.Op + v.Aux = len.Aux + v.resetArgs() + v.AddArgs(len.Args...) + return true + } + goto end0d922460b7e5ca88324034f4bd6c027c + end0d922460b7e5ca88324034f4bd6c027c: + ; + case OpStringPtr: + // match: (StringPtr (StringMake ptr _)) + // cond: + // result: ptr + { + if v.Args[0].Op != OpStringMake { + goto end061edc5d85c73ad909089af2556d9380 + } + ptr := v.Args[0].Args[0] + v.Op = ptr.Op + v.Aux = ptr.Aux + v.resetArgs() + v.AddArgs(ptr.Args...) + return true + } + goto end061edc5d85c73ad909089af2556d9380 + end061edc5d85c73ad909089af2556d9380: + } + return false +} +func rewriteBlockgeneric(b *Block) bool { + switch b.Kind { + case BlockIf: + // match: (If (Const [c]) yes no) + // cond: c.(bool) + // result: (Plain nil yes) + { + v := b.Control + if v.Op != OpConst { + goto end60cde11c1be8092f493d9cda982445ca + } + c := v.Aux + yes := b.Succs[0] + no := b.Succs[1] + if !(c.(bool)) { + goto end60cde11c1be8092f493d9cda982445ca + } + removePredecessor(b, no) + b.Kind = BlockPlain + b.Control = nil + b.Succs = b.Succs[:1] + b.Succs[0] = yes + return true + } + goto end60cde11c1be8092f493d9cda982445ca + end60cde11c1be8092f493d9cda982445ca: + ; + // match: (If (Const [c]) yes no) + // cond: !c.(bool) + // result: (Plain nil no) + { + v := b.Control + if v.Op != OpConst { + goto endf2a5efbfd2d40dead087c33685c8f30b + } + c := v.Aux + yes := b.Succs[0] + no := b.Succs[1] + if !(!c.(bool)) { + goto endf2a5efbfd2d40dead087c33685c8f30b + } + removePredecessor(b, yes) + b.Kind = BlockPlain + b.Control = nil + b.Succs = b.Succs[:1] + b.Succs[0] = no + return true + } + goto endf2a5efbfd2d40dead087c33685c8f30b + endf2a5efbfd2d40dead087c33685c8f30b: + } + return false +} diff --git a/src/cmd/compile/internal/ssa/rulegen/generic.rules b/src/cmd/compile/internal/ssa/rulegen/generic.rules deleted file mode 100644 index 21e5f72d09..0000000000 --- a/src/cmd/compile/internal/ssa/rulegen/generic.rules +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2015 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. - -// values are specified using the following format: -// (op [aux] arg0 arg1 ...) -// the type and aux fields are optional -// on the matching side -// - the types and aux fields must match if they are specified. -// on the generated side -// - the type of the top-level expression is the same as the one on the left-hand side. -// - the type of any subexpressions must be specified explicitly. -// - aux will be nil if not specified. - -// blocks are specified using the following format: -// (kind controlvalue succ0 succ1 ...) -// controlvalue must be "nil" or a value expression -// succ* fields must be variables -// For now, the generated successors must be a permutation of the matched successors. - -// constant folding -(Add (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)+d.(int64)}]) -(Mul (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)*d.(int64)}]) -(IsInBounds (Const [c]) (Const [d])) -> (Const [inBounds(c.(int64),d.(int64))]) - -// tear apart slices -// TODO: anything that generates a slice needs to go in here. -(SlicePtr (Load ptr mem)) -> (Load ptr mem) -(SliceLen (Load ptr mem)) -> (Load (Add ptr (Const [int64(config.ptrSize)])) mem) -(SliceCap (Load ptr mem)) -> (Load (Add ptr (Const [int64(config.ptrSize*2)])) mem) - -// indexing operations -// Note: bounds check has already been done -(ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex ptr idx) mem) -(PtrIndex ptr idx) -> (Add ptr (Mul idx (Const [t.Elem().Size()]))) - -// big-object moves -// TODO: fix size -(Store dst (Load src mem) mem) && t.Size() > 8 -> (Move [t.Size()] dst src mem) - -(BlockIf (Const [c]) yes no) && c.(bool) -> (BlockPlain nil yes) -(BlockIf (Const [c]) yes no) && !c.(bool) -> (BlockPlain nil no) - -// string ops -(Const [s]) && t.IsString() -> (StringMake (OffPtr [2*config.ptrSize] (Global [config.fe.StringSym(s.(string))])) (Const [int64(len(s.(string)))])) // TODO: ptr -(Load ptr mem) && t.IsString() -> (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) -(StringPtr (StringMake ptr _)) -> ptr -(StringLen (StringMake _ len)) -> len -(Store dst str mem) && str.Type.IsString() -> (Store (OffPtr [config.ptrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) diff --git a/src/cmd/compile/internal/ssa/rulegen/lower_amd64.rules b/src/cmd/compile/internal/ssa/rulegen/lower_amd64.rules deleted file mode 100644 index e86e408525..0000000000 --- a/src/cmd/compile/internal/ssa/rulegen/lower_amd64.rules +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015 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. - -// x86 register conventions: -// - Integer types live in the low portion of registers. -// Upper portions are correctly extended. -// - Boolean types use the low-order byte of a register. Upper bytes are junk. -// - We do not use AH,BH,CH,DH registers. -// - Floating-point types will live in the low natural slot of an sse2 register. -// Unused portions are junk. - -// These are the lowerings themselves -(Add x y) && (is64BitInt(t) || isPtr(t)) -> (ADDQ x y) -(Add x y) && is32BitInt(t) -> (ADDL x y) - -(Sub x y) && is64BitInt(t) -> (SUBQ x y) - -(Mul x y) && is64BitInt(t) -> (MULQ x y) -(Lsh x y) && is64BitInt(t) -> (SHLQ x y) // TODO: check y>63 -(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y)) - -(Load ptr mem) && t.IsBoolean() -> (MOVBload [int64(0)] ptr mem) -(Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload [int64(0)] ptr mem) -(Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore [int64(0)] ptr val mem) - -// checks -(IsNonNil p) -> (SETNE (TESTQ p p)) -(IsInBounds idx len) -> (SETB (CMPQ idx len)) - -(Move [size] dst src mem) -> (REPMOVSB dst src (Const [size.(int64)]) mem) - -(OffPtr [off] ptr) -> (ADDQconst [off] ptr) - -(Const [val]) && is64BitInt(t) -> (MOVQconst [val]) - -// block rewrites -(BlockIf (SETL cmp) yes no) -> (BlockLT cmp yes no) -(BlockIf (SETNE cmp) yes no) -> (BlockNE cmp yes no) -(BlockIf (SETB cmp) yes no) -> (BlockULT cmp yes no) -(BlockIf cond yes no) && cond.Op == OpMOVBload -> (BlockNE (TESTB cond cond) yes no) - -// Rules below here apply some simple optimizations after lowering. -// TODO: Should this be a separate pass? - -// global loads/stores -(Global [sym]) -> (LEAQglobal [GlobalOffset{sym,0}]) - -// fold constants into instructions -(ADDQ x (MOVQconst [c])) -> (ADDQconst [c] x) // TODO: restrict c to int32 range? -(ADDQ (MOVQconst [c]) x) -> (ADDQconst [c] x) -(SUBQ x (MOVQconst [c])) -> (SUBQconst x [c]) -(SUBQ (MOVQconst [c]) x) -> (NEGQ (SUBQconst x [c])) -(MULQ x (MOVQconst [c])) && c.(int64) == int64(int32(c.(int64))) -> (MULQconst [c] x) -(MULQ (MOVQconst [c]) x) -> (MULQconst [c] x) -(SHLQ x (MOVQconst [c])) -> (SHLQconst [c] x) -(CMPQ x (MOVQconst [c])) -> (CMPQconst x [c]) -(CMPQ (MOVQconst [c]) x) -> (InvertFlags (CMPQconst x [c])) - -// strength reduction -// TODO: do this a lot more generically -(MULQconst [c] x) && c.(int64) == 8 -> (SHLQconst [int64(3)] x) -(MULQconst [c] x) && c.(int64) == 64 -> (SHLQconst [int64(5)] x) - -// fold add/shift into leaq -(ADDQ x (SHLQconst [shift] y)) && shift.(int64) == 3 -> (LEAQ8 [int64(0)] x y) -(ADDQconst [c] (LEAQ8 [d] x y)) -> (LEAQ8 [addOff(c, d)] x y) - -// reverse ordering of compare instruction -(SETL (InvertFlags x)) -> (SETG x) -(SETG (InvertFlags x)) -> (SETL x) - -// fold constants into memory operations -// Note that this is not always a good idea because if not all the uses of -// the ADDQconst get eliminated, we still have to compute the ADDQconst and we now -// have potentially two live values (ptr and (ADDQconst [off] ptr)) instead of one. -// Nevertheless, let's do it! -(MOVQload [off1] (ADDQconst [off2] ptr) mem) -> (MOVQload [addOff(off1, off2)] ptr mem) -(MOVQstore [off1] (ADDQconst [off2] ptr) val mem) -> (MOVQstore [addOff(off1, off2)] ptr val mem) - -// indexed loads and stores -(MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) -(MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) - -(MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) -(MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) - -(ADDQconst [off] x) && off.(int64) == 0 -> (Copy x) - -// Absorb InvertFlags into branches. -(BlockLT (InvertFlags cmp) yes no) -> (BlockGT cmp yes no) -(BlockGT (InvertFlags cmp) yes no) -> (BlockLT cmp yes no) -(BlockLE (InvertFlags cmp) yes no) -> (BlockGE cmp yes no) -(BlockGE (InvertFlags cmp) yes no) -> (BlockLE cmp yes no) -(BlockULT (InvertFlags cmp) yes no) -> (BlockUGT cmp yes no) -(BlockUGT (InvertFlags cmp) yes no) -> (BlockULT cmp yes no) -(BlockULE (InvertFlags cmp) yes no) -> (BlockUGE cmp yes no) -(BlockUGE (InvertFlags cmp) yes no) -> (BlockULE cmp yes no) -(BlockEQ (InvertFlags cmp) yes no) -> (BlockEQ cmp yes no) -(BlockNE (InvertFlags cmp) yes no) -> (BlockNE cmp yes no) diff --git a/src/cmd/compile/internal/ssa/rulegen/rulegen.go b/src/cmd/compile/internal/ssa/rulegen/rulegen.go deleted file mode 100644 index b0916fa4d2..0000000000 --- a/src/cmd/compile/internal/ssa/rulegen/rulegen.go +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright 2015 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. - -// This program generates Go code that applies rewrite rules to a Value. -// The generated code implements a function of type func (v *Value) bool -// which returns true iff if did something. -// Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html - -// Run with something like "go run rulegen.go lower_amd64.rules lowerBlockAmd64 lowerValueAmd64 lowerAmd64.go" - -package main - -import ( - "bufio" - "bytes" - "crypto/md5" - "fmt" - "go/format" - "io" - "io/ioutil" - "log" - "os" - "sort" - "strings" -) - -// rule syntax: -// sexpr [&& extra conditions] -> sexpr -// -// sexpr are s-expressions (lisp-like parenthesized groupings) -// sexpr ::= (opcode sexpr*) -// | variable -// | [aux] -// | -// | {code} -// -// aux ::= variable | {code} -// type ::= variable | {code} -// variable ::= some token -// opcode ::= one of the opcodes from ../op.go (without the Op prefix) - -// extra conditions is just a chunk of Go that evaluates to a boolean. It may use -// variables declared in the matching sexpr. The variable "v" is predefined to be -// the value matched by the entire rule. - -// If multiple rules match, the first one in file order is selected. - -func main() { - if len(os.Args) < 4 || len(os.Args) > 5 { - fmt.Printf("usage: go run rulegen.go []") - os.Exit(1) - } - rulefile := os.Args[1] - blockfn := os.Args[2] - valuefn := os.Args[3] - - // Open input file. - text, err := os.Open(rulefile) - if err != nil { - log.Fatalf("can't read rule file: %v", err) - } - - // oprules contains a list of rules for each block and opcode - blockrules := map[string][]string{} - oprules := map[string][]string{} - - // read rule file - scanner := bufio.NewScanner(text) - for scanner.Scan() { - line := scanner.Text() - if i := strings.Index(line, "//"); i >= 0 { - // Remove comments. Note that this isn't string safe, so - // it will truncate lines with // inside strings. Oh well. - line = line[:i] - } - line = strings.TrimSpace(line) - if line == "" { - continue - } - op := strings.Split(line, " ")[0][1:] - if strings.HasPrefix(op, "Block") { - blockrules[op] = append(blockrules[op], line) - } else { - oprules[op] = append(oprules[op], line) - } - } - if err := scanner.Err(); err != nil { - log.Fatalf("scanner failed: %v\n", err) - } - - // Start output buffer, write header. - w := new(bytes.Buffer) - fmt.Fprintf(w, "// autogenerated from %s: do not edit!\n", rulefile) - fmt.Fprintf(w, "// generated with: go run rulegen/rulegen.go %s\n", strings.Join(os.Args[1:], " ")) - fmt.Fprintln(w, "package ssa") - fmt.Fprintf(w, "func %s(v *Value, config *Config) bool {\n", valuefn) - - // generate code for each rule - fmt.Fprintf(w, "switch v.Op {\n") - var ops []string - for op := range oprules { - ops = append(ops, op) - } - sort.Strings(ops) - for _, op := range ops { - fmt.Fprintf(w, "case Op%s:\n", op) - for _, rule := range oprules[op] { - // Note: we use a hash to identify the rule so that its - // identity is invariant to adding/removing rules elsewhere - // in the rules file. This is useful to squash spurious - // diffs that would occur if we used rule index. - rulehash := fmt.Sprintf("%02x", md5.Sum([]byte(rule))) - - // split at -> - s := strings.Split(rule, "->") - if len(s) != 2 { - log.Fatalf("no arrow in rule %s", rule) - } - lhs := strings.TrimSpace(s[0]) - result := strings.TrimSpace(s[1]) - - // split match into matching part and additional condition - match := lhs - cond := "" - if i := strings.Index(match, "&&"); i >= 0 { - cond = strings.TrimSpace(match[i+2:]) - match = strings.TrimSpace(match[:i]) - } - - fmt.Fprintf(w, "// match: %s\n", match) - fmt.Fprintf(w, "// cond: %s\n", cond) - fmt.Fprintf(w, "// result: %s\n", result) - - fail := fmt.Sprintf("{\ngoto end%s\n}\n", rulehash) - - fmt.Fprintf(w, "{\n") - genMatch(w, match, fail) - - if cond != "" { - fmt.Fprintf(w, "if !(%s) %s", cond, fail) - } - - genResult(w, result) - fmt.Fprintf(w, "return true\n") - - fmt.Fprintf(w, "}\n") - fmt.Fprintf(w, "goto end%s\n", rulehash) // use label - fmt.Fprintf(w, "end%s:;\n", rulehash) - } - } - fmt.Fprintf(w, "}\n") - fmt.Fprintf(w, "return false\n") - fmt.Fprintf(w, "}\n") - - // Generate block rewrite function. - fmt.Fprintf(w, "func %s(b *Block) bool {\n", blockfn) - fmt.Fprintf(w, "switch b.Kind {\n") - ops = nil - for op := range blockrules { - ops = append(ops, op) - } - sort.Strings(ops) - for _, op := range ops { - fmt.Fprintf(w, "case %s:\n", op) - for _, rule := range blockrules[op] { - rulehash := fmt.Sprintf("%02x", md5.Sum([]byte(rule))) - // split at -> - s := strings.Split(rule, "->") - if len(s) != 2 { - log.Fatalf("no arrow in rule %s", rule) - } - lhs := strings.TrimSpace(s[0]) - result := strings.TrimSpace(s[1]) - - // split match into matching part and additional condition - match := lhs - cond := "" - if i := strings.Index(match, "&&"); i >= 0 { - cond = strings.TrimSpace(match[i+2:]) - match = strings.TrimSpace(match[:i]) - } - - fmt.Fprintf(w, "// match: %s\n", match) - fmt.Fprintf(w, "// cond: %s\n", cond) - fmt.Fprintf(w, "// result: %s\n", result) - - fail := fmt.Sprintf("{\ngoto end%s\n}\n", rulehash) - - fmt.Fprintf(w, "{\n") - s = split(match[1 : len(match)-1]) // remove parens, then split - - // check match of control value - if s[1] != "nil" { - fmt.Fprintf(w, "v := b.Control\n") - genMatch0(w, s[1], "v", fail, map[string]string{}, false) - } - - // assign successor names - succs := s[2:] - for i, a := range succs { - if a != "_" { - fmt.Fprintf(w, "%s := b.Succs[%d]\n", a, i) - } - } - - if cond != "" { - fmt.Fprintf(w, "if !(%s) %s", cond, fail) - } - - // Rule matches. Generate result. - t := split(result[1 : len(result)-1]) // remove parens, then split - newsuccs := t[2:] - - // Check if newsuccs is a subset of succs. - m := map[string]bool{} - for _, succ := range succs { - if m[succ] { - log.Fatalf("can't have a repeat successor name %s in %s", succ, rule) - } - m[succ] = true - } - for _, succ := range newsuccs { - if !m[succ] { - log.Fatalf("unknown successor %s in %s", succ, rule) - } - delete(m, succ) - } - - // Modify predecessor lists for no-longer-reachable blocks - for succ := range m { - fmt.Fprintf(w, "removePredecessor(b, %s)\n", succ) - } - - fmt.Fprintf(w, "b.Kind = %s\n", t[0]) - if t[1] == "nil" { - fmt.Fprintf(w, "b.Control = nil\n") - } else { - fmt.Fprintf(w, "b.Control = %s\n", genResult0(w, t[1], new(int), false)) - } - if len(newsuccs) < len(succs) { - fmt.Fprintf(w, "b.Succs = b.Succs[:%d]\n", len(newsuccs)) - } - for i, a := range newsuccs { - fmt.Fprintf(w, "b.Succs[%d] = %s\n", i, a) - } - - fmt.Fprintf(w, "return true\n") - - fmt.Fprintf(w, "}\n") - fmt.Fprintf(w, "goto end%s\n", rulehash) // use label - fmt.Fprintf(w, "end%s:;\n", rulehash) - } - } - fmt.Fprintf(w, "}\n") - fmt.Fprintf(w, "return false\n") - fmt.Fprintf(w, "}\n") - - // gofmt result - b := w.Bytes() - b, err = format.Source(b) - if err != nil { - panic(err) - } - - // Write to a file if given, otherwise stdout. - if len(os.Args) >= 5 { - err = ioutil.WriteFile(os.Args[4], b, 0666) - } else { - _, err = os.Stdout.Write(b) - } - if err != nil { - log.Fatalf("can't write output: %v\n", err) - } -} - -func genMatch(w io.Writer, match, fail string) { - genMatch0(w, match, "v", fail, map[string]string{}, true) -} - -func genMatch0(w io.Writer, match, v, fail string, m map[string]string, top bool) { - if match[0] != '(' { - if x, ok := m[match]; ok { - // variable already has a definition. Check whether - // the old definition and the new definition match. - // For example, (add x x). Equality is just pointer equality - // on Values (so cse is important to do before lowering). - fmt.Fprintf(w, "if %s != %s %s", v, x, fail) - return - } - // remember that this variable references the given value - if match == "_" { - return - } - m[match] = v - fmt.Fprintf(w, "%s := %s\n", match, v) - return - } - - // split body up into regions. Split by spaces/tabs, except those - // contained in () or {}. - s := split(match[1 : len(match)-1]) // remove parens, then split - - // check op - if !top { - fmt.Fprintf(w, "if %s.Op != Op%s %s", v, s[0], fail) - } - - // check type/aux/args - argnum := 0 - for _, a := range s[1:] { - if a[0] == '<' { - // type restriction - t := a[1 : len(a)-1] // remove <> - if t[0] == '{' { - // code. We must match the results of this code. - fmt.Fprintf(w, "if %s.Type != %s %s", v, t[1:len(t)-1], fail) - } else { - // variable - if u, ok := m[t]; ok { - // must match previous variable - fmt.Fprintf(w, "if %s.Type != %s %s", v, u, fail) - } else { - m[t] = v + ".Type" - fmt.Fprintf(w, "%s := %s.Type\n", t, v) - } - } - } else if a[0] == '[' { - // aux restriction - x := a[1 : len(a)-1] // remove [] - if x[0] == '{' { - // code - fmt.Fprintf(w, "if %s.Aux != %s %s", v, x[1:len(x)-1], fail) - } else { - // variable - if y, ok := m[x]; ok { - fmt.Fprintf(w, "if %s.Aux != %s %s", v, y, fail) - } else { - m[x] = v + ".Aux" - fmt.Fprintf(w, "%s := %s.Aux\n", x, v) - } - } - } else if a[0] == '{' { - fmt.Fprintf(w, "if %s.Args[%d] != %s %s", v, argnum, a[1:len(a)-1], fail) - argnum++ - } else { - // variable or sexpr - genMatch0(w, a, fmt.Sprintf("%s.Args[%d]", v, argnum), fail, m, false) - argnum++ - } - } -} - -func genResult(w io.Writer, result string) { - genResult0(w, result, new(int), true) -} -func genResult0(w io.Writer, result string, alloc *int, top bool) string { - if result[0] != '(' { - // variable - if top { - fmt.Fprintf(w, "v.Op = %s.Op\n", result) - fmt.Fprintf(w, "v.Aux = %s.Aux\n", result) - fmt.Fprintf(w, "v.resetArgs()\n") - fmt.Fprintf(w, "v.AddArgs(%s.Args...)\n", result) - } - return result - } - - s := split(result[1 : len(result)-1]) // remove parens, then split - var v string - var hasType bool - if top { - v = "v" - fmt.Fprintf(w, "v.Op = Op%s\n", s[0]) - fmt.Fprintf(w, "v.Aux = nil\n") - fmt.Fprintf(w, "v.resetArgs()\n") - hasType = true - } else { - v = fmt.Sprintf("v%d", *alloc) - *alloc++ - fmt.Fprintf(w, "%s := v.Block.NewValue(Op%s, TypeInvalid, nil)\n", v, s[0]) - } - for _, a := range s[1:] { - if a[0] == '<' { - // type restriction - t := a[1 : len(a)-1] // remove <> - if t[0] == '{' { - t = t[1 : len(t)-1] // remove {} - } - fmt.Fprintf(w, "%s.Type = %s\n", v, t) - hasType = true - } else if a[0] == '[' { - // aux restriction - x := a[1 : len(a)-1] // remove [] - if x[0] == '{' { - x = x[1 : len(x)-1] // remove {} - } - fmt.Fprintf(w, "%s.Aux = %s\n", v, x) - } else if a[0] == '{' { - fmt.Fprintf(w, "%s.AddArg(%s)\n", v, a[1:len(a)-1]) - } else { - // regular argument (sexpr or variable) - x := genResult0(w, a, alloc, false) - fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x) - } - } - if !hasType { - log.Fatalf("sub-expression %s must have a type", result) - } - return v -} - -func split(s string) []string { - var r []string - -outer: - for s != "" { - d := 0 // depth of ({[< - var open, close byte // opening and closing markers ({[< or )}]> - nonsp := false // found a non-space char so far - for i := 0; i < len(s); i++ { - switch { - case d == 0 && s[i] == '(': - open, close = '(', ')' - d++ - case d == 0 && s[i] == '<': - open, close = '<', '>' - d++ - case d == 0 && s[i] == '[': - open, close = '[', ']' - d++ - case d == 0 && s[i] == '{': - open, close = '{', '}' - d++ - case d == 0 && (s[i] == ' ' || s[i] == '\t'): - if nonsp { - r = append(r, strings.TrimSpace(s[:i])) - s = s[i:] - continue outer - } - case d > 0 && s[i] == open: - d++ - case d > 0 && s[i] == close: - d-- - default: - nonsp = true - } - } - if d != 0 { - panic("imbalanced expression: " + s) - } - if nonsp { - r = append(r, strings.TrimSpace(s)) - } - break - } - return r -} diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index dd55d96ccc..a4ce343b5d 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -91,12 +91,12 @@ func stackalloc(f *Func) { } // TODO: do this with arch-specific rewrite rules somehow? switch v.Op { - case OpADDQ: + case OpAMD64ADDQ: // (ADDQ (FP) x) -> (LEAQ [n] (SP) x) - v.Op = OpLEAQ + v.Op = OpAMD64LEAQ v.Aux = n - case OpLEAQ, OpMOVQload, OpMOVQstore, OpMOVBload, OpMOVQloadidx8: - if v.Op == OpMOVQloadidx8 && i == 1 { + case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVBload, OpAMD64MOVQloadidx8: + if v.Op == OpAMD64MOVQloadidx8 && i == 1 { // Note: we could do it, but it is probably an error log.Panicf("can't do FP->SP adjust on index slot of load %s", v.Op) } @@ -104,6 +104,7 @@ func stackalloc(f *Func) { v.Aux = addOffset(v.Aux.(int64), n) default: log.Panicf("can't do FP->SP adjust on %s", v.Op) + // TODO: OpCopy -> ADDQ } } } diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index dab6239dee..08e368ab04 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -4,10 +4,7 @@ package ssa -import ( - "fmt" - "strings" -) +import "fmt" // A Value represents a value in the SSA representation of the program. // The ID and Type fields must not be modified. The remainder may be modified @@ -51,7 +48,7 @@ func (v *Value) String() string { // long form print. v# = opcode [aux] args [: reg] func (v *Value) LongString() string { - s := fmt.Sprintf("v%d = %s", v.ID, strings.TrimPrefix(v.Op.String(), "Op")) + s := fmt.Sprintf("v%d = %s", v.ID, v.Op.String()) s += " <" + v.Type.String() + ">" if v.Aux != nil { s += fmt.Sprintf(" [%v]", v.Aux) -- cgit v1.3 From 6f1884757f26f4906d71e2465a2238c80245c323 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Wed, 10 Jun 2015 10:39:57 -0700 Subject: [dev.ssa] cmd/compile/internal/ssa: Complete 64-bit shifts Implement correct Go shifts. Allow multi-line rewrite rules. Fix offset & alignment in stack alloc. Change-Id: I0ae9e522c83df9205bbe4ab94bc0e43d16dace58 Reviewed-on: https://go-review.googlesource.com/10891 Reviewed-by: Keith Randall --- src/cmd/compile/internal/amd64/prog.go | 2 + src/cmd/compile/internal/gc/ssa.go | 140 ++++++++++++ src/cmd/compile/internal/gc/type.go | 5 + src/cmd/compile/internal/ssa/TODO | 16 +- src/cmd/compile/internal/ssa/gen/AMD64.rules | 38 +++- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 22 +- src/cmd/compile/internal/ssa/gen/rulegen.go | 38 +++- src/cmd/compile/internal/ssa/opGen.go | 109 ++++++++++ src/cmd/compile/internal/ssa/rewriteAMD64.go | 305 ++++++++++++++++++++++++++- src/cmd/compile/internal/ssa/shift_test.go | 42 ++++ src/cmd/compile/internal/ssa/stackalloc.go | 17 +- src/cmd/compile/internal/ssa/type.go | 47 +++-- 12 files changed, 720 insertions(+), 61 deletions(-) create mode 100644 src/cmd/compile/internal/ssa/shift_test.go (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/amd64/prog.go b/src/cmd/compile/internal/amd64/prog.go index 00918c8691..97f7241fbd 100644 --- a/src/cmd/compile/internal/amd64/prog.go +++ b/src/cmd/compile/internal/amd64/prog.go @@ -57,6 +57,8 @@ var progtable = [x86.ALAST]obj.ProgInfo{ x86.ACWD: {gc.OK, AX, AX | DX, 0}, x86.ACLD: {gc.OK, 0, 0, 0}, x86.ASTD: {gc.OK, 0, 0, 0}, + x86.ACMOVQCC: {gc.SizeQ | gc.LeftRead | gc.RightRead | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ACMOVQCS: {gc.SizeQ | gc.LeftRead | gc.RightRead | gc.RightWrite | gc.UseCarry, 0, 0, 0}, x86.ACMPB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, x86.ACMPL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, x86.ACMPQ: {gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index fd47c54ad0..fcef7d3b81 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -755,6 +755,63 @@ func genValue(v *ssa.Value) { p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = r + case ssa.OpAMD64SHLQ: + x := regnum(v.Args[0]) + r := regnum(v) + if x != r { + if r == x86.REG_CX { + log.Fatalf("can't implement %s, target and shift both in CX", v.LongString()) + } + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + p := Prog(x86.ASHLQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = regnum(v.Args[1]) // should be CX + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpAMD64SHRQ: + x := regnum(v.Args[0]) + r := regnum(v) + if x != r { + if r == x86.REG_CX { + log.Fatalf("can't implement %s, target and shift both in CX", v.LongString()) + } + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + p := Prog(x86.ASHRQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = regnum(v.Args[1]) // should be CX + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpAMD64SARQ: + x := regnum(v.Args[0]) + r := regnum(v) + if x != r { + if r == x86.REG_CX { + log.Fatalf("can't implement %s, target and shift both in CX", v.LongString()) + } + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + p := Prog(x86.ASARQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = regnum(v.Args[1]) // should be CX + p.To.Type = obj.TYPE_REG + p.To.Reg = r case ssa.OpAMD64SHLQconst: x := regnum(v.Args[0]) r := regnum(v) @@ -771,6 +828,89 @@ func genValue(v *ssa.Value) { p.From.Offset = v.Aux.(int64) p.To.Type = obj.TYPE_REG p.To.Reg = r + case ssa.OpAMD64SHRQconst: + x := regnum(v.Args[0]) + r := regnum(v) + if x != r { + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + p := Prog(x86.ASHRQ) + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.Aux.(int64) + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpAMD64SARQconst: + x := regnum(v.Args[0]) + r := regnum(v) + if x != r { + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + p := Prog(x86.ASARQ) + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.Aux.(int64) + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpAMD64SBBQcarrymask: + r := regnum(v) + p := Prog(x86.ASBBQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = r + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpAMD64CMOVQCC: + r := regnum(v) + x := regnum(v.Args[1]) + y := regnum(v.Args[2]) + if x != r && y != r { + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + var p *obj.Prog + if x == r { + p = Prog(x86.ACMOVQCS) + p.From.Reg = y + } else { + p = Prog(x86.ACMOVQCC) + p.From.Reg = x + } + p.From.Type = obj.TYPE_REG + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpAMD64ANDQ: + r := regnum(v) + x := regnum(v.Args[0]) + y := regnum(v.Args[1]) + if x != r && y != r { + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + p := Prog(x86.AANDQ) + p.From.Type = obj.TYPE_REG + p.To.Type = obj.TYPE_REG + p.To.Reg = r + if x == r { + p.From.Reg = y + } else { + p.From.Reg = x + } case ssa.OpAMD64LEAQ: p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index 0ed07ee90a..1417bfc196 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -18,6 +18,11 @@ func (t *Type) Size() int64 { return t.Width } +func (t *Type) Alignment() int64 { + dowidth(t) + return int64(t.Align) +} + func (t *Type) IsBoolean() bool { return t.Etype == TBOOL } diff --git a/src/cmd/compile/internal/ssa/TODO b/src/cmd/compile/internal/ssa/TODO index d5e8788e36..e9b7553534 100644 --- a/src/cmd/compile/internal/ssa/TODO +++ b/src/cmd/compile/internal/ssa/TODO @@ -20,14 +20,6 @@ Values If not that, then cache the interfaces that wrap int64s. - OpStore uses 3 args. Increase the size of argstorage to 3? -Opcodes - - Rename ops to prevent cross-arch conflicts. MOVQ -> MOVQamd64 (or - MOVQ6?). Other option: build opcode table in Config instead of globally. - - It's annoying to list the opcode both in the opcode list and an - opInfo map entry. Specify it one place and use go:generate to - produce both? - - Write barriers - Regalloc - Make less arch-dependent - Don't spill everything at every basic block boundary. @@ -38,7 +30,6 @@ Regalloc Rewrites - Strength reduction (both arch-indep and arch-dependent?) - - Code sequence for shifts >= wordsize - Start another architecture (arm?) - 64-bit ops on 32-bit machines - x y) && (is64BitInt(t) || isPtr(t)) -> (ADDQ x y) (Add x y) && is32BitInt(t) -> (ADDL x y) - (Sub x y) && is64BitInt(t) -> (SUBQ x y) - (Mul x y) && is64BitInt(t) -> (MULQ x y) -(Lsh x y) && is64BitInt(t) -> (SHLQ x y) // TODO: check y>63 + +// Lowering shifts +// Note: unsigned shifts need to return 0 if shift amount is >= 64. +// mask = shift >= 64 ? 0 : 0xffffffffffffffff +// result = mask & arg << shift +(Lsh x y) && is64BitInt(t) -> + (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) +(Rsh x y) && is64BitInt(t) && !t.IsSigned() -> + (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) + +// Note: signed right shift needs to return 0/-1 if shift amount is >= 64. +// if shift > 63 { shift = 63 } +// result = arg >> shift +(Rsh x y) && is64BitInt(t) && t.IsSigned() -> + (SARQ x (CMOVQCC + (CMPQconst [int64(64)] y) + (Const [int64(63)]) + y)) + (Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y)) (Load ptr mem) && t.IsBoolean() -> (MOVBload [int64(0)] ptr mem) @@ -56,7 +74,11 @@ (SUBQ (MOVQconst [c]) x) -> (NEGQ (SUBQconst x [c])) (MULQ x (MOVQconst [c])) && c.(int64) == int64(int32(c.(int64))) -> (MULQconst [c] x) (MULQ (MOVQconst [c]) x) -> (MULQconst [c] x) +(ANDQ x (MOVQconst [c])) -> (ANDQconst [c] x) +(ANDQ (MOVQconst [c]) x) -> (ANDQconst [c] x) (SHLQ x (MOVQconst [c])) -> (SHLQconst [c] x) +(SHRQ x (MOVQconst [c])) -> (SHRQconst [c] x) +(SARQ x (MOVQconst [c])) -> (SARQconst [c] x) (CMPQ x (MOVQconst [c])) -> (CMPQconst x [c]) (CMPQ (MOVQconst [c]) x) -> (InvertFlags (CMPQconst x [c])) @@ -101,3 +123,11 @@ (UGE (InvertFlags cmp) yes no) -> (ULE cmp yes no) (EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no) (NE (InvertFlags cmp) yes no) -> (NE cmp yes no) + +// get rid of >=64 code for constant shifts +(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && inBounds(d.(int64), c.(int64)) -> (Const [int64(-1)]) +(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && !inBounds(d.(int64), c.(int64)) -> (Const [int64(0)]) +(ANDQconst [c] _) && c.(int64) == 0 -> (MOVQconst [int64(0)]) +(ANDQconst [c] x) && c.(int64) == -1 -> (Copy x) +(CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) && inBounds(d.(int64), c.(int64)) -> (Copy x) +(CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) && !inBounds(d.(int64), c.(int64)) -> (Copy x) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index b3b2e3b5e2..8bb22d270d 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -72,17 +72,20 @@ func init() { gp := buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15") gpsp := gp | buildReg("SP FP") + flags := buildReg("FLAGS") gp01 := regInfo{[]regMask{}, 0, []regMask{gp}} gp11 := regInfo{[]regMask{gpsp}, 0, []regMask{gp}} gp21 := regInfo{[]regMask{gpsp, gpsp}, 0, []regMask{gp}} gp21shift := regInfo{[]regMask{gpsp, buildReg("CX")}, 0, []regMask{gp}} - gp2flags := regInfo{[]regMask{gpsp, gpsp}, 0, []regMask{buildReg("FLAGS")}} - gp1flags := regInfo{[]regMask{gpsp}, 0, []regMask{buildReg("FLAGS")}} + gp2flags := regInfo{[]regMask{gpsp, gpsp}, 0, []regMask{flags}} + gp1flags := regInfo{[]regMask{gpsp}, 0, []regMask{flags}} + flagsgp1 := regInfo{[]regMask{flags}, 0, []regMask{gp}} gpload := regInfo{[]regMask{gpsp, 0}, 0, []regMask{gp}} gploadidx := regInfo{[]regMask{gpsp, gpsp, 0}, 0, []regMask{gp}} gpstore := regInfo{[]regMask{gpsp, gpsp, 0}, 0, nil} gpstoreidx := regInfo{[]regMask{gpsp, gpsp, gpsp, 0}, 0, nil} - flagsgp := regInfo{[]regMask{buildReg("FLAGS")}, 0, []regMask{gp}} + flagsgp := regInfo{[]regMask{flags}, 0, []regMask{gp}} + cmov := regInfo{[]regMask{flags, gp, gp}, 0, []regMask{gp}} // Suffixes encode the bit width of various instructions. // Q = 64 bit, L = 32 bit, W = 16 bit, B = 8 bit @@ -95,15 +98,24 @@ func init() { {name: "SUBQconst", reg: gp11}, // arg0 - aux.(int64) {name: "MULQ", reg: gp21}, // arg0 * arg1 {name: "MULQconst", reg: gp11}, // arg0 * aux.(int64) + {name: "ANDQ", reg: gp21}, // arg0 & arg1 + {name: "ANDQconst", reg: gp11}, // arg0 & aux.(int64) {name: "SHLQ", reg: gp21shift}, // arg0 << arg1, shift amount is mod 64 {name: "SHLQconst", reg: gp11}, // arg0 << aux.(int64), shift amount 0-63 - {name: "NEGQ", reg: gp11}, // -arg0 + {name: "SHRQ", reg: gp21shift}, // unsigned arg0 >> arg1, shift amount is mod 64 + {name: "SHRQconst", reg: gp11}, // unsigned arg0 >> aux.(int64), shift amount 0-63 + {name: "SARQ", reg: gp21shift}, // signed arg0 >> arg1, shift amount is mod 64 + {name: "SARQconst", reg: gp11}, // signed arg0 >> aux.(int64), shift amount 0-63 + + {name: "NEGQ", reg: gp11}, // -arg0 {name: "CMPQ", reg: gp2flags}, // arg0 compare to arg1 {name: "CMPQconst", reg: gp1flags}, // arg0 compare to aux.(int64) {name: "TESTQ", reg: gp2flags}, // (arg0 & arg1) compare to 0 {name: "TESTB", reg: gp2flags}, // (arg0 & arg1) compare to 0 + {name: "SBBQcarrymask", reg: flagsgp1}, // (int64)(-1) if carry is set, 0 if carry is clear. + {name: "SETEQ", reg: flagsgp}, // extract == condition from arg0 {name: "SETNE", reg: flagsgp}, // extract != condition from arg0 {name: "SETL", reg: flagsgp}, // extract signed < condition from arg0 @@ -111,6 +123,8 @@ func init() { {name: "SETGE", reg: flagsgp}, // extract signed >= condition from arg0 {name: "SETB", reg: flagsgp}, // extract unsigned < condition from arg0 + {name: "CMOVQCC", reg: cmov}, // carry clear + {name: "MOVQconst", reg: gp01}, // aux.(int64) {name: "LEAQ", reg: gp21}, // arg0 + arg1 + aux.(int64) {name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + aux.(int64) diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index 441e08ab5d..4f689199a0 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -57,6 +57,7 @@ func genRules(arch arch) { // read rule file scanner := bufio.NewScanner(text) + rule := "" for scanner.Scan() { line := scanner.Text() if i := strings.Index(line, "//"); i >= 0 { @@ -64,16 +65,27 @@ func genRules(arch arch) { // it will truncate lines with // inside strings. Oh well. line = line[:i] } - line = strings.TrimSpace(line) - if line == "" { + rule += " " + line + rule = strings.TrimSpace(rule) + if rule == "" { continue } - op := strings.Split(line, " ")[0][1:] + if !strings.Contains(rule, "->") { + continue + } + if strings.HasSuffix(rule, "->") { + continue + } + if unbalanced(rule) { + continue + } + op := strings.Split(rule, " ")[0][1:] if isBlock(op, arch) { - blockrules[op] = append(blockrules[op], line) + blockrules[op] = append(blockrules[op], rule) } else { - oprules[op] = append(oprules[op], line) + oprules[op] = append(oprules[op], rule) } + rule = "" } if err := scanner.Err(); err != nil { log.Fatalf("scanner failed: %v\n", err) @@ -105,7 +117,7 @@ func genRules(arch arch) { // split at -> s := strings.Split(rule, "->") if len(s) != 2 { - log.Fatalf("no arrow in rule %s", rule) + log.Fatalf("rule must contain exactly one arrow: %s", rule) } lhs := strings.TrimSpace(s[0]) result := strings.TrimSpace(s[1]) @@ -478,3 +490,17 @@ func blockName(name string, arch arch) string { } return "Block" + arch.name + name } + +// unbalanced returns true if there aren't the same number of ( and ) in the string. +func unbalanced(s string) bool { + var left, right int + for _, c := range s { + if c == '(' { + left++ + } + if c == ')' { + right++ + } + } + return left != right +} diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 550f641c14..a18f0c748b 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -53,19 +53,27 @@ const ( OpAMD64SUBQconst OpAMD64MULQ OpAMD64MULQconst + OpAMD64ANDQ + OpAMD64ANDQconst OpAMD64SHLQ OpAMD64SHLQconst + OpAMD64SHRQ + OpAMD64SHRQconst + OpAMD64SARQ + OpAMD64SARQconst OpAMD64NEGQ OpAMD64CMPQ OpAMD64CMPQconst OpAMD64TESTQ OpAMD64TESTB + OpAMD64SBBQcarrymask OpAMD64SETEQ OpAMD64SETNE OpAMD64SETL OpAMD64SETG OpAMD64SETGE OpAMD64SETB + OpAMD64CMOVQCC OpAMD64MOVQconst OpAMD64LEAQ OpAMD64LEAQ2 @@ -204,6 +212,31 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ANDQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "ANDQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, { name: "SHLQ", reg: regInfo{ @@ -229,6 +262,56 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "SHRQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 2, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SHRQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SARQ", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + 2, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, + { + name: "SARQconst", + reg: regInfo{ + inputs: []regMask{ + 4295032831, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, { name: "NEGQ", reg: regInfo{ @@ -292,6 +375,18 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "SBBQcarrymask", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, { name: "SETEQ", reg: regInfo{ @@ -364,6 +459,20 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "CMOVQCC", + reg: regInfo{ + inputs: []regMask{ + 8589934592, + 65519, + 65519, + }, + clobbers: 0, + outputs: []regMask{ + 65519, + }, + }, + }, { name: "MOVQconst", reg: regInfo{ diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 542dad4500..f57cf7f333 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -108,6 +108,81 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endfa1c7cc5ac4716697e891376787f86ce endfa1c7cc5ac4716697e891376787f86ce: ; + case OpAMD64ANDQ: + // match: (ANDQ x (MOVQconst [c])) + // cond: + // result: (ANDQconst [c] x) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto endb98096e3bbb90933e39c88bf41c688a9 + } + c := v.Args[1].Aux + v.Op = OpAMD64ANDQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto endb98096e3bbb90933e39c88bf41c688a9 + endb98096e3bbb90933e39c88bf41c688a9: + ; + // match: (ANDQ (MOVQconst [c]) x) + // cond: + // result: (ANDQconst [c] x) + { + if v.Args[0].Op != OpAMD64MOVQconst { + goto endd313fd1897a0d2bc79eff70159a81b6b + } + c := v.Args[0].Aux + x := v.Args[1] + v.Op = OpAMD64ANDQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto endd313fd1897a0d2bc79eff70159a81b6b + endd313fd1897a0d2bc79eff70159a81b6b: + ; + case OpAMD64ANDQconst: + // match: (ANDQconst [c] _) + // cond: c.(int64) == 0 + // result: (MOVQconst [int64(0)]) + { + c := v.Aux + if !(c.(int64) == 0) { + goto end383ada81cd8ffa88918387cd221acf5c + } + v.Op = OpAMD64MOVQconst + v.Aux = nil + v.resetArgs() + v.Aux = int64(0) + return true + } + goto end383ada81cd8ffa88918387cd221acf5c + end383ada81cd8ffa88918387cd221acf5c: + ; + // match: (ANDQconst [c] x) + // cond: c.(int64) == -1 + // result: (Copy x) + { + c := v.Aux + x := v.Args[0] + if !(c.(int64) == -1) { + goto end90aef368f20963a6ba27b3e9317ccf03 + } + v.Op = OpCopy + v.Aux = nil + v.resetArgs() + v.AddArg(x) + return true + } + goto end90aef368f20963a6ba27b3e9317ccf03 + end90aef368f20963a6ba27b3e9317ccf03: + ; case OpAdd: // match: (Add x y) // cond: (is64BitInt(t) || isPtr(t)) @@ -149,6 +224,57 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end35a02a1587264e40cf1055856ff8445a end35a02a1587264e40cf1055856ff8445a: ; + case OpAMD64CMOVQCC: + // match: (CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) + // cond: inBounds(d.(int64), c.(int64)) + // result: (Copy x) + { + if v.Args[0].Op != OpAMD64CMPQconst { + goto endb8f4f98b06c41e559bf0323e798c147a + } + c := v.Args[0].Aux + if v.Args[0].Args[0].Op != OpAMD64MOVQconst { + goto endb8f4f98b06c41e559bf0323e798c147a + } + d := v.Args[0].Args[0].Aux + x := v.Args[2] + if !(inBounds(d.(int64), c.(int64))) { + goto endb8f4f98b06c41e559bf0323e798c147a + } + v.Op = OpCopy + v.Aux = nil + v.resetArgs() + v.AddArg(x) + return true + } + goto endb8f4f98b06c41e559bf0323e798c147a + endb8f4f98b06c41e559bf0323e798c147a: + ; + // match: (CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) + // cond: !inBounds(d.(int64), c.(int64)) + // result: (Copy x) + { + if v.Args[0].Op != OpAMD64CMPQconst { + goto end29407b5c4731ac24b4c25600752cb895 + } + c := v.Args[0].Aux + if v.Args[0].Args[0].Op != OpAMD64MOVQconst { + goto end29407b5c4731ac24b4c25600752cb895 + } + d := v.Args[0].Args[0].Aux + x := v.Args[1] + if !(!inBounds(d.(int64), c.(int64))) { + goto end29407b5c4731ac24b4c25600752cb895 + } + v.Op = OpCopy + v.Aux = nil + v.resetArgs() + v.AddArg(x) + return true + } + goto end29407b5c4731ac24b4c25600752cb895 + end29407b5c4731ac24b4c25600752cb895: + ; case OpAMD64CMPQ: // match: (CMPQ x (MOVQconst [c])) // cond: @@ -352,23 +478,34 @@ func rewriteValueAMD64(v *Value, config *Config) bool { case OpLsh: // match: (Lsh x y) // cond: is64BitInt(t) - // result: (SHLQ x y) + // result: (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) { t := v.Type x := v.Args[0] y := v.Args[1] if !(is64BitInt(t)) { - goto end9f05c9539e51db6ad557989e0c822e9b + goto end7002b6d4becf7d1247e3756641ccb0c2 } - v.Op = OpAMD64SHLQ + v.Op = OpAMD64ANDQ v.Aux = nil v.resetArgs() - v.AddArg(x) - v.AddArg(y) + v0 := v.Block.NewValue(v.Line, OpAMD64SHLQ, TypeInvalid, nil) + v0.Type = t + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + v1 := v.Block.NewValue(v.Line, OpAMD64SBBQcarrymask, TypeInvalid, nil) + v1.Type = t + v2 := v.Block.NewValue(v.Line, OpAMD64CMPQconst, TypeInvalid, nil) + v2.Type = TypeFlags + v2.Aux = int64(64) + v2.AddArg(y) + v1.AddArg(v2) + v.AddArg(v1) return true } - goto end9f05c9539e51db6ad557989e0c822e9b - end9f05c9539e51db6ad557989e0c822e9b: + goto end7002b6d4becf7d1247e3756641ccb0c2 + end7002b6d4becf7d1247e3756641ccb0c2: ; case OpAMD64MOVQload: // match: (MOVQload [off1] (ADDQconst [off2] ptr) mem) @@ -663,6 +800,140 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end0429f947ee7ac49ff45a243e461a5290 end0429f947ee7ac49ff45a243e461a5290: ; + case OpRsh: + // match: (Rsh x y) + // cond: is64BitInt(t) && !t.IsSigned() + // result: (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is64BitInt(t) && !t.IsSigned()) { + goto end9463ddaa21c75f8e15cb9f31472a2e23 + } + v.Op = OpAMD64ANDQ + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue(v.Line, OpAMD64SHRQ, TypeInvalid, nil) + v0.Type = t + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + v1 := v.Block.NewValue(v.Line, OpAMD64SBBQcarrymask, TypeInvalid, nil) + v1.Type = t + v2 := v.Block.NewValue(v.Line, OpAMD64CMPQconst, TypeInvalid, nil) + v2.Type = TypeFlags + v2.Aux = int64(64) + v2.AddArg(y) + v1.AddArg(v2) + v.AddArg(v1) + return true + } + goto end9463ddaa21c75f8e15cb9f31472a2e23 + end9463ddaa21c75f8e15cb9f31472a2e23: + ; + // match: (Rsh x y) + // cond: is64BitInt(t) && t.IsSigned() + // result: (SARQ x (CMOVQCC (CMPQconst [int64(64)] y) (Const [int64(63)]) y)) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is64BitInt(t) && t.IsSigned()) { + goto endd297b9e569ac90bf815bd4c425d3b770 + } + v.Op = OpAMD64SARQ + v.Aux = nil + v.resetArgs() + v.Type = t + v.AddArg(x) + v0 := v.Block.NewValue(v.Line, OpAMD64CMOVQCC, TypeInvalid, nil) + v0.Type = t + v1 := v.Block.NewValue(v.Line, OpAMD64CMPQconst, TypeInvalid, nil) + v1.Type = TypeFlags + v1.Aux = int64(64) + v1.AddArg(y) + v0.AddArg(v1) + v2 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) + v2.Type = t + v2.Aux = int64(63) + v0.AddArg(v2) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto endd297b9e569ac90bf815bd4c425d3b770 + endd297b9e569ac90bf815bd4c425d3b770: + ; + case OpAMD64SARQ: + // match: (SARQ x (MOVQconst [c])) + // cond: + // result: (SARQconst [c] x) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto end031712b4008075e25a5827dcb8dd3ebb + } + c := v.Args[1].Aux + v.Op = OpAMD64SARQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto end031712b4008075e25a5827dcb8dd3ebb + end031712b4008075e25a5827dcb8dd3ebb: + ; + case OpAMD64SBBQcarrymask: + // match: (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) + // cond: inBounds(d.(int64), c.(int64)) + // result: (Const [int64(-1)]) + { + if v.Args[0].Op != OpAMD64CMPQconst { + goto end35e369f67ebb9423a1d36a808a16777c + } + c := v.Args[0].Aux + if v.Args[0].Args[0].Op != OpAMD64MOVQconst { + goto end35e369f67ebb9423a1d36a808a16777c + } + d := v.Args[0].Args[0].Aux + if !(inBounds(d.(int64), c.(int64))) { + goto end35e369f67ebb9423a1d36a808a16777c + } + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = int64(-1) + return true + } + goto end35e369f67ebb9423a1d36a808a16777c + end35e369f67ebb9423a1d36a808a16777c: + ; + // match: (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) + // cond: !inBounds(d.(int64), c.(int64)) + // result: (Const [int64(0)]) + { + if v.Args[0].Op != OpAMD64CMPQconst { + goto end5c767fada028c1cc96210af2cf098aff + } + c := v.Args[0].Aux + if v.Args[0].Args[0].Op != OpAMD64MOVQconst { + goto end5c767fada028c1cc96210af2cf098aff + } + d := v.Args[0].Args[0].Aux + if !(!inBounds(d.(int64), c.(int64))) { + goto end5c767fada028c1cc96210af2cf098aff + } + v.Op = OpConst + v.Aux = nil + v.resetArgs() + v.Aux = int64(0) + return true + } + goto end5c767fada028c1cc96210af2cf098aff + end5c767fada028c1cc96210af2cf098aff: + ; case OpAMD64SETG: // match: (SETG (InvertFlags x)) // cond: @@ -719,6 +990,26 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endcca412bead06dc3d56ef034a82d184d6 endcca412bead06dc3d56ef034a82d184d6: ; + case OpAMD64SHRQ: + // match: (SHRQ x (MOVQconst [c])) + // cond: + // result: (SHRQconst [c] x) + { + x := v.Args[0] + if v.Args[1].Op != OpAMD64MOVQconst { + goto endbb0d3a04dd2b810cb3dbdf7ef665f22b + } + c := v.Args[1].Aux + v.Op = OpAMD64SHRQconst + v.Aux = nil + v.resetArgs() + v.Aux = c + v.AddArg(x) + return true + } + goto endbb0d3a04dd2b810cb3dbdf7ef665f22b + endbb0d3a04dd2b810cb3dbdf7ef665f22b: + ; case OpAMD64SUBQ: // match: (SUBQ x (MOVQconst [c])) // cond: diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go new file mode 100644 index 0000000000..bba4f782dc --- /dev/null +++ b/src/cmd/compile/internal/ssa/shift_test.go @@ -0,0 +1,42 @@ +// Copyright 2015 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. + +package ssa + +import ( + "testing" +) + +func TestShiftConstAMD64(t *testing.T) { + c := NewConfig("amd64", DummyFrontend{}) + fun := makeConstShiftFunc(c, 18, OpLsh, TypeUInt64) + checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) + fun = makeConstShiftFunc(c, 66, OpLsh, TypeUInt64) + checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 0, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) + fun = makeConstShiftFunc(c, 18, OpRsh, TypeUInt64) + checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHRQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) + fun = makeConstShiftFunc(c, 66, OpRsh, TypeUInt64) + checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHRQconst: 0, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) + fun = makeConstShiftFunc(c, 18, OpRsh, TypeInt64) + checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SARQconst: 1, OpAMD64CMPQconst: 0}) + fun = makeConstShiftFunc(c, 66, OpRsh, TypeInt64) + checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SARQconst: 1, OpAMD64CMPQconst: 0}) +} + +func makeConstShiftFunc(c *Config, amount int64, op Op, typ Type) fun { + ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"} + fun := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpArg, TypeMem, ".mem"), + Valu("FP", OpFP, TypeUInt64, nil), + Valu("argptr", OpOffPtr, ptyp, int64(8), "FP"), + Valu("resptr", OpOffPtr, ptyp, int64(16), "FP"), + Valu("load", OpLoad, typ, nil, "argptr", "mem"), + Valu("c", OpConst, TypeUInt64, amount), + Valu("shift", op, typ, nil, "load", "c"), + Valu("store", OpStore, TypeMem, nil, "resptr", "shift", "mem"), + Exit("store"))) + Compile(fun.f) + return fun +} diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index a4ce343b5d..d47c8c7b02 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -33,10 +33,9 @@ func stackalloc(f *Func) { if v.Type.IsMemory() { // TODO: only "regallocable" types continue } - n += v.Type.Size() - // a := v.Type.Align() - // n = (n + a - 1) / a * a TODO + n = align(n, v.Type.Alignment()) loc := &LocalSlot{n} + n += v.Type.Size() home = setloc(home, v, loc) for _, w := range v.Args { home = setloc(home, w, loc) @@ -60,15 +59,14 @@ func stackalloc(f *Func) { if len(v.Args) == 1 && (v.Args[0].Op == OpFP || v.Args[0].Op == OpSP || v.Args[0].Op == OpGlobal) { continue } - // a := v.Type.Align() - // n = (n + a - 1) / a * a TODO - n += v.Type.Size() + n = align(n, v.Type.Alignment()) loc := &LocalSlot{n} + n += v.Type.Size() home = setloc(home, v, loc) } } - // TODO: align n + n = align(n, f.Config.ptrSize) n += f.Config.ptrSize // space for return address. TODO: arch-dependent f.RegAlloc = home f.FrameSize = n @@ -114,3 +112,8 @@ func stackalloc(f *Func) { home[fp.ID] = ®isters[4] // TODO: arch-dependent } } + +// align increases n to the next multiple of a. a must be a power of 2. +func align(n int64, a int64) int64 { + return (n + a - 1) &^ (a - 1) +} diff --git a/src/cmd/compile/internal/ssa/type.go b/src/cmd/compile/internal/ssa/type.go index 1a61c75afa..e271131a40 100644 --- a/src/cmd/compile/internal/ssa/type.go +++ b/src/cmd/compile/internal/ssa/type.go @@ -10,6 +10,7 @@ package ssa // Type instances are not guaranteed to be canonical. type Type interface { Size() int64 // return the size in bytes + Alignment() int64 IsBoolean() bool // is a named or unnamed boolean type IsInteger() bool // ... ditto for the others @@ -30,6 +31,7 @@ type Type interface { // Stub implementation for now, until we are completely using ../gc:Type type TypeImpl struct { Size_ int64 + Align int64 Boolean bool Integer bool Signed bool @@ -43,32 +45,33 @@ type TypeImpl struct { Name string } -func (t *TypeImpl) Size() int64 { return t.Size_ } -func (t *TypeImpl) IsBoolean() bool { return t.Boolean } -func (t *TypeImpl) IsInteger() bool { return t.Integer } -func (t *TypeImpl) IsSigned() bool { return t.Signed } -func (t *TypeImpl) IsFloat() bool { return t.Float } -func (t *TypeImpl) IsPtr() bool { return t.Ptr } -func (t *TypeImpl) IsString() bool { return t.string } -func (t *TypeImpl) IsMemory() bool { return t.Memory } -func (t *TypeImpl) IsFlags() bool { return t.Flags } -func (t *TypeImpl) String() string { return t.Name } -func (t *TypeImpl) Elem() Type { panic("not implemented"); return nil } -func (t *TypeImpl) PtrTo() Type { panic("not implemented"); return nil } +func (t *TypeImpl) Size() int64 { return t.Size_ } +func (t *TypeImpl) Alignment() int64 { return t.Align } +func (t *TypeImpl) IsBoolean() bool { return t.Boolean } +func (t *TypeImpl) IsInteger() bool { return t.Integer } +func (t *TypeImpl) IsSigned() bool { return t.Signed } +func (t *TypeImpl) IsFloat() bool { return t.Float } +func (t *TypeImpl) IsPtr() bool { return t.Ptr } +func (t *TypeImpl) IsString() bool { return t.string } +func (t *TypeImpl) IsMemory() bool { return t.Memory } +func (t *TypeImpl) IsFlags() bool { return t.Flags } +func (t *TypeImpl) String() string { return t.Name } +func (t *TypeImpl) Elem() Type { panic("not implemented"); return nil } +func (t *TypeImpl) PtrTo() Type { panic("not implemented"); return nil } var ( // shortcuts for commonly used basic types - TypeInt8 = &TypeImpl{Size_: 1, Integer: true, Signed: true, Name: "int8"} - TypeInt16 = &TypeImpl{Size_: 2, Integer: true, Signed: true, Name: "int16"} - TypeInt32 = &TypeImpl{Size_: 4, Integer: true, Signed: true, Name: "int32"} - TypeInt64 = &TypeImpl{Size_: 8, Integer: true, Signed: true, Name: "int64"} - TypeUInt8 = &TypeImpl{Size_: 1, Integer: true, Name: "uint8"} - TypeUInt16 = &TypeImpl{Size_: 2, Integer: true, Name: "uint16"} - TypeUInt32 = &TypeImpl{Size_: 4, Integer: true, Name: "uint32"} - TypeUInt64 = &TypeImpl{Size_: 8, Integer: true, Name: "uint64"} - TypeBool = &TypeImpl{Size_: 1, Boolean: true, Name: "bool"} + TypeInt8 = &TypeImpl{Size_: 1, Align: 1, Integer: true, Signed: true, Name: "int8"} + TypeInt16 = &TypeImpl{Size_: 2, Align: 2, Integer: true, Signed: true, Name: "int16"} + TypeInt32 = &TypeImpl{Size_: 4, Align: 4, Integer: true, Signed: true, Name: "int32"} + TypeInt64 = &TypeImpl{Size_: 8, Align: 8, Integer: true, Signed: true, Name: "int64"} + TypeUInt8 = &TypeImpl{Size_: 1, Align: 1, Integer: true, Name: "uint8"} + TypeUInt16 = &TypeImpl{Size_: 2, Align: 2, Integer: true, Name: "uint16"} + TypeUInt32 = &TypeImpl{Size_: 4, Align: 4, Integer: true, Name: "uint32"} + TypeUInt64 = &TypeImpl{Size_: 8, Align: 8, Integer: true, Name: "uint64"} + TypeBool = &TypeImpl{Size_: 1, Align: 1, Boolean: true, Name: "bool"} //TypeString = types.Typ[types.String] - TypeBytePtr = &TypeImpl{Size_: 8, Ptr: true, Name: "*byte"} + TypeBytePtr = &TypeImpl{Size_: 8, Align: 8, Ptr: true, Name: "*byte"} TypeInvalid = &TypeImpl{Name: "invalid"} -- cgit v1.3 From 8f22b5292ffc01ea66bd92fa833d0ec25390173b Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 11 Jun 2015 21:29:25 -0700 Subject: [dev.ssa] cmd/compiler/internal/ssa: Add auxint field Add an additional int64 auxiliary field to Value. There are two main reasons for doing this: 1) Ints in interfaces require allocation, and we store ints in Aux a lot. 2) I'd like to have both *gc.Sym and int offsets included in lots of operations (e.g. MOVQloadidx8). It will be more efficient to store them as separate fields instead of a pointer to a sym/int pair. It also simplifies a bunch of code. This is just the refactoring. I'll start using this some more in a subsequent changelist. Change-Id: I1ca797ff572553986cf90cab3ac0a0c1d01ad241 Reviewed-on: https://go-review.googlesource.com/10929 Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/gc/ssa.go | 163 +++++---- src/cmd/compile/internal/ssa/deadcode_test.go | 12 +- src/cmd/compile/internal/ssa/deadstore_test.go | 36 +- src/cmd/compile/internal/ssa/func.go | 115 +++++- src/cmd/compile/internal/ssa/func_test.go | 116 +++--- src/cmd/compile/internal/ssa/gen/AMD64.rules | 40 +-- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 42 +-- src/cmd/compile/internal/ssa/gen/generic.rules | 21 +- src/cmd/compile/internal/ssa/gen/genericOps.go | 8 +- src/cmd/compile/internal/ssa/gen/rulegen.go | 60 ++-- src/cmd/compile/internal/ssa/generic.go | 424 ---------------------- src/cmd/compile/internal/ssa/op.go | 25 -- src/cmd/compile/internal/ssa/regalloc.go | 16 +- src/cmd/compile/internal/ssa/rewrite.go | 7 +- src/cmd/compile/internal/ssa/rewriteAMD64.go | 465 ++++++++++++++----------- src/cmd/compile/internal/ssa/rewritegeneric.go | 162 +++++---- src/cmd/compile/internal/ssa/schedule_test.go | 18 +- src/cmd/compile/internal/ssa/shift_test.go | 16 +- src/cmd/compile/internal/ssa/stackalloc.go | 4 +- src/cmd/compile/internal/ssa/value.go | 9 +- 20 files changed, 756 insertions(+), 1003 deletions(-) delete mode 100644 src/cmd/compile/internal/ssa/generic.go (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index ebb7f44a18..3110fad270 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -38,9 +38,9 @@ func buildssa(fn *Node) *ssa.Func { s.exit = s.f.NewBlock(ssa.BlockExit) // Allocate starting values - s.startmem = s.entryNewValue(ssa.OpArg, ssa.TypeMem, ".mem") - s.fp = s.entryNewValue(ssa.OpFP, s.config.Uintptr, nil) // TODO: use generic pointer type (unsafe.Pointer?) instead - s.sp = s.entryNewValue(ssa.OpSP, s.config.Uintptr, nil) + s.startmem = s.entryNewValue0(ssa.OpArg, ssa.TypeMem) + s.fp = s.entryNewValue0(ssa.OpFP, s.config.Uintptr) // TODO: use generic pointer type (unsafe.Pointer?) instead + s.sp = s.entryNewValue0(ssa.OpSP, s.config.Uintptr) s.vars = map[string]*ssa.Value{} s.labels = map[string]*ssa.Block{} @@ -147,39 +147,59 @@ func (s *state) peekLine() int32 { return s.line[len(s.line)-1] } -// newValue adds a new value with no argueents to the current block. -func (s *state) newValue(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value { - return s.curBlock.NewValue(s.peekLine(), op, t, aux) +// newValue0 adds a new value with no arguments to the current block. +func (s *state) newValue0(op ssa.Op, t ssa.Type) *ssa.Value { + return s.curBlock.NewValue0(s.peekLine(), op, t) +} + +// newValue0A adds a new value with no arguments and an aux value to the current block. +func (s *state) newValue0A(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value { + return s.curBlock.NewValue0A(s.peekLine(), op, t, aux) } // newValue1 adds a new value with one argument to the current block. -func (s *state) newValue1(op ssa.Op, t ssa.Type, aux interface{}, arg *ssa.Value) *ssa.Value { - return s.curBlock.NewValue1(s.peekLine(), op, t, aux, arg) +func (s *state) newValue1(op ssa.Op, t ssa.Type, arg *ssa.Value) *ssa.Value { + return s.curBlock.NewValue1(s.peekLine(), op, t, arg) +} + +// newValue1A adds a new value with one argument and an aux value to the current block. +func (s *state) newValue1A(op ssa.Op, t ssa.Type, aux interface{}, arg *ssa.Value) *ssa.Value { + return s.curBlock.NewValue1A(s.peekLine(), op, t, aux, arg) } // newValue2 adds a new value with two arguments to the current block. -func (s *state) newValue2(op ssa.Op, t ssa.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value { - return s.curBlock.NewValue2(s.peekLine(), op, t, aux, arg0, arg1) +func (s *state) newValue2(op ssa.Op, t ssa.Type, arg0, arg1 *ssa.Value) *ssa.Value { + return s.curBlock.NewValue2(s.peekLine(), op, t, arg0, arg1) } // newValue3 adds a new value with three arguments to the current block. -func (s *state) newValue3(op ssa.Op, t ssa.Type, aux interface{}, arg0, arg1, arg2 *ssa.Value) *ssa.Value { - return s.curBlock.NewValue3(s.peekLine(), op, t, aux, arg0, arg1, arg2) +func (s *state) newValue3(op ssa.Op, t ssa.Type, arg0, arg1, arg2 *ssa.Value) *ssa.Value { + return s.curBlock.NewValue3(s.peekLine(), op, t, arg0, arg1, arg2) } // entryNewValue adds a new value with no arguments to the entry block. -func (s *state) entryNewValue(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value { - return s.f.Entry.NewValue(s.peekLine(), op, t, aux) +func (s *state) entryNewValue0(op ssa.Op, t ssa.Type) *ssa.Value { + return s.f.Entry.NewValue0(s.peekLine(), op, t) +} + +// entryNewValue adds a new value with no arguments and an aux value to the entry block. +func (s *state) entryNewValue0A(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value { + return s.f.Entry.NewValue0A(s.peekLine(), op, t, aux) } // entryNewValue1 adds a new value with one argument to the entry block. -func (s *state) entryNewValue1(op ssa.Op, t ssa.Type, aux interface{}, arg *ssa.Value) *ssa.Value { - return s.f.Entry.NewValue1(s.peekLine(), op, t, aux, arg) +func (s *state) entryNewValue1(op ssa.Op, t ssa.Type, arg *ssa.Value) *ssa.Value { + return s.f.Entry.NewValue1(s.peekLine(), op, t, arg) +} + +// entryNewValue1 adds a new value with one argument and an auxint value to the entry block. +func (s *state) entryNewValue1I(op ssa.Op, t ssa.Type, auxint int64, arg *ssa.Value) *ssa.Value { + return s.f.Entry.NewValue1I(s.peekLine(), op, t, auxint, arg) } // entryNewValue2 adds a new value with two arguments to the entry block. -func (s *state) entryNewValue2(op ssa.Op, t ssa.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value { - return s.f.Entry.NewValue2(s.peekLine(), op, t, aux, arg0, arg1) +func (s *state) entryNewValue2(op ssa.Op, t ssa.Type, arg0, arg1 *ssa.Value) *ssa.Value { + return s.f.Entry.NewValue2(s.peekLine(), op, t, arg0, arg1) } // constInt adds a new const int value to the entry block. @@ -234,11 +254,11 @@ func (s *state) stmt(n *Node) { t := n.Left.Type switch { case t.IsString(): - val = s.entryNewValue(ssa.OpConst, n.Left.Type, "") + val = s.entryNewValue0(ssa.OpConst, n.Left.Type) case t.IsInteger(): - val = s.entryNewValue(ssa.OpConst, n.Left.Type, int64(0)) + val = s.entryNewValue0(ssa.OpConst, n.Left.Type) case t.IsBoolean(): - val = s.entryNewValue(ssa.OpConst, n.Left.Type, false) + val = s.entryNewValue0A(ssa.OpConst, n.Left.Type, false) // TODO: store bools as 0/1 in AuxInt? default: log.Fatalf("zero for type %v not implemented", t) } @@ -252,7 +272,7 @@ func (s *state) stmt(n *Node) { } // not ssa-able. Treat as a store. addr := s.addr(n.Left) - s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem()) + s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, addr, val, s.mem()) case OIF: cond := s.expr(n.Left) b := s.endBlock() @@ -341,20 +361,20 @@ func (s *state) expr(n *Node) *ssa.Value { case ONAME: if n.Class == PFUNC { // "value" of a function is the address of the function's closure - return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), funcsym(n.Sym)) + return s.entryNewValue0A(ssa.OpGlobal, Ptrto(n.Type), funcsym(n.Sym)) } s.argOffsets[n.Sym.Name] = n.Xoffset // TODO: remember this another way? if canSSA(n) { return s.variable(n.Sym.Name, n.Type) } addr := s.addr(n) - return s.newValue2(ssa.OpLoad, n.Type, nil, addr, s.mem()) + return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem()) case OLITERAL: switch n.Val().Ctype() { case CTINT: return s.constInt(n.Type, Mpgetfix(n.Val().U.(*Mpint))) case CTSTR: - return s.entryNewValue(ssa.OpConst, n.Type, n.Val().U) + return s.entryNewValue0A(ssa.OpConst, n.Type, n.Val().U) default: log.Fatalf("unhandled OLITERAL %v", n.Val().Ctype()) return nil @@ -367,24 +387,24 @@ func (s *state) expr(n *Node) *ssa.Value { case OLT: a := s.expr(n.Left) b := s.expr(n.Right) - return s.newValue2(ssa.OpLess, ssa.TypeBool, nil, a, b) + return s.newValue2(ssa.OpLess, ssa.TypeBool, a, b) case OADD: a := s.expr(n.Left) b := s.expr(n.Right) - return s.newValue2(ssa.OpAdd, a.Type, nil, a, b) + return s.newValue2(ssa.OpAdd, a.Type, a, b) case OSUB: // TODO:(khr) fold code for all binary ops together somehow a := s.expr(n.Left) b := s.expr(n.Right) - return s.newValue2(ssa.OpSub, a.Type, nil, a, b) + return s.newValue2(ssa.OpSub, a.Type, a, b) case OLSH: a := s.expr(n.Left) b := s.expr(n.Right) - return s.newValue2(ssa.OpLsh, a.Type, nil, a, b) + return s.newValue2(ssa.OpLsh, a.Type, a, b) case ORSH: a := s.expr(n.Left) b := s.expr(n.Right) - return s.newValue2(ssa.OpRsh, a.Type, nil, a, b) + return s.newValue2(ssa.OpRsh, a.Type, a, b) case OADDR: return s.addr(n.Left) @@ -392,13 +412,13 @@ func (s *state) expr(n *Node) *ssa.Value { case OIND: p := s.expr(n.Left) s.nilCheck(p) - return s.newValue2(ssa.OpLoad, n.Type, nil, p, s.mem()) + return s.newValue2(ssa.OpLoad, n.Type, p, s.mem()) case ODOTPTR: p := s.expr(n.Left) s.nilCheck(p) - p = s.newValue2(ssa.OpAdd, p.Type, nil, p, s.constInt(s.config.Uintptr, n.Xoffset)) - return s.newValue2(ssa.OpLoad, n.Type, nil, p, s.mem()) + p = s.newValue2(ssa.OpAdd, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset)) + return s.newValue2(ssa.OpLoad, n.Type, p, s.mem()) case OINDEX: if n.Left.Type.Bound >= 0 { // array or string @@ -407,17 +427,17 @@ func (s *state) expr(n *Node) *ssa.Value { var elemtype *Type var len *ssa.Value if n.Left.Type.IsString() { - len = s.newValue1(ssa.OpStringLen, s.config.Uintptr, nil, a) + len = s.newValue1(ssa.OpStringLen, s.config.Uintptr, a) elemtype = Types[TUINT8] } else { len = s.constInt(s.config.Uintptr, n.Left.Type.Bound) elemtype = n.Left.Type.Type } s.boundsCheck(i, len) - return s.newValue2(ssa.OpArrayIndex, elemtype, nil, a, i) + return s.newValue2(ssa.OpArrayIndex, elemtype, a, i) } else { // slice p := s.addr(n) - return s.newValue2(ssa.OpLoad, n.Left.Type.Type, nil, p, s.mem()) + return s.newValue2(ssa.OpLoad, n.Left.Type.Type, p, s.mem()) } case OCALLFUNC: @@ -435,10 +455,10 @@ func (s *state) expr(n *Node) *ssa.Value { bNext := s.f.NewBlock(ssa.BlockPlain) var call *ssa.Value if static { - call = s.newValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym, s.mem()) + call = s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym, s.mem()) } else { - entry := s.newValue2(ssa.OpLoad, s.config.Uintptr, nil, closure, s.mem()) - call = s.newValue3(ssa.OpClosureCall, ssa.TypeMem, nil, entry, closure, s.mem()) + entry := s.newValue2(ssa.OpLoad, s.config.Uintptr, closure, s.mem()) + call = s.newValue3(ssa.OpClosureCall, ssa.TypeMem, entry, closure, s.mem()) } b := s.endBlock() b.Kind = ssa.BlockCall @@ -450,8 +470,8 @@ func (s *state) expr(n *Node) *ssa.Value { s.startBlock(bNext) var titer Iter fp := Structfirst(&titer, Getoutarg(n.Left.Type)) - a := s.entryNewValue1(ssa.OpOffPtr, Ptrto(fp.Type), fp.Width, s.sp) - return s.newValue2(ssa.OpLoad, fp.Type, nil, a, call) + a := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Width, s.sp) + return s.newValue2(ssa.OpLoad, fp.Type, a, call) default: log.Fatalf("unhandled expr %s", opnames[n.Op]) return nil @@ -465,10 +485,10 @@ func (s *state) addr(n *Node) *ssa.Value { switch n.Class { case PEXTERN: // global variable - return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), n.Sym) + return s.entryNewValue0A(ssa.OpGlobal, Ptrto(n.Type), n.Sym) case PPARAMOUT: // store to parameter slot - return s.entryNewValue1(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp) + return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp) default: // TODO: address of locals log.Fatalf("variable address of %v not implemented", n) @@ -477,21 +497,21 @@ func (s *state) addr(n *Node) *ssa.Value { case OINDREG: // indirect off a register (TODO: always SP?) // used for storing/loading arguments/returns to/from callees - return s.entryNewValue1(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.sp) + return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.sp) case OINDEX: if n.Left.Type.Bound >= 0 { // array a := s.addr(n.Left) i := s.expr(n.Right) len := s.constInt(s.config.Uintptr, n.Left.Type.Bound) s.boundsCheck(i, len) - return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), nil, a, i) + return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), a, i) } else { // slice a := s.expr(n.Left) i := s.expr(n.Right) - len := s.newValue1(ssa.OpSliceLen, s.config.Uintptr, nil, a) + len := s.newValue1(ssa.OpSliceLen, s.config.Uintptr, a) s.boundsCheck(i, len) - p := s.newValue1(ssa.OpSlicePtr, Ptrto(n.Left.Type.Type), nil, a) - return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), nil, p, i) + p := s.newValue1(ssa.OpSlicePtr, Ptrto(n.Left.Type.Type), a) + return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), p, i) } default: log.Fatalf("addr: bad op %v", Oconv(int(n.Op), 0)) @@ -524,7 +544,7 @@ func canSSA(n *Node) bool { // nilCheck generates nil pointer checking code. // Starts a new block on return. func (s *state) nilCheck(ptr *ssa.Value) { - c := s.newValue1(ssa.OpIsNonNil, ssa.TypeBool, nil, ptr) + c := s.newValue1(ssa.OpIsNonNil, ssa.TypeBool, ptr) b := s.endBlock() b.Kind = ssa.BlockIf b.Control = c @@ -543,7 +563,7 @@ func (s *state) boundsCheck(idx, len *ssa.Value) { // TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero. // bounds check - cmp := s.newValue2(ssa.OpIsInBounds, ssa.TypeBool, nil, idx, len) + cmp := s.newValue2(ssa.OpIsInBounds, ssa.TypeBool, idx, len) b := s.endBlock() b.Kind = ssa.BlockIf b.Control = cmp @@ -562,7 +582,7 @@ func (s *state) variable(name string, t ssa.Type) *ssa.Value { v := s.vars[name] if v == nil { // TODO: get type? Take Sym as arg? - v = s.newValue(ssa.OpFwdRef, t, name) + v = s.newValue0A(ssa.OpFwdRef, t, name) s.vars[name] = v } return v @@ -601,8 +621,8 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va return s.startmem } // variable is live at the entry block. Load it. - addr := s.entryNewValue1(ssa.OpOffPtr, Ptrto(t.(*Type)), s.argOffsets[name], s.fp) - return s.entryNewValue2(ssa.OpLoad, t, nil, addr, s.startmem) + addr := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(t.(*Type)), s.argOffsets[name], s.fp) + return s.entryNewValue2(ssa.OpLoad, t, addr, s.startmem) } var vals []*ssa.Value @@ -613,7 +633,7 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va for i := 1; i < len(vals); i++ { if vals[i] != v0 { // need a phi value - v := b.NewValue(s.peekLine(), ssa.OpPhi, t, nil) + v := b.NewValue0(s.peekLine(), ssa.OpPhi, t) v.AddArgs(vals...) return v } @@ -634,7 +654,7 @@ func (s *state) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name string) *ssa.Va // Make v = copy(w). We need the extra copy to // prevent infinite recursion when looking up the // incoming value of the variable. - v := b.NewValue(s.peekLine(), ssa.OpCopy, t, nil) + v := b.NewValue0(s.peekLine(), ssa.OpCopy, t) m[name] = v v.AddArg(s.lookupVarIncoming(b, t, name)) return v @@ -728,7 +748,7 @@ func genValue(v *ssa.Value) { p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64MULQconst: @@ -736,7 +756,7 @@ func genValue(v *ssa.Value) { // has ever been taught to compile imul $c, r1, r2. p := Prog(x86.AIMULQ) p.From.Type = obj.TYPE_CONST - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.From3.Type = obj.TYPE_REG p.From3.Reg = regnum(v.Args[0]) p.To.Type = obj.TYPE_REG @@ -756,7 +776,7 @@ func genValue(v *ssa.Value) { } p := Prog(x86.ASUBQ) p.From.Type = obj.TYPE_CONST - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = r case ssa.OpAMD64SHLQ: @@ -829,7 +849,7 @@ func genValue(v *ssa.Value) { } p := Prog(x86.ASHLQ) p.From.Type = obj.TYPE_CONST - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = r case ssa.OpAMD64SHRQconst: @@ -845,7 +865,7 @@ func genValue(v *ssa.Value) { } p := Prog(x86.ASHRQ) p.From.Type = obj.TYPE_CONST - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = r case ssa.OpAMD64SARQconst: @@ -861,7 +881,7 @@ func genValue(v *ssa.Value) { } p := Prog(x86.ASARQ) p.From.Type = obj.TYPE_CONST - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = r case ssa.OpAMD64SBBQcarrymask: @@ -921,7 +941,7 @@ func genValue(v *ssa.Value) { p.From.Reg = regnum(v.Args[0]) p.From.Scale = 1 p.From.Index = regnum(v.Args[1]) - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64CMPQ: @@ -935,7 +955,7 @@ func genValue(v *ssa.Value) { p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[0]) p.To.Type = obj.TYPE_CONST - p.To.Offset = v.Aux.(int64) + p.To.Offset = v.AuxInt case ssa.OpAMD64TESTB: p := Prog(x86.ATESTB) p.From.Type = obj.TYPE_REG @@ -946,28 +966,28 @@ func genValue(v *ssa.Value) { x := regnum(v) p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_CONST - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = x case ssa.OpAMD64MOVQload: p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64MOVBload: p := Prog(x86.AMOVB) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64MOVQloadidx8: p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) - p.From.Offset = v.Aux.(int64) + p.From.Offset = v.AuxInt p.From.Scale = 8 p.From.Index = regnum(v.Args[1]) p.To.Type = obj.TYPE_REG @@ -978,7 +998,7 @@ func genValue(v *ssa.Value) { p.From.Reg = regnum(v.Args[1]) p.To.Type = obj.TYPE_MEM p.To.Reg = regnum(v.Args[0]) - p.To.Offset = v.Aux.(int64) + p.To.Offset = v.AuxInt case ssa.OpCopy: // TODO: lower to MOVQ earlier? if v.Type.IsMemory() { return @@ -1021,14 +1041,13 @@ func genValue(v *ssa.Value) { } case ssa.OpArg: // memory arg needs no code - // TODO: only mem arg goes here. + // TODO: check that only mem arg goes here. case ssa.OpAMD64LEAQglobal: - g := v.Aux.(ssa.GlobalOffset) p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_EXTERN - p.From.Sym = Linksym(g.Global.(*Sym)) - p.From.Offset = g.Offset + p.From.Sym = Linksym(v.Aux.(*Sym)) + p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64CALLstatic: diff --git a/src/cmd/compile/internal/ssa/deadcode_test.go b/src/cmd/compile/internal/ssa/deadcode_test.go index 10b8976e0f..edd38e1254 100644 --- a/src/cmd/compile/internal/ssa/deadcode_test.go +++ b/src/cmd/compile/internal/ssa/deadcode_test.go @@ -10,14 +10,14 @@ func TestDeadLoop(t *testing.T) { c := NewConfig("amd64", DummyFrontend{}) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem")), // dead loop Bloc("deadblock", // dead value in dead block - Valu("deadval", OpConst, TypeBool, true), + Valu("deadval", OpConst, TypeBool, 0, true), If("deadval", "deadblock", "exit"))) CheckFunc(fun.f) @@ -40,8 +40,8 @@ func TestDeadValue(t *testing.T) { c := NewConfig("amd64", DummyFrontend{}) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("deadval", OpConst, TypeInt64, int64(37)), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("deadval", OpConst, TypeInt64, 37, nil), Goto("exit")), Bloc("exit", Exit("mem"))) @@ -63,8 +63,8 @@ func TestNeverTaken(t *testing.T) { c := NewConfig("amd64", DummyFrontend{}) fun := Fun(c, "entry", Bloc("entry", - Valu("cond", OpConst, TypeBool, false), - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("cond", OpConst, TypeBool, 0, false), + Valu("mem", OpArg, TypeMem, 0, ".mem"), If("cond", "then", "else")), Bloc("then", Goto("exit")), diff --git a/src/cmd/compile/internal/ssa/deadstore_test.go b/src/cmd/compile/internal/ssa/deadstore_test.go index 70b2092ec3..5143afb6cb 100644 --- a/src/cmd/compile/internal/ssa/deadstore_test.go +++ b/src/cmd/compile/internal/ssa/deadstore_test.go @@ -13,13 +13,13 @@ func TestDeadStore(t *testing.T) { ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing fun := Fun(c, "entry", Bloc("entry", - Valu("start", OpArg, TypeMem, ".mem"), - Valu("v", OpConst, TypeBool, true), - Valu("addr1", OpGlobal, ptrType, nil), - Valu("addr2", OpGlobal, ptrType, nil), - Valu("store1", OpStore, TypeMem, nil, "addr1", "v", "start"), - Valu("store2", OpStore, TypeMem, nil, "addr2", "v", "store1"), - Valu("store3", OpStore, TypeMem, nil, "addr1", "v", "store2"), + Valu("start", OpArg, TypeMem, 0, ".mem"), + Valu("v", OpConst, TypeBool, 0, true), + Valu("addr1", OpGlobal, ptrType, 0, nil), + Valu("addr2", OpGlobal, ptrType, 0, nil), + Valu("store1", OpStore, TypeMem, 0, nil, "addr1", "v", "start"), + Valu("store2", OpStore, TypeMem, 0, nil, "addr2", "v", "store1"), + Valu("store3", OpStore, TypeMem, 0, nil, "addr1", "v", "store2"), Goto("exit")), Bloc("exit", Exit("store3"))) @@ -39,13 +39,13 @@ func TestDeadStorePhi(t *testing.T) { ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing fun := Fun(c, "entry", Bloc("entry", - Valu("start", OpArg, TypeMem, ".mem"), - Valu("v", OpConst, TypeBool, true), - Valu("addr", OpGlobal, ptrType, nil), + Valu("start", OpArg, TypeMem, 0, ".mem"), + Valu("v", OpConst, TypeBool, 0, true), + Valu("addr", OpGlobal, ptrType, 0, nil), Goto("loop")), Bloc("loop", - Valu("phi", OpPhi, TypeMem, nil, "start", "store"), - Valu("store", OpStore, TypeMem, nil, "addr", "v", "phi"), + Valu("phi", OpPhi, TypeMem, 0, nil, "start", "store"), + Valu("store", OpStore, TypeMem, 0, nil, "addr", "v", "phi"), If("v", "loop", "exit")), Bloc("exit", Exit("store"))) @@ -65,12 +65,12 @@ func TestDeadStoreTypes(t *testing.T) { t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"} fun := Fun(c, "entry", Bloc("entry", - Valu("start", OpArg, TypeMem, ".mem"), - Valu("v", OpConst, TypeBool, true), - Valu("addr1", OpGlobal, t1, nil), - Valu("addr2", OpGlobal, t2, nil), - Valu("store1", OpStore, TypeMem, nil, "addr1", "v", "start"), - Valu("store2", OpStore, TypeMem, nil, "addr2", "v", "store1"), + Valu("start", OpArg, TypeMem, 0, ".mem"), + Valu("v", OpConst, TypeBool, 0, true), + Valu("addr1", OpGlobal, t1, 0, nil), + Valu("addr2", OpGlobal, t2, 0, nil), + Valu("store1", OpStore, TypeMem, 0, nil, "addr1", "v", "start"), + Valu("store2", OpStore, TypeMem, 0, nil, "addr2", "v", "store1"), Goto("exit")), Bloc("exit", Exit("store2"))) diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 06a2455e87..2e1b5990dc 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -4,6 +4,8 @@ package ssa +import "log" + // A Func represents a Go func declaration (or function literal) and // its body. This package compiles each Func independently. type Func struct { @@ -42,13 +44,12 @@ func (f *Func) NewBlock(kind BlockKind) *Block { return b } -// NewValue returns a new value in the block with no arguments. -func (b *Block) NewValue(line int32, op Op, t Type, aux interface{}) *Value { +// NewValue0 returns a new value in the block with no arguments and zero aux values. +func (b *Block) NewValue0(line int32, op Op, t Type) *Value { v := &Value{ ID: b.Func.vid.get(), Op: op, Type: t, - Aux: aux, Block: b, } v.Args = v.argstorage[:0] @@ -56,8 +57,28 @@ func (b *Block) NewValue(line int32, op Op, t Type, aux interface{}) *Value { return v } -// NewValue1 returns a new value in the block with one argument. -func (b *Block) NewValue1(line int32, op Op, t Type, aux interface{}, arg *Value) *Value { +// NewValue returns a new value in the block with no arguments and an auxint value. +func (b *Block) NewValue0I(line int32, op Op, t Type, auxint int64) *Value { + v := &Value{ + ID: b.Func.vid.get(), + Op: op, + Type: t, + AuxInt: auxint, + Block: b, + } + v.Args = v.argstorage[:0] + b.Values = append(b.Values, v) + return v +} + +// NewValue returns a new value in the block with no arguments and an aux value. +func (b *Block) NewValue0A(line int32, op Op, t Type, aux interface{}) *Value { + if _, ok := aux.(int64); ok { + // Disallow int64 aux values. They should be in the auxint field instead. + // Maybe we want to allow this at some point, but for now we disallow it + // to prevent errors like using NewValue1A instead of NewValue1I. + log.Fatalf("aux field has int64 type op=%s type=%s aux=%v", op, t, aux) + } v := &Value{ ID: b.Func.vid.get(), Op: op, @@ -65,14 +86,57 @@ func (b *Block) NewValue1(line int32, op Op, t Type, aux interface{}, arg *Value Aux: aux, Block: b, } + v.Args = v.argstorage[:0] + b.Values = append(b.Values, v) + return v +} + +// NewValue returns a new value in the block with no arguments and both an auxint and aux values. +func (b *Block) NewValue0IA(line int32, op Op, t Type, auxint int64, aux interface{}) *Value { + v := &Value{ + ID: b.Func.vid.get(), + Op: op, + Type: t, + AuxInt: auxint, + Aux: aux, + Block: b, + } + v.Args = v.argstorage[:0] + b.Values = append(b.Values, v) + return v +} + +// NewValue1 returns a new value in the block with one argument and zero aux values. +func (b *Block) NewValue1(line int32, op Op, t Type, arg *Value) *Value { + v := &Value{ + ID: b.Func.vid.get(), + Op: op, + Type: t, + Block: b, + } v.Args = v.argstorage[:1] v.Args[0] = arg b.Values = append(b.Values, v) return v } -// NewValue2 returns a new value in the block with two arguments. -func (b *Block) NewValue2(line int32, op Op, t Type, aux interface{}, arg0, arg1 *Value) *Value { +// NewValue1I returns a new value in the block with one argument and an auxint value. +func (b *Block) NewValue1I(line int32, op Op, t Type, auxint int64, arg *Value) *Value { + v := &Value{ + ID: b.Func.vid.get(), + Op: op, + Type: t, + AuxInt: auxint, + Block: b, + } + v.Args = v.argstorage[:1] + v.Args[0] = arg + b.Values = append(b.Values, v) + return v +} + +// NewValue1A returns a new value in the block with one argument and an aux value. +func (b *Block) NewValue1A(line int32, op Op, t Type, aux interface{}, arg *Value) *Value { v := &Value{ ID: b.Func.vid.get(), Op: op, @@ -80,6 +144,36 @@ func (b *Block) NewValue2(line int32, op Op, t Type, aux interface{}, arg0, arg1 Aux: aux, Block: b, } + v.Args = v.argstorage[:1] + v.Args[0] = arg + b.Values = append(b.Values, v) + return v +} + +// NewValue1IA returns a new value in the block with one argument and both an auxint and aux values. +func (b *Block) NewValue1IA(line int32, op Op, t Type, auxint int64, aux interface{}, arg *Value) *Value { + v := &Value{ + ID: b.Func.vid.get(), + Op: op, + Type: t, + AuxInt: auxint, + Aux: aux, + Block: b, + } + v.Args = v.argstorage[:1] + v.Args[0] = arg + b.Values = append(b.Values, v) + return v +} + +// NewValue2 returns a new value in the block with two arguments and zero aux values. +func (b *Block) NewValue2(line int32, op Op, t Type, arg0, arg1 *Value) *Value { + v := &Value{ + ID: b.Func.vid.get(), + Op: op, + Type: t, + Block: b, + } v.Args = v.argstorage[:2] v.Args[0] = arg0 v.Args[1] = arg1 @@ -87,13 +181,12 @@ func (b *Block) NewValue2(line int32, op Op, t Type, aux interface{}, arg0, arg1 return v } -// NewValue3 returns a new value in the block with three arguments. -func (b *Block) NewValue3(line int32, op Op, t Type, aux interface{}, arg0, arg1, arg2 *Value) *Value { +// NewValue3 returns a new value in the block with three arguments and zero aux values. +func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *Value { v := &Value{ ID: b.Func.vid.get(), Op: op, Type: t, - Aux: aux, Block: b, } v.Args = []*Value{arg0, arg1, arg2} @@ -104,5 +197,5 @@ func (b *Block) NewValue3(line int32, op Op, t Type, aux interface{}, arg0, arg1 // ConstInt returns an int constant representing its argument. func (f *Func) ConstInt(line int32, t Type, c int64) *Value { // TODO: cache? - return f.Entry.NewValue(line, OpConst, t, c) + return f.Entry.NewValue0I(line, OpConst, t, c) } diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index 3f94589e8b..7cfc7324ac 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -18,12 +18,12 @@ // // fun := Fun("entry", // Bloc("entry", -// Valu("mem", OpArg, TypeMem, ".mem"), +// Valu("mem", OpArg, TypeMem, 0, ".mem"), // Goto("exit")), // Bloc("exit", // Exit("mem")), // Bloc("deadblock", -// Valu("deadval", OpConst, TypeBool, true), +// Valu("deadval", OpConst, TypeBool, 0, true), // If("deadval", "deadblock", "exit"))) // // and the Blocks or Values used in the Func can be accessed @@ -61,7 +61,7 @@ func Equiv(f, g *Func) bool { // Ignore ids. Ops and Types are compared for equality. // TODO(matloob): Make sure types are canonical and can // be compared for equality. - if fv.Op != gv.Op || fv.Type != gv.Type { + if fv.Op != gv.Op || fv.Type != gv.Type || fv.AuxInt != gv.AuxInt { return false } if !reflect.DeepEqual(fv.Aux, gv.Aux) { @@ -149,7 +149,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun { blocks[bloc.name] = b for _, valu := range bloc.valus { // args are filled in the second pass. - values[valu.name] = b.NewValue(0, valu.op, valu.t, valu.aux) + values[valu.name] = b.NewValue0IA(0, valu.op, valu.t, valu.auxint, valu.aux) } } // Connect the blocks together and specify control values. @@ -212,8 +212,8 @@ func Bloc(name string, entries ...interface{}) bloc { } // Valu defines a value in a block. -func Valu(name string, op Op, t Type, aux interface{}, args ...string) valu { - return valu{name, op, t, aux, args} +func Valu(name string, op Op, t Type, auxint int64, aux interface{}, args ...string) valu { + return valu{name, op, t, auxint, aux, args} } // Goto specifies that this is a BlockPlain and names the single successor. @@ -248,11 +248,12 @@ type ctrl struct { } type valu struct { - name string - op Op - t Type - aux interface{} - args []string + name string + op Op + t Type + auxint int64 + aux interface{} + args []string } func addEdge(b, c *Block) { @@ -264,10 +265,10 @@ func TestArgs(t *testing.T) { c := NewConfig("amd64", DummyFrontend{}) fun := Fun(c, "entry", Bloc("entry", - Valu("a", OpConst, TypeInt64, 14), - Valu("b", OpConst, TypeInt64, 26), - Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Valu("b", OpConst, TypeInt64, 26, nil), + Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))) @@ -287,19 +288,19 @@ func TestEquiv(t *testing.T) { { Fun(c, "entry", Bloc("entry", - Valu("a", OpConst, TypeInt64, 14), - Valu("b", OpConst, TypeInt64, 26), - Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Valu("b", OpConst, TypeInt64, 26, nil), + Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("a", OpConst, TypeInt64, 14), - Valu("b", OpConst, TypeInt64, 26), - Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Valu("b", OpConst, TypeInt64, 26, nil), + Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), @@ -308,10 +309,10 @@ func TestEquiv(t *testing.T) { { Fun(c, "entry", Bloc("entry", - Valu("a", OpConst, TypeInt64, 14), - Valu("b", OpConst, TypeInt64, 26), - Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Valu("b", OpConst, TypeInt64, 26, nil), + Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), @@ -319,10 +320,10 @@ func TestEquiv(t *testing.T) { Bloc("exit", Exit("mem")), Bloc("entry", - Valu("a", OpConst, TypeInt64, 14), - Valu("b", OpConst, TypeInt64, 26), - Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Valu("b", OpConst, TypeInt64, 26, nil), + Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit"))), }, } @@ -339,58 +340,71 @@ func TestEquiv(t *testing.T) { { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), Exit("mem"))), }, // value order changed { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("b", OpConst, TypeInt64, 26), - Valu("a", OpConst, TypeInt64, 14), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("b", OpConst, TypeInt64, 26, nil), + Valu("a", OpConst, TypeInt64, 14, nil), Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("a", OpConst, TypeInt64, 14), - Valu("b", OpConst, TypeInt64, 26), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Valu("b", OpConst, TypeInt64, 26, nil), + Exit("mem"))), + }, + // value auxint different + { + Fun(c, "entry", + Bloc("entry", + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Exit("mem"))), + Fun(c, "entry", + Bloc("entry", + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("a", OpConst, TypeInt64, 26, nil), Exit("mem"))), }, // value aux different { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("a", OpConst, TypeInt64, 14), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("a", OpConst, TypeInt64, 0, 14), Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("a", OpConst, TypeInt64, 26), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("a", OpConst, TypeInt64, 0, 26), Exit("mem"))), }, // value args different { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("a", OpConst, TypeInt64, 14), - Valu("b", OpConst, TypeInt64, 26), - Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("a", OpConst, TypeInt64, 14, nil), + Valu("b", OpConst, TypeInt64, 26, nil), + Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("a", OpConst, TypeInt64, 0), - Valu("b", OpConst, TypeInt64, 14), - Valu("sum", OpAdd, TypeInt64, nil, "b", "a"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("a", OpConst, TypeInt64, 0, nil), + Valu("b", OpConst, TypeInt64, 14, nil), + Valu("sum", OpAdd, TypeInt64, 0, nil, "b", "a"), Exit("mem"))), }, } diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index e9744aed9c..58ab25b392 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -23,30 +23,30 @@ // mask = shift >= 64 ? 0 : 0xffffffffffffffff // result = mask & arg << shift (Lsh x y) && is64BitInt(t) -> - (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) + (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [64] y))) (Rsh x y) && is64BitInt(t) && !t.IsSigned() -> - (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) + (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [64] y))) // Note: signed right shift needs to return 0/-1 if shift amount is >= 64. // if shift > 63 { shift = 63 } // result = arg >> shift (Rsh x y) && is64BitInt(t) && t.IsSigned() -> (SARQ x (CMOVQCC - (CMPQconst [int64(64)] y) - (Const [int64(63)]) + (CMPQconst [64] y) + (Const [63]) y)) (Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y)) -(Load ptr mem) && t.IsBoolean() -> (MOVBload [int64(0)] ptr mem) -(Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload [int64(0)] ptr mem) -(Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore [int64(0)] ptr val mem) +(Load ptr mem) && t.IsBoolean() -> (MOVBload ptr mem) +(Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload ptr mem) +(Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore ptr val mem) // checks (IsNonNil p) -> (SETNE (TESTQ p p)) (IsInBounds idx len) -> (SETB (CMPQ idx len)) -(Move [size] dst src mem) -> (REPMOVSB dst src (Const [size.(int64)]) mem) +(Move [size] dst src mem) -> (REPMOVSB dst src (Const [size]) mem) (OffPtr [off] ptr) -> (ADDQconst [off] ptr) @@ -65,14 +65,14 @@ // TODO: Should this be a separate pass? // global loads/stores -(Global [sym]) -> (LEAQglobal [GlobalOffset{sym,0}]) +(Global {sym}) -> (LEAQglobal {sym}) // fold constants into instructions (ADDQ x (MOVQconst [c])) -> (ADDQconst [c] x) // TODO: restrict c to int32 range? (ADDQ (MOVQconst [c]) x) -> (ADDQconst [c] x) (SUBQ x (MOVQconst [c])) -> (SUBQconst x [c]) (SUBQ (MOVQconst [c]) x) -> (NEGQ (SUBQconst x [c])) -(MULQ x (MOVQconst [c])) && c.(int64) == int64(int32(c.(int64))) -> (MULQconst [c] x) +(MULQ x (MOVQconst [c])) && c == int64(int32(c)) -> (MULQconst [c] x) (MULQ (MOVQconst [c]) x) -> (MULQconst [c] x) (ANDQ x (MOVQconst [c])) -> (ANDQconst [c] x) (ANDQ (MOVQconst [c]) x) -> (ANDQconst [c] x) @@ -84,11 +84,11 @@ // strength reduction // TODO: do this a lot more generically -(MULQconst [c] x) && c.(int64) == 8 -> (SHLQconst [int64(3)] x) -(MULQconst [c] x) && c.(int64) == 64 -> (SHLQconst [int64(5)] x) +(MULQconst [8] x) -> (SHLQconst [3] x) +(MULQconst [64] x) -> (SHLQconst [5] x) // fold add/shift into leaq -(ADDQ x (SHLQconst [shift] y)) && shift.(int64) == 3 -> (LEAQ8 [int64(0)] x y) +(ADDQ x (SHLQconst [3] y)) -> (LEAQ8 x y) (ADDQconst [c] (LEAQ8 [d] x y)) -> (LEAQ8 [addOff(c, d)] x y) // reverse ordering of compare instruction @@ -110,7 +110,7 @@ (MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) (MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) -(ADDQconst [off] x) && off.(int64) == 0 -> (Copy x) +(ADDQconst [0] x) -> (Copy x) // Absorb InvertFlags into branches. (LT (InvertFlags cmp) yes no) -> (GT cmp yes no) @@ -125,9 +125,9 @@ (NE (InvertFlags cmp) yes no) -> (NE cmp yes no) // get rid of >=64 code for constant shifts -(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && inBounds(d.(int64), c.(int64)) -> (Const [int64(-1)]) -(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && !inBounds(d.(int64), c.(int64)) -> (Const [int64(0)]) -(ANDQconst [c] _) && c.(int64) == 0 -> (MOVQconst [int64(0)]) -(ANDQconst [c] x) && c.(int64) == -1 -> (Copy x) -(CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) && inBounds(d.(int64), c.(int64)) -> (Copy x) -(CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) && !inBounds(d.(int64), c.(int64)) -> (Copy x) +(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && inBounds(d, c) -> (Const [-1]) +(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && !inBounds(d, c) -> (Const [0]) +(ANDQconst [0] _) -> (MOVQconst [0]) +(ANDQconst [-1] x) -> (Copy x) +(CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) && inBounds(d, c) -> (Copy x) +(CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) && !inBounds(d, c) -> (Copy x) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index bcb07392c7..13aff4cba7 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -93,24 +93,24 @@ func init() { // TODO: 2-address instructions. Mark ops as needing matching input/output regs. var AMD64ops = []opData{ {name: "ADDQ", reg: gp21}, // arg0 + arg1 - {name: "ADDQconst", reg: gp11}, // arg0 + aux.(int64) + {name: "ADDQconst", reg: gp11}, // arg0 + auxint {name: "SUBQ", reg: gp21}, // arg0 - arg1 - {name: "SUBQconst", reg: gp11}, // arg0 - aux.(int64) + {name: "SUBQconst", reg: gp11}, // arg0 - auxint {name: "MULQ", reg: gp21}, // arg0 * arg1 - {name: "MULQconst", reg: gp11}, // arg0 * aux.(int64) + {name: "MULQconst", reg: gp11}, // arg0 * auxint {name: "ANDQ", reg: gp21}, // arg0 & arg1 - {name: "ANDQconst", reg: gp11}, // arg0 & aux.(int64) + {name: "ANDQconst", reg: gp11}, // arg0 & auxint {name: "SHLQ", reg: gp21shift}, // arg0 << arg1, shift amount is mod 64 - {name: "SHLQconst", reg: gp11}, // arg0 << aux.(int64), shift amount 0-63 + {name: "SHLQconst", reg: gp11}, // arg0 << auxint, shift amount 0-63 {name: "SHRQ", reg: gp21shift}, // unsigned arg0 >> arg1, shift amount is mod 64 - {name: "SHRQconst", reg: gp11}, // unsigned arg0 >> aux.(int64), shift amount 0-63 + {name: "SHRQconst", reg: gp11}, // unsigned arg0 >> auxint, shift amount 0-63 {name: "SARQ", reg: gp21shift}, // signed arg0 >> arg1, shift amount is mod 64 - {name: "SARQconst", reg: gp11}, // signed arg0 >> aux.(int64), shift amount 0-63 + {name: "SARQconst", reg: gp11}, // signed arg0 >> auxint, shift amount 0-63 {name: "NEGQ", reg: gp11}, // -arg0 {name: "CMPQ", reg: gp2flags}, // arg0 compare to arg1 - {name: "CMPQconst", reg: gp1flags}, // arg0 compare to aux.(int64) + {name: "CMPQconst", reg: gp1flags}, // arg0 compare to auxint {name: "TESTQ", reg: gp2flags}, // (arg0 & arg1) compare to 0 {name: "TESTB", reg: gp2flags}, // (arg0 & arg1) compare to 0 @@ -125,21 +125,21 @@ func init() { {name: "CMOVQCC", reg: cmov}, // carry clear - {name: "MOVQconst", reg: gp01}, // aux.(int64) - {name: "LEAQ", reg: gp21}, // arg0 + arg1 + aux.(int64) - {name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + aux.(int64) - {name: "LEAQ4", reg: gp21}, // arg0 + 4*arg1 + aux.(int64) - {name: "LEAQ8", reg: gp21}, // arg0 + 8*arg1 + aux.(int64) - {name: "LEAQglobal", reg: gp01}, // no args. address of aux.(GlobalOffset) + {name: "MOVQconst", reg: gp01}, // auxint + {name: "LEAQ", reg: gp21}, // arg0 + arg1 + auxint + {name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + auxint + {name: "LEAQ4", reg: gp21}, // arg0 + 4*arg1 + auxint + {name: "LEAQ8", reg: gp21}, // arg0 + 8*arg1 + auxint + {name: "LEAQglobal", reg: gp01}, // no args. address of aux.(*gc.Sym) - {name: "MOVBload", reg: gpload}, // load byte from arg0+aux.(int64). arg1=mem + {name: "MOVBload", reg: gpload}, // load byte from arg0+auxint. arg1=mem {name: "MOVBQZXload", reg: gpload}, // ditto, extend to uint64 {name: "MOVBQSXload", reg: gpload}, // ditto, extend to int64 - {name: "MOVQload", reg: gpload}, // load 8 bytes from arg0+aux.(int64). arg1=mem - {name: "MOVQloadidx8", reg: gploadidx}, // load 8 bytes from arg0+8*arg1+aux.(int64). arg2=mem - {name: "MOVBstore", reg: gpstore}, // store byte in arg1 to arg0+aux.(int64). arg2=mem - {name: "MOVQstore", reg: gpstore}, // store 8 bytes in arg1 to arg0+aux.(int64). arg2=mem - {name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+aux.(int64). arg3=mem + {name: "MOVQload", reg: gpload}, // load 8 bytes from arg0+auxint. arg1=mem + {name: "MOVQloadidx8", reg: gploadidx}, // load 8 bytes from arg0+8*arg1+auxint. arg2=mem + {name: "MOVBstore", reg: gpstore}, // store byte in arg1 to arg0+auxint. arg2=mem + {name: "MOVQstore", reg: gpstore}, // store 8 bytes in arg1 to arg0+auxint. arg2=mem + {name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+auxint. arg3=mem // Load/store from global. Same as the above loads, but arg0 is missing and // aux is a GlobalOffset instead of an int64. @@ -147,7 +147,7 @@ func init() { {name: "MOVQstoreglobal"}, // store arg0 to aux.(GlobalOffset). arg1=memory, returns memory. //TODO: set register clobber to everything? - {name: "CALLstatic"}, // call static function. arg0=mem, returns mem + {name: "CALLstatic"}, // call static function aux.(*gc.Sym). arg0=mem, returns mem {name: "CALLclosure", reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, 0, nil}}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem returns mem {name: "REPMOVSB", reg: regInfo{[]regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")}, buildReg("DI SI CX"), nil}}, // move arg2 bytes from arg1 to arg0. arg3=mem, returns memory diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index b01952f402..e0bba1706f 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -3,13 +3,14 @@ // license that can be found in the LICENSE file. // values are specified using the following format: -// (op [aux] arg0 arg1 ...) +// (op [auxint] {aux} arg0 arg1 ...) // the type and aux fields are optional // on the matching side -// - the types and aux fields must match if they are specified. +// - the type, aux, and auxint fields must match if they are specified. // on the generated side // - the type of the top-level expression is the same as the one on the left-hand side. // - the type of any subexpressions must be specified explicitly. +// - auxint will be 0 if not specified. // - aux will be nil if not specified. // blocks are specified using the following format: @@ -19,15 +20,15 @@ // For now, the generated successors must be a permutation of the matched successors. // constant folding -(Add (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)+d.(int64)}]) -(Mul (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)*d.(int64)}]) -(IsInBounds (Const [c]) (Const [d])) -> (Const [inBounds(c.(int64),d.(int64))]) +(Add (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [c+d]) +(Mul (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [c*d]) +(IsInBounds (Const [c]) (Const [d])) -> (Const {inBounds(c,d)}) // tear apart slices // TODO: anything that generates a slice needs to go in here. (SlicePtr (Load ptr mem)) -> (Load ptr mem) -(SliceLen (Load ptr mem)) -> (Load (Add ptr (Const [int64(config.ptrSize)])) mem) -(SliceCap (Load ptr mem)) -> (Load (Add ptr (Const [int64(config.ptrSize*2)])) mem) +(SliceLen (Load ptr mem)) -> (Load (Add ptr (Const [config.ptrSize])) mem) +(SliceCap (Load ptr mem)) -> (Load (Add ptr (Const [config.ptrSize*2])) mem) // indexing operations // Note: bounds check has already been done @@ -39,11 +40,11 @@ (Store dst (Load src mem) mem) && t.Size() > 8 -> (Move [t.Size()] dst src mem) // string ops -(Const [s]) && t.IsString() -> (StringMake (OffPtr [2*config.ptrSize] (Global [config.fe.StringSym(s.(string))])) (Const [int64(len(s.(string)))])) // TODO: ptr +(Const {s}) && t.IsString() -> (StringMake (OffPtr [2*config.ptrSize] (Global {config.fe.StringSym(s.(string))})) (Const [int64(len(s.(string)))])) // TODO: ptr (Load ptr mem) && t.IsString() -> (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) (StringPtr (StringMake ptr _)) -> ptr (StringLen (StringMake _ len)) -> len (Store dst str mem) && str.Type.IsString() -> (Store (OffPtr [config.ptrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) -(If (Const [c]) yes no) && c.(bool) -> (Plain nil yes) -(If (Const [c]) yes no) && !c.(bool) -> (Plain nil no) +(If (Const {c}) yes no) && c.(bool) -> (Plain nil yes) +(If (Const {c}) yes no) && !c.(bool) -> (Plain nil no) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 4a691929b5..c168f2af05 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -37,9 +37,9 @@ var genericOps = []opData{ {name: "Func"}, // entry address of a function // Memory operations - {name: "Load"}, // Load from arg0+aux.(int64). arg1=memory - {name: "Store"}, // Store arg1 to arg0+aux.(int64). arg2=memory. Returns memory. - {name: "Move"}, // arg0=destptr, arg1=srcptr, arg2=mem, aux.(int64)=size. Returns memory. + {name: "Load"}, // Load from arg0. arg1=memory + {name: "Store"}, // Store arg1 to arg0. arg2=memory. Returns memory. + {name: "Move"}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size. Returns memory. // Function calls. Arguments to the call have already been written to the stack. // Return values appear on the stack. The method receiver, if any, is treated @@ -58,7 +58,7 @@ var genericOps = []opData{ // Indexing operations {name: "ArrayIndex"}, // arg0=array, arg1=index. Returns a[i] {name: "PtrIndex"}, // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type - {name: "OffPtr"}, // arg0 + aux.(int64) (arg0 and result are pointers) + {name: "OffPtr"}, // arg0 + auxint (arg0 and result are pointers) // Slices {name: "SliceMake"}, // arg0=ptr, arg1=len, arg2=cap diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index 4f689199a0..1a4b2c1b85 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -19,6 +19,7 @@ import ( "io/ioutil" "log" "os" + "regexp" "sort" "strings" ) @@ -29,9 +30,9 @@ import ( // sexpr are s-expressions (lisp-like parenthesized groupings) // sexpr ::= (opcode sexpr*) // | variable -// | [aux] // | -// | {code} +// | [auxint] +// | {aux} // // aux ::= variable | {code} // type ::= variable | {code} @@ -310,9 +311,9 @@ func genMatch0(w io.Writer, arch arch, match, v, fail string, m map[string]strin if a[0] == '<' { // type restriction t := a[1 : len(a)-1] // remove <> - if t[0] == '{' { + if !isVariable(t) { // code. We must match the results of this code. - fmt.Fprintf(w, "if %s.Type != %s %s", v, t[1:len(t)-1], fail) + fmt.Fprintf(w, "if %s.Type != %s %s", v, t, fail) } else { // variable if u, ok := m[t]; ok { @@ -324,11 +325,26 @@ func genMatch0(w io.Writer, arch arch, match, v, fail string, m map[string]strin } } } else if a[0] == '[' { - // aux restriction + // auxint restriction x := a[1 : len(a)-1] // remove [] - if x[0] == '{' { + if !isVariable(x) { // code - fmt.Fprintf(w, "if %s.Aux != %s %s", v, x[1:len(x)-1], fail) + fmt.Fprintf(w, "if %s.AuxInt != %s %s", v, x, fail) + } else { + // variable + if y, ok := m[x]; ok { + fmt.Fprintf(w, "if %s.AuxInt != %s %s", v, y, fail) + } else { + m[x] = v + ".AuxInt" + fmt.Fprintf(w, "%s := %s.AuxInt\n", x, v) + } + } + } else if a[0] == '{' { + // auxint restriction + x := a[1 : len(a)-1] // remove {} + if !isVariable(x) { + // code + fmt.Fprintf(w, "if %s.Aux != %s %s", v, x, fail) } else { // variable if y, ok := m[x]; ok { @@ -338,9 +354,6 @@ func genMatch0(w io.Writer, arch arch, match, v, fail string, m map[string]strin fmt.Fprintf(w, "%s := %s.Aux\n", x, v) } } - } else if a[0] == '{' { - fmt.Fprintf(w, "if %s.Args[%d] != %s %s", v, argnum, a[1:len(a)-1], fail) - argnum++ } else { // variable or sexpr genMatch0(w, arch, a, fmt.Sprintf("%s.Args[%d]", v, argnum), fail, m, false) @@ -357,6 +370,7 @@ func genResult0(w io.Writer, arch arch, result string, alloc *int, top bool) str // variable if top { fmt.Fprintf(w, "v.Op = %s.Op\n", result) + fmt.Fprintf(w, "v.AuxInt = %s.AuxInt\n", result) fmt.Fprintf(w, "v.Aux = %s.Aux\n", result) fmt.Fprintf(w, "v.resetArgs()\n") fmt.Fprintf(w, "v.AddArgs(%s.Args...)\n", result) @@ -370,32 +384,29 @@ func genResult0(w io.Writer, arch arch, result string, alloc *int, top bool) str if top { v = "v" fmt.Fprintf(w, "v.Op = %s\n", opName(s[0], arch)) + fmt.Fprintf(w, "v.AuxInt = 0\n") fmt.Fprintf(w, "v.Aux = nil\n") fmt.Fprintf(w, "v.resetArgs()\n") hasType = true } else { v = fmt.Sprintf("v%d", *alloc) *alloc++ - fmt.Fprintf(w, "%s := v.Block.NewValue(v.Line, %s, TypeInvalid, nil)\n", v, opName(s[0], arch)) + fmt.Fprintf(w, "%s := v.Block.NewValue0(v.Line, %s, TypeInvalid)\n", v, opName(s[0], arch)) } for _, a := range s[1:] { if a[0] == '<' { // type restriction t := a[1 : len(a)-1] // remove <> - if t[0] == '{' { - t = t[1 : len(t)-1] // remove {} - } fmt.Fprintf(w, "%s.Type = %s\n", v, t) hasType = true } else if a[0] == '[' { - // aux restriction + // auxint restriction x := a[1 : len(a)-1] // remove [] - if x[0] == '{' { - x = x[1 : len(x)-1] // remove {} - } - fmt.Fprintf(w, "%s.Aux = %s\n", v, x) + fmt.Fprintf(w, "%s.AuxInt = %s\n", v, x) } else if a[0] == '{' { - fmt.Fprintf(w, "%s.AddArg(%s)\n", v, a[1:len(a)-1]) + // aux restriction + x := a[1 : len(a)-1] // remove {} + fmt.Fprintf(w, "%s.Aux = %s\n", v, x) } else { // regular argument (sexpr or variable) x := genResult0(w, arch, a, alloc, false) @@ -504,3 +515,12 @@ func unbalanced(s string) bool { } return left != right } + +// isVariable reports whether s is a single Go alphanumeric identifier. +func isVariable(s string) bool { + b, err := regexp.MatchString("[A-Za-z_][A-Za-z_0-9]*", s) + if err != nil { + panic("bad variable regexp") + } + return b +} diff --git a/src/cmd/compile/internal/ssa/generic.go b/src/cmd/compile/internal/ssa/generic.go deleted file mode 100644 index ebbb1327d4..0000000000 --- a/src/cmd/compile/internal/ssa/generic.go +++ /dev/null @@ -1,424 +0,0 @@ -// autogenerated from rulegen/generic.rules: do not edit! -// generated with: go run rulegen/rulegen.go rulegen/generic.rules genericBlockRules genericValueRules generic.go -package ssa - -func genericValueRules(v *Value, config *Config) bool { - switch v.Op { - case OpAdd: - // match: (Add (Const [c]) (Const [d])) - // cond: is64BitInt(t) - // result: (Const [{c.(int64)+d.(int64)}]) - { - t := v.Type - if v.Args[0].Op != OpConst { - goto end8d047ed0ae9537b840adc79ea82c6e05 - } - c := v.Args[0].Aux - if v.Args[1].Op != OpConst { - goto end8d047ed0ae9537b840adc79ea82c6e05 - } - d := v.Args[1].Aux - if !(is64BitInt(t)) { - goto end8d047ed0ae9537b840adc79ea82c6e05 - } - v.Op = OpConst - v.Aux = nil - v.resetArgs() - v.Aux = c.(int64) + d.(int64) - return true - } - goto end8d047ed0ae9537b840adc79ea82c6e05 - end8d047ed0ae9537b840adc79ea82c6e05: - ; - case OpArrayIndex: - // match: (ArrayIndex (Load ptr mem) idx) - // cond: - // result: (Load (PtrIndex ptr idx) mem) - { - if v.Args[0].Op != OpLoad { - goto end3809f4c52270a76313e4ea26e6f0b753 - } - ptr := v.Args[0].Args[0] - mem := v.Args[0].Args[1] - idx := v.Args[1] - v.Op = OpLoad - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpPtrIndex, TypeInvalid, nil) - v0.Type = ptr.Type.Elem().Elem().PtrTo() - v0.AddArg(ptr) - v0.AddArg(idx) - v.AddArg(v0) - v.AddArg(mem) - return true - } - goto end3809f4c52270a76313e4ea26e6f0b753 - end3809f4c52270a76313e4ea26e6f0b753: - ; - case OpConst: - // match: (Const [s]) - // cond: t.IsString() - // result: (StringMake (OffPtr [2*config.ptrSize] (Global [config.fe.StringSym(s.(string))])) (Const [int64(len(s.(string)))])) - { - t := v.Type - s := v.Aux - if !(t.IsString()) { - goto end8442aa5b3f4e5b840055475883110372 - } - v.Op = OpStringMake - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) - v0.Type = TypeBytePtr - v0.Aux = 2 * config.ptrSize - v1 := v.Block.NewValue(v.Line, OpGlobal, TypeInvalid, nil) - v1.Type = TypeBytePtr - v1.Aux = config.fe.StringSym(s.(string)) - v0.AddArg(v1) - v.AddArg(v0) - v2 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) - v2.Type = config.Uintptr - v2.Aux = int64(len(s.(string))) - v.AddArg(v2) - return true - } - goto end8442aa5b3f4e5b840055475883110372 - end8442aa5b3f4e5b840055475883110372: - ; - case OpIsInBounds: - // match: (IsInBounds (Const [c]) (Const [d])) - // cond: - // result: (Const [inBounds(c.(int64),d.(int64))]) - { - if v.Args[0].Op != OpConst { - goto enddbd1a394d9b71ee64335361b8384865c - } - c := v.Args[0].Aux - if v.Args[1].Op != OpConst { - goto enddbd1a394d9b71ee64335361b8384865c - } - d := v.Args[1].Aux - v.Op = OpConst - v.Aux = nil - v.resetArgs() - v.Aux = inBounds(c.(int64), d.(int64)) - return true - } - goto enddbd1a394d9b71ee64335361b8384865c - enddbd1a394d9b71ee64335361b8384865c: - ; - case OpLoad: - // match: (Load ptr mem) - // cond: t.IsString() - // result: (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) - { - t := v.Type - ptr := v.Args[0] - mem := v.Args[1] - if !(t.IsString()) { - goto endd0afd003b70d726a1c5bbaf51fe06182 - } - v.Op = OpStringMake - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil) - v0.Type = TypeBytePtr - v0.AddArg(ptr) - v0.AddArg(mem) - v.AddArg(v0) - v1 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil) - v1.Type = config.Uintptr - v2 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) - v2.Type = TypeBytePtr - v2.Aux = config.ptrSize - v2.AddArg(ptr) - v1.AddArg(v2) - v1.AddArg(mem) - v.AddArg(v1) - return true - } - goto endd0afd003b70d726a1c5bbaf51fe06182 - endd0afd003b70d726a1c5bbaf51fe06182: - ; - case OpMul: - // match: (Mul (Const [c]) (Const [d])) - // cond: is64BitInt(t) - // result: (Const [{c.(int64)*d.(int64)}]) - { - t := v.Type - if v.Args[0].Op != OpConst { - goto end776610f88cf04f438242d76ed2b14f1c - } - c := v.Args[0].Aux - if v.Args[1].Op != OpConst { - goto end776610f88cf04f438242d76ed2b14f1c - } - d := v.Args[1].Aux - if !(is64BitInt(t)) { - goto end776610f88cf04f438242d76ed2b14f1c - } - v.Op = OpConst - v.Aux = nil - v.resetArgs() - v.Aux = c.(int64) * d.(int64) - return true - } - goto end776610f88cf04f438242d76ed2b14f1c - end776610f88cf04f438242d76ed2b14f1c: - ; - case OpPtrIndex: - // match: (PtrIndex ptr idx) - // cond: - // result: (Add ptr (Mul idx (Const [t.Elem().Size()]))) - { - t := v.Type - ptr := v.Args[0] - idx := v.Args[1] - v.Op = OpAdd - v.Aux = nil - v.resetArgs() - v.AddArg(ptr) - v0 := v.Block.NewValue(v.Line, OpMul, TypeInvalid, nil) - v0.Type = config.Uintptr - v0.AddArg(idx) - v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) - v1.Type = config.Uintptr - v1.Aux = t.Elem().Size() - v0.AddArg(v1) - v.AddArg(v0) - return true - } - goto end88c7c383675420d1581daeb899039fa8 - end88c7c383675420d1581daeb899039fa8: - ; - case OpSliceCap: - // match: (SliceCap (Load ptr mem)) - // cond: - // result: (Load (Add ptr (Const [int64(config.ptrSize*2)])) mem) - { - if v.Args[0].Op != OpLoad { - goto endc871dcd9a720b4290c9cae78fe147c8a - } - ptr := v.Args[0].Args[0] - mem := v.Args[0].Args[1] - v.Op = OpLoad - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil) - v0.Type = ptr.Type - v0.AddArg(ptr) - v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) - v1.Type = config.Uintptr - v1.Aux = int64(config.ptrSize * 2) - v0.AddArg(v1) - v.AddArg(v0) - v.AddArg(mem) - return true - } - goto endc871dcd9a720b4290c9cae78fe147c8a - endc871dcd9a720b4290c9cae78fe147c8a: - ; - case OpSliceLen: - // match: (SliceLen (Load ptr mem)) - // cond: - // result: (Load (Add ptr (Const [int64(config.ptrSize)])) mem) - { - if v.Args[0].Op != OpLoad { - goto end1eec05e44f5fc8944e7c176f98a74d92 - } - ptr := v.Args[0].Args[0] - mem := v.Args[0].Args[1] - v.Op = OpLoad - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil) - v0.Type = ptr.Type - v0.AddArg(ptr) - v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) - v1.Type = config.Uintptr - v1.Aux = int64(config.ptrSize) - v0.AddArg(v1) - v.AddArg(v0) - v.AddArg(mem) - return true - } - goto end1eec05e44f5fc8944e7c176f98a74d92 - end1eec05e44f5fc8944e7c176f98a74d92: - ; - case OpSlicePtr: - // match: (SlicePtr (Load ptr mem)) - // cond: - // result: (Load ptr mem) - { - if v.Args[0].Op != OpLoad { - goto end459613b83f95b65729d45c2ed663a153 - } - ptr := v.Args[0].Args[0] - mem := v.Args[0].Args[1] - v.Op = OpLoad - v.Aux = nil - v.resetArgs() - v.AddArg(ptr) - v.AddArg(mem) - return true - } - goto end459613b83f95b65729d45c2ed663a153 - end459613b83f95b65729d45c2ed663a153: - ; - case OpStore: - // match: (Store dst (Load src mem) mem) - // cond: t.Size() > 8 - // result: (Move [t.Size()] dst src mem) - { - dst := v.Args[0] - if v.Args[1].Op != OpLoad { - goto end324ffb6d2771808da4267f62c854e9c8 - } - t := v.Args[1].Type - src := v.Args[1].Args[0] - mem := v.Args[1].Args[1] - if v.Args[2] != v.Args[1].Args[1] { - goto end324ffb6d2771808da4267f62c854e9c8 - } - if !(t.Size() > 8) { - goto end324ffb6d2771808da4267f62c854e9c8 - } - v.Op = OpMove - v.Aux = nil - v.resetArgs() - v.Aux = t.Size() - v.AddArg(dst) - v.AddArg(src) - v.AddArg(mem) - return true - } - goto end324ffb6d2771808da4267f62c854e9c8 - end324ffb6d2771808da4267f62c854e9c8: - ; - // match: (Store dst str mem) - // cond: str.Type.IsString() - // result: (Store (OffPtr [config.ptrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) - { - dst := v.Args[0] - str := v.Args[1] - mem := v.Args[2] - if !(str.Type.IsString()) { - goto end410559d97aed8018f820cd88723de442 - } - v.Op = OpStore - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) - v0.Type = TypeBytePtr - v0.Aux = config.ptrSize - v0.AddArg(dst) - v.AddArg(v0) - v1 := v.Block.NewValue(v.Line, OpStringLen, TypeInvalid, nil) - v1.Type = config.Uintptr - v1.AddArg(str) - v.AddArg(v1) - v2 := v.Block.NewValue(v.Line, OpStore, TypeInvalid, nil) - v2.Type = TypeMem - v2.AddArg(dst) - v3 := v.Block.NewValue(v.Line, OpStringPtr, TypeInvalid, nil) - v3.Type = TypeBytePtr - v3.AddArg(str) - v2.AddArg(v3) - v2.AddArg(mem) - v.AddArg(v2) - return true - } - goto end410559d97aed8018f820cd88723de442 - end410559d97aed8018f820cd88723de442: - ; - case OpStringLen: - // match: (StringLen (StringMake _ len)) - // cond: - // result: len - { - if v.Args[0].Op != OpStringMake { - goto end0d922460b7e5ca88324034f4bd6c027c - } - len := v.Args[0].Args[1] - v.Op = len.Op - v.Aux = len.Aux - v.resetArgs() - v.AddArgs(len.Args...) - return true - } - goto end0d922460b7e5ca88324034f4bd6c027c - end0d922460b7e5ca88324034f4bd6c027c: - ; - case OpStringPtr: - // match: (StringPtr (StringMake ptr _)) - // cond: - // result: ptr - { - if v.Args[0].Op != OpStringMake { - goto end061edc5d85c73ad909089af2556d9380 - } - ptr := v.Args[0].Args[0] - v.Op = ptr.Op - v.Aux = ptr.Aux - v.resetArgs() - v.AddArgs(ptr.Args...) - return true - } - goto end061edc5d85c73ad909089af2556d9380 - end061edc5d85c73ad909089af2556d9380: - } - return false -} -func genericBlockRules(b *Block) bool { - switch b.Kind { - case BlockIf: - // match: (BlockIf (Const [c]) yes no) - // cond: c.(bool) - // result: (BlockPlain nil yes) - { - v := b.Control - if v.Op != OpConst { - goto endbe39807508a6192b4022c7293eb6e114 - } - c := v.Aux - yes := b.Succs[0] - no := b.Succs[1] - if !(c.(bool)) { - goto endbe39807508a6192b4022c7293eb6e114 - } - removePredecessor(b, no) - b.Kind = BlockPlain - b.Control = nil - b.Succs = b.Succs[:1] - b.Succs[0] = yes - return true - } - goto endbe39807508a6192b4022c7293eb6e114 - endbe39807508a6192b4022c7293eb6e114: - ; - // match: (BlockIf (Const [c]) yes no) - // cond: !c.(bool) - // result: (BlockPlain nil no) - { - v := b.Control - if v.Op != OpConst { - goto end69ac35957ebe0a77a5ef5103c1f79fbf - } - c := v.Aux - yes := b.Succs[0] - no := b.Succs[1] - if !(!c.(bool)) { - goto end69ac35957ebe0a77a5ef5103c1f79fbf - } - removePredecessor(b, yes) - b.Kind = BlockPlain - b.Control = nil - b.Succs = b.Succs[:1] - b.Succs[0] = no - return true - } - goto end69ac35957ebe0a77a5ef5103c1f79fbf - end69ac35957ebe0a77a5ef5103c1f79fbf: - } - return false -} diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 19a3fddd49..1103a67d0b 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -4,11 +4,6 @@ package ssa -import ( - "fmt" - "log" -) - // An Op encodes the specific operation that a Value performs. // Opcodes' semantics can be modified by the type and aux fields of the Value. // For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type. @@ -17,26 +12,6 @@ import ( // for each architecture. type Op int32 -// GlobalOffset represents a fixed offset within a global variable -type GlobalOffset struct { - Global interface{} // holds a *gc.Sym - Offset int64 -} - -// offset adds x to the location specified by g and returns it. -func (g GlobalOffset) offset(x int64) GlobalOffset { - y := g.Offset - z := x + y - if x^y >= 0 && x^z < 0 { - log.Panicf("offset overflow %d %d\n", x, y) - } - return GlobalOffset{g.Global, z} -} - -func (g GlobalOffset) String() string { - return fmt.Sprintf("%v+%d", g.Global, g.Offset) -} - type opInfo struct { name string reg regInfo diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index ed80a5b97d..6f7d619247 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -262,25 +262,23 @@ func regalloc(f *Func) { if len(w.Args) == 0 { // Materialize w if w.Op == OpFP || w.Op == OpSP || w.Op == OpGlobal { - c = b.NewValue1(w.Line, OpCopy, w.Type, nil, w) + c = b.NewValue1(w.Line, OpCopy, w.Type, w) } else { - c = b.NewValue(w.Line, w.Op, w.Type, w.Aux) + c = b.NewValue0IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux) } } else if len(w.Args) == 1 && (w.Args[0].Op == OpFP || w.Args[0].Op == OpSP || w.Args[0].Op == OpGlobal) { // Materialize offsets from SP/FP/Global - c = b.NewValue1(w.Line, w.Op, w.Type, w.Aux, w.Args[0]) + c = b.NewValue1IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux, w.Args[0]) } else if wreg != 0 { // Copy from another register. // Typically just an optimization, but this is // required if w is dirty. s := pickReg(wreg) // inv: s != r - c = b.NewValue(w.Line, OpCopy, w.Type, nil) - c.AddArg(regs[s].c) + c = b.NewValue1(w.Line, OpCopy, w.Type, regs[s].c) } else { // Load from home location - c = b.NewValue(w.Line, OpLoadReg8, w.Type, nil) - c.AddArg(w) + c = b.NewValue1(w.Line, OpLoadReg8, w.Type, w) } home = setloc(home, c, ®isters[r]) // Remember what we did @@ -337,7 +335,7 @@ func regalloc(f *Func) { } // Reissue v with new op, with r as its home. - c := b.NewValue(v.Line, v.Op, v.Type, v.Aux) + c := b.NewValue0IA(v.Line, v.Op, v.Type, v.AuxInt, v.Aux) c.AddArgs(v.Args...) home = setloc(home, c, ®isters[r]) @@ -406,7 +404,7 @@ func addPhiCopies(f *Func) { } for i, w := range v.Args { c := b.Preds[i] - cpy := c.NewValue1(w.Line, OpCopy, v.Type, nil, w) + cpy := c.NewValue1(w.Line, OpCopy, v.Type, w) v.Args[i] = cpy } } diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index fd0fc7e1a7..08ee7a9824 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -82,11 +82,8 @@ func typeSize(t Type) int64 { return t.Size() } -// addOff adds two offset aux values. Each should be an int64. Fails if wraparound happens. -func addOff(a, b interface{}) interface{} { - return addOffset(a.(int64), b.(int64)) -} -func addOffset(x, y int64) int64 { +// addOff adds two int64 offsets. Fails if wraparound happens. +func addOff(x, y int64) int64 { z := x + y // x and y have same sign and z has a different sign => overflow if x^y >= 0 && x^z < 0 { diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index f57cf7f333..d466e154e7 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -13,11 +13,12 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[1].Op != OpAMD64MOVQconst { goto endacffd55e74ee0ff59ad58a18ddfc9973 } - c := v.Args[1].Aux + c := v.Args[1].AuxInt v.Op = OpAMD64ADDQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } @@ -31,58 +32,59 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[0].Op != OpAMD64MOVQconst { goto end7166f476d744ab7a51125959d3d3c7e2 } - c := v.Args[0].Aux + c := v.Args[0].AuxInt x := v.Args[1] v.Op = OpAMD64ADDQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } goto end7166f476d744ab7a51125959d3d3c7e2 end7166f476d744ab7a51125959d3d3c7e2: ; - // match: (ADDQ x (SHLQconst [shift] y)) - // cond: shift.(int64) == 3 - // result: (LEAQ8 [int64(0)] x y) + // match: (ADDQ x (SHLQconst [3] y)) + // cond: + // result: (LEAQ8 x y) { x := v.Args[0] if v.Args[1].Op != OpAMD64SHLQconst { - goto endaf4f724e1e17f2b116d336c07da0165d + goto endc02313d35a0525d1d680cd58992e820d } - shift := v.Args[1].Aux - y := v.Args[1].Args[0] - if !(shift.(int64) == 3) { - goto endaf4f724e1e17f2b116d336c07da0165d + if v.Args[1].AuxInt != 3 { + goto endc02313d35a0525d1d680cd58992e820d } + y := v.Args[1].Args[0] v.Op = OpAMD64LEAQ8 + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(0) v.AddArg(x) v.AddArg(y) return true } - goto endaf4f724e1e17f2b116d336c07da0165d - endaf4f724e1e17f2b116d336c07da0165d: + goto endc02313d35a0525d1d680cd58992e820d + endc02313d35a0525d1d680cd58992e820d: ; case OpAMD64ADDQconst: // match: (ADDQconst [c] (LEAQ8 [d] x y)) // cond: // result: (LEAQ8 [addOff(c, d)] x y) { - c := v.Aux + c := v.AuxInt if v.Args[0].Op != OpAMD64LEAQ8 { goto ende2cc681c9abf9913288803fb1b39e639 } - d := v.Args[0].Aux + d := v.Args[0].AuxInt x := v.Args[0].Args[0] y := v.Args[0].Args[1] v.Op = OpAMD64LEAQ8 + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = addOff(c, d) + v.AuxInt = addOff(c, d) v.AddArg(x) v.AddArg(y) return true @@ -90,23 +92,23 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto ende2cc681c9abf9913288803fb1b39e639 ende2cc681c9abf9913288803fb1b39e639: ; - // match: (ADDQconst [off] x) - // cond: off.(int64) == 0 + // match: (ADDQconst [0] x) + // cond: // result: (Copy x) { - off := v.Aux - x := v.Args[0] - if !(off.(int64) == 0) { - goto endfa1c7cc5ac4716697e891376787f86ce + if v.AuxInt != 0 { + goto end288952f259d4a1842f1e8d5c389b3f28 } + x := v.Args[0] v.Op = OpCopy + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) return true } - goto endfa1c7cc5ac4716697e891376787f86ce - endfa1c7cc5ac4716697e891376787f86ce: + goto end288952f259d4a1842f1e8d5c389b3f28 + end288952f259d4a1842f1e8d5c389b3f28: ; case OpAMD64ANDQ: // match: (ANDQ x (MOVQconst [c])) @@ -117,11 +119,12 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[1].Op != OpAMD64MOVQconst { goto endb98096e3bbb90933e39c88bf41c688a9 } - c := v.Args[1].Aux + c := v.Args[1].AuxInt v.Op = OpAMD64ANDQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } @@ -135,12 +138,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[0].Op != OpAMD64MOVQconst { goto endd313fd1897a0d2bc79eff70159a81b6b } - c := v.Args[0].Aux + c := v.Args[0].AuxInt x := v.Args[1] v.Op = OpAMD64ANDQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } @@ -148,40 +152,40 @@ func rewriteValueAMD64(v *Value, config *Config) bool { endd313fd1897a0d2bc79eff70159a81b6b: ; case OpAMD64ANDQconst: - // match: (ANDQconst [c] _) - // cond: c.(int64) == 0 - // result: (MOVQconst [int64(0)]) + // match: (ANDQconst [0] _) + // cond: + // result: (MOVQconst [0]) { - c := v.Aux - if !(c.(int64) == 0) { - goto end383ada81cd8ffa88918387cd221acf5c + if v.AuxInt != 0 { + goto endf2afa4d9d31c344d6638dcdced383cf1 } v.Op = OpAMD64MOVQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(0) + v.AuxInt = 0 return true } - goto end383ada81cd8ffa88918387cd221acf5c - end383ada81cd8ffa88918387cd221acf5c: + goto endf2afa4d9d31c344d6638dcdced383cf1 + endf2afa4d9d31c344d6638dcdced383cf1: ; - // match: (ANDQconst [c] x) - // cond: c.(int64) == -1 + // match: (ANDQconst [-1] x) + // cond: // result: (Copy x) { - c := v.Aux - x := v.Args[0] - if !(c.(int64) == -1) { - goto end90aef368f20963a6ba27b3e9317ccf03 + if v.AuxInt != -1 { + goto end646afc7b328db89ad16ebfa156ae26e5 } + x := v.Args[0] v.Op = OpCopy + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) return true } - goto end90aef368f20963a6ba27b3e9317ccf03 - end90aef368f20963a6ba27b3e9317ccf03: + goto end646afc7b328db89ad16ebfa156ae26e5 + end646afc7b328db89ad16ebfa156ae26e5: ; case OpAdd: // match: (Add x y) @@ -195,6 +199,7 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endf031c523d7dd08e4b8e7010a94cd94c9 } v.Op = OpAMD64ADDQ + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) @@ -215,6 +220,7 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end35a02a1587264e40cf1055856ff8445a } v.Op = OpAMD64ADDL + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) @@ -226,54 +232,56 @@ func rewriteValueAMD64(v *Value, config *Config) bool { ; case OpAMD64CMOVQCC: // match: (CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) - // cond: inBounds(d.(int64), c.(int64)) + // cond: inBounds(d, c) // result: (Copy x) { if v.Args[0].Op != OpAMD64CMPQconst { - goto endb8f4f98b06c41e559bf0323e798c147a + goto endd5357f3fd5516dcc859c8c5b3c9efaa4 } - c := v.Args[0].Aux + c := v.Args[0].AuxInt if v.Args[0].Args[0].Op != OpAMD64MOVQconst { - goto endb8f4f98b06c41e559bf0323e798c147a + goto endd5357f3fd5516dcc859c8c5b3c9efaa4 } - d := v.Args[0].Args[0].Aux + d := v.Args[0].Args[0].AuxInt x := v.Args[2] - if !(inBounds(d.(int64), c.(int64))) { - goto endb8f4f98b06c41e559bf0323e798c147a + if !(inBounds(d, c)) { + goto endd5357f3fd5516dcc859c8c5b3c9efaa4 } v.Op = OpCopy + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) return true } - goto endb8f4f98b06c41e559bf0323e798c147a - endb8f4f98b06c41e559bf0323e798c147a: + goto endd5357f3fd5516dcc859c8c5b3c9efaa4 + endd5357f3fd5516dcc859c8c5b3c9efaa4: ; // match: (CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) - // cond: !inBounds(d.(int64), c.(int64)) + // cond: !inBounds(d, c) // result: (Copy x) { if v.Args[0].Op != OpAMD64CMPQconst { - goto end29407b5c4731ac24b4c25600752cb895 + goto end6ad8b1758415a9afe758272b34970d5d } - c := v.Args[0].Aux + c := v.Args[0].AuxInt if v.Args[0].Args[0].Op != OpAMD64MOVQconst { - goto end29407b5c4731ac24b4c25600752cb895 + goto end6ad8b1758415a9afe758272b34970d5d } - d := v.Args[0].Args[0].Aux + d := v.Args[0].Args[0].AuxInt x := v.Args[1] - if !(!inBounds(d.(int64), c.(int64))) { - goto end29407b5c4731ac24b4c25600752cb895 + if !(!inBounds(d, c)) { + goto end6ad8b1758415a9afe758272b34970d5d } v.Op = OpCopy + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) return true } - goto end29407b5c4731ac24b4c25600752cb895 - end29407b5c4731ac24b4c25600752cb895: + goto end6ad8b1758415a9afe758272b34970d5d + end6ad8b1758415a9afe758272b34970d5d: ; case OpAMD64CMPQ: // match: (CMPQ x (MOVQconst [c])) @@ -284,12 +292,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[1].Op != OpAMD64MOVQconst { goto end32ef1328af280ac18fa8045a3502dae9 } - c := v.Args[1].Aux + c := v.Args[1].AuxInt v.Op = OpAMD64CMPQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) - v.Aux = c + v.AuxInt = c return true } goto end32ef1328af280ac18fa8045a3502dae9 @@ -302,15 +311,16 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[0].Op != OpAMD64MOVQconst { goto endf8ca12fe79290bc82b11cfa463bc9413 } - c := v.Args[0].Aux + c := v.Args[0].AuxInt x := v.Args[1] v.Op = OpAMD64InvertFlags + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAMD64CMPQconst, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64CMPQconst, TypeInvalid) v0.Type = TypeFlags v0.AddArg(x) - v0.Aux = c + v0.AuxInt = c v.AddArg(v0) return true } @@ -326,6 +336,7 @@ func rewriteValueAMD64(v *Value, config *Config) bool { closure := v.Args[1] mem := v.Args[2] v.Op = OpAMD64CALLclosure + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(entry) @@ -342,33 +353,35 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // result: (MOVQconst [val]) { t := v.Type - val := v.Aux + val := v.AuxInt if !(is64BitInt(t)) { goto end7f5c5b34093fbc6860524cb803ee51bf } v.Op = OpAMD64MOVQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = val + v.AuxInt = val return true } goto end7f5c5b34093fbc6860524cb803ee51bf end7f5c5b34093fbc6860524cb803ee51bf: ; case OpGlobal: - // match: (Global [sym]) + // match: (Global {sym}) // cond: - // result: (LEAQglobal [GlobalOffset{sym,0}]) + // result: (LEAQglobal {sym}) { sym := v.Aux v.Op = OpAMD64LEAQglobal + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = GlobalOffset{sym, 0} + v.Aux = sym return true } - goto end3a3c76fac0e2e53c0e1c60b9524e6f1c - end3a3c76fac0e2e53c0e1c60b9524e6f1c: + goto end8f47b6f351fecaeded45abbe5c2beec0 + end8f47b6f351fecaeded45abbe5c2beec0: ; case OpIsInBounds: // match: (IsInBounds idx len) @@ -378,9 +391,10 @@ func rewriteValueAMD64(v *Value, config *Config) bool { idx := v.Args[0] len := v.Args[1] v.Op = OpAMD64SETB + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAMD64CMPQ, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64CMPQ, TypeInvalid) v0.Type = TypeFlags v0.AddArg(idx) v0.AddArg(len) @@ -397,9 +411,10 @@ func rewriteValueAMD64(v *Value, config *Config) bool { { p := v.Args[0] v.Op = OpAMD64SETNE + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAMD64TESTQ, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64TESTQ, TypeInvalid) v0.Type = TypeFlags v0.AddArg(p) v0.AddArg(p) @@ -420,9 +435,10 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endcecf13a952d4c6c2383561c7d68a3cf9 } v.Op = OpAMD64SETL + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAMD64CMPQ, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64CMPQ, TypeInvalid) v0.Type = TypeFlags v0.AddArg(x) v0.AddArg(y) @@ -435,94 +451,96 @@ func rewriteValueAMD64(v *Value, config *Config) bool { case OpLoad: // match: (Load ptr mem) // cond: t.IsBoolean() - // result: (MOVBload [int64(0)] ptr mem) + // result: (MOVBload ptr mem) { t := v.Type ptr := v.Args[0] mem := v.Args[1] if !(t.IsBoolean()) { - goto end73f21632e56c3614902d3c29c82dc4ea + goto endc119e594c7f8e8ce5ff97c00b501dba0 } v.Op = OpAMD64MOVBload + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(0) v.AddArg(ptr) v.AddArg(mem) return true } - goto end73f21632e56c3614902d3c29c82dc4ea - end73f21632e56c3614902d3c29c82dc4ea: + goto endc119e594c7f8e8ce5ff97c00b501dba0 + endc119e594c7f8e8ce5ff97c00b501dba0: ; // match: (Load ptr mem) // cond: (is64BitInt(t) || isPtr(t)) - // result: (MOVQload [int64(0)] ptr mem) + // result: (MOVQload ptr mem) { t := v.Type ptr := v.Args[0] mem := v.Args[1] if !(is64BitInt(t) || isPtr(t)) { - goto end581ce5a20901df1b8143448ba031685b + goto end7c4c53acf57ebc5f03273652ba1d5934 } v.Op = OpAMD64MOVQload + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(0) v.AddArg(ptr) v.AddArg(mem) return true } - goto end581ce5a20901df1b8143448ba031685b - end581ce5a20901df1b8143448ba031685b: + goto end7c4c53acf57ebc5f03273652ba1d5934 + end7c4c53acf57ebc5f03273652ba1d5934: ; case OpLsh: // match: (Lsh x y) // cond: is64BitInt(t) - // result: (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) + // result: (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [64] y))) { t := v.Type x := v.Args[0] y := v.Args[1] if !(is64BitInt(t)) { - goto end7002b6d4becf7d1247e3756641ccb0c2 + goto end5d9e2211940fbc82536685578cf37d08 } v.Op = OpAMD64ANDQ + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAMD64SHLQ, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64SHLQ, TypeInvalid) v0.Type = t v0.AddArg(x) v0.AddArg(y) v.AddArg(v0) - v1 := v.Block.NewValue(v.Line, OpAMD64SBBQcarrymask, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpAMD64SBBQcarrymask, TypeInvalid) v1.Type = t - v2 := v.Block.NewValue(v.Line, OpAMD64CMPQconst, TypeInvalid, nil) + v2 := v.Block.NewValue0(v.Line, OpAMD64CMPQconst, TypeInvalid) v2.Type = TypeFlags - v2.Aux = int64(64) + v2.AuxInt = 64 v2.AddArg(y) v1.AddArg(v2) v.AddArg(v1) return true } - goto end7002b6d4becf7d1247e3756641ccb0c2 - end7002b6d4becf7d1247e3756641ccb0c2: + goto end5d9e2211940fbc82536685578cf37d08 + end5d9e2211940fbc82536685578cf37d08: ; case OpAMD64MOVQload: // match: (MOVQload [off1] (ADDQconst [off2] ptr) mem) // cond: // result: (MOVQload [addOff(off1, off2)] ptr mem) { - off1 := v.Aux + off1 := v.AuxInt if v.Args[0].Op != OpAMD64ADDQconst { goto end843d29b538c4483b432b632e5666d6e3 } - off2 := v.Args[0].Aux + off2 := v.Args[0].AuxInt ptr := v.Args[0].Args[0] mem := v.Args[1] v.Op = OpAMD64MOVQload + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = addOff(off1, off2) + v.AuxInt = addOff(off1, off2) v.AddArg(ptr) v.AddArg(mem) return true @@ -534,18 +552,19 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // cond: // result: (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) { - off1 := v.Aux + off1 := v.AuxInt if v.Args[0].Op != OpAMD64LEAQ8 { goto end02f5ad148292c46463e7c20d3b821735 } - off2 := v.Args[0].Aux + off2 := v.Args[0].AuxInt ptr := v.Args[0].Args[0] idx := v.Args[0].Args[1] mem := v.Args[1] v.Op = OpAMD64MOVQloadidx8 + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = addOff(off1, off2) + v.AuxInt = addOff(off1, off2) v.AddArg(ptr) v.AddArg(idx) v.AddArg(mem) @@ -559,18 +578,19 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // cond: // result: (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) { - off1 := v.Aux + off1 := v.AuxInt if v.Args[0].Op != OpAMD64ADDQconst { goto ende81e44bcfb11f90916ccb440c590121f } - off2 := v.Args[0].Aux + off2 := v.Args[0].AuxInt ptr := v.Args[0].Args[0] idx := v.Args[1] mem := v.Args[2] v.Op = OpAMD64MOVQloadidx8 + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = addOff(off1, off2) + v.AuxInt = addOff(off1, off2) v.AddArg(ptr) v.AddArg(idx) v.AddArg(mem) @@ -584,18 +604,19 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // cond: // result: (MOVQstore [addOff(off1, off2)] ptr val mem) { - off1 := v.Aux + off1 := v.AuxInt if v.Args[0].Op != OpAMD64ADDQconst { goto end2108c693a43c79aed10b9246c39c80aa } - off2 := v.Args[0].Aux + off2 := v.Args[0].AuxInt ptr := v.Args[0].Args[0] val := v.Args[1] mem := v.Args[2] v.Op = OpAMD64MOVQstore + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = addOff(off1, off2) + v.AuxInt = addOff(off1, off2) v.AddArg(ptr) v.AddArg(val) v.AddArg(mem) @@ -608,19 +629,20 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // cond: // result: (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) { - off1 := v.Aux + off1 := v.AuxInt if v.Args[0].Op != OpAMD64LEAQ8 { goto endce1db8c8d37c8397c500a2068a65c215 } - off2 := v.Args[0].Aux + off2 := v.Args[0].AuxInt ptr := v.Args[0].Args[0] idx := v.Args[0].Args[1] val := v.Args[1] mem := v.Args[2] v.Op = OpAMD64MOVQstoreidx8 + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = addOff(off1, off2) + v.AuxInt = addOff(off1, off2) v.AddArg(ptr) v.AddArg(idx) v.AddArg(val) @@ -635,19 +657,20 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // cond: // result: (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) { - off1 := v.Aux + off1 := v.AuxInt if v.Args[0].Op != OpAMD64ADDQconst { goto end01c970657b0fdefeab82458c15022163 } - off2 := v.Args[0].Aux + off2 := v.Args[0].AuxInt ptr := v.Args[0].Args[0] idx := v.Args[1] val := v.Args[2] mem := v.Args[3] v.Op = OpAMD64MOVQstoreidx8 + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = addOff(off1, off2) + v.AuxInt = addOff(off1, off2) v.AddArg(ptr) v.AddArg(idx) v.AddArg(val) @@ -659,26 +682,27 @@ func rewriteValueAMD64(v *Value, config *Config) bool { ; case OpAMD64MULQ: // match: (MULQ x (MOVQconst [c])) - // cond: c.(int64) == int64(int32(c.(int64))) + // cond: c == int64(int32(c)) // result: (MULQconst [c] x) { x := v.Args[0] if v.Args[1].Op != OpAMD64MOVQconst { - goto ende8c09b194fcde7d9cdc69f2deff86304 + goto end680a32a37babfff4bfa7d23be592a131 } - c := v.Args[1].Aux - if !(c.(int64) == int64(int32(c.(int64)))) { - goto ende8c09b194fcde7d9cdc69f2deff86304 + c := v.Args[1].AuxInt + if !(c == int64(int32(c))) { + goto end680a32a37babfff4bfa7d23be592a131 } v.Op = OpAMD64MULQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } - goto ende8c09b194fcde7d9cdc69f2deff86304 - ende8c09b194fcde7d9cdc69f2deff86304: + goto end680a32a37babfff4bfa7d23be592a131 + end680a32a37babfff4bfa7d23be592a131: ; // match: (MULQ (MOVQconst [c]) x) // cond: @@ -687,12 +711,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[0].Op != OpAMD64MOVQconst { goto endc6e18d6968175d6e58eafa6dcf40c1b8 } - c := v.Args[0].Aux + c := v.Args[0].AuxInt x := v.Args[1] v.Op = OpAMD64MULQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } @@ -700,67 +725,68 @@ func rewriteValueAMD64(v *Value, config *Config) bool { endc6e18d6968175d6e58eafa6dcf40c1b8: ; case OpAMD64MULQconst: - // match: (MULQconst [c] x) - // cond: c.(int64) == 8 - // result: (SHLQconst [int64(3)] x) + // match: (MULQconst [8] x) + // cond: + // result: (SHLQconst [3] x) { - c := v.Aux - x := v.Args[0] - if !(c.(int64) == 8) { - goto end7e16978c56138324ff2abf91fd6d94d4 + if v.AuxInt != 8 { + goto ende8d313a52a134fb2e1c0beb54ea599fd } + x := v.Args[0] v.Op = OpAMD64SHLQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(3) + v.AuxInt = 3 v.AddArg(x) return true } - goto end7e16978c56138324ff2abf91fd6d94d4 - end7e16978c56138324ff2abf91fd6d94d4: + goto ende8d313a52a134fb2e1c0beb54ea599fd + ende8d313a52a134fb2e1c0beb54ea599fd: ; - // match: (MULQconst [c] x) - // cond: c.(int64) == 64 - // result: (SHLQconst [int64(5)] x) + // match: (MULQconst [64] x) + // cond: + // result: (SHLQconst [5] x) { - c := v.Aux - x := v.Args[0] - if !(c.(int64) == 64) { - goto end2c7a02f230e4b311ac3a4e22f70a4f08 + if v.AuxInt != 64 { + goto end75c0c250c703f89e6c43d718dd5ea3c0 } + x := v.Args[0] v.Op = OpAMD64SHLQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(5) + v.AuxInt = 5 v.AddArg(x) return true } - goto end2c7a02f230e4b311ac3a4e22f70a4f08 - end2c7a02f230e4b311ac3a4e22f70a4f08: + goto end75c0c250c703f89e6c43d718dd5ea3c0 + end75c0c250c703f89e6c43d718dd5ea3c0: ; case OpMove: // match: (Move [size] dst src mem) // cond: - // result: (REPMOVSB dst src (Const [size.(int64)]) mem) + // result: (REPMOVSB dst src (Const [size]) mem) { - size := v.Aux + size := v.AuxInt dst := v.Args[0] src := v.Args[1] mem := v.Args[2] v.Op = OpAMD64REPMOVSB + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(dst) v.AddArg(src) - v0 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v0.Type = TypeUInt64 - v0.Aux = size.(int64) + v0.AuxInt = size v.AddArg(v0) v.AddArg(mem) return true } - goto end48909259b265a6bb2a076bc2c2dc7d1f - end48909259b265a6bb2a076bc2c2dc7d1f: + goto end1b2d226705fd31dbbe74e3286af178ea + end1b2d226705fd31dbbe74e3286af178ea: ; case OpMul: // match: (Mul x y) @@ -774,6 +800,7 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endfab0d598f376ecba45a22587d50f7aff } v.Op = OpAMD64MULQ + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) @@ -788,12 +815,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // cond: // result: (ADDQconst [off] ptr) { - off := v.Aux + off := v.AuxInt ptr := v.Args[0] v.Op = OpAMD64ADDQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = off + v.AuxInt = off v.AddArg(ptr) return true } @@ -803,67 +831,69 @@ func rewriteValueAMD64(v *Value, config *Config) bool { case OpRsh: // match: (Rsh x y) // cond: is64BitInt(t) && !t.IsSigned() - // result: (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [int64(64)] y))) + // result: (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [64] y))) { t := v.Type x := v.Args[0] y := v.Args[1] if !(is64BitInt(t) && !t.IsSigned()) { - goto end9463ddaa21c75f8e15cb9f31472a2e23 + goto ende3e068773b8e6def1eaedb4f404ca6e5 } v.Op = OpAMD64ANDQ + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAMD64SHRQ, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64SHRQ, TypeInvalid) v0.Type = t v0.AddArg(x) v0.AddArg(y) v.AddArg(v0) - v1 := v.Block.NewValue(v.Line, OpAMD64SBBQcarrymask, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpAMD64SBBQcarrymask, TypeInvalid) v1.Type = t - v2 := v.Block.NewValue(v.Line, OpAMD64CMPQconst, TypeInvalid, nil) + v2 := v.Block.NewValue0(v.Line, OpAMD64CMPQconst, TypeInvalid) v2.Type = TypeFlags - v2.Aux = int64(64) + v2.AuxInt = 64 v2.AddArg(y) v1.AddArg(v2) v.AddArg(v1) return true } - goto end9463ddaa21c75f8e15cb9f31472a2e23 - end9463ddaa21c75f8e15cb9f31472a2e23: + goto ende3e068773b8e6def1eaedb4f404ca6e5 + ende3e068773b8e6def1eaedb4f404ca6e5: ; // match: (Rsh x y) // cond: is64BitInt(t) && t.IsSigned() - // result: (SARQ x (CMOVQCC (CMPQconst [int64(64)] y) (Const [int64(63)]) y)) + // result: (SARQ x (CMOVQCC (CMPQconst [64] y) (Const [63]) y)) { t := v.Type x := v.Args[0] y := v.Args[1] if !(is64BitInt(t) && t.IsSigned()) { - goto endd297b9e569ac90bf815bd4c425d3b770 + goto end901ea4851cd5d2277a1ca1bee8f69d59 } v.Op = OpAMD64SARQ + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.Type = t v.AddArg(x) - v0 := v.Block.NewValue(v.Line, OpAMD64CMOVQCC, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64CMOVQCC, TypeInvalid) v0.Type = t - v1 := v.Block.NewValue(v.Line, OpAMD64CMPQconst, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpAMD64CMPQconst, TypeInvalid) v1.Type = TypeFlags - v1.Aux = int64(64) + v1.AuxInt = 64 v1.AddArg(y) v0.AddArg(v1) - v2 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) + v2 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v2.Type = t - v2.Aux = int64(63) + v2.AuxInt = 63 v0.AddArg(v2) v0.AddArg(y) v.AddArg(v0) return true } - goto endd297b9e569ac90bf815bd4c425d3b770 - endd297b9e569ac90bf815bd4c425d3b770: + goto end901ea4851cd5d2277a1ca1bee8f69d59 + end901ea4851cd5d2277a1ca1bee8f69d59: ; case OpAMD64SARQ: // match: (SARQ x (MOVQconst [c])) @@ -874,11 +904,12 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[1].Op != OpAMD64MOVQconst { goto end031712b4008075e25a5827dcb8dd3ebb } - c := v.Args[1].Aux + c := v.Args[1].AuxInt v.Op = OpAMD64SARQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } @@ -887,52 +918,54 @@ func rewriteValueAMD64(v *Value, config *Config) bool { ; case OpAMD64SBBQcarrymask: // match: (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) - // cond: inBounds(d.(int64), c.(int64)) - // result: (Const [int64(-1)]) + // cond: inBounds(d, c) + // result: (Const [-1]) { if v.Args[0].Op != OpAMD64CMPQconst { - goto end35e369f67ebb9423a1d36a808a16777c + goto endf67d323ecef000dbcd15d7e031c3475e } - c := v.Args[0].Aux + c := v.Args[0].AuxInt if v.Args[0].Args[0].Op != OpAMD64MOVQconst { - goto end35e369f67ebb9423a1d36a808a16777c + goto endf67d323ecef000dbcd15d7e031c3475e } - d := v.Args[0].Args[0].Aux - if !(inBounds(d.(int64), c.(int64))) { - goto end35e369f67ebb9423a1d36a808a16777c + d := v.Args[0].Args[0].AuxInt + if !(inBounds(d, c)) { + goto endf67d323ecef000dbcd15d7e031c3475e } v.Op = OpConst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(-1) + v.AuxInt = -1 return true } - goto end35e369f67ebb9423a1d36a808a16777c - end35e369f67ebb9423a1d36a808a16777c: + goto endf67d323ecef000dbcd15d7e031c3475e + endf67d323ecef000dbcd15d7e031c3475e: ; // match: (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) - // cond: !inBounds(d.(int64), c.(int64)) - // result: (Const [int64(0)]) + // cond: !inBounds(d, c) + // result: (Const [0]) { if v.Args[0].Op != OpAMD64CMPQconst { - goto end5c767fada028c1cc96210af2cf098aff + goto end4157ddea9c4f71bfabfd6fa50e1208ed } - c := v.Args[0].Aux + c := v.Args[0].AuxInt if v.Args[0].Args[0].Op != OpAMD64MOVQconst { - goto end5c767fada028c1cc96210af2cf098aff + goto end4157ddea9c4f71bfabfd6fa50e1208ed } - d := v.Args[0].Args[0].Aux - if !(!inBounds(d.(int64), c.(int64))) { - goto end5c767fada028c1cc96210af2cf098aff + d := v.Args[0].Args[0].AuxInt + if !(!inBounds(d, c)) { + goto end4157ddea9c4f71bfabfd6fa50e1208ed } v.Op = OpConst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(0) + v.AuxInt = 0 return true } - goto end5c767fada028c1cc96210af2cf098aff - end5c767fada028c1cc96210af2cf098aff: + goto end4157ddea9c4f71bfabfd6fa50e1208ed + end4157ddea9c4f71bfabfd6fa50e1208ed: ; case OpAMD64SETG: // match: (SETG (InvertFlags x)) @@ -944,6 +977,7 @@ func rewriteValueAMD64(v *Value, config *Config) bool { } x := v.Args[0].Args[0] v.Op = OpAMD64SETL + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) @@ -962,6 +996,7 @@ func rewriteValueAMD64(v *Value, config *Config) bool { } x := v.Args[0].Args[0] v.Op = OpAMD64SETG + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) @@ -979,11 +1014,12 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[1].Op != OpAMD64MOVQconst { goto endcca412bead06dc3d56ef034a82d184d6 } - c := v.Args[1].Aux + c := v.Args[1].AuxInt v.Op = OpAMD64SHLQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } @@ -999,11 +1035,12 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[1].Op != OpAMD64MOVQconst { goto endbb0d3a04dd2b810cb3dbdf7ef665f22b } - c := v.Args[1].Aux + c := v.Args[1].AuxInt v.Op = OpAMD64SHRQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c + v.AuxInt = c v.AddArg(x) return true } @@ -1019,12 +1056,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[1].Op != OpAMD64MOVQconst { goto end5a74a63bd9ad15437717c6df3b25eebb } - c := v.Args[1].Aux + c := v.Args[1].AuxInt v.Op = OpAMD64SUBQconst + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) - v.Aux = c + v.AuxInt = c return true } goto end5a74a63bd9ad15437717c6df3b25eebb @@ -1038,15 +1076,16 @@ func rewriteValueAMD64(v *Value, config *Config) bool { if v.Args[0].Op != OpAMD64MOVQconst { goto end78e66b6fc298684ff4ac8aec5ce873c9 } - c := v.Args[0].Aux + c := v.Args[0].AuxInt x := v.Args[1] v.Op = OpAMD64NEGQ + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAMD64SUBQconst, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64SUBQconst, TypeInvalid) v0.Type = t v0.AddArg(x) - v0.Aux = c + v0.AuxInt = c v.AddArg(v0) return true } @@ -1058,12 +1097,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { // cond: // result: (CALLstatic [target] mem) { - target := v.Aux + target := v.AuxInt mem := v.Args[0] v.Op = OpAMD64CALLstatic + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = target + v.AuxInt = target v.AddArg(mem) return true } @@ -1073,25 +1113,25 @@ func rewriteValueAMD64(v *Value, config *Config) bool { case OpStore: // match: (Store ptr val mem) // cond: (is64BitInt(val.Type) || isPtr(val.Type)) - // result: (MOVQstore [int64(0)] ptr val mem) + // result: (MOVQstore ptr val mem) { ptr := v.Args[0] val := v.Args[1] mem := v.Args[2] if !(is64BitInt(val.Type) || isPtr(val.Type)) { - goto end9680b43f504bc06f9fab000823ce471a + goto endbaeb60123806948cd2433605820d5af1 } v.Op = OpAMD64MOVQstore + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = int64(0) v.AddArg(ptr) v.AddArg(val) v.AddArg(mem) return true } - goto end9680b43f504bc06f9fab000823ce471a - end9680b43f504bc06f9fab000823ce471a: + goto endbaeb60123806948cd2433605820d5af1 + endbaeb60123806948cd2433605820d5af1: ; case OpSub: // match: (Sub x y) @@ -1105,6 +1145,7 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto ende6ef29f885a8ecf3058212bb95917323 } v.Op = OpAMD64SUBQ + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(x) @@ -1254,7 +1295,7 @@ func rewriteBlockAMD64(b *Block) bool { goto end7e22019fb0effc80f85c05ea30bdb5d9 } b.Kind = BlockAMD64NE - v0 := v.Block.NewValue(v.Line, OpAMD64TESTB, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAMD64TESTB, TypeInvalid) v0.Type = TypeFlags v0.AddArg(cond) v0.AddArg(cond) diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index e38439de14..0ecc436343 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -7,28 +7,29 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpAdd: // match: (Add (Const [c]) (Const [d])) // cond: is64BitInt(t) - // result: (Const [{c.(int64)+d.(int64)}]) + // result: (Const [c+d]) { t := v.Type if v.Args[0].Op != OpConst { - goto end8d047ed0ae9537b840adc79ea82c6e05 + goto end279f4ea85ed10e5ffc5b53f9e060529b } - c := v.Args[0].Aux + c := v.Args[0].AuxInt if v.Args[1].Op != OpConst { - goto end8d047ed0ae9537b840adc79ea82c6e05 + goto end279f4ea85ed10e5ffc5b53f9e060529b } - d := v.Args[1].Aux + d := v.Args[1].AuxInt if !(is64BitInt(t)) { - goto end8d047ed0ae9537b840adc79ea82c6e05 + goto end279f4ea85ed10e5ffc5b53f9e060529b } v.Op = OpConst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c.(int64) + d.(int64) + v.AuxInt = c + d return true } - goto end8d047ed0ae9537b840adc79ea82c6e05 - end8d047ed0ae9537b840adc79ea82c6e05: + goto end279f4ea85ed10e5ffc5b53f9e060529b + end279f4ea85ed10e5ffc5b53f9e060529b: ; case OpArrayIndex: // match: (ArrayIndex (Load ptr mem) idx) @@ -42,9 +43,10 @@ func rewriteValuegeneric(v *Value, config *Config) bool { mem := v.Args[0].Args[1] idx := v.Args[1] v.Op = OpLoad + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpPtrIndex, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpPtrIndex, TypeInvalid) v0.Type = ptr.Type.Elem().Elem().PtrTo() v0.AddArg(ptr) v0.AddArg(idx) @@ -56,56 +58,58 @@ func rewriteValuegeneric(v *Value, config *Config) bool { end3809f4c52270a76313e4ea26e6f0b753: ; case OpConst: - // match: (Const [s]) + // match: (Const {s}) // cond: t.IsString() - // result: (StringMake (OffPtr [2*config.ptrSize] (Global [config.fe.StringSym(s.(string))])) (Const [int64(len(s.(string)))])) + // result: (StringMake (OffPtr [2*config.ptrSize] (Global {config.fe.StringSym(s.(string))})) (Const [int64(len(s.(string)))])) { t := v.Type s := v.Aux if !(t.IsString()) { - goto end8442aa5b3f4e5b840055475883110372 + goto end6d6321106a054a5984b2ed0acec52a5b } v.Op = OpStringMake + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid) v0.Type = TypeBytePtr - v0.Aux = 2 * config.ptrSize - v1 := v.Block.NewValue(v.Line, OpGlobal, TypeInvalid, nil) + v0.AuxInt = 2 * config.ptrSize + v1 := v.Block.NewValue0(v.Line, OpGlobal, TypeInvalid) v1.Type = TypeBytePtr v1.Aux = config.fe.StringSym(s.(string)) v0.AddArg(v1) v.AddArg(v0) - v2 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) + v2 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v2.Type = config.Uintptr - v2.Aux = int64(len(s.(string))) + v2.AuxInt = int64(len(s.(string))) v.AddArg(v2) return true } - goto end8442aa5b3f4e5b840055475883110372 - end8442aa5b3f4e5b840055475883110372: + goto end6d6321106a054a5984b2ed0acec52a5b + end6d6321106a054a5984b2ed0acec52a5b: ; case OpIsInBounds: // match: (IsInBounds (Const [c]) (Const [d])) // cond: - // result: (Const [inBounds(c.(int64),d.(int64))]) + // result: (Const {inBounds(c,d)}) { if v.Args[0].Op != OpConst { - goto enddbd1a394d9b71ee64335361b8384865c + goto enda96ccac78df2d17ae96c8baf2af2e189 } - c := v.Args[0].Aux + c := v.Args[0].AuxInt if v.Args[1].Op != OpConst { - goto enddbd1a394d9b71ee64335361b8384865c + goto enda96ccac78df2d17ae96c8baf2af2e189 } - d := v.Args[1].Aux + d := v.Args[1].AuxInt v.Op = OpConst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = inBounds(c.(int64), d.(int64)) + v.Aux = inBounds(c, d) return true } - goto enddbd1a394d9b71ee64335361b8384865c - enddbd1a394d9b71ee64335361b8384865c: + goto enda96ccac78df2d17ae96c8baf2af2e189 + enda96ccac78df2d17ae96c8baf2af2e189: ; case OpLoad: // match: (Load ptr mem) @@ -119,18 +123,19 @@ func rewriteValuegeneric(v *Value, config *Config) bool { goto endd0afd003b70d726a1c5bbaf51fe06182 } v.Op = OpStringMake + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpLoad, TypeInvalid) v0.Type = TypeBytePtr v0.AddArg(ptr) v0.AddArg(mem) v.AddArg(v0) - v1 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpLoad, TypeInvalid) v1.Type = config.Uintptr - v2 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) + v2 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid) v2.Type = TypeBytePtr - v2.Aux = config.ptrSize + v2.AuxInt = config.ptrSize v2.AddArg(ptr) v1.AddArg(v2) v1.AddArg(mem) @@ -143,28 +148,29 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpMul: // match: (Mul (Const [c]) (Const [d])) // cond: is64BitInt(t) - // result: (Const [{c.(int64)*d.(int64)}]) + // result: (Const [c*d]) { t := v.Type if v.Args[0].Op != OpConst { - goto end776610f88cf04f438242d76ed2b14f1c + goto endd82095c6a872974522d33aaff1ee07be } - c := v.Args[0].Aux + c := v.Args[0].AuxInt if v.Args[1].Op != OpConst { - goto end776610f88cf04f438242d76ed2b14f1c + goto endd82095c6a872974522d33aaff1ee07be } - d := v.Args[1].Aux + d := v.Args[1].AuxInt if !(is64BitInt(t)) { - goto end776610f88cf04f438242d76ed2b14f1c + goto endd82095c6a872974522d33aaff1ee07be } v.Op = OpConst + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = c.(int64) * d.(int64) + v.AuxInt = c * d return true } - goto end776610f88cf04f438242d76ed2b14f1c - end776610f88cf04f438242d76ed2b14f1c: + goto endd82095c6a872974522d33aaff1ee07be + endd82095c6a872974522d33aaff1ee07be: ; case OpPtrIndex: // match: (PtrIndex ptr idx) @@ -175,15 +181,16 @@ func rewriteValuegeneric(v *Value, config *Config) bool { ptr := v.Args[0] idx := v.Args[1] v.Op = OpAdd + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(ptr) - v0 := v.Block.NewValue(v.Line, OpMul, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpMul, TypeInvalid) v0.Type = config.Uintptr v0.AddArg(idx) - v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v1.Type = config.Uintptr - v1.Aux = t.Elem().Size() + v1.AuxInt = t.Elem().Size() v0.AddArg(v1) v.AddArg(v0) return true @@ -194,56 +201,58 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpSliceCap: // match: (SliceCap (Load ptr mem)) // cond: - // result: (Load (Add ptr (Const [int64(config.ptrSize*2)])) mem) + // result: (Load (Add ptr (Const [config.ptrSize*2])) mem) { if v.Args[0].Op != OpLoad { - goto endc871dcd9a720b4290c9cae78fe147c8a + goto end919cfa3d3539eb2e06a435d5f89654b9 } ptr := v.Args[0].Args[0] mem := v.Args[0].Args[1] v.Op = OpLoad + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAdd, TypeInvalid) v0.Type = ptr.Type v0.AddArg(ptr) - v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v1.Type = config.Uintptr - v1.Aux = int64(config.ptrSize * 2) + v1.AuxInt = config.ptrSize * 2 v0.AddArg(v1) v.AddArg(v0) v.AddArg(mem) return true } - goto endc871dcd9a720b4290c9cae78fe147c8a - endc871dcd9a720b4290c9cae78fe147c8a: + goto end919cfa3d3539eb2e06a435d5f89654b9 + end919cfa3d3539eb2e06a435d5f89654b9: ; case OpSliceLen: // match: (SliceLen (Load ptr mem)) // cond: - // result: (Load (Add ptr (Const [int64(config.ptrSize)])) mem) + // result: (Load (Add ptr (Const [config.ptrSize])) mem) { if v.Args[0].Op != OpLoad { - goto end1eec05e44f5fc8944e7c176f98a74d92 + goto end3d74a5ef07180a709a91052da88bcd01 } ptr := v.Args[0].Args[0] mem := v.Args[0].Args[1] v.Op = OpLoad + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpAdd, TypeInvalid) v0.Type = ptr.Type v0.AddArg(ptr) - v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v1.Type = config.Uintptr - v1.Aux = int64(config.ptrSize) + v1.AuxInt = config.ptrSize v0.AddArg(v1) v.AddArg(v0) v.AddArg(mem) return true } - goto end1eec05e44f5fc8944e7c176f98a74d92 - end1eec05e44f5fc8944e7c176f98a74d92: + goto end3d74a5ef07180a709a91052da88bcd01 + end3d74a5ef07180a709a91052da88bcd01: ; case OpSlicePtr: // match: (SlicePtr (Load ptr mem)) @@ -256,6 +265,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool { ptr := v.Args[0].Args[0] mem := v.Args[0].Args[1] v.Op = OpLoad + v.AuxInt = 0 v.Aux = nil v.resetArgs() v.AddArg(ptr) @@ -284,9 +294,10 @@ func rewriteValuegeneric(v *Value, config *Config) bool { goto end324ffb6d2771808da4267f62c854e9c8 } v.Op = OpMove + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.Aux = t.Size() + v.AuxInt = t.Size() v.AddArg(dst) v.AddArg(src) v.AddArg(mem) @@ -306,21 +317,22 @@ func rewriteValuegeneric(v *Value, config *Config) bool { goto end410559d97aed8018f820cd88723de442 } v.Op = OpStore + v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) + v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid) v0.Type = TypeBytePtr - v0.Aux = config.ptrSize + v0.AuxInt = config.ptrSize v0.AddArg(dst) v.AddArg(v0) - v1 := v.Block.NewValue(v.Line, OpStringLen, TypeInvalid, nil) + v1 := v.Block.NewValue0(v.Line, OpStringLen, TypeInvalid) v1.Type = config.Uintptr v1.AddArg(str) v.AddArg(v1) - v2 := v.Block.NewValue(v.Line, OpStore, TypeInvalid, nil) + v2 := v.Block.NewValue0(v.Line, OpStore, TypeInvalid) v2.Type = TypeMem v2.AddArg(dst) - v3 := v.Block.NewValue(v.Line, OpStringPtr, TypeInvalid, nil) + v3 := v.Block.NewValue0(v.Line, OpStringPtr, TypeInvalid) v3.Type = TypeBytePtr v3.AddArg(str) v2.AddArg(v3) @@ -341,6 +353,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool { } len := v.Args[0].Args[1] v.Op = len.Op + v.AuxInt = len.AuxInt v.Aux = len.Aux v.resetArgs() v.AddArgs(len.Args...) @@ -359,6 +372,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool { } ptr := v.Args[0].Args[0] v.Op = ptr.Op + v.AuxInt = ptr.AuxInt v.Aux = ptr.Aux v.resetArgs() v.AddArgs(ptr.Args...) @@ -372,19 +386,19 @@ func rewriteValuegeneric(v *Value, config *Config) bool { func rewriteBlockgeneric(b *Block) bool { switch b.Kind { case BlockIf: - // match: (If (Const [c]) yes no) + // match: (If (Const {c}) yes no) // cond: c.(bool) // result: (Plain nil yes) { v := b.Control if v.Op != OpConst { - goto end60cde11c1be8092f493d9cda982445ca + goto end915e334b6388fed7d63e09aa69ecb05c } c := v.Aux yes := b.Succs[0] no := b.Succs[1] if !(c.(bool)) { - goto end60cde11c1be8092f493d9cda982445ca + goto end915e334b6388fed7d63e09aa69ecb05c } removePredecessor(b, no) b.Kind = BlockPlain @@ -393,22 +407,22 @@ func rewriteBlockgeneric(b *Block) bool { b.Succs[0] = yes return true } - goto end60cde11c1be8092f493d9cda982445ca - end60cde11c1be8092f493d9cda982445ca: + goto end915e334b6388fed7d63e09aa69ecb05c + end915e334b6388fed7d63e09aa69ecb05c: ; - // match: (If (Const [c]) yes no) + // match: (If (Const {c}) yes no) // cond: !c.(bool) // result: (Plain nil no) { v := b.Control if v.Op != OpConst { - goto endf2a5efbfd2d40dead087c33685c8f30b + goto end6452ee3a5bb02c708bddc3181c3ea3cb } c := v.Aux yes := b.Succs[0] no := b.Succs[1] if !(!c.(bool)) { - goto endf2a5efbfd2d40dead087c33685c8f30b + goto end6452ee3a5bb02c708bddc3181c3ea3cb } removePredecessor(b, yes) b.Kind = BlockPlain @@ -417,8 +431,8 @@ func rewriteBlockgeneric(b *Block) bool { b.Succs[0] = no return true } - goto endf2a5efbfd2d40dead087c33685c8f30b - endf2a5efbfd2d40dead087c33685c8f30b: + goto end6452ee3a5bb02c708bddc3181c3ea3cb + end6452ee3a5bb02c708bddc3181c3ea3cb: } return false } diff --git a/src/cmd/compile/internal/ssa/schedule_test.go b/src/cmd/compile/internal/ssa/schedule_test.go index 4830f79628..a7c33d9d59 100644 --- a/src/cmd/compile/internal/ssa/schedule_test.go +++ b/src/cmd/compile/internal/ssa/schedule_test.go @@ -11,15 +11,15 @@ func TestSchedule(t *testing.T) { cases := []fun{ Fun(c, "entry", Bloc("entry", - Valu("mem0", OpArg, TypeMem, ".mem"), - Valu("ptr", OpConst, TypeInt64, 0xABCD), - Valu("v", OpConst, TypeInt64, 12), - Valu("mem1", OpStore, TypeMem, 32, "ptr", "v", "mem0"), - Valu("mem2", OpStore, TypeMem, 32, "ptr", "v", "mem1"), - Valu("mem3", OpStore, TypeInt64, "ptr", "sum", "mem2"), - Valu("l1", OpLoad, TypeInt64, 16, "ptr", "mem1"), - Valu("l2", OpLoad, TypeInt64, 8, "ptr", "mem2"), - Valu("sum", OpAdd, TypeInt64, "l1", "l2"), + Valu("mem0", OpArg, TypeMem, 0, ".mem"), + Valu("ptr", OpConst, TypeInt64, 0xABCD, nil), + Valu("v", OpConst, TypeInt64, 12, nil), + Valu("mem1", OpStore, TypeMem, 0, nil, "ptr", "v", "mem0"), + Valu("mem2", OpStore, TypeMem, 0, nil, "ptr", "v", "mem1"), + Valu("mem3", OpStore, TypeInt64, 0, nil, "ptr", "sum", "mem2"), + Valu("l1", OpLoad, TypeInt64, 0, nil, "ptr", "mem1"), + Valu("l2", OpLoad, TypeInt64, 0, nil, "ptr", "mem2"), + Valu("sum", OpAdd, TypeInt64, 0, nil, "l1", "l2"), Goto("exit")), Bloc("exit", Exit("mem3"))), diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go index bba4f782dc..b4b4f47ff0 100644 --- a/src/cmd/compile/internal/ssa/shift_test.go +++ b/src/cmd/compile/internal/ssa/shift_test.go @@ -28,14 +28,14 @@ func makeConstShiftFunc(c *Config, amount int64, op Op, typ Type) fun { ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"} fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, ".mem"), - Valu("FP", OpFP, TypeUInt64, nil), - Valu("argptr", OpOffPtr, ptyp, int64(8), "FP"), - Valu("resptr", OpOffPtr, ptyp, int64(16), "FP"), - Valu("load", OpLoad, typ, nil, "argptr", "mem"), - Valu("c", OpConst, TypeUInt64, amount), - Valu("shift", op, typ, nil, "load", "c"), - Valu("store", OpStore, TypeMem, nil, "resptr", "shift", "mem"), + Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("FP", OpFP, TypeUInt64, 0, nil), + Valu("argptr", OpOffPtr, ptyp, 8, nil, "FP"), + Valu("resptr", OpOffPtr, ptyp, 16, nil, "FP"), + Valu("load", OpLoad, typ, 0, nil, "argptr", "mem"), + Valu("c", OpConst, TypeUInt64, amount, nil), + Valu("shift", op, typ, 0, nil, "load", "c"), + Valu("store", OpStore, TypeMem, 0, nil, "resptr", "shift", "mem"), Exit("store"))) Compile(fun.f) return fun diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index d47c8c7b02..cb1688f51c 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -92,14 +92,14 @@ func stackalloc(f *Func) { case OpAMD64ADDQ: // (ADDQ (FP) x) -> (LEAQ [n] (SP) x) v.Op = OpAMD64LEAQ - v.Aux = n + v.AuxInt = n case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVBload, OpAMD64MOVQloadidx8: if v.Op == OpAMD64MOVQloadidx8 && i == 1 { // Note: we could do it, but it is probably an error log.Panicf("can't do FP->SP adjust on index slot of load %s", v.Op) } // eg: (MOVQload [c] (FP) mem) -> (MOVQload [c+n] (SP) mem) - v.Aux = addOffset(v.Aux.(int64), n) + v.AuxInt = addOff(v.AuxInt, n) default: log.Panicf("can't do FP->SP adjust on %s", v.Op) // TODO: OpCopy -> ADDQ diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index f249bba43e..3ed1f3c2b9 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -22,7 +22,9 @@ type Value struct { Type Type // Auxiliary info for this value. The type of this information depends on the opcode and type. - Aux interface{} + // AuxInt is used for integer values, Aux is used for other values. + AuxInt int64 + Aux interface{} // Arguments of this value Args []*Value @@ -53,8 +55,11 @@ func (v *Value) String() string { func (v *Value) LongString() string { s := fmt.Sprintf("v%d = %s", v.ID, v.Op.String()) s += " <" + v.Type.String() + ">" + if v.AuxInt != 0 { + s += fmt.Sprintf(" [%d]", v.AuxInt) + } if v.Aux != nil { - s += fmt.Sprintf(" [%v]", v.Aux) + s += fmt.Sprintf(" {%v}", v.Aux) } for _, a := range v.Args { s += fmt.Sprintf(" %v", a) -- cgit v1.3 From 73054f572940c8d4c905f2ba996a3c6aaeeb28df Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Sun, 14 Jun 2015 11:38:46 -0700 Subject: [dev.ssa] cmd/compile/internal/ssa: adds for 8,16,32 bit ints Change-Id: I33025a4a41fd91f6ee317d33a6eebf27fa00ab51 Reviewed-on: https://go-review.googlesource.com/11115 Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/ssa.go | 73 ++++-- src/cmd/compile/internal/ssa/gen/AMD64.rules | 22 +- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 30 ++- src/cmd/compile/internal/ssa/opGen.go | 131 ++++++++++ src/cmd/compile/internal/ssa/rewrite.go | 8 + src/cmd/compile/internal/ssa/rewriteAMD64.go | 359 +++++++++++++++++++++++++-- src/cmd/compile/internal/ssa/stackalloc.go | 5 +- 7 files changed, 565 insertions(+), 63 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 2f116464d4..81a55e4065 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -373,6 +373,9 @@ func (s *state) expr(n *Node) *ssa.Value { case OCONVNOP: x := s.expr(n.Left) return s.newValue1(ssa.OpConvNop, n.Type, x) + case OCONV: + x := s.expr(n.Left) + return s.newValue1(ssa.OpConvert, n.Type, x) // binary ops case OLT: @@ -766,6 +769,43 @@ func genValue(v *ssa.Value) { p.From.Index = regnum(v.Args[1]) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) + case ssa.OpAMD64ADDL: + p := Prog(x86.ALEAL) + p.From.Type = obj.TYPE_MEM + p.From.Reg = regnum(v.Args[0]) + p.From.Scale = 1 + p.From.Index = regnum(v.Args[1]) + p.To.Type = obj.TYPE_REG + p.To.Reg = regnum(v) + case ssa.OpAMD64ADDW: + p := Prog(x86.ALEAW) + p.From.Type = obj.TYPE_MEM + p.From.Reg = regnum(v.Args[0]) + p.From.Scale = 1 + p.From.Index = regnum(v.Args[1]) + p.To.Type = obj.TYPE_REG + p.To.Reg = regnum(v) + case ssa.OpAMD64ADDB, ssa.OpAMD64ANDQ: + r := regnum(v) + x := regnum(v.Args[0]) + y := regnum(v.Args[1]) + if x != r && y != r { + p := Prog(x86.AMOVQ) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = r + x = r + } + p := Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.To.Type = obj.TYPE_REG + p.To.Reg = r + if x == r { + p.From.Reg = y + } else { + p.From.Reg = x + } case ssa.OpAMD64ADDQconst: // TODO: use addq instead of leaq if target is in the right register. p := Prog(x86.ALEAQ) @@ -866,27 +906,6 @@ func genValue(v *ssa.Value) { p.From.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG p.To.Reg = r - case ssa.OpAMD64ANDQ: - r := regnum(v) - x := regnum(v.Args[0]) - y := regnum(v.Args[1]) - if x != r && y != r { - p := Prog(x86.AMOVQ) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = r - x = r - } - p := Prog(x86.AANDQ) - p.From.Type = obj.TYPE_REG - p.To.Type = obj.TYPE_REG - p.To.Reg = r - if x == r { - p.From.Reg = y - } else { - p.From.Reg = x - } case ssa.OpAMD64LEAQ: p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM @@ -915,7 +934,7 @@ func genValue(v *ssa.Value) { p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = x - case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVBload: + case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload: p := Prog(v.Op.Asm()) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) @@ -931,13 +950,19 @@ func genValue(v *ssa.Value) { p.From.Index = regnum(v.Args[1]) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) - case ssa.OpAMD64MOVQstore: - p := Prog(x86.AMOVQ) + case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore: + p := Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[1]) p.To.Type = obj.TYPE_MEM p.To.Reg = regnum(v.Args[0]) p.To.Offset = v.AuxInt + case ssa.OpAMD64MOVLQSX, ssa.OpAMD64MOVWQSX, ssa.OpAMD64MOVBQSX: + p := Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = regnum(v.Args[0]) + p.To.Type = obj.TYPE_REG + p.To.Reg = regnum(v) case ssa.OpCopy: // TODO: lower to MOVQ earlier? if v.Type.IsMemory() { return diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index b62c8767d1..aa4e807712 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -14,10 +14,21 @@ // Lowering arithmetic (Add x y) && (is64BitInt(t) || isPtr(t)) -> (ADDQ x y) -(Add x y) && is32BitInt(t) -> (ADDL x y) +(Add x y) && is32BitInt(t) && !isSigned(t) -> (ADDL x y) +(Add x y) && is32BitInt(t) && isSigned(t) -> (MOVLQSX (ADDL x y)) +(Add x y) && is16BitInt(t) && !isSigned(t) -> (ADDW x y) +(Add x y) && is16BitInt(t) && isSigned(t) -> (MOVWQSX (ADDW x y)) +(Add x y) && is8BitInt(t) && !isSigned(t) -> (ADDB x y) +(Add x y) && is8BitInt(t) && isSigned(t) -> (MOVBQSX (ADDB x y)) (Sub x y) && is64BitInt(t) -> (SUBQ x y) (Mul x y) && is64BitInt(t) -> (MULQ x y) +(MOVLstore ptr (MOVLQSX x) mem) -> (MOVLstore ptr x mem) +(MOVWstore ptr (MOVWQSX x) mem) -> (MOVWstore ptr x mem) +(MOVBstore ptr (MOVBQSX x) mem) -> (MOVBstore ptr x mem) + +(Convert x) && t.IsInteger() && x.Type.IsInteger() -> (Copy x) + // Lowering shifts // Note: unsigned shifts need to return 0 if shift amount is >= 64. // mask = shift >= 64 ? 0 : 0xffffffffffffffff @@ -38,9 +49,14 @@ (Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y)) -(Load ptr mem) && t.IsBoolean() -> (MOVBload ptr mem) (Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload ptr mem) +(Load ptr mem) && is32BitInt(t) -> (MOVLload ptr mem) +(Load ptr mem) && is16BitInt(t) -> (MOVWload ptr mem) +(Load ptr mem) && (t.IsBoolean() || is8BitInt(t)) -> (MOVBload ptr mem) (Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore ptr val mem) +(Store ptr val mem) && is32BitInt(val.Type) -> (MOVLstore ptr val mem) +(Store ptr val mem) && is16BitInt(val.Type) -> (MOVWstore ptr val mem) +(Store ptr val mem) && is8BitInt(val.Type) -> (MOVBstore ptr val mem) // checks (IsNonNil p) -> (SETNE (TESTQ p p)) @@ -50,7 +66,7 @@ (OffPtr [off] ptr) -> (ADDQconst [off] ptr) -(Const [val]) && is64BitInt(t) -> (MOVQconst [val]) +(Const [val]) && t.IsInteger() -> (MOVQconst [val]) // block rewrites (If (SETL cmp) yes no) -> (LT cmp yes no) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index d99f793179..3733ba9721 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -4,9 +4,7 @@ package main -import ( - "strings" -) +import "strings" // copied from ../../amd64/reg.go var regNamesAMD64 = []string{ @@ -127,6 +125,10 @@ func init() { {name: "CMOVQCC", reg: cmov}, // carry clear + {name: "MOVLQSX", reg: gp11, asm: "MOVLQSX"}, // extend arg0 from int32 to int64 + {name: "MOVWQSX", reg: gp11, asm: "MOVWQSX"}, // extend arg0 from int16 to int64 + {name: "MOVBQSX", reg: gp11, asm: "MOVBQSX"}, // extend arg0 from int8 to int64 + {name: "MOVQconst", reg: gp01}, // auxint {name: "LEAQ", reg: gp21}, // arg0 + arg1 + auxint {name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + auxint @@ -134,14 +136,18 @@ func init() { {name: "LEAQ8", reg: gp21}, // arg0 + 8*arg1 + auxint {name: "LEAQglobal", reg: gp01}, // no args. address of aux.(*gc.Sym) - {name: "MOVBload", reg: gpload, asm: "MOVB"}, // load byte from arg0+auxint. arg1=mem - {name: "MOVBQZXload", reg: gpload}, // ditto, extend to uint64 - {name: "MOVBQSXload", reg: gpload}, // ditto, extend to int64 - {name: "MOVQload", reg: gpload, asm: "MOVQ"}, // load 8 bytes from arg0+auxint. arg1=mem - {name: "MOVQloadidx8", reg: gploadidx}, // load 8 bytes from arg0+8*arg1+auxint. arg2=mem - {name: "MOVBstore", reg: gpstore, asm: "MOVB"}, // store byte in arg1 to arg0+auxint. arg2=mem - {name: "MOVQstore", reg: gpstore, asm: "MOVQ"}, // store 8 bytes in arg1 to arg0+auxint. arg2=mem - {name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+auxint. arg3=mem + {name: "MOVBload", reg: gpload, asm: "MOVB"}, // load byte from arg0+auxint. arg1=mem + {name: "MOVBQZXload", reg: gpload}, // ditto, extend to uint64 + {name: "MOVBQSXload", reg: gpload}, // ditto, extend to int64 + {name: "MOVWload", reg: gpload, asm: "MOVW"}, // load 2 bytes from arg0+auxint. arg1=mem + {name: "MOVLload", reg: gpload, asm: "MOVL"}, // load 4 bytes from arg0+auxint. arg1=mem + {name: "MOVQload", reg: gpload, asm: "MOVQ"}, // load 8 bytes from arg0+auxint. arg1=mem + {name: "MOVQloadidx8", reg: gploadidx, asm: "MOVQ"}, // load 8 bytes from arg0+8*arg1+auxint. arg2=mem + {name: "MOVBstore", reg: gpstore, asm: "MOVB"}, // store byte in arg1 to arg0+auxint. arg2=mem + {name: "MOVWstore", reg: gpstore, asm: "MOVW"}, // store 2 bytes in arg1 to arg0+auxint. arg2=mem + {name: "MOVLstore", reg: gpstore, asm: "MOVL"}, // store 4 bytes in arg1 to arg0+auxint. arg2=mem + {name: "MOVQstore", reg: gpstore, asm: "MOVQ"}, // store 8 bytes in arg1 to arg0+auxint. arg2=mem + {name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+auxint. arg3=mem // Load/store from global. Same as the above loads, but arg0 is missing and // aux is a GlobalOffset instead of an int64. @@ -155,6 +161,8 @@ func init() { {name: "REPMOVSB", reg: regInfo{[]regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")}, buildReg("DI SI CX"), nil}}, // move arg2 bytes from arg1 to arg0. arg3=mem, returns memory {name: "ADDL", reg: gp21, asm: "ADDL"}, // arg0+arg1 + {name: "ADDW", reg: gp21, asm: "ADDW"}, // arg0+arg1 + {name: "ADDB", reg: gp21, asm: "ADDB"}, // arg0+arg1 // (InvertFlags (CMPQ a b)) == (CMPQ b a) // So if we want (SETL (CMPQ a b)) but we can't do that because a is a constant, diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index dfe611e8f4..1116be101c 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -76,6 +76,9 @@ const ( OpAMD64SETGE OpAMD64SETB OpAMD64CMOVQCC + OpAMD64MOVLQSX + OpAMD64MOVWQSX + OpAMD64MOVBQSX OpAMD64MOVQconst OpAMD64LEAQ OpAMD64LEAQ2 @@ -85,9 +88,13 @@ const ( OpAMD64MOVBload OpAMD64MOVBQZXload OpAMD64MOVBQSXload + OpAMD64MOVWload + OpAMD64MOVLload OpAMD64MOVQload OpAMD64MOVQloadidx8 OpAMD64MOVBstore + OpAMD64MOVWstore + OpAMD64MOVLstore OpAMD64MOVQstore OpAMD64MOVQstoreidx8 OpAMD64MOVQloadglobal @@ -96,6 +103,8 @@ const ( OpAMD64CALLclosure OpAMD64REPMOVSB OpAMD64ADDL + OpAMD64ADDW + OpAMD64ADDB OpAMD64InvertFlags OpAdd @@ -492,6 +501,45 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "MOVLQSX", + asm: x86.AMOVLQSX, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, + { + name: "MOVWQSX", + asm: x86.AMOVWQSX, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, + { + name: "MOVBQSX", + asm: x86.AMOVBQSX, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, { name: "MOVQconst", reg: regInfo{ @@ -604,6 +652,34 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "MOVWload", + asm: x86.AMOVW, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 0, + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, + { + name: "MOVLload", + asm: x86.AMOVL, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 0, + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, { name: "MOVQload", asm: x86.AMOVQ, @@ -620,6 +696,7 @@ var opcodeTable = [...]opInfo{ }, { name: "MOVQloadidx8", + asm: x86.AMOVQ, reg: regInfo{ inputs: []regMask{ 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP @@ -645,6 +722,32 @@ var opcodeTable = [...]opInfo{ outputs: []regMask{}, }, }, + { + name: "MOVWstore", + asm: x86.AMOVW, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 0, + }, + clobbers: 0, + outputs: []regMask{}, + }, + }, + { + name: "MOVLstore", + asm: x86.AMOVL, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 0, + }, + clobbers: 0, + outputs: []regMask{}, + }, + }, { name: "MOVQstore", asm: x86.AMOVQ, @@ -733,6 +836,34 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ADDW", + asm: x86.AADDW, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, + { + name: "ADDB", + asm: x86.AADDB, + reg: regInfo{ + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, { name: "InvertFlags", reg: regInfo{ diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 08ee7a9824..77aa2b07b4 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -70,6 +70,14 @@ func is32BitInt(t Type) bool { return t.Size() == 4 && t.IsInteger() } +func is16BitInt(t Type) bool { + return t.Size() == 2 && t.IsInteger() +} + +func is8BitInt(t Type) bool { + return t.Size() == 1 && t.IsInteger() +} + func isPtr(t Type) bool { return t.IsPtr() } diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index a3ec3e7cc1..3490adadd7 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -210,14 +210,14 @@ func rewriteValueAMD64(v *Value, config *Config) bool { endf031c523d7dd08e4b8e7010a94cd94c9: ; // match: (Add x y) - // cond: is32BitInt(t) + // cond: is32BitInt(t) && !isSigned(t) // result: (ADDL x y) { t := v.Type x := v.Args[0] y := v.Args[1] - if !(is32BitInt(t)) { - goto end35a02a1587264e40cf1055856ff8445a + if !(is32BitInt(t) && !isSigned(t)) { + goto endce1730b0a04d773ed8029e7eac4f3a50 } v.Op = OpAMD64ADDL v.AuxInt = 0 @@ -227,8 +227,122 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(y) return true } - goto end35a02a1587264e40cf1055856ff8445a - end35a02a1587264e40cf1055856ff8445a: + goto endce1730b0a04d773ed8029e7eac4f3a50 + endce1730b0a04d773ed8029e7eac4f3a50: + ; + // match: (Add x y) + // cond: is32BitInt(t) && isSigned(t) + // result: (MOVLQSX (ADDL x y)) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is32BitInt(t) && isSigned(t)) { + goto end86e07674e2e9d2e1fc5a8f5f74375513 + } + v.Op = OpAMD64MOVLQSX + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue0(v.Line, OpAMD64ADDL, TypeInvalid) + v0.Type = t + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto end86e07674e2e9d2e1fc5a8f5f74375513 + end86e07674e2e9d2e1fc5a8f5f74375513: + ; + // match: (Add x y) + // cond: is16BitInt(t) && !isSigned(t) + // result: (ADDW x y) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is16BitInt(t) && !isSigned(t)) { + goto end99632c2482f1963513f12a317c588800 + } + v.Op = OpAMD64ADDW + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto end99632c2482f1963513f12a317c588800 + end99632c2482f1963513f12a317c588800: + ; + // match: (Add x y) + // cond: is16BitInt(t) && isSigned(t) + // result: (MOVWQSX (ADDW x y)) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is16BitInt(t) && isSigned(t)) { + goto endd215b5658d14e7d1cb469a516aa554e9 + } + v.Op = OpAMD64MOVWQSX + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue0(v.Line, OpAMD64ADDW, TypeInvalid) + v0.Type = t + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto endd215b5658d14e7d1cb469a516aa554e9 + endd215b5658d14e7d1cb469a516aa554e9: + ; + // match: (Add x y) + // cond: is8BitInt(t) && !isSigned(t) + // result: (ADDB x y) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is8BitInt(t) && !isSigned(t)) { + goto end41d7f409a1e1076e9645e2e90b7220ce + } + v.Op = OpAMD64ADDB + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto end41d7f409a1e1076e9645e2e90b7220ce + end41d7f409a1e1076e9645e2e90b7220ce: + ; + // match: (Add x y) + // cond: is8BitInt(t) && isSigned(t) + // result: (MOVBQSX (ADDB x y)) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + if !(is8BitInt(t) && isSigned(t)) { + goto end858e823866524b81b4636f7dd7e8eefe + } + v.Op = OpAMD64MOVBQSX + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue0(v.Line, OpAMD64ADDB, TypeInvalid) + v0.Type = t + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto end858e823866524b81b4636f7dd7e8eefe + end858e823866524b81b4636f7dd7e8eefe: ; case OpAMD64CMOVQCC: // match: (CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) @@ -349,13 +463,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { ; case OpConst: // match: (Const [val]) - // cond: is64BitInt(t) + // cond: t.IsInteger() // result: (MOVQconst [val]) { t := v.Type val := v.AuxInt - if !(is64BitInt(t)) { - goto end7f5c5b34093fbc6860524cb803ee51bf + if !(t.IsInteger()) { + goto end4c8bfe9df26fc5aa2bd76b211792732a } v.Op = OpAMD64MOVQconst v.AuxInt = 0 @@ -364,8 +478,28 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AuxInt = val return true } - goto end7f5c5b34093fbc6860524cb803ee51bf - end7f5c5b34093fbc6860524cb803ee51bf: + goto end4c8bfe9df26fc5aa2bd76b211792732a + end4c8bfe9df26fc5aa2bd76b211792732a: + ; + case OpConvert: + // match: (Convert x) + // cond: t.IsInteger() && x.Type.IsInteger() + // result: (Copy x) + { + t := v.Type + x := v.Args[0] + if !(t.IsInteger() && x.Type.IsInteger()) { + goto endcc7894224d4f6b0bcabcece5d0185912 + } + v.Op = OpCopy + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + return true + } + goto endcc7894224d4f6b0bcabcece5d0185912 + endcc7894224d4f6b0bcabcece5d0185912: ; case OpGlobal: // match: (Global {sym}) @@ -450,16 +584,16 @@ func rewriteValueAMD64(v *Value, config *Config) bool { ; case OpLoad: // match: (Load ptr mem) - // cond: t.IsBoolean() - // result: (MOVBload ptr mem) + // cond: (is64BitInt(t) || isPtr(t)) + // result: (MOVQload ptr mem) { t := v.Type ptr := v.Args[0] mem := v.Args[1] - if !(t.IsBoolean()) { - goto endc119e594c7f8e8ce5ff97c00b501dba0 + if !(is64BitInt(t) || isPtr(t)) { + goto end7c4c53acf57ebc5f03273652ba1d5934 } - v.Op = OpAMD64MOVBload + v.Op = OpAMD64MOVQload v.AuxInt = 0 v.Aux = nil v.resetArgs() @@ -467,20 +601,20 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(mem) return true } - goto endc119e594c7f8e8ce5ff97c00b501dba0 - endc119e594c7f8e8ce5ff97c00b501dba0: + goto end7c4c53acf57ebc5f03273652ba1d5934 + end7c4c53acf57ebc5f03273652ba1d5934: ; // match: (Load ptr mem) - // cond: (is64BitInt(t) || isPtr(t)) - // result: (MOVQload ptr mem) + // cond: is32BitInt(t) + // result: (MOVLload ptr mem) { t := v.Type ptr := v.Args[0] mem := v.Args[1] - if !(is64BitInt(t) || isPtr(t)) { - goto end7c4c53acf57ebc5f03273652ba1d5934 + if !(is32BitInt(t)) { + goto ende1cfcb15bfbcfd448ce303d0882a4057 } - v.Op = OpAMD64MOVQload + v.Op = OpAMD64MOVLload v.AuxInt = 0 v.Aux = nil v.resetArgs() @@ -488,8 +622,50 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(mem) return true } - goto end7c4c53acf57ebc5f03273652ba1d5934 - end7c4c53acf57ebc5f03273652ba1d5934: + goto ende1cfcb15bfbcfd448ce303d0882a4057 + ende1cfcb15bfbcfd448ce303d0882a4057: + ; + // match: (Load ptr mem) + // cond: is16BitInt(t) + // result: (MOVWload ptr mem) + { + t := v.Type + ptr := v.Args[0] + mem := v.Args[1] + if !(is16BitInt(t)) { + goto end2d0a1304501ed9f4e9e2d288505a9c7c + } + v.Op = OpAMD64MOVWload + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(mem) + return true + } + goto end2d0a1304501ed9f4e9e2d288505a9c7c + end2d0a1304501ed9f4e9e2d288505a9c7c: + ; + // match: (Load ptr mem) + // cond: (t.IsBoolean() || is8BitInt(t)) + // result: (MOVBload ptr mem) + { + t := v.Type + ptr := v.Args[0] + mem := v.Args[1] + if !(t.IsBoolean() || is8BitInt(t)) { + goto end8f83bf72293670e75b22d6627bd13f0b + } + v.Op = OpAMD64MOVBload + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(mem) + return true + } + goto end8f83bf72293670e75b22d6627bd13f0b + end8f83bf72293670e75b22d6627bd13f0b: ; case OpLsh: // match: (Lsh x y) @@ -524,6 +700,52 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end5d9e2211940fbc82536685578cf37d08 end5d9e2211940fbc82536685578cf37d08: ; + case OpAMD64MOVBstore: + // match: (MOVBstore ptr (MOVBQSX x) mem) + // cond: + // result: (MOVBstore ptr x mem) + { + ptr := v.Args[0] + if v.Args[1].Op != OpAMD64MOVBQSX { + goto endc356ef104095b9217b36b594f85171c6 + } + x := v.Args[1].Args[0] + mem := v.Args[2] + v.Op = OpAMD64MOVBstore + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(x) + v.AddArg(mem) + return true + } + goto endc356ef104095b9217b36b594f85171c6 + endc356ef104095b9217b36b594f85171c6: + ; + case OpAMD64MOVLstore: + // match: (MOVLstore ptr (MOVLQSX x) mem) + // cond: + // result: (MOVLstore ptr x mem) + { + ptr := v.Args[0] + if v.Args[1].Op != OpAMD64MOVLQSX { + goto endf79c699f70cb356abb52dc28f4abf46b + } + x := v.Args[1].Args[0] + mem := v.Args[2] + v.Op = OpAMD64MOVLstore + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(x) + v.AddArg(mem) + return true + } + goto endf79c699f70cb356abb52dc28f4abf46b + endf79c699f70cb356abb52dc28f4abf46b: + ; case OpAMD64MOVQload: // match: (MOVQload [off1] (ADDQconst [off2] ptr) mem) // cond: @@ -680,6 +902,29 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end01c970657b0fdefeab82458c15022163 end01c970657b0fdefeab82458c15022163: ; + case OpAMD64MOVWstore: + // match: (MOVWstore ptr (MOVWQSX x) mem) + // cond: + // result: (MOVWstore ptr x mem) + { + ptr := v.Args[0] + if v.Args[1].Op != OpAMD64MOVWQSX { + goto endcc13af07a951a61fcfec3299342f7e1f + } + x := v.Args[1].Args[0] + mem := v.Args[2] + v.Op = OpAMD64MOVWstore + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(x) + v.AddArg(mem) + return true + } + goto endcc13af07a951a61fcfec3299342f7e1f + endcc13af07a951a61fcfec3299342f7e1f: + ; case OpAMD64MULQ: // match: (MULQ x (MOVQconst [c])) // cond: c == int64(int32(c)) @@ -1133,6 +1378,72 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endbaeb60123806948cd2433605820d5af1 endbaeb60123806948cd2433605820d5af1: ; + // match: (Store ptr val mem) + // cond: is32BitInt(val.Type) + // result: (MOVLstore ptr val mem) + { + ptr := v.Args[0] + val := v.Args[1] + mem := v.Args[2] + if !(is32BitInt(val.Type)) { + goto end582e895008657c728c141c6b95070de7 + } + v.Op = OpAMD64MOVLstore + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto end582e895008657c728c141c6b95070de7 + end582e895008657c728c141c6b95070de7: + ; + // match: (Store ptr val mem) + // cond: is16BitInt(val.Type) + // result: (MOVWstore ptr val mem) + { + ptr := v.Args[0] + val := v.Args[1] + mem := v.Args[2] + if !(is16BitInt(val.Type)) { + goto enda3f6a985b6ebb277665f80ad30b178df + } + v.Op = OpAMD64MOVWstore + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto enda3f6a985b6ebb277665f80ad30b178df + enda3f6a985b6ebb277665f80ad30b178df: + ; + // match: (Store ptr val mem) + // cond: is8BitInt(val.Type) + // result: (MOVBstore ptr val mem) + { + ptr := v.Args[0] + val := v.Args[1] + mem := v.Args[2] + if !(is8BitInt(val.Type)) { + goto ende2dee0bc82f631e3c6b0031bf8d224c1 + } + v.Op = OpAMD64MOVBstore + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(ptr) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto ende2dee0bc82f631e3c6b0031bf8d224c1 + ende2dee0bc82f631e3c6b0031bf8d224c1: + ; case OpSub: // match: (Sub x y) // cond: is64BitInt(t) diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index cb1688f51c..5db7316dca 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -93,7 +93,10 @@ func stackalloc(f *Func) { // (ADDQ (FP) x) -> (LEAQ [n] (SP) x) v.Op = OpAMD64LEAQ v.AuxInt = n - case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVBload, OpAMD64MOVQloadidx8: + case OpAMD64ADDQconst: + // TODO(matloob): Add LEAQconst op + v.AuxInt = addOff(v.AuxInt, n) + case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVLload, OpAMD64MOVLstore, OpAMD64MOVWload, OpAMD64MOVWstore, OpAMD64MOVBload, OpAMD64MOVBstore, OpAMD64MOVQloadidx8: if v.Op == OpAMD64MOVQloadidx8 && i == 1 { // Note: we could do it, but it is probably an error log.Panicf("can't do FP->SP adjust on index slot of load %s", v.Op) -- cgit v1.3 From 8c6abfeacb27f1bcce8fb01f7da95950590943e3 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 12 Jun 2015 11:01:13 -0700 Subject: [dev.ssa] cmd/compile/ssa: separate logging, work in progress, and fatal errors The SSA implementation logs for three purposes: * debug logging * fatal errors * unimplemented features Separating these three uses lets us attempt an SSA implementation for all functions, not just _ssa functions. This turns the entire standard library into a compilation test, and makes it easy to figure out things like "how much coverage does SSA have now" and "what should we do next to get more coverage?". Functions called _ssa are still special. They log profusely by default and the output of the SSA implementation is used. For all other functions, logging is off, and the implementation is built and discarded, due to lack of support for the runtime. While we're here, fix a few minor bugs and add some extra Unimplementeds to allow all.bash to pass. As of now, SSA handles 20.79% of the functions in the standard library (689 of 3314). The top missing features are: 10.03% 2597 SSA unimplemented: zero for type error not implemented 7.79% 2016 SSA unimplemented: addr: bad op DOTPTR 7.33% 1898 SSA unimplemented: unhandled expr EQ 6.10% 1579 SSA unimplemented: unhandled expr OROR 4.91% 1271 SSA unimplemented: unhandled expr NE 4.49% 1163 SSA unimplemented: unhandled expr LROT 4.00% 1036 SSA unimplemented: unhandled expr LEN 3.56% 923 SSA unimplemented: unhandled stmt CALLFUNC 2.37% 615 SSA unimplemented: zero for type []byte not implemented 1.90% 492 SSA unimplemented: unhandled stmt CALLMETH 1.74% 450 SSA unimplemented: unhandled expr CALLINTER 1.74% 450 SSA unimplemented: unhandled expr DOT 1.71% 444 SSA unimplemented: unhandled expr ANDAND 1.65% 426 SSA unimplemented: unhandled expr CLOSUREVAR 1.54% 400 SSA unimplemented: unhandled expr CALLMETH 1.51% 390 SSA unimplemented: unhandled stmt SWITCH 1.47% 380 SSA unimplemented: unhandled expr CONV 1.33% 345 SSA unimplemented: addr: bad op * 1.30% 336 SSA unimplemented: unhandled OLITERAL 6 Change-Id: I4ca07951e276714dc13c31de28640aead17a1be7 Reviewed-on: https://go-review.googlesource.com/11160 Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/pgen.go | 13 +-- src/cmd/compile/internal/gc/ssa.go | 126 ++++++++++++++++++++----- src/cmd/compile/internal/ssa/TODO | 1 - src/cmd/compile/internal/ssa/block.go | 4 + src/cmd/compile/internal/ssa/check.go | 44 +++++---- src/cmd/compile/internal/ssa/compile.go | 13 +-- src/cmd/compile/internal/ssa/config.go | 18 +++- src/cmd/compile/internal/ssa/deadcode.go | 6 +- src/cmd/compile/internal/ssa/deadcode_test.go | 6 +- src/cmd/compile/internal/ssa/deadstore.go | 6 +- src/cmd/compile/internal/ssa/deadstore_test.go | 6 +- src/cmd/compile/internal/ssa/dom.go | 6 +- src/cmd/compile/internal/ssa/export_test.go | 12 ++- src/cmd/compile/internal/ssa/func.go | 8 +- src/cmd/compile/internal/ssa/func_test.go | 14 +-- src/cmd/compile/internal/ssa/gen/generic.rules | 2 +- src/cmd/compile/internal/ssa/layout.go | 4 +- src/cmd/compile/internal/ssa/lower.go | 4 +- src/cmd/compile/internal/ssa/print.go | 3 +- src/cmd/compile/internal/ssa/regalloc.go | 10 +- src/cmd/compile/internal/ssa/rewrite.go | 11 +-- src/cmd/compile/internal/ssa/rewritegeneric.go | 10 +- src/cmd/compile/internal/ssa/schedule_test.go | 2 +- src/cmd/compile/internal/ssa/shift_test.go | 2 +- src/cmd/compile/internal/ssa/stackalloc.go | 8 +- src/cmd/compile/internal/ssa/value.go | 4 + 26 files changed, 211 insertions(+), 132 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index e6b670f7a2..6a6c213b84 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -355,6 +355,7 @@ func compile(fn *Node) { var gcargs *Sym var gclocals *Sym var ssafn *ssa.Func + var usessa bool if fn.Nbody == nil { if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") { Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name) @@ -406,13 +407,9 @@ func compile(fn *Node) { goto ret } - // Build an SSA backend function - { - name := Curfn.Func.Nname.Sym.Name - if len(name) > 4 && name[len(name)-4:] == "_ssa" { - ssafn = buildssa(Curfn) - } - } + // Build an SSA backend function. + // TODO: get rid of usessa. + ssafn, usessa = buildssa(Curfn) continpc = nil breakpc = nil @@ -475,7 +472,7 @@ func compile(fn *Node) { } } - if ssafn != nil { + if ssafn != nil && usessa { genssa(ssafn, ptxt, gcargs, gclocals) return } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index f2dbabe6ad..1218a23488 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -5,26 +5,48 @@ package gc import ( - "log" + "fmt" "cmd/compile/internal/ssa" "cmd/internal/obj" "cmd/internal/obj/x86" // TODO: remove ) -func buildssa(fn *Node) *ssa.Func { - dumplist("buildssa-enter", fn.Func.Enter) - dumplist("buildssa-body", fn.Nbody) +// buildssa builds an SSA function +// and reports whether it should be used. +// Once the SSA implementation is complete, +// it will never return nil, and the bool can be removed. +func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) { + name := fn.Func.Nname.Sym.Name + usessa = len(name) > 4 && name[len(name)-4:] == "_ssa" + + if usessa { + dumplist("buildssa-enter", fn.Func.Enter) + dumplist("buildssa-body", fn.Nbody) + } var s state - s.pushLine(fn.Lineno) defer s.popLine() // TODO(khr): build config just once at the start of the compiler binary - s.config = ssa.NewConfig(Thearch.Thestring, ssaExport{}) + + var e ssaExport + e.log = usessa + s.config = ssa.NewConfig(Thearch.Thestring, &e) s.f = s.config.NewFunc() - s.f.Name = fn.Func.Nname.Sym.Name + s.f.Name = name + + // If SSA support for the function is incomplete, + // assume that any panics are due to violated + // invariants. Swallow them silently. + defer func() { + if err := recover(); err != nil { + if !e.unimplemented { + panic(err) + } + } + }() // We construct SSA using an algorithm similar to // Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau @@ -67,7 +89,15 @@ func buildssa(fn *Node) *ssa.Func { // Main call to ssa package to compile function ssa.Compile(s.f) - return s.f + // Calculate stats about what percentage of functions SSA handles. + if false { + fmt.Printf("SSA implemented: %t\n", !e.unimplemented) + } + + if e.unimplemented { + return nil, false + } + return s.f, usessa // TODO: return s.f, true once runtime support is in (gc maps, write barriers, etc.) } type state struct { @@ -105,10 +135,13 @@ type state struct { line []int32 } +func (s *state) Fatal(msg string, args ...interface{}) { s.config.Fatal(msg, args...) } +func (s *state) Unimplemented(msg string, args ...interface{}) { s.config.Unimplemented(msg, args...) } + // startBlock sets the current block we're generating code in to b. func (s *state) startBlock(b *ssa.Block) { if s.curBlock != nil { - log.Fatalf("starting block %v when block %v has not ended", b, s.curBlock) + s.Fatal("starting block %v when block %v has not ended", b, s.curBlock) } s.curBlock = b s.vars = map[string]*ssa.Value{} @@ -230,7 +263,7 @@ func (s *state) stmt(n *Node) { return } if compiling_runtime != 0 { - log.Fatalf("%v escapes to heap, not allowed in runtime.", n) + Fatal("%v escapes to heap, not allowed in runtime.", n) } // TODO: the old pass hides the details of PHEAP @@ -260,6 +293,9 @@ func (s *state) stmt(n *Node) { // next we work on the label's target block s.startBlock(t) } + if n.Op == OGOTO && s.curBlock == nil { + s.Unimplemented("goto at start of function; see test/goto.go") + } case OAS, OASWB: s.assign(n.Op, n.Left, n.Right) @@ -317,6 +353,9 @@ func (s *state) stmt(n *Node) { // generate code to test condition // TODO(khr): Left == nil exception + if n.Left == nil { + s.Unimplemented("cond n.Left == nil: %v", n) + } s.startBlock(bCond) cond := s.expr(n.Left) b = s.endBlock() @@ -342,7 +381,7 @@ func (s *state) stmt(n *Node) { // TODO(khr): ??? anything to do here? Only for addrtaken variables? // Maybe just link it in the store chain? default: - log.Fatalf("unhandled stmt %s", opnames[n.Op]) + s.Unimplemented("unhandled stmt %s", opnames[n.Op]) } } @@ -370,7 +409,7 @@ func (s *state) expr(n *Node) *ssa.Value { case CTSTR: return s.entryNewValue0A(ssa.OpConst, n.Type, n.Val().U) default: - log.Fatalf("unhandled OLITERAL %v", n.Val().Ctype()) + s.Unimplemented("unhandled OLITERAL %v", n.Val().Ctype()) return nil } case OCONVNOP: @@ -474,7 +513,7 @@ func (s *state) expr(n *Node) *ssa.Value { a := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Width, s.sp) return s.newValue2(ssa.OpLoad, fp.Type, a, call) default: - log.Fatalf("unhandled expr %s", opnames[n.Op]) + s.Unimplemented("unhandled expr %s", opnames[n.Op]) return nil } } @@ -494,7 +533,7 @@ func (s *state) assign(op uint8, left *Node, right *Node) { case t.IsBoolean(): val = s.entryNewValue0A(ssa.OpConst, left.Type, false) // TODO: store bools as 0/1 in AuxInt? default: - log.Fatalf("zero for type %v not implemented", t) + s.Unimplemented("zero for type %v not implemented", t) } } else { val = s.expr(right) @@ -524,7 +563,7 @@ func (s *state) addr(n *Node) *ssa.Value { return s.expr(n.Name.Heapaddr) default: // TODO: address of locals - log.Fatalf("variable address of %v not implemented", n) + s.Unimplemented("variable address of %v not implemented", n) return nil } case OINDREG: @@ -547,7 +586,7 @@ func (s *state) addr(n *Node) *ssa.Value { return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), p, i) } default: - log.Fatalf("addr: bad op %v", Oconv(int(n.Op), 0)) + s.Unimplemented("addr: bad op %v", Oconv(int(n.Op), 0)) return nil } } @@ -556,7 +595,7 @@ func (s *state) addr(n *Node) *ssa.Value { // n must be an ONAME. func canSSA(n *Node) bool { if n.Op != ONAME { - log.Fatalf("canSSA passed a non-ONAME %s %v", Oconv(int(n.Op), 0), n) + Fatal("canSSA passed a non-ONAME %s %v", Oconv(int(n.Op), 0), n) } if n.Addrtaken { return false @@ -610,7 +649,7 @@ func (s *state) boundsCheck(idx, len *ssa.Value) { // variable returns the value of a variable at the current location. func (s *state) variable(name string, t ssa.Type) *ssa.Value { if s.curBlock == nil { - log.Fatalf("nil curblock!") + s.Fatal("nil curblock!") } v := s.vars[name] if v == nil { @@ -662,6 +701,10 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va for _, p := range b.Preds { vals = append(vals, s.lookupVarOutgoing(p, t, name)) } + if len(vals) == 0 { + s.Unimplemented("TODO: Handle fixedbugs/bug076.go") + return nil + } v0 := vals[0] for i := 1; i < len(vals); i++ { if vals[i] != v0 { @@ -822,11 +865,14 @@ func genValue(v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64MULQconst: + v.Unimplemented("IMULQ doasm") + return // TODO: this isn't right. doasm fails on it. I don't think obj // has ever been taught to compile imul $c, r1, r2. p := Prog(x86.AIMULQ) p.From.Type = obj.TYPE_CONST p.From.Offset = v.AuxInt + p.From3 = new(obj.Addr) p.From3.Type = obj.TYPE_REG p.From3.Reg = regnum(v.Args[0]) p.To.Type = obj.TYPE_REG @@ -854,7 +900,7 @@ func genValue(v *ssa.Value) { r := regnum(v) if x != r { if r == x86.REG_CX { - log.Fatalf("can't implement %s, target and shift both in CX", v.LongString()) + v.Fatal("can't implement %s, target and shift both in CX", v.LongString()) } p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_REG @@ -1003,12 +1049,12 @@ func genValue(v *ssa.Value) { loc := f.RegAlloc[v.ID] for _, a := range v.Args { if f.RegAlloc[a.ID] != loc { // TODO: .Equal() instead? - log.Fatalf("phi arg at different location than phi %v %v %v %v", v, loc, a, f.RegAlloc[a.ID]) + v.Fatal("phi arg at different location than phi %v %v %v %v", v, loc, a, f.RegAlloc[a.ID]) } } case ssa.OpConst: if v.Block.Func.RegAlloc[v.ID] != nil { - log.Fatalf("const value %v shouldn't have a location", v) + v.Fatal("const value %v shouldn't have a location", v) } case ssa.OpArg: // memory arg needs no code @@ -1033,7 +1079,7 @@ func genValue(v *ssa.Value) { case ssa.OpFP, ssa.OpSP: // nothing to do default: - log.Fatalf("value %s not implemented", v.LongString()) + v.Unimplemented("value %s not implemented", v.LongString()) } } @@ -1141,7 +1187,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { } default: - log.Fatalf("branch %s not implemented", b.LongString()) + b.Unimplemented("branch %s not implemented", b.LongString()) } return branches } @@ -1183,10 +1229,40 @@ func localOffset(v *ssa.Value) int64 { } // ssaExport exports a bunch of compiler services for the ssa backend. -type ssaExport struct{} +type ssaExport struct { + log bool + unimplemented bool +} // StringSym returns a symbol (a *Sym wrapped in an interface) which // is a global string constant containing s. -func (serv ssaExport) StringSym(s string) interface{} { +func (*ssaExport) StringSym(s string) interface{} { return stringsym(s) } + +// Log logs a message from the compiler. +func (e *ssaExport) Log(msg string, args ...interface{}) { + // If e was marked as unimplemented, anything could happen. Ignore. + if e.log && !e.unimplemented { + fmt.Printf(msg, args...) + } +} + +// Fatal reports a compiler error and exits. +func (e *ssaExport) Fatal(msg string, args ...interface{}) { + // If e was marked as unimplemented, anything could happen. Ignore. + if !e.unimplemented { + Fatal(msg, args...) + } +} + +// Unimplemented reports that the function cannot be compiled. +// It will be removed once SSA work is complete. +func (e *ssaExport) Unimplemented(msg string, args ...interface{}) { + const alwaysLog = false // enable to calculate top unimplemented features + if !e.unimplemented && (e.log || alwaysLog) { + // first implementation failure, print explanation + fmt.Printf("SSA unimplemented: "+msg+"\n", args...) + } + e.unimplemented = true +} diff --git a/src/cmd/compile/internal/ssa/TODO b/src/cmd/compile/internal/ssa/TODO index e9b7553534..64b581fac0 100644 --- a/src/cmd/compile/internal/ssa/TODO +++ b/src/cmd/compile/internal/ssa/TODO @@ -42,7 +42,6 @@ Common-Subexpression Elimination - Can we move control values out of their basic block? Other - - Use gc.Fatal for errors. Add a callback to Frontend? - Write barriers - For testing, do something more sophisticated than checkOpcodeCounts. Michael Matloob suggests using a similar diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go index db16fb4a53..e0d5c1a55e 100644 --- a/src/cmd/compile/internal/ssa/block.go +++ b/src/cmd/compile/internal/ssa/block.go @@ -69,3 +69,7 @@ func (b *Block) LongString() string { } return s } + +func (b *Block) Log(msg string, args ...interface{}) { b.Func.Log(msg, args...) } +func (b *Block) Fatal(msg string, args ...interface{}) { b.Func.Fatal(msg, args...) } +func (b *Block) Unimplemented(msg string, args ...interface{}) { b.Func.Unimplemented(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 667313ad9f..230d0ec111 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -4,8 +4,6 @@ package ssa -import "log" - // checkFunc checks invariants of f. func checkFunc(f *Func) { blockMark := make([]bool, f.NumBlocks()) @@ -13,17 +11,17 @@ func checkFunc(f *Func) { for _, b := range f.Blocks { if blockMark[b.ID] { - log.Panicf("block %s appears twice in %s!", b, f.Name) + f.Fatal("block %s appears twice in %s!", b, f.Name) } blockMark[b.ID] = true if b.Func != f { - log.Panicf("%s.Func=%s, want %s", b, b.Func.Name, f.Name) + f.Fatal("%s.Func=%s, want %s", b, b.Func.Name, f.Name) } for i, c := range b.Succs { for j, d := range b.Succs { if i != j && c == d { - log.Panicf("%s.Succs has duplicate block %s", b, c) + f.Fatal("%s.Succs has duplicate block %s", b, c) } } } @@ -46,64 +44,64 @@ func checkFunc(f *Func) { } } if !found { - log.Panicf("block %s is not a succ of its pred block %s", b, p) + f.Fatal("block %s is not a succ of its pred block %s", b, p) } } switch b.Kind { case BlockExit: if len(b.Succs) != 0 { - log.Panicf("exit block %s has successors", b) + f.Fatal("exit block %s has successors", b) } if b.Control == nil { - log.Panicf("exit block %s has no control value", b) + f.Fatal("exit block %s has no control value", b) } if !b.Control.Type.IsMemory() { - log.Panicf("exit block %s has non-memory control value %s", b, b.Control.LongString()) + f.Fatal("exit block %s has non-memory control value %s", b, b.Control.LongString()) } case BlockPlain: if len(b.Succs) != 1 { - log.Panicf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) + f.Fatal("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) } if b.Control != nil { - log.Panicf("plain block %s has non-nil control %s", b, b.Control.LongString()) + f.Fatal("plain block %s has non-nil control %s", b, b.Control.LongString()) } case BlockIf: if len(b.Succs) != 2 { - log.Panicf("if block %s len(Succs)==%d, want 2", b, len(b.Succs)) + f.Fatal("if block %s len(Succs)==%d, want 2", b, len(b.Succs)) } if b.Control == nil { - log.Panicf("if block %s has no control value", b) + f.Fatal("if block %s has no control value", b) } if !b.Control.Type.IsBoolean() { - log.Panicf("if block %s has non-bool control value %s", b, b.Control.LongString()) + f.Fatal("if block %s has non-bool control value %s", b, b.Control.LongString()) } case BlockCall: if len(b.Succs) != 2 { - log.Panicf("call block %s len(Succs)==%d, want 2", b, len(b.Succs)) + f.Fatal("call block %s len(Succs)==%d, want 2", b, len(b.Succs)) } if b.Control == nil { - log.Panicf("call block %s has no control value", b) + f.Fatal("call block %s has no control value", b) } if !b.Control.Type.IsMemory() { - log.Panicf("call block %s has non-memory control value %s", b, b.Control.LongString()) + f.Fatal("call block %s has non-memory control value %s", b, b.Control.LongString()) } if b.Succs[1].Kind != BlockExit { - log.Panicf("exception edge from call block %s does not go to exit but %s", b, b.Succs[1]) + f.Fatal("exception edge from call block %s does not go to exit but %s", b, b.Succs[1]) } } for _, v := range b.Values { if valueMark[v.ID] { - log.Panicf("value %s appears twice!", v.LongString()) + f.Fatal("value %s appears twice!", v.LongString()) } valueMark[v.ID] = true if v.Block != b { - log.Panicf("%s.block != %s", v, b) + f.Fatal("%s.block != %s", v, b) } if v.Op == OpPhi && len(v.Args) != len(b.Preds) { - log.Panicf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b) + f.Fatal("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b) } // TODO: check for cycles in values @@ -113,12 +111,12 @@ func checkFunc(f *Func) { for _, id := range f.bid.free { if blockMark[id] { - log.Panicf("used block b%d in free list", id) + f.Fatal("used block b%d in free list", id) } } for _, id := range f.vid.free { if valueMark[id] { - log.Panicf("used value v%d in free list", id) + f.Fatal("used value v%d in free list", id) } } } diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index 02c9b5a4a9..896be01b68 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -4,10 +4,7 @@ package ssa -import ( - "fmt" - "log" -) +import "log" // Compile is the main entry point for this package. // Compile modifies f so that on return: @@ -18,13 +15,13 @@ import ( func Compile(f *Func) { // TODO: debugging - set flags to control verbosity of compiler, // which phases to dump IR before/after, etc. - fmt.Printf("compiling %s\n", f.Name) + f.Log("compiling %s\n", f.Name) // hook to print function & phase if panic happens phaseName := "init" defer func() { if phaseName != "" { - fmt.Printf("panic during %s while compiling %s\n", phaseName, f.Name) + f.Fatal("panic during %s while compiling %s\n", phaseName, f.Name) } }() @@ -33,9 +30,9 @@ func Compile(f *Func) { checkFunc(f) for _, p := range passes { phaseName = p.name - fmt.Printf(" pass %s begin\n", p.name) + f.Log(" pass %s begin\n", p.name) p.fn(f) - fmt.Printf(" pass %s end\n", p.name) + f.Log(" pass %s end\n", p.name) printFunc(f) checkFunc(f) } diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index db2d80a7c4..60c1a5a50b 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -4,8 +4,6 @@ package ssa -import "log" - type Config struct { arch string // "amd64", etc. ptrSize int64 // 4 or 8 @@ -22,6 +20,16 @@ type Frontend interface { // Strings are laid out in read-only memory with one word of pointer, // one word of length, then the contents of the string. StringSym(string) interface{} // returns *gc.Sym + + // Log logs a message from the compiler. + Log(string, ...interface{}) + + // Fatal reports a compiler error and exits. + Fatal(string, ...interface{}) + + // Unimplemented reports that the function cannot be compiled. + // It will be removed once SSA work is complete. + Unimplemented(msg string, args ...interface{}) } // NewConfig returns a new configuration object for the given architecture. @@ -37,7 +45,7 @@ func NewConfig(arch string, fe Frontend) *Config { c.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support default: - log.Fatalf("arch %s not implemented", arch) + fe.Unimplemented("arch %s not implemented", arch) } // cache the intptr type in the config @@ -55,5 +63,9 @@ func (c *Config) NewFunc() *Func { return &Func{Config: c} } +func (c *Config) Log(msg string, args ...interface{}) { c.fe.Log(msg, args...) } +func (c *Config) Fatal(msg string, args ...interface{}) { c.fe.Fatal(msg, args...) } +func (c *Config) Unimplemented(msg string, args ...interface{}) { c.fe.Unimplemented(msg, args...) } + // TODO(khr): do we really need a separate Config, or can we just // store all its fields inside a Func? diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index 1a5589cd0a..f4884520de 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -4,8 +4,6 @@ package ssa -import "log" - // deadcode removes dead code from f. func deadcode(f *Func) { @@ -82,7 +80,7 @@ func deadcode(f *Func) { i++ } else { if len(b.Values) > 0 { - log.Panicf("live values in unreachable block %v: %v", b, b.Values) + b.Fatal("live values in unreachable block %v: %v", b, b.Values) } f.bid.put(b.ID) } @@ -105,7 +103,7 @@ func removePredecessor(b, c *Block) { if n == 0 { // c is now dead - don't bother working on it if c.Preds[0] != b { - log.Panicf("%s.Preds[0]==%s, want %s", c, c.Preds[0], b) + b.Fatal("%s.Preds[0]==%s, want %s", c, c.Preds[0], b) } return } diff --git a/src/cmd/compile/internal/ssa/deadcode_test.go b/src/cmd/compile/internal/ssa/deadcode_test.go index edd38e1254..ff9e6800da 100644 --- a/src/cmd/compile/internal/ssa/deadcode_test.go +++ b/src/cmd/compile/internal/ssa/deadcode_test.go @@ -7,7 +7,7 @@ package ssa import "testing" func TestDeadLoop(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) fun := Fun(c, "entry", Bloc("entry", Valu("mem", OpArg, TypeMem, 0, ".mem"), @@ -37,7 +37,7 @@ func TestDeadLoop(t *testing.T) { } func TestDeadValue(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) fun := Fun(c, "entry", Bloc("entry", Valu("mem", OpArg, TypeMem, 0, ".mem"), @@ -60,7 +60,7 @@ func TestDeadValue(t *testing.T) { } func TestNeverTaken(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) fun := Fun(c, "entry", Bloc("entry", Valu("cond", OpConst, TypeBool, 0, false), diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index b02b35460a..e4d73e7226 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -4,8 +4,6 @@ package ssa -import "log" - // dse does dead-store elimination on the Function. // Dead stores are those which are unconditionally followed by // another store to the same location, with no intervening load. @@ -58,12 +56,12 @@ func dse(f *Func) { continue } if last != nil { - log.Fatalf("two final stores - simultaneous live stores", last, v) + b.Fatal("two final stores - simultaneous live stores", last, v) } last = v } if last == nil { - log.Fatalf("no last store found - cycle?") + b.Fatal("no last store found - cycle?") } // Walk backwards looking for dead stores. Keep track of shadowed addresses. diff --git a/src/cmd/compile/internal/ssa/deadstore_test.go b/src/cmd/compile/internal/ssa/deadstore_test.go index 5143afb6cb..48ea066aa3 100644 --- a/src/cmd/compile/internal/ssa/deadstore_test.go +++ b/src/cmd/compile/internal/ssa/deadstore_test.go @@ -9,7 +9,7 @@ import ( ) func TestDeadStore(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing fun := Fun(c, "entry", Bloc("entry", @@ -35,7 +35,7 @@ func TestDeadStore(t *testing.T) { } func TestDeadStorePhi(t *testing.T) { // make sure we don't get into an infinite loop with phi values. - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing fun := Fun(c, "entry", Bloc("entry", @@ -60,7 +60,7 @@ func TestDeadStoreTypes(t *testing.T) { // stronger restriction, that one store can't shadow another unless the // types of the address fields are identical (where identicalness is // decided by the CSE pass). - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) t1 := &TypeImpl{Size_: 8, Ptr: true, Name: "t1"} t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"} fun := Fun(c, "entry", diff --git a/src/cmd/compile/internal/ssa/dom.go b/src/cmd/compile/internal/ssa/dom.go index aaf3ab3da1..fac2798a60 100644 --- a/src/cmd/compile/internal/ssa/dom.go +++ b/src/cmd/compile/internal/ssa/dom.go @@ -7,8 +7,6 @@ package ssa // This file contains code to compute the dominator tree // of a control-flow graph. -import "log" - // postorder computes a postorder traversal ordering for the // basic blocks in f. Unreachable blocks will not appear. func postorder(f *Func) []*Block { @@ -47,7 +45,7 @@ func postorder(f *Func) []*Block { } } default: - log.Fatalf("bad stack state %v %d", b, mark[b.ID]) + b.Fatal("bad stack state %v %d", b, mark[b.ID]) } } return order @@ -73,7 +71,7 @@ func dominators(f *Func) []*Block { // Make the entry block a self-loop idom[f.Entry.ID] = f.Entry if postnum[f.Entry.ID] != len(post)-1 { - log.Fatalf("entry block %v not last in postorder", f.Entry) + f.Fatal("entry block %v not last in postorder", f.Entry) } // Compute relaxation of idom entries diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 103945a73e..6b006e9238 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -4,13 +4,21 @@ package ssa +import "testing" + var CheckFunc = checkFunc var PrintFunc = printFunc var Opt = opt var Deadcode = deadcode -type DummyFrontend struct{} +type DummyFrontend struct { + t *testing.T +} -func (d DummyFrontend) StringSym(s string) interface{} { +func (DummyFrontend) StringSym(s string) interface{} { return nil } + +func (d DummyFrontend) Log(msg string, args ...interface{}) { d.t.Logf(msg, args...) } +func (d DummyFrontend) Fatal(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) } +func (d DummyFrontend) Unimplemented(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index d73e0ea9e0..56bee1aa3f 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -4,8 +4,6 @@ package ssa -import "log" - // A Func represents a Go func declaration (or function literal) and // its body. This package compiles each Func independently. type Func struct { @@ -79,7 +77,7 @@ func (b *Block) NewValue0A(line int32, op Op, t Type, aux interface{}) *Value { // Disallow int64 aux values. They should be in the auxint field instead. // Maybe we want to allow this at some point, but for now we disallow it // to prevent errors like using NewValue1A instead of NewValue1I. - log.Fatalf("aux field has int64 type op=%s type=%s aux=%v", op, t, aux) + b.Fatal("aux field has int64 type op=%s type=%s aux=%v", op, t, aux) } v := &Value{ ID: b.Func.vid.get(), @@ -209,3 +207,7 @@ func (f *Func) ConstInt(line int32, t Type, c int64) *Value { // TODO: cache? return f.Entry.NewValue0I(line, OpConst, t, c) } + +func (f *Func) Log(msg string, args ...interface{}) { f.Config.Log(msg, args...) } +func (f *Func) Fatal(msg string, args ...interface{}) { f.Config.Fatal(msg, args...) } +func (f *Func) Unimplemented(msg string, args ...interface{}) { f.Config.Unimplemented(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index 7cfc7324ac..b52d470e24 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -37,7 +37,7 @@ package ssa // the parser can be used instead of Fun. import ( - "log" + "fmt" "reflect" "testing" ) @@ -161,7 +161,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun { if c.control != "" { cval, ok := values[c.control] if !ok { - log.Panicf("control value for block %s missing", bloc.name) + f.Fatal("control value for block %s missing", bloc.name) } b.Control = cval } @@ -171,7 +171,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun { for _, arg := range valu.args { a, ok := values[arg] if !ok { - log.Panicf("arg %s missing for value %s in block %s", + b.Fatal("arg %s missing for value %s in block %s", arg, valu.name, bloc.name) } v.AddArg(a) @@ -197,7 +197,7 @@ func Bloc(name string, entries ...interface{}) bloc { case ctrl: // there should be exactly one Ctrl entry. if seenCtrl { - log.Panicf("already seen control for block %s", name) + panic(fmt.Sprintf("already seen control for block %s", name)) } b.control = v seenCtrl = true @@ -206,7 +206,7 @@ func Bloc(name string, entries ...interface{}) bloc { } } if !seenCtrl { - log.Panicf("block %s doesn't have control", b.name) + panic(fmt.Sprintf("block %s doesn't have control", b.name)) } return b } @@ -262,7 +262,7 @@ func addEdge(b, c *Block) { } func TestArgs(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) fun := Fun(c, "entry", Bloc("entry", Valu("a", OpConst, TypeInt64, 14, nil), @@ -282,7 +282,7 @@ func TestArgs(t *testing.T) { } func TestEquiv(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) equivalentCases := []struct{ f, g fun }{ // simple case { diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index e0bba1706f..9d08a35f1f 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -32,7 +32,7 @@ // indexing operations // Note: bounds check has already been done -(ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex ptr idx) mem) +(ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex ptr idx) mem) (PtrIndex ptr idx) -> (Add ptr (Mul idx (Const [t.Elem().Size()]))) // big-object moves diff --git a/src/cmd/compile/internal/ssa/layout.go b/src/cmd/compile/internal/ssa/layout.go index 7123397c4c..0a271b39ad 100644 --- a/src/cmd/compile/internal/ssa/layout.go +++ b/src/cmd/compile/internal/ssa/layout.go @@ -4,8 +4,6 @@ package ssa -import "log" - // layout orders basic blocks in f with the goal of minimizing control flow instructions. // After this phase returns, the order of f.Blocks matters and is the order // in which those blocks will appear in the assembly output. @@ -82,7 +80,7 @@ blockloop: continue blockloop } } - log.Panicf("no block available for layout") + b.Fatal("no block available for layout") } f.Blocks = order } diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index 2ca1db784e..768ac124be 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -4,8 +4,6 @@ package ssa -import "log" - // convert to machine-dependent ops func lower(f *Func) { // repeat rewrites until we find no more rewrites @@ -15,7 +13,7 @@ func lower(f *Func) { for _, b := range f.Blocks { for _, v := range b.Values { if opcodeTable[v.Op].generic && v.Op != OpFP && v.Op != OpSP && v.Op != OpArg && v.Op != OpCopy && v.Op != OpPhi { - log.Panicf("%s not lowered", v.LongString()) + f.Unimplemented("%s not lowered", v.LongString()) } } } diff --git a/src/cmd/compile/internal/ssa/print.go b/src/cmd/compile/internal/ssa/print.go index b9a958c18e..c1b97d2b8f 100644 --- a/src/cmd/compile/internal/ssa/print.go +++ b/src/cmd/compile/internal/ssa/print.go @@ -8,11 +8,10 @@ import ( "bytes" "fmt" "io" - "os" ) func printFunc(f *Func) { - fprintFunc(os.Stdout, f) + f.Log("%s", f.String()) } func (f *Func) String() string { diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 6f7d619247..d1489b20f2 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -4,11 +4,7 @@ package ssa -import ( - "fmt" - "log" - "sort" -) +import "sort" func setloc(home []Location, v *Value, loc Location) []Location { for v.ID >= ID(len(home)) { @@ -353,7 +349,7 @@ func regalloc(f *Func) { if b.Kind == BlockCall { call = b.Control if call != b.Values[len(b.Values)-1] { - log.Fatalf("call not at end of block %b %v", b, call) + b.Fatal("call not at end of block %b %v", b, call) } b.Values = b.Values[:len(b.Values)-1] // TODO: do this for all control types? @@ -423,7 +419,7 @@ func live(f *Func) [][]ID { t := newSparseSet(f.NumValues()) for { for _, b := range f.Blocks { - fmt.Printf("live %s %v\n", b, live[b.ID]) + f.Log("live %s %v\n", b, live[b.ID]) } changed := false diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 77aa2b07b4..2bfd3813ed 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -4,7 +4,7 @@ package ssa -import "log" +import "fmt" func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) { // repeat rewrites until we find no more rewrites @@ -12,11 +12,10 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) var curv *Value defer func() { if curb != nil { - log.Printf("panic during rewrite of block %s\n", curb.LongString()) + curb.Fatal("panic during rewrite of block %s\n", curb.LongString()) } if curv != nil { - log.Printf("panic during rewrite of value %s\n", curv.LongString()) - panic("rewrite failed") + curv.Fatal("panic during rewrite of value %s\n", curv.LongString()) // TODO(khr): print source location also } }() @@ -90,12 +89,12 @@ func typeSize(t Type) int64 { return t.Size() } -// addOff adds two int64 offsets. Fails if wraparound happens. +// addOff adds two int64 offsets. Fails if wraparound happens. func addOff(x, y int64) int64 { z := x + y // x and y have same sign and z has a different sign => overflow if x^y >= 0 && x^z < 0 { - log.Panicf("offset overflow %d %d\n", x, y) + panic(fmt.Sprintf("offset overflow %d %d", x, y)) } return z } diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 0ecc436343..ac4f009881 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -34,10 +34,10 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpArrayIndex: // match: (ArrayIndex (Load ptr mem) idx) // cond: - // result: (Load (PtrIndex ptr idx) mem) + // result: (Load (PtrIndex ptr idx) mem) { if v.Args[0].Op != OpLoad { - goto end3809f4c52270a76313e4ea26e6f0b753 + goto end4894dd7b58383fee5f8a92be08437c33 } ptr := v.Args[0].Args[0] mem := v.Args[0].Args[1] @@ -47,15 +47,15 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.Aux = nil v.resetArgs() v0 := v.Block.NewValue0(v.Line, OpPtrIndex, TypeInvalid) - v0.Type = ptr.Type.Elem().Elem().PtrTo() + v0.Type = v.Type.PtrTo() v0.AddArg(ptr) v0.AddArg(idx) v.AddArg(v0) v.AddArg(mem) return true } - goto end3809f4c52270a76313e4ea26e6f0b753 - end3809f4c52270a76313e4ea26e6f0b753: + goto end4894dd7b58383fee5f8a92be08437c33 + end4894dd7b58383fee5f8a92be08437c33: ; case OpConst: // match: (Const {s}) diff --git a/src/cmd/compile/internal/ssa/schedule_test.go b/src/cmd/compile/internal/ssa/schedule_test.go index a7c33d9d59..a9432579f7 100644 --- a/src/cmd/compile/internal/ssa/schedule_test.go +++ b/src/cmd/compile/internal/ssa/schedule_test.go @@ -7,7 +7,7 @@ package ssa import "testing" func TestSchedule(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) cases := []fun{ Fun(c, "entry", Bloc("entry", diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go index b4b4f47ff0..52ddbbe42d 100644 --- a/src/cmd/compile/internal/ssa/shift_test.go +++ b/src/cmd/compile/internal/ssa/shift_test.go @@ -9,7 +9,7 @@ import ( ) func TestShiftConstAMD64(t *testing.T) { - c := NewConfig("amd64", DummyFrontend{}) + c := NewConfig("amd64", DummyFrontend{t}) fun := makeConstShiftFunc(c, 18, OpLsh, TypeUInt64) checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) fun = makeConstShiftFunc(c, 66, OpLsh, TypeUInt64) diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 5db7316dca..452d0c75a1 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -4,8 +4,6 @@ package ssa -import "log" - // stackalloc allocates storage in the stack frame for // all Values that did not get a register. func stackalloc(f *Func) { @@ -79,7 +77,7 @@ func stackalloc(f *Func) { for _, v := range b.Values { if v.Op == OpFP { if fp != nil { - log.Panicf("multiple FP ops: %s %s", fp, v) + b.Fatal("multiple FP ops: %s %s", fp, v) } fp = v } @@ -99,12 +97,12 @@ func stackalloc(f *Func) { case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVLload, OpAMD64MOVLstore, OpAMD64MOVWload, OpAMD64MOVWstore, OpAMD64MOVBload, OpAMD64MOVBstore, OpAMD64MOVQloadidx8: if v.Op == OpAMD64MOVQloadidx8 && i == 1 { // Note: we could do it, but it is probably an error - log.Panicf("can't do FP->SP adjust on index slot of load %s", v.Op) + f.Fatal("can't do FP->SP adjust on index slot of load %s", v.Op) } // eg: (MOVQload [c] (FP) mem) -> (MOVQload [c+n] (SP) mem) v.AuxInt = addOff(v.AuxInt, n) default: - log.Panicf("can't do FP->SP adjust on %s", v.Op) + f.Unimplemented("can't do FP->SP adjust on %s", v.Op) // TODO: OpCopy -> ADDQ } } diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 3ed1f3c2b9..bfba8dc369 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -106,3 +106,7 @@ func (v *Value) resetArgs() { v.argstorage[1] = nil v.Args = v.argstorage[:0] } + +func (v *Value) Log(msg string, args ...interface{}) { v.Block.Log(msg, args...) } +func (v *Value) Fatal(msg string, args ...interface{}) { v.Block.Fatal(msg, args...) } +func (v *Value) Unimplemented(msg string, args ...interface{}) { v.Block.Unimplemented(msg, args...) } -- cgit v1.3 From 37ddc270ca5360ccde000fd373d49b3450ee8e6e Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 24 Jun 2015 14:03:39 -0700 Subject: [dev.ssa] cmd/compile/ssa: add -f suffix to logging methods Requested in CL 11380. Change-Id: Icf0d23fb8d383c76272401e363cc9b2169d11403 Reviewed-on: https://go-review.googlesource.com/11450 Reviewed-by: Alan Donovan --- src/cmd/compile/internal/gc/ssa.go | 44 ++++++++++++++--------------- src/cmd/compile/internal/ssa/block.go | 6 ++-- src/cmd/compile/internal/ssa/check.go | 42 +++++++++++++-------------- src/cmd/compile/internal/ssa/compile.go | 8 +++--- src/cmd/compile/internal/ssa/config.go | 14 ++++----- src/cmd/compile/internal/ssa/deadcode.go | 4 +-- src/cmd/compile/internal/ssa/deadstore.go | 4 +-- src/cmd/compile/internal/ssa/dom.go | 4 +-- src/cmd/compile/internal/ssa/export_test.go | 6 ++-- src/cmd/compile/internal/ssa/func.go | 8 +++--- src/cmd/compile/internal/ssa/func_test.go | 4 +-- src/cmd/compile/internal/ssa/layout.go | 2 +- src/cmd/compile/internal/ssa/lower.go | 2 +- src/cmd/compile/internal/ssa/print.go | 2 +- src/cmd/compile/internal/ssa/regalloc.go | 4 +-- src/cmd/compile/internal/ssa/rewrite.go | 4 +-- src/cmd/compile/internal/ssa/stackalloc.go | 6 ++-- src/cmd/compile/internal/ssa/value.go | 6 ++-- 18 files changed, 85 insertions(+), 85 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 569b985052..0dd2e44ec5 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -135,13 +135,13 @@ type state struct { line []int32 } -func (s *state) Fatal(msg string, args ...interface{}) { s.config.Fatal(msg, args...) } -func (s *state) Unimplemented(msg string, args ...interface{}) { s.config.Unimplemented(msg, args...) } +func (s *state) Fatalf(msg string, args ...interface{}) { s.config.Fatalf(msg, args...) } +func (s *state) Unimplementedf(msg string, args ...interface{}) { s.config.Unimplementedf(msg, args...) } // startBlock sets the current block we're generating code in to b. func (s *state) startBlock(b *ssa.Block) { if s.curBlock != nil { - s.Fatal("starting block %v when block %v has not ended", b, s.curBlock) + s.Fatalf("starting block %v when block %v has not ended", b, s.curBlock) } s.curBlock = b s.vars = map[string]*ssa.Value{} @@ -294,7 +294,7 @@ func (s *state) stmt(n *Node) { s.startBlock(t) } if n.Op == OGOTO && s.curBlock == nil { - s.Unimplemented("goto at start of function; see test/goto.go") + s.Unimplementedf("goto at start of function; see test/goto.go") } case OAS, OASWB: @@ -354,7 +354,7 @@ func (s *state) stmt(n *Node) { // generate code to test condition // TODO(khr): Left == nil exception if n.Left == nil { - s.Unimplemented("cond n.Left == nil: %v", n) + s.Unimplementedf("cond n.Left == nil: %v", n) } s.startBlock(bCond) cond := s.expr(n.Left) @@ -381,7 +381,7 @@ func (s *state) stmt(n *Node) { // TODO(khr): ??? anything to do here? Only for addrtaken variables? // Maybe just link it in the store chain? default: - s.Unimplemented("unhandled stmt %s", opnames[n.Op]) + s.Unimplementedf("unhandled stmt %s", opnames[n.Op]) } } @@ -409,7 +409,7 @@ func (s *state) expr(n *Node) *ssa.Value { case CTSTR: return s.entryNewValue0A(ssa.OpConst, n.Type, n.Val().U) default: - s.Unimplemented("unhandled OLITERAL %v", n.Val().Ctype()) + s.Unimplementedf("unhandled OLITERAL %v", n.Val().Ctype()) return nil } case OCONVNOP: @@ -513,7 +513,7 @@ func (s *state) expr(n *Node) *ssa.Value { a := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Width, s.sp) return s.newValue2(ssa.OpLoad, fp.Type, a, call) default: - s.Unimplemented("unhandled expr %s", opnames[n.Op]) + s.Unimplementedf("unhandled expr %s", opnames[n.Op]) return nil } } @@ -533,7 +533,7 @@ func (s *state) assign(op uint8, left *Node, right *Node) { case t.IsBoolean(): val = s.entryNewValue0A(ssa.OpConst, left.Type, false) // TODO: store bools as 0/1 in AuxInt? default: - s.Unimplemented("zero for type %v not implemented", t) + s.Unimplementedf("zero for type %v not implemented", t) } } else { val = s.expr(right) @@ -563,7 +563,7 @@ func (s *state) addr(n *Node) *ssa.Value { return s.expr(n.Name.Heapaddr) default: // TODO: address of locals - s.Unimplemented("variable address of %v not implemented", n) + s.Unimplementedf("variable address of %v not implemented", n) return nil } case OINDREG: @@ -586,7 +586,7 @@ func (s *state) addr(n *Node) *ssa.Value { return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), p, i) } default: - s.Unimplemented("addr: bad op %v", Oconv(int(n.Op), 0)) + s.Unimplementedf("addr: bad op %v", Oconv(int(n.Op), 0)) return nil } } @@ -652,7 +652,7 @@ func (s *state) variable(name string, t ssa.Type) *ssa.Value { // Unimplemented instead of Fatal because fixedbugs/bug303.go // demonstrates a case in which this appears to happen legitimately. // TODO: decide on the correct behavior here. - s.Unimplemented("nil curblock adding variable %v (%v)", name, t) + s.Unimplementedf("nil curblock adding variable %v (%v)", name, t) } v := s.vars[name] if v == nil { @@ -705,7 +705,7 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va vals = append(vals, s.lookupVarOutgoing(p, t, name)) } if len(vals) == 0 { - s.Unimplemented("TODO: Handle fixedbugs/bug076.go") + s.Unimplementedf("TODO: Handle fixedbugs/bug076.go") return nil } v0 := vals[0] @@ -868,7 +868,7 @@ func genValue(v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64MULQconst: - v.Unimplemented("IMULQ doasm") + v.Unimplementedf("IMULQ doasm") return // TODO: this isn't right. doasm fails on it. I don't think obj // has ever been taught to compile imul $c, r1, r2. @@ -903,7 +903,7 @@ func genValue(v *ssa.Value) { r := regnum(v) if x != r { if r == x86.REG_CX { - v.Fatal("can't implement %s, target and shift both in CX", v.LongString()) + v.Fatalf("can't implement %s, target and shift both in CX", v.LongString()) } p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_REG @@ -1052,12 +1052,12 @@ func genValue(v *ssa.Value) { loc := f.RegAlloc[v.ID] for _, a := range v.Args { if f.RegAlloc[a.ID] != loc { // TODO: .Equal() instead? - v.Fatal("phi arg at different location than phi %v %v %v %v", v, loc, a, f.RegAlloc[a.ID]) + v.Fatalf("phi arg at different location than phi %v %v %v %v", v, loc, a, f.RegAlloc[a.ID]) } } case ssa.OpConst: if v.Block.Func.RegAlloc[v.ID] != nil { - v.Fatal("const value %v shouldn't have a location", v) + v.Fatalf("const value %v shouldn't have a location", v) } case ssa.OpArg: // memory arg needs no code @@ -1082,7 +1082,7 @@ func genValue(v *ssa.Value) { case ssa.OpFP, ssa.OpSP: // nothing to do default: - v.Unimplemented("value %s not implemented", v.LongString()) + v.Unimplementedf("value %s not implemented", v.LongString()) } } @@ -1190,7 +1190,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { } default: - b.Unimplemented("branch %s not implemented", b.LongString()) + b.Unimplementedf("branch %s not implemented", b.LongString()) } return branches } @@ -1244,7 +1244,7 @@ func (*ssaExport) StringSym(s string) interface{} { } // Log logs a message from the compiler. -func (e *ssaExport) Log(msg string, args ...interface{}) { +func (e *ssaExport) Logf(msg string, args ...interface{}) { // If e was marked as unimplemented, anything could happen. Ignore. if e.log && !e.unimplemented { fmt.Printf(msg, args...) @@ -1252,7 +1252,7 @@ func (e *ssaExport) Log(msg string, args ...interface{}) { } // Fatal reports a compiler error and exits. -func (e *ssaExport) Fatal(msg string, args ...interface{}) { +func (e *ssaExport) Fatalf(msg string, args ...interface{}) { // If e was marked as unimplemented, anything could happen. Ignore. if !e.unimplemented { Fatal(msg, args...) @@ -1261,7 +1261,7 @@ func (e *ssaExport) Fatal(msg string, args ...interface{}) { // Unimplemented reports that the function cannot be compiled. // It will be removed once SSA work is complete. -func (e *ssaExport) Unimplemented(msg string, args ...interface{}) { +func (e *ssaExport) Unimplementedf(msg string, args ...interface{}) { const alwaysLog = false // enable to calculate top unimplemented features if !e.unimplemented && (e.log || alwaysLog) { // first implementation failure, print explanation diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go index e0d5c1a55e..b788031fce 100644 --- a/src/cmd/compile/internal/ssa/block.go +++ b/src/cmd/compile/internal/ssa/block.go @@ -70,6 +70,6 @@ func (b *Block) LongString() string { return s } -func (b *Block) Log(msg string, args ...interface{}) { b.Func.Log(msg, args...) } -func (b *Block) Fatal(msg string, args ...interface{}) { b.Func.Fatal(msg, args...) } -func (b *Block) Unimplemented(msg string, args ...interface{}) { b.Func.Unimplemented(msg, args...) } +func (b *Block) Logf(msg string, args ...interface{}) { b.Func.Logf(msg, args...) } +func (b *Block) Fatalf(msg string, args ...interface{}) { b.Func.Fatalf(msg, args...) } +func (b *Block) Unimplementedf(msg string, args ...interface{}) { b.Func.Unimplementedf(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 230d0ec111..672aeda96a 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -11,17 +11,17 @@ func checkFunc(f *Func) { for _, b := range f.Blocks { if blockMark[b.ID] { - f.Fatal("block %s appears twice in %s!", b, f.Name) + f.Fatalf("block %s appears twice in %s!", b, f.Name) } blockMark[b.ID] = true if b.Func != f { - f.Fatal("%s.Func=%s, want %s", b, b.Func.Name, f.Name) + f.Fatalf("%s.Func=%s, want %s", b, b.Func.Name, f.Name) } for i, c := range b.Succs { for j, d := range b.Succs { if i != j && c == d { - f.Fatal("%s.Succs has duplicate block %s", b, c) + f.Fatalf("%s.Succs has duplicate block %s", b, c) } } } @@ -44,64 +44,64 @@ func checkFunc(f *Func) { } } if !found { - f.Fatal("block %s is not a succ of its pred block %s", b, p) + f.Fatalf("block %s is not a succ of its pred block %s", b, p) } } switch b.Kind { case BlockExit: if len(b.Succs) != 0 { - f.Fatal("exit block %s has successors", b) + f.Fatalf("exit block %s has successors", b) } if b.Control == nil { - f.Fatal("exit block %s has no control value", b) + f.Fatalf("exit block %s has no control value", b) } if !b.Control.Type.IsMemory() { - f.Fatal("exit block %s has non-memory control value %s", b, b.Control.LongString()) + f.Fatalf("exit block %s has non-memory control value %s", b, b.Control.LongString()) } case BlockPlain: if len(b.Succs) != 1 { - f.Fatal("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) + f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) } if b.Control != nil { - f.Fatal("plain block %s has non-nil control %s", b, b.Control.LongString()) + f.Fatalf("plain block %s has non-nil control %s", b, b.Control.LongString()) } case BlockIf: if len(b.Succs) != 2 { - f.Fatal("if block %s len(Succs)==%d, want 2", b, len(b.Succs)) + f.Fatalf("if block %s len(Succs)==%d, want 2", b, len(b.Succs)) } if b.Control == nil { - f.Fatal("if block %s has no control value", b) + f.Fatalf("if block %s has no control value", b) } if !b.Control.Type.IsBoolean() { - f.Fatal("if block %s has non-bool control value %s", b, b.Control.LongString()) + f.Fatalf("if block %s has non-bool control value %s", b, b.Control.LongString()) } case BlockCall: if len(b.Succs) != 2 { - f.Fatal("call block %s len(Succs)==%d, want 2", b, len(b.Succs)) + f.Fatalf("call block %s len(Succs)==%d, want 2", b, len(b.Succs)) } if b.Control == nil { - f.Fatal("call block %s has no control value", b) + f.Fatalf("call block %s has no control value", b) } if !b.Control.Type.IsMemory() { - f.Fatal("call block %s has non-memory control value %s", b, b.Control.LongString()) + f.Fatalf("call block %s has non-memory control value %s", b, b.Control.LongString()) } if b.Succs[1].Kind != BlockExit { - f.Fatal("exception edge from call block %s does not go to exit but %s", b, b.Succs[1]) + f.Fatalf("exception edge from call block %s does not go to exit but %s", b, b.Succs[1]) } } for _, v := range b.Values { if valueMark[v.ID] { - f.Fatal("value %s appears twice!", v.LongString()) + f.Fatalf("value %s appears twice!", v.LongString()) } valueMark[v.ID] = true if v.Block != b { - f.Fatal("%s.block != %s", v, b) + f.Fatalf("%s.block != %s", v, b) } if v.Op == OpPhi && len(v.Args) != len(b.Preds) { - f.Fatal("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b) + f.Fatalf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b) } // TODO: check for cycles in values @@ -111,12 +111,12 @@ func checkFunc(f *Func) { for _, id := range f.bid.free { if blockMark[id] { - f.Fatal("used block b%d in free list", id) + f.Fatalf("used block b%d in free list", id) } } for _, id := range f.vid.free { if valueMark[id] { - f.Fatal("used value v%d in free list", id) + f.Fatalf("used value v%d in free list", id) } } } diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index 27cc0d0609..b02c10a745 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -15,13 +15,13 @@ import "log" func Compile(f *Func) { // TODO: debugging - set flags to control verbosity of compiler, // which phases to dump IR before/after, etc. - f.Log("compiling %s\n", f.Name) + f.Logf("compiling %s\n", f.Name) // hook to print function & phase if panic happens phaseName := "init" defer func() { if phaseName != "" { - f.Fatal("panic during %s while compiling %s\n", phaseName, f.Name) + f.Fatalf("panic during %s while compiling %s\n", phaseName, f.Name) } }() @@ -30,9 +30,9 @@ func Compile(f *Func) { checkFunc(f) for _, p := range passes { phaseName = p.name - f.Log(" pass %s begin\n", p.name) + f.Logf(" pass %s begin\n", p.name) p.fn(f) - f.Log(" pass %s end\n", p.name) + f.Logf(" pass %s end\n", p.name) printFunc(f) checkFunc(f) } diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 60c1a5a50b..53eb5e8eb5 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -22,14 +22,14 @@ type Frontend interface { StringSym(string) interface{} // returns *gc.Sym // Log logs a message from the compiler. - Log(string, ...interface{}) + Logf(string, ...interface{}) // Fatal reports a compiler error and exits. - Fatal(string, ...interface{}) + Fatalf(string, ...interface{}) // Unimplemented reports that the function cannot be compiled. // It will be removed once SSA work is complete. - Unimplemented(msg string, args ...interface{}) + Unimplementedf(msg string, args ...interface{}) } // NewConfig returns a new configuration object for the given architecture. @@ -45,7 +45,7 @@ func NewConfig(arch string, fe Frontend) *Config { c.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support default: - fe.Unimplemented("arch %s not implemented", arch) + fe.Unimplementedf("arch %s not implemented", arch) } // cache the intptr type in the config @@ -63,9 +63,9 @@ func (c *Config) NewFunc() *Func { return &Func{Config: c} } -func (c *Config) Log(msg string, args ...interface{}) { c.fe.Log(msg, args...) } -func (c *Config) Fatal(msg string, args ...interface{}) { c.fe.Fatal(msg, args...) } -func (c *Config) Unimplemented(msg string, args ...interface{}) { c.fe.Unimplemented(msg, args...) } +func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) } +func (c *Config) Fatalf(msg string, args ...interface{}) { c.fe.Fatalf(msg, args...) } +func (c *Config) Unimplementedf(msg string, args ...interface{}) { c.fe.Unimplementedf(msg, args...) } // TODO(khr): do we really need a separate Config, or can we just // store all its fields inside a Func? diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index f4884520de..48d6fd6938 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -80,7 +80,7 @@ func deadcode(f *Func) { i++ } else { if len(b.Values) > 0 { - b.Fatal("live values in unreachable block %v: %v", b, b.Values) + b.Fatalf("live values in unreachable block %v: %v", b, b.Values) } f.bid.put(b.ID) } @@ -103,7 +103,7 @@ func removePredecessor(b, c *Block) { if n == 0 { // c is now dead - don't bother working on it if c.Preds[0] != b { - b.Fatal("%s.Preds[0]==%s, want %s", c, c.Preds[0], b) + b.Fatalf("%s.Preds[0]==%s, want %s", c, c.Preds[0], b) } return } diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index e4d73e7226..9d138e3ac1 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -56,12 +56,12 @@ func dse(f *Func) { continue } if last != nil { - b.Fatal("two final stores - simultaneous live stores", last, v) + b.Fatalf("two final stores - simultaneous live stores", last, v) } last = v } if last == nil { - b.Fatal("no last store found - cycle?") + b.Fatalf("no last store found - cycle?") } // Walk backwards looking for dead stores. Keep track of shadowed addresses. diff --git a/src/cmd/compile/internal/ssa/dom.go b/src/cmd/compile/internal/ssa/dom.go index fac2798a60..343df76b22 100644 --- a/src/cmd/compile/internal/ssa/dom.go +++ b/src/cmd/compile/internal/ssa/dom.go @@ -45,7 +45,7 @@ func postorder(f *Func) []*Block { } } default: - b.Fatal("bad stack state %v %d", b, mark[b.ID]) + b.Fatalf("bad stack state %v %d", b, mark[b.ID]) } } return order @@ -71,7 +71,7 @@ func dominators(f *Func) []*Block { // Make the entry block a self-loop idom[f.Entry.ID] = f.Entry if postnum[f.Entry.ID] != len(post)-1 { - f.Fatal("entry block %v not last in postorder", f.Entry) + f.Fatalf("entry block %v not last in postorder", f.Entry) } // Compute relaxation of idom entries diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 6b006e9238..f254e066ac 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -19,6 +19,6 @@ func (DummyFrontend) StringSym(s string) interface{} { return nil } -func (d DummyFrontend) Log(msg string, args ...interface{}) { d.t.Logf(msg, args...) } -func (d DummyFrontend) Fatal(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) } -func (d DummyFrontend) Unimplemented(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) } +func (d DummyFrontend) Logf(msg string, args ...interface{}) { d.t.Logf(msg, args...) } +func (d DummyFrontend) Fatalf(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) } +func (d DummyFrontend) Unimplementedf(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 56bee1aa3f..046c068eb9 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -77,7 +77,7 @@ func (b *Block) NewValue0A(line int32, op Op, t Type, aux interface{}) *Value { // Disallow int64 aux values. They should be in the auxint field instead. // Maybe we want to allow this at some point, but for now we disallow it // to prevent errors like using NewValue1A instead of NewValue1I. - b.Fatal("aux field has int64 type op=%s type=%s aux=%v", op, t, aux) + b.Fatalf("aux field has int64 type op=%s type=%s aux=%v", op, t, aux) } v := &Value{ ID: b.Func.vid.get(), @@ -208,6 +208,6 @@ func (f *Func) ConstInt(line int32, t Type, c int64) *Value { return f.Entry.NewValue0I(line, OpConst, t, c) } -func (f *Func) Log(msg string, args ...interface{}) { f.Config.Log(msg, args...) } -func (f *Func) Fatal(msg string, args ...interface{}) { f.Config.Fatal(msg, args...) } -func (f *Func) Unimplemented(msg string, args ...interface{}) { f.Config.Unimplemented(msg, args...) } +func (f *Func) Logf(msg string, args ...interface{}) { f.Config.Logf(msg, args...) } +func (f *Func) Fatalf(msg string, args ...interface{}) { f.Config.Fatalf(msg, args...) } +func (f *Func) Unimplementedf(msg string, args ...interface{}) { f.Config.Unimplementedf(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index b52d470e24..a620e8f602 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -161,7 +161,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun { if c.control != "" { cval, ok := values[c.control] if !ok { - f.Fatal("control value for block %s missing", bloc.name) + f.Fatalf("control value for block %s missing", bloc.name) } b.Control = cval } @@ -171,7 +171,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun { for _, arg := range valu.args { a, ok := values[arg] if !ok { - b.Fatal("arg %s missing for value %s in block %s", + b.Fatalf("arg %s missing for value %s in block %s", arg, valu.name, bloc.name) } v.AddArg(a) diff --git a/src/cmd/compile/internal/ssa/layout.go b/src/cmd/compile/internal/ssa/layout.go index 0a271b39ad..c2d72267b1 100644 --- a/src/cmd/compile/internal/ssa/layout.go +++ b/src/cmd/compile/internal/ssa/layout.go @@ -80,7 +80,7 @@ blockloop: continue blockloop } } - b.Fatal("no block available for layout") + b.Fatalf("no block available for layout") } f.Blocks = order } diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index 768ac124be..a72006ab2f 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -13,7 +13,7 @@ func lower(f *Func) { for _, b := range f.Blocks { for _, v := range b.Values { if opcodeTable[v.Op].generic && v.Op != OpFP && v.Op != OpSP && v.Op != OpArg && v.Op != OpCopy && v.Op != OpPhi { - f.Unimplemented("%s not lowered", v.LongString()) + f.Unimplementedf("%s not lowered", v.LongString()) } } } diff --git a/src/cmd/compile/internal/ssa/print.go b/src/cmd/compile/internal/ssa/print.go index c1b97d2b8f..23fdbca7c4 100644 --- a/src/cmd/compile/internal/ssa/print.go +++ b/src/cmd/compile/internal/ssa/print.go @@ -11,7 +11,7 @@ import ( ) func printFunc(f *Func) { - f.Log("%s", f.String()) + f.Logf("%s", f.String()) } func (f *Func) String() string { diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index d1489b20f2..fde1cf457b 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -349,7 +349,7 @@ func regalloc(f *Func) { if b.Kind == BlockCall { call = b.Control if call != b.Values[len(b.Values)-1] { - b.Fatal("call not at end of block %b %v", b, call) + b.Fatalf("call not at end of block %b %v", b, call) } b.Values = b.Values[:len(b.Values)-1] // TODO: do this for all control types? @@ -419,7 +419,7 @@ func live(f *Func) [][]ID { t := newSparseSet(f.NumValues()) for { for _, b := range f.Blocks { - f.Log("live %s %v\n", b, live[b.ID]) + f.Logf("live %s %v\n", b, live[b.ID]) } changed := false diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 2bfd3813ed..0de8830fb2 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -12,10 +12,10 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) var curv *Value defer func() { if curb != nil { - curb.Fatal("panic during rewrite of block %s\n", curb.LongString()) + curb.Fatalf("panic during rewrite of block %s\n", curb.LongString()) } if curv != nil { - curv.Fatal("panic during rewrite of value %s\n", curv.LongString()) + curv.Fatalf("panic during rewrite of value %s\n", curv.LongString()) // TODO(khr): print source location also } }() diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 452d0c75a1..e39a3e7a59 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -77,7 +77,7 @@ func stackalloc(f *Func) { for _, v := range b.Values { if v.Op == OpFP { if fp != nil { - b.Fatal("multiple FP ops: %s %s", fp, v) + b.Fatalf("multiple FP ops: %s %s", fp, v) } fp = v } @@ -97,12 +97,12 @@ func stackalloc(f *Func) { case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVLload, OpAMD64MOVLstore, OpAMD64MOVWload, OpAMD64MOVWstore, OpAMD64MOVBload, OpAMD64MOVBstore, OpAMD64MOVQloadidx8: if v.Op == OpAMD64MOVQloadidx8 && i == 1 { // Note: we could do it, but it is probably an error - f.Fatal("can't do FP->SP adjust on index slot of load %s", v.Op) + f.Fatalf("can't do FP->SP adjust on index slot of load %s", v.Op) } // eg: (MOVQload [c] (FP) mem) -> (MOVQload [c+n] (SP) mem) v.AuxInt = addOff(v.AuxInt, n) default: - f.Unimplemented("can't do FP->SP adjust on %s", v.Op) + f.Unimplementedf("can't do FP->SP adjust on %s", v.Op) // TODO: OpCopy -> ADDQ } } diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index ef10fb20cd..9c7f148a79 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -111,6 +111,6 @@ func (v *Value) resetArgs() { v.Args = v.argstorage[:0] } -func (v *Value) Log(msg string, args ...interface{}) { v.Block.Log(msg, args...) } -func (v *Value) Fatal(msg string, args ...interface{}) { v.Block.Fatal(msg, args...) } -func (v *Value) Unimplemented(msg string, args ...interface{}) { v.Block.Unimplemented(msg, args...) } +func (v *Value) Logf(msg string, args ...interface{}) { v.Block.Logf(msg, args...) } +func (v *Value) Fatalf(msg string, args ...interface{}) { v.Block.Fatalf(msg, args...) } +func (v *Value) Unimplementedf(msg string, args ...interface{}) { v.Block.Unimplementedf(msg, args...) } -- cgit v1.3 From 8c46aa54817063a39dc25bad343d6322e65f8598 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Fri, 19 Jun 2015 21:02:28 -0700 Subject: [dev.ssa] cmd/compile/internal/ssa: Handle variables correctly Use *Node of type ONAME instead of string as the key for variable maps. This will prevent aliasing between two identically named but differently scoped variables. Introduce an Aux value that encodes the offset of a variable from a base pointer (either global base pointer or stack pointer). Allow LEAQ and derivatives (MOVQ, etc.) to also have such an Aux field. Allocate space for AUTO variables in stackalloc. Change-Id: Ibdccdaea4bbc63a1f4882959ac374f2b467e3acd Reviewed-on: https://go-review.googlesource.com/11238 Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/gc/ssa.go | 171 ++++++++++++++++++------- src/cmd/compile/internal/ssa/TODO | 8 ++ src/cmd/compile/internal/ssa/check.go | 6 + src/cmd/compile/internal/ssa/deadstore.go | 2 +- src/cmd/compile/internal/ssa/deadstore_test.go | 10 +- src/cmd/compile/internal/ssa/gen/AMD64.rules | 10 +- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 27 ++-- src/cmd/compile/internal/ssa/gen/generic.rules | 2 +- src/cmd/compile/internal/ssa/gen/genericOps.go | 16 ++- src/cmd/compile/internal/ssa/lower.go | 2 +- src/cmd/compile/internal/ssa/opGen.go | 156 +++++++++++----------- src/cmd/compile/internal/ssa/regalloc.go | 22 ++-- src/cmd/compile/internal/ssa/rewrite.go | 11 ++ src/cmd/compile/internal/ssa/rewriteAMD64.go | 94 +++++++++++--- src/cmd/compile/internal/ssa/rewritegeneric.go | 21 +-- src/cmd/compile/internal/ssa/shift_test.go | 6 +- src/cmd/compile/internal/ssa/stackalloc.go | 58 +++------ src/cmd/compile/internal/ssa/value.go | 37 ++++++ 18 files changed, 425 insertions(+), 234 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 0dd2e44ec5..4e0f4b0cc6 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -9,7 +9,7 @@ import ( "cmd/compile/internal/ssa" "cmd/internal/obj" - "cmd/internal/obj/x86" // TODO: remove + "cmd/internal/obj/x86" ) // buildssa builds an SSA function @@ -60,13 +60,28 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) { s.exit = s.f.NewBlock(ssa.BlockExit) // Allocate starting values + s.vars = map[*Node]*ssa.Value{} + s.labels = map[string]*ssa.Block{} s.startmem = s.entryNewValue0(ssa.OpArg, ssa.TypeMem) - s.fp = s.entryNewValue0(ssa.OpFP, s.config.Uintptr) // TODO: use generic pointer type (unsafe.Pointer?) instead - s.sp = s.entryNewValue0(ssa.OpSP, s.config.Uintptr) + s.sp = s.entryNewValue0(ssa.OpSP, s.config.Uintptr) // TODO: use generic pointer type (unsafe.Pointer?) instead + s.sb = s.entryNewValue0(ssa.OpSB, s.config.Uintptr) - s.vars = map[string]*ssa.Value{} - s.labels = map[string]*ssa.Block{} - s.argOffsets = map[string]int64{} + // Generate addresses of local declarations + s.decladdrs = map[*Node]*ssa.Value{} + for d := fn.Func.Dcl; d != nil; d = d.Next { + n := d.N + switch n.Class { + case PPARAM, PPARAMOUT: + aux := &ssa.ArgSymbol{Typ: n.Type, Offset: n.Xoffset, Sym: n.Sym} + s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) + case PAUTO: + aux := &ssa.AutoSymbol{Typ: n.Type, Offset: -1, Sym: n.Sym} // offset TBD by SSA pass + s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) + } + } + // nodfp is a special argument which is the function's FP. + aux := &ssa.ArgSymbol{Typ: s.config.Uintptr, Offset: 0, Sym: nodfp.Sym} + s.decladdrs[nodfp] = s.entryNewValue1A(ssa.OpAddr, s.config.Uintptr, aux, s.sp) // Convert the AST-based IR to the SSA-based IR s.startBlock(s.f.Entry) @@ -116,20 +131,20 @@ type state struct { // current location where we're interpreting the AST curBlock *ssa.Block - // variable assignments in the current block (map from variable name to ssa value) - vars map[string]*ssa.Value + // variable assignments in the current block (map from variable symbol to ssa value) + // *Node is the unique identifier (an ONAME Node) for the variable. + vars map[*Node]*ssa.Value // all defined variables at the end of each block. Indexed by block ID. - defvars []map[string]*ssa.Value + defvars []map[*Node]*ssa.Value - // offsets of argument slots - // unnamed and unused args are not listed. - argOffsets map[string]int64 + // addresses of PPARAM, PPARAMOUT, and PAUTO variables. + decladdrs map[*Node]*ssa.Value // starting values. Memory, frame pointer, and stack pointer startmem *ssa.Value - fp *ssa.Value sp *ssa.Value + sb *ssa.Value // line number stack. The current line number is top of stack line []int32 @@ -138,13 +153,16 @@ type state struct { func (s *state) Fatalf(msg string, args ...interface{}) { s.config.Fatalf(msg, args...) } func (s *state) Unimplementedf(msg string, args ...interface{}) { s.config.Unimplementedf(msg, args...) } +// dummy node for the memory variable +var memvar = Node{Op: ONAME, Sym: &Sym{Name: "mem"}} + // startBlock sets the current block we're generating code in to b. func (s *state) startBlock(b *ssa.Block) { if s.curBlock != nil { s.Fatalf("starting block %v when block %v has not ended", b, s.curBlock) } s.curBlock = b - s.vars = map[string]*ssa.Value{} + s.vars = map[*Node]*ssa.Value{} } // endBlock marks the end of generating code for the current block. @@ -230,6 +248,11 @@ func (s *state) entryNewValue1I(op ssa.Op, t ssa.Type, auxint int64, arg *ssa.Va return s.f.Entry.NewValue1I(s.peekLine(), op, t, auxint, arg) } +// entryNewValue1A adds a new value with one argument and an aux value to the entry block. +func (s *state) entryNewValue1A(op ssa.Op, t ssa.Type, aux interface{}, arg *ssa.Value) *ssa.Value { + return s.f.Entry.NewValue1A(s.peekLine(), op, t, aux, arg) +} + // entryNewValue2 adds a new value with two arguments to the entry block. func (s *state) entryNewValue2(op ssa.Op, t ssa.Type, arg0, arg1 *ssa.Value) *ssa.Value { return s.f.Entry.NewValue2(s.peekLine(), op, t, arg0, arg1) @@ -394,11 +417,12 @@ func (s *state) expr(n *Node) *ssa.Value { case ONAME: if n.Class == PFUNC { // "value" of a function is the address of the function's closure - return s.entryNewValue0A(ssa.OpGlobal, Ptrto(n.Type), funcsym(n.Sym)) + sym := funcsym(n.Sym) + aux := &ssa.ExternSymbol{n.Type, sym} + return s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sb) } - s.argOffsets[n.Sym.Name] = n.Xoffset // TODO: remember this another way? if canSSA(n) { - return s.variable(n.Sym.Name, n.Type) + return s.variable(n, n.Type) } addr := s.addr(n) return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem()) @@ -540,12 +564,12 @@ func (s *state) assign(op uint8, left *Node, right *Node) { } if left.Op == ONAME && canSSA(left) { // Update variable assignment. - s.vars[left.Sym.Name] = val + s.vars[left] = val return } // not ssa-able. Treat as a store. addr := s.addr(left) - s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, addr, val, s.mem()) + s.vars[&memvar] = s.newValue3(ssa.OpStore, ssa.TypeMem, addr, val, s.mem()) } // addr converts the address of the expression n to SSA, adds it to s and returns the SSA result. @@ -555,14 +579,14 @@ func (s *state) addr(n *Node) *ssa.Value { switch n.Class { case PEXTERN: // global variable - return s.entryNewValue0A(ssa.OpGlobal, Ptrto(n.Type), n.Sym) - case PPARAMOUT: - // store to parameter slot - return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp) + aux := &ssa.ExternSymbol{n.Type, n.Sym} + return s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sb) + case PPARAM, PPARAMOUT, PAUTO: + // parameter/result slot or local variable + return s.decladdrs[n] case PAUTO | PHEAP: return s.expr(n.Name.Heapaddr) default: - // TODO: address of locals s.Unimplementedf("variable address of %v not implemented", n) return nil } @@ -647,7 +671,7 @@ func (s *state) boundsCheck(idx, len *ssa.Value) { } // variable returns the value of a variable at the current location. -func (s *state) variable(name string, t ssa.Type) *ssa.Value { +func (s *state) variable(name *Node, t ssa.Type) *ssa.Value { if s.curBlock == nil { // Unimplemented instead of Fatal because fixedbugs/bug303.go // demonstrates a case in which this appears to happen legitimately. @@ -664,7 +688,7 @@ func (s *state) variable(name string, t ssa.Type) *ssa.Value { } func (s *state) mem() *ssa.Value { - return s.variable(".mem", ssa.TypeMem) + return s.variable(&memvar, ssa.TypeMem) } func (s *state) linkForwardReferences() { @@ -679,7 +703,7 @@ func (s *state) linkForwardReferences() { if v.Op != ssa.OpFwdRef { continue } - name := v.Aux.(string) + name := v.Aux.(*Node) v.Op = ssa.OpCopy v.Aux = nil v.SetArgs1(s.lookupVarIncoming(b, v.Type, name)) @@ -688,17 +712,23 @@ func (s *state) linkForwardReferences() { } // lookupVarIncoming finds the variable's value at the start of block b. -func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Value { +func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name *Node) *ssa.Value { // TODO(khr): have lookupVarIncoming overwrite the fwdRef or copy it // will be used in, instead of having the result used in a copy value. if b == s.f.Entry { - if name == ".mem" { + if name == &memvar { return s.startmem } // variable is live at the entry block. Load it. - addr := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(t.(*Type)), s.argOffsets[name], s.fp) + addr := s.decladdrs[name] + if addr == nil { + // TODO: closure args reach here. + s.Unimplementedf("variable %s not found", name) + } + if _, ok := addr.Aux.(*ssa.ArgSymbol); !ok { + s.Fatalf("variable live at start of function %s is not an argument %s", b.Func.Name, name) + } return s.entryNewValue2(ssa.OpLoad, t, addr, s.startmem) - } var vals []*ssa.Value for _, p := range b.Preds { @@ -721,7 +751,7 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va } // lookupVarOutgoing finds the variable's value at the end of block b. -func (s *state) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name string) *ssa.Value { +func (s *state) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name *Node) *ssa.Value { m := s.defvars[b.ID] if v, ok := m[name]; ok { return v @@ -962,13 +992,20 @@ func genValue(v *ssa.Value) { p.From.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG p.To.Reg = r - case ssa.OpAMD64LEAQ: + case ssa.OpAMD64LEAQ1: p := Prog(x86.ALEAQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) p.From.Scale = 1 p.From.Index = regnum(v.Args[1]) - p.From.Offset = v.AuxInt + addAux(&p.From, v) + p.To.Type = obj.TYPE_REG + p.To.Reg = regnum(v) + case ssa.OpAMD64LEAQ: + p := Prog(x86.ALEAQ) + p.From.Type = obj.TYPE_MEM + p.From.Reg = regnum(v.Args[0]) + addAux(&p.From, v) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64CMPQ, ssa.OpAMD64TESTB, ssa.OpAMD64TESTQ: @@ -994,14 +1031,14 @@ func genValue(v *ssa.Value) { p := Prog(v.Op.Asm()) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) - p.From.Offset = v.AuxInt + addAux(&p.From, v) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) case ssa.OpAMD64MOVQloadidx8: p := Prog(x86.AMOVQ) p.From.Type = obj.TYPE_MEM p.From.Reg = regnum(v.Args[0]) - p.From.Offset = v.AuxInt + addAux(&p.From, v) p.From.Scale = 8 p.From.Index = regnum(v.Args[1]) p.To.Type = obj.TYPE_REG @@ -1012,7 +1049,7 @@ func genValue(v *ssa.Value) { p.From.Reg = regnum(v.Args[1]) p.To.Type = obj.TYPE_MEM p.To.Reg = regnum(v.Args[0]) - p.To.Offset = v.AuxInt + addAux(&p.To, v) case ssa.OpAMD64MOVLQSX, ssa.OpAMD64MOVWQSX, ssa.OpAMD64MOVBQSX: p := Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG @@ -1062,14 +1099,6 @@ func genValue(v *ssa.Value) { case ssa.OpArg: // memory arg needs no code // TODO: check that only mem arg goes here. - case ssa.OpAMD64LEAQglobal: - p := Prog(x86.ALEAQ) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = Linksym(v.Aux.(*Sym)) - p.From.Offset = v.AuxInt - p.To.Type = obj.TYPE_REG - p.To.Reg = regnum(v) case ssa.OpAMD64CALLstatic: p := Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -1079,7 +1108,7 @@ func genValue(v *ssa.Value) { p := Prog(obj.ACALL) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v.Args[0]) - case ssa.OpFP, ssa.OpSP: + case ssa.OpSP, ssa.OpSB: // nothing to do default: v.Unimplementedf("value %s not implemented", v.LongString()) @@ -1195,6 +1224,35 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch { return branches } +// addAux adds the offset in the aux fields (AuxInt and Aux) of v to a. +func addAux(a *obj.Addr, v *ssa.Value) { + if a.Type != obj.TYPE_MEM { + v.Fatalf("bad addAux addr %s", a) + } + // add integer offset + a.Offset += v.AuxInt + + // If no additional symbol offset, we're done. + if v.Aux == nil { + return + } + // Add symbol's offset from its base register. + switch sym := v.Aux.(type) { + case *ssa.ExternSymbol: + a.Name = obj.NAME_EXTERN + a.Sym = Linksym(sym.Sym.(*Sym)) + case *ssa.ArgSymbol: + a.Offset += v.Block.Func.FrameSize + sym.Offset + case *ssa.AutoSymbol: + if sym.Offset == -1 { + v.Fatalf("auto symbol %s offset not calculated", sym.Sym) + } + a.Offset += sym.Offset + default: + v.Fatalf("aux in %s not implemented %#v", v, v.Aux) + } +} + // ssaRegToReg maps ssa register numbers to obj register numbers. var ssaRegToReg = [...]int16{ x86.REG_AX, @@ -1213,7 +1271,23 @@ var ssaRegToReg = [...]int16{ x86.REG_R13, x86.REG_R14, x86.REG_R15, - // TODO: more + x86.REG_X0, + x86.REG_X1, + x86.REG_X2, + x86.REG_X3, + x86.REG_X4, + x86.REG_X5, + x86.REG_X6, + x86.REG_X7, + x86.REG_X8, + x86.REG_X9, + x86.REG_X10, + x86.REG_X11, + x86.REG_X12, + x86.REG_X13, + x86.REG_X14, + x86.REG_X15, + 0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case. // TODO: arch-dependent } @@ -1240,7 +1314,8 @@ type ssaExport struct { // StringSym returns a symbol (a *Sym wrapped in an interface) which // is a global string constant containing s. func (*ssaExport) StringSym(s string) interface{} { - return stringsym(s) + // TODO: is idealstring correct? It might not matter... + return &ssa.ExternSymbol{Typ: idealstring, Sym: stringsym(s)} } // Log logs a message from the compiler. diff --git a/src/cmd/compile/internal/ssa/TODO b/src/cmd/compile/internal/ssa/TODO index 66841c36f0..30d49044e1 100644 --- a/src/cmd/compile/internal/ssa/TODO +++ b/src/cmd/compile/internal/ssa/TODO @@ -28,6 +28,14 @@ Regalloc - Floating point registers - Make calls clobber all registers +StackAlloc: + - Compute size of outargs section correctly + - Sort variables so all ptr-containing ones are first (so stack + maps are smaller) + - Reuse stack slots for noninterfering and type-compatible variables + (both AUTOs and spilled Values). But see issue 8740 for what + "type-compatible variables" mean and what DWARF information provides. + Rewrites - Strength reduction (both arch-indep and arch-dependent?) - Start another architecture (arm?) diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 672aeda96a..1ca6e36ae7 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -104,6 +104,12 @@ func checkFunc(f *Func) { f.Fatalf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b) } + if v.Op == OpAddr { + if v.Args[0].Op != OpSP && v.Args[0].Op != OpSB { + f.Fatalf("bad arg to OpAddr %v", v) + } + } + // TODO: check for cycles in values // TODO: check type } diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index 9d138e3ac1..db3808639a 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -56,7 +56,7 @@ func dse(f *Func) { continue } if last != nil { - b.Fatalf("two final stores - simultaneous live stores", last, v) + b.Fatalf("two final stores - simultaneous live stores %s %s", last, v) } last = v } diff --git a/src/cmd/compile/internal/ssa/deadstore_test.go b/src/cmd/compile/internal/ssa/deadstore_test.go index 48ea066aa3..042e7f66ff 100644 --- a/src/cmd/compile/internal/ssa/deadstore_test.go +++ b/src/cmd/compile/internal/ssa/deadstore_test.go @@ -15,8 +15,8 @@ func TestDeadStore(t *testing.T) { Bloc("entry", Valu("start", OpArg, TypeMem, 0, ".mem"), Valu("v", OpConst, TypeBool, 0, true), - Valu("addr1", OpGlobal, ptrType, 0, nil), - Valu("addr2", OpGlobal, ptrType, 0, nil), + Valu("addr1", OpAddr, ptrType, 0, nil), + Valu("addr2", OpAddr, ptrType, 0, nil), Valu("store1", OpStore, TypeMem, 0, nil, "addr1", "v", "start"), Valu("store2", OpStore, TypeMem, 0, nil, "addr2", "v", "store1"), Valu("store3", OpStore, TypeMem, 0, nil, "addr1", "v", "store2"), @@ -41,7 +41,7 @@ func TestDeadStorePhi(t *testing.T) { Bloc("entry", Valu("start", OpArg, TypeMem, 0, ".mem"), Valu("v", OpConst, TypeBool, 0, true), - Valu("addr", OpGlobal, ptrType, 0, nil), + Valu("addr", OpAddr, ptrType, 0, nil), Goto("loop")), Bloc("loop", Valu("phi", OpPhi, TypeMem, 0, nil, "start", "store"), @@ -67,8 +67,8 @@ func TestDeadStoreTypes(t *testing.T) { Bloc("entry", Valu("start", OpArg, TypeMem, 0, ".mem"), Valu("v", OpConst, TypeBool, 0, true), - Valu("addr1", OpGlobal, t1, 0, nil), - Valu("addr2", OpGlobal, t2, 0, nil), + Valu("addr1", OpAddr, t1, 0, nil), + Valu("addr2", OpAddr, t2, 0, nil), Valu("store1", OpStore, TypeMem, 0, nil, "addr1", "v", "start"), Valu("store2", OpStore, TypeMem, 0, nil, "addr2", "v", "store1"), Goto("exit")), diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index aa4e807712..124b13b6f2 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -68,6 +68,8 @@ (Const [val]) && t.IsInteger() -> (MOVQconst [val]) +(Addr {sym} base) -> (LEAQ {sym} base) + // block rewrites (If (SETL cmp) yes no) -> (LT cmp yes no) (If (SETNE cmp) yes no) -> (NE cmp yes no) @@ -80,9 +82,6 @@ // Rules below here apply some simple optimizations after lowering. // TODO: Should this be a separate pass? -// global loads/stores -(Global {sym}) -> (LEAQglobal {sym}) - // fold constants into instructions (ADDQ x (MOVQconst [c])) -> (ADDQconst [c] x) // TODO: restrict c to int32 range? (ADDQ (MOVQconst [c]) x) -> (ADDQconst [c] x) @@ -119,6 +118,11 @@ (MOVQload [off1] (ADDQconst [off2] ptr) mem) -> (MOVQload [addOff(off1, off2)] ptr mem) (MOVQstore [off1] (ADDQconst [off2] ptr) val mem) -> (MOVQstore [addOff(off1, off2)] ptr val mem) +(MOVQload [off1] {sym1} (LEAQ [off2] {sym2} base) mem) && (sym1 == nil || sym2 == nil) -> + (MOVQload [addOff(off1,off2)] {mergeSym(sym1,sym2)} base mem) +(MOVQstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) && (sym1 == nil || sym2 == nil) -> + (MOVQstore [addOff(off1,off2)] {mergeSym(sym1,sym2)} base val mem) + // indexed loads and stores (MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) (MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 3733ba9721..c0f36b51b3 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -42,7 +42,7 @@ var regNamesAMD64 = []string{ ".X15", // pseudo-registers - ".FP", + ".SB", ".FLAGS", } @@ -71,19 +71,22 @@ func init() { } gp := buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15") - gpsp := gp | buildReg("SP FP") + gpsp := gp | buildReg("SP") + gpspsb := gpsp | buildReg("SB") flags := buildReg("FLAGS") gp01 := regInfo{[]regMask{}, 0, []regMask{gp}} gp11 := regInfo{[]regMask{gpsp}, 0, []regMask{gp}} + gp11sb := regInfo{[]regMask{gpspsb}, 0, []regMask{gp}} gp21 := regInfo{[]regMask{gpsp, gpsp}, 0, []regMask{gp}} + gp21sb := regInfo{[]regMask{gpspsb, gpsp}, 0, []regMask{gp}} gp21shift := regInfo{[]regMask{gpsp, buildReg("CX")}, 0, []regMask{gp}} gp2flags := regInfo{[]regMask{gpsp, gpsp}, 0, []regMask{flags}} gp1flags := regInfo{[]regMask{gpsp}, 0, []regMask{flags}} flagsgp1 := regInfo{[]regMask{flags}, 0, []regMask{gp}} - gpload := regInfo{[]regMask{gpsp, 0}, 0, []regMask{gp}} - gploadidx := regInfo{[]regMask{gpsp, gpsp, 0}, 0, []regMask{gp}} - gpstore := regInfo{[]regMask{gpsp, gpsp, 0}, 0, nil} - gpstoreidx := regInfo{[]regMask{gpsp, gpsp, gpsp, 0}, 0, nil} + gpload := regInfo{[]regMask{gpspsb, 0}, 0, []regMask{gp}} + gploadidx := regInfo{[]regMask{gpspsb, gpsp, 0}, 0, []regMask{gp}} + gpstore := regInfo{[]regMask{gpspsb, gpsp, 0}, 0, nil} + gpstoreidx := regInfo{[]regMask{gpspsb, gpsp, gpsp, 0}, 0, nil} flagsgp := regInfo{[]regMask{flags}, 0, []regMask{gp}} cmov := regInfo{[]regMask{flags, gp, gp}, 0, []regMask{gp}} @@ -129,12 +132,12 @@ func init() { {name: "MOVWQSX", reg: gp11, asm: "MOVWQSX"}, // extend arg0 from int16 to int64 {name: "MOVBQSX", reg: gp11, asm: "MOVBQSX"}, // extend arg0 from int8 to int64 - {name: "MOVQconst", reg: gp01}, // auxint - {name: "LEAQ", reg: gp21}, // arg0 + arg1 + auxint - {name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + auxint - {name: "LEAQ4", reg: gp21}, // arg0 + 4*arg1 + auxint - {name: "LEAQ8", reg: gp21}, // arg0 + 8*arg1 + auxint - {name: "LEAQglobal", reg: gp01}, // no args. address of aux.(*gc.Sym) + {name: "MOVQconst", reg: gp01}, // auxint + {name: "LEAQ", reg: gp11sb}, // arg0 + auxint + offset encoded in aux + {name: "LEAQ1", reg: gp21sb}, // arg0 + arg1 + auxint + {name: "LEAQ2", reg: gp21sb}, // arg0 + 2*arg1 + auxint + {name: "LEAQ4", reg: gp21sb}, // arg0 + 4*arg1 + auxint + {name: "LEAQ8", reg: gp21sb}, // arg0 + 8*arg1 + auxint {name: "MOVBload", reg: gpload, asm: "MOVB"}, // load byte from arg0+auxint. arg1=mem {name: "MOVBQZXload", reg: gpload}, // ditto, extend to uint64 diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index 9d08a35f1f..9f11a60a6b 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -40,7 +40,7 @@ (Store dst (Load src mem) mem) && t.Size() > 8 -> (Move [t.Size()] dst src mem) // string ops -(Const {s}) && t.IsString() -> (StringMake (OffPtr [2*config.ptrSize] (Global {config.fe.StringSym(s.(string))})) (Const [int64(len(s.(string)))])) // TODO: ptr +(Const {s}) && t.IsString() -> (StringMake (OffPtr [2*config.ptrSize] (Addr {config.fe.StringSym(s.(string))} (SB ))) (Const [int64(len(s.(string)))])) // TODO: ptr (Load ptr mem) && t.IsString() -> (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) (StringPtr (StringMake ptr _)) -> ptr (StringLen (StringMake _ len)) -> len diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index c168f2af05..e7c4de8eb1 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -30,11 +30,17 @@ var genericOps = []opData{ {name: "Const"}, // Constant-like things - {name: "Arg"}, // address of a function parameter/result. Memory input is an arg called ".mem". aux is a string (TODO: make it something other than a string?) - {name: "Global"}, // the address of a global variable aux.(*gc.Sym) - {name: "SP"}, // stack pointer - {name: "FP"}, // frame pointer - {name: "Func"}, // entry address of a function + {name: "Arg"}, // memory input to the function. + + // The address of a variable. arg0 is the base pointer (SB or SP, depending + // on whether it is a global or stack variable). The Aux field identifies the + // variable. It will be either an *ExternSymbol (with arg0=SB), *ArgSymbol (arg0=SP), + // or *AutoSymbol (arg0=SP). + {name: "Addr"}, // Address of a variable. Arg0=SP or SB. Aux identifies the variable. + + {name: "SP"}, // stack pointer + {name: "SB"}, // static base pointer (a.k.a. globals pointer) + {name: "Func"}, // entry address of a function // Memory operations {name: "Load"}, // Load from arg0. arg1=memory diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index a72006ab2f..6499dc8565 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -12,7 +12,7 @@ func lower(f *Func) { // Check for unlowered opcodes, fail if we find one. for _, b := range f.Blocks { for _, v := range b.Values { - if opcodeTable[v.Op].generic && v.Op != OpFP && v.Op != OpSP && v.Op != OpArg && v.Op != OpCopy && v.Op != OpPhi { + if opcodeTable[v.Op].generic && v.Op != OpSP && v.Op != OpSB && v.Op != OpArg && v.Op != OpCopy && v.Op != OpPhi { f.Unimplementedf("%s not lowered", v.LongString()) } } diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 1116be101c..20adc62958 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -81,10 +81,10 @@ const ( OpAMD64MOVBQSX OpAMD64MOVQconst OpAMD64LEAQ + OpAMD64LEAQ1 OpAMD64LEAQ2 OpAMD64LEAQ4 OpAMD64LEAQ8 - OpAMD64LEAQglobal OpAMD64MOVBload OpAMD64MOVBQZXload OpAMD64MOVBQSXload @@ -117,9 +117,9 @@ const ( OpCopy OpConst OpArg - OpGlobal + OpAddr OpSP - OpFP + OpSB OpFunc OpLoad OpStore @@ -152,8 +152,8 @@ var opcodeTable = [...]opInfo{ name: "ADDQ", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -165,7 +165,7 @@ var opcodeTable = [...]opInfo{ name: "ADDQconst", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -178,8 +178,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -192,7 +192,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -205,8 +205,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -219,7 +219,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -232,8 +232,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -246,7 +246,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -259,8 +259,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHLQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 2, // .CX + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 2, // .CX }, clobbers: 0, outputs: []regMask{ @@ -273,7 +273,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHLQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -286,8 +286,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHRQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 2, // .CX + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 2, // .CX }, clobbers: 0, outputs: []regMask{ @@ -300,7 +300,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHRQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -313,8 +313,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASARQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 2, // .CX + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 2, // .CX }, clobbers: 0, outputs: []regMask{ @@ -327,7 +327,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASARQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -339,7 +339,7 @@ var opcodeTable = [...]opInfo{ name: "NEGQ", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -352,8 +352,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ACMPQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -366,7 +366,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ACMPQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -379,8 +379,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ATESTQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -393,8 +393,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ATESTB, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -506,7 +506,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVLQSX, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -519,7 +519,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVWQSX, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -532,7 +532,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVBQSX, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -554,8 +554,7 @@ var opcodeTable = [...]opInfo{ name: "LEAQ", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, clobbers: 0, outputs: []regMask{ @@ -564,11 +563,11 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "LEAQ2", + name: "LEAQ1", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -577,11 +576,11 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "LEAQ4", + name: "LEAQ2", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -590,11 +589,11 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "LEAQ8", + name: "LEAQ4", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -603,9 +602,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "LEAQglobal", + name: "LEAQ8", reg: regInfo{ - inputs: []regMask{}, + inputs: []regMask{ + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, clobbers: 0, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -617,7 +619,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVB, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB 0, }, clobbers: 0, @@ -630,7 +632,7 @@ var opcodeTable = [...]opInfo{ name: "MOVBQZXload", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB 0, }, clobbers: 0, @@ -643,7 +645,7 @@ var opcodeTable = [...]opInfo{ name: "MOVBQSXload", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB 0, }, clobbers: 0, @@ -657,7 +659,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVW, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB 0, }, clobbers: 0, @@ -671,7 +673,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVL, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB 0, }, clobbers: 0, @@ -685,7 +687,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB 0, }, clobbers: 0, @@ -699,8 +701,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 0, }, clobbers: 0, @@ -714,8 +716,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVB, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 0, }, clobbers: 0, @@ -727,8 +729,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVW, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 0, }, clobbers: 0, @@ -740,8 +742,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVL, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 0, }, clobbers: 0, @@ -753,8 +755,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AMOVQ, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 0, }, clobbers: 0, @@ -765,9 +767,9 @@ var opcodeTable = [...]opInfo{ name: "MOVQstoreidx8", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 0, }, clobbers: 0, @@ -802,8 +804,8 @@ var opcodeTable = [...]opInfo{ name: "CALLclosure", reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4, // .DX + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 4, // .DX 0, }, clobbers: 0, @@ -827,8 +829,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AADDL, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -841,8 +843,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AADDW, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -855,8 +857,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AADDB, reg: regInfo{ inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .FP + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, clobbers: 0, outputs: []regMask{ @@ -964,7 +966,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Global", + name: "Addr", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -982,7 +984,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "FP", + name: "SB", reg: regInfo{ inputs: []regMask{}, clobbers: 0, diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index fde1cf457b..27e4f754d1 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -54,7 +54,7 @@ var registers = [...]Register{ Register{29, "X13"}, Register{30, "X14"}, Register{31, "X15"}, - Register{32, "FP"}, // pseudo-register, actually a constant offset from SP + Register{32, "SB"}, // pseudo-register for global base pointer (aka %rip) Register{33, "FLAGS"}, // TODO: make arch-dependent @@ -101,15 +101,15 @@ func regalloc(f *Func) { var oldSched []*Value - // Hack to find fp, sp Values and assign them a register. (TODO: make not so hacky) - var fp, sp *Value + // Hack to find sp and sb Values and assign them a register. (TODO: make not so hacky) + var sp, sb *Value for _, v := range f.Entry.Values { switch v.Op { case OpSP: sp = v home = setloc(home, v, ®isters[4]) // TODO: arch-dependent - case OpFP: - fp = v + case OpSB: + sb = v home = setloc(home, v, ®isters[32]) // TODO: arch-dependent } } @@ -147,7 +147,7 @@ func regalloc(f *Func) { // TODO: hack: initialize fixed registers regs[4] = regInfo{sp, sp, false} - regs[32] = regInfo{fp, fp, false} + regs[32] = regInfo{sb, sb, false} var used regMask // has a 1 for each non-nil entry in regs var dirty regMask // has a 1 for each dirty entry in regs @@ -193,7 +193,7 @@ func regalloc(f *Func) { // nospill contains registers that we can't spill because // we already set them up for use by the current instruction. var nospill regMask - nospill |= 0x100000010 // SP and FP can't be spilled (TODO: arch-specific) + nospill |= 0x100000010 // SP & SB can't be spilled (TODO: arch-specific) // Move inputs into registers for _, o := range order { @@ -257,13 +257,15 @@ func regalloc(f *Func) { var c *Value if len(w.Args) == 0 { // Materialize w - if w.Op == OpFP || w.Op == OpSP || w.Op == OpGlobal { + if w.Op == OpSB { + c = w + } else if w.Op == OpSP { c = b.NewValue1(w.Line, OpCopy, w.Type, w) } else { c = b.NewValue0IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux) } - } else if len(w.Args) == 1 && (w.Args[0].Op == OpFP || w.Args[0].Op == OpSP || w.Args[0].Op == OpGlobal) { - // Materialize offsets from SP/FP/Global + } else if len(w.Args) == 1 && (w.Args[0].Op == OpSP || w.Args[0].Op == OpSB) { + // Materialize offsets from SP/SB c = b.NewValue1IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux, w.Args[0]) } else if wreg != 0 { // Copy from another register. diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 0de8830fb2..b2c45969e4 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -99,6 +99,17 @@ func addOff(x, y int64) int64 { return z } +func mergeSym(x, y interface{}) interface{} { + if x == nil { + return y + } + if y == nil { + return x + } + panic(fmt.Sprintf("mergeSym with two non-nil syms %s %s", x, y)) + return nil +} + func inBounds(idx, len int64) bool { return idx >= 0 && idx < len } diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 3490adadd7..dfed084875 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -344,6 +344,24 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end858e823866524b81b4636f7dd7e8eefe end858e823866524b81b4636f7dd7e8eefe: ; + case OpAddr: + // match: (Addr {sym} base) + // cond: + // result: (LEAQ {sym} base) + { + sym := v.Aux + base := v.Args[0] + v.Op = OpAMD64LEAQ + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.Aux = sym + v.AddArg(base) + return true + } + goto end53cad0c3c9daa5575680e77c14e05e72 + end53cad0c3c9daa5575680e77c14e05e72: + ; case OpAMD64CMOVQCC: // match: (CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) // cond: inBounds(d, c) @@ -501,22 +519,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endcc7894224d4f6b0bcabcece5d0185912 endcc7894224d4f6b0bcabcece5d0185912: ; - case OpGlobal: - // match: (Global {sym}) - // cond: - // result: (LEAQglobal {sym}) - { - sym := v.Aux - v.Op = OpAMD64LEAQglobal - v.AuxInt = 0 - v.Aux = nil - v.resetArgs() - v.Aux = sym - return true - } - goto end8f47b6f351fecaeded45abbe5c2beec0 - end8f47b6f351fecaeded45abbe5c2beec0: - ; case OpIsInBounds: // match: (IsInBounds idx len) // cond: @@ -769,6 +771,35 @@ func rewriteValueAMD64(v *Value, config *Config) bool { } goto end843d29b538c4483b432b632e5666d6e3 end843d29b538c4483b432b632e5666d6e3: + ; + // match: (MOVQload [off1] {sym1} (LEAQ [off2] {sym2} base) mem) + // cond: (sym1 == nil || sym2 == nil) + // result: (MOVQload [addOff(off1,off2)] {mergeSym(sym1,sym2)} base mem) + { + off1 := v.AuxInt + sym1 := v.Aux + if v.Args[0].Op != OpAMD64LEAQ { + goto end227426af95e74caddcf59fdcd30ca8bc + } + off2 := v.Args[0].AuxInt + sym2 := v.Args[0].Aux + base := v.Args[0].Args[0] + mem := v.Args[1] + if !(sym1 == nil || sym2 == nil) { + goto end227426af95e74caddcf59fdcd30ca8bc + } + v.Op = OpAMD64MOVQload + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AuxInt = addOff(off1, off2) + v.Aux = mergeSym(sym1, sym2) + v.AddArg(base) + v.AddArg(mem) + return true + } + goto end227426af95e74caddcf59fdcd30ca8bc + end227426af95e74caddcf59fdcd30ca8bc: ; // match: (MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) // cond: @@ -846,6 +877,37 @@ func rewriteValueAMD64(v *Value, config *Config) bool { } goto end2108c693a43c79aed10b9246c39c80aa end2108c693a43c79aed10b9246c39c80aa: + ; + // match: (MOVQstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // cond: (sym1 == nil || sym2 == nil) + // result: (MOVQstore [addOff(off1,off2)] {mergeSym(sym1,sym2)} base val mem) + { + off1 := v.AuxInt + sym1 := v.Aux + if v.Args[0].Op != OpAMD64LEAQ { + goto end5061f48193268a5eb1e1740bdd23c43d + } + off2 := v.Args[0].AuxInt + sym2 := v.Args[0].Aux + base := v.Args[0].Args[0] + val := v.Args[1] + mem := v.Args[2] + if !(sym1 == nil || sym2 == nil) { + goto end5061f48193268a5eb1e1740bdd23c43d + } + v.Op = OpAMD64MOVQstore + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AuxInt = addOff(off1, off2) + v.Aux = mergeSym(sym1, sym2) + v.AddArg(base) + v.AddArg(val) + v.AddArg(mem) + return true + } + goto end5061f48193268a5eb1e1740bdd23c43d + end5061f48193268a5eb1e1740bdd23c43d: ; // match: (MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) // cond: diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index ac4f009881..e2feeb53cc 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -60,12 +60,12 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpConst: // match: (Const {s}) // cond: t.IsString() - // result: (StringMake (OffPtr [2*config.ptrSize] (Global {config.fe.StringSym(s.(string))})) (Const [int64(len(s.(string)))])) + // result: (StringMake (OffPtr [2*config.ptrSize] (Addr {config.fe.StringSym(s.(string))} (SB ))) (Const [int64(len(s.(string)))])) { t := v.Type s := v.Aux if !(t.IsString()) { - goto end6d6321106a054a5984b2ed0acec52a5b + goto end55cd8fd3b98a2459d0ee9d6cbb456b01 } v.Op = OpStringMake v.AuxInt = 0 @@ -74,19 +74,22 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid) v0.Type = TypeBytePtr v0.AuxInt = 2 * config.ptrSize - v1 := v.Block.NewValue0(v.Line, OpGlobal, TypeInvalid) + v1 := v.Block.NewValue0(v.Line, OpAddr, TypeInvalid) v1.Type = TypeBytePtr v1.Aux = config.fe.StringSym(s.(string)) + v2 := v.Block.NewValue0(v.Line, OpSB, TypeInvalid) + v2.Type = config.Uintptr + v1.AddArg(v2) v0.AddArg(v1) v.AddArg(v0) - v2 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) - v2.Type = config.Uintptr - v2.AuxInt = int64(len(s.(string))) - v.AddArg(v2) + v3 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) + v3.Type = config.Uintptr + v3.AuxInt = int64(len(s.(string))) + v.AddArg(v3) return true } - goto end6d6321106a054a5984b2ed0acec52a5b - end6d6321106a054a5984b2ed0acec52a5b: + goto end55cd8fd3b98a2459d0ee9d6cbb456b01 + end55cd8fd3b98a2459d0ee9d6cbb456b01: ; case OpIsInBounds: // match: (IsInBounds (Const [c]) (Const [d])) diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go index 52ddbbe42d..29b47c125e 100644 --- a/src/cmd/compile/internal/ssa/shift_test.go +++ b/src/cmd/compile/internal/ssa/shift_test.go @@ -29,9 +29,9 @@ func makeConstShiftFunc(c *Config, amount int64, op Op, typ Type) fun { fun := Fun(c, "entry", Bloc("entry", Valu("mem", OpArg, TypeMem, 0, ".mem"), - Valu("FP", OpFP, TypeUInt64, 0, nil), - Valu("argptr", OpOffPtr, ptyp, 8, nil, "FP"), - Valu("resptr", OpOffPtr, ptyp, 16, nil, "FP"), + Valu("SP", OpSP, TypeUInt64, 0, nil), + Valu("argptr", OpOffPtr, ptyp, 8, nil, "SP"), + Valu("resptr", OpOffPtr, ptyp, 16, nil, "SP"), Valu("load", OpLoad, typ, 0, nil, "argptr", "mem"), Valu("c", OpConst, TypeUInt64, amount, nil), Valu("shift", op, typ, 0, nil, "load", "c"), diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index e39a3e7a59..85a55ece7c 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -54,7 +54,7 @@ func stackalloc(f *Func) { // v will have been materialized wherever it is needed. continue } - if len(v.Args) == 1 && (v.Args[0].Op == OpFP || v.Args[0].Op == OpSP || v.Args[0].Op == OpGlobal) { + if len(v.Args) == 1 && (v.Args[0].Op == OpSP || v.Args[0].Op == OpSB) { continue } n = align(n, v.Type.Alignment()) @@ -64,54 +64,26 @@ func stackalloc(f *Func) { } } + // Finally, allocate space for all autos that we used + for _, b := range f.Blocks { + for _, v := range b.Values { + s, ok := v.Aux.(*AutoSymbol) + if !ok || s.Offset >= 0 { + continue + } + t := s.Typ + n = align(n, t.Alignment()) + s.Offset = n + n += t.Size() + } + } + n = align(n, f.Config.ptrSize) n += f.Config.ptrSize // space for return address. TODO: arch-dependent f.RegAlloc = home f.FrameSize = n // TODO: share stack slots among noninterfering (& gc type compatible) values - - // adjust all uses of FP to SP now that we have the frame size. - var fp *Value - for _, b := range f.Blocks { - for _, v := range b.Values { - if v.Op == OpFP { - if fp != nil { - b.Fatalf("multiple FP ops: %s %s", fp, v) - } - fp = v - } - for i, a := range v.Args { - if a.Op != OpFP { - continue - } - // TODO: do this with arch-specific rewrite rules somehow? - switch v.Op { - case OpAMD64ADDQ: - // (ADDQ (FP) x) -> (LEAQ [n] (SP) x) - v.Op = OpAMD64LEAQ - v.AuxInt = n - case OpAMD64ADDQconst: - // TODO(matloob): Add LEAQconst op - v.AuxInt = addOff(v.AuxInt, n) - case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVLload, OpAMD64MOVLstore, OpAMD64MOVWload, OpAMD64MOVWstore, OpAMD64MOVBload, OpAMD64MOVBstore, OpAMD64MOVQloadidx8: - if v.Op == OpAMD64MOVQloadidx8 && i == 1 { - // Note: we could do it, but it is probably an error - f.Fatalf("can't do FP->SP adjust on index slot of load %s", v.Op) - } - // eg: (MOVQload [c] (FP) mem) -> (MOVQload [c+n] (SP) mem) - v.AuxInt = addOff(v.AuxInt, n) - default: - f.Unimplementedf("can't do FP->SP adjust on %s", v.Op) - // TODO: OpCopy -> ADDQ - } - } - } - } - if fp != nil { - fp.Op = OpSP - home[fp.ID] = ®isters[4] // TODO: arch-dependent - } } // align increases n to the next multiple of a. a must be a power of 2. diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 9c7f148a79..e6e23d5270 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -114,3 +114,40 @@ func (v *Value) resetArgs() { func (v *Value) Logf(msg string, args ...interface{}) { v.Block.Logf(msg, args...) } func (v *Value) Fatalf(msg string, args ...interface{}) { v.Block.Fatalf(msg, args...) } func (v *Value) Unimplementedf(msg string, args ...interface{}) { v.Block.Unimplementedf(msg, args...) } + +// ExternSymbol is an aux value that encodes a variable's +// constant offset from the static base pointer. +type ExternSymbol struct { + Typ Type // Go type + Sym fmt.Stringer // A *gc.Sym referring to a global variable + // Note: the offset for an external symbol is not + // calculated until link time. +} + +// ArgSymbol is an aux value that encodes an argument or result +// variable's constant offset from FP (FP = SP + framesize). +type ArgSymbol struct { + Typ Type // Go type + Offset int64 // Distance above frame pointer + Sym fmt.Stringer // A *gc.Sym referring to the argument/result variable. +} + +// AutoSymbol is an aux value that encodes a local variable's +// constant offset from SP. +type AutoSymbol struct { + Typ Type // Go type + Offset int64 // Distance above stack pointer. Set by stackalloc in SSA. + Sym fmt.Stringer // A *gc.Sym referring to a local (auto) variable. +} + +func (s *ExternSymbol) String() string { + return s.Sym.String() +} + +func (s *ArgSymbol) String() string { + return s.Sym.String() +} + +func (s *AutoSymbol) String() string { + return s.Sym.String() +} -- cgit v1.3 From 3dcc424be70b9d1824f756ab81f97508ae1a7738 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 14 Jul 2015 13:20:08 -0700 Subject: [dev.ssa] cmd/compile/internal/ssa: compute outarg size correctly Keep track of the outargs size needed at each call. Compute the size of the outargs section of the stack frame. It's just the max of the outargs size at all the callsites in the function. Change-Id: I3d0640f654f01307633b1a5f75bab16e211ea6c0 Reviewed-on: https://go-review.googlesource.com/12178 Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/gc/ssa.go | 2 ++ src/cmd/compile/internal/ssa/TODO | 1 - src/cmd/compile/internal/ssa/gen/AMD64.rules | 4 ++-- src/cmd/compile/internal/ssa/rewriteAMD64.go | 20 ++++++++++++-------- src/cmd/compile/internal/ssa/stackalloc.go | 16 ++++++++++------ 5 files changed, 26 insertions(+), 17 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 96351def6e..2dad3e1a10 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -626,6 +626,8 @@ func (s *state) expr(n *Node) *ssa.Value { entry := s.newValue2(ssa.OpLoad, s.config.Uintptr, closure, s.mem()) call = s.newValue3(ssa.OpClosureCall, ssa.TypeMem, entry, closure, s.mem()) } + dowidth(n.Left.Type) + call.AuxInt = n.Left.Type.Argwid // call operations carry the argsize of the callee along with them b := s.endBlock() b.Kind = ssa.BlockCall b.Control = call diff --git a/src/cmd/compile/internal/ssa/TODO b/src/cmd/compile/internal/ssa/TODO index 340c905654..cfaf520510 100644 --- a/src/cmd/compile/internal/ssa/TODO +++ b/src/cmd/compile/internal/ssa/TODO @@ -29,7 +29,6 @@ Regalloc - Make calls clobber all registers StackAlloc: - - Compute size of outargs section correctly - Sort variables so all ptr-containing ones are first (so stack maps are smaller) - Reuse stack slots for noninterfering and type-compatible variables diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 6882621f71..47e1fb9c6a 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -88,8 +88,8 @@ (If (SETB cmp) yes no) -> (ULT cmp yes no) (If cond yes no) && cond.Op == OpAMD64MOVBload -> (NE (TESTB cond cond) yes no) -(StaticCall {target} mem) -> (CALLstatic {target} mem) -(ClosureCall entry closure mem) -> (CALLclosure entry closure mem) +(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem) +(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem) // Rules below here apply some simple optimizations after lowering. // TODO: Should this be a separate pass? diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 7393cd9a89..7e892c3844 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -460,10 +460,11 @@ func rewriteValueAMD64(v *Value, config *Config) bool { endf8ca12fe79290bc82b11cfa463bc9413: ; case OpClosureCall: - // match: (ClosureCall entry closure mem) + // match: (ClosureCall [argwid] entry closure mem) // cond: - // result: (CALLclosure entry closure mem) + // result: (CALLclosure [argwid] entry closure mem) { + argwid := v.AuxInt entry := v.Args[0] closure := v.Args[1] mem := v.Args[2] @@ -471,13 +472,14 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AuxInt = 0 v.Aux = nil v.resetArgs() + v.AuxInt = argwid v.AddArg(entry) v.AddArg(closure) v.AddArg(mem) return true } - goto endee26da781e813a3c602ccb4f7ade98c7 - endee26da781e813a3c602ccb4f7ade98c7: + goto endfd75d26316012d86cb71d0dd1214259b + endfd75d26316012d86cb71d0dd1214259b: ; case OpConst: // match: (Const [val]) @@ -1611,22 +1613,24 @@ func rewriteValueAMD64(v *Value, config *Config) bool { end78e66b6fc298684ff4ac8aec5ce873c9: ; case OpStaticCall: - // match: (StaticCall {target} mem) + // match: (StaticCall [argwid] {target} mem) // cond: - // result: (CALLstatic {target} mem) + // result: (CALLstatic [argwid] {target} mem) { + argwid := v.AuxInt target := v.Aux mem := v.Args[0] v.Op = OpAMD64CALLstatic v.AuxInt = 0 v.Aux = nil v.resetArgs() + v.AuxInt = argwid v.Aux = target v.AddArg(mem) return true } - goto end1948857a7cfc2a4f905045e58d3b9ec1 - end1948857a7cfc2a4f905045e58d3b9ec1: + goto end32c5cbec813d1c2ae94fc9b1090e4b2a + end32c5cbec813d1c2ae94fc9b1090e4b2a: ; case OpStore: // match: (Store ptr val mem) diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 85a55ece7c..0bd64a1a14 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -9,12 +9,16 @@ package ssa func stackalloc(f *Func) { home := f.RegAlloc - // First compute the size of the outargs section. - n := int64(16) //TODO: compute max of all callsites - - // Include one slot for deferreturn. - if false && n < f.Config.ptrSize { //TODO: check for deferreturn - n = f.Config.ptrSize + // Start with space for callee arguments/returns. + var n int64 + for _, b := range f.Blocks { + if b.Kind != BlockCall { + continue + } + v := b.Control + if n < v.AuxInt { + n = v.AuxInt + } } // TODO: group variables by ptr/nonptr, size, etc. Emit ptr vars last -- cgit v1.3 From 67fdb0de8656f7c7b76afe8eff614da7bc13b221 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 19 Jul 2015 15:48:20 -0700 Subject: [dev.ssa] cmd/compile/internal/ssa: use width and sign specific opcodes Bake the bit width and signedness into opcodes. Pro: Rewrite rules become easier. Less chance for confusion. Con: Lots more opcodes. Let me know what you think. I'm leaning towards this, but I could be convinced otherwise if people think this is too ugly. Update #11467 Change-Id: Icf1b894268cdf73515877bb123839800d97b9df9 Reviewed-on: https://go-review.googlesource.com/12362 Reviewed-by: Alan Donovan Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/gc/ssa.go | 139 ++++- src/cmd/compile/internal/ssa/config.go | 8 +- src/cmd/compile/internal/ssa/func_test.go | 14 +- src/cmd/compile/internal/ssa/gen/AMD64.rules | 45 +- src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 4 + src/cmd/compile/internal/ssa/gen/generic.rules | 15 +- src/cmd/compile/internal/ssa/gen/genericOps.go | 90 +++- src/cmd/compile/internal/ssa/opGen.go | 675 ++++++++++++++++++++++++- src/cmd/compile/internal/ssa/rewriteAMD64.go | 485 +++++++++++------- src/cmd/compile/internal/ssa/rewritegeneric.go | 101 ++-- src/cmd/compile/internal/ssa/schedule_test.go | 2 +- src/cmd/compile/internal/ssa/shift_test.go | 12 +- src/cmd/compile/internal/ssa/stackalloc.go | 4 +- 13 files changed, 1278 insertions(+), 316 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index d4e4298b39..889b9d8cf8 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -446,19 +446,122 @@ func (s *state) stmt(n *Node) { } } -var binOpToSSA = [...]ssa.Op{ - // Comparisons - OEQ: ssa.OpEq, - ONE: ssa.OpNeq, - OLT: ssa.OpLess, - OLE: ssa.OpLeq, - OGT: ssa.OpGreater, - OGE: ssa.OpGeq, - // Arithmetic - OADD: ssa.OpAdd, - OSUB: ssa.OpSub, - OLSH: ssa.OpLsh, - ORSH: ssa.OpRsh, +type opAndType struct { + op uint8 + etype uint8 +} + +var opToSSA = map[opAndType]ssa.Op{ + opAndType{OADD, TINT8}: ssa.OpAdd8, + opAndType{OADD, TUINT8}: ssa.OpAdd8U, + opAndType{OADD, TINT16}: ssa.OpAdd16, + opAndType{OADD, TUINT16}: ssa.OpAdd16U, + opAndType{OADD, TINT32}: ssa.OpAdd32, + opAndType{OADD, TUINT32}: ssa.OpAdd32U, + opAndType{OADD, TINT64}: ssa.OpAdd64, + opAndType{OADD, TUINT64}: ssa.OpAdd64U, + + opAndType{OSUB, TINT8}: ssa.OpSub8, + opAndType{OSUB, TUINT8}: ssa.OpSub8U, + opAndType{OSUB, TINT16}: ssa.OpSub16, + opAndType{OSUB, TUINT16}: ssa.OpSub16U, + opAndType{OSUB, TINT32}: ssa.OpSub32, + opAndType{OSUB, TUINT32}: ssa.OpSub32U, + opAndType{OSUB, TINT64}: ssa.OpSub64, + opAndType{OSUB, TUINT64}: ssa.OpSub64U, + + opAndType{OLSH, TINT8}: ssa.OpLsh8, + opAndType{OLSH, TUINT8}: ssa.OpLsh8, + opAndType{OLSH, TINT16}: ssa.OpLsh16, + opAndType{OLSH, TUINT16}: ssa.OpLsh16, + opAndType{OLSH, TINT32}: ssa.OpLsh32, + opAndType{OLSH, TUINT32}: ssa.OpLsh32, + opAndType{OLSH, TINT64}: ssa.OpLsh64, + opAndType{OLSH, TUINT64}: ssa.OpLsh64, + + opAndType{ORSH, TINT8}: ssa.OpRsh8, + opAndType{ORSH, TUINT8}: ssa.OpRsh8U, + opAndType{ORSH, TINT16}: ssa.OpRsh16, + opAndType{ORSH, TUINT16}: ssa.OpRsh16U, + opAndType{ORSH, TINT32}: ssa.OpRsh32, + opAndType{ORSH, TUINT32}: ssa.OpRsh32U, + opAndType{ORSH, TINT64}: ssa.OpRsh64, + opAndType{ORSH, TUINT64}: ssa.OpRsh64U, + + opAndType{OEQ, TINT8}: ssa.OpEq8, + opAndType{OEQ, TUINT8}: ssa.OpEq8, + opAndType{OEQ, TINT16}: ssa.OpEq16, + opAndType{OEQ, TUINT16}: ssa.OpEq16, + opAndType{OEQ, TINT32}: ssa.OpEq32, + opAndType{OEQ, TUINT32}: ssa.OpEq32, + opAndType{OEQ, TINT64}: ssa.OpEq64, + opAndType{OEQ, TUINT64}: ssa.OpEq64, + + opAndType{ONE, TINT8}: ssa.OpNeq8, + opAndType{ONE, TUINT8}: ssa.OpNeq8, + opAndType{ONE, TINT16}: ssa.OpNeq16, + opAndType{ONE, TUINT16}: ssa.OpNeq16, + opAndType{ONE, TINT32}: ssa.OpNeq32, + opAndType{ONE, TUINT32}: ssa.OpNeq32, + opAndType{ONE, TINT64}: ssa.OpNeq64, + opAndType{ONE, TUINT64}: ssa.OpNeq64, + + opAndType{OLT, TINT8}: ssa.OpLess8, + opAndType{OLT, TUINT8}: ssa.OpLess8U, + opAndType{OLT, TINT16}: ssa.OpLess16, + opAndType{OLT, TUINT16}: ssa.OpLess16U, + opAndType{OLT, TINT32}: ssa.OpLess32, + opAndType{OLT, TUINT32}: ssa.OpLess32U, + opAndType{OLT, TINT64}: ssa.OpLess64, + opAndType{OLT, TUINT64}: ssa.OpLess64U, + + opAndType{OGT, TINT8}: ssa.OpGreater8, + opAndType{OGT, TUINT8}: ssa.OpGreater8U, + opAndType{OGT, TINT16}: ssa.OpGreater16, + opAndType{OGT, TUINT16}: ssa.OpGreater16U, + opAndType{OGT, TINT32}: ssa.OpGreater32, + opAndType{OGT, TUINT32}: ssa.OpGreater32U, + opAndType{OGT, TINT64}: ssa.OpGreater64, + opAndType{OGT, TUINT64}: ssa.OpGreater64U, + + opAndType{OLE, TINT8}: ssa.OpLeq8, + opAndType{OLE, TUINT8}: ssa.OpLeq8U, + opAndType{OLE, TINT16}: ssa.OpLeq16, + opAndType{OLE, TUINT16}: ssa.OpLeq16U, + opAndType{OLE, TINT32}: ssa.OpLeq32, + opAndType{OLE, TUINT32}: ssa.OpLeq32U, + opAndType{OLE, TINT64}: ssa.OpLeq64, + opAndType{OLE, TUINT64}: ssa.OpLeq64U, + + opAndType{OGE, TINT8}: ssa.OpGeq8, + opAndType{OGE, TUINT8}: ssa.OpGeq8U, + opAndType{OGE, TINT16}: ssa.OpGeq16, + opAndType{OGE, TUINT16}: ssa.OpGeq16U, + opAndType{OGE, TINT32}: ssa.OpGeq32, + opAndType{OGE, TUINT32}: ssa.OpGeq32U, + opAndType{OGE, TINT64}: ssa.OpGeq64, + opAndType{OGE, TUINT64}: ssa.OpGeq64U, +} + +func (s *state) ssaOp(op uint8, t *Type) ssa.Op { + etype := t.Etype + switch etype { + case TINT: + etype = TINT32 + if s.config.PtrSize == 8 { + etype = TINT64 + } + case TUINT: + etype = TUINT32 + if s.config.PtrSize == 8 { + etype = TUINT64 + } + } + x, ok := opToSSA[opAndType{op, etype}] + if !ok { + s.Unimplementedf("unhandled binary op %s etype=%d", opnames[op], etype) + } + return x } // expr converts the expression n to ssa, adds it to s and returns the ssa result. @@ -503,11 +606,11 @@ func (s *state) expr(n *Node) *ssa.Value { case OLT, OEQ, ONE, OLE, OGE, OGT: a := s.expr(n.Left) b := s.expr(n.Right) - return s.newValue2(binOpToSSA[n.Op], ssa.TypeBool, a, b) + return s.newValue2(s.ssaOp(n.Op, n.Left.Type), ssa.TypeBool, a, b) case OADD, OSUB, OLSH, ORSH: a := s.expr(n.Left) b := s.expr(n.Right) - return s.newValue2(binOpToSSA[n.Op], a.Type, a, b) + return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b) case OANDAND, OOROR: // To implement OANDAND (and OOROR), we introduce a // new temporary variable to hold the result. The @@ -569,7 +672,7 @@ func (s *state) expr(n *Node) *ssa.Value { case ODOTPTR: p := s.expr(n.Left) s.nilCheck(p) - p = s.newValue2(ssa.OpAdd, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset)) + p = s.newValue2(ssa.OpAddPtr, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset)) return s.newValue2(ssa.OpLoad, n.Type, p, s.mem()) case OINDEX: @@ -742,11 +845,11 @@ func (s *state) addr(n *Node) *ssa.Value { return p case ODOT: p := s.addr(n.Left) - return s.newValue2(ssa.OpAdd, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset)) + return s.newValue2(ssa.OpAddPtr, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset)) case ODOTPTR: p := s.expr(n.Left) s.nilCheck(p) - return s.newValue2(ssa.OpAdd, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset)) + return s.newValue2(ssa.OpAddPtr, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset)) default: s.Unimplementedf("addr: bad op %v", Oconv(int(n.Op), 0)) return nil diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index c6c7bf36e9..c9e543ba37 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -6,7 +6,7 @@ package ssa type Config struct { arch string // "amd64", etc. - ptrSize int64 // 4 or 8 + PtrSize int64 // 4 or 8 Uintptr Type // pointer arithmetic type Int Type lowerBlock func(*Block) bool // lowering function @@ -38,11 +38,11 @@ func NewConfig(arch string, fe Frontend) *Config { c := &Config{arch: arch, fe: fe} switch arch { case "amd64": - c.ptrSize = 8 + c.PtrSize = 8 c.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 case "386": - c.ptrSize = 4 + c.PtrSize = 4 c.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support default: @@ -52,7 +52,7 @@ func NewConfig(arch string, fe Frontend) *Config { // cache the frequently-used types in the config c.Uintptr = TypeUInt32 c.Int = TypeInt32 - if c.ptrSize == 8 { + if c.PtrSize == 8 { c.Uintptr = TypeUInt64 c.Int = TypeInt64 } diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index a620e8f602..edea8f78d1 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -267,7 +267,7 @@ func TestArgs(t *testing.T) { Bloc("entry", Valu("a", OpConst, TypeInt64, 14, nil), Valu("b", OpConst, TypeInt64, 26, nil), - Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", @@ -290,7 +290,7 @@ func TestEquiv(t *testing.T) { Bloc("entry", Valu("a", OpConst, TypeInt64, 14, nil), Valu("b", OpConst, TypeInt64, 26, nil), - Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", @@ -299,7 +299,7 @@ func TestEquiv(t *testing.T) { Bloc("entry", Valu("a", OpConst, TypeInt64, 14, nil), Valu("b", OpConst, TypeInt64, 26, nil), - Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", @@ -311,7 +311,7 @@ func TestEquiv(t *testing.T) { Bloc("entry", Valu("a", OpConst, TypeInt64, 14, nil), Valu("b", OpConst, TypeInt64, 26, nil), - Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", @@ -322,7 +322,7 @@ func TestEquiv(t *testing.T) { Bloc("entry", Valu("a", OpConst, TypeInt64, 14, nil), Valu("b", OpConst, TypeInt64, 26, nil), - Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Valu("mem", OpArg, TypeMem, 0, ".mem"), Goto("exit"))), }, @@ -397,14 +397,14 @@ func TestEquiv(t *testing.T) { Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("a", OpConst, TypeInt64, 14, nil), Valu("b", OpConst, TypeInt64, 26, nil), - Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Exit("mem"))), Fun(c, "entry", Bloc("entry", Valu("mem", OpArg, TypeMem, 0, ".mem"), Valu("a", OpConst, TypeInt64, 0, nil), Valu("b", OpConst, TypeInt64, 14, nil), - Valu("sum", OpAdd, TypeInt64, 0, nil, "b", "a"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "b", "a"), Exit("mem"))), }, } diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 3e25929af5..eba3710460 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -13,14 +13,25 @@ // Unused portions are junk. // Lowering arithmetic -(Add x y) && (is64BitInt(t) || isPtr(t)) -> (ADDQ x y) -(Add x y) && is32BitInt(t) && !isSigned(t) -> (ADDL x y) -(Add x y) && is32BitInt(t) && isSigned(t) -> (MOVLQSX (ADDL x y)) -(Add x y) && is16BitInt(t) && !isSigned(t) -> (ADDW x y) -(Add x y) && is16BitInt(t) && isSigned(t) -> (MOVWQSX (ADDW x y)) -(Add x y) && is8BitInt(t) && !isSigned(t) -> (ADDB x y) -(Add x y) && is8BitInt(t) && isSigned(t) -> (MOVBQSX (ADDB x y)) -(Sub x y) && is64BitInt(t) -> (SUBQ x y) +(Add64 x y) -> (ADDQ x y) +(Add64U x y) -> (ADDQ x y) +(AddPtr x y) -> (ADDQ x y) +(Add32U x y) -> (ADDL x y) +(Add32 x y) -> (MOVLQSX (ADDL x y)) +(Add16U x y) -> (ADDW x y) +(Add16 x y) -> (MOVWQSX (ADDW x y)) +(Add8U x y) -> (ADDB x y) +(Add8 x y) -> (MOVBQSX (ADDB x y)) + +(Sub64 x y) -> (SUBQ x y) +(Sub64U x y) -> (SUBQ x y) +(Sub32U x y) -> (SUBL x y) +(Sub32 x y) -> (MOVLQSX (SUBL x y)) +(Sub16U x y) -> (SUBW x y) +(Sub16 x y) -> (MOVWQSX (SUBW x y)) +(Sub8U x y) -> (SUBB x y) +(Sub8 x y) -> (MOVBQSX (SUBB x y)) + (Mul x y) && is64BitInt(t) -> (MULQ x y) (MOVLstore ptr (MOVLQSX x) mem) -> (MOVLstore ptr x mem) @@ -34,26 +45,26 @@ // Note: unsigned shifts need to return 0 if shift amount is >= 64. // mask = shift >= 64 ? 0 : 0xffffffffffffffff // result = mask & arg << shift -(Lsh x y) && is64BitInt(t) -> +(Lsh64 x y) -> (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [64] y))) -(Rsh x y) && is64BitInt(t) && !t.IsSigned() -> +(Rsh64U x y) -> (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [64] y))) // Note: signed right shift needs to return 0/-1 if shift amount is >= 64. // if shift > 63 { shift = 63 } // result = arg >> shift -(Rsh x y) && is64BitInt(t) && t.IsSigned() -> +(Rsh64 x y) -> (SARQ x (CMOVQCC (CMPQconst [64] y) (Const [63]) y)) -(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y)) -(Leq x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETLE (CMPQ x y)) -(Greater x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETG (CMPQ x y)) -(Geq x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETGE (CMPQ x y)) -(Eq x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETEQ (CMPQ x y)) -(Neq x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETNE (CMPQ x y)) +(Less64 x y) -> (SETL (CMPQ x y)) +(Leq64 x y) -> (SETLE (CMPQ x y)) +(Greater64 x y) -> (SETG (CMPQ x y)) +(Geq64 x y) -> (SETGE (CMPQ x y)) +(Eq64 x y) -> (SETEQ (CMPQ x y)) +(Neq64 x y) -> (SETNE (CMPQ x y)) (Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload ptr mem) (Load ptr mem) && is32BitInt(t) -> (MOVLload ptr mem) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 31beb005f8..602949eac9 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -174,6 +174,10 @@ func init() { {name: "ADDW", reg: gp21, asm: "ADDW"}, // arg0+arg1 {name: "ADDB", reg: gp21, asm: "ADDB"}, // arg0+arg1 + {name: "SUBL", reg: gp21, asm: "SUBL"}, // arg0-arg1 + {name: "SUBW", reg: gp21, asm: "SUBW"}, // arg0-arg1 + {name: "SUBB", reg: gp21, asm: "SUBB"}, // arg0-arg1 + // (InvertFlags (CMPQ a b)) == (CMPQ b a) // So if we want (SETL (CMPQ a b)) but we can't do that because a is a constant, // then we do (SETL (InvertFlags (CMPQ b a))) instead. diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index a906ec6a5c..e505c43d26 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -20,20 +20,21 @@ // For now, the generated successors must be a permutation of the matched successors. // constant folding -(Add (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [c+d]) +(Add64 (Const [c]) (Const [d])) -> (Const [c+d]) +(Add64U (Const [c]) (Const [d])) -> (Const [c+d]) (Mul (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [c*d]) (IsInBounds (Const [c]) (Const [d])) -> (Const {inBounds(c,d)}) // tear apart slices // TODO: anything that generates a slice needs to go in here. (SlicePtr (Load ptr mem)) -> (Load ptr mem) -(SliceLen (Load ptr mem)) -> (Load (Add ptr (Const [config.ptrSize])) mem) -(SliceCap (Load ptr mem)) -> (Load (Add ptr (Const [config.ptrSize*2])) mem) +(SliceLen (Load ptr mem)) -> (Load (AddPtr ptr (Const [config.PtrSize])) mem) +(SliceCap (Load ptr mem)) -> (Load (AddPtr ptr (Const [config.PtrSize*2])) mem) // indexing operations // Note: bounds check has already been done (ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex ptr idx) mem) -(PtrIndex ptr idx) -> (Add ptr (Mul idx (Const [t.Elem().Size()]))) +(PtrIndex ptr idx) -> (AddPtr ptr (Mul idx (Const [t.Elem().Size()]))) (StructSelect [idx] (Load ptr mem)) -> (Load (OffPtr [idx] ptr) mem) // big-object moves @@ -41,11 +42,11 @@ (Store dst (Load src mem) mem) && t.Size() > 8 -> (Move [t.Size()] dst src mem) // string ops -(Const {s}) && t.IsString() -> (StringMake (OffPtr [2*config.ptrSize] (Addr {config.fe.StringSym(s.(string))} (SB ))) (Const [int64(len(s.(string)))])) // TODO: ptr -(Load ptr mem) && t.IsString() -> (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) +(Const {s}) && t.IsString() -> (StringMake (OffPtr [2*config.PtrSize] (Addr {config.fe.StringSym(s.(string))} (SB ))) (Const [int64(len(s.(string)))])) // TODO: ptr +(Load ptr mem) && t.IsString() -> (StringMake (Load ptr mem) (Load (OffPtr [config.PtrSize] ptr) mem)) (StringPtr (StringMake ptr _)) -> ptr (StringLen (StringMake _ len)) -> len -(Store dst str mem) && str.Type.IsString() -> (Store (OffPtr [config.ptrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) +(Store dst str mem) && str.Type.IsString() -> (Store (OffPtr [config.PtrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) (If (Const {c}) yes no) && c.(bool) -> (Plain nil yes) (If (Const {c}) yes no) && !c.(bool) -> (Plain nil no) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 0af7df1775..12c2901076 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -8,19 +8,89 @@ var genericOps = []opData{ // 2-input arithmetic // Types must be consistent with Go typing. Add, for example, must take two values // of the same type and produces that same type. - {name: "Add"}, // arg0 + arg1 - {name: "Sub"}, // arg0 - arg1 + {name: "Add8"}, // arg0 + arg1 + {name: "Add16"}, + {name: "Add32"}, + {name: "Add64"}, + {name: "Add8U"}, + {name: "Add16U"}, + {name: "Add32U"}, + {name: "Add64U"}, + {name: "AddPtr"}, + // TODO: Add32F, Add64F, Add64C, Add128C + + {name: "Sub8"}, // arg0 - arg1 + {name: "Sub16"}, + {name: "Sub32"}, + {name: "Sub64"}, + {name: "Sub8U"}, + {name: "Sub16U"}, + {name: "Sub32U"}, + {name: "Sub64U"}, + // TODO: Sub32F, Sub64F, Sub64C, Sub128C + {name: "Mul"}, // arg0 * arg1 - {name: "Lsh"}, // arg0 << arg1 - {name: "Rsh"}, // arg0 >> arg1 (signed/unsigned depending on signedness of type) + + {name: "Lsh8"}, // arg0 << arg1 + {name: "Lsh16"}, + {name: "Lsh32"}, + {name: "Lsh64"}, + + {name: "Rsh8"}, // arg0 >> arg1 + {name: "Rsh8U"}, + {name: "Rsh16"}, + {name: "Rsh16U"}, + {name: "Rsh32"}, + {name: "Rsh32U"}, + {name: "Rsh64"}, + {name: "Rsh64U"}, // 2-input comparisons - {name: "Eq"}, // arg0 == arg1 - {name: "Neq"}, // arg0 != arg1 - {name: "Less"}, // arg0 < arg1 - {name: "Leq"}, // arg0 <= arg1 - {name: "Greater"}, // arg0 > arg1 - {name: "Geq"}, // arg0 <= arg1 + {name: "Eq8"}, // arg0 == arg1 + {name: "Eq16"}, + {name: "Eq32"}, + {name: "Eq64"}, + + {name: "Neq8"}, // arg0 != arg1 + {name: "Neq16"}, + {name: "Neq32"}, + {name: "Neq64"}, + + {name: "Less8"}, // arg0 < arg1 + {name: "Less8U"}, + {name: "Less16"}, + {name: "Less16U"}, + {name: "Less32"}, + {name: "Less32U"}, + {name: "Less64"}, + {name: "Less64U"}, + + {name: "Leq8"}, // arg0 <= arg1 + {name: "Leq8U"}, + {name: "Leq16"}, + {name: "Leq16U"}, + {name: "Leq32"}, + {name: "Leq32U"}, + {name: "Leq64"}, + {name: "Leq64U"}, + + {name: "Greater8"}, // arg0 > arg1 + {name: "Greater8U"}, + {name: "Greater16"}, + {name: "Greater16U"}, + {name: "Greater32"}, + {name: "Greater32U"}, + {name: "Greater64"}, + {name: "Greater64U"}, + + {name: "Geq8"}, // arg0 <= arg1 + {name: "Geq8U"}, + {name: "Geq16"}, + {name: "Geq16U"}, + {name: "Geq32"}, + {name: "Geq32U"}, + {name: "Geq64"}, + {name: "Geq64U"}, // 1-input ops {name: "Not"}, // !arg0 diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 74d30e1df5..95e2ef798a 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -111,19 +111,81 @@ const ( OpAMD64ADDL OpAMD64ADDW OpAMD64ADDB + OpAMD64SUBL + OpAMD64SUBW + OpAMD64SUBB OpAMD64InvertFlags - OpAdd - OpSub + OpAdd8 + OpAdd16 + OpAdd32 + OpAdd64 + OpAdd8U + OpAdd16U + OpAdd32U + OpAdd64U + OpAddPtr + OpSub8 + OpSub16 + OpSub32 + OpSub64 + OpSub8U + OpSub16U + OpSub32U + OpSub64U OpMul - OpLsh - OpRsh - OpEq - OpNeq - OpLess - OpLeq - OpGreater - OpGeq + OpLsh8 + OpLsh16 + OpLsh32 + OpLsh64 + OpRsh8 + OpRsh8U + OpRsh16 + OpRsh16U + OpRsh32 + OpRsh32U + OpRsh64 + OpRsh64U + OpEq8 + OpEq16 + OpEq32 + OpEq64 + OpNeq8 + OpNeq16 + OpNeq32 + OpNeq64 + OpLess8 + OpLess8U + OpLess16 + OpLess16U + OpLess32 + OpLess32U + OpLess64 + OpLess64U + OpLeq8 + OpLeq8U + OpLeq16 + OpLeq16U + OpLeq32 + OpLeq32U + OpLeq64 + OpLeq64U + OpGreater8 + OpGreater8U + OpGreater16 + OpGreater16U + OpGreater32 + OpGreater32U + OpGreater64 + OpGreater64U + OpGeq8 + OpGeq8U + OpGeq16 + OpGeq16U + OpGeq32 + OpGeq32U + OpGeq64 + OpGeq64U OpNot OpPhi OpCopy @@ -927,6 +989,48 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "SUBL", + asm: x86.ASUBL, + reg: regInfo{ + inputs: []regMask{ + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, + { + name: "SUBW", + asm: x86.ASUBW, + reg: regInfo{ + inputs: []regMask{ + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, + { + name: "SUBB", + asm: x86.ASUBB, + reg: regInfo{ + inputs: []regMask{ + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + clobbers: 0, + outputs: []regMask{ + 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + }, + }, + }, { name: "InvertFlags", reg: regInfo{ @@ -937,7 +1041,142 @@ var opcodeTable = [...]opInfo{ }, { - name: "Add", + name: "Add8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Add16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Add32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Add64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Add8U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Add16U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Add32U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Add64U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "AddPtr", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub8U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub16U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Sub32U", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -946,7 +1185,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Sub", + name: "Sub64U", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -964,7 +1203,403 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Lsh", + name: "Lsh8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Lsh16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Lsh32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Lsh64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh8U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh16U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh32U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Rsh64U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Eq8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Eq16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Eq32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Eq64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Neq8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Neq16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Neq32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Neq64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less8U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less16U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less32U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Less64U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq8U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq16U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq32U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Leq64U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater8", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater8U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater16", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater16U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater32", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater32U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater64", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Greater64U", + reg: regInfo{ + inputs: []regMask{}, + clobbers: 0, + outputs: []regMask{}, + }, + generic: true, + }, + { + name: "Geq8", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -973,7 +1608,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Rsh", + name: "Geq8U", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -982,7 +1617,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Eq", + name: "Geq16", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -991,7 +1626,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Neq", + name: "Geq16U", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -1000,7 +1635,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Less", + name: "Geq32", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -1009,7 +1644,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Leq", + name: "Geq32U", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -1018,7 +1653,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Greater", + name: "Geq64", reg: regInfo{ inputs: []regMask{}, clobbers: 0, @@ -1027,7 +1662,7 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "Geq", + name: "Geq64U", reg: regInfo{ inputs: []regMask{}, clobbers: 0, diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 3e24f9f618..9a879a39bb 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -187,39 +187,35 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end646afc7b328db89ad16ebfa156ae26e5 end646afc7b328db89ad16ebfa156ae26e5: ; - case OpAdd: - // match: (Add x y) - // cond: (is64BitInt(t) || isPtr(t)) - // result: (ADDQ x y) + case OpAdd16: + // match: (Add16 x y) + // cond: + // result: (MOVWQSX (ADDW x y)) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is64BitInt(t) || isPtr(t)) { - goto endf031c523d7dd08e4b8e7010a94cd94c9 - } - v.Op = OpAMD64ADDQ + v.Op = OpAMD64MOVWQSX v.AuxInt = 0 v.Aux = nil v.resetArgs() - v.AddArg(x) - v.AddArg(y) + v0 := v.Block.NewValue0(v.Line, OpAMD64ADDW, TypeInvalid) + v0.Type = v.Type + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) return true } - goto endf031c523d7dd08e4b8e7010a94cd94c9 - endf031c523d7dd08e4b8e7010a94cd94c9: + goto end2aef2dab49f6b2ca337f58ad0a8209ae + end2aef2dab49f6b2ca337f58ad0a8209ae: ; - // match: (Add x y) - // cond: is32BitInt(t) && !isSigned(t) - // result: (ADDL x y) + case OpAdd16U: + // match: (Add16U x y) + // cond: + // result: (ADDW x y) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is32BitInt(t) && !isSigned(t)) { - goto endce1730b0a04d773ed8029e7eac4f3a50 - } - v.Op = OpAMD64ADDL + v.Op = OpAMD64ADDW v.AuxInt = 0 v.Aux = nil v.resetArgs() @@ -227,44 +223,38 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(y) return true } - goto endce1730b0a04d773ed8029e7eac4f3a50 - endce1730b0a04d773ed8029e7eac4f3a50: + goto end8ca34beeb0897b0c70352ba90cca4a1d + end8ca34beeb0897b0c70352ba90cca4a1d: ; - // match: (Add x y) - // cond: is32BitInt(t) && isSigned(t) - // result: (MOVLQSX (ADDL x y)) + case OpAdd32: + // match: (Add32 x y) + // cond: + // result: (MOVLQSX (ADDL x y)) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is32BitInt(t) && isSigned(t)) { - goto end86e07674e2e9d2e1fc5a8f5f74375513 - } v.Op = OpAMD64MOVLQSX v.AuxInt = 0 v.Aux = nil v.resetArgs() v0 := v.Block.NewValue0(v.Line, OpAMD64ADDL, TypeInvalid) - v0.Type = t + v0.Type = v.Type v0.AddArg(x) v0.AddArg(y) v.AddArg(v0) return true } - goto end86e07674e2e9d2e1fc5a8f5f74375513 - end86e07674e2e9d2e1fc5a8f5f74375513: + goto end7f18bca004d8c158f50b04e7511af49f + end7f18bca004d8c158f50b04e7511af49f: ; - // match: (Add x y) - // cond: is16BitInt(t) && !isSigned(t) - // result: (ADDW x y) + case OpAdd32U: + // match: (Add32U x y) + // cond: + // result: (ADDL x y) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is16BitInt(t) && !isSigned(t)) { - goto end99632c2482f1963513f12a317c588800 - } - v.Op = OpAMD64ADDW + v.Op = OpAMD64ADDL v.AuxInt = 0 v.Aux = nil v.resetArgs() @@ -272,44 +262,35 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(y) return true } - goto end99632c2482f1963513f12a317c588800 - end99632c2482f1963513f12a317c588800: + goto end72ff71aa883fa569307ae06289ac1e30 + end72ff71aa883fa569307ae06289ac1e30: ; - // match: (Add x y) - // cond: is16BitInt(t) && isSigned(t) - // result: (MOVWQSX (ADDW x y)) + case OpAdd64: + // match: (Add64 x y) + // cond: + // result: (ADDQ x y) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is16BitInt(t) && isSigned(t)) { - goto endd215b5658d14e7d1cb469a516aa554e9 - } - v.Op = OpAMD64MOVWQSX + v.Op = OpAMD64ADDQ v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue0(v.Line, OpAMD64ADDW, TypeInvalid) - v0.Type = t - v0.AddArg(x) - v0.AddArg(y) - v.AddArg(v0) + v.AddArg(x) + v.AddArg(y) return true } - goto endd215b5658d14e7d1cb469a516aa554e9 - endd215b5658d14e7d1cb469a516aa554e9: + goto endd88f18b3f39e3ccc201477a616f0abc0 + endd88f18b3f39e3ccc201477a616f0abc0: ; - // match: (Add x y) - // cond: is8BitInt(t) && !isSigned(t) - // result: (ADDB x y) + case OpAdd64U: + // match: (Add64U x y) + // cond: + // result: (ADDQ x y) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is8BitInt(t) && !isSigned(t)) { - goto end41d7f409a1e1076e9645e2e90b7220ce - } - v.Op = OpAMD64ADDB + v.Op = OpAMD64ADDQ v.AuxInt = 0 v.Aux = nil v.resetArgs() @@ -317,32 +298,65 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(y) return true } - goto end41d7f409a1e1076e9645e2e90b7220ce - end41d7f409a1e1076e9645e2e90b7220ce: + goto endee28cc0dbdf2664cb3f6a5ddb3960b1b + endee28cc0dbdf2664cb3f6a5ddb3960b1b: ; - // match: (Add x y) - // cond: is8BitInt(t) && isSigned(t) - // result: (MOVBQSX (ADDB x y)) + case OpAdd8: + // match: (Add8 x y) + // cond: + // result: (MOVBQSX (ADDB x y)) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is8BitInt(t) && isSigned(t)) { - goto end858e823866524b81b4636f7dd7e8eefe - } v.Op = OpAMD64MOVBQSX v.AuxInt = 0 v.Aux = nil v.resetArgs() v0 := v.Block.NewValue0(v.Line, OpAMD64ADDB, TypeInvalid) - v0.Type = t + v0.Type = v.Type v0.AddArg(x) v0.AddArg(y) v.AddArg(v0) return true } - goto end858e823866524b81b4636f7dd7e8eefe - end858e823866524b81b4636f7dd7e8eefe: + goto end7078e2b21b2da3acc80e79ba1386d098 + end7078e2b21b2da3acc80e79ba1386d098: + ; + case OpAdd8U: + // match: (Add8U x y) + // cond: + // result: (ADDB x y) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64ADDB + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto endb5cb0e4b3566464c17acf1df5e4b0543 + endb5cb0e4b3566464c17acf1df5e4b0543: + ; + case OpAddPtr: + // match: (AddPtr x y) + // cond: + // result: (ADDQ x y) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64ADDQ + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto enda1d5640788c7157996f9d4af602dec1c + enda1d5640788c7157996f9d4af602dec1c: ; case OpAddr: // match: (Addr {sym} base) @@ -595,16 +609,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endcc7894224d4f6b0bcabcece5d0185912 endcc7894224d4f6b0bcabcece5d0185912: ; - case OpEq: - // match: (Eq x y) - // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) + case OpEq64: + // match: (Eq64 x y) + // cond: // result: (SETEQ (CMPQ x y)) { x := v.Args[0] y := v.Args[1] - if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { - goto endad64a62086703de09f52315e190bdf0e - } v.Op = OpAMD64SETEQ v.AuxInt = 0 v.Aux = nil @@ -616,19 +627,16 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto endad64a62086703de09f52315e190bdf0e - endad64a62086703de09f52315e190bdf0e: + goto endae6c62e4e20b4f62694b6ee40dbd9211 + endae6c62e4e20b4f62694b6ee40dbd9211: ; - case OpGeq: - // match: (Geq x y) - // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) + case OpGeq64: + // match: (Geq64 x y) + // cond: // result: (SETGE (CMPQ x y)) { x := v.Args[0] y := v.Args[1] - if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { - goto end31ba1968829a3b451a35431111140fec - } v.Op = OpAMD64SETGE v.AuxInt = 0 v.Aux = nil @@ -640,19 +648,16 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto end31ba1968829a3b451a35431111140fec - end31ba1968829a3b451a35431111140fec: + goto end63f44e3fec8d92723b5bde42d6d7eea0 + end63f44e3fec8d92723b5bde42d6d7eea0: ; - case OpGreater: - // match: (Greater x y) - // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) + case OpGreater64: + // match: (Greater64 x y) + // cond: // result: (SETG (CMPQ x y)) { x := v.Args[0] y := v.Args[1] - if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { - goto end1cff30b1bf40104e5e30ab73d6568f7f - } v.Op = OpAMD64SETG v.AuxInt = 0 v.Aux = nil @@ -664,8 +669,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto end1cff30b1bf40104e5e30ab73d6568f7f - end1cff30b1bf40104e5e30ab73d6568f7f: + goto endaef0cfa5e27e23cf5e527061cf251069 + endaef0cfa5e27e23cf5e527061cf251069: ; case OpIsInBounds: // match: (IsInBounds idx len) @@ -708,16 +713,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endff508c3726edfb573abc6128c177e76c endff508c3726edfb573abc6128c177e76c: ; - case OpLeq: - // match: (Leq x y) - // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) + case OpLeq64: + // match: (Leq64 x y) + // cond: // result: (SETLE (CMPQ x y)) { x := v.Args[0] y := v.Args[1] - if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { - goto enddb4f100c01cdd95d69d399ffc37e33e7 - } v.Op = OpAMD64SETLE v.AuxInt = 0 v.Aux = nil @@ -729,19 +731,16 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto enddb4f100c01cdd95d69d399ffc37e33e7 - enddb4f100c01cdd95d69d399ffc37e33e7: + goto endf03da5e28dccdb4797671f39e824fb10 + endf03da5e28dccdb4797671f39e824fb10: ; - case OpLess: - // match: (Less x y) - // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) + case OpLess64: + // match: (Less64 x y) + // cond: // result: (SETL (CMPQ x y)) { x := v.Args[0] y := v.Args[1] - if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { - goto endcecf13a952d4c6c2383561c7d68a3cf9 - } v.Op = OpAMD64SETL v.AuxInt = 0 v.Aux = nil @@ -753,8 +752,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto endcecf13a952d4c6c2383561c7d68a3cf9 - endcecf13a952d4c6c2383561c7d68a3cf9: + goto endf8e7a24c25692045bbcfd2c9356d1a8c + endf8e7a24c25692045bbcfd2c9356d1a8c: ; case OpLoad: // match: (Load ptr mem) @@ -841,17 +840,14 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end8f83bf72293670e75b22d6627bd13f0b end8f83bf72293670e75b22d6627bd13f0b: ; - case OpLsh: - // match: (Lsh x y) - // cond: is64BitInt(t) + case OpLsh64: + // match: (Lsh64 x y) + // cond: // result: (ANDQ (SHLQ x y) (SBBQcarrymask (CMPQconst [64] y))) { t := v.Type x := v.Args[0] y := v.Args[1] - if !(is64BitInt(t)) { - goto end5d9e2211940fbc82536685578cf37d08 - } v.Op = OpAMD64ANDQ v.AuxInt = 0 v.Aux = nil @@ -871,8 +867,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v1) return true } - goto end5d9e2211940fbc82536685578cf37d08 - end5d9e2211940fbc82536685578cf37d08: + goto end02b17b9d1aca859d392e527fe6fc58da + end02b17b9d1aca859d392e527fe6fc58da: ; case OpAMD64MOVBstore: // match: (MOVBstore ptr (MOVBQSX x) mem) @@ -1289,16 +1285,13 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto endfab0d598f376ecba45a22587d50f7aff endfab0d598f376ecba45a22587d50f7aff: ; - case OpNeq: - // match: (Neq x y) - // cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) + case OpNeq64: + // match: (Neq64 x y) + // cond: // result: (SETNE (CMPQ x y)) { x := v.Args[0] y := v.Args[1] - if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) { - goto enddccbd4e7581ae8d9916b933d3501987b - } v.Op = OpAMD64SETNE v.AuxInt = 0 v.Aux = nil @@ -1310,8 +1303,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto enddccbd4e7581ae8d9916b933d3501987b - enddccbd4e7581ae8d9916b933d3501987b: + goto end8ab0bcb910c0d3213dd8726fbcc4848e + end8ab0bcb910c0d3213dd8726fbcc4848e: ; case OpNot: // match: (Not x) @@ -1348,49 +1341,14 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end0429f947ee7ac49ff45a243e461a5290 end0429f947ee7ac49ff45a243e461a5290: ; - case OpRsh: - // match: (Rsh x y) - // cond: is64BitInt(t) && !t.IsSigned() - // result: (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [64] y))) - { - t := v.Type - x := v.Args[0] - y := v.Args[1] - if !(is64BitInt(t) && !t.IsSigned()) { - goto ende3e068773b8e6def1eaedb4f404ca6e5 - } - v.Op = OpAMD64ANDQ - v.AuxInt = 0 - v.Aux = nil - v.resetArgs() - v0 := v.Block.NewValue0(v.Line, OpAMD64SHRQ, TypeInvalid) - v0.Type = t - v0.AddArg(x) - v0.AddArg(y) - v.AddArg(v0) - v1 := v.Block.NewValue0(v.Line, OpAMD64SBBQcarrymask, TypeInvalid) - v1.Type = t - v2 := v.Block.NewValue0(v.Line, OpAMD64CMPQconst, TypeInvalid) - v2.Type = TypeFlags - v2.AuxInt = 64 - v2.AddArg(y) - v1.AddArg(v2) - v.AddArg(v1) - return true - } - goto ende3e068773b8e6def1eaedb4f404ca6e5 - ende3e068773b8e6def1eaedb4f404ca6e5: - ; - // match: (Rsh x y) - // cond: is64BitInt(t) && t.IsSigned() + case OpRsh64: + // match: (Rsh64 x y) + // cond: // result: (SARQ x (CMOVQCC (CMPQconst [64] y) (Const [63]) y)) { t := v.Type x := v.Args[0] y := v.Args[1] - if !(is64BitInt(t) && t.IsSigned()) { - goto end901ea4851cd5d2277a1ca1bee8f69d59 - } v.Op = OpAMD64SARQ v.AuxInt = 0 v.Aux = nil @@ -1412,8 +1370,38 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto end901ea4851cd5d2277a1ca1bee8f69d59 - end901ea4851cd5d2277a1ca1bee8f69d59: + goto end831ac9db492245c5e6c83d0b2a96b2d3 + end831ac9db492245c5e6c83d0b2a96b2d3: + ; + case OpRsh64U: + // match: (Rsh64U x y) + // cond: + // result: (ANDQ (SHRQ x y) (SBBQcarrymask (CMPQconst [64] y))) + { + t := v.Type + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64ANDQ + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue0(v.Line, OpAMD64SHRQ, TypeInvalid) + v0.Type = t + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + v1 := v.Block.NewValue0(v.Line, OpAMD64SBBQcarrymask, TypeInvalid) + v1.Type = t + v2 := v.Block.NewValue0(v.Line, OpAMD64CMPQconst, TypeInvalid) + v2.Type = TypeFlags + v2.AuxInt = 64 + v2.AddArg(y) + v1.AddArg(v2) + v.AddArg(v1) + return true + } + goto end90c34fa7de598170ea23d23d9a03ebfc + end90c34fa7de598170ea23d23d9a03ebfc: ; case OpAMD64SARQ: // match: (SARQ x (MOVQconst [c])) @@ -1743,17 +1731,109 @@ func rewriteValueAMD64(v *Value, config *Config) bool { goto end6f343b676bf49740054e459f972b24f5 end6f343b676bf49740054e459f972b24f5: ; - case OpSub: - // match: (Sub x y) - // cond: is64BitInt(t) + case OpSub16: + // match: (Sub16 x y) + // cond: + // result: (MOVWQSX (SUBW x y)) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64MOVWQSX + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue0(v.Line, OpAMD64SUBW, TypeInvalid) + v0.Type = v.Type + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto endf9d14f07ce4212200662acd073b77a79 + endf9d14f07ce4212200662acd073b77a79: + ; + case OpSub16U: + // match: (Sub16U x y) + // cond: + // result: (SUBW x y) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64SUBW + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto end1d72e18fad1c22bb770963f167b98c96 + end1d72e18fad1c22bb770963f167b98c96: + ; + case OpSub32: + // match: (Sub32 x y) + // cond: + // result: (MOVLQSX (SUBL x y)) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64MOVLQSX + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue0(v.Line, OpAMD64SUBL, TypeInvalid) + v0.Type = v.Type + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto end4c091fbf93fb9599a70c001845424614 + end4c091fbf93fb9599a70c001845424614: + ; + case OpSub32U: + // match: (Sub32U x y) + // cond: + // result: (SUBL x y) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64SUBL + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto end281d1020f0e75fce9df321580f07c4d5 + end281d1020f0e75fce9df321580f07c4d5: + ; + case OpSub64: + // match: (Sub64 x y) + // cond: + // result: (SUBQ x y) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64SUBQ + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto endd88d5646309fd9174584888ecc8aca2c + endd88d5646309fd9174584888ecc8aca2c: + ; + case OpSub64U: + // match: (Sub64U x y) + // cond: // result: (SUBQ x y) { - t := v.Type x := v.Args[0] y := v.Args[1] - if !(is64BitInt(t)) { - goto ende6ef29f885a8ecf3058212bb95917323 - } v.Op = OpAMD64SUBQ v.AuxInt = 0 v.Aux = nil @@ -1762,8 +1842,47 @@ func rewriteValueAMD64(v *Value, config *Config) bool { v.AddArg(y) return true } - goto ende6ef29f885a8ecf3058212bb95917323 - ende6ef29f885a8ecf3058212bb95917323: + goto end288f94a53865cdb00a0290d8358bb7da + end288f94a53865cdb00a0290d8358bb7da: + ; + case OpSub8: + // match: (Sub8 x y) + // cond: + // result: (MOVBQSX (SUBB x y)) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64MOVBQSX + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := v.Block.NewValue0(v.Line, OpAMD64SUBB, TypeInvalid) + v0.Type = v.Type + v0.AddArg(x) + v0.AddArg(y) + v.AddArg(v0) + return true + } + goto endfa3ef95107dcb01ae343f2243e485e80 + endfa3ef95107dcb01ae343f2243e485e80: + ; + case OpSub8U: + // match: (Sub8U x y) + // cond: + // result: (SUBB x y) + { + x := v.Args[0] + y := v.Args[1] + v.Op = OpAMD64SUBB + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AddArg(x) + v.AddArg(y) + return true + } + goto end8f5160f898dfa43da7d7d9f8cbaf9615 + end8f5160f898dfa43da7d7d9f8cbaf9615: ; case OpZero: // match: (Zero [0] _ mem) diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index ca523ee19b..7a4b6bf6ef 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -4,23 +4,42 @@ package ssa func rewriteValuegeneric(v *Value, config *Config) bool { switch v.Op { - case OpAdd: - // match: (Add (Const [c]) (Const [d])) - // cond: is64BitInt(t) + case OpAdd64: + // match: (Add64 (Const [c]) (Const [d])) + // cond: // result: (Const [c+d]) { - t := v.Type if v.Args[0].Op != OpConst { - goto end279f4ea85ed10e5ffc5b53f9e060529b + goto endd2f4bfaaf6c937171a287b73e5c2f73e } c := v.Args[0].AuxInt if v.Args[1].Op != OpConst { - goto end279f4ea85ed10e5ffc5b53f9e060529b + goto endd2f4bfaaf6c937171a287b73e5c2f73e } d := v.Args[1].AuxInt - if !(is64BitInt(t)) { - goto end279f4ea85ed10e5ffc5b53f9e060529b + v.Op = OpConst + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v.AuxInt = c + d + return true + } + goto endd2f4bfaaf6c937171a287b73e5c2f73e + endd2f4bfaaf6c937171a287b73e5c2f73e: + ; + case OpAdd64U: + // match: (Add64U (Const [c]) (Const [d])) + // cond: + // result: (Const [c+d]) + { + if v.Args[0].Op != OpConst { + goto endfedc373d8be0243cb5dbbc948996fe3a } + c := v.Args[0].AuxInt + if v.Args[1].Op != OpConst { + goto endfedc373d8be0243cb5dbbc948996fe3a + } + d := v.Args[1].AuxInt v.Op = OpConst v.AuxInt = 0 v.Aux = nil @@ -28,8 +47,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.AuxInt = c + d return true } - goto end279f4ea85ed10e5ffc5b53f9e060529b - end279f4ea85ed10e5ffc5b53f9e060529b: + goto endfedc373d8be0243cb5dbbc948996fe3a + endfedc373d8be0243cb5dbbc948996fe3a: ; case OpArrayIndex: // match: (ArrayIndex (Load ptr mem) idx) @@ -60,12 +79,12 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpConst: // match: (Const {s}) // cond: t.IsString() - // result: (StringMake (OffPtr [2*config.ptrSize] (Addr {config.fe.StringSym(s.(string))} (SB ))) (Const [int64(len(s.(string)))])) + // result: (StringMake (OffPtr [2*config.PtrSize] (Addr {config.fe.StringSym(s.(string))} (SB ))) (Const [int64(len(s.(string)))])) { t := v.Type s := v.Aux if !(t.IsString()) { - goto end55cd8fd3b98a2459d0ee9d6cbb456b01 + goto endedcb8bd24122d6a47bdc9b752460c344 } v.Op = OpStringMake v.AuxInt = 0 @@ -73,7 +92,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.resetArgs() v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid) v0.Type = TypeBytePtr - v0.AuxInt = 2 * config.ptrSize + v0.AuxInt = 2 * config.PtrSize v1 := v.Block.NewValue0(v.Line, OpAddr, TypeInvalid) v1.Type = TypeBytePtr v1.Aux = config.fe.StringSym(s.(string)) @@ -88,8 +107,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.AddArg(v3) return true } - goto end55cd8fd3b98a2459d0ee9d6cbb456b01 - end55cd8fd3b98a2459d0ee9d6cbb456b01: + goto endedcb8bd24122d6a47bdc9b752460c344 + endedcb8bd24122d6a47bdc9b752460c344: ; case OpIsInBounds: // match: (IsInBounds (Const [c]) (Const [d])) @@ -117,13 +136,13 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpLoad: // match: (Load ptr mem) // cond: t.IsString() - // result: (StringMake (Load ptr mem) (Load (OffPtr [config.ptrSize] ptr) mem)) + // result: (StringMake (Load ptr mem) (Load (OffPtr [config.PtrSize] ptr) mem)) { t := v.Type ptr := v.Args[0] mem := v.Args[1] if !(t.IsString()) { - goto endd0afd003b70d726a1c5bbaf51fe06182 + goto endce3ba169a57b8a9f6b12751d49b4e23a } v.Op = OpStringMake v.AuxInt = 0 @@ -138,15 +157,15 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v1.Type = config.Uintptr v2 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid) v2.Type = TypeBytePtr - v2.AuxInt = config.ptrSize + v2.AuxInt = config.PtrSize v2.AddArg(ptr) v1.AddArg(v2) v1.AddArg(mem) v.AddArg(v1) return true } - goto endd0afd003b70d726a1c5bbaf51fe06182 - endd0afd003b70d726a1c5bbaf51fe06182: + goto endce3ba169a57b8a9f6b12751d49b4e23a + endce3ba169a57b8a9f6b12751d49b4e23a: ; case OpMul: // match: (Mul (Const [c]) (Const [d])) @@ -178,12 +197,12 @@ func rewriteValuegeneric(v *Value, config *Config) bool { case OpPtrIndex: // match: (PtrIndex ptr idx) // cond: - // result: (Add ptr (Mul idx (Const [t.Elem().Size()]))) + // result: (AddPtr ptr (Mul idx (Const [t.Elem().Size()]))) { t := v.Type ptr := v.Args[0] idx := v.Args[1] - v.Op = OpAdd + v.Op = OpAddPtr v.AuxInt = 0 v.Aux = nil v.resetArgs() @@ -198,16 +217,16 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.AddArg(v0) return true } - goto end88c7c383675420d1581daeb899039fa8 - end88c7c383675420d1581daeb899039fa8: + goto endc181347cd3c740e2a1da431a981fdd7e + endc181347cd3c740e2a1da431a981fdd7e: ; case OpSliceCap: // match: (SliceCap (Load ptr mem)) // cond: - // result: (Load (Add ptr (Const [config.ptrSize*2])) mem) + // result: (Load (AddPtr ptr (Const [config.PtrSize*2])) mem) { if v.Args[0].Op != OpLoad { - goto end919cfa3d3539eb2e06a435d5f89654b9 + goto end83c0ff7760465a4184bad9e4b47f7be8 } ptr := v.Args[0].Args[0] mem := v.Args[0].Args[1] @@ -215,27 +234,27 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue0(v.Line, OpAdd, TypeInvalid) + v0 := v.Block.NewValue0(v.Line, OpAddPtr, TypeInvalid) v0.Type = ptr.Type v0.AddArg(ptr) v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v1.Type = config.Uintptr - v1.AuxInt = config.ptrSize * 2 + v1.AuxInt = config.PtrSize * 2 v0.AddArg(v1) v.AddArg(v0) v.AddArg(mem) return true } - goto end919cfa3d3539eb2e06a435d5f89654b9 - end919cfa3d3539eb2e06a435d5f89654b9: + goto end83c0ff7760465a4184bad9e4b47f7be8 + end83c0ff7760465a4184bad9e4b47f7be8: ; case OpSliceLen: // match: (SliceLen (Load ptr mem)) // cond: - // result: (Load (Add ptr (Const [config.ptrSize])) mem) + // result: (Load (AddPtr ptr (Const [config.PtrSize])) mem) { if v.Args[0].Op != OpLoad { - goto end3d74a5ef07180a709a91052da88bcd01 + goto end20579b262d017d875d579683996f0ef9 } ptr := v.Args[0].Args[0] mem := v.Args[0].Args[1] @@ -243,19 +262,19 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.AuxInt = 0 v.Aux = nil v.resetArgs() - v0 := v.Block.NewValue0(v.Line, OpAdd, TypeInvalid) + v0 := v.Block.NewValue0(v.Line, OpAddPtr, TypeInvalid) v0.Type = ptr.Type v0.AddArg(ptr) v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid) v1.Type = config.Uintptr - v1.AuxInt = config.ptrSize + v1.AuxInt = config.PtrSize v0.AddArg(v1) v.AddArg(v0) v.AddArg(mem) return true } - goto end3d74a5ef07180a709a91052da88bcd01 - end3d74a5ef07180a709a91052da88bcd01: + goto end20579b262d017d875d579683996f0ef9 + end20579b262d017d875d579683996f0ef9: ; case OpSlicePtr: // match: (SlicePtr (Load ptr mem)) @@ -311,13 +330,13 @@ func rewriteValuegeneric(v *Value, config *Config) bool { ; // match: (Store dst str mem) // cond: str.Type.IsString() - // result: (Store (OffPtr [config.ptrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) + // result: (Store (OffPtr [config.PtrSize] dst) (StringLen str) (Store dst (StringPtr str) mem)) { dst := v.Args[0] str := v.Args[1] mem := v.Args[2] if !(str.Type.IsString()) { - goto end410559d97aed8018f820cd88723de442 + goto endb47e037c1e5ac54c3a41d53163d8aef6 } v.Op = OpStore v.AuxInt = 0 @@ -325,7 +344,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.resetArgs() v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid) v0.Type = TypeBytePtr - v0.AuxInt = config.ptrSize + v0.AuxInt = config.PtrSize v0.AddArg(dst) v.AddArg(v0) v1 := v.Block.NewValue0(v.Line, OpStringLen, TypeInvalid) @@ -343,8 +362,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool { v.AddArg(v2) return true } - goto end410559d97aed8018f820cd88723de442 - end410559d97aed8018f820cd88723de442: + goto endb47e037c1e5ac54c3a41d53163d8aef6 + endb47e037c1e5ac54c3a41d53163d8aef6: ; case OpStringLen: // match: (StringLen (StringMake _ len)) diff --git a/src/cmd/compile/internal/ssa/schedule_test.go b/src/cmd/compile/internal/ssa/schedule_test.go index a9432579f7..3a89483a9a 100644 --- a/src/cmd/compile/internal/ssa/schedule_test.go +++ b/src/cmd/compile/internal/ssa/schedule_test.go @@ -19,7 +19,7 @@ func TestSchedule(t *testing.T) { Valu("mem3", OpStore, TypeInt64, 0, nil, "ptr", "sum", "mem2"), Valu("l1", OpLoad, TypeInt64, 0, nil, "ptr", "mem1"), Valu("l2", OpLoad, TypeInt64, 0, nil, "ptr", "mem2"), - Valu("sum", OpAdd, TypeInt64, 0, nil, "l1", "l2"), + Valu("sum", OpAdd64, TypeInt64, 0, nil, "l1", "l2"), Goto("exit")), Bloc("exit", Exit("mem3"))), diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go index 29b47c125e..cde48d355a 100644 --- a/src/cmd/compile/internal/ssa/shift_test.go +++ b/src/cmd/compile/internal/ssa/shift_test.go @@ -10,17 +10,17 @@ import ( func TestShiftConstAMD64(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}) - fun := makeConstShiftFunc(c, 18, OpLsh, TypeUInt64) + fun := makeConstShiftFunc(c, 18, OpLsh64, TypeUInt64) checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) - fun = makeConstShiftFunc(c, 66, OpLsh, TypeUInt64) + fun = makeConstShiftFunc(c, 66, OpLsh64, TypeUInt64) checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 0, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) - fun = makeConstShiftFunc(c, 18, OpRsh, TypeUInt64) + fun = makeConstShiftFunc(c, 18, OpRsh64U, TypeUInt64) checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHRQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) - fun = makeConstShiftFunc(c, 66, OpRsh, TypeUInt64) + fun = makeConstShiftFunc(c, 66, OpRsh64U, TypeUInt64) checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHRQconst: 0, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0}) - fun = makeConstShiftFunc(c, 18, OpRsh, TypeInt64) + fun = makeConstShiftFunc(c, 18, OpRsh64, TypeInt64) checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SARQconst: 1, OpAMD64CMPQconst: 0}) - fun = makeConstShiftFunc(c, 66, OpRsh, TypeInt64) + fun = makeConstShiftFunc(c, 66, OpRsh64, TypeInt64) checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SARQconst: 1, OpAMD64CMPQconst: 0}) } diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 0bd64a1a14..5f18acabfd 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -82,8 +82,8 @@ func stackalloc(f *Func) { } } - n = align(n, f.Config.ptrSize) - n += f.Config.ptrSize // space for return address. TODO: arch-dependent + n = align(n, f.Config.PtrSize) + n += f.Config.PtrSize // space for return address. TODO: arch-dependent f.RegAlloc = home f.FrameSize = n -- cgit v1.3 From 67bfd6956494173a0e2fa6b20bf61bf7b57589e6 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 20 Jul 2015 15:24:51 -0700 Subject: [dev.ssa] cmd/compile: fix stackalloc handling of zero-aligned variables Prior to this fix, a zero-aligned variable such as a flags variable would reset n to 0. While we're here, log the stack layout so that debugging and reading the generated assembly is easier. Change-Id: I18ef83ea95b6ea877c83f2e595e14c48c9ad7d84 Reviewed-on: https://go-review.googlesource.com/12439 Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/stackalloc.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 5f18acabfd..2d639bf594 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -20,6 +20,7 @@ func stackalloc(f *Func) { n = v.AuxInt } } + f.Logf("stackalloc: 0-%d for callee arguments/returns\n", n) // TODO: group variables by ptr/nonptr, size, etc. Emit ptr vars last // so stackmap is smaller. @@ -36,6 +37,7 @@ func stackalloc(f *Func) { continue } n = align(n, v.Type.Alignment()) + f.Logf("stackalloc: %d-%d for %v\n", n, n+v.Type.Size(), v) loc := &LocalSlot{n} n += v.Type.Size() home = setloc(home, v, loc) @@ -62,6 +64,7 @@ func stackalloc(f *Func) { continue } n = align(n, v.Type.Alignment()) + f.Logf("stackalloc: %d-%d for %v\n", n, n+v.Type.Size(), v) loc := &LocalSlot{n} n += v.Type.Size() home = setloc(home, v, loc) @@ -77,12 +80,14 @@ func stackalloc(f *Func) { } t := s.Typ n = align(n, t.Alignment()) + f.Logf("stackalloc: %d-%d for auto %v\n", n, n+t.Size(), v) s.Offset = n n += t.Size() } } n = align(n, f.Config.PtrSize) + f.Logf("stackalloc: %d-%d for return address\n", n, n+f.Config.ptrSize) n += f.Config.PtrSize // space for return address. TODO: arch-dependent f.RegAlloc = home f.FrameSize = n @@ -92,5 +97,8 @@ func stackalloc(f *Func) { // align increases n to the next multiple of a. a must be a power of 2. func align(n int64, a int64) int64 { + if a == 0 { + return n + } return (n + a - 1) &^ (a - 1) } -- cgit v1.3 From ac1935b3a7f1873a389e77586ed6e62e9ed5339e Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 21 Jul 2015 06:58:32 -0700 Subject: [dev.ssa] cmd/compile: fix build Bad rebase in CL 12439. Change-Id: I7ad359519c6274be37456b655f19bf0ca6ac6692 Reviewed-on: https://go-review.googlesource.com/12449 Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/ssa/stackalloc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 2d639bf594..064b84a804 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -87,7 +87,7 @@ func stackalloc(f *Func) { } n = align(n, f.Config.PtrSize) - f.Logf("stackalloc: %d-%d for return address\n", n, n+f.Config.ptrSize) + f.Logf("stackalloc: %d-%d for return address\n", n, n+f.Config.PtrSize) n += f.Config.PtrSize // space for return address. TODO: arch-dependent f.RegAlloc = home f.FrameSize = n -- cgit v1.3 From 0b46b42943ee9d7ad4e9a19772d22468718173c9 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 11 Aug 2015 12:51:33 -0700 Subject: [dev.ssa] cmd/compile/internal/ssa: New register allocator Implement a global (whole function) register allocator. This replaces the local (per basic block) register allocator. Clobbering of registers by instructions is handled properly. A separate change will add the correct clobbers to all the instructions. Change-Id: I38ce4dc7dccb8303c1c0e0295fe70247b0a3f2ea Reviewed-on: https://go-review.googlesource.com/13622 Reviewed-by: Josh Bleecher Snyder Reviewed-by: Todd Neal --- src/cmd/compile/internal/gc/ssa.go | 12 +- .../compile/internal/gc/testdata/regalloc_ssa.go | 57 ++ src/cmd/compile/internal/ssa/deadcode.go | 8 + src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 34 +- src/cmd/compile/internal/ssa/gen/main.go | 42 +- src/cmd/compile/internal/ssa/html.go | 2 +- src/cmd/compile/internal/ssa/op.go | 7 +- src/cmd/compile/internal/ssa/opGen.go | 909 ++++++++-------- src/cmd/compile/internal/ssa/regalloc.go | 1081 ++++++++++++++------ src/cmd/compile/internal/ssa/stackalloc.go | 18 +- src/cmd/compile/internal/ssa/tighten.go | 7 - src/cmd/compile/internal/ssa/value.go | 4 +- 12 files changed, 1439 insertions(+), 742 deletions(-) create mode 100644 src/cmd/compile/internal/gc/testdata/regalloc_ssa.go (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 4e115a0fcd..ef90ed40e7 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -2277,7 +2277,10 @@ func genValue(v *ssa.Value) { p.To.Reg = x86.REG_SP p.To.Offset = localOffset(v) case ssa.OpPhi: - // just check to make sure regalloc did it right + // just check to make sure regalloc and stackalloc did it right + if v.Type.IsMemory() { + return + } f := v.Block.Func loc := f.RegAlloc[v.ID] for _, a := range v.Args { @@ -2376,13 +2379,16 @@ func genValue(v *ssa.Value) { case ssa.OpAMD64InvertFlags: v.Fatalf("InvertFlags should never make it to codegen %v", v) case ssa.OpAMD64REPSTOSQ: + p := Prog(x86.AXORL) // TODO: lift out zeroing into its own instruction? + p.From.Type = obj.TYPE_REG + p.From.Reg = x86.REG_AX + p.To.Type = obj.TYPE_REG + p.To.Reg = x86.REG_AX Prog(x86.AREP) Prog(x86.ASTOSQ) - v.Unimplementedf("REPSTOSQ clobbers not implemented: %s", v.LongString()) case ssa.OpAMD64REPMOVSB: Prog(x86.AREP) Prog(x86.AMOVSB) - v.Unimplementedf("REPMOVSB clobbers not implemented: %s", v.LongString()) default: v.Unimplementedf("genValue not implemented: %s", v.LongString()) } diff --git a/src/cmd/compile/internal/gc/testdata/regalloc_ssa.go b/src/cmd/compile/internal/gc/testdata/regalloc_ssa.go new file mode 100644 index 0000000000..f752692952 --- /dev/null +++ b/src/cmd/compile/internal/gc/testdata/regalloc_ssa.go @@ -0,0 +1,57 @@ +// run + +// Copyright 2015 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. + +// Tests phi implementation + +package main + +func phiOverwrite_ssa() int { + var n int + for i := 0; i < 10; i++ { + if i == 6 { + break + } + n = i + } + return n +} + +func phiOverwrite() { + want := 5 + got := phiOverwrite_ssa() + if got != want { + println("phiOverwrite_ssa()=", want, ", got", got) + failed = true + } +} + +func phiOverwriteBig_ssa() int { + var a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z int + a = 1 + for idx := 0; idx < 26; idx++ { + a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z = b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, a + } + return a*1 + b*2 + c*3 + d*4 + e*5 + f*6 + g*7 + h*8 + i*9 + j*10 + k*11 + l*12 + m*13 + n*14 + o*15 + p*16 + q*17 + r*18 + s*19 + t*20 + u*21 + v*22 + w*23 + x*24 + y*25 + z*26 +} + +func phiOverwriteBig() { + want := 1 + got := phiOverwriteBig_ssa() + if got != want { + println("phiOverwriteBig_ssa()=", want, ", got", got) + failed = true + } +} + +var failed = false + +func main() { + phiOverwrite() + phiOverwriteBig() + if failed { + panic("failed") + } +} diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index 109b3dd09f..8c306c8412 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -59,6 +59,14 @@ func findlive(f *Func) (reachable []bool, live []bool) { // deadcode removes dead code from f. func deadcode(f *Func) { + // deadcode after regalloc is forbidden for now. Regalloc + // doesn't quite generate legal SSA which will lead to some + // required moves being eliminated. See the comment at the + // top of regalloc.go for details. + if f.RegAlloc != nil { + f.Fatalf("deadcode after regalloc") + } + reachable, live := findlive(f) // Remove dead values from blocks' value list. Return dead diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 8bdcfaaac7..5aa5e60e33 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -72,13 +72,14 @@ func init() { // Common individual register masks var ( - cx = buildReg("CX") - x15 = buildReg("X15") - gp = buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15") - fp = buildReg("X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15") - gpsp = gp | buildReg("SP") - gpspsb = gpsp | buildReg("SB") - flags = buildReg("FLAGS") + cx = buildReg("CX") + x15 = buildReg("X15") + gp = buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15") + fp = buildReg("X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15") + gpsp = gp | buildReg("SP") + gpspsb = gpsp | buildReg("SB") + flags = buildReg("FLAGS") + callerSave = gp | fp | flags ) // Common slices of register masks @@ -90,16 +91,16 @@ func init() { // Common regInfo var ( - gp01 = regInfo{inputs: []regMask{}, outputs: gponly} - gp11 = regInfo{inputs: []regMask{gpsp}, outputs: gponly} - gp11sb = regInfo{inputs: []regMask{gpspsb}, outputs: gponly} - gp21 = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: gponly} - gp21sb = regInfo{inputs: []regMask{gpspsb, gpsp}, outputs: gponly} - gp21shift = regInfo{inputs: []regMask{gpsp, cx}, outputs: []regMask{gp &^ cx}} + gp01 = regInfo{inputs: []regMask{}, outputs: gponly, clobbers: flags} + gp11 = regInfo{inputs: []regMask{gpsp}, outputs: gponly, clobbers: flags} + gp11sb = regInfo{inputs: []regMask{gpspsb}, outputs: gponly, clobbers: flags} + gp21 = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: gponly, clobbers: flags} + gp21sb = regInfo{inputs: []regMask{gpspsb, gpsp}, outputs: gponly, clobbers: flags} + gp21shift = regInfo{inputs: []regMask{gpsp, cx}, outputs: []regMask{gp &^ cx}, clobbers: flags} gp2flags = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: flagsonly} gp1flags = regInfo{inputs: []regMask{gpsp}, outputs: flagsonly} - flagsgp = regInfo{inputs: flagsonly, outputs: gponly} + flagsgp = regInfo{inputs: flagsonly, outputs: gponly, clobbers: flags} gpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: gponly} gploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: gponly} @@ -122,6 +123,7 @@ func init() { fpstore = regInfo{inputs: []regMask{gpspsb, fp, 0}} fpstoreidx = regInfo{inputs: []regMask{gpspsb, gpsp, fp, 0}} ) + // TODO: most ops clobber flags // Suffixes encode the bit width of various instructions. // Q = 64 bit, L = 32 bit, W = 16 bit, B = 8 bit @@ -318,8 +320,8 @@ func init() { {name: "REPSTOSQ", reg: regInfo{[]regMask{buildReg("DI"), buildReg("CX")}, buildReg("DI AX CX"), nil}}, // store arg1 8-byte words containing zero into arg0 using STOSQ. arg2=mem. //TODO: set register clobber to everything? - {name: "CALLstatic"}, // call static function aux.(*gc.Sym). arg0=mem, returns mem - {name: "CALLclosure", reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, 0, nil}}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem returns mem + {name: "CALLstatic", reg: regInfo{clobbers: callerSave}}, // call static function aux.(*gc.Sym). arg0=mem, returns mem + {name: "CALLclosure", reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, callerSave, nil}}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem returns mem {name: "REPMOVSB", reg: regInfo{[]regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")}, buildReg("DI SI CX"), nil}}, // move arg2 bytes from arg1 to arg0. arg3=mem, returns memory diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go index 97ac802cbd..6620c0a1d0 100644 --- a/src/cmd/compile/internal/ssa/gen/main.go +++ b/src/cmd/compile/internal/ssa/gen/main.go @@ -15,6 +15,7 @@ import ( "io/ioutil" "log" "regexp" + "sort" ) type arch struct { @@ -125,11 +126,22 @@ func genOp() { fmt.Fprintf(w, "asm: x86.A%s,\n", v.asm) } fmt.Fprintln(w, "reg:regInfo{") - // reg inputs - if len(v.reg.inputs) > 0 { - fmt.Fprintln(w, "inputs: []regMask{") - for _, r := range v.reg.inputs { - fmt.Fprintf(w, "%d,%s\n", r, a.regMaskComment(r)) + + // Compute input allocation order. We allocate from the + // most to the least constrained input. This order guarantees + // that we will always be able to find a register. + var s []intPair + for i, r := range v.reg.inputs { + if r != 0 { + s = append(s, intPair{countRegs(r), i}) + } + } + if len(s) > 0 { + sort.Sort(byKey(s)) + fmt.Fprintln(w, "inputs: []inputInfo{") + for _, p := range s { + r := v.reg.inputs[p.val] + fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r)) } fmt.Fprintln(w, "},") } @@ -205,3 +217,23 @@ func genLower() { genRules(a) } } + +// countRegs returns the number of set bits in the register mask. +func countRegs(r regMask) int { + n := 0 + for r != 0 { + n += int(r & 1) + r >>= 1 + } + return n +} + +// for sorting a pair of integers by key +type intPair struct { + key, val int +} +type byKey []intPair + +func (a byKey) Len() int { return len(a) } +func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key } diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 848e016129..5c23320680 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -362,7 +362,7 @@ func (v *Value) LongHTML() string { s += fmt.Sprintf(" %s", a.HTML()) } r := v.Block.Func.RegAlloc - if r != nil && r[v.ID] != nil { + if int(v.ID) < len(r) && r[v.ID] != nil { s += " : " + r[v.ID].Name() } diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 4ca8c770cb..356084fb02 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -19,8 +19,13 @@ type opInfo struct { generic bool // this is a generic (arch-independent) opcode } +type inputInfo struct { + idx int // index in Args array + regs regMask // allowed input registers +} + type regInfo struct { - inputs []regMask + inputs []inputInfo // ordered in register allocation order clobbers regMask outputs []regMask // NOTE: values can only have 1 output for now. } diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 2155cd318e..cbabbfade5 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -425,9 +425,9 @@ var opcodeTable = [...]opInfo{ name: "ADDSS", asm: x86.AADDSS, reg: regInfo{ - inputs: []regMask{ - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + inputs: []inputInfo{ + {0, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {1, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -438,9 +438,9 @@ var opcodeTable = [...]opInfo{ name: "ADDSD", asm: x86.AADDSD, reg: regInfo{ - inputs: []regMask{ - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + inputs: []inputInfo{ + {0, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {1, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -451,9 +451,9 @@ var opcodeTable = [...]opInfo{ name: "SUBSS", asm: x86.ASUBSS, reg: regInfo{ - inputs: []regMask{ - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + inputs: []inputInfo{ + {0, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + {1, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 }, clobbers: 2147483648, // .X15 outputs: []regMask{ @@ -465,9 +465,9 @@ var opcodeTable = [...]opInfo{ name: "SUBSD", asm: x86.ASUBSD, reg: regInfo{ - inputs: []regMask{ - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + inputs: []inputInfo{ + {0, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + {1, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 }, clobbers: 2147483648, // .X15 outputs: []regMask{ @@ -479,9 +479,9 @@ var opcodeTable = [...]opInfo{ name: "MULSS", asm: x86.AMULSS, reg: regInfo{ - inputs: []regMask{ - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + inputs: []inputInfo{ + {0, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {1, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -492,9 +492,9 @@ var opcodeTable = [...]opInfo{ name: "MULSD", asm: x86.AMULSD, reg: regInfo{ - inputs: []regMask{ - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + inputs: []inputInfo{ + {0, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {1, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -505,9 +505,9 @@ var opcodeTable = [...]opInfo{ name: "DIVSS", asm: x86.ADIVSS, reg: regInfo{ - inputs: []regMask{ - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + inputs: []inputInfo{ + {0, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + {1, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 }, clobbers: 2147483648, // .X15 outputs: []regMask{ @@ -519,9 +519,9 @@ var opcodeTable = [...]opInfo{ name: "DIVSD", asm: x86.ADIVSD, reg: regInfo{ - inputs: []regMask{ - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 - 2147418112, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + inputs: []inputInfo{ + {0, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 + {1, 2147418112}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 }, clobbers: 2147483648, // .X15 outputs: []regMask{ @@ -533,9 +533,8 @@ var opcodeTable = [...]opInfo{ name: "MOVSSload", asm: x86.AMOVSS, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -546,9 +545,8 @@ var opcodeTable = [...]opInfo{ name: "MOVSDload", asm: x86.AMOVSD, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -577,10 +575,9 @@ var opcodeTable = [...]opInfo{ name: "MOVSSloadidx4", asm: x86.AMOVSS, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -591,10 +588,9 @@ var opcodeTable = [...]opInfo{ name: "MOVSDloadidx8", asm: x86.AMOVSD, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 @@ -605,10 +601,9 @@ var opcodeTable = [...]opInfo{ name: "MOVSSstore", asm: x86.AMOVSS, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 0, + inputs: []inputInfo{ + {1, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -616,10 +611,9 @@ var opcodeTable = [...]opInfo{ name: "MOVSDstore", asm: x86.AMOVSD, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 0, + inputs: []inputInfo{ + {1, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -627,11 +621,10 @@ var opcodeTable = [...]opInfo{ name: "MOVSSstoreidx4", asm: x86.AMOVSS, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {2, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -639,11 +632,10 @@ var opcodeTable = [...]opInfo{ name: "MOVSDstoreidx8", asm: x86.AMOVSD, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 4294901760, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {2, 4294901760}, // .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -651,10 +643,11 @@ var opcodeTable = [...]opInfo{ name: "ADDQ", asm: x86.AADDQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -664,10 +657,11 @@ var opcodeTable = [...]opInfo{ name: "ADDL", asm: x86.AADDL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -677,10 +671,11 @@ var opcodeTable = [...]opInfo{ name: "ADDW", asm: x86.AADDW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -690,10 +685,11 @@ var opcodeTable = [...]opInfo{ name: "ADDB", asm: x86.AADDB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -703,9 +699,10 @@ var opcodeTable = [...]opInfo{ name: "ADDQconst", asm: x86.AADDQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -715,9 +712,10 @@ var opcodeTable = [...]opInfo{ name: "ADDLconst", asm: x86.AADDL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -727,9 +725,10 @@ var opcodeTable = [...]opInfo{ name: "ADDWconst", asm: x86.AADDW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -739,9 +738,10 @@ var opcodeTable = [...]opInfo{ name: "ADDBconst", asm: x86.AADDB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -751,10 +751,11 @@ var opcodeTable = [...]opInfo{ name: "SUBQ", asm: x86.ASUBQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -764,10 +765,11 @@ var opcodeTable = [...]opInfo{ name: "SUBL", asm: x86.ASUBL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -777,10 +779,11 @@ var opcodeTable = [...]opInfo{ name: "SUBW", asm: x86.ASUBW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -790,10 +793,11 @@ var opcodeTable = [...]opInfo{ name: "SUBB", asm: x86.ASUBB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -803,9 +807,10 @@ var opcodeTable = [...]opInfo{ name: "SUBQconst", asm: x86.ASUBQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -815,9 +820,10 @@ var opcodeTable = [...]opInfo{ name: "SUBLconst", asm: x86.ASUBL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -827,9 +833,10 @@ var opcodeTable = [...]opInfo{ name: "SUBWconst", asm: x86.ASUBW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -839,9 +846,10 @@ var opcodeTable = [...]opInfo{ name: "SUBBconst", asm: x86.ASUBB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -851,10 +859,11 @@ var opcodeTable = [...]opInfo{ name: "MULQ", asm: x86.AIMULQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -864,10 +873,11 @@ var opcodeTable = [...]opInfo{ name: "MULL", asm: x86.AIMULL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -877,10 +887,11 @@ var opcodeTable = [...]opInfo{ name: "MULW", asm: x86.AIMULW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -890,10 +901,11 @@ var opcodeTable = [...]opInfo{ name: "MULB", asm: x86.AIMULW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -903,9 +915,10 @@ var opcodeTable = [...]opInfo{ name: "MULQconst", asm: x86.AIMULQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -915,9 +928,10 @@ var opcodeTable = [...]opInfo{ name: "MULLconst", asm: x86.AIMULL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -927,9 +941,10 @@ var opcodeTable = [...]opInfo{ name: "MULWconst", asm: x86.AIMULW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -939,9 +954,10 @@ var opcodeTable = [...]opInfo{ name: "MULBconst", asm: x86.AIMULW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -951,10 +967,11 @@ var opcodeTable = [...]opInfo{ name: "ANDQ", asm: x86.AANDQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -964,10 +981,11 @@ var opcodeTable = [...]opInfo{ name: "ANDL", asm: x86.AANDL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -977,10 +995,11 @@ var opcodeTable = [...]opInfo{ name: "ANDW", asm: x86.AANDW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -990,10 +1009,11 @@ var opcodeTable = [...]opInfo{ name: "ANDB", asm: x86.AANDB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1003,9 +1023,10 @@ var opcodeTable = [...]opInfo{ name: "ANDQconst", asm: x86.AANDQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1015,9 +1036,10 @@ var opcodeTable = [...]opInfo{ name: "ANDLconst", asm: x86.AANDL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1027,9 +1049,10 @@ var opcodeTable = [...]opInfo{ name: "ANDWconst", asm: x86.AANDW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1039,9 +1062,10 @@ var opcodeTable = [...]opInfo{ name: "ANDBconst", asm: x86.AANDB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1051,10 +1075,11 @@ var opcodeTable = [...]opInfo{ name: "ORQ", asm: x86.AORQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1064,10 +1089,11 @@ var opcodeTable = [...]opInfo{ name: "ORL", asm: x86.AORL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1077,10 +1103,11 @@ var opcodeTable = [...]opInfo{ name: "ORW", asm: x86.AORW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1090,10 +1117,11 @@ var opcodeTable = [...]opInfo{ name: "ORB", asm: x86.AORB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1103,9 +1131,10 @@ var opcodeTable = [...]opInfo{ name: "ORQconst", asm: x86.AORQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1115,9 +1144,10 @@ var opcodeTable = [...]opInfo{ name: "ORLconst", asm: x86.AORL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1127,9 +1157,10 @@ var opcodeTable = [...]opInfo{ name: "ORWconst", asm: x86.AORW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1139,9 +1170,10 @@ var opcodeTable = [...]opInfo{ name: "ORBconst", asm: x86.AORB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1151,10 +1183,11 @@ var opcodeTable = [...]opInfo{ name: "XORQ", asm: x86.AXORQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1164,10 +1197,11 @@ var opcodeTable = [...]opInfo{ name: "XORL", asm: x86.AXORL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1177,10 +1211,11 @@ var opcodeTable = [...]opInfo{ name: "XORW", asm: x86.AXORW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1190,10 +1225,11 @@ var opcodeTable = [...]opInfo{ name: "XORB", asm: x86.AXORB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1203,9 +1239,10 @@ var opcodeTable = [...]opInfo{ name: "XORQconst", asm: x86.AXORQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1215,9 +1252,10 @@ var opcodeTable = [...]opInfo{ name: "XORLconst", asm: x86.AXORL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1227,9 +1265,10 @@ var opcodeTable = [...]opInfo{ name: "XORWconst", asm: x86.AXORW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1239,9 +1278,10 @@ var opcodeTable = [...]opInfo{ name: "XORBconst", asm: x86.AXORB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1251,9 +1291,9 @@ var opcodeTable = [...]opInfo{ name: "CMPQ", asm: x86.ACMPQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1264,9 +1304,9 @@ var opcodeTable = [...]opInfo{ name: "CMPL", asm: x86.ACMPL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1277,9 +1317,9 @@ var opcodeTable = [...]opInfo{ name: "CMPW", asm: x86.ACMPW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1290,9 +1330,9 @@ var opcodeTable = [...]opInfo{ name: "CMPB", asm: x86.ACMPB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1303,8 +1343,8 @@ var opcodeTable = [...]opInfo{ name: "CMPQconst", asm: x86.ACMPQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1315,8 +1355,8 @@ var opcodeTable = [...]opInfo{ name: "CMPLconst", asm: x86.ACMPL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1327,8 +1367,8 @@ var opcodeTable = [...]opInfo{ name: "CMPWconst", asm: x86.ACMPW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1339,8 +1379,8 @@ var opcodeTable = [...]opInfo{ name: "CMPBconst", asm: x86.ACMPB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1351,9 +1391,9 @@ var opcodeTable = [...]opInfo{ name: "TESTQ", asm: x86.ATESTQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1364,9 +1404,9 @@ var opcodeTable = [...]opInfo{ name: "TESTL", asm: x86.ATESTL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1377,9 +1417,9 @@ var opcodeTable = [...]opInfo{ name: "TESTW", asm: x86.ATESTW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1390,9 +1430,9 @@ var opcodeTable = [...]opInfo{ name: "TESTB", asm: x86.ATESTB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1403,8 +1443,8 @@ var opcodeTable = [...]opInfo{ name: "TESTQconst", asm: x86.ATESTQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1415,8 +1455,8 @@ var opcodeTable = [...]opInfo{ name: "TESTLconst", asm: x86.ATESTL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1427,8 +1467,8 @@ var opcodeTable = [...]opInfo{ name: "TESTWconst", asm: x86.ATESTW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1439,8 +1479,8 @@ var opcodeTable = [...]opInfo{ name: "TESTBconst", asm: x86.ATESTB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, outputs: []regMask{ 8589934592, // .FLAGS @@ -1451,10 +1491,11 @@ var opcodeTable = [...]opInfo{ name: "SHLQ", asm: x86.ASHLQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1464,10 +1505,11 @@ var opcodeTable = [...]opInfo{ name: "SHLL", asm: x86.ASHLL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1477,10 +1519,11 @@ var opcodeTable = [...]opInfo{ name: "SHLW", asm: x86.ASHLW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1490,10 +1533,11 @@ var opcodeTable = [...]opInfo{ name: "SHLB", asm: x86.ASHLB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1503,9 +1547,10 @@ var opcodeTable = [...]opInfo{ name: "SHLQconst", asm: x86.ASHLQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1515,9 +1560,10 @@ var opcodeTable = [...]opInfo{ name: "SHLLconst", asm: x86.ASHLL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1527,9 +1573,10 @@ var opcodeTable = [...]opInfo{ name: "SHLWconst", asm: x86.ASHLW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1539,9 +1586,10 @@ var opcodeTable = [...]opInfo{ name: "SHLBconst", asm: x86.ASHLB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1551,10 +1599,11 @@ var opcodeTable = [...]opInfo{ name: "SHRQ", asm: x86.ASHRQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1564,10 +1613,11 @@ var opcodeTable = [...]opInfo{ name: "SHRL", asm: x86.ASHRL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1577,10 +1627,11 @@ var opcodeTable = [...]opInfo{ name: "SHRW", asm: x86.ASHRW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1590,10 +1641,11 @@ var opcodeTable = [...]opInfo{ name: "SHRB", asm: x86.ASHRB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1603,9 +1655,10 @@ var opcodeTable = [...]opInfo{ name: "SHRQconst", asm: x86.ASHRQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1615,9 +1668,10 @@ var opcodeTable = [...]opInfo{ name: "SHRLconst", asm: x86.ASHRL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1627,9 +1681,10 @@ var opcodeTable = [...]opInfo{ name: "SHRWconst", asm: x86.ASHRW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1639,9 +1694,10 @@ var opcodeTable = [...]opInfo{ name: "SHRBconst", asm: x86.ASHRB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1651,10 +1707,11 @@ var opcodeTable = [...]opInfo{ name: "SARQ", asm: x86.ASARQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1664,10 +1721,11 @@ var opcodeTable = [...]opInfo{ name: "SARL", asm: x86.ASARL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1677,10 +1735,11 @@ var opcodeTable = [...]opInfo{ name: "SARW", asm: x86.ASARW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1690,10 +1749,11 @@ var opcodeTable = [...]opInfo{ name: "SARB", asm: x86.ASARB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 2, // .CX + inputs: []inputInfo{ + {1, 2}, // .CX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65517, // .AX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1703,9 +1763,10 @@ var opcodeTable = [...]opInfo{ name: "SARQconst", asm: x86.ASARQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1715,9 +1776,10 @@ var opcodeTable = [...]opInfo{ name: "SARLconst", asm: x86.ASARL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1727,9 +1789,10 @@ var opcodeTable = [...]opInfo{ name: "SARWconst", asm: x86.ASARW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1739,9 +1802,10 @@ var opcodeTable = [...]opInfo{ name: "SARBconst", asm: x86.ASARB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1751,9 +1815,10 @@ var opcodeTable = [...]opInfo{ name: "ROLQconst", asm: x86.AROLQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1763,9 +1828,10 @@ var opcodeTable = [...]opInfo{ name: "ROLLconst", asm: x86.AROLL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1775,9 +1841,10 @@ var opcodeTable = [...]opInfo{ name: "ROLWconst", asm: x86.AROLW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1787,9 +1854,10 @@ var opcodeTable = [...]opInfo{ name: "ROLBconst", asm: x86.AROLB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1799,9 +1867,10 @@ var opcodeTable = [...]opInfo{ name: "NEGQ", asm: x86.ANEGQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1811,9 +1880,10 @@ var opcodeTable = [...]opInfo{ name: "NEGL", asm: x86.ANEGL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1823,9 +1893,10 @@ var opcodeTable = [...]opInfo{ name: "NEGW", asm: x86.ANEGW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1835,9 +1906,10 @@ var opcodeTable = [...]opInfo{ name: "NEGB", asm: x86.ANEGB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1847,9 +1919,10 @@ var opcodeTable = [...]opInfo{ name: "NOTQ", asm: x86.ANOTQ, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1859,9 +1932,10 @@ var opcodeTable = [...]opInfo{ name: "NOTL", asm: x86.ANOTL, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1871,9 +1945,10 @@ var opcodeTable = [...]opInfo{ name: "NOTW", asm: x86.ANOTW, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1883,9 +1958,10 @@ var opcodeTable = [...]opInfo{ name: "NOTB", asm: x86.ANOTB, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1895,9 +1971,10 @@ var opcodeTable = [...]opInfo{ name: "SBBQcarrymask", asm: x86.ASBBQ, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1907,9 +1984,10 @@ var opcodeTable = [...]opInfo{ name: "SBBLcarrymask", asm: x86.ASBBL, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1919,9 +1997,10 @@ var opcodeTable = [...]opInfo{ name: "SETEQ", asm: x86.ASETEQ, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1931,9 +2010,10 @@ var opcodeTable = [...]opInfo{ name: "SETNE", asm: x86.ASETNE, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1943,9 +2023,10 @@ var opcodeTable = [...]opInfo{ name: "SETL", asm: x86.ASETLT, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1955,9 +2036,10 @@ var opcodeTable = [...]opInfo{ name: "SETLE", asm: x86.ASETLE, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1967,9 +2049,10 @@ var opcodeTable = [...]opInfo{ name: "SETG", asm: x86.ASETGT, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1979,9 +2062,10 @@ var opcodeTable = [...]opInfo{ name: "SETGE", asm: x86.ASETGE, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -1991,9 +2075,10 @@ var opcodeTable = [...]opInfo{ name: "SETB", asm: x86.ASETCS, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2003,9 +2088,10 @@ var opcodeTable = [...]opInfo{ name: "SETBE", asm: x86.ASETLS, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2015,9 +2101,10 @@ var opcodeTable = [...]opInfo{ name: "SETA", asm: x86.ASETHI, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2027,9 +2114,10 @@ var opcodeTable = [...]opInfo{ name: "SETAE", asm: x86.ASETCC, reg: regInfo{ - inputs: []regMask{ - 8589934592, // .FLAGS + inputs: []inputInfo{ + {0, 8589934592}, // .FLAGS }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2039,9 +2127,10 @@ var opcodeTable = [...]opInfo{ name: "MOVBQSX", asm: x86.AMOVBQSX, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2051,9 +2140,10 @@ var opcodeTable = [...]opInfo{ name: "MOVBQZX", asm: x86.AMOVBQZX, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2063,9 +2153,10 @@ var opcodeTable = [...]opInfo{ name: "MOVWQSX", asm: x86.AMOVWQSX, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2075,9 +2166,10 @@ var opcodeTable = [...]opInfo{ name: "MOVWQZX", asm: x86.AMOVWQZX, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2087,9 +2179,10 @@ var opcodeTable = [...]opInfo{ name: "MOVLQSX", asm: x86.AMOVLQSX, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2099,9 +2192,10 @@ var opcodeTable = [...]opInfo{ name: "MOVLQZX", asm: x86.AMOVLQZX, reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2111,6 +2205,7 @@ var opcodeTable = [...]opInfo{ name: "MOVBconst", asm: x86.AMOVB, reg: regInfo{ + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2120,6 +2215,7 @@ var opcodeTable = [...]opInfo{ name: "MOVWconst", asm: x86.AMOVW, reg: regInfo{ + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2129,6 +2225,7 @@ var opcodeTable = [...]opInfo{ name: "MOVLconst", asm: x86.AMOVL, reg: regInfo{ + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2138,6 +2235,7 @@ var opcodeTable = [...]opInfo{ name: "MOVQconst", asm: x86.AMOVQ, reg: regInfo{ + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2146,9 +2244,10 @@ var opcodeTable = [...]opInfo{ { name: "LEAQ", reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2157,10 +2256,11 @@ var opcodeTable = [...]opInfo{ { name: "LEAQ1", reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2169,10 +2269,11 @@ var opcodeTable = [...]opInfo{ { name: "LEAQ2", reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2181,10 +2282,11 @@ var opcodeTable = [...]opInfo{ { name: "LEAQ4", reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2193,10 +2295,11 @@ var opcodeTable = [...]opInfo{ { name: "LEAQ8", reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, @@ -2206,9 +2309,8 @@ var opcodeTable = [...]opInfo{ name: "MOVBload", asm: x86.AMOVB, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -2219,9 +2321,8 @@ var opcodeTable = [...]opInfo{ name: "MOVBQSXload", asm: x86.AMOVBQSX, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -2232,9 +2333,8 @@ var opcodeTable = [...]opInfo{ name: "MOVBQZXload", asm: x86.AMOVBQZX, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -2245,9 +2345,8 @@ var opcodeTable = [...]opInfo{ name: "MOVWload", asm: x86.AMOVW, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -2258,9 +2357,8 @@ var opcodeTable = [...]opInfo{ name: "MOVLload", asm: x86.AMOVL, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -2271,9 +2369,8 @@ var opcodeTable = [...]opInfo{ name: "MOVQload", asm: x86.AMOVQ, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -2284,10 +2381,9 @@ var opcodeTable = [...]opInfo{ name: "MOVQloadidx8", asm: x86.AMOVQ, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 @@ -2298,10 +2394,9 @@ var opcodeTable = [...]opInfo{ name: "MOVBstore", asm: x86.AMOVB, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -2309,10 +2404,9 @@ var opcodeTable = [...]opInfo{ name: "MOVWstore", asm: x86.AMOVW, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -2320,10 +2414,9 @@ var opcodeTable = [...]opInfo{ name: "MOVLstore", asm: x86.AMOVL, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -2331,10 +2424,9 @@ var opcodeTable = [...]opInfo{ name: "MOVQstore", asm: x86.AMOVQ, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, @@ -2342,54 +2434,54 @@ var opcodeTable = [...]opInfo{ name: "MOVQstoreidx8", asm: x86.AMOVQ, reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 0, + inputs: []inputInfo{ + {1, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {2, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, { name: "MOVXzero", reg: regInfo{ - inputs: []regMask{ - 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB - 0, + inputs: []inputInfo{ + {0, 4295032831}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB }, }, }, { name: "REPSTOSQ", reg: regInfo{ - inputs: []regMask{ - 128, // .DI - 2, // .CX + inputs: []inputInfo{ + {0, 128}, // .DI + {1, 2}, // .CX }, clobbers: 131, // .AX .CX .DI }, }, { name: "CALLstatic", - reg: regInfo{}, + reg: regInfo{ + clobbers: 12884901871, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 .FLAGS + }, }, { name: "CALLclosure", reg: regInfo{ - inputs: []regMask{ - 65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 - 4, // .DX - 0, + inputs: []inputInfo{ + {1, 4}, // .DX + {0, 65535}, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, + clobbers: 12884901871, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .X0 .X1 .X2 .X3 .X4 .X5 .X6 .X7 .X8 .X9 .X10 .X11 .X12 .X13 .X14 .X15 .FLAGS }, }, { name: "REPMOVSB", reg: regInfo{ - inputs: []regMask{ - 128, // .DI - 64, // .SI - 2, // .CX + inputs: []inputInfo{ + {0, 128}, // .DI + {1, 64}, // .SI + {2, 2}, // .CX }, clobbers: 194, // .CX .SI .DI }, @@ -2405,6 +2497,7 @@ var opcodeTable = [...]opInfo{ { name: "LoweredGetG", reg: regInfo{ + clobbers: 8589934592, // .FLAGS outputs: []regMask{ 65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 }, diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index b8a2f24c33..d593faf95b 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -2,22 +2,132 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// Register allocation. +// +// We use a version of a linear scan register allocator. We treat the +// whole function as a single long basic block and run through +// it using a greedy register allocator. Then all merge edges +// (those targeting a block with len(Preds)>1) are processed to +// shuffle data into the place that the target of the edge expects. +// +// The greedy allocator moves values into registers just before they +// are used, spills registers only when necessary, and spills the +// value whose next use is farthest in the future. +// +// The register allocator requires that a block is not scheduled until +// at least one of its predecessors have been scheduled. The most recent +// such predecessor provides the starting register state for a block. +// +// It also requires that there are no critical edges (critical = +// comes from a block with >1 successor and goes to a block with >1 +// predecessor). This makes it easy to add fixup code on merge edges - +// the source of a merge edge has only one successor, so we can add +// fixup code to the end of that block. + +// Spilling +// +// For every value, we generate a spill immediately after the value itself. +// x = Op y z : AX +// x2 = StoreReg x +// While AX still holds x, any uses of x will use that value. When AX is needed +// for another value, we simply reuse AX. Spill code has already been generated +// so there is no code generated at "spill" time. When x is referenced +// subsequently, we issue a load to restore x to a register using x2 as +// its argument: +// x3 = Restore x2 : CX +// x3 can then be used wherever x is referenced again. +// If the spill (x2) is never used, it will be removed at the end of regalloc. +// +// Phi values are special, as always. We define two kinds of phis, those +// where the merge happens in a register (a "register" phi) and those where +// the merge happens in a stack location (a "stack" phi). +// +// A register phi must have the phi and all of its inputs allocated to the +// same register. Register phis are spilled similarly to regular ops: +// b1: y = ... : AX b2: z = ... : AX +// goto b3 goto b3 +// b3: x = phi(y, z) : AX +// x2 = StoreReg x +// +// A stack phi must have the phi and all of its inputs allocated to the same +// stack location. Stack phis start out life already spilled - each phi +// input must be a store (using StoreReg) at the end of the corresponding +// predecessor block. +// b1: y = ... : AX b2: z = ... : BX +// y2 = StoreReg y z2 = StoreReg z +// goto b3 goto b3 +// b3: x = phi(y2, z2) +// The stack allocator knows that StoreReg args of stack-allocated phis +// must be allocated to the same stack slot as the phi that uses them. +// x is now a spilled value and a restore must appear before its first use. + +// TODO + +// Use an affinity graph to mark two values which should use the +// same register. This affinity graph will be used to prefer certain +// registers for allocation. This affinity helps eliminate moves that +// are required for phi implementations and helps generate allocations +// for 2-register architectures. + +// Note: regalloc generates a not-quite-SSA output. If we have: +// +// b1: x = ... : AX +// x2 = StoreReg x +// ... AX gets reused for something else ... +// if ... goto b3 else b4 +// +// b3: x3 = LoadReg x2 : BX b4: x4 = LoadReg x2 : CX +// ... use x3 ... ... use x4 ... +// +// b2: ... use x3 ... +// +// If b3 is the primary predecessor of b2, then we use x3 in b2 and +// add a x4:CX->BX copy at the end of b4. +// But the definition of x3 doesn't dominate b2. We should really +// insert a dummy phi at the start of b2 (x5=phi(x3,x4):BX) to keep +// SSA form. For now, we ignore this problem as remaining in strict +// SSA form isn't needed after regalloc. We'll just leave the use +// of x3 not dominated by the definition of x3, and the CX->BX copy +// will have no use (so don't run deadcode after regalloc!). +// TODO: maybe we should introduce these extra phis? + package ssa -import "sort" +import ( + "fmt" + "unsafe" +) -func setloc(home []Location, v *Value, loc Location) []Location { - for v.ID >= ID(len(home)) { - home = append(home, nil) - } - home[v.ID] = loc - return home +const regDebug = false + +// regalloc performs register allocation on f. It sets f.RegAlloc +// to the resulting allocation. +func regalloc(f *Func) { + var s regAllocState + s.init(f) + s.regalloc(f) } -type register uint +type register uint8 + +const noRegister register = 255 type regMask uint64 +func (m regMask) String() string { + s := "" + for r := register(0); r < numRegs; r++ { + if m>>r&1 == 0 { + continue + } + if s != "" { + s += " " + } + s += fmt.Sprintf("r%d", r) + } + return s +} + // TODO: make arch-dependent var numRegs register = 64 @@ -84,343 +194,719 @@ func pickReg(r regMask) register { } } -// regalloc performs register allocation on f. It sets f.RegAlloc -// to the resulting allocation. -func regalloc(f *Func) { - // For now, a very simple allocator. Everything has a home - // location on the stack (TBD as a subsequent stackalloc pass). - // Values live in the home locations at basic block boundaries. - // We use a simple greedy allocator within a basic block. - home := make([]Location, f.NumValues()) +// A use is a record of a position (2*pc for value uses, odd numbers for other uses) +// and a value ID that is used at that position. +type use struct { + idx int32 + vid ID +} - addPhiCopies(f) // add copies of phi inputs in preceeding blocks +type valState struct { + regs regMask // the set of registers holding a Value (usually just one) + uses []int32 // sorted list of places where Value is used + usestorage [2]int32 + spill *Value // spilled copy of the Value + spill2 *Value // special alternate spill location used for phi resolution + spillUsed bool + spill2used bool +} - // Compute live values at the end of each block. - live := live(f) - lastUse := make([]int, f.NumValues()) +type regState struct { + v *Value // Original (preregalloc) Value stored in this register. + c *Value // A Value equal to v which is currently in register. Might be v or a copy of it. + // If a register is unused, v==c==nil +} - var oldSched []*Value +type regAllocState struct { + f *Func + + // for each block, its primary predecessor. + // A predecessor of b is primary if it is the closest + // predecessor that appears before b in the layout order. + // We record the index in the Preds list where the primary predecessor sits. + primary []int32 + + // live values on each edge. live[b.ID][idx] is a list of value IDs + // which are live on b's idx'th successor edge. + live [][][]ID + + // current state of each (preregalloc) Value + values []valState + + // current state of each register + regs []regState + + // registers that contain values which can't be kicked out + nospill regMask + + // mask of registers currently in use + used regMask + + // An ordered list (by idx) of all uses in the function + uses []use - // Hack to find sp and sb Values and assign them a register. - // TODO: make not so hacky; update the tighten pass when this is done - var sp, sb *Value - for _, v := range f.Entry.Values { - switch v.Op { - case OpSP: - sp = v - home = setloc(home, v, ®isters[4]) // TODO: arch-dependent - case OpSB: - sb = v - home = setloc(home, v, ®isters[32]) // TODO: arch-dependent + // Home locations (registers) for Values + home []Location + + // current block we're working on + curBlock *Block +} + +// freeReg frees up register r. Any current user of r is kicked out. +func (s *regAllocState) freeReg(r register) { + v := s.regs[r].v + if v == nil { + s.f.Fatalf("tried to free an already free register %d\n", r) + } + + // Mark r as unused. + if regDebug { + fmt.Printf("freeReg %d (dump %s/%s)\n", r, v, s.regs[r].c) + } + s.regs[r] = regState{} + s.values[v.ID].regs &^= regMask(1) << r + s.used &^= regMask(1) << r +} + +// freeRegs frees up all registers listed in m. +func (s *regAllocState) freeRegs(m regMask) { + for m&s.used != 0 { + s.freeReg(pickReg(m & s.used)) + } +} + +func (s *regAllocState) setHome(v *Value, r register) { + // Remember assignment. + for int(v.ID) >= len(s.home) { + s.home = append(s.home, nil) + s.home = s.home[:cap(s.home)] + } + s.home[v.ID] = ®isters[r] +} +func (s *regAllocState) getHome(v *Value) register { + if int(v.ID) >= len(s.home) || s.home[v.ID] == nil { + return noRegister + } + return register(s.home[v.ID].(*Register).Num) +} + +// assignReg assigns register r to hold c, a copy of v. +// r must be unused. +func (s *regAllocState) assignReg(r register, v *Value, c *Value) { + if regDebug { + fmt.Printf("assignReg %d %s/%s\n", r, v, c) + } + if s.regs[r].v != nil { + s.f.Fatalf("tried to assign register %d to %s/%s but it is already used by %s", r, v, c, s.regs[r].v) + } + + // Update state. + s.regs[r] = regState{v, c} + s.values[v.ID].regs |= regMask(1) << r + s.used |= regMask(1) << r + s.setHome(c, r) +} + +// allocReg picks an unused register from regmask. If there is no unused register, +// a Value will be kicked out of a register to make room. +func (s *regAllocState) allocReg(mask regMask) register { + // Pick a register to use. + mask &^= s.nospill + if mask == 0 { + s.f.Fatalf("no register available") + } + + var r register + if unused := mask & ^s.used; unused != 0 { + // Pick an unused register. + return pickReg(unused) + // TODO: use affinity graph to pick a good register + } + // Pick a value to spill. Spill the value with the + // farthest-in-the-future use. + // TODO: Prefer registers with already spilled Values? + // TODO: Modify preference using affinity graph. + mask &^= 1<<4 | 1<<32 // don't spill SP or SB + maxuse := int32(-1) + for t := register(0); t < numRegs; t++ { + if mask>>t&1 == 0 { + continue + } + v := s.regs[t].v + if len(s.values[v.ID].uses) == 0 { + // This can happen when fixing up merge blocks at the end. + // We've already run through the use lists so they are empty. + // Any register would be ok at this point. + r = t + maxuse = 0 + break } + if n := s.values[v.ID].uses[0]; n > maxuse { + r = t + maxuse = n + } + } + if maxuse == -1 { + s.f.Unimplementedf("couldn't find register to spill") + } + s.freeReg(r) + return r +} + +// allocValToReg allocates v to a register selected from regMask and +// returns the register copy of v. Any previous user is kicked out and spilled +// (if necessary). Load code is added at the current pc. If nospill is set the +// allocated register is marked nospill so the assignment cannot be +// undone until the caller allows it by clearing nospill. Returns a +// *Value which is either v or a copy of v allocated to the chosen register. +func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool) *Value { + vi := &s.values[v.ID] + + // Check if v is already in a requested register. + if mask&vi.regs != 0 { + r := pickReg(mask & vi.regs) + if s.regs[r].v != v || s.regs[r].c == nil { + panic("bad register state") + } + if nospill { + s.nospill |= regMask(1) << r + } + return s.regs[r].c + } + + // SP and SB are allocated specially. No regular value should + // be allocated to them. + mask &^= 1<<4 | 1<<32 + + // Allocate a register. + r := s.allocReg(mask) + + // Allocate v to the new register. + var c *Value + if vi.regs != 0 { + // Copy from a register that v is already in. + r2 := pickReg(vi.regs) + if s.regs[r2].v != v { + panic("bad register state") + } + c = s.curBlock.NewValue1(v.Line, OpCopy, v.Type, s.regs[r2].c) + } else { + // Load v from its spill location. + // TODO: rematerialize if we can. + if vi.spill2 != nil { + c = s.curBlock.NewValue1(v.Line, OpLoadReg, v.Type, vi.spill2) + vi.spill2used = true + } else { + c = s.curBlock.NewValue1(v.Line, OpLoadReg, v.Type, vi.spill) + vi.spillUsed = true + } + if v.Type.IsFlags() { + v.Unimplementedf("spill of flags not implemented yet") + } + } + s.assignReg(r, v, c) + if nospill { + s.nospill |= regMask(1) << r + } + return c +} + +func (s *regAllocState) init(f *Func) { + if numRegs > noRegister || numRegs > register(unsafe.Sizeof(regMask(0))*8) { + panic("too many registers") + } + + s.f = f + s.regs = make([]regState, numRegs) + s.values = make([]valState, f.NumValues()) + for i := range s.values { + s.values[i].uses = s.values[i].usestorage[:0] } + s.live = f.live() - // Register allocate each block separately. All live values will live - // in home locations (stack slots) between blocks. + // Compute block order. This array allows us to distinguish forward edges + // from backward edges and compute how far they go. + blockOrder := make([]int32, f.NumBlocks()) + for i, b := range f.Blocks { + blockOrder[b.ID] = int32(i) + } + + // Compute primary predecessors. + s.primary = make([]int32, f.NumBlocks()) for _, b := range f.Blocks { + best := -1 + for i, p := range b.Preds { + if blockOrder[p.ID] >= blockOrder[b.ID] { + continue // backward edge + } + if best == -1 || blockOrder[p.ID] > blockOrder[b.Preds[best].ID] { + best = i + } + } + s.primary[b.ID] = int32(best) + } - // Compute the index of the last use of each Value in the Block. - // Scheduling has already happened, so Values are totally ordered. - // lastUse[x] = max(i) where b.Value[i] uses Value x. - for i, v := range b.Values { - lastUse[v.ID] = -1 - for _, w := range v.Args { - // could condition this store on w.Block == b, but no need - lastUse[w.ID] = i + // Compute uses. We assign a PC to each Value in the program, in f.Blocks + // and then b.Values order. Uses are recorded using this numbering. + // Uses by Values are recorded as 2*PC. Special uses (block control values, + // pseudo-uses for backedges) are recorded as 2*(last PC in block)+1. + var pc int32 + for _, b := range f.Blocks { + // uses in regular Values + for _, v := range b.Values { + for _, a := range v.Args { + s.values[a.ID].uses = append(s.values[a.ID].uses, pc*2) + s.uses = append(s.uses, use{pc * 2, a.ID}) } + pc++ } - // Values which are live at block exit have a lastUse of len(b.Values). + // use as a block control value + endIdx := pc*2 - 1 if b.Control != nil { - lastUse[b.Control.ID] = len(b.Values) + s.values[b.Control.ID].uses = append(s.values[b.Control.ID].uses, endIdx) + s.uses = append(s.uses, use{endIdx, b.Control.ID}) } - // Values live after block exit have a lastUse of len(b.Values)+1. - for _, vid := range live[b.ID] { - lastUse[vid] = len(b.Values) + 1 + // uses by backedges + // Backedges are treated as uses so that the uses span the entire live + // range of the value. + for i, c := range b.Succs { + if blockOrder[c.ID] > blockOrder[b.ID] { + continue // forward edge + } + for _, vid := range s.live[b.ID][i] { + s.values[vid].uses = append(s.values[vid].uses, endIdx) + s.uses = append(s.uses, use{endIdx, vid}) + } } + } + if pc*2 < 0 { + f.Fatalf("pc too large: function too big") + } +} - // For each register, store which value it contains - type regInfo struct { - v *Value // stack-homed original value (or nil if empty) - c *Value // the register copy of v - dirty bool // if the stack-homed copy is out of date +// clearUses drops any uses <= useIdx. Any values which have no future +// uses are dropped from registers. +func (s *regAllocState) clearUses(useIdx int32) { + for len(s.uses) > 0 && s.uses[0].idx <= useIdx { + idx := s.uses[0].idx + vid := s.uses[0].vid + s.uses = s.uses[1:] + + vi := &s.values[vid] + if vi.uses[0] != idx { + s.f.Fatalf("use mismatch for v%d\n", vid) } - regs := make([]regInfo, numRegs) + vi.uses = vi.uses[1:] + if len(vi.uses) != 0 { + continue + } + // Value is dead, free all registers that hold it (except SP & SB). + s.freeRegs(vi.regs &^ (1<<4 | 1<<32)) + } +} - // TODO: hack: initialize fixed registers - regs[4] = regInfo{sp, sp, false} - regs[32] = regInfo{sb, sb, false} +// Sets the state of the registers to that encoded in state. +func (s *regAllocState) setState(state []regState) { + s.freeRegs(s.used) + for r, x := range state { + if x.c == nil { + continue + } + s.assignReg(register(r), x.v, x.c) + } +} - var used regMask // has a 1 for each non-nil entry in regs - var dirty regMask // has a 1 for each dirty entry in regs +func (s *regAllocState) regalloc(f *Func) { + liveset := newSparseSet(f.NumValues()) + argset := newSparseSet(f.NumValues()) + var oldSched []*Value + var phis []*Value + var stackPhis []*Value + var regPhis []*Value + + if f.Entry != f.Blocks[0] { + f.Fatalf("entry block must be first") + } + + var phiRegs []register + + // For each merge block, we record the starting register state (after phi ops) + // for that merge block. Indexed by blockid/regnum. + startRegs := make([][]*Value, f.NumBlocks()) + // end state of registers for each block, idexed by blockid/regnum. + endRegs := make([][]regState, f.NumBlocks()) + var pc int32 + for _, b := range f.Blocks { + s.curBlock = b - oldSched = append(oldSched[:0], b.Values...) + // Make a copy of the block schedule so we can generate a new one in place. + // We make a separate copy for phis and regular values. + nphi := 0 + for _, v := range b.Values { + if v.Op != OpPhi { + break + } + nphi++ + } + phis = append(phis[:0], b.Values[:nphi]...) + oldSched = append(oldSched[:0], b.Values[nphi:]...) b.Values = b.Values[:0] - for idx, v := range oldSched { - // For each instruction, do: - // set up inputs to v in registers - // pick output register - // run insn - // mark output register as dirty - // Note that v represents the Value at "home" (on the stack), and c - // is its register equivalent. There are two ways to establish c: - // - use of v. c will be a load from v's home. - // - definition of v. c will be identical to v but will live in - // a register. v will be modified into a spill of c. - regspec := opcodeTable[v.Op].reg - if v.Op == OpCopy { - // TODO: make this less of a hack - regspec = opcodeTable[OpAMD64ADDQconst].reg + // Initialize start state of block. + if b == f.Entry { + // Regalloc state is empty to start. + if nphi > 0 { + f.Fatalf("phis in entry block") } - inputs := regspec.inputs - outputs := regspec.outputs - if len(inputs) == 0 && len(outputs) == 0 { - // No register allocation required (or none specified yet) + } else if len(b.Preds) == 1 { + // Start regalloc state with the end state of the previous block. + s.setState(endRegs[b.Preds[0].ID]) + if nphi > 0 { + f.Fatalf("phis in single-predecessor block") + } + } else { + // This is the complicated case. We have more than one predecessor, + // which means we may have Phi ops. + + // Copy phi ops into new schedule. + b.Values = append(b.Values, phis...) + + // Start with the final register state of the primary predecessor + idx := s.primary[b.ID] + if idx < 0 { + f.Fatalf("block with no primary predecessor %s", b) + } + p := b.Preds[idx] + s.setState(endRegs[p.ID]) + + // Drop anything not live on the c->b edge. + var idx2 int + for idx2 = 0; idx2 < len(p.Succs); idx2++ { + if p.Succs[idx2] == b { + break + } + } + liveset.clear() + liveset.addAll(s.live[p.ID][idx2]) + for r := register(0); r < numRegs; r++ { + v := s.regs[r].v + if v == nil { + continue + } + if !liveset.contains(v.ID) { + s.freeReg(r) + } + } + + // Decide on registers for phi ops. Use the registers determined + // by the primary predecessor if we can. + // TODO: pick best of (already processed) predecessors? + // Majority vote? Deepest nesting level? + phiRegs = phiRegs[:0] + var used regMask + for _, v := range phis { + if v.Type.IsMemory() { + phiRegs = append(phiRegs, noRegister) + continue + } + regs := s.values[v.Args[idx].ID].regs + m := regs &^ used + var r register + if m != 0 { + r = pickReg(m) + used |= regMask(1) << r + } else { + r = noRegister + } + phiRegs = append(phiRegs, r) + } + // Change register user from phi input to phi. Add phi spill code. + for i, v := range phis { + if v.Type.IsMemory() { + continue + } + r := phiRegs[i] + if r == noRegister { + // stack-based phi + // Spills will be inserted in all the predecessors below. + s.values[v.ID].spill = v // v starts life spilled + s.values[v.ID].spillUsed = true // use is guaranteed + continue + } + // register-based phi + // Transfer ownership of register from input arg to phi. + s.freeReg(r) + s.assignReg(r, v, v) + // Spill the phi in case we need to restore it later. + spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v) + s.values[v.ID].spill = spill + s.values[v.ID].spillUsed = false + } + + // Save the starting state for use by incoming edges below. + startRegs[b.ID] = make([]*Value, numRegs) + for r := register(0); r < numRegs; r++ { + startRegs[b.ID][r] = s.regs[r].v + } + } + + // Process all the non-phi values. + pc += int32(nphi) + for _, v := range oldSched { + if v.Op == OpPhi { + f.Fatalf("phi %s not at start of block", v) + } + if v.Op == OpSP { + s.assignReg(4, v, v) // TODO: arch-dependent b.Values = append(b.Values, v) + pc++ continue } - if v.Op == OpCopy && v.Type.IsMemory() { + if v.Op == OpSB { + s.assignReg(32, v, v) // TODO: arch-dependent b.Values = append(b.Values, v) + pc++ continue } - - // Compute a good input ordering. Start with the most constrained input. - order := make([]intPair, len(inputs)) - for i, input := range inputs { - order[i] = intPair{countRegs(input), i} + s.clearUses(pc*2 - 1) + regspec := opcodeTable[v.Op].reg + if regDebug { + fmt.Printf("%d: working on %s %s %v\n", pc, v, v.LongString(), regspec) + } + if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 { + // No register allocation required (or none specified yet) + s.freeRegs(regspec.clobbers) + b.Values = append(b.Values, v) + pc++ + continue } - sort.Sort(byKey(order)) - // nospill contains registers that we can't spill because - // we already set them up for use by the current instruction. - var nospill regMask - nospill |= 0x100000010 // SP & SB can't be spilled (TODO: arch-specific) + // TODO: If value is rematerializeable, don't issue it here. + // Instead, rely on argument loading code to put it in a register when needed. - // Move inputs into registers - for _, o := range order { - w := v.Args[o.val] - mask := inputs[o.val] - if mask == 0 { - // Input doesn't need a register - continue - } - // TODO: 2-address overwrite instructions + // Move arguments to registers + for _, i := range regspec.inputs { + a := v.Args[i.idx] + v.Args[i.idx] = s.allocValToReg(a, i.regs, true) + } - // Find registers that w is already in - var wreg regMask - for r := register(0); r < numRegs; r++ { - if regs[r].v == w { - wreg |= regMask(1) << r - } - } + // Now that all args are in regs, we're ready to issue the value itself. + // Before we pick a register for the value, allow input registers + // to be deallocated. We do this here so that the output can use the + // same register as a dying input. + s.nospill = 0 + s.clearUses(pc * 2) + + // Dump any registers which will be clobbered + s.freeRegs(regspec.clobbers) + + // Pick register for output. + var r register + var mask regMask + if len(regspec.outputs) > 0 { + mask = regspec.outputs[0] + } + if mask != 0 { + r = s.allocReg(mask) + s.assignReg(r, v, v) + } - var r register - if mask&wreg != 0 { - // w is already in an allowed register. We're done. - r = pickReg(mask & wreg) - } else { - // Pick a register for w - // Priorities (in order) - // - an unused register - // - a clean register - // - a dirty register - // TODO: for used registers, pick the one whose next use is the - // farthest in the future. - mask &^= nospill - if mask & ^dirty != 0 { - mask &^= dirty - } - if mask & ^used != 0 { - mask &^= used - } - r = pickReg(mask) - - // Kick out whomever is using this register. - if regs[r].v != nil { - x := regs[r].v - c := regs[r].c - if regs[r].dirty && lastUse[x.ID] >= idx { - // Write x back to home. Its value is currently held in c. - x.Op = OpStoreReg - x.Aux = nil - x.resetArgs() - x.AddArg(c) - b.Values = append(b.Values, x) - regs[r].dirty = false - dirty &^= regMask(1) << r - } - regs[r].v = nil - regs[r].c = nil - used &^= regMask(1) << r - } + // Issue the Value itself. + b.Values = append(b.Values, v) - // Load w into this register - var c *Value - if len(w.Args) == 0 { - // Materialize w - if w.Op == OpSB { - c = w - } else if w.Op == OpSP { - c = b.NewValue1(w.Line, OpCopy, w.Type, w) - } else { - c = b.NewValue0IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux) - } - } else if len(w.Args) == 1 && (w.Args[0].Op == OpSP || w.Args[0].Op == OpSB) { - // Materialize offsets from SP/SB - c = b.NewValue1IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux, w.Args[0]) - } else if wreg != 0 { - // Copy from another register. - // Typically just an optimization, but this is - // required if w is dirty. - s := pickReg(wreg) - // inv: s != r - c = b.NewValue1(w.Line, OpCopy, w.Type, regs[s].c) - } else { - // Load from home location - c = b.NewValue1(w.Line, OpLoadReg, w.Type, w) - } - home = setloc(home, c, ®isters[r]) - // Remember what we did - regs[r].v = w - regs[r].c = c - regs[r].dirty = false - used |= regMask(1) << r - } + // Issue a spill for this value. We issue spills unconditionally, + // then at the end of regalloc delete the ones we never use. + spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v) + s.values[v.ID].spill = spill + s.values[v.ID].spillUsed = false - // Replace w with its in-register copy. - v.SetArg(o.val, regs[r].c) + // Increment pc for next Value. + pc++ + } - // Remember not to undo this register assignment until after - // the instruction is issued. - nospill |= regMask(1) << r - } + // Load control value into reg + if b.Control != nil && !b.Control.Type.IsMemory() { + // TODO: regspec for block control values, instead of using + // register set from the control op's output. + s.allocValToReg(b.Control, opcodeTable[b.Control.Op].reg.outputs[0], false) + } - // TODO: do any clobbering + // Record endRegs + endRegs[b.ID] = make([]regState, numRegs) + copy(endRegs[b.ID], s.regs) - // pick a register for v itself. - if len(outputs) > 1 { - panic("can't do multi-output yet") + // Allow control Values and Values live only on backedges to be dropped. + s.clearUses(pc*2 - 1) + } + + // Process merge block input edges. They are the tricky ones. + dst := make([]*Value, numRegs) + for _, b := range f.Blocks { + if len(b.Preds) <= 1 { + continue + } + for i, p := range b.Preds { + if regDebug { + fmt.Printf("processing %s->%s\n", p, b) } - if len(outputs) == 0 || outputs[0] == 0 { - // output doesn't need a register - b.Values = append(b.Values, v) - } else { - mask := outputs[0] - if mask & ^dirty != 0 { - mask &^= dirty + + // Find phis, separate them into stack & register classes. + stackPhis = stackPhis[:0] + regPhis = regPhis[:0] + for _, v := range b.Values { + if v.Op != OpPhi { + break } - if mask & ^used != 0 { - mask &^= used + if v.Type.IsMemory() { + continue } - r := pickReg(mask) - - // Kick out whomever is using this register. - if regs[r].v != nil { - x := regs[r].v - c := regs[r].c - if regs[r].dirty && lastUse[x.ID] >= idx { - // Write x back to home. Its value is currently held in c. - x.Op = OpStoreReg - x.Aux = nil - x.resetArgs() - x.AddArg(c) - b.Values = append(b.Values, x) - regs[r].dirty = false - dirty &^= regMask(1) << r - } - regs[r].v = nil - regs[r].c = nil - used &^= regMask(1) << r + if s.getHome(v) != noRegister { + regPhis = append(regPhis, v) + } else { + stackPhis = append(stackPhis, v) } - - // Reissue v with new op, with r as its home. - c := b.NewValue0IA(v.Line, v.Op, v.Type, v.AuxInt, v.Aux) - c.AddArgs(v.Args...) - home = setloc(home, c, ®isters[r]) - - // Remember what we did - regs[r].v = v - regs[r].c = c - regs[r].dirty = true - used |= regMask(1) << r - dirty |= regMask(1) << r } - } - // If the block ends in a call, we must put the call after the spill code. - var call *Value - if b.Kind == BlockCall { - call = b.Control - if call != b.Values[len(b.Values)-1] { - b.Fatalf("call not at end of block %v %v", b, call) + // Start with the state that exists at the end of the + // predecessor block. We'll be adding instructions here + // to shuffle registers & stack phis into the right spot. + s.setState(endRegs[p.ID]) + s.curBlock = p + + // Handle stack-based phi ops first. We need to handle them + // first because we need a register with which to copy them. + + // We must be careful not to overwrite any stack phis which are + // themselves args of other phis. For example: + // v1 = phi(v2, v3) : 8(SP) + // v2 = phi(v4, v5) : 16(SP) + // Here we must not write v2 until v2 is read and written to v1. + // The situation could be even more complicated, with cycles, etc. + // So in the interest of being simple, we find all the phis which + // are arguments of other phis and copy their values to a temporary + // location first. This temporary location is called "spill2" and + // represents a higher-priority but temporary spill location for the value. + // Note this is not a problem for register-based phis because + // if needed we will use the spilled location as the source, and + // the spill location is not clobbered by the code generated here. + argset.clear() + for _, v := range stackPhis { + argset.add(v.Args[i].ID) } - b.Values = b.Values[:len(b.Values)-1] - // TODO: do this for all control types? - } - - // at the end of the block, spill any remaining dirty, live values - for r := register(0); r < numRegs; r++ { - if !regs[r].dirty { - continue + for _, v := range regPhis { + argset.add(v.Args[i].ID) } - v := regs[r].v - c := regs[r].c - if lastUse[v.ID] <= len(oldSched) { - if v == v.Block.Control { - // link control value to register version - v.Block.Control = c + for _, v := range stackPhis { + if !argset.contains(v.ID) { + continue } - continue // not live after block + // This stack-based phi is the argument of some other + // phi in this block. We must make a copy of its + // value so that we don't clobber it prematurely. + c := s.allocValToReg(v, s.values[v.ID].regs|1<<0, false) + d := p.NewValue1(v.Line, OpStoreReg, v.Type, c) + s.values[v.ID].spill2 = d } - // change v to be a copy of c - v.Op = OpStoreReg - v.Aux = nil - v.resetArgs() - v.AddArg(c) - b.Values = append(b.Values, v) + // Assign to stack-based phis. We do stack phis first because + // we might need a register to do the assignment. + for _, v := range stackPhis { + // Load phi arg into a register, then store it with a StoreReg. + // If already in a register, use that. If not, use register 0. + // TODO: choose a better default register (set of reg by type?). + c := s.allocValToReg(v.Args[i], s.values[v.Args[i].ID].regs|1<<0, false) + v.Args[i] = p.NewValue1(v.Line, OpStoreReg, v.Type, c) + } + // Figure out what value goes in each register. + for r := register(0); r < numRegs; r++ { + dst[r] = startRegs[b.ID][r] + } + // Handle register-based phi ops. + for _, v := range regPhis { + r := s.getHome(v) + if dst[r] != v { + f.Fatalf("dst not right") + } + v.Args[i] = s.allocValToReg(v.Args[i], regMask(1)<CX and CX->DX, do the latter first. Now if we do the + // former first then the latter must be a restore instead of a register move. + + // Erase any spills we never used + for i := range s.values { + vi := s.values[i] + if vi.spillUsed { + continue + } + spill := vi.spill + if spill == nil { + // Constants, SP, SB, ... + continue } + spill.Op = OpInvalid + spill.Type = TypeInvalid + spill.resetArgs() } - f.RegAlloc = home - deadcode(f) // remove values that had all of their uses rematerialized. TODO: separate pass? -} - -// addPhiCopies adds copies of phi inputs in the blocks -// immediately preceding the phi's block. -func addPhiCopies(f *Func) { for _, b := range f.Blocks { - phis := true // all phis should appear first; confirm that as we go + i := 0 for _, v := range b.Values { - switch { - case v.Op == OpPhi && !phis: - f.Fatalf("phi var %v not at beginning of block %v:\n%s\n", v, v.Block, f) - break - case v.Op != OpPhi: - phis = false + if v.Op == OpInvalid { continue - case v.Type.IsMemory(): // TODO: only "regallocable" types - continue - } - for i, w := range v.Args { - c := b.Preds[i] - cpy := c.NewValue1(w.Line, OpCopy, v.Type, w) - v.Args[i] = cpy } + b.Values[i] = v + i++ } + b.Values = b.Values[:i] + // TODO: zero b.Values[i:], recycle Values + // Not important now because this is the last phase that manipulates Values } + + // Set final regalloc result. + f.RegAlloc = s.home } -// live returns a map from block ID to a list of value IDs live at the end of that block +// live returns a map from block ID and successor edge index to a list +// of value IDs live on that edge. // TODO: this could be quadratic if lots of variables are live across lots of // basic blocks. Figure out a way to make this function (or, more precisely, the user // of this function) require only linear size & time. -func live(f *Func) [][]ID { - live := make([][]ID, f.NumBlocks()) +func (f *Func) live() [][][]ID { + live := make([][][]ID, f.NumBlocks()) + for _, b := range f.Blocks { + live[b.ID] = make([][]ID, len(b.Succs)) + } var phis []*Value s := newSparseSet(f.NumValues()) @@ -445,7 +931,11 @@ func live(f *Func) [][]ID { for _, b := range po { // Start with known live values at the end of the block s.clear() - s.addAll(live[b.ID]) + for i := 0; i < len(b.Succs); i++ { + s.addAll(live[b.ID][i]) + } + + // Mark control value as live if b.Control != nil { s.add(b.Control.ID) } @@ -467,19 +957,24 @@ func live(f *Func) [][]ID { // for each predecessor of b, expand its list of live-at-end values // invariant: s contains the values live at the start of b (excluding phi inputs) for i, p := range b.Preds { + // Find index of b in p's successors. + var j int + for j = 0; j < len(p.Succs); j++ { + if p.Succs[j] == b { + break + } + } t.clear() - t.addAll(live[p.ID]) + t.addAll(live[p.ID][j]) t.addAll(s.contents()) for _, v := range phis { t.add(v.Args[i].ID) } - if t.size() == len(live[p.ID]) { + if t.size() == len(live[p.ID][j]) { continue } // grow p's live set - c := make([]ID, t.size()) - copy(c, t.contents()) - live[p.ID] = c + live[p.ID][j] = append(live[p.ID][j][:0], t.contents()...) changed = true } } @@ -490,13 +985,3 @@ func live(f *Func) [][]ID { } return live } - -// for sorting a pair of integers by key -type intPair struct { - key, val int -} -type byKey []intPair - -func (a byKey) Len() int { return len(a) } -func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key } diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 064b84a804..626fb8f369 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -4,6 +4,15 @@ package ssa +// setloc sets the home location of v to loc. +func setloc(home []Location, v *Value, loc Location) []Location { + for v.ID >= ID(len(home)) { + home = append(home, nil) + } + home[v.ID] = loc + return home +} + // stackalloc allocates storage in the stack frame for // all Values that did not get a register. func stackalloc(f *Func) { @@ -26,7 +35,7 @@ func stackalloc(f *Func) { // so stackmap is smaller. // Assign stack locations to phis first, because we - // must also assign the same locations to the phi copies + // must also assign the same locations to the phi stores // introduced during regalloc. for _, b := range f.Blocks { for _, v := range b.Values { @@ -36,12 +45,19 @@ func stackalloc(f *Func) { if v.Type.IsMemory() { // TODO: only "regallocable" types continue } + if int(v.ID) < len(home) && home[v.ID] != nil { + continue // register-based phi + } + // stack-based phi n = align(n, v.Type.Alignment()) f.Logf("stackalloc: %d-%d for %v\n", n, n+v.Type.Size(), v) loc := &LocalSlot{n} n += v.Type.Size() home = setloc(home, v, loc) for _, w := range v.Args { + if w.Op != OpStoreReg { + f.Fatalf("stack-based phi must have StoreReg args") + } home = setloc(home, w, loc) } } diff --git a/src/cmd/compile/internal/ssa/tighten.go b/src/cmd/compile/internal/ssa/tighten.go index 02b1f701f5..a43218095e 100644 --- a/src/cmd/compile/internal/ssa/tighten.go +++ b/src/cmd/compile/internal/ssa/tighten.go @@ -57,13 +57,6 @@ func tighten(f *Func) { if v.Op == OpPhi { continue } - if v.Op == OpSB || v.Op == OpSP { - // regalloc expects OpSP and OpSB values to be in the entry block, - // so don't move them. - // TODO: Handle this more gracefully in regalloc and - // remove this restriction. - continue - } if uses[v.ID] == 1 && !phi[v.ID] && home[v.ID] != b && len(v.Args) < 2 { // v is used in exactly one block, and it is not b. // Furthermore, it takes at most one input, diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index e6e23d5270..286edc0cda 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -11,7 +11,7 @@ import "fmt" // if they preserve the value of the Value (e.g. changing a (mul 2 x) to an (add x x)). type Value struct { // A unique identifier for the value. For performance we allocate these IDs - // densely starting at 0. There is no guarantee that there won't be occasional holes, though. + // densely starting at 1. There is no guarantee that there won't be occasional holes, though. ID ID // The operation that computes this value. See op.go. @@ -69,7 +69,7 @@ func (v *Value) LongString() string { s += fmt.Sprintf(" %v", a) } r := v.Block.Func.RegAlloc - if r != nil && r[v.ID] != nil { + if int(v.ID) < len(r) && r[v.ID] != nil { s += " : " + r[v.ID].Name() } return s -- cgit v1.3 From d2107fc98724662c7aa343d8004ed9d391fdeb59 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 24 Aug 2015 02:16:19 -0700 Subject: [dev.ssa] cmd/runtime: generate gc bitmaps for SSA-compiled code This change is all about leveraging the gc bitmap generation that is already done by the current compiler. We rearrange how stack allocation is done so that we generate a variable declaration for each spill. We also reorganize how args/locals are recorded during SSA. Then we can use the existing allocauto/defframe to allocate the stack frame and liveness to make the gc bitmaps. With this change, stack copying works correctly and we no longer need hacks in runtime/stack*.go to make tests work. GC is close to working, it just needs write barriers. Change-Id: I990fb4e3fbe98850c6be35c3185a1c85d9e1a6ba Reviewed-on: https://go-review.googlesource.com/13894 Reviewed-by: David Chase Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/gc/ssa.go | 140 +++++++++++++++---------- src/cmd/compile/internal/ssa/config.go | 6 ++ src/cmd/compile/internal/ssa/export_test.go | 8 +- src/cmd/compile/internal/ssa/func.go | 2 - src/cmd/compile/internal/ssa/gen/genericOps.go | 7 +- src/cmd/compile/internal/ssa/location.go | 4 +- src/cmd/compile/internal/ssa/lower.go | 2 +- src/cmd/compile/internal/ssa/opGen.go | 10 ++ src/cmd/compile/internal/ssa/schedule.go | 3 +- src/cmd/compile/internal/ssa/stackalloc.go | 46 +------- src/cmd/compile/internal/ssa/value.go | 14 ++- 11 files changed, 128 insertions(+), 114 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 9bd3655e52..407b143809 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -91,11 +91,11 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) { n := d.N switch n.Class { case PPARAM, PPARAMOUT: - aux := &ssa.ArgSymbol{Typ: n.Type, Offset: n.Xoffset, Sym: n.Sym} + aux := &ssa.ArgSymbol{Typ: n.Type, Node: n} s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) case PAUTO: - aux := &ssa.AutoSymbol{Typ: n.Type, Offset: -1, Sym: n.Sym} // offset TBD by SSA pass - s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) + // processed at each use, to prevent Addr coming + // before the decl. default: str := "" if n.Class&PHEAP != 0 { @@ -105,7 +105,7 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) { } } // nodfp is a special argument which is the function's FP. - aux := &ssa.ArgSymbol{Typ: Types[TUINTPTR], Offset: 0, Sym: nodfp.Sym} + aux := &ssa.ArgSymbol{Typ: Types[TUINTPTR], Node: nodfp} s.decladdrs[nodfp] = s.entryNewValue1A(ssa.OpAddr, Types[TUINTPTR], aux, s.sp) // Convert the AST-based IR to the SSA-based IR @@ -200,7 +200,7 @@ type state struct { // all defined variables at the end of each block. Indexed by block ID. defvars []map[*Node]*ssa.Value - // addresses of PPARAM, PPARAMOUT, and PAUTO variables. + // addresses of PPARAM and PPARAMOUT variables. decladdrs map[*Node]*ssa.Value // starting values. Memory, frame pointer, and stack pointer @@ -721,8 +721,11 @@ func (s *state) stmt(n *Node) { s.startBlock(bEnd) case OVARKILL: - // TODO(khr): ??? anything to do here? Only for addrtaken variables? - // Maybe just link it in the store chain? + // Insert a varkill op to record that a variable is no longer live. + // We only care about liveness info at call sites, so putting the + // varkill in the store chain is enough to keep it correctly ordered + // with respect to call ops. + s.vars[&memvar] = s.newValue1A(ssa.OpVarKill, ssa.TypeMem, n.Left, s.mem()) default: s.Unimplementedf("unhandled stmt %s", opnames[n.Op]) } @@ -1175,9 +1178,9 @@ func (s *state) expr(n *Node) *ssa.Value { return s.entryNewValue0A(ssa.OpConstString, n.Type, n.Val().U) case CTBOOL: if n.Val().U.(bool) { - return s.entryNewValue0I(ssa.OpConstBool, n.Type, 1) // 1 = true + return s.entryNewValue0I(ssa.OpConstBool, Types[TBOOL], 1) // 1 = true } else { - return s.entryNewValue0I(ssa.OpConstBool, n.Type, 0) // 0 = false + return s.entryNewValue0I(ssa.OpConstBool, Types[TBOOL], 0) // 0 = false } case CTNIL: t := n.Type @@ -1798,6 +1801,9 @@ func (s *state) assign(op uint8, left *Node, right *Node) { if !canSSA(left) { // if we can't ssa this memory, treat it as just zeroing out the backing memory addr := s.addr(left) + if left.Op == ONAME { + s.vars[&memvar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, left, s.mem()) + } s.vars[&memvar] = s.newValue2I(ssa.OpZero, ssa.TypeMem, t.Size(), addr, s.mem()) return } @@ -1812,6 +1818,9 @@ func (s *state) assign(op uint8, left *Node, right *Node) { } // not ssa-able. Treat as a store. addr := s.addr(left) + if left.Op == ONAME { + s.vars[&memvar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, left, s.mem()) + } s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, t.Size(), addr, val, s.mem()) } @@ -1857,7 +1866,7 @@ func (s *state) zeroVal(t *Type) *ssa.Value { case t.IsPtr(): return s.entryNewValue0(ssa.OpConstNil, t) case t.IsBoolean(): - return s.entryNewValue0I(ssa.OpConstBool, t, 0) // 0 = false + return s.entryNewValue0I(ssa.OpConstBool, Types[TBOOL], 0) // 0 = false case t.IsInterface(): return s.entryNewValue0(ssa.OpConstInterface, t) case t.IsSlice(): @@ -1894,7 +1903,7 @@ func (s *state) addr(n *Node) *ssa.Value { v = s.entryNewValue1I(ssa.OpOffPtr, v.Type, n.Xoffset, v) } return v - case PPARAM, PPARAMOUT, PAUTO: + case PPARAM, PPARAMOUT: // parameter/result slot or local variable v := s.decladdrs[n] if v == nil { @@ -1904,6 +1913,17 @@ func (s *state) addr(n *Node) *ssa.Value { s.Fatalf("addr of undeclared ONAME %v. declared: %v", n, s.decladdrs) } return v + case PAUTO: + // We need to regenerate the address of autos + // at every use. This prevents LEA instructions + // from occurring before the corresponding VarDef + // op and confusing the liveness analysis into thinking + // the variable is live at function entry. + // TODO: I'm not sure if this really works or we're just + // getting lucky. We might need a real dependency edge + // between vardef and addr ops. + aux := &ssa.AutoSymbol{Typ: n.Type, Node: n} + return s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) case PAUTO | PHEAP, PPARAMREF: return s.expr(n.Name.Heapaddr) default: @@ -2477,23 +2497,12 @@ type branch struct { // genssa appends entries to ptxt for each instruction in f. // gcargs and gclocals are filled in with pointer maps for the frame. func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { - // TODO: line numbers - - if f.FrameSize > 1<<31 { - Yyerror("stack frame too large (>2GB)") - return - } - e := f.Config.Frontend().(*ssaExport) // We're about to emit a bunch of Progs. // Since the only way to get here is to explicitly request it, // just fail on unimplemented instead of trying to unwind our mess. e.mustImplement = true - ptxt.To.Type = obj.TYPE_TEXTSIZE - ptxt.To.Val = int32(Rnd(Curfn.Type.Argwid, int64(Widthptr))) // arg size - ptxt.To.Offset = f.FrameSize - 8 // TODO: arch-dependent - // Remember where each block starts. bstart := make([]*obj.Prog, f.NumBlocks()) @@ -2592,18 +2601,22 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { } } - // TODO: liveness - // TODO: gcargs - // TODO: gclocals + // Allocate stack frame + allocauto(ptxt) + + // Generate gc bitmaps. + liveness(Curfn, ptxt, gcargs, gclocals) + gcsymdup(gcargs) + gcsymdup(gclocals) - // TODO: dump frame if -f + // Add frame prologue. Zero ambiguously live variables. + Thearch.Defframe(ptxt) + if Debug['f'] != 0 { + frame(0) + } - // Emit garbage collection symbols. TODO: put something in them - //liveness(Curfn, ptxt, gcargs, gclocals) - duint32(gcargs, 0, 0) - ggloblsym(gcargs, 4, obj.RODATA|obj.DUPOK) - duint32(gclocals, 0, 0) - ggloblsym(gclocals, 4, obj.RODATA|obj.DUPOK) + // Remove leftover instrumentation from the instruction stream. + removevardef(ptxt) f.Config.HTML.Close() } @@ -3056,9 +3069,11 @@ func genValue(v *ssa.Value) { return } p := Prog(movSizeByType(v.Type)) + n := autoVar(v.Args[0]) p.From.Type = obj.TYPE_MEM - p.From.Reg = x86.REG_SP - p.From.Offset = localOffset(v.Args[0]) + p.From.Name = obj.NAME_AUTO + p.From.Node = n + p.From.Sym = Linksym(n.Sym) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) @@ -3070,9 +3085,11 @@ func genValue(v *ssa.Value) { p := Prog(movSizeByType(v.Type)) p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[0]) + n := autoVar(v) p.To.Type = obj.TYPE_MEM - p.To.Reg = x86.REG_SP - p.To.Offset = localOffset(v) + p.To.Name = obj.NAME_AUTO + p.To.Node = n + p.To.Sym = Linksym(n.Sym) case ssa.OpPhi: // just check to make sure regalloc and stackalloc did it right if v.Type.IsMemory() { @@ -3106,19 +3123,19 @@ func genValue(v *ssa.Value) { q.From.Reg = x86.REG_AX q.To.Type = obj.TYPE_MEM q.To.Reg = r - // TODO: need AUNDEF here? + Prog(obj.AUNDEF) // tell plive.go that we never reach here case ssa.OpAMD64LoweredPanicIndexCheck: p := Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = Linksym(Panicindex.Sym) - // TODO: need AUNDEF here? + Prog(obj.AUNDEF) case ssa.OpAMD64LoweredPanicSliceCheck: p := Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = Linksym(panicslice.Sym) - // TODO: need AUNDEF here? + Prog(obj.AUNDEF) case ssa.OpAMD64LoweredGetG: r := regnum(v) // See the comments in cmd/internal/obj/x86/obj6.go @@ -3151,10 +3168,16 @@ func genValue(v *ssa.Value) { p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = Linksym(v.Aux.(*Sym)) + if Maxarg < v.AuxInt { + Maxarg = v.AuxInt + } case ssa.OpAMD64CALLclosure: p := Prog(obj.ACALL) p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v.Args[0]) + if Maxarg < v.AuxInt { + Maxarg = v.AuxInt + } case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL, ssa.OpAMD64NEGW, ssa.OpAMD64NEGB, ssa.OpAMD64NOTQ, ssa.OpAMD64NOTL, ssa.OpAMD64NOTW, ssa.OpAMD64NOTB: x := regnum(v.Args[0]) @@ -3215,6 +3238,10 @@ func genValue(v *ssa.Value) { case ssa.OpAMD64REPMOVSB: Prog(x86.AREP) Prog(x86.AMOVSB) + case ssa.OpVarDef: + Gvardef(v.Aux.(*Node)) + case ssa.OpVarKill: + gvarkill(v.Aux.(*Node)) default: v.Unimplementedf("genValue not implemented: %s", v.LongString()) } @@ -3414,12 +3441,16 @@ func addAux(a *obj.Addr, v *ssa.Value) { a.Name = obj.NAME_EXTERN a.Sym = Linksym(sym.Sym.(*Sym)) case *ssa.ArgSymbol: - a.Offset += v.Block.Func.FrameSize + sym.Offset + n := sym.Node.(*Node) + a.Name = obj.NAME_PARAM + a.Node = n + a.Sym = Linksym(n.Orig.Sym) + a.Offset += n.Xoffset // TODO: why do I have to add this here? I don't for auto variables. case *ssa.AutoSymbol: - if sym.Offset == -1 { - v.Fatalf("auto symbol %s offset not calculated", sym.Sym) - } - a.Offset += sym.Offset + n := sym.Node.(*Node) + a.Name = obj.NAME_AUTO + a.Node = n + a.Sym = Linksym(n.Sym) default: v.Fatalf("aux in %s not implemented %#v", v, v.Aux) } @@ -3571,18 +3602,9 @@ func regnum(v *ssa.Value) int16 { return ssaRegToReg[reg.(*ssa.Register).Num] } -// localOffset returns the offset below the frame pointer where -// a stack-allocated local has been allocated. Panics if v -// is not assigned to a local slot. -// TODO: Make this panic again once it stops happening routinely. -func localOffset(v *ssa.Value) int64 { - reg := v.Block.Func.RegAlloc[v.ID] - slot, ok := reg.(*ssa.LocalSlot) - if !ok { - v.Unimplementedf("localOffset of non-LocalSlot value: %s\n%s\n", v.LongString(), v.Block.Func) - return 0 - } - return slot.Idx +// autoVar returns a *Node representing the auto variable assigned to v. +func autoVar(v *ssa.Value) *Node { + return v.Block.Func.RegAlloc[v.ID].(*ssa.LocalSlot).N.(*Node) } // ssaExport exports a bunch of compiler services for the ssa backend. @@ -3616,6 +3638,12 @@ func (*ssaExport) StringData(s string) interface{} { return &ssa.ExternSymbol{Typ: idealstring, Sym: data} } +func (e *ssaExport) Auto(t ssa.Type) fmt.Stringer { + n := temp(t.(*Type)) // Note: adds new auto to Curfn.Func.Dcl list + e.mustImplement = true // This modifies the input to SSA, so we want to make sure we succeed from here! + return n +} + // Log logs a message from the compiler. func (e *ssaExport) Logf(msg string, args ...interface{}) { // If e was marked as unimplemented, anything could happen. Ignore. diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 865066870d..8ae74d0b2f 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -4,6 +4,8 @@ package ssa +import "fmt" + type Config struct { arch string // "amd64", etc. IntSize int64 // 4 or 8 @@ -52,6 +54,10 @@ type Frontend interface { // StringData returns a symbol pointing to the given string's contents. StringData(string) interface{} // returns *gc.Sym + + // Auto returns a Node for an auto variable of the given type. + // The SSA compiler uses this function to allocate space for spills. + Auto(Type) fmt.Stringer // returns *gc.Node } // NewConfig returns a new configuration object for the given architecture. diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 5b56aa5184..7c314c2630 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -4,7 +4,10 @@ package ssa -import "testing" +import ( + "fmt" + "testing" +) var CheckFunc = checkFunc var PrintFunc = printFunc @@ -24,6 +27,9 @@ type DummyFrontend struct { func (DummyFrontend) StringData(s string) interface{} { return nil } +func (DummyFrontend) Auto(t Type) fmt.Stringer { + return nil +} func (d DummyFrontend) Logf(msg string, args ...interface{}) { d.t.Logf(msg, args...) } func (d DummyFrontend) Fatalf(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 747a5c7f03..b6956a459f 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -25,8 +25,6 @@ type Func struct { // when register allocation is done, maps value ids to locations RegAlloc []Location - // when stackalloc is done, the size of the stack frame - FrameSize int64 } // NumBlocks returns an integer larger than the id of any Block in the Func. diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 8cd8165028..81fe20547e 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -278,8 +278,8 @@ var genericOps = []opData{ // Function calls. Arguments to the call have already been written to the stack. // Return values appear on the stack. The method receiver, if any, is treated // as a phantom first argument. - {name: "ClosureCall"}, // arg0=code pointer, arg1=context ptr, arg2=memory. Returns memory. - {name: "StaticCall"}, // call function aux.(*gc.Sym), arg0=memory. Returns memory. + {name: "ClosureCall"}, // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory. + {name: "StaticCall"}, // call function aux.(*gc.Sym), arg0=memory. auxint=arg size. Returns memory. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", typ: "Int16"}, @@ -359,6 +359,9 @@ var genericOps = []opData{ // Used during ssa construction. Like Copy, but the arg has not been specified yet. {name: "FwdRef"}, + + {name: "VarDef"}, // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem + {name: "VarKill"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem } // kind control successors diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go index 1b6f6d66c1..9f445e5b5a 100644 --- a/src/cmd/compile/internal/ssa/location.go +++ b/src/cmd/compile/internal/ssa/location.go @@ -26,9 +26,9 @@ func (r *Register) Name() string { // A LocalSlot is a location in the stack frame. type LocalSlot struct { - Idx int64 // offset in locals area (distance up from SP) + N fmt.Stringer // a *gc.Node for an auto variable } func (s *LocalSlot) Name() string { - return fmt.Sprintf("%d(SP)", s.Idx) + return s.N.String() } diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index 3dac264fac..9c28bd10a5 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -21,7 +21,7 @@ func checkLower(f *Func) { continue // lowered } switch v.Op { - case OpSP, OpSB, OpArg, OpCopy, OpPhi: + case OpSP, OpSB, OpArg, OpCopy, OpPhi, OpVarDef, OpVarKill: continue // ok not to lower } s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString() diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index a61c31ad5a..087a0e75b8 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -524,6 +524,8 @@ const ( OpStoreReg OpLoadReg OpFwdRef + OpVarDef + OpVarKill ) var opcodeTable = [...]opInfo{ @@ -4109,6 +4111,14 @@ var opcodeTable = [...]opInfo{ name: "FwdRef", generic: true, }, + { + name: "VarDef", + generic: true, + }, + { + name: "VarKill", + generic: true, + }, } func (o Op) Asm() int { return opcodeTable[o].asm } diff --git a/src/cmd/compile/internal/ssa/schedule.go b/src/cmd/compile/internal/ssa/schedule.go index cf5f872e0f..e551a6375c 100644 --- a/src/cmd/compile/internal/ssa/schedule.go +++ b/src/cmd/compile/internal/ssa/schedule.go @@ -74,7 +74,8 @@ func schedule(f *Func) { score[v.ID] = 0 case v.Type.IsMemory(): // Schedule stores as early as possible. This tends to - // reduce register pressure. + // reduce register pressure. It also helps make sure + // VARDEF ops are scheduled before the corresponding LEA. score[v.ID] = 1 case v.Type.IsFlags(): // Schedule flag register generation as late as possible. diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 626fb8f369..d60f8d1df2 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -18,22 +18,6 @@ func setloc(home []Location, v *Value, loc Location) []Location { func stackalloc(f *Func) { home := f.RegAlloc - // Start with space for callee arguments/returns. - var n int64 - for _, b := range f.Blocks { - if b.Kind != BlockCall { - continue - } - v := b.Control - if n < v.AuxInt { - n = v.AuxInt - } - } - f.Logf("stackalloc: 0-%d for callee arguments/returns\n", n) - - // TODO: group variables by ptr/nonptr, size, etc. Emit ptr vars last - // so stackmap is smaller. - // Assign stack locations to phis first, because we // must also assign the same locations to the phi stores // introduced during regalloc. @@ -49,10 +33,9 @@ func stackalloc(f *Func) { continue // register-based phi } // stack-based phi - n = align(n, v.Type.Alignment()) - f.Logf("stackalloc: %d-%d for %v\n", n, n+v.Type.Size(), v) + n := f.Config.fe.Auto(v.Type) + f.Logf("stackalloc: %s: for %v <%v>\n", n, v, v.Type) loc := &LocalSlot{n} - n += v.Type.Size() home = setloc(home, v, loc) for _, w := range v.Args { if w.Op != OpStoreReg { @@ -79,34 +62,15 @@ func stackalloc(f *Func) { if len(v.Args) == 1 && (v.Args[0].Op == OpSP || v.Args[0].Op == OpSB) { continue } - n = align(n, v.Type.Alignment()) - f.Logf("stackalloc: %d-%d for %v\n", n, n+v.Type.Size(), v) + + n := f.Config.fe.Auto(v.Type) + f.Logf("stackalloc: %s for %v\n", n, v) loc := &LocalSlot{n} - n += v.Type.Size() home = setloc(home, v, loc) } } - // Finally, allocate space for all autos that we used - for _, b := range f.Blocks { - for _, v := range b.Values { - s, ok := v.Aux.(*AutoSymbol) - if !ok || s.Offset >= 0 { - continue - } - t := s.Typ - n = align(n, t.Alignment()) - f.Logf("stackalloc: %d-%d for auto %v\n", n, n+t.Size(), v) - s.Offset = n - n += t.Size() - } - } - - n = align(n, f.Config.PtrSize) - f.Logf("stackalloc: %d-%d for return address\n", n, n+f.Config.PtrSize) - n += f.Config.PtrSize // space for return address. TODO: arch-dependent f.RegAlloc = home - f.FrameSize = n // TODO: share stack slots among noninterfering (& gc type compatible) values } diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index d213b72df3..a5915da025 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -142,17 +142,15 @@ type ExternSymbol struct { // ArgSymbol is an aux value that encodes an argument or result // variable's constant offset from FP (FP = SP + framesize). type ArgSymbol struct { - Typ Type // Go type - Offset int64 // Distance above frame pointer - Sym fmt.Stringer // A *gc.Sym referring to the argument/result variable. + Typ Type // Go type + Node fmt.Stringer // A *gc.Node referring to the argument/result variable. } // AutoSymbol is an aux value that encodes a local variable's // constant offset from SP. type AutoSymbol struct { - Typ Type // Go type - Offset int64 // Distance above stack pointer. Set by stackalloc in SSA. - Sym fmt.Stringer // A *gc.Sym referring to a local (auto) variable. + Typ Type // Go type + Node fmt.Stringer // A *gc.Node referring to a local (auto) variable. } func (s *ExternSymbol) String() string { @@ -160,9 +158,9 @@ func (s *ExternSymbol) String() string { } func (s *ArgSymbol) String() string { - return s.Sym.String() + return s.Node.String() } func (s *AutoSymbol) String() string { - return s.Sym.String() + return s.Node.String() } -- cgit v1.3 From 366dcc4529d09c31f7b0df65003792022bc5ec09 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sat, 17 Oct 2015 21:14:56 -0700 Subject: [dev.ssa] cmd/compile: Reuse stack slots for spill locations For each type, maintain a list of stack slots used to spill SSA values to the stack. Reuse those stack slots for noninterfering spills. Lowers frame sizes. As an example, runtime.mSpan_Sweep goes from 584 bytes to 392 bytes. heapBitsSetType goes from 576 bytes to 152 bytes. Change-Id: I0e9afe80c2fd84aff9eb368318685de293c363d0 Reviewed-on: https://go-review.googlesource.com/16022 Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/stackalloc.go | 252 +++++++++++++++++++++++------ 1 file changed, 203 insertions(+), 49 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index d60f8d1df2..17d1f66cea 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -2,83 +2,237 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package ssa +// TODO: live at start of block instead? -// setloc sets the home location of v to loc. -func setloc(home []Location, v *Value, loc Location) []Location { - for v.ID >= ID(len(home)) { - home = append(home, nil) - } - home[v.ID] = loc - return home -} +package ssa // stackalloc allocates storage in the stack frame for // all Values that did not get a register. func stackalloc(f *Func) { - home := f.RegAlloc - - // Assign stack locations to phis first, because we - // must also assign the same locations to the phi stores - // introduced during regalloc. + // Cache value types by ID. + types := make([]Type, f.NumValues()) for _, b := range f.Blocks { for _, v := range b.Values { - if v.Op != OpPhi { - continue + types[v.ID] = v.Type + } + } + + // Build interference graph among StoreReg and stack phi ops. + live := f.liveSpills() + interfere := make([][]ID, f.NumValues()) + s := newSparseSet(f.NumValues()) + for _, b := range f.Blocks { + // Start with known live values at the end of the block. + s.clear() + for i := 0; i < len(b.Succs); i++ { + s.addAll(live[b.ID][i]) + } + + // Propagate backwards to the start of the block. + // Remember interfering sets. + for i := len(b.Values) - 1; i >= 0; i-- { + v := b.Values[i] + switch { + case v.Op == OpStoreReg, v.isStackPhi(): + s.remove(v.ID) + for _, id := range s.contents() { + if v.Type == types[id] { + interfere[v.ID] = append(interfere[v.ID], id) + interfere[id] = append(interfere[id], v.ID) + } + } + case v.Op == OpLoadReg: + s.add(v.Args[0].ID) } - if v.Type.IsMemory() { // TODO: only "regallocable" types + } + } + + // Figure out which StoreReg ops are phi args. We don't pick slots for + // phi args because a stack phi and its args must all use the same stack slot. + phiArg := make([]bool, f.NumValues()) + for _, b := range f.Blocks { + for _, v := range b.Values { + if !v.isStackPhi() { continue } - if int(v.ID) < len(home) && home[v.ID] != nil { - continue // register-based phi - } - // stack-based phi - n := f.Config.fe.Auto(v.Type) - f.Logf("stackalloc: %s: for %v <%v>\n", n, v, v.Type) - loc := &LocalSlot{n} - home = setloc(home, v, loc) - for _, w := range v.Args { - if w.Op != OpStoreReg { - f.Fatalf("stack-based phi must have StoreReg args") - } - home = setloc(home, w, loc) + for _, a := range v.Args { + phiArg[a.ID] = true } } } - // Now do all other unassigned values. + // For each type, we keep track of all the stack slots we + // have allocated for that type. + locations := map[Type][]*LocalSlot{} + + // Each time we assign a stack slot to a value v, we remember + // the slot we used via an index into locations[v.Type]. + slots := make([]int, f.NumValues()) + for i := f.NumValues() - 1; i >= 0; i-- { + slots[i] = -1 + } + + // Pick a stack slot for each non-phi-arg StoreReg and each stack phi. + used := make([]bool, f.NumValues()) for _, b := range f.Blocks { for _, v := range b.Values { - if v.ID < ID(len(home)) && home[v.ID] != nil { + if v.Op != OpStoreReg && !v.isStackPhi() { continue } - if v.Type.IsMemory() { // TODO: only "regallocable" types + if phiArg[v.ID] { continue } - if len(v.Args) == 0 { - // v will have been materialized wherever it is needed. - continue + // Set of stack slots we could reuse. + locs := locations[v.Type] + // Mark all positions in locs used by interfering values. + for i := 0; i < len(locs); i++ { + used[i] = false } - if len(v.Args) == 1 && (v.Args[0].Op == OpSP || v.Args[0].Op == OpSB) { - continue + for _, xid := range interfere[v.ID] { + slot := slots[xid] + if slot >= 0 { + used[slot] = true + } } + if v.Op == OpPhi { + // Stack phi and args must get the same stack slot, so + // anything they interfere with is something v the phi + // interferes with. + for _, a := range v.Args { + for _, xid := range interfere[a.ID] { + slot := slots[xid] + if slot >= 0 { + used[slot] = true + } + } + } + } + // Find an unused stack slot. + var i int + for i = 0; i < len(locs); i++ { + if !used[i] { + break + } + } + // If there is no unused stack slot, allocate a new one. + if i == len(locs) { + locs = append(locs, &LocalSlot{f.Config.fe.Auto(v.Type)}) + locations[v.Type] = locs + } + // Use the stack variable at that index for v. + loc := locs[i] + f.setHome(v, loc) + slots[v.ID] = i + if v.Op == OpPhi { + for _, a := range v.Args { + f.setHome(a, loc) + slots[a.ID] = i + } + } + } + } +} + +// live returns a map from block ID and successor edge index to a list +// of StoreReg/stackphi value IDs live on that edge. +// TODO: this could be quadratic if lots of variables are live across lots of +// basic blocks. Figure out a way to make this function (or, more precisely, the user +// of this function) require only linear size & time. +func (f *Func) liveSpills() [][][]ID { + live := make([][][]ID, f.NumBlocks()) + for _, b := range f.Blocks { + live[b.ID] = make([][]ID, len(b.Succs)) + } + var phis []*Value + + s := newSparseSet(f.NumValues()) + t := newSparseSet(f.NumValues()) + + // Instead of iterating over f.Blocks, iterate over their postordering. + // Liveness information flows backward, so starting at the end + // increases the probability that we will stabilize quickly. + po := postorder(f) + for { + changed := false + for _, b := range po { + // Start with known live values at the end of the block + s.clear() + for i := 0; i < len(b.Succs); i++ { + s.addAll(live[b.ID][i]) + } + + // Propagate backwards to the start of the block + phis = phis[:0] + for i := len(b.Values) - 1; i >= 0; i-- { + v := b.Values[i] + switch { + case v.Op == OpStoreReg: + s.remove(v.ID) + case v.Op == OpLoadReg: + s.add(v.Args[0].ID) + case v.isStackPhi(): + s.remove(v.ID) + // save stack phi ops for later + phis = append(phis, v) + } + } + + // for each predecessor of b, expand its list of live-at-end values + // invariant: s contains the values live at the start of b (excluding phi inputs) + for i, p := range b.Preds { + // Find index of b in p's successors. + var j int + for j = 0; j < len(p.Succs); j++ { + if p.Succs[j] == b { + break + } + } + t.clear() + t.addAll(live[p.ID][j]) + t.addAll(s.contents()) + for _, v := range phis { + t.add(v.Args[i].ID) + } + if t.size() == len(live[p.ID][j]) { + continue + } + // grow p's live set + live[p.ID][j] = append(live[p.ID][j][:0], t.contents()...) + changed = true + } + } - n := f.Config.fe.Auto(v.Type) - f.Logf("stackalloc: %s for %v\n", n, v) - loc := &LocalSlot{n} - home = setloc(home, v, loc) + if !changed { + break } } + return live +} - f.RegAlloc = home +func (f *Func) getHome(v *Value) Location { + if int(v.ID) >= len(f.RegAlloc) { + return nil + } + return f.RegAlloc[v.ID] +} - // TODO: share stack slots among noninterfering (& gc type compatible) values +func (f *Func) setHome(v *Value, loc Location) { + for v.ID >= ID(len(f.RegAlloc)) { + f.RegAlloc = append(f.RegAlloc, nil) + } + f.RegAlloc[v.ID] = loc } -// align increases n to the next multiple of a. a must be a power of 2. -func align(n int64, a int64) int64 { - if a == 0 { - return n +func (v *Value) isStackPhi() bool { + if v.Op != OpPhi { + return false + } + if v.Type == TypeMem { + return false + } + if int(v.ID) >= len(v.Block.Func.RegAlloc) { + return true } - return (n + a - 1) &^ (a - 1) + return v.Block.Func.RegAlloc[v.ID] == nil + // TODO: use a separate opcode for StackPhi? } -- cgit v1.3 From c24681ae2e1c96bd67c149cffa8f5ed394e68453 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 22 Oct 2015 14:22:38 -0700 Subject: [dev.ssa] cmd/compile: remember names of values For debugging, spill values to named variables instead of autotmp_ variables if possible. We do this by keeping a name -> value map for each function, keep it up-to-date during deadcode elim, and use it to override spill decisions in stackalloc. It might even make stack frames a bit smaller, as it makes it easy to identify a set of spills which are likely not to interfere. This just works for one-word variables for now. Strings/slices will be a separate CL. Change-Id: Ie89eba8cab16bcd41b311c479ec46dd7e64cdb67 Reviewed-on: https://go-review.googlesource.com/16336 Run-TryBot: Keith Randall Reviewed-by: David Chase --- src/cmd/compile/internal/gc/closure.go | 1 + src/cmd/compile/internal/gc/ssa.go | 73 +++++++++++++++++++++-------- src/cmd/compile/internal/ssa/config.go | 16 ++++--- src/cmd/compile/internal/ssa/deadcode.go | 19 ++++++++ src/cmd/compile/internal/ssa/decompose.go | 4 +- src/cmd/compile/internal/ssa/export_test.go | 3 +- src/cmd/compile/internal/ssa/func.go | 7 +++ src/cmd/compile/internal/ssa/location.go | 6 +-- src/cmd/compile/internal/ssa/stackalloc.go | 63 +++++++++++++++++++++++-- src/cmd/compile/internal/ssa/value.go | 8 ++-- 10 files changed, 156 insertions(+), 44 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index e7bece8bd9..8ebdd66553 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -604,6 +604,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { ptr.Ullman = 1 ptr.Used = true ptr.Name.Curfn = xfunc + ptr.Xoffset = 0 xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) var body *NodeList if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index f7100fefbe..c988465e9f 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -304,14 +304,14 @@ func (s *state) Unimplementedf(msg string, args ...interface{}) { s.config.Unimp var ( // dummy node for the memory variable - memVar = Node{Op: ONAME, Sym: &Sym{Name: "mem"}} + memVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "mem"}} // dummy nodes for temporary variables - ptrVar = Node{Op: ONAME, Sym: &Sym{Name: "ptr"}} - capVar = Node{Op: ONAME, Sym: &Sym{Name: "cap"}} - typVar = Node{Op: ONAME, Sym: &Sym{Name: "typ"}} - idataVar = Node{Op: ONAME, Sym: &Sym{Name: "idata"}} - okVar = Node{Op: ONAME, Sym: &Sym{Name: "ok"}} + ptrVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ptr"}} + capVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "cap"}} + typVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "typ"}} + idataVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "idata"}} + okVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ok"}} ) // startBlock sets the current block we're generating code in to b. @@ -2021,6 +2021,7 @@ func (s *state) assign(left *Node, right *ssa.Value, wb bool) { if left.Op == ONAME && canSSA(left) { // Update variable assignment. s.vars[left] = right + s.addNamedValue(left, right) return } // not ssa-able. Treat as a store. @@ -2245,13 +2246,14 @@ func (s *state) lookupSymbol(n *Node, sym interface{}) interface{} { // If bounded is true then this address does not require a nil check for its operand // even if that would otherwise be implied. func (s *state) addr(n *Node, bounded bool) *ssa.Value { + t := Ptrto(n.Type) switch n.Op { case ONAME: switch n.Class { case PEXTERN: // global variable aux := &ssa.ExternSymbol{n.Type, n.Sym} - v := s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sb) + v := s.entryNewValue1A(ssa.OpAddr, t, aux, s.sb) // TODO: Make OpAddr use AuxInt as well as Aux. if n.Xoffset != 0 { v = s.entryNewValue1I(ssa.OpOffPtr, v.Type, n.Xoffset, v) @@ -2277,12 +2279,12 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value { // getting lucky. We might need a real dependency edge // between vardef and addr ops. aux := &ssa.AutoSymbol{Typ: n.Type, Node: n} - return s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) + return s.newValue1A(ssa.OpAddr, t, aux, s.sp) case PPARAMOUT: // Same as PAUTO -- cannot generate LEA early. // ensure that we reuse symbols for out parameters so // that cse works on their addresses aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n}) - return s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) + return s.newValue1A(ssa.OpAddr, t, aux, s.sp) case PAUTO | PHEAP, PPARAM | PHEAP, PPARAMOUT | PHEAP, PPARAMREF: return s.expr(n.Name.Heapaddr) default: @@ -2296,18 +2298,18 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value { s.Unimplementedf("OINDREG of non-SP register %s in addr: %v", obj.Rconv(int(n.Reg)), n) return nil } - return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.sp) + return s.entryNewValue1I(ssa.OpOffPtr, t, n.Xoffset, s.sp) case OINDEX: if n.Left.Type.IsSlice() { a := s.expr(n.Left) i := s.expr(n.Right) i = s.extendIndex(i) - len := s.newValue1(ssa.OpSliceLen, Types[TUINTPTR], a) + len := s.newValue1(ssa.OpSliceLen, Types[TINT], a) if !n.Bounded { s.boundsCheck(i, len) } - p := s.newValue1(ssa.OpSlicePtr, Ptrto(n.Left.Type.Type), a) - return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), p, i) + p := s.newValue1(ssa.OpSlicePtr, t, a) + return s.newValue2(ssa.OpPtrIndex, t, p, i) } else { // array a := s.addr(n.Left, bounded) i := s.expr(n.Right) @@ -2326,15 +2328,15 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value { return p case ODOT: p := s.addr(n.Left, bounded) - return s.newValue2(ssa.OpAddPtr, p.Type, p, s.constIntPtr(Types[TUINTPTR], n.Xoffset)) + return s.newValue2(ssa.OpAddPtr, t, p, s.constIntPtr(Types[TUINTPTR], n.Xoffset)) case ODOTPTR: p := s.expr(n.Left) if !bounded { s.nilCheck(p) } - return s.newValue2(ssa.OpAddPtr, p.Type, p, s.constIntPtr(Types[TUINTPTR], n.Xoffset)) + return s.newValue2(ssa.OpAddPtr, t, p, s.constIntPtr(Types[TUINTPTR], n.Xoffset)) case OCLOSUREVAR: - return s.newValue2(ssa.OpAddPtr, Ptrto(n.Type), + return s.newValue2(ssa.OpAddPtr, t, s.entryNewValue0(ssa.OpGetClosurePtr, Ptrto(Types[TUINT8])), s.constIntPtr(Types[TUINTPTR], n.Xoffset)) case OPARAM: @@ -2347,11 +2349,10 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value { original_p := *p original_p.Xoffset = n.Xoffset aux := &ssa.ArgSymbol{Typ: n.Type, Node: &original_p} - return s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) + return s.entryNewValue1A(ssa.OpAddr, t, aux, s.sp) case OCONVNOP: addr := s.addr(n.Left, bounded) - to := Ptrto(n.Type) - return s.newValue1(ssa.OpCopy, to, addr) // ensure that addr has the right type + return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type default: s.Unimplementedf("unhandled addr %v", Oconv(int(n.Op), 0)) @@ -3155,6 +3156,7 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name *Node) *ssa.Val // need a phi value v := b.NewValue0(s.peekLine(), ssa.OpPhi, t) v.AddArgs(vals...) + s.addNamedValue(name, v) return v } } @@ -3182,6 +3184,33 @@ func (s *state) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name *Node) *ssa.Val // TODO: the above mutually recursive functions can lead to very deep stacks. Fix that. +func (s *state) addNamedValue(n *Node, v *ssa.Value) { + if n.Class == Pxxx { + // Don't track our dummy nodes (&memVar etc.). + return + } + if n.Sym == nil { + // TODO: What the heck is this? + return + } + if strings.HasPrefix(n.Sym.Name, "autotmp_") { + // Don't track autotmp_ variables. + return + } + if n.Class == PPARAM || n.Class == PPARAMOUT { + // TODO: Remove this + return + } + if n.Class == PAUTO && n.Xoffset != 0 { + s.Fatalf("AUTO var with offset %s %d", n, n.Xoffset) + } + values, ok := s.f.NamedValues[n] + if !ok { + s.f.Names = append(s.f.Names, n) + } + s.f.NamedValues[n] = append(values, v) +} + // an unresolved branch type branch struct { p *obj.Prog // branch instruction @@ -4441,7 +4470,7 @@ func (*ssaExport) StringData(s string) interface{} { return &ssa.ExternSymbol{Typ: idealstring, Sym: data} } -func (e *ssaExport) Auto(t ssa.Type) fmt.Stringer { +func (e *ssaExport) Auto(t ssa.Type) ssa.GCNode { n := temp(t.(*Type)) // Note: adds new auto to Curfn.Func.Dcl list e.mustImplement = true // This modifies the input to SSA, so we want to make sure we succeed from here! return n @@ -4480,3 +4509,7 @@ func (e *ssaExport) Unimplementedf(msg string, args ...interface{}) { } e.unimplemented = true } + +func (n *Node) Typ() ssa.Type { + return n.Type +} diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index efb8b146a1..cfba10bc24 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -4,10 +4,7 @@ package ssa -import ( - "cmd/internal/obj" - "fmt" -) +import "cmd/internal/obj" type Config struct { arch string // "amd64", etc. @@ -63,7 +60,14 @@ type Frontend interface { // Auto returns a Node for an auto variable of the given type. // The SSA compiler uses this function to allocate space for spills. - Auto(Type) fmt.Stringer // returns *gc.Node + Auto(Type) GCNode +} + +// interface used to hold *gc.Node. We'd use *gc.Node directly but +// that would lead to an import cycle. +type GCNode interface { + Typ() Type + String() string } // NewConfig returns a new configuration object for the given architecture. @@ -93,7 +97,7 @@ func (c *Config) Frontend() Frontend { return c.fe } // NewFunc returns a new, empty function object func (c *Config) NewFunc() *Func { // TODO(khr): should this function take name, type, etc. as arguments? - return &Func{Config: c} + return &Func{Config: c, NamedValues: map[GCNode][]*Value{}} } func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index be25eddb47..3351589fda 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -162,6 +162,25 @@ func deadcode(f *Func) { } f.Blocks = f.Blocks[:i] + // Remove dead entries from namedValues map. + for name, values := range f.NamedValues { + i := 0 + for _, v := range values { + for v.Op == OpCopy { + v = v.Args[0] + } + if live[v.ID] { + values[i] = v + i++ + } + } + f.NamedValues[name] = values[:i] + tail := values[i:] + for j := range tail { + tail[j] = nil + } + } + // TODO: renumber Blocks and Values densely? // TODO: save dead Values and Blocks for reuse? Or should we just let GC handle it? } diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index 3ef20ef34f..2057d8ea5c 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -36,7 +36,7 @@ func decompose(f *Func) { func decomposeStringPhi(v *Value) { fe := v.Block.Func.Config.fe ptrType := fe.TypeBytePtr() - lenType := fe.TypeUintptr() + lenType := fe.TypeInt() ptr := v.Block.NewValue0(v.Line, OpPhi, ptrType) len := v.Block.NewValue0(v.Line, OpPhi, lenType) @@ -55,7 +55,7 @@ func decomposeStringPhi(v *Value) { func decomposeSlicePhi(v *Value) { fe := v.Block.Func.Config.fe ptrType := fe.TypeBytePtr() - lenType := fe.TypeUintptr() + lenType := fe.TypeInt() ptr := v.Block.NewValue0(v.Line, OpPhi, ptrType) len := v.Block.NewValue0(v.Line, OpPhi, lenType) diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 76a05f91d9..d0ba7b1c09 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -6,7 +6,6 @@ package ssa import ( "cmd/internal/obj" - "fmt" "testing" ) @@ -29,7 +28,7 @@ type DummyFrontend struct { func (DummyFrontend) StringData(s string) interface{} { return nil } -func (DummyFrontend) Auto(t Type) fmt.Stringer { +func (DummyFrontend) Auto(t Type) GCNode { return nil } diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 1ea7c2e2de..772fffce33 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -25,6 +25,13 @@ type Func struct { // when register allocation is done, maps value ids to locations RegAlloc []Location + + // map from *gc.Node to set of Values that represent that Node. + // The Node must be an ONAME with PPARAM, PPARAMOUT, or PAUTO class. + NamedValues map[GCNode][]*Value + // Names is a copy of NamedValues.Keys. We keep a separate list + // of keys to make iteration order deterministic. + Names []GCNode } // NumBlocks returns an integer larger than the id of any Block in the Func. diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go index 9f445e5b5a..0f9fb33eeb 100644 --- a/src/cmd/compile/internal/ssa/location.go +++ b/src/cmd/compile/internal/ssa/location.go @@ -4,10 +4,6 @@ package ssa -import ( - "fmt" -) - // A place that an ssa variable can reside. type Location interface { Name() string // name to use in assembly templates: %rax, 16(%rsp), ... @@ -26,7 +22,7 @@ func (r *Register) Name() string { // A LocalSlot is a location in the stack frame. type LocalSlot struct { - N fmt.Stringer // a *gc.Node for an auto variable + N GCNode // a *gc.Node for an auto variable } func (s *LocalSlot) Name() string { diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 17d1f66cea..793162a797 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -36,7 +36,8 @@ func stackalloc(f *Func) { case v.Op == OpStoreReg, v.isStackPhi(): s.remove(v.ID) for _, id := range s.contents() { - if v.Type == types[id] { + if v.Type.Equal(types[id]) { + // Only need interferences between equivalent types. interfere[v.ID] = append(interfere[v.ID], id) interfere[id] = append(interfere[id], v.ID) } @@ -47,6 +48,18 @@ func stackalloc(f *Func) { } } + // Build map from values to their names, if any. + // A value may be associated with more than one name (e.g. after + // the assignment i=j). This step picks one name per value arbitrarily. + names := make([]GCNode, f.NumValues()) + for _, name := range f.Names { + // Note: not "range f.NamedValues" above, because + // that would be nondeterministic. + for _, v := range f.NamedValues[name] { + names[v.ID] = name + } + } + // Figure out which StoreReg ops are phi args. We don't pick slots for // phi args because a stack phi and its args must all use the same stack slot. phiArg := make([]bool, f.NumValues()) @@ -67,6 +80,7 @@ func stackalloc(f *Func) { // Each time we assign a stack slot to a value v, we remember // the slot we used via an index into locations[v.Type]. + // TODO: share slots among equivalent types. slots := make([]int, f.NumValues()) for i := f.NumValues() - 1; i >= 0; i-- { slots[i] = -1 @@ -82,6 +96,45 @@ func stackalloc(f *Func) { if phiArg[v.ID] { continue } + + // If this is a named value, try to use the name as + // the spill location. + var name GCNode + if v.Op == OpStoreReg { + name = names[v.Args[0].ID] + } else { + name = names[v.ID] + } + if name != nil && v.Type.Equal(name.Typ()) { + for _, id := range interfere[v.ID] { + h := f.getHome(id) + if h != nil && h.(*LocalSlot).N == name { + // A variable can interfere with itself. + // It is rare, but but it can happen. + goto noname + } + } + if v.Op == OpPhi { + for _, a := range v.Args { + for _, id := range interfere[a.ID] { + h := f.getHome(id) + if h != nil && h.(*LocalSlot).N == name { + goto noname + } + } + } + } + loc := &LocalSlot{name} + f.setHome(v, loc) + if v.Op == OpPhi { + for _, a := range v.Args { + f.setHome(a, loc) + } + } + continue + } + + noname: // Set of stack slots we could reuse. locs := locations[v.Type] // Mark all positions in locs used by interfering values. @@ -96,7 +149,7 @@ func stackalloc(f *Func) { } if v.Op == OpPhi { // Stack phi and args must get the same stack slot, so - // anything they interfere with is something v the phi + // anything the args interfere with is something the phi // interferes with. for _, a := range v.Args { for _, xid := range interfere[a.ID] { @@ -209,11 +262,11 @@ func (f *Func) liveSpills() [][][]ID { return live } -func (f *Func) getHome(v *Value) Location { - if int(v.ID) >= len(f.RegAlloc) { +func (f *Func) getHome(vid ID) Location { + if int(vid) >= len(f.RegAlloc) { return nil } - return f.RegAlloc[v.ID] + return f.RegAlloc[vid] } func (f *Func) setHome(v *Value, loc Location) { diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index a5915da025..661a05989a 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -142,15 +142,15 @@ type ExternSymbol struct { // ArgSymbol is an aux value that encodes an argument or result // variable's constant offset from FP (FP = SP + framesize). type ArgSymbol struct { - Typ Type // Go type - Node fmt.Stringer // A *gc.Node referring to the argument/result variable. + Typ Type // Go type + Node GCNode // A *gc.Node referring to the argument/result variable. } // AutoSymbol is an aux value that encodes a local variable's // constant offset from SP. type AutoSymbol struct { - Typ Type // Go type - Node fmt.Stringer // A *gc.Node referring to a local (auto) variable. + Typ Type // Go type + Node GCNode // A *gc.Node referring to a local (auto) variable. } func (s *ExternSymbol) String() string { -- cgit v1.3 From 02f4d0a130ba95d7a03418c3ef308d7d21b34af3 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 2 Nov 2015 08:10:26 -0800 Subject: [dev.ssa] cmd/compile: start arguments as spilled Declare a function's arguments as having already been spilled so their use just requires a restore. Allow spill locations to be portions of larger objects the stack. Required to load portions of compound input arguments. Rename the memory input to InputMem. Use Arg for the pre-spilled argument values. Change-Id: I8fe2a03ffbba1022d98bfae2052b376b96d32dda Reviewed-on: https://go-review.googlesource.com/16536 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: David Chase --- src/cmd/compile/internal/gc/pgen.go | 3 + src/cmd/compile/internal/gc/ssa.go | 54 +++++-- src/cmd/compile/internal/ssa/compile.go | 2 +- src/cmd/compile/internal/ssa/config.go | 2 +- src/cmd/compile/internal/ssa/deadcode.go | 34 ++-- src/cmd/compile/internal/ssa/deadcode_test.go | 8 +- src/cmd/compile/internal/ssa/deadstore_test.go | 6 +- src/cmd/compile/internal/ssa/decompose.go | 69 ++++++++- src/cmd/compile/internal/ssa/dom_test.go | 26 ++-- src/cmd/compile/internal/ssa/func.go | 7 +- src/cmd/compile/internal/ssa/func_test.go | 32 ++-- src/cmd/compile/internal/ssa/gen/generic.rules | 45 ++++-- src/cmd/compile/internal/ssa/gen/genericOps.go | 3 +- src/cmd/compile/internal/ssa/html.go | 4 + src/cmd/compile/internal/ssa/location.go | 14 +- src/cmd/compile/internal/ssa/lower.go | 2 +- src/cmd/compile/internal/ssa/nilcheck_test.go | 20 +-- src/cmd/compile/internal/ssa/opGen.go | 5 + src/cmd/compile/internal/ssa/print.go | 8 + src/cmd/compile/internal/ssa/regalloc.go | 10 ++ src/cmd/compile/internal/ssa/regalloc_test.go | 2 +- src/cmd/compile/internal/ssa/rewritegeneric.go | 206 +++++++++++++++++++++---- src/cmd/compile/internal/ssa/schedule_test.go | 2 +- src/cmd/compile/internal/ssa/shift_test.go | 2 +- src/cmd/compile/internal/ssa/stackalloc.go | 34 ++-- src/cmd/compile/internal/ssa/tighten.go | 4 +- src/runtime/runtime-gdb_test.go | 7 - 27 files changed, 470 insertions(+), 141 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 87e99df2e6..c8f2059543 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -484,6 +484,9 @@ func compile(fn *Node) { if ssafn != nil && usessa { genssa(ssafn, ptxt, gcargs, gclocals) + if Curfn.Func.Endlineno != 0 { + lineno = Curfn.Func.Endlineno + } return } Genlist(Curfn.Func.Enter) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 5a8e43dedb..2c935b7247 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -97,7 +97,7 @@ func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) { // Allocate starting values s.labels = map[string]*ssaLabel{} s.labeledNodes = map[*Node]*ssaLabel{} - s.startmem = s.entryNewValue0(ssa.OpArg, ssa.TypeMem) + s.startmem = s.entryNewValue0(ssa.OpInitMem, ssa.TypeMem) s.sp = s.entryNewValue0(ssa.OpSP, Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead s.sb = s.entryNewValue0(ssa.OpSB, Types[TUINTPTR]) @@ -3168,6 +3168,12 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name *Node) *ssa.Val if name == &memVar { return s.startmem } + if canSSA(name) { + v := s.entryNewValue0A(ssa.OpArg, t, name) + // v starts with AuxInt == 0. + s.addNamedValue(name, v) + return v + } // variable is live at the entry block. Load it. addr := s.decladdrs[name] if addr == nil { @@ -3239,18 +3245,21 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) { // Don't track autotmp_ variables. return } - if n.Class == PPARAM || n.Class == PPARAMOUT { - // TODO: Remove this + if n.Class == PAUTO && (v.Type.IsString() || v.Type.IsSlice() || v.Type.IsInterface()) { + // TODO: can't handle auto compound objects with pointers yet. + // The live variable analysis barfs because we don't put VARDEF + // pseudos in the right place when we spill to these nodes. return } if n.Class == PAUTO && n.Xoffset != 0 { s.Fatalf("AUTO var with offset %s %d", n, n.Xoffset) } - values, ok := s.f.NamedValues[n] + loc := ssa.LocalSlot{N: n, Type: n.Type, Off: 0} + values, ok := s.f.NamedValues[loc] if !ok { - s.f.Names = append(s.f.Names, n) + s.f.Names = append(s.f.Names, loc) } - s.f.NamedValues[n] = append(values, v) + s.f.NamedValues[loc] = append(values, v) } // an unresolved branch @@ -3873,11 +3882,17 @@ func (s *genState) genValue(v *ssa.Value) { return } p := Prog(movSizeByType(v.Type)) - n := autoVar(v.Args[0]) + n, off := autoVar(v.Args[0]) p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_AUTO p.From.Node = n p.From.Sym = Linksym(n.Sym) + p.From.Offset = off + if n.Class == PPARAM { + p.From.Name = obj.NAME_PARAM + p.From.Offset += n.Xoffset + } else { + p.From.Name = obj.NAME_AUTO + } p.To.Type = obj.TYPE_REG p.To.Reg = regnum(v) @@ -3889,11 +3904,17 @@ func (s *genState) genValue(v *ssa.Value) { p := Prog(movSizeByType(v.Type)) p.From.Type = obj.TYPE_REG p.From.Reg = regnum(v.Args[0]) - n := autoVar(v) + n, off := autoVar(v) p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_AUTO p.To.Node = n p.To.Sym = Linksym(n.Sym) + p.To.Offset = off + if n.Class == PPARAM { + p.To.Name = obj.NAME_PARAM + p.To.Offset += n.Xoffset + } else { + p.To.Name = obj.NAME_AUTO + } case ssa.OpPhi: // just check to make sure regalloc and stackalloc did it right if v.Type.IsMemory() { @@ -3912,9 +3933,10 @@ func (s *genState) genValue(v *ssa.Value) { v.Fatalf("const value %v shouldn't have a location", v) } - case ssa.OpArg: + case ssa.OpInitMem: // memory arg needs no code - // TODO: check that only mem arg goes here. + case ssa.OpArg: + // input args need no code case ssa.OpAMD64LoweredGetClosurePtr: // Output is hardwired to DX only, // and DX contains the closure pointer on @@ -4476,9 +4498,11 @@ func regnum(v *ssa.Value) int16 { return ssaRegToReg[reg.(*ssa.Register).Num] } -// autoVar returns a *Node representing the auto variable assigned to v. -func autoVar(v *ssa.Value) *Node { - return v.Block.Func.RegAlloc[v.ID].(*ssa.LocalSlot).N.(*Node) +// autoVar returns a *Node and int64 representing the auto variable and offset within it +// where v should be spilled. +func autoVar(v *ssa.Value) (*Node, int64) { + loc := v.Block.Func.RegAlloc[v.ID].(ssa.LocalSlot) + return loc.N.(*Node), loc.Off } // ssaExport exports a bunch of compiler services for the ssa backend. diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index af672eea99..01238f24ca 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -83,8 +83,8 @@ type pass struct { var passes = [...]pass{ {"phielim", phielim}, {"copyelim", copyelim}, - {"decompose", decompose}, {"early deadcode", deadcode}, // remove generated dead code to avoid doing pointless work during opt + {"decompose", decompose}, {"opt", opt}, {"opt deadcode", deadcode}, // remove any blocks orphaned during opt {"generic cse", cse}, diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 014c960267..6d3a949a6a 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -103,7 +103,7 @@ func (c *Config) Frontend() Frontend { return c.fe } // NewFunc returns a new, empty function object func (c *Config) NewFunc() *Func { // TODO(khr): should this function take name, type, etc. as arguments? - return &Func{Config: c, NamedValues: map[GCNode][]*Value{}} + return &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}} } func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) } diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index 3351589fda..e9d6525701 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -162,24 +162,38 @@ func deadcode(f *Func) { } f.Blocks = f.Blocks[:i] - // Remove dead entries from namedValues map. - for name, values := range f.NamedValues { - i := 0 + // Remove dead & duplicate entries from namedValues map. + s := newSparseSet(f.NumValues()) + i = 0 + for _, name := range f.Names { + j := 0 + s.clear() + values := f.NamedValues[name] for _, v := range values { for v.Op == OpCopy { v = v.Args[0] } - if live[v.ID] { - values[i] = v - i++ + if live[v.ID] && !s.contains(v.ID) { + values[j] = v + j++ + s.add(v.ID) } } - f.NamedValues[name] = values[:i] - tail := values[i:] - for j := range tail { - tail[j] = nil + if j == 0 { + delete(f.NamedValues, name) + } else { + f.Names[i] = name + i++ + for k := len(values) - 1; k >= j; k-- { + values[k] = nil + } + f.NamedValues[name] = values[:j] } } + for k := len(f.Names) - 1; k >= i; k-- { + f.Names[k] = LocalSlot{} + } + f.Names = f.Names[:i] // TODO: renumber Blocks and Values densely? // TODO: save dead Values and Blocks for reuse? Or should we just let GC handle it? diff --git a/src/cmd/compile/internal/ssa/deadcode_test.go b/src/cmd/compile/internal/ssa/deadcode_test.go index 7f491c77f9..c59d77ea60 100644 --- a/src/cmd/compile/internal/ssa/deadcode_test.go +++ b/src/cmd/compile/internal/ssa/deadcode_test.go @@ -10,7 +10,7 @@ func TestDeadLoop(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem")), @@ -40,7 +40,7 @@ func TestDeadValue(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("deadval", OpConst64, TypeInt64, 37, nil), Goto("exit")), Bloc("exit", @@ -64,7 +64,7 @@ func TestNeverTaken(t *testing.T) { fun := Fun(c, "entry", Bloc("entry", Valu("cond", OpConstBool, TypeBool, 0, nil), - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), If("cond", "then", "else")), Bloc("then", Goto("exit")), @@ -98,7 +98,7 @@ func TestNestedDeadBlocks(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("cond", OpConstBool, TypeBool, 0, nil), If("cond", "b2", "b4")), Bloc("b2", diff --git a/src/cmd/compile/internal/ssa/deadstore_test.go b/src/cmd/compile/internal/ssa/deadstore_test.go index 159ac4e439..4514c99004 100644 --- a/src/cmd/compile/internal/ssa/deadstore_test.go +++ b/src/cmd/compile/internal/ssa/deadstore_test.go @@ -12,7 +12,7 @@ func TestDeadStore(t *testing.T) { ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing fun := Fun(c, "entry", Bloc("entry", - Valu("start", OpArg, TypeMem, 0, ".mem"), + Valu("start", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("v", OpConstBool, TypeBool, 1, nil), Valu("addr1", OpAddr, ptrType, 0, nil, "sb"), @@ -47,7 +47,7 @@ func TestDeadStorePhi(t *testing.T) { ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing fun := Fun(c, "entry", Bloc("entry", - Valu("start", OpArg, TypeMem, 0, ".mem"), + Valu("start", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("v", OpConstBool, TypeBool, 1, nil), Valu("addr", OpAddr, ptrType, 0, nil, "sb"), @@ -74,7 +74,7 @@ func TestDeadStoreTypes(t *testing.T) { t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"} fun := Fun(c, "entry", Bloc("entry", - Valu("start", OpArg, TypeMem, 0, ".mem"), + Valu("start", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("v", OpConstBool, TypeBool, 1, nil), Valu("addr1", OpAddr, t1, 0, nil, "sb"), diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index 2057d8ea5c..c8a1df281a 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -29,8 +29,75 @@ func decompose(f *Func) { } } } - // TODO: decompose complex? // TODO: decompose 64-bit ops on 32-bit archs? + + // Split up named values into their components. + // NOTE: the component values we are making are dead at this point. + // We must do the opt pass before any deadcode elimination or we will + // lose the name->value correspondence. + for _, name := range f.Names { + t := name.Type + switch { + case t.IsComplex(): + var elemType Type + if t.Size() == 16 { + elemType = f.Config.fe.TypeFloat64() + } else { + elemType = f.Config.fe.TypeFloat32() + } + rName := LocalSlot{name.N, elemType, name.Off} + iName := LocalSlot{name.N, elemType, name.Off + elemType.Size()} + f.Names = append(f.Names, rName, iName) + for _, v := range f.NamedValues[name] { + r := v.Block.NewValue1(v.Line, OpComplexReal, elemType, v) + i := v.Block.NewValue1(v.Line, OpComplexImag, elemType, v) + f.NamedValues[rName] = append(f.NamedValues[rName], r) + f.NamedValues[iName] = append(f.NamedValues[iName], i) + } + case t.IsString(): + ptrType := f.Config.fe.TypeBytePtr() + lenType := f.Config.fe.TypeInt() + ptrName := LocalSlot{name.N, ptrType, name.Off} + lenName := LocalSlot{name.N, lenType, name.Off + f.Config.PtrSize} + f.Names = append(f.Names, ptrName, lenName) + for _, v := range f.NamedValues[name] { + ptr := v.Block.NewValue1(v.Line, OpStringPtr, ptrType, v) + len := v.Block.NewValue1(v.Line, OpStringLen, lenType, v) + f.NamedValues[ptrName] = append(f.NamedValues[ptrName], ptr) + f.NamedValues[lenName] = append(f.NamedValues[lenName], len) + } + case t.IsSlice(): + ptrType := f.Config.fe.TypeBytePtr() + lenType := f.Config.fe.TypeInt() + ptrName := LocalSlot{name.N, ptrType, name.Off} + lenName := LocalSlot{name.N, lenType, name.Off + f.Config.PtrSize} + capName := LocalSlot{name.N, lenType, name.Off + 2*f.Config.PtrSize} + f.Names = append(f.Names, ptrName, lenName, capName) + for _, v := range f.NamedValues[name] { + ptr := v.Block.NewValue1(v.Line, OpSlicePtr, ptrType, v) + len := v.Block.NewValue1(v.Line, OpSliceLen, lenType, v) + cap := v.Block.NewValue1(v.Line, OpSliceCap, lenType, v) + f.NamedValues[ptrName] = append(f.NamedValues[ptrName], ptr) + f.NamedValues[lenName] = append(f.NamedValues[lenName], len) + f.NamedValues[capName] = append(f.NamedValues[capName], cap) + } + case t.IsInterface(): + ptrType := f.Config.fe.TypeBytePtr() + typeName := LocalSlot{name.N, ptrType, name.Off} + dataName := LocalSlot{name.N, ptrType, name.Off + f.Config.PtrSize} + f.Names = append(f.Names, typeName, dataName) + for _, v := range f.NamedValues[name] { + typ := v.Block.NewValue1(v.Line, OpITab, ptrType, v) + data := v.Block.NewValue1(v.Line, OpIData, ptrType, v) + f.NamedValues[typeName] = append(f.NamedValues[typeName], typ) + f.NamedValues[dataName] = append(f.NamedValues[dataName], data) + } + //case t.IsStruct(): + // TODO + case t.Size() > f.Config.IntSize: + f.Unimplementedf("undecomposed type %s", t) + } + } } func decomposeStringPhi(v *Value) { diff --git a/src/cmd/compile/internal/ssa/dom_test.go b/src/cmd/compile/internal/ssa/dom_test.go index eff7205fa3..84e0093799 100644 --- a/src/cmd/compile/internal/ssa/dom_test.go +++ b/src/cmd/compile/internal/ssa/dom_test.go @@ -20,7 +20,7 @@ func genLinear(size int) []bloc { var blocs []bloc blocs = append(blocs, Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto(blockn(0)), ), ) @@ -43,7 +43,7 @@ func genFwdBack(size int) []bloc { var blocs []bloc blocs = append(blocs, Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), Goto(blockn(0)), ), @@ -73,7 +73,7 @@ func genManyPred(size int) []bloc { var blocs []bloc blocs = append(blocs, Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), Goto(blockn(0)), ), @@ -111,7 +111,7 @@ func genMaxPred(size int) []bloc { var blocs []bloc blocs = append(blocs, Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), Goto(blockn(0)), ), @@ -136,7 +136,7 @@ func genMaxPredValue(size int) []bloc { var blocs []bloc blocs = append(blocs, Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), Goto(blockn(0)), ), @@ -223,7 +223,7 @@ func TestDominatorsSingleBlock(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Exit("mem"))) doms := map[string]string{} @@ -238,7 +238,7 @@ func TestDominatorsSimple(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("a")), Bloc("a", Goto("b")), @@ -266,7 +266,7 @@ func TestDominatorsMultPredFwd(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), If("p", "a", "c")), Bloc("a", @@ -294,7 +294,7 @@ func TestDominatorsDeadCode(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 0, nil), If("p", "b3", "b5")), Bloc("b2", Exit("mem")), @@ -319,7 +319,7 @@ func TestDominatorsMultPredRev(t *testing.T) { Bloc("entry", Goto("first")), Bloc("first", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), Goto("a")), Bloc("a", @@ -348,7 +348,7 @@ func TestDominatorsMultPred(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), If("p", "a", "c")), Bloc("a", @@ -376,7 +376,7 @@ func TestPostDominators(t *testing.T) { c := testConfig(t) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), If("p", "a", "c")), Bloc("a", @@ -403,7 +403,7 @@ func TestInfiniteLoop(t *testing.T) { // note lack of an exit block fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("p", OpConstBool, TypeBool, 1, nil), Goto("a")), Bloc("a", diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index ce11b184f6..e5fbfdb5ff 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -26,12 +26,11 @@ type Func struct { // when register allocation is done, maps value ids to locations RegAlloc []Location - // map from *gc.Node to set of Values that represent that Node. - // The Node must be an ONAME with PPARAM, PPARAMOUT, or PAUTO class. - NamedValues map[GCNode][]*Value + // map from LocalSlot to set of Values that we want to store in that slot. + NamedValues map[LocalSlot][]*Value // Names is a copy of NamedValues.Keys. We keep a separate list // of keys to make iteration order deterministic. - Names []GCNode + Names []LocalSlot } // NumBlocks returns an integer larger than the id of any Block in the Func. diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index dc5d220db8..d35690a30c 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -18,7 +18,7 @@ // // fun := Fun("entry", // Bloc("entry", -// Valu("mem", OpArg, TypeMem, 0, ".mem"), +// Valu("mem", OpInitMem, TypeMem, 0, ".mem"), // Goto("exit")), // Bloc("exit", // Exit("mem")), @@ -263,7 +263,7 @@ func TestArgs(t *testing.T) { Valu("a", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 26, nil), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))) @@ -286,7 +286,7 @@ func TestEquiv(t *testing.T) { Valu("a", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 26, nil), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), @@ -295,7 +295,7 @@ func TestEquiv(t *testing.T) { Valu("a", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 26, nil), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), @@ -307,7 +307,7 @@ func TestEquiv(t *testing.T) { Valu("a", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 26, nil), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), @@ -318,7 +318,7 @@ func TestEquiv(t *testing.T) { Valu("a", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 26, nil), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("exit"))), }, } @@ -335,26 +335,26 @@ func TestEquiv(t *testing.T) { { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Goto("exit")), Bloc("exit", Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Exit("mem"))), }, // value order changed { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("b", OpConst64, TypeInt64, 26, nil), Valu("a", OpConst64, TypeInt64, 14, nil), Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("a", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 26, nil), Exit("mem"))), @@ -363,12 +363,12 @@ func TestEquiv(t *testing.T) { { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("a", OpConst64, TypeInt64, 14, nil), Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("a", OpConst64, TypeInt64, 26, nil), Exit("mem"))), }, @@ -376,12 +376,12 @@ func TestEquiv(t *testing.T) { { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("a", OpConst64, TypeInt64, 0, 14), Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("a", OpConst64, TypeInt64, 0, 26), Exit("mem"))), }, @@ -389,14 +389,14 @@ func TestEquiv(t *testing.T) { { Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("a", OpConst64, TypeInt64, 14, nil), Valu("b", OpConst64, TypeInt64, 26, nil), Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), Exit("mem"))), Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("a", OpConst64, TypeInt64, 0, nil), Valu("b", OpConst64, TypeInt64, 14, nil), Valu("sum", OpAdd64, TypeInt64, 0, nil, "b", "a"), diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index bb347aea8b..9c1da92b7e 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -188,12 +188,12 @@ (Load ptr mem) && t.IsString() -> (StringMake (Load ptr mem) - (Load - (OffPtr [config.PtrSize] ptr) + (Load + (OffPtr [config.PtrSize] ptr) mem)) (Store [2*config.PtrSize] dst (StringMake ptr len) mem) -> (Store [config.PtrSize] - (OffPtr [config.PtrSize] dst) + (OffPtr [config.PtrSize] dst) len (Store [config.PtrSize] dst ptr mem)) @@ -215,18 +215,18 @@ (Load ptr mem) && t.IsSlice() -> (SliceMake (Load ptr mem) - (Load - (OffPtr [config.PtrSize] ptr) + (Load + (OffPtr [config.PtrSize] ptr) mem) - (Load - (OffPtr [2*config.PtrSize] ptr) + (Load + (OffPtr [2*config.PtrSize] ptr) mem)) (Store [3*config.PtrSize] dst (SliceMake ptr len cap) mem) -> (Store [config.PtrSize] - (OffPtr [2*config.PtrSize] dst) + (OffPtr [2*config.PtrSize] dst) cap (Store [config.PtrSize] - (OffPtr [config.PtrSize] dst) + (OffPtr [config.PtrSize] dst) len (Store [config.PtrSize] dst ptr mem))) @@ -261,3 +261,30 @@ // Get rid of Convert ops for pointer arithmetic on unsafe.Pointer. (Convert (Add64 (Convert ptr) off)) -> (Add64 ptr off) + +// Decompose compound argument values +(Arg {n} [off]) && v.Type.IsString() -> + (StringMake + (Arg {n} [off]) + (Arg {n} [off+config.PtrSize])) + +(Arg {n} [off]) && v.Type.IsSlice() -> + (SliceMake + (Arg {n} [off]) + (Arg {n} [off+config.PtrSize]) + (Arg {n} [off+2*config.PtrSize])) + +(Arg {n} [off]) && v.Type.IsInterface() -> + (IMake + (Arg {n} [off]) + (Arg {n} [off+config.PtrSize])) + +(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 16 -> + (ComplexMake + (Arg {n} [off]) + (Arg {n} [off+8])) + +(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 8 -> + (ComplexMake + (Arg {n} [off]) + (Arg {n} [off+4])) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 162ee0dab4..8eb10a7d9b 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -260,7 +260,8 @@ var genericOps = []opData{ // TODO: Const32F, ... // Constant-like things - {name: "Arg"}, // memory input to the function. + {name: "InitMem"}, // memory input to the function. + {name: "Arg"}, // argument to the function. aux=GCNode of arg, off = offset in that arg. // The address of a variable. arg0 is the base pointer (SB or SP, depending // on whether it is a global or stack variable). The Aux field identifies the diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 68a432c676..9b8fc3750b 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -472,3 +472,7 @@ func (p htmlFuncPrinter) startDepCycle() { func (p htmlFuncPrinter) endDepCycle() { fmt.Fprintln(p.w, "") } + +func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) { + // TODO +} diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go index 0f9fb33eeb..85f525565b 100644 --- a/src/cmd/compile/internal/ssa/location.go +++ b/src/cmd/compile/internal/ssa/location.go @@ -4,6 +4,8 @@ package ssa +import "fmt" + // A place that an ssa variable can reside. type Location interface { Name() string // name to use in assembly templates: %rax, 16(%rsp), ... @@ -21,10 +23,16 @@ func (r *Register) Name() string { } // A LocalSlot is a location in the stack frame. +// It is (possibly a subpiece of) a PPARAM, PPARAMOUT, or PAUTO ONAME node. type LocalSlot struct { - N GCNode // a *gc.Node for an auto variable + N GCNode // an ONAME *gc.Node representing a variable on the stack + Type Type // type of slot + Off int64 // offset of slot in N } -func (s *LocalSlot) Name() string { - return s.N.String() +func (s LocalSlot) Name() string { + if s.Off == 0 { + return fmt.Sprintf("%s[%s]", s.N, s.Type) + } + return fmt.Sprintf("%s+%d[%s]", s.N, s.Off, s.Type) } diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index 9c28bd10a5..bf3c15f78b 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -21,7 +21,7 @@ func checkLower(f *Func) { continue // lowered } switch v.Op { - case OpSP, OpSB, OpArg, OpCopy, OpPhi, OpVarDef, OpVarKill: + case OpSP, OpSB, OpInitMem, OpArg, OpCopy, OpPhi, OpVarDef, OpVarKill: continue // ok not to lower } s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString() diff --git a/src/cmd/compile/internal/ssa/nilcheck_test.go b/src/cmd/compile/internal/ssa/nilcheck_test.go index 8f32f32b1d..d4a55c0855 100644 --- a/src/cmd/compile/internal/ssa/nilcheck_test.go +++ b/src/cmd/compile/internal/ssa/nilcheck_test.go @@ -21,7 +21,7 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) { var blocs []bloc blocs = append(blocs, Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto(blockn(0)), ), @@ -67,7 +67,7 @@ func TestNilcheckSimple(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", @@ -104,7 +104,7 @@ func TestNilcheckDomOrder(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", @@ -140,7 +140,7 @@ func TestNilcheckAddr(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", @@ -173,7 +173,7 @@ func TestNilcheckAddPtr(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", @@ -207,7 +207,7 @@ func TestNilcheckPhi(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Valu("sp", OpSP, TypeInvalid, 0, nil), Valu("baddr", OpAddr, TypeBool, 0, "b", "sp"), @@ -251,7 +251,7 @@ func TestNilcheckKeepRemove(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", @@ -299,7 +299,7 @@ func TestNilcheckInFalseBranch(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", @@ -350,7 +350,7 @@ func TestNilcheckUser(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", @@ -389,7 +389,7 @@ func TestNilcheckBug(t *testing.T) { c := NewConfig("amd64", DummyFrontend{t}, nil) fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("sb", OpSB, TypeInvalid, 0, nil), Goto("checkPtr")), Bloc("checkPtr", diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 400f59e174..d043e076ea 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -475,6 +475,7 @@ const ( OpConst64F OpConstInterface OpConstSlice + OpInitMem OpArg OpAddr OpSP @@ -3987,6 +3988,10 @@ var opcodeTable = [...]opInfo{ name: "ConstSlice", generic: true, }, + { + name: "InitMem", + generic: true, + }, { name: "Arg", generic: true, diff --git a/src/cmd/compile/internal/ssa/print.go b/src/cmd/compile/internal/ssa/print.go index 192dc83b39..b61e6f1cc7 100644 --- a/src/cmd/compile/internal/ssa/print.go +++ b/src/cmd/compile/internal/ssa/print.go @@ -28,6 +28,7 @@ type funcPrinter interface { value(v *Value, live bool) startDepCycle() endDepCycle() + named(n LocalSlot, vals []*Value) } type stringFuncPrinter struct { @@ -73,6 +74,10 @@ func (p stringFuncPrinter) startDepCycle() { func (p stringFuncPrinter) endDepCycle() {} +func (p stringFuncPrinter) named(n LocalSlot, vals []*Value) { + fmt.Fprintf(p.w, "name %s: %v\n", n.Name(), vals) +} + func fprintFunc(p funcPrinter, f *Func) { reachable, live := findlive(f) p.header(f) @@ -136,4 +141,7 @@ func fprintFunc(p funcPrinter, f *Func) { p.endBlock(b) } + for name, vals := range f.NamedValues { + p.named(name, vals) + } } diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 89deb14a4a..a751d66988 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -759,6 +759,16 @@ func (s *regAllocState) regalloc(f *Func) { pc++ continue } + if v.Op == OpArg { + // Args are "pre-spilled" values. We don't allocate + // any register here. We just set up the spill pointer to + // point at itself and any later user will restore it to use it. + s.values[v.ID].spill = v + s.values[v.ID].spillUsed = true // use is guaranteed + b.Values = append(b.Values, v) + pc++ + continue + } s.clearUses(pc*2 - 1) regspec := opcodeTable[v.Op].reg if regDebug { diff --git a/src/cmd/compile/internal/ssa/regalloc_test.go b/src/cmd/compile/internal/ssa/regalloc_test.go index dcd253ea14..08260fbbbb 100644 --- a/src/cmd/compile/internal/ssa/regalloc_test.go +++ b/src/cmd/compile/internal/ssa/regalloc_test.go @@ -10,7 +10,7 @@ func TestLiveControlOps(t *testing.T) { c := testConfig(t) f := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("x", OpAMD64MOVBconst, TypeInt8, 0, 1), Valu("y", OpAMD64MOVBconst, TypeInt8, 0, 2), Valu("a", OpAMD64TESTB, TypeBool, 0, nil, "x", "y"), diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 2448b43547..c349603583 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -23,6 +23,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool { return rewriteValuegeneric_OpAnd64(v, config) case OpAnd8: return rewriteValuegeneric_OpAnd8(v, config) + case OpArg: + return rewriteValuegeneric_OpArg(v, config) case OpArrayIndex: return rewriteValuegeneric_OpArrayIndex(v, config) case OpCom16: @@ -402,6 +404,156 @@ endeaf127389bd0d4b0e0e297830f8f463b: ; return false } +func rewriteValuegeneric_OpArg(v *Value, config *Config) bool { + b := v.Block + _ = b + // match: (Arg {n} [off]) + // cond: v.Type.IsString() + // result: (StringMake (Arg {n} [off]) (Arg {n} [off+config.PtrSize])) + { + n := v.Aux + off := v.AuxInt + if !(v.Type.IsString()) { + goto end939d3f946bf61eb85b46b374e7afa9e9 + } + v.Op = OpStringMake + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v0.Type = config.fe.TypeBytePtr() + v0.Aux = n + v0.AuxInt = off + v.AddArg(v0) + v1 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v1.Type = config.fe.TypeInt() + v1.Aux = n + v1.AuxInt = off + config.PtrSize + v.AddArg(v1) + return true + } + goto end939d3f946bf61eb85b46b374e7afa9e9 +end939d3f946bf61eb85b46b374e7afa9e9: + ; + // match: (Arg {n} [off]) + // cond: v.Type.IsSlice() + // result: (SliceMake (Arg {n} [off]) (Arg {n} [off+config.PtrSize]) (Arg {n} [off+2*config.PtrSize])) + { + n := v.Aux + off := v.AuxInt + if !(v.Type.IsSlice()) { + goto endab4b93ad3b1cf55e5bf25d1fd9cd498e + } + v.Op = OpSliceMake + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v0.Type = config.fe.TypeBytePtr() + v0.Aux = n + v0.AuxInt = off + v.AddArg(v0) + v1 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v1.Type = config.fe.TypeInt() + v1.Aux = n + v1.AuxInt = off + config.PtrSize + v.AddArg(v1) + v2 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v2.Type = config.fe.TypeInt() + v2.Aux = n + v2.AuxInt = off + 2*config.PtrSize + v.AddArg(v2) + return true + } + goto endab4b93ad3b1cf55e5bf25d1fd9cd498e +endab4b93ad3b1cf55e5bf25d1fd9cd498e: + ; + // match: (Arg {n} [off]) + // cond: v.Type.IsInterface() + // result: (IMake (Arg {n} [off]) (Arg {n} [off+config.PtrSize])) + { + n := v.Aux + off := v.AuxInt + if !(v.Type.IsInterface()) { + goto end851de8e588a39e81b4e2aef06566bf3e + } + v.Op = OpIMake + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v0.Type = config.fe.TypeBytePtr() + v0.Aux = n + v0.AuxInt = off + v.AddArg(v0) + v1 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v1.Type = config.fe.TypeBytePtr() + v1.Aux = n + v1.AuxInt = off + config.PtrSize + v.AddArg(v1) + return true + } + goto end851de8e588a39e81b4e2aef06566bf3e +end851de8e588a39e81b4e2aef06566bf3e: + ; + // match: (Arg {n} [off]) + // cond: v.Type.IsComplex() && v.Type.Size() == 16 + // result: (ComplexMake (Arg {n} [off]) (Arg {n} [off+8])) + { + n := v.Aux + off := v.AuxInt + if !(v.Type.IsComplex() && v.Type.Size() == 16) { + goto end0988fc6a62c810b2f4976cb6cf44387f + } + v.Op = OpComplexMake + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v0.Type = config.fe.TypeFloat64() + v0.Aux = n + v0.AuxInt = off + v.AddArg(v0) + v1 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v1.Type = config.fe.TypeFloat64() + v1.Aux = n + v1.AuxInt = off + 8 + v.AddArg(v1) + return true + } + goto end0988fc6a62c810b2f4976cb6cf44387f +end0988fc6a62c810b2f4976cb6cf44387f: + ; + // match: (Arg {n} [off]) + // cond: v.Type.IsComplex() && v.Type.Size() == 8 + // result: (ComplexMake (Arg {n} [off]) (Arg {n} [off+4])) + { + n := v.Aux + off := v.AuxInt + if !(v.Type.IsComplex() && v.Type.Size() == 8) { + goto enda348e93e0036873dd7089a2939c22e3e + } + v.Op = OpComplexMake + v.AuxInt = 0 + v.Aux = nil + v.resetArgs() + v0 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v0.Type = config.fe.TypeFloat32() + v0.Aux = n + v0.AuxInt = off + v.AddArg(v0) + v1 := b.NewValue0(v.Line, OpArg, TypeInvalid) + v1.Type = config.fe.TypeFloat32() + v1.Aux = n + v1.AuxInt = off + 4 + v.AddArg(v1) + return true + } + goto enda348e93e0036873dd7089a2939c22e3e +enda348e93e0036873dd7089a2939c22e3e: + ; + return false +} func rewriteValuegeneric_OpArrayIndex(v *Value, config *Config) bool { b := v.Block _ = b @@ -2115,13 +2267,13 @@ end1b106f89e0e3e26c613b957a7c98d8ad: ; // match: (Load ptr mem) // cond: t.IsString() - // result: (StringMake (Load ptr mem) (Load (OffPtr [config.PtrSize] ptr) mem)) + // result: (StringMake (Load ptr mem) (Load (OffPtr [config.PtrSize] ptr) mem)) { t := v.Type ptr := v.Args[0] mem := v.Args[1] if !(t.IsString()) { - goto end7c75255555bf9dd796298d9f6eaf9cf2 + goto enddd15a6f3d53a6ce7a19d4e181dd1c13a } v.Op = OpStringMake v.AuxInt = 0 @@ -2133,9 +2285,9 @@ end1b106f89e0e3e26c613b957a7c98d8ad: v0.AddArg(mem) v.AddArg(v0) v1 := b.NewValue0(v.Line, OpLoad, TypeInvalid) - v1.Type = config.fe.TypeUintptr() + v1.Type = config.fe.TypeInt() v2 := b.NewValue0(v.Line, OpOffPtr, TypeInvalid) - v2.Type = config.fe.TypeUintptr().PtrTo() + v2.Type = config.fe.TypeInt().PtrTo() v2.AuxInt = config.PtrSize v2.AddArg(ptr) v1.AddArg(v2) @@ -2143,18 +2295,18 @@ end1b106f89e0e3e26c613b957a7c98d8ad: v.AddArg(v1) return true } - goto end7c75255555bf9dd796298d9f6eaf9cf2 -end7c75255555bf9dd796298d9f6eaf9cf2: + goto enddd15a6f3d53a6ce7a19d4e181dd1c13a +enddd15a6f3d53a6ce7a19d4e181dd1c13a: ; // match: (Load ptr mem) // cond: t.IsSlice() - // result: (SliceMake (Load ptr mem) (Load (OffPtr [config.PtrSize] ptr) mem) (Load (OffPtr [2*config.PtrSize] ptr) mem)) + // result: (SliceMake (Load ptr mem) (Load (OffPtr [config.PtrSize] ptr) mem) (Load (OffPtr [2*config.PtrSize] ptr) mem)) { t := v.Type ptr := v.Args[0] mem := v.Args[1] if !(t.IsSlice()) { - goto end12c46556d962198680eb3238859e3016 + goto end65e8b0055aa7491b9b6066d9fe1b2c13 } v.Op = OpSliceMake v.AuxInt = 0 @@ -2166,18 +2318,18 @@ end7c75255555bf9dd796298d9f6eaf9cf2: v0.AddArg(mem) v.AddArg(v0) v1 := b.NewValue0(v.Line, OpLoad, TypeInvalid) - v1.Type = config.fe.TypeUintptr() + v1.Type = config.fe.TypeInt() v2 := b.NewValue0(v.Line, OpOffPtr, TypeInvalid) - v2.Type = config.fe.TypeUintptr().PtrTo() + v2.Type = config.fe.TypeInt().PtrTo() v2.AuxInt = config.PtrSize v2.AddArg(ptr) v1.AddArg(v2) v1.AddArg(mem) v.AddArg(v1) v3 := b.NewValue0(v.Line, OpLoad, TypeInvalid) - v3.Type = config.fe.TypeUintptr() + v3.Type = config.fe.TypeInt() v4 := b.NewValue0(v.Line, OpOffPtr, TypeInvalid) - v4.Type = config.fe.TypeUintptr().PtrTo() + v4.Type = config.fe.TypeInt().PtrTo() v4.AuxInt = 2 * config.PtrSize v4.AddArg(ptr) v3.AddArg(v4) @@ -2185,8 +2337,8 @@ end7c75255555bf9dd796298d9f6eaf9cf2: v.AddArg(v3) return true } - goto end12c46556d962198680eb3238859e3016 -end12c46556d962198680eb3238859e3016: + goto end65e8b0055aa7491b9b6066d9fe1b2c13 +end65e8b0055aa7491b9b6066d9fe1b2c13: ; // match: (Load ptr mem) // cond: t.IsInterface() @@ -2916,14 +3068,14 @@ end3851a482d7bd37a93c4d81581e85b3ab: ; // match: (Store [2*config.PtrSize] dst (StringMake ptr len) mem) // cond: - // result: (Store [config.PtrSize] (OffPtr [config.PtrSize] dst) len (Store [config.PtrSize] dst ptr mem)) + // result: (Store [config.PtrSize] (OffPtr [config.PtrSize] dst) len (Store [config.PtrSize] dst ptr mem)) { if v.AuxInt != 2*config.PtrSize { - goto end12abe4021d24e76ed56d64b18730bffb + goto endd3a6ecebdad5899570a79fe5c62f34f1 } dst := v.Args[0] if v.Args[1].Op != OpStringMake { - goto end12abe4021d24e76ed56d64b18730bffb + goto endd3a6ecebdad5899570a79fe5c62f34f1 } ptr := v.Args[1].Args[0] len := v.Args[1].Args[1] @@ -2934,7 +3086,7 @@ end3851a482d7bd37a93c4d81581e85b3ab: v.resetArgs() v.AuxInt = config.PtrSize v0 := b.NewValue0(v.Line, OpOffPtr, TypeInvalid) - v0.Type = config.fe.TypeUintptr().PtrTo() + v0.Type = config.fe.TypeInt().PtrTo() v0.AuxInt = config.PtrSize v0.AddArg(dst) v.AddArg(v0) @@ -2948,19 +3100,19 @@ end3851a482d7bd37a93c4d81581e85b3ab: v.AddArg(v1) return true } - goto end12abe4021d24e76ed56d64b18730bffb -end12abe4021d24e76ed56d64b18730bffb: + goto endd3a6ecebdad5899570a79fe5c62f34f1 +endd3a6ecebdad5899570a79fe5c62f34f1: ; // match: (Store [3*config.PtrSize] dst (SliceMake ptr len cap) mem) // cond: - // result: (Store [config.PtrSize] (OffPtr [2*config.PtrSize] dst) cap (Store [config.PtrSize] (OffPtr [config.PtrSize] dst) len (Store [config.PtrSize] dst ptr mem))) + // result: (Store [config.PtrSize] (OffPtr [2*config.PtrSize] dst) cap (Store [config.PtrSize] (OffPtr [config.PtrSize] dst) len (Store [config.PtrSize] dst ptr mem))) { if v.AuxInt != 3*config.PtrSize { - goto end7498d25e17db5398cf073a8590e35cc2 + goto endd5cc8c3dad7d24c845b0b88fc51487ae } dst := v.Args[0] if v.Args[1].Op != OpSliceMake { - goto end7498d25e17db5398cf073a8590e35cc2 + goto endd5cc8c3dad7d24c845b0b88fc51487ae } ptr := v.Args[1].Args[0] len := v.Args[1].Args[1] @@ -2972,7 +3124,7 @@ end12abe4021d24e76ed56d64b18730bffb: v.resetArgs() v.AuxInt = config.PtrSize v0 := b.NewValue0(v.Line, OpOffPtr, TypeInvalid) - v0.Type = config.fe.TypeUintptr().PtrTo() + v0.Type = config.fe.TypeInt().PtrTo() v0.AuxInt = 2 * config.PtrSize v0.AddArg(dst) v.AddArg(v0) @@ -2980,7 +3132,7 @@ end12abe4021d24e76ed56d64b18730bffb: v1 := b.NewValue0(v.Line, OpStore, TypeInvalid) v1.AuxInt = config.PtrSize v2 := b.NewValue0(v.Line, OpOffPtr, TypeInvalid) - v2.Type = config.fe.TypeUintptr().PtrTo() + v2.Type = config.fe.TypeInt().PtrTo() v2.AuxInt = config.PtrSize v2.AddArg(dst) v1.AddArg(v2) @@ -2996,8 +3148,8 @@ end12abe4021d24e76ed56d64b18730bffb: v.AddArg(v1) return true } - goto end7498d25e17db5398cf073a8590e35cc2 -end7498d25e17db5398cf073a8590e35cc2: + goto endd5cc8c3dad7d24c845b0b88fc51487ae +endd5cc8c3dad7d24c845b0b88fc51487ae: ; // match: (Store [2*config.PtrSize] dst (IMake itab data) mem) // cond: diff --git a/src/cmd/compile/internal/ssa/schedule_test.go b/src/cmd/compile/internal/ssa/schedule_test.go index 7f62ab9e3b..30c029ef7c 100644 --- a/src/cmd/compile/internal/ssa/schedule_test.go +++ b/src/cmd/compile/internal/ssa/schedule_test.go @@ -11,7 +11,7 @@ func TestSchedule(t *testing.T) { cases := []fun{ Fun(c, "entry", Bloc("entry", - Valu("mem0", OpArg, TypeMem, 0, ".mem"), + Valu("mem0", OpInitMem, TypeMem, 0, ".mem"), Valu("ptr", OpConst64, TypeInt64, 0xABCD, nil), Valu("v", OpConst64, TypeInt64, 12, nil), Valu("mem1", OpStore, TypeMem, 8, nil, "ptr", "v", "mem0"), diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go index 611b418b6d..9e7f0585a6 100644 --- a/src/cmd/compile/internal/ssa/shift_test.go +++ b/src/cmd/compile/internal/ssa/shift_test.go @@ -28,7 +28,7 @@ func makeConstShiftFunc(c *Config, amount int64, op Op, typ Type) fun { ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"} fun := Fun(c, "entry", Bloc("entry", - Valu("mem", OpArg, TypeMem, 0, ".mem"), + Valu("mem", OpInitMem, TypeMem, 0, ".mem"), Valu("SP", OpSP, TypeUInt64, 0, nil), Valu("argptr", OpOffPtr, ptyp, 8, nil, "SP"), Valu("resptr", OpOffPtr, ptyp, 16, nil, "SP"), diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 793162a797..3eb5c3cf4a 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -44,6 +44,13 @@ func stackalloc(f *Func) { } case v.Op == OpLoadReg: s.add(v.Args[0].ID) + case v.Op == OpArg: + // This is an input argument which is pre-spilled. It is kind of + // like a StoreReg, but we don't remove v.ID here because we want + // this value to appear live even before this point. Being live + // all the way to the start of the entry block prevents other + // values from being allocated to the same slot and clobbering + // the input value before we have a chance to load it. } } } @@ -51,7 +58,7 @@ func stackalloc(f *Func) { // Build map from values to their names, if any. // A value may be associated with more than one name (e.g. after // the assignment i=j). This step picks one name per value arbitrarily. - names := make([]GCNode, f.NumValues()) + names := make([]LocalSlot, f.NumValues()) for _, name := range f.Names { // Note: not "range f.NamedValues" above, because // that would be nondeterministic. @@ -74,9 +81,17 @@ func stackalloc(f *Func) { } } + // Allocate args to their assigned locations. + for _, v := range f.Entry.Values { + if v.Op != OpArg { + continue + } + f.setHome(v, LocalSlot{v.Aux.(GCNode), v.Type, v.AuxInt}) + } + // For each type, we keep track of all the stack slots we // have allocated for that type. - locations := map[Type][]*LocalSlot{} + locations := map[Type][]LocalSlot{} // Each time we assign a stack slot to a value v, we remember // the slot we used via an index into locations[v.Type]. @@ -99,16 +114,16 @@ func stackalloc(f *Func) { // If this is a named value, try to use the name as // the spill location. - var name GCNode + var name LocalSlot if v.Op == OpStoreReg { name = names[v.Args[0].ID] } else { name = names[v.ID] } - if name != nil && v.Type.Equal(name.Typ()) { + if name.N != nil && v.Type.Equal(name.Type) { for _, id := range interfere[v.ID] { h := f.getHome(id) - if h != nil && h.(*LocalSlot).N == name { + if h != nil && h.(LocalSlot) == name { // A variable can interfere with itself. // It is rare, but but it can happen. goto noname @@ -118,17 +133,16 @@ func stackalloc(f *Func) { for _, a := range v.Args { for _, id := range interfere[a.ID] { h := f.getHome(id) - if h != nil && h.(*LocalSlot).N == name { + if h != nil && h.(LocalSlot) == name { goto noname } } } } - loc := &LocalSlot{name} - f.setHome(v, loc) + f.setHome(v, name) if v.Op == OpPhi { for _, a := range v.Args { - f.setHome(a, loc) + f.setHome(a, name) } } continue @@ -169,7 +183,7 @@ func stackalloc(f *Func) { } // If there is no unused stack slot, allocate a new one. if i == len(locs) { - locs = append(locs, &LocalSlot{f.Config.fe.Auto(v.Type)}) + locs = append(locs, LocalSlot{N: f.Config.fe.Auto(v.Type), Type: v.Type, Off: 0}) locations[v.Type] = locs } // Use the stack variable at that index for v. diff --git a/src/cmd/compile/internal/ssa/tighten.go b/src/cmd/compile/internal/ssa/tighten.go index 4fa26d2d18..6726c06e76 100644 --- a/src/cmd/compile/internal/ssa/tighten.go +++ b/src/cmd/compile/internal/ssa/tighten.go @@ -54,8 +54,8 @@ func tighten(f *Func) { for _, b := range f.Blocks { for i := 0; i < len(b.Values); i++ { v := b.Values[i] - if v.Op == OpPhi || v.Op == OpGetClosurePtr || v.Op == OpConvert { - // GetClosurePtr must stay in entry block. + if v.Op == OpPhi || v.Op == OpGetClosurePtr || v.Op == OpConvert || v.Op == OpArg { + // GetClosurePtr & Arg must stay in entry block. // OpConvert must not float over call sites. // TODO do we instead need a dependence edge of some sort for OpConvert? // Would memory do the trick, or do we need something else that relates diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 2843633ee1..48567f1b9c 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -94,9 +94,6 @@ func TestGdbPython(t *testing.T) { "-ex", "echo END\n", "-ex", "echo BEGIN print strvar\n", "-ex", "print strvar", - "-ex", "echo END\n", - "-ex", "echo BEGIN print ptrvar\n", - "-ex", "print ptrvar", "-ex", "echo END\n"} // without framepointer, gdb cannot backtrace our non-standard @@ -151,10 +148,6 @@ func TestGdbPython(t *testing.T) { t.Fatalf("print strvar failed: %s", bl) } - if bl := blocks["print ptrvar"]; !strVarRe.MatchString(bl) { - t.Fatalf("print ptrvar failed: %s", bl) - } - btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`) if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) { t.Fatalf("goroutine 2 bt failed: %s", bl) -- cgit v1.3 From 7d9f1067d1c8a2d0252fa2a115f1d016f94f7087 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 17 Dec 2015 10:01:24 -0800 Subject: [dev.ssa] cmd/compile: better register allocator Reorder how register & stack allocation is done. We used to allocate registers, then fix up merge edges, then allocate stack slots. This lead to lots of unnecessary copies on merge edges: v2 = LoadReg v1 v3 = StoreReg v2 If v1 and v3 are allocated to the same stack slot, then this code is unnecessary. But at regalloc time we didn't know the homes of v1 and v3. To fix this problem, allocate all the stack slots before fixing up the merge edges. That way, we know what stack slots values use so we know what copies are required. Use a good technique for shuffling values around on merge edges. Improves performance of the go1 TimeParse benchmark by ~12% Change-Id: I731f43e4ff1a7e0dc4cd4aa428fcdb97812b86fa Reviewed-on: https://go-review.googlesource.com/17915 Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/compile.go | 3 - src/cmd/compile/internal/ssa/flagalloc.go | 9 - src/cmd/compile/internal/ssa/regalloc.go | 898 ++++++++++++++++++++--------- src/cmd/compile/internal/ssa/stackalloc.go | 297 +++++----- 4 files changed, 793 insertions(+), 414 deletions(-) (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index 767b774ab0..20af6fd5bd 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -102,7 +102,6 @@ var passes = [...]pass{ {"schedule", schedule}, // schedule values {"flagalloc", flagalloc}, // allocate flags register {"regalloc", regalloc}, - {"stackalloc", stackalloc}, } // Double-check phase ordering constraints. @@ -138,8 +137,6 @@ var passOrder = [...]constraint{ {"critical", "regalloc"}, // regalloc requires all the values in a block to be scheduled {"schedule", "regalloc"}, - // stack allocation requires register allocation - {"regalloc", "stackalloc"}, // checkLower must run after lowering & subsequent dead code elim {"lower", "checkLower"}, {"lowered deadcode", "checkLower"}, diff --git a/src/cmd/compile/internal/ssa/flagalloc.go b/src/cmd/compile/internal/ssa/flagalloc.go index 714ac016a2..c088158057 100644 --- a/src/cmd/compile/internal/ssa/flagalloc.go +++ b/src/cmd/compile/internal/ssa/flagalloc.go @@ -21,15 +21,6 @@ func flagalloc(f *Func) { // Walk blocks backwards. Poor-man's postorder traversal. for i := len(f.Blocks) - 1; i >= 0; i-- { b := f.Blocks[i] - if len(b.Preds) > 1 { - // Don't use any flags register at the start - // of a merge block. This causes problems - // in regalloc because some of the rematerialization - // instructions used on incoming merge edges clobber - // the flags register. - // TODO: only for architectures where this matters? - continue - } // Walk values backwards to figure out what flag // value we want in the flag register at the start // of the block. diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 2690b6188e..0f1068a337 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -99,7 +99,7 @@ import ( "unsafe" ) -const regDebug = false +const regDebug = false // TODO: compiler flag const logSpills = false // regalloc performs register allocation on f. It sets f.RegAlloc @@ -201,12 +201,12 @@ type use struct { } type valState struct { - regs regMask // the set of registers holding a Value (usually just one) - uses *use // list of uses in this block - spill *Value // spilled copy of the Value - spill2 *Value // special alternate spill location used for phi resolution - spillUsed bool - spill2used bool + regs regMask // the set of registers holding a Value (usually just one) + uses *use // list of uses in this block + spill *Value // spilled copy of the Value + spillUsed bool + needReg bool // cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !.v.Type.IsFlags() + rematerializeable bool // cached value of v.rematerializeable() } type regState struct { @@ -218,10 +218,6 @@ type regState struct { type regAllocState struct { f *Func - // For each value, whether it needs a register or not. - // Cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags(). - needReg []bool - // for each block, its primary predecessor. // A predecessor of b is primary if it is the closest // predecessor that appears before b in the layout order. @@ -249,14 +245,33 @@ type regAllocState struct { // mask of registers currently in use used regMask - // Home locations (registers) for Values - home []Location - // current block we're working on curBlock *Block // cache of use records freeUseRecords *use + + // endRegs[blockid] is the register state at the end of each block. + // encoded as a set of endReg records. + endRegs [][]endReg + + // startRegs[blockid] is the register state at the start of merge blocks. + // saved state does not include the state of phi ops in the block. + startRegs [][]startReg + + // spillLive[blockid] is the set of live spills at the end of each block + spillLive [][]ID +} + +type endReg struct { + r register + v *Value // pre-regalloc value held in this register (TODO: can we use ID here?) + c *Value // cached version of the value +} + +type startReg struct { + r register + vid ID // pre-regalloc value needed in this register } // freeReg frees up register r. Any current user of r is kicked out. @@ -268,7 +283,7 @@ func (s *regAllocState) freeReg(r register) { // Mark r as unused. if regDebug { - fmt.Printf("freeReg %d (dump %s/%s)\n", r, v, s.regs[r].c) + fmt.Printf("freeReg %s (dump %s/%s)\n", registers[r].Name(), v, s.regs[r].c) } s.regs[r] = regState{} s.values[v.ID].regs &^= regMask(1) << r @@ -282,21 +297,6 @@ func (s *regAllocState) freeRegs(m regMask) { } } -func (s *regAllocState) setHome(v *Value, r register) { - // Remember assignment. - for int(v.ID) >= len(s.home) { - s.home = append(s.home, nil) - s.home = s.home[:cap(s.home)] - } - s.home[v.ID] = ®isters[r] -} -func (s *regAllocState) getHome(v *Value) register { - if int(v.ID) >= len(s.home) || s.home[v.ID] == nil { - return noRegister - } - return register(s.home[v.ID].(*Register).Num) -} - // setOrig records that c's original value is the same as // v's original value. func (s *regAllocState) setOrig(c *Value, v *Value) { @@ -313,7 +313,7 @@ func (s *regAllocState) setOrig(c *Value, v *Value) { // r must be unused. func (s *regAllocState) assignReg(r register, v *Value, c *Value) { if regDebug { - fmt.Printf("assignReg %d %s/%s\n", r, v, c) + fmt.Printf("assignReg %s %s/%s\n", registers[r].Name(), v, c) } if s.regs[r].v != nil { s.f.Fatalf("tried to assign register %d to %s/%s but it is already used by %s", r, v, c, s.regs[r].v) @@ -323,7 +323,7 @@ func (s *regAllocState) assignReg(r register, v *Value, c *Value) { s.regs[r] = regState{v, c} s.values[v.ID].regs |= regMask(1) << r s.used |= regMask(1) << r - s.setHome(c, r) + s.f.setHome(c, ®isters[r]) } // allocReg picks an unused register from regmask. If there is no unused register, @@ -361,16 +361,6 @@ func (s *regAllocState) allocReg(mask regMask) register { continue } v := s.regs[t].v - - if s.values[v.ID].uses == nil { - // No subsequent use. - // This can happen when fixing up merge blocks at the end. - // We've already run through the use lists so they are empty. - // Any register would be ok at this point. - r = t - maxuse = 0 - break - } if n := s.values[v.ID].uses.dist; n > maxuse { // v's next use is farther in the future than any value // we've seen so far. A new best spill candidate. @@ -432,12 +422,6 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool) *Val } else { switch { // Load v from its spill location. - case vi.spill2 != nil: - if logSpills { - fmt.Println("regalloc: load spill2") - } - c = s.curBlock.NewValue1(v.Line, OpLoadReg, v.Type, vi.spill2) - vi.spill2used = true case vi.spill != nil: if logSpills { fmt.Println("regalloc: load spill") @@ -462,17 +446,16 @@ func (s *regAllocState) init(f *Func) { } s.f = f - s.needReg = make([]bool, f.NumValues()) s.regs = make([]regState, numRegs) s.values = make([]valState, f.NumValues()) s.orig = make([]*Value, f.NumValues()) for _, b := range f.Blocks { for _, v := range b.Values { - if v.Type.IsMemory() || v.Type.IsVoid() || v.Type.IsFlags() { - continue + if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() { + s.values[v.ID].needReg = true + s.values[v.ID].rematerializeable = v.rematerializeable() + s.orig[v.ID] = v } - s.needReg[v.ID] = true - s.orig[v.ID] = v } } s.computeLive() @@ -498,6 +481,10 @@ func (s *regAllocState) init(f *Func) { } s.primary[b.ID] = int32(best) } + + s.endRegs = make([][]endReg, f.NumBlocks()) + s.startRegs = make([][]startReg, f.NumBlocks()) + s.spillLive = make([][]ID, f.NumBlocks()) } // Adds a use record for id at distance dist from the start of the block. @@ -521,7 +508,7 @@ func (s *regAllocState) addUse(id ID, dist int32) { // Any values which have no more uses are deallocated from registers. func (s *regAllocState) advanceUses(v *Value) { for _, a := range v.Args { - if !s.needReg[a.ID] { + if !s.values[a.ID].needReg { continue } ai := &s.values[a.ID] @@ -536,21 +523,18 @@ func (s *regAllocState) advanceUses(v *Value) { } } -// Sets the state of the registers to that encoded in state. -func (s *regAllocState) setState(state []regState) { +// Sets the state of the registers to that encoded in regs. +func (s *regAllocState) setState(regs []endReg) { s.freeRegs(s.used) - for r, x := range state { - if x.c == nil { - continue - } - s.assignReg(register(r), x.v, x.c) + for _, x := range regs { + s.assignReg(x.r, x.v, x.c) } } -// compatRegs returns the set of registers which can store v. -func (s *regAllocState) compatRegs(v *Value) regMask { +// compatRegs returns the set of registers which can store a type t. +func (s *regAllocState) compatRegs(t Type) regMask { var m regMask - if v.Type.IsFloat() { + if t.IsFloat() { m = 0xffff << 16 // X0-X15 } else { m = 0xffef << 0 // AX-R15, except SP @@ -560,11 +544,8 @@ func (s *regAllocState) compatRegs(v *Value) regMask { func (s *regAllocState) regalloc(f *Func) { liveSet := newSparseSet(f.NumValues()) - argset := newSparseSet(f.NumValues()) var oldSched []*Value var phis []*Value - var stackPhis []*Value - var regPhis []*Value var phiRegs []register var args []*Value @@ -572,11 +553,6 @@ func (s *regAllocState) regalloc(f *Func) { f.Fatalf("entry block must be first") } - // For each merge block, we record the starting register state (after phi ops) - // for that merge block. Indexed by blockid/regnum. - startRegs := make([][]*Value, f.NumBlocks()) - // end state of registers for each block, idexed by blockid/regnum. - endRegs := make([][]regState, f.NumBlocks()) for _, b := range f.Blocks { s.curBlock = b @@ -587,18 +563,21 @@ func (s *regAllocState) regalloc(f *Func) { s.addUse(e.ID, int32(len(b.Values))+e.dist) // pseudo-uses from beyond end of block liveSet.add(e.ID) } - if c := b.Control; c != nil && s.needReg[c.ID] { - s.addUse(c.ID, int32(len(b.Values))) // psuedo-use by control value - liveSet.add(c.ID) + if v := b.Control; v != nil && s.values[v.ID].needReg { + s.addUse(v.ID, int32(len(b.Values))) // psuedo-use by control value + liveSet.add(v.ID) } for i := len(b.Values) - 1; i >= 0; i-- { v := b.Values[i] + liveSet.remove(v.ID) if v.Op == OpPhi { - break // Don't process phi ops. + // Remove v from the live set, but don't add + // any inputs. This is the state the len(b.Preds)>1 + // case below desires; it wants to process phis specially. + continue } - liveSet.remove(v.ID) for _, a := range v.Args { - if !s.needReg[a.ID] { + if !s.values[a.ID].needReg { continue } s.addUse(a.ID, int32(i)) @@ -613,7 +592,7 @@ func (s *regAllocState) regalloc(f *Func) { if u == nil { continue } - fmt.Printf("v%d:", i) + fmt.Printf(" v%d:", i) for u != nil { fmt.Printf(" %d", u.dist) u = u.next @@ -643,7 +622,7 @@ func (s *regAllocState) regalloc(f *Func) { } } else if len(b.Preds) == 1 { // Start regalloc state with the end state of the previous block. - s.setState(endRegs[b.Preds[0].ID]) + s.setState(s.endRegs[b.Preds[0].ID]) if nphi > 0 { f.Fatalf("phis in single-predecessor block") } @@ -669,52 +648,83 @@ func (s *regAllocState) regalloc(f *Func) { f.Fatalf("block with no primary predecessor %s", b) } p := b.Preds[idx] - s.setState(endRegs[p.ID]) + s.setState(s.endRegs[p.ID]) + + if regDebug { + fmt.Printf("starting merge block %s with end state of %s:\n", b, p) + for _, x := range s.endRegs[p.ID] { + fmt.Printf(" %s: orig:%s cache:%s\n", registers[x.r].Name(), x.v, x.c) + } + } // Decide on registers for phi ops. Use the registers determined // by the primary predecessor if we can. // TODO: pick best of (already processed) predecessors? // Majority vote? Deepest nesting level? phiRegs = phiRegs[:0] - var used regMask + var phiUsed regMask for _, v := range phis { - if v.Type.IsMemory() { + if !s.values[v.ID].needReg { phiRegs = append(phiRegs, noRegister) continue } - regs := s.values[v.Args[idx].ID].regs - m := regs &^ used + a := v.Args[idx] + m := s.values[a.ID].regs &^ phiUsed var r register if m != 0 { r = pickReg(m) - used |= regMask(1) << r + s.freeReg(r) + phiUsed |= regMask(1) << r + phiRegs = append(phiRegs, r) } else { - r = noRegister + phiRegs = append(phiRegs, noRegister) + } + } + + // Second pass - deallocate any phi inputs which are now dead. + for _, v := range phis { + if !s.values[v.ID].needReg { + continue + } + a := v.Args[idx] + if !liveSet.contains(a.ID) { + // Input is dead beyond the phi, deallocate + // anywhere else it might live. + s.freeRegs(s.values[a.ID].regs) } - phiRegs = append(phiRegs, r) } - // Change register user from phi input to phi. Add phi spill code. + + // Third pass - pick registers for phis whose inputs + // were not in a register. for i, v := range phis { - if v.Type.IsMemory() { + if !s.values[v.ID].needReg { + continue + } + if phiRegs[i] != noRegister { + continue + } + m := s.compatRegs(v.Type) &^ phiUsed &^ s.used + if m != 0 { + r := pickReg(m) + phiRegs[i] = r + phiUsed |= regMask(1) << r + } + } + + // Set registers for phis. Add phi spill code. + for i, v := range phis { + if !s.values[v.ID].needReg { continue } r := phiRegs[i] if r == noRegister { - m := s.compatRegs(v) & ^s.used - if m == 0 { - // stack-based phi - // Spills will be inserted in all the predecessors below. - s.values[v.ID].spill = v // v starts life spilled - s.values[v.ID].spillUsed = true // use is guaranteed - continue - } - // Allocate phi to an unused register. - r = pickReg(m) - } else { - s.freeReg(r) + // stack-based phi + // Spills will be inserted in all the predecessors below. + s.values[v.ID].spill = v // v starts life spilled + s.values[v.ID].spillUsed = true // use is guaranteed + continue } // register-based phi - // Transfer ownership of register from input arg to phi. s.assignReg(r, v, v) // Spill the phi in case we need to restore it later. spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v) @@ -723,15 +733,35 @@ func (s *regAllocState) regalloc(f *Func) { s.values[v.ID].spillUsed = false } - // Save the starting state for use by incoming edges below. - startRegs[b.ID] = make([]*Value, numRegs) + // Save the starting state for use by merge edges. + var regList []startReg for r := register(0); r < numRegs; r++ { - startRegs[b.ID][r] = s.regs[r].v + v := s.regs[r].v + if v == nil { + continue + } + if phiUsed>>r&1 != 0 { + // Skip registers that phis used, we'll handle those + // specially during merge edge processing. + continue + } + regList = append(regList, startReg{r, v.ID}) + } + s.startRegs[b.ID] = regList + + if regDebug { + fmt.Printf("after phis\n") + for _, x := range s.startRegs[b.ID] { + fmt.Printf(" %s: v%d\n", registers[x.r].Name(), x.vid) + } } } // Process all the non-phi values. - for idx, v := range oldSched { + for _, v := range oldSched { + if regDebug { + fmt.Printf(" processing %s\n", v.LongString()) + } if v.Op == OpPhi { f.Fatalf("phi %s not at start of block", v) } @@ -758,9 +788,6 @@ func (s *regAllocState) regalloc(f *Func) { continue } regspec := opcodeTable[v.Op].reg - if regDebug { - fmt.Printf("%d: working on %s %s %v\n", idx, v, v.LongString(), regspec) - } if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 { // No register allocation required (or none specified yet) s.freeRegs(regspec.clobbers) @@ -768,7 +795,7 @@ func (s *regAllocState) regalloc(f *Func) { continue } - if v.rematerializeable() { + if s.values[v.ID].rematerializeable { // Value is rematerializeable, don't issue it here. // It will get issued just before each use (see // allocValueToReg). @@ -800,7 +827,7 @@ func (s *regAllocState) regalloc(f *Func) { // Pick register for output. var r register var mask regMask - if s.needReg[v.ID] { + if s.values[v.ID].needReg { mask = regspec.outputs[0] &^ s.reserved() if mask>>33&1 != 0 { s.f.Fatalf("bad mask %s\n", v.LongString()) @@ -827,7 +854,7 @@ func (s *regAllocState) regalloc(f *Func) { // f() // } // It would be good to have both spill and restore inside the IF. - if s.needReg[v.ID] { + if s.values[v.ID].needReg { spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v) s.setOrig(spill, v) s.values[v.ID].spill = spill @@ -835,21 +862,70 @@ func (s *regAllocState) regalloc(f *Func) { } } - if c := b.Control; c != nil && s.needReg[c.ID] { + if v := b.Control; v != nil && s.values[v.ID].needReg { + if regDebug { + fmt.Printf(" processing control %s\n", v.LongString()) + } // Load control value into reg. // TODO: regspec for block control values, instead of using // register set from the control op's output. - s.allocValToReg(c, opcodeTable[c.Op].reg.outputs[0], false) + s.allocValToReg(v, opcodeTable[v.Op].reg.outputs[0], false) // Remove this use from the uses list. - u := s.values[c.ID].uses - s.values[c.ID].uses = u.next + vi := &s.values[v.ID] + u := vi.uses + vi.uses = u.next + if u.next == nil { + s.freeRegs(vi.regs) // value is dead + } u.next = s.freeUseRecords s.freeUseRecords = u } - // Record endRegs - endRegs[b.ID] = make([]regState, numRegs) - copy(endRegs[b.ID], s.regs) + // Save end-of-block register state. + var regList []endReg + for r := register(0); r < numRegs; r++ { + v := s.regs[r].v + if v == nil { + continue + } + regList = append(regList, endReg{r, v, s.regs[r].c}) + } + s.endRegs[b.ID] = regList + + // Check. TODO: remove + { + liveSet.clear() + for _, x := range s.live[b.ID] { + liveSet.add(x.ID) + } + for r := register(0); r < numRegs; r++ { + v := s.regs[r].v + if v == nil { + continue + } + if !liveSet.contains(v.ID) { + s.f.Fatalf("val %s is in reg but not live at end of %s", v, b) + } + } + } + + // If a value is live at the end of the block and + // isn't in a register, remember that its spill location + // is live. We need to remember this information so that + // the liveness analysis in stackalloc correct. + for _, e := range s.live[b.ID] { + if s.values[e.ID].regs != 0 { + // in a register, we'll use that source for the merge. + continue + } + spill := s.values[e.ID].spill + if spill == nil { + // rematerializeable values will have spill==nil. + continue + } + s.spillLive[b.ID] = append(s.spillLive[b.ID], spill.ID) + s.values[e.ID].spillUsed = true + } // Clear any final uses. // All that is left should be the pseudo-uses added for values which @@ -868,137 +944,6 @@ func (s *regAllocState) regalloc(f *Func) { } } - // Process merge block input edges. They are the tricky ones. - dst := make([]*Value, numRegs) - for _, b := range f.Blocks { - if len(b.Preds) <= 1 { - continue - } - for i, p := range b.Preds { - if regDebug { - fmt.Printf("processing %s->%s\n", p, b) - } - - // Find phis, separate them into stack & register classes. - stackPhis = stackPhis[:0] - regPhis = regPhis[:0] - for _, v := range b.Values { - if v.Op != OpPhi { - break - } - if v.Type.IsMemory() { - continue - } - if s.getHome(v) != noRegister { - regPhis = append(regPhis, v) - } else { - stackPhis = append(stackPhis, v) - } - } - - // Start with the state that exists at the end of the - // predecessor block. We'll be adding instructions here - // to shuffle registers & stack phis into the right spot. - s.setState(endRegs[p.ID]) - s.curBlock = p - - // Handle stack-based phi ops first. We need to handle them - // first because we need a register with which to copy them. - - // We must be careful not to overwrite any stack phis which are - // themselves args of other phis. For example: - // v1 = phi(v2, v3) : 8(SP) - // v2 = phi(v4, v5) : 16(SP) - // Here we must not write v2 until v2 is read and written to v1. - // The situation could be even more complicated, with cycles, etc. - // So in the interest of being simple, we find all the phis which - // are arguments of other phis and copy their values to a temporary - // location first. This temporary location is called "spill2" and - // represents a higher-priority but temporary spill location for the value. - // Note this is not a problem for register-based phis because - // if needed we will use the spilled location as the source, and - // the spill location is not clobbered by the code generated here. - argset.clear() - for _, v := range stackPhis { - argset.add(v.Args[i].ID) - } - for _, v := range regPhis { - argset.add(v.Args[i].ID) - } - for _, v := range stackPhis { - if !argset.contains(v.ID) { - continue - } - - // This stack-based phi is the argument of some other - // phi in this block. We must make a copy of its - // value so that we don't clobber it prematurely. - c := s.allocValToReg(v, s.compatRegs(v), false) - d := p.NewValue1(v.Line, OpStoreReg, v.Type, c) - s.setOrig(d, v) - s.values[v.ID].spill2 = d - } - - // Assign to stack-based phis. We do stack phis first because - // we might need a register to do the assignment. - for _, v := range stackPhis { - // Load phi arg into a register, then store it with a StoreReg. - // If already in a register, use that. If not, pick a compatible - // register. - w := v.Args[i] - c := s.allocValToReg(w, s.compatRegs(w), false) - v.Args[i] = p.NewValue1(v.Line, OpStoreReg, v.Type, c) - s.setOrig(v.Args[i], w) - } - // Figure out what value goes in each register. - for r := register(0); r < numRegs; r++ { - dst[r] = startRegs[b.ID][r] - } - // Handle register-based phi ops. - for _, v := range regPhis { - r := s.getHome(v) - if dst[r] != v { - f.Fatalf("dst not right") - } - v.Args[i] = s.allocValToReg(v.Args[i], regMask(1)<CX and CX->DX, do the latter first. Now if we do the - // former first then the latter must be a restore instead of a register move. - // Erase any spills we never used for i := range s.values { vi := s.values[i] @@ -1031,24 +976,450 @@ func (s *regAllocState) regalloc(f *Func) { // Not important now because this is the last phase that manipulates Values } - // Set final regalloc result. - f.RegAlloc = s.home + // Anything that didn't get a register gets a stack location here. + // (StoreReg, stack-based phis, inputs, ...) + stacklive := stackalloc(s.f, s.spillLive) + + // Fix up all merge edges. + s.shuffle(stacklive) +} + +// shuffle fixes up all the merge edges (those going into blocks of indegree > 1). +func (s *regAllocState) shuffle(stacklive [][]ID) { + var e edgeState + e.s = s + e.cache = map[ID][]*Value{} + e.contents = map[Location]contentRecord{} + if regDebug { + fmt.Printf("shuffle %s\n", s.f.Name) + fmt.Println(s.f.String()) + } + + for _, b := range s.f.Blocks { + if len(b.Preds) <= 1 { + continue + } + e.b = b + for i, p := range b.Preds { + e.p = p + e.setup(i, s.endRegs[p.ID], s.startRegs[b.ID], stacklive[p.ID]) + e.process() + } + } +} + +type edgeState struct { + s *regAllocState + p, b *Block // edge goes from p->b. + + // for each pre-regalloc value, a list of equivalent cached values + cache map[ID][]*Value + + // map from location to the value it contains + contents map[Location]contentRecord + + // desired destination locations + destinations []dstRecord + extra []dstRecord + + usedRegs regMask // registers currently holding something + uniqueRegs regMask // registers holding the only copy of a value + finalRegs regMask // registers holding final target +} + +type contentRecord struct { + vid ID // pre-regalloc value + c *Value // cached value + final bool // this is a satisfied destination +} + +type dstRecord struct { + loc Location // register or stack slot + vid ID // pre-regalloc value it should contain + splice **Value // place to store reference to the generating instruction +} + +// setup initializes the edge state for shuffling. +func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive []ID) { + if regDebug { + fmt.Printf("edge %s->%s\n", e.p, e.b) + } + + // Clear state. + for k := range e.cache { + delete(e.cache, k) + } + for k := range e.contents { + delete(e.contents, k) + } + + // Live registers can be sources. + for _, x := range srcReg { + e.set(®isters[x.r], x.v.ID, x.c, false) + } + // So can all of the spill locations. + for _, spillID := range stacklive { + v := e.s.orig[spillID] + spill := e.s.values[v.ID].spill + e.set(e.s.f.getHome(spillID), v.ID, spill, false) + } + + // Figure out all the destinations we need. + dsts := e.destinations[:0] + for _, x := range dstReg { + dsts = append(dsts, dstRecord{®isters[x.r], x.vid, nil}) + } + // Phis need their args to end up in a specific location. + for _, v := range e.b.Values { + if v.Op != OpPhi { + break + } + loc := e.s.f.getHome(v.ID) + if loc == nil { + continue + } + dsts = append(dsts, dstRecord{loc, v.Args[idx].ID, &v.Args[idx]}) + } + e.destinations = dsts + + if regDebug { + for vid, a := range e.cache { + for _, c := range a { + fmt.Printf("src %s: v%d cache=%s\n", e.s.f.getHome(c.ID).Name(), vid, c) + } + } + for _, d := range e.destinations { + fmt.Printf("dst %s: v%d\n", d.loc.Name(), d.vid) + } + } +} + +// process generates code to move all the values to the right destination locations. +func (e *edgeState) process() { + dsts := e.destinations + + // Process the destinations until they are all satisfied. + for len(dsts) > 0 { + i := 0 + for _, d := range dsts { + if !e.processDest(d.loc, d.vid, d.splice) { + // Failed - save for next iteration. + dsts[i] = d + i++ + } + } + if i < len(dsts) { + // Made some progress. Go around again. + dsts = dsts[:i] + + // Append any extras destinations we generated. + dsts = append(dsts, e.extra...) + e.extra = e.extra[:0] + continue + } + + // We made no progress. That means that any + // remaining unsatisfied moves are in simple cycles. + // For example, A -> B -> C -> D -> A. + // A ----> B + // ^ | + // | | + // | v + // D <---- C + + // To break the cycle, we pick an unused register, say R, + // and put a copy of B there. + // A ----> B + // ^ | + // | | + // | v + // D <---- C <---- R=copyofB + // When we resume the outer loop, the A->B move can now proceed, + // and eventually the whole cycle completes. + + // Copy any cycle location to a temp register. This duplicates + // one of the cycle entries, allowing the just duplicated value + // to be overwritten and the cycle to proceed. + loc := dsts[0].loc + vid := e.contents[loc].vid + c := e.contents[loc].c + r := e.findRegFor(c.Type) + if regDebug { + fmt.Printf("breaking cycle with v%d in %s:%s\n", vid, loc.Name(), c) + } + if _, isReg := loc.(*Register); isReg { + c = e.p.NewValue1(c.Line, OpCopy, c.Type, c) + } else { + c = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c) + } + e.set(r, vid, c, false) + } +} + +// processDest generates code to put value vid into location loc. Returns true +// if progress was made. +func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool { + occupant := e.contents[loc] + if occupant.vid == vid { + // Value is already in the correct place. + e.contents[loc] = contentRecord{vid, occupant.c, true} + if splice != nil { + *splice = occupant.c + } + // Note: if splice==nil then c will appear dead. This is + // non-SSA formed code, so be careful after this pass not to run + // deadcode elimination. + return true + } + + // Check if we're allowed to clobber the destination location. + if len(e.cache[occupant.vid]) == 1 && !e.s.values[occupant.vid].rematerializeable { + // We can't overwrite the last copy + // of a value that needs to survive. + return false + } + + // Copy from a source of v, register preferred. + v := e.s.orig[vid] + var c *Value + var src Location + if regDebug { + fmt.Printf("moving v%d to %s\n", vid, loc.Name()) + fmt.Printf("sources of v%d:", vid) + } + for _, w := range e.cache[vid] { + h := e.s.f.getHome(w.ID) + if regDebug { + fmt.Printf(" %s:%s", h.Name(), w) + } + _, isreg := h.(*Register) + if src == nil || isreg { + c = w + src = h + } + } + if regDebug { + if src != nil { + fmt.Printf(" [use %s]\n", src.Name()) + } else { + fmt.Printf(" [no source]\n") + } + } + _, dstReg := loc.(*Register) + var x *Value + if c == nil { + if !e.s.values[vid].rematerializeable { + e.s.f.Fatalf("can't find source for %s->%s: v%d\n", e.p, e.b, vid) + } + if dstReg { + x = v.copyInto(e.p) + } else { + // Rematerialize into stack slot. Need a free + // register to accomplish this. + e.erase(loc) // see pre-clobber comment below + r := e.findRegFor(v.Type) + x = v.copyInto(e.p) + e.set(r, vid, x, false) + x = e.p.NewValue1(x.Line, OpStoreReg, x.Type, x) + } + } else { + // Emit move from src to dst. + _, srcReg := src.(*Register) + if srcReg { + if dstReg { + x = e.p.NewValue1(c.Line, OpCopy, c.Type, c) + } else { + x = e.p.NewValue1(c.Line, OpStoreReg, c.Type, c) + } + } else { + if dstReg { + x = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c) + } else { + // mem->mem. Use temp register. + + // Pre-clobber destination. This avoids the + // following situation: + // - v is currently held in R0 and stacktmp0. + // - We want to copy stacktmp1 to stacktmp0. + // - We choose R0 as the temporary register. + // During the copy, both R0 and stacktmp0 are + // clobbered, losing both copies of v. Oops! + // Erasing the destination early means R0 will not + // be chosen as the temp register, as it will then + // be the last copy of v. + e.erase(loc) + + r := e.findRegFor(c.Type) + t := e.p.NewValue1(c.Line, OpLoadReg, c.Type, c) + e.set(r, vid, t, false) + x = e.p.NewValue1(c.Line, OpStoreReg, c.Type, t) + } + } + } + e.set(loc, vid, x, true) + if splice != nil { + *splice = x + } + return true +} + +// set changes the contents of location loc to hold the given value and its cached representative. +func (e *edgeState) set(loc Location, vid ID, c *Value, final bool) { + e.s.f.setHome(c, loc) + e.erase(loc) + e.contents[loc] = contentRecord{vid, c, final} + a := e.cache[vid] + a = append(a, c) + e.cache[vid] = a + if r, ok := loc.(*Register); ok { + e.usedRegs |= regMask(1) << uint(r.Num) + if final { + e.finalRegs |= regMask(1) << uint(r.Num) + } + if len(a) == 1 { + e.uniqueRegs |= regMask(1) << uint(r.Num) + } + if len(a) == 2 { + if t, ok := e.s.f.getHome(a[0].ID).(*Register); ok { + e.uniqueRegs &^= regMask(1) << uint(t.Num) + } + } + } + if regDebug { + fmt.Printf("%s\n", c.LongString()) + fmt.Printf("v%d now available in %s:%s\n", vid, loc.Name(), c) + } +} + +// erase removes any user of loc. +func (e *edgeState) erase(loc Location) { + cr := e.contents[loc] + if cr.c == nil { + return + } + vid := cr.vid + + if cr.final { + // Add a destination to move this value back into place. + // Make sure it gets added to the tail of the destination queue + // so we make progress on other moves first. + e.extra = append(e.extra, dstRecord{loc, cr.vid, nil}) + } + + // Remove c from the list of cached values. + a := e.cache[vid] + for i, c := range a { + if e.s.f.getHome(c.ID) == loc { + if regDebug { + fmt.Printf("v%d no longer available in %s:%s\n", vid, loc.Name(), c) + } + a[i], a = a[len(a)-1], a[:len(a)-1] + break + } + } + e.cache[vid] = a + + // Update register masks. + if r, ok := loc.(*Register); ok { + e.usedRegs &^= regMask(1) << uint(r.Num) + if cr.final { + e.finalRegs &^= regMask(1) << uint(r.Num) + } + } + if len(a) == 1 { + if r, ok := e.s.f.getHome(a[0].ID).(*Register); ok { + e.uniqueRegs |= regMask(1) << uint(r.Num) + } + } +} + +// findRegFor finds a register we can use to make a temp copy of type typ. +func (e *edgeState) findRegFor(typ Type) Location { + // Which registers are possibilities. + var m regMask + if typ.IsFloat() { + m = e.s.compatRegs(e.s.f.Config.fe.TypeFloat64()) + } else { + m = e.s.compatRegs(e.s.f.Config.fe.TypeInt64()) + } + + // Pick a register. In priority order: + // 1) an unused register + // 2) a non-unique register not holding a final value + // 3) a non-unique register + x := m &^ e.usedRegs + if x != 0 { + return ®isters[pickReg(x)] + } + x = m &^ e.uniqueRegs &^ e.finalRegs + if x != 0 { + return ®isters[pickReg(x)] + } + x = m &^ e.uniqueRegs + if x != 0 { + return ®isters[pickReg(x)] + } + + // No register is available. Allocate a temp location to spill a register to. + // The type of the slot is immaterial - it will not be live across + // any safepoint. Just use a type big enough to hold any register. + typ = e.s.f.Config.fe.TypeInt64() + t := LocalSlot{e.s.f.Config.fe.Auto(typ), typ, 0} + // TODO: reuse these slots. + + // Pick a register to spill. + for vid, a := range e.cache { + for _, c := range a { + if r, ok := e.s.f.getHome(c.ID).(*Register); ok && m>>uint(r.Num)&1 != 0 { + x := e.p.NewValue1(c.Line, OpStoreReg, c.Type, c) + e.set(t, vid, x, false) + if regDebug { + fmt.Printf(" SPILL %s->%s %s\n", r.Name(), t.Name(), x.LongString()) + } + // r will now be overwritten by the caller. At some point + // later, the newly saved value will be moved back to its + // final destination in processDest. + return r + } + } + } + + e.s.f.Fatalf("can't find empty register on edge %s->%s", e.p, e.b) + return nil } func (v *Value) rematerializeable() bool { // TODO: add a flags field to opInfo for this test? + regspec := opcodeTable[v.Op].reg // rematerializeable ops must be able to fill any register. - outputs := opcodeTable[v.Op].reg.outputs + outputs := regspec.outputs if len(outputs) == 0 || countRegs(outputs[0]) <= 1 { // Note: this case handles OpAMD64LoweredGetClosurePtr // which can't be moved. return false } + + // We can't rematerialize instructions which + // clobber the flags register. + if regspec.clobbers&flagRegMask != 0 { + if v.Op == OpAMD64MOVQconst && v.AuxInt != 0 || + v.Op == OpAMD64MOVLconst && int32(v.AuxInt) != 0 || + v.Op == OpAMD64MOVWconst && int16(v.AuxInt) != 0 || + v.Op == OpAMD64MOVBconst && int8(v.AuxInt) != 0 { + // These are marked as clobbering flags, but only + // the 0 versions actually do. TODO: fix MOV->XOR rewrites + // to understand when they are allowed to clobber flags? + return true + } + return false + } + if len(v.Args) == 0 { return true } if len(v.Args) == 1 && (v.Args[0].Op == OpSP || v.Args[0].Op == OpSB) { + // SP and SB (generated by OpSP and OpSB) are always available. return true } return false @@ -1084,9 +1455,6 @@ func (s *regAllocState) computeLive() { // out to all of them. po := postorder(f) for { - for _, b := range po { - f.Logf("live %s %v\n", b, s.live[b.ID]) - } changed := false for _, b := range po { @@ -1099,7 +1467,7 @@ func (s *regAllocState) computeLive() { } // Mark control value as live - if b.Control != nil && s.needReg[b.Control.ID] { + if b.Control != nil && s.values[b.Control.ID].needReg { live.set(b.Control.ID, int32(len(b.Values))) } @@ -1115,7 +1483,7 @@ func (s *regAllocState) computeLive() { continue } for _, a := range v.Args { - if s.needReg[a.ID] { + if s.values[a.ID].needReg { live.set(a.ID, int32(i)) } } @@ -1162,7 +1530,7 @@ func (s *regAllocState) computeLive() { // simultaneously happening at the start of the block). for _, v := range phis { id := v.Args[i].ID - if s.needReg[id] && !t.contains(id) || delta < t.get(id) { + if s.values[id].needReg && !t.contains(id) || delta < t.get(id) { update = true t.set(id, delta) } @@ -1185,6 +1553,16 @@ func (s *regAllocState) computeLive() { break } } + if regDebug { + fmt.Println("live values at end of each block") + for _, b := range f.Blocks { + fmt.Printf(" %s:", b) + for _, x := range s.live[b.ID] { + fmt.Printf(" v%d", x.ID) + } + fmt.Println() + } + } } // reserved returns a mask of reserved registers. diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 3eb5c3cf4a..797a6b05e6 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -6,55 +6,65 @@ package ssa +import "fmt" + +const stackDebug = false // TODO: compiler flag + +type stackAllocState struct { + f *Func + values []stackValState + live [][]ID // live[b.id] = live values at the end of block b. + interfere [][]ID // interfere[v.id] = values that interfere with v. +} + +type stackValState struct { + typ Type + spill *Value + needSlot bool +} + // stackalloc allocates storage in the stack frame for // all Values that did not get a register. -func stackalloc(f *Func) { - // Cache value types by ID. - types := make([]Type, f.NumValues()) - for _, b := range f.Blocks { - for _, v := range b.Values { - types[v.ID] = v.Type - } +// Returns a map from block ID to the stack values live at the end of that block. +func stackalloc(f *Func, spillLive [][]ID) [][]ID { + if stackDebug { + fmt.Println("before stackalloc") + fmt.Println(f.String()) } + var s stackAllocState + s.init(f, spillLive) + s.stackalloc() + return s.live +} - // Build interference graph among StoreReg and stack phi ops. - live := f.liveSpills() - interfere := make([][]ID, f.NumValues()) - s := newSparseSet(f.NumValues()) - for _, b := range f.Blocks { - // Start with known live values at the end of the block. - s.clear() - for i := 0; i < len(b.Succs); i++ { - s.addAll(live[b.ID][i]) - } +func (s *stackAllocState) init(f *Func, spillLive [][]ID) { + s.f = f - // Propagate backwards to the start of the block. - // Remember interfering sets. - for i := len(b.Values) - 1; i >= 0; i-- { - v := b.Values[i] - switch { - case v.Op == OpStoreReg, v.isStackPhi(): - s.remove(v.ID) - for _, id := range s.contents() { - if v.Type.Equal(types[id]) { - // Only need interferences between equivalent types. - interfere[v.ID] = append(interfere[v.ID], id) - interfere[id] = append(interfere[id], v.ID) - } - } - case v.Op == OpLoadReg: - s.add(v.Args[0].ID) - case v.Op == OpArg: - // This is an input argument which is pre-spilled. It is kind of - // like a StoreReg, but we don't remove v.ID here because we want - // this value to appear live even before this point. Being live - // all the way to the start of the entry block prevents other - // values from being allocated to the same slot and clobbering - // the input value before we have a chance to load it. + // Initialize value information. + s.values = make([]stackValState, f.NumValues()) + for _, b := range f.Blocks { + for _, v := range b.Values { + s.values[v.ID].typ = v.Type + s.values[v.ID].needSlot = !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && f.getHome(v.ID) == nil && !v.rematerializeable() + if stackDebug && s.values[v.ID].needSlot { + fmt.Printf("%s needs a stack slot\n", v) + } + if v.Op == OpStoreReg { + s.values[v.Args[0].ID].spill = v } } } + // Compute liveness info for values needing a slot. + s.computeLive(spillLive) + + // Build interference graph among values needing a slot. + s.buildInterferenceGraph() +} + +func (s *stackAllocState) stackalloc() { + f := s.f + // Build map from values to their names, if any. // A value may be associated with more than one name (e.g. after // the assignment i=j). This step picks one name per value arbitrarily. @@ -67,49 +77,41 @@ func stackalloc(f *Func) { } } - // Figure out which StoreReg ops are phi args. We don't pick slots for - // phi args because a stack phi and its args must all use the same stack slot. - phiArg := make([]bool, f.NumValues()) - for _, b := range f.Blocks { - for _, v := range b.Values { - if !v.isStackPhi() { - continue - } - for _, a := range v.Args { - phiArg[a.ID] = true - } - } - } - // Allocate args to their assigned locations. for _, v := range f.Entry.Values { if v.Op != OpArg { continue } - f.setHome(v, LocalSlot{v.Aux.(GCNode), v.Type, v.AuxInt}) + loc := LocalSlot{v.Aux.(GCNode), v.Type, v.AuxInt} + if stackDebug { + fmt.Printf("stackalloc %s to %s\n", v, loc.Name()) + } + f.setHome(v, loc) } // For each type, we keep track of all the stack slots we // have allocated for that type. + // TODO: share slots among equivalent types. We would need to + // only share among types with the same GC signature. See the + // type.Equal calls below for where this matters. locations := map[Type][]LocalSlot{} // Each time we assign a stack slot to a value v, we remember // the slot we used via an index into locations[v.Type]. - // TODO: share slots among equivalent types. slots := make([]int, f.NumValues()) for i := f.NumValues() - 1; i >= 0; i-- { slots[i] = -1 } - // Pick a stack slot for each non-phi-arg StoreReg and each stack phi. + // Pick a stack slot for each value needing one. used := make([]bool, f.NumValues()) for _, b := range f.Blocks { for _, v := range b.Values { - if v.Op != OpStoreReg && !v.isStackPhi() { + if !s.values[v.ID].needSlot { continue } - if phiArg[v.ID] { - continue + if v.Op == OpArg { + continue // already picked } // If this is a named value, try to use the name as @@ -121,7 +123,7 @@ func stackalloc(f *Func) { name = names[v.ID] } if name.N != nil && v.Type.Equal(name.Type) { - for _, id := range interfere[v.ID] { + for _, id := range s.interfere[v.ID] { h := f.getHome(id) if h != nil && h.(LocalSlot) == name { // A variable can interfere with itself. @@ -129,22 +131,10 @@ func stackalloc(f *Func) { goto noname } } - if v.Op == OpPhi { - for _, a := range v.Args { - for _, id := range interfere[a.ID] { - h := f.getHome(id) - if h != nil && h.(LocalSlot) == name { - goto noname - } - } - } + if stackDebug { + fmt.Printf("stackalloc %s to %s\n", v, name.Name()) } f.setHome(v, name) - if v.Op == OpPhi { - for _, a := range v.Args { - f.setHome(a, name) - } - } continue } @@ -155,25 +145,12 @@ func stackalloc(f *Func) { for i := 0; i < len(locs); i++ { used[i] = false } - for _, xid := range interfere[v.ID] { + for _, xid := range s.interfere[v.ID] { slot := slots[xid] if slot >= 0 { used[slot] = true } } - if v.Op == OpPhi { - // Stack phi and args must get the same stack slot, so - // anything the args interfere with is something the phi - // interferes with. - for _, a := range v.Args { - for _, xid := range interfere[a.ID] { - slot := slots[xid] - if slot >= 0 { - used[slot] = true - } - } - } - } // Find an unused stack slot. var i int for i = 0; i < len(locs); i++ { @@ -188,83 +165,80 @@ func stackalloc(f *Func) { } // Use the stack variable at that index for v. loc := locs[i] + if stackDebug { + fmt.Printf("stackalloc %s to %s\n", v, loc.Name()) + } f.setHome(v, loc) slots[v.ID] = i - if v.Op == OpPhi { - for _, a := range v.Args { - f.setHome(a, loc) - slots[a.ID] = i - } - } } } } -// live returns a map from block ID and successor edge index to a list -// of StoreReg/stackphi value IDs live on that edge. +// computeLive computes a map from block ID to a list of +// stack-slot-needing value IDs live at the end of that block. // TODO: this could be quadratic if lots of variables are live across lots of // basic blocks. Figure out a way to make this function (or, more precisely, the user // of this function) require only linear size & time. -func (f *Func) liveSpills() [][][]ID { - live := make([][][]ID, f.NumBlocks()) - for _, b := range f.Blocks { - live[b.ID] = make([][]ID, len(b.Succs)) - } +func (s *stackAllocState) computeLive(spillLive [][]ID) { + s.live = make([][]ID, s.f.NumBlocks()) var phis []*Value - - s := newSparseSet(f.NumValues()) - t := newSparseSet(f.NumValues()) + live := newSparseSet(s.f.NumValues()) + t := newSparseSet(s.f.NumValues()) // Instead of iterating over f.Blocks, iterate over their postordering. // Liveness information flows backward, so starting at the end // increases the probability that we will stabilize quickly. - po := postorder(f) + po := postorder(s.f) for { changed := false for _, b := range po { // Start with known live values at the end of the block - s.clear() - for i := 0; i < len(b.Succs); i++ { - s.addAll(live[b.ID][i]) - } + live.clear() + live.addAll(s.live[b.ID]) // Propagate backwards to the start of the block phis = phis[:0] for i := len(b.Values) - 1; i >= 0; i-- { v := b.Values[i] - switch { - case v.Op == OpStoreReg: - s.remove(v.ID) - case v.Op == OpLoadReg: - s.add(v.Args[0].ID) - case v.isStackPhi(): - s.remove(v.ID) - // save stack phi ops for later - phis = append(phis, v) + live.remove(v.ID) + if v.Op == OpPhi { + // Save phi for later. + // Note: its args might need a stack slot even though + // the phi itself doesn't. So don't use needSlot. + if !v.Type.IsMemory() && !v.Type.IsVoid() { + phis = append(phis, v) + } + continue + } + for _, a := range v.Args { + if s.values[a.ID].needSlot { + live.add(a.ID) + } } } // for each predecessor of b, expand its list of live-at-end values // invariant: s contains the values live at the start of b (excluding phi inputs) for i, p := range b.Preds { - // Find index of b in p's successors. - var j int - for j = 0; j < len(p.Succs); j++ { - if p.Succs[j] == b { - break - } - } t.clear() - t.addAll(live[p.ID][j]) - t.addAll(s.contents()) + t.addAll(s.live[p.ID]) + t.addAll(live.contents()) + t.addAll(spillLive[p.ID]) for _, v := range phis { - t.add(v.Args[i].ID) + a := v.Args[i] + if s.values[a.ID].needSlot { + t.add(a.ID) + } + if spill := s.values[a.ID].spill; spill != nil { + //TODO: remove? Subsumed by SpillUse? + t.add(spill.ID) + } } - if t.size() == len(live[p.ID][j]) { + if t.size() == len(s.live[p.ID]) { continue } // grow p's live set - live[p.ID][j] = append(live[p.ID][j][:0], t.contents()...) + s.live[p.ID] = append(s.live[p.ID][:0], t.contents()...) changed = true } } @@ -273,7 +247,11 @@ func (f *Func) liveSpills() [][][]ID { break } } - return live + if stackDebug { + for _, b := range s.f.Blocks { + fmt.Printf("stacklive %s %v\n", b, s.live[b.ID]) + } + } } func (f *Func) getHome(vid ID) Location { @@ -290,16 +268,51 @@ func (f *Func) setHome(v *Value, loc Location) { f.RegAlloc[v.ID] = loc } -func (v *Value) isStackPhi() bool { - if v.Op != OpPhi { - return false - } - if v.Type == TypeMem { - return false +func (s *stackAllocState) buildInterferenceGraph() { + f := s.f + s.interfere = make([][]ID, f.NumValues()) + live := newSparseSet(f.NumValues()) + for _, b := range f.Blocks { + // Propagate liveness backwards to the start of the block. + // Two values interfere if one is defined while the other is live. + live.clear() + live.addAll(s.live[b.ID]) + for i := len(b.Values) - 1; i >= 0; i-- { + v := b.Values[i] + if s.values[v.ID].needSlot { + live.remove(v.ID) + for _, id := range live.contents() { + if s.values[v.ID].typ.Equal(s.values[id].typ) { + s.interfere[v.ID] = append(s.interfere[v.ID], id) + s.interfere[id] = append(s.interfere[id], v.ID) + } + } + } + for _, a := range v.Args { + if s.values[a.ID].needSlot { + live.add(a.ID) + } + } + if v.Op == OpArg && s.values[v.ID].needSlot { + // OpArg is an input argument which is pre-spilled. + // We add back v.ID here because we want this value + // to appear live even before this point. Being live + // all the way to the start of the entry block prevents other + // values from being allocated to the same slot and clobbering + // the input value before we have a chance to load it. + live.add(v.ID) + } + } } - if int(v.ID) >= len(v.Block.Func.RegAlloc) { - return true + if stackDebug { + for vid, i := range s.interfere { + if len(i) > 0 { + fmt.Printf("v%d interferes with", vid) + for _, x := range i { + fmt.Printf(" v%d", x) + } + fmt.Println() + } + } } - return v.Block.Func.RegAlloc[v.ID] == nil - // TODO: use a separate opcode for StackPhi? } -- cgit v1.3 From f962f33035bccd67c08fa3e0002659d6b9978bbc Mon Sep 17 00:00:00 2001 From: Todd Neal Date: Thu, 28 Jan 2016 22:19:46 -0600 Subject: [dev.ssa] cmd/compile: reuse sparse sets across compiler passes Cache sparse sets in the function so they can be reused by subsequent compiler passes. benchmark old ns/op new ns/op delta BenchmarkDSEPass-8 206945 180022 -13.01% BenchmarkDSEPassBlock-8 5286103 2614054 -50.55% BenchmarkCSEPass-8 1790277 1790655 +0.02% BenchmarkCSEPassBlock-8 18083588 18112771 +0.16% BenchmarkDeadcodePass-8 59837 41375 -30.85% BenchmarkDeadcodePassBlock-8 1651575 511169 -69.05% BenchmarkMultiPass-8 531529 427506 -19.57% BenchmarkMultiPassBlock-8 7033496 4487814 -36.19% benchmark old allocs new allocs delta BenchmarkDSEPass-8 11 4 -63.64% BenchmarkDSEPassBlock-8 599 120 -79.97% BenchmarkCSEPass-8 18 18 +0.00% BenchmarkCSEPassBlock-8 2700 2700 +0.00% BenchmarkDeadcodePass-8 4 3 -25.00% BenchmarkDeadcodePassBlock-8 30 9 -70.00% BenchmarkMultiPass-8 24 20 -16.67% BenchmarkMultiPassBlock-8 1800 1000 -44.44% benchmark old bytes new bytes delta BenchmarkDSEPass-8 221367 142 -99.94% BenchmarkDSEPassBlock-8 3695207 3846 -99.90% BenchmarkCSEPass-8 303328 303328 +0.00% BenchmarkCSEPassBlock-8 5006400 5006400 +0.00% BenchmarkDeadcodePass-8 84232 10506 -87.53% BenchmarkDeadcodePassBlock-8 1274940 163680 -87.16% BenchmarkMultiPass-8 608674 313834 -48.44% BenchmarkMultiPassBlock-8 9906001 5003450 -49.49% Change-Id: Ib1fa58c7f494b374d1a4bb9cffbc2c48377b59d3 Reviewed-on: https://go-review.googlesource.com/19100 Reviewed-by: David Chase Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/deadcode.go | 3 +- src/cmd/compile/internal/ssa/deadstore.go | 9 ++- src/cmd/compile/internal/ssa/func.go | 25 +++++++ src/cmd/compile/internal/ssa/layout.go | 6 +- src/cmd/compile/internal/ssa/passbm_test.go | 101 ++++++++++++++++++++++++++++ src/cmd/compile/internal/ssa/regalloc.go | 3 +- src/cmd/compile/internal/ssa/sparseset.go | 4 ++ src/cmd/compile/internal/ssa/stackalloc.go | 9 ++- 8 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 src/cmd/compile/internal/ssa/passbm_test.go (limited to 'src/cmd/compile/internal/ssa/stackalloc.go') diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index 80e1490014..87244a6248 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -134,7 +134,8 @@ func deadcode(f *Func) { live := liveValues(f, reachable) // Remove dead & duplicate entries from namedValues map. - s := newSparseSet(f.NumValues()) + s := f.newSparseSet(f.NumValues()) + defer f.retSparseSet(s) i := 0 for _, name := range f.Names { j := 0 diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index 89f7504341..bad0e0096f 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -10,9 +10,12 @@ package ssa // This implementation only works within a basic block. TODO: use something more global. func dse(f *Func) { var stores []*Value - loadUse := newSparseSet(f.NumValues()) - storeUse := newSparseSet(f.NumValues()) - shadowed := newSparseSet(f.NumValues()) + loadUse := f.newSparseSet(f.NumValues()) + defer f.retSparseSet(loadUse) + storeUse := f.newSparseSet(f.NumValues()) + defer f.retSparseSet(storeUse) + shadowed := f.newSparseSet(f.NumValues()) + defer f.retSparseSet(shadowed) for _, b := range f.Blocks { // Find all the stores in this block. Categorize their uses: // loadUse contains stores which are used by a subsequent load. diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index a28484010d..9da390904d 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -31,6 +31,8 @@ type Func struct { freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil. freeBlocks *Block // free Blocks linked by succstorage[0]. All other fields except ID are 0/nil. + + scrSparse []*sparseSet // sparse sets to be re-used. } // NumBlocks returns an integer larger than the id of any Block in the Func. @@ -43,6 +45,29 @@ func (f *Func) NumValues() int { return f.vid.num() } +// newSparseSet returns a sparse set that can store at least up to n integers. +func (f *Func) newSparseSet(n int) *sparseSet { + for i, scr := range f.scrSparse { + if scr != nil && scr.cap() >= n { + f.scrSparse[i] = nil + scr.clear() + return scr + } + } + return newSparseSet(n) +} + +// retSparseSet returns a sparse set to the function's cache to be reused by f.newSparseSet. +func (f *Func) retSparseSet(ss *sparseSet) { + for i, scr := range f.scrSparse { + if scr == nil { + f.scrSparse[i] = ss + return + } + } + f.scrSparse = append(f.scrSparse, ss) +} + // newValue allocates a new Value with the given fields and places it at the end of b.Values. func (f *Func) newValue(op Op, t Type, b *Block, line int32) *Value { var v *Value diff --git a/src/cmd/compile/internal/ssa/layout.go b/src/cmd/compile/internal/ssa/layout.go index 7e865f948e..8dd4b65979 100644 --- a/src/cmd/compile/internal/ssa/layout.go +++ b/src/cmd/compile/internal/ssa/layout.go @@ -12,8 +12,10 @@ func layout(f *Func) { scheduled := make([]bool, f.NumBlocks()) idToBlock := make([]*Block, f.NumBlocks()) indegree := make([]int, f.NumBlocks()) - posdegree := newSparseSet(f.NumBlocks()) // blocks with positive remaining degree - zerodegree := newSparseSet(f.NumBlocks()) // blocks with zero remaining degree + posdegree := f.newSparseSet(f.NumBlocks()) // blocks with positive remaining degree + defer f.retSparseSet(posdegree) + zerodegree := f.newSparseSet(f.NumBlocks()) // blocks with zero remaining degree + defer f.retSparseSet(zerodegree) // Initialize indegree of each block for _, b := range f.Blocks { diff --git a/src/cmd/compile/internal/ssa/passbm_test.go b/src/cmd/compile/internal/ssa/passbm_test.go new file mode 100644 index 0000000000..9b11ff1256 --- /dev/null +++ b/src/cmd/compile/internal/ssa/passbm_test.go @@ -0,0 +1,101 @@ +// Copyright 2015 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. +package ssa + +import ( + "fmt" + "testing" +) + +const ( + blockCount = 1000 + passCount = 15000 +) + +type passFunc func(*Func) + +func BenchmarkDSEPass(b *testing.B) { benchFnPass(b, dse, blockCount, genFunction) } +func BenchmarkDSEPassBlock(b *testing.B) { benchFnBlock(b, dse, genFunction) } +func BenchmarkCSEPass(b *testing.B) { benchFnPass(b, cse, blockCount, genFunction) } +func BenchmarkCSEPassBlock(b *testing.B) { benchFnBlock(b, cse, genFunction) } +func BenchmarkDeadcodePass(b *testing.B) { benchFnPass(b, deadcode, blockCount, genFunction) } +func BenchmarkDeadcodePassBlock(b *testing.B) { benchFnBlock(b, deadcode, genFunction) } + +func multi(f *Func) { + cse(f) + dse(f) + deadcode(f) +} +func BenchmarkMultiPass(b *testing.B) { benchFnPass(b, multi, blockCount, genFunction) } +func BenchmarkMultiPassBlock(b *testing.B) { benchFnBlock(b, multi, genFunction) } + +// benchFnPass runs passFunc b.N times across a single function. +func benchFnPass(b *testing.B, fn passFunc, size int, bg blockGen) { + b.ReportAllocs() + c := NewConfig("amd64", DummyFrontend{b}, nil, true) + fun := Fun(c, "entry", bg(size)...) + + CheckFunc(fun.f) + b.ResetTimer() + for i := 0; i < b.N; i++ { + fn(fun.f) + b.StopTimer() + CheckFunc(fun.f) + b.StartTimer() + } +} + +// benchFnPass runs passFunc across a function with b.N blocks. +func benchFnBlock(b *testing.B, fn passFunc, bg blockGen) { + b.ReportAllocs() + c := NewConfig("amd64", DummyFrontend{b}, nil, true) + fun := Fun(c, "entry", bg(b.N)...) + + CheckFunc(fun.f) + b.ResetTimer() + for i := 0; i < passCount; i++ { + fn(fun.f) + } + b.StopTimer() +} + +func genFunction(size int) []bloc { + var blocs []bloc + elemType := &TypeImpl{Size_: 8, Name: "testtype"} + ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing + + valn := func(s string, m, n int) string { return fmt.Sprintf("%s%d-%d", s, m, n) } + blocs = append(blocs, + Bloc("entry", + Valu(valn("store", 0, 4), OpArg, TypeMem, 0, ".mem"), + Valu("sb", OpSB, TypeInvalid, 0, nil), + Goto(blockn(1)), + ), + ) + for i := 1; i < size+1; i++ { + blocs = append(blocs, Bloc(blockn(i), + Valu(valn("v", i, 0), OpConstBool, TypeBool, 1, nil), + Valu(valn("addr", i, 1), OpAddr, ptrType, 0, nil, "sb"), + Valu(valn("addr", i, 2), OpAddr, ptrType, 0, nil, "sb"), + Valu(valn("addr", i, 3), OpAddr, ptrType, 0, nil, "sb"), + Valu(valn("zero", i, 1), OpZero, TypeMem, 8, nil, valn("addr", i, 3), + valn("store", i-1, 4)), + Valu(valn("store", i, 1), OpStore, TypeMem, 0, nil, valn("addr", i, 1), + valn("v", i, 0), valn("zero", i, 1)), + Valu(valn("store", i, 2), OpStore, TypeMem, 0, nil, valn("addr", i, 2), + valn("v", i, 0), valn("store", i, 1)), + Valu(valn("store", i, 3), OpStore, TypeMem, 0, nil, valn("addr", i, 1), + valn("v", i, 0), valn("store", i, 2)), + Valu(valn("store", i, 4), OpStore, TypeMem, 0, nil, valn("addr", i, 3), + valn("v", i, 0), valn("store", i, 3)), + Goto(blockn(i+1)))) + } + + blocs = append(blocs, + Bloc(blockn(size+1), Goto("exit")), + Bloc("exit", Exit("store0-4")), + ) + + return blocs +} diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 61f694355e..2d88850999 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -559,7 +559,8 @@ func (s *regAllocState) compatRegs(t Type) regMask { } func (s *regAllocState) regalloc(f *Func) { - liveSet := newSparseSet(f.NumValues()) + liveSet := f.newSparseSet(f.NumValues()) + defer f.retSparseSet(liveSet) var oldSched []*Value var phis []*Value var phiRegs []register diff --git a/src/cmd/compile/internal/ssa/sparseset.go b/src/cmd/compile/internal/ssa/sparseset.go index b79aee8497..66bebf139e 100644 --- a/src/cmd/compile/internal/ssa/sparseset.go +++ b/src/cmd/compile/internal/ssa/sparseset.go @@ -18,6 +18,10 @@ func newSparseSet(n int) *sparseSet { return &sparseSet{nil, make([]int, n)} } +func (s *sparseSet) cap() int { + return len(s.sparse) +} + func (s *sparseSet) size() int { return len(s.dense) } diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 797a6b05e6..0e6cae0924 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -182,8 +182,10 @@ func (s *stackAllocState) stackalloc() { func (s *stackAllocState) computeLive(spillLive [][]ID) { s.live = make([][]ID, s.f.NumBlocks()) var phis []*Value - live := newSparseSet(s.f.NumValues()) - t := newSparseSet(s.f.NumValues()) + live := s.f.newSparseSet(s.f.NumValues()) + defer s.f.retSparseSet(live) + t := s.f.newSparseSet(s.f.NumValues()) + defer s.f.retSparseSet(t) // Instead of iterating over f.Blocks, iterate over their postordering. // Liveness information flows backward, so starting at the end @@ -271,7 +273,8 @@ func (f *Func) setHome(v *Value, loc Location) { func (s *stackAllocState) buildInterferenceGraph() { f := s.f s.interfere = make([][]ID, f.NumValues()) - live := newSparseSet(f.NumValues()) + live := f.newSparseSet(f.NumValues()) + defer f.retSparseSet(live) for _, b := range f.Blocks { // Propagate liveness backwards to the start of the block. // Two values interfere if one is defined while the other is live. -- cgit v1.3