From 741445068f7f582824f1c5625159e0b728090265 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 30 Oct 2016 21:10:03 -0700 Subject: [PATCH] cmd/compile: make [0]T and [1]T SSAable types 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 TryBot-Result: Gobot Gobot Reviewed-by: David Chase --- src/cmd/compile/internal/gc/ssa.go | 114 ++++++++---- src/cmd/compile/internal/ssa/config.go | 1 + src/cmd/compile/internal/ssa/decompose.go | 46 ++++- src/cmd/compile/internal/ssa/export_test.go | 3 + .../compile/internal/ssa/gen/generic.rules | 24 ++- .../compile/internal/ssa/gen/genericOps.go | 10 +- src/cmd/compile/internal/ssa/opGen.go | 26 ++- .../compile/internal/ssa/rewritegeneric.go | 168 ++++++++++++++++-- 8 files changed, 329 insertions(+), 63 deletions(-) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index cb27853968..ea9fc5b845 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -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 { diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 1d388afe39..933672d007 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -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. diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index 04f45c1134..b2ee2f0a2b 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -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 diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 1eef2da15a..010c4d7680 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -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" } diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index e866fe756a..ca491c33d8 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -668,7 +668,6 @@ // indexing operations // Note: bounds check has already been done -(ArrayIndex [0] x:(Load ptr mem)) -> @x.Block (Load ptr mem) (PtrIndex ptr idx) && config.PtrSize == 4 -> (AddPtr ptr (Mul32 idx (Const32 [t.ElemType().Size()]))) (PtrIndex ptr idx) && config.PtrSize == 8 -> (AddPtr ptr (Mul64 idx (Const64 [t.ElemType().Size()]))) @@ -736,12 +735,30 @@ 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 src mem) mem) && !config.fe.CanSSA(t) -> (Move [MakeSizeAndAlign(size, t.Alignment()).Int64()] dst src mem) (Store [size] dst (Load 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.IsArray() && t.NumElem() == 0 -> + (ArrayMake0) + +(Load ptr mem) && t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) -> + (ArrayMake1 (Load 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 @@ -850,6 +867,11 @@ (Arg {n} [off+t.FieldOff(2)]) (Arg {n} [off+t.FieldOff(3)])) +(Arg ) && t.IsArray() && t.NumElem() == 0 -> + (ArrayMake0) +(Arg {n} [off]) && t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) -> + (ArrayMake1 (Arg {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? diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index d935e74b9f..fe93e521e3 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -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 diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index e889787c4e..c95131dbcd 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -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, diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 7972acf8a7..818e08b7e0 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -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 ) + // 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 {n} [off]) + // cond: t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) + // result: (ArrayMake1 (Arg {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 [0] x:(Load ptr mem)) + // match: (ArraySelect (ArrayMake1 x)) // cond: - // result: @x.Block (Load 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 _ _) + // 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 ptr mem) + // cond: t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) + // result: (ArrayMake1 (Load 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 { -- 2.48.1