Skip to content

cmd/compile/internal/devirtualize: improve concrete type analysis #71935

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
461 changes: 450 additions & 11 deletions src/cmd/compile/internal/devirtualize/devirtualize.go

Large diffs are not rendered by default.

37 changes: 21 additions & 16 deletions src/cmd/compile/internal/inline/interleaved/interleaved.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
inlState := make(map[*ir.Func]*inlClosureState)
calleeUseCounts := make(map[*ir.Func]int)

var state devirtualize.State

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

// Do a first pass at counting call sites.
for i := range s.parens {
s.resolve(i)
s.resolve(&state, i)
}
}

Expand Down Expand Up @@ -102,10 +104,11 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
for {
for i := l0; i < l1; i++ { // can't use "range parens" here
paren := s.parens[i]
if new := s.edit(i); new != nil {
if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
// Update AST and recursively mark nodes.
paren.X = new
ir.EditChildren(new, s.mark) // mark may append to parens
paren.X = inlinedCall
ir.EditChildren(inlinedCall, s.mark) // mark may append to parens
state.InlinedCall(s.fn, origCall, inlinedCall)
done = false
}
}
Expand All @@ -114,7 +117,7 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
break
}
for i := l0; i < l1; i++ {
s.resolve(i)
s.resolve(&state, i)
}

}
Expand Down Expand Up @@ -188,7 +191,7 @@ type inlClosureState struct {
// resolve attempts to resolve a call to a potentially inlineable callee
// and updates use counts on the callees. Returns the call site count
// for that callee.
func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
func (s *inlClosureState) resolve(state *devirtualize.State, i int) (*ir.Func, int) {
p := s.parens[i]
if i < len(s.resolved) {
if callee := s.resolved[i]; callee != nil {
Expand All @@ -200,7 +203,7 @@ func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
if !ok { // previously inlined
return nil, -1
}
devirtualize.StaticCall(call)
devirtualize.StaticCall(state, call)
if callee := inline.InlineCallTarget(s.fn, call, s.profile); callee != nil {
for len(s.resolved) <= i {
s.resolved = append(s.resolved, nil)
Expand All @@ -213,23 +216,23 @@ func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
return nil, 0
}

func (s *inlClosureState) edit(i int) ir.Node {
func (s *inlClosureState) edit(state *devirtualize.State, i int) (*ir.CallExpr, *ir.InlinedCallExpr) {
n := s.parens[i].X
call, ok := n.(*ir.CallExpr)
if !ok {
return nil
return nil, nil
}
// This is redundant with earlier calls to
// resolve, but because things can change it
// must be re-checked.
callee, count := s.resolve(i)
callee, count := s.resolve(state, i)
if count <= 0 {
return nil
return nil, nil
}
if inlCall := inline.TryInlineCall(s.fn, call, s.bigCaller, s.profile, count == 1 && callee.ClosureParent != nil); inlCall != nil {
return inlCall
return call, inlCall
}
return nil
return nil, nil
}

// Mark inserts parentheses, and is called repeatedly.
Expand Down Expand Up @@ -350,16 +353,18 @@ func (s *inlClosureState) unparenthesize() {
// returns.
func (s *inlClosureState) fixpoint() bool {
changed := false
var state devirtualize.State
ir.WithFunc(s.fn, func() {
done := false
for !done {
done = true
for i := 0; i < len(s.parens); i++ { // can't use "range parens" here
paren := s.parens[i]
if new := s.edit(i); new != nil {
if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
// Update AST and recursively mark nodes.
paren.X = new
ir.EditChildren(new, s.mark) // mark may append to parens
paren.X = inlinedCall
ir.EditChildren(inlinedCall, s.mark) // mark may append to parens
state.InlinedCall(s.fn, origCall, inlinedCall)
done = false
changed = true
}
Expand Down
5 changes: 5 additions & 0 deletions src/cmd/compile/internal/ir/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,11 @@ type TypeAssertExpr struct {

// An internal/abi.TypeAssert descriptor to pass to the runtime.
Descriptor *obj.LSym

// When set to true, if this assert would panic, then use a nil pointer panic
// instead of an interface conversion panic.
// It must not be set for type asserts using the commaok form.
UseNilPanic bool
}

func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr {
Expand Down
1 change: 1 addition & 0 deletions src/cmd/compile/internal/noder/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2941,6 +2941,7 @@ func (r *reader) multiExpr() []ir.Node {
as.Def = true
for i := range results {
tmp := r.temp(pos, r.typ())
tmp.Defn = as
as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
as.Lhs.Append(tmp)

Expand Down
19 changes: 19 additions & 0 deletions src/cmd/compile/internal/ssagen/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -5771,6 +5771,25 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
if n.ITab != nil {
targetItab = s.expr(n.ITab)
}

if n.UseNilPanic {
if commaok {
base.Fatalf("unexpected *ir.TypeAssertExpr with UseNilPanic == true && commaok == true")
}
if n.Type().IsInterface() {
// Currently we do not expect the compiler to emit type asserts with UseNilPanic, that assert to an interface type.
// If needed, this can be relaxed in the future, but for now we can assert that.
base.Fatalf("unexpected *ir.TypeAssertExpr with UseNilPanic == true && Type().IsInterface() == true")
}
typs := s.f.Config.Types
iface = s.newValue2(
ssa.OpIMake,
iface.Type,
s.nilCheck(s.newValue1(ssa.OpITab, typs.BytePtr, iface)),
s.newValue1(ssa.OpIData, typs.BytePtr, iface),
)
}

return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, nil, target, targetItab, commaok, n.Descriptor)
}

Expand Down
14 changes: 14 additions & 0 deletions src/crypto/sha256/sha256_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,17 @@ func BenchmarkHash256K(b *testing.B) {
func BenchmarkHash1M(b *testing.B) {
benchmarkSize(b, 1024*1024)
}

func TestAllocatonsWithTypeAsserts(t *testing.T) {
cryptotest.SkipTestAllocations(t)
allocs := testing.AllocsPerRun(100, func() {
h := New()
h.Write([]byte{1, 2, 3})
marshaled, _ := h.(encoding.BinaryMarshaler).MarshalBinary()
marshaled, _ = h.(encoding.BinaryAppender).AppendBinary(marshaled[:0])
h.(encoding.BinaryUnmarshaler).UnmarshalBinary(marshaled)
})
if allocs != 0 {
t.Fatalf("allocs = %v; want = 0", allocs)
}
}
5 changes: 5 additions & 0 deletions src/runtime/pprof/pprof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@ func (h inlineWrapper) dump(pcs []uintptr) {

func inlinedWrapperCallerDump(pcs []uintptr) {
var h inlineWrapperInterface

// Take the address of h, such that h.dump() call (below)
// does not get devirtualized by the compiler.
_ = &h

h = &inlineWrapper{}
h.dump(pcs)
}
Expand Down
Loading