From 667d3b93566011a3094a2b7d1d8fe5d6354c7df3 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Thu, 29 Dec 2016 16:33:23 +0300 Subject: [PATCH] Switch when to phi. (#172) --- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 58 +++++++++---------- backend.native/tests/build.gradle | 8 +++ .../tests/codegen/branching/when6.kt | 6 ++ .../tests/codegen/branching/when7.kt | 4 ++ 4 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 backend.native/tests/codegen/branching/when6.kt create mode 100644 backend.native/tests/codegen/branching/when7.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 647c27eee0b..d6b7ea3110c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1146,28 +1146,29 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateWhen(expression: IrWhen): LLVMValueRef { context.log("evaluateWhen : ${ir2string(expression)}") var bbExit: LLVMBasicBlockRef? = null // By default "when" does not have "exit". - if (!KotlinBuiltIns.isNothing(expression.type)) // If "when" has "exit". - bbExit = codegen.basicBlock() // Create basic block to process "exit". - val isUnit = KotlinBuiltIns.isUnit(expression.type) val isNothing = KotlinBuiltIns.isNothing(expression.type) - val neitherUnitNorNothing = !isNothing && !isUnit - val tmpLlvmVariable = if (!isUnit) codegen.vars.createAnonymousMutable(expression.type) else -1 - expression.branches.forEach { // Iterate through "when" branches (clauses). - var bbNext = bbExit // For last clause bbNext coincides with bbExit. - if (it != expression.branches.last()) // If it is not last clause. - bbNext = codegen.basicBlock() // Create new basic block for next clause. - val isUnitBranch = KotlinBuiltIns.isUnit(it.result.type) - val isNothingBranch = KotlinBuiltIns.isNothing(it.result.type) - generateWhenCase(isUnitBranch, isNothingBranch, tmpLlvmVariable, it, bbNext, bbExit) // Generate code for current clause. + if (!isNothing) // If "when" has "exit". + bbExit = codegen.basicBlock() // Create basic block to process "exit". + + val resultPhi = if (isUnit || isNothing) null else + codegen.appendingTo(bbExit!!) { + codegen.phi(codegen.getLLVMType(expression.type)) + } + + expression.branches.forEach { // Iterate through "when" branches (clauses). + var bbNext = bbExit // For last clause bbNext coincides with bbExit. + if (it != expression.branches.last()) // If it is not last clause. + bbNext = codegen.basicBlock() // Create new basic block for next clause. + generateWhenCase(resultPhi, it, bbNext, bbExit) // Generate code for current clause. } return when { // FIXME: remove the hacks. isUnit -> codegen.theUnitInstanceRef.llvm isNothing -> codegen.kNothingFakeValue - else -> codegen.vars.load(tmpLlvmVariable) + else -> resultPhi!! } } @@ -1760,30 +1761,27 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// - private fun generateWhenCase(isUnit:Boolean, isNothing:Boolean, resultPtr: Int, - branch: IrBranch, bbNext: LLVMBasicBlockRef?, bbExit: LLVMBasicBlockRef?) { - val neitherUnitNorNothing = !isNothing && !isUnit // If branches doesn't end with 'return' either result hasn't got 'unit' type. + private fun generateWhenCase(resultPhi: LLVMValueRef?, branch: IrBranch, + bbNext: LLVMBasicBlockRef?, bbExit: LLVMBasicBlockRef?) { val branchResult = branch.result - // TODO: use phis here! - if (isUnconditional(branch)) { // It is the "else" clause. - val brResult = evaluateExpression(branchResult) // Generate clause body. - if (neitherUnitNorNothing) // If nor unit neither result ends with return - codegen.vars.store(brResult, resultPtr) // we store result to temporal variable. - if (bbExit == null) return // If 'bbExit' isn't defined just return - codegen.br(bbExit) // Generate branch to bbExit. - codegen.positionAtEnd(bbExit) // Switch generation to bbExit. + val isNothing = KotlinBuiltIns.isNothing(branchResult.type) + val brResult = if (isUnconditional(branch)) { // It is the "else" clause. + evaluateExpression(branchResult) // Generate clause body. } else { // It is conditional clause. val bbCurr = codegen.basicBlock() // Create block for clause body. val condition = evaluateExpression(branch.condition) // Generate cmp instruction. codegen.condBr(condition, bbCurr, bbNext) // Conditional branch depending on cmp result. codegen.positionAtEnd(bbCurr) // Switch generation to block for clause body. - val brResult = evaluateExpression(branch.result) // Generate clause body. - if (neitherUnitNorNothing) // If nor unit neither result ends with return - codegen.vars.store(brResult, resultPtr) // we store result to temporal variable. - if (!isNothing) // If basic block doesn't end with 'return' - codegen.br(bbExit!!) // generate branch to bbExit. - codegen.positionAtEnd(bbNext!!) // Switch generation to bbNextClause. + evaluateExpression(branch.result) // Generate clause body. } + if (resultPhi != null && !isNothing) + codegen.assignPhis(resultPhi to brResult) + if (bbExit != null && !isNothing) + codegen.br(bbExit!!) + if (bbNext != null) // Switch generation to next or exit. + codegen.positionAtEnd(bbNext) + else if (bbExit != null) + codegen.positionAtEnd(bbExit) } //-------------------------------------------------------------------------// diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 8d96996c315..0afd93d21b0 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -279,6 +279,14 @@ task when5(type: RunKonanTest) { source = "codegen/branching/when5.kt" } +task when6(type: RunKonanTest) { + source = "codegen/branching/when6.kt" +} + +task when7(type: RunKonanTest) { + source = "codegen/branching/when7.kt" +} + task when_through(type: RunKonanTest) { source = "codegen/branching/when_through.kt" } diff --git a/backend.native/tests/codegen/branching/when6.kt b/backend.native/tests/codegen/branching/when6.kt new file mode 100644 index 00000000000..ca3524332b7 --- /dev/null +++ b/backend.native/tests/codegen/branching/when6.kt @@ -0,0 +1,6 @@ +fun foo() { +} + +fun main(args: Array) { + if (true) foo() else foo() +} \ No newline at end of file diff --git a/backend.native/tests/codegen/branching/when7.kt b/backend.native/tests/codegen/branching/when7.kt new file mode 100644 index 00000000000..26351a53c65 --- /dev/null +++ b/backend.native/tests/codegen/branching/when7.kt @@ -0,0 +1,4 @@ +fun main(args: Array) { + val b = args.size < 1 + val x = if (b) Any() else throw Error() +} \ No newline at end of file