From 4765a20ae8d949381309588e7ce50daf7aef28c4 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Thu, 19 Jan 2017 17:19:04 +0300 Subject: [PATCH 1/4] Add bitwise operations for Short and Byte This commit adds and, or and xor operations to Byte and Short types. It also implements inv operation for all integer types, shl for Long and Char + Int operation. --- runtime/src/main/cpp/Operator.cpp | 19 ++++++++++--- runtime/src/main/kotlin/kotlin/Primitives.kt | 28 +++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/runtime/src/main/cpp/Operator.cpp b/runtime/src/main/cpp/Operator.cpp index d3e9b49c479..0561ae40e0c 100644 --- a/runtime/src/main/cpp/Operator.cpp +++ b/runtime/src/main/cpp/Operator.cpp @@ -16,7 +16,7 @@ KInt Kotlin_Boolean_compareTo_Boolean(KBoolean a, KBoolean b) { if (a == b) //--- Char --------------------------------------------------------------------// KInt Kotlin_Char_compareTo_Char (KChar a, KChar b) { if (a == b) return 0; return (a < b) ? -1 : 1; } -KChar Kotlin_Char_plus_Char (KChar a, KInt b) { return a + b; } +KChar Kotlin_Char_plus_Int (KChar a, KInt b) { return a + b; } KInt Kotlin_Char_minus_Char (KChar a, KChar b) { return a - b; } KChar Kotlin_Char_minus_Int (KChar a, KInt b) { return a - b; } KChar Kotlin_Char_inc (KChar a ) { return a + 1; } @@ -79,6 +79,11 @@ KByte Kotlin_Byte_dec (KByte a ) { return --a; } KInt Kotlin_Byte_unaryPlus (KByte a ) { return +a; } KInt Kotlin_Byte_unaryMinus (KByte a ) { return -a; } +KByte Kotlin_Byte_or_Byte (KByte a, KByte b) { return a | b; } +KByte Kotlin_Byte_xor_Byte (KByte a, KByte b) { return a ^ b; } +KByte Kotlin_Byte_and_Byte (KByte a, KByte b) { return a & b; } +KByte Kotlin_Byte_inv (KByte a ) { return ~a; } + KByte Kotlin_Byte_toByte (KByte a ) { return a; } KChar Kotlin_Byte_toChar (KByte a ) { return a; } KShort Kotlin_Byte_toShort (KByte a ) { return a; } @@ -136,6 +141,11 @@ KShort Kotlin_Short_dec (KShort a ) { return --a; } KInt Kotlin_Short_unaryPlus (KShort a ) { return +a; } KInt Kotlin_Short_unaryMinus (KShort a ) { return -a; } +KShort Kotlin_Short_or_Short (KShort a, KShort b) { return a | b; } +KShort Kotlin_Short_xor_Short (KShort a, KShort b) { return a ^ b; } +KShort Kotlin_Short_and_Short (KShort a, KShort b) { return a & b; } +KShort Kotlin_Short_inv (KShort a ) { return ~a; } + KByte Kotlin_Short_toByte (KShort a ) { return a; } KChar Kotlin_Short_toChar (KShort a ) { return a; } KShort Kotlin_Short_toShort (KShort a ) { return a; } @@ -196,6 +206,7 @@ KInt Kotlin_Int_unaryMinus (KInt a ) { return -a; } KInt Kotlin_Int_or_Int (KInt a, KInt b) { return a | b; } KInt Kotlin_Int_xor_Int (KInt a, KInt b) { return a ^ b; } KInt Kotlin_Int_and_Int (KInt a, KInt b) { return a & b; } +KInt Kotlin_Int_inv (KInt a ) { return ~a; } KInt Kotlin_Int_shl_Int (KInt a, KInt b) { return a << b; } KInt Kotlin_Int_shr_Int (KInt a, KInt b) { return a >> b; } KInt Kotlin_Int_ushr_Int (KInt a, KInt b) { @@ -262,9 +273,9 @@ KLong Kotlin_Long_unaryMinus (KLong a ) { return -a; } KLong Kotlin_Long_xor_Long (KLong a, KLong b) { return a ^ b; } KLong Kotlin_Long_or_Long (KLong a, KLong b) { return a | b; } KLong Kotlin_Long_and_Long (KLong a, KLong b) { return a & b; } -KLong Kotlin_Long_shr_Int (KLong a, KInt b) { - return a >> b; -} +KLong Kotlin_Long_inv (KLong a ) { return ~a; } +KLong Kotlin_Long_shl_Int (KLong a, KInt b) { return a << b; } +KLong Kotlin_Long_shr_Int (KLong a, KInt b) { return a >> b; } KLong Kotlin_Long_ushr_Int (KLong a, KInt b) { return static_cast(a) >> b; } diff --git a/runtime/src/main/kotlin/kotlin/Primitives.kt b/runtime/src/main/kotlin/kotlin/Primitives.kt index 439470934ec..f4d11b7f18f 100644 --- a/runtime/src/main/kotlin/kotlin/Primitives.kt +++ b/runtime/src/main/kotlin/kotlin/Primitives.kt @@ -168,6 +168,19 @@ public final class Byte : Number(), Comparable { @SymbolName("Kotlin_Byte_unaryMinus") external public operator fun unaryMinus(): Int + /** Performs a bitwise AND operation between the two values. */ + @SymbolName("Kotlin_Byte_and_Byte") + external public infix fun and(other: Byte): Byte + /** Performs a bitwise OR operation between the two values. */ + @SymbolName("Kotlin_Byte_or_Byte") + external public infix fun or(other: Byte): Byte + /** Performs a bitwise XOR operation between the two values. */ + @SymbolName("Kotlin_Byte_xor_Byte") + external public infix fun xor(other: Byte): Byte + /** Inverts the bits in this value/ */ + @SymbolName("Kotlin_Byte_inv") + external public fun inv(): Byte + @SymbolName("Kotlin_Byte_toByte") external public override fun toByte(): Byte @SymbolName("Kotlin_Byte_toChar") @@ -377,6 +390,19 @@ public final class Short : Number(), Comparable { @SymbolName("Kotlin_Short_unaryMinus") external public operator fun unaryMinus(): Int + /** Performs a bitwise AND operation between the two values. */ + @SymbolName("Kotlin_Short_and_Short") + external public infix fun and(other: Short): Short + /** Performs a bitwise OR operation between the two values. */ + @SymbolName("Kotlin_Short_or_Short") + external public infix fun or(other: Short): Short + /** Performs a bitwise XOR operation between the two values. */ + @SymbolName("Kotlin_Short_xor_Short") + external public infix fun xor(other: Short): Short + /** Inverts the bits in this value/ */ + @SymbolName("Kotlin_Short_inv") + external public fun inv(): Short + /** Creates a range from this value to the specified [other] value. */ public operator fun rangeTo(other: Byte): IntRange { return IntRange(this.toInt(), other.toInt()) @@ -861,7 +887,7 @@ public final class Long : Number(), Comparable { } /** Shifts this value left by [bits]. */ - @SymbolName("Kotlin_Long_shl_Long") + @SymbolName("Kotlin_Long_shl_Int") external public infix fun shl(bitCount: Int): Long /** Shifts this value right by [bits], filling the leftmost bits with copies of the sign bit. */ @SymbolName("Kotlin_Long_shr_Int") From 7f911b8085b6f396d8744bceca96740971df27f1 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Fri, 20 Jan 2017 15:25:02 +0300 Subject: [PATCH 2/4] backend/tests: Ignore binaryOp/compareWithBoxed... tests --- .../codegen/blackbox/binaryOp/compareWithBoxedDouble.kt | 4 ++-- .../codegen/blackbox/binaryOp/compareWithBoxedLong.kt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedDouble.kt b/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedDouble.kt index 553773be648..a1040aac132 100644 --- a/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedDouble.kt +++ b/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedDouble.kt @@ -1,9 +1,9 @@ // IGNORE_BACKEND: JS // reason - multifile tests are not supported in JS tests // IGNORE_BACKEND: NATIVE -// reason - no java interop. Consider testing by another way +// reason - no java interop. -//FILE: Holder.kt +//FILE: Holder.java class Holder { public Double value; diff --git a/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedLong.kt b/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedLong.kt index 14a07fe1eb3..68c226c8295 100644 --- a/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedLong.kt +++ b/backend.native/tests/external/codegen/blackbox/binaryOp/compareWithBoxedLong.kt @@ -1,5 +1,8 @@ // IGNORE_BACKEND: JS // reason - multifile tests are not supported in JS tests +// IGNORE_BACKEND: NATIVE +// reason - no java interop. + //FILE: JavaClass.java class JavaClass { From 154c889517a5d2f627f3ab533c7b7b837ab8160c Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Fri, 20 Jan 2017 16:06:34 +0300 Subject: [PATCH 3/4] runtime: Add exception for division by zero --- runtime/src/main/cpp/Exceptions.h | 2 + runtime/src/main/cpp/Operator.cpp | 43 ++++++++++++------- .../kotlin/konan/internal/RuntimeUtils.kt | 5 +++ runtime/src/main/kotlin/kotlin/Exceptions.kt | 8 ++++ 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/runtime/src/main/cpp/Exceptions.h b/runtime/src/main/cpp/Exceptions.h index ce6b2b18fa5..a7fb95bcaae 100644 --- a/runtime/src/main/cpp/Exceptions.h +++ b/runtime/src/main/cpp/Exceptions.h @@ -22,6 +22,8 @@ void ThrowNullPointerException(); void ThrowArrayIndexOutOfBoundsException(); // Throws class cast exception. void ThrowClassCastException(); +// Throws arithmetic exception +void ThrowArithmeticException(); #ifdef __cplusplus } // extern "C" diff --git a/runtime/src/main/cpp/Operator.cpp b/runtime/src/main/cpp/Operator.cpp index 0561ae40e0c..6d751e06f09 100644 --- a/runtime/src/main/cpp/Operator.cpp +++ b/runtime/src/main/cpp/Operator.cpp @@ -1,7 +1,18 @@ #include #include "Natives.h" +#include "Exceptions.h" +namespace { + +inline template R div(Ta a, Tb b) { + if (__builtin_expect(b == 0, false)) { + ThrowArithmeticException(); + } + return a / b; +} + +} extern "C" { @@ -53,10 +64,10 @@ KLong Kotlin_Byte_minus_Long (KByte a, KLong b) { return a - b; } KFloat Kotlin_Byte_minus_Float (KByte a, KFloat b) { return a - b; } KDouble Kotlin_Byte_minus_Double (KByte a, KDouble b) { return a - b; } -KInt Kotlin_Byte_div_Byte (KByte a, KByte b) { return a / b; } -KInt Kotlin_Byte_div_Short (KByte a, KShort b) { return a / b; } -KInt Kotlin_Byte_div_Int (KByte a, KInt b) { return a / b; } -KLong Kotlin_Byte_div_Long (KByte a, KLong b) { return a / b; } +KInt Kotlin_Byte_div_Byte (KByte a, KByte b) { return div(a, b); } +KInt Kotlin_Byte_div_Short (KByte a, KShort b) { return div(a, b); } +KInt Kotlin_Byte_div_Int (KByte a, KInt b) { return div(a, b); } +KLong Kotlin_Byte_div_Long (KByte a, KLong b) { return div(a, b); } KFloat Kotlin_Byte_div_Float (KByte a, KFloat b) { return a / b; } KDouble Kotlin_Byte_div_Double (KByte a, KDouble b) { return a / b; } @@ -115,10 +126,10 @@ KLong Kotlin_Short_minus_Long (KShort a, KLong b) { return a - b; } KFloat Kotlin_Short_minus_Float (KShort a, KFloat b) { return a - b; } KDouble Kotlin_Short_minus_Double (KShort a, KDouble b) { return a - b; } -KInt Kotlin_Short_div_Byte (KShort a, KByte b) { return a / b; } -KInt Kotlin_Short_div_Short (KShort a, KShort b) { return a / b; } -KInt Kotlin_Short_div_Int (KShort a, KInt b) { return a / b; } -KLong Kotlin_Short_div_Long (KShort a, KLong b) { return a / b; } +KInt Kotlin_Short_div_Byte (KShort a, KByte b) { return div(a, b); } +KInt Kotlin_Short_div_Short (KShort a, KShort b) { return div(a, b); } +KInt Kotlin_Short_div_Int (KShort a, KInt b) { return div(a, b); } +KLong Kotlin_Short_div_Long (KShort a, KLong b) { return div(a, b); } KFloat Kotlin_Short_div_Float (KShort a, KFloat b) { return a / b; } KDouble Kotlin_Short_div_Double (KShort a, KDouble b) { return a / b; } @@ -177,10 +188,10 @@ KLong Kotlin_Int_minus_Long (KInt a, KLong b) { return a - b; } KFloat Kotlin_Int_minus_Float (KInt a, KFloat b) { return a - b; } KDouble Kotlin_Int_minus_Double (KInt a, KDouble b) { return a - b; } -KInt Kotlin_Int_div_Byte (KInt a, KByte b) { return a / b; } -KInt Kotlin_Int_div_Short (KInt a, KShort b) { return a / b; } -KInt Kotlin_Int_div_Int (KInt a, KInt b) { return a / b; } -KLong Kotlin_Int_div_Long (KInt a, KLong b) { return a / b; } +KInt Kotlin_Int_div_Byte (KInt a, KByte b) { return div(a, b); } +KInt Kotlin_Int_div_Short (KInt a, KShort b) { return div(a, b); } +KInt Kotlin_Int_div_Int (KInt a, KInt b) { return div(a, b); } +KLong Kotlin_Int_div_Long (KInt a, KLong b) { return div(a, b); } KFloat Kotlin_Int_div_Float (KInt a, KFloat b) { return a / b; } KDouble Kotlin_Int_div_Double (KInt a, KDouble b) { return a / b; } @@ -244,10 +255,10 @@ KLong Kotlin_Long_minus_Long (KLong a, KLong b) { return a - b; } KFloat Kotlin_Long_minus_Float (KLong a, KFloat b) { return a - b; } KDouble Kotlin_Long_minus_Double (KLong a, KDouble b) { return a - b; } -KLong Kotlin_Long_div_Byte (KLong a, KByte b) { return a / b; } -KLong Kotlin_Long_div_Short (KLong a, KShort b) { return a / b; } -KLong Kotlin_Long_div_Int (KLong a, KInt b) { return a / b; } -KLong Kotlin_Long_div_Long (KLong a, KLong b) { return a / b; } +KLong Kotlin_Long_div_Byte (KLong a, KByte b) { return div(a, b); } +KLong Kotlin_Long_div_Short (KLong a, KShort b) { return div(a, b); } +KLong Kotlin_Long_div_Int (KLong a, KInt b) { return div(a, b); } +KLong Kotlin_Long_div_Long (KLong a, KLong b) { return div(a, b); } KFloat Kotlin_Long_div_Float (KLong a, KFloat b) { return a / b; } KDouble Kotlin_Long_div_Double (KLong a, KDouble b) { return a / b; } diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index e0cf2cce35f..c7587ced3f0 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -15,6 +15,11 @@ internal fun ThrowClassCastException(): Nothing { throw ClassCastException() } +@ExportForCppRuntime +internal fun ThrowArithmeticException() : Nothing { + throw ArithmeticException() +} + internal fun ThrowNoWhenBranchMatchedException(): Nothing { throw NoWhenBranchMatchedException() } diff --git a/runtime/src/main/kotlin/kotlin/Exceptions.kt b/runtime/src/main/kotlin/kotlin/Exceptions.kt index 75f3c2c32a0..79efffba2f3 100644 --- a/runtime/src/main/kotlin/kotlin/Exceptions.kt +++ b/runtime/src/main/kotlin/kotlin/Exceptions.kt @@ -126,6 +126,14 @@ public class ClassCastException : RuntimeException { } } +public class ArithmeticException : RuntimeException { + constructor() : super() { + } + + constructor(s: String) : super(s) { + } +} + public class AssertionError : Error { constructor() { From a3b8d26c1ffdb405205bd7d937b201f57641770c Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Fri, 20 Jan 2017 18:06:14 +0300 Subject: [PATCH 4/4] runtime: Avoid UB in shift operations --- runtime/src/main/cpp/Operator.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/runtime/src/main/cpp/Operator.cpp b/runtime/src/main/cpp/Operator.cpp index 6d751e06f09..e88bbeb3bba 100644 --- a/runtime/src/main/cpp/Operator.cpp +++ b/runtime/src/main/cpp/Operator.cpp @@ -218,10 +218,13 @@ KInt Kotlin_Int_or_Int (KInt a, KInt b) { return a | b; } KInt Kotlin_Int_xor_Int (KInt a, KInt b) { return a ^ b; } KInt Kotlin_Int_and_Int (KInt a, KInt b) { return a & b; } KInt Kotlin_Int_inv (KInt a ) { return ~a; } -KInt Kotlin_Int_shl_Int (KInt a, KInt b) { return a << b; } -KInt Kotlin_Int_shr_Int (KInt a, KInt b) { return a >> b; } + +// According to C++11 the result is undefined if the second operator is < 0 or >= . +// We avoid it by using only the least significant bits +KInt Kotlin_Int_shl_Int (KInt a, KInt b) { return a << (b & 31); } +KInt Kotlin_Int_shr_Int (KInt a, KInt b) { return a >> (b & 31); } KInt Kotlin_Int_ushr_Int (KInt a, KInt b) { - return static_cast(a) >> b; + return static_cast(a) >> (b & 31); } KByte Kotlin_Int_toByte (KInt a ) { return a; } @@ -285,10 +288,13 @@ KLong Kotlin_Long_xor_Long (KLong a, KLong b) { return a ^ b; } KLong Kotlin_Long_or_Long (KLong a, KLong b) { return a | b; } KLong Kotlin_Long_and_Long (KLong a, KLong b) { return a & b; } KLong Kotlin_Long_inv (KLong a ) { return ~a; } -KLong Kotlin_Long_shl_Int (KLong a, KInt b) { return a << b; } -KLong Kotlin_Long_shr_Int (KLong a, KInt b) { return a >> b; } + +// According to C++11 the result is undefined if the second operator is < 0 or >= . +// We avoid it by using only the least significant bits +KLong Kotlin_Long_shl_Int (KLong a, KInt b) { return a << (b & 63); } +KLong Kotlin_Long_shr_Int (KLong a, KInt b) { return a >> (b & 63); } KLong Kotlin_Long_ushr_Int (KLong a, KInt b) { - return static_cast(a) >> b; + return static_cast(a) >> (b & 63); } KByte Kotlin_Long_toByte (KLong a ) { return a; }