Skip to content

Commit

Permalink
cmd/compile: correct leaf type when "selecting" singleton register-si…
Browse files Browse the repository at this point in the history
…zed struct

Two part fix:
1) bring the type "correction" forward from a later CL in the expand calls series
2) when a leaf-selwect is rewritten in place, update the type (it might have been
   changed by the type correction in 1).

Fixes #41736.

Change-Id: Id097efd10481bf0ad92aaead81a7207221c144b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/259203
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
  • Loading branch information
dr2chase committed Oct 6, 2020
1 parent d2a80f3 commit f8d8097
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/ssa/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ const (
ClassParamOut // return value
)

const go116lateCallExpansion = false
const go116lateCallExpansion = true

// LateCallExpansionEnabledWithin returns true if late call expansion should be tested
// within compilation of a function/method triggered by GOSSAHASH (defaults to "yes").
Expand Down
41 changes: 35 additions & 6 deletions src/cmd/compile/internal/ssa/expand_calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,29 @@ func expandCalls(f *Func) {
return t.IsStruct() || t.IsArray() || regSize == 4 && t.Size() > 4 && t.IsInteger()
}

// removeTrivialWrapperTypes unwraps layers of
// struct { singleField SomeType } and [1]SomeType
// until a non-wrapper type is reached. This is useful
// for working with assignments to/from interface data
// fields (either second operand to OpIMake or OpIData)
// where the wrapping or type conversion can be elided
// because of type conversions/assertions in source code
// that do not appear in SSA.
removeTrivialWrapperTypes := func(t *types.Type) *types.Type {
for {
if t.IsStruct() && t.NumFields() == 1 {
t = t.Field(0).Type
continue
}
if t.IsArray() && t.NumElem() == 1 {
t = t.Elem()
continue
}
break
}
return t
}

// Calls that need lowering have some number of inputs, including a memory input,
// and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.

Expand All @@ -84,14 +107,15 @@ func expandCalls(f *Func) {
// rewrite v as a Copy of call -- the replacement call will produce a mem.
leaf.copyOf(call)
} else {
leafType := leaf.Type
leafType := removeTrivialWrapperTypes(leaf.Type)
pt := types.NewPtr(leafType)
if canSSAType(leafType) {
off := f.ConstOffPtrSP(pt, offset+aux.OffsetOfResult(which), sp)
// Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
if leaf.Block == call.Block {
leaf.reset(OpLoad)
leaf.SetArgs2(off, call)
leaf.Type = leafType
} else {
w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
leaf.copyOf(w)
Expand Down Expand Up @@ -192,26 +216,31 @@ func expandCalls(f *Func) {

case types.TARRAY:
elt := t.Elem()
if src.Op == OpIData && t.NumElem() == 1 && t.Width == regSize && elt.Width == regSize {
t = removeTrivialWrapperTypes(t)
if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY {
f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it")
}
break // handle the leaf type.
}
for i := int64(0); i < t.NumElem(); i++ {
sel := src.Block.NewValue1I(pos, OpArraySelect, elt, i, src)
mem = splitStore(dst, sel, mem, v, elt, offset+i*elt.Width, firstStorePos)
firstStorePos = firstStorePos.WithNotStmt()
}
return mem
case types.TSTRUCT:
if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
// This peculiar test deals with accesses to immediate interface data.
// It works okay because everything is the same size.
// Example code that triggers this can be found in go/constant/value.go, function ToComplex
// v119 (+881) = IData <intVal> v6
// v121 (+882) = StaticLECall <floatVal,mem> {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1
// This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)"
// Guard against "struct{struct{*foo}}"
for t.Etype == types.TSTRUCT && t.NumFields() == 1 {
t = t.Field(0).Type
}
t = removeTrivialWrapperTypes(t)
if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY {
f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct in it")
f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it")
}
break // handle the leaf type.
}
Expand Down
105 changes: 105 additions & 0 deletions test/fixedbugs/issue41736.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// compile

// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package p

type I struct {
x int64
}

type F struct {
x float64
}

type C struct {
x *complex128
}

type D struct {
x complex64
}

type A [1]*complex128

//go:noinline
func (i I) X() C {
cx := complex(0, float64(i.x))
return C{&cx}
}

//go:noinline
func (f F) X() C {
cx := complex(f.x, 0)
return C{&cx}
}

//go:noinline
func (c C) X() C {
cx := complex(imag(*c.x), real(*c.x))
return C{&cx}
}

//go:noinline
func (d D) X() C {
cx := complex(float64(imag(d.x)), -float64(real(d.x)))
return C{&cx}
}

//go:noinline
func (a A) X() C {
cx := complex(-float64(imag(*a[0])), float64(real(*a[0])))
return C{&cx}
}

//go:noinline
func (i I) id() I {
return i
}

//go:noinline
func (f F) id() F {
return f
}

//go:noinline
func (c C) id() C {
return c
}

//go:noinline
func (d D) id() D {
return d
}

//go:noinline
func (a A) id() A {
return a
}

type T interface {
X() C
}

func G(x []T) []T {
var y []T
for _, a := range x {
var v T
switch u := a.(type) {
case I:
v = u.id()
case F:
v = u.id()
case C:
v = u.id()
case D:
v = u.id()
case A:
v = u.id()
}
y = append(y, v)
}
return y
}

0 comments on commit f8d8097

Please sign in to comment.