Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions bend2/comp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ const OPERATIONS: Record<string, Intr> = Object.setPrototypeOf({
u32_mul: { C: "U32_BIN($0, *, $1)", JS: "(Math.imul($0, $1) >>> 0)" },
// C only: a JS file runs Base's U32.pow, as intr_of skips an op with no JS
u32_pow: { C: "u32_pow($0, $1)" },
u32_log2: { C: "(31 - CLZ((u32)$0 | 1))" },
u32_div: {
C: "((u32)($1) == 0 ? 0 : (u64)U32_QUO((u32)($0), (u32)($1)))",
JS: "($1 === 0 ? 0 : ($0 / $1) >>> 0)",
Expand Down Expand Up @@ -233,6 +234,8 @@ const OPERATIONS: Record<string, Intr> = Object.setPrototypeOf({
...tpl_ops("nat_", "add:$1 double:$0", "nat_chk(e, $0 + $o)",
"nat_chk($0 + $o)"),
nat_mul: { C: "nat_mul(e, $0, $1)", JS: "nat_chk($0 * $1)" },
nat_pow: { C: "nat_pow(e, $0, $1)" },
map_msb_u: { C: "map_msb_u($0, $1)" },
nat_cmp: { C: "(($0 > $1) + ($0 >= $1))", JS: "cmp_new($0, $1)" },
...tpl_ops("nat_", "sub", "($0 < $1 ? 0 : $0 - $1)"),
...tpl_ops("nat_", "is_lt:<", "($0 $o $1)"),
Expand Down Expand Up @@ -384,6 +387,22 @@ INLINE u64 nat_mul(Env e, u64 a, u64 b) {
return nat_chk(e, b != 0 && a > NAT_IMM / b ? NAT_IMM + 1 : a * b);
}

INLINE u64 nat_pow(Env e, u64 a, u64 n) {
u64 r = 1;
while (n > 0) {
if (n & 1) r = nat_mul(e, r, a);
n >>= 1;
if (n) a = nat_mul(e, a, a);
}
return r;
}

INLINE u64 map_msb_u(u64 n, u32 x) {
if (n == 0 || x == 0) return 0;
u64 width = 32 - CLZ(x);
return n < width ? n : width;
}

#if DEVICE

#define f32_show(e, x) (err_post(e.mem, ERR_FIDS), 0)
Expand Down
8 changes: 4 additions & 4 deletions guide/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -638,10 +638,10 @@ A `Nat` literal past `256n` is `U32.to_nat(n)` underneath, up to `4294967295n`.

## Under the Hood

Bend's compiler emits one C file, and that file is both the CPU program and the
GPU kernel. clang compiles it for the CPU. Metal (on Apple) or CUDA (on NVIDIA)
compiles the same file for the GPU. So a `!` call runs the same code on
whichever chip it lands on.
Bend emits one C file for CPU and GPU: clang builds the CPU program,
Metal the Apple kernel, CUDA the NVIDIA kernel. Native scalar paths keep
Base's definitions: checked repeated squaring for `Nat.pow`, count-leading-zero
queries for `U32.log2` and fuel-bounded `Map.msb.u`.

A term is one 64-bit word: small values are stored inline, everything else is a
pointer into a single heap shared by every core and by the GPU. There is no
Expand Down
8 changes: 8 additions & 0 deletions tests/run/nat_power_overflow.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Repeated squaring must check the 48-bit Nat wall, not just u64 overflow.
import Base

def main() -> IO(Unit):
IO.print(Nat.show(Nat.pow(2n, 48n)))

#|bend: a Nat past the largest immediate 2^48-1
#|exit 1
9 changes: 9 additions & 0 deletions tests/run/nat_power_wide_overflow.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Squaring the largest immediate must fail before the product wraps u64.
import Base

def main() -> IO(Unit):
largest = Nat.add(Nat.mul(16777215n, 16777216n), 16777215n)
IO.print(Nat.show(Nat.pow(largest, 2n)))

#|bend: a Nat past the largest immediate 2^48-1
#|exit 1
53 changes: 53 additions & 0 deletions tests/run/native_numeric_boundaries.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Native scalar paths agree with Base at zero, bit-width and Nat boundaries.
import Base

def main() -> IO(Unit):
+largest = Nat.add(Nat.mul(16777215n, 16777216n), 16777215n)
do IO<Unit>:
IO.print(Nat.show(Nat.pow(0n, 0n)))
IO.print(Nat.show(Nat.pow(0n, 7n)))
IO.print(Nat.show(Nat.pow(3n, 7n)))
IO.print(Nat.show(Nat.pow(1n, 257n)))
IO.print(Nat.show(Nat.pow(2n, 47n)))
IO.print(Nat.show(Nat.pow(largest, 0n)))
IO.print(Nat.show(Nat.pow(largest, 1n)))
IO.print(Nat.show(U32.log2(0)))
IO.print(Nat.show(U32.log2(1)))
IO.print(Nat.show(U32.log2(2)))
IO.print(Nat.show(U32.log2(3)))
IO.print(Nat.show(U32.log2(4)))
IO.print(Nat.show(U32.log2(2147483647)))
IO.print(Nat.show(U32.log2(2147483648)))
IO.print(Nat.show(U32.log2(4294967295)))
IO.print(Nat.show(Map.msb.u(0n, 4294967295)))
IO.print(Nat.show(Map.msb.u(1n, 0)))
IO.print(Nat.show(Map.msb.u(1n, 4294967295)))
IO.print(Nat.show(Map.msb.u(31n, 4294967295)))
IO.print(Nat.show(Map.msb.u(32n, 4294967295)))
IO.print(Nat.show(Map.msb.u(99n, 4294967295)))
IO.print(Nat.show(Map.msb.u(32n, 1)))
IO.print(Nat.show(Map.msb.u(99n, 2147483648)))

#|1
#|0
#|2187
#|1
#|140737488355328
#|1
#|281474976710655
#|0
#|0
#|1
#|1
#|2
#|30
#|31
#|31
#|0
#|0
#|1
#|31
#|32
#|32
#|1
#|32