Skip to content

Commit 08e8555

Browse files
committed
cmd/compile: improve concrete type analysis for devirtualization
Change-Id: Iebc7737713d73690f861955a01ae6f7fa25e32ab
1 parent 6df855e commit 08e8555

File tree

12 files changed

+2005
-35
lines changed

12 files changed

+2005
-35
lines changed

src/cmd/compile/internal/devirtualize/devirtualize.go

Lines changed: 450 additions & 11 deletions
Large diffs are not rendered by default.

src/cmd/compile/internal/inline/interleaved/interleaved.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
4545
inlState := make(map[*ir.Func]*inlClosureState)
4646
calleeUseCounts := make(map[*ir.Func]int)
4747

48+
var state devirtualize.State
49+
4850
// Pre-process all the functions, adding parentheses around call sites and starting their "inl state".
4951
for _, fn := range typecheck.Target.Funcs {
5052
bigCaller := base.Flag.LowerL != 0 && inline.IsBigFunc(fn)
@@ -58,7 +60,7 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
5860

5961
// Do a first pass at counting call sites.
6062
for i := range s.parens {
61-
s.resolve(i)
63+
s.resolve(&state, i)
6264
}
6365
}
6466

@@ -102,10 +104,11 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
102104
for {
103105
for i := l0; i < l1; i++ { // can't use "range parens" here
104106
paren := s.parens[i]
105-
if new := s.edit(i); new != nil {
107+
if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
106108
// Update AST and recursively mark nodes.
107-
paren.X = new
108-
ir.EditChildren(new, s.mark) // mark may append to parens
109+
paren.X = inlinedCall
110+
ir.EditChildren(inlinedCall, s.mark) // mark may append to parens
111+
state.InlinedCall(s.fn, origCall, inlinedCall)
109112
done = false
110113
}
111114
}
@@ -114,7 +117,7 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
114117
break
115118
}
116119
for i := l0; i < l1; i++ {
117-
s.resolve(i)
120+
s.resolve(&state, i)
118121
}
119122

120123
}
@@ -188,7 +191,7 @@ type inlClosureState struct {
188191
// resolve attempts to resolve a call to a potentially inlineable callee
189192
// and updates use counts on the callees. Returns the call site count
190193
// for that callee.
191-
func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
194+
func (s *inlClosureState) resolve(state *devirtualize.State, i int) (*ir.Func, int) {
192195
p := s.parens[i]
193196
if i < len(s.resolved) {
194197
if callee := s.resolved[i]; callee != nil {
@@ -200,7 +203,7 @@ func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
200203
if !ok { // previously inlined
201204
return nil, -1
202205
}
203-
devirtualize.StaticCall(call)
206+
devirtualize.StaticCall(state, call)
204207
if callee := inline.InlineCallTarget(s.fn, call, s.profile); callee != nil {
205208
for len(s.resolved) <= i {
206209
s.resolved = append(s.resolved, nil)
@@ -213,23 +216,23 @@ func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
213216
return nil, 0
214217
}
215218

216-
func (s *inlClosureState) edit(i int) ir.Node {
219+
func (s *inlClosureState) edit(state *devirtualize.State, i int) (*ir.CallExpr, *ir.InlinedCallExpr) {
217220
n := s.parens[i].X
218221
call, ok := n.(*ir.CallExpr)
219222
if !ok {
220-
return nil
223+
return nil, nil
221224
}
222225
// This is redundant with earlier calls to
223226
// resolve, but because things can change it
224227
// must be re-checked.
225-
callee, count := s.resolve(i)
228+
callee, count := s.resolve(state, i)
226229
if count <= 0 {
227-
return nil
230+
return nil, nil
228231
}
229232
if inlCall := inline.TryInlineCall(s.fn, call, s.bigCaller, s.profile, count == 1 && callee.ClosureParent != nil); inlCall != nil {
230-
return inlCall
233+
return call, inlCall
231234
}
232-
return nil
235+
return nil, nil
233236
}
234237

235238
// Mark inserts parentheses, and is called repeatedly.
@@ -350,16 +353,18 @@ func (s *inlClosureState) unparenthesize() {
350353
// returns.
351354
func (s *inlClosureState) fixpoint() bool {
352355
changed := false
356+
var state devirtualize.State
353357
ir.WithFunc(s.fn, func() {
354358
done := false
355359
for !done {
356360
done = true
357361
for i := 0; i < len(s.parens); i++ { // can't use "range parens" here
358362
paren := s.parens[i]
359-
if new := s.edit(i); new != nil {
363+
if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
360364
// Update AST and recursively mark nodes.
361-
paren.X = new
362-
ir.EditChildren(new, s.mark) // mark may append to parens
365+
paren.X = inlinedCall
366+
ir.EditChildren(inlinedCall, s.mark) // mark may append to parens
367+
state.InlinedCall(s.fn, origCall, inlinedCall)
363368
done = false
364369
changed = true
365370
}

src/cmd/compile/internal/ir/expr.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,11 @@ type TypeAssertExpr struct {
676676

677677
// An internal/abi.TypeAssert descriptor to pass to the runtime.
678678
Descriptor *obj.LSym
679+
680+
// When set to true, if this assert would panic, then use a nil pointer panic
681+
// instead of an interface conversion panic.
682+
// It must not be set for type asserts using the commaok form.
683+
UseNilPanic bool
679684
}
680685

681686
func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr {

src/cmd/compile/internal/noder/reader.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2941,6 +2941,7 @@ func (r *reader) multiExpr() []ir.Node {
29412941
as.Def = true
29422942
for i := range results {
29432943
tmp := r.temp(pos, r.typ())
2944+
tmp.Defn = as
29442945
as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
29452946
as.Lhs.Append(tmp)
29462947

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5771,6 +5771,25 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
57715771
if n.ITab != nil {
57725772
targetItab = s.expr(n.ITab)
57735773
}
5774+
5775+
if n.UseNilPanic {
5776+
if commaok {
5777+
base.Fatalf("unexpected *ir.TypeAssertExpr with UseNilPanic == true && commaok == true")
5778+
}
5779+
if n.Type().IsInterface() {
5780+
// Currently we do not expect the compiler to emit type asserts with UseNilPanic, that assert to an interface type.
5781+
// If needed, this can be relaxed in the future, but for now we can assert that.
5782+
base.Fatalf("unexpected *ir.TypeAssertExpr with UseNilPanic == true && Type().IsInterface() == true")
5783+
}
5784+
typs := s.f.Config.Types
5785+
iface = s.newValue2(
5786+
ssa.OpIMake,
5787+
iface.Type,
5788+
s.nilCheck(s.newValue1(ssa.OpITab, typs.BytePtr, iface)),
5789+
s.newValue1(ssa.OpIData, typs.BytePtr, iface),
5790+
)
5791+
}
5792+
57745793
return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, nil, target, targetItab, commaok, n.Descriptor)
57755794
}
57765795

src/crypto/sha256/sha256_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,17 @@ func BenchmarkHash256K(b *testing.B) {
464464
func BenchmarkHash1M(b *testing.B) {
465465
benchmarkSize(b, 1024*1024)
466466
}
467+
468+
func TestAllocatonsWithTypeAsserts(t *testing.T) {
469+
cryptotest.SkipTestAllocations(t)
470+
allocs := testing.AllocsPerRun(100, func() {
471+
h := New()
472+
h.Write([]byte{1, 2, 3})
473+
marshaled, _ := h.(encoding.BinaryMarshaler).MarshalBinary()
474+
marshaled, _ = h.(encoding.BinaryAppender).AppendBinary(marshaled[:0])
475+
h.(encoding.BinaryUnmarshaler).UnmarshalBinary(marshaled)
476+
})
477+
if allocs != 0 {
478+
t.Fatalf("allocs = %v; want = 0", allocs)
479+
}
480+
}

src/runtime/pprof/pprof_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ func (h inlineWrapper) dump(pcs []uintptr) {
348348

349349
func inlinedWrapperCallerDump(pcs []uintptr) {
350350
var h inlineWrapperInterface
351+
352+
// Take the address of h, such that h.dump() call (below)
353+
// does not get devirtualized by the compiler.
354+
_ = &h
355+
351356
h = &inlineWrapper{}
352357
h.dump(pcs)
353358
}

0 commit comments

Comments
 (0)