001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.cfg.pseudocode;
018
019 import com.google.common.collect.*;
020 import com.intellij.util.containers.BidirectionalMap;
021 import kotlin.Function0;
022 import kotlin.KotlinPackage;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.jet.lang.cfg.Label;
026 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*;
027 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MergeInstruction;
028 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction;
029 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction;
030 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.NondeterministicJumpInstruction;
031 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
032 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineEnterInstruction;
033 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
034 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineSinkInstruction;
035 import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.PseudocodeTraverserPackage;
036 import org.jetbrains.jet.lang.psi.JetElement;
037
038 import java.util.*;
039
040 import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD;
041 import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;
042
043 public class PseudocodeImpl implements Pseudocode {
044
045 public class PseudocodeLabel implements Label {
046 private final String name;
047 private final String comment;
048 private Integer targetInstructionIndex;
049
050
051 private PseudocodeLabel(@NotNull String name, @Nullable String comment) {
052 this.name = name;
053 this.comment = comment;
054 }
055
056 @NotNull
057 @Override
058 public String getName() {
059 return name;
060 }
061
062 @Override
063 public String toString() {
064 return comment == null ? name : (name + " [" + comment + "]");
065 }
066
067 public Integer getTargetInstructionIndex() {
068 return targetInstructionIndex;
069 }
070
071 public void setTargetInstructionIndex(int targetInstructionIndex) {
072 this.targetInstructionIndex = targetInstructionIndex;
073 }
074
075 @Nullable
076 private List<Instruction> resolve() {
077 assert targetInstructionIndex != null;
078 return mutableInstructionList.subList(getTargetInstructionIndex(), mutableInstructionList.size());
079 }
080
081 public Instruction resolveToInstruction() {
082 assert targetInstructionIndex != null;
083 return mutableInstructionList.get(targetInstructionIndex);
084 }
085
086 public PseudocodeLabel copy(int newLabelIndex) {
087 return new PseudocodeLabel("L" + newLabelIndex, "copy of " + name + ", " + comment);
088 }
089 }
090
091 private final List<Instruction> mutableInstructionList = new ArrayList<Instruction>();
092 private final List<Instruction> instructions = new ArrayList<Instruction>();
093
094 private final BidirectionalMap<JetElement, PseudoValue> elementsToValues = new BidirectionalMap<JetElement, PseudoValue>();
095
096 private final Map<PseudoValue, List<Instruction>> valueUsages = Maps.newHashMap();
097 private final Map<PseudoValue, Set<PseudoValue>> mergedValues = Maps.newHashMap();
098 private final Set<Instruction> sideEffectFree = Sets.newHashSet();
099
100 private Pseudocode parent = null;
101 private Set<LocalFunctionDeclarationInstruction> localDeclarations = null;
102 //todo getters
103 private final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
104
105 private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>();
106
107 private final JetElement correspondingElement;
108 private SubroutineExitInstruction exitInstruction;
109 private SubroutineSinkInstruction sinkInstruction;
110 private SubroutineExitInstruction errorInstruction;
111 private boolean postPrecessed = false;
112
113 public PseudocodeImpl(JetElement correspondingElement) {
114 this.correspondingElement = correspondingElement;
115 }
116
117 @NotNull
118 @Override
119 public JetElement getCorrespondingElement() {
120 return correspondingElement;
121 }
122
123 @NotNull
124 @Override
125 public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() {
126 if (localDeclarations == null) {
127 localDeclarations = getLocalDeclarations(this);
128 }
129 return localDeclarations;
130 }
131
132 @NotNull
133 private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations(@NotNull Pseudocode pseudocode) {
134 Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet();
135 for (Instruction instruction : ((PseudocodeImpl)pseudocode).mutableInstructionList) {
136 if (instruction instanceof LocalFunctionDeclarationInstruction) {
137 localDeclarations.add((LocalFunctionDeclarationInstruction) instruction);
138 localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody()));
139 }
140 }
141 return localDeclarations;
142 }
143
144 @Override
145 @Nullable
146 public Pseudocode getParent() {
147 return parent;
148 }
149
150 private void setParent(Pseudocode parent) {
151 this.parent = parent;
152 }
153
154 @NotNull
155 public Pseudocode getRootPseudocode() {
156 Pseudocode parent = getParent();
157 while (parent != null) {
158 if (parent.getParent() == null) return parent;
159 parent = parent.getParent();
160 }
161 return this;
162 }
163
164 /*package*/ PseudocodeLabel createLabel(@NotNull String name, @Nullable String comment) {
165 PseudocodeLabel label = new PseudocodeLabel(name, comment);
166 labels.add(label);
167 return label;
168 }
169
170 @Override
171 @NotNull
172 public List<Instruction> getInstructions() {
173 return instructions;
174 }
175
176 @NotNull
177 @Override
178 public List<Instruction> getReversedInstructions() {
179 LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet();
180 PseudocodeTraverserPackage.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null);
181 if (traversedInstructions.size() < instructions.size()) {
182 List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions);
183 Collections.reverse(simplyReversedInstructions);
184 for (Instruction instruction : simplyReversedInstructions) {
185 if (!traversedInstructions.contains(instruction)) {
186 PseudocodeTraverserPackage.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null);
187 }
188 }
189 }
190 return Lists.newArrayList(traversedInstructions);
191 }
192
193 @Override
194 @NotNull
195 public List<Instruction> getInstructionsIncludingDeadCode() {
196 return mutableInstructionList;
197 }
198
199 //for tests only
200 @NotNull
201 public List<PseudocodeLabel> getLabels() {
202 return labels;
203 }
204
205 /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) {
206 addInstruction(exitInstruction);
207 assert this.exitInstruction == null;
208 this.exitInstruction = exitInstruction;
209 }
210
211 /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) {
212 addInstruction(sinkInstruction);
213 assert this.sinkInstruction == null;
214 this.sinkInstruction = sinkInstruction;
215 }
216
217 /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) {
218 addInstruction(errorInstruction);
219 assert this.errorInstruction == null;
220 this.errorInstruction = errorInstruction;
221 }
222
223 /*package*/ void addInstruction(Instruction instruction) {
224 mutableInstructionList.add(instruction);
225 instruction.setOwner(this);
226
227 if (instruction instanceof JetElementInstruction) {
228 JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
229 representativeInstructions.put(elementInstruction.getElement(), instruction);
230 }
231
232 if (instruction instanceof MergeInstruction) {
233 addMergedValues((MergeInstruction) instruction);
234 }
235
236 for (PseudoValue inputValue : instruction.getInputValues()) {
237 addValueUsage(inputValue, instruction);
238 for (PseudoValue mergedValue : getMergedValues(inputValue)) {
239 addValueUsage(mergedValue, instruction);
240 }
241 }
242 if (PseudocodePackage.calcSideEffectFree(instruction)) {
243 sideEffectFree.add(instruction);
244 }
245 }
246
247 @Override
248 @NotNull
249 public SubroutineExitInstruction getExitInstruction() {
250 return exitInstruction;
251 }
252
253 @Override
254 @NotNull
255 public SubroutineSinkInstruction getSinkInstruction() {
256 return sinkInstruction;
257 }
258
259 @Override
260 @NotNull
261 public SubroutineEnterInstruction getEnterInstruction() {
262 return (SubroutineEnterInstruction) mutableInstructionList.get(0);
263 }
264
265 @Nullable
266 @Override
267 public PseudoValue getElementValue(@Nullable JetElement element) {
268 return elementsToValues.get(element);
269 }
270
271 @NotNull
272 @Override
273 public List<? extends JetElement> getValueElements(@Nullable PseudoValue value) {
274 List<? extends JetElement> result = elementsToValues.getKeysByValue(value);
275 return result != null ? result : Collections.<JetElement>emptyList();
276 }
277
278 @NotNull
279 @Override
280 public List<? extends Instruction> getUsages(@Nullable PseudoValue value) {
281 List<? extends Instruction> result = valueUsages.get(value);
282 return result != null ? result : Collections.<Instruction>emptyList();
283 }
284
285 @Override
286 public boolean isSideEffectFree(@NotNull Instruction instruction) {
287 return sideEffectFree.contains(instruction);
288 }
289
290 /*package*/ void bindElementToValue(@NotNull JetElement element, @NotNull PseudoValue value) {
291 elementsToValues.put(element, value);
292 }
293
294 /*package*/ void bindLabel(Label label) {
295 ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size());
296 }
297
298 private Set<PseudoValue> getMergedValues(@NotNull PseudoValue value) {
299 Set<PseudoValue> result = mergedValues.get(value);
300 return result != null ? result : Collections.<PseudoValue>emptySet();
301 }
302
303 private void addMergedValues(@NotNull MergeInstruction instruction) {
304 Set<PseudoValue> result = new LinkedHashSet<PseudoValue>();
305 for (PseudoValue value : instruction.getInputValues()) {
306 result.addAll(getMergedValues(value));
307 result.add(value);
308 }
309 mergedValues.put(instruction.getOutputValue(), result);
310 }
311
312 private void addValueUsage(PseudoValue value, Instruction usage) {
313 if (usage instanceof MergeInstruction) return;
314 KotlinPackage.getOrPut(
315 valueUsages,
316 value,
317 new Function0<List<Instruction>>() {
318 @Override
319 public List<Instruction> invoke() {
320 return Lists.newArrayList();
321 }
322 }
323 ).add(usage);
324 }
325
326 public void postProcess() {
327 if (postPrecessed) return;
328 postPrecessed = true;
329 errorInstruction.setSink(getSinkInstruction());
330 exitInstruction.setSink(getSinkInstruction());
331 int index = 0;
332 for (Instruction instruction : mutableInstructionList) {
333 //recursively invokes 'postProcess' for local declarations
334 processInstruction(instruction, index);
335 index++;
336 }
337 if (getParent() != null) return;
338
339 // Collecting reachable instructions should be done after processing all instructions
340 // (including instructions in local declarations) to avoid being in incomplete state.
341 collectAndCacheReachableInstructions();
342 for (LocalFunctionDeclarationInstruction localFunctionDeclarationInstruction : getLocalDeclarations()) {
343 ((PseudocodeImpl) localFunctionDeclarationInstruction.getBody()).collectAndCacheReachableInstructions();
344 }
345 }
346
347 private void collectAndCacheReachableInstructions() {
348 Set<Instruction> reachableInstructions = collectReachableInstructions();
349 for (Instruction instruction : mutableInstructionList) {
350 if (reachableInstructions.contains(instruction)) {
351 instructions.add(instruction);
352 }
353 }
354 markDeadInstructions();
355 }
356
357 private void processInstruction(Instruction instruction, final int currentPosition) {
358 instruction.accept(new InstructionVisitor() {
359 @Override
360 public void visitInstructionWithNext(InstructionWithNext instruction) {
361 instruction.setNext(getNextPosition(currentPosition));
362 }
363
364 @Override
365 public void visitJump(AbstractJumpInstruction instruction) {
366 instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
367 }
368
369 @Override
370 public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
371 instruction.setNext(getNextPosition(currentPosition));
372 List<Label> targetLabels = instruction.getTargetLabels();
373 for (Label targetLabel : targetLabels) {
374 instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel));
375 }
376 }
377
378 @Override
379 public void visitConditionalJump(ConditionalJumpInstruction instruction) {
380 Instruction nextInstruction = getNextPosition(currentPosition);
381 Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
382 if (instruction.getOnTrue()) {
383 instruction.setNextOnFalse(nextInstruction);
384 instruction.setNextOnTrue(jumpTarget);
385 }
386 else {
387 instruction.setNextOnFalse(jumpTarget);
388 instruction.setNextOnTrue(nextInstruction);
389 }
390 visitJump(instruction);
391 }
392
393 @Override
394 public void visitLocalFunctionDeclarationInstruction(LocalFunctionDeclarationInstruction instruction) {
395 PseudocodeImpl body = (PseudocodeImpl) instruction.getBody();
396 body.setParent(PseudocodeImpl.this);
397 body.postProcess();
398 instruction.setNext(getSinkInstruction());
399 }
400
401 @Override
402 public void visitSubroutineExit(SubroutineExitInstruction instruction) {
403 // Nothing
404 }
405
406 @Override
407 public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
408 // Nothing
409 }
410
411 @Override
412 public void visitInstruction(Instruction instruction) {
413 throw new UnsupportedOperationException(instruction.toString());
414 }
415 });
416 }
417
418 private Set<Instruction> collectReachableInstructions() {
419 Set<Instruction> visited = Sets.newHashSet();
420 PseudocodeTraverserPackage.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, null);
421 if (!visited.contains(getExitInstruction())) {
422 visited.add(getExitInstruction());
423 }
424 if (!visited.contains(errorInstruction)) {
425 visited.add(errorInstruction);
426 }
427 if (!visited.contains(getSinkInstruction())) {
428 visited.add(getSinkInstruction());
429 }
430 return visited;
431 }
432
433 private void markDeadInstructions() {
434 Set<Instruction> instructionSet = Sets.newHashSet(instructions);
435 for (Instruction instruction : mutableInstructionList) {
436 if (!instructionSet.contains(instruction)) {
437 ((InstructionImpl)instruction).setMarkedAsDead(true);
438 for (Instruction nextInstruction : instruction.getNextInstructions()) {
439 nextInstruction.getPreviousInstructions().remove(instruction);
440 }
441 }
442 }
443 }
444
445 @NotNull
446 private Instruction getJumpTarget(@NotNull Label targetLabel) {
447 return ((PseudocodeLabel)targetLabel).resolveToInstruction();
448 }
449
450 @NotNull
451 private Instruction getNextPosition(int currentPosition) {
452 int targetPosition = currentPosition + 1;
453 assert targetPosition < mutableInstructionList.size() : currentPosition;
454 return mutableInstructionList.get(targetPosition);
455 }
456
457 public int repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel, int labelCount) {
458 Integer startIndex = ((PseudocodeLabel) startLabel).getTargetInstructionIndex();
459 assert startIndex != null;
460 Integer finishIndex = ((PseudocodeLabel) finishLabel).getTargetInstructionIndex();
461 assert finishIndex != null;
462
463 Map<Label, Label> originalToCopy = Maps.newLinkedHashMap();
464 Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create();
465 for (PseudocodeLabel label : labels) {
466 Integer index = label.getTargetInstructionIndex();
467 if (index == null) continue; //label is not bounded yet
468 if (label == startLabel || label == finishLabel) continue;
469
470 if (startIndex <= index && index <= finishIndex) {
471 originalToCopy.put(label, label.copy(labelCount++));
472 originalLabelsForInstruction.put(getJumpTarget(label), label);
473 }
474 }
475 for (Label label : originalToCopy.values()) {
476 labels.add((PseudocodeLabel) label);
477 }
478 for (int index = startIndex; index < finishIndex; index++) {
479 Instruction originalInstruction = mutableInstructionList.get(index);
480 repeatLabelsBindingForInstruction(originalInstruction, originalToCopy, originalLabelsForInstruction);
481 addInstruction(copyInstruction(originalInstruction, originalToCopy));
482 }
483 repeatLabelsBindingForInstruction(mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction);
484 return labelCount;
485 }
486
487 private void repeatLabelsBindingForInstruction(
488 @NotNull Instruction originalInstruction,
489 @NotNull Map<Label, Label> originalToCopy,
490 @NotNull Multimap<Instruction, Label> originalLabelsForInstruction
491 ) {
492 for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) {
493 bindLabel(originalToCopy.get(originalLabel));
494 }
495 }
496
497 private Instruction copyInstruction(@NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) {
498 if (instruction instanceof AbstractJumpInstruction) {
499 Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel();
500 if (originalToCopy.containsKey(originalTarget)) {
501 return ((AbstractJumpInstruction)instruction).copy(originalToCopy.get(originalTarget));
502 }
503 }
504 if (instruction instanceof NondeterministicJumpInstruction) {
505 List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels();
506 List<Label> copyTargets = copyLabels(originalTargets, originalToCopy);
507 return ((NondeterministicJumpInstruction) instruction).copy(copyTargets);
508 }
509 return ((InstructionImpl)instruction).copy();
510 }
511
512 @NotNull
513 private List<Label> copyLabels(Collection<Label> labels, Map<Label, Label> originalToCopy) {
514 List<Label> newLabels = Lists.newArrayList();
515 for (Label label : labels) {
516 Label newLabel = originalToCopy.get(label);
517 newLabels.add(newLabel != null ? newLabel : label);
518 }
519 return newLabels;
520 }
521 }