Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/cranelift/src/translate/code_translator.rs
1692 views
1
//! This module contains the bulk of the interesting code performing the translation between
2
//! WebAssembly and Cranelift IR.
3
//!
4
//! The translation is done in one pass, opcode by opcode. Two main data structures are used during
5
//! code translations: the value stack and the control stack. The value stack mimics the execution
6
//! of the WebAssembly stack machine: each instruction result is pushed onto the stack and
7
//! instruction arguments are popped off the stack. Similarly, when encountering a control flow
8
//! block, it is pushed onto the control stack and popped off when encountering the corresponding
9
//! `End`.
10
//!
11
//! Another data structure, the translation state, records information concerning unreachable code
12
//! status and about if inserting a return at the end of the function is necessary.
13
//!
14
//! Some of the WebAssembly instructions need information about the environment for which they
15
//! are being translated:
16
//!
17
//! - the loads and stores need the memory base address;
18
//! - the `get_global` and `set_global` instructions depend on how the globals are implemented;
19
//! - `memory.size` and `memory.grow` are runtime functions;
20
//! - `call_indirect` has to translate the function index into the address of where this
21
//! is;
22
//!
23
//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
24
//! argument.
25
//!
26
//! There is extra complexity associated with translation of 128-bit SIMD instructions.
27
//! Wasm only considers there to be a single 128-bit vector type. But CLIF's type system
28
//! distinguishes different lane configurations, so considers 8X16, 16X8, 32X4 and 64X2 to be
29
//! different types. The result is that, in wasm, it's perfectly OK to take the output of (eg)
30
//! an `add.16x8` and use that as an operand of a `sub.32x4`, without using any cast. But when
31
//! translated into CLIF, that will cause a verifier error due to the apparent type mismatch.
32
//!
33
//! This file works around that problem by liberally inserting `bitcast` instructions in many
34
//! places -- mostly, before the use of vector values, either as arguments to CLIF instructions
35
//! or as block actual parameters. These are no-op casts which nevertheless have different
36
//! input and output types, and are used (mostly) to "convert" 16X8, 32X4 and 64X2-typed vectors
37
//! to the "canonical" type, 8X16. Hence the functions `optionally_bitcast_vector`,
38
//! `bitcast_arguments`, `pop*_with_bitcast`, `canonicalise_then_jump`,
39
//! `canonicalise_then_br{z,nz}`, `is_non_canonical_v128` and `canonicalise_v128_values`.
40
//! Note that the `bitcast*` functions are occasionally used to convert to some type other than
41
//! 8X16, but the `canonicalise*` functions always convert to type 8X16.
42
//!
43
//! Be careful when adding support for new vector instructions. And when adding new jumps, even
44
//! if they are apparently don't have any connection to vectors. Never generate any kind of
45
//! (inter-block) jump directly. Instead use `canonicalise_then_jump` and
46
//! `canonicalise_then_br{z,nz}`.
47
//!
48
//! The use of bitcasts is ugly and inefficient, but currently unavoidable:
49
//!
50
//! * they make the logic in this file fragile: miss out a bitcast for any reason, and there is
51
//! the risk of the system failing in the verifier. At least for debug builds.
52
//!
53
//! * in the new backends, they potentially interfere with pattern matching on CLIF -- the
54
//! patterns need to take into account the presence of bitcast nodes.
55
//!
56
//! * in the new backends, they get translated into machine-level vector-register-copy
57
//! instructions, none of which are actually necessary. We then depend on the register
58
//! allocator to coalesce them all out.
59
//!
60
//! * they increase the total number of CLIF nodes that have to be processed, hence slowing down
61
//! the compilation pipeline. Also, the extra coalescing work generates a slowdown.
62
//!
63
//! A better solution which would avoid all four problems would be to remove the 8X16, 16X8,
64
//! 32X4 and 64X2 types from CLIF and instead have a single V128 type.
65
//!
66
//! For further background see also:
67
//! <https://github.com/bytecodealliance/wasmtime/issues/1147>
68
//! ("Too many raw_bitcasts in SIMD code")
69
//! <https://github.com/bytecodealliance/cranelift/pull/1251>
70
//! ("Add X128 type to represent WebAssembly's V128 type")
71
//! <https://github.com/bytecodealliance/cranelift/pull/1236>
72
//! ("Relax verification to allow I8X16 to act as a default vector type")
73
74
use crate::Reachability;
75
use crate::bounds_checks::{BoundsCheck, bounds_check_and_compute_addr};
76
use crate::func_environ::{Extension, FuncEnvironment};
77
use crate::translate::TargetEnvironment;
78
use crate::translate::environ::StructFieldsVec;
79
use crate::translate::stack::{ControlStackFrame, ElseData, FuncTranslationStacks};
80
use crate::translate::translation_utils::{
81
block_with_params, blocktype_params_results, f32_translation, f64_translation,
82
};
83
use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
84
use cranelift_codegen::ir::immediates::Offset32;
85
use cranelift_codegen::ir::{
86
self, AtomicRmwOp, ExceptionTag, InstBuilder, JumpTableData, MemFlags, Value, ValueLabel,
87
};
88
use cranelift_codegen::ir::{BlockArg, types::*};
89
use cranelift_codegen::packed_option::ReservedValue;
90
use cranelift_frontend::{FunctionBuilder, Variable};
91
use itertools::Itertools;
92
use smallvec::{SmallVec, ToSmallVec};
93
use std::collections::{HashMap, hash_map};
94
use std::vec::Vec;
95
use wasmparser::{FuncValidator, MemArg, Operator, WasmModuleResources};
96
use wasmtime_environ::{
97
DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TagIndex, TypeConvert,
98
TypeIndex, WasmHeapType, WasmRefType, WasmResult, WasmValType, wasm_unsupported,
99
};
100
101
/// Given a `Reachability<T>`, unwrap the inner `T` or, when unreachable, set
102
/// `state.reachable = false` and return.
103
///
104
/// Used in combination with calling `prepare_addr` and `prepare_atomic_addr`
105
/// when we can statically determine that a Wasm access will unconditionally
106
/// trap.
107
macro_rules! unwrap_or_return_unreachable_state {
108
($state:ident, $value:expr) => {
109
match $value {
110
Reachability::Reachable(x) => x,
111
Reachability::Unreachable => {
112
$state.reachable = false;
113
return Ok(());
114
}
115
}
116
};
117
}
118
119
/// Translates wasm operators into Cranelift IR instructions.
120
pub fn translate_operator(
121
validator: &mut FuncValidator<impl WasmModuleResources>,
122
op: &Operator,
123
operand_types: Option<&[WasmValType]>,
124
builder: &mut FunctionBuilder,
125
stack: &mut FuncTranslationStacks,
126
environ: &mut FuncEnvironment<'_>,
127
) -> WasmResult<()> {
128
log::trace!("Translating Wasm opcode: {op:?}");
129
130
if !stack.reachable {
131
translate_unreachable_operator(validator, &op, builder, stack, environ)?;
132
return Ok(());
133
}
134
135
// Given that we believe the current block is reachable, the FunctionBuilder ought to agree.
136
debug_assert!(!builder.is_unreachable());
137
138
let operand_types = operand_types.unwrap_or_else(|| {
139
panic!("should always have operand types available for valid, reachable ops; op = {op:?}")
140
});
141
142
// This big match treats all Wasm code operators.
143
match op {
144
/********************************** Locals ****************************************
145
* `get_local` and `set_local` are treated as non-SSA variables and will completely
146
* disappear in the Cranelift Code
147
***********************************************************************************/
148
Operator::LocalGet { local_index } => {
149
let val = builder.use_var(Variable::from_u32(*local_index));
150
stack.push1(val);
151
let label = ValueLabel::from_u32(*local_index);
152
builder.set_val_label(val, label);
153
}
154
Operator::LocalSet { local_index } => {
155
let mut val = stack.pop1();
156
157
// Ensure SIMD values are cast to their default Cranelift type, I8x16.
158
let ty = builder.func.dfg.value_type(val);
159
if ty.is_vector() {
160
val = optionally_bitcast_vector(val, I8X16, builder);
161
}
162
163
builder.def_var(Variable::from_u32(*local_index), val);
164
let label = ValueLabel::from_u32(*local_index);
165
builder.set_val_label(val, label);
166
}
167
Operator::LocalTee { local_index } => {
168
let mut val = stack.peek1();
169
170
// Ensure SIMD values are cast to their default Cranelift type, I8x16.
171
let ty = builder.func.dfg.value_type(val);
172
if ty.is_vector() {
173
val = optionally_bitcast_vector(val, I8X16, builder);
174
}
175
176
builder.def_var(Variable::from_u32(*local_index), val);
177
let label = ValueLabel::from_u32(*local_index);
178
builder.set_val_label(val, label);
179
}
180
/********************************** Globals ****************************************
181
* `get_global` and `set_global` are handled by the environment.
182
***********************************************************************************/
183
Operator::GlobalGet { global_index } => {
184
let global_index = GlobalIndex::from_u32(*global_index);
185
let val = environ.translate_global_get(builder, global_index)?;
186
stack.push1(val);
187
}
188
Operator::GlobalSet { global_index } => {
189
let global_index = GlobalIndex::from_u32(*global_index);
190
let mut val = stack.pop1();
191
// Ensure SIMD values are cast to their default Cranelift type, I8x16.
192
if builder.func.dfg.value_type(val).is_vector() {
193
val = optionally_bitcast_vector(val, I8X16, builder);
194
}
195
environ.translate_global_set(builder, global_index, val)?;
196
}
197
/********************************* Stack misc ***************************************
198
* `drop`, `nop`, `unreachable` and `select`.
199
***********************************************************************************/
200
Operator::Drop => {
201
stack.pop1();
202
}
203
Operator::Select => {
204
let (mut arg1, mut arg2, cond) = stack.pop3();
205
if builder.func.dfg.value_type(arg1).is_vector() {
206
arg1 = optionally_bitcast_vector(arg1, I8X16, builder);
207
}
208
if builder.func.dfg.value_type(arg2).is_vector() {
209
arg2 = optionally_bitcast_vector(arg2, I8X16, builder);
210
}
211
stack.push1(builder.ins().select(cond, arg1, arg2));
212
}
213
Operator::TypedSelect { ty: _ } => {
214
// We ignore the explicit type parameter as it is only needed for
215
// validation, which we require to have been performed before
216
// translation.
217
let (mut arg1, mut arg2, cond) = stack.pop3();
218
if builder.func.dfg.value_type(arg1).is_vector() {
219
arg1 = optionally_bitcast_vector(arg1, I8X16, builder);
220
}
221
if builder.func.dfg.value_type(arg2).is_vector() {
222
arg2 = optionally_bitcast_vector(arg2, I8X16, builder);
223
}
224
stack.push1(builder.ins().select(cond, arg1, arg2));
225
}
226
Operator::Nop => {
227
// We do nothing
228
}
229
Operator::Unreachable => {
230
environ.trap(builder, crate::TRAP_UNREACHABLE);
231
stack.reachable = false;
232
}
233
/***************************** Control flow blocks **********************************
234
* When starting a control flow block, we create a new `Block` that will hold the code
235
* after the block, and we push a frame on the control stack. Depending on the type
236
* of block, we create a new `Block` for the body of the block with an associated
237
* jump instruction.
238
*
239
* The `End` instruction pops the last control frame from the control stack, seals
240
* the destination block (since `br` instructions targeting it only appear inside the
241
* block and have already been translated) and modify the value stack to use the
242
* possible `Block`'s arguments values.
243
***********************************************************************************/
244
Operator::Block { blockty } => {
245
let (params, results) = blocktype_params_results(validator, *blockty)?;
246
let next = block_with_params(builder, results.clone(), environ)?;
247
stack.push_block(next, params.len(), results.len());
248
}
249
Operator::Loop { blockty } => {
250
let (params, results) = blocktype_params_results(validator, *blockty)?;
251
let loop_body = block_with_params(builder, params.clone(), environ)?;
252
let next = block_with_params(builder, results.clone(), environ)?;
253
canonicalise_then_jump(builder, loop_body, stack.peekn(params.len()));
254
stack.push_loop(loop_body, next, params.len(), results.len());
255
256
// Pop the initial `Block` actuals and replace them with the `Block`'s
257
// params since control flow joins at the top of the loop.
258
stack.popn(params.len());
259
stack
260
.stack
261
.extend_from_slice(builder.block_params(loop_body));
262
263
builder.switch_to_block(loop_body);
264
environ.translate_loop_header(builder)?;
265
}
266
Operator::If { blockty } => {
267
let val = stack.pop1();
268
269
let next_block = builder.create_block();
270
let (params, results) = blocktype_params_results(validator, *blockty)?;
271
let (destination, else_data) = if params.clone().eq(results.clone()) {
272
// It is possible there is no `else` block, so we will only
273
// allocate a block for it if/when we find the `else`. For now,
274
// we if the condition isn't true, then we jump directly to the
275
// destination block following the whole `if...end`. If we do end
276
// up discovering an `else`, then we will allocate a block for it
277
// and go back and patch the jump.
278
let destination = block_with_params(builder, results.clone(), environ)?;
279
let branch_inst = canonicalise_brif(
280
builder,
281
val,
282
next_block,
283
&[],
284
destination,
285
stack.peekn(params.len()),
286
);
287
(
288
destination,
289
ElseData::NoElse {
290
branch_inst,
291
placeholder: destination,
292
},
293
)
294
} else {
295
// The `if` type signature is not valid without an `else` block,
296
// so we eagerly allocate the `else` block here.
297
let destination = block_with_params(builder, results.clone(), environ)?;
298
let else_block = block_with_params(builder, params.clone(), environ)?;
299
canonicalise_brif(
300
builder,
301
val,
302
next_block,
303
&[],
304
else_block,
305
stack.peekn(params.len()),
306
);
307
builder.seal_block(else_block);
308
(destination, ElseData::WithElse { else_block })
309
};
310
311
builder.seal_block(next_block); // Only predecessor is the current block.
312
builder.switch_to_block(next_block);
313
314
// Here we append an argument to a Block targeted by an argumentless jump instruction
315
// But in fact there are two cases:
316
// - either the If does not have a Else clause, in that case ty = EmptyBlock
317
// and we add nothing;
318
// - either the If have an Else clause, in that case the destination of this jump
319
// instruction will be changed later when we translate the Else operator.
320
stack.push_if(
321
destination,
322
else_data,
323
params.len(),
324
results.len(),
325
*blockty,
326
);
327
}
328
Operator::Else => {
329
let i = stack.control_stack.len() - 1;
330
match stack.control_stack[i] {
331
ControlStackFrame::If {
332
ref else_data,
333
head_is_reachable,
334
ref mut consequent_ends_reachable,
335
num_return_values,
336
blocktype,
337
destination,
338
..
339
} => {
340
// We finished the consequent, so record its final
341
// reachability state.
342
debug_assert!(consequent_ends_reachable.is_none());
343
*consequent_ends_reachable = Some(stack.reachable);
344
345
if head_is_reachable {
346
// We have a branch from the head of the `if` to the `else`.
347
stack.reachable = true;
348
349
// Ensure we have a block for the `else` block (it may have
350
// already been pre-allocated, see `ElseData` for details).
351
let else_block = match *else_data {
352
ElseData::NoElse {
353
branch_inst,
354
placeholder,
355
} => {
356
let (params, _results) =
357
blocktype_params_results(validator, blocktype)?;
358
debug_assert_eq!(params.len(), num_return_values);
359
let else_block =
360
block_with_params(builder, params.clone(), environ)?;
361
canonicalise_then_jump(
362
builder,
363
destination,
364
stack.peekn(params.len()),
365
);
366
stack.popn(params.len());
367
368
builder.change_jump_destination(
369
branch_inst,
370
placeholder,
371
else_block,
372
);
373
builder.seal_block(else_block);
374
else_block
375
}
376
ElseData::WithElse { else_block } => {
377
canonicalise_then_jump(
378
builder,
379
destination,
380
stack.peekn(num_return_values),
381
);
382
stack.popn(num_return_values);
383
else_block
384
}
385
};
386
387
// You might be expecting that we push the parameters for this
388
// `else` block here, something like this:
389
//
390
// state.pushn(&control_stack_frame.params);
391
//
392
// We don't do that because they are already on the top of the stack
393
// for us: we pushed the parameters twice when we saw the initial
394
// `if` so that we wouldn't have to save the parameters in the
395
// `ControlStackFrame` as another `Vec` allocation.
396
397
builder.switch_to_block(else_block);
398
399
// We don't bother updating the control frame's `ElseData`
400
// to `WithElse` because nothing else will read it.
401
}
402
}
403
_ => unreachable!(),
404
}
405
}
406
Operator::End => {
407
let frame = stack.control_stack.pop().unwrap();
408
let next_block = frame.following_code();
409
let return_count = frame.num_return_values();
410
let return_args = stack.peekn_mut(return_count);
411
412
canonicalise_then_jump(builder, next_block, return_args);
413
// You might expect that if we just finished an `if` block that
414
// didn't have a corresponding `else` block, then we would clean
415
// up our duplicate set of parameters that we pushed earlier
416
// right here. However, we don't have to explicitly do that,
417
// since we truncate the stack back to the original height
418
// below.
419
420
builder.switch_to_block(next_block);
421
builder.seal_block(next_block);
422
423
// If it is a loop we also have to seal the body loop block
424
if let ControlStackFrame::Loop { header, .. } = frame {
425
builder.seal_block(header)
426
}
427
428
frame.restore_catch_handlers(&mut stack.handlers, builder);
429
430
frame.truncate_value_stack_to_original_size(&mut stack.stack);
431
stack
432
.stack
433
.extend_from_slice(builder.block_params(next_block));
434
}
435
/**************************** Branch instructions *********************************
436
* The branch instructions all have as arguments a target nesting level, which
437
* corresponds to how many control stack frames do we have to pop to get the
438
* destination `Block`.
439
*
440
* Once the destination `Block` is found, we sometimes have to declare a certain depth
441
* of the stack unreachable, because some branch instructions are terminator.
442
*
443
* The `br_table` case is much more complicated because Cranelift's `br_table` instruction
444
* does not support jump arguments like all the other branch instructions. That is why, in
445
* the case where we would use jump arguments for every other branch instruction, we
446
* need to split the critical edges leaving the `br_tables` by creating one `Block` per
447
* table destination; the `br_table` will point to these newly created `Blocks` and these
448
* `Block`s contain only a jump instruction pointing to the final destination, this time with
449
* jump arguments.
450
*
451
* This system is also implemented in Cranelift's SSA construction algorithm, because
452
* `use_var` located in a destination `Block` of a `br_table` might trigger the addition
453
* of jump arguments in each predecessor branch instruction, one of which might be a
454
* `br_table`.
455
***********************************************************************************/
456
Operator::Br { relative_depth } => {
457
let i = stack.control_stack.len() - 1 - (*relative_depth as usize);
458
let (return_count, br_destination) = {
459
let frame = &mut stack.control_stack[i];
460
// We signal that all the code that follows until the next End is unreachable
461
frame.set_branched_to_exit();
462
let return_count = if frame.is_loop() {
463
frame.num_param_values()
464
} else {
465
frame.num_return_values()
466
};
467
(return_count, frame.br_destination())
468
};
469
let destination_args = stack.peekn_mut(return_count);
470
canonicalise_then_jump(builder, br_destination, destination_args);
471
stack.popn(return_count);
472
stack.reachable = false;
473
}
474
Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, stack),
475
Operator::BrTable { targets } => {
476
let default = targets.default();
477
let mut min_depth = default;
478
for depth in targets.targets() {
479
let depth = depth?;
480
if depth < min_depth {
481
min_depth = depth;
482
}
483
}
484
let jump_args_count = {
485
let i = stack.control_stack.len() - 1 - (min_depth as usize);
486
let min_depth_frame = &stack.control_stack[i];
487
if min_depth_frame.is_loop() {
488
min_depth_frame.num_param_values()
489
} else {
490
min_depth_frame.num_return_values()
491
}
492
};
493
let val = stack.pop1();
494
let mut data = Vec::with_capacity(targets.len() as usize);
495
if jump_args_count == 0 {
496
// No jump arguments
497
for depth in targets.targets() {
498
let depth = depth?;
499
let block = {
500
let i = stack.control_stack.len() - 1 - (depth as usize);
501
let frame = &mut stack.control_stack[i];
502
frame.set_branched_to_exit();
503
frame.br_destination()
504
};
505
data.push(builder.func.dfg.block_call(block, &[]));
506
}
507
let block = {
508
let i = stack.control_stack.len() - 1 - (default as usize);
509
let frame = &mut stack.control_stack[i];
510
frame.set_branched_to_exit();
511
frame.br_destination()
512
};
513
let block = builder.func.dfg.block_call(block, &[]);
514
let jt = builder.create_jump_table(JumpTableData::new(block, &data));
515
builder.ins().br_table(val, jt);
516
} else {
517
// Here we have jump arguments, but Cranelift's br_table doesn't support them
518
// We then proceed to split the edges going out of the br_table
519
let return_count = jump_args_count;
520
let mut dest_block_sequence = vec![];
521
let mut dest_block_map = HashMap::new();
522
for depth in targets.targets() {
523
let depth = depth?;
524
let branch_block = match dest_block_map.entry(depth as usize) {
525
hash_map::Entry::Occupied(entry) => *entry.get(),
526
hash_map::Entry::Vacant(entry) => {
527
let block = builder.create_block();
528
dest_block_sequence.push((depth as usize, block));
529
*entry.insert(block)
530
}
531
};
532
data.push(builder.func.dfg.block_call(branch_block, &[]));
533
}
534
let default_branch_block = match dest_block_map.entry(default as usize) {
535
hash_map::Entry::Occupied(entry) => *entry.get(),
536
hash_map::Entry::Vacant(entry) => {
537
let block = builder.create_block();
538
dest_block_sequence.push((default as usize, block));
539
*entry.insert(block)
540
}
541
};
542
let default_branch_block = builder.func.dfg.block_call(default_branch_block, &[]);
543
let jt = builder.create_jump_table(JumpTableData::new(default_branch_block, &data));
544
builder.ins().br_table(val, jt);
545
for (depth, dest_block) in dest_block_sequence {
546
builder.switch_to_block(dest_block);
547
builder.seal_block(dest_block);
548
let real_dest_block = {
549
let i = stack.control_stack.len() - 1 - depth;
550
let frame = &mut stack.control_stack[i];
551
frame.set_branched_to_exit();
552
frame.br_destination()
553
};
554
let destination_args = stack.peekn_mut(return_count);
555
canonicalise_then_jump(builder, real_dest_block, destination_args);
556
}
557
stack.popn(return_count);
558
}
559
stack.reachable = false;
560
}
561
Operator::Return => {
562
let return_count = {
563
let frame = &mut stack.control_stack[0];
564
frame.num_return_values()
565
};
566
{
567
let return_args = stack.peekn_mut(return_count);
568
environ.handle_before_return(&return_args, builder);
569
bitcast_wasm_returns(return_args, builder);
570
builder.ins().return_(return_args);
571
}
572
stack.popn(return_count);
573
stack.reachable = false;
574
}
575
/********************************** Exception handling **********************************/
576
Operator::Catch { .. }
577
| Operator::Rethrow { .. }
578
| Operator::Delegate { .. }
579
| Operator::CatchAll => {
580
return Err(wasm_unsupported!(
581
"legacy exception handling proposal is not supported"
582
));
583
}
584
585
Operator::TryTable { try_table } => {
586
// First, create a block on the control stack. This also
587
// updates the handler state that is attached to all calls
588
// made within this block.
589
let body = builder.create_block();
590
let (params, results) = blocktype_params_results(validator, try_table.ty)?;
591
let next = block_with_params(builder, results.clone(), environ)?;
592
builder.ins().jump(body, []);
593
builder.seal_block(body);
594
595
// For each catch clause, create a block with the
596
// equivalent of `br` to the target (unboxing the exnref
597
// into stack values or pushing it directly, depending on
598
// the kind of clause).
599
let ckpt = stack.handlers.take_checkpoint();
600
let mut catch_blocks = vec![];
601
// Process in *reverse* order: see the comment on
602
// [`HandlerState`]. In brief, this allows us to unify the
603
// left-to-right matching semantics of a single
604
// `try_table`'s catch clauses with the inside-out
605
// (deepest scope first) semantics of nested `try_table`s.
606
for catch in try_table.catches.iter().rev() {
607
// This will register the block in `state.handlers`
608
// under the appropriate tag.
609
catch_blocks.push(create_catch_block(builder, stack, catch, environ)?);
610
}
611
612
stack.push_try_table_block(next, catch_blocks, params.len(), results.len(), ckpt);
613
614
// Continue codegen into the main body block.
615
builder.switch_to_block(body);
616
}
617
618
Operator::Throw { tag_index } => {
619
let tag_index = TagIndex::from_u32(*tag_index);
620
let arity = environ.tag_param_arity(tag_index);
621
let args = stack.peekn(arity);
622
environ.translate_exn_throw(builder, tag_index, args, stack.handlers.handlers())?;
623
stack.popn(arity);
624
stack.reachable = false;
625
}
626
627
Operator::ThrowRef => {
628
let exnref = stack.pop1();
629
environ.translate_exn_throw_ref(builder, exnref, stack.handlers.handlers())?;
630
stack.reachable = false;
631
}
632
633
/************************************ Calls ****************************************
634
* The call instructions pop off their arguments from the stack and append their
635
* return values to it. `call_indirect` needs environment support because there is an
636
* argument referring to an index in the external functions table of the module.
637
************************************************************************************/
638
Operator::Call { function_index } => {
639
let function_index = FuncIndex::from_u32(*function_index);
640
let ty = environ.module.functions[function_index]
641
.signature
642
.unwrap_module_type_index();
643
let sig_ref = environ.get_or_create_interned_sig_ref(builder.func, ty);
644
let num_args = environ.num_params_for_func(function_index);
645
646
// Bitcast any vector arguments to their default type, I8X16, before calling.
647
let args = stack.peekn_mut(num_args);
648
bitcast_wasm_params(environ, sig_ref, args, builder);
649
let args = stack.peekn(num_args); // Re-borrow immutably.
650
651
let inst_results = environ.translate_call(
652
builder,
653
function_index,
654
sig_ref,
655
args,
656
stack.handlers.handlers(),
657
)?;
658
659
debug_assert_eq!(
660
inst_results.len(),
661
builder.func.dfg.signatures[sig_ref].returns.len(),
662
"translate_call results should match the call signature"
663
);
664
stack.popn(num_args);
665
stack.pushn(&inst_results);
666
}
667
Operator::CallIndirect {
668
type_index,
669
table_index,
670
} => {
671
// `type_index` is the index of the function's signature and
672
// `table_index` is the index of the table to search the function
673
// in.
674
let type_index = TypeIndex::from_u32(*type_index);
675
let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
676
let num_args = environ.num_params_for_function_type(type_index);
677
let callee = stack.pop1();
678
679
// Bitcast any vector arguments to their default type, I8X16, before calling.
680
let args = stack.peekn_mut(num_args);
681
bitcast_wasm_params(environ, sigref, args, builder);
682
683
let inst_results = environ.translate_call_indirect(
684
builder,
685
validator.features(),
686
TableIndex::from_u32(*table_index),
687
type_index,
688
sigref,
689
callee,
690
stack.peekn(num_args),
691
stack.handlers.handlers(),
692
)?;
693
let inst_results = match inst_results {
694
Some(results) => results,
695
None => {
696
stack.reachable = false;
697
return Ok(());
698
}
699
};
700
701
debug_assert_eq!(
702
inst_results.len(),
703
builder.func.dfg.signatures[sigref].returns.len(),
704
"translate_call_indirect results should match the call signature"
705
);
706
stack.popn(num_args);
707
stack.pushn(&inst_results);
708
}
709
/******************************* Tail Calls ******************************************
710
* The tail call instructions pop their arguments from the stack and
711
* then permanently transfer control to their callee. The indirect
712
* version requires environment support (while the direct version can
713
* optionally be hooked but doesn't require it) it interacts with the
714
* VM's runtime state via tables.
715
************************************************************************************/
716
Operator::ReturnCall { function_index } => {
717
let function_index = FuncIndex::from_u32(*function_index);
718
let ty = environ.module.functions[function_index]
719
.signature
720
.unwrap_module_type_index();
721
let sig_ref = environ.get_or_create_interned_sig_ref(builder.func, ty);
722
let num_args = environ.num_params_for_func(function_index);
723
724
// Bitcast any vector arguments to their default type, I8X16, before calling.
725
let args = stack.peekn_mut(num_args);
726
bitcast_wasm_params(environ, sig_ref, args, builder);
727
728
environ.translate_return_call(builder, function_index, sig_ref, args)?;
729
730
stack.popn(num_args);
731
stack.reachable = false;
732
}
733
Operator::ReturnCallIndirect {
734
type_index,
735
table_index,
736
} => {
737
// `type_index` is the index of the function's signature and
738
// `table_index` is the index of the table to search the function
739
// in.
740
let type_index = TypeIndex::from_u32(*type_index);
741
let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
742
let num_args = environ.num_params_for_function_type(type_index);
743
let callee = stack.pop1();
744
745
// Bitcast any vector arguments to their default type, I8X16, before calling.
746
let args = stack.peekn_mut(num_args);
747
bitcast_wasm_params(environ, sigref, args, builder);
748
749
environ.translate_return_call_indirect(
750
builder,
751
validator.features(),
752
TableIndex::from_u32(*table_index),
753
type_index,
754
sigref,
755
callee,
756
stack.peekn(num_args),
757
)?;
758
759
stack.popn(num_args);
760
stack.reachable = false;
761
}
762
Operator::ReturnCallRef { type_index } => {
763
// Get function signature
764
// `index` is the index of the function's signature and `table_index` is the index of
765
// the table to search the function in.
766
let type_index = TypeIndex::from_u32(*type_index);
767
let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
768
let num_args = environ.num_params_for_function_type(type_index);
769
let callee = stack.pop1();
770
771
// Bitcast any vector arguments to their default type, I8X16, before calling.
772
let args = stack.peekn_mut(num_args);
773
bitcast_wasm_params(environ, sigref, args, builder);
774
775
environ.translate_return_call_ref(builder, sigref, callee, stack.peekn(num_args))?;
776
777
stack.popn(num_args);
778
stack.reachable = false;
779
}
780
/******************************* Memory management ***********************************
781
* Memory management is handled by environment. It is usually translated into calls to
782
* special functions.
783
************************************************************************************/
784
Operator::MemoryGrow { mem } => {
785
// The WebAssembly MVP only supports one linear memory, but we expect the reserved
786
// argument to be a memory index.
787
let mem = MemoryIndex::from_u32(*mem);
788
let _heap = environ.get_or_create_heap(builder.func, mem);
789
let val = stack.pop1();
790
environ.before_memory_grow(builder, val, mem);
791
stack.push1(environ.translate_memory_grow(builder, mem, val)?)
792
}
793
Operator::MemorySize { mem } => {
794
let mem = MemoryIndex::from_u32(*mem);
795
let _heap = environ.get_or_create_heap(builder.func, mem);
796
stack.push1(environ.translate_memory_size(builder.cursor(), mem)?);
797
}
798
/******************************* Load instructions ***********************************
799
* Wasm specifies an integer alignment flag but we drop it in Cranelift.
800
* The memory base address is provided by the environment.
801
************************************************************************************/
802
Operator::I32Load8U { memarg } => {
803
unwrap_or_return_unreachable_state!(
804
stack,
805
translate_load(memarg, ir::Opcode::Uload8, I32, builder, stack, environ)?
806
);
807
}
808
Operator::I32Load16U { memarg } => {
809
unwrap_or_return_unreachable_state!(
810
stack,
811
translate_load(memarg, ir::Opcode::Uload16, I32, builder, stack, environ)?
812
);
813
}
814
Operator::I32Load8S { memarg } => {
815
unwrap_or_return_unreachable_state!(
816
stack,
817
translate_load(memarg, ir::Opcode::Sload8, I32, builder, stack, environ)?
818
);
819
}
820
Operator::I32Load16S { memarg } => {
821
unwrap_or_return_unreachable_state!(
822
stack,
823
translate_load(memarg, ir::Opcode::Sload16, I32, builder, stack, environ)?
824
);
825
}
826
Operator::I64Load8U { memarg } => {
827
unwrap_or_return_unreachable_state!(
828
stack,
829
translate_load(memarg, ir::Opcode::Uload8, I64, builder, stack, environ)?
830
);
831
}
832
Operator::I64Load16U { memarg } => {
833
unwrap_or_return_unreachable_state!(
834
stack,
835
translate_load(memarg, ir::Opcode::Uload16, I64, builder, stack, environ)?
836
);
837
}
838
Operator::I64Load8S { memarg } => {
839
unwrap_or_return_unreachable_state!(
840
stack,
841
translate_load(memarg, ir::Opcode::Sload8, I64, builder, stack, environ)?
842
);
843
}
844
Operator::I64Load16S { memarg } => {
845
unwrap_or_return_unreachable_state!(
846
stack,
847
translate_load(memarg, ir::Opcode::Sload16, I64, builder, stack, environ)?
848
);
849
}
850
Operator::I64Load32S { memarg } => {
851
unwrap_or_return_unreachable_state!(
852
stack,
853
translate_load(memarg, ir::Opcode::Sload32, I64, builder, stack, environ)?
854
);
855
}
856
Operator::I64Load32U { memarg } => {
857
unwrap_or_return_unreachable_state!(
858
stack,
859
translate_load(memarg, ir::Opcode::Uload32, I64, builder, stack, environ)?
860
);
861
}
862
Operator::I32Load { memarg } => {
863
unwrap_or_return_unreachable_state!(
864
stack,
865
translate_load(memarg, ir::Opcode::Load, I32, builder, stack, environ)?
866
);
867
}
868
Operator::F32Load { memarg } => {
869
unwrap_or_return_unreachable_state!(
870
stack,
871
translate_load(memarg, ir::Opcode::Load, F32, builder, stack, environ)?
872
);
873
}
874
Operator::I64Load { memarg } => {
875
unwrap_or_return_unreachable_state!(
876
stack,
877
translate_load(memarg, ir::Opcode::Load, I64, builder, stack, environ)?
878
);
879
}
880
Operator::F64Load { memarg } => {
881
unwrap_or_return_unreachable_state!(
882
stack,
883
translate_load(memarg, ir::Opcode::Load, F64, builder, stack, environ)?
884
);
885
}
886
Operator::V128Load { memarg } => {
887
unwrap_or_return_unreachable_state!(
888
stack,
889
translate_load(memarg, ir::Opcode::Load, I8X16, builder, stack, environ)?
890
);
891
}
892
Operator::V128Load8x8S { memarg } => {
893
//TODO(#6829): add before_load() and before_store() hooks for SIMD loads and stores.
894
let (flags, _, base) = unwrap_or_return_unreachable_state!(
895
stack,
896
prepare_addr(memarg, 8, builder, stack, environ)?
897
);
898
let loaded = builder.ins().sload8x8(flags, base, 0);
899
stack.push1(loaded);
900
}
901
Operator::V128Load8x8U { memarg } => {
902
let (flags, _, base) = unwrap_or_return_unreachable_state!(
903
stack,
904
prepare_addr(memarg, 8, builder, stack, environ)?
905
);
906
let loaded = builder.ins().uload8x8(flags, base, 0);
907
stack.push1(loaded);
908
}
909
Operator::V128Load16x4S { memarg } => {
910
let (flags, _, base) = unwrap_or_return_unreachable_state!(
911
stack,
912
prepare_addr(memarg, 8, builder, stack, environ)?
913
);
914
let loaded = builder.ins().sload16x4(flags, base, 0);
915
stack.push1(loaded);
916
}
917
Operator::V128Load16x4U { memarg } => {
918
let (flags, _, base) = unwrap_or_return_unreachable_state!(
919
stack,
920
prepare_addr(memarg, 8, builder, stack, environ)?
921
);
922
let loaded = builder.ins().uload16x4(flags, base, 0);
923
stack.push1(loaded);
924
}
925
Operator::V128Load32x2S { memarg } => {
926
let (flags, _, base) = unwrap_or_return_unreachable_state!(
927
stack,
928
prepare_addr(memarg, 8, builder, stack, environ)?
929
);
930
let loaded = builder.ins().sload32x2(flags, base, 0);
931
stack.push1(loaded);
932
}
933
Operator::V128Load32x2U { memarg } => {
934
let (flags, _, base) = unwrap_or_return_unreachable_state!(
935
stack,
936
prepare_addr(memarg, 8, builder, stack, environ)?
937
);
938
let loaded = builder.ins().uload32x2(flags, base, 0);
939
stack.push1(loaded);
940
}
941
/****************************** Store instructions ***********************************
942
* Wasm specifies an integer alignment flag but we drop it in Cranelift.
943
* The memory base address is provided by the environment.
944
************************************************************************************/
945
Operator::I32Store { memarg }
946
| Operator::I64Store { memarg }
947
| Operator::F32Store { memarg }
948
| Operator::F64Store { memarg } => {
949
translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;
950
}
951
Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => {
952
translate_store(memarg, ir::Opcode::Istore8, builder, stack, environ)?;
953
}
954
Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => {
955
translate_store(memarg, ir::Opcode::Istore16, builder, stack, environ)?;
956
}
957
Operator::I64Store32 { memarg } => {
958
translate_store(memarg, ir::Opcode::Istore32, builder, stack, environ)?;
959
}
960
Operator::V128Store { memarg } => {
961
translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;
962
}
963
/****************************** Nullary Operators ************************************/
964
Operator::I32Const { value } => {
965
stack.push1(builder.ins().iconst(I32, i64::from(value.cast_unsigned())));
966
}
967
Operator::I64Const { value } => stack.push1(builder.ins().iconst(I64, *value)),
968
Operator::F32Const { value } => {
969
stack.push1(builder.ins().f32const(f32_translation(*value)));
970
}
971
Operator::F64Const { value } => {
972
stack.push1(builder.ins().f64const(f64_translation(*value)));
973
}
974
/******************************* Unary Operators *************************************/
975
Operator::I32Clz | Operator::I64Clz => {
976
let arg = stack.pop1();
977
stack.push1(builder.ins().clz(arg));
978
}
979
Operator::I32Ctz | Operator::I64Ctz => {
980
let arg = stack.pop1();
981
stack.push1(builder.ins().ctz(arg));
982
}
983
Operator::I32Popcnt | Operator::I64Popcnt => {
984
let arg = stack.pop1();
985
stack.push1(builder.ins().popcnt(arg));
986
}
987
Operator::I64ExtendI32S => {
988
let val = stack.pop1();
989
stack.push1(builder.ins().sextend(I64, val));
990
}
991
Operator::I64ExtendI32U => {
992
let val = stack.pop1();
993
stack.push1(builder.ins().uextend(I64, val));
994
}
995
Operator::I32WrapI64 => {
996
let val = stack.pop1();
997
stack.push1(builder.ins().ireduce(I32, val));
998
}
999
Operator::F32Sqrt | Operator::F64Sqrt => {
1000
let arg = stack.pop1();
1001
stack.push1(builder.ins().sqrt(arg));
1002
}
1003
Operator::F32Ceil => {
1004
let arg = stack.pop1();
1005
stack.push1(environ.ceil_f32(builder, arg));
1006
}
1007
Operator::F64Ceil => {
1008
let arg = stack.pop1();
1009
stack.push1(environ.ceil_f64(builder, arg));
1010
}
1011
Operator::F32Floor => {
1012
let arg = stack.pop1();
1013
stack.push1(environ.floor_f32(builder, arg));
1014
}
1015
Operator::F64Floor => {
1016
let arg = stack.pop1();
1017
stack.push1(environ.floor_f64(builder, arg));
1018
}
1019
Operator::F32Trunc => {
1020
let arg = stack.pop1();
1021
stack.push1(environ.trunc_f32(builder, arg));
1022
}
1023
Operator::F64Trunc => {
1024
let arg = stack.pop1();
1025
stack.push1(environ.trunc_f64(builder, arg));
1026
}
1027
Operator::F32Nearest => {
1028
let arg = stack.pop1();
1029
stack.push1(environ.nearest_f32(builder, arg));
1030
}
1031
Operator::F64Nearest => {
1032
let arg = stack.pop1();
1033
stack.push1(environ.nearest_f64(builder, arg));
1034
}
1035
Operator::F32Abs | Operator::F64Abs => {
1036
let val = stack.pop1();
1037
stack.push1(builder.ins().fabs(val));
1038
}
1039
Operator::F32Neg | Operator::F64Neg => {
1040
let arg = stack.pop1();
1041
stack.push1(builder.ins().fneg(arg));
1042
}
1043
Operator::F64ConvertI64U | Operator::F64ConvertI32U => {
1044
let val = stack.pop1();
1045
stack.push1(builder.ins().fcvt_from_uint(F64, val));
1046
}
1047
Operator::F64ConvertI64S | Operator::F64ConvertI32S => {
1048
let val = stack.pop1();
1049
stack.push1(builder.ins().fcvt_from_sint(F64, val));
1050
}
1051
Operator::F32ConvertI64S | Operator::F32ConvertI32S => {
1052
let val = stack.pop1();
1053
stack.push1(builder.ins().fcvt_from_sint(F32, val));
1054
}
1055
Operator::F32ConvertI64U | Operator::F32ConvertI32U => {
1056
let val = stack.pop1();
1057
stack.push1(builder.ins().fcvt_from_uint(F32, val));
1058
}
1059
Operator::F64PromoteF32 => {
1060
let val = stack.pop1();
1061
stack.push1(builder.ins().fpromote(F64, val));
1062
}
1063
Operator::F32DemoteF64 => {
1064
let val = stack.pop1();
1065
stack.push1(builder.ins().fdemote(F32, val));
1066
}
1067
Operator::I64TruncF64S | Operator::I64TruncF32S => {
1068
let val = stack.pop1();
1069
stack.push1(environ.translate_fcvt_to_sint(builder, I64, val));
1070
}
1071
Operator::I32TruncF64S | Operator::I32TruncF32S => {
1072
let val = stack.pop1();
1073
stack.push1(environ.translate_fcvt_to_sint(builder, I32, val));
1074
}
1075
Operator::I64TruncF64U | Operator::I64TruncF32U => {
1076
let val = stack.pop1();
1077
stack.push1(environ.translate_fcvt_to_uint(builder, I64, val));
1078
}
1079
Operator::I32TruncF64U | Operator::I32TruncF32U => {
1080
let val = stack.pop1();
1081
stack.push1(environ.translate_fcvt_to_uint(builder, I32, val));
1082
}
1083
Operator::I64TruncSatF64S | Operator::I64TruncSatF32S => {
1084
let val = stack.pop1();
1085
stack.push1(builder.ins().fcvt_to_sint_sat(I64, val));
1086
}
1087
Operator::I32TruncSatF64S | Operator::I32TruncSatF32S => {
1088
let val = stack.pop1();
1089
stack.push1(builder.ins().fcvt_to_sint_sat(I32, val));
1090
}
1091
Operator::I64TruncSatF64U | Operator::I64TruncSatF32U => {
1092
let val = stack.pop1();
1093
stack.push1(builder.ins().fcvt_to_uint_sat(I64, val));
1094
}
1095
Operator::I32TruncSatF64U | Operator::I32TruncSatF32U => {
1096
let val = stack.pop1();
1097
stack.push1(builder.ins().fcvt_to_uint_sat(I32, val));
1098
}
1099
Operator::F32ReinterpretI32 => {
1100
let val = stack.pop1();
1101
stack.push1(builder.ins().bitcast(F32, MemFlags::new(), val));
1102
}
1103
Operator::F64ReinterpretI64 => {
1104
let val = stack.pop1();
1105
stack.push1(builder.ins().bitcast(F64, MemFlags::new(), val));
1106
}
1107
Operator::I32ReinterpretF32 => {
1108
let val = stack.pop1();
1109
stack.push1(builder.ins().bitcast(I32, MemFlags::new(), val));
1110
}
1111
Operator::I64ReinterpretF64 => {
1112
let val = stack.pop1();
1113
stack.push1(builder.ins().bitcast(I64, MemFlags::new(), val));
1114
}
1115
Operator::I32Extend8S => {
1116
let val = stack.pop1();
1117
stack.push1(builder.ins().ireduce(I8, val));
1118
let val = stack.pop1();
1119
stack.push1(builder.ins().sextend(I32, val));
1120
}
1121
Operator::I32Extend16S => {
1122
let val = stack.pop1();
1123
stack.push1(builder.ins().ireduce(I16, val));
1124
let val = stack.pop1();
1125
stack.push1(builder.ins().sextend(I32, val));
1126
}
1127
Operator::I64Extend8S => {
1128
let val = stack.pop1();
1129
stack.push1(builder.ins().ireduce(I8, val));
1130
let val = stack.pop1();
1131
stack.push1(builder.ins().sextend(I64, val));
1132
}
1133
Operator::I64Extend16S => {
1134
let val = stack.pop1();
1135
stack.push1(builder.ins().ireduce(I16, val));
1136
let val = stack.pop1();
1137
stack.push1(builder.ins().sextend(I64, val));
1138
}
1139
Operator::I64Extend32S => {
1140
let val = stack.pop1();
1141
stack.push1(builder.ins().ireduce(I32, val));
1142
let val = stack.pop1();
1143
stack.push1(builder.ins().sextend(I64, val));
1144
}
1145
/****************************** Binary Operators ************************************/
1146
Operator::I32Add | Operator::I64Add => {
1147
let (arg1, arg2) = stack.pop2();
1148
stack.push1(builder.ins().iadd(arg1, arg2));
1149
}
1150
Operator::I32And | Operator::I64And => {
1151
let (arg1, arg2) = stack.pop2();
1152
stack.push1(builder.ins().band(arg1, arg2));
1153
}
1154
Operator::I32Or | Operator::I64Or => {
1155
let (arg1, arg2) = stack.pop2();
1156
stack.push1(builder.ins().bor(arg1, arg2));
1157
}
1158
Operator::I32Xor | Operator::I64Xor => {
1159
let (arg1, arg2) = stack.pop2();
1160
stack.push1(builder.ins().bxor(arg1, arg2));
1161
}
1162
Operator::I32Shl | Operator::I64Shl => {
1163
let (arg1, arg2) = stack.pop2();
1164
stack.push1(builder.ins().ishl(arg1, arg2));
1165
}
1166
Operator::I32ShrS | Operator::I64ShrS => {
1167
let (arg1, arg2) = stack.pop2();
1168
stack.push1(builder.ins().sshr(arg1, arg2));
1169
}
1170
Operator::I32ShrU | Operator::I64ShrU => {
1171
let (arg1, arg2) = stack.pop2();
1172
stack.push1(builder.ins().ushr(arg1, arg2));
1173
}
1174
Operator::I32Rotl | Operator::I64Rotl => {
1175
let (arg1, arg2) = stack.pop2();
1176
stack.push1(builder.ins().rotl(arg1, arg2));
1177
}
1178
Operator::I32Rotr | Operator::I64Rotr => {
1179
let (arg1, arg2) = stack.pop2();
1180
stack.push1(builder.ins().rotr(arg1, arg2));
1181
}
1182
Operator::F32Add | Operator::F64Add => {
1183
let (arg1, arg2) = stack.pop2();
1184
stack.push1(builder.ins().fadd(arg1, arg2));
1185
}
1186
Operator::I32Sub | Operator::I64Sub => {
1187
let (arg1, arg2) = stack.pop2();
1188
stack.push1(builder.ins().isub(arg1, arg2));
1189
}
1190
Operator::F32Sub | Operator::F64Sub => {
1191
let (arg1, arg2) = stack.pop2();
1192
stack.push1(builder.ins().fsub(arg1, arg2));
1193
}
1194
Operator::I32Mul | Operator::I64Mul => {
1195
let (arg1, arg2) = stack.pop2();
1196
stack.push1(builder.ins().imul(arg1, arg2));
1197
}
1198
Operator::F32Mul | Operator::F64Mul => {
1199
let (arg1, arg2) = stack.pop2();
1200
stack.push1(builder.ins().fmul(arg1, arg2));
1201
}
1202
Operator::F32Div | Operator::F64Div => {
1203
let (arg1, arg2) = stack.pop2();
1204
stack.push1(builder.ins().fdiv(arg1, arg2));
1205
}
1206
Operator::I32DivS | Operator::I64DivS => {
1207
let (arg1, arg2) = stack.pop2();
1208
stack.push1(environ.translate_sdiv(builder, arg1, arg2));
1209
}
1210
Operator::I32DivU | Operator::I64DivU => {
1211
let (arg1, arg2) = stack.pop2();
1212
stack.push1(environ.translate_udiv(builder, arg1, arg2));
1213
}
1214
Operator::I32RemS | Operator::I64RemS => {
1215
let (arg1, arg2) = stack.pop2();
1216
stack.push1(environ.translate_srem(builder, arg1, arg2));
1217
}
1218
Operator::I32RemU | Operator::I64RemU => {
1219
let (arg1, arg2) = stack.pop2();
1220
stack.push1(environ.translate_urem(builder, arg1, arg2));
1221
}
1222
Operator::F32Min | Operator::F64Min => {
1223
let (arg1, arg2) = stack.pop2();
1224
stack.push1(builder.ins().fmin(arg1, arg2));
1225
}
1226
Operator::F32Max | Operator::F64Max => {
1227
let (arg1, arg2) = stack.pop2();
1228
stack.push1(builder.ins().fmax(arg1, arg2));
1229
}
1230
Operator::F32Copysign | Operator::F64Copysign => {
1231
let (arg1, arg2) = stack.pop2();
1232
stack.push1(builder.ins().fcopysign(arg1, arg2));
1233
}
1234
/**************************** Comparison Operators **********************************/
1235
Operator::I32LtS | Operator::I64LtS => {
1236
translate_icmp(IntCC::SignedLessThan, builder, stack)
1237
}
1238
Operator::I32LtU | Operator::I64LtU => {
1239
translate_icmp(IntCC::UnsignedLessThan, builder, stack)
1240
}
1241
Operator::I32LeS | Operator::I64LeS => {
1242
translate_icmp(IntCC::SignedLessThanOrEqual, builder, stack)
1243
}
1244
Operator::I32LeU | Operator::I64LeU => {
1245
translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, stack)
1246
}
1247
Operator::I32GtS | Operator::I64GtS => {
1248
translate_icmp(IntCC::SignedGreaterThan, builder, stack)
1249
}
1250
Operator::I32GtU | Operator::I64GtU => {
1251
translate_icmp(IntCC::UnsignedGreaterThan, builder, stack)
1252
}
1253
Operator::I32GeS | Operator::I64GeS => {
1254
translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, stack)
1255
}
1256
Operator::I32GeU | Operator::I64GeU => {
1257
translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, stack)
1258
}
1259
Operator::I32Eqz | Operator::I64Eqz => {
1260
let arg = stack.pop1();
1261
let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0);
1262
stack.push1(builder.ins().uextend(I32, val));
1263
}
1264
Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, stack),
1265
Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, stack),
1266
Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, stack),
1267
Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, stack),
1268
Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, stack),
1269
Operator::F32Ge | Operator::F64Ge => {
1270
translate_fcmp(FloatCC::GreaterThanOrEqual, builder, stack)
1271
}
1272
Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, stack),
1273
Operator::F32Le | Operator::F64Le => {
1274
translate_fcmp(FloatCC::LessThanOrEqual, builder, stack)
1275
}
1276
Operator::RefNull { hty } => {
1277
let hty = environ.convert_heap_type(*hty)?;
1278
stack.push1(environ.translate_ref_null(builder.cursor(), hty)?)
1279
}
1280
Operator::RefIsNull => {
1281
let value = stack.pop1();
1282
let [WasmValType::Ref(ty)] = operand_types else {
1283
unreachable!("validation")
1284
};
1285
stack.push1(environ.translate_ref_is_null(builder.cursor(), value, *ty)?);
1286
}
1287
Operator::RefFunc { function_index } => {
1288
let index = FuncIndex::from_u32(*function_index);
1289
stack.push1(environ.translate_ref_func(builder.cursor(), index)?);
1290
}
1291
Operator::MemoryAtomicWait32 { memarg } | Operator::MemoryAtomicWait64 { memarg } => {
1292
// The WebAssembly MVP only supports one linear memory and
1293
// wasmparser will ensure that the memory indices specified are
1294
// zero.
1295
let implied_ty = match op {
1296
Operator::MemoryAtomicWait64 { .. } => I64,
1297
Operator::MemoryAtomicWait32 { .. } => I32,
1298
_ => unreachable!(),
1299
};
1300
let memory_index = MemoryIndex::from_u32(memarg.memory);
1301
let heap = environ.get_or_create_heap(builder.func, memory_index);
1302
let timeout = stack.pop1(); // 64 (fixed)
1303
let expected = stack.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`)
1304
assert!(builder.func.dfg.value_type(expected) == implied_ty);
1305
let addr = stack.pop1();
1306
let effective_addr = if memarg.offset == 0 {
1307
addr
1308
} else {
1309
let index_type = environ.heaps()[heap].index_type();
1310
let offset = builder.ins().iconst(index_type, memarg.offset as i64);
1311
environ.uadd_overflow_trap(builder, addr, offset, ir::TrapCode::HEAP_OUT_OF_BOUNDS)
1312
};
1313
// `fn translate_atomic_wait` can inspect the type of `expected` to figure out what
1314
// code it needs to generate, if it wants.
1315
let res = environ.translate_atomic_wait(
1316
builder,
1317
memory_index,
1318
heap,
1319
effective_addr,
1320
expected,
1321
timeout,
1322
)?;
1323
stack.push1(res);
1324
}
1325
Operator::MemoryAtomicNotify { memarg } => {
1326
let memory_index = MemoryIndex::from_u32(memarg.memory);
1327
let heap = environ.get_or_create_heap(builder.func, memory_index);
1328
let count = stack.pop1(); // 32 (fixed)
1329
let addr = stack.pop1();
1330
let effective_addr = if memarg.offset == 0 {
1331
addr
1332
} else {
1333
let index_type = environ.heaps()[heap].index_type();
1334
let offset = builder.ins().iconst(index_type, memarg.offset as i64);
1335
environ.uadd_overflow_trap(builder, addr, offset, ir::TrapCode::HEAP_OUT_OF_BOUNDS)
1336
};
1337
let res = environ.translate_atomic_notify(
1338
builder,
1339
memory_index,
1340
heap,
1341
effective_addr,
1342
count,
1343
)?;
1344
stack.push1(res);
1345
}
1346
Operator::I32AtomicLoad { memarg } => {
1347
translate_atomic_load(I32, I32, memarg, builder, stack, environ)?
1348
}
1349
Operator::I64AtomicLoad { memarg } => {
1350
translate_atomic_load(I64, I64, memarg, builder, stack, environ)?
1351
}
1352
Operator::I32AtomicLoad8U { memarg } => {
1353
translate_atomic_load(I32, I8, memarg, builder, stack, environ)?
1354
}
1355
Operator::I32AtomicLoad16U { memarg } => {
1356
translate_atomic_load(I32, I16, memarg, builder, stack, environ)?
1357
}
1358
Operator::I64AtomicLoad8U { memarg } => {
1359
translate_atomic_load(I64, I8, memarg, builder, stack, environ)?
1360
}
1361
Operator::I64AtomicLoad16U { memarg } => {
1362
translate_atomic_load(I64, I16, memarg, builder, stack, environ)?
1363
}
1364
Operator::I64AtomicLoad32U { memarg } => {
1365
translate_atomic_load(I64, I32, memarg, builder, stack, environ)?
1366
}
1367
1368
Operator::I32AtomicStore { memarg } => {
1369
translate_atomic_store(I32, memarg, builder, stack, environ)?
1370
}
1371
Operator::I64AtomicStore { memarg } => {
1372
translate_atomic_store(I64, memarg, builder, stack, environ)?
1373
}
1374
Operator::I32AtomicStore8 { memarg } => {
1375
translate_atomic_store(I8, memarg, builder, stack, environ)?
1376
}
1377
Operator::I32AtomicStore16 { memarg } => {
1378
translate_atomic_store(I16, memarg, builder, stack, environ)?
1379
}
1380
Operator::I64AtomicStore8 { memarg } => {
1381
translate_atomic_store(I8, memarg, builder, stack, environ)?
1382
}
1383
Operator::I64AtomicStore16 { memarg } => {
1384
translate_atomic_store(I16, memarg, builder, stack, environ)?
1385
}
1386
Operator::I64AtomicStore32 { memarg } => {
1387
translate_atomic_store(I32, memarg, builder, stack, environ)?
1388
}
1389
1390
Operator::I32AtomicRmwAdd { memarg } => {
1391
translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1392
}
1393
Operator::I64AtomicRmwAdd { memarg } => {
1394
translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1395
}
1396
Operator::I32AtomicRmw8AddU { memarg } => {
1397
translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1398
}
1399
Operator::I32AtomicRmw16AddU { memarg } => {
1400
translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1401
}
1402
Operator::I64AtomicRmw8AddU { memarg } => {
1403
translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1404
}
1405
Operator::I64AtomicRmw16AddU { memarg } => {
1406
translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1407
}
1408
Operator::I64AtomicRmw32AddU { memarg } => {
1409
translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, stack, environ)?
1410
}
1411
1412
Operator::I32AtomicRmwSub { memarg } => {
1413
translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1414
}
1415
Operator::I64AtomicRmwSub { memarg } => {
1416
translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1417
}
1418
Operator::I32AtomicRmw8SubU { memarg } => {
1419
translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1420
}
1421
Operator::I32AtomicRmw16SubU { memarg } => {
1422
translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1423
}
1424
Operator::I64AtomicRmw8SubU { memarg } => {
1425
translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1426
}
1427
Operator::I64AtomicRmw16SubU { memarg } => {
1428
translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1429
}
1430
Operator::I64AtomicRmw32SubU { memarg } => {
1431
translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, stack, environ)?
1432
}
1433
1434
Operator::I32AtomicRmwAnd { memarg } => {
1435
translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, stack, environ)?
1436
}
1437
Operator::I64AtomicRmwAnd { memarg } => {
1438
translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, stack, environ)?
1439
}
1440
Operator::I32AtomicRmw8AndU { memarg } => {
1441
translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, stack, environ)?
1442
}
1443
Operator::I32AtomicRmw16AndU { memarg } => {
1444
translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, stack, environ)?
1445
}
1446
Operator::I64AtomicRmw8AndU { memarg } => {
1447
translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, stack, environ)?
1448
}
1449
Operator::I64AtomicRmw16AndU { memarg } => {
1450
translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, stack, environ)?
1451
}
1452
Operator::I64AtomicRmw32AndU { memarg } => {
1453
translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, stack, environ)?
1454
}
1455
1456
Operator::I32AtomicRmwOr { memarg } => {
1457
translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1458
}
1459
Operator::I64AtomicRmwOr { memarg } => {
1460
translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1461
}
1462
Operator::I32AtomicRmw8OrU { memarg } => {
1463
translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1464
}
1465
Operator::I32AtomicRmw16OrU { memarg } => {
1466
translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1467
}
1468
Operator::I64AtomicRmw8OrU { memarg } => {
1469
translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1470
}
1471
Operator::I64AtomicRmw16OrU { memarg } => {
1472
translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1473
}
1474
Operator::I64AtomicRmw32OrU { memarg } => {
1475
translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, stack, environ)?
1476
}
1477
1478
Operator::I32AtomicRmwXor { memarg } => {
1479
translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1480
}
1481
Operator::I64AtomicRmwXor { memarg } => {
1482
translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1483
}
1484
Operator::I32AtomicRmw8XorU { memarg } => {
1485
translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1486
}
1487
Operator::I32AtomicRmw16XorU { memarg } => {
1488
translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1489
}
1490
Operator::I64AtomicRmw8XorU { memarg } => {
1491
translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1492
}
1493
Operator::I64AtomicRmw16XorU { memarg } => {
1494
translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1495
}
1496
Operator::I64AtomicRmw32XorU { memarg } => {
1497
translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, stack, environ)?
1498
}
1499
1500
Operator::I32AtomicRmwXchg { memarg } => {
1501
translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1502
}
1503
Operator::I64AtomicRmwXchg { memarg } => {
1504
translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1505
}
1506
Operator::I32AtomicRmw8XchgU { memarg } => {
1507
translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1508
}
1509
Operator::I32AtomicRmw16XchgU { memarg } => {
1510
translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1511
}
1512
Operator::I64AtomicRmw8XchgU { memarg } => {
1513
translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1514
}
1515
Operator::I64AtomicRmw16XchgU { memarg } => {
1516
translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1517
}
1518
Operator::I64AtomicRmw32XchgU { memarg } => {
1519
translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, stack, environ)?
1520
}
1521
1522
Operator::I32AtomicRmwCmpxchg { memarg } => {
1523
translate_atomic_cas(I32, I32, memarg, builder, stack, environ)?
1524
}
1525
Operator::I64AtomicRmwCmpxchg { memarg } => {
1526
translate_atomic_cas(I64, I64, memarg, builder, stack, environ)?
1527
}
1528
Operator::I32AtomicRmw8CmpxchgU { memarg } => {
1529
translate_atomic_cas(I32, I8, memarg, builder, stack, environ)?
1530
}
1531
Operator::I32AtomicRmw16CmpxchgU { memarg } => {
1532
translate_atomic_cas(I32, I16, memarg, builder, stack, environ)?
1533
}
1534
Operator::I64AtomicRmw8CmpxchgU { memarg } => {
1535
translate_atomic_cas(I64, I8, memarg, builder, stack, environ)?
1536
}
1537
Operator::I64AtomicRmw16CmpxchgU { memarg } => {
1538
translate_atomic_cas(I64, I16, memarg, builder, stack, environ)?
1539
}
1540
Operator::I64AtomicRmw32CmpxchgU { memarg } => {
1541
translate_atomic_cas(I64, I32, memarg, builder, stack, environ)?
1542
}
1543
1544
Operator::AtomicFence { .. } => {
1545
builder.ins().fence();
1546
}
1547
Operator::MemoryCopy { src_mem, dst_mem } => {
1548
let src_index = MemoryIndex::from_u32(*src_mem);
1549
let _src_heap = environ.get_or_create_heap(builder.func, src_index);
1550
1551
let dst_index = MemoryIndex::from_u32(*dst_mem);
1552
let _dst_heap = environ.get_or_create_heap(builder.func, dst_index);
1553
1554
let len = stack.pop1();
1555
let src_pos = stack.pop1();
1556
let dst_pos = stack.pop1();
1557
environ.translate_memory_copy(builder, src_index, dst_index, dst_pos, src_pos, len)?;
1558
}
1559
Operator::MemoryFill { mem } => {
1560
let mem = MemoryIndex::from_u32(*mem);
1561
let _heap = environ.get_or_create_heap(builder.func, mem);
1562
let len = stack.pop1();
1563
let val = stack.pop1();
1564
let dest = stack.pop1();
1565
environ.translate_memory_fill(builder, mem, dest, val, len)?;
1566
}
1567
Operator::MemoryInit { data_index, mem } => {
1568
let mem = MemoryIndex::from_u32(*mem);
1569
let _heap = environ.get_or_create_heap(builder.func, mem);
1570
let len = stack.pop1();
1571
let src = stack.pop1();
1572
let dest = stack.pop1();
1573
environ.translate_memory_init(builder, mem, *data_index, dest, src, len)?;
1574
}
1575
Operator::DataDrop { data_index } => {
1576
environ.translate_data_drop(builder.cursor(), *data_index)?;
1577
}
1578
Operator::TableSize { table: index } => {
1579
stack.push1(
1580
environ.translate_table_size(builder.cursor(), TableIndex::from_u32(*index))?,
1581
);
1582
}
1583
Operator::TableGrow { table: index } => {
1584
let table_index = TableIndex::from_u32(*index);
1585
let delta = stack.pop1();
1586
let init_value = stack.pop1();
1587
stack.push1(environ.translate_table_grow(builder, table_index, delta, init_value)?);
1588
}
1589
Operator::TableGet { table: index } => {
1590
let table_index = TableIndex::from_u32(*index);
1591
let index = stack.pop1();
1592
stack.push1(environ.translate_table_get(builder, table_index, index)?);
1593
}
1594
Operator::TableSet { table: index } => {
1595
let table_index = TableIndex::from_u32(*index);
1596
let value = stack.pop1();
1597
let index = stack.pop1();
1598
environ.translate_table_set(builder, table_index, value, index)?;
1599
}
1600
Operator::TableCopy {
1601
dst_table: dst_table_index,
1602
src_table: src_table_index,
1603
} => {
1604
let len = stack.pop1();
1605
let src = stack.pop1();
1606
let dest = stack.pop1();
1607
environ.translate_table_copy(
1608
builder,
1609
TableIndex::from_u32(*dst_table_index),
1610
TableIndex::from_u32(*src_table_index),
1611
dest,
1612
src,
1613
len,
1614
)?;
1615
}
1616
Operator::TableFill { table } => {
1617
let table_index = TableIndex::from_u32(*table);
1618
let len = stack.pop1();
1619
let val = stack.pop1();
1620
let dest = stack.pop1();
1621
environ.translate_table_fill(builder, table_index, dest, val, len)?;
1622
}
1623
Operator::TableInit {
1624
elem_index,
1625
table: table_index,
1626
} => {
1627
let len = stack.pop1();
1628
let src = stack.pop1();
1629
let dest = stack.pop1();
1630
environ.translate_table_init(
1631
builder,
1632
*elem_index,
1633
TableIndex::from_u32(*table_index),
1634
dest,
1635
src,
1636
len,
1637
)?;
1638
}
1639
Operator::ElemDrop { elem_index } => {
1640
environ.translate_elem_drop(builder.cursor(), *elem_index)?;
1641
}
1642
Operator::V128Const { value } => {
1643
let data = value.bytes().to_vec().into();
1644
let handle = builder.func.dfg.constants.insert(data);
1645
let value = builder.ins().vconst(I8X16, handle);
1646
// the v128.const is typed in CLIF as a I8x16 but bitcast to a different type
1647
// before use
1648
stack.push1(value)
1649
}
1650
Operator::I8x16Splat | Operator::I16x8Splat => {
1651
let reduced = builder.ins().ireduce(type_of(op).lane_type(), stack.pop1());
1652
let splatted = builder.ins().splat(type_of(op), reduced);
1653
stack.push1(splatted)
1654
}
1655
Operator::I32x4Splat
1656
| Operator::I64x2Splat
1657
| Operator::F32x4Splat
1658
| Operator::F64x2Splat => {
1659
let splatted = builder.ins().splat(type_of(op), stack.pop1());
1660
stack.push1(splatted)
1661
}
1662
Operator::V128Load8Splat { memarg }
1663
| Operator::V128Load16Splat { memarg }
1664
| Operator::V128Load32Splat { memarg }
1665
| Operator::V128Load64Splat { memarg } => {
1666
unwrap_or_return_unreachable_state!(
1667
stack,
1668
translate_load(
1669
memarg,
1670
ir::Opcode::Load,
1671
type_of(op).lane_type(),
1672
builder,
1673
stack,
1674
environ,
1675
)?
1676
);
1677
let splatted = builder.ins().splat(type_of(op), stack.pop1());
1678
stack.push1(splatted)
1679
}
1680
Operator::V128Load32Zero { memarg } | Operator::V128Load64Zero { memarg } => {
1681
unwrap_or_return_unreachable_state!(
1682
stack,
1683
translate_load(
1684
memarg,
1685
ir::Opcode::Load,
1686
type_of(op).lane_type(),
1687
builder,
1688
stack,
1689
environ,
1690
)?
1691
);
1692
let as_vector = builder.ins().scalar_to_vector(type_of(op), stack.pop1());
1693
stack.push1(as_vector)
1694
}
1695
Operator::V128Load8Lane { memarg, lane }
1696
| Operator::V128Load16Lane { memarg, lane }
1697
| Operator::V128Load32Lane { memarg, lane }
1698
| Operator::V128Load64Lane { memarg, lane } => {
1699
let vector = pop1_with_bitcast(stack, type_of(op), builder);
1700
unwrap_or_return_unreachable_state!(
1701
stack,
1702
translate_load(
1703
memarg,
1704
ir::Opcode::Load,
1705
type_of(op).lane_type(),
1706
builder,
1707
stack,
1708
environ,
1709
)?
1710
);
1711
let replacement = stack.pop1();
1712
stack.push1(builder.ins().insertlane(vector, replacement, *lane))
1713
}
1714
Operator::V128Store8Lane { memarg, lane }
1715
| Operator::V128Store16Lane { memarg, lane }
1716
| Operator::V128Store32Lane { memarg, lane }
1717
| Operator::V128Store64Lane { memarg, lane } => {
1718
let vector = pop1_with_bitcast(stack, type_of(op), builder);
1719
stack.push1(builder.ins().extractlane(vector, *lane));
1720
translate_store(memarg, ir::Opcode::Store, builder, stack, environ)?;
1721
}
1722
Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {
1723
let vector = pop1_with_bitcast(stack, type_of(op), builder);
1724
let extracted = builder.ins().extractlane(vector, *lane);
1725
stack.push1(builder.ins().sextend(I32, extracted))
1726
}
1727
Operator::I8x16ExtractLaneU { lane } | Operator::I16x8ExtractLaneU { lane } => {
1728
let vector = pop1_with_bitcast(stack, type_of(op), builder);
1729
let extracted = builder.ins().extractlane(vector, *lane);
1730
stack.push1(builder.ins().uextend(I32, extracted));
1731
// On x86, PEXTRB zeroes the upper bits of the destination register of extractlane so
1732
// uextend could be elided; for now, uextend is needed for Cranelift's type checks to
1733
// work.
1734
}
1735
Operator::I32x4ExtractLane { lane }
1736
| Operator::I64x2ExtractLane { lane }
1737
| Operator::F32x4ExtractLane { lane }
1738
| Operator::F64x2ExtractLane { lane } => {
1739
let vector = pop1_with_bitcast(stack, type_of(op), builder);
1740
stack.push1(builder.ins().extractlane(vector, *lane))
1741
}
1742
Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => {
1743
let (vector, replacement) = stack.pop2();
1744
let ty = type_of(op);
1745
let reduced = builder.ins().ireduce(ty.lane_type(), replacement);
1746
let vector = optionally_bitcast_vector(vector, ty, builder);
1747
stack.push1(builder.ins().insertlane(vector, reduced, *lane))
1748
}
1749
Operator::I32x4ReplaceLane { lane }
1750
| Operator::I64x2ReplaceLane { lane }
1751
| Operator::F32x4ReplaceLane { lane }
1752
| Operator::F64x2ReplaceLane { lane } => {
1753
let (vector, replacement) = stack.pop2();
1754
let vector = optionally_bitcast_vector(vector, type_of(op), builder);
1755
stack.push1(builder.ins().insertlane(vector, replacement, *lane))
1756
}
1757
Operator::I8x16Shuffle { lanes, .. } => {
1758
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
1759
stack.push1(environ.i8x16_shuffle(builder, a, b, lanes));
1760
// At this point the original types of a and b are lost; users of this value (i.e. this
1761
// WASM-to-CLIF translator) may need to bitcast for type-correctness. This is due
1762
// to WASM using the less specific v128 type for certain operations and more specific
1763
// types (e.g. i8x16) for others.
1764
}
1765
Operator::I8x16Swizzle => {
1766
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
1767
stack.push1(environ.swizzle(builder, a, b));
1768
}
1769
Operator::I8x16Add | Operator::I16x8Add | Operator::I32x4Add | Operator::I64x2Add => {
1770
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1771
stack.push1(builder.ins().iadd(a, b))
1772
}
1773
Operator::I8x16AddSatS | Operator::I16x8AddSatS => {
1774
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1775
stack.push1(builder.ins().sadd_sat(a, b))
1776
}
1777
Operator::I8x16AddSatU | Operator::I16x8AddSatU => {
1778
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1779
stack.push1(builder.ins().uadd_sat(a, b))
1780
}
1781
Operator::I8x16Sub | Operator::I16x8Sub | Operator::I32x4Sub | Operator::I64x2Sub => {
1782
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1783
stack.push1(builder.ins().isub(a, b))
1784
}
1785
Operator::I8x16SubSatS | Operator::I16x8SubSatS => {
1786
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1787
stack.push1(builder.ins().ssub_sat(a, b))
1788
}
1789
Operator::I8x16SubSatU | Operator::I16x8SubSatU => {
1790
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1791
stack.push1(builder.ins().usub_sat(a, b))
1792
}
1793
Operator::I8x16MinS | Operator::I16x8MinS | Operator::I32x4MinS => {
1794
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1795
stack.push1(builder.ins().smin(a, b))
1796
}
1797
Operator::I8x16MinU | Operator::I16x8MinU | Operator::I32x4MinU => {
1798
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1799
stack.push1(builder.ins().umin(a, b))
1800
}
1801
Operator::I8x16MaxS | Operator::I16x8MaxS | Operator::I32x4MaxS => {
1802
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1803
stack.push1(builder.ins().smax(a, b))
1804
}
1805
Operator::I8x16MaxU | Operator::I16x8MaxU | Operator::I32x4MaxU => {
1806
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1807
stack.push1(builder.ins().umax(a, b))
1808
}
1809
Operator::I8x16AvgrU | Operator::I16x8AvgrU => {
1810
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1811
stack.push1(builder.ins().avg_round(a, b))
1812
}
1813
Operator::I8x16Neg | Operator::I16x8Neg | Operator::I32x4Neg | Operator::I64x2Neg => {
1814
let a = pop1_with_bitcast(stack, type_of(op), builder);
1815
stack.push1(builder.ins().ineg(a))
1816
}
1817
Operator::I8x16Abs | Operator::I16x8Abs | Operator::I32x4Abs | Operator::I64x2Abs => {
1818
let a = pop1_with_bitcast(stack, type_of(op), builder);
1819
stack.push1(builder.ins().iabs(a))
1820
}
1821
Operator::I16x8Mul | Operator::I32x4Mul | Operator::I64x2Mul => {
1822
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1823
stack.push1(builder.ins().imul(a, b))
1824
}
1825
Operator::V128Or => {
1826
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1827
stack.push1(builder.ins().bor(a, b))
1828
}
1829
Operator::V128Xor => {
1830
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1831
stack.push1(builder.ins().bxor(a, b))
1832
}
1833
Operator::V128And => {
1834
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1835
stack.push1(builder.ins().band(a, b))
1836
}
1837
Operator::V128AndNot => {
1838
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1839
stack.push1(builder.ins().band_not(a, b))
1840
}
1841
Operator::V128Not => {
1842
let a = stack.pop1();
1843
stack.push1(builder.ins().bnot(a));
1844
}
1845
Operator::I8x16Shl | Operator::I16x8Shl | Operator::I32x4Shl | Operator::I64x2Shl => {
1846
let (a, b) = stack.pop2();
1847
let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
1848
// The spec expects to shift with `b mod lanewidth`; This is directly compatible
1849
// with cranelift's instruction.
1850
stack.push1(builder.ins().ishl(bitcast_a, b))
1851
}
1852
Operator::I8x16ShrU | Operator::I16x8ShrU | Operator::I32x4ShrU | Operator::I64x2ShrU => {
1853
let (a, b) = stack.pop2();
1854
let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
1855
// The spec expects to shift with `b mod lanewidth`; This is directly compatible
1856
// with cranelift's instruction.
1857
stack.push1(builder.ins().ushr(bitcast_a, b))
1858
}
1859
Operator::I8x16ShrS | Operator::I16x8ShrS | Operator::I32x4ShrS | Operator::I64x2ShrS => {
1860
let (a, b) = stack.pop2();
1861
let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder);
1862
// The spec expects to shift with `b mod lanewidth`; This is directly compatible
1863
// with cranelift's instruction.
1864
stack.push1(builder.ins().sshr(bitcast_a, b))
1865
}
1866
Operator::V128Bitselect => {
1867
let (a, b, c) = pop3_with_bitcast(stack, I8X16, builder);
1868
// The CLIF operand ordering is slightly different and the types of all three
1869
// operands must match (hence the bitcast).
1870
stack.push1(builder.ins().bitselect(c, a, b))
1871
}
1872
Operator::V128AnyTrue => {
1873
let a = pop1_with_bitcast(stack, type_of(op), builder);
1874
let bool_result = builder.ins().vany_true(a);
1875
stack.push1(builder.ins().uextend(I32, bool_result))
1876
}
1877
Operator::I8x16AllTrue
1878
| Operator::I16x8AllTrue
1879
| Operator::I32x4AllTrue
1880
| Operator::I64x2AllTrue => {
1881
let a = pop1_with_bitcast(stack, type_of(op), builder);
1882
let bool_result = builder.ins().vall_true(a);
1883
stack.push1(builder.ins().uextend(I32, bool_result))
1884
}
1885
Operator::I8x16Bitmask
1886
| Operator::I16x8Bitmask
1887
| Operator::I32x4Bitmask
1888
| Operator::I64x2Bitmask => {
1889
let a = pop1_with_bitcast(stack, type_of(op), builder);
1890
stack.push1(builder.ins().vhigh_bits(I32, a));
1891
}
1892
Operator::I8x16Eq | Operator::I16x8Eq | Operator::I32x4Eq | Operator::I64x2Eq => {
1893
translate_vector_icmp(IntCC::Equal, type_of(op), builder, stack)
1894
}
1895
Operator::I8x16Ne | Operator::I16x8Ne | Operator::I32x4Ne | Operator::I64x2Ne => {
1896
translate_vector_icmp(IntCC::NotEqual, type_of(op), builder, stack)
1897
}
1898
Operator::I8x16GtS | Operator::I16x8GtS | Operator::I32x4GtS | Operator::I64x2GtS => {
1899
translate_vector_icmp(IntCC::SignedGreaterThan, type_of(op), builder, stack)
1900
}
1901
Operator::I8x16LtS | Operator::I16x8LtS | Operator::I32x4LtS | Operator::I64x2LtS => {
1902
translate_vector_icmp(IntCC::SignedLessThan, type_of(op), builder, stack)
1903
}
1904
Operator::I8x16GtU | Operator::I16x8GtU | Operator::I32x4GtU => {
1905
translate_vector_icmp(IntCC::UnsignedGreaterThan, type_of(op), builder, stack)
1906
}
1907
Operator::I8x16LtU | Operator::I16x8LtU | Operator::I32x4LtU => {
1908
translate_vector_icmp(IntCC::UnsignedLessThan, type_of(op), builder, stack)
1909
}
1910
Operator::I8x16GeS | Operator::I16x8GeS | Operator::I32x4GeS | Operator::I64x2GeS => {
1911
translate_vector_icmp(IntCC::SignedGreaterThanOrEqual, type_of(op), builder, stack)
1912
}
1913
Operator::I8x16LeS | Operator::I16x8LeS | Operator::I32x4LeS | Operator::I64x2LeS => {
1914
translate_vector_icmp(IntCC::SignedLessThanOrEqual, type_of(op), builder, stack)
1915
}
1916
Operator::I8x16GeU | Operator::I16x8GeU | Operator::I32x4GeU => translate_vector_icmp(
1917
IntCC::UnsignedGreaterThanOrEqual,
1918
type_of(op),
1919
builder,
1920
stack,
1921
),
1922
Operator::I8x16LeU | Operator::I16x8LeU | Operator::I32x4LeU => {
1923
translate_vector_icmp(IntCC::UnsignedLessThanOrEqual, type_of(op), builder, stack)
1924
}
1925
Operator::F32x4Eq | Operator::F64x2Eq => {
1926
translate_vector_fcmp(FloatCC::Equal, type_of(op), builder, stack)
1927
}
1928
Operator::F32x4Ne | Operator::F64x2Ne => {
1929
translate_vector_fcmp(FloatCC::NotEqual, type_of(op), builder, stack)
1930
}
1931
Operator::F32x4Lt | Operator::F64x2Lt => {
1932
translate_vector_fcmp(FloatCC::LessThan, type_of(op), builder, stack)
1933
}
1934
Operator::F32x4Gt | Operator::F64x2Gt => {
1935
translate_vector_fcmp(FloatCC::GreaterThan, type_of(op), builder, stack)
1936
}
1937
Operator::F32x4Le | Operator::F64x2Le => {
1938
translate_vector_fcmp(FloatCC::LessThanOrEqual, type_of(op), builder, stack)
1939
}
1940
Operator::F32x4Ge | Operator::F64x2Ge => {
1941
translate_vector_fcmp(FloatCC::GreaterThanOrEqual, type_of(op), builder, stack)
1942
}
1943
Operator::F32x4Add | Operator::F64x2Add => {
1944
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1945
stack.push1(builder.ins().fadd(a, b))
1946
}
1947
Operator::F32x4Sub | Operator::F64x2Sub => {
1948
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1949
stack.push1(builder.ins().fsub(a, b))
1950
}
1951
Operator::F32x4Mul | Operator::F64x2Mul => {
1952
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1953
stack.push1(builder.ins().fmul(a, b))
1954
}
1955
Operator::F32x4Div | Operator::F64x2Div => {
1956
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1957
stack.push1(builder.ins().fdiv(a, b))
1958
}
1959
Operator::F32x4Max | Operator::F64x2Max => {
1960
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1961
stack.push1(builder.ins().fmax(a, b))
1962
}
1963
Operator::F32x4Min | Operator::F64x2Min => {
1964
let (a, b) = pop2_with_bitcast(stack, type_of(op), builder);
1965
stack.push1(builder.ins().fmin(a, b))
1966
}
1967
Operator::F32x4PMax | Operator::F64x2PMax => {
1968
// Note the careful ordering here with respect to `fcmp` and
1969
// `bitselect`. This matches the spec definition of:
1970
//
1971
// fpmax(z1, z2) =
1972
// * If z1 is less than z2 then return z2.
1973
// * Else return z1.
1974
let ty = type_of(op);
1975
let (a, b) = pop2_with_bitcast(stack, ty, builder);
1976
let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b);
1977
let cmp = optionally_bitcast_vector(cmp, ty, builder);
1978
stack.push1(builder.ins().bitselect(cmp, b, a))
1979
}
1980
Operator::F32x4PMin | Operator::F64x2PMin => {
1981
// Note the careful ordering here which is similar to `pmax` above:
1982
//
1983
// fpmin(z1, z2) =
1984
// * If z2 is less than z1 then return z2.
1985
// * Else return z1.
1986
let ty = type_of(op);
1987
let (a, b) = pop2_with_bitcast(stack, ty, builder);
1988
let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a);
1989
let cmp = optionally_bitcast_vector(cmp, ty, builder);
1990
stack.push1(builder.ins().bitselect(cmp, b, a))
1991
}
1992
Operator::F32x4Sqrt | Operator::F64x2Sqrt => {
1993
let a = pop1_with_bitcast(stack, type_of(op), builder);
1994
stack.push1(builder.ins().sqrt(a))
1995
}
1996
Operator::F32x4Neg | Operator::F64x2Neg => {
1997
let a = pop1_with_bitcast(stack, type_of(op), builder);
1998
stack.push1(builder.ins().fneg(a))
1999
}
2000
Operator::F32x4Abs | Operator::F64x2Abs => {
2001
let a = pop1_with_bitcast(stack, type_of(op), builder);
2002
stack.push1(builder.ins().fabs(a))
2003
}
2004
Operator::F32x4ConvertI32x4S => {
2005
let a = pop1_with_bitcast(stack, I32X4, builder);
2006
stack.push1(builder.ins().fcvt_from_sint(F32X4, a))
2007
}
2008
Operator::F32x4ConvertI32x4U => {
2009
let a = pop1_with_bitcast(stack, I32X4, builder);
2010
stack.push1(builder.ins().fcvt_from_uint(F32X4, a))
2011
}
2012
Operator::F64x2ConvertLowI32x4S => {
2013
let a = pop1_with_bitcast(stack, I32X4, builder);
2014
let widened_a = builder.ins().swiden_low(a);
2015
stack.push1(builder.ins().fcvt_from_sint(F64X2, widened_a));
2016
}
2017
Operator::F64x2ConvertLowI32x4U => {
2018
let a = pop1_with_bitcast(stack, I32X4, builder);
2019
let widened_a = builder.ins().uwiden_low(a);
2020
stack.push1(builder.ins().fcvt_from_uint(F64X2, widened_a));
2021
}
2022
Operator::F64x2PromoteLowF32x4 => {
2023
let a = pop1_with_bitcast(stack, F32X4, builder);
2024
stack.push1(builder.ins().fvpromote_low(a));
2025
}
2026
Operator::F32x4DemoteF64x2Zero => {
2027
let a = pop1_with_bitcast(stack, F64X2, builder);
2028
stack.push1(builder.ins().fvdemote(a));
2029
}
2030
Operator::I32x4TruncSatF32x4S => {
2031
let a = pop1_with_bitcast(stack, F32X4, builder);
2032
stack.push1(builder.ins().fcvt_to_sint_sat(I32X4, a))
2033
}
2034
Operator::I32x4TruncSatF64x2SZero => {
2035
let a = pop1_with_bitcast(stack, F64X2, builder);
2036
let converted_a = builder.ins().fcvt_to_sint_sat(I64X2, a);
2037
let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into());
2038
let zero = builder.ins().vconst(I64X2, handle);
2039
2040
stack.push1(builder.ins().snarrow(converted_a, zero));
2041
}
2042
2043
// FIXME(#5913): the relaxed instructions here are translated the same
2044
// as the saturating instructions, even when the code generator
2045
// configuration allow for different semantics across hosts. On x86,
2046
// however, it's theoretically possible to have a slightly more optimal
2047
// lowering which accounts for NaN differently, although the lowering is
2048
// still not trivial (e.g. one instruction). At this time the
2049
// more-optimal-but-still-large lowering for x86 is not implemented so
2050
// the relaxed instructions are listed here instead of down below with
2051
// the other relaxed instructions. An x86-specific implementation (or
2052
// perhaps for other backends too) should be added and the codegen for
2053
// the relaxed instruction should conditionally be different.
2054
Operator::I32x4RelaxedTruncF32x4U | Operator::I32x4TruncSatF32x4U => {
2055
let a = pop1_with_bitcast(stack, F32X4, builder);
2056
stack.push1(builder.ins().fcvt_to_uint_sat(I32X4, a))
2057
}
2058
Operator::I32x4RelaxedTruncF64x2UZero | Operator::I32x4TruncSatF64x2UZero => {
2059
let a = pop1_with_bitcast(stack, F64X2, builder);
2060
let zero_constant = builder.func.dfg.constants.insert(vec![0u8; 16].into());
2061
let result = if environ.is_x86() && !environ.isa().has_round() {
2062
// On x86 the vector lowering for `fcvt_to_uint_sat` requires
2063
// SSE4.1 `round` instructions. If SSE4.1 isn't available it
2064
// falls back to a libcall which we don't want in Wasmtime.
2065
// Handle this by falling back to the scalar implementation
2066
// which does not require SSE4.1 instructions.
2067
let lane0 = builder.ins().extractlane(a, 0);
2068
let lane1 = builder.ins().extractlane(a, 1);
2069
let lane0_rounded = builder.ins().fcvt_to_uint_sat(I32, lane0);
2070
let lane1_rounded = builder.ins().fcvt_to_uint_sat(I32, lane1);
2071
let result = builder.ins().vconst(I32X4, zero_constant);
2072
let result = builder.ins().insertlane(result, lane0_rounded, 0);
2073
builder.ins().insertlane(result, lane1_rounded, 1)
2074
} else {
2075
let converted_a = builder.ins().fcvt_to_uint_sat(I64X2, a);
2076
let zero = builder.ins().vconst(I64X2, zero_constant);
2077
builder.ins().uunarrow(converted_a, zero)
2078
};
2079
stack.push1(result);
2080
}
2081
2082
Operator::I8x16NarrowI16x8S => {
2083
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2084
stack.push1(builder.ins().snarrow(a, b))
2085
}
2086
Operator::I16x8NarrowI32x4S => {
2087
let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2088
stack.push1(builder.ins().snarrow(a, b))
2089
}
2090
Operator::I8x16NarrowI16x8U => {
2091
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2092
stack.push1(builder.ins().unarrow(a, b))
2093
}
2094
Operator::I16x8NarrowI32x4U => {
2095
let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2096
stack.push1(builder.ins().unarrow(a, b))
2097
}
2098
Operator::I16x8ExtendLowI8x16S => {
2099
let a = pop1_with_bitcast(stack, I8X16, builder);
2100
stack.push1(builder.ins().swiden_low(a))
2101
}
2102
Operator::I16x8ExtendHighI8x16S => {
2103
let a = pop1_with_bitcast(stack, I8X16, builder);
2104
stack.push1(builder.ins().swiden_high(a))
2105
}
2106
Operator::I16x8ExtendLowI8x16U => {
2107
let a = pop1_with_bitcast(stack, I8X16, builder);
2108
stack.push1(builder.ins().uwiden_low(a))
2109
}
2110
Operator::I16x8ExtendHighI8x16U => {
2111
let a = pop1_with_bitcast(stack, I8X16, builder);
2112
stack.push1(builder.ins().uwiden_high(a))
2113
}
2114
Operator::I32x4ExtendLowI16x8S => {
2115
let a = pop1_with_bitcast(stack, I16X8, builder);
2116
stack.push1(builder.ins().swiden_low(a))
2117
}
2118
Operator::I32x4ExtendHighI16x8S => {
2119
let a = pop1_with_bitcast(stack, I16X8, builder);
2120
stack.push1(builder.ins().swiden_high(a))
2121
}
2122
Operator::I32x4ExtendLowI16x8U => {
2123
let a = pop1_with_bitcast(stack, I16X8, builder);
2124
stack.push1(builder.ins().uwiden_low(a))
2125
}
2126
Operator::I32x4ExtendHighI16x8U => {
2127
let a = pop1_with_bitcast(stack, I16X8, builder);
2128
stack.push1(builder.ins().uwiden_high(a))
2129
}
2130
Operator::I64x2ExtendLowI32x4S => {
2131
let a = pop1_with_bitcast(stack, I32X4, builder);
2132
stack.push1(builder.ins().swiden_low(a))
2133
}
2134
Operator::I64x2ExtendHighI32x4S => {
2135
let a = pop1_with_bitcast(stack, I32X4, builder);
2136
stack.push1(builder.ins().swiden_high(a))
2137
}
2138
Operator::I64x2ExtendLowI32x4U => {
2139
let a = pop1_with_bitcast(stack, I32X4, builder);
2140
stack.push1(builder.ins().uwiden_low(a))
2141
}
2142
Operator::I64x2ExtendHighI32x4U => {
2143
let a = pop1_with_bitcast(stack, I32X4, builder);
2144
stack.push1(builder.ins().uwiden_high(a))
2145
}
2146
Operator::I16x8ExtAddPairwiseI8x16S => {
2147
let a = pop1_with_bitcast(stack, I8X16, builder);
2148
let widen_low = builder.ins().swiden_low(a);
2149
let widen_high = builder.ins().swiden_high(a);
2150
stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2151
}
2152
Operator::I32x4ExtAddPairwiseI16x8S => {
2153
let a = pop1_with_bitcast(stack, I16X8, builder);
2154
let widen_low = builder.ins().swiden_low(a);
2155
let widen_high = builder.ins().swiden_high(a);
2156
stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2157
}
2158
Operator::I16x8ExtAddPairwiseI8x16U => {
2159
let a = pop1_with_bitcast(stack, I8X16, builder);
2160
let widen_low = builder.ins().uwiden_low(a);
2161
let widen_high = builder.ins().uwiden_high(a);
2162
stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2163
}
2164
Operator::I32x4ExtAddPairwiseI16x8U => {
2165
let a = pop1_with_bitcast(stack, I16X8, builder);
2166
let widen_low = builder.ins().uwiden_low(a);
2167
let widen_high = builder.ins().uwiden_high(a);
2168
stack.push1(builder.ins().iadd_pairwise(widen_low, widen_high));
2169
}
2170
Operator::F32x4Ceil => {
2171
let arg = pop1_with_bitcast(stack, F32X4, builder);
2172
stack.push1(environ.ceil_f32x4(builder, arg));
2173
}
2174
Operator::F64x2Ceil => {
2175
let arg = pop1_with_bitcast(stack, F64X2, builder);
2176
stack.push1(environ.ceil_f64x2(builder, arg));
2177
}
2178
Operator::F32x4Floor => {
2179
let arg = pop1_with_bitcast(stack, F32X4, builder);
2180
stack.push1(environ.floor_f32x4(builder, arg));
2181
}
2182
Operator::F64x2Floor => {
2183
let arg = pop1_with_bitcast(stack, F64X2, builder);
2184
stack.push1(environ.floor_f64x2(builder, arg));
2185
}
2186
Operator::F32x4Trunc => {
2187
let arg = pop1_with_bitcast(stack, F32X4, builder);
2188
stack.push1(environ.trunc_f32x4(builder, arg));
2189
}
2190
Operator::F64x2Trunc => {
2191
let arg = pop1_with_bitcast(stack, F64X2, builder);
2192
stack.push1(environ.trunc_f64x2(builder, arg));
2193
}
2194
Operator::F32x4Nearest => {
2195
let arg = pop1_with_bitcast(stack, F32X4, builder);
2196
stack.push1(environ.nearest_f32x4(builder, arg));
2197
}
2198
Operator::F64x2Nearest => {
2199
let arg = pop1_with_bitcast(stack, F64X2, builder);
2200
stack.push1(environ.nearest_f64x2(builder, arg));
2201
}
2202
Operator::I32x4DotI16x8S => {
2203
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2204
let alow = builder.ins().swiden_low(a);
2205
let blow = builder.ins().swiden_low(b);
2206
let low = builder.ins().imul(alow, blow);
2207
let ahigh = builder.ins().swiden_high(a);
2208
let bhigh = builder.ins().swiden_high(b);
2209
let high = builder.ins().imul(ahigh, bhigh);
2210
stack.push1(builder.ins().iadd_pairwise(low, high));
2211
}
2212
Operator::I8x16Popcnt => {
2213
let arg = pop1_with_bitcast(stack, type_of(op), builder);
2214
stack.push1(builder.ins().popcnt(arg));
2215
}
2216
Operator::I16x8Q15MulrSatS => {
2217
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2218
stack.push1(builder.ins().sqmul_round_sat(a, b))
2219
}
2220
Operator::I16x8ExtMulLowI8x16S => {
2221
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2222
let a_low = builder.ins().swiden_low(a);
2223
let b_low = builder.ins().swiden_low(b);
2224
stack.push1(builder.ins().imul(a_low, b_low));
2225
}
2226
Operator::I16x8ExtMulHighI8x16S => {
2227
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2228
let a_high = builder.ins().swiden_high(a);
2229
let b_high = builder.ins().swiden_high(b);
2230
stack.push1(builder.ins().imul(a_high, b_high));
2231
}
2232
Operator::I16x8ExtMulLowI8x16U => {
2233
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2234
let a_low = builder.ins().uwiden_low(a);
2235
let b_low = builder.ins().uwiden_low(b);
2236
stack.push1(builder.ins().imul(a_low, b_low));
2237
}
2238
Operator::I16x8ExtMulHighI8x16U => {
2239
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2240
let a_high = builder.ins().uwiden_high(a);
2241
let b_high = builder.ins().uwiden_high(b);
2242
stack.push1(builder.ins().imul(a_high, b_high));
2243
}
2244
Operator::I32x4ExtMulLowI16x8S => {
2245
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2246
let a_low = builder.ins().swiden_low(a);
2247
let b_low = builder.ins().swiden_low(b);
2248
stack.push1(builder.ins().imul(a_low, b_low));
2249
}
2250
Operator::I32x4ExtMulHighI16x8S => {
2251
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2252
let a_high = builder.ins().swiden_high(a);
2253
let b_high = builder.ins().swiden_high(b);
2254
stack.push1(builder.ins().imul(a_high, b_high));
2255
}
2256
Operator::I32x4ExtMulLowI16x8U => {
2257
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2258
let a_low = builder.ins().uwiden_low(a);
2259
let b_low = builder.ins().uwiden_low(b);
2260
stack.push1(builder.ins().imul(a_low, b_low));
2261
}
2262
Operator::I32x4ExtMulHighI16x8U => {
2263
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2264
let a_high = builder.ins().uwiden_high(a);
2265
let b_high = builder.ins().uwiden_high(b);
2266
stack.push1(builder.ins().imul(a_high, b_high));
2267
}
2268
Operator::I64x2ExtMulLowI32x4S => {
2269
let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2270
let a_low = builder.ins().swiden_low(a);
2271
let b_low = builder.ins().swiden_low(b);
2272
stack.push1(builder.ins().imul(a_low, b_low));
2273
}
2274
Operator::I64x2ExtMulHighI32x4S => {
2275
let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2276
let a_high = builder.ins().swiden_high(a);
2277
let b_high = builder.ins().swiden_high(b);
2278
stack.push1(builder.ins().imul(a_high, b_high));
2279
}
2280
Operator::I64x2ExtMulLowI32x4U => {
2281
let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2282
let a_low = builder.ins().uwiden_low(a);
2283
let b_low = builder.ins().uwiden_low(b);
2284
stack.push1(builder.ins().imul(a_low, b_low));
2285
}
2286
Operator::I64x2ExtMulHighI32x4U => {
2287
let (a, b) = pop2_with_bitcast(stack, I32X4, builder);
2288
let a_high = builder.ins().uwiden_high(a);
2289
let b_high = builder.ins().uwiden_high(b);
2290
stack.push1(builder.ins().imul(a_high, b_high));
2291
}
2292
Operator::MemoryDiscard { .. } => {
2293
return Err(wasm_unsupported!(
2294
"proposed memory-control operator {:?}",
2295
op
2296
));
2297
}
2298
2299
Operator::F32x4RelaxedMax | Operator::F64x2RelaxedMax => {
2300
let ty = type_of(op);
2301
let (a, b) = pop2_with_bitcast(stack, ty, builder);
2302
stack.push1(
2303
if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2304
// Deterministic semantics match the `fmax` instruction, or
2305
// the `fAAxBB.max` wasm instruction.
2306
builder.ins().fmax(a, b)
2307
} else {
2308
// Note that this matches the `pmax` translation which has
2309
// careful ordering of its operands to trigger
2310
// pattern-matches in the x86 backend.
2311
let cmp = builder.ins().fcmp(FloatCC::LessThan, a, b);
2312
let cmp = optionally_bitcast_vector(cmp, ty, builder);
2313
builder.ins().bitselect(cmp, b, a)
2314
},
2315
)
2316
}
2317
2318
Operator::F32x4RelaxedMin | Operator::F64x2RelaxedMin => {
2319
let ty = type_of(op);
2320
let (a, b) = pop2_with_bitcast(stack, ty, builder);
2321
stack.push1(
2322
if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2323
// Deterministic semantics match the `fmin` instruction, or
2324
// the `fAAxBB.min` wasm instruction.
2325
builder.ins().fmin(a, b)
2326
} else {
2327
// Note that this matches the `pmin` translation which has
2328
// careful ordering of its operands to trigger
2329
// pattern-matches in the x86 backend.
2330
let cmp = builder.ins().fcmp(FloatCC::LessThan, b, a);
2331
let cmp = optionally_bitcast_vector(cmp, ty, builder);
2332
builder.ins().bitselect(cmp, b, a)
2333
},
2334
);
2335
}
2336
2337
Operator::I8x16RelaxedSwizzle => {
2338
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2339
stack.push1(environ.relaxed_swizzle(builder, a, b));
2340
}
2341
2342
Operator::F32x4RelaxedMadd => {
2343
let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2344
stack.push1(environ.fma_f32x4(builder, a, b, c));
2345
}
2346
Operator::F64x2RelaxedMadd => {
2347
let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2348
stack.push1(environ.fma_f64x2(builder, a, b, c));
2349
}
2350
Operator::F32x4RelaxedNmadd => {
2351
let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2352
let a = builder.ins().fneg(a);
2353
stack.push1(environ.fma_f32x4(builder, a, b, c));
2354
}
2355
Operator::F64x2RelaxedNmadd => {
2356
let (a, b, c) = pop3_with_bitcast(stack, type_of(op), builder);
2357
let a = builder.ins().fneg(a);
2358
stack.push1(environ.fma_f64x2(builder, a, b, c));
2359
}
2360
2361
Operator::I8x16RelaxedLaneselect
2362
| Operator::I16x8RelaxedLaneselect
2363
| Operator::I32x4RelaxedLaneselect
2364
| Operator::I64x2RelaxedLaneselect => {
2365
let ty = type_of(op);
2366
let (a, b, c) = pop3_with_bitcast(stack, ty, builder);
2367
// Note that the variable swaps here are intentional due to
2368
// the difference of the order of the wasm op and the clif
2369
// op.
2370
stack.push1(
2371
if environ.relaxed_simd_deterministic()
2372
|| !environ.use_x86_blendv_for_relaxed_laneselect(ty)
2373
{
2374
// Deterministic semantics are a `bitselect` along the lines
2375
// of the wasm `v128.bitselect` instruction.
2376
builder.ins().bitselect(c, a, b)
2377
} else {
2378
builder.ins().x86_blendv(c, a, b)
2379
},
2380
);
2381
}
2382
2383
Operator::I32x4RelaxedTruncF32x4S => {
2384
let a = pop1_with_bitcast(stack, F32X4, builder);
2385
stack.push1(
2386
if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2387
// Deterministic semantics are to match the
2388
// `i32x4.trunc_sat_f32x4_s` instruction.
2389
builder.ins().fcvt_to_sint_sat(I32X4, a)
2390
} else {
2391
builder.ins().x86_cvtt2dq(I32X4, a)
2392
},
2393
)
2394
}
2395
Operator::I32x4RelaxedTruncF64x2SZero => {
2396
let a = pop1_with_bitcast(stack, F64X2, builder);
2397
let converted_a = if environ.relaxed_simd_deterministic() || !environ.is_x86() {
2398
// Deterministic semantics are to match the
2399
// `i32x4.trunc_sat_f64x2_s_zero` instruction.
2400
builder.ins().fcvt_to_sint_sat(I64X2, a)
2401
} else {
2402
builder.ins().x86_cvtt2dq(I64X2, a)
2403
};
2404
let handle = builder.func.dfg.constants.insert(vec![0u8; 16].into());
2405
let zero = builder.ins().vconst(I64X2, handle);
2406
2407
stack.push1(builder.ins().snarrow(converted_a, zero));
2408
}
2409
Operator::I16x8RelaxedQ15mulrS => {
2410
let (a, b) = pop2_with_bitcast(stack, I16X8, builder);
2411
stack.push1(
2412
if environ.relaxed_simd_deterministic()
2413
|| !environ.use_x86_pmulhrsw_for_relaxed_q15mul()
2414
{
2415
// Deterministic semantics are to match the
2416
// `i16x8.q15mulr_sat_s` instruction.
2417
builder.ins().sqmul_round_sat(a, b)
2418
} else {
2419
builder.ins().x86_pmulhrsw(a, b)
2420
},
2421
);
2422
}
2423
Operator::I16x8RelaxedDotI8x16I7x16S => {
2424
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2425
stack.push1(
2426
if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() {
2427
// Deterministic semantics are to treat both operands as
2428
// signed integers and perform the dot product.
2429
let alo = builder.ins().swiden_low(a);
2430
let blo = builder.ins().swiden_low(b);
2431
let lo = builder.ins().imul(alo, blo);
2432
let ahi = builder.ins().swiden_high(a);
2433
let bhi = builder.ins().swiden_high(b);
2434
let hi = builder.ins().imul(ahi, bhi);
2435
builder.ins().iadd_pairwise(lo, hi)
2436
} else {
2437
builder.ins().x86_pmaddubsw(a, b)
2438
},
2439
);
2440
}
2441
2442
Operator::I32x4RelaxedDotI8x16I7x16AddS => {
2443
let c = pop1_with_bitcast(stack, I32X4, builder);
2444
let (a, b) = pop2_with_bitcast(stack, I8X16, builder);
2445
let dot =
2446
if environ.relaxed_simd_deterministic() || !environ.use_x86_pmaddubsw_for_dot() {
2447
// Deterministic semantics are to treat both operands as
2448
// signed integers and perform the dot product.
2449
let alo = builder.ins().swiden_low(a);
2450
let blo = builder.ins().swiden_low(b);
2451
let lo = builder.ins().imul(alo, blo);
2452
let ahi = builder.ins().swiden_high(a);
2453
let bhi = builder.ins().swiden_high(b);
2454
let hi = builder.ins().imul(ahi, bhi);
2455
builder.ins().iadd_pairwise(lo, hi)
2456
} else {
2457
builder.ins().x86_pmaddubsw(a, b)
2458
};
2459
let dotlo = builder.ins().swiden_low(dot);
2460
let dothi = builder.ins().swiden_high(dot);
2461
let dot32 = builder.ins().iadd_pairwise(dotlo, dothi);
2462
stack.push1(builder.ins().iadd(dot32, c));
2463
}
2464
2465
Operator::BrOnNull { relative_depth } => {
2466
let r = stack.pop1();
2467
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2468
unreachable!("validation")
2469
};
2470
let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack);
2471
let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
2472
let else_block = builder.create_block();
2473
canonicalise_brif(builder, is_null, br_destination, inputs, else_block, &[]);
2474
2475
builder.seal_block(else_block); // The only predecessor is the current block.
2476
builder.switch_to_block(else_block);
2477
stack.push1(r);
2478
}
2479
Operator::BrOnNonNull { relative_depth } => {
2480
// We write this a bit differently from the spec to avoid an extra
2481
// block/branch and the typed accounting thereof. Instead of the
2482
// spec's approach, it's described as such:
2483
// Peek the value val from the stack.
2484
// If val is ref.null ht, then: pop the value val from the stack.
2485
// Else: Execute the instruction (br relative_depth).
2486
let r = stack.peek1();
2487
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2488
unreachable!("validation")
2489
};
2490
let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
2491
let (br_destination, inputs) = translate_br_if_args(*relative_depth, stack);
2492
let else_block = builder.create_block();
2493
canonicalise_brif(builder, is_null, else_block, &[], br_destination, inputs);
2494
2495
// In the null case, pop the ref
2496
stack.pop1();
2497
2498
builder.seal_block(else_block); // The only predecessor is the current block.
2499
2500
// The rest of the translation operates on our is null case, which is
2501
// currently an empty block
2502
builder.switch_to_block(else_block);
2503
}
2504
Operator::CallRef { type_index } => {
2505
// Get function signature
2506
// `index` is the index of the function's signature and `table_index` is the index of
2507
// the table to search the function in.
2508
let type_index = TypeIndex::from_u32(*type_index);
2509
let sigref = environ.get_or_create_sig_ref(builder.func, type_index);
2510
let num_args = environ.num_params_for_function_type(type_index);
2511
let callee = stack.pop1();
2512
2513
// Bitcast any vector arguments to their default type, I8X16, before calling.
2514
let args = stack.peekn_mut(num_args);
2515
bitcast_wasm_params(environ, sigref, args, builder);
2516
2517
let inst_results = environ.translate_call_ref(
2518
builder,
2519
sigref,
2520
callee,
2521
stack.peekn(num_args),
2522
stack.handlers.handlers(),
2523
)?;
2524
2525
debug_assert_eq!(
2526
inst_results.len(),
2527
builder.func.dfg.signatures[sigref].returns.len(),
2528
"translate_call_ref results should match the call signature"
2529
);
2530
stack.popn(num_args);
2531
stack.pushn(&inst_results);
2532
}
2533
Operator::RefAsNonNull => {
2534
let r = stack.pop1();
2535
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2536
unreachable!("validation")
2537
};
2538
let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
2539
environ.trapnz(builder, is_null, crate::TRAP_NULL_REFERENCE);
2540
stack.push1(r);
2541
}
2542
2543
Operator::RefI31 => {
2544
let val = stack.pop1();
2545
let i31ref = environ.translate_ref_i31(builder.cursor(), val)?;
2546
stack.push1(i31ref);
2547
}
2548
Operator::I31GetS => {
2549
let i31ref = stack.pop1();
2550
let val = environ.translate_i31_get_s(builder, i31ref)?;
2551
stack.push1(val);
2552
}
2553
Operator::I31GetU => {
2554
let i31ref = stack.pop1();
2555
let val = environ.translate_i31_get_u(builder, i31ref)?;
2556
stack.push1(val);
2557
}
2558
2559
Operator::StructNew { struct_type_index } => {
2560
let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2561
let arity = environ.struct_fields_len(struct_type_index)?;
2562
let fields: StructFieldsVec = stack.peekn(arity).iter().copied().collect();
2563
stack.popn(arity);
2564
let struct_ref = environ.translate_struct_new(builder, struct_type_index, fields)?;
2565
stack.push1(struct_ref);
2566
}
2567
2568
Operator::StructNewDefault { struct_type_index } => {
2569
let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2570
let struct_ref = environ.translate_struct_new_default(builder, struct_type_index)?;
2571
stack.push1(struct_ref);
2572
}
2573
2574
Operator::StructSet {
2575
struct_type_index,
2576
field_index,
2577
} => {
2578
let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2579
let val = stack.pop1();
2580
let struct_ref = stack.pop1();
2581
environ.translate_struct_set(
2582
builder,
2583
struct_type_index,
2584
*field_index,
2585
struct_ref,
2586
val,
2587
)?;
2588
}
2589
2590
Operator::StructGetS {
2591
struct_type_index,
2592
field_index,
2593
} => {
2594
let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2595
let struct_ref = stack.pop1();
2596
let val = environ.translate_struct_get(
2597
builder,
2598
struct_type_index,
2599
*field_index,
2600
struct_ref,
2601
Some(Extension::Sign),
2602
)?;
2603
stack.push1(val);
2604
}
2605
2606
Operator::StructGetU {
2607
struct_type_index,
2608
field_index,
2609
} => {
2610
let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2611
let struct_ref = stack.pop1();
2612
let val = environ.translate_struct_get(
2613
builder,
2614
struct_type_index,
2615
*field_index,
2616
struct_ref,
2617
Some(Extension::Zero),
2618
)?;
2619
stack.push1(val);
2620
}
2621
2622
Operator::StructGet {
2623
struct_type_index,
2624
field_index,
2625
} => {
2626
let struct_type_index = TypeIndex::from_u32(*struct_type_index);
2627
let struct_ref = stack.pop1();
2628
let val = environ.translate_struct_get(
2629
builder,
2630
struct_type_index,
2631
*field_index,
2632
struct_ref,
2633
None,
2634
)?;
2635
stack.push1(val);
2636
}
2637
2638
Operator::ArrayNew { array_type_index } => {
2639
let array_type_index = TypeIndex::from_u32(*array_type_index);
2640
let (elem, len) = stack.pop2();
2641
let array_ref = environ.translate_array_new(builder, array_type_index, elem, len)?;
2642
stack.push1(array_ref);
2643
}
2644
Operator::ArrayNewDefault { array_type_index } => {
2645
let array_type_index = TypeIndex::from_u32(*array_type_index);
2646
let len = stack.pop1();
2647
let array_ref = environ.translate_array_new_default(builder, array_type_index, len)?;
2648
stack.push1(array_ref);
2649
}
2650
Operator::ArrayNewFixed {
2651
array_type_index,
2652
array_size,
2653
} => {
2654
let array_type_index = TypeIndex::from_u32(*array_type_index);
2655
let array_size = usize::try_from(*array_size).unwrap();
2656
let elems = stack.peekn(array_size);
2657
let array_ref = environ.translate_array_new_fixed(builder, array_type_index, elems)?;
2658
stack.popn(array_size);
2659
stack.push1(array_ref);
2660
}
2661
Operator::ArrayNewData {
2662
array_type_index,
2663
array_data_index,
2664
} => {
2665
let array_type_index = TypeIndex::from_u32(*array_type_index);
2666
let array_data_index = DataIndex::from_u32(*array_data_index);
2667
let (data_offset, len) = stack.pop2();
2668
let array_ref = environ.translate_array_new_data(
2669
builder,
2670
array_type_index,
2671
array_data_index,
2672
data_offset,
2673
len,
2674
)?;
2675
stack.push1(array_ref);
2676
}
2677
Operator::ArrayNewElem {
2678
array_type_index,
2679
array_elem_index,
2680
} => {
2681
let array_type_index = TypeIndex::from_u32(*array_type_index);
2682
let array_elem_index = ElemIndex::from_u32(*array_elem_index);
2683
let (elem_offset, len) = stack.pop2();
2684
let array_ref = environ.translate_array_new_elem(
2685
builder,
2686
array_type_index,
2687
array_elem_index,
2688
elem_offset,
2689
len,
2690
)?;
2691
stack.push1(array_ref);
2692
}
2693
Operator::ArrayCopy {
2694
array_type_index_dst,
2695
array_type_index_src,
2696
} => {
2697
let array_type_index_dst = TypeIndex::from_u32(*array_type_index_dst);
2698
let array_type_index_src = TypeIndex::from_u32(*array_type_index_src);
2699
let (dst_array, dst_index, src_array, src_index, len) = stack.pop5();
2700
environ.translate_array_copy(
2701
builder,
2702
array_type_index_dst,
2703
dst_array,
2704
dst_index,
2705
array_type_index_src,
2706
src_array,
2707
src_index,
2708
len,
2709
)?;
2710
}
2711
Operator::ArrayFill { array_type_index } => {
2712
let array_type_index = TypeIndex::from_u32(*array_type_index);
2713
let (array, index, val, len) = stack.pop4();
2714
environ.translate_array_fill(builder, array_type_index, array, index, val, len)?;
2715
}
2716
Operator::ArrayInitData {
2717
array_type_index,
2718
array_data_index,
2719
} => {
2720
let array_type_index = TypeIndex::from_u32(*array_type_index);
2721
let array_data_index = DataIndex::from_u32(*array_data_index);
2722
let (array, dst_index, src_index, len) = stack.pop4();
2723
environ.translate_array_init_data(
2724
builder,
2725
array_type_index,
2726
array,
2727
dst_index,
2728
array_data_index,
2729
src_index,
2730
len,
2731
)?;
2732
}
2733
Operator::ArrayInitElem {
2734
array_type_index,
2735
array_elem_index,
2736
} => {
2737
let array_type_index = TypeIndex::from_u32(*array_type_index);
2738
let array_elem_index = ElemIndex::from_u32(*array_elem_index);
2739
let (array, dst_index, src_index, len) = stack.pop4();
2740
environ.translate_array_init_elem(
2741
builder,
2742
array_type_index,
2743
array,
2744
dst_index,
2745
array_elem_index,
2746
src_index,
2747
len,
2748
)?;
2749
}
2750
Operator::ArrayLen => {
2751
let array = stack.pop1();
2752
let len = environ.translate_array_len(builder, array)?;
2753
stack.push1(len);
2754
}
2755
Operator::ArrayGet { array_type_index } => {
2756
let array_type_index = TypeIndex::from_u32(*array_type_index);
2757
let (array, index) = stack.pop2();
2758
let elem =
2759
environ.translate_array_get(builder, array_type_index, array, index, None)?;
2760
stack.push1(elem);
2761
}
2762
Operator::ArrayGetS { array_type_index } => {
2763
let array_type_index = TypeIndex::from_u32(*array_type_index);
2764
let (array, index) = stack.pop2();
2765
let elem = environ.translate_array_get(
2766
builder,
2767
array_type_index,
2768
array,
2769
index,
2770
Some(Extension::Sign),
2771
)?;
2772
stack.push1(elem);
2773
}
2774
Operator::ArrayGetU { array_type_index } => {
2775
let array_type_index = TypeIndex::from_u32(*array_type_index);
2776
let (array, index) = stack.pop2();
2777
let elem = environ.translate_array_get(
2778
builder,
2779
array_type_index,
2780
array,
2781
index,
2782
Some(Extension::Zero),
2783
)?;
2784
stack.push1(elem);
2785
}
2786
Operator::ArraySet { array_type_index } => {
2787
let array_type_index = TypeIndex::from_u32(*array_type_index);
2788
let (array, index, elem) = stack.pop3();
2789
environ.translate_array_set(builder, array_type_index, array, index, elem)?;
2790
}
2791
Operator::RefEq => {
2792
let (r1, r2) = stack.pop2();
2793
let eq = builder.ins().icmp(ir::condcodes::IntCC::Equal, r1, r2);
2794
let eq = builder.ins().uextend(ir::types::I32, eq);
2795
stack.push1(eq);
2796
}
2797
Operator::RefTestNonNull { hty } => {
2798
let r = stack.pop1();
2799
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2800
unreachable!("validation")
2801
};
2802
let heap_type = environ.convert_heap_type(*hty)?;
2803
let result = environ.translate_ref_test(
2804
builder,
2805
WasmRefType {
2806
heap_type,
2807
nullable: false,
2808
},
2809
r,
2810
*r_ty,
2811
)?;
2812
stack.push1(result);
2813
}
2814
Operator::RefTestNullable { hty } => {
2815
let r = stack.pop1();
2816
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2817
unreachable!("validation")
2818
};
2819
let heap_type = environ.convert_heap_type(*hty)?;
2820
let result = environ.translate_ref_test(
2821
builder,
2822
WasmRefType {
2823
heap_type,
2824
nullable: true,
2825
},
2826
r,
2827
*r_ty,
2828
)?;
2829
stack.push1(result);
2830
}
2831
Operator::RefCastNonNull { hty } => {
2832
let r = stack.pop1();
2833
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2834
unreachable!("validation")
2835
};
2836
let heap_type = environ.convert_heap_type(*hty)?;
2837
let cast_okay = environ.translate_ref_test(
2838
builder,
2839
WasmRefType {
2840
heap_type,
2841
nullable: false,
2842
},
2843
r,
2844
*r_ty,
2845
)?;
2846
environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);
2847
stack.push1(r);
2848
}
2849
Operator::RefCastNullable { hty } => {
2850
let r = stack.pop1();
2851
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2852
unreachable!("validation")
2853
};
2854
let heap_type = environ.convert_heap_type(*hty)?;
2855
let cast_okay = environ.translate_ref_test(
2856
builder,
2857
WasmRefType {
2858
heap_type,
2859
nullable: true,
2860
},
2861
r,
2862
*r_ty,
2863
)?;
2864
environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);
2865
stack.push1(r);
2866
}
2867
Operator::BrOnCast {
2868
relative_depth,
2869
to_ref_type,
2870
from_ref_type: _,
2871
} => {
2872
let r = stack.peek1();
2873
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2874
unreachable!("validation")
2875
};
2876
2877
let to_ref_type = environ.convert_ref_type(*to_ref_type)?;
2878
let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;
2879
2880
let (cast_succeeds_block, inputs) = translate_br_if_args(*relative_depth, stack);
2881
let cast_fails_block = builder.create_block();
2882
canonicalise_brif(
2883
builder,
2884
cast_is_okay,
2885
cast_succeeds_block,
2886
inputs,
2887
cast_fails_block,
2888
&[
2889
// NB: the `cast_fails_block` is dominated by the current
2890
// block, and therefore doesn't need any block params.
2891
],
2892
);
2893
2894
// The only predecessor is the current block.
2895
builder.seal_block(cast_fails_block);
2896
2897
// The next Wasm instruction is executed when the cast failed and we
2898
// did not branch away.
2899
builder.switch_to_block(cast_fails_block);
2900
}
2901
Operator::BrOnCastFail {
2902
relative_depth,
2903
to_ref_type,
2904
from_ref_type: _,
2905
} => {
2906
let r = stack.peek1();
2907
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2908
unreachable!("validation")
2909
};
2910
2911
let to_ref_type = environ.convert_ref_type(*to_ref_type)?;
2912
let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;
2913
2914
let (cast_fails_block, inputs) = translate_br_if_args(*relative_depth, stack);
2915
let cast_succeeds_block = builder.create_block();
2916
canonicalise_brif(
2917
builder,
2918
cast_is_okay,
2919
cast_succeeds_block,
2920
&[
2921
// NB: the `cast_succeeds_block` is dominated by the current
2922
// block, and therefore doesn't need any block params.
2923
],
2924
cast_fails_block,
2925
inputs,
2926
);
2927
2928
// The only predecessor is the current block.
2929
builder.seal_block(cast_succeeds_block);
2930
2931
// The next Wasm instruction is executed when the cast succeeded and
2932
// we did not branch away.
2933
builder.switch_to_block(cast_succeeds_block);
2934
}
2935
2936
Operator::AnyConvertExtern => {
2937
// Pop an `externref`, push an `anyref`. But they have the same
2938
// representation, so we don't actually need to do anything.
2939
}
2940
Operator::ExternConvertAny => {
2941
// Pop an `anyref`, push an `externref`. But they have the same
2942
// representation, so we don't actually need to do anything.
2943
}
2944
2945
Operator::ContNew { cont_type_index } => {
2946
let cont_type_index = TypeIndex::from_u32(*cont_type_index);
2947
let arg_types: SmallVec<[_; 8]> = environ
2948
.continuation_arguments(cont_type_index)
2949
.to_smallvec();
2950
let result_types: SmallVec<[_; 8]> =
2951
environ.continuation_returns(cont_type_index).to_smallvec();
2952
let r = stack.pop1();
2953
let contobj = environ.translate_cont_new(builder, r, &arg_types, &result_types)?;
2954
stack.push1(contobj);
2955
}
2956
Operator::ContBind {
2957
argument_index,
2958
result_index,
2959
} => {
2960
let src_types = environ.continuation_arguments(TypeIndex::from_u32(*argument_index));
2961
let dst_arity = environ
2962
.continuation_arguments(TypeIndex::from_u32(*result_index))
2963
.len();
2964
let arg_count = src_types.len() - dst_arity;
2965
2966
let arg_types = &src_types[0..arg_count];
2967
for arg_type in arg_types {
2968
// We can't bind GC objects using cont.bind at the moment: We
2969
// don't have the necessary infrastructure to traverse the
2970
// buffers used by cont.bind when looking for GC roots. Thus,
2971
// this crude check ensures that these buffers can never contain
2972
// GC roots to begin with.
2973
if arg_type.is_vmgcref_type_and_not_i31() {
2974
return Err(wasmtime_environ::WasmError::Unsupported(
2975
"cont.bind does not support GC types at the moment".into(),
2976
));
2977
}
2978
}
2979
2980
let (original_contobj, args) = stack.peekn(arg_count + 1).split_last().unwrap();
2981
2982
let new_contobj = environ.translate_cont_bind(builder, *original_contobj, args);
2983
2984
stack.popn(arg_count + 1);
2985
stack.push1(new_contobj);
2986
}
2987
Operator::Suspend { tag_index } => {
2988
let tag_index = TagIndex::from_u32(*tag_index);
2989
let param_types = environ.tag_params(tag_index).to_vec();
2990
let return_types: SmallVec<[_; 8]> = environ
2991
.tag_returns(tag_index)
2992
.iter()
2993
.map(|ty| crate::value_type(environ.isa(), *ty))
2994
.collect();
2995
2996
let params = stack.peekn(param_types.len());
2997
let param_count = params.len();
2998
2999
let return_values =
3000
environ.translate_suspend(builder, tag_index.as_u32(), params, &return_types);
3001
3002
stack.popn(param_count);
3003
stack.pushn(&return_values);
3004
}
3005
Operator::Resume {
3006
cont_type_index,
3007
resume_table: wasm_resume_table,
3008
} => {
3009
// We translate the block indices in the wasm resume_table to actual Blocks.
3010
let mut clif_resume_table = vec![];
3011
for handle in &wasm_resume_table.handlers {
3012
match handle {
3013
wasmparser::Handle::OnLabel { tag, label } => {
3014
let i = stack.control_stack.len() - 1 - (*label as usize);
3015
let frame = &mut stack.control_stack[i];
3016
// This is side-effecting!
3017
frame.set_branched_to_exit();
3018
clif_resume_table.push((*tag, Some(frame.br_destination())));
3019
}
3020
wasmparser::Handle::OnSwitch { tag } => {
3021
clif_resume_table.push((*tag, None));
3022
}
3023
}
3024
}
3025
3026
let cont_type_index = TypeIndex::from_u32(*cont_type_index);
3027
let arity = environ.continuation_arguments(cont_type_index).len();
3028
let (contobj, call_args) = stack.peekn(arity + 1).split_last().unwrap();
3029
3030
let cont_return_vals = environ.translate_resume(
3031
builder,
3032
cont_type_index.as_u32(),
3033
*contobj,
3034
call_args,
3035
&clif_resume_table,
3036
)?;
3037
3038
stack.popn(arity + 1); // arguments + continuation
3039
stack.pushn(&cont_return_vals);
3040
}
3041
Operator::ResumeThrow {
3042
cont_type_index: _,
3043
tag_index: _,
3044
resume_table: _,
3045
} => {
3046
// TODO(10248) This depends on exception handling
3047
return Err(wasmtime_environ::WasmError::Unsupported(
3048
"resume.throw instructions not supported, yet".to_string(),
3049
));
3050
}
3051
Operator::Switch {
3052
cont_type_index,
3053
tag_index,
3054
} => {
3055
// Arguments of the continuation we are going to switch to
3056
let continuation_argument_types: SmallVec<[_; 8]> = environ
3057
.continuation_arguments(TypeIndex::from_u32(*cont_type_index))
3058
.to_smallvec();
3059
// Arity includes the continuation argument
3060
let arity = continuation_argument_types.len();
3061
let (contobj, switch_args) = stack.peekn(arity).split_last().unwrap();
3062
3063
// Type of the continuation we are going to create by suspending the
3064
// currently running stack
3065
let current_continuation_type = continuation_argument_types.last().unwrap();
3066
let current_continuation_type = current_continuation_type.unwrap_ref_type();
3067
3068
// Argument types of current_continuation_type. These will in turn
3069
// be the types of the arguments we receive when someone switches
3070
// back to this switch instruction
3071
let current_continuation_arg_types: SmallVec<[_; 8]> =
3072
match current_continuation_type.heap_type {
3073
WasmHeapType::ConcreteCont(index) => {
3074
let mti = index
3075
.as_module_type_index()
3076
.expect("Only supporting module type indices on switch for now");
3077
3078
environ
3079
.continuation_arguments(TypeIndex::from_u32(mti.as_u32()))
3080
.iter()
3081
.map(|ty| crate::value_type(environ.isa(), *ty))
3082
.collect()
3083
}
3084
_ => panic!("Invalid type on switch"),
3085
};
3086
3087
let switch_return_values = environ.translate_switch(
3088
builder,
3089
*tag_index,
3090
*contobj,
3091
switch_args,
3092
&current_continuation_arg_types,
3093
)?;
3094
3095
stack.popn(arity);
3096
stack.pushn(&switch_return_values)
3097
}
3098
3099
Operator::GlobalAtomicGet { .. }
3100
| Operator::GlobalAtomicSet { .. }
3101
| Operator::GlobalAtomicRmwAdd { .. }
3102
| Operator::GlobalAtomicRmwSub { .. }
3103
| Operator::GlobalAtomicRmwOr { .. }
3104
| Operator::GlobalAtomicRmwXor { .. }
3105
| Operator::GlobalAtomicRmwAnd { .. }
3106
| Operator::GlobalAtomicRmwXchg { .. }
3107
| Operator::GlobalAtomicRmwCmpxchg { .. }
3108
| Operator::TableAtomicGet { .. }
3109
| Operator::TableAtomicSet { .. }
3110
| Operator::TableAtomicRmwXchg { .. }
3111
| Operator::TableAtomicRmwCmpxchg { .. }
3112
| Operator::StructAtomicGet { .. }
3113
| Operator::StructAtomicGetS { .. }
3114
| Operator::StructAtomicGetU { .. }
3115
| Operator::StructAtomicSet { .. }
3116
| Operator::StructAtomicRmwAdd { .. }
3117
| Operator::StructAtomicRmwSub { .. }
3118
| Operator::StructAtomicRmwOr { .. }
3119
| Operator::StructAtomicRmwXor { .. }
3120
| Operator::StructAtomicRmwAnd { .. }
3121
| Operator::StructAtomicRmwXchg { .. }
3122
| Operator::StructAtomicRmwCmpxchg { .. }
3123
| Operator::ArrayAtomicGet { .. }
3124
| Operator::ArrayAtomicGetS { .. }
3125
| Operator::ArrayAtomicGetU { .. }
3126
| Operator::ArrayAtomicSet { .. }
3127
| Operator::ArrayAtomicRmwAdd { .. }
3128
| Operator::ArrayAtomicRmwSub { .. }
3129
| Operator::ArrayAtomicRmwOr { .. }
3130
| Operator::ArrayAtomicRmwXor { .. }
3131
| Operator::ArrayAtomicRmwAnd { .. }
3132
| Operator::ArrayAtomicRmwXchg { .. }
3133
| Operator::ArrayAtomicRmwCmpxchg { .. }
3134
| Operator::RefI31Shared { .. } => {
3135
return Err(wasm_unsupported!(
3136
"shared-everything-threads operators are not yet implemented"
3137
));
3138
}
3139
3140
Operator::I64MulWideS => {
3141
let (arg1, arg2) = stack.pop2();
3142
let arg1 = builder.ins().sextend(I128, arg1);
3143
let arg2 = builder.ins().sextend(I128, arg2);
3144
let result = builder.ins().imul(arg1, arg2);
3145
let (lo, hi) = builder.ins().isplit(result);
3146
stack.push2(lo, hi);
3147
}
3148
Operator::I64MulWideU => {
3149
let (arg1, arg2) = stack.pop2();
3150
let arg1 = builder.ins().uextend(I128, arg1);
3151
let arg2 = builder.ins().uextend(I128, arg2);
3152
let result = builder.ins().imul(arg1, arg2);
3153
let (lo, hi) = builder.ins().isplit(result);
3154
stack.push2(lo, hi);
3155
}
3156
Operator::I64Add128 => {
3157
let (arg1, arg2, arg3, arg4) = stack.pop4();
3158
let arg1 = builder.ins().iconcat(arg1, arg2);
3159
let arg2 = builder.ins().iconcat(arg3, arg4);
3160
let result = builder.ins().iadd(arg1, arg2);
3161
let (res1, res2) = builder.ins().isplit(result);
3162
stack.push2(res1, res2);
3163
}
3164
Operator::I64Sub128 => {
3165
let (arg1, arg2, arg3, arg4) = stack.pop4();
3166
let arg1 = builder.ins().iconcat(arg1, arg2);
3167
let arg2 = builder.ins().iconcat(arg3, arg4);
3168
let result = builder.ins().isub(arg1, arg2);
3169
let (res1, res2) = builder.ins().isplit(result);
3170
stack.push2(res1, res2);
3171
}
3172
3173
// catch-all as `Operator` is `#[non_exhaustive]`
3174
op => return Err(wasm_unsupported!("operator {op:?}")),
3175
};
3176
Ok(())
3177
}
3178
3179
/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them
3180
/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable
3181
/// portion so the translation state must be updated accordingly.
3182
fn translate_unreachable_operator(
3183
validator: &FuncValidator<impl WasmModuleResources>,
3184
op: &Operator,
3185
builder: &mut FunctionBuilder,
3186
stack: &mut FuncTranslationStacks,
3187
environ: &mut FuncEnvironment<'_>,
3188
) -> WasmResult<()> {
3189
debug_assert!(!stack.reachable);
3190
match *op {
3191
Operator::If { blockty } => {
3192
// Push a placeholder control stack entry. The if isn't reachable,
3193
// so we don't have any branches anywhere.
3194
stack.push_if(
3195
ir::Block::reserved_value(),
3196
ElseData::NoElse {
3197
branch_inst: ir::Inst::reserved_value(),
3198
placeholder: ir::Block::reserved_value(),
3199
},
3200
0,
3201
0,
3202
blockty,
3203
);
3204
}
3205
Operator::Loop { blockty: _ }
3206
| Operator::Block { blockty: _ }
3207
| Operator::TryTable { try_table: _ } => {
3208
stack.push_block(ir::Block::reserved_value(), 0, 0);
3209
}
3210
Operator::Else => {
3211
let i = stack.control_stack.len() - 1;
3212
match stack.control_stack[i] {
3213
ControlStackFrame::If {
3214
ref else_data,
3215
head_is_reachable,
3216
ref mut consequent_ends_reachable,
3217
blocktype,
3218
..
3219
} => {
3220
debug_assert!(consequent_ends_reachable.is_none());
3221
*consequent_ends_reachable = Some(stack.reachable);
3222
3223
if head_is_reachable {
3224
// We have a branch from the head of the `if` to the `else`.
3225
stack.reachable = true;
3226
3227
let else_block = match *else_data {
3228
ElseData::NoElse {
3229
branch_inst,
3230
placeholder,
3231
} => {
3232
let (params, _results) =
3233
blocktype_params_results(validator, blocktype)?;
3234
let else_block = block_with_params(builder, params, environ)?;
3235
let frame = stack.control_stack.last().unwrap();
3236
frame.truncate_value_stack_to_else_params(&mut stack.stack);
3237
3238
// We change the target of the branch instruction.
3239
builder.change_jump_destination(
3240
branch_inst,
3241
placeholder,
3242
else_block,
3243
);
3244
builder.seal_block(else_block);
3245
else_block
3246
}
3247
ElseData::WithElse { else_block } => {
3248
let frame = stack.control_stack.last().unwrap();
3249
frame.truncate_value_stack_to_else_params(&mut stack.stack);
3250
else_block
3251
}
3252
};
3253
3254
builder.switch_to_block(else_block);
3255
3256
// Again, no need to push the parameters for the `else`,
3257
// since we already did when we saw the original `if`. See
3258
// the comment for translating `Operator::Else` in
3259
// `translate_operator` for details.
3260
}
3261
}
3262
_ => unreachable!(),
3263
}
3264
}
3265
Operator::End => {
3266
let value_stack = &mut stack.stack;
3267
let control_stack = &mut stack.control_stack;
3268
let frame = control_stack.pop().unwrap();
3269
3270
frame.restore_catch_handlers(&mut stack.handlers, builder);
3271
3272
// Pop unused parameters from stack.
3273
frame.truncate_value_stack_to_original_size(value_stack);
3274
3275
let reachable_anyway = match frame {
3276
// If it is a loop we also have to seal the body loop block
3277
ControlStackFrame::Loop { header, .. } => {
3278
builder.seal_block(header);
3279
// And loops can't have branches to the end.
3280
false
3281
}
3282
// If we never set `consequent_ends_reachable` then that means
3283
// we are finishing the consequent now, and there was no
3284
// `else`. Whether the following block is reachable depends only
3285
// on if the head was reachable.
3286
ControlStackFrame::If {
3287
head_is_reachable,
3288
consequent_ends_reachable: None,
3289
..
3290
} => head_is_reachable,
3291
// Since we are only in this function when in unreachable code,
3292
// we know that the alternative just ended unreachable. Whether
3293
// the following block is reachable depends on if the consequent
3294
// ended reachable or not.
3295
ControlStackFrame::If {
3296
head_is_reachable,
3297
consequent_ends_reachable: Some(consequent_ends_reachable),
3298
..
3299
} => head_is_reachable && consequent_ends_reachable,
3300
// All other control constructs are already handled.
3301
_ => false,
3302
};
3303
3304
if frame.exit_is_branched_to() || reachable_anyway {
3305
builder.switch_to_block(frame.following_code());
3306
builder.seal_block(frame.following_code());
3307
3308
// And add the return values of the block but only if the next block is reachable
3309
// (which corresponds to testing if the stack depth is 1)
3310
value_stack.extend_from_slice(builder.block_params(frame.following_code()));
3311
stack.reachable = true;
3312
}
3313
}
3314
_ => {
3315
// We don't translate because this is unreachable code
3316
}
3317
}
3318
3319
Ok(())
3320
}
3321
3322
/// This function is a generalized helper for validating that a wasm-supplied
3323
/// heap address is in-bounds.
3324
///
3325
/// This function takes a litany of parameters and requires that the *Wasm*
3326
/// address to be verified is at the top of the stack in `state`. This will
3327
/// generate necessary IR to validate that the heap address is correctly
3328
/// in-bounds, and various parameters are returned describing the valid *native*
3329
/// heap address if execution reaches that point.
3330
///
3331
/// Returns `None` when the Wasm access will unconditionally trap.
3332
///
3333
/// Returns `(flags, wasm_addr, native_addr)`.
3334
fn prepare_addr(
3335
memarg: &MemArg,
3336
access_size: u8,
3337
builder: &mut FunctionBuilder,
3338
stack: &mut FuncTranslationStacks,
3339
environ: &mut FuncEnvironment<'_>,
3340
) -> WasmResult<Reachability<(MemFlags, Value, Value)>> {
3341
let index = stack.pop1();
3342
3343
let memory_index = MemoryIndex::from_u32(memarg.memory);
3344
let heap = environ.get_or_create_heap(builder.func, memory_index);
3345
3346
// How exactly the bounds check is performed here and what it's performed
3347
// on is a bit tricky. Generally we want to rely on access violations (e.g.
3348
// segfaults) to generate traps since that means we don't have to bounds
3349
// check anything explicitly.
3350
//
3351
// (1) If we don't have a guard page of unmapped memory, though, then we
3352
// can't rely on this trapping behavior through segfaults. Instead we need
3353
// to bounds-check the entire memory access here which is everything from
3354
// `addr32 + offset` to `addr32 + offset + width` (not inclusive). In this
3355
// scenario our adjusted offset that we're checking is `memarg.offset +
3356
// access_size`. Note that we do saturating arithmetic here to avoid
3357
// overflow. The addition here is in the 64-bit space, which means that
3358
// we'll never overflow for 32-bit wasm but for 64-bit this is an issue. If
3359
// our effective offset is u64::MAX though then it's impossible for for
3360
// that to actually be a valid offset because otherwise the wasm linear
3361
// memory would take all of the host memory!
3362
//
3363
// (2) If we have a guard page, however, then we can perform a further
3364
// optimization of the generated code by only checking multiples of the
3365
// offset-guard size to be more CSE-friendly. Knowing that we have at least
3366
// 1 page of a guard page we're then able to disregard the `width` since we
3367
// know it's always less than one page. Our bounds check will be for the
3368
// first byte which will either succeed and be guaranteed to fault if it's
3369
// actually out of bounds, or the bounds check itself will fail. In any case
3370
// we assert that the width is reasonably small for now so this assumption
3371
// can be adjusted in the future if we get larger widths.
3372
//
3373
// Put another way we can say, where `y < offset_guard_size`:
3374
//
3375
// n * offset_guard_size + y = offset
3376
//
3377
// We'll then pass `n * offset_guard_size` as the bounds check value. If
3378
// this traps then our `offset` would have trapped anyway. If this check
3379
// passes we know
3380
//
3381
// addr32 + n * offset_guard_size < bound
3382
//
3383
// which means
3384
//
3385
// addr32 + n * offset_guard_size + y < bound + offset_guard_size
3386
//
3387
// because `y < offset_guard_size`, which then means:
3388
//
3389
// addr32 + offset < bound + offset_guard_size
3390
//
3391
// Since we know that that guard size bytes are all unmapped we're
3392
// guaranteed that `offset` and the `width` bytes after it are either
3393
// in-bounds or will hit the guard page, meaning we'll get the desired
3394
// semantics we want.
3395
//
3396
// ---
3397
//
3398
// With all that in mind remember that the goal is to bounds check as few
3399
// things as possible. To facilitate this the "fast path" is expected to be
3400
// hit like so:
3401
//
3402
// * For wasm32, wasmtime defaults to 4gb "static" memories with 2gb guard
3403
// regions. This means that for all offsets <=2gb, we hit the optimized
3404
// case for `heap_addr` on static memories 4gb in size in cranelift's
3405
// legalization of `heap_addr`, eliding the bounds check entirely.
3406
//
3407
// * For wasm64 offsets <=2gb will generate a single `heap_addr`
3408
// instruction, but at this time all heaps are "dynamic" which means that
3409
// a single bounds check is forced. Ideally we'd do better here, but
3410
// that's the current state of affairs.
3411
//
3412
// Basically we assume that most configurations have a guard page and most
3413
// offsets in `memarg` are <=2gb, which means we get the fast path of one
3414
// `heap_addr` instruction plus a hardcoded i32-offset in memory-related
3415
// instructions.
3416
let heap = environ.heaps()[heap].clone();
3417
let addr = match u32::try_from(memarg.offset) {
3418
// If our offset fits within a u32, then we can place the it into the
3419
// offset immediate of the `heap_addr` instruction.
3420
Ok(offset) => bounds_check_and_compute_addr(
3421
builder,
3422
environ,
3423
&heap,
3424
index,
3425
BoundsCheck::StaticOffset {
3426
offset,
3427
access_size,
3428
},
3429
ir::TrapCode::HEAP_OUT_OF_BOUNDS,
3430
),
3431
3432
// If the offset doesn't fit within a u32, then we can't pass it
3433
// directly into `heap_addr`.
3434
//
3435
// One reasonable question you might ask is "why not?". There's no
3436
// fundamental reason why `heap_addr` *must* take a 32-bit offset. The
3437
// reason this isn't done, though, is that blindly changing the offset
3438
// to a 64-bit offset increases the size of the `InstructionData` enum
3439
// in cranelift by 8 bytes (16 to 24). This can have significant
3440
// performance implications so the conclusion when this was written was
3441
// that we shouldn't do that.
3442
//
3443
// Without the ability to put the whole offset into the `heap_addr`
3444
// instruction we need to fold the offset into the address itself with
3445
// an unsigned addition. In doing so though we need to check for
3446
// overflow because that would mean the address is out-of-bounds (wasm
3447
// bounds checks happen on the effective 33 or 65 bit address once the
3448
// offset is factored in).
3449
//
3450
// Once we have the effective address, offset already folded in, then
3451
// `heap_addr` is used to verify that the address is indeed in-bounds.
3452
//
3453
// Note that this is generating what's likely to be at least two
3454
// branches, one for the overflow and one for the bounds check itself.
3455
// For now though that should hopefully be ok since 4gb+ offsets are
3456
// relatively odd/rare. In the future if needed we can look into
3457
// optimizing this more.
3458
Err(_) => {
3459
let offset = builder
3460
.ins()
3461
.iconst(heap.index_type(), memarg.offset.cast_signed());
3462
let adjusted_index = environ.uadd_overflow_trap(
3463
builder,
3464
index,
3465
offset,
3466
ir::TrapCode::HEAP_OUT_OF_BOUNDS,
3467
);
3468
bounds_check_and_compute_addr(
3469
builder,
3470
environ,
3471
&heap,
3472
adjusted_index,
3473
BoundsCheck::StaticOffset {
3474
offset: 0,
3475
access_size,
3476
},
3477
ir::TrapCode::HEAP_OUT_OF_BOUNDS,
3478
)
3479
}
3480
};
3481
let addr = match addr {
3482
Reachability::Unreachable => return Ok(Reachability::Unreachable),
3483
Reachability::Reachable(a) => a,
3484
};
3485
3486
// Note that we don't set `is_aligned` here, even if the load instruction's
3487
// alignment immediate may says it's aligned, because WebAssembly's
3488
// immediate field is just a hint, while Cranelift's aligned flag needs a
3489
// guarantee. WebAssembly memory accesses are always little-endian.
3490
let mut flags = MemFlags::new();
3491
flags.set_endianness(ir::Endianness::Little);
3492
3493
if heap.pcc_memory_type.is_some() {
3494
// Proof-carrying code is enabled; check this memory access.
3495
flags.set_checked();
3496
}
3497
3498
// The access occurs to the `heap` disjoint category of abstract
3499
// state. This may allow alias analysis to merge redundant loads,
3500
// etc. when heap accesses occur interleaved with other (table,
3501
// vmctx, stack) accesses.
3502
flags.set_alias_region(Some(ir::AliasRegion::Heap));
3503
3504
Ok(Reachability::Reachable((flags, index, addr)))
3505
}
3506
3507
fn align_atomic_addr(
3508
memarg: &MemArg,
3509
loaded_bytes: u8,
3510
builder: &mut FunctionBuilder,
3511
stack: &mut FuncTranslationStacks,
3512
environ: &mut FuncEnvironment<'_>,
3513
) {
3514
// Atomic addresses must all be aligned correctly, and for now we check
3515
// alignment before we check out-of-bounds-ness. The order of this check may
3516
// need to be updated depending on the outcome of the official threads
3517
// proposal itself.
3518
//
3519
// Note that with an offset>0 we generate an `iadd_imm` where the result is
3520
// thrown away after the offset check. This may truncate the offset and the
3521
// result may overflow as well, but those conditions won't affect the
3522
// alignment check itself. This can probably be optimized better and we
3523
// should do so in the future as well.
3524
if loaded_bytes > 1 {
3525
let addr = stack.pop1(); // "peek" via pop then push
3526
stack.push1(addr);
3527
let effective_addr = if memarg.offset == 0 {
3528
addr
3529
} else {
3530
builder.ins().iadd_imm(addr, memarg.offset.cast_signed())
3531
};
3532
debug_assert!(loaded_bytes.is_power_of_two());
3533
let misalignment = builder
3534
.ins()
3535
.band_imm(effective_addr, i64::from(loaded_bytes - 1));
3536
let f = builder.ins().icmp_imm(IntCC::NotEqual, misalignment, 0);
3537
environ.trapnz(builder, f, crate::TRAP_HEAP_MISALIGNED);
3538
}
3539
}
3540
3541
/// Like `prepare_addr` but for atomic accesses.
3542
///
3543
/// Returns `None` when the Wasm access will unconditionally trap.
3544
fn prepare_atomic_addr(
3545
memarg: &MemArg,
3546
loaded_bytes: u8,
3547
builder: &mut FunctionBuilder,
3548
stack: &mut FuncTranslationStacks,
3549
environ: &mut FuncEnvironment<'_>,
3550
) -> WasmResult<Reachability<(MemFlags, Value, Value)>> {
3551
align_atomic_addr(memarg, loaded_bytes, builder, stack, environ);
3552
prepare_addr(memarg, loaded_bytes, builder, stack, environ)
3553
}
3554
3555
/// Translate a load instruction.
3556
///
3557
/// Returns the execution state's reachability after the load is translated.
3558
fn translate_load(
3559
memarg: &MemArg,
3560
opcode: ir::Opcode,
3561
result_ty: Type,
3562
builder: &mut FunctionBuilder,
3563
stack: &mut FuncTranslationStacks,
3564
environ: &mut FuncEnvironment<'_>,
3565
) -> WasmResult<Reachability<()>> {
3566
let mem_op_size = mem_op_size(opcode, result_ty);
3567
let (flags, wasm_index, base) =
3568
match prepare_addr(memarg, mem_op_size, builder, stack, environ)? {
3569
Reachability::Unreachable => return Ok(Reachability::Unreachable),
3570
Reachability::Reachable((f, i, b)) => (f, i, b),
3571
};
3572
3573
environ.before_load(builder, mem_op_size, wasm_index, memarg.offset);
3574
3575
let (load, dfg) = builder
3576
.ins()
3577
.Load(opcode, result_ty, flags, Offset32::new(0), base);
3578
stack.push1(dfg.first_result(load));
3579
Ok(Reachability::Reachable(()))
3580
}
3581
3582
/// Translate a store instruction.
3583
fn translate_store(
3584
memarg: &MemArg,
3585
opcode: ir::Opcode,
3586
builder: &mut FunctionBuilder,
3587
stack: &mut FuncTranslationStacks,
3588
environ: &mut FuncEnvironment<'_>,
3589
) -> WasmResult<()> {
3590
let val = stack.pop1();
3591
let val_ty = builder.func.dfg.value_type(val);
3592
let mem_op_size = mem_op_size(opcode, val_ty);
3593
3594
let (flags, wasm_index, base) = unwrap_or_return_unreachable_state!(
3595
stack,
3596
prepare_addr(memarg, mem_op_size, builder, stack, environ)?
3597
);
3598
3599
environ.before_store(builder, mem_op_size, wasm_index, memarg.offset);
3600
3601
builder
3602
.ins()
3603
.Store(opcode, val_ty, flags, Offset32::new(0), val, base);
3604
Ok(())
3605
}
3606
3607
fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u8 {
3608
match opcode {
3609
ir::Opcode::Istore8 | ir::Opcode::Sload8 | ir::Opcode::Uload8 => 1,
3610
ir::Opcode::Istore16 | ir::Opcode::Sload16 | ir::Opcode::Uload16 => 2,
3611
ir::Opcode::Istore32 | ir::Opcode::Sload32 | ir::Opcode::Uload32 => 4,
3612
ir::Opcode::Store | ir::Opcode::Load => u8::try_from(ty.bytes()).unwrap(),
3613
_ => panic!("unknown size of mem op for {opcode:?}"),
3614
}
3615
}
3616
3617
fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) {
3618
let (arg0, arg1) = stack.pop2();
3619
let val = builder.ins().icmp(cc, arg0, arg1);
3620
stack.push1(builder.ins().uextend(I32, val));
3621
}
3622
3623
fn translate_atomic_rmw(
3624
widened_ty: Type,
3625
access_ty: Type,
3626
op: AtomicRmwOp,
3627
memarg: &MemArg,
3628
builder: &mut FunctionBuilder,
3629
stack: &mut FuncTranslationStacks,
3630
environ: &mut FuncEnvironment<'_>,
3631
) -> WasmResult<()> {
3632
let mut arg2 = stack.pop1();
3633
let arg2_ty = builder.func.dfg.value_type(arg2);
3634
3635
// The operation is performed at type `access_ty`, and the old value is zero-extended
3636
// to type `widened_ty`.
3637
match access_ty {
3638
I8 | I16 | I32 | I64 => {}
3639
_ => {
3640
return Err(wasm_unsupported!(
3641
"atomic_rmw: unsupported access type {:?}",
3642
access_ty
3643
));
3644
}
3645
};
3646
let w_ty_ok = match widened_ty {
3647
I32 | I64 => true,
3648
_ => false,
3649
};
3650
assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
3651
3652
assert!(arg2_ty.bytes() >= access_ty.bytes());
3653
if arg2_ty.bytes() > access_ty.bytes() {
3654
arg2 = builder.ins().ireduce(access_ty, arg2);
3655
}
3656
3657
let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3658
stack,
3659
prepare_atomic_addr(
3660
memarg,
3661
u8::try_from(access_ty.bytes()).unwrap(),
3662
builder,
3663
stack,
3664
environ,
3665
)?
3666
);
3667
3668
let mut res = builder.ins().atomic_rmw(access_ty, flags, op, addr, arg2);
3669
if access_ty != widened_ty {
3670
res = builder.ins().uextend(widened_ty, res);
3671
}
3672
stack.push1(res);
3673
Ok(())
3674
}
3675
3676
fn translate_atomic_cas(
3677
widened_ty: Type,
3678
access_ty: Type,
3679
memarg: &MemArg,
3680
builder: &mut FunctionBuilder,
3681
stack: &mut FuncTranslationStacks,
3682
environ: &mut FuncEnvironment<'_>,
3683
) -> WasmResult<()> {
3684
let (mut expected, mut replacement) = stack.pop2();
3685
let expected_ty = builder.func.dfg.value_type(expected);
3686
let replacement_ty = builder.func.dfg.value_type(replacement);
3687
3688
// The compare-and-swap is performed at type `access_ty`, and the old value is zero-extended
3689
// to type `widened_ty`.
3690
match access_ty {
3691
I8 | I16 | I32 | I64 => {}
3692
_ => {
3693
return Err(wasm_unsupported!(
3694
"atomic_cas: unsupported access type {:?}",
3695
access_ty
3696
));
3697
}
3698
};
3699
let w_ty_ok = match widened_ty {
3700
I32 | I64 => true,
3701
_ => false,
3702
};
3703
assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
3704
3705
assert!(expected_ty.bytes() >= access_ty.bytes());
3706
if expected_ty.bytes() > access_ty.bytes() {
3707
expected = builder.ins().ireduce(access_ty, expected);
3708
}
3709
assert!(replacement_ty.bytes() >= access_ty.bytes());
3710
if replacement_ty.bytes() > access_ty.bytes() {
3711
replacement = builder.ins().ireduce(access_ty, replacement);
3712
}
3713
3714
let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3715
stack,
3716
prepare_atomic_addr(
3717
memarg,
3718
u8::try_from(access_ty.bytes()).unwrap(),
3719
builder,
3720
stack,
3721
environ,
3722
)?
3723
);
3724
let mut res = builder.ins().atomic_cas(flags, addr, expected, replacement);
3725
if access_ty != widened_ty {
3726
res = builder.ins().uextend(widened_ty, res);
3727
}
3728
stack.push1(res);
3729
Ok(())
3730
}
3731
3732
fn translate_atomic_load(
3733
widened_ty: Type,
3734
access_ty: Type,
3735
memarg: &MemArg,
3736
builder: &mut FunctionBuilder,
3737
stack: &mut FuncTranslationStacks,
3738
environ: &mut FuncEnvironment<'_>,
3739
) -> WasmResult<()> {
3740
// The load is performed at type `access_ty`, and the loaded value is zero extended
3741
// to `widened_ty`.
3742
match access_ty {
3743
I8 | I16 | I32 | I64 => {}
3744
_ => {
3745
return Err(wasm_unsupported!(
3746
"atomic_load: unsupported access type {:?}",
3747
access_ty
3748
));
3749
}
3750
};
3751
let w_ty_ok = match widened_ty {
3752
I32 | I64 => true,
3753
_ => false,
3754
};
3755
assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes());
3756
3757
let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3758
stack,
3759
prepare_atomic_addr(
3760
memarg,
3761
u8::try_from(access_ty.bytes()).unwrap(),
3762
builder,
3763
stack,
3764
environ,
3765
)?
3766
);
3767
let mut res = builder.ins().atomic_load(access_ty, flags, addr);
3768
if access_ty != widened_ty {
3769
res = builder.ins().uextend(widened_ty, res);
3770
}
3771
stack.push1(res);
3772
Ok(())
3773
}
3774
3775
fn translate_atomic_store(
3776
access_ty: Type,
3777
memarg: &MemArg,
3778
builder: &mut FunctionBuilder,
3779
stack: &mut FuncTranslationStacks,
3780
environ: &mut FuncEnvironment<'_>,
3781
) -> WasmResult<()> {
3782
let mut data = stack.pop1();
3783
let data_ty = builder.func.dfg.value_type(data);
3784
3785
// The operation is performed at type `access_ty`, and the data to be stored may first
3786
// need to be narrowed accordingly.
3787
match access_ty {
3788
I8 | I16 | I32 | I64 => {}
3789
_ => {
3790
return Err(wasm_unsupported!(
3791
"atomic_store: unsupported access type {:?}",
3792
access_ty
3793
));
3794
}
3795
};
3796
let d_ty_ok = match data_ty {
3797
I32 | I64 => true,
3798
_ => false,
3799
};
3800
assert!(d_ty_ok && data_ty.bytes() >= access_ty.bytes());
3801
3802
if data_ty.bytes() > access_ty.bytes() {
3803
data = builder.ins().ireduce(access_ty, data);
3804
}
3805
3806
let (flags, _, addr) = unwrap_or_return_unreachable_state!(
3807
stack,
3808
prepare_atomic_addr(
3809
memarg,
3810
u8::try_from(access_ty.bytes()).unwrap(),
3811
builder,
3812
stack,
3813
environ,
3814
)?
3815
);
3816
builder.ins().atomic_store(flags, data, addr);
3817
Ok(())
3818
}
3819
3820
fn translate_vector_icmp(
3821
cc: IntCC,
3822
needed_type: Type,
3823
builder: &mut FunctionBuilder,
3824
stack: &mut FuncTranslationStacks,
3825
) {
3826
let (a, b) = stack.pop2();
3827
let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
3828
let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
3829
stack.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b))
3830
}
3831
3832
fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, stack: &mut FuncTranslationStacks) {
3833
let (arg0, arg1) = stack.pop2();
3834
let val = builder.ins().fcmp(cc, arg0, arg1);
3835
stack.push1(builder.ins().uextend(I32, val));
3836
}
3837
3838
fn translate_vector_fcmp(
3839
cc: FloatCC,
3840
needed_type: Type,
3841
builder: &mut FunctionBuilder,
3842
stack: &mut FuncTranslationStacks,
3843
) {
3844
let (a, b) = stack.pop2();
3845
let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
3846
let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
3847
stack.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b))
3848
}
3849
3850
fn translate_br_if(
3851
relative_depth: u32,
3852
builder: &mut FunctionBuilder,
3853
stack: &mut FuncTranslationStacks,
3854
) {
3855
let val = stack.pop1();
3856
let (br_destination, inputs) = translate_br_if_args(relative_depth, stack);
3857
let next_block = builder.create_block();
3858
canonicalise_brif(builder, val, br_destination, inputs, next_block, &[]);
3859
3860
builder.seal_block(next_block); // The only predecessor is the current block.
3861
builder.switch_to_block(next_block);
3862
}
3863
3864
fn translate_br_if_args(
3865
relative_depth: u32,
3866
stack: &mut FuncTranslationStacks,
3867
) -> (ir::Block, &mut [ir::Value]) {
3868
let i = stack.control_stack.len() - 1 - (relative_depth as usize);
3869
let (return_count, br_destination) = {
3870
let frame = &mut stack.control_stack[i];
3871
// The values returned by the branch are still available for the reachable
3872
// code that comes after it
3873
frame.set_branched_to_exit();
3874
let return_count = if frame.is_loop() {
3875
frame.num_param_values()
3876
} else {
3877
frame.num_return_values()
3878
};
3879
(return_count, frame.br_destination())
3880
};
3881
let inputs = stack.peekn_mut(return_count);
3882
(br_destination, inputs)
3883
}
3884
3885
/// Determine the returned value type of a WebAssembly operator
3886
fn type_of(operator: &Operator) -> Type {
3887
match operator {
3888
Operator::V128Load { .. }
3889
| Operator::V128Store { .. }
3890
| Operator::V128Const { .. }
3891
| Operator::V128Not
3892
| Operator::V128And
3893
| Operator::V128AndNot
3894
| Operator::V128Or
3895
| Operator::V128Xor
3896
| Operator::V128AnyTrue
3897
| Operator::V128Bitselect => I8X16, // default type representing V128
3898
3899
Operator::I8x16Shuffle { .. }
3900
| Operator::I8x16Splat
3901
| Operator::V128Load8Splat { .. }
3902
| Operator::V128Load8Lane { .. }
3903
| Operator::V128Store8Lane { .. }
3904
| Operator::I8x16ExtractLaneS { .. }
3905
| Operator::I8x16ExtractLaneU { .. }
3906
| Operator::I8x16ReplaceLane { .. }
3907
| Operator::I8x16Eq
3908
| Operator::I8x16Ne
3909
| Operator::I8x16LtS
3910
| Operator::I8x16LtU
3911
| Operator::I8x16GtS
3912
| Operator::I8x16GtU
3913
| Operator::I8x16LeS
3914
| Operator::I8x16LeU
3915
| Operator::I8x16GeS
3916
| Operator::I8x16GeU
3917
| Operator::I8x16Neg
3918
| Operator::I8x16Abs
3919
| Operator::I8x16AllTrue
3920
| Operator::I8x16Shl
3921
| Operator::I8x16ShrS
3922
| Operator::I8x16ShrU
3923
| Operator::I8x16Add
3924
| Operator::I8x16AddSatS
3925
| Operator::I8x16AddSatU
3926
| Operator::I8x16Sub
3927
| Operator::I8x16SubSatS
3928
| Operator::I8x16SubSatU
3929
| Operator::I8x16MinS
3930
| Operator::I8x16MinU
3931
| Operator::I8x16MaxS
3932
| Operator::I8x16MaxU
3933
| Operator::I8x16AvgrU
3934
| Operator::I8x16Bitmask
3935
| Operator::I8x16Popcnt
3936
| Operator::I8x16RelaxedLaneselect => I8X16,
3937
3938
Operator::I16x8Splat
3939
| Operator::V128Load16Splat { .. }
3940
| Operator::V128Load16Lane { .. }
3941
| Operator::V128Store16Lane { .. }
3942
| Operator::I16x8ExtractLaneS { .. }
3943
| Operator::I16x8ExtractLaneU { .. }
3944
| Operator::I16x8ReplaceLane { .. }
3945
| Operator::I16x8Eq
3946
| Operator::I16x8Ne
3947
| Operator::I16x8LtS
3948
| Operator::I16x8LtU
3949
| Operator::I16x8GtS
3950
| Operator::I16x8GtU
3951
| Operator::I16x8LeS
3952
| Operator::I16x8LeU
3953
| Operator::I16x8GeS
3954
| Operator::I16x8GeU
3955
| Operator::I16x8Neg
3956
| Operator::I16x8Abs
3957
| Operator::I16x8AllTrue
3958
| Operator::I16x8Shl
3959
| Operator::I16x8ShrS
3960
| Operator::I16x8ShrU
3961
| Operator::I16x8Add
3962
| Operator::I16x8AddSatS
3963
| Operator::I16x8AddSatU
3964
| Operator::I16x8Sub
3965
| Operator::I16x8SubSatS
3966
| Operator::I16x8SubSatU
3967
| Operator::I16x8MinS
3968
| Operator::I16x8MinU
3969
| Operator::I16x8MaxS
3970
| Operator::I16x8MaxU
3971
| Operator::I16x8AvgrU
3972
| Operator::I16x8Mul
3973
| Operator::I16x8Bitmask
3974
| Operator::I16x8RelaxedLaneselect => I16X8,
3975
3976
Operator::I32x4Splat
3977
| Operator::V128Load32Splat { .. }
3978
| Operator::V128Load32Lane { .. }
3979
| Operator::V128Store32Lane { .. }
3980
| Operator::I32x4ExtractLane { .. }
3981
| Operator::I32x4ReplaceLane { .. }
3982
| Operator::I32x4Eq
3983
| Operator::I32x4Ne
3984
| Operator::I32x4LtS
3985
| Operator::I32x4LtU
3986
| Operator::I32x4GtS
3987
| Operator::I32x4GtU
3988
| Operator::I32x4LeS
3989
| Operator::I32x4LeU
3990
| Operator::I32x4GeS
3991
| Operator::I32x4GeU
3992
| Operator::I32x4Neg
3993
| Operator::I32x4Abs
3994
| Operator::I32x4AllTrue
3995
| Operator::I32x4Shl
3996
| Operator::I32x4ShrS
3997
| Operator::I32x4ShrU
3998
| Operator::I32x4Add
3999
| Operator::I32x4Sub
4000
| Operator::I32x4Mul
4001
| Operator::I32x4MinS
4002
| Operator::I32x4MinU
4003
| Operator::I32x4MaxS
4004
| Operator::I32x4MaxU
4005
| Operator::I32x4Bitmask
4006
| Operator::I32x4TruncSatF32x4S
4007
| Operator::I32x4TruncSatF32x4U
4008
| Operator::I32x4RelaxedLaneselect
4009
| Operator::V128Load32Zero { .. } => I32X4,
4010
4011
Operator::I64x2Splat
4012
| Operator::V128Load64Splat { .. }
4013
| Operator::V128Load64Lane { .. }
4014
| Operator::V128Store64Lane { .. }
4015
| Operator::I64x2ExtractLane { .. }
4016
| Operator::I64x2ReplaceLane { .. }
4017
| Operator::I64x2Eq
4018
| Operator::I64x2Ne
4019
| Operator::I64x2LtS
4020
| Operator::I64x2GtS
4021
| Operator::I64x2LeS
4022
| Operator::I64x2GeS
4023
| Operator::I64x2Neg
4024
| Operator::I64x2Abs
4025
| Operator::I64x2AllTrue
4026
| Operator::I64x2Shl
4027
| Operator::I64x2ShrS
4028
| Operator::I64x2ShrU
4029
| Operator::I64x2Add
4030
| Operator::I64x2Sub
4031
| Operator::I64x2Mul
4032
| Operator::I64x2Bitmask
4033
| Operator::I64x2RelaxedLaneselect
4034
| Operator::V128Load64Zero { .. } => I64X2,
4035
4036
Operator::F32x4Splat
4037
| Operator::F32x4ExtractLane { .. }
4038
| Operator::F32x4ReplaceLane { .. }
4039
| Operator::F32x4Eq
4040
| Operator::F32x4Ne
4041
| Operator::F32x4Lt
4042
| Operator::F32x4Gt
4043
| Operator::F32x4Le
4044
| Operator::F32x4Ge
4045
| Operator::F32x4Abs
4046
| Operator::F32x4Neg
4047
| Operator::F32x4Sqrt
4048
| Operator::F32x4Add
4049
| Operator::F32x4Sub
4050
| Operator::F32x4Mul
4051
| Operator::F32x4Div
4052
| Operator::F32x4Min
4053
| Operator::F32x4Max
4054
| Operator::F32x4PMin
4055
| Operator::F32x4PMax
4056
| Operator::F32x4ConvertI32x4S
4057
| Operator::F32x4ConvertI32x4U
4058
| Operator::F32x4Ceil
4059
| Operator::F32x4Floor
4060
| Operator::F32x4Trunc
4061
| Operator::F32x4Nearest
4062
| Operator::F32x4RelaxedMax
4063
| Operator::F32x4RelaxedMin
4064
| Operator::F32x4RelaxedMadd
4065
| Operator::F32x4RelaxedNmadd => F32X4,
4066
4067
Operator::F64x2Splat
4068
| Operator::F64x2ExtractLane { .. }
4069
| Operator::F64x2ReplaceLane { .. }
4070
| Operator::F64x2Eq
4071
| Operator::F64x2Ne
4072
| Operator::F64x2Lt
4073
| Operator::F64x2Gt
4074
| Operator::F64x2Le
4075
| Operator::F64x2Ge
4076
| Operator::F64x2Abs
4077
| Operator::F64x2Neg
4078
| Operator::F64x2Sqrt
4079
| Operator::F64x2Add
4080
| Operator::F64x2Sub
4081
| Operator::F64x2Mul
4082
| Operator::F64x2Div
4083
| Operator::F64x2Min
4084
| Operator::F64x2Max
4085
| Operator::F64x2PMin
4086
| Operator::F64x2PMax
4087
| Operator::F64x2Ceil
4088
| Operator::F64x2Floor
4089
| Operator::F64x2Trunc
4090
| Operator::F64x2Nearest
4091
| Operator::F64x2RelaxedMax
4092
| Operator::F64x2RelaxedMin
4093
| Operator::F64x2RelaxedMadd
4094
| Operator::F64x2RelaxedNmadd => F64X2,
4095
4096
_ => unimplemented!(
4097
"Currently only SIMD instructions are mapped to their return type; the \
4098
following instruction is not mapped: {:?}",
4099
operator
4100
),
4101
}
4102
}
4103
4104
/// Some SIMD operations only operate on I8X16 in CLIF; this will convert them to that type by
4105
/// adding a bitcast if necessary.
4106
fn optionally_bitcast_vector(
4107
value: Value,
4108
needed_type: Type,
4109
builder: &mut FunctionBuilder,
4110
) -> Value {
4111
if builder.func.dfg.value_type(value) != needed_type {
4112
let mut flags = MemFlags::new();
4113
flags.set_endianness(ir::Endianness::Little);
4114
builder.ins().bitcast(needed_type, flags, value)
4115
} else {
4116
value
4117
}
4118
}
4119
4120
#[inline(always)]
4121
fn is_non_canonical_v128(ty: ir::Type) -> bool {
4122
match ty {
4123
I64X2 | I32X4 | I16X8 | F32X4 | F64X2 => true,
4124
_ => false,
4125
}
4126
}
4127
4128
/// Cast to I8X16, any vector values in `values` that are of "non-canonical" type (meaning, not
4129
/// I8X16), and return them in a slice. A pre-scan is made to determine whether any casts are
4130
/// actually necessary, and if not, the original slice is returned. Otherwise the cast values
4131
/// are returned in a slice that belongs to the caller-supplied `SmallVec`.
4132
fn canonicalise_v128_values<'a>(
4133
tmp_canonicalised: &'a mut SmallVec<[BlockArg; 16]>,
4134
builder: &mut FunctionBuilder,
4135
values: &'a [ir::Value],
4136
) -> &'a [BlockArg] {
4137
debug_assert!(tmp_canonicalised.is_empty());
4138
// Cast, and push the resulting `Value`s into `canonicalised`.
4139
for v in values {
4140
let value = if is_non_canonical_v128(builder.func.dfg.value_type(*v)) {
4141
let mut flags = MemFlags::new();
4142
flags.set_endianness(ir::Endianness::Little);
4143
builder.ins().bitcast(I8X16, flags, *v)
4144
} else {
4145
*v
4146
};
4147
tmp_canonicalised.push(BlockArg::from(value));
4148
}
4149
tmp_canonicalised.as_slice()
4150
}
4151
4152
/// Generate a `jump` instruction, but first cast all 128-bit vector values to I8X16 if they
4153
/// don't have that type. This is done in somewhat roundabout way so as to ensure that we
4154
/// almost never have to do any heap allocation.
4155
fn canonicalise_then_jump(
4156
builder: &mut FunctionBuilder,
4157
destination: ir::Block,
4158
params: &[ir::Value],
4159
) -> ir::Inst {
4160
let mut tmp_canonicalised = SmallVec::<[_; 16]>::new();
4161
let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
4162
builder.ins().jump(destination, canonicalised)
4163
}
4164
4165
/// The same but for a `brif` instruction.
4166
fn canonicalise_brif(
4167
builder: &mut FunctionBuilder,
4168
cond: ir::Value,
4169
block_then: ir::Block,
4170
params_then: &[ir::Value],
4171
block_else: ir::Block,
4172
params_else: &[ir::Value],
4173
) -> ir::Inst {
4174
let mut tmp_canonicalised_then = SmallVec::<[_; 16]>::new();
4175
let canonicalised_then =
4176
canonicalise_v128_values(&mut tmp_canonicalised_then, builder, params_then);
4177
let mut tmp_canonicalised_else = SmallVec::<[_; 16]>::new();
4178
let canonicalised_else =
4179
canonicalise_v128_values(&mut tmp_canonicalised_else, builder, params_else);
4180
builder.ins().brif(
4181
cond,
4182
block_then,
4183
canonicalised_then,
4184
block_else,
4185
canonicalised_else,
4186
)
4187
}
4188
4189
/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by
4190
/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF
4191
/// typing issues.
4192
fn pop1_with_bitcast(
4193
stack: &mut FuncTranslationStacks,
4194
needed_type: Type,
4195
builder: &mut FunctionBuilder,
4196
) -> Value {
4197
optionally_bitcast_vector(stack.pop1(), needed_type, builder)
4198
}
4199
4200
/// A helper for popping and bitcasting two values; since SIMD values can lose their type by
4201
/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF
4202
/// typing issues.
4203
fn pop2_with_bitcast(
4204
stack: &mut FuncTranslationStacks,
4205
needed_type: Type,
4206
builder: &mut FunctionBuilder,
4207
) -> (Value, Value) {
4208
let (a, b) = stack.pop2();
4209
let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
4210
let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
4211
(bitcast_a, bitcast_b)
4212
}
4213
4214
fn pop3_with_bitcast(
4215
stack: &mut FuncTranslationStacks,
4216
needed_type: Type,
4217
builder: &mut FunctionBuilder,
4218
) -> (Value, Value, Value) {
4219
let (a, b, c) = stack.pop3();
4220
let bitcast_a = optionally_bitcast_vector(a, needed_type, builder);
4221
let bitcast_b = optionally_bitcast_vector(b, needed_type, builder);
4222
let bitcast_c = optionally_bitcast_vector(c, needed_type, builder);
4223
(bitcast_a, bitcast_b, bitcast_c)
4224
}
4225
4226
fn bitcast_arguments<'a>(
4227
builder: &FunctionBuilder,
4228
arguments: &'a mut [Value],
4229
params: &[ir::AbiParam],
4230
param_predicate: impl Fn(usize) -> bool,
4231
) -> Vec<(Type, &'a mut Value)> {
4232
let filtered_param_types = params
4233
.iter()
4234
.enumerate()
4235
.filter(|(i, _)| param_predicate(*i))
4236
.map(|(_, param)| param.value_type);
4237
4238
// zip_eq, from the itertools::Itertools trait, is like Iterator::zip but panics if one
4239
// iterator ends before the other. The `param_predicate` is required to select exactly as many
4240
// elements of `params` as there are elements in `arguments`.
4241
let pairs = filtered_param_types.zip_eq(arguments.iter_mut());
4242
4243
// The arguments which need to be bitcasted are those which have some vector type but the type
4244
// expected by the parameter is not the same vector type as that of the provided argument.
4245
pairs
4246
.filter(|(param_type, _)| param_type.is_vector())
4247
.filter(|(param_type, arg)| {
4248
let arg_type = builder.func.dfg.value_type(**arg);
4249
assert!(
4250
arg_type.is_vector(),
4251
"unexpected type mismatch: expected {}, argument {} was actually of type {}",
4252
param_type,
4253
*arg,
4254
arg_type
4255
);
4256
4257
// This is the same check that would be done by `optionally_bitcast_vector`, except we
4258
// can't take a mutable borrow of the FunctionBuilder here, so we defer inserting the
4259
// bitcast instruction to the caller.
4260
arg_type != *param_type
4261
})
4262
.collect()
4263
}
4264
4265
/// A helper for bitcasting a sequence of return values for the function currently being built. If
4266
/// a value is a vector type that does not match its expected type, this will modify the value in
4267
/// place to point to the result of a `bitcast`. This conversion is necessary to translate Wasm
4268
/// code that uses `V128` as function parameters (or implicitly in block parameters) and still use
4269
/// specific CLIF types (e.g. `I32X4`) in the function body.
4270
pub fn bitcast_wasm_returns(arguments: &mut [Value], builder: &mut FunctionBuilder) {
4271
let changes = bitcast_arguments(builder, arguments, &builder.func.signature.returns, |i| {
4272
builder.func.signature.returns[i].purpose == ir::ArgumentPurpose::Normal
4273
});
4274
for (t, arg) in changes {
4275
let mut flags = MemFlags::new();
4276
flags.set_endianness(ir::Endianness::Little);
4277
*arg = builder.ins().bitcast(t, flags, *arg);
4278
}
4279
}
4280
4281
/// Like `bitcast_wasm_returns`, but for the parameters being passed to a specified callee.
4282
fn bitcast_wasm_params(
4283
environ: &mut FuncEnvironment<'_>,
4284
callee_signature: ir::SigRef,
4285
arguments: &mut [Value],
4286
builder: &mut FunctionBuilder,
4287
) {
4288
let callee_signature = &builder.func.dfg.signatures[callee_signature];
4289
let changes = bitcast_arguments(builder, arguments, &callee_signature.params, |i| {
4290
environ.is_wasm_parameter(&callee_signature, i)
4291
});
4292
for (t, arg) in changes {
4293
let mut flags = MemFlags::new();
4294
flags.set_endianness(ir::Endianness::Little);
4295
*arg = builder.ins().bitcast(t, flags, *arg);
4296
}
4297
}
4298
4299
fn create_catch_block(
4300
builder: &mut FunctionBuilder,
4301
stacks: &mut FuncTranslationStacks,
4302
catch: &wasmparser::Catch,
4303
environ: &mut FuncEnvironment<'_>,
4304
) -> WasmResult<ir::Block> {
4305
let (is_ref, tag, label) = match catch {
4306
wasmparser::Catch::One { tag, label } => (false, Some(*tag), *label),
4307
wasmparser::Catch::OneRef { tag, label } => (true, Some(*tag), *label),
4308
wasmparser::Catch::All { label } => (false, None, *label),
4309
wasmparser::Catch::AllRef { label } => (true, None, *label),
4310
};
4311
4312
// We always create a handler block with one blockparam for the
4313
// one exception payload value that we use (`exn0` block-call
4314
// argument). This one payload value is the `exnref`. Note,
4315
// however, that we carry it in a native host-pointer-sized
4316
// payload (because this is what the exception ABI in Cranelift
4317
// requires). We then generate the args for the actual branch to
4318
// the handler block: we add unboxing code to load each value in
4319
// the exception signature if a specific tag is expected (hence
4320
// signature is known), and then append the `exnref` itself if we
4321
// are compiling a `*Ref` variant.
4322
4323
let (exn_ref_ty, needs_stack_map) = environ.reference_type(WasmHeapType::Exn);
4324
let (exn_payload_wasm_ty, exn_payload_ty) = match environ.pointer_type().bits() {
4325
32 => (wasmparser::ValType::I32, I32),
4326
64 => (wasmparser::ValType::I64, I64),
4327
_ => panic!("Unsupported pointer width"),
4328
};
4329
let block = block_with_params(builder, [exn_payload_wasm_ty], environ)?;
4330
builder.switch_to_block(block);
4331
let exn_ref = builder.func.dfg.block_params(block)[0];
4332
debug_assert!(exn_ref_ty.bits() <= exn_payload_ty.bits());
4333
let exn_ref = if exn_ref_ty.bits() < exn_payload_ty.bits() {
4334
builder.ins().ireduce(exn_ref_ty, exn_ref)
4335
} else {
4336
exn_ref
4337
};
4338
4339
if needs_stack_map {
4340
builder.declare_value_needs_stack_map(exn_ref);
4341
}
4342
4343
// We encode tag indices from the module directly as Cranelift
4344
// `ExceptionTag`s. We will translate those to (instance,
4345
// defined-tag-index) pairs during the unwind walk -- necessarily
4346
// dynamic because tag imports are provided only at instantiation
4347
// time.
4348
let clif_tag = tag.map(|t| ExceptionTag::from_u32(t));
4349
4350
stacks.handlers.add_handler(clif_tag, block);
4351
4352
let mut params = vec![];
4353
4354
if let Some(tag) = tag {
4355
let tag = TagIndex::from_u32(tag);
4356
params.extend(environ.translate_exn_unbox(builder, tag, exn_ref)?);
4357
}
4358
if is_ref {
4359
params.push(exn_ref);
4360
}
4361
4362
// Generate the branch itself.
4363
let i = stacks.control_stack.len() - 1 - (label as usize);
4364
let frame = &mut stacks.control_stack[i];
4365
frame.set_branched_to_exit();
4366
canonicalise_then_jump(builder, frame.br_destination(), &params);
4367
4368
Ok(block)
4369
}
4370
4371