From 587a9558bc8dd472937497a7d88ebc1faead9ec8 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 3 Jun 2014 21:06:16 +0400 Subject: [PATCH] Pseudocode: Introduce "merge" instruction --- .../jet/lang/cfg/JetControlFlowBuilder.java | 6 +++ .../cfg/JetControlFlowBuilderAdapter.java | 8 ++++ .../jet/lang/cfg/JetControlFlowProcessor.java | 2 +- .../lang/cfg/JetFlowInformationProvider.java | 1 + .../jet/lang/cfg/TailRecursionDetector.java | 6 +++ .../JetControlFlowInstructionsGenerator.java | 8 ++++ .../instructions/InstructionVisitor.kt | 4 ++ .../InstructionVisitorWithResult.kt | 4 ++ .../eval/operationInstructions.kt | 41 ++++++++++++++++--- .../FinallyTestCopy.instructions | 8 ++-- .../cfg/expressions/Assignments.instructions | 4 +- .../cfg/expressions/LazyBooleans.instructions | 12 +++--- .../tailCalls/tryCatchFinally.instructions | 2 +- 13 files changed, 87 insertions(+), 19 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java index 672b097361f..a92573192ce 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilder.java @@ -118,6 +118,12 @@ public interface JetControlFlowBuilder { boolean synthetic ); + @NotNull + PseudoValue merge( + @NotNull JetExpression expression, + @NotNull List inputValues + ); + @NotNull PseudoValue readThis(@NotNull JetExpression expression, @Nullable ReceiverParameterDescriptor parameterDescriptor); @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java index 359e3e24ac8..6a1bc510368 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowBuilderAdapter.java @@ -76,6 +76,14 @@ public abstract class JetControlFlowBuilderAdapter implements JetControlFlowBuil return getDelegateBuilder().magic(instructionElement, valueElement, inputValues, synthetic); } + @NotNull + @Override + public PseudoValue merge( + @NotNull JetExpression expression, @NotNull List inputValues + ) { + return getDelegateBuilder().merge(expression, inputValues); + } + @NotNull @Override public PseudoValue readThis(@NotNull JetExpression expression, @Nullable ReceiverParameterDescriptor parameterDescriptor) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index d5d18a27af4..781eb6940bc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -203,7 +203,7 @@ public class JetControlFlowProcessor { builder.bindValue(values.get(0), to); break; default: - builder.magic(to, to, values, true); + builder.merge(to, values); break; } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index 37d275a1e71..26944142c2c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -256,6 +256,7 @@ public class JetFlowInformationProvider { for (Instruction deadInstruction : pseudocode.getDeadInstructions()) { if (!(deadInstruction instanceof JetElementInstruction) || deadInstruction instanceof LoadUnitValueInstruction + || deadInstruction instanceof MergeInstruction || (deadInstruction instanceof MagicInstruction && ((MagicInstruction) deadInstruction).getSynthetic())) continue; JetElement element = ((JetElementInstruction) deadInstruction).getElement(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/TailRecursionDetector.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/TailRecursionDetector.java index cb465a98b18..b3c12246c65 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/TailRecursionDetector.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/TailRecursionDetector.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitorWithResult; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MagicInstruction; +import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MergeInstruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.ThrowExceptionInstruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.MarkInstruction; @@ -76,4 +77,9 @@ public class TailRecursionDetector extends InstructionVisitorWithResult public Boolean visitMagic(@NotNull MagicInstruction instruction) { return instruction.getSynthetic(); } + + @Override + public Boolean visitMerge(@NotNull MergeInstruction instruction) { + return true; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java index 7ec2c8cd7f8..2d3f76d2138 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/JetControlFlowInstructionsGenerator.java @@ -437,6 +437,14 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd return instruction.getOutputValue(); } + @NotNull + @Override + public PseudoValue merge(@NotNull JetExpression expression, @NotNull List inputValues) { + MergeInstruction instruction = MergeInstruction.object$.create(expression, getCurrentScope(), inputValues, valueFactory); + add(instruction); + return instruction.getOutputValue(); + } + @NotNull @Override public PseudoValue readThis(@NotNull JetExpression expression, @Nullable ReceiverParameterDescriptor parameterDescriptor) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitor.kt b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitor.kt index 8f41cee9d88..3bc0867c2c8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitor.kt @@ -101,6 +101,10 @@ public open class InstructionVisitor() { visitOperation(instruction) } + public open fun visitMerge(instruction: MergeInstruction) { + visitOperation(instruction) + } + public open fun visitCompilationErrorInstruction(instruction: CompilationErrorInstruction) { visitInstructionWithNext(instruction) } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitorWithResult.kt b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitorWithResult.kt index 43afa1f88ab..5ecda68c914 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitorWithResult.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/InstructionVisitorWithResult.kt @@ -100,6 +100,10 @@ public abstract class InstructionVisitorWithResult() { return visitOperation(instruction) } + public open fun visitMerge(instruction: MergeInstruction): R { + return visitOperation(instruction) + } + public open fun visitCompilationErrorInstruction(instruction: CompilationErrorInstruction): R { return visitInstructionWithNext(instruction) } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/eval/operationInstructions.kt b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/eval/operationInstructions.kt index 0c9e34274c6..339b23442cb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/eval/operationInstructions.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/pseudocode/instructions/eval/operationInstructions.kt @@ -52,6 +52,11 @@ public abstract class OperationInstruction protected( } } +trait StrictlyValuedOperationInstruction: OperationInstruction { + override val outputValue: PseudoValue + get() = resultValue!! +} + public class CallInstruction private( element: JetElement, lexicalScope: LexicalScope, @@ -95,16 +100,13 @@ public class CallInstruction private( // denote value transformation which can't be expressed by other instructions (such as call or read) // pass more than one value to instruction which formally requires only one (e.g. jump) // "Synthetic" means that the instruction does not correspond to some operation explicitly expressed by PSI element -// Examples: merging branches of 'if', 'when' and 'try' expressions, providing initial values for parameters, etc. +// Examples: providing initial values for parameters, missing right-hand side in assignments public class MagicInstruction( element: JetElement, lexicalScope: LexicalScope, val synthetic: Boolean, inputValues: List -) : OperationInstruction(element, lexicalScope, inputValues) { - override val outputValue: PseudoValue - get() = resultValue!! - +) : OperationInstruction(element, lexicalScope, inputValues), StrictlyValuedOperationInstruction { override fun accept(visitor: InstructionVisitor) { visitor.visitMagic(this) } @@ -128,4 +130,33 @@ public class MagicInstruction( factory: PseudoValueFactory ): MagicInstruction = MagicInstruction(element, lexicalScope, synthetic, inputValues).setResult(factory, valueElement) as MagicInstruction } +} + +// Merges values produced by alternative control-flow paths (such as 'if' branches) +class MergeInstruction private( + element: JetElement, + lexicalScope: LexicalScope, + inputValues: List +): OperationInstruction(element, lexicalScope, inputValues), StrictlyValuedOperationInstruction { + override fun accept(visitor: InstructionVisitor) { + visitor.visitMerge(this) + } + + override fun accept(visitor: InstructionVisitorWithResult): R { + return visitor.visitMerge(this) + } + + override fun createCopy() = + MergeInstruction(element, lexicalScope, inputValues).setResult(resultValue) + + override fun toString() = renderInstruction("merge", render(element)) + + class object { + fun create( + element: JetElement, + lexicalScope: LexicalScope, + inputValues: List, + factory: PseudoValueFactory + ): MergeInstruction = MergeInstruction(element, lexicalScope, inputValues).setResult(factory) as MergeInstruction + } } \ No newline at end of file diff --git a/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions b/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions index 36282757f5c..10fc552489d 100644 --- a/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions +++ b/compiler/testData/cfg/controlStructures/FinallyTestCopy.instructions @@ -110,7 +110,7 @@ L6 [skipFinallyToErrorBlock]: 3 mark({ return 1 }) PREV:[jmp(L6 [skipFinallyToErrorBlock])] r(1) -> ret(*|) L1 NEXT:[] -- 2 magic(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { return 1 }|, , ) -> PREV:[] +- 2 merge(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { return 1 }|, , ) -> PREV:[] L1: 1 NEXT:[] PREV:[ret(*|) L1, ret(*|) L1] error: @@ -195,9 +195,9 @@ L10 [skipFinallyToErrorBlock]: call(cond, cond) -> jf(copy L12|) NEXT:[jmp(L2 [loop entry point]), ret L1] ret L1 NEXT:[] -- jmp(copy L13) NEXT:[magic(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { if (cond()) return else continue }|, , ) -> ] PREV:[] +- jmp(copy L13) NEXT:[merge(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { if (cond()) return else continue }|, , ) -> ] PREV:[] jmp(L2 [loop entry point]) NEXT:[mark(cond())] PREV:[jf(copy L12|)] -- 3 magic(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { if (cond()) return else continue }|, , ) -> PREV:[] +- 3 merge(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { if (cond()) return else continue }|, , ) -> PREV:[] - 2 jmp(L2 [loop entry point]) NEXT:[mark(cond())] PREV:[] L3 [loop exit point]: read (Unit) PREV:[jf(L3 [loop exit point]|)] @@ -276,7 +276,7 @@ L6 [skipFinallyToErrorBlock]: jf(copy L9 [loop exit point]|) NEXT:[read (Unit), jmp(copy L8 [loop entry point])] jmp(copy L8 [loop entry point]) NEXT:[mark(cond())] read (Unit) PREV:[jf(copy L9 [loop exit point]|)] - 2 magic(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { while (cond()); }|, , ) -> + 2 merge(try { doSmth() } catch (e: NullPointerException) { doSmth1() } catch (e: Exception) { doSmth2() } finally { while (cond()); }|, , ) -> L1: 1 NEXT:[] error: diff --git a/compiler/testData/cfg/expressions/Assignments.instructions b/compiler/testData/cfg/expressions/Assignments.instructions index f3917aca78e..32aaef586a0 100644 --- a/compiler/testData/cfg/expressions/Assignments.instructions +++ b/compiler/testData/cfg/expressions/Assignments.instructions @@ -46,11 +46,11 @@ L0: r(true) -> jf(L2|) NEXT:[r(2) -> , r(1) -> ] r(1) -> - jmp(L3) NEXT:[magic(if (true) 1 else 2|, ) -> ] + jmp(L3) NEXT:[merge(if (true) 1 else 2|, ) -> ] L2: r(2) -> PREV:[jf(L2|)] L3: - magic(if (true) 1 else 2|, ) -> PREV:[jmp(L3), r(2) -> ] + merge(if (true) 1 else 2|, ) -> PREV:[jmp(L3), r(2) -> ] w(x|) v(val y = true && false) r(true) -> diff --git a/compiler/testData/cfg/expressions/LazyBooleans.instructions b/compiler/testData/cfg/expressions/LazyBooleans.instructions index dd957e4e152..3d5bb8a9941 100644 --- a/compiler/testData/cfg/expressions/LazyBooleans.instructions +++ b/compiler/testData/cfg/expressions/LazyBooleans.instructions @@ -31,12 +31,12 @@ L0: jf(L2|) NEXT:[mark({ 2 }), mark({ 1 })] 3 mark({ 1 }) r(1) -> - 2 jmp(L3) NEXT:[magic(if (a) { 1 } else { 2 }|, ) -> ] + 2 jmp(L3) NEXT:[merge(if (a) { 1 } else { 2 }|, ) -> ] L2: 3 mark({ 2 }) PREV:[jf(L2|)] r(2) -> L3: - 2 magic(if (a) { 1 } else { 2 }|, ) -> PREV:[jmp(L3), r(2) -> ] + 2 merge(if (a) { 1 } else { 2 }|, ) -> PREV:[jmp(L3), r(2) -> ] r(3) -> mark(if (a && b) 5 else 6) r(a) -> @@ -45,11 +45,11 @@ L3: L4: jf(L5) NEXT:[r(6) -> , r(5) -> ] PREV:[jf(L4|), r(b) -> ] r(5) -> - jmp(L6) NEXT:[magic(if (a && b) 5 else 6|, ) -> ] + jmp(L6) NEXT:[merge(if (a && b) 5 else 6|, ) -> ] L5: r(6) -> PREV:[jf(L5)] L6: - magic(if (a && b) 5 else 6|, ) -> PREV:[jmp(L6), r(6) -> ] + merge(if (a && b) 5 else 6|, ) -> PREV:[jmp(L6), r(6) -> ] r(7) -> mark(if (a || b) 8 else 9) r(a) -> @@ -58,11 +58,11 @@ L6: L7: jf(L8) NEXT:[r(9) -> , r(8) -> ] PREV:[jt(L7|), r(b) -> ] r(8) -> - jmp(L9) NEXT:[magic(if (a || b) 8 else 9|, ) -> ] + jmp(L9) NEXT:[merge(if (a || b) 8 else 9|, ) -> ] L8: r(9) -> PREV:[jf(L8)] L9: - magic(if (a || b) 8 else 9|, ) -> PREV:[jmp(L9), r(9) -> ] + merge(if (a || b) 8 else 9|, ) -> PREV:[jmp(L9), r(9) -> ] r(10) -> mark(if (a) 11) r(a) -> diff --git a/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions b/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions index 5de86467e81..688a0d1dcaf 100644 --- a/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions +++ b/compiler/testData/cfg/tailCalls/tryCatchFinally.instructions @@ -40,7 +40,7 @@ L5 [skipFinallyToErrorBlock]: 3 mark({ test() }) PREV:[jmp(L5 [skipFinallyToErrorBlock])] mark(test()) call(test, test) -> - 2 magic(try { test() } catch (any : Exception) { test() } finally { test() }|, ) -> + 2 merge(try { test() } catch (any : Exception) { test() } finally { test() }|, ) -> L1: 1 NEXT:[] error: