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

Side by Side Diff: lib/compiler/implementation/ssa/codegen_helpers.dart

Issue 10495013: Recognize logical operations before code generation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Instead of emitting each SSA instruction with a temporary variable 6 * Instead of emitting each SSA instruction with a temporary variable
7 * mark instructions that can be emitted at their use-site. 7 * mark instructions that can be emitted at their use-site.
8 * For example, in: 8 * For example, in:
9 * t0 = 4; 9 * t0 = 4;
10 * t1 = 3; 10 * t1 = 3;
(...skipping 11 matching lines...) Expand all
22 visitDominatorTree(graph); 22 visitDominatorTree(graph);
23 } 23 }
24 24
25 void visitInstruction(HInstruction instruction) { 25 void visitInstruction(HInstruction instruction) {
26 // A code motion invariant instruction is dealt before visiting it. 26 // A code motion invariant instruction is dealt before visiting it.
27 assert(!instruction.isCodeMotionInvariant()); 27 assert(!instruction.isCodeMotionInvariant());
28 for (HInstruction input in instruction.inputs) { 28 for (HInstruction input in instruction.inputs) {
29 if (!generateAtUseSite.contains(input) 29 if (!generateAtUseSite.contains(input)
30 && !input.isCodeMotionInvariant() 30 && !input.isCodeMotionInvariant()
31 && input.usedBy.length == 1 31 && input.usedBy.length == 1
32 && input is! HPhi) { 32 && input is !HPhi) {
33 expectedInputs.add(input); 33 expectedInputs.add(input);
34 } 34 }
35 } 35 }
36 } 36 }
37 37
38 // The codegen might use the input multiple times, so it must not be 38 // The codegen might use the input multiple times, so it must not be
39 // set generate at use site. 39 // set generate at use site.
40 void visitIs(HIs instruction) {} 40 void visitIs(HIs instruction) {}
41 41
42 // A check method must not have its input generate at use site, 42 // A check method must not have its input generate at use site,
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 } 126 }
127 } 127 }
128 128
129 /** 129 /**
130 * Detect control flow arising from short-circuit logical operators, and 130 * Detect control flow arising from short-circuit logical operators, and
131 * prepare the program to be generated using these operators instead of 131 * prepare the program to be generated using these operators instead of
132 * nested ifs and boolean variables. 132 * nested ifs and boolean variables.
133 */ 133 */
134 class SsaConditionMerger extends HGraphVisitor { 134 class SsaConditionMerger extends HGraphVisitor {
135 Set<HInstruction> generateAtUseSite; 135 Set<HInstruction> generateAtUseSite;
136 Map<HPhi, String> logicalOperations; 136 Set<HInstruction> logicalOperations;
137 137
138 SsaConditionMerger(this.generateAtUseSite, this.logicalOperations); 138 SsaConditionMerger(this.generateAtUseSite, this.logicalOperations);
139 139
140 void visitGraph(HGraph graph) { 140 void visitGraph(HGraph graph) {
141 visitDominatorTree(graph); 141 visitPostDominatorTree(graph);
142 } 142 }
143 143
144 /** 144 /**
145 * Returns true if the given instruction is an expression that uses up all 145 * Check if a block has at least one statement other than
146 * instructions up to the given [limit]. 146 * [instruction].
147 *
148 * That is, all instructions starting after the [limit] block (at the branch
149 * leading to the [instruction]) down to the given [instruction] can be
150 * generated at use-site.
151 */ 147 */
152 bool isExpression(HInstruction instruction, HBasicBlock limit) { 148 bool hasStatement(HBasicBlock block, HInstruction instruction) {
153 HBasicBlock block = instruction.block; 149 // If [instruction] is not in [block], then if the block is not
154 if (instruction is HPhi) { 150 // empty, we know there will be a statement to emit.
155 if (!logicalOperations.containsKey(instruction)) { 151 if (instruction.block !== block) return block.last !== block.first;
156 return false; 152
157 } 153 // If [instruction] is not the last instruction of the block
158 } else { 154 // before the control flow instruction, then we will have to emit
159 while (instruction.previous != null) { 155 // a statement for that last instruction.
160 instruction = instruction.previous; 156 if (instruction !== block.last.previous) return true;
161 if (!generateAtUseSite.contains(instruction)) { 157
162 return false; 158 // If one of the instructions in the block until [instruction] is
163 } 159 // not generated at use site, then we will have to emit a
164 } 160 // statement for it.
165 // Now [instruction] is the first instruction of the block 161 // TODO(ngeoffray): we could generate a comma separated
166 // (aka [block.first]). If there are also a phi, check the current 162 // list of expressions.
167 // [instruction] normally and make [instruction] be the phi. 163 for (HInstruction temp = block.first;
168 if (!block.phis.isEmpty()) { 164 temp !== instruction;
169 if (!generateAtUseSite.contains(instruction)) { 165 temp = temp.next) {
170 return false; 166 if (!generateAtUseSite.contains(temp)) return true;
171 }
172 instruction = block.phis.last;
173 if (block.phis.first !== instruction) {
174 // If there is more than one phi, don't try to undestand it.
175 return false;
176 }
177 if (!logicalOperations.containsKey(instruction)) {
178 return false;
179 }
180 }
181 } 167 }
182 if (instruction is HPhi) {
183 assert(logicalOperations.containsKey(instruction));
184 return isExpression(instruction.inputs[0], limit);
185 }
186 if (block.predecessors.length !== 1) {
187 return false;
188 }
189 HBasicBlock previousBlock = block.predecessors[0];
190 if (previousBlock === limit) return true;
191 if (previousBlock.successors.length !== 1 ||
192 previousBlock.last is! HGoto) {
193 return false;
194 }
195 return isExpression(previousBlock.last, limit);
196 }
197 168
198 void replaceWithLogicalOperator(HPhi phi, String type) { 169 return false;
199 if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi);
200 logicalOperations[phi] = type;
201 // If the phi corresponds to logical control flow, mark the
202 // control-flow instructions as generate-at-use-site.
203 generateAtUseSite.add(phi.block.predecessors[0].last);
204 generateAtUseSite.add(phi.block.predecessors[1].last);
205 // If the first input is only used as branch condition and result, it too
206 // can be generate-at-use-site.
207 if (phi.inputs[0].usedBy.length == 2) {
208 generateAtUseSite.add(phi.inputs[0]);
209 }
210 if (phi.inputs[1].usedBy.length == 1) {
211 generateAtUseSite.add(phi.inputs[1]);
212 }
213 }
214
215 bool canGenerateAtUseSite(HPhi phi) {
216 if (phi.usedBy.length != 1) {
217 return false;
218 }
219 assert(phi.next == null);
220 HInstruction use = phi.usedBy[0];
221
222 HInstruction current = phi.block.first;
223 while (current != use) {
224 // Check that every instruction between the start of the block and the
225 // use of the phi (i.e., every instruction between the phi and the use)
226 // is itself generated at use site. That means that the phi can be
227 // moved to its use site without crossing any other code, because those
228 // instructions (if any) are moved too.
229 if (current is! HControlFlow && !generateAtUseSite.contains(current)) {
230 return false;
231 }
232 if (current.next != null) {
233 current = current.next;
234 } else if (current is HPhi) {
235 current = current.block.first;
236 } else {
237 assert(current is HControlFlow);
238 if (current is !HGoto) {
239 return false;
240 }
241 HBasicBlock nextBlock = current.block.successors[0];
242 if (!nextBlock.phis.isEmpty()) {
243 current = nextBlock.phis.first;
244 } else {
245 current = nextBlock.first;
246 }
247 }
248 }
249 return true;
250 }
251
252 HInstruction previousInstruction(HInstruction instruction) {
253 if (instruction.previous != null) return instruction.previous;
254 HBasicBlock block = instruction.block;
255 if (instruction is! HPhi) {
256 if (block.phis.last != null) return block.phis.last;
257 }
258 if (block.predecessors.length == 1) {
259 HBasicBlock previousBlock = block.predecessors[0];
260 if (previousBlock.last is HGoto) {
261 assert(previousBlock.successors.length == 1);
262 assert(previousBlock.successors[0] === block);
263 return previousInstruction(previousBlock.last);
264 }
265 }
266 return null;
267 }
268
269 void detectLogicControlFlow(HPhi phi) {
270 // Check for the most common pattern for a short-circuit logic operation:
271 // B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1)
272 // |\
273 // | B1 b1 = ...; goto B2
274 // |/
275 // B2 b2 = phi(b0,b1); if(b2) ...
276 // TODO(lrn): Also recognize ?:-flow?
277 if (phi.inputs.length != 2) return;
278 HInstruction first = phi.inputs[0];
279 HBasicBlock firstBlock = phi.block.predecessors[0];
280 HInstruction second = phi.inputs[1];
281 HBasicBlock secondBlock = phi.block.predecessors[1];
282 // Check second input of phi being an expression followed by a goto.
283 if (second.usedBy.length != 1) return;
284 HInstruction secondNext =
285 (second is HPhi) ? secondBlock.first : second.next;
286 if (secondNext != secondBlock.last) return;
287 if (secondBlock.last is !HGoto) return;
288 if (secondBlock.successors[0] != phi.block) return;
289 if (!isExpression(second, firstBlock)) return;
290 // Check first input of phi being followed by a (possibly negated)
291 // conditional branch based on the same value.
292 if (firstBlock != phi.block.dominator) return;
293 if (firstBlock.last is! HIf) return;
294 if (firstBlock.successors[1] != phi.block) return;
295 HIf firstBranch = firstBlock.last;
296 HInstruction condition = firstBranch.inputs[0];
297 if (condition === first) {
298 replaceWithLogicalOperator(phi, "&&");
299 } else if (condition is HNot &&
300 condition.inputs[0] == first) {
301 replaceWithLogicalOperator(phi, "||");
302 // If the negation is only used by this logical operation, or only by
303 // logical operators in general, it won't need to be generated.
304 if (!generateAtUseSite.contains(condition)) {
305 for (HInstruction user in condition.usedBy) {
306 if (user is! HIf || !generateAtUseSite.contains(user)) {
307 return;
308 }
309 }
310 generateAtUseSite.add(condition);
311 }
312 }
313 return;
314 } 170 }
315 171
316 void visitBasicBlock(HBasicBlock block) { 172 void visitBasicBlock(HBasicBlock block) {
317 if (!block.phis.isEmpty() && 173 if (block.last is !HIf) return;
318 block.phis.first === block.phis.last) { 174 HIf startIf = block.last;
319 detectLogicControlFlow(block.phis.first); 175 HBasicBlock end = startIf.joinBlock;
176
177 // We check that the structure is the following:
178 // If
179 // / \
180 // / \
181 // 1 expr goto
182 // goto /
183 // \ /
184 // \ /
185 // phi(expr, true|false)
186 if (end == null) return;
187 if (end.phis.isEmpty()) return;
188 if (end.phis.first !== end.phis.last) return;
189 HBasicBlock thenBlock = startIf.thenBlock;
190 HBasicBlock elseBlock = startIf.elseBlock;
191 if (end.predecessors[0] !== thenBlock) return;
192 if (end.predecessors[1] !== elseBlock) return;
193 HPhi phi = end.phis.first;
194 if (!phi.inputs[1].isConstantBoolean()) return;
195
196 HInstruction thenInput = phi.inputs[0];
197 if (hasStatement(thenBlock, thenInput)) return;
198 if (elseBlock.first !== elseBlock.last) return;
199
200 // From now on, we have recognized a logical operation built from
201 // the builder. We don't expect the builder and the optimizers to
202 // generate the then and else branches with multiple successors,
203 // and the join branch to have more than two predecessors.
204
205 // Mark the if instruction as a logical operation.
206 logicalOperations.add(startIf);
207
208 // If the logical operation is only used by the first instruction
209 // of its block, it can be generated at use site.
210 if (phi.usedBy.length == 1 && phi.usedBy[0] === phi.block.first) {
211 generateAtUseSite.add(phi);
212 }
213
214 // If [thenInput] is defined in [thenBlock], then it is only used
215 // by [phi] and can be generated at use site.
216 if (thenInput.block === thenBlock) {
217 assert(thenInput.usedBy.length == 1);
218 generateAtUseSite.add(thenInput);
320 } 219 }
321 } 220 }
322 } 221 }
323 222
324 // Precedence information for JavaScript operators. 223 // Precedence information for JavaScript operators.
325 class JSPrecedence { 224 class JSPrecedence {
326 // Used as precedence for something that's not even an expression. 225 // Used as precedence for something that's not even an expression.
327 static final int STATEMENT_PRECEDENCE = 0; 226 static final int STATEMENT_PRECEDENCE = 0;
328 // Precedences of JS operators. 227 // Precedences of JS operators.
329 static final int EXPRESSION_PRECEDENCE = 1; 228 static final int EXPRESSION_PRECEDENCE = 1;
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 }; 293 };
395 } 294 }
396 295
397 class JSBinaryOperatorPrecedence { 296 class JSBinaryOperatorPrecedence {
398 final int left; 297 final int left;
399 final int right; 298 final int right;
400 const JSBinaryOperatorPrecedence(this.left, this.right); 299 const JSBinaryOperatorPrecedence(this.left, this.right);
401 // All binary operators (excluding assignment) are left associative. 300 // All binary operators (excluding assignment) are left associative.
402 int get precedence() => left; 301 int get precedence() => left;
403 } 302 }
OLDNEW
« no previous file with comments | « lib/compiler/implementation/ssa/codegen.dart ('k') | lib/compiler/implementation/ssa/nodes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698