Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(651)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/cps_ir_integrity.dart

Issue 2246623002: Delete CPS IR (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 library dart2js.cps_ir_integrity;
2
3 import '../tracer.dart' as tracer;
4 import 'cps_ir_nodes.dart';
5 import 'cps_ir_nodes_sexpr.dart';
6
7 /// Dump S-expressions on error if the tracer is enabled.
8 ///
9 /// Technically this has nothing to do with the tracer, but if you want one
10 /// enabled, you typically want the other as well, so we use the same flag.
11 const bool ENABLE_DUMP = tracer.TRACE_FILTER_PATTERN != null;
12
13 enum ScopeType { InScope, InDefinition, NotInScope }
14
15 /// Performs integrity checks on the CPS IR.
16 ///
17 /// To be run for debugging purposes, not for use in production.
18 ///
19 /// The following integrity checks are performed:
20 ///
21 /// - References are in scope of their definitions.
22 /// - Recursive Continuations and InvokeContinuations are marked as recursive.
23 /// - InvokeContinuations have the same arity as their target.
24 /// - Reference chains are valid doubly-linked lists.
25 /// - Reference chains contain exactly the references that are in the IR.
26 /// - Each definition object occurs only once in the IR (no redeclaring).
27 /// - Each reference object occurs only once in the IR (no sharing).
28 ///
29 class CheckCpsIntegrity extends TrampolineRecursiveVisitor {
30 FunctionDefinition topLevelNode;
31 final Map<Definition, ScopeType> inScope = <Definition, ScopeType>{};
32 final List<Definition> definitions = [];
33 String previousPass;
34
35 void handleDeclaration(Definition def) {
36 definitions.add(def);
37 // Check the reference chain for cycles broken links.
38 Reference anchor = null;
39 int i = 0;
40 for (Reference ref = def.firstRef; ref != null; ref = ref.next) {
41 if (ref.definition != def) {
42 error(
43 'Reference to ${ref.definition} found in '
44 'reference chain for $def',
45 def);
46 }
47 if (ref == anchor) {
48 error('Cyclic reference chain for $def', def);
49 }
50 if (i & ++i == 0) {
51 // Move the anchor every 2^Nth step.
52 anchor = ref;
53 }
54 }
55 }
56
57 void enterScope(Iterable<Definition> definitions) {
58 for (Definition def in definitions) {
59 inScope[def] = ScopeType.InScope;
60 }
61 pushAction(() {
62 for (Definition def in definitions) {
63 inScope[def] = ScopeType.NotInScope;
64 }
65 });
66 }
67
68 void enterContinuation(Continuation cont) {
69 inScope[cont] = ScopeType.InDefinition;
70 pushAction(() {
71 inScope[cont] = ScopeType.NotInScope;
72 });
73 }
74
75 void check(FunctionDefinition node, String previousPass) {
76 // [check] will be called multiple times per instance to avoid reallocating
77 // the large [inScope] map. Reset the other fields.
78 this.topLevelNode = node;
79 this.previousPass = previousPass;
80 this.definitions.clear();
81 ParentChecker.checkParents(node, this);
82 visit(node);
83 // Check for broken reference chains. We check this last, so out-of-scope
84 // references are not classified as a broken reference chain.
85 definitions.forEach(checkReferenceChain);
86 }
87
88 @override
89 Expression traverseLetCont(LetCont node) {
90 node.continuations.forEach(handleDeclaration);
91 node.continuations.forEach(push);
92
93 // Put all continuations in scope when visiting the body.
94 enterScope(node.continuations);
95
96 return node.body;
97 }
98
99 @override
100 Expression traverseLetPrim(LetPrim node) {
101 handleDeclaration(node.primitive);
102
103 // Process references in the primitive.
104 visit(node.primitive);
105
106 // Put the primitive in scope when visiting the body.
107 enterScope([node.primitive]);
108
109 return node.body;
110 }
111
112 @override
113 Expression traverseLetMutable(LetMutable node) {
114 handleDeclaration(node.variable);
115 processReference(node.valueRef);
116
117 // Put the primitive in scope when visiting the body.
118 enterScope([node.variable]);
119
120 return node.body;
121 }
122
123 @override
124 Expression traverseContinuation(Continuation cont) {
125 if (cont.isReturnContinuation) {
126 error('Non-return continuation missing body', cont);
127 }
128 cont.parameters.forEach(handleDeclaration);
129 enterScope(cont.parameters);
130 // Put every continuation in scope at its own body. The isRecursive
131 // flag is checked explicitly using [insideContinuations].
132 enterScope([cont]);
133 enterContinuation(cont);
134 return cont.body;
135 }
136
137 @override
138 visitFunctionDefinition(FunctionDefinition node) {
139 if (node.interceptorParameter != null) {
140 handleDeclaration(node.interceptorParameter);
141 enterScope([node.interceptorParameter]);
142 }
143 if (node.receiverParameter != null) {
144 handleDeclaration(node.receiverParameter);
145 enterScope([node.receiverParameter]);
146 }
147 node.parameters.forEach(handleDeclaration);
148 enterScope(node.parameters);
149 handleDeclaration(node.returnContinuation);
150 enterScope([node.returnContinuation]);
151 if (!node.returnContinuation.isReturnContinuation) {
152 error('Return continuation with a body', node);
153 }
154 visit(node.body);
155 }
156
157 @override
158 processReference(Reference ref) {
159 Definition def = ref.definition;
160 if (inScope[def] == ScopeType.NotInScope) {
161 error('Referenced out of scope: $def', ref);
162 }
163 if (ref.previous == ref) {
164 error('Shared Reference object to $def', ref);
165 }
166 if (ref.previous == null && def.firstRef != ref ||
167 ref.previous != null && ref.previous.next != ref) {
168 error('Broken .previous link in reference to $def', def);
169 }
170 ref.previous = ref; // Mark reference as "seen". We will repair it later.
171 }
172
173 @override
174 processInvokeContinuation(InvokeContinuation node) {
175 Continuation target = node.continuation;
176 if (node.isRecursive && inScope[target] == ScopeType.InScope) {
177 error('Non-recursive InvokeContinuation marked as recursive', node);
178 }
179 if (!node.isRecursive && inScope[target] == ScopeType.InDefinition) {
180 error('Recursive InvokeContinuation marked as non-recursive', node);
181 }
182 if (node.isRecursive && !target.isRecursive) {
183 error('Recursive Continuation was not marked as recursive', node);
184 }
185 if (node.argumentRefs.length != target.parameters.length) {
186 error('Arity mismatch in InvokeContinuation', node);
187 }
188 }
189
190 @override
191 processInvokeMethod(InvokeMethod node) {
192 if (node.callingConvention == CallingConvention.Intercepted) {
193 if (node.interceptorRef == null) {
194 error('No interceptor on intercepted call', node);
195 }
196 } else {
197 if (node.interceptorRef != null) {
198 error('Interceptor on call with ${node.callingConvention}', node);
199 }
200 }
201 }
202
203 void checkReferenceChain(Definition def) {
204 Reference previous = null;
205 for (Reference ref = def.firstRef; ref != null; ref = ref.next) {
206 if (ref.previous != ref) {
207 // Reference was not seen during IR traversal, so it is orphaned.
208 error('Orphaned reference in reference chain for $def', def);
209 }
210 // Repair the .previous link that was used for marking.
211 ref.previous = previous;
212 previous = ref;
213 }
214 }
215
216 error(String message, node) {
217 String sexpr;
218 if (ENABLE_DUMP) {
219 try {
220 Decorator decorator = (n, String s) => n == node ? '**$s**' : s;
221 sexpr = new SExpressionStringifier(decorator).visit(topLevelNode);
222 sexpr = 'SExpr dump (offending node marked with **):\n\n$sexpr';
223 } catch (e) {
224 sexpr = '(Exception thrown by SExpressionStringifier: $e)';
225 }
226 } else {
227 sexpr = '(Set DUMP_IR flag to enable SExpr dump)';
228 }
229 throw 'CPS integrity violation\n'
230 'After \'$previousPass\' on ${topLevelNode.element}\n'
231 '$message\n\n'
232 '$sexpr\n';
233 }
234 }
235
236 /// Traverses the CPS term and checks that node.parent is correctly set
237 /// for each visited node.
238 class ParentChecker extends DeepRecursiveVisitor {
239 static void checkParents(Node node, CheckCpsIntegrity main) {
240 ParentChecker visitor = new ParentChecker._make(main);
241 visitor._worklist.add(node);
242 visitor.trampoline();
243 }
244
245 ParentChecker._make(this.main);
246
247 Node _parent;
248 final List<Node> _worklist = <Node>[];
249 final CheckCpsIntegrity main;
250
251 void trampoline() {
252 while (_worklist.isNotEmpty) {
253 _parent = _worklist.removeLast();
254 _parent.accept(this);
255 }
256 }
257
258 error(String message, node) => main.error(message, node);
259
260 @override
261 visit(Node node) {
262 _worklist.add(node);
263 if (node.parent != _parent) {
264 error('Parent pointer on $node is ${node.parent} but should be $_parent',
265 node);
266 }
267 }
268
269 @override
270 processReference(Reference node) {
271 if (node.parent != _parent) {
272 error('Parent pointer on $node is ${node.parent} but should be $_parent',
273 node);
274 }
275 }
276 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart ('k') | pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698