From 4c9a470d468b6b6fa0520f32e034a1762dba3f9d Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 21 Mar 2016 22:57:26 -0700 Subject: [PATCH] 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 --- src/cmd/compile/internal/arm/galign.go | 6 + src/cmd/compile/internal/arm/ssa.go | 152 +++++++++ src/cmd/compile/internal/gc/ssa.go | 10 +- src/cmd/compile/internal/ssa/config.go | 8 + src/cmd/compile/internal/ssa/gen/ARM.rules | 32 ++ src/cmd/compile/internal/ssa/gen/ARMOps.go | 66 ++++ src/cmd/compile/internal/ssa/gen/decOps.go | 7 +- .../compile/internal/ssa/gen/genericOps.go | 7 +- src/cmd/compile/internal/ssa/gen/main.go | 13 + src/cmd/compile/internal/ssa/op.go | 2 + src/cmd/compile/internal/ssa/opGen.go | 178 +++++++++++ src/cmd/compile/internal/ssa/regalloc.go | 105 +++---- src/cmd/compile/internal/ssa/rewriteARM.go | 294 ++++++++++++++++++ 13 files changed, 807 insertions(+), 73 deletions(-) create mode 100644 src/cmd/compile/internal/arm/ssa.go create mode 100644 src/cmd/compile/internal/ssa/gen/ARM.rules create mode 100644 src/cmd/compile/internal/ssa/gen/ARMOps.go create mode 100644 src/cmd/compile/internal/ssa/rewriteARM.go diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go index d89b852d7f..e05f4d06bb 100644 --- a/src/cmd/compile/internal/arm/galign.go +++ b/src/cmd/compile/internal/arm/galign.go @@ -6,6 +6,7 @@ package arm import ( "cmd/compile/internal/gc" + "cmd/compile/internal/ssa" "cmd/internal/obj/arm" ) @@ -65,6 +66,11 @@ func Main() { gc.Thearch.Doregbits = doregbits 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.Exit(0) } diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go new file mode 100644 index 0000000000..a9baf43f7e --- /dev/null +++ b/src/cmd/compile/internal/arm/ssa.go @@ -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]}) + } +} diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7467acb028..93b820b17e 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -30,8 +30,14 @@ func initssa() *ssa.Config { } func shouldssa(fn *Node) bool { - if Thearch.Thestring != "amd64" { - return false + switch Thearch.Thestring { + default: + // Only available for testing. + if os.Getenv("SSATEST") == "" { + return false + } + // Generally available. + case "amd64": } if !ssaEnabled { return false diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index e7f4aece2a..d0de429f35 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -18,6 +18,7 @@ type Config struct { PtrSize int64 // 4 or 8 lowerBlock func(*Block) bool // lowering function lowerValue func(*Value, *Config) bool // lowering function + registers []Register // machine registers fe Frontend // callbacks into compiler frontend HTML *HTMLWriter // html writer, for debugging 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.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 + c.registers = registersAMD64[:] case "386": c.IntSize = 4 c.PtrSize = 4 c.lowerBlock = rewriteBlockAMD64 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: fe.Unimplementedf(0, "arch %s not implemented", arch) } diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules new file mode 100644 index 0000000000..273500fc38 --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/ARM.rules @@ -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 [off]) ptr) + +(Addr {sym} base) -> (ADDconst {sym} base) + +(Load 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) diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go new file mode 100644 index 0000000000..2b2a750ebc --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -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>r&1 == 0 { continue } + m &^= regMask(1) << r if s != "" { s += " " } @@ -141,47 +142,6 @@ func (m regMask) String() string { 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. func countRegs(r regMask) int { n := 0 @@ -231,6 +191,11 @@ type regState struct { type regAllocState struct { f *Func + registers []Register + numRegs register + SPReg register + SBReg register + // 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. @@ -298,7 +263,7 @@ func (s *regAllocState) freeReg(r register) { // Mark r as unused. 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.values[v.ID].regs &^= regMask(1) << r @@ -328,7 +293,7 @@ func (s *regAllocState) setOrig(c *Value, v *Value) { // r must be unused. func (s *regAllocState) assignReg(r register, v *Value, c *Value) { 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 { 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.values[v.ID].regs |= 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. @@ -377,14 +342,14 @@ func (s *regAllocState) allocReg(v *Value, mask regMask) register { // SP and SB are allocated specially. No regular value should // be allocated to them. - mask &^= 1<<4 | 1<<32 + mask &^= 1<>t&1 == 0 { continue } @@ -425,10 +390,10 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line } if v.Op != OpSP { - mask &^= 1 << 4 // dont' spill SP + mask &^= 1 << s.SPReg // dont' spill SP } if v.Op != OpSB { - mask &^= 1 << 32 // don't spill SB + mask &^= 1 << s.SBReg // don't spill SB } mask &^= s.reserved() @@ -469,12 +434,22 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line } 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") } + 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.regs = make([]regState, numRegs) + s.regs = make([]regState, s.numRegs) s.values = make([]valState, f.NumValues()) s.orig = make([]*Value, f.NumValues()) for _, b := range f.Blocks { @@ -663,7 +638,7 @@ func (s *regAllocState) regalloc(f *Func) { // Drop any values which are no longer live. // This may happen because at the end of p, a value may be // 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 if v != nil && !liveSet.contains(v.ID) { s.freeReg(r) @@ -687,7 +662,7 @@ func (s *regAllocState) regalloc(f *Func) { if s.f.pass.debug > 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) + 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. var regList []startReg - for r := register(0); r < numRegs; r++ { + for r := register(0); r < s.numRegs; r++ { v := s.regs[r].v if v == nil { continue @@ -786,7 +761,7 @@ func (s *regAllocState) regalloc(f *Func) { if s.f.pass.debug > 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) + 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) } if v.Op == OpSP { - s.assignReg(4, v, v) // TODO: arch-dependent + s.assignReg(s.SPReg, v, v) b.Values = append(b.Values, v) s.advanceUses(v) continue } if v.Op == OpSB { - s.assignReg(32, v, v) // TODO: arch-dependent + s.assignReg(s.SBReg, v, v) b.Values = append(b.Values, v) s.advanceUses(v) continue @@ -1030,7 +1005,7 @@ func (s *regAllocState) regalloc(f *Func) { // Save end-of-block register state. // First count how many, this cuts allocations in half. k := 0 - for r := register(0); r < numRegs; r++ { + for r := register(0); r < s.numRegs; r++ { v := s.regs[r].v if v == nil { continue @@ -1038,7 +1013,7 @@ func (s *regAllocState) regalloc(f *Func) { 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 if v == nil { continue @@ -1053,7 +1028,7 @@ func (s *regAllocState) regalloc(f *Func) { for _, x := range s.live[b.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 if v == nil { continue @@ -1214,7 +1189,7 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive // Live registers can be sources. 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. 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. dsts := e.destinations[:0] 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. for _, v := range e.b.Values { @@ -1519,15 +1494,15 @@ func (e *edgeState) findRegFor(typ Type) Location { // 3) a non-unique register x := m &^ e.usedRegs if x != 0 { - return ®isters[pickReg(x)] + return &e.s.registers[pickReg(x)] } x = m &^ e.uniqueRegs &^ e.finalRegs if x != 0 { - return ®isters[pickReg(x)] + return &e.s.registers[pickReg(x)] } x = m &^ e.uniqueRegs 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. diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go new file mode 100644 index 0000000000..67eff5065d --- /dev/null +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -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 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 [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 +} -- 2.48.1