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