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 }