1// Copyright 2021 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	"cmd/compile/internal/base"
9	"cmd/compile/internal/ir"
10	"cmd/internal/src"
11)
12
13// A ScopeMarker tracks scope nesting and boundaries for later use
14// during DWARF generation.
15type ScopeMarker struct {
16	parents []ir.ScopeID
17	marks   []ir.Mark
18}
19
20// checkPos validates the given position and returns the current scope.
21func (m *ScopeMarker) checkPos(pos src.XPos) ir.ScopeID {
22	if !pos.IsKnown() {
23		base.Fatalf("unknown scope position")
24	}
25
26	if len(m.marks) == 0 {
27		return 0
28	}
29
30	last := &m.marks[len(m.marks)-1]
31	if xposBefore(pos, last.Pos) {
32		base.FatalfAt(pos, "non-monotonic scope positions\n\t%v: previous scope position", base.FmtPos(last.Pos))
33	}
34	return last.Scope
35}
36
37// Push records a transition to a new child scope of the current scope.
38func (m *ScopeMarker) Push(pos src.XPos) {
39	current := m.checkPos(pos)
40
41	m.parents = append(m.parents, current)
42	child := ir.ScopeID(len(m.parents))
43
44	m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: child})
45}
46
47// Pop records a transition back to the current scope's parent.
48func (m *ScopeMarker) Pop(pos src.XPos) {
49	current := m.checkPos(pos)
50
51	parent := m.parents[current-1]
52
53	m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: parent})
54}
55
56// Unpush removes the current scope, which must be empty.
57func (m *ScopeMarker) Unpush() {
58	i := len(m.marks) - 1
59	current := m.marks[i].Scope
60
61	if current != ir.ScopeID(len(m.parents)) {
62		base.FatalfAt(m.marks[i].Pos, "current scope is not empty")
63	}
64
65	m.parents = m.parents[:current-1]
66	m.marks = m.marks[:i]
67}
68
69// WriteTo writes the recorded scope marks to the given function,
70// and resets the marker for reuse.
71func (m *ScopeMarker) WriteTo(fn *ir.Func) {
72	m.compactMarks()
73
74	fn.Parents = make([]ir.ScopeID, len(m.parents))
75	copy(fn.Parents, m.parents)
76	m.parents = m.parents[:0]
77
78	fn.Marks = make([]ir.Mark, len(m.marks))
79	copy(fn.Marks, m.marks)
80	m.marks = m.marks[:0]
81}
82
83func (m *ScopeMarker) compactMarks() {
84	n := 0
85	for _, next := range m.marks {
86		if n > 0 && next.Pos == m.marks[n-1].Pos {
87			m.marks[n-1].Scope = next.Scope
88			continue
89		}
90		m.marks[n] = next
91		n++
92	}
93	m.marks = m.marks[:n]
94}
95