Module: Dewasm::Pozeiden::WasmModule::Rt
Defined Under Namespace
Classes: LinkError, Memory, Table, Trap, WASI
Constant Summary
collapse
- M32 =
0xffff_ffff
- M64 =
0xffff_ffff_ffff_ffff
- F32_MAX =
Round a double to single precision.
For a magnitude in [2^-126, 2^127) the two subtractions around x * (2^29 + 1) are Veltkamp splitting: they return x with its significand rounded to 24 bits, to nearest, ties to even, exactly, because a double carries 53 >= 2 * 24 + 2 bits and neither product nor difference leaves the double range here.
Every other magnitude needs the pack path: below 2^-126 the rounding happens at the fixed subnormal exponent rather than at 24 significant bits, and at or above 2^127 the 24-bit result can be 2^128, which is not an f32. NaN fails both comparisons and lands there too.
Zero needs no rounding at all and is returned as it came, sign included.
MRI's pack("e") overflows straight to infinity for out-of-float-range doubles instead of rounding, so values below the rounding boundary (2^128 - 2^103) are mapped back to the largest finite f32.
3.4028234663852886e+38
- F32_OVERFLOW =
2.0**128 - 2.0**103
- F32_MIN_NORMAL =
2.0**-126
- F32_SPLIT_LIMIT =
2.0**127
- F32_SPLIT =
536870913.0
- LOW_MASK =
Masking a left-shift operand with LOW_MASK[width - shift] before the shift keeps the intermediate within the wasm width, so MRI never allocates a bignum wider than the result.
Kept out of the always-bundled rt/_module prelude so a module without rotates does not build the table.
Array.new(65) { |n| (1 << n) - 1 }
Class Method Summary
collapse
-
.check_import_kind(value, kind, mod, name) ⇒ Object
A present-but-wrong-kind import (e.g. a global where a func was declared) is a link error, distinct from a missing one (which still falls through to the caller's WASI/ENOSYS/raise fallback via ||).
-
.cvt_f32_i(v) ⇒ Object
Convert a (signed) Integer to f32 with correct rounding.
-
.cvt_f64_i(v) ⇒ Object
MRI's Integer#to_f rounds to nearest-even, which is exactly the wasm convert semantics.
-
.f32(x) ⇒ Object
-
.f32_abs(x) ⇒ Object
-
.f32_bits(x) ⇒ Object
-
.f32_demote(x) ⇒ Object
-
.f32_from_bits(b) ⇒ Object
MRI's pack("e")/unpack("e") canonicalize NaNs during the double<->float conversion, losing sign and payload.
-
.f32_neg(x) ⇒ Object
-
.f32_reinterpret_i32(x) ⇒ Object
-
.f64_abs(x) ⇒ Object
-
.f64_bits(x) ⇒ Object
-
.f64_copysign(a, b) ⇒ Object
-
.f64_from_bits(b) ⇒ Object
-
.f64_neg(x) ⇒ Object
-
.f64_promote(x) ⇒ Object
-
.f64_reinterpret_i64(x) ⇒ Object
-
.ffloor(x) ⇒ Object
-
.fsqrt(x) ⇒ Object
-
.i32_clz(x) ⇒ Object
-
.i32_div_s(a, b) ⇒ Object
-
.i32_div_u(a, b) ⇒ Object
-
.i32_extend16_s(x) ⇒ Object
-
.i32_extend8_s(x) ⇒ Object
-
.i32_reinterpret_f32(x) ⇒ Object
-
.i32_rem_u(a, b) ⇒ Object
-
.i32_rotl(a, b) ⇒ Object
The left shift can leave MRI's fixnum range; masking it before the OR confines the bignum to that one operand.
-
.i32_trunc_sat_s(x) ⇒ Object
-
.i32_trunc_sat_u(x) ⇒ Object
-
.i64_clz(x) ⇒ Object
-
.i64_ctz(x) ⇒ Object
-
.i64_div_u(a, b) ⇒ Object
-
.i64_extend_i32_s(x) ⇒ Object
-
.i64_reinterpret_f64(x) ⇒ Object
-
.i64_rem_u(a, b) ⇒ Object
-
.i64_rotl(a, b) ⇒ Object
-
.m64(x) ⇒ Object
-
.quiet_nan(x) ⇒ Object
Quiet a NaN (set the quiet bit), preserving sign and payload.
-
.resolve_import(imports, mod, name) ⇒ Object
Resolve one imported function from the embedder's imports table.
-
.s32(x) ⇒ Object
-
.s64(x) ⇒ Object
-
.sext(x, bits, mask) ⇒ Object
-
.trap(message) ⇒ Object
-
.trunc_sat(x, min, max) ⇒ Object
Class Method Details
.check_import_kind(value, kind, mod, name) ⇒ Object
A present-but-wrong-kind import (e.g. a global where a func was declared) is a link error, distinct from a missing one (which still falls through to the caller's WASI/ENOSYS/raise fallback via ||).
Function values are bare Method/Proc objects; the runtime's own
Global/Table/Memory/Tag wrappers self-report via wasm_kind.
20
21
22
23
24
25
26
27
28
29
30
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 20
def check_import_kind(value, kind, mod, name)
return value if value.nil?
ok =
if kind == :func
value.is_a?(Method) || value.is_a?(Proc)
else
value.respond_to?(:wasm_kind) && value.wasm_kind == kind
end
return value if ok
raise(LinkError, "incompatible import type for #{mod}.#{name}")
end
|
.cvt_f32_i(v) ⇒ Object
Convert a (signed) Integer to f32 with correct rounding.
Values beyond
2**53 are pre-rounded to odd so that the double->single step cannot double-round.
35
36
37
38
39
40
41
42
43
44
45
46
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 35
def cvt_f32_i(v)
a = v.abs
if a < (1 << 53)
f32(v.to_f)
else
sh = a.bit_length - 53
hi = a >> sh
hi |= 1 if a != (hi << sh)
hi = -hi if v < 0
f32(hi.to_f * (2.0**sh))
end
end
|
.cvt_f64_i(v) ⇒ Object
MRI's Integer#to_f rounds to nearest-even, which is exactly the wasm convert semantics.
49
50
51
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 49
def cvt_f64_i(v)
v.to_f
end
|
.f32(x) ⇒ Object
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 64
def f32(x)
a = x.abs
if a >= F32_MIN_NORMAL && a < F32_SPLIT_LIMIT
t = x * F32_SPLIT
return t - (t - x)
end
return x if x == 0.0
r = [x].pack("e").unpack1("e")
if r.infinite? && x.finite? && x.abs < F32_OVERFLOW
r = x < 0 ? -F32_MAX : F32_MAX
end
r
end
|
.f32_abs(x) ⇒ Object
79
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 79
def f32_abs(x) = f32_from_bits(f32_bits(x) & 0x7fff_ffff)
|
.f32_bits(x) ⇒ Object
81
82
83
84
85
86
87
88
89
90
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 81
def f32_bits(x)
if x.nan?
b = f64_bits(x)
payload = (b >> 29) & 0x7f_ffff
payload = 0x40_0000 if payload == 0
(((b >> 63) & 1) << 31) | 0x7f80_0000 | payload
else
[x].pack("e").unpack1("L<")
end
end
|
.f32_demote(x) ⇒ Object
92
93
94
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 92
def f32_demote(x)
x.nan? ? f32_from_bits((f32_bits(x) & 0x8000_0000) | 0x7fc0_0000) : f32(x)
end
|
.f32_from_bits(b) ⇒ Object
MRI's pack("e")/unpack("e") canonicalize NaNs during the double<->float conversion, losing sign and payload.
Take a software path for NaNs.
98
99
100
101
102
103
104
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 98
def f32_from_bits(b)
if (b & 0x7f80_0000) == 0x7f80_0000 && (b & 0x7f_ffff) != 0
f64_from_bits(((b >> 31) << 63) | 0x7ff0_0000_0000_0000 | ((b & 0x7f_ffff) << 29))
else
[b].pack("L<").unpack1("e")
end
end
|
.f32_neg(x) ⇒ Object
106
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 106
def f32_neg(x) = f32_from_bits(f32_bits(x) ^ 0x8000_0000)
|
.f32_reinterpret_i32(x) ⇒ Object
108
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 108
def f32_reinterpret_i32(x) = f32_from_bits(x)
|
.f64_abs(x) ⇒ Object
110
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 110
def f64_abs(x) = f64_from_bits(f64_bits(x) & 0x7fff_ffff_ffff_ffff)
|
.f64_bits(x) ⇒ Object
112
113
114
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 112
def f64_bits(x)
[x].pack("E").unpack1("Q<")
end
|
.f64_copysign(a, b) ⇒ Object
116
117
118
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 116
def f64_copysign(a, b)
f64_from_bits((f64_bits(a) & 0x7fff_ffff_ffff_ffff) | (f64_bits(b) & 0x8000_0000_0000_0000))
end
|
.f64_from_bits(b) ⇒ Object
120
121
122
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 120
def f64_from_bits(b)
[b].pack("Q<").unpack1("E")
end
|
.f64_neg(x) ⇒ Object
124
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 124
def f64_neg(x) = f64_from_bits(f64_bits(x) ^ 0x8000_0000_0000_0000)
|
126
127
128
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 126
def f64_promote(x)
x.nan? ? quiet_nan(x) : x
end
|
.f64_reinterpret_i64(x) ⇒ Object
130
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 130
def f64_reinterpret_i64(x) = f64_from_bits(x)
|
.ffloor(x) ⇒ Object
132
133
134
135
136
137
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 132
def ffloor(x)
return quiet_nan(x) if x.nan?
return x unless x.finite?
return x if x == 0.0 x.floor.to_f
end
|
.fsqrt(x) ⇒ Object
139
140
141
142
143
144
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 139
def fsqrt(x)
return quiet_nan(x) if x.nan?
return Float::NAN if x < 0.0
return x if x == 0.0 Math.sqrt(x)
end
|
.i32_clz(x) ⇒ Object
146
147
148
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 146
def i32_clz(x)
x == 0 ? 32 : 32 - x.bit_length
end
|
.i32_div_s(a, b) ⇒ Object
150
151
152
153
154
155
156
157
158
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 150
def i32_div_s(a, b)
sa = s32(a)
sb = s32(b)
trap("integer divide by zero") if sb == 0
q = sa.abs / sb.abs
q = -q if (sa < 0) ^ (sb < 0)
trap("integer overflow") if q > 0x7fff_ffff
q & M32
end
|
.i32_div_u(a, b) ⇒ Object
160
161
162
163
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 160
def i32_div_u(a, b)
trap("integer divide by zero") if b == 0
a / b
end
|
.i32_extend16_s(x) ⇒ Object
165
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 165
def i32_extend16_s(x) = sext(x, 16, M32)
|
.i32_extend8_s(x) ⇒ Object
167
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 167
def i32_extend8_s(x) = sext(x, 8, M32)
|
.i32_reinterpret_f32(x) ⇒ Object
169
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 169
def i32_reinterpret_f32(x) = f32_bits(x)
|
.i32_rem_u(a, b) ⇒ Object
171
172
173
174
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 171
def i32_rem_u(a, b)
trap("integer divide by zero") if b == 0
a % b
end
|
.i32_rotl(a, b) ⇒ Object
The left shift can leave MRI's fixnum range; masking it before the OR confines the bignum to that one operand.
177
178
179
180
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 177
def i32_rotl(a, b)
r = b & 31
((a << r) & M32) | (a >> (32 - r))
end
|
.i32_trunc_sat_s(x) ⇒ Object
182
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 182
def i32_trunc_sat_s(x) = trunc_sat(x, -0x8000_0000, 0x7fff_ffff) & M32
|
.i32_trunc_sat_u(x) ⇒ Object
184
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 184
def i32_trunc_sat_u(x) = trunc_sat(x, 0, M32)
|
.i64_clz(x) ⇒ Object
186
187
188
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 186
def i64_clz(x)
x == 0 ? 64 : 64 - x.bit_length
end
|
.i64_ctz(x) ⇒ Object
190
191
192
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 190
def i64_ctz(x)
x == 0 ? 64 : (x & -x).bit_length - 1
end
|
.i64_div_u(a, b) ⇒ Object
194
195
196
197
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 194
def i64_div_u(a, b)
trap("integer divide by zero") if b == 0
a / b
end
|
.i64_extend_i32_s(x) ⇒ Object
199
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 199
def i64_extend_i32_s(x) = m64(s32(x))
|
.i64_reinterpret_f64(x) ⇒ Object
201
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 201
def i64_reinterpret_f64(x) = f64_bits(x)
|
.i64_rem_u(a, b) ⇒ Object
203
204
205
206
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 203
def i64_rem_u(a, b)
trap("integer divide by zero") if b == 0
a % b
end
|
.i64_rotl(a, b) ⇒ Object
208
209
210
211
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 208
def i64_rotl(a, b)
r = b & 63
((a & LOW_MASK[64 - r]) << r) | (a >> (64 - r))
end
|
.m64(x) ⇒ Object
221
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 221
def m64(x) = (x >= 0 && x <= M64) ? x : (x & M64)
|
.quiet_nan(x) ⇒ Object
Quiet a NaN (set the quiet bit), preserving sign and payload.
The f64 quiet bit maps to the f32 quiet bit for NaNs that came from f32.
225
226
227
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 225
def quiet_nan(x)
f64_from_bits(f64_bits(x) | 0x0008_0000_0000_0000)
end
|
.resolve_import(imports, mod, name) ⇒ Object
Resolve one imported function from the embedder's imports table.
The value under a module name is either a Hash (name -> callable) or a provider object responding to import(name); providers may also define attach(instance), which generated code calls once the instance is fully constructed.
231
232
233
234
235
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 231
def resolve_import(imports, mod, name)
source = imports[mod]
return nil if source.nil?
source.respond_to?(:import) ? source.import(name) : source[name]
end
|
.s32(x) ⇒ Object
237
238
239
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 237
def s32(x)
x >= 0x8000_0000 ? x - 0x1_0000_0000 : x
end
|
.s64(x) ⇒ Object
241
242
243
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 241
def s64(x)
x >= 0x8000_0000_0000_0000 ? x - 0x1_0000_0000_0000_0000 : x
end
|
.sext(x, bits, mask) ⇒ Object
245
246
247
248
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 245
def sext(x, bits, mask)
half = 1 << (bits - 1)
(((x & ((1 << bits) - 1)) ^ half) - half) & mask
end
|
.trap(message) ⇒ Object
252
253
254
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 252
def trap(message)
raise Trap, message
end
|
.trunc_sat(x, min, max) ⇒ Object
256
257
258
259
260
|
# File 'lib/dewasm/pozeiden/wasm_module.rb', line 256
def trunc_sat(x, min, max)
return 0 if x.nan?
t = x.infinite? ? (x > 0 ? max : min) : x.truncate
t.clamp(min, max)
end
|