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; 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 Set<LocalFunctionDeclarationInstruction> localDeclarations = null; 082 //todo getters 083 private final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>(); 084 private final Map<JetExpression, LoopInfo> loopInfo = Maps.newHashMap(); 085 086 private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>(); 087 088 private final JetElement correspondingElement; 089 private SubroutineExitInstruction exitInstruction; 090 private SubroutineSinkInstruction sinkInstruction; 091 private SubroutineExitInstruction errorInstruction; 092 private boolean postPrecessed = false; 093 094 public PseudocodeImpl(JetElement correspondingElement) { 095 this.correspondingElement = correspondingElement; 096 } 097 098 @NotNull 099 @Override 100 public JetElement getCorrespondingElement() { 101 return correspondingElement; 102 } 103 104 @NotNull 105 @Override 106 public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() { 107 if (localDeclarations == null) { 108 localDeclarations = getLocalDeclarations(this); 109 } 110 return localDeclarations; 111 } 112 113 @NotNull 114 private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations(@NotNull Pseudocode pseudocode) { 115 Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet(); 116 for (Instruction instruction : pseudocode.getInstructions()) { 117 if (instruction instanceof LocalFunctionDeclarationInstruction) { 118 localDeclarations.add((LocalFunctionDeclarationInstruction) instruction); 119 localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody())); 120 } 121 } 122 return localDeclarations; 123 } 124 125 /*package*/ PseudocodeLabel createLabel(String name) { 126 PseudocodeLabel label = new PseudocodeLabel(name); 127 labels.add(label); 128 return label; 129 } 130 131 @Override 132 @NotNull 133 public List<Instruction> getInstructions() { 134 return instructions; 135 } 136 137 @NotNull 138 @Override 139 public List<Instruction> getReversedInstructions() { 140 LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet(); 141 PseudocodeTraverser.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null); 142 if (traversedInstructions.size() < instructions.size()) { 143 List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions); 144 Collections.reverse(simplyReversedInstructions); 145 for (Instruction instruction : simplyReversedInstructions) { 146 if (!traversedInstructions.contains(instruction)) { 147 PseudocodeTraverser.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null); 148 } 149 } 150 } 151 return Lists.newArrayList(traversedInstructions); 152 } 153 154 //for tests only 155 @NotNull 156 public List<Instruction> getAllInstructions() { 157 return mutableInstructionList; 158 } 159 160 @Override 161 @NotNull 162 public List<Instruction> getDeadInstructions() { 163 List<Instruction> deadInstructions = Lists.newArrayList(); 164 for (Instruction instruction : mutableInstructionList) { 165 if (isDead(instruction)) { 166 deadInstructions.add(instruction); 167 } 168 } 169 return deadInstructions; 170 } 171 172 private static boolean isDead(@NotNull Instruction instruction) { 173 if (!((InstructionImpl)instruction).isDead()) return false; 174 for (Instruction copy : instruction.getCopies()) { 175 if (!((InstructionImpl)copy).isDead()) return false; 176 } 177 return true; 178 } 179 180 //for tests only 181 @NotNull 182 public List<PseudocodeLabel> getLabels() { 183 return labels; 184 } 185 186 /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) { 187 addInstruction(exitInstruction); 188 assert this.exitInstruction == null; 189 this.exitInstruction = exitInstruction; 190 } 191 192 /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) { 193 addInstruction(sinkInstruction); 194 assert this.sinkInstruction == null; 195 this.sinkInstruction = sinkInstruction; 196 } 197 198 /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) { 199 addInstruction(errorInstruction); 200 assert this.errorInstruction == null; 201 this.errorInstruction = errorInstruction; 202 } 203 204 /*package*/ void addInstruction(Instruction instruction) { 205 mutableInstructionList.add(instruction); 206 instruction.setOwner(this); 207 208 if (instruction instanceof JetElementInstruction) { 209 JetElementInstruction elementInstruction = (JetElementInstruction) instruction; 210 representativeInstructions.put(elementInstruction.getElement(), instruction); 211 } 212 } 213 214 /*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) { 215 loopInfo.put(expression, blockInfo); 216 } 217 218 @Override 219 @NotNull 220 public SubroutineExitInstruction getExitInstruction() { 221 return exitInstruction; 222 } 223 224 @Override 225 @NotNull 226 public SubroutineSinkInstruction getSinkInstruction() { 227 return sinkInstruction; 228 } 229 230 @Override 231 @NotNull 232 public SubroutineEnterInstruction getEnterInstruction() { 233 return (SubroutineEnterInstruction) mutableInstructionList.get(0); 234 } 235 236 /*package*/ void bindLabel(Label label) { 237 ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size()); 238 } 239 240 public void postProcess() { 241 if (postPrecessed) return; 242 postPrecessed = true; 243 errorInstruction.setSink(getSinkInstruction()); 244 exitInstruction.setSink(getSinkInstruction()); 245 for (int i = 0, instructionsSize = mutableInstructionList.size(); i < instructionsSize; i++) { 246 processInstruction(mutableInstructionList.get(i), i); 247 } 248 Set<Instruction> reachableInstructions = collectReachableInstructions(); 249 for (Instruction instruction : mutableInstructionList) { 250 if (reachableInstructions.contains(instruction)) { 251 instructions.add(instruction); 252 } 253 } 254 markDeadInstructions(); 255 } 256 257 private void processInstruction(Instruction instruction, final int currentPosition) { 258 instruction.accept(new InstructionVisitor() { 259 @Override 260 public void visitInstructionWithNext(InstructionWithNext instruction) { 261 instruction.setNext(getNextPosition(currentPosition)); 262 } 263 264 @Override 265 public void visitJump(AbstractJumpInstruction instruction) { 266 instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel())); 267 } 268 269 @Override 270 public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) { 271 instruction.setNext(getNextPosition(currentPosition)); 272 List<Label> targetLabels = instruction.getTargetLabels(); 273 for (Label targetLabel : targetLabels) { 274 instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel)); 275 } 276 } 277 278 @Override 279 public void visitConditionalJump(ConditionalJumpInstruction instruction) { 280 Instruction nextInstruction = getNextPosition(currentPosition); 281 Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel()); 282 if (instruction.onTrue()) { 283 instruction.setNextOnFalse(nextInstruction); 284 instruction.setNextOnTrue(jumpTarget); 285 } 286 else { 287 instruction.setNextOnFalse(jumpTarget); 288 instruction.setNextOnTrue(nextInstruction); 289 } 290 visitJump(instruction); 291 } 292 293 @Override 294 public void visitLocalFunctionDeclarationInstruction(LocalFunctionDeclarationInstruction instruction) { 295 ((PseudocodeImpl)instruction.getBody()).postProcess(); 296 instruction.setNext(getSinkInstruction()); 297 } 298 299 @Override 300 public void visitSubroutineExit(SubroutineExitInstruction instruction) { 301 // Nothing 302 } 303 304 @Override 305 public void visitSubroutineSink(SubroutineSinkInstruction instruction) { 306 // Nothing 307 } 308 309 @Override 310 public void visitInstruction(Instruction instruction) { 311 throw new UnsupportedOperationException(instruction.toString()); 312 } 313 }); 314 } 315 316 private Set<Instruction> collectReachableInstructions() { 317 Set<Instruction> visited = Sets.newHashSet(); 318 PseudocodeTraverser.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, null); 319 if (!visited.contains(getExitInstruction())) { 320 visited.add(getExitInstruction()); 321 } 322 if (!visited.contains(errorInstruction)) { 323 visited.add(errorInstruction); 324 } 325 if (!visited.contains(getSinkInstruction())) { 326 visited.add(getSinkInstruction()); 327 } 328 return visited; 329 } 330 331 private void markDeadInstructions() { 332 Set<Instruction> instructionSet = Sets.newHashSet(instructions); 333 for (Instruction instruction : mutableInstructionList) { 334 if (!instructionSet.contains(instruction)) { 335 ((InstructionImpl)instruction).die(); 336 for (Instruction nextInstruction : instruction.getNextInstructions()) { 337 nextInstruction.getPreviousInstructions().remove(instruction); 338 } 339 } 340 } 341 } 342 343 @NotNull 344 private Instruction getJumpTarget(@NotNull Label targetLabel) { 345 return ((PseudocodeLabel)targetLabel).resolveToInstruction(); 346 } 347 348 @NotNull 349 private Instruction getNextPosition(int currentPosition) { 350 int targetPosition = currentPosition + 1; 351 assert targetPosition < mutableInstructionList.size() : currentPosition; 352 return mutableInstructionList.get(targetPosition); 353 } 354 355 public void repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel) { 356 Integer startIndex = ((PseudocodeLabel) startLabel).getTargetInstructionIndex(); 357 assert startIndex != null; 358 Integer finishIndex = ((PseudocodeLabel) finishLabel).getTargetInstructionIndex(); 359 assert finishIndex != null; 360 361 Map<Label, Label> originalToCopy = Maps.newHashMap(); 362 Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create(); 363 for (PseudocodeLabel label : labels) { 364 Integer index = label.getTargetInstructionIndex(); 365 if (index == null) continue; //label is not bounded yet 366 if (label == startLabel || label == finishLabel) continue; 367 368 if (startIndex <= index && index <= finishIndex) { 369 originalToCopy.put(label, label.copy()); 370 originalLabelsForInstruction.put(getJumpTarget(label), label); 371 } 372 } 373 for (int index = startIndex; index < finishIndex; index++) { 374 Instruction originalInstruction = mutableInstructionList.get(index); 375 repeatLabelsBindingForInstruction(originalInstruction, originalToCopy, originalLabelsForInstruction); 376 addInstruction(copyInstruction(originalInstruction, originalToCopy)); 377 } 378 repeatLabelsBindingForInstruction(mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction); 379 } 380 381 private void repeatLabelsBindingForInstruction( 382 @NotNull Instruction originalInstruction, 383 @NotNull Map<Label, Label> originalToCopy, 384 @NotNull Multimap<Instruction, Label> originalLabelsForInstruction 385 ) { 386 for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) { 387 bindLabel(originalToCopy.get(originalLabel)); 388 } 389 } 390 391 private Instruction copyInstruction(@NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) { 392 if (instruction instanceof AbstractJumpInstruction) { 393 Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel(); 394 if (originalToCopy.containsKey(originalTarget)) { 395 return ((AbstractJumpInstruction)instruction).copy(originalToCopy.get(originalTarget)); 396 } 397 } 398 if (instruction instanceof NondeterministicJumpInstruction) { 399 List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels(); 400 List<Label> copyTargets = copyLabels(originalTargets, originalToCopy); 401 return ((NondeterministicJumpInstruction) instruction).copy(copyTargets); 402 } 403 return ((InstructionImpl)instruction).copy(); 404 } 405 406 @NotNull 407 private List<Label> copyLabels(Collection<Label> labels, Map<Label, Label> originalToCopy) { 408 List<Label> newLabels = Lists.newArrayList(); 409 for (Label label : labels) { 410 Label newLabel = originalToCopy.get(label); 411 newLabels.add(newLabel != null ? newLabel : label); 412 } 413 return newLabels; 414 } 415 }