diff --git a/go/ir/doc.go b/go/ir/doc.go index 7158a0aec..178768dbd 100644 --- a/go/ir/doc.go +++ b/go/ir/doc.go @@ -93,6 +93,7 @@ // *Sigma ✔ ✔ // *Slice ✔ ✔ // *SliceToArrayPointer ✔ ✔ +// *SliceToArray ✔ ✔ // *Store ✔ ✔ // *StringLookup ✔ ✔ // *Type ✔ (type) diff --git a/go/ir/emit.go b/go/ir/emit.go index e0607d470..fca043f82 100644 --- a/go/ir/emit.go +++ b/go/ir/emit.go @@ -279,6 +279,25 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value { return f.emit(c, source) } + // Conversion from slice to array. This is almost the same as converting from slice to array pointer, then + // dereferencing the pointer. Except that a nil slice can be converted to [0]T, whereas converting a nil slice to + // (*[0]T) results in a nil pointer, dereferencing which would panic. To hide the extra branching we use a dedicated + // instruction, SliceToArray. + if tset_src.All(func(termSrc *types.Term) bool { + return tset_dst.All(func(termDst *types.Term) bool { + if slice, ok := termSrc.Type().Underlying().(*types.Slice); ok { + if arr, ok := termDst.Type().Underlying().(*types.Array); ok && types.Identical(slice.Elem(), arr.Elem()) { + return true + } + } + return false + }) + }) { + c := &SliceToArray{X: val} + c.setType(t_dst) + return f.emit(c, source) + } + // A representation-changing conversion? // At least one of {ut_src,ut_dst} must be *Basic. // (The other may be []byte or []rune.) diff --git a/go/ir/lift.go b/go/ir/lift.go index e7d932dbd..4af8ddafa 100644 --- a/go/ir/lift.go +++ b/go/ir/lift.go @@ -1488,6 +1488,10 @@ func splitOnNewInformation(u *BasicBlock, renaming *StackMap) { // A slice to array pointer conversion tells us the minimum length of the slice rename(instr.X, instr, CopyInfoUnspecified, i) i++ + case *SliceToArray: + // A slice to array conversion tells us the minimum length of the slice + rename(instr.X, instr, CopyInfoUnspecified, i) + i++ case *Slice: // Slicing tells us about some of the bounds off := 0 diff --git a/go/ir/print.go b/go/ir/print.go index e7c17e3e9..08bf0fdc9 100644 --- a/go/ir/print.go +++ b/go/ir/print.go @@ -174,6 +174,7 @@ func (v *ChangeType) String() string { return printConv("ChangeType", v func (v *Convert) String() string { return printConv("Convert", v, v.X) } func (v *ChangeInterface) String() string { return printConv("ChangeInterface", v, v.X) } func (v *SliceToArrayPointer) String() string { return printConv("SliceToArrayPointer", v, v.X) } +func (v *SliceToArray) String() string { return printConv("SliceToArray", v, v.X) } func (v *MakeInterface) String() string { return printConv("MakeInterface", v, v.X) } func (v *MakeClosure) String() string { diff --git a/go/ir/sanity.go b/go/ir/sanity.go index 6998312ed..bdde1ecd9 100644 --- a/go/ir/sanity.go +++ b/go/ir/sanity.go @@ -142,6 +142,7 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { case *ChangeInterface: case *ChangeType: case *SliceToArrayPointer: + case *SliceToArray: case *Convert: if _, ok := instr.X.Type().Underlying().(*types.Basic); !ok { if _, ok := instr.Type().Underlying().(*types.Basic); !ok { diff --git a/go/ir/ssa.go b/go/ir/ssa.go index aee543104..0c05da49e 100644 --- a/go/ir/ssa.go +++ b/go/ir/ssa.go @@ -927,13 +927,27 @@ type ChangeInterface struct { // from an explicit conversion in the source. // // Example printed form: -// t1 = SliceToArrayPointer <*[4]byte> t1 // +// t2 = SliceToArrayPointer <*[4]byte> t1 type SliceToArrayPointer struct { register X Value } +// The SliceToArray instruction yields the conversion of slice X to +// array. +// +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. +// +// Example printed form: +// +// t2 = SliceToArray <[4]byte> t1 +type SliceToArray struct { + register + X Value +} + // MakeInterface constructs an instance of an interface type from a // value of a concrete type. // @@ -1911,6 +1925,10 @@ func (v *SliceToArrayPointer) Operands(rands []*Value) []*Value { return append(rands, &v.X) } +func (v *SliceToArray) Operands(rands []*Value) []*Value { + return append(rands, &v.X) +} + func (s *DebugRef) Operands(rands []*Value) []*Value { return append(rands, &s.X) } diff --git a/unused/unused.go b/unused/unused.go index 05b10d272..d6a6b55ca 100644 --- a/unused/unused.go +++ b/unused/unused.go @@ -1789,6 +1789,8 @@ func (g *graph) instructions(fn *ir.Function) { // nothing to do case *ir.SliceToArrayPointer: // nothing to do + case *ir.SliceToArray: + // nothing to do case *ir.CompositeValue: if t, ok := typeutil.CoreType(instr.Type()).(*types.Struct); ok { for i := 0; i < len(instr.Values); i++ {