OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library ir_builder; | |
kasperl
2013/11/25 11:32:54
dart2js.ir_builder?
lukas
2013/11/25 14:50:52
Done.
| |
6 | |
7 import 'ir_nodes.dart'; | |
8 import '../elements/elements.dart'; | |
9 import '../dart2jslib.dart'; | |
10 import '../source_file.dart'; | |
11 import '../tree/tree.dart'; | |
12 import '../scanner/scannerlib.dart' show Token; | |
13 import '../js_backend/js_backend.dart' show JavaScriptBackend; | |
14 | |
15 /** | |
16 * This task iterates through all resolved elements and builds [IrNode]s. The | |
17 * nodes are stored in the [nodes] map and accessible through [hasIr] and | |
18 * [getIr]. | |
19 * | |
20 * The functionality of the IrNodes is added gradually, therefore elements might | |
21 * have an IR or not, depending on the language features that are used. For | |
22 * elements that do have an IR, the tree [Node]s and the [Token]s are not used | |
23 * in the rest of the compilation. This is ensured by setting the element's | |
24 * cached tree to [:null:] and also breaking the token stream to crash future | |
25 * attempts to parse. | |
26 * | |
27 * The type inferrer works on either IR nodes or tree nodes. The IR nodes are | |
28 * then translated into the SSA form for optimizations and code generation. | |
29 * Long-term, once the IR supports the full language, the backend can be | |
30 * re-implemented to work directly on the IR. | |
31 */ | |
32 class IrBuilderTask extends CompilerTask { | |
33 final Map<Element, IrNode> nodes = new Map<Element, IrNode>(); | |
34 | |
35 IrBuilderTask(Compiler compiler) : super(compiler); | |
36 | |
37 String get name => 'IR builder'; | |
38 | |
39 bool hasIr(Element element) => nodes.containsKey(element.implementation); | |
40 | |
41 IrNode getIr(Element element) => nodes[element.implementation]; | |
42 | |
43 void buildNodes() { | |
44 if (!irEnabled()) return; | |
45 measure(() { | |
46 Map<Element, TreeElements> resolved = | |
47 compiler.enqueuer.resolution.resolvedElements; | |
48 resolved.forEach((Element element, TreeElements elementsMapping) { | |
49 if (canBuild(element)) { | |
50 element = element.implementation; | |
51 | |
52 SourceFile sourceFile = elementSourceFile(element); | |
53 IrNodeBuilderVisitor visitor = | |
54 new IrNodeBuilderVisitor(elementsMapping, compiler, sourceFile); | |
55 IrNode irNode; | |
56 ElementKind kind = element.kind; | |
57 if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
58 // TODO(lry): build ir for constructors. | |
59 } else if (kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY || | |
60 kind == ElementKind.FUNCTION || | |
61 kind == ElementKind.GETTER || | |
62 kind == ElementKind.SETTER) { | |
63 irNode = visitor.buildMethod(element); | |
64 } else if (kind == ElementKind.FIELD) { | |
65 // TODO(lry): build ir for lazy initializers of static fields. | |
66 } else { | |
67 compiler.internalErrorOnElement(element, | |
68 'unexpected element kind $kind'); | |
69 } | |
70 | |
71 if (irNode != null) { | |
72 nodes[element] = irNode; | |
73 unlinkTreeAndToken(element); | |
74 } | |
75 } | |
76 }); | |
77 }); | |
78 } | |
79 | |
80 bool irEnabled() { | |
81 // TODO(lry): support checked-mode checks. | |
82 if (compiler.enableTypeAssertions) return false; | |
83 if (compiler.backend is !JavaScriptBackend) return false; | |
84 return true; | |
85 } | |
86 | |
87 bool canBuild(Element element) { | |
88 // TODO(lry): support lazy initializers. | |
89 FunctionElement function = element.asFunctionElement(); | |
90 if (function == null) return false; | |
91 | |
92 // TODO(lry): support functions with parameters. | |
93 FunctionSignature signature = function.computeSignature(compiler); | |
94 if (signature.parameterCount > 0) return false; | |
95 | |
96 // TODO(lry): support intercepted methods. Then the dependency on | |
97 // JavaScriptBackend will go away. | |
98 JavaScriptBackend backend = compiler.backend; | |
99 if (backend.isInterceptedMethod(element)) return false; | |
100 | |
101 return true; | |
102 } | |
103 | |
104 void unlinkTreeAndToken(element) { | |
105 element.beginToken.next = null; | |
106 element.cachedNode = null; | |
107 } | |
108 | |
109 SourceFile elementSourceFile(Element element) { | |
110 if (element is FunctionElement) { | |
111 FunctionElement functionElement = element; | |
112 if (functionElement.patch != null) element = functionElement.patch; | |
113 } | |
114 return element.getCompilationUnit().script.file; | |
115 } | |
116 } | |
117 | |
118 /** | |
119 * A tree visitor that builds [IrNodes]. The visit methods add statements using | |
120 * to the [builder] and return the last added statement for trees that represent | |
121 * an expression. | |
122 */ | |
123 class IrNodeBuilderVisitor extends ResolvedVisitor<IrNode> { | |
124 final SourceFile sourceFile; | |
125 | |
126 IrNodeBuilderVisitor( | |
127 TreeElements elements, | |
128 Compiler compiler, | |
129 this.sourceFile) | |
130 : super(elements, compiler); | |
131 | |
132 IrBuilder builder; | |
133 | |
134 /** | |
135 * Builds the [IrFunction] for a function element. In case the function | |
136 * uses features that cannot be expressed in the IR, this function returns | |
137 * [:null:]. | |
138 */ | |
139 IrFunction buildMethod(FunctionElement functionElement) { | |
140 return nullIfGiveup(() => buildMethodInternal(functionElement)); | |
141 } | |
142 | |
143 IrFunction buildMethodInternal(FunctionElement functionElement) { | |
144 assert(invariant(functionElement, functionElement.isImplementation)); | |
145 FunctionExpression function = functionElement.parseNode(compiler); | |
146 assert(function != null); | |
147 assert(!function.modifiers.isExternal()); | |
148 assert(elements[function] != null); | |
149 | |
150 int endPosition = function.getEndToken().charOffset; | |
151 int namePosition = elements[function].position().charOffset; | |
152 IrFunction result = new IrFunction( | |
153 nodePosition(function), endPosition, namePosition, <IrNode>[]); | |
154 builder = new IrBuilder(this); | |
155 builder.enterBlock(); | |
156 if (function.hasBody()) { | |
157 function.body.accept(this); | |
158 ensureReturn(function); | |
159 result.statements | |
160 ..addAll(builder.constants.values) | |
161 ..addAll(builder.statements); | |
162 } | |
163 builder.exitBlock(); | |
164 return result; | |
165 } | |
166 | |
167 ConstantSystem get constantSystem => compiler.backend.constantSystem; | |
168 | |
169 /* int | PositionWithIdentifierName */ nodePosition(Node node) { | |
170 Token token = node.getBeginToken(); | |
171 if (token.isIdentifier()) { | |
172 return new PositionWithIdentifierName(token.charOffset, token.value); | |
173 } else { | |
174 return token.charOffset; | |
175 } | |
176 } | |
177 | |
178 /** | |
179 * Add an explicit [:return null:] for functions that don't have a return | |
180 * statement on each branch. This includes functions with an empty body, | |
181 * such as [:foo(){ }:]. | |
182 */ | |
183 void ensureReturn(FunctionExpression node) { | |
184 if (builder.returnOnAllBranches) return; | |
185 IrConstant nullValue = | |
186 builder.addConstant(constantSystem.createNull(), node); | |
187 builder.addStatement(new IrReturn(nodePosition(node), nullValue)); | |
188 } | |
189 | |
190 IrNode visitBlock(Block node) { | |
191 for (Node n in node.statements.nodes) { | |
192 n.accept(this); | |
193 } | |
194 } | |
195 | |
196 IrNode visitReturn(Return node) { | |
197 IrNode value; | |
198 if (node.expression == null) { | |
199 if (node.beginToken.value == 'native') return giveup(); | |
200 value = builder.addConstant(constantSystem.createNull(), node); | |
201 } else { | |
202 value = node.expression.accept(this); | |
203 } | |
204 builder.branchReturns(); | |
205 return builder.addStatement(new IrReturn(nodePosition(node), value)); | |
206 } | |
207 | |
208 IrConstant visitLiteralBool(LiteralBool node) => | |
209 builder.addConstant(constantSystem.createBool(node.value), node); | |
210 | |
211 IrConstant visitLiteralDouble(LiteralDouble node) => | |
212 builder.addConstant(constantSystem.createDouble(node.value), node); | |
213 | |
214 IrConstant visitLiteralInt(LiteralInt node) => | |
215 builder.addConstant(constantSystem.createInt(node.value), node); | |
216 | |
217 IrConstant visitLiteralString(LiteralString node) => | |
218 builder.addConstant( | |
219 constantSystem.createString(node.dartString, node), node); | |
220 | |
221 IrConstant visitLiteralNull(LiteralNull node) => | |
222 builder.addConstant(constantSystem.createNull(), node); | |
223 | |
224 // TODO(lry): other literals. | |
225 // IrNode visitLiteralList(LiteralList node) => visitExpression(node); | |
226 // IrNode visitLiteralMap(LiteralMap node) => visitExpression(node); | |
227 // IrNode visitLiteralMapEntry(LiteralMapEntry node) => visitNode(node); | |
228 // IrNode visitLiteralSymbol(LiteralSymbol node) => visitExpression(node); | |
229 | |
230 IrNode visitAssert(Send node) { | |
231 return giveup(); | |
232 } | |
233 | |
234 IrNode visitClosureSend(Send node) { | |
235 return giveup(); | |
236 } | |
237 | |
238 IrNode visitDynamicSend(Send node) { | |
239 return giveup(); | |
240 } | |
241 | |
242 IrNode visitGetterSend(Send node) { | |
243 return giveup(); | |
244 } | |
245 | |
246 IrNode visitOperatorSend(Send node) { | |
247 return giveup(); | |
248 } | |
249 | |
250 IrNode visitStaticSend(Send node) { | |
251 return giveup(); | |
252 } | |
253 | |
254 IrNode visitSuperSend(Send node) { | |
255 return giveup(); | |
256 } | |
257 | |
258 IrNode visitTypeReferenceSend(Send node) { | |
259 return giveup(); | |
260 } | |
261 | |
262 static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; | |
263 | |
264 IrNode giveup() => throw ABORT_IRNODE_BUILDER; | |
265 | |
266 IrNode nullIfGiveup(IrNode action()) { | |
267 try { | |
268 return action(); | |
269 } catch(e) { | |
270 if (e == ABORT_IRNODE_BUILDER) return null; | |
271 rethrow; | |
272 } | |
273 } | |
274 | |
275 void internalError(String reason, {Node node}) { | |
276 giveup(); | |
277 } | |
278 } | |
279 | |
280 class IrBuilder { | |
281 final IrNodeBuilderVisitor visitor; | |
282 IrBuilder(this.visitor); | |
283 | |
284 // Statements lists for nested blocks. | |
285 List<List<IrNode>> statementsList = <List<IrNode>>[]; | |
286 List<IrNode> get statements => statementsList.last; | |
287 | |
288 // TODO(lry): Need to fix this once we actually have branching. Probably | |
289 // this will be handled by the LocalsHandler, not here. | |
290 List<bool> returnOnAllBranchesList = <bool>[]; | |
291 bool get returnOnAllBranches => returnOnAllBranchesList.last; | |
292 void branchReturns() { | |
293 returnOnAllBranchesList[returnOnAllBranchesList.length - 1] = true; | |
294 } | |
295 | |
296 Map<Constant, IrConstant> constants = <Constant, IrConstant>{}; | |
297 | |
298 IrConstant addConstant(Constant value, Node node) { | |
299 return constants.putIfAbsent( | |
300 value, () => new IrConstant(visitor.nodePosition(node), value)); | |
301 } | |
302 | |
303 IrNode addStatement(IrNode statement) { | |
304 statements.add(statement); | |
305 return statement; | |
306 } | |
307 | |
308 void enterBlock() { | |
309 statementsList.add(<IrNode>[]); | |
310 returnOnAllBranchesList.add(false); | |
311 } | |
312 | |
313 void exitBlock() { | |
314 statementsList.removeLast(); | |
315 returnOnAllBranchesList.removeLast(); | |
316 } | |
317 } | |
OLD | NEW |