diff --git a/.clangd b/.clangd index 93a1996..341a5c4 100644 --- a/.clangd +++ b/.clangd @@ -1,13 +1,5 @@ If: - PathMatch: .*\.cpp - -CompileFlags: - Add: [-std=c++23] - ---- - -If: - PathMatch: .*\.c + PathMatch: .*\.[ch] CompileFlags: Add: [-std=c23] \ No newline at end of file diff --git a/build/assets/include/cassert b/build/assets/include/cassert deleted file mode 100644 index cad9086..0000000 --- a/build/assets/include/cassert +++ /dev/null @@ -1 +0,0 @@ -#define assert(x) ((void)(x)) diff --git a/build/assets/include/cfloat b/build/assets/include/cfloat deleted file mode 100644 index 2478ef2..0000000 --- a/build/assets/include/cfloat +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/build/assets/include/climits b/build/assets/include/climits deleted file mode 100644 index 990e9b5..0000000 --- a/build/assets/include/climits +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/build/assets/include/cmath b/build/assets/include/cmath deleted file mode 100644 index 4140673..0000000 --- a/build/assets/include/cmath +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/build/assets/include/cstddef b/build/assets/include/cstddef deleted file mode 100644 index 1fa32ca..0000000 --- a/build/assets/include/cstddef +++ /dev/null @@ -1,6 +0,0 @@ -#include - -namespace std { - using nullptr_t = decltype(nullptr); - using size_t = ::size_t; -} \ No newline at end of file diff --git a/build/assets/include/cstdint b/build/assets/include/cstdint deleted file mode 100644 index 2aadec1..0000000 --- a/build/assets/include/cstdint +++ /dev/null @@ -1,12 +0,0 @@ -#include -namespace std { - using uint8_t = ::uint8_t; - using int8_t = ::int8_t; - using uint16_t = ::uint16_t; - using int16_t = ::int16_t; - using uint32_t = ::uint32_t; - using int32_t = ::int32_t; - using uint64_t = ::uint64_t; - using int64_t = ::int64_t; - using uintptr_t = ::uintptr_t; -} \ No newline at end of file diff --git a/build/assets/include/js.h b/build/assets/include/js.h new file mode 100644 index 0000000..cbf9900 --- /dev/null +++ b/build/assets/include/js.h @@ -0,0 +1,8 @@ +#pragma once + +#define IMPORT(name) __attribute__((import_module("env"), import_name(#name))) +#define EXPORT(name) __attribute__((export_name(#name))) + +IMPORT(log) int console_log(const char* format, ...); + +EXPORT(__srand) void srand(long long seed); diff --git a/build/assets/include/limits b/build/assets/include/limits deleted file mode 100644 index da9b153..0000000 --- a/build/assets/include/limits +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -namespace std { - template - struct numeric_limits { - static constexpr bool is_iec559 = std::is_floating_point::value; - static constexpr bool is_integer = std::is_integral::value; - static constexpr bool is_signed = std::is_signed::value; - }; -} \ No newline at end of file diff --git a/build/assets/include/math.h b/build/assets/include/math.h deleted file mode 100644 index 21e8de7..0000000 --- a/build/assets/include/math.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include - -#define M_PI 3.14159265359 -#define M_PI_2 1.57079632679 - -#define abs(x) (((x) < 0) ? -(x) : (x)) -#define max(a, b) (((a) > (b)) ? (a) : (b)) -#define min(a, b) (((a) < (b)) ? (a) : (b)) - -#ifdef __cplusplus -extern "C" { -#endif - -static inline bool isnan(double x) { - return x != x; -} -static inline bool isinf(double x) { - return !isnan(x) && isnan(x - x); -} - -#define IMPORT_UNARY(fn) __attribute__((import_module("Math"), import_name(#fn))) double fn(double); -#define IMPORT_BINARY(fn) __attribute__((import_module("Math"), import_name(#fn))) double fn(double, double); - -IMPORT_UNARY(floor) -IMPORT_UNARY(ceil) -IMPORT_UNARY(round) -IMPORT_UNARY(trunc) -IMPORT_UNARY(exp) -IMPORT_UNARY(log) -IMPORT_UNARY(log10) -IMPORT_UNARY(sqrt) -IMPORT_UNARY(sin) -IMPORT_UNARY(cos) -IMPORT_UNARY(tan) -IMPORT_UNARY(asin) -IMPORT_UNARY(acos) -IMPORT_UNARY(atan) -IMPORT_UNARY(sinh) -IMPORT_UNARY(cosh) -IMPORT_UNARY(tanh) - -IMPORT_BINARY(fmod) -IMPORT_BINARY(pow) -IMPORT_BINARY(atan2) - -#undef IMPORT_UNARY -#undef IMPORT_BINARY - -#ifdef __cplusplus -} -#endif diff --git a/build/assets/include/math.hpp b/build/assets/include/math.hpp deleted file mode 100644 index 68b9281..0000000 --- a/build/assets/include/math.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include - -#ifdef abs -#undef abs -#endif - -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - -namespace std { -template T max(T a, T b) { - return a < b ? b : a; -} -template T min(T a, T b) { - return a < b ? a : b; -} -template T abs(T a) { - return a < 0 ? -a : a; -} - -#define UNARY(fn) \ - template \ - typename std::enable_if::value, T>::type \ - fn(T a) { return static_cast(::fn(static_cast(a))); } - -#define UNARY_BOOL(fn) \ - template \ - typename std::enable_if::value, bool>::type \ - fn(T a) { return ::fn(static_cast(a)); } - -#define BINARY(fn) \ - template \ - typename std::enable_if::value, T>::type \ - fn(T a, T b) { return static_cast(::fn(static_cast(a), static_cast(b))); } - -UNARY(floor) -UNARY(ceil) -UNARY(round) -UNARY(trunc) -UNARY(exp) -UNARY(log) -UNARY(log10) -UNARY(sqrt) -UNARY(sin) -UNARY(cos) -UNARY(tan) -UNARY(asin) -UNARY(acos) -UNARY(atan) -UNARY(sinh) -UNARY(cosh) -UNARY(tanh) -UNARY_BOOL(isnan) -UNARY_BOOL(isinf) - -BINARY(fmod) -BINARY(pow) -BINARY(atan2) - -#undef UNARY -#undef UNARY_BOOL -#undef BINARY -} // namespace std - diff --git a/build/assets/include/stdlib.h b/build/assets/include/stdlib.h deleted file mode 100644 index 1c964b0..0000000 --- a/build/assets/include/stdlib.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#define RAND_MAX INT_MAX - -#ifdef __cplusplus -extern "C" { -#endif - -extern unsigned char __heap_base; - -#define IMPORT(name) __attribute__((import_module("env"), import_name(#name))) -#define EXPORT(name) __attribute__((export_name(#name))) - -void* malloc(size_t size); -void* calloc(size_t n, size_t size); -void* realloc(void* ptr, size_t size); -void free(void* ptr); - -__attribute__((__always_inline__)) static inline -void* memset(void* d, int c, size_t n) { - return __builtin_memset(d, c, n); -} - -__attribute__((__always_inline__)) static inline -void* memcpy(void* dest, const void* src, size_t n) { - return __builtin_memcpy(dest, src, n); -} - -int memcmp(const void* s1, const void* s2, size_t n); - -IMPORT(log) void printf(const char* format, ...); - -EXPORT(__srand) void srand(uint64_t seed); -int rand(void); - -#ifdef __cplusplus -} -#endif diff --git a/build/assets/include/type_traits b/build/assets/include/type_traits deleted file mode 100644 index ad6afba..0000000 --- a/build/assets/include/type_traits +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/build/assets/include/type_traits.hpp b/build/assets/include/type_traits.hpp deleted file mode 100644 index 0c2a792..0000000 --- a/build/assets/include/type_traits.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -namespace std { - template - struct integral_constant - { - static constexpr T value = v; - using value_type = T; - using type = integral_constant; // using injected-class-name - constexpr operator value_type() const noexcept { return value; } - constexpr value_type operator()() const noexcept { return value; } // since c++14 - }; - - using true_type = std::integral_constant; - using false_type = std::integral_constant; - - template< bool B > - using bool_constant = integral_constant; - - template - struct enable_if {}; - - template - struct enable_if { typedef T type; }; - - template - struct is_same : std::false_type {}; - - template - struct is_same : std::true_type {}; - - namespace detail - { - template - struct type_identity { using type = T; }; // or use std::type_identity (since C++20) - - template // Note that “cv void&” is a substitution failure - auto try_add_lvalue_reference(int) -> type_identity; - template // Handle T = cv void case - auto try_add_lvalue_reference(...) -> type_identity; - - template - auto try_add_rvalue_reference(int) -> type_identity; - template - auto try_add_rvalue_reference(...) -> type_identity; - } // namespace detail - - template - struct add_lvalue_reference - : decltype(detail::try_add_lvalue_reference(0)) {}; - - template - struct add_rvalue_reference - : decltype(detail::try_add_rvalue_reference(0)) {}; - - template - typename std::add_rvalue_reference::type declval() noexcept - { - static_assert(false, "declval not allowed in an evaluated context"); - } - - template - struct conditional { using type = T; }; - - template - struct conditional { using type = F; }; - - template struct remove_cv { typedef T type; }; - template struct remove_cv { typedef T type; }; - template struct remove_cv { typedef T type; }; - template struct remove_cv { typedef T type; }; - - template struct remove_const { typedef T type; }; - template struct remove_const { typedef T type; }; - - template struct remove_volatile { typedef T type; }; - template struct remove_volatile { typedef T type; }; - - template struct remove_reference { typedef T type; }; - template struct remove_reference { typedef T type; }; - template struct remove_reference { typedef T type; }; - - template - struct is_floating_point - : std::integral_constant< - bool, - std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - > {}; - - template - struct is_integral - : std::integral_constant< - bool, - std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - || std::is_same::type>::value - > {}; - - template - struct is_arithmetic : std::integral_constant::value || - std::is_floating_point::value> {}; - - namespace detail - { - template::value> - struct is_signed : std::integral_constant {}; - - template - struct is_signed : std::false_type {}; - } - - template - struct is_signed : detail::is_signed::type {}; - - template - struct is_void : std::is_same::type> {}; - - namespace detail - { - template - auto test_returnable(int) -> decltype( - void(static_cast(nullptr)), std::true_type{} - ); - template - auto test_returnable(...) -> std::false_type; - - template - auto test_implicitly_convertible(int) -> decltype( - void(std::declval()(std::declval())), std::true_type{} - ); - template - auto test_implicitly_convertible(...) -> std::false_type; - } // namespace detail - - template - struct is_convertible : std::integral_constant(0))::value && - decltype(detail::test_implicitly_convertible(0))::value) || - (std::is_void::value && std::is_void::value) - > {}; -} \ No newline at end of file diff --git a/build/assets/lib/graphics.c b/build/assets/lib/graphics.c index 4606e98..6b79659 100644 --- a/build/assets/lib/graphics.c +++ b/build/assets/lib/graphics.c @@ -1,7 +1,15 @@ #include -#include +#include #include +static inline int max(int a, int b) { + return a > b ? a : b; +} + +static inline int min(int a, int b) { + return a < b ? a : b; +} + image_data_t image_create(uint16_t width, uint16_t height) { image_data_t data; data.width = width; diff --git a/build/assets/lib/stdlib.c b/build/assets/lib/stdlib.c deleted file mode 100644 index 2c47ff1..0000000 --- a/build/assets/lib/stdlib.c +++ /dev/null @@ -1,183 +0,0 @@ -#include - -#define BLOCK_SIZE (64 * 1024) -#define BLOCK_1MB 16 - -typedef struct header { - size_t size : 31; - uint8_t is_free : 1; -} header_t; -static header_t* head; - -static uintptr_t bump_pointer = (uintptr_t)&__heap_base; -static uintptr_t heap_end = BLOCK_1MB * BLOCK_SIZE; -IMPORT(grow) void grow(uint32_t blocks); - -static void* bump_alloc(uintptr_t n) { - uintptr_t r = bump_pointer; - bump_pointer += n; - - while (bump_pointer >= heap_end) { - grow(heap_end / BLOCK_SIZE); - heap_end *= 2; - } - - return (void*)r; -} - -static header_t* get_next_header(header_t* block) { - if (!block) { - return NULL; - } - uint8_t* data = (uint8_t*)(block + 1); - uint8_t* next_header = data + block->size; - if ((uintptr_t)next_header >= bump_pointer) { - return NULL; - } - return (header_t*)next_header; -} - -static header_t* get_prev_header(header_t* block) { - header_t* curr = head; - while (curr) { - header_t* next = get_next_header(curr); - if (next == block) { - return curr; - } - curr = next; - } - return NULL; -} - -static header_t* get_free_block(size_t size) { - header_t* curr = head; - while (curr) { - if (curr->is_free && curr->size >= size) { - return curr; - } - curr = get_next_header(curr); - } - return NULL; -} - -void* malloc(size_t size) { - if (!size) { - return NULL; - } - - header_t* header = get_free_block(size); - if (header) { - header->is_free = 0; - if (header->size > size + sizeof(header_t) * 2) { - size_t old_size = header->size; - header->size = size; - - header_t* next = get_next_header(header); - next->size = old_size - size - sizeof(header_t); - next->is_free = 1; - } - return (void*)(header + 1); - } - size_t total_size = sizeof(header_t) + size; - - void* block = bump_alloc(total_size); - if (!block) { - return NULL; - } - - header = block; - header->size = size; - header->is_free = 0; - - if (!head) { - head = header; - } - - return (void*)(header + 1); -} - -void free(void* block) { - if (!block) { - return; - } - - header_t* header = ((header_t*)block) - 1; - if (header->is_free) { - return; - } - header->is_free = 1; - - header_t* next_header = get_next_header(header); - if (next_header && next_header->is_free) { - header->size += next_header->size + sizeof(header_t); - } - - header_t* prev_header = get_prev_header(header); - if (prev_header && prev_header->is_free) { - prev_header->size += header->size + sizeof(header_t); - } -} - -void* realloc(void* block, size_t size) { - header_t* header; - void* ret; - - if (!block || !size) { - return malloc(size); - } - - header = (header_t*)block - 1; - if (header->size >= size) { - return block; - } - - ret = malloc(size); - if (ret) { - memcpy(ret, block, header->size); - free(block); - } - return ret; -} - -void* calloc(size_t n, size_t size) { - const size_t s = n * size; - if (s == 0) { - return NULL; - } - - void* ret = malloc(n * size); - if (ret) { - memset(ret, 0, n * size); - } - - return ret; -} - -int memcmp(const void* s1, const void* s2, size_t n) { - const uint8_t* p1 = (const uint8_t*)s1; - const uint8_t* p2 = (const uint8_t*)s2; - - while (n--) { - if (*p1 != *p2) { - return (*p1 - *p2); - } - p1++; - p2++; - } - - return 0; // Memory blocks are equal -} - -static uint64_t rand_state; - -void srand(uint64_t seed) { - rand_state = seed; -} - -int rand(void) { - uint64_t z = (rand_state += 0x9e3779b97f4a7c15); - z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; - z = (z ^ (z >> 27)) * 0x94d049bb133111eb; - int r = (int)(z ^ (z >> 31)); - return r < 0 ? -r : r; -} diff --git a/build/wasmPlugin.ts b/build/wasmPlugin.ts index f36bf63..e20572a 100644 --- a/build/wasmPlugin.ts +++ b/build/wasmPlugin.ts @@ -1,11 +1,64 @@ import { plugin, $, type BunPlugin } from "bun"; import path from 'path'; +import fs from 'fs/promises'; interface WasmLoaderConfig { production?: boolean; portable?: boolean; } +interface CompilerWithFlags { + cc: string; + flags: string[]; +} + +const wasiArchiveURL = 'https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz'; +const rtURL = 'https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/libclang_rt.builtins-wasm32-wasi-25.0.tar.gz'; + +const getCompiler = async (): Promise => { + const wasiDir = path.resolve(import.meta.dir, '..', 'dist', 'wasi'); + const cc: CompilerWithFlags = { + cc: 'clang', + flags: [ + '--target=wasm32', + '--no-standard-libraries', + '-fno-builtin', + ], + }; + + await fs.mkdir(wasiDir, { recursive: true }); + + if (!await Bun.file(path.resolve(wasiDir, 'VERSION')).exists()) { + const response = await fetch(wasiArchiveURL); + + if (!response.ok) { + return cc; + } + + const bytes = await response.bytes(); + + await $`tar -xzv -C ${wasiDir} --strip-components=1 < ${bytes}`; + + const rtResponse = await fetch(rtURL); + + if (!rtResponse.ok) { + return cc; + } + + const rtBytes = await rtResponse.bytes(); + + await $`tar -xzv -C ${wasiDir} --strip-components=1 < ${rtBytes}`; + } + + cc.cc = `${path.resolve(wasiDir, 'bin', 'clang')}`; + cc.flags = [ + '--target=wasm32-wasi', + `--sysroot=${path.resolve(wasiDir, 'share', 'wasi-sysroot')}`, + ]; + + return cc; +} + const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin => { const p: BunPlugin = { name: "WASM loader", @@ -32,6 +85,8 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin env: { memory, log(format, argsPtr) { + let bufLen = buf.length; + let count = 0; format = getString(format); let isFormat = false; let w = 4; @@ -42,6 +97,8 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin isFormat = true; } else if (c === '\\n') { console.log('[wasm]', buf); + count += buf.length - bufLen + 1; + bufLen = 0; buf = ''; } else { buf += c; @@ -59,6 +116,8 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin default: buf += '%' + c; isFormat = false; break; } } + count += buf.length - bufLen; + return count; }, grow(blocks) { if (blocks > 0) { @@ -68,6 +127,15 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin }, }, Math: Math, + wasi_snapshot_preview1: { + random_get: (ptr, length) => { + for (let i=0; i < length; i++) { + data.setUint8(ptr + i, Math.random() * 256); + } + }, + environ_sizes_get(){ return 0; }, + environ_get() { return 0; }, + } }); return { @@ -89,6 +157,7 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`); const stdlib = await Array.fromAsync(glob.scan()); const objPath = wasmPath + '.o'; + const cc = await getCompiler(); const features = [ 'bulk-memory', @@ -104,20 +173,19 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin ].map(f => `-m${f}`); const flags = [ - '--target=wasm32', + ...cc.flags, production ? '-O3' : '-O0', '-flto', - '-fno-builtin', - '--no-standard-libraries', + '-fno-exceptions', '-Wall', '-Wextra', '-Wpedantic', '-Werror', '-Wshadow', - ...features, + ...features, ]; - const std = args.path.endsWith('.cpp') ? '-std=gnu++23': '-std=gnu23'; - const compileResult = await $`clang -c ${flags} ${std} -I ${include} -o ${objPath} ${args.path}`; + const std = args.path.endsWith('.cpp') ? '-std=gnu++23' : '-std=gnu23'; + const compileResult = await $`${cc.cc} -c ${flags} ${std} -I ${include} -o ${objPath} ${args.path}`; if (compileResult.exitCode !== 0) { throw new Error('Compile failed, check output'); @@ -129,7 +197,7 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin '--import-memory', ].map(f => `-Wl,${f}`); - const linkResult = await $`clang ${flags} -std=gnu23 -I ${include} ${linkFlags} -o ${wasmPath} ${objPath} ${stdlib}`; + const linkResult = await $`${cc.cc} ${flags} -std=gnu23 -I ${include} ${linkFlags} -lstdc++ -nostartfiles -o ${wasmPath} ${objPath} ${stdlib}`; if (linkResult.exitCode !== 0) { throw new Error('Link failed, check output'); diff --git a/compile_flags.txt b/compile_flags.txt index e15216f..0e10a5a 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -1,6 +1,7 @@ ---target=wasm32 --fno-builtin ---no-standard-libraries +--target=wasm32-wasi +--sysroot=dist/wasi/share/wasi-sysroot +-std=c++23 +-fno-exceptions -I build/assets/include -Wall diff --git a/src/games/playground/awoo.cpp b/src/games/playground/awoo.cpp index a301247..0c37c57 100644 --- a/src/games/playground/awoo.cpp +++ b/src/games/playground/awoo.cpp @@ -1,12 +1,12 @@ -#include +#include #include using namespace linalg::aliases; -EXPORT(main) auto init() { +EXPORT(awoo) auto awoo() { static constexpr double2 vel = {-1, -1}; static constexpr double2 left = {1, 0}; static constexpr double2 refl = linalg::reflect(vel, left); - return refl.y; -} \ No newline at end of file + return refl.x; +} diff --git a/src/games/playground/index.tsx b/src/games/playground/index.tsx index 4267bd1..97b3885 100644 --- a/src/games/playground/index.tsx +++ b/src/games/playground/index.tsx @@ -1,5 +1,5 @@ import awoo from "./awoo.cpp"; export default function main() { - console.log(awoo, awoo.main()); + console.log(awoo, awoo.awoo(), awoo.blah()); } \ No newline at end of file