codegen: fix implementation of branch generation to generate 'ternary' operator

for code:
--------8<--------
> cat ../backend.native/tests/runtime/basic/hello4.kt
fun main(args : Array<String>) {
    val x = 2
    println(if (x == 2) "Hello" else "Привет")
    println(if (x == 3) "Bye" else "Пока")
 }
--------8<--------
code generator produces:
--------8<--------
> llvm-dis-mp-3.8 ../backend.native/tests/runtime/basic/hello4.kt.bc -o -
; ModuleID = '../backend.native/tests/runtime/basic/hello4.kt.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.12.0"

...
define void @"kfun:main(Array<String>)"(i8*) {
entry:
  %args = alloca i8*
  store i8* %0, i8** %args
  %x = alloca i32
  store i32 2, i32* %x
  %tmp_1 = alloca i8*                             ; allocation of temporal variable (0)
  %tmp_3 = load i32, i32* %x
  %tmp_2 = icmp eq i32 %tmp_3, 2
  br i1 %tmp_2, label %label_2, label %label_1

label_0:                                          ; preds = %label_2, %label_1
  %tmp_0 = load i8*, i8** %tmp_1                  ; load value from tmp_1 (2)
  call void @Kotlin_io_Console_println(i8* %tmp_0)
  %tmp_8 = alloca i8*
  %tmp_10 = load i32, i32* %x
  %tmp_9 = icmp eq i32 %tmp_10, 3
  br i1 %tmp_9, label %label_5, label %label_4

label_1:                                          ; preds = %entry  store value to tmp_1 (1)
  store i8* @"kstr:KAWujn4S8YITX5L7kIQ7sQgNO+g=", i8** %tmp_1
  br label %label_0

label_2:                                          ; preds = %entry
  store i8* @"kstr:9/+ei3uy4Jtwk1pdeF4MxdnQq/A=", i8** %tmp_1
  br label %label_0

label_3:                                          ; preds = %label_5, %label_4
  %tmp_7 = load i8*, i8** %tmp_8
  call void @Kotlin_io_Console_println(i8* %tmp_7)
  ret void

label_4:                                          ; preds = %label_0
  store i8* @"kstr:q9lt9dN14ItE52B0fKxQuwMq4cc=", i8** %tmp_8
  br label %label_3

label_5:                                          ; preds = %label_0
  store i8* @"kstr:95JCQGTQyhp9FO/gWI8QwFLSjmk=", i8** %tmp_8
  br label %label_3
}

...
--------8<--------
This commit is contained in:
Vasily Levchenko
2016-11-21 17:09:53 +03:00
committed by vvlevchenko
parent c54e786c33
commit cf466e0cd7
@@ -96,7 +96,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
override fun visitWhen(expression: IrWhen) {
logger.log("visitWhen : ${ir2string(expression)}")
evaluateWhen(expression)
evaluateWhen("", expression)
}
//-------------------------------------------------------------------------//
@@ -326,7 +326,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
is IrReturn -> return evaluateReturn ( value)
is IrBlock -> return evaluateBlock ( value)
is IrExpressionBody -> return evaluateExpression (tmpVariableName, value.expression)
is IrWhen -> return evaluateWhen ( value)
is IrWhen -> return evaluateWhen (tmpVariableName, value)
null -> return null
else -> {
TODO()
@@ -336,17 +336,26 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
//-------------------------------------------------------------------------//
private fun evaluateWhen(expression: IrWhen): LLVMOpaqueValue? {
var bbExit:LLVMOpaqueBasicBlock? = null // By default "when" does not have "exit"
private fun evaluateWhen(tmpVariableName:String, expression: IrWhen): LLVMOpaqueValue? {
logger.log("evaluateWhen : ${ir2string(expression)}")
var bbExit:LLVMOpaqueBasicBlock? = 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 tmpVariable = if (!isUnit) codegen.newVar() else null
val tmpLlvmVariablePtr = if (!isUnit) codegen.alloca(expression.type, tmpVariable!!) else null
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(it, bbNext, bbExit) // Generate code for current clause.
generateWhenCase(isUnit, isNothing, tmpLlvmVariablePtr, it, bbNext, bbExit) // Generate code for current clause.
}
if (neitherUnitNorNothing) // If result hasn't Unit type and block doesn't end with return
return codegen.load(tmpLlvmVariablePtr!!, tmpVariableName) // load value from variable.
return null
}
@@ -717,10 +726,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
//-------------------------------------------------------------------------//
private fun generateWhenCase(branch: IrBranch, bbNext: LLVMOpaqueBasicBlock?, bbExit: LLVMOpaqueBasicBlock?) {
private fun generateWhenCase(isUnit:Boolean, isNothing:Boolean, resultPtr:LLVMOpaqueValue?, branch: IrBranch, bbNext: LLVMOpaqueBasicBlock?, bbExit: LLVMOpaqueBasicBlock?) {
val neitherUnitNorNothing = !isNothing && !isUnit // If branches doesn't end with 'return' either result hasn't got 'unit' type.
if (isUnconditional(branch)) { // It is the "else" clause.
evaluateExpression(codegen.newVar(), branch.result) // Generate clause body.
if (bbExit == null) return // If "when" does not have exit - return.
val brResult = evaluateExpression(codegen.newVar(), branch.result) // Generate clause body.
if (neitherUnitNorNothing) // If nor unit neither result ends with return
codegen.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.
} else { // It is conditional clause.
@@ -728,9 +740,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val condition = evaluateExpression(codegen.newVar(), 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.
evaluateExpression(codegen.newVar(), branch.result) // Generate clause body.
if (!KotlinBuiltIns.isNothing(branch.result.type)) // If clause code does not contain "return".
codegen.br(bbExit!!) // Generate branch to bbExit.
val brResult = evaluateExpression(codegen.newVar(), branch.result) // Generate clause body.
if (neitherUnitNorNothing) // If nor unit neither result ends with return
codegen.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.
}
}