translator: add npe operator, exceptions emulation, fix for nulls, test
This commit is contained in:
@@ -90,7 +90,7 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va
|
||||
fun evaluateExpression(expr: PsiElement?, scopeDepth: Int): LLVMSingleValue? {
|
||||
return when (expr) {
|
||||
is KtBlockExpression -> {
|
||||
expressionWalker(expr.firstChild, null, scopeDepth + 1)
|
||||
expressionWalker(expr.firstChild, breakLabel = null, scopeDepth = scopeDepth + 1)
|
||||
return null
|
||||
}
|
||||
is KtBinaryExpression -> evaluateBinaryExpression(expr, scopeDepth)
|
||||
@@ -636,6 +636,18 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va
|
||||
codeBuilder.storeVariable(firstOp, resultOp)
|
||||
return oldValue
|
||||
}
|
||||
KtTokens.EXCLEXCL -> {
|
||||
val nullLabel = codeBuilder.getNewLabel(prefix = "nullCheck")
|
||||
val notNullLabel = codeBuilder.getNewLabel(prefix = "nullCheck")
|
||||
val nullCheck = codeBuilder.nullCheck(firstOp)
|
||||
codeBuilder.addCondition(nullCheck, nullLabel, notNullLabel)
|
||||
|
||||
codeBuilder.markWithLabel(nullLabel)
|
||||
codeBuilder.addExceptionCall("KotlinNullPointerException")
|
||||
codeBuilder.addUnconditionalJump(notNullLabel)
|
||||
codeBuilder.markWithLabel(notNullLabel)
|
||||
return firstOp
|
||||
}
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
@@ -895,7 +907,8 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va
|
||||
|
||||
when (expressionType) {
|
||||
is LLVMVoidType,
|
||||
is LLVMNullType -> {}
|
||||
is LLVMNullType -> {
|
||||
}
|
||||
is LLVMReferenceType -> codeBuilder.allocStaticVar(resultVariable, asValue = true)
|
||||
else -> codeBuilder.allocStackVar(resultVariable, asValue = true)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package org.kotlinnative.translator.llvm
|
||||
|
||||
import org.kotlinnative.translator.llvm.types.LLVMCharType
|
||||
import org.kotlinnative.translator.llvm.types.LLVMIntType
|
||||
import org.kotlinnative.translator.llvm.types.LLVMStringType
|
||||
import org.kotlinnative.translator.llvm.types.LLVMType
|
||||
import org.kotlinnative.translator.llvm.types.*
|
||||
import java.util.*
|
||||
|
||||
class LLVMBuilder(val arm: Boolean = false) {
|
||||
@@ -18,22 +15,29 @@ class LLVMBuilder(val arm: Boolean = false) {
|
||||
initBuilder()
|
||||
}
|
||||
|
||||
var exceptions: Map<String, LLVMVariable> = mapOf()
|
||||
|
||||
private fun initBuilder() {
|
||||
val declares = arrayOf(
|
||||
"declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1)",
|
||||
"declare i8* @malloc_static(i32)")
|
||||
"declare i8* @malloc_static(i32)",
|
||||
"declare i32 @printf(i8*, ...)",
|
||||
"declare void @abort()")
|
||||
|
||||
declares.forEach { globalCode.appendln(it) }
|
||||
|
||||
exceptions = mapOf(
|
||||
Pair("KotlinNullPointerException", initializeString("Exception in thread main kotlin.KotlinNullPointerException")))
|
||||
|
||||
val funcAttributes = """attributes #0 = { nounwind "stack-protector-buffer-size"="8" "target-cpu"="cortex-m3" "target-features"="+hwdiv,+strict-align" }"""
|
||||
if (arm) {
|
||||
globalCode.appendln(funcAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
fun getNewVariable(type: LLVMType, pointer: Int = 0, kotlinName: String? = null, scope: LLVMScope = LLVMRegisterScope()): LLVMVariable {
|
||||
fun getNewVariable(type: LLVMType, pointer: Int = 0, kotlinName: String? = null, scope: LLVMScope = LLVMRegisterScope(), prefix: String = "var"): LLVMVariable {
|
||||
variableCount++
|
||||
return LLVMVariable("var$variableCount", type, kotlinName, scope, pointer)
|
||||
return LLVMVariable("${prefix}$variableCount", type, kotlinName, scope, pointer)
|
||||
}
|
||||
|
||||
fun getNewLabel(scope: LLVMScope = LLVMRegisterScope(), prefix: String): LLVMLabel {
|
||||
@@ -77,9 +81,15 @@ class LLVMBuilder(val arm: Boolean = false) {
|
||||
localCode.appendln("ret $type $value")
|
||||
}
|
||||
|
||||
private fun initializeString(string: String): LLVMVariable {
|
||||
val result = getNewVariable(LLVMStringType(string.length), pointer = 0, scope = LLVMVariableScope(), prefix = "exceptions.str.")
|
||||
addStringConstant(result, string)
|
||||
return result
|
||||
}
|
||||
|
||||
fun addStringConstant(variable: LLVMVariable, value: String) {
|
||||
val type = variable.type as LLVMStringType
|
||||
globalCode.appendln("$variable = private unnamed_addr constant ${type.fullType()} c\"$value\\00\", align 1")
|
||||
globalCode.appendln("$variable = private unnamed_addr constant ${type.fullType()} c\"${value.replace("\"", "\\\"")}\\00\", align 1")
|
||||
}
|
||||
|
||||
fun addGlobalInitialize(target: LLVMVariable, fields: ArrayList<LLVMVariable>, initializers: Map<LLVMVariable, String>, classType: LLVMType) {
|
||||
@@ -121,6 +131,17 @@ class LLVMBuilder(val arm: Boolean = false) {
|
||||
localCode.appendln(code)
|
||||
}
|
||||
|
||||
fun nullCheck(variable: LLVMVariable): LLVMVariable {
|
||||
val result = getNewVariable(LLVMBooleanType(), pointer = 0)
|
||||
|
||||
val loaded = getNewVariable(variable.type, pointer = variable.pointer - 1)
|
||||
loadVariable(loaded, variable)
|
||||
|
||||
val code = "$result = icmp eq ${loaded.getType()} null, $loaded"
|
||||
localCode.appendln(code)
|
||||
return result
|
||||
}
|
||||
|
||||
fun addComment(comment: String) {
|
||||
localCode.appendln("; " + comment)
|
||||
}
|
||||
@@ -218,6 +239,17 @@ class LLVMBuilder(val arm: Boolean = false) {
|
||||
return empty
|
||||
}
|
||||
|
||||
fun addExceptionCall(exceptionName: String) {
|
||||
val exception = exceptions[exceptionName]
|
||||
val printResult = getNewVariable(LLVMIntType(), pointer = 0)
|
||||
localCode.appendln("$printResult = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds (${(exception!!.type as LLVMStringType).fullType()}* $exception, i32 0, i32 0))")
|
||||
addFunctionCall(LLVMVariable("abort", LLVMVoidType(), scope = LLVMVariableScope()), emptyList())
|
||||
}
|
||||
|
||||
fun addFunctionCall(functionName: LLVMVariable, arguments: List<LLVMVariable>) {
|
||||
localCode.appendln("call ${functionName.type} $functionName(${arguments.joinToString { it -> "${it.type} $it" }})")
|
||||
}
|
||||
|
||||
fun memcpy(castedDst: LLVMVariable, castedSrc: LLVMVariable, size: Int, align: Int = 4, volatile: Boolean = false) {
|
||||
val code = "call void @llvm.memcpy.p0i8.p0i8.i64(i8* $castedDst, i8* $castedSrc, i64 $size, i32 $align, i1 $volatile)"
|
||||
localCode.appendln(code)
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
nullable_test_Int(1) == 1
|
||||
nullable_test_2_Int(4) == 5
|
||||
nullable_1_test_Int(1) == 1
|
||||
nullable_1_test_2_Int(4) == 5
|
||||
nullable_1_test_2_npe_operator_Int(23) == 578
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
class nullable_1_MyAwesomeClass(var i: Int)
|
||||
|
||||
class MyAwesomeClass(var i: Int)
|
||||
|
||||
fun nullable_test(i: Int): Int {
|
||||
var x: MyAwesomeClass? = null
|
||||
x = MyAwesomeClass(i)
|
||||
fun nullable_1_test(i: Int): Int {
|
||||
var x: nullable_1_MyAwesomeClass? = null
|
||||
x = nullable_1_MyAwesomeClass(i)
|
||||
return x.i
|
||||
}
|
||||
|
||||
fun nullable_test_2(i: Int): Int {
|
||||
var x: MyAwesomeClass? = MyAwesomeClass(i)
|
||||
fun nullable_1_test_2(i: Int): Int {
|
||||
var x: nullable_1_MyAwesomeClass? = nullable_1_MyAwesomeClass(i)
|
||||
x = null
|
||||
x = MyAwesomeClass(i)
|
||||
x = nullable_1_MyAwesomeClass(i)
|
||||
|
||||
return x.i + 1
|
||||
}
|
||||
|
||||
fun nullable_1_test_2_npe_operator(i: Int): Int {
|
||||
val x: nullable_1_MyAwesomeClass? = nullable_1_MyAwesomeClass(i)
|
||||
x!!
|
||||
return x.i + 555
|
||||
}
|
||||
Reference in New Issue
Block a user