Module: Dewasm::Pozeiden::WasmModule::Rt

Included in:
Dewasm::Pozeiden::WasmModule
Defined in:
lib/dewasm/pozeiden/wasm_module.rb

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

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.

Raises:



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)

.f64_promote(x) ⇒ Object



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 # preserves -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 # preserves -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

Raises:



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