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