mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
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 <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
9c066bab64
commit
741445068f
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,12 +305,32 @@ 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
|
||||
// can have and still be SSAable.
|
||||
const MaxStruct = 4
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -668,7 +668,6 @@
|
|||
|
||||
// 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()])))
|
||||
|
||||
|
@ -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 <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
|
||||
|
@ -850,6 +867,11 @@
|
|||
(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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValuegeneric_OpArrayIndex(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (ArrayIndex <t> [0] x:(Load ptr mem))
|
||||
// cond:
|
||||
// result: @x.Block (Load <t> ptr mem)
|
||||
// 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_OpArraySelect(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (ArraySelect (ArrayMake1 x))
|
||||
// cond:
|
||||
// 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 {
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue