1
0
Fork 0

Compare commits

...

2 Commits

Author SHA1 Message Date
Pabloader 9ffe98a8c5 Make wasm instantiation function separate 2025-06-16 10:59:47 +00:00
Pabloader d8fe2a7922 Migrate to WASI 2025-06-10 07:42:00 +00:00
21 changed files with 213 additions and 646 deletions

10
.clangd
View File

@ -1,13 +1,5 @@
If: If:
PathMatch: .*\.cpp PathMatch: .*\.[ch]
CompileFlags:
Add: [-std=c++23]
---
If:
PathMatch: .*\.c
CompileFlags: CompileFlags:
Add: [-std=c23] Add: [-std=c23]

View File

@ -1 +0,0 @@
#define assert(x) ((void)(x))

View File

@ -1 +0,0 @@
#include <float.h>

View File

@ -1 +0,0 @@
#include <limits.h>

View File

@ -1 +0,0 @@
#include <math.hpp>

View File

@ -1,6 +0,0 @@
#include <stddef.h>
namespace std {
using nullptr_t = decltype(nullptr);
using size_t = ::size_t;
}

View File

@ -1,12 +0,0 @@
#include <stdint.h>
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;
}

View File

@ -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);

View File

@ -1,12 +0,0 @@
#pragma once
#include <type_traits.hpp>
namespace std {
template <typename T>
struct numeric_limits {
static constexpr bool is_iec559 = std::is_floating_point<T>::value;
static constexpr bool is_integer = std::is_integral<T>::value;
static constexpr bool is_signed = std::is_signed<T>::value;
};
}

View File

@ -49,10 +49,13 @@
#ifndef LINALG_H #ifndef LINALG_H
#define LINALG_H #define LINALG_H
#include <cmath> #include <cmath> // For various unary math functions, such as std::sqrt
#include <type_traits> #include <cstdlib> // To resolve std::abs ambiguity on clang
#include <cstdint> #include <cstdint> // For implementing namespace linalg::aliases
#include <cstddef> #include <array> // For std::array
#include <iosfwd> // For forward definitions of std::ostream
#include <type_traits> // For std::enable_if, std::is_same, std::declval
#include <functional> // For std::hash declaration
namespace linalg namespace linalg
{ {
@ -109,8 +112,8 @@ namespace linalg
template<int A> struct make_seq_impl<A,3> { using type=seq<A+0,A+1,A+2>; }; template<int A> struct make_seq_impl<A,3> { using type=seq<A+0,A+1,A+2>; };
template<int A> struct make_seq_impl<A,4> { using type=seq<A+0,A+1,A+2,A+3>; }; template<int A> struct make_seq_impl<A,4> { using type=seq<A+0,A+1,A+2,A+3>; };
template<int A, int B> using make_seq = typename make_seq_impl<A,B-A>::type; template<int A, int B> using make_seq = typename make_seq_impl<A,B-A>::type;
template<class T, int M, int... I> vec<T,sizeof...(I)> constexpr swizzle(const vec<T,M> & v, seq<I...> i [[maybe_unused]]) { return {getter<I>{}(v)...}; } template<class T, int M, int... I> vec<T,sizeof...(I)> constexpr swizzle(const vec<T,M> & v, seq<I...>) { return {getter<I>{}(v)...}; }
template<class T, int M, int N, int... I, int... J> mat<T,sizeof...(I),sizeof...(J)> constexpr swizzle(const mat<T,M,N> & m, seq<I...> i, seq<J...> j [[maybe_unused]]) { return {swizzle(getter<J>{}(m),i)...}; } template<class T, int M, int N, int... I, int... J> mat<T,sizeof...(I),sizeof...(J)> constexpr swizzle(const mat<T,M,N> & m, seq<I...> i, seq<J...>) { return {swizzle(getter<J>{}(m),i)...}; }
// SFINAE helpers to determine result of function application // SFINAE helpers to determine result of function application
template<class F, class... T> using ret_t = decltype(std::declval<F>()(std::declval<T>()...)); template<class F, class... T> using ret_t = decltype(std::declval<F>()(std::declval<T>()...));
@ -193,6 +196,7 @@ namespace linalg
struct std_fmod { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::fmod (a, b)) { return std::fmod (a, b); } }; struct std_fmod { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::fmod (a, b)) { return std::fmod (a, b); } };
struct std_pow { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::pow (a, b)) { return std::pow (a, b); } }; struct std_pow { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::pow (a, b)) { return std::pow (a, b); } };
struct std_atan2 { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::atan2 (a, b)) { return std::atan2 (a, b); } }; struct std_atan2 { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::atan2 (a, b)) { return std::atan2 (a, b); } };
struct std_copysign { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::copysign(a, b)) { return std::copysign(a, b); } };
} }
// Small, fixed-length vector type, consisting of exactly M elements of type T, and presumed to be a column-vector unless otherwise noted // Small, fixed-length vector type, consisting of exactly M elements of type T, and presumed to be a column-vector unless otherwise noted
@ -204,8 +208,8 @@ namespace linalg
// NOTE: vec<T,1> does NOT have a constructor from pointer, this can conflict with initializing its single element from zero // NOTE: vec<T,1> does NOT have a constructor from pointer, this can conflict with initializing its single element from zero
template<class U> template<class U>
constexpr explicit vec(const vec<U,1> & v) : vec(static_cast<T>(v.x)) {} constexpr explicit vec(const vec<U,1> & v) : vec(static_cast<T>(v.x)) {}
constexpr const T & operator[] (int i [[maybe_unused]]) const { return x; } constexpr const T & operator[] (int) const { return x; }
constexpr T & operator[] (int i [[maybe_unused]]) { return x; } constexpr T & operator[] (int) { return x; }
template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {} template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {}
template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); } template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); }
@ -220,7 +224,7 @@ namespace linalg
template<class U> template<class U>
constexpr explicit vec(const vec<U,2> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y)) {} constexpr explicit vec(const vec<U,2> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y)) {}
constexpr const T & operator[] (int i) const { return i==0?x:y; } constexpr const T & operator[] (int i) const { return i==0?x:y; }
constexpr T & operator[] (int i) { return i==0?x:y; } constexpr T & operator[] (int i) { return i==0?x:y; }
template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {} template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {}
template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); } template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); }
@ -238,7 +242,7 @@ namespace linalg
template<class U> template<class U>
constexpr explicit vec(const vec<U,3> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y), static_cast<T>(v.z)) {} constexpr explicit vec(const vec<U,3> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y), static_cast<T>(v.z)) {}
constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:z; } constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:z; }
constexpr T & operator[] (int i) { return i==0?x:i==1?y:z; } constexpr T & operator[] (int i) { return i==0?x:i==1?y:z; }
constexpr const vec<T,2> & xy() const { return *reinterpret_cast<const vec<T,2> *>(this); } constexpr const vec<T,2> & xy() const { return *reinterpret_cast<const vec<T,2> *>(this); }
vec<T,2> & xy() { return *reinterpret_cast<vec<T,2> *>(this); } vec<T,2> & xy() { return *reinterpret_cast<vec<T,2> *>(this); }
@ -260,7 +264,7 @@ namespace linalg
template<class U> template<class U>
constexpr explicit vec(const vec<U,4> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y), static_cast<T>(v.z), static_cast<T>(v.w)) {} constexpr explicit vec(const vec<U,4> & v) : vec(static_cast<T>(v.x), static_cast<T>(v.y), static_cast<T>(v.z), static_cast<T>(v.w)) {}
constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:i==2?z:w; } constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:i==2?z:w; }
constexpr T & operator[] (int i) { return i==0?x:i==1?y:i==2?z:w; } constexpr T & operator[] (int i) { return i==0?x:i==1?y:i==2?z:w; }
constexpr const vec<T,2> & xy() const { return *reinterpret_cast<const vec<T,2> *>(this); } constexpr const vec<T,2> & xy() const { return *reinterpret_cast<const vec<T,2> *>(this); }
constexpr const vec<T,3> & xyz() const { return *reinterpret_cast<const vec<T,3> *>(this); } constexpr const vec<T,3> & xyz() const { return *reinterpret_cast<const vec<T,3> *>(this); }
vec<T,2> & xy() { return *reinterpret_cast<vec<T,2> *>(this); } vec<T,2> & xy() { return *reinterpret_cast<vec<T,2> *>(this); }
@ -282,8 +286,8 @@ namespace linalg
template<class U> template<class U>
constexpr explicit mat(const mat<U,M,1> & m) : mat(V(m.x)) {} constexpr explicit mat(const mat<U,M,1> & m) : mat(V(m.x)) {}
constexpr vec<T,1> row(int i) const { return {x[i]}; } constexpr vec<T,1> row(int i) const { return {x[i]}; }
constexpr const V & operator[] (int j [[maybe_unused]]) const { return x; } constexpr const V & operator[] (int) const { return x; }
constexpr V & operator[] (int j [[maybe_unused]]) { return x; } constexpr V & operator[] (int) { return x; }
template<class U, class=detail::conv_t<mat,U>> constexpr mat(const U & u) : mat(converter<mat,U>{}(u)) {} template<class U, class=detail::conv_t<mat,U>> constexpr mat(const U & u) : mat(converter<mat,U>{}(u)) {}
template<class U, class=detail::conv_t<U,mat>> constexpr operator U () const { return converter<U,mat>{}(*this); } template<class U, class=detail::conv_t<U,mat>> constexpr operator U () const { return converter<U,mat>{}(*this); }
@ -465,6 +469,7 @@ namespace linalg
template<class A, class B> apply_t<detail::std_fmod, A, B> fmod (const A & a, const B & b) { return apply(detail::std_fmod{}, a, b); } template<class A, class B> apply_t<detail::std_fmod, A, B> fmod (const A & a, const B & b) { return apply(detail::std_fmod{}, a, b); }
template<class A, class B> apply_t<detail::std_pow, A, B> pow (const A & a, const B & b) { return apply(detail::std_pow{}, a, b); } template<class A, class B> apply_t<detail::std_pow, A, B> pow (const A & a, const B & b) { return apply(detail::std_pow{}, a, b); }
template<class A, class B> apply_t<detail::std_atan2, A, B> atan2 (const A & a, const B & b) { return apply(detail::std_atan2{}, a, b); } template<class A, class B> apply_t<detail::std_atan2, A, B> atan2 (const A & a, const B & b) { return apply(detail::std_atan2{}, a, b); }
template<class A, class B> apply_t<detail::std_copysign, A, B> copysign(const A & a, const B & b) { return apply(detail::std_copysign{}, a, b); }
// Component-wise relational functions on vectors // Component-wise relational functions on vectors
template<class A, class B> constexpr apply_t<detail::op_eq, A, B> equal (const A & a, const B & b) { return apply(detail::op_eq{}, a, b); } template<class A, class B> constexpr apply_t<detail::op_eq, A, B> equal (const A & a, const B & b) { return apply(detail::op_eq{}, a, b); }
@ -546,7 +551,7 @@ namespace linalg
template<class T, int M> constexpr mat<T,M,3> transpose(const mat<T,3,M> & m) { return {m.row(0), m.row(1), m.row(2)}; } template<class T, int M> constexpr mat<T,M,3> transpose(const mat<T,3,M> & m) { return {m.row(0), m.row(1), m.row(2)}; }
template<class T, int M> constexpr mat<T,M,4> transpose(const mat<T,4,M> & m) { return {m.row(0), m.row(1), m.row(2), m.row(3)}; } template<class T, int M> constexpr mat<T,M,4> transpose(const mat<T,4,M> & m) { return {m.row(0), m.row(1), m.row(2), m.row(3)}; }
template<class T, int M> constexpr mat<T,1,M> transpose(const vec<T,M> & m) { return transpose(mat<T,M,1>(m)); } template<class T, int M> constexpr mat<T,1,M> transpose(const vec<T,M> & m) { return transpose(mat<T,M,1>(m)); }
template<class T> constexpr mat<T,1,1> adjugate(const mat<T,1,1> & a [[maybe_unused]]) { return {vec<T,1>{1}}; } template<class T> constexpr mat<T,1,1> adjugate(const mat<T,1,1> &) { return {vec<T,1>{1}}; }
template<class T> constexpr mat<T,2,2> adjugate(const mat<T,2,2> & a) { return {{a.y.y, -a.x.y}, {-a.y.x, a.x.x}}; } template<class T> constexpr mat<T,2,2> adjugate(const mat<T,2,2> & a) { return {{a.y.y, -a.x.y}, {-a.y.x, a.x.x}}; }
template<class T> constexpr mat<T,3,3> adjugate(const mat<T,3,3> & a); template<class T> constexpr mat<T,3,3> adjugate(const mat<T,3,3> & a);
template<class T> constexpr mat<T,4,4> adjugate(const mat<T,4,4> & a); template<class T> constexpr mat<T,4,4> adjugate(const mat<T,4,4> & a);
@ -580,6 +585,17 @@ namespace linalg
template<class T> mat<T,4,4> frustum_matrix (T x0, T x1, T y0, T y1, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one); template<class T> mat<T,4,4> frustum_matrix (T x0, T x1, T y0, T y1, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one);
template<class T> mat<T,4,4> perspective_matrix(T fovy, T aspect, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one) { T y = n*std::tan(fovy / 2), x = y*aspect; return frustum_matrix(-x, x, -y, y, n, f, a, z); } template<class T> mat<T,4,4> perspective_matrix(T fovy, T aspect, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one) { T y = n*std::tan(fovy / 2), x = y*aspect; return frustum_matrix(-x, x, -y, y, n, f, a, z); }
// Provide implicit conversion between linalg::vec<T,M> and std::array<T,M>
template<class T> struct converter<vec<T,1>, std::array<T,1>> { vec<T,1> operator() (const std::array<T,1> & a) const { return {a[0]}; } };
template<class T> struct converter<vec<T,2>, std::array<T,2>> { vec<T,2> operator() (const std::array<T,2> & a) const { return {a[0], a[1]}; } };
template<class T> struct converter<vec<T,3>, std::array<T,3>> { vec<T,3> operator() (const std::array<T,3> & a) const { return {a[0], a[1], a[2]}; } };
template<class T> struct converter<vec<T,4>, std::array<T,4>> { vec<T,4> operator() (const std::array<T,4> & a) const { return {a[0], a[1], a[2], a[3]}; } };
template<class T> struct converter<std::array<T,1>, vec<T,1>> { std::array<T,1> operator() (const vec<T,1> & a) const { return {a[0]}; } };
template<class T> struct converter<std::array<T,2>, vec<T,2>> { std::array<T,2> operator() (const vec<T,2> & a) const { return {a[0], a[1]}; } };
template<class T> struct converter<std::array<T,3>, vec<T,3>> { std::array<T,3> operator() (const vec<T,3> & a) const { return {a[0], a[1], a[2]}; } };
template<class T> struct converter<std::array<T,4>, vec<T,4>> { std::array<T,4> operator() (const vec<T,4> & a) const { return {a[0], a[1], a[2], a[3]}; } };
// Provide typedefs for common element types and vector/matrix sizes // Provide typedefs for common element types and vector/matrix sizes
namespace aliases namespace aliases
{ {
@ -608,6 +624,34 @@ namespace linalg
typedef mat<bool,4,3> bool4x3; typedef mat<int,4,3> int4x3; typedef mat<float,4,3> float4x3; typedef mat<double,4,3> double4x3; typedef mat<bool,4,3> bool4x3; typedef mat<int,4,3> int4x3; typedef mat<float,4,3> float4x3; typedef mat<double,4,3> double4x3;
typedef mat<bool,4,4> bool4x4; typedef mat<int,4,4> int4x4; typedef mat<float,4,4> float4x4; typedef mat<double,4,4> double4x4; typedef mat<bool,4,4> bool4x4; typedef mat<int,4,4> int4x4; typedef mat<float,4,4> float4x4; typedef mat<double,4,4> double4x4;
} }
// Provide output streaming operators, writing something that resembles an aggregate literal that could be used to construct the specified value
namespace ostream_overloads
{
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,1> & v) { return out << '{' << v[0] << '}'; }
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,2> & v) { return out << '{' << v[0] << ',' << v[1] << '}'; }
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,3> & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}'; }
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,4> & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,1> & m) { return out << '{' << m[0] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,2> & m) { return out << '{' << m[0] << ',' << m[1] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,3> & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}'; }
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,4> & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}'; }
}
}
namespace std
{
// Provide specializations for std::hash<...> with linalg types
template<class T> struct hash<linalg::vec<T,1>> { std::size_t operator()(const linalg::vec<T,1> & v) const { std::hash<T> h; return h(v.x); } };
template<class T> struct hash<linalg::vec<T,2>> { std::size_t operator()(const linalg::vec<T,2> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1); } };
template<class T> struct hash<linalg::vec<T,3>> { std::size_t operator()(const linalg::vec<T,3> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2); } };
template<class T> struct hash<linalg::vec<T,4>> { std::size_t operator()(const linalg::vec<T,4> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2) ^ (h(v.w) << 3); } };
template<class T, int M> struct hash<linalg::mat<T,M,1>> { std::size_t operator()(const linalg::mat<T,M,1> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x); } };
template<class T, int M> struct hash<linalg::mat<T,M,2>> { std::size_t operator()(const linalg::mat<T,M,2> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M); } };
template<class T, int M> struct hash<linalg::mat<T,M,3>> { std::size_t operator()(const linalg::mat<T,M,3> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M*2)); } };
template<class T, int M> struct hash<linalg::mat<T,M,4>> { std::size_t operator()(const linalg::mat<T,M,4> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M*2)) ^ (h(v.w) << (M*3)); } };
} }
// Definitions of functions too long to be defined inline // Definitions of functions too long to be defined inline

View File

@ -1,53 +0,0 @@
#pragma once
#include <stdbool.h>
#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

View File

@ -1,72 +0,0 @@
#pragma once
#include <math.h>
#include <type_traits.hpp>
#ifdef abs
#undef abs
#endif
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace std {
template <typename T> T max(T a, T b) {
return a < b ? b : a;
}
template <typename T> T min(T a, T b) {
return a < b ? a : b;
}
template <typename T> T abs(T a) {
return a < 0 ? -a : a;
}
#define UNARY(fn) \
template <typename T> \
typename std::enable_if<std::is_convertible<T, double>::value, T>::type \
fn(T a) { return static_cast<T>(::fn(static_cast<double>(a))); }
#define UNARY_BOOL(fn) \
template <typename T> \
typename std::enable_if<std::is_convertible<T, double>::value, bool>::type \
fn(T a) { return ::fn(static_cast<double>(a)); }
#define BINARY(fn) \
template <typename T> \
typename std::enable_if<std::is_convertible<T, double>::value, T>::type \
fn(T a, T b) { return static_cast<T>(::fn(static_cast<double>(a), static_cast<double>(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

View File

@ -1,43 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <stddef.h>
#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

View File

@ -1 +0,0 @@
#include <type_traits.hpp>

View File

@ -1,151 +0,0 @@
#pragma once
namespace std {
template<class T, T v>
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<bool, true>;
using false_type = std::integral_constant<bool, false>;
template< bool B >
using bool_constant = integral_constant<bool, B>;
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
template<class T, class U>
struct is_same : std::false_type {};
template<class T>
struct is_same<T, T> : std::true_type {};
namespace detail
{
template<class T>
struct type_identity { using type = T; }; // or use std::type_identity (since C++20)
template<class T> // Note that “cv void&” is a substitution failure
auto try_add_lvalue_reference(int) -> type_identity<T&>;
template<class T> // Handle T = cv void case
auto try_add_lvalue_reference(...) -> type_identity<T>;
template<class T>
auto try_add_rvalue_reference(int) -> type_identity<T&&>;
template<class T>
auto try_add_rvalue_reference(...) -> type_identity<T>;
} // namespace detail
template<class T>
struct add_lvalue_reference
: decltype(detail::try_add_lvalue_reference<T>(0)) {};
template<class T>
struct add_rvalue_reference
: decltype(detail::try_add_rvalue_reference<T>(0)) {};
template<typename T>
typename std::add_rvalue_reference<T>::type declval() noexcept
{
static_assert(false, "declval not allowed in an evaluated context");
}
template<bool B, class T, class F>
struct conditional { using type = T; };
template<class T, class F>
struct conditional<false, T, F> { using type = F; };
template<class T> struct remove_cv { typedef T type; };
template<class T> struct remove_cv<const T> { typedef T type; };
template<class T> struct remove_cv<volatile T> { typedef T type; };
template<class T> struct remove_cv<const volatile T> { typedef T type; };
template<class T> struct remove_const { typedef T type; };
template<class T> struct remove_const<const T> { typedef T type; };
template<class T> struct remove_volatile { typedef T type; };
template<class T> struct remove_volatile<volatile T> { typedef T type; };
template<class T> struct remove_reference { typedef T type; };
template<class T> struct remove_reference<T&> { typedef T type; };
template<class T> struct remove_reference<T&&> { typedef T type; };
template<class T>
struct is_floating_point
: std::integral_constant<
bool,
std::is_same<float, typename std::remove_cv<T>::type>::value
|| std::is_same<double, typename std::remove_cv<T>::type>::value
|| std::is_same<long double, typename std::remove_cv<T>::type>::value
> {};
template<class T>
struct is_integral
: std::integral_constant<
bool,
std::is_same<char, typename std::remove_cv<T>::type>::value
|| std::is_same<unsigned char, typename std::remove_cv<T>::type>::value
|| std::is_same<short, typename std::remove_cv<T>::type>::value
|| std::is_same<unsigned short, typename std::remove_cv<T>::type>::value
|| std::is_same<int, typename std::remove_cv<T>::type>::value
|| std::is_same<unsigned int, typename std::remove_cv<T>::type>::value
|| std::is_same<long, typename std::remove_cv<T>::type>::value
|| std::is_same<unsigned long, typename std::remove_cv<T>::type>::value
|| std::is_same<long long, typename std::remove_cv<T>::type>::value
|| std::is_same<unsigned long long, typename std::remove_cv<T>::type>::value
> {};
template<class T>
struct is_arithmetic : std::integral_constant<bool,
std::is_integral<T>::value ||
std::is_floating_point<T>::value> {};
namespace detail
{
template<typename T, bool = std::is_arithmetic<T>::value>
struct is_signed : std::integral_constant<bool, T(-1) < T(0)> {};
template<typename T>
struct is_signed<T, false> : std::false_type {};
}
template<typename T>
struct is_signed : detail::is_signed<T>::type {};
template<class T>
struct is_void : std::is_same<void, typename std::remove_cv<T>::type> {};
namespace detail
{
template<class T>
auto test_returnable(int) -> decltype(
void(static_cast<T(*)()>(nullptr)), std::true_type{}
);
template<class>
auto test_returnable(...) -> std::false_type;
template<class From, class To>
auto test_implicitly_convertible(int) -> decltype(
void(std::declval<void(&)(To)>()(std::declval<From>())), std::true_type{}
);
template<class, class>
auto test_implicitly_convertible(...) -> std::false_type;
} // namespace detail
template<class From, class To>
struct is_convertible : std::integral_constant<bool,
(decltype(detail::test_returnable<To>(0))::value &&
decltype(detail::test_implicitly_convertible<From, To>(0))::value) ||
(std::is_void<From>::value && std::is_void<To>::value)
> {};
}

View File

@ -1,7 +1,15 @@
#include <graphics.h> #include <graphics.h>
#include <math.h> #include <memory.h>
#include <stdlib.h> #include <stdlib.h>
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 image_create(uint16_t width, uint16_t height) {
image_data_t data; image_data_t data;
data.width = width; data.width = width;

View File

@ -1,183 +0,0 @@
#include <stdlib.h>
#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;
}

View File

@ -1,83 +1,133 @@
import { plugin, $, type BunPlugin } from "bun"; import { plugin, $, type BunPlugin } from "bun";
import path from 'path'; import path from 'path';
import fs from 'fs/promises';
interface WasmLoaderConfig { interface WasmLoaderConfig {
production?: boolean; production?: boolean;
portable?: boolean; portable?: boolean;
} }
const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin => { 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<CompilerWithFlags> => {
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;
}
async function instantiate(url: string) {
const memory = new WebAssembly.Memory({
initial: 32,
});
let data = new DataView(memory.buffer);
const decoder = new TextDecoder();
let buf = '';
let errBuf = '';
const { instance } = await WebAssembly.instantiateStreaming(fetch(url), {
env: { memory },
wasi_snapshot_preview1: {
random_get: (ptr: number, length: number) => {
for (let i = 0; i < length; i++) {
data.setUint8(ptr + i, Math.random() * 256);
}
},
environ_sizes_get: (...args: any[]) => { console.debug(`[environ_sizes_get]`, args); return 0; },
environ_get: (...args: any[]) => { console.debug(`[environ_get]`, args); return 0; },
proc_exit: (...args: any[]) => { console.debug(`[proc_exit]`, args); return 0; },
fd_close: (...args: any[]) => { console.debug(`[fd_close]`, args); return 0; },
fd_seek: (...args: any[]) => { console.debug(`[fd_seek]`, args); return 0; },
fd_write: (fd: number, iovsPtr: number, iovsLength: number, bytesWrittenPtr: number) => {
const iovs = new Uint32Array(memory.buffer, iovsPtr, iovsLength * 2);
if (fd === 1 || fd === 2) {
let text = "";
let totalBytesWritten = 0;
for (let i = 0; i < iovsLength * 2; i += 2) {
const offset = iovs[i];
const length = iovs[i + 1];
const textChunk = decoder.decode(new Int8Array(memory.buffer, offset, length));
text += textChunk;
totalBytesWritten += length;
}
data.setInt32(bytesWrittenPtr, totalBytesWritten, true);
if (fd === 1) {
const lines = (buf + text).split('\n');
buf = lines.pop() ?? '';
lines.forEach(l => console.log(`[wasm] ${l}`));
} else {
const lines = (errBuf + text).split('\n');
errBuf = lines.pop() ?? '';
lines.forEach(l => console.error(`[wasm] ${l}`));
}
}
return 0;
},
fd_read: (...args: any[]) => { console.debug(`[fd_read]`, args); return 0; },
fd_fdstat_get: (fd: number, fdstatPtr: number) => { console.debug(`[fd_fdstat_get] fd=${fd}, ptr=${fdstatPtr}`); return 0; },
fd_prestat_get: (...args: any[]) => { console.debug(`[fd_prestat_get]`, args); return 0; },
fd_prestat_dir_name: (...args: any[]) => { console.debug(`[fd_prestat_dir_name]`, args); return 0; },
}
});
return {
...instance.exports,
memory,
get data() { return data; },
};
}
const wasmPlugin = ({ production }: WasmLoaderConfig = {}): BunPlugin => {
const p: BunPlugin = { const p: BunPlugin = {
name: "WASM loader", name: "WASM loader",
async setup(build) { async setup(build) {
build.onLoad({ filter: /\.(c(pp)?|wasm)$/ }, async (args) => { build.onLoad({ filter: /\.(c(pp)?|wasm)$/ }, async (args) => {
let wasmPath = path.resolve(import.meta.dir, '..', 'dist', 'tmp.wasm'); let wasmPath = path.resolve(import.meta.dir, '..', 'dist', 'tmp.wasm');
let jsContent: string = ` let jsContent: string = `
async function instantiate(url) { ${instantiate}
const memory = new WebAssembly.Memory({
initial: 32,
});
let data = new DataView(memory.buffer);
const decoder = new TextDecoder();
const getString = (ptr) => {
const view = new Uint8Array(memory.buffer, ptr);
const start = ptr;
const end = ptr + view.indexOf(0);
return decoder.decode(memory.buffer.slice(start, end));
};
let buf = '';
Math.fmod = (x, y) => x % y;
const { instance } = await WebAssembly.instantiateStreaming(fetch(url), {
env: {
memory,
log(format, argsPtr) {
format = getString(format);
let isFormat = false;
let w = 4;
const align = (a) => argsPtr += (argsPtr % a);
for (const c of format) {
if (!isFormat) {
if (c === '%') {
isFormat = true;
} else if (c === '\\n') {
console.log('[wasm]', buf);
buf = '';
} else {
buf += c;
}
} else switch(c) {
case '%': buf += '%'; isFormat = false; break;
case 's': align(4); buf += getString(data.getInt32(argsPtr, true)); argsPtr += 4; isFormat = false; break;
case 'd': align(w); buf += data[w === 4 ? 'getInt32': 'getBigInt64'](argsPtr, true); argsPtr += w; isFormat = false; break;
case 'x': align(w); buf += data[w === 4 ? 'getUint32': 'getBigUint64'](argsPtr, true).toString(16); argsPtr += w; isFormat = false; break;
case 'o': align(w); buf += data[w === 4 ? 'getUint32': 'getBigUint64'](argsPtr, true).toString(8); argsPtr += w; isFormat = false; break;
case 'u': align(w); buf += data[w === 4 ? 'getUint32': 'getBigUint64'](argsPtr, true); argsPtr += w; isFormat = false; break;
case 'f': align(8); buf += data.getFloat64(argsPtr, true); argsPtr += 8; isFormat = false; break;
case 'c': align(4); buf += String.fromCharCode(data.getInt32(argsPtr, true)); argsPtr += 4; isFormat = false; break;
case 'l': w = 8; break;
default: buf += '%' + c; isFormat = false; break;
}
}
},
grow(blocks) {
if (blocks > 0) {
memory.grow(blocks);
data = new DataView(memory.buffer);
}
},
},
Math: Math,
});
return {
...instance.exports,
memory,
get data() { return data; },
};
}
const module = await instantiate(new URL($WASM$)); const module = await instantiate(new URL($WASM$));
if (typeof module.__srand === 'function') module.__srand(BigInt(Date.now()));
export default module; export default module;
`; `;
@ -89,6 +139,7 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`); const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`);
const stdlib = await Array.fromAsync(glob.scan()); const stdlib = await Array.fromAsync(glob.scan());
const objPath = wasmPath + '.o'; const objPath = wasmPath + '.o';
const cc = await getCompiler();
const features = [ const features = [
'bulk-memory', 'bulk-memory',
@ -104,20 +155,20 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
].map(f => `-m${f}`); ].map(f => `-m${f}`);
const flags = [ const flags = [
'--target=wasm32', ...cc.flags,
production ? '-O3' : '-O0', production ? '-O3' : '-O0',
'-flto', '-flto',
'-fno-builtin', '-fno-exceptions',
'--no-standard-libraries',
'-Wall', '-Wall',
'-Wextra', '-Wextra',
'-Wpedantic', '-Wpedantic',
'-Werror', '-Werror',
'-Wshadow', '-Wshadow',
...features, ...features,
'-I', include,
]; ];
const std = args.path.endsWith('.cpp') ? '-std=gnu++23': '-std=gnu23'; 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 compileResult = await $`${cc.cc} -c ${flags} ${std} -o ${objPath} ${args.path}`;
if (compileResult.exitCode !== 0) { if (compileResult.exitCode !== 0) {
throw new Error('Compile failed, check output'); throw new Error('Compile failed, check output');
@ -129,7 +180,7 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
'--import-memory', '--import-memory',
].map(f => `-Wl,${f}`); ].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 ${linkFlags} -lstdc++ -nostartfiles -o ${wasmPath} ${objPath} ${stdlib}`;
if (linkResult.exitCode !== 0) { if (linkResult.exitCode !== 0) {
throw new Error('Link failed, check output'); throw new Error('Link failed, check output');

View File

@ -1,6 +1,7 @@
--target=wasm32 --target=wasm32-wasi
-fno-builtin --sysroot=dist/wasi/share/wasi-sysroot
--no-standard-libraries -std=c++23
-fno-exceptions
-I -I
build/assets/include build/assets/include
-Wall -Wall

View File

@ -1,12 +1,12 @@
#include <js.h>
#include <stdlib.h> #include <stdlib.h>
#include <linalg.hpp>
using namespace linalg::aliases; struct boob {
int x;
boob(int x_): x(x_) {};
operator int() { return rand() + x; }
} boob(rand());
EXPORT(main) auto init() { EXPORT(awoo) auto awoo() -> int {
static constexpr double2 vel = {-1, -1}; return boob;
static constexpr double2 left = {1, 0}; }
static constexpr double2 refl = linalg::reflect(vel, left);
return refl.y;
}

View File

@ -1,5 +1,5 @@
import awoo from "./awoo.cpp"; import awoo from "./awoo.cpp";
export default function main() { export default function main() {
console.log(awoo, awoo.main()); console.log(awoo, awoo.awoo());
} }