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 com.intellij.util.containers.BidirectionalMap;
021    import jet.runtime.typeinfo.JetValueParameter;
022    import kotlin.Function0;
023    import kotlin.Function1;
024    import kotlin.KotlinPackage;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.jet.lang.cfg.Label;
028    import org.jetbrains.jet.lang.cfg.LoopInfo;
029    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*;
030    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.InstructionWithValue;
031    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MergeInstruction;
032    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction;
033    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction;
034    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.NondeterministicJumpInstruction;
035    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
036    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineEnterInstruction;
037    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
038    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineSinkInstruction;
039    import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.PseudocodeTraverserPackage;
040    import org.jetbrains.jet.lang.psi.JetElement;
041    import org.jetbrains.jet.lang.psi.JetExpression;
042    
043    import java.util.*;
044    
045    import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD;
046    import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;
047    
048    public class PseudocodeImpl implements Pseudocode {
049    
050        public class PseudocodeLabel implements Label {
051            private final String name;
052            private Integer targetInstructionIndex;
053    
054    
055            private PseudocodeLabel(String name) {
056                this.name = name;
057            }
058    
059            @NotNull
060            @Override
061            public String getName() {
062                return name;
063            }
064    
065            @Override
066            public String toString() {
067                return name;
068            }
069    
070            public Integer getTargetInstructionIndex() {
071                return targetInstructionIndex;
072            }
073    
074            public void setTargetInstructionIndex(int targetInstructionIndex) {
075                this.targetInstructionIndex = targetInstructionIndex;
076            }
077    
078            @Nullable
079            private List<Instruction> resolve() {
080                assert targetInstructionIndex != null;
081                return mutableInstructionList.subList(getTargetInstructionIndex(), mutableInstructionList.size());
082            }
083    
084            public Instruction resolveToInstruction() {
085                assert targetInstructionIndex != null;
086                return mutableInstructionList.get(targetInstructionIndex);
087            }
088    
089            public Label copy() {
090                return new PseudocodeLabel("copy " + name);
091            }
092        }
093    
094        private final List<Instruction> mutableInstructionList = new ArrayList<Instruction>();
095        private final List<Instruction> instructions = new ArrayList<Instruction>();
096    
097        private final BidirectionalMap<JetElement, PseudoValue> elementsToValues = new BidirectionalMap<JetElement, PseudoValue>();
098    
099        private final Map<PseudoValue, List<Instruction>> valueUsages = Maps.newHashMap();
100        private final Map<PseudoValue, Set<PseudoValue>> mergedValues = Maps.newHashMap();
101        private final Set<Instruction> sideEffectFree = Sets.newHashSet();
102    
103        private Pseudocode parent = null;
104        private Set<LocalFunctionDeclarationInstruction> localDeclarations = null;
105        //todo getters
106        private final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
107        private final Map<JetExpression, LoopInfo> loopInfo = Maps.newHashMap();
108        
109        private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>();
110    
111        private final JetElement correspondingElement;
112        private SubroutineExitInstruction exitInstruction;
113        private SubroutineSinkInstruction sinkInstruction;
114        private SubroutineExitInstruction errorInstruction;
115        private boolean postPrecessed = false;
116    
117        public PseudocodeImpl(JetElement correspondingElement) {
118            this.correspondingElement = correspondingElement;
119        }
120    
121        @NotNull
122        @Override
123        public JetElement getCorrespondingElement() {
124            return correspondingElement;
125        }
126    
127        @NotNull
128        @Override
129        public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() {
130            if (localDeclarations == null) {
131                localDeclarations = getLocalDeclarations(this);
132            }
133            return localDeclarations;
134        }
135    
136        @NotNull
137        private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations(@NotNull Pseudocode pseudocode) {
138            Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet();
139            for (Instruction instruction : ((PseudocodeImpl)pseudocode).mutableInstructionList) {
140                if (instruction instanceof LocalFunctionDeclarationInstruction) {
141                    localDeclarations.add((LocalFunctionDeclarationInstruction) instruction);
142                    localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody()));
143                }
144            }
145            return localDeclarations;
146        }
147    
148        @Override
149        @Nullable
150        public Pseudocode getParent() {
151            return parent;
152        }
153    
154        private void setParent(Pseudocode parent) {
155            this.parent = parent;
156        }
157    
158        @NotNull
159        public Pseudocode getRootPseudocode() {
160            Pseudocode parent = getParent();
161            while (parent != null) {
162                if (parent.getParent() == null) return parent;
163                parent = parent.getParent();
164            }
165            return this;
166        }
167    
168        /*package*/ PseudocodeLabel createLabel(String name) {
169            PseudocodeLabel label = new PseudocodeLabel(name);
170            labels.add(label);
171            return label;
172        }
173        
174        @Override
175        @NotNull
176        public List<Instruction> getInstructions() {
177            return instructions;
178        }
179    
180        @NotNull
181        @Override
182        public List<Instruction> getReversedInstructions() {
183            LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet();
184            PseudocodeTraverserPackage.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null);
185            if (traversedInstructions.size() < instructions.size()) {
186                List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions);
187                Collections.reverse(simplyReversedInstructions);
188                for (Instruction instruction : simplyReversedInstructions) {
189                    if (!traversedInstructions.contains(instruction)) {
190                        PseudocodeTraverserPackage.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null);
191                    }
192                }
193            }
194            return Lists.newArrayList(traversedInstructions);
195        }
196    
197        @Override
198        @NotNull
199        public List<Instruction> getInstructionsIncludingDeadCode() {
200            return mutableInstructionList;
201        }
202    
203        //for tests only
204        @NotNull
205        public List<PseudocodeLabel> getLabels() {
206            return labels;
207        }
208    
209        /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) {
210            addInstruction(exitInstruction);
211            assert this.exitInstruction == null;
212            this.exitInstruction = exitInstruction;
213        }
214        
215        /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) {
216            addInstruction(sinkInstruction);
217            assert this.sinkInstruction == null;
218            this.sinkInstruction = sinkInstruction;
219        }
220    
221        /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) {
222            addInstruction(errorInstruction);
223            assert this.errorInstruction == null;
224            this.errorInstruction = errorInstruction;
225        }
226    
227        /*package*/ void addInstruction(Instruction instruction) {
228            mutableInstructionList.add(instruction);
229            instruction.setOwner(this);
230    
231            if (instruction instanceof JetElementInstruction) {
232                JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
233                representativeInstructions.put(elementInstruction.getElement(), instruction);
234            }
235    
236            if (instruction instanceof MergeInstruction) {
237                addMergedValues((MergeInstruction) instruction);
238            }
239    
240            for (PseudoValue inputValue : instruction.getInputValues()) {
241                addValueUsage(inputValue, instruction);
242                for (PseudoValue mergedValue : getMergedValues(inputValue)) {
243                    addValueUsage(mergedValue, instruction);
244                }
245            }
246            if (PseudocodePackage.calcSideEffectFree(instruction)) {
247                sideEffectFree.add(instruction);
248            }
249        }
250    
251        /*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) {
252            loopInfo.put(expression, blockInfo);
253        }
254    
255        @Override
256        @NotNull
257        public SubroutineExitInstruction getExitInstruction() {
258            return exitInstruction;
259        }
260    
261        @Override
262        @NotNull
263        public SubroutineSinkInstruction getSinkInstruction() {
264            return sinkInstruction;
265        }
266    
267        @Override
268        @NotNull
269        public SubroutineEnterInstruction getEnterInstruction() {
270            return (SubroutineEnterInstruction) mutableInstructionList.get(0);
271        }
272    
273        @Nullable
274        @Override
275        public PseudoValue getElementValue(@Nullable JetElement element) {
276            return elementsToValues.get(element);
277        }
278    
279        @NotNull
280        @Override
281        public List<? extends JetElement> getValueElements(@Nullable PseudoValue value) {
282            List<? extends JetElement> result = elementsToValues.getKeysByValue(value);
283            return result != null ? result : Collections.<JetElement>emptyList();
284        }
285    
286        @NotNull
287        @Override
288        public List<? extends Instruction> getUsages(@Nullable PseudoValue value) {
289            List<? extends Instruction> result = valueUsages.get(value);
290            return result != null ? result : Collections.<Instruction>emptyList();
291        }
292    
293        @Override
294        public boolean isSideEffectFree(@NotNull Instruction instruction) {
295            return sideEffectFree.contains(instruction);
296        }
297    
298        /*package*/ void bindElementToValue(@NotNull JetElement element, @NotNull PseudoValue value) {
299            elementsToValues.put(element, value);
300        }
301    
302        /*package*/ void bindLabel(Label label) {
303            ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size());
304        }
305        
306        private Set<PseudoValue> getMergedValues(@NotNull PseudoValue value) {
307            Set<PseudoValue> result = mergedValues.get(value);
308            return result != null ? result : Collections.<PseudoValue>emptySet();
309        }
310        
311        private void addMergedValues(@NotNull MergeInstruction instruction) {
312            Set<PseudoValue> result = new LinkedHashSet<PseudoValue>();
313            for (PseudoValue value : instruction.getInputValues()) {
314                result.addAll(getMergedValues(value));
315                result.add(value);
316            }
317            mergedValues.put(instruction.getOutputValue(), result);
318        }
319    
320        private void addValueUsage(PseudoValue value, Instruction usage) {
321            if (usage instanceof MergeInstruction) return;
322            KotlinPackage.getOrPut(
323                    valueUsages,
324                    value,
325                    new Function0<List<Instruction>>() {
326                        @Override
327                        public List<Instruction> invoke() {
328                            return Lists.newArrayList();
329                        }
330                    }
331            ).add(usage);
332        }
333    
334        public void postProcess() {
335            if (postPrecessed) return;
336            postPrecessed = true;
337            errorInstruction.setSink(getSinkInstruction());
338            exitInstruction.setSink(getSinkInstruction());
339            int index = 0;
340            for (Instruction instruction : mutableInstructionList) {
341                //recursively invokes 'postProcess' for local declarations
342                processInstruction(instruction, index);
343                index++;
344            }
345            if (getParent() != null) return;
346    
347            // Collecting reachable instructions should be done after processing all instructions
348            // (including instructions in local declarations) to avoid being in incomplete state.
349            collectAndCacheReachableInstructions();
350            for (LocalFunctionDeclarationInstruction localFunctionDeclarationInstruction : getLocalDeclarations()) {
351                ((PseudocodeImpl) localFunctionDeclarationInstruction.getBody()).collectAndCacheReachableInstructions();
352            }
353        }
354    
355        private void collectAndCacheReachableInstructions() {
356            Set<Instruction> reachableInstructions = collectReachableInstructions();
357            for (Instruction instruction : mutableInstructionList) {
358                if (reachableInstructions.contains(instruction)) {
359                    instructions.add(instruction);
360                }
361            }
362            markDeadInstructions();
363        }
364    
365        private void processInstruction(Instruction instruction, final int currentPosition) {
366            instruction.accept(new InstructionVisitor() {
367                @Override
368                public void visitInstructionWithNext(InstructionWithNext instruction) {
369                    instruction.setNext(getNextPosition(currentPosition));
370                }
371    
372                @Override
373                public void visitJump(AbstractJumpInstruction instruction) {
374                    instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
375                }
376    
377                @Override
378                public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
379                    instruction.setNext(getNextPosition(currentPosition));
380                    List<Label> targetLabels = instruction.getTargetLabels();
381                    for (Label targetLabel : targetLabels) {
382                        instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel));
383                    }
384                }
385    
386                @Override
387                public void visitConditionalJump(ConditionalJumpInstruction instruction) {
388                    Instruction nextInstruction = getNextPosition(currentPosition);
389                    Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
390                    if (instruction.getOnTrue()) {
391                        instruction.setNextOnFalse(nextInstruction);
392                        instruction.setNextOnTrue(jumpTarget);
393                    }
394                    else {
395                        instruction.setNextOnFalse(jumpTarget);
396                        instruction.setNextOnTrue(nextInstruction);
397                    }
398                    visitJump(instruction);
399                }
400    
401                @Override
402                public void visitLocalFunctionDeclarationInstruction(LocalFunctionDeclarationInstruction instruction) {
403                    PseudocodeImpl body = (PseudocodeImpl) instruction.getBody();
404                    body.setParent(PseudocodeImpl.this);
405                    body.postProcess();
406                    instruction.setNext(getSinkInstruction());
407                }
408    
409                @Override
410                public void visitSubroutineExit(SubroutineExitInstruction instruction) {
411                    // Nothing
412                }
413    
414                @Override
415                public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
416                    // Nothing
417                }
418    
419                @Override
420                public void visitInstruction(Instruction instruction) {
421                    throw new UnsupportedOperationException(instruction.toString());
422                }
423            });
424        }
425    
426        private Set<Instruction> collectReachableInstructions() {
427            Set<Instruction> visited = Sets.newHashSet();
428            PseudocodeTraverserPackage.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, null);
429            if (!visited.contains(getExitInstruction())) {
430                visited.add(getExitInstruction());
431            }
432            if (!visited.contains(errorInstruction)) {
433                visited.add(errorInstruction);
434            }
435            if (!visited.contains(getSinkInstruction())) {
436                visited.add(getSinkInstruction());
437            }
438            return visited;
439        }
440    
441        private void markDeadInstructions() {
442            Set<Instruction> instructionSet = Sets.newHashSet(instructions);
443            for (Instruction instruction : mutableInstructionList) {
444                if (!instructionSet.contains(instruction)) {
445                    ((InstructionImpl)instruction).setMarkedAsDead(true);
446                    for (Instruction nextInstruction : instruction.getNextInstructions()) {
447                        nextInstruction.getPreviousInstructions().remove(instruction);
448                    }
449                }
450            }
451        }
452    
453        @NotNull
454        private Instruction getJumpTarget(@NotNull Label targetLabel) {
455            return ((PseudocodeLabel)targetLabel).resolveToInstruction();
456        }
457    
458        @NotNull
459        private Instruction getNextPosition(int currentPosition) {
460            int targetPosition = currentPosition + 1;
461            assert targetPosition < mutableInstructionList.size() : currentPosition;
462            return mutableInstructionList.get(targetPosition);
463        }
464    
465        public void repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel) {
466            Integer startIndex = ((PseudocodeLabel) startLabel).getTargetInstructionIndex();
467            assert startIndex != null;
468            Integer finishIndex = ((PseudocodeLabel) finishLabel).getTargetInstructionIndex();
469            assert finishIndex != null;
470    
471            Map<Label, Label> originalToCopy = Maps.newHashMap();
472            Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create();
473            for (PseudocodeLabel label : labels) {
474                Integer index = label.getTargetInstructionIndex();
475                if (index == null) continue; //label is not bounded yet
476                if (label == startLabel || label == finishLabel) continue;
477    
478                if (startIndex <= index && index <= finishIndex) {
479                    originalToCopy.put(label, label.copy());
480                    originalLabelsForInstruction.put(getJumpTarget(label), label);
481                }
482            }
483            for (int index = startIndex; index < finishIndex; index++) {
484                Instruction originalInstruction = mutableInstructionList.get(index);
485                repeatLabelsBindingForInstruction(originalInstruction, originalToCopy, originalLabelsForInstruction);
486                addInstruction(copyInstruction(originalInstruction, originalToCopy));
487            }
488            repeatLabelsBindingForInstruction(mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction);
489        }
490    
491        private void repeatLabelsBindingForInstruction(
492                @NotNull Instruction originalInstruction,
493                @NotNull Map<Label, Label> originalToCopy,
494                @NotNull Multimap<Instruction, Label> originalLabelsForInstruction
495        ) {
496            for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) {
497                bindLabel(originalToCopy.get(originalLabel));
498            }
499        }
500    
501        private Instruction copyInstruction(@NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) {
502            if (instruction instanceof AbstractJumpInstruction) {
503                Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel();
504                if (originalToCopy.containsKey(originalTarget)) {
505                    return ((AbstractJumpInstruction)instruction).copy(originalToCopy.get(originalTarget));
506                }
507            }
508            if (instruction instanceof NondeterministicJumpInstruction) {
509                List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels();
510                List<Label> copyTargets = copyLabels(originalTargets, originalToCopy);
511                return ((NondeterministicJumpInstruction) instruction).copy(copyTargets);
512            }
513            return ((InstructionImpl)instruction).copy();
514        }
515    
516        @NotNull
517        private List<Label> copyLabels(Collection<Label> labels, Map<Label, Label> originalToCopy) {
518            List<Label> newLabels = Lists.newArrayList();
519            for (Label label : labels) {
520                Label newLabel = originalToCopy.get(label);
521                newLabels.add(newLabel != null ? newLabel : label);
522            }
523            return newLabels;
524        }
525    }