Skip to content

Commit f8d8097

Browse files
committed
cmd/compile: correct leaf type when "selecting" singleton register-sized 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 <[email protected]> Run-TryBot: David Chase <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent d2a80f3 commit f8d8097

File tree

3 files changed

+141
-7
lines changed

3 files changed

+141
-7
lines changed

src/cmd/compile/internal/ssa/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ const (
195195
ClassParamOut // return value
196196
)
197197

198-
const go116lateCallExpansion = false
198+
const go116lateCallExpansion = true
199199

200200
// LateCallExpansionEnabledWithin returns true if late call expansion should be tested
201201
// within compilation of a function/method triggered by GOSSAHASH (defaults to "yes").

src/cmd/compile/internal/ssa/expand_calls.go

+35-6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,29 @@ func expandCalls(f *Func) {
5858
return t.IsStruct() || t.IsArray() || regSize == 4 && t.Size() > 4 && t.IsInteger()
5959
}
6060

61+
// removeTrivialWrapperTypes unwraps layers of
62+
// struct { singleField SomeType } and [1]SomeType
63+
// until a non-wrapper type is reached. This is useful
64+
// for working with assignments to/from interface data
65+
// fields (either second operand to OpIMake or OpIData)
66+
// where the wrapping or type conversion can be elided
67+
// because of type conversions/assertions in source code
68+
// that do not appear in SSA.
69+
removeTrivialWrapperTypes := func(t *types.Type) *types.Type {
70+
for {
71+
if t.IsStruct() && t.NumFields() == 1 {
72+
t = t.Field(0).Type
73+
continue
74+
}
75+
if t.IsArray() && t.NumElem() == 1 {
76+
t = t.Elem()
77+
continue
78+
}
79+
break
80+
}
81+
return t
82+
}
83+
6184
// Calls that need lowering have some number of inputs, including a memory input,
6285
// and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
6386

@@ -84,14 +107,15 @@ func expandCalls(f *Func) {
84107
// rewrite v as a Copy of call -- the replacement call will produce a mem.
85108
leaf.copyOf(call)
86109
} else {
87-
leafType := leaf.Type
110+
leafType := removeTrivialWrapperTypes(leaf.Type)
88111
pt := types.NewPtr(leafType)
89112
if canSSAType(leafType) {
90113
off := f.ConstOffPtrSP(pt, offset+aux.OffsetOfResult(which), sp)
91114
// Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
92115
if leaf.Block == call.Block {
93116
leaf.reset(OpLoad)
94117
leaf.SetArgs2(off, call)
118+
leaf.Type = leafType
95119
} else {
96120
w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
97121
leaf.copyOf(w)
@@ -192,26 +216,31 @@ func expandCalls(f *Func) {
192216

193217
case types.TARRAY:
194218
elt := t.Elem()
219+
if src.Op == OpIData && t.NumElem() == 1 && t.Width == regSize && elt.Width == regSize {
220+
t = removeTrivialWrapperTypes(t)
221+
if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY {
222+
f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it")
223+
}
224+
break // handle the leaf type.
225+
}
195226
for i := int64(0); i < t.NumElem(); i++ {
196227
sel := src.Block.NewValue1I(pos, OpArraySelect, elt, i, src)
197228
mem = splitStore(dst, sel, mem, v, elt, offset+i*elt.Width, firstStorePos)
198229
firstStorePos = firstStorePos.WithNotStmt()
199230
}
200231
return mem
201232
case types.TSTRUCT:
202-
if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
233+
if src.Op == OpIData && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
203234
// This peculiar test deals with accesses to immediate interface data.
204235
// It works okay because everything is the same size.
205236
// Example code that triggers this can be found in go/constant/value.go, function ToComplex
206237
// v119 (+881) = IData <intVal> v6
207238
// v121 (+882) = StaticLECall <floatVal,mem> {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1
208239
// This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)"
209240
// Guard against "struct{struct{*foo}}"
210-
for t.Etype == types.TSTRUCT && t.NumFields() == 1 {
211-
t = t.Field(0).Type
212-
}
241+
t = removeTrivialWrapperTypes(t)
213242
if t.Etype == types.TSTRUCT || t.Etype == types.TARRAY {
214-
f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct in it")
243+
f.Fatalf("Did not expect to find IDATA-immediate with non-trivial struct/array in it")
215244
}
216245
break // handle the leaf type.
217246
}

test/fixedbugs/issue41736.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// compile
2+
3+
// Copyright 2020 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package p
8+
9+
type I struct {
10+
x int64
11+
}
12+
13+
type F struct {
14+
x float64
15+
}
16+
17+
type C struct {
18+
x *complex128
19+
}
20+
21+
type D struct {
22+
x complex64
23+
}
24+
25+
type A [1]*complex128
26+
27+
//go:noinline
28+
func (i I) X() C {
29+
cx := complex(0, float64(i.x))
30+
return C{&cx}
31+
}
32+
33+
//go:noinline
34+
func (f F) X() C {
35+
cx := complex(f.x, 0)
36+
return C{&cx}
37+
}
38+
39+
//go:noinline
40+
func (c C) X() C {
41+
cx := complex(imag(*c.x), real(*c.x))
42+
return C{&cx}
43+
}
44+
45+
//go:noinline
46+
func (d D) X() C {
47+
cx := complex(float64(imag(d.x)), -float64(real(d.x)))
48+
return C{&cx}
49+
}
50+
51+
//go:noinline
52+
func (a A) X() C {
53+
cx := complex(-float64(imag(*a[0])), float64(real(*a[0])))
54+
return C{&cx}
55+
}
56+
57+
//go:noinline
58+
func (i I) id() I {
59+
return i
60+
}
61+
62+
//go:noinline
63+
func (f F) id() F {
64+
return f
65+
}
66+
67+
//go:noinline
68+
func (c C) id() C {
69+
return c
70+
}
71+
72+
//go:noinline
73+
func (d D) id() D {
74+
return d
75+
}
76+
77+
//go:noinline
78+
func (a A) id() A {
79+
return a
80+
}
81+
82+
type T interface {
83+
X() C
84+
}
85+
86+
func G(x []T) []T {
87+
var y []T
88+
for _, a := range x {
89+
var v T
90+
switch u := a.(type) {
91+
case I:
92+
v = u.id()
93+
case F:
94+
v = u.id()
95+
case C:
96+
v = u.id()
97+
case D:
98+
v = u.id()
99+
case A:
100+
v = u.id()
101+
}
102+
y = append(y, v)
103+
}
104+
return y
105+
}

0 commit comments

Comments
 (0)