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 }