]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: make [0]T and [1]T SSAable types
authorKeith Randall <khr@golang.org>
Mon, 31 Oct 2016 04:10:03 +0000 (21:10 -0700)
committerKeith Randall <khr@golang.org>
Mon, 31 Oct 2016 19:44:19 +0000 (19:44 +0000)
We used to have to keep on-stack copies of these types.
Now they can be registerized.

[0]T is kind of trivial but might as well handle it.

This change enables another change I'm working on to improve how x.(T)
expressions are handled (#17405).  This CL helps because now all
types that are direct interface types are registerizeable (e.g. [1]*byte).

No higher-degree arrays for now because non-constant indexes are hard.

Update #17405

Change-Id: I2399940965d17b3969ae66f6fe447a8cefdd6edd
Reviewed-on: https://go-review.googlesource.com/32416
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/config.go
src/cmd/compile/internal/ssa/decompose.go
src/cmd/compile/internal/ssa/export_test.go
src/cmd/compile/internal/ssa/gen/generic.rules
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/rewritegeneric.go

index cb27853968d3b7c464b7d8a872a1a335b93c00e8..ea9fc5b845ac717f3ac5773baa47c160c1493377 100644 (file)
@@ -1974,7 +1974,22 @@ func (s *state) expr(n *Node) *ssa.Value {
                        p, _ := s.addr(n, false)
                        return s.newValue2(ssa.OpLoad, n.Left.Type.Elem(), p, s.mem())
                case n.Left.Type.IsArray():
-                       // TODO: fix when we can SSA arrays of length 1.
+                       if bound := n.Left.Type.NumElem(); bound <= 1 {
+                               // SSA can handle arrays of length at most 1.
+                               a := s.expr(n.Left)
+                               i := s.expr(n.Right)
+                               if bound == 0 {
+                                       // Bounds check will never succeed.  Might as well
+                                       // use constants for the bounds check.
+                                       z := s.constInt(Types[TINT], 0)
+                                       s.boundsCheck(z, z)
+                                       // The return value won't be live, return junk.
+                                       return s.newValue0(ssa.OpUnknown, n.Type)
+                               }
+                               i = s.extendIndex(i, panicindex)
+                               s.boundsCheck(i, s.constInt(Types[TINT], bound))
+                               return s.newValue1I(ssa.OpArraySelect, n.Type, 0, a)
+                       }
                        p, _ := s.addr(n, false)
                        return s.newValue2(ssa.OpLoad, n.Left.Type.Elem(), p, s.mem())
                default:
@@ -2017,32 +2032,6 @@ func (s *state) expr(n *Node) *ssa.Value {
        case OEFACE:
                tab := s.expr(n.Left)
                data := s.expr(n.Right)
-               // The frontend allows putting things like struct{*byte} in
-               // the data portion of an eface. But we don't want struct{*byte}
-               // as a register type because (among other reasons) the liveness
-               // analysis is confused by the "fat" variables that result from
-               // such types being spilled.
-               // So here we ensure that we are selecting the underlying pointer
-               // when we build an eface.
-               // TODO: get rid of this now that structs can be SSA'd?
-               for !data.Type.IsPtrShaped() {
-                       switch {
-                       case data.Type.IsArray():
-                               data = s.newValue1I(ssa.OpArrayIndex, data.Type.ElemType(), 0, data)
-                       case data.Type.IsStruct():
-                               for i := data.Type.NumFields() - 1; i >= 0; i-- {
-                                       f := data.Type.FieldType(i)
-                                       if f.Size() == 0 {
-                                               // eface type could also be struct{p *byte; q [0]int}
-                                               continue
-                                       }
-                                       data = s.newValue1I(ssa.OpStructSelect, f, int64(i), data)
-                                       break
-                               }
-                       default:
-                               s.Fatalf("type being put into an eface isn't a pointer")
-                       }
-               }
                return s.newValue2(ssa.OpIMake, n.Type, tab, data)
 
        case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
@@ -2377,6 +2366,30 @@ func (s *state) assign(left *Node, right *ssa.Value, wb, deref bool, line int32,
                        // TODO: do we need to update named values here?
                        return
                }
+               if left.Op == OINDEX && left.Left.Type.IsArray() {
+                       // We're assigning to an element of an ssa-able array.
+                       // a[i] = v
+                       t := left.Left.Type
+                       n := t.NumElem()
+
+                       i := s.expr(left.Right) // index
+                       if n == 0 {
+                               // The bounds check must fail.  Might as well
+                               // ignore the actual index and just use zeros.
+                               z := s.constInt(Types[TINT], 0)
+                               s.boundsCheck(z, z)
+                               return
+                       }
+                       if n != 1 {
+                               s.Fatalf("assigning to non-1-length array")
+                       }
+                       // Rewrite to a = [1]{v}
+                       i = s.extendIndex(i, panicindex)
+                       s.boundsCheck(i, s.constInt(Types[TINT], 1))
+                       v := s.newValue1(ssa.OpArrayMake1, t, right)
+                       s.assign(left.Left, v, false, false, line, 0, rightIsVolatile)
+                       return
+               }
                // Update variable assignment.
                s.vars[left] = right
                s.addNamedValue(left, right)
@@ -2475,6 +2488,13 @@ func (s *state) zeroVal(t *Type) *ssa.Value {
                        v.AddArg(s.zeroVal(t.FieldType(i).(*Type)))
                }
                return v
+       case t.IsArray():
+               switch t.NumElem() {
+               case 0:
+                       return s.entryNewValue0(ssa.OpArrayMake0, t)
+               case 1:
+                       return s.entryNewValue1(ssa.OpArrayMake1, t, s.zeroVal(t.Elem()))
+               }
        }
        s.Fatalf("zero for type %v not implemented", t)
        return nil
@@ -3071,7 +3091,7 @@ func (s *state) canSSA(n *Node) bool {
        if Debug['N'] != 0 {
                return false
        }
-       for n.Op == ODOT {
+       for n.Op == ODOT || (n.Op == OINDEX && n.Left.Type.IsArray()) {
                n = n.Left
        }
        if n.Op != ONAME {
@@ -3123,11 +3143,15 @@ func canSSAType(t *Type) bool {
        }
        switch t.Etype {
        case TARRAY:
-               // We can't do arrays because dynamic indexing is
+               // We can't do larger arrays because dynamic indexing is
                // not supported on SSA variables.
-               // TODO: maybe allow if length is <=1?  All indexes
-               // are constant?  Might be good for the arrays
-               // introduced by the compiler for variadic functions.
+               // TODO: allow if all indexes are constant.
+               if t.NumElem() == 0 {
+                       return true
+               }
+               if t.NumElem() == 1 {
+                       return canSSAType(t.Elem())
+               }
                return false
        case TSTRUCT:
                if t.NumFields() > ssa.MaxStruct {
@@ -3406,6 +3430,10 @@ func (s *state) storeTypeScalars(t *Type, left, right *ssa.Value, skip skipMask)
                        val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
                        s.storeTypeScalars(ft.(*Type), addr, val, 0)
                }
+       case t.IsArray() && t.NumElem() == 0:
+               // nothing
+       case t.IsArray() && t.NumElem() == 1:
+               s.storeTypeScalars(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right), 0)
        default:
                s.Fatalf("bad write barrier type %v", t)
        }
@@ -3438,6 +3466,10 @@ func (s *state) storeTypePtrs(t *Type, left, right *ssa.Value) {
                        val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
                        s.storeTypePtrs(ft.(*Type), addr, val)
                }
+       case t.IsArray() && t.NumElem() == 0:
+               // nothing
+       case t.IsArray() && t.NumElem() == 1:
+               s.storeTypePtrs(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right))
        default:
                s.Fatalf("bad write barrier type %v", t)
        }
@@ -3470,6 +3502,10 @@ func (s *state) storeTypePtrsWB(t *Type, left, right *ssa.Value) {
                        val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
                        s.storeTypePtrsWB(ft.(*Type), addr, val)
                }
+       case t.IsArray() && t.NumElem() == 0:
+               // nothing
+       case t.IsArray() && t.NumElem() == 1:
+               s.storeTypePtrsWB(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right))
        default:
                s.Fatalf("bad write barrier type %v", t)
        }
@@ -4567,6 +4603,20 @@ func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
        return ssa.LocalSlot{N: n, Type: ft, Off: name.Off + st.FieldOff(i)}
 }
 
+func (e *ssaExport) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
+       n := name.N.(*Node)
+       at := name.Type
+       if at.NumElem() != 1 {
+               Fatalf("bad array size")
+       }
+       et := at.ElemType()
+       if n.Class == PAUTO && !n.Addrtaken {
+               x := e.namedAuto(n.Sym.Name+"[0]", et)
+               return ssa.LocalSlot{N: x, Type: et, Off: 0}
+       }
+       return ssa.LocalSlot{N: n, Type: et, Off: name.Off}
+}
+
 // namedAuto returns a new AUTO variable with the given name and type.
 // These are exposed to the debugger.
 func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode {
index 1d388afe3962c5b85af355f26185761b3b2b4884..933672d0079cc0b20de021de0525bb7ed1308d5e 100644 (file)
@@ -115,6 +115,7 @@ type Frontend interface {
        SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
        SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
        SplitStruct(LocalSlot, int) LocalSlot
+       SplitArray(LocalSlot) LocalSlot              // array must be length 1
        SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
 
        // Line returns a string describing the given line number.
index 04f45c1134b6bf1ce3339f90ace10fae17f05d21..b2ee2f0a2bf26a8b52b628a6201ede00e3cbed05 100644 (file)
@@ -253,6 +253,21 @@ func decomposeUser(f *Func) {
                        }
                        delete(f.NamedValues, name)
                        newNames = append(newNames, fnames...)
+               case t.IsArray():
+                       if t.NumElem() == 0 {
+                               // TODO(khr): Not sure what to do here.  Probably nothing.
+                               // Names for empty arrays aren't important.
+                               break
+                       }
+                       if t.NumElem() != 1 {
+                               f.Fatalf("array not of size 1")
+                       }
+                       elemName := f.Config.fe.SplitArray(name)
+                       for _, v := range f.NamedValues[name] {
+                               e := v.Block.NewValue1I(v.Line, OpArraySelect, t.ElemType(), 0, v)
+                               f.NamedValues[elemName] = append(f.NamedValues[elemName], e)
+                       }
+
                default:
                        f.Names[i] = name
                        i++
@@ -266,10 +281,13 @@ func decomposeUserPhi(v *Value) {
        switch {
        case v.Type.IsStruct():
                decomposeStructPhi(v)
+       case v.Type.IsArray():
+               decomposeArrayPhi(v)
        }
-       // TODO: Arrays of length 1?
 }
 
+// decomposeStructPhi replaces phi-of-struct with structmake(phi-for-each-field),
+// and then recursively decomposes the phis for each field.
 func decomposeStructPhi(v *Value) {
        t := v.Type
        n := t.NumFields()
@@ -287,10 +305,30 @@ func decomposeStructPhi(v *Value) {
 
        // Recursively decompose phis for each field.
        for _, f := range fields[:n] {
-               if f.Type.IsStruct() {
-                       decomposeStructPhi(f)
-               }
+               decomposeUserPhi(f)
+       }
+}
+
+// decomposeArrayPhi replaces phi-of-array with arraymake(phi-of-array-element),
+// and then recursively decomposes the element phi.
+func decomposeArrayPhi(v *Value) {
+       t := v.Type
+       if t.NumElem() == 0 {
+               v.reset(OpArrayMake0)
+               return
+       }
+       if t.NumElem() != 1 {
+               v.Fatalf("SSAable array must have no more than 1 element")
        }
+       elem := v.Block.NewValue0(v.Line, OpPhi, t.ElemType())
+       for _, a := range v.Args {
+               elem.AddArg(a.Block.NewValue1I(v.Line, OpArraySelect, t.ElemType(), 0, a))
+       }
+       v.reset(OpArrayMake1)
+       v.AddArg(elem)
+
+       // Recursively decompose elem phi.
+       decomposeUserPhi(elem)
 }
 
 // MaxStruct is the maximum number of fields a struct
index 1eef2da15a0bd06ab97f9d171b9aa81edc894dd7..010c4d7680a150e192d460907a27efda2f725649 100644 (file)
@@ -58,6 +58,9 @@ func (d DummyFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
 func (d DummyFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
        return LocalSlot{s.N, s.Type.FieldType(i), s.Off + s.Type.FieldOff(i)}
 }
+func (d DummyFrontend) SplitArray(s LocalSlot) LocalSlot {
+       return LocalSlot{s.N, s.Type.ElemType(), s.Off}
+}
 func (DummyFrontend) Line(line int32) string {
        return "unknown.go:0"
 }
index e866fe756aa65ff2980b0eec4f7f01500637893f..ca491c33d8ed0aaadabe32b6344a15285e2ad069 100644 (file)
 
 // indexing operations
 // Note: bounds check has already been done
-(ArrayIndex <t> [0] x:(Load ptr mem)) -> @x.Block (Load <t> ptr mem)
 (PtrIndex <t> ptr idx) && config.PtrSize == 4 -> (AddPtr ptr (Mul32 <config.fe.TypeInt()> idx (Const32 <config.fe.TypeInt()> [t.ElemType().Size()])))
 (PtrIndex <t> ptr idx) && config.PtrSize == 8 -> (AddPtr ptr (Mul64 <config.fe.TypeInt()> idx (Const64 <config.fe.TypeInt()> [t.ElemType().Size()])))
 
         f1
         (Store [t.FieldType(0).Size()] dst f0 mem))))
 
+(IMake typ (StructMake1 val)) -> (IMake typ val)
+
 // un-SSAable values use mem->mem copies
 (Store [size] dst (Load <t> src mem) mem) && !config.fe.CanSSA(t) ->
        (Move [MakeSizeAndAlign(size, t.Alignment()).Int64()] dst src mem)
 (Store [size] dst (Load <t> src mem) (VarDef {x} mem)) && !config.fe.CanSSA(t) ->
        (Move [MakeSizeAndAlign(size, t.Alignment()).Int64()] dst src (VarDef {x} mem))
 
+// array ops
+(ArraySelect (ArrayMake1 x)) -> x
+
+(Load <t> _ _) && t.IsArray() && t.NumElem() == 0 ->
+  (ArrayMake0)
+
+(Load <t> ptr mem) && t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) ->
+  (ArrayMake1 (Load <t.ElemType()> ptr mem))
+
+(Store _ (ArrayMake0) mem) -> mem
+(Store [size] dst (ArrayMake1 e) mem) -> (Store [size] dst e mem)
+
+(ArraySelect [0] (Load ptr mem)) -> (Load ptr mem)
+
+(IMake typ (ArrayMake1 val)) -> (IMake typ val)
+
 // string ops
 // Decomposing StringMake and lowering of StringPtr and StringLen
 // happens in a later pass, dec, so that these operations are available
     (Arg <t.FieldType(2)> {n} [off+t.FieldOff(2)])
     (Arg <t.FieldType(3)> {n} [off+t.FieldOff(3)]))
 
+(Arg <t>) && t.IsArray() && t.NumElem() == 0 ->
+  (ArrayMake0)
+(Arg <t> {n} [off]) && t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) ->
+  (ArrayMake1 (Arg <t.ElemType()> {n} [off]))
+
 // strength reduction of divide by a constant.
 // Note: frontend does <=32 bits. We only need to do 64 bits here.
 // TODO: Do them all here?
index d935e74b9fedbe2160925ee9298ccacedc3d5595..fe93e521e313dd5436731bfb8b6426501900c394 100644 (file)
@@ -373,9 +373,8 @@ var genericOps = []opData{
        {name: "GetClosurePtr"},      // get closure pointer from dedicated register
 
        // Indexing operations
-       {name: "ArrayIndex", aux: "Int64", argLength: 1}, // arg0=array, auxint=index. Returns a[i]
-       {name: "PtrIndex", argLength: 2},                 // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type
-       {name: "OffPtr", argLength: 1, aux: "Int64"},     // arg0 + auxint (arg0 and result are pointers)
+       {name: "PtrIndex", argLength: 2},             // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type
+       {name: "OffPtr", argLength: 1, aux: "Int64"}, // arg0 + auxint (arg0 and result are pointers)
 
        // Slices
        {name: "SliceMake", argLength: 3},                // arg0=ptr, arg1=len, arg2=cap
@@ -406,6 +405,11 @@ var genericOps = []opData{
        {name: "StructMake4", argLength: 4},                // arg0..3=field0..3.  Returns struct.
        {name: "StructSelect", argLength: 1, aux: "Int64"}, // arg0=struct, auxint=field index.  Returns the auxint'th field.
 
+       // Arrays
+       {name: "ArrayMake0"},                              // Returns array with 0 elements
+       {name: "ArrayMake1", argLength: 1},                // Returns array with 1 element
+       {name: "ArraySelect", argLength: 1, aux: "Int64"}, // arg0=array, auxint=index. Returns a[i].
+
        // Spill&restore ops for the register allocator. These are
        // semantically identical to OpCopy; they do not take/return
        // stores like regular memory ops do. We can get away without memory
index e889787c4e04267305e9379fd1bcb8f1501d5501..c95131dbcd8b835e278ceb67c5aabcdeaf136ed3 100644 (file)
@@ -1698,7 +1698,6 @@ const (
        OpNilCheck
        OpGetG
        OpGetClosurePtr
-       OpArrayIndex
        OpPtrIndex
        OpOffPtr
        OpSliceMake
@@ -1720,6 +1719,9 @@ const (
        OpStructMake3
        OpStructMake4
        OpStructSelect
+       OpArrayMake0
+       OpArrayMake1
+       OpArraySelect
        OpStoreReg
        OpLoadReg
        OpFwdRef
@@ -19616,12 +19618,6 @@ var opcodeTable = [...]opInfo{
                argLen:  0,
                generic: true,
        },
-       {
-               name:    "ArrayIndex",
-               auxType: auxInt64,
-               argLen:  1,
-               generic: true,
-       },
        {
                name:    "PtrIndex",
                argLen:  2,
@@ -19729,6 +19725,22 @@ var opcodeTable = [...]opInfo{
                argLen:  1,
                generic: true,
        },
+       {
+               name:    "ArrayMake0",
+               argLen:  0,
+               generic: true,
+       },
+       {
+               name:    "ArrayMake1",
+               argLen:  1,
+               generic: true,
+       },
+       {
+               name:    "ArraySelect",
+               auxType: auxInt64,
+               argLen:  1,
+               generic: true,
+       },
        {
                name:    "StoreReg",
                argLen:  1,
index 7972acf8a7542fdf5e411dcd2de5f6f1b4cb907e..818e08b7e0b1e17804a561a04a83c61ae5a6b821 100644 (file)
@@ -32,8 +32,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
                return rewriteValuegeneric_OpAnd8(v, config)
        case OpArg:
                return rewriteValuegeneric_OpArg(v, config)
-       case OpArrayIndex:
-               return rewriteValuegeneric_OpArrayIndex(v, config)
+       case OpArraySelect:
+               return rewriteValuegeneric_OpArraySelect(v, config)
        case OpCom16:
                return rewriteValuegeneric_OpCom16(v, config)
        case OpCom32:
@@ -110,6 +110,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
                return rewriteValuegeneric_OpGreater8(v, config)
        case OpGreater8U:
                return rewriteValuegeneric_OpGreater8U(v, config)
+       case OpIMake:
+               return rewriteValuegeneric_OpIMake(v, config)
        case OpIsInBounds:
                return rewriteValuegeneric_OpIsInBounds(v, config)
        case OpIsSliceInBounds:
@@ -1607,31 +1609,69 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
                v.AddArg(v3)
                return true
        }
+       // match: (Arg <t>)
+       // cond: t.IsArray() && t.NumElem() == 0
+       // result: (ArrayMake0)
+       for {
+               t := v.Type
+               if !(t.IsArray() && t.NumElem() == 0) {
+                       break
+               }
+               v.reset(OpArrayMake0)
+               return true
+       }
+       // match: (Arg <t> {n} [off])
+       // cond: t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)
+       // result: (ArrayMake1 (Arg <t.ElemType()> {n} [off]))
+       for {
+               t := v.Type
+               off := v.AuxInt
+               n := v.Aux
+               if !(t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)) {
+                       break
+               }
+               v.reset(OpArrayMake1)
+               v0 := b.NewValue0(v.Line, OpArg, t.ElemType())
+               v0.AuxInt = off
+               v0.Aux = n
+               v.AddArg(v0)
+               return true
+       }
        return false
 }
-func rewriteValuegeneric_OpArrayIndex(v *Value, config *Config) bool {
+func rewriteValuegeneric_OpArraySelect(v *Value, config *Config) bool {
        b := v.Block
        _ = b
-       // match: (ArrayIndex <t> [0] x:(Load ptr mem))
+       // match: (ArraySelect (ArrayMake1 x))
        // cond:
-       // result: @x.Block (Load <t> ptr mem)
+       // result: x
+       for {
+               v_0 := v.Args[0]
+               if v_0.Op != OpArrayMake1 {
+                       break
+               }
+               x := v_0.Args[0]
+               v.reset(OpCopy)
+               v.Type = x.Type
+               v.AddArg(x)
+               return true
+       }
+       // match: (ArraySelect [0] (Load ptr mem))
+       // cond:
+       // result: (Load ptr mem)
        for {
-               t := v.Type
                if v.AuxInt != 0 {
                        break
                }
-               x := v.Args[0]
-               if x.Op != OpLoad {
+               v_0 := v.Args[0]
+               if v_0.Op != OpLoad {
                        break
                }
-               ptr := x.Args[0]
-               mem := x.Args[1]
-               b = x.Block
-               v0 := b.NewValue0(v.Line, OpLoad, t)
-               v.reset(OpCopy)
-               v.AddArg(v0)
-               v0.AddArg(ptr)
-               v0.AddArg(mem)
+               ptr := v_0.Args[0]
+               mem := v_0.Args[1]
+               v.reset(OpLoad)
+               v.AddArg(ptr)
+               v.AddArg(mem)
                return true
        }
        return false
@@ -3101,6 +3141,41 @@ func rewriteValuegeneric_OpGreater8U(v *Value, config *Config) bool {
        }
        return false
 }
+func rewriteValuegeneric_OpIMake(v *Value, config *Config) bool {
+       b := v.Block
+       _ = b
+       // match: (IMake typ (StructMake1 val))
+       // cond:
+       // result: (IMake typ val)
+       for {
+               typ := v.Args[0]
+               v_1 := v.Args[1]
+               if v_1.Op != OpStructMake1 {
+                       break
+               }
+               val := v_1.Args[0]
+               v.reset(OpIMake)
+               v.AddArg(typ)
+               v.AddArg(val)
+               return true
+       }
+       // match: (IMake typ (ArrayMake1 val))
+       // cond:
+       // result: (IMake typ val)
+       for {
+               typ := v.Args[0]
+               v_1 := v.Args[1]
+               if v_1.Op != OpArrayMake1 {
+                       break
+               }
+               val := v_1.Args[0]
+               v.reset(OpIMake)
+               v.AddArg(typ)
+               v.AddArg(val)
+               return true
+       }
+       return false
+}
 func rewriteValuegeneric_OpIsInBounds(v *Value, config *Config) bool {
        b := v.Block
        _ = b
@@ -3982,6 +4057,34 @@ func rewriteValuegeneric_OpLoad(v *Value, config *Config) bool {
                v.AddArg(v5)
                return true
        }
+       // match: (Load <t> _ _)
+       // cond: t.IsArray() && t.NumElem() == 0
+       // result: (ArrayMake0)
+       for {
+               t := v.Type
+               if !(t.IsArray() && t.NumElem() == 0) {
+                       break
+               }
+               v.reset(OpArrayMake0)
+               return true
+       }
+       // match: (Load <t> ptr mem)
+       // cond: t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)
+       // result: (ArrayMake1 (Load <t.ElemType()> ptr mem))
+       for {
+               t := v.Type
+               ptr := v.Args[0]
+               mem := v.Args[1]
+               if !(t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)) {
+                       break
+               }
+               v.reset(OpArrayMake1)
+               v0 := b.NewValue0(v.Line, OpLoad, t.ElemType())
+               v0.AddArg(ptr)
+               v0.AddArg(mem)
+               v.AddArg(v0)
+               return true
+       }
        return false
 }
 func rewriteValuegeneric_OpLsh16x16(v *Value, config *Config) bool {
@@ -10291,6 +10394,39 @@ func rewriteValuegeneric_OpStore(v *Value, config *Config) bool {
                v.AddArg(v0)
                return true
        }
+       // match: (Store _ (ArrayMake0) mem)
+       // cond:
+       // result: mem
+       for {
+               v_1 := v.Args[1]
+               if v_1.Op != OpArrayMake0 {
+                       break
+               }
+               mem := v.Args[2]
+               v.reset(OpCopy)
+               v.Type = mem.Type
+               v.AddArg(mem)
+               return true
+       }
+       // match: (Store [size] dst (ArrayMake1 e) mem)
+       // cond:
+       // result: (Store [size] dst e mem)
+       for {
+               size := v.AuxInt
+               dst := v.Args[0]
+               v_1 := v.Args[1]
+               if v_1.Op != OpArrayMake1 {
+                       break
+               }
+               e := v_1.Args[0]
+               mem := v.Args[2]
+               v.reset(OpStore)
+               v.AuxInt = size
+               v.AddArg(dst)
+               v.AddArg(e)
+               v.AddArg(mem)
+               return true
+       }
        return false
 }
 func rewriteValuegeneric_OpStringLen(v *Value, config *Config) bool {