]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.ssa] Merge branch 'master' into dev.ssa
authorDavid Chase <drchase@google.com>
Fri, 27 May 2016 19:18:49 +0000 (15:18 -0400)
committerDavid Chase <drchase@google.com>
Fri, 27 May 2016 19:19:33 +0000 (15:19 -0400)
Change-Id: Iabc80b6e0734efbd234d998271e110d2eaad41dd

1  2 
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/config.go
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/regalloc.go
src/runtime/mgc.go
src/runtime/runtime1.go

index e824b476e1a9ab271075ad7ffcbe8ae1528185ab,a107f91ef3e70a33b68a0476a07a5bad047c7657..ecff7c07dea5932da3ac84d5b769d838c3329979
@@@ -161,23 -161,19 +161,19 @@@ func buildssa(fn *Node) *ssa.Func 
                                // the function.
                                s.returns = append(s.returns, n)
                        }
-               case PAUTO | PHEAP:
-                       // TODO this looks wrong for PAUTO|PHEAP, no vardef, but also no definition
-                       aux := s.lookupSymbol(n, &ssa.AutoSymbol{Typ: n.Type, Node: n})
-                       s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
-               case PPARAM | PHEAP, PPARAMOUT | PHEAP:
-               // This ends up wrong, have to do it at the PARAM node instead.
+                       if n.Class == PPARAM && s.canSSA(n) && n.Type.IsPtrShaped() {
+                               s.ptrargs = append(s.ptrargs, n)
+                               n.SetNotLiveAtEnd(true) // SSA takes care of this explicitly
+                       }
                case PAUTO:
                        // processed at each use, to prevent Addr coming
                        // before the decl.
+               case PAUTOHEAP:
+                       // moved to heap - already handled by frontend
                case PFUNC:
                        // local function - already handled by frontend
                default:
-                       str := ""
-                       if n.Class&PHEAP != 0 {
-                               str = ",heap"
-                       }
-                       s.Unimplementedf("local variable with class %s%s unimplemented", classnames[n.Class&^PHEAP], str)
+                       s.Unimplementedf("local variable with class %s unimplemented", classnames[n.Class])
                }
        }
  
                return nil
        }
  
+       prelinkNumvars := s.f.NumValues()
+       sparseDefState := s.locatePotentialPhiFunctions(fn)
        // Link up variable uses to variable definitions
-       s.linkForwardReferences()
+       s.linkForwardReferences(sparseDefState)
+       if ssa.BuildStats > 0 {
+               s.f.LogStat("build", s.f.NumBlocks(), "blocks", prelinkNumvars, "vars_before",
+                       s.f.NumValues(), "vars_after", prelinkNumvars*s.f.NumBlocks(), "ssa_phi_loc_cutoff_score")
+       }
  
        // Don't carry reference this around longer than necessary
        s.exitCode = Nodes{}
@@@ -282,9 -286,13 +286,13 @@@ type state struct 
        // list of FwdRef values.
        fwdRefs []*ssa.Value
  
-       // list of PPARAMOUT (return) variables. Does not include PPARAM|PHEAP vars.
+       // list of PPARAMOUT (return) variables.
        returns []*Node
  
+       // list of PPARAM SSA-able pointer-shaped args. We ensure these are live
+       // throughout the function to help users avoid premature finalizers.
+       ptrargs []*Node
        cgoUnsafeArgs bool
        noWB          bool
        WBLineno      int32 // line number of first write barrier. 0=no write barriers
@@@ -577,24 -585,9 +585,9 @@@ func (s *state) stmt(n *Node) 
                return
  
        case ODCL:
-               if n.Left.Class&PHEAP == 0 {
-                       return
-               }
-               if compiling_runtime {
-                       Fatalf("%v escapes to heap, not allowed in runtime.", n)
-               }
-               // TODO: the old pass hides the details of PHEAP
-               // variables behind ONAME nodes. Figure out if it's better
-               // to rewrite the tree and make the heapaddr construct explicit
-               // or to keep this detail hidden behind the scenes.
-               palloc := prealloc[n.Left]
-               if palloc == nil {
-                       palloc = callnew(n.Left.Type)
-                       prealloc[n.Left] = palloc
+               if n.Left.Class == PAUTOHEAP {
+                       Fatalf("DCL %v", n)
                }
-               r := s.expr(palloc)
-               s.assign(n.Left.Name.Heapaddr, r, false, false, n.Lineno, 0)
  
        case OLABEL:
                sym := n.Left.Sym
@@@ -980,8 -973,7 +973,7 @@@ func (s *state) exit() *ssa.Block 
  
        // Store SSAable PPARAMOUT variables back to stack locations.
        for _, n := range s.returns {
-               aux := &ssa.ArgSymbol{Typ: n.Type, Node: n}
-               addr := s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
+               addr := s.decladdrs[n]
                val := s.variable(n, n.Type)
                s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, n, s.mem())
                s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, n.Type.Size(), addr, val, s.mem())
                // currently.
        }
  
+       // Keep input pointer args live until the return. This is a bandaid
+       // fix for 1.7 for what will become in 1.8 explicit runtime.KeepAlive calls.
+       // For <= 1.7 we guarantee that pointer input arguments live to the end of
+       // the function to prevent premature (from the user's point of view)
+       // execution of finalizers. See issue 15277.
+       // TODO: remove for 1.8?
+       for _, n := range s.ptrargs {
+               s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
+       }
        // Do actual return.
        m := s.mem()
        b := s.endBlock()
@@@ -1135,7 -1137,6 +1137,7 @@@ var opToSSA = map[opAndType]ssa.Op
        opAndType{OEQ, TFUNC}:      ssa.OpEqPtr,
        opAndType{OEQ, TMAP}:       ssa.OpEqPtr,
        opAndType{OEQ, TCHAN}:      ssa.OpEqPtr,
 +      opAndType{OEQ, TPTR32}:     ssa.OpEqPtr,
        opAndType{OEQ, TPTR64}:     ssa.OpEqPtr,
        opAndType{OEQ, TUINTPTR}:   ssa.OpEqPtr,
        opAndType{OEQ, TUNSAFEPTR}: ssa.OpEqPtr,
        opAndType{ONE, TFUNC}:      ssa.OpNeqPtr,
        opAndType{ONE, TMAP}:       ssa.OpNeqPtr,
        opAndType{ONE, TCHAN}:      ssa.OpNeqPtr,
 +      opAndType{ONE, TPTR32}:     ssa.OpNeqPtr,
        opAndType{ONE, TPTR64}:     ssa.OpNeqPtr,
        opAndType{ONE, TUINTPTR}:   ssa.OpNeqPtr,
        opAndType{ONE, TUNSAFEPTR}: ssa.OpNeqPtr,
@@@ -1428,9 -1428,6 +1430,6 @@@ func (s *state) expr(n *Node) *ssa.Valu
        case OCFUNC:
                aux := s.lookupSymbol(n, &ssa.ExternSymbol{Typ: n.Type, Sym: n.Left.Sym})
                return s.entryNewValue1A(ssa.OpAddr, n.Type, aux, s.sb)
-       case OPARAM:
-               addr := s.addr(n, false)
-               return s.newValue2(ssa.OpLoad, n.Left.Type, addr, s.mem())
        case ONAME:
                if n.Class == PFUNC {
                        // "value" of a function is the address of the function's closure
@@@ -2597,11 -2594,9 +2596,11 @@@ func (s *state) call(n *Node, k callKin
        // Defer/go args
        if k != callNormal {
                // Write argsize and closure (args to Newproc/Deferproc).
 +              argStart := Ctxt.FixedFrameSize()
                argsize := s.constInt32(Types[TUINT32], int32(stksize))
 -              s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, 4, s.sp, argsize, s.mem())
 -              addr := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(Types[TUINTPTR]), int64(Widthptr), s.sp)
 +              addr := s.entryNewValue1I(ssa.OpOffPtr, Types[TUINTPTR], argStart, s.sp)
 +              s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, 4, addr, argsize, s.mem())
 +              addr = s.entryNewValue1I(ssa.OpOffPtr, Ptrto(Types[TUINTPTR]), argStart+int64(Widthptr), s.sp)
                s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), addr, closure, s.mem())
                stksize += 2 * int64(Widthptr)
        }
  
        // Start exit block, find address of result.
        s.startBlock(bNext)
+       // Keep input pointer args live across calls.  This is a bandaid until 1.8.
+       for _, n := range s.ptrargs {
+               s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
+       }
        res := n.Left.Type.Results()
        if res.NumFields() == 0 || k != callNormal {
                // call has no return value. Continue with the next statement.
@@@ -2724,10 -2723,8 +2727,8 @@@ func (s *state) addr(n *Node, bounded b
                        // that cse works on their addresses
                        aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
                        return s.newValue1A(ssa.OpAddr, t, aux, s.sp)
-               case PAUTO | PHEAP, PPARAM | PHEAP, PPARAMOUT | PHEAP, PPARAMREF:
-                       return s.expr(n.Name.Heapaddr)
                default:
-                       s.Unimplementedf("variable address class %v not implemented", n.Class)
+                       s.Unimplementedf("variable address class %v not implemented", classnames[n.Class])
                        return nil
                }
        case OINDREG:
        case OCLOSUREVAR:
                return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
                        s.entryNewValue0(ssa.OpGetClosurePtr, Ptrto(Types[TUINT8])))
-       case OPARAM:
-               p := n.Left
-               if p.Op != ONAME || !(p.Class == PPARAM|PHEAP || p.Class == PPARAMOUT|PHEAP) {
-                       s.Fatalf("OPARAM not of ONAME,{PPARAM,PPARAMOUT}|PHEAP, instead %s", nodedump(p, 0))
-               }
-               // Recover original offset to address passed-in param value.
-               original_p := *p
-               original_p.Xoffset = n.Xoffset
-               aux := &ssa.ArgSymbol{Typ: n.Type, Node: &original_p}
-               return s.entryNewValue1A(ssa.OpAddr, t, aux, s.sp)
        case OCONVNOP:
                addr := s.addr(n.Left, bounded)
                return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
@@@ -2808,12 -2794,14 +2798,14 @@@ func (s *state) canSSA(n *Node) bool 
        if n.Addrtaken {
                return false
        }
-       if n.Class&PHEAP != 0 {
+       if n.isParamHeapCopy() {
                return false
        }
+       if n.Class == PAUTOHEAP {
+               Fatalf("canSSA of PAUTOHEAP %v", n)
+       }
        switch n.Class {
-       case PEXTERN, PPARAMREF:
-               // TODO: maybe treat PPARAMREF with an Arg-like op to read from closure?
+       case PEXTERN:
                return false
        case PPARAMOUT:
                if hasdefer {
@@@ -2958,7 -2946,7 +2950,7 @@@ func (s *state) check(cmp *ssa.Value, f
  // is started to load the return values.
  func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Value) []*ssa.Value {
        // Write args to the stack
 -      var off int64 // TODO: arch-dependent starting offset?
 +      off := Ctxt.FixedFrameSize()
        for _, arg := range args {
                t := arg.Type
                off = Rnd(off, t.Alignment())
        b.AddEdgeTo(bNext)
        s.startBlock(bNext)
  
+       // Keep input pointer args live across calls.  This is a bandaid until 1.8.
+       for _, n := range s.ptrargs {
+               s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
+       }
        // Load results
        res := make([]*ssa.Value, len(results))
        for i, t := range results {
@@@ -3033,9 -3026,10 +3030,9 @@@ func (s *state) insertWBmove(t *Type, l
  
        aux := &ssa.ExternSymbol{Typ: Types[TBOOL], Sym: syslook("writeBarrier").Sym}
        flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TUINT32]), aux, s.sb)
 -      // TODO: select the .enabled field. It is currently first, so not needed for now.
 -      // Load word, test byte, avoiding partial register write from load byte.
 +      // Load word, test word, avoiding partial register write from load byte.
        flag := s.newValue2(ssa.OpLoad, Types[TUINT32], flagaddr, s.mem())
 -      flag = s.newValue1(ssa.OpTrunc64to8, Types[TBOOL], flag)
 +      flag = s.newValue2(ssa.OpNeq32, Types[TBOOL], flag, s.constInt32(Types[TUINT32], 0))
        b := s.endBlock()
        b.Kind = ssa.BlockIf
        b.Likely = ssa.BranchUnlikely
@@@ -3083,9 -3077,10 +3080,9 @@@ func (s *state) insertWBstore(t *Type, 
  
        aux := &ssa.ExternSymbol{Typ: Types[TBOOL], Sym: syslook("writeBarrier").Sym}
        flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TUINT32]), aux, s.sb)
 -      // TODO: select the .enabled field. It is currently first, so not needed for now.
 -      // Load word, test byte, avoiding partial register write from load byte.
 +      // Load word, test word, avoiding partial register write from load byte.
        flag := s.newValue2(ssa.OpLoad, Types[TUINT32], flagaddr, s.mem())
 -      flag = s.newValue1(ssa.OpTrunc64to8, Types[TBOOL], flag)
 +      flag = s.newValue2(ssa.OpNeq32, Types[TBOOL], flag, s.constInt32(Types[TUINT32], 0))
        b := s.endBlock()
        b.Kind = ssa.BlockIf
        b.Likely = ssa.BranchUnlikely
@@@ -3743,7 -3738,8 +3740,8 @@@ func (s *state) mem() *ssa.Value 
        return s.variable(&memVar, ssa.TypeMem)
  }
  
- func (s *state) linkForwardReferences() {
+ func (s *state) linkForwardReferences(dm *sparseDefState) {
        // Build SSA graph. Each variable on its first use in a basic block
        // leaves a FwdRef in that block representing the incoming value
        // of that variable. This function links that ref up with possible definitions,
        for len(s.fwdRefs) > 0 {
                v := s.fwdRefs[len(s.fwdRefs)-1]
                s.fwdRefs = s.fwdRefs[:len(s.fwdRefs)-1]
-               s.resolveFwdRef(v)
+               s.resolveFwdRef(v, dm)
        }
  }
  
  // resolveFwdRef modifies v to be the variable's value at the start of its block.
  // v must be a FwdRef op.
- func (s *state) resolveFwdRef(v *ssa.Value) {
+ func (s *state) resolveFwdRef(v *ssa.Value, dm *sparseDefState) {
        b := v.Block
        name := v.Aux.(*Node)
        v.Aux = nil
        args := argstore[:0]
        for _, e := range b.Preds {
                p := e.Block()
+               p = dm.FindBetterDefiningBlock(name, p) // try sparse improvement on p
                args = append(args, s.lookupVarOutgoing(p, v.Type, name, v.Line))
        }
  
@@@ -4096,7 -4093,7 +4095,7 @@@ func AddAux(a *obj.Addr, v *ssa.Value) 
        AddAux2(a, v, v.AuxInt)
  }
  func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
 -      if a.Type != obj.TYPE_MEM {
 +      if a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR {
                v.Fatalf("bad AddAux addr %v", a)
        }
        // add integer offset
index 26f16bae580b3dc9fa351c2f10e372ca150cd936,e8ab17806c0e151b64ae71779028b1cd83a3a4ac..ddb58d9f79b92c166a2fd2ad221401927d2ab839
@@@ -9,23 -9,24 +9,25 @@@ import 
        "crypto/sha1"
        "fmt"
        "os"
+       "strconv"
        "strings"
  )
  
  type Config struct {
-       arch         string                     // "amd64", etc.
-       IntSize      int64                      // 4 or 8
-       PtrSize      int64                      // 4 or 8
-       lowerBlock   func(*Block) bool          // lowering function
-       lowerValue   func(*Value, *Config) bool // lowering function
-       registers    []Register                 // machine registers
-       flagRegMask  regMask                    // flag register mask
-       fe           Frontend                   // callbacks into compiler frontend
-       HTML         *HTMLWriter                // html writer, for debugging
-       ctxt         *obj.Link                  // Generic arch information
-       optimize     bool                       // Do optimization
-       noDuffDevice bool                       // Don't use Duff's device
-       curFunc      *Func
+       arch            string                     // "amd64", etc.
+       IntSize         int64                      // 4 or 8
+       PtrSize         int64                      // 4 or 8
+       lowerBlock      func(*Block) bool          // lowering function
+       lowerValue      func(*Value, *Config) bool // lowering function
+       registers       []Register                 // machine registers
++      flagRegMask     regMask                    // flag register mask
+       fe              Frontend                   // callbacks into compiler frontend
+       HTML            *HTMLWriter                // html writer, for debugging
+       ctxt            *obj.Link                  // Generic arch information
+       optimize        bool                       // Do optimization
+       noDuffDevice    bool                       // Don't use Duff's device
+       sparsePhiCutoff uint64                     // Sparse phi location algorithm used above this #blocks*#variables score
+       curFunc         *Func
  
        // TODO: more stuff. Compiler flags of interest, ...
  
@@@ -127,7 -128,6 +129,7 @@@ func NewConfig(arch string, fe Frontend
                c.lowerBlock = rewriteBlockAMD64
                c.lowerValue = rewriteValueAMD64
                c.registers = registersAMD64[:]
 +              c.flagRegMask = flagRegMaskAMD64
        case "386":
                c.IntSize = 4
                c.PtrSize = 4
                c.lowerBlock = rewriteBlockARM
                c.lowerValue = rewriteValueARM
                c.registers = registersARM[:]
 +              c.flagRegMask = flagRegMaskARM
        default:
                fe.Unimplementedf(0, "arch %s not implemented", arch)
        }
  
        c.logfiles = make(map[string]*os.File)
  
+       // cutoff is compared with product of numblocks and numvalues,
+       // if product is smaller than cutoff, use old non-sparse method.
+       // cutoff == 0 implies all sparse.
+       // cutoff == -1 implies none sparse.
+       // Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
+       // TODO: get this from a flag, not an environment variable
+       c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
+       ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
+       if ev != "" {
+               v, err := strconv.ParseInt(ev, 10, 64)
+               if err != nil {
+                       fe.Fatalf(0, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
+               }
+               c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
+       }
        return c
  }
  
- func (c *Config) Frontend() Frontend { return c.fe }
+ func (c *Config) Frontend() Frontend      { return c.fe }
+ func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
  
  // NewFunc returns a new, empty function object.
  // Caller must call f.Free() before calling NewFunc again.
@@@ -262,3 -278,7 +281,7 @@@ func (c *Config) DebugHashMatch(evname
        }
        return false
  }
+ func (c *Config) DebugNameMatch(evname, name string) bool {
+       return os.Getenv(evname) == name
+ }
index 8c40c77f06d5cbd738f7635ece77a1e11520a727,8388ea8946873045f5bce6ad1fdfa03c1a8c12b0..94650141949c8be6250eed382a5ef1547a82772d
@@@ -326,15 -326,15 +326,15 @@@ var genericOps = []opData
  
        // Conversions: signed extensions, zero (unsigned) extensions, truncations
        {name: "SignExt8to16", argLength: 1, typ: "Int16"},
 -      {name: "SignExt8to32", argLength: 1},
 +      {name: "SignExt8to32", argLength: 1, typ: "Int32"},
        {name: "SignExt8to64", argLength: 1},
 -      {name: "SignExt16to32", argLength: 1},
 +      {name: "SignExt16to32", argLength: 1, typ: "Int32"},
        {name: "SignExt16to64", argLength: 1},
        {name: "SignExt32to64", argLength: 1},
        {name: "ZeroExt8to16", argLength: 1, typ: "UInt16"},
 -      {name: "ZeroExt8to32", argLength: 1},
 +      {name: "ZeroExt8to32", argLength: 1, typ: "UInt32"},
        {name: "ZeroExt8to64", argLength: 1},
 -      {name: "ZeroExt16to32", argLength: 1},
 +      {name: "ZeroExt16to32", argLength: 1, typ: "UInt32"},
        {name: "ZeroExt16to64", argLength: 1},
        {name: "ZeroExt32to64", argLength: 1},
        {name: "Trunc16to8", argLength: 1},
        {name: "ComplexImag", argLength: 1}, // imag(arg0)
  
        // Strings
-       {name: "StringMake", argLength: 2}, // arg0=ptr, arg1=len
-       {name: "StringPtr", argLength: 1},  // ptr(arg0)
-       {name: "StringLen", argLength: 1},  // len(arg0)
+       {name: "StringMake", argLength: 2},                // arg0=ptr, arg1=len
+       {name: "StringPtr", argLength: 1, typ: "BytePtr"}, // ptr(arg0)
+       {name: "StringLen", argLength: 1, typ: "Int"},     // len(arg0)
  
        // Interfaces
        {name: "IMake", argLength: 2},                // arg0=itab, arg1=data
        {name: "LoadReg", argLength: 1},
  
        // Used during ssa construction. Like Copy, but the arg has not been specified yet.
-       {name: "FwdRef"},
+       {name: "FwdRef", aux: "Sym"},
  
        // Unknown value. Used for Values whose values don't matter because they are dead code.
        {name: "Unknown"},
        {name: "VarDef", argLength: 1, aux: "Sym", typ: "Mem"}, // aux is a *gc.Node of a variable that is about to be initialized.  arg0=mem, returns mem
        {name: "VarKill", argLength: 1, aux: "Sym"},            // aux is a *gc.Node of a variable that is known to be dead.  arg0=mem, returns mem
        {name: "VarLive", argLength: 1, aux: "Sym"},            // aux is a *gc.Node of a variable that must be kept live.  arg0=mem, returns mem
+       {name: "KeepAlive", argLength: 2, typ: "Mem"},          // arg[0] is a value that must be kept alive until this mark.  arg[1]=mem, returns mem
  }
  
  //     kind           control    successors       implicit exit
index 558d041624a1310f647e5e6306da0858dfef2aa7,383f1ae5f3d16550b601d86033022fea1ec60fa2..fc8214b9d9263e470955c4c07c15220c05879431
@@@ -325,69 -325,12 +325,69 @@@ const 
  
        OpARMADD
        OpARMADDconst
 -      OpARMMOVWconst
 +      OpARMSUB
 +      OpARMSUBconst
 +      OpARMRSB
 +      OpARMRSBconst
 +      OpARMMUL
 +      OpARMHMUL
 +      OpARMHMULU
 +      OpARMAND
 +      OpARMANDconst
 +      OpARMOR
 +      OpARMORconst
 +      OpARMXOR
 +      OpARMXORconst
 +      OpARMBIC
 +      OpARMBICconst
 +      OpARMMVN
 +      OpARMSLL
 +      OpARMSLLconst
 +      OpARMSRL
 +      OpARMSRLconst
 +      OpARMSRA
 +      OpARMSRAconst
        OpARMCMP
 +      OpARMCMPconst
 +      OpARMCMN
 +      OpARMCMNconst
 +      OpARMTST
 +      OpARMTSTconst
 +      OpARMTEQ
 +      OpARMTEQconst
 +      OpARMMOVWconst
 +      OpARMMOVBload
 +      OpARMMOVBUload
 +      OpARMMOVHload
 +      OpARMMOVHUload
        OpARMMOVWload
 +      OpARMMOVBstore
 +      OpARMMOVHstore
        OpARMMOVWstore
 +      OpARMMOVBreg
 +      OpARMMOVBUreg
 +      OpARMMOVHreg
 +      OpARMMOVHUreg
        OpARMCALLstatic
 +      OpARMCALLclosure
 +      OpARMCALLdefer
 +      OpARMCALLgo
 +      OpARMCALLinter
 +      OpARMLoweredNilCheck
 +      OpARMEqual
 +      OpARMNotEqual
        OpARMLessThan
 +      OpARMLessEqual
 +      OpARMGreaterThan
 +      OpARMGreaterEqual
 +      OpARMLessThanU
 +      OpARMLessEqualU
 +      OpARMGreaterThanU
 +      OpARMGreaterEqualU
 +      OpARMDUFFZERO
 +      OpARMDUFFCOPY
 +      OpARMLoweredZero
 +      OpARMLoweredMove
  
        OpAdd8
        OpAdd16
        OpVarDef
        OpVarKill
        OpVarLive
+       OpKeepAlive
  )
  
  var opcodeTable = [...]opInfo{
                asm:         arm.AADD,
                reg: regInfo{
                        inputs: []inputInfo{
 -                              {0, 31}, // R0 R1 R2 R3 SP
 -                              {1, 31}, // R0 R1 R2 R3 SP
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                        outputs: []regMask{
 -                              31, // R0 R1 R2 R3 SP
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                },
        },
                asm:     arm.AADD,
                reg: regInfo{
                        inputs: []inputInfo{
 -                              {0, 31}, // R0 R1 R2 R3 SP
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
                        },
                        outputs: []regMask{
 -                              31, // R0 R1 R2 R3 SP
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                },
        },
        {
 -              name:              "MOVWconst",
 -              auxType:           auxInt32,
 -              argLen:            0,
 -              rematerializeable: true,
 -              asm:               arm.AMOVW,
 +              name:   "SUB",
 +              argLen: 2,
 +              asm:    arm.ASUB,
                reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
                        outputs: []regMask{
 -                              31, // R0 R1 R2 R3 SP
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                },
        },
        {
 -              name:   "CMP",
 -              argLen: 2,
 -              asm:    arm.ACMP,
 +              name:    "SUBconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ASUB,
                reg: regInfo{
                        inputs: []inputInfo{
 -                              {0, 31}, // R0 R1 R2 R3 SP
 -                              {1, 31}, // R0 R1 R2 R3 SP
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                        outputs: []regMask{
 -                              32, // FLAGS
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                },
        },
        {
 -              name:    "MOVWload",
 -              auxType: auxSymOff,
 -              argLen:  2,
 -              asm:     arm.AMOVW,
 +              name:   "RSB",
 +              argLen: 2,
 +              asm:    arm.ARSB,
                reg: regInfo{
                        inputs: []inputInfo{
 -                              {0, 31}, // R0 R1 R2 R3 SP
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                        outputs: []regMask{
 -                              31, // R0 R1 R2 R3 SP
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                },
        },
        {
 -              name:    "MOVWstore",
 -              auxType: auxSymOff,
 -              argLen:  3,
 -              asm:     arm.AMOVW,
 +              name:    "RSBconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ARSB,
                reg: regInfo{
                        inputs: []inputInfo{
 -                              {0, 31}, // R0 R1 R2 R3 SP
 -                              {1, 31}, // R0 R1 R2 R3 SP
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                },
        },
        {
 -              name:    "CALLstatic",
 -              auxType: auxSymOff,
 -              argLen:  1,
 +              name:        "MUL",
 +              argLen:      2,
 +              commutative: true,
 +              asm:         arm.AMUL,
                reg: regInfo{
 -                      clobbers: 15, // R0 R1 R2 R3
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
                },
        },
        {
 -              name:   "LessThan",
 -              argLen: 1,
 +              name:        "HMUL",
 +              argLen:      2,
 +              commutative: true,
 +              asm:         arm.AMULL,
                reg: regInfo{
                        inputs: []inputInfo{
 -                              {0, 32}, // FLAGS
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                        outputs: []regMask{
 -                              31, // R0 R1 R2 R3 SP
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
                        },
                },
        },
 -
        {
 -              name:        "Add8",
 +              name:        "HMULU",
                argLen:      2,
                commutative: true,
 -              generic:     true,
 +              asm:         arm.AMULLU,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:        "Add16",
 +              name:        "AND",
                argLen:      2,
                commutative: true,
 -              generic:     true,
 +              asm:         arm.AAND,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:        "Add32",
 -              argLen:      2,
 -              commutative: true,
 -              generic:     true,
 +              name:    "ANDconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.AAND,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:        "Add64",
 +              name:        "OR",
                argLen:      2,
                commutative: true,
 -              generic:     true,
 +              asm:         arm.AORR,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "AddPtr",
 -              argLen:  2,
 -              generic: true,
 +              name:    "ORconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.AORR,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Add32F",
 -              argLen:  2,
 -              generic: true,
 +              name:        "XOR",
 +              argLen:      2,
 +              commutative: true,
 +              asm:         arm.AEOR,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Add64F",
 -              argLen:  2,
 -              generic: true,
 +              name:    "XORconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.AEOR,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Sub8",
 -              argLen:  2,
 -              generic: true,
 +              name:   "BIC",
 +              argLen: 2,
 +              asm:    arm.ABIC,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Sub16",
 -              argLen:  2,
 -              generic: true,
 +              name:    "BICconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ABIC,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Sub32",
 -              argLen:  2,
 -              generic: true,
 +              name:   "MVN",
 +              argLen: 1,
 +              asm:    arm.AMVN,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Sub64",
 -              argLen:  2,
 -              generic: true,
 +              name:   "SLL",
 +              argLen: 2,
 +              asm:    arm.ASLL,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      clobbers: 65536, // FLAGS
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "SubPtr",
 -              argLen:  2,
 -              generic: true,
 +              name:    "SLLconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ASLL,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Sub32F",
 -              argLen:  2,
 -              generic: true,
 +              name:   "SRL",
 +              argLen: 2,
 +              asm:    arm.ASRL,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      clobbers: 65536, // FLAGS
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:    "Sub64F",
 -              argLen:  2,
 -              generic: true,
 +              name:    "SRLconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ASRL,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:        "Mul8",
 -              argLen:      2,
 -              commutative: true,
 -              generic:     true,
 +              name:   "SRA",
 +              argLen: 2,
 +              asm:    arm.ASRA,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      clobbers: 65536, // FLAGS
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:        "Mul16",
 -              argLen:      2,
 -              commutative: true,
 -              generic:     true,
 +              name:    "SRAconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ASRA,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
        },
        {
 -              name:        "Mul32",
 -              argLen:      2,
 -              commutative: true,
 -              generic:     true,
 +              name:   "CMP",
 +              argLen: 2,
 +              asm:    arm.ACMP,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
        },
        {
 -              name:        "Mul64",
 -              argLen:      2,
 -              commutative: true,
 -              generic:     true,
 +              name:    "CMPconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ACMP,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
        },
        {
 -              name:    "Mul32F",
 -              argLen:  2,
 -              generic: true,
 +              name:   "CMN",
 +              argLen: 2,
 +              asm:    arm.ACMN,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
        },
        {
 -              name:    "Mul64F",
 -              argLen:  2,
 -              generic: true,
 +              name:    "CMNconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ACMN,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
        },
        {
 -              name:    "Div32F",
 -              argLen:  2,
 -              generic: true,
 +              name:        "TST",
 +              argLen:      2,
 +              commutative: true,
 +              asm:         arm.ATST,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
 +      },
 +      {
 +              name:    "TSTconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ATST,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
 +      },
 +      {
 +              name:        "TEQ",
 +              argLen:      2,
 +              commutative: true,
 +              asm:         arm.ATEQ,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
 +      },
 +      {
 +              name:    "TEQconst",
 +              auxType: auxInt32,
 +              argLen:  1,
 +              asm:     arm.ATEQ,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              65536, // FLAGS
 +                      },
 +              },
 +      },
 +      {
 +              name:              "MOVWconst",
 +              auxType:           auxInt32,
 +              argLen:            0,
 +              rematerializeable: true,
 +              asm:               arm.AMOVW,
 +              reg: regInfo{
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVBload",
 +              auxType: auxSymOff,
 +              argLen:  2,
 +              asm:     arm.AMOVB,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVBUload",
 +              auxType: auxSymOff,
 +              argLen:  2,
 +              asm:     arm.AMOVBU,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVHload",
 +              auxType: auxSymOff,
 +              argLen:  2,
 +              asm:     arm.AMOVH,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVHUload",
 +              auxType: auxSymOff,
 +              argLen:  2,
 +              asm:     arm.AMOVHU,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVWload",
 +              auxType: auxSymOff,
 +              argLen:  2,
 +              asm:     arm.AMOVW,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVBstore",
 +              auxType: auxSymOff,
 +              argLen:  3,
 +              asm:     arm.AMOVB,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {1, 5119},   // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVHstore",
 +              auxType: auxSymOff,
 +              argLen:  3,
 +              asm:     arm.AMOVH,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {1, 5119},   // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +              },
 +      },
 +      {
 +              name:    "MOVWstore",
 +              auxType: auxSymOff,
 +              argLen:  3,
 +              asm:     arm.AMOVW,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {1, 5119},   // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {0, 144383}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP SB
 +                      },
 +              },
 +      },
 +      {
 +              name:   "MOVBreg",
 +              argLen: 1,
 +              asm:    arm.AMOVBS,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "MOVBUreg",
 +              argLen: 1,
 +              asm:    arm.AMOVBU,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "MOVHreg",
 +              argLen: 1,
 +              asm:    arm.AMOVHS,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "MOVHUreg",
 +              argLen: 1,
 +              asm:    arm.AMOVHU,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "CALLstatic",
 +              auxType: auxSymOff,
 +              argLen:  1,
 +              reg: regInfo{
 +                      clobbers: 5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +              },
 +      },
 +      {
 +              name:    "CALLclosure",
 +              auxType: auxInt64,
 +              argLen:  3,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {1, 128},   // R7
 +                              {0, 13311}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP
 +                      },
 +                      clobbers: 5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +              },
 +      },
 +      {
 +              name:    "CALLdefer",
 +              auxType: auxInt64,
 +              argLen:  1,
 +              reg: regInfo{
 +                      clobbers: 5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +              },
 +      },
 +      {
 +              name:    "CALLgo",
 +              auxType: auxInt64,
 +              argLen:  1,
 +              reg: regInfo{
 +                      clobbers: 5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +              },
 +      },
 +      {
 +              name:    "CALLinter",
 +              auxType: auxInt64,
 +              argLen:  2,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      clobbers: 5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +              },
 +      },
 +      {
 +              name:   "LoweredNilCheck",
 +              argLen: 2,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 13311}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP
 +                      },
 +                      clobbers: 65536, // FLAGS
 +              },
 +      },
 +      {
 +              name:   "Equal",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "NotEqual",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "LessThan",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "LessEqual",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "GreaterThan",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "GreaterEqual",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "LessThanU",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "LessEqualU",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "GreaterThanU",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:   "GreaterEqualU",
 +              argLen: 1,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 65536}, // FLAGS
 +                      },
 +                      outputs: []regMask{
 +                              5119, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +              },
 +      },
 +      {
 +              name:    "DUFFZERO",
 +              auxType: auxInt64,
 +              argLen:  3,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 2}, // R1
 +                              {1, 1}, // R0
 +                      },
 +                      clobbers: 2, // R1
 +              },
 +      },
 +      {
 +              name:    "DUFFCOPY",
 +              auxType: auxInt64,
 +              argLen:  3,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 4}, // R2
 +                              {1, 2}, // R1
 +                      },
 +                      clobbers: 7, // R0 R1 R2
 +              },
 +      },
 +      {
 +              name:   "LoweredZero",
 +              argLen: 4,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 2},    // R1
 +                              {1, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                              {2, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      clobbers: 65538, // R1 FLAGS
 +              },
 +      },
 +      {
 +              name:   "LoweredMove",
 +              argLen: 4,
 +              reg: regInfo{
 +                      inputs: []inputInfo{
 +                              {0, 4},    // R2
 +                              {1, 2},    // R1
 +                              {2, 5119}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12
 +                      },
 +                      clobbers: 65542, // R1 R2 FLAGS
 +              },
 +      },
 +
 +      {
 +              name:        "Add8",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:        "Add16",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:        "Add32",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:        "Add64",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:    "AddPtr",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Add32F",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Add64F",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Sub8",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Sub16",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Sub32",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Sub64",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "SubPtr",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Sub32F",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Sub64F",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:        "Mul8",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:        "Mul16",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:        "Mul32",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:        "Mul64",
 +              argLen:      2,
 +              commutative: true,
 +              generic:     true,
 +      },
 +      {
 +              name:    "Mul32F",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Mul64F",
 +              argLen:  2,
 +              generic: true,
 +      },
 +      {
 +              name:    "Div32F",
 +              argLen:  2,
 +              generic: true,
        },
        {
                name:    "Div64F",
        },
        {
                name:    "FwdRef",
+               auxType: auxSym,
                argLen:  0,
                generic: true,
        },
                argLen:  1,
                generic: true,
        },
+       {
+               name:    "KeepAlive",
+               argLen:  2,
+               generic: true,
+       },
  }
  
  func (o Op) Asm() obj.As    { return opcodeTable[o].asm }
@@@ -6234,25 -5431,12 +6241,25 @@@ var registersAMD64 = [...]Register
        {32, "SB"},
        {33, "FLAGS"},
  }
 +var flagRegMaskAMD64 = regMask(8589934592)
  var registersARM = [...]Register{
        {0, "R0"},
        {1, "R1"},
        {2, "R2"},
        {3, "R3"},
 -      {4, "SP"},
 -      {5, "FLAGS"},
 -      {6, "SB"},
 +      {4, "R4"},
 +      {5, "R5"},
 +      {6, "R6"},
 +      {7, "R7"},
 +      {8, "R8"},
 +      {9, "R9"},
 +      {10, "R10"},
 +      {11, "R11"},
 +      {12, "R12"},
 +      {13, "SP"},
 +      {14, "R14"},
 +      {15, "R15"},
 +      {16, "FLAGS"},
 +      {17, "SB"},
  }
 +var flagRegMaskARM = regMask(65536)
index e0dc1009afff1b7b3e46e953b07f6196fe20157b,1eecd49c40cf3a322628f5a4419667fd32917ef8..8603615f251720f54a1f98ad467f240843717300
  package ssa
  
  import (
-       "cmd/internal/obj"
        "fmt"
        "unsafe"
  )
@@@ -456,7 -455,7 +455,7 @@@ func (s *regAllocState) init(f *Func) 
        s.allocatable = regMask(1)<<s.numRegs - 1
        s.allocatable &^= 1 << s.SPReg
        s.allocatable &^= 1 << s.SBReg
-       if obj.Framepointer_enabled != 0 {
+       if s.f.Config.ctxt.Framepointer_enabled {
                s.allocatable &^= 1 << 5 // BP
        }
        if s.f.Config.ctxt.Flag_dynlink {
@@@ -941,11 -940,29 +940,29 @@@ func (s *regAllocState) regalloc(f *Fun
                                s.advanceUses(v)
                                continue
                        }
+                       if v.Op == OpKeepAlive {
+                               // Make sure the argument to v is still live here.
+                               s.advanceUses(v)
+                               vi := &s.values[v.Args[0].ID]
+                               if vi.spillUsed {
+                                       // Use the spill location.
+                                       v.SetArg(0, vi.spill)
+                               } else {
+                                       // No need to keep unspilled values live.
+                                       // These are typically rematerializeable constants like nil,
+                                       // or values of a variable that were modified since the last call.
+                                       v.Op = OpCopy
+                                       v.SetArgs1(v.Args[1])
+                               }
+                               b.Values = append(b.Values, v)
+                               continue
+                       }
                        regspec := opcodeTable[v.Op].reg
                        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)
+                               s.advanceUses(v)
                                continue
                        }
  
                        args = append(args[:0], v.Args...)
                        for _, i := range regspec.inputs {
                                mask := i.regs
 -                              if mask == flagRegMask {
 +                              if mask == f.Config.flagRegMask {
                                        // TODO: remove flag input from regspec.inputs.
                                        continue
                                }
                                                // Start with live at end.
                                                for _, li := range s.live[ss.ID] {
                                                        if s.isLoopSpillCandidate(loop, s.orig[li.ID]) {
+                                                               // s.live contains original IDs, use s.orig above to map back to *Value
                                                                entryCandidates.setBit(li.ID, uint(whichExit))
                                                        }
                                                }
                                                // Control can also be live.
-                                               if ss.Control != nil && s.isLoopSpillCandidate(loop, ss.Control) {
-                                                       entryCandidates.setBit(ss.Control.ID, uint(whichExit))
+                                               if ss.Control != nil && s.orig[ss.Control.ID] != nil && s.isLoopSpillCandidate(loop, s.orig[ss.Control.ID]) {
+                                                       entryCandidates.setBit(s.orig[ss.Control.ID].ID, uint(whichExit))
                                                }
                                                // Walk backwards, filling in locally live values, removing those defined.
                                                for i := len(ss.Values) - 1; i >= 0; i-- {
                                                        v := ss.Values[i]
-                                                       entryCandidates.remove(v.ID) // Cannot be an issue, only keeps the sets smaller.
+                                                       vorig := s.orig[v.ID]
+                                                       if vorig != nil {
+                                                               entryCandidates.remove(vorig.ID) // Cannot be an issue, only keeps the sets smaller.
+                                                       }
                                                        for _, a := range v.Args {
-                                                               if s.isLoopSpillCandidate(loop, a) {
-                                                                       entryCandidates.setBit(a.ID, uint(whichExit))
+                                                               aorig := s.orig[a.ID]
+                                                               if aorig != nil && s.isLoopSpillCandidate(loop, aorig) {
+                                                                       entryCandidates.setBit(aorig.ID, uint(whichExit))
                                                                }
                                                        }
                                                }
@@@ -1524,7 -1546,7 +1546,7 @@@ sinking
        }
  
        if f.pass.stats > 0 {
-               f.logStat("spills_info",
+               f.LogStat("spills_info",
                        nSpills, "spills", nSpillsInner, "inner_spills_remaining", nSpillsSunk, "inner_spills_sunk", nSpillsSunkUnused, "inner_spills_unused", nSpillsNotSunkLateUse, "inner_spills_shuffled", nSpillsChanged, "inner_spills_changed")
        }
  }
diff --combined src/runtime/mgc.go
index c497ccee67e724c240d6d2fc33adad80c5ad0b9f,1eabf43d6f24c1eeabec8e413e716c6afbde4935..c50bd028810f0b947c3c29d792bd565aed3654a4
@@@ -220,11 -220,10 +220,11 @@@ var gcphase uint3
  // The compiler knows about this variable.
  // If you change it, you must change the compiler too.
  var writeBarrier struct {
 -      enabled bool   // compiler emits a check of this before calling write barrier
 -      needed  bool   // whether we need a write barrier for current GC phase
 -      cgo     bool   // whether we need a write barrier for a cgo check
 -      alignme uint64 // guarantee alignment so that compiler can use a 32 or 64-bit load
 +      enabled bool    // compiler emits a check of this before calling write barrier
 +      pad     [3]byte // compiler uses 32-bit load for "enabled" field
 +      needed  bool    // whether we need a write barrier for current GC phase
 +      cgo     bool    // whether we need a write barrier for a cgo check
 +      alignme uint64  // guarantee alignment so that compiler can use a 32 or 64-bit load
  }
  
  // gcBlackenEnabled is 1 if mutator assists and background mark
@@@ -1389,7 -1388,7 +1389,7 @@@ func gcBgMarkWorker(_p_ *p) 
        notewakeup(&work.bgMarkReady)
  
        for {
-               // Go to sleep until woken by gcContoller.findRunnable.
+               // Go to sleep until woken by gcController.findRunnable.
                // We can't releasem yet since even the call to gopark
                // may be preempted.
                gopark(func(g *g, parkp unsafe.Pointer) bool {
@@@ -1705,7 -1704,7 +1705,7 @@@ func gcSweep(mode gcMode) 
        lock(&sweep.lock)
        if sweep.parked {
                sweep.parked = false
-               ready(sweep.g, 0)
+               ready(sweep.g, 0, true)
        }
        unlock(&sweep.lock)
        mProf_GC()
diff --combined src/runtime/runtime1.go
index e19c7fe93248bc98f1f6a76bb8d70aa53c015b7c,302f58de5fa91a9e3724451b962d446c029c9ce8..3c9eed5905c53eef57e74a83dc6bbedf5c4da4e2
@@@ -68,6 -68,7 +68,6 @@@ func goargs() 
        if GOOS == "windows" {
                return
        }
 -
        argslice = make([]string, argc)
        for i := int32(0); i < argc; i++ {
                argslice[i] = gostringnocopy(argv_index(argv, i))
@@@ -508,7 -509,7 +508,7 @@@ func reflect_resolveTextOff(rtype unsaf
  // reflect_addReflectOff adds a pointer to the reflection offset lookup map.
  //go:linkname reflect_addReflectOff reflect.addReflectOff
  func reflect_addReflectOff(ptr unsafe.Pointer) int32 {
-       lock(&reflectOffs.lock)
+       reflectOffsLock()
        if reflectOffs.m == nil {
                reflectOffs.m = make(map[int32]unsafe.Pointer)
                reflectOffs.minv = make(map[unsafe.Pointer]int32)
                reflectOffs.m[id] = ptr
                reflectOffs.minv[ptr] = id
        }
-       unlock(&reflectOffs.lock)
+       reflectOffsUnlock()
        return id
  }