Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/fuzzgen/src/function_generator.rs
1692 views
1
use crate::config::Config;
2
use crate::cranelift_arbitrary::CraneliftArbitrary;
3
use crate::target_isa_extras::TargetIsaExtras;
4
use anyhow::Result;
5
use arbitrary::{Arbitrary, Unstructured};
6
use cranelift::codegen::data_value::DataValue;
7
use cranelift::codegen::ir::immediates::Offset32;
8
use cranelift::codegen::ir::instructions::{InstructionFormat, ResolvedConstraint};
9
use cranelift::codegen::ir::stackslot::StackSize;
10
11
use cranelift::codegen::ir::{
12
AliasRegion, AtomicRmwOp, Block, BlockArg, ConstantData, Endianness, ExternalName, FuncRef,
13
Function, LibCall, Opcode, SigRef, Signature, StackSlot, UserExternalName, UserFuncName, Value,
14
types::*,
15
};
16
use cranelift::codegen::isa::CallConv;
17
use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Switch, Variable};
18
use cranelift::prelude::isa::OwnedTargetIsa;
19
use cranelift::prelude::{
20
ExtFuncData, FloatCC, InstBuilder, IntCC, JumpTableData, MemFlags, StackSlotData, StackSlotKind,
21
};
22
use std::collections::HashMap;
23
use std::ops::RangeInclusive;
24
use std::str::FromStr;
25
use std::sync::LazyLock;
26
use target_lexicon::{Architecture, Triple};
27
28
type BlockSignature = Vec<Type>;
29
30
fn insert_opcode(
31
fgen: &mut FunctionGenerator,
32
builder: &mut FunctionBuilder,
33
opcode: Opcode,
34
args: &[Type],
35
rets: &[Type],
36
) -> Result<()> {
37
let mut vals = Vec::with_capacity(args.len());
38
for &arg in args.into_iter() {
39
let var = fgen.get_variable_of_type(arg)?;
40
let val = builder.use_var(var);
41
vals.push(val);
42
}
43
44
// Some opcodes require us to look at their input arguments to determine the
45
// controlling type. This is not the general case, but we can neatly check this
46
// using `requires_typevar_operand`.
47
let ctrl_type = if opcode.constraints().requires_typevar_operand() {
48
args.first()
49
} else {
50
rets.first()
51
}
52
.copied()
53
.unwrap_or(INVALID);
54
55
// Choose the appropriate instruction format for this opcode
56
let (inst, dfg) = match opcode.format() {
57
InstructionFormat::NullAry => builder.ins().NullAry(opcode, ctrl_type),
58
InstructionFormat::Unary => builder.ins().Unary(opcode, ctrl_type, vals[0]),
59
InstructionFormat::Binary => builder.ins().Binary(opcode, ctrl_type, vals[0], vals[1]),
60
InstructionFormat::Ternary => builder
61
.ins()
62
.Ternary(opcode, ctrl_type, vals[0], vals[1], vals[2]),
63
_ => unimplemented!(),
64
};
65
let results = dfg.inst_results(inst).to_vec();
66
67
for (val, &ty) in results.into_iter().zip(rets) {
68
let var = fgen.get_variable_of_type(ty)?;
69
builder.def_var(var, val);
70
}
71
Ok(())
72
}
73
74
fn insert_call_to_function(
75
fgen: &mut FunctionGenerator,
76
builder: &mut FunctionBuilder,
77
call_opcode: Opcode,
78
sig: &Signature,
79
sig_ref: SigRef,
80
func_ref: FuncRef,
81
) -> Result<()> {
82
let actuals = fgen.generate_values_for_signature(
83
builder,
84
sig.params.iter().map(|abi_param| abi_param.value_type),
85
)?;
86
87
let addr_ty = fgen.isa.pointer_type();
88
let call = match call_opcode {
89
Opcode::Call => builder.ins().call(func_ref, &actuals),
90
Opcode::ReturnCall => builder.ins().return_call(func_ref, &actuals),
91
Opcode::CallIndirect => {
92
let addr = builder.ins().func_addr(addr_ty, func_ref);
93
builder.ins().call_indirect(sig_ref, addr, &actuals)
94
}
95
Opcode::ReturnCallIndirect => {
96
let addr = builder.ins().func_addr(addr_ty, func_ref);
97
builder.ins().return_call_indirect(sig_ref, addr, &actuals)
98
}
99
_ => unreachable!(),
100
};
101
102
// Assign the return values to random variables
103
let ret_values = builder.inst_results(call).to_vec();
104
let ret_types = sig.returns.iter().map(|p| p.value_type);
105
for (ty, val) in ret_types.zip(ret_values) {
106
let var = fgen.get_variable_of_type(ty)?;
107
builder.def_var(var, val);
108
}
109
110
Ok(())
111
}
112
113
fn insert_call(
114
fgen: &mut FunctionGenerator,
115
builder: &mut FunctionBuilder,
116
opcode: Opcode,
117
_args: &[Type],
118
_rets: &[Type],
119
) -> Result<()> {
120
assert!(matches!(opcode, Opcode::Call | Opcode::CallIndirect));
121
let (sig, sig_ref, func_ref) = fgen.u.choose(&fgen.resources.func_refs)?.clone();
122
123
insert_call_to_function(fgen, builder, opcode, &sig, sig_ref, func_ref)
124
}
125
126
fn insert_stack_load(
127
fgen: &mut FunctionGenerator,
128
builder: &mut FunctionBuilder,
129
_opcode: Opcode,
130
_args: &[Type],
131
rets: &[Type],
132
) -> Result<()> {
133
let typevar = rets[0];
134
let type_size = typevar.bytes();
135
let (slot, slot_size, _align, category) = fgen.stack_slot_with_size(type_size)?;
136
137
// `stack_load` doesn't support setting MemFlags, and it does not set any
138
// alias analysis bits, so we can only emit it for `Other` slots.
139
if category != AACategory::Other {
140
return Err(arbitrary::Error::IncorrectFormat.into());
141
}
142
143
let offset = fgen.u.int_in_range(0..=(slot_size - type_size))? as i32;
144
145
let val = builder.ins().stack_load(typevar, slot, offset);
146
let var = fgen.get_variable_of_type(typevar)?;
147
builder.def_var(var, val);
148
149
Ok(())
150
}
151
152
fn insert_stack_store(
153
fgen: &mut FunctionGenerator,
154
builder: &mut FunctionBuilder,
155
_opcode: Opcode,
156
args: &[Type],
157
_rets: &[Type],
158
) -> Result<()> {
159
let typevar = args[0];
160
let type_size = typevar.bytes();
161
162
let (slot, slot_size, _align, category) = fgen.stack_slot_with_size(type_size)?;
163
164
// `stack_store` doesn't support setting MemFlags, and it does not set any
165
// alias analysis bits, so we can only emit it for `Other` slots.
166
if category != AACategory::Other {
167
return Err(arbitrary::Error::IncorrectFormat.into());
168
}
169
170
let offset = fgen.u.int_in_range(0..=(slot_size - type_size))? as i32;
171
172
let arg0 = fgen.get_variable_of_type(typevar)?;
173
let arg0 = builder.use_var(arg0);
174
175
builder.ins().stack_store(arg0, slot, offset);
176
Ok(())
177
}
178
179
fn insert_cmp(
180
fgen: &mut FunctionGenerator,
181
builder: &mut FunctionBuilder,
182
opcode: Opcode,
183
args: &[Type],
184
rets: &[Type],
185
) -> Result<()> {
186
let lhs = fgen.get_variable_of_type(args[0])?;
187
let lhs = builder.use_var(lhs);
188
189
let rhs = fgen.get_variable_of_type(args[1])?;
190
let rhs = builder.use_var(rhs);
191
192
let res = if opcode == Opcode::Fcmp {
193
let cc = *fgen.u.choose(FloatCC::all())?;
194
195
// We filter out condition codes that aren't supported by the target at
196
// this point after randomly choosing one, instead of randomly choosing a
197
// supported one, to avoid invalidating the corpus when these get implemented.
198
let unimplemented_cc = match (fgen.isa.triple().architecture, cc) {
199
// Some FloatCC's are not implemented on AArch64, see:
200
// https://github.com/bytecodealliance/wasmtime/issues/4850
201
(Architecture::Aarch64(_), FloatCC::OrderedNotEqual) => true,
202
(Architecture::Aarch64(_), FloatCC::UnorderedOrEqual) => true,
203
(Architecture::Aarch64(_), FloatCC::UnorderedOrLessThan) => true,
204
(Architecture::Aarch64(_), FloatCC::UnorderedOrLessThanOrEqual) => true,
205
(Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThan) => true,
206
(Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThanOrEqual) => true,
207
208
// These are not implemented on x86_64, for vectors.
209
(Architecture::X86_64, FloatCC::UnorderedOrEqual | FloatCC::OrderedNotEqual) => {
210
args[0].is_vector()
211
}
212
_ => false,
213
};
214
if unimplemented_cc {
215
return Err(arbitrary::Error::IncorrectFormat.into());
216
}
217
218
builder.ins().fcmp(cc, lhs, rhs)
219
} else {
220
let cc = *fgen.u.choose(IntCC::all())?;
221
builder.ins().icmp(cc, lhs, rhs)
222
};
223
224
let var = fgen.get_variable_of_type(rets[0])?;
225
builder.def_var(var, res);
226
227
Ok(())
228
}
229
230
fn insert_const(
231
fgen: &mut FunctionGenerator,
232
builder: &mut FunctionBuilder,
233
_opcode: Opcode,
234
_args: &[Type],
235
rets: &[Type],
236
) -> Result<()> {
237
let typevar = rets[0];
238
let var = fgen.get_variable_of_type(typevar)?;
239
let val = fgen.generate_const(builder, typevar)?;
240
builder.def_var(var, val);
241
Ok(())
242
}
243
244
fn insert_bitcast(
245
fgen: &mut FunctionGenerator,
246
builder: &mut FunctionBuilder,
247
args: &[Type],
248
rets: &[Type],
249
) -> Result<()> {
250
let from_var = fgen.get_variable_of_type(args[0])?;
251
let from_val = builder.use_var(from_var);
252
253
let to_var = fgen.get_variable_of_type(rets[0])?;
254
255
// TODO: We can generate little/big endian flags here.
256
let mut memflags = MemFlags::new();
257
258
// When bitcasting between vectors of different lane counts, we need to
259
// specify the endianness.
260
if args[0].lane_count() != rets[0].lane_count() {
261
memflags.set_endianness(Endianness::Little);
262
}
263
264
let res = builder.ins().bitcast(rets[0], memflags, from_val);
265
builder.def_var(to_var, res);
266
Ok(())
267
}
268
269
fn insert_load_store(
270
fgen: &mut FunctionGenerator,
271
builder: &mut FunctionBuilder,
272
opcode: Opcode,
273
args: &[Type],
274
rets: &[Type],
275
) -> Result<()> {
276
if opcode == Opcode::Bitcast {
277
return insert_bitcast(fgen, builder, args, rets);
278
}
279
280
let ctrl_type = *rets.first().or(args.first()).unwrap();
281
let type_size = ctrl_type.bytes();
282
283
let is_atomic = [Opcode::AtomicLoad, Opcode::AtomicStore].contains(&opcode);
284
let (address, flags, offset) =
285
fgen.generate_address_and_memflags(builder, type_size, is_atomic)?;
286
287
// The variable being loaded or stored into
288
let var = fgen.get_variable_of_type(ctrl_type)?;
289
290
match opcode.format() {
291
InstructionFormat::LoadNoOffset => {
292
let (inst, dfg) = builder
293
.ins()
294
.LoadNoOffset(opcode, ctrl_type, flags, address);
295
296
let new_val = dfg.first_result(inst);
297
builder.def_var(var, new_val);
298
}
299
InstructionFormat::StoreNoOffset => {
300
let val = builder.use_var(var);
301
302
builder
303
.ins()
304
.StoreNoOffset(opcode, ctrl_type, flags, val, address);
305
}
306
InstructionFormat::Store => {
307
let val = builder.use_var(var);
308
309
builder
310
.ins()
311
.Store(opcode, ctrl_type, flags, offset, val, address);
312
}
313
InstructionFormat::Load => {
314
let (inst, dfg) = builder
315
.ins()
316
.Load(opcode, ctrl_type, flags, offset, address);
317
318
let new_val = dfg.first_result(inst);
319
builder.def_var(var, new_val);
320
}
321
_ => unimplemented!(),
322
}
323
324
Ok(())
325
}
326
327
fn insert_atomic_rmw(
328
fgen: &mut FunctionGenerator,
329
builder: &mut FunctionBuilder,
330
_: Opcode,
331
_: &[Type],
332
rets: &[Type],
333
) -> Result<()> {
334
let ctrl_type = *rets.first().unwrap();
335
let type_size = ctrl_type.bytes();
336
337
let rmw_op = *fgen.u.choose(AtomicRmwOp::all())?;
338
339
let (address, flags, offset) = fgen.generate_address_and_memflags(builder, type_size, true)?;
340
341
// AtomicRMW does not directly support offsets, so add the offset to the address separately.
342
let address = builder.ins().iadd_imm(address, i64::from(offset));
343
344
// Load and store target variables
345
let source_var = fgen.get_variable_of_type(ctrl_type)?;
346
let target_var = fgen.get_variable_of_type(ctrl_type)?;
347
348
let source_val = builder.use_var(source_var);
349
let new_val = builder
350
.ins()
351
.atomic_rmw(ctrl_type, flags, rmw_op, address, source_val);
352
353
builder.def_var(target_var, new_val);
354
Ok(())
355
}
356
357
fn insert_atomic_cas(
358
fgen: &mut FunctionGenerator,
359
builder: &mut FunctionBuilder,
360
_: Opcode,
361
_: &[Type],
362
rets: &[Type],
363
) -> Result<()> {
364
let ctrl_type = *rets.first().unwrap();
365
let type_size = ctrl_type.bytes();
366
367
let (address, flags, offset) = fgen.generate_address_and_memflags(builder, type_size, true)?;
368
369
// AtomicCas does not directly support offsets, so add the offset to the address separately.
370
let address = builder.ins().iadd_imm(address, i64::from(offset));
371
372
// Source and Target variables
373
let expected_var = fgen.get_variable_of_type(ctrl_type)?;
374
let store_var = fgen.get_variable_of_type(ctrl_type)?;
375
let loaded_var = fgen.get_variable_of_type(ctrl_type)?;
376
377
let expected_val = builder.use_var(expected_var);
378
let store_val = builder.use_var(store_var);
379
let new_val = builder
380
.ins()
381
.atomic_cas(flags, address, expected_val, store_val);
382
383
builder.def_var(loaded_var, new_val);
384
Ok(())
385
}
386
387
fn insert_shuffle(
388
fgen: &mut FunctionGenerator,
389
builder: &mut FunctionBuilder,
390
opcode: Opcode,
391
_: &[Type],
392
rets: &[Type],
393
) -> Result<()> {
394
let ctrl_type = *rets.first().unwrap();
395
396
let lhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?);
397
let rhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?);
398
399
let mask = {
400
let mut lanes = [0u8; 16];
401
for lane in lanes.iter_mut() {
402
*lane = fgen.u.int_in_range(0..=31)?;
403
}
404
let lanes = ConstantData::from(lanes.as_ref());
405
builder.func.dfg.immediates.push(lanes)
406
};
407
408
// This function is called for any `InstructionFormat::Shuffle`. Which today is just
409
// `shuffle`, but lets assert that, just to be sure we don't accidentally insert
410
// something else.
411
assert_eq!(opcode, Opcode::Shuffle);
412
let res = builder.ins().shuffle(lhs, rhs, mask);
413
414
let target_var = fgen.get_variable_of_type(ctrl_type)?;
415
builder.def_var(target_var, res);
416
417
Ok(())
418
}
419
420
fn insert_ins_ext_lane(
421
fgen: &mut FunctionGenerator,
422
builder: &mut FunctionBuilder,
423
opcode: Opcode,
424
args: &[Type],
425
rets: &[Type],
426
) -> Result<()> {
427
let vector_type = *args.first().unwrap();
428
let ret_type = *rets.first().unwrap();
429
430
let lhs = builder.use_var(fgen.get_variable_of_type(vector_type)?);
431
let max_lane = (vector_type.lane_count() as u8) - 1;
432
let lane = fgen.u.int_in_range(0..=max_lane)?;
433
434
let res = match opcode {
435
Opcode::Insertlane => {
436
let rhs = builder.use_var(fgen.get_variable_of_type(args[1])?);
437
builder.ins().insertlane(lhs, rhs, lane)
438
}
439
Opcode::Extractlane => builder.ins().extractlane(lhs, lane),
440
_ => todo!(),
441
};
442
443
let target_var = fgen.get_variable_of_type(ret_type)?;
444
builder.def_var(target_var, res);
445
446
Ok(())
447
}
448
449
type OpcodeInserter = fn(
450
fgen: &mut FunctionGenerator,
451
builder: &mut FunctionBuilder,
452
Opcode,
453
&[Type],
454
&[Type],
455
) -> Result<()>;
456
457
macro_rules! exceptions {
458
($op:expr, $args:expr, $rets:expr, $(($($cases:pat),*)),* $(,)?) => {
459
match ($op, $args, $rets) {
460
$( ($($cases,)* ..) => return false, )*
461
_ => true,
462
}
463
}
464
}
465
466
/// Returns true if we believe this `OpcodeSignature` should compile correctly
467
/// for the given target triple. We currently have a range of known issues
468
/// with specific lowerings on specific backends, and we don't want to get
469
/// fuzz bug reports for those. Over time our goal is to eliminate all of these
470
/// exceptions.
471
fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -> bool {
472
// Rule out invalid combinations that we don't yet have a good way of rejecting with the
473
// instruction DSL type constraints.
474
match op {
475
Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => {
476
assert_eq!(args.len(), 1);
477
assert_eq!(rets.len(), 1);
478
479
let arg = args[0];
480
let ret = rets[0];
481
482
// Vector arguments must produce vector results, and scalar arguments must produce
483
// scalar results.
484
if arg.is_vector() != ret.is_vector() {
485
return false;
486
}
487
488
if arg.is_vector() && ret.is_vector() {
489
// Vector conversions must have the same number of lanes, and the lanes must be the
490
// same bit-width.
491
if arg.lane_count() != ret.lane_count() {
492
return false;
493
}
494
495
if arg.lane_of().bits() != ret.lane_of().bits() {
496
return false;
497
}
498
}
499
}
500
501
Opcode::Bitcast => {
502
assert_eq!(args.len(), 1);
503
assert_eq!(rets.len(), 1);
504
505
let arg = args[0];
506
let ret = rets[0];
507
508
// The opcode generator still allows bitcasts between different sized types, but these
509
// are rejected in the verifier.
510
if arg.bits() != ret.bits() {
511
return false;
512
}
513
}
514
515
// This requires precise runtime integration so it's not supported at
516
// all in fuzzgen just yet.
517
Opcode::StackSwitch => return false,
518
519
_ => {}
520
}
521
522
match triple.architecture {
523
Architecture::X86_64 => {
524
exceptions!(
525
op,
526
args,
527
rets,
528
(Opcode::UmulOverflow | Opcode::SmulOverflow, &[I128, I128]),
529
(Opcode::Imul, &[I8X16, I8X16]),
530
// https://github.com/bytecodealliance/wasmtime/issues/4756
531
(Opcode::Udiv | Opcode::Sdiv, &[I128, I128]),
532
// https://github.com/bytecodealliance/wasmtime/issues/5474
533
(Opcode::Urem | Opcode::Srem, &[I128, I128]),
534
// https://github.com/bytecodealliance/wasmtime/issues/3370
535
(
536
Opcode::Smin | Opcode::Umin | Opcode::Smax | Opcode::Umax,
537
&[I128, I128]
538
),
539
// https://github.com/bytecodealliance/wasmtime/issues/5107
540
(Opcode::Cls, &[I8], &[I8]),
541
(Opcode::Cls, &[I16], &[I16]),
542
(Opcode::Cls, &[I32], &[I32]),
543
(Opcode::Cls, &[I64], &[I64]),
544
(Opcode::Cls, &[I128], &[I128]),
545
// TODO
546
(Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
547
// https://github.com/bytecodealliance/wasmtime/issues/4897
548
// https://github.com/bytecodealliance/wasmtime/issues/4899
549
(
550
Opcode::FcvtToUint
551
| Opcode::FcvtToUintSat
552
| Opcode::FcvtToSint
553
| Opcode::FcvtToSintSat,
554
&[F32 | F64],
555
&[I8 | I16 | I128]
556
),
557
(Opcode::FcvtToUint | Opcode::FcvtToSint, &[F32X4], &[I32X4]),
558
(
559
Opcode::FcvtToUint
560
| Opcode::FcvtToUintSat
561
| Opcode::FcvtToSint
562
| Opcode::FcvtToSintSat,
563
&[F64X2],
564
&[I64X2]
565
),
566
// https://github.com/bytecodealliance/wasmtime/issues/4900
567
(Opcode::FcvtFromUint, &[I128], &[F32 | F64]),
568
// This has a lowering, but only when preceded by `uwiden_low`.
569
(Opcode::FcvtFromUint, &[I64X2], &[F64X2]),
570
// https://github.com/bytecodealliance/wasmtime/issues/4900
571
(Opcode::FcvtFromSint, &[I128], &[F32 | F64]),
572
(Opcode::FcvtFromSint, &[I64X2], &[F64X2]),
573
(
574
Opcode::Umulhi | Opcode::Smulhi,
575
&([I8X16, I8X16] | [I16X8, I16X8] | [I32X4, I32X4] | [I64X2, I64X2])
576
),
577
(
578
Opcode::UaddSat | Opcode::SaddSat | Opcode::UsubSat | Opcode::SsubSat,
579
&([I32X4, I32X4] | [I64X2, I64X2])
580
),
581
(Opcode::Fcopysign, &([F32X4, F32X4] | [F64X2, F64X2])),
582
(Opcode::Popcnt, &([I8X16] | [I16X8] | [I32X4] | [I64X2])),
583
(
584
Opcode::Umax | Opcode::Smax | Opcode::Umin | Opcode::Smin,
585
&[I64X2, I64X2]
586
),
587
// https://github.com/bytecodealliance/wasmtime/issues/6104
588
(Opcode::Bitcast, &[I128], &[_]),
589
(Opcode::Bitcast, &[_], &[I128]),
590
(Opcode::Uunarrow),
591
(Opcode::Snarrow | Opcode::Unarrow, &[I64X2, I64X2]),
592
(Opcode::SqmulRoundSat, &[I32X4, I32X4]),
593
// This Icmp is not implemented: #5529
594
(Opcode::Icmp, &[I64X2, I64X2]),
595
// IaddPairwise is implemented, but only for some types, and with some preceding ops.
596
(Opcode::IaddPairwise),
597
// Nothing wrong with this select. But we have an isle rule that can optimize it
598
// into a `min`/`max` instructions, which we don't have implemented yet.
599
(Opcode::Select, &[_, I128, I128]),
600
// These stack accesses can cause segfaults if they are merged into an SSE instruction.
601
// See: #5922
602
(
603
Opcode::StackStore,
604
&[I8X16 | I16X8 | I32X4 | I64X2 | F32X4 | F64X2]
605
),
606
(
607
Opcode::StackLoad,
608
&[],
609
&[I8X16 | I16X8 | I32X4 | I64X2 | F32X4 | F64X2]
610
),
611
// TODO
612
(
613
Opcode::Sshr | Opcode::Ushr | Opcode::Ishl,
614
&[I8X16 | I16X8 | I32X4 | I64X2, I128]
615
),
616
(
617
Opcode::Rotr | Opcode::Rotl,
618
&[I8X16 | I16X8 | I32X4 | I64X2, _]
619
),
620
)
621
}
622
623
Architecture::Aarch64(_) => {
624
exceptions!(
625
op,
626
args,
627
rets,
628
(Opcode::UmulOverflow | Opcode::SmulOverflow, &[I128, I128]),
629
// https://github.com/bytecodealliance/wasmtime/issues/4864
630
(Opcode::Udiv | Opcode::Sdiv, &[I128, I128]),
631
// https://github.com/bytecodealliance/wasmtime/issues/5472
632
(Opcode::Urem | Opcode::Srem, &[I128, I128]),
633
// https://github.com/bytecodealliance/wasmtime/issues/4313
634
(
635
Opcode::Smin | Opcode::Umin | Opcode::Smax | Opcode::Umax,
636
&[I128, I128]
637
),
638
// https://github.com/bytecodealliance/wasmtime/issues/4870
639
(Opcode::Bnot, &[F32 | F64]),
640
(
641
Opcode::Band
642
| Opcode::Bor
643
| Opcode::Bxor
644
| Opcode::BandNot
645
| Opcode::BorNot
646
| Opcode::BxorNot,
647
&([F32, F32] | [F64, F64])
648
),
649
// https://github.com/bytecodealliance/wasmtime/issues/5198
650
(Opcode::Bitselect, &[I128, I128, I128]),
651
// https://github.com/bytecodealliance/wasmtime/issues/4934
652
(
653
Opcode::FcvtToUint
654
| Opcode::FcvtToUintSat
655
| Opcode::FcvtToSint
656
| Opcode::FcvtToSintSat,
657
&[F32 | F64],
658
&[I128]
659
),
660
// https://github.com/bytecodealliance/wasmtime/issues/4933
661
(
662
Opcode::FcvtFromUint | Opcode::FcvtFromSint,
663
&[I128],
664
&[F32 | F64]
665
),
666
(
667
Opcode::Umulhi | Opcode::Smulhi,
668
&([I8X16, I8X16] | [I16X8, I16X8] | [I32X4, I32X4] | [I64X2, I64X2])
669
),
670
(Opcode::Popcnt, &[I16X8 | I32X4 | I64X2]),
671
// Nothing wrong with this select. But we have an isle rule that can optimize it
672
// into a `min`/`max` instructions, which we don't have implemented yet.
673
(Opcode::Select, &[I8, I128, I128]),
674
// https://github.com/bytecodealliance/wasmtime/issues/6104
675
(Opcode::Bitcast, &[I128], &[_]),
676
(Opcode::Bitcast, &[_], &[I128]),
677
// TODO
678
(
679
Opcode::Sshr | Opcode::Ushr | Opcode::Ishl,
680
&[I8X16 | I16X8 | I32X4 | I64X2, I128]
681
),
682
(
683
Opcode::Rotr | Opcode::Rotl,
684
&[I8X16 | I16X8 | I32X4 | I64X2, _]
685
),
686
// TODO
687
(Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
688
(Opcode::VhighBits, &[F32X4 | F64X2]),
689
)
690
}
691
692
Architecture::S390x => {
693
exceptions!(
694
op,
695
args,
696
rets,
697
(Opcode::UaddOverflow | Opcode::SaddOverflow),
698
(Opcode::UsubOverflow | Opcode::SsubOverflow),
699
(Opcode::UmulOverflow | Opcode::SmulOverflow),
700
(
701
Opcode::Udiv | Opcode::Sdiv | Opcode::Urem | Opcode::Srem,
702
&[I128, I128]
703
),
704
(Opcode::Bnot, &[F32 | F64]),
705
(
706
Opcode::Band
707
| Opcode::Bor
708
| Opcode::Bxor
709
| Opcode::BandNot
710
| Opcode::BorNot
711
| Opcode::BxorNot,
712
&([F32, F32] | [F64, F64])
713
),
714
(
715
Opcode::FcvtToUint
716
| Opcode::FcvtToUintSat
717
| Opcode::FcvtToSint
718
| Opcode::FcvtToSintSat,
719
&[F32 | F64],
720
&[I128]
721
),
722
(
723
Opcode::FcvtFromUint | Opcode::FcvtFromSint,
724
&[I128],
725
&[F32 | F64]
726
),
727
(Opcode::SsubSat | Opcode::SaddSat, &[I64X2, I64X2]),
728
// https://github.com/bytecodealliance/wasmtime/issues/6104
729
(Opcode::Bitcast, &[I128], &[_]),
730
(Opcode::Bitcast, &[_], &[I128]),
731
// TODO
732
(Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
733
)
734
}
735
736
Architecture::Riscv64(_) => {
737
exceptions!(
738
op,
739
args,
740
rets,
741
// TODO
742
(Opcode::UaddOverflow | Opcode::SaddOverflow),
743
(Opcode::UsubOverflow | Opcode::SsubOverflow),
744
(Opcode::UmulOverflow | Opcode::SmulOverflow),
745
// TODO
746
(
747
Opcode::Udiv | Opcode::Sdiv | Opcode::Urem | Opcode::Srem,
748
&[I128, I128]
749
),
750
// TODO
751
(Opcode::Iabs, &[I128]),
752
// TODO
753
(Opcode::Bitselect, &[I128, I128, I128]),
754
// https://github.com/bytecodealliance/wasmtime/issues/5528
755
(
756
Opcode::FcvtToUint | Opcode::FcvtToSint,
757
[F32 | F64],
758
&[I128]
759
),
760
(
761
Opcode::FcvtToUintSat | Opcode::FcvtToSintSat,
762
&[F32 | F64],
763
&[I128]
764
),
765
// https://github.com/bytecodealliance/wasmtime/issues/5528
766
(
767
Opcode::FcvtFromUint | Opcode::FcvtFromSint,
768
&[I128],
769
&[F32 | F64]
770
),
771
// TODO
772
(
773
Opcode::SelectSpectreGuard,
774
&[_, _, _],
775
&[F32 | F64 | I8X16 | I16X8 | I32X4 | I64X2 | F64X2 | F32X4]
776
),
777
// TODO
778
(Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
779
(
780
Opcode::Rotr | Opcode::Rotl,
781
&[I8X16 | I16X8 | I32X4 | I64X2, _]
782
),
783
)
784
}
785
786
_ => true,
787
}
788
}
789
790
type OpcodeSignature = (Opcode, Vec<Type>, Vec<Type>);
791
792
static OPCODE_SIGNATURES: LazyLock<Vec<OpcodeSignature>> = LazyLock::new(|| {
793
let types = &[
794
I8, I16, I32, I64, I128, // Scalar Integers
795
F32, F64, // Scalar Floats
796
I8X16, I16X8, I32X4, I64X2, // SIMD Integers
797
F32X4, F64X2, // SIMD Floats
798
];
799
800
// When this env variable is passed, we only generate instructions for the opcodes listed in
801
// the comma-separated list. This is useful for debugging, as it allows us to focus on a few
802
// specific opcodes.
803
let allowed_opcodes = std::env::var("FUZZGEN_ALLOWED_OPS").ok().map(|s| {
804
s.split(',')
805
.map(|s| s.trim())
806
.filter(|s| !s.is_empty())
807
.map(|s| Opcode::from_str(s).expect("Unrecoginzed opcode"))
808
.collect::<Vec<_>>()
809
});
810
811
Opcode::all()
812
.iter()
813
.filter(|op| {
814
match op {
815
// Control flow opcodes should not be generated through `generate_instructions`.
816
Opcode::BrTable
817
| Opcode::Brif
818
| Opcode::Jump
819
| Opcode::Return
820
| Opcode::ReturnCall
821
| Opcode::ReturnCallIndirect
822
| Opcode::TryCall
823
| Opcode::TryCallIndirect => false,
824
825
// Constants are generated outside of `generate_instructions`
826
Opcode::Iconst => false,
827
828
// TODO: extract_vector raises exceptions during return type generation because it
829
// uses dynamic vectors.
830
Opcode::ExtractVector => false,
831
832
_ => true,
833
}
834
})
835
.flat_map(|op| {
836
let constraints = op.constraints();
837
838
let ctrl_types = if let Some(ctrls) = constraints.ctrl_typeset() {
839
Vec::from_iter(types.iter().copied().filter(|ty| ctrls.contains(*ty)))
840
} else {
841
vec![INVALID]
842
};
843
844
ctrl_types.into_iter().flat_map(move |ctrl_type| {
845
let rets = Vec::from_iter(
846
(0..constraints.num_fixed_results())
847
.map(|i| constraints.result_type(i, ctrl_type)),
848
);
849
850
// Cols is a vector whose length will match `num_fixed_value_arguments`, and whose
851
// elements will be vectors of types that are valid for that fixed argument
852
// position.
853
let mut cols = vec![];
854
855
for i in 0..constraints.num_fixed_value_arguments() {
856
match constraints.value_argument_constraint(i, ctrl_type) {
857
ResolvedConstraint::Bound(ty) => cols.push(Vec::from([ty])),
858
ResolvedConstraint::Free(tys) => cols.push(Vec::from_iter(
859
types.iter().copied().filter(|ty| tys.contains(*ty)),
860
)),
861
}
862
}
863
864
// Generate the cartesian product of cols to produce a vector of argument lists,
865
// argss. The argss vector is seeded with the empty argument list, so there's an
866
// initial value to be extended in the loop below.
867
let mut argss = vec![vec![]];
868
let mut cols = cols.as_slice();
869
while let Some((col, rest)) = cols.split_last() {
870
cols = rest;
871
872
let mut next = vec![];
873
for current in argss.iter() {
874
// Extend the front of each argument candidate with every type in `col`.
875
for ty in col {
876
let mut args = vec![*ty];
877
args.extend_from_slice(&current);
878
next.push(args);
879
}
880
}
881
882
let _ = std::mem::replace(&mut argss, next);
883
}
884
885
argss.into_iter().map(move |args| (*op, args, rets.clone()))
886
})
887
})
888
.filter(|(op, args, rets)| {
889
// These op/signature combinations need to be vetted
890
exceptions!(
891
op,
892
args.as_slice(),
893
rets.as_slice(),
894
(Opcode::Debugtrap),
895
(Opcode::Trap),
896
(Opcode::Trapz),
897
(Opcode::Trapnz),
898
(Opcode::CallIndirect, &[I32]),
899
(Opcode::FuncAddr),
900
(Opcode::X86Pshufb),
901
(Opcode::AvgRound),
902
(Opcode::Uload8x8),
903
(Opcode::Sload8x8),
904
(Opcode::Uload16x4),
905
(Opcode::Sload16x4),
906
(Opcode::Uload32x2),
907
(Opcode::Sload32x2),
908
(Opcode::StackAddr),
909
(Opcode::DynamicStackLoad),
910
(Opcode::DynamicStackStore),
911
(Opcode::DynamicStackAddr),
912
(Opcode::GlobalValue),
913
(Opcode::SymbolValue),
914
(Opcode::TlsValue),
915
(Opcode::GetPinnedReg),
916
(Opcode::SetPinnedReg),
917
(Opcode::GetFramePointer),
918
(Opcode::GetStackPointer),
919
(Opcode::GetReturnAddress),
920
(Opcode::X86Blendv),
921
(Opcode::IcmpImm),
922
(Opcode::X86Pmulhrsw),
923
(Opcode::IaddImm),
924
(Opcode::ImulImm),
925
(Opcode::UdivImm),
926
(Opcode::SdivImm),
927
(Opcode::UremImm),
928
(Opcode::SremImm),
929
(Opcode::IrsubImm),
930
(Opcode::UaddOverflowCin),
931
(Opcode::SaddOverflowCin),
932
(Opcode::UaddOverflowTrap),
933
(Opcode::UsubOverflowBin),
934
(Opcode::SsubOverflowBin),
935
(Opcode::BandImm),
936
(Opcode::BorImm),
937
(Opcode::BxorImm),
938
(Opcode::RotlImm),
939
(Opcode::RotrImm),
940
(Opcode::IshlImm),
941
(Opcode::UshrImm),
942
(Opcode::SshrImm),
943
(Opcode::ScalarToVector),
944
(Opcode::X86Pmaddubsw),
945
(Opcode::X86Cvtt2dq),
946
(Opcode::Umulhi, &[I128, I128], &[I128]),
947
(Opcode::Smulhi, &[I128, I128], &[I128]),
948
// https://github.com/bytecodealliance/wasmtime/issues/6073
949
(Opcode::Iconcat, &[I32, I32], &[I64]),
950
(Opcode::Iconcat, &[I16, I16], &[I32]),
951
(Opcode::Iconcat, &[I8, I8], &[I16]),
952
// https://github.com/bytecodealliance/wasmtime/issues/6073
953
(Opcode::Isplit, &[I64], &[I32, I32]),
954
(Opcode::Isplit, &[I32], &[I16, I16]),
955
(Opcode::Isplit, &[I16], &[I8, I8]),
956
(Opcode::Fmin, &[F32X4, F32X4], &[F32X4]),
957
(Opcode::Fmin, &[F64X2, F64X2], &[F64X2]),
958
(Opcode::Fmax, &[F32X4, F32X4], &[F32X4]),
959
(Opcode::Fmax, &[F64X2, F64X2], &[F64X2]),
960
(Opcode::FcvtToUintSat, &[F32X4], &[I8]),
961
(Opcode::FcvtToUintSat, &[F64X2], &[I8]),
962
(Opcode::FcvtToUintSat, &[F32X4], &[I16]),
963
(Opcode::FcvtToUintSat, &[F64X2], &[I16]),
964
(Opcode::FcvtToUintSat, &[F32X4], &[I32]),
965
(Opcode::FcvtToUintSat, &[F64X2], &[I32]),
966
(Opcode::FcvtToUintSat, &[F32X4], &[I64]),
967
(Opcode::FcvtToUintSat, &[F64X2], &[I64]),
968
(Opcode::FcvtToUintSat, &[F32X4], &[I128]),
969
(Opcode::FcvtToUintSat, &[F64X2], &[I128]),
970
(Opcode::FcvtToUintSat, &[F32], &[I8X16]),
971
(Opcode::FcvtToUintSat, &[F64], &[I8X16]),
972
(Opcode::FcvtToUintSat, &[F32X4], &[I8X16]),
973
(Opcode::FcvtToUintSat, &[F64X2], &[I8X16]),
974
(Opcode::FcvtToUintSat, &[F32], &[I16X8]),
975
(Opcode::FcvtToUintSat, &[F64], &[I16X8]),
976
(Opcode::FcvtToUintSat, &[F32X4], &[I16X8]),
977
(Opcode::FcvtToUintSat, &[F64X2], &[I16X8]),
978
(Opcode::FcvtToUintSat, &[F32], &[I32X4]),
979
(Opcode::FcvtToUintSat, &[F64], &[I32X4]),
980
(Opcode::FcvtToUintSat, &[F64X2], &[I32X4]),
981
(Opcode::FcvtToUintSat, &[F32], &[I64X2]),
982
(Opcode::FcvtToUintSat, &[F64], &[I64X2]),
983
(Opcode::FcvtToUintSat, &[F32X4], &[I64X2]),
984
(Opcode::FcvtToSintSat, &[F32X4], &[I8]),
985
(Opcode::FcvtToSintSat, &[F64X2], &[I8]),
986
(Opcode::FcvtToSintSat, &[F32X4], &[I16]),
987
(Opcode::FcvtToSintSat, &[F64X2], &[I16]),
988
(Opcode::FcvtToSintSat, &[F32X4], &[I32]),
989
(Opcode::FcvtToSintSat, &[F64X2], &[I32]),
990
(Opcode::FcvtToSintSat, &[F32X4], &[I64]),
991
(Opcode::FcvtToSintSat, &[F64X2], &[I64]),
992
(Opcode::FcvtToSintSat, &[F32X4], &[I128]),
993
(Opcode::FcvtToSintSat, &[F64X2], &[I128]),
994
(Opcode::FcvtToSintSat, &[F32], &[I8X16]),
995
(Opcode::FcvtToSintSat, &[F64], &[I8X16]),
996
(Opcode::FcvtToSintSat, &[F32X4], &[I8X16]),
997
(Opcode::FcvtToSintSat, &[F64X2], &[I8X16]),
998
(Opcode::FcvtToSintSat, &[F32], &[I16X8]),
999
(Opcode::FcvtToSintSat, &[F64], &[I16X8]),
1000
(Opcode::FcvtToSintSat, &[F32X4], &[I16X8]),
1001
(Opcode::FcvtToSintSat, &[F64X2], &[I16X8]),
1002
(Opcode::FcvtToSintSat, &[F32], &[I32X4]),
1003
(Opcode::FcvtToSintSat, &[F64], &[I32X4]),
1004
(Opcode::FcvtToSintSat, &[F64X2], &[I32X4]),
1005
(Opcode::FcvtToSintSat, &[F32], &[I64X2]),
1006
(Opcode::FcvtToSintSat, &[F64], &[I64X2]),
1007
(Opcode::FcvtToSintSat, &[F32X4], &[I64X2]),
1008
(Opcode::FcvtFromUint, &[I8X16], &[F32]),
1009
(Opcode::FcvtFromUint, &[I16X8], &[F32]),
1010
(Opcode::FcvtFromUint, &[I32X4], &[F32]),
1011
(Opcode::FcvtFromUint, &[I64X2], &[F32]),
1012
(Opcode::FcvtFromUint, &[I8X16], &[F64]),
1013
(Opcode::FcvtFromUint, &[I16X8], &[F64]),
1014
(Opcode::FcvtFromUint, &[I32X4], &[F64]),
1015
(Opcode::FcvtFromUint, &[I64X2], &[F64]),
1016
(Opcode::FcvtFromUint, &[I8], &[F32X4]),
1017
(Opcode::FcvtFromUint, &[I16], &[F32X4]),
1018
(Opcode::FcvtFromUint, &[I32], &[F32X4]),
1019
(Opcode::FcvtFromUint, &[I64], &[F32X4]),
1020
(Opcode::FcvtFromUint, &[I128], &[F32X4]),
1021
(Opcode::FcvtFromUint, &[I8X16], &[F32X4]),
1022
(Opcode::FcvtFromUint, &[I16X8], &[F32X4]),
1023
(Opcode::FcvtFromUint, &[I64X2], &[F32X4]),
1024
(Opcode::FcvtFromUint, &[I8], &[F64X2]),
1025
(Opcode::FcvtFromUint, &[I16], &[F64X2]),
1026
(Opcode::FcvtFromUint, &[I32], &[F64X2]),
1027
(Opcode::FcvtFromUint, &[I64], &[F64X2]),
1028
(Opcode::FcvtFromUint, &[I128], &[F64X2]),
1029
(Opcode::FcvtFromUint, &[I8X16], &[F64X2]),
1030
(Opcode::FcvtFromUint, &[I16X8], &[F64X2]),
1031
(Opcode::FcvtFromUint, &[I32X4], &[F64X2]),
1032
(Opcode::FcvtFromSint, &[I8X16], &[F32]),
1033
(Opcode::FcvtFromSint, &[I16X8], &[F32]),
1034
(Opcode::FcvtFromSint, &[I32X4], &[F32]),
1035
(Opcode::FcvtFromSint, &[I64X2], &[F32]),
1036
(Opcode::FcvtFromSint, &[I8X16], &[F64]),
1037
(Opcode::FcvtFromSint, &[I16X8], &[F64]),
1038
(Opcode::FcvtFromSint, &[I32X4], &[F64]),
1039
(Opcode::FcvtFromSint, &[I64X2], &[F64]),
1040
(Opcode::FcvtFromSint, &[I8], &[F32X4]),
1041
(Opcode::FcvtFromSint, &[I16], &[F32X4]),
1042
(Opcode::FcvtFromSint, &[I32], &[F32X4]),
1043
(Opcode::FcvtFromSint, &[I64], &[F32X4]),
1044
(Opcode::FcvtFromSint, &[I128], &[F32X4]),
1045
(Opcode::FcvtFromSint, &[I8X16], &[F32X4]),
1046
(Opcode::FcvtFromSint, &[I16X8], &[F32X4]),
1047
(Opcode::FcvtFromSint, &[I64X2], &[F32X4]),
1048
(Opcode::FcvtFromSint, &[I8], &[F64X2]),
1049
(Opcode::FcvtFromSint, &[I16], &[F64X2]),
1050
(Opcode::FcvtFromSint, &[I32], &[F64X2]),
1051
(Opcode::FcvtFromSint, &[I64], &[F64X2]),
1052
(Opcode::FcvtFromSint, &[I128], &[F64X2]),
1053
(Opcode::FcvtFromSint, &[I8X16], &[F64X2]),
1054
(Opcode::FcvtFromSint, &[I16X8], &[F64X2]),
1055
(Opcode::FcvtFromSint, &[I32X4], &[F64X2]),
1056
// Only supported on x64 with a feature at this time, so 128-bit
1057
// atomics are not suitable to fuzz yet.
1058
(Opcode::AtomicRmw, _, &[I128]),
1059
(Opcode::AtomicCas, _, &[I128]),
1060
(Opcode::AtomicLoad, _, &[I128]),
1061
(Opcode::AtomicStore, &[I128, _], _),
1062
)
1063
})
1064
.filter(|(op, ..)| {
1065
allowed_opcodes
1066
.as_ref()
1067
.map_or(true, |opcodes| opcodes.contains(op))
1068
})
1069
.collect()
1070
});
1071
1072
fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
1073
match fmt {
1074
InstructionFormat::AtomicCas => insert_atomic_cas,
1075
InstructionFormat::AtomicRmw => insert_atomic_rmw,
1076
InstructionFormat::Binary => insert_opcode,
1077
InstructionFormat::BinaryImm64 => todo!(),
1078
InstructionFormat::BinaryImm8 => insert_ins_ext_lane,
1079
InstructionFormat::Call => insert_call,
1080
InstructionFormat::CallIndirect => insert_call,
1081
InstructionFormat::CondTrap => todo!(),
1082
InstructionFormat::DynamicStackLoad => todo!(),
1083
InstructionFormat::DynamicStackStore => todo!(),
1084
InstructionFormat::FloatCompare => insert_cmp,
1085
InstructionFormat::FuncAddr => todo!(),
1086
InstructionFormat::IntAddTrap => todo!(),
1087
InstructionFormat::IntCompare => insert_cmp,
1088
InstructionFormat::IntCompareImm => todo!(),
1089
InstructionFormat::Load => insert_load_store,
1090
InstructionFormat::LoadNoOffset => insert_load_store,
1091
InstructionFormat::NullAry => insert_opcode,
1092
InstructionFormat::Shuffle => insert_shuffle,
1093
InstructionFormat::StackLoad => insert_stack_load,
1094
InstructionFormat::StackStore => insert_stack_store,
1095
InstructionFormat::Store => insert_load_store,
1096
InstructionFormat::StoreNoOffset => insert_load_store,
1097
InstructionFormat::Ternary => insert_opcode,
1098
InstructionFormat::TernaryImm8 => insert_ins_ext_lane,
1099
InstructionFormat::Trap => todo!(),
1100
InstructionFormat::Unary => insert_opcode,
1101
InstructionFormat::UnaryConst => insert_const,
1102
InstructionFormat::UnaryGlobalValue => todo!(),
1103
InstructionFormat::UnaryIeee16 => insert_const,
1104
InstructionFormat::UnaryIeee32 => insert_const,
1105
InstructionFormat::UnaryIeee64 => insert_const,
1106
InstructionFormat::UnaryImm => insert_const,
1107
1108
InstructionFormat::BranchTable
1109
| InstructionFormat::Brif
1110
| InstructionFormat::Jump
1111
| InstructionFormat::MultiAry
1112
| InstructionFormat::TryCall
1113
| InstructionFormat::TryCallIndirect => {
1114
panic!("Control-flow instructions should be handled by 'insert_terminator': {fmt:?}")
1115
}
1116
}
1117
}
1118
1119
pub struct FunctionGenerator<'r, 'data>
1120
where
1121
'data: 'r,
1122
{
1123
u: &'r mut Unstructured<'data>,
1124
config: &'r Config,
1125
resources: Resources,
1126
isa: OwnedTargetIsa,
1127
name: UserFuncName,
1128
signature: Signature,
1129
}
1130
1131
#[derive(Debug, Clone)]
1132
enum BlockTerminator {
1133
Return,
1134
Jump(Block),
1135
Br(Block, Block),
1136
BrTable(Block, Vec<Block>),
1137
Switch(Type, Block, HashMap<u128, Block>),
1138
TailCall(FuncRef),
1139
TailCallIndirect(FuncRef),
1140
}
1141
1142
#[derive(Debug, Clone)]
1143
enum BlockTerminatorKind {
1144
Return,
1145
Jump,
1146
Br,
1147
BrTable,
1148
Switch,
1149
TailCall,
1150
TailCallIndirect,
1151
}
1152
1153
/// Alias Analysis Category
1154
///
1155
/// Our alias analysis pass supports 4 categories of accesses to distinguish
1156
/// different regions. The "Other" region is the general case, and is the default
1157
/// Although they have highly suggestive names there is no difference between any
1158
/// of the categories.
1159
///
1160
/// We assign each stack slot a category when we first generate them, and then
1161
/// ensure that all accesses to that stack slot are correctly tagged. We already
1162
/// ensure that memory accesses never cross stack slots, so there is no risk
1163
/// of a memory access being tagged with the wrong category.
1164
#[derive(Debug, PartialEq, Clone, Copy)]
1165
enum AACategory {
1166
Other,
1167
Heap,
1168
Table,
1169
VmCtx,
1170
}
1171
1172
impl AACategory {
1173
pub fn all() -> &'static [Self] {
1174
&[
1175
AACategory::Other,
1176
AACategory::Heap,
1177
AACategory::Table,
1178
AACategory::VmCtx,
1179
]
1180
}
1181
1182
pub fn update_memflags(&self, flags: &mut MemFlags) {
1183
flags.set_alias_region(match self {
1184
AACategory::Other => None,
1185
AACategory::Heap => Some(AliasRegion::Heap),
1186
AACategory::Table => Some(AliasRegion::Table),
1187
AACategory::VmCtx => Some(AliasRegion::Vmctx),
1188
})
1189
}
1190
}
1191
1192
pub type StackAlignment = StackSize;
1193
1194
#[derive(Default)]
1195
struct Resources {
1196
vars: HashMap<Type, Vec<Variable>>,
1197
blocks: Vec<(Block, BlockSignature)>,
1198
blocks_without_params: Vec<Block>,
1199
block_terminators: Vec<BlockTerminator>,
1200
func_refs: Vec<(Signature, SigRef, FuncRef)>,
1201
/// This field is required to be sorted by stack slot size at all times.
1202
/// We use this invariant when searching for stack slots with a given size.
1203
/// See [FunctionGenerator::stack_slot_with_size]
1204
stack_slots: Vec<(StackSlot, StackSize, StackAlignment, AACategory)>,
1205
usercalls: Vec<(UserExternalName, Signature)>,
1206
libcalls: Vec<LibCall>,
1207
}
1208
1209
impl Resources {
1210
/// Partitions blocks at `block`. Only blocks that can be targeted by branches are considered.
1211
///
1212
/// The first slice includes all blocks up to and including `block`.
1213
/// The second slice includes all remaining blocks.
1214
fn partition_target_blocks(
1215
&self,
1216
block: Block,
1217
) -> (&[(Block, BlockSignature)], &[(Block, BlockSignature)]) {
1218
// Blocks are stored in-order and have no gaps, this means that we can simply index them by
1219
// their number. We also need to exclude the entry block since it isn't a valid target.
1220
let target_blocks = &self.blocks[1..];
1221
target_blocks.split_at(block.as_u32() as usize)
1222
}
1223
1224
/// Returns blocks forward of `block`. Only blocks that can be targeted by branches are considered.
1225
fn forward_blocks(&self, block: Block) -> &[(Block, BlockSignature)] {
1226
let (_, forward_blocks) = self.partition_target_blocks(block);
1227
forward_blocks
1228
}
1229
1230
/// Generates a slice of `blocks_without_params` ahead of `block`
1231
fn forward_blocks_without_params(&self, block: Block) -> &[Block] {
1232
let partition_point = self.blocks_without_params.partition_point(|b| *b <= block);
1233
&self.blocks_without_params[partition_point..]
1234
}
1235
1236
/// Generates an iterator of all valid tail call targets. This includes all functions with both
1237
/// the `tail` calling convention and the same return values as the caller.
1238
fn tail_call_targets<'a>(
1239
&'a self,
1240
caller_sig: &'a Signature,
1241
) -> impl Iterator<Item = &'a (Signature, SigRef, FuncRef)> {
1242
self.func_refs.iter().filter(|(sig, _, _)| {
1243
sig.call_conv == CallConv::Tail && sig.returns == caller_sig.returns
1244
})
1245
}
1246
}
1247
1248
impl<'r, 'data> FunctionGenerator<'r, 'data>
1249
where
1250
'data: 'r,
1251
{
1252
pub fn new(
1253
u: &'r mut Unstructured<'data>,
1254
config: &'r Config,
1255
isa: OwnedTargetIsa,
1256
name: UserFuncName,
1257
signature: Signature,
1258
usercalls: Vec<(UserExternalName, Signature)>,
1259
libcalls: Vec<LibCall>,
1260
) -> Self {
1261
Self {
1262
u,
1263
config,
1264
resources: Resources {
1265
usercalls,
1266
libcalls,
1267
..Resources::default()
1268
},
1269
isa,
1270
name,
1271
signature,
1272
}
1273
}
1274
1275
/// Generates a random value for config `param`
1276
fn param(&mut self, param: &RangeInclusive<usize>) -> Result<usize> {
1277
Ok(self.u.int_in_range(param.clone())?)
1278
}
1279
1280
fn system_callconv(&mut self) -> CallConv {
1281
// TODO: This currently only runs on linux, so this is the only choice
1282
// We should improve this once we generate flags and targets
1283
CallConv::SystemV
1284
}
1285
1286
/// Finds a stack slot with size of at least n bytes
1287
fn stack_slot_with_size(
1288
&mut self,
1289
n: u32,
1290
) -> Result<(StackSlot, StackSize, StackAlignment, AACategory)> {
1291
let first = self
1292
.resources
1293
.stack_slots
1294
.partition_point(|&(_slot, size, _align, _category)| size < n);
1295
Ok(*self.u.choose(&self.resources.stack_slots[first..])?)
1296
}
1297
1298
/// Generates an address that should allow for a store or a load.
1299
///
1300
/// Addresses aren't generated like other values. They are never stored in variables so that
1301
/// we don't run the risk of returning them from a function, which would make the fuzzer
1302
/// complain since they are different from the interpreter to the backend.
1303
///
1304
/// `min_size`: Controls the amount of space that the address should have.
1305
///
1306
/// `aligned`: When passed as true, the resulting address is guaranteed to be aligned
1307
/// on an 8 byte boundary.
1308
///
1309
/// Returns a valid address and the maximum possible offset that still respects `min_size`.
1310
fn generate_load_store_address(
1311
&mut self,
1312
builder: &mut FunctionBuilder,
1313
min_size: u32,
1314
aligned: bool,
1315
) -> Result<(Value, u32, AACategory)> {
1316
// TODO: Currently our only source of addresses is stack_addr, but we
1317
// should add global_value, symbol_value eventually
1318
let (addr, available_size, category) = {
1319
let (ss, slot_size, _align, category) = self.stack_slot_with_size(min_size)?;
1320
1321
// stack_slot_with_size guarantees that slot_size >= min_size
1322
let max_offset = slot_size - min_size;
1323
let offset = if aligned {
1324
self.u.int_in_range(0..=max_offset / min_size)? * min_size
1325
} else {
1326
self.u.int_in_range(0..=max_offset)?
1327
};
1328
1329
let base_addr = builder.ins().stack_addr(I64, ss, offset as i32);
1330
let available_size = slot_size.saturating_sub(offset);
1331
(base_addr, available_size, category)
1332
};
1333
1334
// TODO: Insert a bunch of amode opcodes here to modify the address!
1335
1336
// Now that we have an address and a size, we just choose a random offset to return to the
1337
// caller. Preserving min_size bytes.
1338
let max_offset = available_size.saturating_sub(min_size);
1339
Ok((addr, max_offset, category))
1340
}
1341
1342
// Generates an address and memflags for a load or store.
1343
fn generate_address_and_memflags(
1344
&mut self,
1345
builder: &mut FunctionBuilder,
1346
min_size: u32,
1347
is_atomic: bool,
1348
) -> Result<(Value, MemFlags, Offset32)> {
1349
// Should we generate an aligned address
1350
// Some backends have issues with unaligned atomics.
1351
// AArch64: https://github.com/bytecodealliance/wasmtime/issues/5483
1352
// RISCV: https://github.com/bytecodealliance/wasmtime/issues/5882
1353
let requires_aligned_atomics = matches!(
1354
self.isa.triple().architecture,
1355
Architecture::Aarch64(_) | Architecture::Riscv64(_)
1356
);
1357
let aligned = if is_atomic && requires_aligned_atomics {
1358
true
1359
} else if min_size > 8 {
1360
// TODO: We currently can't guarantee that a stack_slot will be aligned on a 16 byte
1361
// boundary. We don't have a way to specify alignment when creating stack slots, and
1362
// cranelift only guarantees 8 byte alignment between stack slots.
1363
// See: https://github.com/bytecodealliance/wasmtime/issues/5922#issuecomment-1457926624
1364
false
1365
} else {
1366
bool::arbitrary(self.u)?
1367
};
1368
1369
let mut flags = MemFlags::new();
1370
// Even if we picked an aligned address, we can always generate unaligned memflags
1371
if aligned && bool::arbitrary(self.u)? {
1372
flags.set_aligned();
1373
}
1374
// If the address is aligned, then we know it won't trap
1375
if aligned && bool::arbitrary(self.u)? {
1376
flags.set_notrap();
1377
}
1378
1379
let (address, max_offset, category) =
1380
self.generate_load_store_address(builder, min_size, aligned)?;
1381
1382
// Set the Alias Analysis bits on the memflags
1383
category.update_memflags(&mut flags);
1384
1385
// Pick an offset to pass into the load/store.
1386
let offset = if aligned {
1387
0
1388
} else {
1389
self.u.int_in_range(0..=max_offset)? as i32
1390
}
1391
.into();
1392
1393
Ok((address, flags, offset))
1394
}
1395
1396
/// Get a variable of type `ty` from the current function
1397
fn get_variable_of_type(&mut self, ty: Type) -> Result<Variable> {
1398
let opts = self.resources.vars.get(&ty).map_or(&[][..], Vec::as_slice);
1399
let var = self.u.choose(opts)?;
1400
Ok(*var)
1401
}
1402
1403
/// Generates an instruction(`iconst`/`fconst`/etc...) to introduce a constant value
1404
fn generate_const(&mut self, builder: &mut FunctionBuilder, ty: Type) -> Result<Value> {
1405
Ok(match self.u.datavalue(ty)? {
1406
DataValue::I8(i) => builder.ins().iconst(ty, i as u8 as i64),
1407
DataValue::I16(i) => builder.ins().iconst(ty, i as u16 as i64),
1408
DataValue::I32(i) => builder.ins().iconst(ty, i as u32 as i64),
1409
DataValue::I64(i) => builder.ins().iconst(ty, i),
1410
DataValue::I128(i) => {
1411
let hi = builder.ins().iconst(I64, (i >> 64) as i64);
1412
let lo = builder.ins().iconst(I64, i as i64);
1413
builder.ins().iconcat(lo, hi)
1414
}
1415
DataValue::F16(f) => builder.ins().f16const(f),
1416
DataValue::F32(f) => builder.ins().f32const(f),
1417
DataValue::F64(f) => builder.ins().f64const(f),
1418
DataValue::F128(f) => {
1419
let handle = builder.func.dfg.constants.insert(f.into());
1420
builder.ins().f128const(handle)
1421
}
1422
DataValue::V128(bytes) => {
1423
let data = bytes.to_vec().into();
1424
let handle = builder.func.dfg.constants.insert(data);
1425
builder.ins().vconst(ty, handle)
1426
}
1427
_ => unimplemented!(),
1428
})
1429
}
1430
1431
/// Chooses a random block which can be targeted by a jump / branch.
1432
/// This means any block that is not the first block.
1433
fn generate_target_block(&mut self, source_block: Block) -> Result<Block> {
1434
// We try to mostly generate forward branches to avoid generating an excessive amount of
1435
// infinite loops. But they are still important, so give them a small chance of existing.
1436
let (backwards_blocks, forward_blocks) =
1437
self.resources.partition_target_blocks(source_block);
1438
let ratio = self.config.backwards_branch_ratio;
1439
let block_targets = if !backwards_blocks.is_empty() && self.u.ratio(ratio.0, ratio.1)? {
1440
backwards_blocks
1441
} else {
1442
forward_blocks
1443
};
1444
assert!(!block_targets.is_empty());
1445
1446
let (block, _) = self.u.choose(block_targets)?.clone();
1447
Ok(block)
1448
}
1449
1450
fn generate_values_for_block(
1451
&mut self,
1452
builder: &mut FunctionBuilder,
1453
block: Block,
1454
) -> Result<Vec<BlockArg>> {
1455
let (_, sig) = self.resources.blocks[block.as_u32() as usize].clone();
1456
Ok(self
1457
.generate_values_for_signature(builder, sig.iter().copied())?
1458
.into_iter()
1459
.map(|val| BlockArg::Value(val))
1460
.collect::<Vec<_>>())
1461
}
1462
1463
fn generate_values_for_signature<I: Iterator<Item = Type>>(
1464
&mut self,
1465
builder: &mut FunctionBuilder,
1466
signature: I,
1467
) -> Result<Vec<Value>> {
1468
signature
1469
.map(|ty| {
1470
let var = self.get_variable_of_type(ty)?;
1471
let val = builder.use_var(var);
1472
Ok(val)
1473
})
1474
.collect()
1475
}
1476
1477
/// The terminator that we need to insert has already been picked ahead of time
1478
/// we just need to build the instructions for it
1479
fn insert_terminator(
1480
&mut self,
1481
builder: &mut FunctionBuilder,
1482
source_block: Block,
1483
) -> Result<()> {
1484
let terminator = self.resources.block_terminators[source_block.as_u32() as usize].clone();
1485
1486
match terminator {
1487
BlockTerminator::Return => {
1488
let types: Vec<Type> = {
1489
let rets = &builder.func.signature.returns;
1490
rets.iter().map(|p| p.value_type).collect()
1491
};
1492
let vals = self.generate_values_for_signature(builder, types.into_iter())?;
1493
1494
builder.ins().return_(&vals[..]);
1495
}
1496
BlockTerminator::Jump(target) => {
1497
let args = self.generate_values_for_block(builder, target)?;
1498
builder.ins().jump(target, &args[..]);
1499
}
1500
BlockTerminator::Br(left, right) => {
1501
let left_args = self.generate_values_for_block(builder, left)?;
1502
let right_args = self.generate_values_for_block(builder, right)?;
1503
1504
let condbr_types = [I8, I16, I32, I64, I128];
1505
let _type = *self.u.choose(&condbr_types[..])?;
1506
let val = builder.use_var(self.get_variable_of_type(_type)?);
1507
builder
1508
.ins()
1509
.brif(val, left, &left_args[..], right, &right_args[..]);
1510
}
1511
BlockTerminator::BrTable(default, targets) => {
1512
// Create jump tables on demand
1513
let mut jt = Vec::with_capacity(targets.len());
1514
for block in targets {
1515
let args = self.generate_values_for_block(builder, block)?;
1516
jt.push(builder.func.dfg.block_call(block, &args))
1517
}
1518
1519
let args = self.generate_values_for_block(builder, default)?;
1520
let jt_data = JumpTableData::new(builder.func.dfg.block_call(default, &args), &jt);
1521
let jt = builder.create_jump_table(jt_data);
1522
1523
// br_table only supports I32
1524
let val = builder.use_var(self.get_variable_of_type(I32)?);
1525
1526
builder.ins().br_table(val, jt);
1527
}
1528
BlockTerminator::Switch(_type, default, entries) => {
1529
let mut switch = Switch::new();
1530
for (&entry, &block) in entries.iter() {
1531
switch.set_entry(entry, block);
1532
}
1533
1534
let switch_val = builder.use_var(self.get_variable_of_type(_type)?);
1535
1536
switch.emit(builder, switch_val, default);
1537
}
1538
BlockTerminator::TailCall(target) | BlockTerminator::TailCallIndirect(target) => {
1539
let (sig, sig_ref, func_ref) = self
1540
.resources
1541
.func_refs
1542
.iter()
1543
.find(|(_, _, f)| *f == target)
1544
.expect("Failed to find previously selected function")
1545
.clone();
1546
1547
let opcode = match terminator {
1548
BlockTerminator::TailCall(_) => Opcode::ReturnCall,
1549
BlockTerminator::TailCallIndirect(_) => Opcode::ReturnCallIndirect,
1550
_ => unreachable!(),
1551
};
1552
1553
insert_call_to_function(self, builder, opcode, &sig, sig_ref, func_ref)?;
1554
}
1555
}
1556
1557
Ok(())
1558
}
1559
1560
/// Fills the current block with random instructions
1561
fn generate_instructions(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
1562
for _ in 0..self.param(&self.config.instructions_per_block)? {
1563
let (op, args, rets) = self.u.choose(&OPCODE_SIGNATURES)?;
1564
1565
// We filter out instructions that aren't supported by the target at this point instead
1566
// of building a single vector of valid instructions at the beginning of function
1567
// generation, to avoid invalidating the corpus when instructions are enabled/disabled.
1568
if !valid_for_target(&self.isa.triple(), *op, &args, &rets) {
1569
return Err(arbitrary::Error::IncorrectFormat.into());
1570
}
1571
1572
let inserter = inserter_for_format(op.format());
1573
inserter(self, builder, *op, &args, &rets)?;
1574
}
1575
1576
Ok(())
1577
}
1578
1579
fn generate_funcrefs(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
1580
let usercalls: Vec<_> = self
1581
.resources
1582
.usercalls
1583
.iter()
1584
.map(|(name, signature)| {
1585
let user_func_ref = builder.func.declare_imported_user_function(name.clone());
1586
let name = ExternalName::User(user_func_ref);
1587
let never_colocated = false;
1588
(name, signature.clone(), never_colocated)
1589
})
1590
.collect();
1591
1592
let lib_callconv = self.system_callconv();
1593
let libcalls: Vec<_> = self
1594
.resources
1595
.libcalls
1596
.iter()
1597
.map(|libcall| {
1598
let pointer_type = Type::int_with_byte_size(
1599
self.isa.triple().pointer_width().unwrap().bytes().into(),
1600
)
1601
.unwrap();
1602
let signature = libcall.signature(lib_callconv, pointer_type);
1603
let name = ExternalName::LibCall(*libcall);
1604
// libcalls can't be colocated to generated code because we
1605
// don't know where in the address space the function will go
1606
// relative to where the libcall is.
1607
let never_colocated = true;
1608
(name, signature, never_colocated)
1609
})
1610
.collect();
1611
1612
for (name, signature, never_colocated) in usercalls.into_iter().chain(libcalls) {
1613
let sig_ref = builder.import_signature(signature.clone());
1614
let func_ref = builder.import_function(ExtFuncData {
1615
name,
1616
signature: sig_ref,
1617
colocated: if never_colocated {
1618
false
1619
} else {
1620
self.u.arbitrary()?
1621
},
1622
});
1623
1624
self.resources
1625
.func_refs
1626
.push((signature, sig_ref, func_ref));
1627
}
1628
1629
Ok(())
1630
}
1631
1632
fn generate_stack_slots(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
1633
for _ in 0..self.param(&self.config.static_stack_slots_per_function)? {
1634
let bytes = self.param(&self.config.static_stack_slot_size)? as u32;
1635
let alignment = self.param(&self.config.stack_slot_alignment_log2)? as u8;
1636
let alignment_bytes = 1 << alignment;
1637
1638
let ss_data = StackSlotData::new(StackSlotKind::ExplicitSlot, bytes, alignment);
1639
let slot = builder.create_sized_stack_slot(ss_data);
1640
1641
// Generate one Alias Analysis Category for each slot
1642
let category = *self.u.choose(AACategory::all())?;
1643
1644
self.resources
1645
.stack_slots
1646
.push((slot, bytes, alignment_bytes, category));
1647
}
1648
1649
self.resources
1650
.stack_slots
1651
.sort_unstable_by_key(|&(_slot, bytes, _align, _category)| bytes);
1652
1653
Ok(())
1654
}
1655
1656
/// Zero initializes the stack slot by inserting `stack_store`'s.
1657
fn initialize_stack_slots(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
1658
let i8_zero = builder.ins().iconst(I8, 0);
1659
let i16_zero = builder.ins().iconst(I16, 0);
1660
let i32_zero = builder.ins().iconst(I32, 0);
1661
let i64_zero = builder.ins().iconst(I64, 0);
1662
let i128_zero = builder.ins().uextend(I128, i64_zero);
1663
1664
for &(slot, init_size, _align, category) in self.resources.stack_slots.iter() {
1665
let mut size = init_size;
1666
1667
// Insert the largest available store for the remaining size.
1668
while size != 0 {
1669
let offset = (init_size - size) as i32;
1670
let (val, filled) = match size {
1671
sz if sz / 16 > 0 => (i128_zero, 16),
1672
sz if sz / 8 > 0 => (i64_zero, 8),
1673
sz if sz / 4 > 0 => (i32_zero, 4),
1674
sz if sz / 2 > 0 => (i16_zero, 2),
1675
_ => (i8_zero, 1),
1676
};
1677
let addr = builder.ins().stack_addr(I64, slot, offset);
1678
1679
// Each stack slot has an associated category, that means we have to set the
1680
// correct memflags for it. So we can't use `stack_store` directly.
1681
let mut flags = MemFlags::new();
1682
flags.set_notrap();
1683
category.update_memflags(&mut flags);
1684
1685
builder.ins().store(flags, val, addr, 0);
1686
1687
size -= filled;
1688
}
1689
}
1690
Ok(())
1691
}
1692
1693
/// Creates a random amount of blocks in this function
1694
fn generate_blocks(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
1695
let extra_block_count = self.param(&self.config.blocks_per_function)?;
1696
1697
// We must always have at least one block, so we generate the "extra" blocks and add 1 for
1698
// the entry block.
1699
let block_count = 1 + extra_block_count;
1700
1701
// Blocks need to be sorted in ascending order
1702
self.resources.blocks = (0..block_count)
1703
.map(|i| {
1704
let is_entry = i == 0;
1705
let block = builder.create_block();
1706
1707
// Optionally mark blocks that are not the entry block as cold
1708
if !is_entry {
1709
if bool::arbitrary(self.u)? {
1710
builder.set_cold_block(block);
1711
}
1712
}
1713
1714
// The first block has to have the function signature, but for the rest of them we generate
1715
// a random signature;
1716
if is_entry {
1717
builder.append_block_params_for_function_params(block);
1718
Ok((
1719
block,
1720
self.signature.params.iter().map(|a| a.value_type).collect(),
1721
))
1722
} else {
1723
let sig = self.generate_block_signature()?;
1724
sig.iter().for_each(|ty| {
1725
builder.append_block_param(block, *ty);
1726
});
1727
Ok((block, sig))
1728
}
1729
})
1730
.collect::<Result<Vec<_>>>()?;
1731
1732
// Valid blocks for jump tables have to have no parameters in the signature, and must also
1733
// not be the first block.
1734
self.resources.blocks_without_params = self.resources.blocks[1..]
1735
.iter()
1736
.filter(|(_, sig)| sig.len() == 0)
1737
.map(|(b, _)| *b)
1738
.collect();
1739
1740
// Compute the block CFG
1741
//
1742
// cranelift-frontend requires us to never generate unreachable blocks
1743
// To ensure this property we start by constructing a main "spine" of blocks. So block1 can
1744
// always jump to block2, and block2 can always jump to block3, etc...
1745
//
1746
// That is not a very interesting CFG, so we introduce variations on that, but always
1747
// ensuring that the property of pointing to the next block is maintained whatever the
1748
// branching mechanism we use.
1749
let blocks = self.resources.blocks.clone();
1750
self.resources.block_terminators = blocks
1751
.iter()
1752
.map(|&(block, _)| {
1753
let next_block = Block::with_number(block.as_u32() + 1).unwrap();
1754
let forward_blocks = self.resources.forward_blocks(block);
1755
let paramless_targets = self.resources.forward_blocks_without_params(block);
1756
let has_paramless_targets = !paramless_targets.is_empty();
1757
let next_block_is_paramless = paramless_targets.contains(&next_block);
1758
1759
let mut valid_terminators = vec![];
1760
1761
if forward_blocks.is_empty() {
1762
// Return is only valid on the last block.
1763
valid_terminators.push(BlockTerminatorKind::Return);
1764
} else {
1765
// If we have more than one block we can allow terminators that target blocks.
1766
// TODO: We could add some kind of BrReturn here, to explore edges where we
1767
// exit in the middle of the function
1768
valid_terminators.extend_from_slice(&[
1769
BlockTerminatorKind::Jump,
1770
BlockTerminatorKind::Br,
1771
BlockTerminatorKind::BrTable,
1772
]);
1773
}
1774
1775
// As the Switch interface only allows targeting blocks without params we need
1776
// to ensure that the next block has no params, since that one is guaranteed to be
1777
// picked in either case.
1778
if has_paramless_targets && next_block_is_paramless {
1779
valid_terminators.push(BlockTerminatorKind::Switch);
1780
}
1781
1782
// Tail Calls are a block terminator, so we should insert them as any other block
1783
// terminator. We should ensure that we can select at least one target before considering
1784
// them as candidate instructions.
1785
let has_tail_callees = self
1786
.resources
1787
.tail_call_targets(&self.signature)
1788
.next()
1789
.is_some();
1790
let is_tail_caller = self.signature.call_conv == CallConv::Tail;
1791
1792
let supports_tail_calls = match self.isa.triple().architecture {
1793
Architecture::Aarch64(_) | Architecture::Riscv64(_) => true,
1794
// TODO: x64 currently requires frame pointers for tail calls.
1795
Architecture::X86_64 => self.isa.flags().preserve_frame_pointers(),
1796
// TODO: Other platforms do not support tail calls yet.
1797
_ => false,
1798
};
1799
1800
if is_tail_caller && has_tail_callees && supports_tail_calls {
1801
valid_terminators.extend([
1802
BlockTerminatorKind::TailCall,
1803
BlockTerminatorKind::TailCallIndirect,
1804
]);
1805
}
1806
1807
let terminator = self.u.choose(&valid_terminators)?;
1808
1809
// Choose block targets for the terminators that we picked above
1810
Ok(match terminator {
1811
BlockTerminatorKind::Return => BlockTerminator::Return,
1812
BlockTerminatorKind::Jump => BlockTerminator::Jump(next_block),
1813
BlockTerminatorKind::Br => {
1814
BlockTerminator::Br(next_block, self.generate_target_block(block)?)
1815
}
1816
// TODO: Allow generating backwards branches here
1817
BlockTerminatorKind::BrTable => {
1818
// Make the default the next block, and then we don't have to worry
1819
// that we can reach it via the targets
1820
let default = next_block;
1821
1822
let target_count = self.param(&self.config.jump_table_entries)?;
1823
let targets = Result::from_iter(
1824
(0..target_count).map(|_| self.generate_target_block(block)),
1825
)?;
1826
1827
BlockTerminator::BrTable(default, targets)
1828
}
1829
BlockTerminatorKind::Switch => {
1830
// Make the default the next block, and then we don't have to worry
1831
// that we can reach it via the entries below
1832
let default_block = next_block;
1833
1834
let _type = *self.u.choose(&[I8, I16, I32, I64, I128][..])?;
1835
1836
// Build this into a HashMap since we cannot have duplicate entries.
1837
let mut entries = HashMap::new();
1838
for _ in 0..self.param(&self.config.switch_cases)? {
1839
// The Switch API only allows for entries that are addressable by the index type
1840
// so we need to limit the range of values that we generate.
1841
let (ty_min, ty_max) = _type.bounds(false);
1842
let range_start = self.u.int_in_range(ty_min..=ty_max)?;
1843
1844
// We can either insert a contiguous range of blocks or a individual block
1845
// This is done because the Switch API specializes contiguous ranges.
1846
let range_size = if bool::arbitrary(self.u)? {
1847
1
1848
} else {
1849
self.param(&self.config.switch_max_range_size)?
1850
} as u128;
1851
1852
// Build the switch entries
1853
for i in 0..range_size {
1854
let index = range_start.wrapping_add(i) % ty_max;
1855
let block = *self
1856
.u
1857
.choose(self.resources.forward_blocks_without_params(block))?;
1858
1859
entries.insert(index, block);
1860
}
1861
}
1862
1863
BlockTerminator::Switch(_type, default_block, entries)
1864
}
1865
BlockTerminatorKind::TailCall => {
1866
let targets = self
1867
.resources
1868
.tail_call_targets(&self.signature)
1869
.collect::<Vec<_>>();
1870
let (_, _, funcref) = *self.u.choose(&targets[..])?;
1871
BlockTerminator::TailCall(*funcref)
1872
}
1873
BlockTerminatorKind::TailCallIndirect => {
1874
let targets = self
1875
.resources
1876
.tail_call_targets(&self.signature)
1877
.collect::<Vec<_>>();
1878
let (_, _, funcref) = *self.u.choose(&targets[..])?;
1879
BlockTerminator::TailCallIndirect(*funcref)
1880
}
1881
})
1882
})
1883
.collect::<Result<_>>()?;
1884
1885
Ok(())
1886
}
1887
1888
fn generate_block_signature(&mut self) -> Result<BlockSignature> {
1889
let param_count = self.param(&self.config.block_signature_params)?;
1890
1891
let mut params = Vec::with_capacity(param_count);
1892
for _ in 0..param_count {
1893
params.push(self.u._type((&*self.isa).supports_simd())?);
1894
}
1895
Ok(params)
1896
}
1897
1898
fn build_variable_pool(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
1899
let block = builder.current_block().unwrap();
1900
1901
// Define variables for the function signature
1902
let mut vars: Vec<_> = builder
1903
.func
1904
.signature
1905
.params
1906
.iter()
1907
.map(|param| param.value_type)
1908
.zip(builder.block_params(block).iter().copied())
1909
.collect();
1910
1911
// Create a pool of vars that are going to be used in this function
1912
for _ in 0..self.param(&self.config.vars_per_function)? {
1913
let ty = self.u._type((&*self.isa).supports_simd())?;
1914
let value = self.generate_const(builder, ty)?;
1915
vars.push((ty, value));
1916
}
1917
1918
for (ty, value) in vars.into_iter() {
1919
let var = builder.declare_var(ty);
1920
builder.def_var(var, value);
1921
1922
// Randomly declare variables as needing a stack map.
1923
// We limit these to only types that have fewer than 16 bytes
1924
// since the stack map mechanism does not support larger types.
1925
if ty.bytes() <= 16 && self.u.arbitrary()? {
1926
builder.declare_var_needs_stack_map(var);
1927
}
1928
1929
self.resources
1930
.vars
1931
.entry(ty)
1932
.or_insert_with(Vec::new)
1933
.push(var);
1934
}
1935
1936
Ok(())
1937
}
1938
1939
/// We generate a function in multiple stages:
1940
///
1941
/// * First we generate a random number of empty blocks
1942
/// * Then we generate a random pool of variables to be used throughout the function
1943
/// * We then visit each block and generate random instructions
1944
///
1945
/// Because we generate all blocks and variables up front we already know everything that
1946
/// we need when generating instructions (i.e. jump targets / variables)
1947
pub fn generate(mut self) -> Result<Function> {
1948
let mut fn_builder_ctx = FunctionBuilderContext::new();
1949
let mut func = Function::with_name_signature(self.name.clone(), self.signature.clone());
1950
1951
let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx);
1952
1953
// Build the function references before generating the block CFG since we store
1954
// function references in the CFG.
1955
self.generate_funcrefs(&mut builder)?;
1956
self.generate_blocks(&mut builder)?;
1957
1958
// Function preamble
1959
self.generate_stack_slots(&mut builder)?;
1960
1961
// Main instruction generation loop
1962
for (block, block_sig) in self.resources.blocks.clone().into_iter() {
1963
let is_block0 = block.as_u32() == 0;
1964
builder.switch_to_block(block);
1965
1966
if is_block0 {
1967
// The first block is special because we must create variables both for the
1968
// block signature and for the variable pool. Additionally, we must also define
1969
// initial values for all variables that are not the function signature.
1970
self.build_variable_pool(&mut builder)?;
1971
1972
// Stack slots have random bytes at the beginning of the function
1973
// initialize them to a constant value so that execution stays predictable.
1974
self.initialize_stack_slots(&mut builder)?;
1975
} else {
1976
// Define variables for the block params
1977
for (i, ty) in block_sig.iter().enumerate() {
1978
let var = self.get_variable_of_type(*ty)?;
1979
let block_param = builder.block_params(block)[i];
1980
builder.def_var(var, block_param);
1981
}
1982
}
1983
1984
// Generate block instructions
1985
self.generate_instructions(&mut builder)?;
1986
1987
// Insert a terminator to safely exit the block
1988
self.insert_terminator(&mut builder, block)?;
1989
}
1990
1991
builder.seal_all_blocks();
1992
builder.finalize();
1993
1994
Ok(func)
1995
}
1996
}
1997
1998