mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: start on ARM port
Start working on arm port. Gets close to correct
code for fibonacci:
func fib(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
Still a lot to do, but this is a good starting point.
Cleaned up some arch-specific dependencies in regalloc.
Change-Id: I4301c6c31a8402168e50dcfee8bcf7aee73ea9d5
Reviewed-on: https://go-review.googlesource.com/21000
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
44d3f89e99
commit
4c9a470d46
13 changed files with 807 additions and 73 deletions
|
|
@ -6,6 +6,7 @@ package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/gc"
|
"cmd/compile/internal/gc"
|
||||||
|
"cmd/compile/internal/ssa"
|
||||||
"cmd/internal/obj/arm"
|
"cmd/internal/obj/arm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -65,6 +66,11 @@ func Main() {
|
||||||
gc.Thearch.Doregbits = doregbits
|
gc.Thearch.Doregbits = doregbits
|
||||||
gc.Thearch.Regnames = regnames
|
gc.Thearch.Regnames = regnames
|
||||||
|
|
||||||
|
gc.Thearch.SSARegToReg = ssaRegToReg
|
||||||
|
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||||
|
gc.Thearch.SSAGenValue = ssaGenValue
|
||||||
|
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||||
|
|
||||||
gc.Main()
|
gc.Main()
|
||||||
gc.Exit(0)
|
gc.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
152
src/cmd/compile/internal/arm/ssa.go
Normal file
152
src/cmd/compile/internal/arm/ssa.go
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
// Copyright 2016 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 arm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/gc"
|
||||||
|
"cmd/compile/internal/ssa"
|
||||||
|
"cmd/internal/obj"
|
||||||
|
"cmd/internal/obj/arm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ssaRegToReg = []int16{
|
||||||
|
arm.REG_R0,
|
||||||
|
arm.REG_R1,
|
||||||
|
arm.REG_R2,
|
||||||
|
arm.REG_R3,
|
||||||
|
arm.REGSP, // aka R13
|
||||||
|
}
|
||||||
|
|
||||||
|
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
||||||
|
s.SetLineno(v.Line)
|
||||||
|
switch v.Op {
|
||||||
|
case ssa.OpInitMem:
|
||||||
|
// memory arg needs no code
|
||||||
|
case ssa.OpArg:
|
||||||
|
// input args need no code
|
||||||
|
case ssa.OpSP, ssa.OpSB:
|
||||||
|
// nothing to do
|
||||||
|
case ssa.OpCopy:
|
||||||
|
case ssa.OpLoadReg:
|
||||||
|
// TODO: by type
|
||||||
|
p := gc.Prog(arm.AMOVW)
|
||||||
|
n, off := gc.AutoVar(v.Args[0])
|
||||||
|
p.From.Type = obj.TYPE_MEM
|
||||||
|
p.From.Node = n
|
||||||
|
p.From.Sym = gc.Linksym(n.Sym)
|
||||||
|
p.From.Offset = off
|
||||||
|
if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
|
||||||
|
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 = gc.SSARegNum(v)
|
||||||
|
|
||||||
|
case ssa.OpStoreReg:
|
||||||
|
// TODO: by type
|
||||||
|
p := gc.Prog(arm.AMOVW)
|
||||||
|
p.From.Type = obj.TYPE_REG
|
||||||
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||||
|
n, off := gc.AutoVar(v)
|
||||||
|
p.To.Type = obj.TYPE_MEM
|
||||||
|
p.To.Node = n
|
||||||
|
p.To.Sym = gc.Linksym(n.Sym)
|
||||||
|
p.To.Offset = off
|
||||||
|
if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
|
||||||
|
p.To.Name = obj.NAME_PARAM
|
||||||
|
p.To.Offset += n.Xoffset
|
||||||
|
} else {
|
||||||
|
p.To.Name = obj.NAME_AUTO
|
||||||
|
}
|
||||||
|
case ssa.OpARMADD:
|
||||||
|
r := gc.SSARegNum(v)
|
||||||
|
r1 := gc.SSARegNum(v.Args[0])
|
||||||
|
r2 := gc.SSARegNum(v.Args[1])
|
||||||
|
p := gc.Prog(v.Op.Asm())
|
||||||
|
p.From.Type = obj.TYPE_REG
|
||||||
|
p.From.Reg = r1
|
||||||
|
p.Reg = r2
|
||||||
|
p.To.Type = obj.TYPE_REG
|
||||||
|
p.To.Reg = r
|
||||||
|
case ssa.OpARMADDconst:
|
||||||
|
p := gc.Prog(v.Op.Asm())
|
||||||
|
p.From.Type = obj.TYPE_CONST
|
||||||
|
p.From.Offset = v.AuxInt
|
||||||
|
if v.Aux != nil {
|
||||||
|
panic("can't handle symbolic constant yet")
|
||||||
|
}
|
||||||
|
p.Reg = gc.SSARegNum(v.Args[0])
|
||||||
|
p.To.Type = obj.TYPE_REG
|
||||||
|
p.To.Reg = gc.SSARegNum(v)
|
||||||
|
case ssa.OpARMMOVWconst:
|
||||||
|
p := gc.Prog(v.Op.Asm())
|
||||||
|
p.From.Type = obj.TYPE_CONST
|
||||||
|
p.From.Offset = v.AuxInt2Int64()
|
||||||
|
p.To.Type = obj.TYPE_REG
|
||||||
|
p.To.Reg = gc.SSARegNum(v)
|
||||||
|
case ssa.OpARMCMP:
|
||||||
|
p := gc.Prog(v.Op.Asm())
|
||||||
|
p.From.Type = obj.TYPE_REG
|
||||||
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||||
|
p.Reg = gc.SSARegNum(v.Args[1])
|
||||||
|
case ssa.OpARMMOVWload:
|
||||||
|
p := gc.Prog(v.Op.Asm())
|
||||||
|
p.From.Type = obj.TYPE_MEM
|
||||||
|
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||||
|
gc.AddAux(&p.From, v)
|
||||||
|
p.To.Type = obj.TYPE_REG
|
||||||
|
p.To.Reg = gc.SSARegNum(v)
|
||||||
|
case ssa.OpARMMOVWstore:
|
||||||
|
p := gc.Prog(v.Op.Asm())
|
||||||
|
p.From.Type = obj.TYPE_REG
|
||||||
|
p.From.Reg = gc.SSARegNum(v.Args[1])
|
||||||
|
p.To.Type = obj.TYPE_MEM
|
||||||
|
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||||
|
gc.AddAux(&p.To, v)
|
||||||
|
case ssa.OpARMCALLstatic:
|
||||||
|
// TODO: deferreturn
|
||||||
|
p := gc.Prog(obj.ACALL)
|
||||||
|
p.To.Type = obj.TYPE_MEM
|
||||||
|
p.To.Name = obj.NAME_EXTERN
|
||||||
|
p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
|
||||||
|
if gc.Maxarg < v.AuxInt {
|
||||||
|
gc.Maxarg = v.AuxInt
|
||||||
|
}
|
||||||
|
case ssa.OpVarDef:
|
||||||
|
gc.Gvardef(v.Aux.(*gc.Node))
|
||||||
|
case ssa.OpVarKill:
|
||||||
|
gc.Gvarkill(v.Aux.(*gc.Node))
|
||||||
|
case ssa.OpVarLive:
|
||||||
|
gc.Gvarlive(v.Aux.(*gc.Node))
|
||||||
|
case ssa.OpARMLessThan:
|
||||||
|
v.Fatalf("pseudo-op made it to output: %s", v.LongString())
|
||||||
|
default:
|
||||||
|
v.Unimplementedf("genValue not implemented: %s", v.LongString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
|
||||||
|
s.SetLineno(b.Line)
|
||||||
|
|
||||||
|
switch b.Kind {
|
||||||
|
case ssa.BlockCall:
|
||||||
|
if b.Succs[0] != next {
|
||||||
|
p := gc.Prog(obj.AJMP)
|
||||||
|
p.To.Type = obj.TYPE_BRANCH
|
||||||
|
s.Branches = append(s.Branches, gc.Branch{p, b.Succs[0]})
|
||||||
|
}
|
||||||
|
case ssa.BlockRet:
|
||||||
|
gc.Prog(obj.ARET)
|
||||||
|
case ssa.BlockARMLT:
|
||||||
|
p := gc.Prog(arm.ABGE)
|
||||||
|
p.To.Type = obj.TYPE_BRANCH
|
||||||
|
s.Branches = append(s.Branches, gc.Branch{p, b.Succs[0]})
|
||||||
|
p = gc.Prog(obj.AJMP)
|
||||||
|
p.To.Type = obj.TYPE_BRANCH
|
||||||
|
s.Branches = append(s.Branches, gc.Branch{p, b.Succs[1]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,9 +30,15 @@ func initssa() *ssa.Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldssa(fn *Node) bool {
|
func shouldssa(fn *Node) bool {
|
||||||
if Thearch.Thestring != "amd64" {
|
switch Thearch.Thestring {
|
||||||
|
default:
|
||||||
|
// Only available for testing.
|
||||||
|
if os.Getenv("SSATEST") == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Generally available.
|
||||||
|
case "amd64":
|
||||||
|
}
|
||||||
if !ssaEnabled {
|
if !ssaEnabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ type Config struct {
|
||||||
PtrSize int64 // 4 or 8
|
PtrSize int64 // 4 or 8
|
||||||
lowerBlock func(*Block) bool // lowering function
|
lowerBlock func(*Block) bool // lowering function
|
||||||
lowerValue func(*Value, *Config) bool // lowering function
|
lowerValue func(*Value, *Config) bool // lowering function
|
||||||
|
registers []Register // machine registers
|
||||||
fe Frontend // callbacks into compiler frontend
|
fe Frontend // callbacks into compiler frontend
|
||||||
HTML *HTMLWriter // html writer, for debugging
|
HTML *HTMLWriter // html writer, for debugging
|
||||||
ctxt *obj.Link // Generic arch information
|
ctxt *obj.Link // Generic arch information
|
||||||
|
|
@ -112,11 +113,18 @@ func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config
|
||||||
c.PtrSize = 8
|
c.PtrSize = 8
|
||||||
c.lowerBlock = rewriteBlockAMD64
|
c.lowerBlock = rewriteBlockAMD64
|
||||||
c.lowerValue = rewriteValueAMD64
|
c.lowerValue = rewriteValueAMD64
|
||||||
|
c.registers = registersAMD64[:]
|
||||||
case "386":
|
case "386":
|
||||||
c.IntSize = 4
|
c.IntSize = 4
|
||||||
c.PtrSize = 4
|
c.PtrSize = 4
|
||||||
c.lowerBlock = rewriteBlockAMD64
|
c.lowerBlock = rewriteBlockAMD64
|
||||||
c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
|
c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
|
||||||
|
case "arm":
|
||||||
|
c.IntSize = 4
|
||||||
|
c.PtrSize = 4
|
||||||
|
c.lowerBlock = rewriteBlockARM
|
||||||
|
c.lowerValue = rewriteValueARM
|
||||||
|
c.registers = registersARM[:]
|
||||||
default:
|
default:
|
||||||
fe.Unimplementedf(0, "arch %s not implemented", arch)
|
fe.Unimplementedf(0, "arch %s not implemented", arch)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
src/cmd/compile/internal/ssa/gen/ARM.rules
Normal file
32
src/cmd/compile/internal/ssa/gen/ARM.rules
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
(Add32 x y) -> (ADD x y)
|
||||||
|
|
||||||
|
(Const32 [val]) -> (MOVWconst [val])
|
||||||
|
|
||||||
|
(Less32 x y) -> (LessThan (CMP x y))
|
||||||
|
|
||||||
|
(OffPtr [off] ptr) -> (ADD (MOVWconst <config.Frontend().TypeInt32()> [off]) ptr)
|
||||||
|
|
||||||
|
(Addr {sym} base) -> (ADDconst {sym} base)
|
||||||
|
|
||||||
|
(Load <t> ptr mem) && is32BitInt(t) -> (MOVWload ptr mem)
|
||||||
|
(Store [4] ptr val mem) -> (MOVWstore ptr val mem)
|
||||||
|
|
||||||
|
(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
|
||||||
|
|
||||||
|
// Absorb LessThan into blocks.
|
||||||
|
(If (LessThan cc) yes no) -> (LT cc yes no)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Optimizations
|
||||||
|
|
||||||
|
(ADD (MOVWconst [c]) x) -> (ADDconst [c] x)
|
||||||
|
(ADD x (MOVWconst [c])) -> (ADDconst [c] x)
|
||||||
|
(MOVWload [off1] {sym1} (ADDconst [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
|
||||||
|
(MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||||
|
(MOVWstore [off1] {sym1} (ADDconst [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
|
||||||
|
(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||||
66
src/cmd/compile/internal/ssa/gen/ARMOps.go
Normal file
66
src/cmd/compile/internal/ssa/gen/ARMOps.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2016 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
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var (
|
||||||
|
gp01 = regInfo{inputs: []regMask{}, outputs: []regMask{31}}
|
||||||
|
gp11 = regInfo{inputs: []regMask{31}, outputs: []regMask{31}}
|
||||||
|
gp21 = regInfo{inputs: []regMask{31, 31}, outputs: []regMask{31}}
|
||||||
|
gp2flags = regInfo{inputs: []regMask{31, 31}, outputs: []regMask{32}}
|
||||||
|
gpload = regInfo{inputs: []regMask{31}, outputs: []regMask{31}}
|
||||||
|
gpstore = regInfo{inputs: []regMask{31, 31}, outputs: []regMask{}}
|
||||||
|
flagsgp = regInfo{inputs: []regMask{32}, outputs: []regMask{31}}
|
||||||
|
callerSave = regMask(15)
|
||||||
|
)
|
||||||
|
ops := []opData{
|
||||||
|
{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1
|
||||||
|
{name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "SymOff"}, // arg0 + auxInt + aux.(*gc.Sym)
|
||||||
|
|
||||||
|
{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", rematerializeable: true}, // 32 low bits of auxint
|
||||||
|
|
||||||
|
{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1
|
||||||
|
|
||||||
|
{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW"}, // load from arg0 + auxInt + aux. arg1=mem.
|
||||||
|
{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
|
||||||
|
|
||||||
|
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff"}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
|
||||||
|
|
||||||
|
// pseudo-ops
|
||||||
|
{name: "LessThan", argLength: 2, reg: flagsgp}, // bool, 1 flags encode x<y 0 otherwise.
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks := []blockData{
|
||||||
|
{name: "EQ"},
|
||||||
|
{name: "NE"},
|
||||||
|
{name: "LT"},
|
||||||
|
{name: "LE"},
|
||||||
|
{name: "GT"},
|
||||||
|
{name: "GE"},
|
||||||
|
{name: "ULT"},
|
||||||
|
{name: "ULE"},
|
||||||
|
{name: "UGT"},
|
||||||
|
{name: "UGE"},
|
||||||
|
}
|
||||||
|
|
||||||
|
regNames := []string{
|
||||||
|
"R0",
|
||||||
|
"R1",
|
||||||
|
"R2",
|
||||||
|
"R3",
|
||||||
|
"SP",
|
||||||
|
"FLAGS",
|
||||||
|
"SB",
|
||||||
|
}
|
||||||
|
|
||||||
|
archs = append(archs, arch{
|
||||||
|
name: "ARM",
|
||||||
|
pkg: "cmd/internal/obj/arm",
|
||||||
|
genfile: "../../arm/ssa.go",
|
||||||
|
ops: ops,
|
||||||
|
blocks: blocks,
|
||||||
|
regnames: regNames,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -13,5 +13,6 @@ func init() {
|
||||||
name: "dec",
|
name: "dec",
|
||||||
ops: decOps,
|
ops: decOps,
|
||||||
blocks: decBlocks,
|
blocks: decBlocks,
|
||||||
|
generic: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -417,5 +417,6 @@ func init() {
|
||||||
name: "generic",
|
name: "generic",
|
||||||
ops: genericOps,
|
ops: genericOps,
|
||||||
blocks: genericBlocks,
|
blocks: genericBlocks,
|
||||||
|
generic: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ type arch struct {
|
||||||
ops []opData
|
ops []opData
|
||||||
blocks []blockData
|
blocks []blockData
|
||||||
regnames []string
|
regnames []string
|
||||||
|
generic bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type opData struct {
|
type opData struct {
|
||||||
|
|
@ -205,6 +206,18 @@ func genOp() {
|
||||||
// generate op string method
|
// generate op string method
|
||||||
fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }")
|
fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }")
|
||||||
|
|
||||||
|
// generate registers
|
||||||
|
for _, a := range archs {
|
||||||
|
if a.generic {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "var registers%s = [...]Register {\n", a.name)
|
||||||
|
for i, r := range a.regnames {
|
||||||
|
fmt.Fprintf(w, " {%d, \"%s\"},\n", i, r)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(w, "}")
|
||||||
|
}
|
||||||
|
|
||||||
// gofmt result
|
// gofmt result
|
||||||
b := w.Bytes()
|
b := w.Bytes()
|
||||||
var err error
|
var err error
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ const (
|
||||||
auxSym // aux is a symbol
|
auxSym // aux is a symbol
|
||||||
auxSymOff // aux is a symbol, auxInt is an offset
|
auxSymOff // aux is a symbol, auxInt is an offset
|
||||||
auxSymValAndOff // aux is a symbol, auxInt is a ValAndOff
|
auxSymValAndOff // aux is a symbol, auxInt is a ValAndOff
|
||||||
|
|
||||||
|
auxSymInt32 // aux is a symbol, auxInt is a 32-bit integer
|
||||||
)
|
)
|
||||||
|
|
||||||
// A ValAndOff is used by the several opcodes. It holds
|
// A ValAndOff is used by the several opcodes. It holds
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ package ssa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
|
"cmd/internal/obj/arm"
|
||||||
"cmd/internal/obj/x86"
|
"cmd/internal/obj/x86"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -26,6 +27,17 @@ const (
|
||||||
BlockAMD64ORD
|
BlockAMD64ORD
|
||||||
BlockAMD64NAN
|
BlockAMD64NAN
|
||||||
|
|
||||||
|
BlockARMEQ
|
||||||
|
BlockARMNE
|
||||||
|
BlockARMLT
|
||||||
|
BlockARMLE
|
||||||
|
BlockARMGT
|
||||||
|
BlockARMGE
|
||||||
|
BlockARMULT
|
||||||
|
BlockARMULE
|
||||||
|
BlockARMUGT
|
||||||
|
BlockARMUGE
|
||||||
|
|
||||||
BlockPlain
|
BlockPlain
|
||||||
BlockIf
|
BlockIf
|
||||||
BlockCall
|
BlockCall
|
||||||
|
|
@ -56,6 +68,17 @@ var blockString = [...]string{
|
||||||
BlockAMD64ORD: "ORD",
|
BlockAMD64ORD: "ORD",
|
||||||
BlockAMD64NAN: "NAN",
|
BlockAMD64NAN: "NAN",
|
||||||
|
|
||||||
|
BlockARMEQ: "EQ",
|
||||||
|
BlockARMNE: "NE",
|
||||||
|
BlockARMLT: "LT",
|
||||||
|
BlockARMLE: "LE",
|
||||||
|
BlockARMGT: "GT",
|
||||||
|
BlockARMGE: "GE",
|
||||||
|
BlockARMULT: "ULT",
|
||||||
|
BlockARMULE: "ULE",
|
||||||
|
BlockARMUGT: "UGT",
|
||||||
|
BlockARMUGE: "UGE",
|
||||||
|
|
||||||
BlockPlain: "Plain",
|
BlockPlain: "Plain",
|
||||||
BlockIf: "If",
|
BlockIf: "If",
|
||||||
BlockCall: "Call",
|
BlockCall: "Call",
|
||||||
|
|
@ -309,6 +332,15 @@ const (
|
||||||
OpAMD64FlagGT_UGT
|
OpAMD64FlagGT_UGT
|
||||||
OpAMD64FlagGT_ULT
|
OpAMD64FlagGT_ULT
|
||||||
|
|
||||||
|
OpARMADD
|
||||||
|
OpARMADDconst
|
||||||
|
OpARMMOVWconst
|
||||||
|
OpARMCMP
|
||||||
|
OpARMMOVWload
|
||||||
|
OpARMMOVWstore
|
||||||
|
OpARMCALLstatic
|
||||||
|
OpARMLessThan
|
||||||
|
|
||||||
OpAdd8
|
OpAdd8
|
||||||
OpAdd16
|
OpAdd16
|
||||||
OpAdd32
|
OpAdd32
|
||||||
|
|
@ -3915,6 +3947,106 @@ var opcodeTable = [...]opInfo{
|
||||||
reg: regInfo{},
|
reg: regInfo{},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "ADD",
|
||||||
|
argLen: 2,
|
||||||
|
commutative: true,
|
||||||
|
asm: arm.AADD,
|
||||||
|
reg: regInfo{
|
||||||
|
inputs: []inputInfo{
|
||||||
|
{0, 31}, // R0 R1 R2 R3 SP
|
||||||
|
{1, 31}, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
outputs: []regMask{
|
||||||
|
31, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ADDconst",
|
||||||
|
auxType: auxSymOff,
|
||||||
|
argLen: 1,
|
||||||
|
asm: arm.AADD,
|
||||||
|
reg: regInfo{
|
||||||
|
inputs: []inputInfo{
|
||||||
|
{0, 31}, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
outputs: []regMask{
|
||||||
|
31, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MOVWconst",
|
||||||
|
auxType: auxInt32,
|
||||||
|
argLen: 0,
|
||||||
|
rematerializeable: true,
|
||||||
|
asm: arm.AMOVW,
|
||||||
|
reg: regInfo{
|
||||||
|
outputs: []regMask{
|
||||||
|
31, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CMP",
|
||||||
|
argLen: 2,
|
||||||
|
asm: arm.ACMP,
|
||||||
|
reg: regInfo{
|
||||||
|
inputs: []inputInfo{
|
||||||
|
{0, 31}, // R0 R1 R2 R3 SP
|
||||||
|
{1, 31}, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
outputs: []regMask{
|
||||||
|
32, // FLAGS
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MOVWload",
|
||||||
|
argLen: 2,
|
||||||
|
asm: arm.AMOVW,
|
||||||
|
reg: regInfo{
|
||||||
|
inputs: []inputInfo{
|
||||||
|
{0, 31}, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
outputs: []regMask{
|
||||||
|
31, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MOVWstore",
|
||||||
|
argLen: 3,
|
||||||
|
asm: arm.AMOVW,
|
||||||
|
reg: regInfo{
|
||||||
|
inputs: []inputInfo{
|
||||||
|
{0, 31}, // R0 R1 R2 R3 SP
|
||||||
|
{1, 31}, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CALLstatic",
|
||||||
|
auxType: auxSymOff,
|
||||||
|
argLen: 1,
|
||||||
|
reg: regInfo{
|
||||||
|
clobbers: 15, // R0 R1 R2 R3
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "LessThan",
|
||||||
|
argLen: 2,
|
||||||
|
reg: regInfo{
|
||||||
|
inputs: []inputInfo{
|
||||||
|
{0, 32}, // FLAGS
|
||||||
|
},
|
||||||
|
outputs: []regMask{
|
||||||
|
31, // R0 R1 R2 R3 SP
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "Add8",
|
name: "Add8",
|
||||||
argLen: 2,
|
argLen: 2,
|
||||||
|
|
@ -5343,3 +5475,49 @@ var opcodeTable = [...]opInfo{
|
||||||
|
|
||||||
func (o Op) Asm() obj.As { return opcodeTable[o].asm }
|
func (o Op) Asm() obj.As { return opcodeTable[o].asm }
|
||||||
func (o Op) String() string { return opcodeTable[o].name }
|
func (o Op) String() string { return opcodeTable[o].name }
|
||||||
|
|
||||||
|
var registersAMD64 = [...]Register{
|
||||||
|
{0, "AX"},
|
||||||
|
{1, "CX"},
|
||||||
|
{2, "DX"},
|
||||||
|
{3, "BX"},
|
||||||
|
{4, "SP"},
|
||||||
|
{5, "BP"},
|
||||||
|
{6, "SI"},
|
||||||
|
{7, "DI"},
|
||||||
|
{8, "R8"},
|
||||||
|
{9, "R9"},
|
||||||
|
{10, "R10"},
|
||||||
|
{11, "R11"},
|
||||||
|
{12, "R12"},
|
||||||
|
{13, "R13"},
|
||||||
|
{14, "R14"},
|
||||||
|
{15, "R15"},
|
||||||
|
{16, "X0"},
|
||||||
|
{17, "X1"},
|
||||||
|
{18, "X2"},
|
||||||
|
{19, "X3"},
|
||||||
|
{20, "X4"},
|
||||||
|
{21, "X5"},
|
||||||
|
{22, "X6"},
|
||||||
|
{23, "X7"},
|
||||||
|
{24, "X8"},
|
||||||
|
{25, "X9"},
|
||||||
|
{26, "X10"},
|
||||||
|
{27, "X11"},
|
||||||
|
{28, "X12"},
|
||||||
|
{29, "X13"},
|
||||||
|
{30, "X14"},
|
||||||
|
{31, "X15"},
|
||||||
|
{32, "SB"},
|
||||||
|
{33, "FLAGS"},
|
||||||
|
}
|
||||||
|
var registersARM = [...]Register{
|
||||||
|
{0, "R0"},
|
||||||
|
{1, "R1"},
|
||||||
|
{2, "R2"},
|
||||||
|
{3, "R3"},
|
||||||
|
{4, "SP"},
|
||||||
|
{5, "FLAGS"},
|
||||||
|
{6, "SB"},
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,11 @@ type regMask uint64
|
||||||
|
|
||||||
func (m regMask) String() string {
|
func (m regMask) String() string {
|
||||||
s := ""
|
s := ""
|
||||||
for r := register(0); r < numRegs; r++ {
|
for r := register(0); m != 0; r++ {
|
||||||
if m>>r&1 == 0 {
|
if m>>r&1 == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
m &^= regMask(1) << r
|
||||||
if s != "" {
|
if s != "" {
|
||||||
s += " "
|
s += " "
|
||||||
}
|
}
|
||||||
|
|
@ -141,47 +142,6 @@ func (m regMask) String() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make arch-dependent
|
|
||||||
var numRegs register = 64
|
|
||||||
|
|
||||||
var registers = [...]Register{
|
|
||||||
Register{0, "AX"},
|
|
||||||
Register{1, "CX"},
|
|
||||||
Register{2, "DX"},
|
|
||||||
Register{3, "BX"},
|
|
||||||
Register{4, "SP"},
|
|
||||||
Register{5, "BP"},
|
|
||||||
Register{6, "SI"},
|
|
||||||
Register{7, "DI"},
|
|
||||||
Register{8, "R8"},
|
|
||||||
Register{9, "R9"},
|
|
||||||
Register{10, "R10"},
|
|
||||||
Register{11, "R11"},
|
|
||||||
Register{12, "R12"},
|
|
||||||
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, "SB"}, // pseudo-register for global base pointer (aka %rip)
|
|
||||||
|
|
||||||
// TODO: make arch-dependent
|
|
||||||
}
|
|
||||||
|
|
||||||
// countRegs returns the number of set bits in the register mask.
|
// countRegs returns the number of set bits in the register mask.
|
||||||
func countRegs(r regMask) int {
|
func countRegs(r regMask) int {
|
||||||
n := 0
|
n := 0
|
||||||
|
|
@ -231,6 +191,11 @@ type regState struct {
|
||||||
type regAllocState struct {
|
type regAllocState struct {
|
||||||
f *Func
|
f *Func
|
||||||
|
|
||||||
|
registers []Register
|
||||||
|
numRegs register
|
||||||
|
SPReg register
|
||||||
|
SBReg register
|
||||||
|
|
||||||
// for each block, its primary predecessor.
|
// for each block, its primary predecessor.
|
||||||
// A predecessor of b is primary if it is the closest
|
// A predecessor of b is primary if it is the closest
|
||||||
// predecessor that appears before b in the layout order.
|
// predecessor that appears before b in the layout order.
|
||||||
|
|
@ -298,7 +263,7 @@ func (s *regAllocState) freeReg(r register) {
|
||||||
|
|
||||||
// Mark r as unused.
|
// Mark r as unused.
|
||||||
if s.f.pass.debug > regDebug {
|
if s.f.pass.debug > regDebug {
|
||||||
fmt.Printf("freeReg %s (dump %s/%s)\n", registers[r].Name(), v, s.regs[r].c)
|
fmt.Printf("freeReg %s (dump %s/%s)\n", s.registers[r].Name(), v, s.regs[r].c)
|
||||||
}
|
}
|
||||||
s.regs[r] = regState{}
|
s.regs[r] = regState{}
|
||||||
s.values[v.ID].regs &^= regMask(1) << r
|
s.values[v.ID].regs &^= regMask(1) << r
|
||||||
|
|
@ -328,7 +293,7 @@ func (s *regAllocState) setOrig(c *Value, v *Value) {
|
||||||
// r must be unused.
|
// r must be unused.
|
||||||
func (s *regAllocState) assignReg(r register, v *Value, c *Value) {
|
func (s *regAllocState) assignReg(r register, v *Value, c *Value) {
|
||||||
if s.f.pass.debug > regDebug {
|
if s.f.pass.debug > regDebug {
|
||||||
fmt.Printf("assignReg %s %s/%s\n", registers[r].Name(), v, c)
|
fmt.Printf("assignReg %s %s/%s\n", s.registers[r].Name(), v, c)
|
||||||
}
|
}
|
||||||
if s.regs[r].v != nil {
|
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)
|
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)
|
||||||
|
|
@ -338,7 +303,7 @@ func (s *regAllocState) assignReg(r register, v *Value, c *Value) {
|
||||||
s.regs[r] = regState{v, c}
|
s.regs[r] = regState{v, c}
|
||||||
s.values[v.ID].regs |= regMask(1) << r
|
s.values[v.ID].regs |= regMask(1) << r
|
||||||
s.used |= regMask(1) << r
|
s.used |= regMask(1) << r
|
||||||
s.f.setHome(c, ®isters[r])
|
s.f.setHome(c, &s.registers[r])
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocReg chooses a register for v from the set of registers in mask.
|
// allocReg chooses a register for v from the set of registers in mask.
|
||||||
|
|
@ -377,14 +342,14 @@ func (s *regAllocState) allocReg(v *Value, mask regMask) register {
|
||||||
|
|
||||||
// SP and SB are allocated specially. No regular value should
|
// SP and SB are allocated specially. No regular value should
|
||||||
// be allocated to them.
|
// be allocated to them.
|
||||||
mask &^= 1<<4 | 1<<32
|
mask &^= 1<<s.SPReg | 1<<s.SBReg
|
||||||
|
|
||||||
// Find a register to spill. We spill the register containing the value
|
// Find a register to spill. We spill the register containing the value
|
||||||
// whose next use is as far in the future as possible.
|
// whose next use is as far in the future as possible.
|
||||||
// https://en.wikipedia.org/wiki/Page_replacement_algorithm#The_theoretically_optimal_page_replacement_algorithm
|
// https://en.wikipedia.org/wiki/Page_replacement_algorithm#The_theoretically_optimal_page_replacement_algorithm
|
||||||
var r register
|
var r register
|
||||||
maxuse := int32(-1)
|
maxuse := int32(-1)
|
||||||
for t := register(0); t < numRegs; t++ {
|
for t := register(0); t < s.numRegs; t++ {
|
||||||
if mask>>t&1 == 0 {
|
if mask>>t&1 == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -425,10 +390,10 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Op != OpSP {
|
if v.Op != OpSP {
|
||||||
mask &^= 1 << 4 // dont' spill SP
|
mask &^= 1 << s.SPReg // dont' spill SP
|
||||||
}
|
}
|
||||||
if v.Op != OpSB {
|
if v.Op != OpSB {
|
||||||
mask &^= 1 << 32 // don't spill SB
|
mask &^= 1 << s.SBReg // don't spill SB
|
||||||
}
|
}
|
||||||
mask &^= s.reserved()
|
mask &^= s.reserved()
|
||||||
|
|
||||||
|
|
@ -469,12 +434,22 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *regAllocState) init(f *Func) {
|
func (s *regAllocState) init(f *Func) {
|
||||||
if numRegs > noRegister || numRegs > register(unsafe.Sizeof(regMask(0))*8) {
|
s.registers = f.Config.registers
|
||||||
|
s.numRegs = register(len(s.registers))
|
||||||
|
if s.numRegs > noRegister || s.numRegs > register(unsafe.Sizeof(regMask(0))*8) {
|
||||||
panic("too many registers")
|
panic("too many registers")
|
||||||
}
|
}
|
||||||
|
for r := register(0); r < s.numRegs; r++ {
|
||||||
|
if s.registers[r].Name() == "SP" {
|
||||||
|
s.SPReg = r
|
||||||
|
}
|
||||||
|
if s.registers[r].Name() == "SB" {
|
||||||
|
s.SBReg = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.f = f
|
s.f = f
|
||||||
s.regs = make([]regState, numRegs)
|
s.regs = make([]regState, s.numRegs)
|
||||||
s.values = make([]valState, f.NumValues())
|
s.values = make([]valState, f.NumValues())
|
||||||
s.orig = make([]*Value, f.NumValues())
|
s.orig = make([]*Value, f.NumValues())
|
||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
|
|
@ -663,7 +638,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
// Drop any values which are no longer live.
|
// Drop any values which are no longer live.
|
||||||
// This may happen because at the end of p, a value may be
|
// This may happen because at the end of p, a value may be
|
||||||
// live but only used by some other successor of p.
|
// live but only used by some other successor of p.
|
||||||
for r := register(0); r < numRegs; r++ {
|
for r := register(0); r < s.numRegs; r++ {
|
||||||
v := s.regs[r].v
|
v := s.regs[r].v
|
||||||
if v != nil && !liveSet.contains(v.ID) {
|
if v != nil && !liveSet.contains(v.ID) {
|
||||||
s.freeReg(r)
|
s.freeReg(r)
|
||||||
|
|
@ -687,7 +662,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
if s.f.pass.debug > regDebug {
|
if s.f.pass.debug > regDebug {
|
||||||
fmt.Printf("starting merge block %s with end state of %s:\n", b, p)
|
fmt.Printf("starting merge block %s with end state of %s:\n", b, p)
|
||||||
for _, x := range s.endRegs[p.ID] {
|
for _, x := range s.endRegs[p.ID] {
|
||||||
fmt.Printf(" %s: orig:%s cache:%s\n", registers[x.r].Name(), x.v, x.c)
|
fmt.Printf(" %s: orig:%s cache:%s\n", s.registers[x.r].Name(), x.v, x.c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -769,7 +744,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
|
|
||||||
// Save the starting state for use by merge edges.
|
// Save the starting state for use by merge edges.
|
||||||
var regList []startReg
|
var regList []startReg
|
||||||
for r := register(0); r < numRegs; r++ {
|
for r := register(0); r < s.numRegs; r++ {
|
||||||
v := s.regs[r].v
|
v := s.regs[r].v
|
||||||
if v == nil {
|
if v == nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -786,7 +761,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
if s.f.pass.debug > regDebug {
|
if s.f.pass.debug > regDebug {
|
||||||
fmt.Printf("after phis\n")
|
fmt.Printf("after phis\n")
|
||||||
for _, x := range s.startRegs[b.ID] {
|
for _, x := range s.startRegs[b.ID] {
|
||||||
fmt.Printf(" %s: v%d\n", registers[x.r].Name(), x.vid)
|
fmt.Printf(" %s: v%d\n", s.registers[x.r].Name(), x.vid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -866,13 +841,13 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
f.Fatalf("phi %s not at start of block", v)
|
f.Fatalf("phi %s not at start of block", v)
|
||||||
}
|
}
|
||||||
if v.Op == OpSP {
|
if v.Op == OpSP {
|
||||||
s.assignReg(4, v, v) // TODO: arch-dependent
|
s.assignReg(s.SPReg, v, v)
|
||||||
b.Values = append(b.Values, v)
|
b.Values = append(b.Values, v)
|
||||||
s.advanceUses(v)
|
s.advanceUses(v)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if v.Op == OpSB {
|
if v.Op == OpSB {
|
||||||
s.assignReg(32, v, v) // TODO: arch-dependent
|
s.assignReg(s.SBReg, v, v)
|
||||||
b.Values = append(b.Values, v)
|
b.Values = append(b.Values, v)
|
||||||
s.advanceUses(v)
|
s.advanceUses(v)
|
||||||
continue
|
continue
|
||||||
|
|
@ -1030,7 +1005,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
// Save end-of-block register state.
|
// Save end-of-block register state.
|
||||||
// First count how many, this cuts allocations in half.
|
// First count how many, this cuts allocations in half.
|
||||||
k := 0
|
k := 0
|
||||||
for r := register(0); r < numRegs; r++ {
|
for r := register(0); r < s.numRegs; r++ {
|
||||||
v := s.regs[r].v
|
v := s.regs[r].v
|
||||||
if v == nil {
|
if v == nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -1038,7 +1013,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
k++
|
k++
|
||||||
}
|
}
|
||||||
regList := make([]endReg, 0, k)
|
regList := make([]endReg, 0, k)
|
||||||
for r := register(0); r < numRegs; r++ {
|
for r := register(0); r < s.numRegs; r++ {
|
||||||
v := s.regs[r].v
|
v := s.regs[r].v
|
||||||
if v == nil {
|
if v == nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -1053,7 +1028,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||||
for _, x := range s.live[b.ID] {
|
for _, x := range s.live[b.ID] {
|
||||||
liveSet.add(x.ID)
|
liveSet.add(x.ID)
|
||||||
}
|
}
|
||||||
for r := register(0); r < numRegs; r++ {
|
for r := register(0); r < s.numRegs; r++ {
|
||||||
v := s.regs[r].v
|
v := s.regs[r].v
|
||||||
if v == nil {
|
if v == nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -1214,7 +1189,7 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive
|
||||||
|
|
||||||
// Live registers can be sources.
|
// Live registers can be sources.
|
||||||
for _, x := range srcReg {
|
for _, x := range srcReg {
|
||||||
e.set(®isters[x.r], x.v.ID, x.c, false)
|
e.set(&e.s.registers[x.r], x.v.ID, x.c, false)
|
||||||
}
|
}
|
||||||
// So can all of the spill locations.
|
// So can all of the spill locations.
|
||||||
for _, spillID := range stacklive {
|
for _, spillID := range stacklive {
|
||||||
|
|
@ -1226,7 +1201,7 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive
|
||||||
// Figure out all the destinations we need.
|
// Figure out all the destinations we need.
|
||||||
dsts := e.destinations[:0]
|
dsts := e.destinations[:0]
|
||||||
for _, x := range dstReg {
|
for _, x := range dstReg {
|
||||||
dsts = append(dsts, dstRecord{®isters[x.r], x.vid, nil})
|
dsts = append(dsts, dstRecord{&e.s.registers[x.r], x.vid, nil})
|
||||||
}
|
}
|
||||||
// Phis need their args to end up in a specific location.
|
// Phis need their args to end up in a specific location.
|
||||||
for _, v := range e.b.Values {
|
for _, v := range e.b.Values {
|
||||||
|
|
@ -1519,15 +1494,15 @@ func (e *edgeState) findRegFor(typ Type) Location {
|
||||||
// 3) a non-unique register
|
// 3) a non-unique register
|
||||||
x := m &^ e.usedRegs
|
x := m &^ e.usedRegs
|
||||||
if x != 0 {
|
if x != 0 {
|
||||||
return ®isters[pickReg(x)]
|
return &e.s.registers[pickReg(x)]
|
||||||
}
|
}
|
||||||
x = m &^ e.uniqueRegs &^ e.finalRegs
|
x = m &^ e.uniqueRegs &^ e.finalRegs
|
||||||
if x != 0 {
|
if x != 0 {
|
||||||
return ®isters[pickReg(x)]
|
return &e.s.registers[pickReg(x)]
|
||||||
}
|
}
|
||||||
x = m &^ e.uniqueRegs
|
x = m &^ e.uniqueRegs
|
||||||
if x != 0 {
|
if x != 0 {
|
||||||
return ®isters[pickReg(x)]
|
return &e.s.registers[pickReg(x)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// No register is available. Allocate a temp location to spill a register to.
|
// No register is available. Allocate a temp location to spill a register to.
|
||||||
|
|
|
||||||
294
src/cmd/compile/internal/ssa/rewriteARM.go
Normal file
294
src/cmd/compile/internal/ssa/rewriteARM.go
Normal file
|
|
@ -0,0 +1,294 @@
|
||||||
|
// autogenerated from gen/ARM.rules: do not edit!
|
||||||
|
// generated with: cd gen; go run *.go
|
||||||
|
|
||||||
|
package ssa
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
var _ = math.MinInt8 // in case not otherwise used
|
||||||
|
func rewriteValueARM(v *Value, config *Config) bool {
|
||||||
|
switch v.Op {
|
||||||
|
case OpARMADD:
|
||||||
|
return rewriteValueARM_OpARMADD(v, config)
|
||||||
|
case OpAdd32:
|
||||||
|
return rewriteValueARM_OpAdd32(v, config)
|
||||||
|
case OpAddr:
|
||||||
|
return rewriteValueARM_OpAddr(v, config)
|
||||||
|
case OpConst32:
|
||||||
|
return rewriteValueARM_OpConst32(v, config)
|
||||||
|
case OpLess32:
|
||||||
|
return rewriteValueARM_OpLess32(v, config)
|
||||||
|
case OpLoad:
|
||||||
|
return rewriteValueARM_OpLoad(v, config)
|
||||||
|
case OpARMMOVWload:
|
||||||
|
return rewriteValueARM_OpARMMOVWload(v, config)
|
||||||
|
case OpARMMOVWstore:
|
||||||
|
return rewriteValueARM_OpARMMOVWstore(v, config)
|
||||||
|
case OpOffPtr:
|
||||||
|
return rewriteValueARM_OpOffPtr(v, config)
|
||||||
|
case OpStaticCall:
|
||||||
|
return rewriteValueARM_OpStaticCall(v, config)
|
||||||
|
case OpStore:
|
||||||
|
return rewriteValueARM_OpStore(v, config)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpARMADD(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (ADD (MOVWconst [c]) x)
|
||||||
|
// cond:
|
||||||
|
// result: (ADDconst [c] x)
|
||||||
|
for {
|
||||||
|
v_0 := v.Args[0]
|
||||||
|
if v_0.Op != OpARMMOVWconst {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c := v_0.AuxInt
|
||||||
|
x := v.Args[1]
|
||||||
|
v.reset(OpARMADDconst)
|
||||||
|
v.AuxInt = c
|
||||||
|
v.AddArg(x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// match: (ADD x (MOVWconst [c]))
|
||||||
|
// cond:
|
||||||
|
// result: (ADDconst [c] x)
|
||||||
|
for {
|
||||||
|
x := v.Args[0]
|
||||||
|
v_1 := v.Args[1]
|
||||||
|
if v_1.Op != OpARMMOVWconst {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c := v_1.AuxInt
|
||||||
|
v.reset(OpARMADDconst)
|
||||||
|
v.AuxInt = c
|
||||||
|
v.AddArg(x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpAdd32(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (Add32 x y)
|
||||||
|
// cond:
|
||||||
|
// result: (ADD x y)
|
||||||
|
for {
|
||||||
|
x := v.Args[0]
|
||||||
|
y := v.Args[1]
|
||||||
|
v.reset(OpARMADD)
|
||||||
|
v.AddArg(x)
|
||||||
|
v.AddArg(y)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpAddr(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (Addr {sym} base)
|
||||||
|
// cond:
|
||||||
|
// result: (ADDconst {sym} base)
|
||||||
|
for {
|
||||||
|
sym := v.Aux
|
||||||
|
base := v.Args[0]
|
||||||
|
v.reset(OpARMADDconst)
|
||||||
|
v.Aux = sym
|
||||||
|
v.AddArg(base)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpConst32(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (Const32 [val])
|
||||||
|
// cond:
|
||||||
|
// result: (MOVWconst [val])
|
||||||
|
for {
|
||||||
|
val := v.AuxInt
|
||||||
|
v.reset(OpARMMOVWconst)
|
||||||
|
v.AuxInt = val
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpLess32(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (Less32 x y)
|
||||||
|
// cond:
|
||||||
|
// result: (LessThan (CMP x y))
|
||||||
|
for {
|
||||||
|
x := v.Args[0]
|
||||||
|
y := v.Args[1]
|
||||||
|
v.reset(OpARMLessThan)
|
||||||
|
v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
|
||||||
|
v0.AddArg(x)
|
||||||
|
v0.AddArg(y)
|
||||||
|
v.AddArg(v0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpLoad(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (Load <t> ptr mem)
|
||||||
|
// cond: is32BitInt(t)
|
||||||
|
// result: (MOVWload ptr mem)
|
||||||
|
for {
|
||||||
|
t := v.Type
|
||||||
|
ptr := v.Args[0]
|
||||||
|
mem := v.Args[1]
|
||||||
|
if !(is32BitInt(t)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v.reset(OpARMMOVWload)
|
||||||
|
v.AddArg(ptr)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpARMMOVWload(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (MOVWload [off1] {sym1} (ADDconst [off2] {sym2} ptr) mem)
|
||||||
|
// cond: canMergeSym(sym1,sym2)
|
||||||
|
// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||||
|
for {
|
||||||
|
off1 := v.AuxInt
|
||||||
|
sym1 := v.Aux
|
||||||
|
v_0 := v.Args[0]
|
||||||
|
if v_0.Op != OpARMADDconst {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
off2 := v_0.AuxInt
|
||||||
|
sym2 := v_0.Aux
|
||||||
|
ptr := v_0.Args[0]
|
||||||
|
mem := v.Args[1]
|
||||||
|
if !(canMergeSym(sym1, sym2)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v.reset(OpARMMOVWload)
|
||||||
|
v.AuxInt = off1 + off2
|
||||||
|
v.Aux = mergeSym(sym1, sym2)
|
||||||
|
v.AddArg(ptr)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpARMMOVWstore(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (MOVWstore [off1] {sym1} (ADDconst [off2] {sym2} ptr) val mem)
|
||||||
|
// cond: canMergeSym(sym1,sym2)
|
||||||
|
// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||||
|
for {
|
||||||
|
off1 := v.AuxInt
|
||||||
|
sym1 := v.Aux
|
||||||
|
v_0 := v.Args[0]
|
||||||
|
if v_0.Op != OpARMADDconst {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
off2 := v_0.AuxInt
|
||||||
|
sym2 := v_0.Aux
|
||||||
|
ptr := v_0.Args[0]
|
||||||
|
val := v.Args[1]
|
||||||
|
mem := v.Args[2]
|
||||||
|
if !(canMergeSym(sym1, sym2)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v.reset(OpARMMOVWstore)
|
||||||
|
v.AuxInt = off1 + off2
|
||||||
|
v.Aux = mergeSym(sym1, sym2)
|
||||||
|
v.AddArg(ptr)
|
||||||
|
v.AddArg(val)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpOffPtr(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (OffPtr [off] ptr)
|
||||||
|
// cond:
|
||||||
|
// result: (ADD (MOVWconst <config.Frontend().TypeInt32()> [off]) ptr)
|
||||||
|
for {
|
||||||
|
off := v.AuxInt
|
||||||
|
ptr := v.Args[0]
|
||||||
|
v.reset(OpARMADD)
|
||||||
|
v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.Frontend().TypeInt32())
|
||||||
|
v0.AuxInt = off
|
||||||
|
v.AddArg(v0)
|
||||||
|
v.AddArg(ptr)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpStaticCall(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (StaticCall [argwid] {target} mem)
|
||||||
|
// cond:
|
||||||
|
// result: (CALLstatic [argwid] {target} mem)
|
||||||
|
for {
|
||||||
|
argwid := v.AuxInt
|
||||||
|
target := v.Aux
|
||||||
|
mem := v.Args[0]
|
||||||
|
v.reset(OpARMCALLstatic)
|
||||||
|
v.AuxInt = argwid
|
||||||
|
v.Aux = target
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteValueARM_OpStore(v *Value, config *Config) bool {
|
||||||
|
b := v.Block
|
||||||
|
_ = b
|
||||||
|
// match: (Store [4] ptr val mem)
|
||||||
|
// cond:
|
||||||
|
// result: (MOVWstore ptr val mem)
|
||||||
|
for {
|
||||||
|
if v.AuxInt != 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ptr := v.Args[0]
|
||||||
|
val := v.Args[1]
|
||||||
|
mem := v.Args[2]
|
||||||
|
v.reset(OpARMMOVWstore)
|
||||||
|
v.AddArg(ptr)
|
||||||
|
v.AddArg(val)
|
||||||
|
v.AddArg(mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func rewriteBlockARM(b *Block) bool {
|
||||||
|
switch b.Kind {
|
||||||
|
case BlockIf:
|
||||||
|
// match: (If (LessThan cc) yes no)
|
||||||
|
// cond:
|
||||||
|
// result: (LT cc yes no)
|
||||||
|
for {
|
||||||
|
v := b.Control
|
||||||
|
if v.Op != OpARMLessThan {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cc := v.Args[0]
|
||||||
|
yes := b.Succs[0]
|
||||||
|
no := b.Succs[1]
|
||||||
|
b.Kind = BlockARMLT
|
||||||
|
b.SetControl(cc)
|
||||||
|
b.Succs[0] = yes
|
||||||
|
b.Succs[1] = no
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue