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 }