From d86f91789bf4fc6eb5c15e28b7538d0d3421fa47 Mon Sep 17 00:00:00 2001 From: Dennis Ranke Date: Sat, 6 Nov 2021 19:46:16 +0100 Subject: [PATCH] move all imports to module "env", some tweaks to rust example --- examples/rust/readme.txt | 6 +-- examples/rust/tunnel.rs | 40 +++++++++++----- examples/rust/tunnel.uw8 | Bin 137 -> 137 bytes site/.gitignore | 1 + site/content/_index.md | 23 +++++----- site/static/v0.1pre1/index.html | 2 +- uw8-tool/src/base_module.rs | 78 +++++++++++++++++++++----------- web/src/main.js | 62 ++++++++++++------------- 8 files changed, 126 insertions(+), 86 deletions(-) create mode 100644 site/.gitignore diff --git a/examples/rust/readme.txt b/examples/rust/readme.txt index c3ed732..450e4dd 100644 --- a/examples/rust/readme.txt +++ b/examples/rust/readme.txt @@ -5,8 +5,8 @@ A nightly rust compiler is needed for the unstable sqrtf32 intrinsic. Simply compiling with rustc as shown in build.sh results in a -339 byte tunnel.wasm. Using wasm-opt this can be reduced to -244 bytes. +342 byte tunnel.wasm. Using wasm-opt this can be reduced to +243 bytes. When you disassemble this wasm file using wasm2wat you can see these globals and exports: @@ -21,5 +21,5 @@ values that are not simple scalars (i32, f32, etc.). Since our code doesn't actually use any of that, we can just delete them in a text editor and assemble the code again with wat2wasm. -This gives us a 200 byte wasm file. Running this through +This gives us a 199 byte wasm file. Running this through uw8-tool pack brings us to the final size of 137 bytes. \ No newline at end of file diff --git a/examples/rust/tunnel.rs b/examples/rust/tunnel.rs index a030f4f..55acf9d 100644 --- a/examples/rust/tunnel.rs +++ b/examples/rust/tunnel.rs @@ -1,25 +1,43 @@ #![no_std] #![feature(core_intrinsics)] -#[link(wasm_import_module = "math")] -extern "C" { - fn atan2(x: f32, y: f32) -> f32; +mod env { + // "env" is the default module for imports, but it is still needed here + // since there is a compiler builtin of the same name which is used + // if we don't make it clear that this is a module import. + #[link(wasm_import_module = "env")] + extern "C" { + pub fn atan2(x: f32, y: f32) -> f32; + } +} + +fn atan2(x: f32, y: f32) -> f32 { + unsafe { env::atan2(x, y) } } fn sqrt(v: f32) -> f32 { unsafe { core::intrinsics::sqrtf32(v) } } +fn ftoi(v: f32) -> i32 { + // The compiler is allowed to do bad things to our code if this + // ever results in a value that doesn't fit in an i32. + // (the joy of undefined behavior) + // But that would trap in wasm anyway, so we don't really + // care. + unsafe { v.to_int_unchecked() } +} + #[no_mangle] pub fn tic(time: i32) { - unsafe { - for i in 0..320 * 256 { - let t = time as f32 / 10 as f32; - let x = (i % 320 - 160) as f32; - let y = (i / 320 - 128) as f32; - let d = 20000 as f32 / sqrt(x * x + y * y + 1 as f32); - let u = atan2(x, y) * 512f32 / 3.141; - let c = ((d + t).to_int_unchecked::() ^ (u + t).to_int_unchecked::()) as u8; + for i in 0..320 * 256 { + let t = time as f32 / 10 as f32; + let x = (i % 320 - 160) as f32; + let y = (i / 320 - 128) as f32; + let d = 20000 as f32 / sqrt(x * x + y * y + 1 as f32); + let u = atan2(x, y) * 512f32 / 3.141; + let c = (ftoi(d + t) ^ ftoi(u + t)) as u8; + unsafe { *((120 + i) as *mut u8) = c; } } diff --git a/examples/rust/tunnel.uw8 b/examples/rust/tunnel.uw8 index 36d523383a61215dc042aadbd7de2f44835d2883..21c1c16a12e9549578ec6e0f2706482ff6b8f994 100644 GIT binary patch delta 37 scmeBV>}2F&DJMH?lM<5x%S1P40F%uKivR!s delta 37 rcmeBV>}2F( f32` -* * `fn asin(f32) -> f32` -* * `fn atan(f32) -> f32` -* * `fn atan2(f32, f32) -> f32` -* * `fn cos(f32) -> f32` -* * `fn exp(f32, f32) -> f32` -* * `fn log(f32) -> f32` -* * `fn sin(f32) -> f32` -* * `fn tan(f32) -> f32` -* * `fn pow(f32) -> f32` +* `fn acos(f32) -> f32` +* `fn asin(f32) -> f32` +* `fn atan(f32) -> f32` +* `fn atan2(f32, f32) -> f32` +* `fn cos(f32) -> f32` +* `fn exp(f32, f32) -> f32` +* `fn log(f32) -> f32` +* `fn sin(f32) -> f32` +* `fn tan(f32) -> f32` +* `fn pow(f32) -> f32` ## `.uw8` format diff --git a/site/static/v0.1pre1/index.html b/site/static/v0.1pre1/index.html index 101ce92..c8331a1 100644 --- a/site/static/v0.1pre1/index.html +++ b/site/static/v0.1pre1/index.html @@ -1 +1 @@ -MicroW8
\ No newline at end of file +MicroW8
\ No newline at end of file diff --git a/uw8-tool/src/base_module.rs b/uw8-tool/src/base_module.rs index 3dcf210..3ccf823 100644 --- a/uw8-tool/src/base_module.rs +++ b/uw8-tool/src/base_module.rs @@ -1,25 +1,25 @@ use std::{collections::HashMap, fs::File, path::Path}; +use anyhow::{bail, Result}; +use std::io::prelude::*; use wasm_encoder::{ - CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, - ImportSection, Instruction, MemoryType, Module, TypeSection, ValType, + CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection, + Instruction, MemoryType, Module, TypeSection, ValType, }; use ValType::*; -use anyhow::{Result, bail}; -use std::io::prelude::*; pub struct BaseModule { pub types: Vec, pub function_imports: Vec<(&'static str, String, u32)>, pub functions: Vec, pub exports: Vec<(&'static str, u32)>, - pub memory: u32 + pub memory: u32, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FunctionType { pub params: Vec, - pub result: Option + pub result: Option, } impl BaseModule { @@ -48,18 +48,24 @@ impl BaseModule { } let mut functions = vec![]; - add_function(&mut functions, &type_map, "math","sin", &[F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "cos", &[F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "tan", &[F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "asin", &[F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "acos", &[F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "atan", &[F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "atan2", &[F32, F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "pow", &[F32, F32], Some(F32)); - add_function(&mut functions, &type_map, "math", "log", &[F32], Some(F32)); + add_function(&mut functions, &type_map, "sin", &[F32], Some(F32)); + add_function(&mut functions, &type_map, "cos", &[F32], Some(F32)); + add_function(&mut functions, &type_map, "tan", &[F32], Some(F32)); + add_function(&mut functions, &type_map, "asin", &[F32], Some(F32)); + add_function(&mut functions, &type_map, "acos", &[F32], Some(F32)); + add_function(&mut functions, &type_map, "atan", &[F32], Some(F32)); + add_function(&mut functions, &type_map, "atan2", &[F32, F32], Some(F32)); + add_function(&mut functions, &type_map, "pow", &[F32, F32], Some(F32)); + add_function(&mut functions, &type_map, "log", &[F32], Some(F32)); for i in functions.len()..64 { - add_function(&mut functions, &type_map, "uw8", &format!("reserved{}", i), &[], None); + add_function( + &mut functions, + &type_map, + &format!("reserved{}", i), + &[], + None, + ); } let first_function = functions.len() as u32; @@ -69,7 +75,7 @@ impl BaseModule { function_imports: functions, functions: vec![lookup_type(&type_map, &[I32], None)], exports: vec![("tic", first_function)], - memory: 4 + memory: 4, }) } @@ -92,11 +98,15 @@ impl BaseModule { imports.import(*module, Some(name.as_str()), EntityType::Function(*type_)); } - imports.import("env", Some("memory"), MemoryType { - minimum: m.memory as u64, - maximum: None, - memory64: false - }); + imports.import( + "env", + Some("memory"), + MemoryType { + minimum: m.memory as u64, + maximum: None, + memory64: false, + }, + ); module.section(&imports); } @@ -143,14 +153,28 @@ impl BaseModule { } } -fn add_function(functions: &mut Vec<(&'static str, String, u32)>, type_map: &HashMap, module: &'static str, name: &str, params: &[ValType], result: Option) { - functions.push((module, name.to_string(), lookup_type(type_map, params, result))); +fn add_function( + functions: &mut Vec<(&'static str, String, u32)>, + type_map: &HashMap, + name: &str, + params: &[ValType], + result: Option, +) { + functions.push(( + "env".into(), + name.to_string(), + lookup_type(type_map, params, result), + )); } -fn lookup_type(type_map: &HashMap, params: &[ValType], result: Option) -> u32 { +fn lookup_type( + type_map: &HashMap, + params: &[ValType], + result: Option, +) -> u32 { let key = FunctionType { params: params.to_vec(), - result + result, }; *type_map.get(&key).unwrap() -} \ No newline at end of file +} diff --git a/web/src/main.js b/web/src/main.js index d410488..a5f848e 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -10,7 +10,7 @@ async function loadWasm(url, imports) { function setMessage(size, error) { let html = size ? `${size} bytes` : 'Insert cart'; - if(error) { + if (error) { html += ` - ${error.replaceAll('<', '<')}` } document.getElementById('message').innerHTML = html; @@ -27,7 +27,7 @@ let canvasCtx = screen.getContext('2d'); let cancelFunction; async function runModule(data) { - if(cancelFunction) { + if (cancelFunction) { cancelFunction(); cancelFunction = null; } @@ -35,21 +35,21 @@ async function runModule(data) { let cartridgeSize = data.byteLength; setMessage(cartridgeSize); - if(cartridgeSize == 0) { + if (cartridgeSize == 0) { return; } let dataU8Array = new Uint8Array(data); let newURL = window.location.pathname; - if(cartridgeSize <= 1024) { + if (cartridgeSize <= 1024) { let dataString = ''; - for(let byte of dataU8Array) { + for (let byte of dataU8Array) { dataString += String.fromCharCode(byte); } newURL += '#' + btoa(dataString); } - if(newURL != window.location.pathname + window.location.hash) { + if (newURL != window.location.pathname + window.location.hash) { history.pushState(null, null, newURL); } @@ -64,55 +64,53 @@ async function runModule(data) { }; let loadMem = loaderImport.env.memory.buffer; let loader = await loadWasm(loaderUrl, loaderImport); - + let baseModule = await (await fetch(baseUrl)).arrayBuffer(); - - if(dataU8Array[0] != 0) { + + if (dataU8Array[0] != 0) { new Uint8Array(loadMem).set(dataU8Array); new Uint8Array(loadMem).set(new Uint8Array(baseModule), data.byteLength); - + let destOffset = data.byteLength + baseModule.byteLength; let endOffset = loader.exports.load_uw8(0, data.byteLength, data.byteLength, destOffset); - + data = new ArrayBuffer(endOffset - destOffset); new Uint8Array(data).set(new Uint8Array(loadMem).slice(destOffset, endOffset)); } - + let importObject = { env: { memory: new WebAssembly.Memory({ initial: 4, maximum: 4 }), }, - math: {}, - uw8: {} }; - for(let n of ['acos','asin','atan','atan2','cos','exp','log','sin','tan','pow']) { - importObject.math[n] = Math[n]; + for (let n of ['acos', 'asin', 'atan', 'atan2', 'cos', 'exp', 'log', 'sin', 'tan', 'pow']) { + importObject.env[n] = Math[n]; } - for(let i = 9; i < 64; ++i) { - importObject.uw8['reserved' + i] = () => {}; + for (let i = 9; i < 64; ++i) { + importObject.env['reserved' + i] = () => { }; } - + let instance = new WebAssembly.Instance(await WebAssembly.compile(data), importObject); - + let buffer = imageData.data; - + let startTime = Date.now(); - + let keepRunning = true; cancelFunction = () => keepRunning = false; function mainloop() { - if(!keepRunning) { + if (!keepRunning) { return; } try { instance.exports.tic(Date.now() - startTime); - - let framebuffer = new Uint8Array(importObject.env.memory.buffer.slice(120, 120 + 320*256)); - for(let i = 0; i < 320*256; ++i) { + + let framebuffer = new Uint8Array(importObject.env.memory.buffer.slice(120, 120 + 320 * 256)); + for (let i = 0; i < 320 * 256; ++i) { buffer[i * 4] = framebuffer[i]; buffer[i * 4 + 1] = framebuffer[i]; buffer[i * 4 + 2] = framebuffer[i]; @@ -121,15 +119,15 @@ async function runModule(data) { framebufferCanvasCtx.putImageData(imageData, 0, 0); canvasCtx.imageSmoothingEnabled = false; canvasCtx.drawImage(framebufferCanvas, 0, 0, 640, 512); - + window.requestAnimationFrame(mainloop); - } catch(err) { + } catch (err) { setMessage(cartridgeSize, err.toString()); } } - + mainloop(); - } catch(err) { + } catch (err) { setMessage(cartridgeSize, err.toString()); } } @@ -140,7 +138,7 @@ async function runModuleFromURL(url) { function runModuleFromHash() { let base64Data = window.location.hash.slice(1); - if(base64Data.length > 0) { + if (base64Data.length > 0) { runModuleFromURL('data:;base64,' + base64Data); } else { runModule(new ArrayBuffer(0)); @@ -155,7 +153,7 @@ document.getElementById('cartButton').onclick = () => { fileInput.type = 'file'; fileInput.accept = '.wasm,.uw8,application/wasm'; fileInput.onchange = () => { - if(fileInput.files.length > 0) { + if (fileInput.files.length > 0) { runModuleFromURL(URL.createObjectURL(fileInput.files[0])); } };