470 lines
19 KiB
Rust
470 lines
19 KiB
Rust
// Web target differential program (run.sh): the same output from rustc's build of it (a
|
|
// wasm32-unknown-unknown cdylib run in node for wasm32; a host binary for wasm64, whose data
|
|
// layout is x86_64's) and from Relax running it as the web target in the trace host's mode
|
|
// (MAKEPAD_RUN_CHECK_TARGET=wasm{32,64}-unknown-unknown). Output goes through the page-side
|
|
// import `js_console_log` (a page has no stdout). Pointer-heavy on purpose: fat pointers,
|
|
// trait objects, vtables, statics holding pointers, Box/Rc/Vec/String, enums with pointer niches,
|
|
// fn pointers, closures, unaligned/packed fields, 64- and 128-bit math, atomics.
|
|
#![allow(dead_code)]
|
|
use std::cell::{Cell, RefCell};
|
|
use std::collections::{BTreeMap, VecDeque};
|
|
use std::fmt::Write;
|
|
use std::rc::Rc;
|
|
use std::sync::atomic::{AtomicPtr, AtomicU64, AtomicUsize, Ordering};
|
|
use std::sync::Mutex;
|
|
|
|
#[cfg(target_family = "wasm")]
|
|
extern "C" {
|
|
fn js_console_log(ptr: *const u8, len: usize);
|
|
}
|
|
#[cfg(target_family = "wasm")]
|
|
fn out(s: &str) {
|
|
unsafe { js_console_log(s.as_ptr(), s.len()) }
|
|
}
|
|
#[cfg(not(target_family = "wasm"))]
|
|
fn out(s: &str) {
|
|
use std::io::Write;
|
|
let _ = std::io::stderr().write_all(s.as_bytes());
|
|
}
|
|
|
|
macro_rules! say {
|
|
($($t:tt)*) => {{
|
|
let mut s = String::new();
|
|
let _ = writeln!(s, $($t)*);
|
|
out(&s);
|
|
}};
|
|
}
|
|
|
|
trait Shape {
|
|
fn area(&self) -> f64;
|
|
fn name(&self) -> String;
|
|
}
|
|
struct Sq(f64);
|
|
struct Circ { r: f64, label: &'static str }
|
|
impl Shape for Sq {
|
|
fn area(&self) -> f64 { self.0 * self.0 }
|
|
fn name(&self) -> String { format!("sq{}", self.0) }
|
|
}
|
|
impl Shape for Circ {
|
|
fn area(&self) -> f64 { 3.0 * self.r * self.r }
|
|
fn name(&self) -> String { format!("circ:{}", self.label) }
|
|
}
|
|
|
|
static NAMES: [&str; 3] = ["alpha", "beta", "gamma"];
|
|
static TABLE: &[(&str, fn(u32) -> u32)] = &[("dbl", dbl), ("sq", sqr), ("inc", |x| x + 1)];
|
|
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
|
static BIG: AtomicU64 = AtomicU64::new(1 << 40);
|
|
static mut CELL: [u32; 4] = [1, 2, 3, 4];
|
|
thread_local! {
|
|
static TL: Cell<u32> = const { Cell::new(7) };
|
|
}
|
|
|
|
fn dbl(x: u32) -> u32 { x * 2 }
|
|
fn sqr(x: u32) -> u32 { x * x }
|
|
|
|
struct OnceState<const N: usize>([u8; N]);
|
|
impl<const N: usize> Drop for OnceState<N> {
|
|
fn drop(&mut self) { ONCE_DROPS.fetch_add(1, Ordering::SeqCst); }
|
|
}
|
|
static ONCE_DROPS: AtomicUsize = AtomicUsize::new(0);
|
|
#[inline(never)]
|
|
fn take_once<const N: usize>(state: OnceState<N>) -> usize {
|
|
state.0.iter().map(|&x| x as usize).sum()
|
|
}
|
|
#[inline(never)]
|
|
fn call_once_box(f: Box<dyn FnOnce() -> usize>) -> usize { f() }
|
|
fn once_size<const N: usize>() -> usize {
|
|
let state = OnceState([3; N]);
|
|
call_once_box(Box::new(move || take_once(state)))
|
|
}
|
|
#[repr(align(256))]
|
|
struct OnceAligned(OnceState<24>);
|
|
#[inline(never)]
|
|
fn take_aligned(state: OnceAligned) -> usize { take_once(state.0) }
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
enum Node {
|
|
Leaf(i64),
|
|
Pair(Box<Node>, Box<Node>),
|
|
Name(String),
|
|
Empty,
|
|
}
|
|
|
|
fn eval(n: &Node) -> i64 {
|
|
match n {
|
|
Node::Leaf(v) => *v,
|
|
Node::Pair(a, b) => eval(a) * 3 + eval(b),
|
|
Node::Name(s) => s.len() as i64,
|
|
Node::Empty => -1,
|
|
}
|
|
}
|
|
|
|
#[repr(C, packed)]
|
|
struct Packed { a: u8, b: u32, c: u16 }
|
|
|
|
#[repr(C)]
|
|
struct Header { tag: u8, ptr: *const u8, len: usize, next: Option<&'static Header> }
|
|
unsafe impl Sync for Header {}
|
|
static H2: Header = Header { tag: 2, ptr: b"two".as_ptr(), len: 3, next: None };
|
|
static H1: Header = Header { tag: 1, ptr: b"one!".as_ptr(), len: 4, next: Some(&H2) };
|
|
|
|
fn walk(h: &Header) -> String {
|
|
let mut s = String::new();
|
|
let mut cur = Some(h);
|
|
while let Some(x) = cur {
|
|
let bytes = unsafe { std::slice::from_raw_parts(x.ptr, x.len) };
|
|
let _ = write!(s, "{}:{} ", x.tag, std::str::from_utf8(bytes).unwrap());
|
|
cur = x.next;
|
|
}
|
|
s
|
|
}
|
|
|
|
// C-unwind callbacks have the ScriptProtoPropsFn shape. Keep the table and its
|
|
// invocation out of line so these exercise the indirect C ABI, including taking
|
|
// the address of a by-value argument and returning an aggregate by value.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
struct Object8 { index: u32, generation: u16 }
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
struct Object12 { a: u32, b: u32, c: u32 }
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
struct Object24 { a: u64, b: u64, c: u64 }
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
struct ObjectMixed { a: f64, b: u32 }
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
struct ObjectFloat24 { a: f64, b: f64, c: f64 }
|
|
#[repr(C, packed)]
|
|
#[derive(Clone, Copy)]
|
|
struct ObjectPacked { a: u8, b: u32 }
|
|
#[repr(C)]
|
|
struct Vm { words: [u64; 96] }
|
|
#[repr(C)]
|
|
struct Props { sum: u64, calls: u32 }
|
|
|
|
trait Object: Copy {
|
|
fn sum(&self) -> u64;
|
|
fn changed(self) -> Self;
|
|
}
|
|
impl Object for Object8 {
|
|
fn sum(&self) -> u64 { self.index as u64 + self.generation as u64 }
|
|
fn changed(self) -> Self { Self { index: self.index + 7, generation: self.generation + 3 } }
|
|
}
|
|
impl Object for Object12 {
|
|
fn sum(&self) -> u64 { self.a as u64 + self.b as u64 + self.c as u64 }
|
|
fn changed(self) -> Self { Self { a: self.a + 1, b: self.b + 2, c: self.c + 3 } }
|
|
}
|
|
impl Object for Object24 {
|
|
fn sum(&self) -> u64 { self.a + self.b + self.c }
|
|
fn changed(self) -> Self { Self { a: self.a + 4, b: self.b + 5, c: self.c + 6 } }
|
|
}
|
|
impl Object for ObjectMixed {
|
|
fn sum(&self) -> u64 { self.a as u64 + self.b as u64 }
|
|
fn changed(self) -> Self { Self { a: self.a + 0.5, b: self.b + 9 } }
|
|
}
|
|
impl Object for ObjectFloat24 {
|
|
fn sum(&self) -> u64 { (self.a + self.b + self.c) as u64 }
|
|
fn changed(self) -> Self { Self { a: self.a + 0.25, b: self.b + 0.5, c: self.c + 0.75 } }
|
|
}
|
|
impl Object for ObjectPacked {
|
|
fn sum(&self) -> u64 { self.a as u64 + self.b as u64 }
|
|
fn changed(self) -> Self { Self { a: self.a + 1, b: self.b + 19 } }
|
|
}
|
|
struct Callbacks<T> {
|
|
props: extern "C-unwind" fn(&mut Vm, T, &mut Props),
|
|
result: extern "C-unwind" fn(&mut Vm, T, &mut Props) -> T,
|
|
}
|
|
#[inline(never)]
|
|
fn object_sum<T: Object>(object: &T) -> u64 { object.sum() }
|
|
#[inline(never)]
|
|
extern "C-unwind" fn proto_props_c<T: Object>(vm: &mut Vm, object: T, props: &mut Props) {
|
|
let n = object_sum(&object);
|
|
vm.words[83] += n;
|
|
props.sum += n * 3;
|
|
props.calls += 1;
|
|
}
|
|
#[inline(never)]
|
|
extern "C-unwind" fn proto_return_c<T: Object>(vm: &mut Vm, object: T, props: &mut Props) -> T {
|
|
proto_props_c(vm, object, props);
|
|
object.changed()
|
|
}
|
|
#[inline(never)]
|
|
fn call_callbacks<T: Object>(table: &Callbacks<T>, vm: &mut Vm, object: T, props: &mut Props) -> T {
|
|
(table.props)(vm, object, props);
|
|
(table.result)(vm, object, props)
|
|
}
|
|
struct SpillCallback {
|
|
f: extern "C-unwind" fn(&mut Vm, u64, u64, u64, u64, u64, Object12, &mut Props) -> Object12,
|
|
}
|
|
#[inline(never)]
|
|
extern "C-unwind" fn proto_spill_c(vm: &mut Vm, a: u64, b: u64, c: u64, d: u64, e: u64, object: Object12, props: &mut Props) -> Object12 {
|
|
// All six SysV integer registers are occupied before the 12-byte object.
|
|
vm.words[7] = a + b + c + d + e;
|
|
proto_return_c(vm, object, props)
|
|
}
|
|
#[inline(never)]
|
|
fn call_spill(table: &SpillCallback, vm: &mut Vm, object: Object12, props: &mut Props) -> Object12 {
|
|
(table.f)(vm, 101, 203, 307, 409, 503, object, props)
|
|
}
|
|
fn c_aggregates() {
|
|
let mut vm = Vm { words: [0; 96] };
|
|
let mut props = Props { sum: 0, calls: 0 };
|
|
let a = call_callbacks(&Callbacks { props: proto_props_c, result: proto_return_c }, &mut vm,
|
|
Object8 { index: 0x12345678, generation: 0x4321 }, &mut props);
|
|
say!("c8 {} {} {} {} {}", a.index, a.generation, vm.words[83], props.sum, props.calls);
|
|
let b = call_callbacks(&Callbacks { props: proto_props_c, result: proto_return_c }, &mut vm,
|
|
Object12 { a: 11, b: 23, c: 47 }, &mut props);
|
|
say!("c12 {} {} {} {} {} {}", b.a, b.b, b.c, vm.words[83], props.sum, props.calls);
|
|
let c = call_callbacks(&Callbacks { props: proto_props_c, result: proto_return_c }, &mut vm,
|
|
Object24 { a: 1 << 40, b: 101, c: 203 }, &mut props);
|
|
say!("c24 {} {} {} {} {} {}", c.a, c.b, c.c, vm.words[83], props.sum, props.calls);
|
|
let m = call_callbacks(&Callbacks { props: proto_props_c, result: proto_return_c }, &mut vm,
|
|
ObjectMixed { a: 17.25, b: 31 }, &mut props);
|
|
say!("c_mixed {} {} {} {} {}", m.a, m.b, vm.words[83], props.sum, props.calls);
|
|
let f = call_callbacks(&Callbacks { props: proto_props_c, result: proto_return_c }, &mut vm,
|
|
ObjectFloat24 { a: 1.5, b: 2.25, c: 3.75 }, &mut props);
|
|
say!("c_float24 {} {} {} {} {} {}", f.a, f.b, f.c, vm.words[83], props.sum, props.calls);
|
|
let p = call_callbacks(&Callbacks { props: proto_props_c, result: proto_return_c }, &mut vm,
|
|
ObjectPacked { a: 3, b: 98765 }, &mut props);
|
|
let pb = p.b;
|
|
say!("c_packed {} {} {} {} {}", p.a, pb, vm.words[83], props.sum, props.calls);
|
|
let s = call_spill(&SpillCallback { f: proto_spill_c }, &mut vm,
|
|
Object12 { a: 17, b: 37, c: 73 }, &mut props);
|
|
say!("c_spill {} {} {} {} {} {} {}", s.a, s.b, s.c, vm.words[7], vm.words[83], props.sum, props.calls);
|
|
}
|
|
|
|
// Coverage probes execute between pointer uses in these small callees and in
|
|
// the string/lock implementations. Keep them hot and cross call boundaries.
|
|
#[inline(never)]
|
|
fn coverage_chars(text: &str, total: &Mutex<u64>, count: &AtomicUsize) -> u64 {
|
|
let mut sum = 0u64;
|
|
for (offset, ch) in text.char_indices() {
|
|
sum = sum.wrapping_add(ch as u64 + offset as u64);
|
|
count.fetch_or(1 << (offset % 16), Ordering::Relaxed);
|
|
}
|
|
*total.lock().unwrap() += sum;
|
|
sum + count.load(Ordering::Acquire) as u64
|
|
}
|
|
|
|
fn coverage_hot() {
|
|
let total = Mutex::new(0u64);
|
|
let count = AtomicUsize::new(0);
|
|
let texts = [String::from("ASCII"), String::from("Grüße ✓ 🦀"), String::new()];
|
|
let mut sum = 0u64;
|
|
for i in 0..4096 {
|
|
sum += coverage_chars(&texts[i % texts.len()], &total, &count);
|
|
}
|
|
say!("coverage {} {} {}", sum, *total.lock().unwrap(), count.load(Ordering::Relaxed));
|
|
}
|
|
|
|
fn body() {
|
|
numeric_casts();
|
|
web_runtime_cases();
|
|
coverage_hot();
|
|
c_aggregates();
|
|
say!("usize {} ptr {} fat {} dyn {} opt_box {} vec {} string {}",
|
|
std::mem::size_of::<usize>(), std::mem::size_of::<*const u8>(), std::mem::size_of::<&[u8]>(),
|
|
std::mem::size_of::<&dyn Shape>(), std::mem::size_of::<Option<Box<u8>>>(), std::mem::size_of::<Vec<u8>>(),
|
|
std::mem::size_of::<String>());
|
|
// trait objects through vtables, in a Vec<Box<dyn>>
|
|
let shapes: Vec<Box<dyn Shape>> = vec![Box::new(Sq(2.0)), Box::new(Circ { r: 1.5, label: "c1" }), Box::new(Sq(0.5))];
|
|
let total: f64 = shapes.iter().map(|s| s.area()).sum();
|
|
let names: Vec<String> = shapes.iter().map(|s| s.name()).collect();
|
|
say!("shapes {:.3} {:?}", total, names);
|
|
// statics of pointers: &str arrays, fn pointer tables, a linked list of statics
|
|
for (i, n) in NAMES.iter().enumerate() {
|
|
say!("name {} {} {}", i, n, n.len());
|
|
}
|
|
for (n, f) in TABLE {
|
|
say!("fn {} {}", n, f(7));
|
|
}
|
|
say!("walk {}", walk(&H1));
|
|
// recursive boxed enum
|
|
let tree = Node::Pair(Box::new(Node::Pair(Box::new(Node::Leaf(4)), Box::new(Node::Name("hello".to_string())))), Box::new(Node::Leaf(-9)));
|
|
say!("tree {} {:?}", eval(&tree), tree.clone() == tree);
|
|
say!("empty {} {:?}", eval(&Node::Empty), Node::Name("x".into()));
|
|
// Rc / RefCell graph
|
|
let shared = Rc::new(RefCell::new(Vec::<u32>::new()));
|
|
let handles: Vec<Rc<RefCell<Vec<u32>>>> = (0..4).map(|_| shared.clone()).collect();
|
|
for (i, h) in handles.iter().enumerate() {
|
|
h.borrow_mut().push(i as u32 * 10);
|
|
}
|
|
say!("rc {} {:?}", Rc::strong_count(&shared), shared.borrow());
|
|
// closures capturing by reference and by move, boxed FnMut
|
|
let mut acc = 0u64;
|
|
let mut add = |x: u64| acc += x;
|
|
for i in 1..=10 {
|
|
add(i * i);
|
|
}
|
|
let k = 5u64;
|
|
let mut fs: Vec<Box<dyn FnMut(u64) -> u64>> = Vec::new();
|
|
fs.push(Box::new(move |x| x * k));
|
|
let mut st = 0u64;
|
|
fs.push(Box::new(move |x| { st += x; st }));
|
|
let r: Vec<u64> = (0..3).map(|i| { let a = fs[0](i); fs[1](a) }).collect();
|
|
say!("closures {} {:?}", acc, r);
|
|
// Box<dyn FnOnce> moves its environment out before freeing the allocation. Its
|
|
// compiler-called free helper must retain the vtable on both wasm pointer widths.
|
|
let state = OnceAligned(OnceState([7; 24]));
|
|
let aligned = call_once_box(Box::new(move || take_aligned(state)));
|
|
say!("box once {} {} {} {} {} drops {}", once_size::<0>(), once_size::<1>(),
|
|
once_size::<24>(), once_size::<4096>(), aligned, ONCE_DROPS.load(Ordering::SeqCst));
|
|
// strings, chars, formatting
|
|
let mut s = String::from("Grüße, wasm32!");
|
|
s.push_str(" ✓");
|
|
let up: String = s.chars().rev().filter(|c| !c.is_whitespace()).collect();
|
|
say!("str {} {} {} {:?}", s.len(), s.chars().count(), up, s.find("wasm"));
|
|
say!("fmt {:>8}|{:<6}|{:^7}|{:08.3}|{:x}|{:#b}|{:e}", "r", "l", "c", -3.14159, 0xdead_beefu32, 5u8, 1234.5f64);
|
|
// collections
|
|
let mut m = BTreeMap::new();
|
|
for w in "the quick brown fox jumps over the lazy dog the end".split(' ') {
|
|
*m.entry(w).or_insert(0usize) += 1;
|
|
}
|
|
say!("btree {:?}", m);
|
|
let mut q: VecDeque<i32> = (1..=5).collect();
|
|
q.rotate_left(2);
|
|
q.push_front(-1);
|
|
say!("deque {:?}", q);
|
|
let mut v: Vec<i64> = (0..40).map(|i| (i * 7919) % 101 - 50).collect();
|
|
v.sort_unstable();
|
|
v.dedup();
|
|
say!("sorted {} {:?} {:?}", v.len(), &v[..5], v.binary_search(&0));
|
|
// 64/128-bit integer math, wrapping, checked
|
|
let a: u64 = 0xffff_ffff_0000_0001;
|
|
let b: u128 = (a as u128) * (a as u128);
|
|
say!("int {} {} {:?} {:?} {}", a.wrapping_mul(3), b, a.checked_add(u64::MAX), 7usize.checked_sub(9), (-7i64).rem_euclid(3));
|
|
say!("usize {} {} {}", usize::MAX, isize::MIN, (usize::MAX as u64).count_ones());
|
|
// floats
|
|
let xs = [0.1f64, 2.5, -3.75, 1e10, 1.0 / 3.0];
|
|
let prod: f64 = xs.iter().product();
|
|
say!("float {} {:.6} {} {}", prod, xs.iter().map(|x| x.abs().sqrt()).sum::<f64>(), (2.5f32).powi(3), f64::EPSILON);
|
|
// packed and repr(C) fields
|
|
let p = Packed { a: 1, b: 0x0102_0304, c: 0x0506 };
|
|
let pb = p.b;
|
|
let pc = p.c;
|
|
say!("packed {} {} {} {}", std::mem::size_of::<Packed>(), p.a, pb, pc);
|
|
// atomics on pointers and wide integers
|
|
let x = Box::into_raw(Box::new(41u32));
|
|
let ap = AtomicPtr::new(std::ptr::null_mut());
|
|
ap.store(x, Ordering::SeqCst);
|
|
unsafe { *ap.load(Ordering::SeqCst) += 1 };
|
|
COUNT.fetch_add(3, Ordering::Relaxed);
|
|
BIG.fetch_add(5, Ordering::Relaxed);
|
|
say!("atomic {} {} {}", unsafe { *x }, COUNT.load(Ordering::Relaxed), BIG.load(Ordering::Relaxed));
|
|
unsafe { drop(Box::from_raw(x)) };
|
|
// raw pointer arithmetic, pointer <-> integer casts
|
|
let arr = [10u16, 20, 30, 40];
|
|
let p0 = arr.as_ptr();
|
|
let p3 = unsafe { p0.add(3) };
|
|
let diff = (p3 as usize - p0 as usize) / std::mem::size_of::<u16>();
|
|
let back = (p0 as usize + 2 * std::mem::size_of::<u16>()) as *const u16;
|
|
say!("ptr {} {} {}", diff, unsafe { *back }, unsafe { p3.offset_from(p0) });
|
|
unsafe {
|
|
let c = &mut *std::ptr::addr_of_mut!(CELL);
|
|
c[2] += 40;
|
|
say!("static mut {:?}", c);
|
|
}
|
|
TL.with(|t| t.set(t.get() * 6));
|
|
say!("tls {}", TL.with(|t| t.get()));
|
|
// Option<&T> / Option<Box<T>> niches
|
|
let some: Option<&u32> = arr.iter().map(|_| &7u32).next();
|
|
let none: Option<Box<u64>> = None;
|
|
say!("niche {:?} {:?} {}", some, none, std::mem::size_of_val(&none));
|
|
say!("done");
|
|
}
|
|
|
|
/// rustc's wasm32 build: node calls this (run.sh)
|
|
#[no_mangle]
|
|
pub extern "C" fn run() {
|
|
body()
|
|
}
|
|
|
|
fn main() {
|
|
body()
|
|
}
|
|
|
|
fn web_runtime_cases() {
|
|
use std::hint::black_box;
|
|
let unsigned = [0u128, 1, u64::MAX as u128, 1u128 << 64, 1u128 << 127, u128::MAX];
|
|
let mut sum = 0u128;
|
|
for a in unsigned {
|
|
for b in unsigned {
|
|
if b != 0 {
|
|
let a = black_box(a);
|
|
let b = black_box(b);
|
|
sum = sum.wrapping_add(a / b).wrapping_add(a % b);
|
|
}
|
|
}
|
|
}
|
|
let signed = [i128::MIN, -123456789012345678901i128, -1, 0, 1, i128::MAX];
|
|
for a in signed {
|
|
for b in signed {
|
|
if b != 0 && !(a == i128::MIN && b == -1) {
|
|
let a = black_box(a);
|
|
let b = black_box(b);
|
|
sum = sum.wrapping_add((a / b) as u128).wrapping_add((a % b) as u128);
|
|
}
|
|
}
|
|
}
|
|
let text = String::from("a runtime string longer than sixteen bytes");
|
|
let same = text.clone();
|
|
let mut different = text.clone();
|
|
different.push('!');
|
|
assert!(black_box(text.as_str()) == black_box(same.as_str()));
|
|
assert!(black_box(text.as_str()) != black_box(different.as_str()));
|
|
different.pop();
|
|
different.replace_range(20..21, "X");
|
|
assert!(black_box(text.as_str()) != black_box(different.as_str()));
|
|
unsafe {
|
|
use std::alloc::{GlobalAlloc, Layout, System};
|
|
let layout = Layout::from_size_align(131072, 256).unwrap();
|
|
let p = System.alloc_zeroed(layout);
|
|
assert!(!p.is_null());
|
|
assert_eq!(p as usize % 256, 0);
|
|
for i in 0..layout.size() { assert_eq!(*p.add(i), 0); *p.add(i) = (i % 251) as u8; }
|
|
let q = System.realloc(p, layout, 393216);
|
|
assert!(!q.is_null());
|
|
for i in 0..layout.size() { assert_eq!(*q.add(i), (i % 251) as u8); }
|
|
let large = Layout::from_size_align(393216, 256).unwrap();
|
|
let p = System.realloc(q, large, 65536);
|
|
assert!(!p.is_null());
|
|
for i in 0..65536 { assert_eq!(*p.add(i), (i % 251) as u8); }
|
|
System.dealloc(p, Layout::from_size_align(65536, 256).unwrap());
|
|
}
|
|
say!("web_runtime {sum}");
|
|
}
|
|
|
|
fn numeric_casts() {
|
|
use std::hint::black_box;
|
|
// Both signednesses and precisions, including halfway values on either side
|
|
// of an even significand and carries into the next exponent (f32 infinity).
|
|
for value in [0u128, 1, (1u128 << 24) + 1, (1u128 << 24) + 3,
|
|
(1u128 << 53) + 1, (1u128 << 53) + 3, 1u128 << 63,
|
|
u64::MAX as u128, 1u128 << 64, (1u128 << 100) + (1u128 << 76) + 1,
|
|
(1u128 << 100) + (1u128 << 47) + 1, 1u128 << 127, u128::MAX] {
|
|
let x = black_box(value);
|
|
let y = black_box(value as i128);
|
|
say!("wide_cast {} {} {} {} {}", x, (x as f32).to_bits(), (x as f64).to_bits(),
|
|
(y as f32).to_bits(), (y as f64).to_bits());
|
|
let n = black_box(y.wrapping_neg());
|
|
say!("wide_negative {} {} {}", n, (n as f32).to_bits(), (n as f64).to_bits());
|
|
let x = black_box(value as u64);
|
|
let y = black_box(value as i64);
|
|
say!("int_cast {} {} {} {} {}", x, (x as f32).to_bits(), (x as f64).to_bits(),
|
|
(y as f32).to_bits(), (y as f64).to_bits());
|
|
}
|
|
for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY, -0.0, 0.0, -1.0, 1.5,
|
|
-129.0, 256.0, -32769.0, 65536.0, -2147483649.0, 4294967296.0,
|
|
9007199254740992.0, 9223372036854775808.0, 18446744073709551616.0,
|
|
170141183460469231731687303715884105728.0, 340282366920938463463374607431768211456.0] {
|
|
let x = black_box(value);
|
|
let y = black_box(value as f32);
|
|
say!("f64_cast {} {} {} {} {} {} {} {} {} {}", x as i8, x as u8, x as i16,
|
|
x as u16, x as i32, x as u32, x as i64, x as u64, x as i128, x as u128);
|
|
say!("f32_cast {} {} {} {} {} {} {} {} {} {}", y as i8, y as u8, y as i16,
|
|
y as u16, y as i32, y as u32, y as i64, y as u64, y as i128, y as u128);
|
|
}
|
|
}
|