Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/fact/trampoline.rs
3092 views
1
//! Low-level compilation of an fused adapter function.
2
//!
3
//! This module is tasked with the top-level `compile` function which creates a
4
//! single WebAssembly function which will perform the steps of the fused
5
//! adapter for an `AdapterData` provided. This is the "meat" of compilation
6
//! where the validation of the canonical ABI or similar all happens to
7
//! translate arguments from one module to another.
8
//!
9
//! ## Traps and their ordering
10
//!
11
//! Currently this compiler is pretty "loose" about the ordering of precisely
12
//! what trap happens where. The main reason for this is that to core wasm all
13
//! traps are the same and for fused adapters if a trap happens no intermediate
14
//! side effects are visible (as designed by the canonical ABI itself). For this
15
//! it's important to note that some of the precise choices of control flow here
16
//! can be somewhat arbitrary, an intentional decision.
17
18
use crate::component::{
19
CanonicalAbiInfo, ComponentTypesBuilder, FLAG_MAY_LEAVE, FixedEncoding as FE, FlatType,
20
InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT,
21
PREPARE_ASYNC_WITH_RESULT, START_FLAG_ASYNC_CALLEE, StringEncoding, Transcode,
22
TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFixedLengthListIndex,
23
TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, TypeRecordIndex,
24
TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex,
25
TypeVariantIndex, VariantInfo,
26
};
27
use crate::fact::signature::Signature;
28
use crate::fact::transcode::Transcoder;
29
use crate::fact::{
30
AdapterData, Body, Function, FunctionId, Helper, HelperLocation, HelperType,
31
LinearMemoryOptions, Module, Options,
32
};
33
use crate::prelude::*;
34
use crate::{FuncIndex, GlobalIndex, Trap};
35
use std::collections::HashMap;
36
use std::mem;
37
use std::ops::Range;
38
use wasm_encoder::{BlockType, Encode, Instruction, Instruction::*, MemArg, ValType};
39
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
40
41
use super::DataModel;
42
43
const MAX_STRING_BYTE_LENGTH: u32 = 1 << 31;
44
const UTF16_TAG: u32 = 1 << 31;
45
46
/// This value is arbitrarily chosen and should be fine to change at any time,
47
/// it just seemed like a halfway reasonable starting point.
48
const INITIAL_FUEL: usize = 1_000;
49
50
struct Compiler<'a, 'b> {
51
types: &'a ComponentTypesBuilder,
52
module: &'b mut Module<'a>,
53
result: FunctionId,
54
55
/// The encoded WebAssembly function body so far, not including locals.
56
code: Vec<u8>,
57
58
/// Total number of locals generated so far.
59
nlocals: u32,
60
61
/// Locals partitioned by type which are not currently in use.
62
free_locals: HashMap<ValType, Vec<u32>>,
63
64
/// A heuristic which is intended to limit the size of a generated function
65
/// to a certain maximum to avoid generating arbitrarily large functions.
66
///
67
/// This fuel counter is decremented each time `translate` is called and
68
/// when fuel is entirely consumed further translations, if necessary, will
69
/// be done through calls to other functions in the module. This is intended
70
/// to be a heuristic to split up the main function into theoretically
71
/// reusable portions.
72
fuel: usize,
73
74
/// Indicates whether an "enter call" should be emitted in the generated
75
/// function with a call to `Resource{Enter,Exit}Call` at the beginning and
76
/// end of the function for tracking of information related to borrowed
77
/// resources.
78
emit_resource_call: bool,
79
}
80
81
pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
82
fn compiler<'a, 'b>(
83
module: &'b mut Module<'a>,
84
adapter: &AdapterData,
85
) -> (Compiler<'a, 'b>, Signature, Signature) {
86
let lower_sig = module.types.signature(&adapter.lower);
87
let lift_sig = module.types.signature(&adapter.lift);
88
let ty = module
89
.core_types
90
.function(&lower_sig.params, &lower_sig.results);
91
let result = module
92
.funcs
93
.push(Function::new(Some(adapter.name.clone()), ty));
94
95
// If this type signature contains any borrowed resources then invocations
96
// of enter/exit call for resource-related metadata tracking must be used.
97
// It shouldn't matter whether the lower/lift signature is used here as both
98
// should return the same answer.
99
let emit_resource_call = module.types.contains_borrow_resource(&adapter.lower);
100
assert_eq!(
101
emit_resource_call,
102
module.types.contains_borrow_resource(&adapter.lift)
103
);
104
105
(
106
Compiler::new(
107
module,
108
result,
109
lower_sig.params.len() as u32,
110
emit_resource_call,
111
),
112
lower_sig,
113
lift_sig,
114
)
115
}
116
117
// If the lift and lower instances are equal, or if one is an ancestor of
118
// the other, we trap unconditionally. This ensures that recursive
119
// reentrance via an adapter is impossible.
120
if adapter.lift.instance == adapter.lower.instance
121
|| adapter.lower.ancestors.contains(&adapter.lift.instance)
122
|| adapter.lift.ancestors.contains(&adapter.lower.instance)
123
{
124
let (mut compiler, _, _) = compiler(module, adapter);
125
compiler.trap(Trap::CannotEnterComponent);
126
compiler.finish();
127
return;
128
}
129
130
// This closure compiles a function to be exported to the host which host to
131
// lift the parameters from the caller and lower them to the callee.
132
//
133
// This allows the host to delay copying the parameters until the callee
134
// signals readiness by clearing its backpressure flag.
135
let async_start_adapter = |module: &mut Module| {
136
let sig = module
137
.types
138
.async_start_signature(&adapter.lower, &adapter.lift);
139
let ty = module.core_types.function(&sig.params, &sig.results);
140
let result = module.funcs.push(Function::new(
141
Some(format!("[async-start]{}", adapter.name)),
142
ty,
143
));
144
145
Compiler::new(module, result, sig.params.len() as u32, false)
146
.compile_async_start_adapter(adapter, &sig);
147
148
result
149
};
150
151
// This closure compiles a function to be exported by the adapter module and
152
// called by the host to lift the results from the callee and lower them to
153
// the caller.
154
//
155
// Given that async-lifted exports return their results via the
156
// `task.return` intrinsic, the host will need to copy the results from
157
// callee to caller when that intrinsic is called rather than when the
158
// callee task fully completes (which may happen much later).
159
let async_return_adapter = |module: &mut Module| {
160
let sig = module
161
.types
162
.async_return_signature(&adapter.lower, &adapter.lift);
163
let ty = module.core_types.function(&sig.params, &sig.results);
164
let result = module.funcs.push(Function::new(
165
Some(format!("[async-return]{}", adapter.name)),
166
ty,
167
));
168
169
Compiler::new(module, result, sig.params.len() as u32, false)
170
.compile_async_return_adapter(adapter, &sig);
171
172
result
173
};
174
175
match (adapter.lower.options.async_, adapter.lift.options.async_) {
176
(false, false) => {
177
// We can adapt sync->sync case with only minimal use of intrinsics,
178
// e.g. resource enter and exit calls as needed.
179
let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
180
compiler.compile_sync_to_sync_adapter(adapter, &lower_sig, &lift_sig)
181
}
182
(true, true) => {
183
assert!(module.tunables.concurrency_support);
184
185
// In the async->async case, we must compile a couple of helper functions:
186
//
187
// - `async-start`: copies the parameters from the caller to the callee
188
// - `async-return`: copies the result from the callee to the caller
189
//
190
// Unlike synchronous calls, the above operations are asynchronous
191
// and subject to backpressure. If the callee is not yet ready to
192
// handle a new call, the `async-start` function will not be called
193
// immediately. Instead, control will return to the caller,
194
// allowing it to do other work while waiting for this call to make
195
// progress. Once the callee indicates it is ready, `async-start`
196
// will be called, and sometime later (possibly after various task
197
// switch events), when the callee has produced a result, it will
198
// call `async-return` via the `task.return` intrinsic, at which
199
// point a `STATUS_RETURNED` event will be delivered to the caller.
200
let start = async_start_adapter(module);
201
let return_ = async_return_adapter(module);
202
let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
203
compiler.compile_async_to_async_adapter(
204
adapter,
205
start,
206
return_,
207
i32::try_from(lift_sig.params.len()).unwrap(),
208
&lower_sig,
209
);
210
}
211
(false, true) => {
212
assert!(module.tunables.concurrency_support);
213
214
// Like the async->async case above, for the sync->async case we
215
// also need `async-start` and `async-return` helper functions to
216
// allow the callee to asynchronously "pull" the parameters and
217
// "push" the results when it is ready.
218
//
219
// However, since the caller is using the synchronous ABI, the
220
// parameters may have been passed via the stack rather than linear
221
// memory. In that case, we pass them to the host to store in a
222
// task-local location temporarily in the case of backpressure.
223
// Similarly, the host will also temporarily store the results that
224
// the callee provides to `async-return` until it is ready to resume
225
// the caller.
226
let start = async_start_adapter(module);
227
let return_ = async_return_adapter(module);
228
let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
229
compiler.compile_sync_to_async_adapter(
230
adapter,
231
start,
232
return_,
233
i32::try_from(lift_sig.params.len()).unwrap(),
234
&lower_sig,
235
);
236
}
237
(true, false) => {
238
assert!(module.tunables.concurrency_support);
239
240
// As with the async->async and sync->async cases above, for the
241
// async->sync case we use `async-start` and `async-return` helper
242
// functions. Here, those functions allow the host to enforce
243
// backpressure in the case where the callee instance already has
244
// another synchronous call in progress, in which case we can't
245
// start a new one until the current one (and any others already
246
// waiting in line behind it) has completed.
247
//
248
// In the case of backpressure, we'll return control to the caller
249
// immediately so it can do other work. Later, once the callee is
250
// ready, the host will call the `async-start` function to retrieve
251
// the parameters and pass them to the callee. At that point, the
252
// callee may block on a host call, at which point the host will
253
// suspend the fiber it is running on and allow the caller (or any
254
// other ready instance) to run concurrently with the blocked
255
// callee. Once the callee finally returns, the host will call the
256
// `async-return` function to write the result to the caller's
257
// linear memory and deliver a `STATUS_RETURNED` event to the
258
// caller.
259
let lift_sig = module.types.signature(&adapter.lift);
260
let start = async_start_adapter(module);
261
let return_ = async_return_adapter(module);
262
let (compiler, lower_sig, ..) = compiler(module, adapter);
263
compiler.compile_async_to_sync_adapter(
264
adapter,
265
start,
266
return_,
267
i32::try_from(lift_sig.params.len()).unwrap(),
268
i32::try_from(lift_sig.results.len()).unwrap(),
269
&lower_sig,
270
);
271
}
272
}
273
}
274
275
/// Compiles a helper function as specified by the `Helper` configuration.
276
///
277
/// This function is invoked when the translation process runs out of fuel for
278
/// some prior function which enqueues a helper to get translated later. This
279
/// translation function will perform one type translation as specified by
280
/// `Helper` which can either be in the stack or memory for each side.
281
pub(super) fn compile_helper(module: &mut Module<'_>, result: FunctionId, helper: Helper) {
282
let mut nlocals = 0;
283
let src_flat;
284
let src = match helper.src.loc {
285
// If the source is on the stack then it's specified in the parameters
286
// to the function, so this creates the flattened representation and
287
// then lists those as the locals with appropriate types for the source
288
// values.
289
HelperLocation::Stack => {
290
src_flat = module
291
.types
292
.flatten_types(&helper.src.opts, usize::MAX, [helper.src.ty])
293
.unwrap()
294
.iter()
295
.enumerate()
296
.map(|(i, ty)| (i as u32, *ty))
297
.collect::<Vec<_>>();
298
nlocals += src_flat.len() as u32;
299
Source::Stack(Stack {
300
locals: &src_flat,
301
opts: &helper.src.opts,
302
})
303
}
304
// If the source is in memory then that's just propagated here as the
305
// first local is the pointer to the source.
306
HelperLocation::Memory => {
307
nlocals += 1;
308
Source::Memory(Memory {
309
opts: &helper.src.opts,
310
addr: TempLocal::new(0, helper.src.opts.data_model.unwrap_memory().ptr()),
311
offset: 0,
312
})
313
}
314
HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
315
};
316
let dst_flat;
317
let dst = match helper.dst.loc {
318
// This is the same as the stack-based source although `Destination` is
319
// configured slightly differently.
320
HelperLocation::Stack => {
321
dst_flat = module
322
.types
323
.flatten_types(&helper.dst.opts, usize::MAX, [helper.dst.ty])
324
.unwrap();
325
Destination::Stack(&dst_flat, &helper.dst.opts)
326
}
327
// This is the same as a memory-based source but note that the address
328
// of the destination is passed as the final parameter to the function.
329
HelperLocation::Memory => {
330
nlocals += 1;
331
Destination::Memory(Memory {
332
opts: &helper.dst.opts,
333
addr: TempLocal::new(
334
nlocals - 1,
335
helper.dst.opts.data_model.unwrap_memory().ptr(),
336
),
337
offset: 0,
338
})
339
}
340
HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
341
};
342
let mut compiler = Compiler {
343
types: module.types,
344
module,
345
code: Vec::new(),
346
nlocals,
347
free_locals: HashMap::new(),
348
result,
349
fuel: INITIAL_FUEL,
350
// This is a helper function and only the top-level function is
351
// responsible for emitting these intrinsic calls.
352
emit_resource_call: false,
353
};
354
compiler.translate(&helper.src.ty, &src, &helper.dst.ty, &dst);
355
compiler.finish();
356
}
357
358
/// Possible ways that a interface value is represented in the core wasm
359
/// canonical ABI.
360
enum Source<'a> {
361
/// This value is stored on the "stack" in wasm locals.
362
///
363
/// This could mean that it's inline from the parameters to the function or
364
/// that after a function call the results were stored in locals and the
365
/// locals are the inline results.
366
Stack(Stack<'a>),
367
368
/// This value is stored in linear memory described by the `Memory`
369
/// structure.
370
Memory(Memory<'a>),
371
372
/// This value is stored in a GC struct field described by the `GcStruct`
373
/// structure.
374
#[allow(dead_code, reason = "CM+GC is still WIP")]
375
Struct(GcStruct<'a>),
376
377
/// This value is stored in a GC array element described by the `GcArray`
378
/// structure.
379
#[allow(dead_code, reason = "CM+GC is still WIP")]
380
Array(GcArray<'a>),
381
}
382
383
/// Same as `Source` but for where values are translated into.
384
enum Destination<'a> {
385
/// This value is destined for the WebAssembly stack which means that
386
/// results are simply pushed as we go along.
387
///
388
/// The types listed are the types that are expected to be on the stack at
389
/// the end of translation.
390
Stack(&'a [ValType], &'a Options),
391
392
/// This value is to be placed in linear memory described by `Memory`.
393
Memory(Memory<'a>),
394
395
/// This value is to be placed in a GC struct field described by the
396
/// `GcStruct` structure.
397
#[allow(dead_code, reason = "CM+GC is still WIP")]
398
Struct(GcStruct<'a>),
399
400
/// This value is to be placed in a GC array element described by the
401
/// `GcArray` structure.
402
#[allow(dead_code, reason = "CM+GC is still WIP")]
403
Array(GcArray<'a>),
404
}
405
406
struct Stack<'a> {
407
/// The locals that comprise a particular value.
408
///
409
/// The length of this list represents the flattened list of types that make
410
/// up the component value. Each list has the index of the local being
411
/// accessed as well as the type of the local itself.
412
locals: &'a [(u32, ValType)],
413
/// The lifting/lowering options for where this stack of values comes from
414
opts: &'a Options,
415
}
416
417
/// Representation of where a value is going to be stored in linear memory.
418
struct Memory<'a> {
419
/// The lifting/lowering options with memory configuration
420
opts: &'a Options,
421
/// The index of the local that contains the base address of where the
422
/// storage is happening.
423
addr: TempLocal,
424
/// A "static" offset that will be baked into wasm instructions for where
425
/// memory loads/stores happen.
426
offset: u32,
427
}
428
429
impl<'a> Memory<'a> {
430
fn mem_opts(&self) -> &'a LinearMemoryOptions {
431
self.opts.data_model.unwrap_memory()
432
}
433
}
434
435
/// Representation of where a value is coming from or going to in a GC struct.
436
struct GcStruct<'a> {
437
opts: &'a Options,
438
// TODO: more fields to come in the future.
439
}
440
441
/// Representation of where a value is coming from or going to in a GC array.
442
struct GcArray<'a> {
443
opts: &'a Options,
444
// TODO: more fields to come in the future.
445
}
446
447
impl<'a, 'b> Compiler<'a, 'b> {
448
fn new(
449
module: &'b mut Module<'a>,
450
result: FunctionId,
451
nlocals: u32,
452
emit_resource_call: bool,
453
) -> Self {
454
Self {
455
types: module.types,
456
module,
457
result,
458
code: Vec::new(),
459
nlocals,
460
free_locals: HashMap::new(),
461
fuel: INITIAL_FUEL,
462
emit_resource_call,
463
}
464
}
465
466
/// Compile an adapter function supporting an async-lowered import to an
467
/// async-lifted export.
468
///
469
/// This uses a pair of `async-prepare` and `async-start` built-in functions
470
/// to set up and start a subtask, respectively. `async-prepare` accepts
471
/// `start` and `return_` functions which copy the parameters and results,
472
/// respectively; the host will call the former when the callee has cleared
473
/// its backpressure flag and the latter when the callee has called
474
/// `task.return`.
475
fn compile_async_to_async_adapter(
476
mut self,
477
adapter: &AdapterData,
478
start: FunctionId,
479
return_: FunctionId,
480
param_count: i32,
481
lower_sig: &Signature,
482
) {
483
let start_call =
484
self.module
485
.import_async_start_call(&adapter.name, adapter.lift.options.callback, None);
486
487
self.call_prepare(adapter, start, return_, lower_sig, false);
488
489
// TODO: As an optimization, consider checking the backpressure flag on
490
// the callee instance and, if it's unset _and_ the callee uses a
491
// callback, translate the params and call the callee function directly
492
// here (and make sure `start_call` knows _not_ to call it in that case).
493
494
// We export this function so we can pass a funcref to the host.
495
//
496
// TODO: Use a declarative element segment instead of exporting this.
497
self.module.exports.push((
498
adapter.callee.as_u32(),
499
format!("[adapter-callee]{}", adapter.name),
500
));
501
502
self.instruction(RefFunc(adapter.callee.as_u32()));
503
self.instruction(I32Const(param_count));
504
// The result count for an async callee is either one (if there's a
505
// callback) or zero (if there's no callback). We conservatively use
506
// one here to ensure the host provides room for the result, if any.
507
self.instruction(I32Const(1));
508
self.instruction(I32Const(START_FLAG_ASYNC_CALLEE));
509
self.instruction(Call(start_call.as_u32()));
510
511
self.finish()
512
}
513
514
/// Invokes the `prepare_call` builtin with the provided parameters for this
515
/// adapter.
516
///
517
/// This is part of a async lower and/or async lift adapter. This is not
518
/// used for a sync->sync function call. This is done to create the task on
519
/// the host side of the runtime and such. This will notably invoke a
520
/// Cranelift builtin which will spill all wasm-level parameters to the
521
/// stack to handle variadic signatures.
522
///
523
/// Note that the `prepare_sync` parameter here configures the
524
/// `result_count_or_max_if_async` parameter to indicate whether this is a
525
/// sync or async prepare.
526
fn call_prepare(
527
&mut self,
528
adapter: &AdapterData,
529
start: FunctionId,
530
return_: FunctionId,
531
lower_sig: &Signature,
532
prepare_sync: bool,
533
) {
534
let prepare = self.module.import_prepare_call(
535
&adapter.name,
536
&lower_sig.params,
537
match adapter.lift.options.data_model {
538
DataModel::Gc {} => todo!("CM+GC"),
539
DataModel::LinearMemory(LinearMemoryOptions { memory, .. }) => memory,
540
},
541
);
542
543
self.flush_code();
544
self.module.funcs[self.result]
545
.body
546
.push(Body::RefFunc(start));
547
self.module.funcs[self.result]
548
.body
549
.push(Body::RefFunc(return_));
550
self.instruction(I32Const(
551
i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
552
));
553
self.instruction(I32Const(
554
i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
555
));
556
self.instruction(I32Const(
557
i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(),
558
));
559
self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
560
1
561
} else {
562
0
563
}));
564
self.instruction(I32Const(i32::from(
565
adapter.lift.options.string_encoding as u8,
566
)));
567
568
// flag this as a preparation for either an async call or sync call,
569
// depending on `prepare_sync`
570
let result_types = &self.types[self.types[adapter.lower.ty].results].types;
571
if prepare_sync {
572
self.instruction(I32Const(
573
i32::try_from(
574
self.types
575
.flatten_types(
576
&adapter.lower.options,
577
usize::MAX,
578
result_types.iter().copied(),
579
)
580
.map(|v| v.len())
581
.unwrap_or(usize::try_from(i32::MAX).unwrap()),
582
)
583
.unwrap(),
584
));
585
} else {
586
if result_types.len() > 0 {
587
self.instruction(I32Const(PREPARE_ASYNC_WITH_RESULT.cast_signed()));
588
} else {
589
self.instruction(I32Const(PREPARE_ASYNC_NO_RESULT.cast_signed()));
590
}
591
}
592
593
// forward all our own arguments on to the host stub
594
for index in 0..lower_sig.params.len() {
595
self.instruction(LocalGet(u32::try_from(index).unwrap()));
596
}
597
self.instruction(Call(prepare.as_u32()));
598
}
599
600
/// Compile an adapter function supporting a sync-lowered import to an
601
/// async-lifted export.
602
///
603
/// This uses a pair of `sync-prepare` and `sync-start` built-in functions
604
/// to set up and start a subtask, respectively. `sync-prepare` accepts
605
/// `start` and `return_` functions which copy the parameters and results,
606
/// respectively; the host will call the former when the callee has cleared
607
/// its backpressure flag and the latter when the callee has called
608
/// `task.return`.
609
fn compile_sync_to_async_adapter(
610
mut self,
611
adapter: &AdapterData,
612
start: FunctionId,
613
return_: FunctionId,
614
lift_param_count: i32,
615
lower_sig: &Signature,
616
) {
617
let start_call = self.module.import_sync_start_call(
618
&adapter.name,
619
adapter.lift.options.callback,
620
&lower_sig.results,
621
);
622
623
self.call_prepare(adapter, start, return_, lower_sig, true);
624
625
// TODO: As an optimization, consider checking the backpressure flag on
626
// the callee instance and, if it's unset _and_ the callee uses a
627
// callback, translate the params and call the callee function directly
628
// here (and make sure `start_call` knows _not_ to call it in that case).
629
630
// We export this function so we can pass a funcref to the host.
631
//
632
// TODO: Use a declarative element segment instead of exporting this.
633
self.module.exports.push((
634
adapter.callee.as_u32(),
635
format!("[adapter-callee]{}", adapter.name),
636
));
637
638
self.instruction(RefFunc(adapter.callee.as_u32()));
639
self.instruction(I32Const(lift_param_count));
640
self.instruction(Call(start_call.as_u32()));
641
642
self.finish()
643
}
644
645
/// Compile an adapter function supporting an async-lowered import to a
646
/// sync-lifted export.
647
///
648
/// This uses a pair of `async-prepare` and `async-start` built-in functions
649
/// to set up and start a subtask, respectively. `async-prepare` accepts
650
/// `start` and `return_` functions which copy the parameters and results,
651
/// respectively; the host will call the former when the callee has cleared
652
/// its backpressure flag and the latter when the callee has returned its
653
/// result(s).
654
fn compile_async_to_sync_adapter(
655
mut self,
656
adapter: &AdapterData,
657
start: FunctionId,
658
return_: FunctionId,
659
param_count: i32,
660
result_count: i32,
661
lower_sig: &Signature,
662
) {
663
let start_call =
664
self.module
665
.import_async_start_call(&adapter.name, None, adapter.lift.post_return);
666
667
self.call_prepare(adapter, start, return_, lower_sig, false);
668
669
// We export this function so we can pass a funcref to the host.
670
//
671
// TODO: Use a declarative element segment instead of exporting this.
672
self.module.exports.push((
673
adapter.callee.as_u32(),
674
format!("[adapter-callee]{}", adapter.name),
675
));
676
677
self.instruction(RefFunc(adapter.callee.as_u32()));
678
self.instruction(I32Const(param_count));
679
self.instruction(I32Const(result_count));
680
self.instruction(I32Const(0));
681
self.instruction(Call(start_call.as_u32()));
682
683
self.finish()
684
}
685
686
/// Compiles a function to be exported to the host which host to lift the
687
/// parameters from the caller and lower them to the callee.
688
///
689
/// This allows the host to delay copying the parameters until the callee
690
/// signals readiness by clearing its backpressure flag.
691
fn compile_async_start_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
692
let param_locals = sig
693
.params
694
.iter()
695
.enumerate()
696
.map(|(i, ty)| (i as u32, *ty))
697
.collect::<Vec<_>>();
698
699
self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false);
700
self.translate_params(adapter, &param_locals);
701
self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true);
702
703
self.finish();
704
}
705
706
/// Compiles a function to be exported by the adapter module and called by
707
/// the host to lift the results from the callee and lower them to the
708
/// caller.
709
///
710
/// Given that async-lifted exports return their results via the
711
/// `task.return` intrinsic, the host will need to copy the results from
712
/// callee to caller when that intrinsic is called rather than when the
713
/// callee task fully completes (which may happen much later).
714
fn compile_async_return_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
715
let param_locals = sig
716
.params
717
.iter()
718
.enumerate()
719
.map(|(i, ty)| (i as u32, *ty))
720
.collect::<Vec<_>>();
721
722
self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false);
723
// Note that we pass `param_locals` as _both_ the `param_locals` and
724
// `result_locals` parameters to `translate_results`. That's because
725
// the _parameters_ to `task.return` are actually the _results_ that the
726
// caller is waiting for.
727
//
728
// Additionally, the host will append a return
729
// pointer to the end of that list before calling this adapter's
730
// `async-return` function if the results exceed `MAX_FLAT_RESULTS` or
731
// the import is lowered async, in which case `translate_results` will
732
// use that pointer to store the results.
733
self.translate_results(adapter, &param_locals, &param_locals);
734
self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true);
735
736
self.finish()
737
}
738
739
/// Compile an adapter function supporting a sync-lowered import to a
740
/// sync-lifted export.
741
///
742
/// Unlike calls involving async-lowered imports or async-lifted exports,
743
/// this adapter need not involve host built-ins except possibly for
744
/// resource bookkeeping.
745
fn compile_sync_to_sync_adapter(
746
mut self,
747
adapter: &AdapterData,
748
lower_sig: &Signature,
749
lift_sig: &Signature,
750
) {
751
// Check the instance flags required for this trampoline.
752
//
753
// This inserts the initial check required by `canon_lower` that the
754
// caller instance can be left and additionally checks the
755
// flags on the callee if necessary whether it can be entered.
756
self.trap_if_not_flag(
757
adapter.lower.flags,
758
FLAG_MAY_LEAVE,
759
Trap::CannotLeaveComponent,
760
);
761
762
let old_task_may_block = if self.module.tunables.concurrency_support {
763
// Save, clear, and later restore the `may_block` field.
764
let task_may_block = self.module.import_task_may_block();
765
let old_task_may_block = if self.types[adapter.lift.ty].async_ {
766
self.instruction(GlobalGet(task_may_block.as_u32()));
767
self.instruction(I32Eqz);
768
self.instruction(If(BlockType::Empty));
769
self.trap(Trap::CannotBlockSyncTask);
770
self.instruction(End);
771
None
772
} else {
773
let task_may_block = self.module.import_task_may_block();
774
self.instruction(GlobalGet(task_may_block.as_u32()));
775
let old_task_may_block = self.local_set_new_tmp(ValType::I32);
776
self.instruction(I32Const(0));
777
self.instruction(GlobalSet(task_may_block.as_u32()));
778
Some(old_task_may_block)
779
};
780
781
// Push a task onto the current task stack.
782
//
783
// FIXME: Apply the optimizations described in #12311.
784
785
self.instruction(I32Const(
786
i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
787
));
788
self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
789
1
790
} else {
791
0
792
}));
793
self.instruction(I32Const(
794
i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
795
));
796
let enter_sync_call = self.module.import_enter_sync_call();
797
self.instruction(Call(enter_sync_call.as_u32()));
798
799
old_task_may_block
800
} else if self.emit_resource_call {
801
let enter_sync_call = self.module.import_enter_sync_call();
802
self.instruction(Call(enter_sync_call.as_u32()));
803
None
804
} else {
805
None
806
};
807
808
// Perform the translation of arguments. Note that `FLAG_MAY_LEAVE` is
809
// cleared around this invocation for the callee as per the
810
// `canon_lift` definition in the spec. Additionally note that the
811
// precise ordering of traps here is not required since internal state
812
// is not visible to either instance and a trap will "lock down" both
813
// instances to no longer be visible. This means that we're free to
814
// reorder lifts/lowers and flags and such as is necessary and
815
// convenient here.
816
//
817
// TODO: if translation doesn't actually call any functions in either
818
// instance then there's no need to set/clear the flag here and that can
819
// be optimized away.
820
self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false);
821
let param_locals = lower_sig
822
.params
823
.iter()
824
.enumerate()
825
.map(|(i, ty)| (i as u32, *ty))
826
.collect::<Vec<_>>();
827
self.translate_params(adapter, &param_locals);
828
self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true);
829
830
// With all the arguments on the stack the actual target function is
831
// now invoked. The core wasm results of the function are then placed
832
// into locals for result translation afterwards.
833
self.instruction(Call(adapter.callee.as_u32()));
834
let mut result_locals = Vec::with_capacity(lift_sig.results.len());
835
let mut temps = Vec::new();
836
for ty in lift_sig.results.iter().rev() {
837
let local = self.local_set_new_tmp(*ty);
838
result_locals.push((local.idx, *ty));
839
temps.push(local);
840
}
841
result_locals.reverse();
842
843
// Like above during the translation of results the caller cannot be
844
// left (as we might invoke things like `realloc`). Again the precise
845
// order of everything doesn't matter since intermediate states cannot
846
// be witnessed, hence the setting of flags here to encapsulate both
847
// liftings and lowerings.
848
//
849
// TODO: like above the management of the `MAY_LEAVE` flag can probably
850
// be elided here for "simple" results.
851
self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false);
852
self.translate_results(adapter, &param_locals, &result_locals);
853
self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true);
854
855
// And finally post-return state is handled here once all results/etc
856
// are all translated.
857
if let Some(func) = adapter.lift.post_return {
858
for (result, _) in result_locals.iter() {
859
self.instruction(LocalGet(*result));
860
}
861
self.instruction(Call(func.as_u32()));
862
}
863
864
for tmp in temps {
865
self.free_temp_local(tmp);
866
}
867
868
if self.emit_resource_call || self.module.tunables.concurrency_support {
869
// Pop the task we pushed earlier off of the current task stack.
870
//
871
// FIXME: Apply the optimizations described in #12311.
872
let exit_sync_call = self.module.import_exit_sync_call();
873
self.instruction(Call(exit_sync_call.as_u32()));
874
}
875
876
if self.module.tunables.concurrency_support {
877
// Restore old `may_block_field`
878
if let Some(old_task_may_block) = old_task_may_block {
879
let task_may_block = self.module.import_task_may_block();
880
self.instruction(LocalGet(old_task_may_block.idx));
881
self.instruction(GlobalSet(task_may_block.as_u32()));
882
self.free_temp_local(old_task_may_block);
883
}
884
}
885
886
self.finish()
887
}
888
889
fn translate_params(&mut self, adapter: &AdapterData, param_locals: &[(u32, ValType)]) {
890
let src_tys = self.types[adapter.lower.ty].params;
891
let src_tys = self.types[src_tys]
892
.types
893
.iter()
894
.copied()
895
.collect::<Vec<_>>();
896
let dst_tys = self.types[adapter.lift.ty].params;
897
let dst_tys = self.types[dst_tys]
898
.types
899
.iter()
900
.copied()
901
.collect::<Vec<_>>();
902
let lift_opts = &adapter.lift.options;
903
let lower_opts = &adapter.lower.options;
904
905
// TODO: handle subtyping
906
assert_eq!(src_tys.len(), dst_tys.len());
907
908
// Async lowered functions have a smaller limit on flat parameters, but
909
// their destination, a lifted function, does not have a different limit
910
// than sync functions.
911
let max_flat_params = if adapter.lower.options.async_ {
912
MAX_FLAT_ASYNC_PARAMS
913
} else {
914
MAX_FLAT_PARAMS
915
};
916
let src_flat =
917
self.types
918
.flatten_types(lower_opts, max_flat_params, src_tys.iter().copied());
919
let dst_flat =
920
self.types
921
.flatten_types(lift_opts, MAX_FLAT_PARAMS, dst_tys.iter().copied());
922
923
let src = if let Some(flat) = &src_flat {
924
Source::Stack(Stack {
925
locals: &param_locals[..flat.len()],
926
opts: lower_opts,
927
})
928
} else {
929
// If there are too many parameters then that means the parameters
930
// are actually a tuple stored in linear memory addressed by the
931
// first parameter local.
932
let lower_mem_opts = lower_opts.data_model.unwrap_memory();
933
let (addr, ty) = param_locals[0];
934
assert_eq!(ty, lower_mem_opts.ptr());
935
let align = src_tys
936
.iter()
937
.map(|t| self.types.align(lower_mem_opts, t))
938
.max()
939
.unwrap_or(1);
940
Source::Memory(self.memory_operand(lower_opts, TempLocal::new(addr, ty), align))
941
};
942
943
let dst = if let Some(flat) = &dst_flat {
944
Destination::Stack(flat, lift_opts)
945
} else {
946
let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
947
match lift_opts.data_model {
948
DataModel::Gc {} => todo!("CM+GC"),
949
DataModel::LinearMemory(LinearMemoryOptions { memory64, .. }) => {
950
let (size, align) = if memory64 {
951
(abi.size64, abi.align64)
952
} else {
953
(abi.size32, abi.align32)
954
};
955
956
// If there are too many parameters then space is allocated in the
957
// destination module for the parameters via its `realloc` function.
958
let size = MallocSize::Const(size);
959
Destination::Memory(self.malloc(lift_opts, size, align))
960
}
961
}
962
};
963
964
let srcs = src
965
.record_field_srcs(self.types, src_tys.iter().copied())
966
.zip(src_tys.iter());
967
let dsts = dst
968
.record_field_dsts(self.types, dst_tys.iter().copied())
969
.zip(dst_tys.iter());
970
for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
971
self.translate(&src_ty, &src, &dst_ty, &dst);
972
}
973
974
// If the destination was linear memory instead of the stack then the
975
// actual parameter that we're passing is the address of the values
976
// stored, so ensure that's happening in the wasm body here.
977
if let Destination::Memory(mem) = dst {
978
self.instruction(LocalGet(mem.addr.idx));
979
self.free_temp_local(mem.addr);
980
}
981
}
982
983
fn translate_results(
984
&mut self,
985
adapter: &AdapterData,
986
param_locals: &[(u32, ValType)],
987
result_locals: &[(u32, ValType)],
988
) {
989
let src_tys = self.types[adapter.lift.ty].results;
990
let src_tys = self.types[src_tys]
991
.types
992
.iter()
993
.copied()
994
.collect::<Vec<_>>();
995
let dst_tys = self.types[adapter.lower.ty].results;
996
let dst_tys = self.types[dst_tys]
997
.types
998
.iter()
999
.copied()
1000
.collect::<Vec<_>>();
1001
let lift_opts = &adapter.lift.options;
1002
let lower_opts = &adapter.lower.options;
1003
1004
let src_flat = self
1005
.types
1006
.flatten_lifting_types(lift_opts, src_tys.iter().copied());
1007
let dst_flat = self
1008
.types
1009
.flatten_lowering_types(lower_opts, dst_tys.iter().copied());
1010
1011
let src = if src_flat.is_some() {
1012
Source::Stack(Stack {
1013
locals: result_locals,
1014
opts: lift_opts,
1015
})
1016
} else {
1017
// The original results to read from in this case come from the
1018
// return value of the function itself. The imported function will
1019
// return a linear memory address at which the values can be read
1020
// from.
1021
let lift_mem_opts = lift_opts.data_model.unwrap_memory();
1022
let align = src_tys
1023
.iter()
1024
.map(|t| self.types.align(lift_mem_opts, t))
1025
.max()
1026
.unwrap_or(1);
1027
assert_eq!(
1028
result_locals.len(),
1029
if lower_opts.async_ || lift_opts.async_ {
1030
2
1031
} else {
1032
1
1033
}
1034
);
1035
let (addr, ty) = result_locals[0];
1036
assert_eq!(ty, lift_opts.data_model.unwrap_memory().ptr());
1037
Source::Memory(self.memory_operand(lift_opts, TempLocal::new(addr, ty), align))
1038
};
1039
1040
let dst = if let Some(flat) = &dst_flat {
1041
Destination::Stack(flat, lower_opts)
1042
} else {
1043
// This is slightly different than `translate_params` where the
1044
// return pointer was provided by the caller of this function
1045
// meaning the last parameter local is a pointer into linear memory.
1046
let lower_mem_opts = lower_opts.data_model.unwrap_memory();
1047
let align = dst_tys
1048
.iter()
1049
.map(|t| self.types.align(lower_mem_opts, t))
1050
.max()
1051
.unwrap_or(1);
1052
let (addr, ty) = *param_locals.last().expect("no retptr");
1053
assert_eq!(ty, lower_opts.data_model.unwrap_memory().ptr());
1054
Destination::Memory(self.memory_operand(lower_opts, TempLocal::new(addr, ty), align))
1055
};
1056
1057
let srcs = src
1058
.record_field_srcs(self.types, src_tys.iter().copied())
1059
.zip(src_tys.iter());
1060
let dsts = dst
1061
.record_field_dsts(self.types, dst_tys.iter().copied())
1062
.zip(dst_tys.iter());
1063
for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
1064
self.translate(&src_ty, &src, &dst_ty, &dst);
1065
}
1066
}
1067
1068
fn translate(
1069
&mut self,
1070
src_ty: &InterfaceType,
1071
src: &Source<'_>,
1072
dst_ty: &InterfaceType,
1073
dst: &Destination,
1074
) {
1075
if let Source::Memory(mem) = src {
1076
self.assert_aligned(src_ty, mem);
1077
}
1078
if let Destination::Memory(mem) = dst {
1079
self.assert_aligned(dst_ty, mem);
1080
}
1081
1082
// Calculate a cost heuristic for what the translation of this specific
1083
// layer of the type is going to incur. The purpose of this cost is that
1084
// we'll deduct it from `self.fuel` and if no fuel is remaining then
1085
// translation is outlined into a separate function rather than being
1086
// translated into this function.
1087
//
1088
// The general goal is to avoid creating an exponentially sized function
1089
// for a linearly sized input (the type section). By outlining helper
1090
// functions there will ideally be a constant set of helper functions
1091
// per type (to accommodate in-memory or on-stack transfers as well as
1092
// src/dst options) which means that each function is at most a certain
1093
// size and we have a linear number of functions which should guarantee
1094
// an overall linear size of the output.
1095
//
1096
// To implement this the current heuristic is that each layer of
1097
// translating a type has a cost associated with it and this cost is
1098
// accounted for in `self.fuel`. Some conversions are considered free as
1099
// they generate basically as much code as the `call` to the translation
1100
// function while other are considered proportionally expensive to the
1101
// size of the type. The hope is that some upper layers are of a type's
1102
// translation are all inlined into one function but bottom layers end
1103
// up getting outlined to separate functions. Theoretically, again this
1104
// is built on hopes and dreams, the outlining can be shared amongst
1105
// tightly-intertwined type hierarchies which will reduce the size of
1106
// the output module due to the helpers being used.
1107
//
1108
// This heuristic of how to split functions has changed a few times in
1109
// the past and this isn't necessarily guaranteed to be the final
1110
// iteration.
1111
let cost = match src_ty {
1112
// These types are all quite simple to load/store and equate to
1113
// basically the same cost of the `call` instruction to call an
1114
// out-of-line translation function, so give them 0 cost.
1115
InterfaceType::Bool
1116
| InterfaceType::U8
1117
| InterfaceType::S8
1118
| InterfaceType::U16
1119
| InterfaceType::S16
1120
| InterfaceType::U32
1121
| InterfaceType::S32
1122
| InterfaceType::U64
1123
| InterfaceType::S64
1124
| InterfaceType::Float32
1125
| InterfaceType::Float64 => 0,
1126
1127
// This has a small amount of validation associated with it, so
1128
// give it a cost of 1.
1129
InterfaceType::Char => 1,
1130
1131
// This has a fair bit of code behind it depending on the
1132
// strings/encodings in play, so arbitrarily assign it this cost.
1133
InterfaceType::String => 40,
1134
1135
// Iteration of a loop is along the lines of the cost of a string
1136
// so give it the same cost
1137
InterfaceType::List(_) => 40,
1138
1139
InterfaceType::Flags(i) => {
1140
let count = self.module.types[*i].names.len();
1141
match FlagsSize::from_count(count) {
1142
FlagsSize::Size0 => 0,
1143
FlagsSize::Size1 | FlagsSize::Size2 => 1,
1144
FlagsSize::Size4Plus(n) => n.into(),
1145
}
1146
}
1147
1148
InterfaceType::Record(i) => self.types[*i].fields.len(),
1149
InterfaceType::Tuple(i) => self.types[*i].types.len(),
1150
InterfaceType::Variant(i) => self.types[*i].cases.len(),
1151
InterfaceType::Enum(i) => self.types[*i].names.len(),
1152
1153
// 2 cases to consider for each of these variants.
1154
InterfaceType::Option(_) | InterfaceType::Result(_) => 2,
1155
1156
// TODO(#6696) - something nonzero, is 1 right?
1157
InterfaceType::Own(_)
1158
| InterfaceType::Borrow(_)
1159
| InterfaceType::Future(_)
1160
| InterfaceType::Stream(_)
1161
| InterfaceType::ErrorContext(_) => 1,
1162
InterfaceType::FixedLengthList(i) => self.types[*i].size as usize,
1163
};
1164
1165
match self.fuel.checked_sub(cost) {
1166
// This function has enough fuel to perform the layer of translation
1167
// necessary for this type, so the fuel is updated in-place and
1168
// translation continues. Note that the recursion here is bounded by
1169
// the static recursion limit for all interface types as imposed
1170
// during the translation phase.
1171
Some(n) => {
1172
self.fuel = n;
1173
match src_ty {
1174
InterfaceType::Bool => self.translate_bool(src, dst_ty, dst),
1175
InterfaceType::U8 => self.translate_u8(src, dst_ty, dst),
1176
InterfaceType::S8 => self.translate_s8(src, dst_ty, dst),
1177
InterfaceType::U16 => self.translate_u16(src, dst_ty, dst),
1178
InterfaceType::S16 => self.translate_s16(src, dst_ty, dst),
1179
InterfaceType::U32 => self.translate_u32(src, dst_ty, dst),
1180
InterfaceType::S32 => self.translate_s32(src, dst_ty, dst),
1181
InterfaceType::U64 => self.translate_u64(src, dst_ty, dst),
1182
InterfaceType::S64 => self.translate_s64(src, dst_ty, dst),
1183
InterfaceType::Float32 => self.translate_f32(src, dst_ty, dst),
1184
InterfaceType::Float64 => self.translate_f64(src, dst_ty, dst),
1185
InterfaceType::Char => self.translate_char(src, dst_ty, dst),
1186
InterfaceType::String => self.translate_string(src, dst_ty, dst),
1187
InterfaceType::List(t) => self.translate_list(*t, src, dst_ty, dst),
1188
InterfaceType::Record(t) => self.translate_record(*t, src, dst_ty, dst),
1189
InterfaceType::Flags(f) => self.translate_flags(*f, src, dst_ty, dst),
1190
InterfaceType::Tuple(t) => self.translate_tuple(*t, src, dst_ty, dst),
1191
InterfaceType::Variant(v) => self.translate_variant(*v, src, dst_ty, dst),
1192
InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst),
1193
InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst),
1194
InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst),
1195
InterfaceType::Own(t) => self.translate_own(*t, src, dst_ty, dst),
1196
InterfaceType::Borrow(t) => self.translate_borrow(*t, src, dst_ty, dst),
1197
InterfaceType::Future(t) => self.translate_future(*t, src, dst_ty, dst),
1198
InterfaceType::Stream(t) => self.translate_stream(*t, src, dst_ty, dst),
1199
InterfaceType::ErrorContext(t) => {
1200
self.translate_error_context(*t, src, dst_ty, dst)
1201
}
1202
InterfaceType::FixedLengthList(t) => {
1203
self.translate_fixed_length_list(*t, src, dst_ty, dst);
1204
}
1205
}
1206
}
1207
1208
// This function does not have enough fuel left to perform this
1209
// layer of translation so the translation is deferred to a helper
1210
// function. The actual translation here is then done by marshalling
1211
// the src/dst into the function we're calling and then processing
1212
// the results.
1213
None => {
1214
let src_loc = match src {
1215
// If the source is on the stack then `stack_get` is used to
1216
// convert everything to the appropriate flat representation
1217
// for the source type.
1218
Source::Stack(stack) => {
1219
for (i, ty) in stack
1220
.opts
1221
.flat_types(src_ty, self.types)
1222
.unwrap()
1223
.iter()
1224
.enumerate()
1225
{
1226
let stack = stack.slice(i..i + 1);
1227
self.stack_get(&stack, (*ty).into());
1228
}
1229
HelperLocation::Stack
1230
}
1231
// If the source is in memory then the pointer is passed
1232
// through, but note that the offset must be factored in
1233
// here since the translation function will start from
1234
// offset 0.
1235
Source::Memory(mem) => {
1236
self.push_mem_addr(mem);
1237
HelperLocation::Memory
1238
}
1239
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1240
};
1241
let dst_loc = match dst {
1242
Destination::Stack(..) => HelperLocation::Stack,
1243
Destination::Memory(mem) => {
1244
self.push_mem_addr(mem);
1245
HelperLocation::Memory
1246
}
1247
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1248
};
1249
// Generate a `FunctionId` corresponding to the `Helper`
1250
// configuration that is necessary here. This will ideally be a
1251
// "cache hit" and use a preexisting helper which represents
1252
// outlining what would otherwise be duplicate code within a
1253
// function to one function.
1254
let helper = self.module.translate_helper(Helper {
1255
src: HelperType {
1256
ty: *src_ty,
1257
opts: *src.opts(),
1258
loc: src_loc,
1259
},
1260
dst: HelperType {
1261
ty: *dst_ty,
1262
opts: *dst.opts(),
1263
loc: dst_loc,
1264
},
1265
});
1266
// Emit a `call` instruction which will get "relocated" to a
1267
// function index once translation has completely finished.
1268
self.flush_code();
1269
self.module.funcs[self.result].body.push(Body::Call(helper));
1270
1271
// If the destination of the translation was on the stack then
1272
// the types on the stack need to be optionally converted to
1273
// different types (e.g. if the result here is part of a variant
1274
// somewhere else).
1275
//
1276
// This translation happens inline here by popping the results
1277
// into new locals and then using those locals to do a
1278
// `stack_set`.
1279
if let Destination::Stack(tys, opts) = dst {
1280
let flat = self
1281
.types
1282
.flatten_types(opts, usize::MAX, [*dst_ty])
1283
.unwrap();
1284
assert_eq!(flat.len(), tys.len());
1285
let locals = flat
1286
.iter()
1287
.rev()
1288
.map(|ty| self.local_set_new_tmp(*ty))
1289
.collect::<Vec<_>>();
1290
for (ty, local) in tys.iter().zip(locals.into_iter().rev()) {
1291
self.instruction(LocalGet(local.idx));
1292
self.stack_set(std::slice::from_ref(ty), local.ty);
1293
self.free_temp_local(local);
1294
}
1295
}
1296
}
1297
}
1298
}
1299
1300
fn push_mem_addr(&mut self, mem: &Memory<'_>) {
1301
self.instruction(LocalGet(mem.addr.idx));
1302
if mem.offset != 0 {
1303
self.ptr_uconst(mem.mem_opts(), mem.offset);
1304
self.ptr_add(mem.mem_opts());
1305
}
1306
}
1307
1308
fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1309
// TODO: subtyping
1310
assert!(matches!(dst_ty, InterfaceType::Bool));
1311
self.push_dst_addr(dst);
1312
1313
// Booleans are canonicalized to 0 or 1 as they pass through the
1314
// component boundary, so use a `select` instruction to do so.
1315
self.instruction(I32Const(1));
1316
self.instruction(I32Const(0));
1317
match src {
1318
Source::Memory(mem) => self.i32_load8u(mem),
1319
Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1320
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1321
}
1322
self.instruction(Select);
1323
1324
match dst {
1325
Destination::Memory(mem) => self.i32_store8(mem),
1326
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1327
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1328
}
1329
}
1330
1331
fn translate_u8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1332
// TODO: subtyping
1333
assert!(matches!(dst_ty, InterfaceType::U8));
1334
self.convert_u8_mask(src, dst, 0xff);
1335
}
1336
1337
fn convert_u8_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u8) {
1338
self.push_dst_addr(dst);
1339
let mut needs_mask = true;
1340
match src {
1341
Source::Memory(mem) => {
1342
self.i32_load8u(mem);
1343
needs_mask = mask != 0xff;
1344
}
1345
Source::Stack(stack) => {
1346
self.stack_get(stack, ValType::I32);
1347
}
1348
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1349
}
1350
if needs_mask {
1351
self.instruction(I32Const(i32::from(mask)));
1352
self.instruction(I32And);
1353
}
1354
match dst {
1355
Destination::Memory(mem) => self.i32_store8(mem),
1356
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1357
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1358
}
1359
}
1360
1361
fn translate_s8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1362
// TODO: subtyping
1363
assert!(matches!(dst_ty, InterfaceType::S8));
1364
self.push_dst_addr(dst);
1365
match src {
1366
Source::Memory(mem) => self.i32_load8s(mem),
1367
Source::Stack(stack) => {
1368
self.stack_get(stack, ValType::I32);
1369
self.instruction(I32Extend8S);
1370
}
1371
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1372
}
1373
match dst {
1374
Destination::Memory(mem) => self.i32_store8(mem),
1375
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1376
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1377
}
1378
}
1379
1380
fn translate_u16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1381
// TODO: subtyping
1382
assert!(matches!(dst_ty, InterfaceType::U16));
1383
self.convert_u16_mask(src, dst, 0xffff);
1384
}
1385
1386
fn convert_u16_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u16) {
1387
self.push_dst_addr(dst);
1388
let mut needs_mask = true;
1389
match src {
1390
Source::Memory(mem) => {
1391
self.i32_load16u(mem);
1392
needs_mask = mask != 0xffff;
1393
}
1394
Source::Stack(stack) => {
1395
self.stack_get(stack, ValType::I32);
1396
}
1397
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1398
}
1399
if needs_mask {
1400
self.instruction(I32Const(i32::from(mask)));
1401
self.instruction(I32And);
1402
}
1403
match dst {
1404
Destination::Memory(mem) => self.i32_store16(mem),
1405
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1406
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1407
}
1408
}
1409
1410
fn translate_s16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1411
// TODO: subtyping
1412
assert!(matches!(dst_ty, InterfaceType::S16));
1413
self.push_dst_addr(dst);
1414
match src {
1415
Source::Memory(mem) => self.i32_load16s(mem),
1416
Source::Stack(stack) => {
1417
self.stack_get(stack, ValType::I32);
1418
self.instruction(I32Extend16S);
1419
}
1420
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1421
}
1422
match dst {
1423
Destination::Memory(mem) => self.i32_store16(mem),
1424
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1425
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1426
}
1427
}
1428
1429
fn translate_u32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1430
// TODO: subtyping
1431
assert!(matches!(dst_ty, InterfaceType::U32));
1432
self.convert_u32_mask(src, dst, 0xffffffff)
1433
}
1434
1435
fn convert_u32_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u32) {
1436
self.push_dst_addr(dst);
1437
match src {
1438
Source::Memory(mem) => self.i32_load(mem),
1439
Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1440
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1441
}
1442
if mask != 0xffffffff {
1443
self.instruction(I32Const(mask as i32));
1444
self.instruction(I32And);
1445
}
1446
match dst {
1447
Destination::Memory(mem) => self.i32_store(mem),
1448
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1449
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1450
}
1451
}
1452
1453
fn translate_s32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1454
// TODO: subtyping
1455
assert!(matches!(dst_ty, InterfaceType::S32));
1456
self.push_dst_addr(dst);
1457
match src {
1458
Source::Memory(mem) => self.i32_load(mem),
1459
Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1460
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1461
}
1462
match dst {
1463
Destination::Memory(mem) => self.i32_store(mem),
1464
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1465
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1466
}
1467
}
1468
1469
fn translate_u64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1470
// TODO: subtyping
1471
assert!(matches!(dst_ty, InterfaceType::U64));
1472
self.push_dst_addr(dst);
1473
match src {
1474
Source::Memory(mem) => self.i64_load(mem),
1475
Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1476
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1477
}
1478
match dst {
1479
Destination::Memory(mem) => self.i64_store(mem),
1480
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1481
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1482
}
1483
}
1484
1485
fn translate_s64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1486
// TODO: subtyping
1487
assert!(matches!(dst_ty, InterfaceType::S64));
1488
self.push_dst_addr(dst);
1489
match src {
1490
Source::Memory(mem) => self.i64_load(mem),
1491
Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1492
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1493
}
1494
match dst {
1495
Destination::Memory(mem) => self.i64_store(mem),
1496
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1497
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1498
}
1499
}
1500
1501
fn translate_f32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1502
// TODO: subtyping
1503
assert!(matches!(dst_ty, InterfaceType::Float32));
1504
self.push_dst_addr(dst);
1505
match src {
1506
Source::Memory(mem) => self.f32_load(mem),
1507
Source::Stack(stack) => self.stack_get(stack, ValType::F32),
1508
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1509
}
1510
match dst {
1511
Destination::Memory(mem) => self.f32_store(mem),
1512
Destination::Stack(stack, _) => self.stack_set(stack, ValType::F32),
1513
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1514
}
1515
}
1516
1517
fn translate_f64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1518
// TODO: subtyping
1519
assert!(matches!(dst_ty, InterfaceType::Float64));
1520
self.push_dst_addr(dst);
1521
match src {
1522
Source::Memory(mem) => self.f64_load(mem),
1523
Source::Stack(stack) => self.stack_get(stack, ValType::F64),
1524
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1525
}
1526
match dst {
1527
Destination::Memory(mem) => self.f64_store(mem),
1528
Destination::Stack(stack, _) => self.stack_set(stack, ValType::F64),
1529
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1530
}
1531
}
1532
1533
fn translate_char(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1534
assert!(matches!(dst_ty, InterfaceType::Char));
1535
match src {
1536
Source::Memory(mem) => self.i32_load(mem),
1537
Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1538
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1539
}
1540
let local = self.local_set_new_tmp(ValType::I32);
1541
1542
// This sequence is copied from the output of LLVM for:
1543
//
1544
// pub extern "C" fn foo(x: u32) -> char {
1545
// char::try_from(x)
1546
// .unwrap_or_else(|_| std::arch::wasm32::unreachable())
1547
// }
1548
//
1549
// Apparently this does what's required by the canonical ABI:
1550
//
1551
// def i32_to_char(opts, i):
1552
// trap_if(i >= 0x110000)
1553
// trap_if(0xD800 <= i <= 0xDFFF)
1554
// return chr(i)
1555
//
1556
// ... but I don't know how it works other than "well I trust LLVM"
1557
self.instruction(Block(BlockType::Empty));
1558
self.instruction(Block(BlockType::Empty));
1559
self.instruction(LocalGet(local.idx));
1560
self.instruction(I32Const(0xd800));
1561
self.instruction(I32Xor);
1562
self.instruction(I32Const(-0x110000));
1563
self.instruction(I32Add);
1564
self.instruction(I32Const(-0x10f800));
1565
self.instruction(I32LtU);
1566
self.instruction(BrIf(0));
1567
self.instruction(LocalGet(local.idx));
1568
self.instruction(I32Const(0x110000));
1569
self.instruction(I32Ne);
1570
self.instruction(BrIf(1));
1571
self.instruction(End);
1572
self.trap(Trap::InvalidChar);
1573
self.instruction(End);
1574
1575
self.push_dst_addr(dst);
1576
self.instruction(LocalGet(local.idx));
1577
match dst {
1578
Destination::Memory(mem) => {
1579
self.i32_store(mem);
1580
}
1581
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1582
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1583
}
1584
1585
self.free_temp_local(local);
1586
}
1587
1588
fn translate_string(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1589
assert!(matches!(dst_ty, InterfaceType::String));
1590
let src_opts = src.opts();
1591
let dst_opts = dst.opts();
1592
1593
let src_mem_opts = match &src_opts.data_model {
1594
DataModel::Gc {} => todo!("CM+GC"),
1595
DataModel::LinearMemory(opts) => opts,
1596
};
1597
let dst_mem_opts = match &dst_opts.data_model {
1598
DataModel::Gc {} => todo!("CM+GC"),
1599
DataModel::LinearMemory(opts) => opts,
1600
};
1601
1602
// Load the pointer/length of this string into temporary locals. These
1603
// will be referenced a good deal so this just makes it easier to deal
1604
// with them consistently below rather than trying to reload from memory
1605
// for example.
1606
match src {
1607
Source::Stack(s) => {
1608
assert_eq!(s.locals.len(), 2);
1609
self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
1610
self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
1611
}
1612
Source::Memory(mem) => {
1613
self.ptr_load(mem);
1614
self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
1615
}
1616
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1617
}
1618
let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
1619
let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
1620
let src_str = WasmString {
1621
ptr: src_ptr,
1622
len: src_len,
1623
opts: src_opts,
1624
};
1625
1626
let dst_str = match src_opts.string_encoding {
1627
StringEncoding::Utf8 => match dst_opts.string_encoding {
1628
StringEncoding::Utf8 => self.string_copy(&src_str, FE::Utf8, dst_opts, FE::Utf8),
1629
StringEncoding::Utf16 => self.string_utf8_to_utf16(&src_str, dst_opts),
1630
StringEncoding::CompactUtf16 => {
1631
self.string_to_compact(&src_str, FE::Utf8, dst_opts)
1632
}
1633
},
1634
1635
StringEncoding::Utf16 => {
1636
self.verify_aligned(src_mem_opts, src_str.ptr.idx, 2);
1637
match dst_opts.string_encoding {
1638
StringEncoding::Utf8 => {
1639
self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1640
}
1641
StringEncoding::Utf16 => {
1642
self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1643
}
1644
StringEncoding::CompactUtf16 => {
1645
self.string_to_compact(&src_str, FE::Utf16, dst_opts)
1646
}
1647
}
1648
}
1649
1650
StringEncoding::CompactUtf16 => {
1651
self.verify_aligned(src_mem_opts, src_str.ptr.idx, 2);
1652
1653
// Test the tag big to see if this is a utf16 or a latin1 string
1654
// at runtime...
1655
self.instruction(LocalGet(src_str.len.idx));
1656
self.ptr_uconst(src_mem_opts, UTF16_TAG);
1657
self.ptr_and(src_mem_opts);
1658
self.ptr_if(src_mem_opts, BlockType::Empty);
1659
1660
// In the utf16 block unset the upper bit from the length local
1661
// so further calculations have the right value. Afterwards the
1662
// string transcode proceeds assuming utf16.
1663
self.instruction(LocalGet(src_str.len.idx));
1664
self.ptr_uconst(src_mem_opts, UTF16_TAG);
1665
self.ptr_xor(src_mem_opts);
1666
self.instruction(LocalSet(src_str.len.idx));
1667
let s1 = match dst_opts.string_encoding {
1668
StringEncoding::Utf8 => {
1669
self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1670
}
1671
StringEncoding::Utf16 => {
1672
self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1673
}
1674
StringEncoding::CompactUtf16 => {
1675
self.string_compact_utf16_to_compact(&src_str, dst_opts)
1676
}
1677
};
1678
1679
self.instruction(Else);
1680
1681
// In the latin1 block the `src_len` local is already the number
1682
// of code units, so the string transcoding is all that needs to
1683
// happen.
1684
let s2 = match dst_opts.string_encoding {
1685
StringEncoding::Utf16 => {
1686
self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Utf16)
1687
}
1688
StringEncoding::Utf8 => {
1689
self.string_deflate_to_utf8(&src_str, FE::Latin1, dst_opts)
1690
}
1691
StringEncoding::CompactUtf16 => {
1692
self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Latin1)
1693
}
1694
};
1695
// Set our `s2` generated locals to the `s2` generated locals
1696
// as the resulting pointer of this transcode.
1697
self.instruction(LocalGet(s2.ptr.idx));
1698
self.instruction(LocalSet(s1.ptr.idx));
1699
self.instruction(LocalGet(s2.len.idx));
1700
self.instruction(LocalSet(s1.len.idx));
1701
self.instruction(End);
1702
self.free_temp_local(s2.ptr);
1703
self.free_temp_local(s2.len);
1704
s1
1705
}
1706
};
1707
1708
// Store the ptr/length in the desired destination
1709
match dst {
1710
Destination::Stack(s, _) => {
1711
self.instruction(LocalGet(dst_str.ptr.idx));
1712
self.stack_set(&s[..1], dst_mem_opts.ptr());
1713
self.instruction(LocalGet(dst_str.len.idx));
1714
self.stack_set(&s[1..], dst_mem_opts.ptr());
1715
}
1716
Destination::Memory(mem) => {
1717
self.instruction(LocalGet(mem.addr.idx));
1718
self.instruction(LocalGet(dst_str.ptr.idx));
1719
self.ptr_store(mem);
1720
self.instruction(LocalGet(mem.addr.idx));
1721
self.instruction(LocalGet(dst_str.len.idx));
1722
self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into()));
1723
}
1724
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1725
}
1726
1727
self.free_temp_local(src_str.ptr);
1728
self.free_temp_local(src_str.len);
1729
self.free_temp_local(dst_str.ptr);
1730
self.free_temp_local(dst_str.len);
1731
}
1732
1733
// Corresponding function for `store_string_copy` in the spec.
1734
//
1735
// This performs a transcoding of the string with a one-pass copy from
1736
// the `src` encoding to the `dst` encoding. This is only possible for
1737
// fixed encodings where the first allocation is guaranteed to be an
1738
// appropriate fit so it's not suitable for all encodings.
1739
//
1740
// Imported host transcoding functions here take the src/dst pointers as
1741
// well as the number of code units in the source (which always matches
1742
// the number of code units in the destination). There is no return
1743
// value from the transcode function since the encoding should always
1744
// work on the first pass.
1745
fn string_copy<'c>(
1746
&mut self,
1747
src: &WasmString<'_>,
1748
src_enc: FE,
1749
dst_opts: &'c Options,
1750
dst_enc: FE,
1751
) -> WasmString<'c> {
1752
assert!(dst_enc.width() >= src_enc.width());
1753
self.validate_string_length(src, dst_enc);
1754
1755
let src_mem_opts = {
1756
match &src.opts.data_model {
1757
DataModel::Gc {} => todo!("CM+GC"),
1758
DataModel::LinearMemory(opts) => opts,
1759
}
1760
};
1761
let dst_mem_opts = {
1762
match &dst_opts.data_model {
1763
DataModel::Gc {} => todo!("CM+GC"),
1764
DataModel::LinearMemory(opts) => opts,
1765
}
1766
};
1767
1768
// Calculate the source byte length given the size of each code
1769
// unit. Note that this shouldn't overflow given
1770
// `validate_string_length` above.
1771
let mut src_byte_len_tmp = None;
1772
let src_byte_len = if src_enc.width() == 1 {
1773
src.len.idx
1774
} else {
1775
assert_eq!(src_enc.width(), 2);
1776
self.instruction(LocalGet(src.len.idx));
1777
self.ptr_uconst(src_mem_opts, 1);
1778
self.ptr_shl(src_mem_opts);
1779
let tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1780
let ret = tmp.idx;
1781
src_byte_len_tmp = Some(tmp);
1782
ret
1783
};
1784
1785
// Convert the source code units length to the destination byte
1786
// length type.
1787
self.convert_src_len_to_dst(
1788
src.len.idx,
1789
src.opts.data_model.unwrap_memory().ptr(),
1790
dst_opts.data_model.unwrap_memory().ptr(),
1791
);
1792
let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1793
if dst_enc.width() > 1 {
1794
assert_eq!(dst_enc.width(), 2);
1795
self.ptr_uconst(dst_mem_opts, 1);
1796
self.ptr_shl(dst_mem_opts);
1797
}
1798
let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1799
1800
// Allocate space in the destination using the calculated byte
1801
// length.
1802
let dst = {
1803
let dst_mem = self.malloc(
1804
dst_opts,
1805
MallocSize::Local(dst_byte_len.idx),
1806
dst_enc.width().into(),
1807
);
1808
WasmString {
1809
ptr: dst_mem.addr,
1810
len: dst_len,
1811
opts: dst_opts,
1812
}
1813
};
1814
1815
// Validate that `src_len + src_ptr` and
1816
// `dst_mem.addr_local + dst_byte_len` are both in-bounds. This
1817
// is done by loading the last byte of the string and if that
1818
// doesn't trap then it's known valid.
1819
self.validate_string_inbounds(src, src_byte_len);
1820
self.validate_string_inbounds(&dst, dst_byte_len.idx);
1821
1822
// If the validations pass then the host `transcode` intrinsic
1823
// is invoked. This will either raise a trap or otherwise succeed
1824
// in which case we're done.
1825
let op = if src_enc == dst_enc {
1826
Transcode::Copy(src_enc)
1827
} else {
1828
assert_eq!(src_enc, FE::Latin1);
1829
assert_eq!(dst_enc, FE::Utf16);
1830
Transcode::Latin1ToUtf16
1831
};
1832
let transcode = self.transcoder(src, &dst, op);
1833
self.instruction(LocalGet(src.ptr.idx));
1834
self.instruction(LocalGet(src.len.idx));
1835
self.instruction(LocalGet(dst.ptr.idx));
1836
self.instruction(Call(transcode.as_u32()));
1837
1838
self.free_temp_local(dst_byte_len);
1839
if let Some(tmp) = src_byte_len_tmp {
1840
self.free_temp_local(tmp);
1841
}
1842
1843
dst
1844
}
1845
// Corresponding function for `store_string_to_utf8` in the spec.
1846
//
1847
// This translation works by possibly performing a number of
1848
// reallocations. First a buffer of size input-code-units is used to try
1849
// to get the transcoding correct on the first try. If that fails the
1850
// maximum worst-case size is used and then that is resized down if it's
1851
// too large.
1852
//
1853
// The host transcoding function imported here will receive src ptr/len
1854
// and dst ptr/len and return how many code units were consumed on both
1855
// sides. The amount of code units consumed in the source dictates which
1856
// branches are taken in this conversion.
1857
fn string_deflate_to_utf8<'c>(
1858
&mut self,
1859
src: &WasmString<'_>,
1860
src_enc: FE,
1861
dst_opts: &'c Options,
1862
) -> WasmString<'c> {
1863
let src_mem_opts = match &src.opts.data_model {
1864
DataModel::Gc {} => todo!("CM+GC"),
1865
DataModel::LinearMemory(opts) => opts,
1866
};
1867
let dst_mem_opts = match &dst_opts.data_model {
1868
DataModel::Gc {} => todo!("CM+GC"),
1869
DataModel::LinearMemory(opts) => opts,
1870
};
1871
1872
self.validate_string_length(src, src_enc);
1873
1874
// Optimistically assume that the code unit length of the source is
1875
// all that's needed in the destination. Perform that allocation
1876
// here and proceed to transcoding below.
1877
self.convert_src_len_to_dst(
1878
src.len.idx,
1879
src.opts.data_model.unwrap_memory().ptr(),
1880
dst_opts.data_model.unwrap_memory().ptr(),
1881
);
1882
let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1883
let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1884
1885
let dst = {
1886
let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 1);
1887
WasmString {
1888
ptr: dst_mem.addr,
1889
len: dst_len,
1890
opts: dst_opts,
1891
}
1892
};
1893
1894
// Ensure buffers are all in-bounds
1895
let mut src_byte_len_tmp = None;
1896
let src_byte_len = match src_enc {
1897
FE::Latin1 => src.len.idx,
1898
FE::Utf16 => {
1899
self.instruction(LocalGet(src.len.idx));
1900
self.ptr_uconst(src_mem_opts, 1);
1901
self.ptr_shl(src_mem_opts);
1902
let tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1903
let ret = tmp.idx;
1904
src_byte_len_tmp = Some(tmp);
1905
ret
1906
}
1907
FE::Utf8 => unreachable!(),
1908
};
1909
self.validate_string_inbounds(src, src_byte_len);
1910
self.validate_string_inbounds(&dst, dst_byte_len.idx);
1911
1912
// Perform the initial transcode
1913
let op = match src_enc {
1914
FE::Latin1 => Transcode::Latin1ToUtf8,
1915
FE::Utf16 => Transcode::Utf16ToUtf8,
1916
FE::Utf8 => unreachable!(),
1917
};
1918
let transcode = self.transcoder(src, &dst, op);
1919
self.instruction(LocalGet(src.ptr.idx));
1920
self.instruction(LocalGet(src.len.idx));
1921
self.instruction(LocalGet(dst.ptr.idx));
1922
self.instruction(LocalGet(dst_byte_len.idx));
1923
self.instruction(Call(transcode.as_u32()));
1924
self.instruction(LocalSet(dst.len.idx));
1925
let src_len_tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1926
1927
// Test if the source was entirely transcoded by comparing
1928
// `src_len_tmp`, the number of code units transcoded from the
1929
// source, with `src_len`, the original number of code units.
1930
self.instruction(LocalGet(src_len_tmp.idx));
1931
self.instruction(LocalGet(src.len.idx));
1932
self.ptr_ne(src_mem_opts);
1933
self.instruction(If(BlockType::Empty));
1934
1935
// Here a worst-case reallocation is performed to grow `dst_mem`.
1936
// In-line a check is also performed that the worst-case byte size
1937
// fits within the maximum size of strings.
1938
self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
1939
self.instruction(LocalGet(dst_byte_len.idx)); // old_size
1940
self.ptr_uconst(dst_mem_opts, 1); // align
1941
let factor = match src_enc {
1942
FE::Latin1 => 2,
1943
FE::Utf16 => 3,
1944
_ => unreachable!(),
1945
};
1946
self.validate_string_length_u8(src, factor);
1947
self.convert_src_len_to_dst(
1948
src.len.idx,
1949
src.opts.data_model.unwrap_memory().ptr(),
1950
dst_opts.data_model.unwrap_memory().ptr(),
1951
);
1952
self.ptr_uconst(dst_mem_opts, factor.into());
1953
self.ptr_mul(dst_mem_opts);
1954
self.instruction(LocalTee(dst_byte_len.idx));
1955
self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
1956
self.instruction(LocalSet(dst.ptr.idx));
1957
1958
// Verify that the destination is still in-bounds
1959
self.validate_string_inbounds(&dst, dst_byte_len.idx);
1960
1961
// Perform another round of transcoding that should be guaranteed
1962
// to succeed. Note that all the parameters here are offset by the
1963
// results of the first transcoding to only perform the remaining
1964
// transcode on the final units.
1965
self.instruction(LocalGet(src.ptr.idx));
1966
self.instruction(LocalGet(src_len_tmp.idx));
1967
if let FE::Utf16 = src_enc {
1968
self.ptr_uconst(src_mem_opts, 1);
1969
self.ptr_shl(src_mem_opts);
1970
}
1971
self.ptr_add(src_mem_opts);
1972
self.instruction(LocalGet(src.len.idx));
1973
self.instruction(LocalGet(src_len_tmp.idx));
1974
self.ptr_sub(src_mem_opts);
1975
self.instruction(LocalGet(dst.ptr.idx));
1976
self.instruction(LocalGet(dst.len.idx));
1977
self.ptr_add(dst_mem_opts);
1978
self.instruction(LocalGet(dst_byte_len.idx));
1979
self.instruction(LocalGet(dst.len.idx));
1980
self.ptr_sub(dst_mem_opts);
1981
self.instruction(Call(transcode.as_u32()));
1982
1983
// Add the second result, the amount of destination units encoded,
1984
// to `dst_len` so it's an accurate reflection of the final size of
1985
// the destination buffer.
1986
self.instruction(LocalGet(dst.len.idx));
1987
self.ptr_add(dst_mem_opts);
1988
self.instruction(LocalSet(dst.len.idx));
1989
1990
// In debug mode verify the first result consumed the entire string,
1991
// otherwise simply discard it.
1992
if self.module.tunables.debug_adapter_modules {
1993
self.instruction(LocalGet(src.len.idx));
1994
self.instruction(LocalGet(src_len_tmp.idx));
1995
self.ptr_sub(src_mem_opts);
1996
self.ptr_ne(src_mem_opts);
1997
self.instruction(If(BlockType::Empty));
1998
self.trap(Trap::DebugAssertStringEncodingFinished);
1999
self.instruction(End);
2000
} else {
2001
self.instruction(Drop);
2002
}
2003
2004
// Perform a downsizing if the worst-case size was too large
2005
self.instruction(LocalGet(dst.len.idx));
2006
self.instruction(LocalGet(dst_byte_len.idx));
2007
self.ptr_ne(dst_mem_opts);
2008
self.instruction(If(BlockType::Empty));
2009
self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2010
self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2011
self.ptr_uconst(dst_mem_opts, 1); // align
2012
self.instruction(LocalGet(dst.len.idx)); // new_size
2013
self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2014
self.instruction(LocalSet(dst.ptr.idx));
2015
self.instruction(End);
2016
2017
// If the first transcode was enough then assert that the returned
2018
// amount of destination items written equals the byte size.
2019
if self.module.tunables.debug_adapter_modules {
2020
self.instruction(Else);
2021
2022
self.instruction(LocalGet(dst.len.idx));
2023
self.instruction(LocalGet(dst_byte_len.idx));
2024
self.ptr_ne(dst_mem_opts);
2025
self.instruction(If(BlockType::Empty));
2026
self.trap(Trap::DebugAssertStringEncodingFinished);
2027
self.instruction(End);
2028
}
2029
2030
self.instruction(End); // end of "first transcode not enough"
2031
2032
self.free_temp_local(src_len_tmp);
2033
self.free_temp_local(dst_byte_len);
2034
if let Some(tmp) = src_byte_len_tmp {
2035
self.free_temp_local(tmp);
2036
}
2037
2038
dst
2039
}
2040
2041
// Corresponds to the `store_utf8_to_utf16` function in the spec.
2042
//
2043
// When converting utf-8 to utf-16 a pessimistic allocation is
2044
// done which is twice the byte length of the utf-8 string.
2045
// The host then transcodes and returns how many code units were
2046
// actually used during the transcoding and if it's beneath the
2047
// pessimistic maximum then the buffer is reallocated down to
2048
// a smaller amount.
2049
//
2050
// The host-imported transcoding function takes the src/dst pointer as
2051
// well as the code unit size of both the source and destination. The
2052
// destination should always be big enough to hold the result of the
2053
// transcode and so the result of the host function is how many code
2054
// units were written to the destination.
2055
fn string_utf8_to_utf16<'c>(
2056
&mut self,
2057
src: &WasmString<'_>,
2058
dst_opts: &'c Options,
2059
) -> WasmString<'c> {
2060
let src_mem_opts = match &src.opts.data_model {
2061
DataModel::Gc {} => todo!("CM+GC"),
2062
DataModel::LinearMemory(opts) => opts,
2063
};
2064
let dst_mem_opts = match &dst_opts.data_model {
2065
DataModel::Gc {} => todo!("CM+GC"),
2066
DataModel::LinearMemory(opts) => opts,
2067
};
2068
2069
self.validate_string_length(src, FE::Utf16);
2070
self.convert_src_len_to_dst(
2071
src.len.idx,
2072
src_mem_opts.ptr(),
2073
dst_opts.data_model.unwrap_memory().ptr(),
2074
);
2075
let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2076
self.ptr_uconst(dst_mem_opts, 1);
2077
self.ptr_shl(dst_mem_opts);
2078
let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2079
let dst = {
2080
let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 2);
2081
WasmString {
2082
ptr: dst_mem.addr,
2083
len: dst_len,
2084
opts: dst_opts,
2085
}
2086
};
2087
2088
self.validate_string_inbounds(src, src.len.idx);
2089
self.validate_string_inbounds(&dst, dst_byte_len.idx);
2090
2091
let transcode = self.transcoder(src, &dst, Transcode::Utf8ToUtf16);
2092
self.instruction(LocalGet(src.ptr.idx));
2093
self.instruction(LocalGet(src.len.idx));
2094
self.instruction(LocalGet(dst.ptr.idx));
2095
self.instruction(Call(transcode.as_u32()));
2096
self.instruction(LocalSet(dst.len.idx));
2097
2098
// If the number of code units returned by transcode is not
2099
// equal to the original number of code units then
2100
// the buffer must be shrunk.
2101
//
2102
// Note that the byte length of the final allocation we
2103
// want is twice the code unit length returned by the
2104
// transcoding function.
2105
self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2106
self.instruction(LocalGet(dst.len.idx));
2107
self.ptr_ne(dst_mem_opts);
2108
self.instruction(If(BlockType::Empty));
2109
self.instruction(LocalGet(dst.ptr.idx));
2110
self.instruction(LocalGet(dst_byte_len.idx));
2111
self.ptr_uconst(dst_mem_opts, 2);
2112
self.instruction(LocalGet(dst.len.idx));
2113
self.ptr_uconst(dst_mem_opts, 1);
2114
self.ptr_shl(dst_mem_opts);
2115
self.instruction(Call(match dst.opts.data_model {
2116
DataModel::Gc {} => todo!("CM+GC"),
2117
DataModel::LinearMemory(LinearMemoryOptions { realloc, .. }) => {
2118
realloc.unwrap().as_u32()
2119
}
2120
}));
2121
self.instruction(LocalSet(dst.ptr.idx));
2122
self.instruction(End); // end of shrink-to-fit
2123
2124
self.free_temp_local(dst_byte_len);
2125
2126
dst
2127
}
2128
2129
// Corresponds to `store_probably_utf16_to_latin1_or_utf16` in the spec.
2130
//
2131
// This will try to transcode the input utf16 string to utf16 in the
2132
// destination. If utf16 isn't needed though and latin1 could be used
2133
// then that's used instead and a reallocation to downsize occurs
2134
// afterwards.
2135
//
2136
// The host transcode function here will take the src/dst pointers as
2137
// well as src length. The destination byte length is twice the src code
2138
// unit length. The return value is the tagged length of the returned
2139
// string. If the upper bit is set then utf16 was used and the
2140
// conversion is done. If the upper bit is not set then latin1 was used
2141
// and a downsizing needs to happen.
2142
fn string_compact_utf16_to_compact<'c>(
2143
&mut self,
2144
src: &WasmString<'_>,
2145
dst_opts: &'c Options,
2146
) -> WasmString<'c> {
2147
let src_mem_opts = match &src.opts.data_model {
2148
DataModel::Gc {} => todo!("CM+GC"),
2149
DataModel::LinearMemory(opts) => opts,
2150
};
2151
let dst_mem_opts = match &dst_opts.data_model {
2152
DataModel::Gc {} => todo!("CM+GC"),
2153
DataModel::LinearMemory(opts) => opts,
2154
};
2155
2156
self.validate_string_length(src, FE::Utf16);
2157
self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2158
let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2159
self.ptr_uconst(dst_mem_opts, 1);
2160
self.ptr_shl(dst_mem_opts);
2161
let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2162
let dst = {
2163
let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 2);
2164
WasmString {
2165
ptr: dst_mem.addr,
2166
len: dst_len,
2167
opts: dst_opts,
2168
}
2169
};
2170
2171
self.convert_src_len_to_dst(
2172
dst_byte_len.idx,
2173
dst.opts.data_model.unwrap_memory().ptr(),
2174
src_mem_opts.ptr(),
2175
);
2176
let src_byte_len = self.local_set_new_tmp(src_mem_opts.ptr());
2177
2178
self.validate_string_inbounds(src, src_byte_len.idx);
2179
self.validate_string_inbounds(&dst, dst_byte_len.idx);
2180
2181
let transcode = self.transcoder(src, &dst, Transcode::Utf16ToCompactProbablyUtf16);
2182
self.instruction(LocalGet(src.ptr.idx));
2183
self.instruction(LocalGet(src.len.idx));
2184
self.instruction(LocalGet(dst.ptr.idx));
2185
self.instruction(Call(transcode.as_u32()));
2186
self.instruction(LocalSet(dst.len.idx));
2187
2188
// Assert that the untagged code unit length is the same as the
2189
// source code unit length.
2190
if self.module.tunables.debug_adapter_modules {
2191
self.instruction(LocalGet(dst.len.idx));
2192
self.ptr_uconst(dst_mem_opts, !UTF16_TAG);
2193
self.ptr_and(dst_mem_opts);
2194
self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2195
self.ptr_ne(dst_mem_opts);
2196
self.instruction(If(BlockType::Empty));
2197
self.trap(Trap::DebugAssertEqualCodeUnits);
2198
self.instruction(End);
2199
}
2200
2201
// If the UTF16_TAG is set then utf16 was used and the destination
2202
// should be appropriately sized. Bail out of the "is this string
2203
// empty" block and fall through otherwise to resizing.
2204
self.instruction(LocalGet(dst.len.idx));
2205
self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2206
self.ptr_and(dst_mem_opts);
2207
self.ptr_br_if(dst_mem_opts, 0);
2208
2209
// Here `realloc` is used to downsize the string
2210
self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2211
self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2212
self.ptr_uconst(dst_mem_opts, 2); // align
2213
self.instruction(LocalGet(dst.len.idx)); // new_size
2214
self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2215
self.instruction(LocalSet(dst.ptr.idx));
2216
2217
self.free_temp_local(dst_byte_len);
2218
self.free_temp_local(src_byte_len);
2219
2220
dst
2221
}
2222
2223
// Corresponds to `store_string_to_latin1_or_utf16` in the spec.
2224
//
2225
// This will attempt a first pass of transcoding to latin1 and on
2226
// failure a larger buffer is allocated for utf16 and then utf16 is
2227
// encoded in-place into the buffer. After either latin1 or utf16 the
2228
// buffer is then resized to fit the final string allocation.
2229
fn string_to_compact<'c>(
2230
&mut self,
2231
src: &WasmString<'_>,
2232
src_enc: FE,
2233
dst_opts: &'c Options,
2234
) -> WasmString<'c> {
2235
let src_mem_opts = match &src.opts.data_model {
2236
DataModel::Gc {} => todo!("CM+GC"),
2237
DataModel::LinearMemory(opts) => opts,
2238
};
2239
let dst_mem_opts = match &dst_opts.data_model {
2240
DataModel::Gc {} => todo!("CM+GC"),
2241
DataModel::LinearMemory(opts) => opts,
2242
};
2243
2244
self.validate_string_length(src, src_enc);
2245
self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2246
let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2247
let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2248
let dst = {
2249
let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 2);
2250
WasmString {
2251
ptr: dst_mem.addr,
2252
len: dst_len,
2253
opts: dst_opts,
2254
}
2255
};
2256
2257
self.validate_string_inbounds(src, src.len.idx);
2258
self.validate_string_inbounds(&dst, dst_byte_len.idx);
2259
2260
// Perform the initial latin1 transcode. This returns the number of
2261
// source code units consumed and the number of destination code
2262
// units (bytes) written.
2263
let (latin1, utf16) = match src_enc {
2264
FE::Utf8 => (Transcode::Utf8ToLatin1, Transcode::Utf8ToCompactUtf16),
2265
FE::Utf16 => (Transcode::Utf16ToLatin1, Transcode::Utf16ToCompactUtf16),
2266
FE::Latin1 => unreachable!(),
2267
};
2268
let transcode_latin1 = self.transcoder(src, &dst, latin1);
2269
let transcode_utf16 = self.transcoder(src, &dst, utf16);
2270
self.instruction(LocalGet(src.ptr.idx));
2271
self.instruction(LocalGet(src.len.idx));
2272
self.instruction(LocalGet(dst.ptr.idx));
2273
self.instruction(Call(transcode_latin1.as_u32()));
2274
self.instruction(LocalSet(dst.len.idx));
2275
let src_len_tmp = self.local_set_new_tmp(src_mem_opts.ptr());
2276
2277
// If the source was entirely consumed then the transcode completed
2278
// and all that's necessary is to optionally shrink the buffer.
2279
self.instruction(LocalGet(src_len_tmp.idx));
2280
self.instruction(LocalGet(src.len.idx));
2281
self.ptr_eq(src_mem_opts);
2282
self.instruction(If(BlockType::Empty)); // if latin1-or-utf16 block
2283
2284
// Test if the original byte length of the allocation is the same as
2285
// the number of written bytes, and if not then shrink the buffer
2286
// with a call to `realloc`.
2287
self.instruction(LocalGet(dst_byte_len.idx));
2288
self.instruction(LocalGet(dst.len.idx));
2289
self.ptr_ne(dst_mem_opts);
2290
self.instruction(If(BlockType::Empty));
2291
self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2292
self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2293
self.ptr_uconst(dst_mem_opts, 2); // align
2294
self.instruction(LocalGet(dst.len.idx)); // new_size
2295
self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2296
self.instruction(LocalSet(dst.ptr.idx));
2297
self.instruction(End);
2298
2299
// In this block the latin1 encoding failed. The host transcode
2300
// returned how many units were consumed from the source and how
2301
// many bytes were written to the destination. Here the buffer is
2302
// inflated and sized and the second utf16 intrinsic is invoked to
2303
// perform the final inflation.
2304
self.instruction(Else); // else latin1-or-utf16 block
2305
2306
// For utf8 validate that the inflated size is still within bounds.
2307
if src_enc.width() == 1 {
2308
self.validate_string_length_u8(src, 2);
2309
}
2310
2311
// Reallocate the buffer with twice the source code units in byte
2312
// size.
2313
self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2314
self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2315
self.ptr_uconst(dst_mem_opts, 2); // align
2316
self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2317
self.ptr_uconst(dst_mem_opts, 1);
2318
self.ptr_shl(dst_mem_opts);
2319
self.instruction(LocalTee(dst_byte_len.idx));
2320
self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2321
self.instruction(LocalSet(dst.ptr.idx));
2322
2323
// Call the host utf16 transcoding function. This will inflate the
2324
// prior latin1 bytes and then encode the rest of the source string
2325
// as utf16 into the remaining space in the destination buffer.
2326
self.instruction(LocalGet(src.ptr.idx));
2327
self.instruction(LocalGet(src_len_tmp.idx));
2328
if let FE::Utf16 = src_enc {
2329
self.ptr_uconst(src_mem_opts, 1);
2330
self.ptr_shl(src_mem_opts);
2331
}
2332
self.ptr_add(src_mem_opts);
2333
self.instruction(LocalGet(src.len.idx));
2334
self.instruction(LocalGet(src_len_tmp.idx));
2335
self.ptr_sub(src_mem_opts);
2336
self.instruction(LocalGet(dst.ptr.idx));
2337
self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2338
self.instruction(LocalGet(dst.len.idx));
2339
self.instruction(Call(transcode_utf16.as_u32()));
2340
self.instruction(LocalSet(dst.len.idx));
2341
2342
// If the returned number of code units written to the destination
2343
// is not equal to the size of the allocation then the allocation is
2344
// resized down to the appropriate size.
2345
//
2346
// Note that the byte size desired is `2*dst_len` and the current
2347
// byte buffer size is `2*src_len` so the `2` factor isn't checked
2348
// here, just the lengths.
2349
self.instruction(LocalGet(dst.len.idx));
2350
self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2351
self.ptr_ne(dst_mem_opts);
2352
self.instruction(If(BlockType::Empty));
2353
self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2354
self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2355
self.ptr_uconst(dst_mem_opts, 2); // align
2356
self.instruction(LocalGet(dst.len.idx));
2357
self.ptr_uconst(dst_mem_opts, 1);
2358
self.ptr_shl(dst_mem_opts);
2359
self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2360
self.instruction(LocalSet(dst.ptr.idx));
2361
self.instruction(End);
2362
2363
// Tag the returned pointer as utf16
2364
self.instruction(LocalGet(dst.len.idx));
2365
self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2366
self.ptr_or(dst_mem_opts);
2367
self.instruction(LocalSet(dst.len.idx));
2368
2369
self.instruction(End); // end latin1-or-utf16 block
2370
2371
self.free_temp_local(src_len_tmp);
2372
self.free_temp_local(dst_byte_len);
2373
2374
dst
2375
}
2376
2377
fn validate_string_length(&mut self, src: &WasmString<'_>, dst: FE) {
2378
self.validate_string_length_u8(src, dst.width())
2379
}
2380
2381
fn validate_string_length_u8(&mut self, s: &WasmString<'_>, dst: u8) {
2382
let mem_opts = match &s.opts.data_model {
2383
DataModel::Gc {} => todo!("CM+GC"),
2384
DataModel::LinearMemory(opts) => opts,
2385
};
2386
2387
// Check to see if the source byte length is out of bounds in
2388
// which case a trap is generated.
2389
self.instruction(LocalGet(s.len.idx));
2390
let max = MAX_STRING_BYTE_LENGTH / u32::from(dst);
2391
self.ptr_uconst(mem_opts, max);
2392
self.ptr_ge_u(mem_opts);
2393
self.instruction(If(BlockType::Empty));
2394
self.trap(Trap::StringOutOfBounds);
2395
self.instruction(End);
2396
}
2397
2398
fn transcoder(
2399
&mut self,
2400
src: &WasmString<'_>,
2401
dst: &WasmString<'_>,
2402
op: Transcode,
2403
) -> FuncIndex {
2404
match (src.opts.data_model, dst.opts.data_model) {
2405
(DataModel::Gc {}, _) | (_, DataModel::Gc {}) => {
2406
todo!("CM+GC")
2407
}
2408
(
2409
DataModel::LinearMemory(LinearMemoryOptions {
2410
memory64: src64,
2411
memory: src_mem,
2412
realloc: _,
2413
}),
2414
DataModel::LinearMemory(LinearMemoryOptions {
2415
memory64: dst64,
2416
memory: dst_mem,
2417
realloc: _,
2418
}),
2419
) => self.module.import_transcoder(Transcoder {
2420
from_memory: src_mem.unwrap(),
2421
from_memory64: src64,
2422
to_memory: dst_mem.unwrap(),
2423
to_memory64: dst64,
2424
op,
2425
}),
2426
}
2427
}
2428
2429
fn validate_string_inbounds(&mut self, s: &WasmString<'_>, byte_len: u32) {
2430
match &s.opts.data_model {
2431
DataModel::Gc {} => todo!("CM+GC"),
2432
DataModel::LinearMemory(opts) => {
2433
self.validate_memory_inbounds(opts, s.ptr.idx, byte_len, Trap::StringOutOfBounds)
2434
}
2435
}
2436
}
2437
2438
fn validate_memory_inbounds(
2439
&mut self,
2440
opts: &LinearMemoryOptions,
2441
ptr_local: u32,
2442
byte_len_local: u32,
2443
trap: Trap,
2444
) {
2445
let extend_to_64 = |me: &mut Self| {
2446
if !opts.memory64 {
2447
me.instruction(I64ExtendI32U);
2448
}
2449
};
2450
2451
self.instruction(Block(BlockType::Empty));
2452
self.instruction(Block(BlockType::Empty));
2453
2454
// Calculate the full byte size of memory with `memory.size`. Note that
2455
// arithmetic here is done always in 64-bits to accommodate 4G memories.
2456
// Additionally it's assumed that 64-bit memories never fill up
2457
// entirely.
2458
self.instruction(MemorySize(opts.memory.unwrap().as_u32()));
2459
extend_to_64(self);
2460
self.instruction(I64Const(16));
2461
self.instruction(I64Shl);
2462
2463
// Calculate the end address of the string. This is done by adding the
2464
// base pointer to the byte length. For 32-bit memories there's no need
2465
// to check for overflow since everything is extended to 64-bit, but for
2466
// 64-bit memories overflow is checked.
2467
self.instruction(LocalGet(ptr_local));
2468
extend_to_64(self);
2469
self.instruction(LocalGet(byte_len_local));
2470
extend_to_64(self);
2471
self.instruction(I64Add);
2472
if opts.memory64 {
2473
let tmp = self.local_tee_new_tmp(ValType::I64);
2474
self.instruction(LocalGet(ptr_local));
2475
self.ptr_lt_u(opts);
2476
self.instruction(BrIf(0));
2477
self.instruction(LocalGet(tmp.idx));
2478
self.free_temp_local(tmp);
2479
}
2480
2481
// If the byte size of memory is greater than the final address of the
2482
// string then the string is invalid. Note that if it's precisely equal
2483
// then that's ok.
2484
self.instruction(I64GeU);
2485
self.instruction(BrIf(1));
2486
2487
self.instruction(End);
2488
self.trap(trap);
2489
self.instruction(End);
2490
}
2491
2492
fn translate_list(
2493
&mut self,
2494
src_ty: TypeListIndex,
2495
src: &Source<'_>,
2496
dst_ty: &InterfaceType,
2497
dst: &Destination,
2498
) {
2499
let src_mem_opts = match &src.opts().data_model {
2500
DataModel::Gc {} => todo!("CM+GC"),
2501
DataModel::LinearMemory(opts) => opts,
2502
};
2503
let dst_mem_opts = match &dst.opts().data_model {
2504
DataModel::Gc {} => todo!("CM+GC"),
2505
DataModel::LinearMemory(opts) => opts,
2506
};
2507
2508
let src_element_ty = &self.types[src_ty].element;
2509
let dst_element_ty = match dst_ty {
2510
InterfaceType::List(r) => &self.types[*r].element,
2511
_ => panic!("expected a list"),
2512
};
2513
let src_opts = src.opts();
2514
let dst_opts = dst.opts();
2515
let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty);
2516
let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty);
2517
2518
// Load the pointer/length of this list into temporary locals. These
2519
// will be referenced a good deal so this just makes it easier to deal
2520
// with them consistently below rather than trying to reload from memory
2521
// for example.
2522
match src {
2523
Source::Stack(s) => {
2524
assert_eq!(s.locals.len(), 2);
2525
self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
2526
self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
2527
}
2528
Source::Memory(mem) => {
2529
self.ptr_load(mem);
2530
self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
2531
}
2532
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
2533
}
2534
let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
2535
let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2536
2537
// Create a `Memory` operand which will internally assert that the
2538
// `src_ptr` value is properly aligned.
2539
let src_mem = self.memory_operand(src_opts, src_ptr, src_align);
2540
2541
// Calculate the source/destination byte lengths into unique locals.
2542
let src_byte_len = self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_size);
2543
let dst_byte_len = if src_size == dst_size {
2544
self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2545
self.local_set_new_tmp(dst_mem_opts.ptr())
2546
} else if src_mem_opts.ptr() == dst_mem_opts.ptr() {
2547
self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_size)
2548
} else {
2549
self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2550
let tmp = self.local_set_new_tmp(dst_mem_opts.ptr());
2551
let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_size);
2552
self.free_temp_local(tmp);
2553
ret
2554
};
2555
2556
// Here `realloc` is invoked (in a `malloc`-like fashion) to allocate
2557
// space for the list in the destination memory. This will also
2558
// internally insert checks that the returned pointer is aligned
2559
// correctly for the destination.
2560
let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), dst_align);
2561
2562
// With all the pointers and byte lengths verity that both the source
2563
// and the destination buffers are in-bounds.
2564
self.validate_memory_inbounds(
2565
src_mem_opts,
2566
src_mem.addr.idx,
2567
src_byte_len.idx,
2568
Trap::ListOutOfBounds,
2569
);
2570
self.validate_memory_inbounds(
2571
dst_mem_opts,
2572
dst_mem.addr.idx,
2573
dst_byte_len.idx,
2574
Trap::ListOutOfBounds,
2575
);
2576
2577
self.free_temp_local(src_byte_len);
2578
self.free_temp_local(dst_byte_len);
2579
2580
// This is the main body of the loop to actually translate list types.
2581
// Note that if both element sizes are 0 then this won't actually do
2582
// anything so the loop is removed entirely.
2583
if src_size > 0 || dst_size > 0 {
2584
// This block encompasses the entire loop and is use to exit before even
2585
// entering the loop if the list size is zero.
2586
self.instruction(Block(BlockType::Empty));
2587
2588
// Set the `remaining` local and only continue if it's > 0
2589
self.instruction(LocalGet(src_len.idx));
2590
let remaining = self.local_tee_new_tmp(src_mem_opts.ptr());
2591
self.ptr_eqz(src_mem_opts);
2592
self.instruction(BrIf(0));
2593
2594
// Initialize the two destination pointers to their initial values
2595
self.instruction(LocalGet(src_mem.addr.idx));
2596
let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2597
self.instruction(LocalGet(dst_mem.addr.idx));
2598
let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
2599
2600
self.instruction(Loop(BlockType::Empty));
2601
2602
// Translate the next element in the list
2603
let element_src = Source::Memory(Memory {
2604
opts: src_opts,
2605
offset: 0,
2606
addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
2607
});
2608
let element_dst = Destination::Memory(Memory {
2609
opts: dst_opts,
2610
offset: 0,
2611
addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
2612
});
2613
self.translate(src_element_ty, &element_src, dst_element_ty, &element_dst);
2614
2615
// Update the two loop pointers
2616
if src_size > 0 {
2617
self.instruction(LocalGet(cur_src_ptr.idx));
2618
self.ptr_uconst(src_mem_opts, src_size);
2619
self.ptr_add(src_mem_opts);
2620
self.instruction(LocalSet(cur_src_ptr.idx));
2621
}
2622
if dst_size > 0 {
2623
self.instruction(LocalGet(cur_dst_ptr.idx));
2624
self.ptr_uconst(dst_mem_opts, dst_size);
2625
self.ptr_add(dst_mem_opts);
2626
self.instruction(LocalSet(cur_dst_ptr.idx));
2627
}
2628
2629
// Update the remaining count, falling through to break out if it's zero
2630
// now.
2631
self.instruction(LocalGet(remaining.idx));
2632
self.ptr_iconst(src_mem_opts, -1);
2633
self.ptr_add(src_mem_opts);
2634
self.instruction(LocalTee(remaining.idx));
2635
self.ptr_br_if(src_mem_opts, 0);
2636
self.instruction(End); // end of loop
2637
self.instruction(End); // end of block
2638
2639
self.free_temp_local(cur_dst_ptr);
2640
self.free_temp_local(cur_src_ptr);
2641
self.free_temp_local(remaining);
2642
}
2643
2644
// Store the ptr/length in the desired destination
2645
match dst {
2646
Destination::Stack(s, _) => {
2647
self.instruction(LocalGet(dst_mem.addr.idx));
2648
self.stack_set(&s[..1], dst_mem_opts.ptr());
2649
self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2650
self.stack_set(&s[1..], dst_mem_opts.ptr());
2651
}
2652
Destination::Memory(mem) => {
2653
self.instruction(LocalGet(mem.addr.idx));
2654
self.instruction(LocalGet(dst_mem.addr.idx));
2655
self.ptr_store(mem);
2656
self.instruction(LocalGet(mem.addr.idx));
2657
self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2658
self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into()));
2659
}
2660
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
2661
}
2662
2663
self.free_temp_local(src_len);
2664
self.free_temp_local(src_mem.addr);
2665
self.free_temp_local(dst_mem.addr);
2666
}
2667
2668
fn calculate_list_byte_len(
2669
&mut self,
2670
opts: &LinearMemoryOptions,
2671
len_local: u32,
2672
elt_size: u32,
2673
) -> TempLocal {
2674
// Zero-size types are easy to handle here because the byte size of the
2675
// destination is always zero.
2676
if elt_size == 0 {
2677
self.ptr_uconst(opts, 0);
2678
return self.local_set_new_tmp(opts.ptr());
2679
}
2680
2681
// For one-byte elements in the destination the check here can be a bit
2682
// more optimal than the general case below. In these situations if the
2683
// source pointer type is 32-bit then we're guaranteed to not overflow,
2684
// so the source length is simply casted to the destination's type.
2685
//
2686
// If the source is 64-bit then all that needs to be checked is to
2687
// ensure that it does not have the upper 32-bits set.
2688
if elt_size == 1 {
2689
if let ValType::I64 = opts.ptr() {
2690
self.instruction(LocalGet(len_local));
2691
self.instruction(I64Const(32));
2692
self.instruction(I64ShrU);
2693
self.instruction(I32WrapI64);
2694
self.instruction(If(BlockType::Empty));
2695
self.trap(Trap::ListOutOfBounds);
2696
self.instruction(End);
2697
}
2698
self.instruction(LocalGet(len_local));
2699
return self.local_set_new_tmp(opts.ptr());
2700
}
2701
2702
// The main check implemented by this function is to verify that
2703
// `src_len_local` does not exceed the 32-bit range. Byte sizes for
2704
// lists must always fit in 32-bits to get transferred to 32-bit
2705
// memories.
2706
self.instruction(Block(BlockType::Empty));
2707
self.instruction(Block(BlockType::Empty));
2708
self.instruction(LocalGet(len_local));
2709
match opts.ptr() {
2710
// The source's list length is guaranteed to be less than 32-bits
2711
// so simply extend it up to a 64-bit type for the multiplication
2712
// below.
2713
ValType::I32 => self.instruction(I64ExtendI32U),
2714
2715
// If the source is a 64-bit memory then if the item length doesn't
2716
// fit in 32-bits the byte length definitely won't, so generate a
2717
// branch to our overflow trap here if any of the upper 32-bits are set.
2718
ValType::I64 => {
2719
self.instruction(I64Const(32));
2720
self.instruction(I64ShrU);
2721
self.instruction(I32WrapI64);
2722
self.instruction(BrIf(0));
2723
self.instruction(LocalGet(len_local));
2724
}
2725
2726
_ => unreachable!(),
2727
}
2728
2729
// Next perform a 64-bit multiplication with the element byte size that
2730
// is itself guaranteed to fit in 32-bits. The result is then checked
2731
// to see if we overflowed the 32-bit space. The two input operands to
2732
// the multiplication are guaranteed to be 32-bits at most which means
2733
// that this multiplication shouldn't overflow.
2734
//
2735
// The result of the multiplication is saved into a local as well to
2736
// get the result afterwards.
2737
self.instruction(I64Const(elt_size.into()));
2738
self.instruction(I64Mul);
2739
let tmp = self.local_tee_new_tmp(ValType::I64);
2740
// Branch to success if the upper 32-bits are zero, otherwise
2741
// fall-through to the trap.
2742
self.instruction(I64Const(32));
2743
self.instruction(I64ShrU);
2744
self.instruction(I64Eqz);
2745
self.instruction(BrIf(1));
2746
self.instruction(End);
2747
self.trap(Trap::ListOutOfBounds);
2748
self.instruction(End);
2749
2750
// If a fresh local was used to store the result of the multiplication
2751
// then convert it down to 32-bits which should be guaranteed to not
2752
// lose information at this point.
2753
if opts.ptr() == ValType::I64 {
2754
tmp
2755
} else {
2756
self.instruction(LocalGet(tmp.idx));
2757
self.instruction(I32WrapI64);
2758
self.free_temp_local(tmp);
2759
self.local_set_new_tmp(ValType::I32)
2760
}
2761
}
2762
2763
fn convert_src_len_to_dst(
2764
&mut self,
2765
src_len_local: u32,
2766
src_ptr_ty: ValType,
2767
dst_ptr_ty: ValType,
2768
) {
2769
self.instruction(LocalGet(src_len_local));
2770
match (src_ptr_ty, dst_ptr_ty) {
2771
(ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
2772
(ValType::I64, ValType::I32) => self.instruction(I32WrapI64),
2773
(src, dst) => assert_eq!(src, dst),
2774
}
2775
}
2776
2777
fn translate_record(
2778
&mut self,
2779
src_ty: TypeRecordIndex,
2780
src: &Source<'_>,
2781
dst_ty: &InterfaceType,
2782
dst: &Destination,
2783
) {
2784
let src_ty = &self.types[src_ty];
2785
let dst_ty = match dst_ty {
2786
InterfaceType::Record(r) => &self.types[*r],
2787
_ => panic!("expected a record"),
2788
};
2789
2790
// TODO: subtyping
2791
assert_eq!(src_ty.fields.len(), dst_ty.fields.len());
2792
2793
// First a map is made of the source fields to where they're coming
2794
// from (e.g. which offset or which locals). This map is keyed by the
2795
// fields' names
2796
let mut src_fields = HashMap::new();
2797
for (i, src) in src
2798
.record_field_srcs(self.types, src_ty.fields.iter().map(|f| f.ty))
2799
.enumerate()
2800
{
2801
let field = &src_ty.fields[i];
2802
src_fields.insert(&field.name, (src, &field.ty));
2803
}
2804
2805
// .. and next translation is performed in the order of the destination
2806
// fields in case the destination is the stack to ensure that the stack
2807
// has the fields all in the right order.
2808
//
2809
// Note that the lookup in `src_fields` is an infallible lookup which
2810
// will panic if the field isn't found.
2811
//
2812
// TODO: should that lookup be fallible with subtyping?
2813
for (i, dst) in dst
2814
.record_field_dsts(self.types, dst_ty.fields.iter().map(|f| f.ty))
2815
.enumerate()
2816
{
2817
let field = &dst_ty.fields[i];
2818
let (src, src_ty) = &src_fields[&field.name];
2819
self.translate(src_ty, src, &field.ty, &dst);
2820
}
2821
}
2822
2823
fn translate_flags(
2824
&mut self,
2825
src_ty: TypeFlagsIndex,
2826
src: &Source<'_>,
2827
dst_ty: &InterfaceType,
2828
dst: &Destination,
2829
) {
2830
let src_ty = &self.types[src_ty];
2831
let dst_ty = match dst_ty {
2832
InterfaceType::Flags(r) => &self.types[*r],
2833
_ => panic!("expected a record"),
2834
};
2835
2836
// TODO: subtyping
2837
//
2838
// Notably this implementation does not support reordering flags from
2839
// the source to the destination nor having more flags in the
2840
// destination. Currently this is a copy from source to destination
2841
// in-bulk. Otherwise reordering indices would have to have some sort of
2842
// fancy bit twiddling tricks or something like that.
2843
assert_eq!(src_ty.names, dst_ty.names);
2844
let cnt = src_ty.names.len();
2845
match FlagsSize::from_count(cnt) {
2846
FlagsSize::Size0 => {}
2847
FlagsSize::Size1 => {
2848
let mask = if cnt == 8 { 0xff } else { (1 << cnt) - 1 };
2849
self.convert_u8_mask(src, dst, mask);
2850
}
2851
FlagsSize::Size2 => {
2852
let mask = if cnt == 16 { 0xffff } else { (1 << cnt) - 1 };
2853
self.convert_u16_mask(src, dst, mask);
2854
}
2855
FlagsSize::Size4Plus(n) => {
2856
let srcs = src.record_field_srcs(self.types, (0..n).map(|_| InterfaceType::U32));
2857
let dsts = dst.record_field_dsts(self.types, (0..n).map(|_| InterfaceType::U32));
2858
let n = usize::from(n);
2859
for (i, (src, dst)) in srcs.zip(dsts).enumerate() {
2860
let mask = if i == n - 1 && (cnt % 32 != 0) {
2861
(1 << (cnt % 32)) - 1
2862
} else {
2863
0xffffffff
2864
};
2865
self.convert_u32_mask(&src, &dst, mask);
2866
}
2867
}
2868
}
2869
}
2870
2871
fn translate_tuple(
2872
&mut self,
2873
src_ty: TypeTupleIndex,
2874
src: &Source<'_>,
2875
dst_ty: &InterfaceType,
2876
dst: &Destination,
2877
) {
2878
let src_ty = &self.types[src_ty];
2879
let dst_ty = match dst_ty {
2880
InterfaceType::Tuple(t) => &self.types[*t],
2881
_ => panic!("expected a tuple"),
2882
};
2883
2884
// TODO: subtyping
2885
assert_eq!(src_ty.types.len(), dst_ty.types.len());
2886
2887
let srcs = src
2888
.record_field_srcs(self.types, src_ty.types.iter().copied())
2889
.zip(src_ty.types.iter());
2890
let dsts = dst
2891
.record_field_dsts(self.types, dst_ty.types.iter().copied())
2892
.zip(dst_ty.types.iter());
2893
for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
2894
self.translate(src_ty, &src, dst_ty, &dst);
2895
}
2896
}
2897
2898
fn translate_fixed_length_list(
2899
&mut self,
2900
src_ty: TypeFixedLengthListIndex,
2901
src: &Source<'_>,
2902
dst_ty: &InterfaceType,
2903
dst: &Destination,
2904
) {
2905
let src_ty = &self.types[src_ty];
2906
let dst_ty = match dst_ty {
2907
InterfaceType::FixedLengthList(t) => &self.types[*t],
2908
_ => panic!("expected a fixed size list"),
2909
};
2910
2911
// TODO: subtyping
2912
assert_eq!(src_ty.size, dst_ty.size);
2913
2914
match (&src, &dst) {
2915
// Generate custom code for memory to memory copy
2916
(Source::Memory(src_mem), Destination::Memory(dst_mem)) => {
2917
let src_mem_opts = match &src_mem.opts.data_model {
2918
DataModel::Gc {} => todo!("CM+GC"),
2919
DataModel::LinearMemory(opts) => opts,
2920
};
2921
let dst_mem_opts = match &dst_mem.opts.data_model {
2922
DataModel::Gc {} => todo!("CM+GC"),
2923
DataModel::LinearMemory(opts) => opts,
2924
};
2925
let src_element_bytes = self.types.size_align(src_mem_opts, &src_ty.element).0;
2926
let dst_element_bytes = self.types.size_align(dst_mem_opts, &dst_ty.element).0;
2927
assert_ne!(src_element_bytes, 0);
2928
assert_ne!(dst_element_bytes, 0);
2929
2930
// because data is stored in-line, we assume that source and destination memory have been validated upstream
2931
2932
self.instruction(LocalGet(src_mem.addr.idx));
2933
if src_mem.offset != 0 {
2934
self.ptr_uconst(src_mem_opts, src_mem.offset);
2935
self.ptr_add(src_mem_opts);
2936
}
2937
let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2938
self.instruction(LocalGet(dst_mem.addr.idx));
2939
if dst_mem.offset != 0 {
2940
self.ptr_uconst(dst_mem_opts, dst_mem.offset);
2941
self.ptr_add(dst_mem_opts);
2942
}
2943
let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
2944
2945
self.instruction(I32Const(src_ty.size as i32));
2946
let remaining = self.local_set_new_tmp(ValType::I32);
2947
2948
self.instruction(Loop(BlockType::Empty));
2949
2950
// Translate the next element in the list
2951
let element_src = Source::Memory(Memory {
2952
opts: src_mem.opts,
2953
offset: 0,
2954
addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
2955
});
2956
let element_dst = Destination::Memory(Memory {
2957
opts: dst_mem.opts,
2958
offset: 0,
2959
addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
2960
});
2961
self.translate(&src_ty.element, &element_src, &dst_ty.element, &element_dst);
2962
2963
// Update the two loop pointers
2964
self.instruction(LocalGet(cur_src_ptr.idx));
2965
self.ptr_uconst(src_mem_opts, src_element_bytes);
2966
self.ptr_add(src_mem_opts);
2967
self.instruction(LocalSet(cur_src_ptr.idx));
2968
self.instruction(LocalGet(cur_dst_ptr.idx));
2969
self.ptr_uconst(dst_mem_opts, dst_element_bytes);
2970
self.ptr_add(dst_mem_opts);
2971
self.instruction(LocalSet(cur_dst_ptr.idx));
2972
2973
// Update the remaining count, falling through to break out if it's zero
2974
// now.
2975
self.instruction(LocalGet(remaining.idx));
2976
self.ptr_iconst(src_mem_opts, -1);
2977
self.ptr_add(src_mem_opts);
2978
self.instruction(LocalTee(remaining.idx));
2979
self.ptr_br_if(src_mem_opts, 0);
2980
self.instruction(End); // end of loop
2981
2982
self.free_temp_local(cur_dst_ptr);
2983
self.free_temp_local(cur_src_ptr);
2984
self.free_temp_local(remaining);
2985
return;
2986
}
2987
// for the non-memory-to-memory case fall back to using generic tuple translation
2988
(_, _) => {
2989
// Assumes that the number of elements are small enough for this unrolling
2990
assert!(
2991
src_ty.size as usize <= MAX_FLAT_PARAMS
2992
&& dst_ty.size as usize <= MAX_FLAT_PARAMS
2993
);
2994
let srcs =
2995
src.record_field_srcs(self.types, (0..src_ty.size).map(|_| src_ty.element));
2996
let dsts =
2997
dst.record_field_dsts(self.types, (0..dst_ty.size).map(|_| dst_ty.element));
2998
for (src, dst) in srcs.zip(dsts) {
2999
self.translate(&src_ty.element, &src, &dst_ty.element, &dst);
3000
}
3001
}
3002
}
3003
}
3004
3005
fn translate_variant(
3006
&mut self,
3007
src_ty: TypeVariantIndex,
3008
src: &Source<'_>,
3009
dst_ty: &InterfaceType,
3010
dst: &Destination,
3011
) {
3012
let src_ty = &self.types[src_ty];
3013
let dst_ty = match dst_ty {
3014
InterfaceType::Variant(t) => &self.types[*t],
3015
_ => panic!("expected a variant"),
3016
};
3017
3018
let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
3019
let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
3020
3021
let iter = src_ty
3022
.cases
3023
.iter()
3024
.enumerate()
3025
.map(|(src_i, (src_case, src_case_ty))| {
3026
let dst_i = dst_ty
3027
.cases
3028
.iter()
3029
.position(|(c, _)| c == src_case)
3030
.unwrap();
3031
let dst_case_ty = &dst_ty.cases[dst_i];
3032
let src_i = u32::try_from(src_i).unwrap();
3033
let dst_i = u32::try_from(dst_i).unwrap();
3034
VariantCase {
3035
src_i,
3036
src_ty: src_case_ty.as_ref(),
3037
dst_i,
3038
dst_ty: dst_case_ty.as_ref(),
3039
}
3040
});
3041
self.convert_variant(src, &src_info, dst, &dst_info, iter);
3042
}
3043
3044
fn translate_enum(
3045
&mut self,
3046
src_ty: TypeEnumIndex,
3047
src: &Source<'_>,
3048
dst_ty: &InterfaceType,
3049
dst: &Destination,
3050
) {
3051
let src_ty = &self.types[src_ty];
3052
let dst_ty = match dst_ty {
3053
InterfaceType::Enum(t) => &self.types[*t],
3054
_ => panic!("expected an option"),
3055
};
3056
3057
debug_assert_eq!(src_ty.info.size, dst_ty.info.size);
3058
debug_assert_eq!(src_ty.names.len(), dst_ty.names.len());
3059
debug_assert!(
3060
src_ty
3061
.names
3062
.iter()
3063
.zip(dst_ty.names.iter())
3064
.all(|(a, b)| a == b)
3065
);
3066
3067
// Get the discriminant.
3068
match src {
3069
Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3070
Source::Memory(mem) => match src_ty.info.size {
3071
DiscriminantSize::Size1 => self.i32_load8u(mem),
3072
DiscriminantSize::Size2 => self.i32_load16u(mem),
3073
DiscriminantSize::Size4 => self.i32_load(mem),
3074
},
3075
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3076
}
3077
let tmp = self.local_tee_new_tmp(ValType::I32);
3078
3079
// Assert that the discriminant is valid.
3080
self.instruction(I32Const(i32::try_from(src_ty.names.len()).unwrap()));
3081
self.instruction(I32GeU);
3082
self.instruction(If(BlockType::Empty));
3083
self.trap(Trap::InvalidDiscriminant);
3084
self.instruction(End);
3085
3086
// Save the discriminant to the destination.
3087
match dst {
3088
Destination::Stack(stack, _) => {
3089
self.local_get_tmp(&tmp);
3090
self.stack_set(&stack[..1], ValType::I32)
3091
}
3092
Destination::Memory(mem) => {
3093
self.push_dst_addr(dst);
3094
self.local_get_tmp(&tmp);
3095
match dst_ty.info.size {
3096
DiscriminantSize::Size1 => self.i32_store8(mem),
3097
DiscriminantSize::Size2 => self.i32_store16(mem),
3098
DiscriminantSize::Size4 => self.i32_store(mem),
3099
}
3100
}
3101
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3102
}
3103
self.free_temp_local(tmp);
3104
}
3105
3106
fn translate_option(
3107
&mut self,
3108
src_ty: TypeOptionIndex,
3109
src: &Source<'_>,
3110
dst_ty: &InterfaceType,
3111
dst: &Destination,
3112
) {
3113
let src_ty = &self.types[src_ty].ty;
3114
let dst_ty = match dst_ty {
3115
InterfaceType::Option(t) => &self.types[*t].ty,
3116
_ => panic!("expected an option"),
3117
};
3118
let src_ty = Some(src_ty);
3119
let dst_ty = Some(dst_ty);
3120
3121
let src_info = variant_info(self.types, [None, src_ty]);
3122
let dst_info = variant_info(self.types, [None, dst_ty]);
3123
3124
self.convert_variant(
3125
src,
3126
&src_info,
3127
dst,
3128
&dst_info,
3129
[
3130
VariantCase {
3131
src_i: 0,
3132
dst_i: 0,
3133
src_ty: None,
3134
dst_ty: None,
3135
},
3136
VariantCase {
3137
src_i: 1,
3138
dst_i: 1,
3139
src_ty,
3140
dst_ty,
3141
},
3142
]
3143
.into_iter(),
3144
);
3145
}
3146
3147
fn translate_result(
3148
&mut self,
3149
src_ty: TypeResultIndex,
3150
src: &Source<'_>,
3151
dst_ty: &InterfaceType,
3152
dst: &Destination,
3153
) {
3154
let src_ty = &self.types[src_ty];
3155
let dst_ty = match dst_ty {
3156
InterfaceType::Result(t) => &self.types[*t],
3157
_ => panic!("expected a result"),
3158
};
3159
3160
let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]);
3161
let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]);
3162
3163
self.convert_variant(
3164
src,
3165
&src_info,
3166
dst,
3167
&dst_info,
3168
[
3169
VariantCase {
3170
src_i: 0,
3171
dst_i: 0,
3172
src_ty: src_ty.ok.as_ref(),
3173
dst_ty: dst_ty.ok.as_ref(),
3174
},
3175
VariantCase {
3176
src_i: 1,
3177
dst_i: 1,
3178
src_ty: src_ty.err.as_ref(),
3179
dst_ty: dst_ty.err.as_ref(),
3180
},
3181
]
3182
.into_iter(),
3183
);
3184
}
3185
3186
fn convert_variant<'c>(
3187
&mut self,
3188
src: &Source<'_>,
3189
src_info: &VariantInfo,
3190
dst: &Destination,
3191
dst_info: &VariantInfo,
3192
src_cases: impl ExactSizeIterator<Item = VariantCase<'c>>,
3193
) {
3194
// The outermost block is special since it has the result type of the
3195
// translation here. That will depend on the `dst`.
3196
let outer_block_ty = match dst {
3197
Destination::Stack(dst_flat, _) => match dst_flat.len() {
3198
0 => BlockType::Empty,
3199
1 => BlockType::Result(dst_flat[0]),
3200
_ => {
3201
let ty = self.module.core_types.function(&[], &dst_flat);
3202
BlockType::FunctionType(ty)
3203
}
3204
},
3205
Destination::Memory(_) => BlockType::Empty,
3206
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3207
};
3208
self.instruction(Block(outer_block_ty));
3209
3210
// After the outermost block generate a new block for each of the
3211
// remaining cases.
3212
let src_cases_len = src_cases.len();
3213
for _ in 0..src_cases_len - 1 {
3214
self.instruction(Block(BlockType::Empty));
3215
}
3216
3217
// Generate a block for an invalid variant discriminant
3218
self.instruction(Block(BlockType::Empty));
3219
3220
// And generate one final block that we'll be jumping out of with the
3221
// `br_table`
3222
self.instruction(Block(BlockType::Empty));
3223
3224
// Load the discriminant
3225
match src {
3226
Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3227
Source::Memory(mem) => match src_info.size {
3228
DiscriminantSize::Size1 => self.i32_load8u(mem),
3229
DiscriminantSize::Size2 => self.i32_load16u(mem),
3230
DiscriminantSize::Size4 => self.i32_load(mem),
3231
},
3232
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3233
}
3234
3235
// Generate the `br_table` for the discriminant. Each case has an
3236
// offset of 1 to skip the trapping block.
3237
let mut targets = Vec::new();
3238
for i in 0..src_cases_len {
3239
targets.push((i + 1) as u32);
3240
}
3241
self.instruction(BrTable(targets[..].into(), 0));
3242
self.instruction(End); // end the `br_table` block
3243
3244
self.trap(Trap::InvalidDiscriminant);
3245
self.instruction(End); // end the "invalid discriminant" block
3246
3247
// Translate each case individually within its own block. Note that the
3248
// iteration order here places the first case in the innermost block
3249
// and the last case in the outermost block. This matches the order
3250
// of the jump targets in the `br_table` instruction.
3251
let src_cases_len = u32::try_from(src_cases_len).unwrap();
3252
for case in src_cases {
3253
let VariantCase {
3254
src_i,
3255
src_ty,
3256
dst_i,
3257
dst_ty,
3258
} = case;
3259
3260
// Translate the discriminant here, noting that `dst_i` may be
3261
// different than `src_i`.
3262
self.push_dst_addr(dst);
3263
self.instruction(I32Const(dst_i as i32));
3264
match dst {
3265
Destination::Stack(stack, _) => self.stack_set(&stack[..1], ValType::I32),
3266
Destination::Memory(mem) => match dst_info.size {
3267
DiscriminantSize::Size1 => self.i32_store8(mem),
3268
DiscriminantSize::Size2 => self.i32_store16(mem),
3269
DiscriminantSize::Size4 => self.i32_store(mem),
3270
},
3271
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3272
}
3273
3274
let src_payload = src.payload_src(self.types, src_info, src_ty);
3275
let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty);
3276
3277
// Translate the payload of this case using the various types from
3278
// the dst/src.
3279
match (src_ty, dst_ty) {
3280
(Some(src_ty), Some(dst_ty)) => {
3281
self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
3282
}
3283
(None, None) => {}
3284
_ => unimplemented!(),
3285
}
3286
3287
// If the results of this translation were placed on the stack then
3288
// the stack values may need to be padded with more zeros due to
3289
// this particular case being possibly smaller than the entire
3290
// variant. That's handled here by pushing remaining zeros after
3291
// accounting for the discriminant pushed as well as the results of
3292
// this individual payload.
3293
if let Destination::Stack(payload_results, _) = dst_payload {
3294
if let Destination::Stack(dst_results, _) = dst {
3295
let remaining = &dst_results[1..][payload_results.len()..];
3296
for ty in remaining {
3297
match ty {
3298
ValType::I32 => self.instruction(I32Const(0)),
3299
ValType::I64 => self.instruction(I64Const(0)),
3300
ValType::F32 => self.instruction(F32Const(0.0.into())),
3301
ValType::F64 => self.instruction(F64Const(0.0.into())),
3302
_ => unreachable!(),
3303
}
3304
}
3305
}
3306
}
3307
3308
// Branch to the outermost block. Note that this isn't needed for
3309
// the outermost case since it simply falls through.
3310
if src_i != src_cases_len - 1 {
3311
self.instruction(Br(src_cases_len - src_i - 1));
3312
}
3313
self.instruction(End); // end this case's block
3314
}
3315
}
3316
3317
fn translate_future(
3318
&mut self,
3319
src_ty: TypeFutureTableIndex,
3320
src: &Source<'_>,
3321
dst_ty: &InterfaceType,
3322
dst: &Destination,
3323
) {
3324
let dst_ty = match dst_ty {
3325
InterfaceType::Future(t) => *t,
3326
_ => panic!("expected a `Future`"),
3327
};
3328
let transfer = self.module.import_future_transfer();
3329
self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3330
}
3331
3332
fn translate_stream(
3333
&mut self,
3334
src_ty: TypeStreamTableIndex,
3335
src: &Source<'_>,
3336
dst_ty: &InterfaceType,
3337
dst: &Destination,
3338
) {
3339
let dst_ty = match dst_ty {
3340
InterfaceType::Stream(t) => *t,
3341
_ => panic!("expected a `Stream`"),
3342
};
3343
let transfer = self.module.import_stream_transfer();
3344
self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3345
}
3346
3347
fn translate_error_context(
3348
&mut self,
3349
src_ty: TypeComponentLocalErrorContextTableIndex,
3350
src: &Source<'_>,
3351
dst_ty: &InterfaceType,
3352
dst: &Destination,
3353
) {
3354
let dst_ty = match dst_ty {
3355
InterfaceType::ErrorContext(t) => *t,
3356
_ => panic!("expected an `ErrorContext`"),
3357
};
3358
let transfer = self.module.import_error_context_transfer();
3359
self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3360
}
3361
3362
fn translate_own(
3363
&mut self,
3364
src_ty: TypeResourceTableIndex,
3365
src: &Source<'_>,
3366
dst_ty: &InterfaceType,
3367
dst: &Destination,
3368
) {
3369
let dst_ty = match dst_ty {
3370
InterfaceType::Own(t) => *t,
3371
_ => panic!("expected an `Own`"),
3372
};
3373
let transfer = self.module.import_resource_transfer_own();
3374
self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3375
}
3376
3377
fn translate_borrow(
3378
&mut self,
3379
src_ty: TypeResourceTableIndex,
3380
src: &Source<'_>,
3381
dst_ty: &InterfaceType,
3382
dst: &Destination,
3383
) {
3384
let dst_ty = match dst_ty {
3385
InterfaceType::Borrow(t) => *t,
3386
_ => panic!("expected an `Borrow`"),
3387
};
3388
3389
let transfer = self.module.import_resource_transfer_borrow();
3390
self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3391
}
3392
3393
/// Translates the index `src`, which resides in the table `src_ty`, into
3394
/// and index within `dst_ty` and is stored at `dst`.
3395
///
3396
/// Actual translation of the index happens in a wasmtime libcall, which a
3397
/// cranelift-generated trampoline to satisfy this import will call. The
3398
/// `transfer` function is an imported function which takes the src, src_ty,
3399
/// and dst_ty, and returns the dst index.
3400
fn translate_handle(
3401
&mut self,
3402
src_ty: u32,
3403
src: &Source<'_>,
3404
dst_ty: u32,
3405
dst: &Destination,
3406
transfer: FuncIndex,
3407
) {
3408
self.push_dst_addr(dst);
3409
match src {
3410
Source::Memory(mem) => self.i32_load(mem),
3411
Source::Stack(stack) => self.stack_get(stack, ValType::I32),
3412
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3413
}
3414
self.instruction(I32Const(src_ty as i32));
3415
self.instruction(I32Const(dst_ty as i32));
3416
self.instruction(Call(transfer.as_u32()));
3417
match dst {
3418
Destination::Memory(mem) => self.i32_store(mem),
3419
Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
3420
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3421
}
3422
}
3423
3424
fn trap_if_not_flag(&mut self, flags_global: GlobalIndex, flag_to_test: i32, trap: Trap) {
3425
self.instruction(GlobalGet(flags_global.as_u32()));
3426
self.instruction(I32Const(flag_to_test));
3427
self.instruction(I32And);
3428
self.instruction(I32Eqz);
3429
self.instruction(If(BlockType::Empty));
3430
self.trap(trap);
3431
self.instruction(End);
3432
}
3433
3434
fn set_flag(&mut self, flags_global: GlobalIndex, flag_to_set: i32, value: bool) {
3435
self.instruction(GlobalGet(flags_global.as_u32()));
3436
if value {
3437
self.instruction(I32Const(flag_to_set));
3438
self.instruction(I32Or);
3439
} else {
3440
self.instruction(I32Const(!flag_to_set));
3441
self.instruction(I32And);
3442
}
3443
self.instruction(GlobalSet(flags_global.as_u32()));
3444
}
3445
3446
fn verify_aligned(&mut self, opts: &LinearMemoryOptions, addr_local: u32, align: u32) {
3447
// If the alignment is 1 then everything is trivially aligned and the
3448
// check can be omitted.
3449
if align == 1 {
3450
return;
3451
}
3452
self.instruction(LocalGet(addr_local));
3453
assert!(align.is_power_of_two());
3454
self.ptr_uconst(opts, align - 1);
3455
self.ptr_and(opts);
3456
self.ptr_if(opts, BlockType::Empty);
3457
self.trap(Trap::UnalignedPointer);
3458
self.instruction(End);
3459
}
3460
3461
fn assert_aligned(&mut self, ty: &InterfaceType, mem: &Memory) {
3462
let mem_opts = mem.mem_opts();
3463
if !self.module.tunables.debug_adapter_modules {
3464
return;
3465
}
3466
let align = self.types.align(mem_opts, ty);
3467
if align == 1 {
3468
return;
3469
}
3470
assert!(align.is_power_of_two());
3471
self.instruction(LocalGet(mem.addr.idx));
3472
self.ptr_uconst(mem_opts, mem.offset);
3473
self.ptr_add(mem_opts);
3474
self.ptr_uconst(mem_opts, align - 1);
3475
self.ptr_and(mem_opts);
3476
self.ptr_if(mem_opts, BlockType::Empty);
3477
self.trap(Trap::DebugAssertPointerAligned);
3478
self.instruction(End);
3479
}
3480
3481
fn malloc<'c>(&mut self, opts: &'c Options, size: MallocSize, align: u32) -> Memory<'c> {
3482
match &opts.data_model {
3483
DataModel::Gc {} => todo!("CM+GC"),
3484
DataModel::LinearMemory(mem_opts) => {
3485
let realloc = mem_opts.realloc.unwrap();
3486
self.ptr_uconst(mem_opts, 0);
3487
self.ptr_uconst(mem_opts, 0);
3488
self.ptr_uconst(mem_opts, align);
3489
match size {
3490
MallocSize::Const(size) => self.ptr_uconst(mem_opts, size),
3491
MallocSize::Local(idx) => self.instruction(LocalGet(idx)),
3492
}
3493
self.instruction(Call(realloc.as_u32()));
3494
let addr = self.local_set_new_tmp(mem_opts.ptr());
3495
self.memory_operand(opts, addr, align)
3496
}
3497
}
3498
}
3499
3500
fn memory_operand<'c>(&mut self, opts: &'c Options, addr: TempLocal, align: u32) -> Memory<'c> {
3501
let ret = Memory {
3502
addr,
3503
offset: 0,
3504
opts,
3505
};
3506
self.verify_aligned(opts.data_model.unwrap_memory(), ret.addr.idx, align);
3507
ret
3508
}
3509
3510
/// Generates a new local in this function of the `ty` specified,
3511
/// initializing it with the top value on the current wasm stack.
3512
///
3513
/// The returned `TempLocal` must be freed after it is finished with
3514
/// `free_temp_local`.
3515
fn local_tee_new_tmp(&mut self, ty: ValType) -> TempLocal {
3516
self.gen_temp_local(ty, LocalTee)
3517
}
3518
3519
/// Same as `local_tee_new_tmp` but initializes the local with `LocalSet`
3520
/// instead of `LocalTee`.
3521
fn local_set_new_tmp(&mut self, ty: ValType) -> TempLocal {
3522
self.gen_temp_local(ty, LocalSet)
3523
}
3524
3525
fn local_get_tmp(&mut self, local: &TempLocal) {
3526
self.instruction(LocalGet(local.idx));
3527
}
3528
3529
fn gen_temp_local(&mut self, ty: ValType, insn: fn(u32) -> Instruction<'static>) -> TempLocal {
3530
// First check to see if any locals are available in this function which
3531
// were previously generated but are no longer in use.
3532
if let Some(idx) = self.free_locals.get_mut(&ty).and_then(|v| v.pop()) {
3533
self.instruction(insn(idx));
3534
return TempLocal {
3535
ty,
3536
idx,
3537
needs_free: true,
3538
};
3539
}
3540
3541
// Failing that generate a fresh new local.
3542
let locals = &mut self.module.funcs[self.result].locals;
3543
match locals.last_mut() {
3544
Some((cnt, prev_ty)) if ty == *prev_ty => *cnt += 1,
3545
_ => locals.push((1, ty)),
3546
}
3547
self.nlocals += 1;
3548
let idx = self.nlocals - 1;
3549
self.instruction(insn(idx));
3550
TempLocal {
3551
ty,
3552
idx,
3553
needs_free: true,
3554
}
3555
}
3556
3557
/// Used to release a `TempLocal` from a particular lexical scope to allow
3558
/// its possible reuse in later scopes.
3559
fn free_temp_local(&mut self, mut local: TempLocal) {
3560
assert!(local.needs_free);
3561
self.free_locals
3562
.entry(local.ty)
3563
.or_insert(Vec::new())
3564
.push(local.idx);
3565
local.needs_free = false;
3566
}
3567
3568
fn instruction(&mut self, instr: Instruction) {
3569
instr.encode(&mut self.code);
3570
}
3571
3572
fn trap(&mut self, trap: Trap) {
3573
let trap_func = self.module.import_trap();
3574
self.instruction(I32Const(trap as i32));
3575
self.instruction(Call(trap_func.as_u32()));
3576
self.instruction(Unreachable);
3577
}
3578
3579
/// Flushes out the current `code` instructions into the destination
3580
/// function.
3581
///
3582
/// This is a noop if no instructions have been encoded yet.
3583
fn flush_code(&mut self) {
3584
if self.code.is_empty() {
3585
return;
3586
}
3587
self.module.funcs[self.result]
3588
.body
3589
.push(Body::Raw(mem::take(&mut self.code)));
3590
}
3591
3592
fn finish(mut self) {
3593
// Append the final `end` instruction which all functions require, and
3594
// then empty out the temporary buffer in `Compiler`.
3595
self.instruction(End);
3596
self.flush_code();
3597
3598
// Flag the function as "done" which helps with an assert later on in
3599
// emission that everything was eventually finished.
3600
self.module.funcs[self.result].filled_in = true;
3601
}
3602
3603
/// Fetches the value contained with the local specified by `stack` and
3604
/// converts it to `dst_ty`.
3605
///
3606
/// This is only intended for use in primitive operations where `stack` is
3607
/// guaranteed to have only one local. The type of the local on the stack is
3608
/// then converted to `dst_ty` appropriately. Note that the types may be
3609
/// different due to the "flattening" of variant types.
3610
fn stack_get(&mut self, stack: &Stack<'_>, dst_ty: ValType) {
3611
assert_eq!(stack.locals.len(), 1);
3612
let (idx, src_ty) = stack.locals[0];
3613
self.instruction(LocalGet(idx));
3614
match (src_ty, dst_ty) {
3615
(ValType::I32, ValType::I32)
3616
| (ValType::I64, ValType::I64)
3617
| (ValType::F32, ValType::F32)
3618
| (ValType::F64, ValType::F64) => {}
3619
3620
(ValType::I32, ValType::F32) => self.instruction(F32ReinterpretI32),
3621
(ValType::I64, ValType::I32) => {
3622
self.assert_i64_upper_bits_not_set(idx);
3623
self.instruction(I32WrapI64);
3624
}
3625
(ValType::I64, ValType::F64) => self.instruction(F64ReinterpretI64),
3626
(ValType::I64, ValType::F32) => {
3627
self.assert_i64_upper_bits_not_set(idx);
3628
self.instruction(I32WrapI64);
3629
self.instruction(F32ReinterpretI32);
3630
}
3631
3632
// should not be possible given the `join` function for variants
3633
(ValType::I32, ValType::I64)
3634
| (ValType::I32, ValType::F64)
3635
| (ValType::F32, ValType::I32)
3636
| (ValType::F32, ValType::I64)
3637
| (ValType::F32, ValType::F64)
3638
| (ValType::F64, ValType::I32)
3639
| (ValType::F64, ValType::I64)
3640
| (ValType::F64, ValType::F32)
3641
3642
// not used in the component model
3643
| (ValType::Ref(_), _)
3644
| (_, ValType::Ref(_))
3645
| (ValType::V128, _)
3646
| (_, ValType::V128) => {
3647
panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3648
}
3649
}
3650
}
3651
3652
fn assert_i64_upper_bits_not_set(&mut self, local: u32) {
3653
if !self.module.tunables.debug_adapter_modules {
3654
return;
3655
}
3656
self.instruction(LocalGet(local));
3657
self.instruction(I64Const(32));
3658
self.instruction(I64ShrU);
3659
self.instruction(I32WrapI64);
3660
self.instruction(If(BlockType::Empty));
3661
self.trap(Trap::DebugAssertUpperBitsUnset);
3662
self.instruction(End);
3663
}
3664
3665
/// Converts the top value on the WebAssembly stack which has type
3666
/// `src_ty` to `dst_tys[0]`.
3667
///
3668
/// This is only intended for conversion of primitives where the `dst_tys`
3669
/// list is known to be of length 1.
3670
fn stack_set(&mut self, dst_tys: &[ValType], src_ty: ValType) {
3671
assert_eq!(dst_tys.len(), 1);
3672
let dst_ty = dst_tys[0];
3673
match (src_ty, dst_ty) {
3674
(ValType::I32, ValType::I32)
3675
| (ValType::I64, ValType::I64)
3676
| (ValType::F32, ValType::F32)
3677
| (ValType::F64, ValType::F64) => {}
3678
3679
(ValType::F32, ValType::I32) => self.instruction(I32ReinterpretF32),
3680
(ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
3681
(ValType::F64, ValType::I64) => self.instruction(I64ReinterpretF64),
3682
(ValType::F32, ValType::I64) => {
3683
self.instruction(I32ReinterpretF32);
3684
self.instruction(I64ExtendI32U);
3685
}
3686
3687
// should not be possible given the `join` function for variants
3688
(ValType::I64, ValType::I32)
3689
| (ValType::F64, ValType::I32)
3690
| (ValType::I32, ValType::F32)
3691
| (ValType::I64, ValType::F32)
3692
| (ValType::F64, ValType::F32)
3693
| (ValType::I32, ValType::F64)
3694
| (ValType::I64, ValType::F64)
3695
| (ValType::F32, ValType::F64)
3696
3697
// not used in the component model
3698
| (ValType::Ref(_), _)
3699
| (_, ValType::Ref(_))
3700
| (ValType::V128, _)
3701
| (_, ValType::V128) => {
3702
panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3703
}
3704
}
3705
}
3706
3707
fn i32_load8u(&mut self, mem: &Memory) {
3708
self.instruction(LocalGet(mem.addr.idx));
3709
self.instruction(I32Load8U(mem.memarg(0)));
3710
}
3711
3712
fn i32_load8s(&mut self, mem: &Memory) {
3713
self.instruction(LocalGet(mem.addr.idx));
3714
self.instruction(I32Load8S(mem.memarg(0)));
3715
}
3716
3717
fn i32_load16u(&mut self, mem: &Memory) {
3718
self.instruction(LocalGet(mem.addr.idx));
3719
self.instruction(I32Load16U(mem.memarg(1)));
3720
}
3721
3722
fn i32_load16s(&mut self, mem: &Memory) {
3723
self.instruction(LocalGet(mem.addr.idx));
3724
self.instruction(I32Load16S(mem.memarg(1)));
3725
}
3726
3727
fn i32_load(&mut self, mem: &Memory) {
3728
self.instruction(LocalGet(mem.addr.idx));
3729
self.instruction(I32Load(mem.memarg(2)));
3730
}
3731
3732
fn i64_load(&mut self, mem: &Memory) {
3733
self.instruction(LocalGet(mem.addr.idx));
3734
self.instruction(I64Load(mem.memarg(3)));
3735
}
3736
3737
fn ptr_load(&mut self, mem: &Memory) {
3738
if mem.mem_opts().memory64 {
3739
self.i64_load(mem);
3740
} else {
3741
self.i32_load(mem);
3742
}
3743
}
3744
3745
fn ptr_add(&mut self, opts: &LinearMemoryOptions) {
3746
if opts.memory64 {
3747
self.instruction(I64Add);
3748
} else {
3749
self.instruction(I32Add);
3750
}
3751
}
3752
3753
fn ptr_sub(&mut self, opts: &LinearMemoryOptions) {
3754
if opts.memory64 {
3755
self.instruction(I64Sub);
3756
} else {
3757
self.instruction(I32Sub);
3758
}
3759
}
3760
3761
fn ptr_mul(&mut self, opts: &LinearMemoryOptions) {
3762
if opts.memory64 {
3763
self.instruction(I64Mul);
3764
} else {
3765
self.instruction(I32Mul);
3766
}
3767
}
3768
3769
fn ptr_ge_u(&mut self, opts: &LinearMemoryOptions) {
3770
if opts.memory64 {
3771
self.instruction(I64GeU);
3772
} else {
3773
self.instruction(I32GeU);
3774
}
3775
}
3776
3777
fn ptr_lt_u(&mut self, opts: &LinearMemoryOptions) {
3778
if opts.memory64 {
3779
self.instruction(I64LtU);
3780
} else {
3781
self.instruction(I32LtU);
3782
}
3783
}
3784
3785
fn ptr_shl(&mut self, opts: &LinearMemoryOptions) {
3786
if opts.memory64 {
3787
self.instruction(I64Shl);
3788
} else {
3789
self.instruction(I32Shl);
3790
}
3791
}
3792
3793
fn ptr_eqz(&mut self, opts: &LinearMemoryOptions) {
3794
if opts.memory64 {
3795
self.instruction(I64Eqz);
3796
} else {
3797
self.instruction(I32Eqz);
3798
}
3799
}
3800
3801
fn ptr_uconst(&mut self, opts: &LinearMemoryOptions, val: u32) {
3802
if opts.memory64 {
3803
self.instruction(I64Const(val.into()));
3804
} else {
3805
self.instruction(I32Const(val as i32));
3806
}
3807
}
3808
3809
fn ptr_iconst(&mut self, opts: &LinearMemoryOptions, val: i32) {
3810
if opts.memory64 {
3811
self.instruction(I64Const(val.into()));
3812
} else {
3813
self.instruction(I32Const(val));
3814
}
3815
}
3816
3817
fn ptr_eq(&mut self, opts: &LinearMemoryOptions) {
3818
if opts.memory64 {
3819
self.instruction(I64Eq);
3820
} else {
3821
self.instruction(I32Eq);
3822
}
3823
}
3824
3825
fn ptr_ne(&mut self, opts: &LinearMemoryOptions) {
3826
if opts.memory64 {
3827
self.instruction(I64Ne);
3828
} else {
3829
self.instruction(I32Ne);
3830
}
3831
}
3832
3833
fn ptr_and(&mut self, opts: &LinearMemoryOptions) {
3834
if opts.memory64 {
3835
self.instruction(I64And);
3836
} else {
3837
self.instruction(I32And);
3838
}
3839
}
3840
3841
fn ptr_or(&mut self, opts: &LinearMemoryOptions) {
3842
if opts.memory64 {
3843
self.instruction(I64Or);
3844
} else {
3845
self.instruction(I32Or);
3846
}
3847
}
3848
3849
fn ptr_xor(&mut self, opts: &LinearMemoryOptions) {
3850
if opts.memory64 {
3851
self.instruction(I64Xor);
3852
} else {
3853
self.instruction(I32Xor);
3854
}
3855
}
3856
3857
fn ptr_if(&mut self, opts: &LinearMemoryOptions, ty: BlockType) {
3858
if opts.memory64 {
3859
self.instruction(I64Const(0));
3860
self.instruction(I64Ne);
3861
}
3862
self.instruction(If(ty));
3863
}
3864
3865
fn ptr_br_if(&mut self, opts: &LinearMemoryOptions, depth: u32) {
3866
if opts.memory64 {
3867
self.instruction(I64Const(0));
3868
self.instruction(I64Ne);
3869
}
3870
self.instruction(BrIf(depth));
3871
}
3872
3873
fn f32_load(&mut self, mem: &Memory) {
3874
self.instruction(LocalGet(mem.addr.idx));
3875
self.instruction(F32Load(mem.memarg(2)));
3876
}
3877
3878
fn f64_load(&mut self, mem: &Memory) {
3879
self.instruction(LocalGet(mem.addr.idx));
3880
self.instruction(F64Load(mem.memarg(3)));
3881
}
3882
3883
fn push_dst_addr(&mut self, dst: &Destination) {
3884
if let Destination::Memory(mem) = dst {
3885
self.instruction(LocalGet(mem.addr.idx));
3886
}
3887
}
3888
3889
fn i32_store8(&mut self, mem: &Memory) {
3890
self.instruction(I32Store8(mem.memarg(0)));
3891
}
3892
3893
fn i32_store16(&mut self, mem: &Memory) {
3894
self.instruction(I32Store16(mem.memarg(1)));
3895
}
3896
3897
fn i32_store(&mut self, mem: &Memory) {
3898
self.instruction(I32Store(mem.memarg(2)));
3899
}
3900
3901
fn i64_store(&mut self, mem: &Memory) {
3902
self.instruction(I64Store(mem.memarg(3)));
3903
}
3904
3905
fn ptr_store(&mut self, mem: &Memory) {
3906
if mem.mem_opts().memory64 {
3907
self.i64_store(mem);
3908
} else {
3909
self.i32_store(mem);
3910
}
3911
}
3912
3913
fn f32_store(&mut self, mem: &Memory) {
3914
self.instruction(F32Store(mem.memarg(2)));
3915
}
3916
3917
fn f64_store(&mut self, mem: &Memory) {
3918
self.instruction(F64Store(mem.memarg(3)));
3919
}
3920
}
3921
3922
impl<'a> Source<'a> {
3923
/// Given this `Source` returns an iterator over the `Source` for each of
3924
/// the component `fields` specified.
3925
///
3926
/// This will automatically slice stack-based locals to the appropriate
3927
/// width for each component type and additionally calculate the appropriate
3928
/// offset for each memory-based type.
3929
fn record_field_srcs<'b>(
3930
&'b self,
3931
types: &'b ComponentTypesBuilder,
3932
fields: impl IntoIterator<Item = InterfaceType> + 'b,
3933
) -> impl Iterator<Item = Source<'a>> + 'b
3934
where
3935
'a: 'b,
3936
{
3937
let mut offset = 0;
3938
fields.into_iter().map(move |ty| match self {
3939
Source::Memory(mem) => {
3940
let mem = next_field_offset(&mut offset, types, &ty, mem);
3941
Source::Memory(mem)
3942
}
3943
Source::Stack(stack) => {
3944
let cnt = types.flat_types(&ty).unwrap().len() as u32;
3945
offset += cnt;
3946
Source::Stack(stack.slice((offset - cnt) as usize..offset as usize))
3947
}
3948
Source::Struct(_) => todo!(),
3949
Source::Array(_) => todo!(),
3950
})
3951
}
3952
3953
/// Returns the corresponding discriminant source and payload source f
3954
fn payload_src(
3955
&self,
3956
types: &ComponentTypesBuilder,
3957
info: &VariantInfo,
3958
case: Option<&InterfaceType>,
3959
) -> Source<'a> {
3960
match self {
3961
Source::Stack(s) => {
3962
let flat_len = match case {
3963
Some(case) => types.flat_types(case).unwrap().len(),
3964
None => 0,
3965
};
3966
Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
3967
}
3968
Source::Memory(mem) => {
3969
let mem = if mem.mem_opts().memory64 {
3970
mem.bump(info.payload_offset64)
3971
} else {
3972
mem.bump(info.payload_offset32)
3973
};
3974
Source::Memory(mem)
3975
}
3976
Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3977
}
3978
}
3979
3980
fn opts(&self) -> &'a Options {
3981
match self {
3982
Source::Stack(s) => s.opts,
3983
Source::Memory(mem) => mem.opts,
3984
Source::Struct(s) => s.opts,
3985
Source::Array(a) => a.opts,
3986
}
3987
}
3988
}
3989
3990
impl<'a> Destination<'a> {
3991
/// Same as `Source::record_field_srcs` but for destinations.
3992
fn record_field_dsts<'b, I>(
3993
&'b self,
3994
types: &'b ComponentTypesBuilder,
3995
fields: I,
3996
) -> impl Iterator<Item = Destination<'b>> + use<'b, I>
3997
where
3998
'a: 'b,
3999
I: IntoIterator<Item = InterfaceType> + 'b,
4000
{
4001
let mut offset = 0;
4002
fields.into_iter().map(move |ty| match self {
4003
Destination::Memory(mem) => {
4004
let mem = next_field_offset(&mut offset, types, &ty, mem);
4005
Destination::Memory(mem)
4006
}
4007
Destination::Stack(s, opts) => {
4008
let cnt = types.flat_types(&ty).unwrap().len() as u32;
4009
offset += cnt;
4010
Destination::Stack(&s[(offset - cnt) as usize..offset as usize], opts)
4011
}
4012
Destination::Struct(_) => todo!(),
4013
Destination::Array(_) => todo!(),
4014
})
4015
}
4016
4017
/// Returns the corresponding discriminant source and payload source f
4018
fn payload_dst(
4019
&self,
4020
types: &ComponentTypesBuilder,
4021
info: &VariantInfo,
4022
case: Option<&InterfaceType>,
4023
) -> Destination<'_> {
4024
match self {
4025
Destination::Stack(s, opts) => {
4026
let flat_len = match case {
4027
Some(case) => types.flat_types(case).unwrap().len(),
4028
None => 0,
4029
};
4030
Destination::Stack(&s[1..][..flat_len], opts)
4031
}
4032
Destination::Memory(mem) => {
4033
let mem = if mem.mem_opts().memory64 {
4034
mem.bump(info.payload_offset64)
4035
} else {
4036
mem.bump(info.payload_offset32)
4037
};
4038
Destination::Memory(mem)
4039
}
4040
Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
4041
}
4042
}
4043
4044
fn opts(&self) -> &'a Options {
4045
match self {
4046
Destination::Stack(_, opts) => opts,
4047
Destination::Memory(mem) => mem.opts,
4048
Destination::Struct(s) => s.opts,
4049
Destination::Array(a) => a.opts,
4050
}
4051
}
4052
}
4053
4054
fn next_field_offset<'a>(
4055
offset: &mut u32,
4056
types: &ComponentTypesBuilder,
4057
field: &InterfaceType,
4058
mem: &Memory<'a>,
4059
) -> Memory<'a> {
4060
let abi = types.canonical_abi(field);
4061
let offset = if mem.mem_opts().memory64 {
4062
abi.next_field64(offset)
4063
} else {
4064
abi.next_field32(offset)
4065
};
4066
mem.bump(offset)
4067
}
4068
4069
impl<'a> Memory<'a> {
4070
fn memarg(&self, align: u32) -> MemArg {
4071
MemArg {
4072
offset: u64::from(self.offset),
4073
align,
4074
memory_index: self.mem_opts().memory.unwrap().as_u32(),
4075
}
4076
}
4077
4078
fn bump(&self, offset: u32) -> Memory<'a> {
4079
Memory {
4080
opts: self.opts,
4081
addr: TempLocal::new(self.addr.idx, self.addr.ty),
4082
offset: self.offset + offset,
4083
}
4084
}
4085
}
4086
4087
impl<'a> Stack<'a> {
4088
fn slice(&self, range: Range<usize>) -> Stack<'a> {
4089
Stack {
4090
locals: &self.locals[range],
4091
opts: self.opts,
4092
}
4093
}
4094
}
4095
4096
struct VariantCase<'a> {
4097
src_i: u32,
4098
src_ty: Option<&'a InterfaceType>,
4099
dst_i: u32,
4100
dst_ty: Option<&'a InterfaceType>,
4101
}
4102
4103
fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
4104
where
4105
I: IntoIterator<Item = Option<&'a InterfaceType>>,
4106
I::IntoIter: ExactSizeIterator,
4107
{
4108
VariantInfo::new(
4109
cases
4110
.into_iter()
4111
.map(|ty| ty.map(|ty| types.canonical_abi(ty))),
4112
)
4113
.0
4114
}
4115
4116
enum MallocSize {
4117
Const(u32),
4118
Local(u32),
4119
}
4120
4121
struct WasmString<'a> {
4122
ptr: TempLocal,
4123
len: TempLocal,
4124
opts: &'a Options,
4125
}
4126
4127
struct TempLocal {
4128
idx: u32,
4129
ty: ValType,
4130
needs_free: bool,
4131
}
4132
4133
impl TempLocal {
4134
fn new(idx: u32, ty: ValType) -> TempLocal {
4135
TempLocal {
4136
idx,
4137
ty,
4138
needs_free: false,
4139
}
4140
}
4141
}
4142
4143
impl std::ops::Drop for TempLocal {
4144
fn drop(&mut self) {
4145
if self.needs_free {
4146
panic!("temporary local not free'd");
4147
}
4148
}
4149
}
4150
4151
impl From<FlatType> for ValType {
4152
fn from(ty: FlatType) -> ValType {
4153
match ty {
4154
FlatType::I32 => ValType::I32,
4155
FlatType::I64 => ValType::I64,
4156
FlatType::F32 => ValType::F32,
4157
FlatType::F64 => ValType::F64,
4158
}
4159
}
4160
}
4161
4162