diff --git a/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala b/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala index 70ccfcf5e..4356dc342 100644 --- a/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala +++ b/effekt/shared/src/main/scala/effekt/core/vm/Builtin.scala @@ -129,8 +129,30 @@ lazy val integers: Builtins = Map( builtin("effekt::infixMul(Int, Int)") { case As.Int(x) :: As.Int(y) :: Nil => Value.Int(x * y) }, + builtin("effekt::rem(Int, Int)") { + case As.Int(x) :: As.Int(y) :: Nil => Value.Int(x - y * (x / y)) + }, + builtin("effekt::quot(Int, Int)") { + case As.Int(x) :: As.Int(y) :: Nil => Value.Int(x / y) + }, + builtin("effekt::div(Int, Int)") { + case As.Int(x) :: As.Int(y) :: Nil => + val q = x / y + val r = x - y * (x / y) + if (r < 0) { + Value.Int(q - y.sign) + } else { + Value.Int(q) + } + }, builtin("effekt::mod(Int, Int)") { - case As.Int(x) :: As.Int(y) :: Nil => Value.Int(x % y) + case As.Int(x) :: As.Int(y) :: Nil => + val r = x - y * (x / y) + if (r < 0) { + Value.Int(r + y.abs) + } else { + Value.Int(r) + } }, builtin("effekt::infixDiv(Int, Int)") { case As.Int(x) :: As.Int(y) :: Nil => Value.Int(x / y) @@ -150,6 +172,9 @@ lazy val integers: Builtins = Map( builtin("effekt::bitwiseXor(Int, Int)") { case As.Int(x) :: As.Int(y) :: Nil => Value.Int(x ^ y) }, + builtin("effekt::bitwiseMsb(Int)") { + case As.Int(x) :: Nil => Value.Int(x >> 63) + }, // Comparison // ---------- diff --git a/examples/stdlib/mod.check b/examples/stdlib/mod.check new file mode 100644 index 000000000..16c3c158f --- /dev/null +++ b/examples/stdlib/mod.check @@ -0,0 +1,10 @@ +8 `divmod` 3 = 2, 2 +8 `divmod` -3 = -2, 2 +-8 `divmod` 3 = -3, 1 +-8 `divmod` -3 = 3, 1 + +8 `quotrem` 3 = 2, 2 +8 `quotrem` -3 = -2, 2 +-8 `quotrem` 3 = -2, -2 +-8 `quotrem` -3 = 2, -2 + diff --git a/examples/stdlib/mod.effekt b/examples/stdlib/mod.effekt new file mode 100644 index 000000000..268659f39 --- /dev/null +++ b/examples/stdlib/mod.effekt @@ -0,0 +1,15 @@ +def main() = { + val ops = [ + (box { (x: Int, y: Int) => (x.div(y), x.mod(y), x == y * x.div(y) + x.mod(y)) }, "divmod"), + (box { (x: Int, y: Int) => (x.quot(y), x.rem(y), x == y * x.quot(y) + x.rem(y)) }, "quotrem"), + ] + ops.foreach { case (op, name) => + [8, 8.neg].foreach { dividend => + [3, 3.neg].foreach { divisor => + val (q, r, assertion) = op(dividend, divisor) + println(s"${dividend.show} `${name}` ${divisor.show} = ${q.show}, ${r.show}") + } + } + println("") + } +} diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index 05a2883d7..a47fe2f9b 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -319,6 +319,12 @@ extern def infixNeq(x: Bool, y: Bool) at {}: Bool = // Math ops // ======== + +// Numeric representations in the backends: +// LLVM: int64_t (64-bit integer) +// JS: 64-bit floating-point (64-bit IEEE 754) +// Chez: 64-bit integer + extern def infixAdd(x: Int, y: Int) at {}: Int = js "(${x} + ${y})" chez "(+ ${x} ${y})" @@ -332,8 +338,8 @@ extern def infixMul(x: Int, y: Int) at {}: Int = vm "effekt::infixMul(Int, Int)" extern def infixDiv(x: Int, y: Int) at {}: Int = - js "Math.floor(${x} / ${y})" - chez "(floor (/ ${x} ${y}))" + js "Math.trunc(${x} / ${y})" + chez "(truncate (/ ${x} ${y}))" llvm "%z = sdiv %Int ${x}, ${y} ret %Int %z" vm "effekt::infixDiv(Int, Int)" @@ -343,11 +349,31 @@ extern def infixSub(x: Int, y: Int) at {}: Int = llvm "%z = sub %Int ${x}, ${y} ret %Int %z" vm "effekt::infixSub(Int, Int)" -extern def mod(x: Int, y: Int) at {}: Int = - js "(${x} % ${y})" - chez "(modulo ${x} ${y})" - llvm "%z = srem %Int ${x}, ${y} ret %Int %z" - vm "effekt::mod(Int, Int)" +/// Computes the quotient based on truncated-division +def quot(x: Int, y: Int): Int = + infixDiv(x, y) + +/// Computes the remainder based on truncated-division + extern def rem(x: Int, y: Int) at {}: Int = + llvm "%z = srem %Int ${x}, ${y} ret %Int %z" + default { x - y * (x / y) } + +/// Computes the result of Euclidean division +def div(x: Int, y: Int): Int = { + val q = x / y + val r = x.rem(y) + val rNeg = r.bitwiseMsb + val ySign = y.bitwiseMsb.bitwiseOr(1) + q - rNeg.bitwiseAnd(ySign) +} + +/// Computes the result of Euclidean modulo +def mod(x: Int, y: Int): Int = { + val r = x.rem(y) + val rNeg = r.bitwiseMsb + val absY = y.bitwiseXor(y.bitwiseMsb) - y.bitwiseMsb + r + rNeg.bitwiseAnd(absY) +} extern def infixAdd(x: Double, y: Double) at {}: Double = js "(${x} + ${y})" @@ -677,6 +703,11 @@ extern def bitwiseXor(x: Int, y: Int) at {}: Int = llvm "%z = xor %Int ${x}, ${y} ret %Int %z" vm "effekt::bitwiseXor(Int, Int)" +extern def bitwiseMsb(x: Int) at {}: Int = + js "${x} >> 31" + chez "(bitwise-arithmetic-shift-right ${x} 63)" + llvm "%z = ashr %Int ${x}, 63 ret %Int %z" + vm "effekt::bitwiseMsb(Int)" // Byte operations // ===============