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