1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package dwarfgen
6
7import (
8	"sort"
9
10	"cmd/compile/internal/base"
11	"cmd/compile/internal/ir"
12	"cmd/internal/dwarf"
13	"cmd/internal/obj"
14	"cmd/internal/src"
15)
16
17// See golang.org/issue/20390.
18func xposBefore(p, q src.XPos) bool {
19	return base.Ctxt.PosTable.Pos(p).Before(base.Ctxt.PosTable.Pos(q))
20}
21
22func findScope(marks []ir.Mark, pos src.XPos) ir.ScopeID {
23	i := sort.Search(len(marks), func(i int) bool {
24		return xposBefore(pos, marks[i].Pos)
25	})
26	if i == 0 {
27		return 0
28	}
29	return marks[i-1].Scope
30}
31
32func assembleScopes(fnsym *obj.LSym, fn *ir.Func, dwarfVars []*dwarf.Var, varScopes []ir.ScopeID) []dwarf.Scope {
33	// Initialize the DWARF scope tree based on lexical scopes.
34	dwarfScopes := make([]dwarf.Scope, 1+len(fn.Parents))
35	for i, parent := range fn.Parents {
36		dwarfScopes[i+1].Parent = int32(parent)
37	}
38
39	scopeVariables(dwarfVars, varScopes, dwarfScopes, fnsym.ABI() != obj.ABI0)
40	if fnsym.Func().Text != nil {
41		scopePCs(fnsym, fn.Marks, dwarfScopes)
42	}
43	return compactScopes(dwarfScopes)
44}
45
46// scopeVariables assigns DWARF variable records to their scopes.
47func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ir.ScopeID, dwarfScopes []dwarf.Scope, regabi bool) {
48	if regabi {
49		sort.Stable(varsByScope{dwarfVars, varScopes})
50	} else {
51		sort.Stable(varsByScopeAndOffset{dwarfVars, varScopes})
52	}
53
54	i0 := 0
55	for i := range dwarfVars {
56		if varScopes[i] == varScopes[i0] {
57			continue
58		}
59		dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:i]
60		i0 = i
61	}
62	if i0 < len(dwarfVars) {
63		dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:]
64	}
65}
66
67// scopePCs assigns PC ranges to their scopes.
68func scopePCs(fnsym *obj.LSym, marks []ir.Mark, dwarfScopes []dwarf.Scope) {
69	// If there aren't any child scopes (in particular, when scope
70	// tracking is disabled), we can skip a whole lot of work.
71	if len(marks) == 0 {
72		return
73	}
74	p0 := fnsym.Func().Text
75	scope := findScope(marks, p0.Pos)
76	for p := p0; p != nil; p = p.Link {
77		if p.Pos == p0.Pos {
78			continue
79		}
80		dwarfScopes[scope].AppendRange(dwarf.Range{Start: p0.Pc, End: p.Pc})
81		p0 = p
82		scope = findScope(marks, p0.Pos)
83	}
84	if p0.Pc < fnsym.Size {
85		dwarfScopes[scope].AppendRange(dwarf.Range{Start: p0.Pc, End: fnsym.Size})
86	}
87}
88
89func compactScopes(dwarfScopes []dwarf.Scope) []dwarf.Scope {
90	// Reverse pass to propagate PC ranges to parent scopes.
91	for i := len(dwarfScopes) - 1; i > 0; i-- {
92		s := &dwarfScopes[i]
93		dwarfScopes[s.Parent].UnifyRanges(s)
94	}
95
96	return dwarfScopes
97}
98
99type varsByScopeAndOffset struct {
100	vars   []*dwarf.Var
101	scopes []ir.ScopeID
102}
103
104func (v varsByScopeAndOffset) Len() int {
105	return len(v.vars)
106}
107
108func (v varsByScopeAndOffset) Less(i, j int) bool {
109	if v.scopes[i] != v.scopes[j] {
110		return v.scopes[i] < v.scopes[j]
111	}
112	return v.vars[i].StackOffset < v.vars[j].StackOffset
113}
114
115func (v varsByScopeAndOffset) Swap(i, j int) {
116	v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
117	v.scopes[i], v.scopes[j] = v.scopes[j], v.scopes[i]
118}
119
120type varsByScope struct {
121	vars   []*dwarf.Var
122	scopes []ir.ScopeID
123}
124
125func (v varsByScope) Len() int {
126	return len(v.vars)
127}
128
129func (v varsByScope) Less(i, j int) bool {
130	return v.scopes[i] < v.scopes[j]
131}
132
133func (v varsByScope) Swap(i, j int) {
134	v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
135	v.scopes[i], v.scopes[j] = v.scopes[j], v.scopes[i]
136}
137