1
2
3
4
5 package ssagen
6
7 import (
8 "bytes"
9 "fmt"
10
11 "cmd/compile/internal/base"
12 "cmd/compile/internal/ir"
13 "cmd/compile/internal/typecheck"
14 "cmd/compile/internal/types"
15 "cmd/internal/obj"
16 "cmd/internal/src"
17 )
18
19 func EnableNoWriteBarrierRecCheck() {
20 nowritebarrierrecCheck = newNowritebarrierrecChecker()
21 }
22
23 func NoWriteBarrierRecCheck() {
24
25
26 nowritebarrierrecCheck.check()
27 nowritebarrierrecCheck = nil
28 }
29
30 var nowritebarrierrecCheck *nowritebarrierrecChecker
31
32 type nowritebarrierrecChecker struct {
33
34
35
36 extraCalls map[*ir.Func][]nowritebarrierrecCall
37
38
39 curfn *ir.Func
40 }
41
42 type nowritebarrierrecCall struct {
43 target *ir.Func
44 lineno src.XPos
45 }
46
47
48
49 func newNowritebarrierrecChecker() *nowritebarrierrecChecker {
50 c := &nowritebarrierrecChecker{
51 extraCalls: make(map[*ir.Func][]nowritebarrierrecCall),
52 }
53
54
55
56
57
58
59 for _, n := range typecheck.Target.Decls {
60 if n.Op() != ir.ODCLFUNC {
61 continue
62 }
63 c.curfn = n.(*ir.Func)
64 if c.curfn.ABIWrapper() {
65
66
67
68 continue
69 }
70 ir.Visit(n, c.findExtraCalls)
71 }
72 c.curfn = nil
73 return c
74 }
75
76 func (c *nowritebarrierrecChecker) findExtraCalls(nn ir.Node) {
77 if nn.Op() != ir.OCALLFUNC {
78 return
79 }
80 n := nn.(*ir.CallExpr)
81 if n.X == nil || n.X.Op() != ir.ONAME {
82 return
83 }
84 fn := n.X.(*ir.Name)
85 if fn.Class != ir.PFUNC || fn.Defn == nil {
86 return
87 }
88 if !types.IsRuntimePkg(fn.Sym().Pkg) || fn.Sym().Name != "systemstack" {
89 return
90 }
91
92 var callee *ir.Func
93 arg := n.Args[0]
94 switch arg.Op() {
95 case ir.ONAME:
96 arg := arg.(*ir.Name)
97 callee = arg.Defn.(*ir.Func)
98 case ir.OCLOSURE:
99 arg := arg.(*ir.ClosureExpr)
100 callee = arg.Func
101 default:
102 base.Fatalf("expected ONAME or OCLOSURE node, got %+v", arg)
103 }
104 if callee.Op() != ir.ODCLFUNC {
105 base.Fatalf("expected ODCLFUNC node, got %+v", callee)
106 }
107 c.extraCalls[c.curfn] = append(c.extraCalls[c.curfn], nowritebarrierrecCall{callee, n.Pos()})
108 }
109
110
111
112
113
114
115
116
117
118 func (c *nowritebarrierrecChecker) recordCall(fn *ir.Func, to *obj.LSym, pos src.XPos) {
119
120 if fn.NWBRCalls == nil {
121 fn.NWBRCalls = new([]ir.SymAndPos)
122 }
123 *fn.NWBRCalls = append(*fn.NWBRCalls, ir.SymAndPos{Sym: to, Pos: pos})
124 }
125
126 func (c *nowritebarrierrecChecker) check() {
127
128
129
130
131 symToFunc := make(map[*obj.LSym]*ir.Func)
132
133
134
135
136
137
138 funcs := make(map[*ir.Func]nowritebarrierrecCall)
139
140 var q ir.NameQueue
141
142 for _, n := range typecheck.Target.Decls {
143 if n.Op() != ir.ODCLFUNC {
144 continue
145 }
146 fn := n.(*ir.Func)
147
148 symToFunc[fn.LSym] = fn
149
150
151 if fn.Pragma&ir.Nowritebarrierrec != 0 {
152 funcs[fn] = nowritebarrierrecCall{}
153 q.PushRight(fn.Nname)
154 }
155
156 if fn.Pragma&ir.Nowritebarrier != 0 && fn.WBPos.IsKnown() {
157 base.ErrorfAt(fn.WBPos, "write barrier prohibited")
158 }
159 }
160
161
162
163 enqueue := func(src, target *ir.Func, pos src.XPos) {
164 if target.Pragma&ir.Yeswritebarrierrec != 0 {
165
166 return
167 }
168 if _, ok := funcs[target]; ok {
169
170 return
171 }
172
173
174 funcs[target] = nowritebarrierrecCall{target: src, lineno: pos}
175 q.PushRight(target.Nname)
176 }
177 for !q.Empty() {
178 fn := q.PopLeft().Func
179
180
181 if fn.WBPos.IsKnown() {
182 var err bytes.Buffer
183 call := funcs[fn]
184 for call.target != nil {
185 fmt.Fprintf(&err, "\n\t%v: called by %v", base.FmtPos(call.lineno), call.target.Nname)
186 call = funcs[call.target]
187 }
188 base.ErrorfAt(fn.WBPos, "write barrier prohibited by caller; %v%s", fn.Nname, err.String())
189 continue
190 }
191
192
193 for _, callee := range c.extraCalls[fn] {
194 enqueue(fn, callee.target, callee.lineno)
195 }
196 if fn.NWBRCalls == nil {
197 continue
198 }
199 for _, callee := range *fn.NWBRCalls {
200 target := symToFunc[callee.Sym]
201 if target != nil {
202 enqueue(fn, target, callee.Pos)
203 }
204 }
205 }
206 }
207
View as plain text