Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/write.rs
1693 views
1
//! Converting Cranelift IR to text.
2
//!
3
//! The `write` module provides the `write_function` function which converts an IR `Function` to an
4
//! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate.
5
6
use crate::entity::SecondaryMap;
7
use crate::ir::entities::AnyEntity;
8
use crate::ir::immediates::Ieee128;
9
use crate::ir::pcc::Fact;
10
use crate::ir::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef};
11
use crate::packed_option::ReservedValue;
12
use alloc::string::{String, ToString};
13
use alloc::vec::Vec;
14
use core::fmt::{self, Write};
15
16
/// A `FuncWriter` used to decorate functions during printing.
17
pub trait FuncWriter {
18
/// Write the basic block header for the current function.
19
fn write_block_header(
20
&mut self,
21
w: &mut dyn Write,
22
func: &Function,
23
block: Block,
24
indent: usize,
25
) -> fmt::Result;
26
27
/// Write the given `inst` to `w`.
28
fn write_instruction(
29
&mut self,
30
w: &mut dyn Write,
31
func: &Function,
32
aliases: &SecondaryMap<Value, Vec<Value>>,
33
inst: Inst,
34
indent: usize,
35
) -> fmt::Result;
36
37
/// Write the preamble to `w`. By default, this uses `write_entity_definition`.
38
fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
39
self.super_preamble(w, func)
40
}
41
42
/// Default impl of `write_preamble`
43
fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
44
let mut any = false;
45
46
for (ss, slot) in func.dynamic_stack_slots.iter() {
47
any = true;
48
self.write_entity_definition(w, func, ss.into(), slot, None)?;
49
}
50
51
for (ss, slot) in func.sized_stack_slots.iter() {
52
any = true;
53
self.write_entity_definition(w, func, ss.into(), slot, None)?;
54
}
55
56
for (gv, gv_data) in &func.global_values {
57
any = true;
58
let maybe_fact = func.global_value_facts[gv].as_ref();
59
self.write_entity_definition(w, func, gv.into(), gv_data, maybe_fact)?;
60
}
61
62
for (mt, mt_data) in &func.memory_types {
63
any = true;
64
self.write_entity_definition(w, func, mt.into(), mt_data, None)?;
65
}
66
67
// Write out all signatures before functions since function declarations can refer to
68
// signatures.
69
for (sig, sig_data) in &func.dfg.signatures {
70
any = true;
71
self.write_entity_definition(w, func, sig.into(), &sig_data, None)?;
72
}
73
74
for (fnref, ext_func) in &func.dfg.ext_funcs {
75
if ext_func.signature != SigRef::reserved_value() {
76
any = true;
77
self.write_entity_definition(
78
w,
79
func,
80
fnref.into(),
81
&ext_func.display(Some(&func.params)),
82
None,
83
)?;
84
}
85
}
86
87
for (&cref, cval) in func.dfg.constants.iter() {
88
any = true;
89
self.write_entity_definition(w, func, cref.into(), cval, None)?;
90
}
91
92
if let Some(limit) = func.stack_limit {
93
any = true;
94
self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit, None)?;
95
}
96
97
Ok(any)
98
}
99
100
/// Write an entity definition defined in the preamble to `w`.
101
fn write_entity_definition(
102
&mut self,
103
w: &mut dyn Write,
104
func: &Function,
105
entity: AnyEntity,
106
value: &dyn fmt::Display,
107
maybe_fact: Option<&Fact>,
108
) -> fmt::Result {
109
self.super_entity_definition(w, func, entity, value, maybe_fact)
110
}
111
112
/// Default impl of `write_entity_definition`
113
fn super_entity_definition(
114
&mut self,
115
w: &mut dyn Write,
116
_func: &Function,
117
entity: AnyEntity,
118
value: &dyn fmt::Display,
119
maybe_fact: Option<&Fact>,
120
) -> fmt::Result {
121
if let Some(fact) = maybe_fact {
122
writeln!(w, " {entity} ! {fact} = {value}")
123
} else {
124
writeln!(w, " {entity} = {value}")
125
}
126
}
127
}
128
129
/// A `PlainWriter` that doesn't decorate the function.
130
pub struct PlainWriter;
131
132
impl FuncWriter for PlainWriter {
133
fn write_instruction(
134
&mut self,
135
w: &mut dyn Write,
136
func: &Function,
137
aliases: &SecondaryMap<Value, Vec<Value>>,
138
inst: Inst,
139
indent: usize,
140
) -> fmt::Result {
141
write_instruction(w, func, aliases, inst, indent)
142
}
143
144
fn write_block_header(
145
&mut self,
146
w: &mut dyn Write,
147
func: &Function,
148
block: Block,
149
indent: usize,
150
) -> fmt::Result {
151
write_block_header(w, func, block, indent)
152
}
153
}
154
155
/// Write `func` to `w` as equivalent text.
156
/// Use `isa` to emit ISA-dependent annotations.
157
pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result {
158
decorate_function(&mut PlainWriter, w, func)
159
}
160
161
/// Create a reverse-alias map from a value to all aliases having that value as a direct target
162
fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
163
let mut aliases = SecondaryMap::<_, Vec<_>>::new();
164
for v in func.dfg.values() {
165
// VADFS returns the immediate target of an alias
166
if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
167
aliases[k].push(v);
168
}
169
}
170
aliases
171
}
172
173
/// Writes `func` to `w` as text.
174
/// write_function_plain is passed as 'closure' to print instructions as text.
175
/// pretty_function_error is passed as 'closure' to add error decoration.
176
pub fn decorate_function<FW: FuncWriter>(
177
func_w: &mut FW,
178
w: &mut dyn Write,
179
func: &Function,
180
) -> fmt::Result {
181
write!(w, "function ")?;
182
write_function_spec(w, func)?;
183
writeln!(w, " {{")?;
184
let aliases = alias_map(func);
185
let mut any = func_w.write_preamble(w, func)?;
186
for block in &func.layout {
187
if any {
188
writeln!(w)?;
189
}
190
decorate_block(func_w, w, func, &aliases, block)?;
191
any = true;
192
}
193
writeln!(w, "}}")
194
}
195
196
//----------------------------------------------------------------------
197
//
198
// Function spec.
199
200
/// Writes the spec (name and signature) of 'func' to 'w' as text.
201
pub fn write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result {
202
write!(w, "{}{}", func.name, func.signature)
203
}
204
205
//----------------------------------------------------------------------
206
//
207
// Basic blocks
208
209
fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result {
210
let ty = func.dfg.value_type(arg);
211
if let Some(f) = &func.dfg.facts[arg] {
212
write!(w, "{arg} ! {f}: {ty}")
213
} else {
214
write!(w, "{arg}: {ty}")
215
}
216
}
217
218
/// Write out the basic block header, outdented:
219
///
220
/// block1:
221
/// block1(v1: i32):
222
/// block10(v4: f64, v5: i8):
223
///
224
pub fn write_block_header(
225
w: &mut dyn Write,
226
func: &Function,
227
block: Block,
228
indent: usize,
229
) -> fmt::Result {
230
let cold = if func.layout.is_cold(block) {
231
" cold"
232
} else {
233
""
234
};
235
236
// The `indent` is the instruction indentation. block headers are 4 spaces out from that.
237
write!(w, "{1:0$}{2}", indent - 4, "", block)?;
238
239
let mut args = func.dfg.block_params(block).iter().cloned();
240
match args.next() {
241
None => return writeln!(w, "{cold}:"),
242
Some(arg) => {
243
write!(w, "(")?;
244
write_arg(w, func, arg)?;
245
}
246
}
247
// Remaining arguments.
248
for arg in args {
249
write!(w, ", ")?;
250
write_arg(w, func, arg)?;
251
}
252
writeln!(w, "){cold}:")
253
}
254
255
fn decorate_block<FW: FuncWriter>(
256
func_w: &mut FW,
257
w: &mut dyn Write,
258
func: &Function,
259
aliases: &SecondaryMap<Value, Vec<Value>>,
260
block: Block,
261
) -> fmt::Result {
262
// Indent all instructions if any srclocs are present.
263
let indent = if func.rel_srclocs().is_empty() { 4 } else { 36 };
264
265
func_w.write_block_header(w, func, block, indent)?;
266
for a in func.dfg.block_params(block).iter().cloned() {
267
write_value_aliases(w, aliases, a, indent)?;
268
}
269
270
for inst in func.layout.block_insts(block) {
271
func_w.write_instruction(w, func, aliases, inst, indent)?;
272
}
273
274
Ok(())
275
}
276
277
//----------------------------------------------------------------------
278
//
279
// Instructions
280
281
// Should `inst` be printed with a type suffix?
282
//
283
// Polymorphic instructions may need a suffix indicating the value of the controlling type variable
284
// if it can't be trivially inferred.
285
//
286
fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
287
let inst_data = &func.dfg.insts[inst];
288
let constraints = inst_data.opcode().constraints();
289
290
if !constraints.is_polymorphic() {
291
return None;
292
}
293
294
// If the controlling type variable can be inferred from the type of the designated value input
295
// operand, we don't need the type suffix.
296
if constraints.use_typevar_operand() {
297
let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
298
let def_block = match func.dfg.value_def(ctrl_var) {
299
ValueDef::Result(instr, _) => func.layout.inst_block(instr),
300
ValueDef::Param(block, _) => Some(block),
301
ValueDef::Union(..) => None,
302
};
303
if def_block.is_some() && def_block == func.layout.inst_block(inst) {
304
return None;
305
}
306
}
307
308
let rtype = func.dfg.ctrl_typevar(inst);
309
assert!(
310
!rtype.is_invalid(),
311
"Polymorphic instruction must produce a result"
312
);
313
Some(rtype)
314
}
315
316
/// Write out any aliases to the given target, including indirect aliases
317
fn write_value_aliases(
318
w: &mut dyn Write,
319
aliases: &SecondaryMap<Value, Vec<Value>>,
320
target: Value,
321
indent: usize,
322
) -> fmt::Result {
323
let mut todo_stack = vec![target];
324
while let Some(target) = todo_stack.pop() {
325
for &a in &aliases[target] {
326
writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
327
todo_stack.push(a);
328
}
329
}
330
331
Ok(())
332
}
333
334
fn write_instruction(
335
w: &mut dyn Write,
336
func: &Function,
337
aliases: &SecondaryMap<Value, Vec<Value>>,
338
inst: Inst,
339
indent: usize,
340
) -> fmt::Result {
341
// Prefix containing source location, encoding, and value locations.
342
let mut s = String::with_capacity(16);
343
344
// Source location goes first.
345
let srcloc = func.srcloc(inst);
346
if !srcloc.is_default() {
347
write!(s, "{srcloc} ")?;
348
}
349
350
// Write out prefix and indent the instruction.
351
write!(w, "{s:indent$}")?;
352
353
// Write out the result values, if any.
354
let mut has_results = false;
355
for r in func.dfg.inst_results(inst) {
356
if !has_results {
357
has_results = true;
358
write!(w, "{r}")?;
359
} else {
360
write!(w, ", {r}")?;
361
}
362
if let Some(f) = &func.dfg.facts[*r] {
363
write!(w, " ! {f}")?;
364
}
365
}
366
if has_results {
367
write!(w, " = ")?;
368
}
369
370
// Then the opcode, possibly with a '.type' suffix.
371
let opcode = func.dfg.insts[inst].opcode();
372
373
match type_suffix(func, inst) {
374
Some(suf) => write!(w, "{opcode}.{suf}")?,
375
None => write!(w, "{opcode}")?,
376
}
377
378
write_operands(w, &func.dfg, inst)?;
379
writeln!(w)?;
380
381
// Value aliases come out on lines after the instruction defining the referent.
382
for r in func.dfg.inst_results(inst) {
383
write_value_aliases(w, aliases, *r, indent)?;
384
}
385
Ok(())
386
}
387
388
/// Write the operands of `inst` to `w` with a prepended space.
389
pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
390
let pool = &dfg.value_lists;
391
let jump_tables = &dfg.jump_tables;
392
let exception_tables = &dfg.exception_tables;
393
use crate::ir::instructions::InstructionData::*;
394
let ctrl_ty = dfg.ctrl_typevar(inst);
395
match dfg.insts[inst] {
396
AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
397
AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
398
LoadNoOffset { flags, arg, .. } => write!(w, "{flags} {arg}"),
399
StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
400
Unary { arg, .. } => write!(w, " {arg}"),
401
UnaryImm { imm, .. } => write!(w, " {}", {
402
let mut imm = imm;
403
if ctrl_ty.bits() != 0 {
404
imm = imm.sign_extend_from_width(ctrl_ty.bits());
405
}
406
imm
407
}),
408
UnaryIeee16 { imm, .. } => write!(w, " {imm}"),
409
UnaryIeee32 { imm, .. } => write!(w, " {imm}"),
410
UnaryIeee64 { imm, .. } => write!(w, " {imm}"),
411
UnaryGlobalValue { global_value, .. } => write!(w, " {global_value}"),
412
UnaryConst {
413
constant_handle, ..
414
} => write!(w, " {constant_handle}"),
415
Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
416
BinaryImm8 { arg, imm, .. } => write!(w, " {arg}, {imm}"),
417
BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, {
418
let mut imm = imm;
419
if ctrl_ty.bits() != 0 {
420
imm = imm.sign_extend_from_width(ctrl_ty.bits());
421
}
422
imm
423
}),
424
Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
425
MultiAry { ref args, .. } => {
426
if args.is_empty() {
427
write!(w, "")
428
} else {
429
write!(w, " {}", DisplayValues(args.as_slice(pool)))
430
}
431
}
432
NullAry { .. } => write!(w, " "),
433
TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
434
Shuffle { imm, args, .. } => {
435
let data = dfg.immediates.get(imm).expect(
436
"Expected the shuffle mask to already be inserted into the immediates table",
437
);
438
write!(w, " {}, {}, {}", args[0], args[1], data)
439
}
440
IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
441
IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, {
442
let mut imm = imm;
443
if ctrl_ty.bits() != 0 {
444
imm = imm.sign_extend_from_width(ctrl_ty.bits());
445
}
446
imm
447
}),
448
IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code),
449
FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
450
Jump { destination, .. } => {
451
write!(w, " {}", destination.display(pool))
452
}
453
Brif {
454
arg,
455
blocks: [block_then, block_else],
456
..
457
} => {
458
write!(w, " {}, {}", arg, block_then.display(pool))?;
459
write!(w, ", {}", block_else.display(pool))
460
}
461
BranchTable { arg, table, .. } => {
462
write!(w, " {}, {}", arg, jump_tables[table].display(pool))
463
}
464
Call {
465
func_ref, ref args, ..
466
} => {
467
write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))?;
468
write_user_stack_map_entries(w, dfg, inst)
469
}
470
CallIndirect {
471
sig_ref, ref args, ..
472
} => {
473
let args = args.as_slice(pool);
474
write!(
475
w,
476
" {}, {}({})",
477
sig_ref,
478
args[0],
479
DisplayValues(&args[1..])
480
)?;
481
write_user_stack_map_entries(w, dfg, inst)
482
}
483
TryCall {
484
func_ref,
485
ref args,
486
exception,
487
..
488
} => {
489
write!(
490
w,
491
" {}({}), {}",
492
func_ref,
493
DisplayValues(args.as_slice(pool)),
494
exception_tables[exception].display(pool),
495
)
496
}
497
TryCallIndirect {
498
ref args,
499
exception,
500
..
501
} => {
502
let args = args.as_slice(pool);
503
write!(
504
w,
505
" {}({}), {}",
506
args[0],
507
DisplayValues(&args[1..]),
508
exception_tables[exception].display(pool),
509
)
510
}
511
FuncAddr { func_ref, .. } => write!(w, " {func_ref}"),
512
StackLoad {
513
stack_slot, offset, ..
514
} => write!(w, " {stack_slot}{offset}"),
515
StackStore {
516
arg,
517
stack_slot,
518
offset,
519
..
520
} => write!(w, " {arg}, {stack_slot}{offset}"),
521
DynamicStackLoad {
522
dynamic_stack_slot, ..
523
} => write!(w, " {dynamic_stack_slot}"),
524
DynamicStackStore {
525
arg,
526
dynamic_stack_slot,
527
..
528
} => write!(w, " {arg}, {dynamic_stack_slot}"),
529
Load {
530
flags, arg, offset, ..
531
} => write!(w, "{flags} {arg}{offset}"),
532
Store {
533
flags,
534
args,
535
offset,
536
..
537
} => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
538
Trap { code, .. } => write!(w, " {code}"),
539
CondTrap { arg, code, .. } => write!(w, " {arg}, {code}"),
540
ExceptionHandlerAddress { block, imm, .. } => write!(w, " {block}, {imm}"),
541
}?;
542
543
let mut sep = " ; ";
544
for arg in dfg.inst_values(inst) {
545
if let ValueDef::Result(src, _) = dfg.value_def(arg) {
546
let imm = match dfg.insts[src] {
547
UnaryImm { imm, .. } => {
548
let mut imm = imm;
549
if dfg.ctrl_typevar(src).bits() != 0 {
550
imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits());
551
}
552
imm.to_string()
553
}
554
UnaryIeee16 { imm, .. } => imm.to_string(),
555
UnaryIeee32 { imm, .. } => imm.to_string(),
556
UnaryIeee64 { imm, .. } => imm.to_string(),
557
UnaryConst {
558
constant_handle,
559
opcode: Opcode::F128const,
560
} => Ieee128::try_from(dfg.constants.get(constant_handle))
561
.expect("16-byte f128 constant")
562
.to_string(),
563
UnaryConst {
564
constant_handle, ..
565
} => constant_handle.to_string(),
566
_ => continue,
567
};
568
write!(w, "{sep}{arg} = {imm}")?;
569
sep = ", ";
570
}
571
}
572
Ok(())
573
}
574
575
fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
576
let entries = match dfg.user_stack_map_entries(inst) {
577
None => return Ok(()),
578
Some(es) => es,
579
};
580
write!(w, ", stack_map=[")?;
581
let mut need_comma = false;
582
for entry in entries {
583
if need_comma {
584
write!(w, ", ")?;
585
}
586
write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?;
587
need_comma = true;
588
}
589
write!(w, "]")?;
590
Ok(())
591
}
592
593
/// Displayable slice of values.
594
struct DisplayValues<'a>(&'a [Value]);
595
596
impl<'a> fmt::Display for DisplayValues<'a> {
597
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598
for (i, val) in self.0.iter().enumerate() {
599
if i == 0 {
600
write!(f, "{val}")?;
601
} else {
602
write!(f, ", {val}")?;
603
}
604
}
605
Ok(())
606
}
607
}
608
609
#[cfg(test)]
610
mod tests {
611
use crate::cursor::{Cursor, CursorPosition, FuncCursor};
612
use crate::ir::types;
613
use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
614
use alloc::string::ToString;
615
616
#[test]
617
fn basic() {
618
let mut f = Function::new();
619
assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
620
621
f.name = UserFuncName::testcase("foo");
622
assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
623
624
f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0));
625
assert_eq!(
626
f.to_string(),
627
"function %foo() fast {\n ss0 = explicit_slot 4\n}\n"
628
);
629
630
let block = f.dfg.make_block();
631
f.layout.append_block(block);
632
assert_eq!(
633
f.to_string(),
634
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n"
635
);
636
637
f.dfg.append_block_param(block, types::I8);
638
assert_eq!(
639
f.to_string(),
640
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
641
);
642
643
f.dfg.append_block_param(block, types::F32.by(4).unwrap());
644
assert_eq!(
645
f.to_string(),
646
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
647
);
648
649
{
650
let mut cursor = FuncCursor::new(&mut f);
651
cursor.set_position(CursorPosition::After(block));
652
cursor.ins().return_(&[])
653
};
654
assert_eq!(
655
f.to_string(),
656
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n"
657
);
658
659
let mut f = Function::new();
660
f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2));
661
assert_eq!(
662
f.to_string(),
663
"function u0:0() fast {\n ss0 = explicit_slot 4, align = 4\n}\n"
664
);
665
}
666
667
#[test]
668
fn aliases() {
669
use crate::ir::InstBuilder;
670
671
let mut func = Function::new();
672
{
673
let block0 = func.dfg.make_block();
674
let mut pos = FuncCursor::new(&mut func);
675
pos.insert_block(block0);
676
677
// make some detached values for change_to_alias
678
let v0 = pos.func.dfg.append_block_param(block0, types::I32);
679
let v1 = pos.func.dfg.append_block_param(block0, types::I32);
680
let v2 = pos.func.dfg.append_block_param(block0, types::I32);
681
pos.func.dfg.detach_block_params(block0);
682
683
// alias to a param--will be printed at beginning of block defining param
684
let v3 = pos.func.dfg.append_block_param(block0, types::I32);
685
pos.func.dfg.change_to_alias(v0, v3);
686
687
// alias to an alias--should print attached to alias, not ultimate target
688
pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
689
690
// alias to a result--will be printed after instruction producing result
691
let _dummy0 = pos.ins().iconst(types::I32, 42);
692
let v4 = pos.ins().iadd(v0, v0);
693
pos.func.dfg.change_to_alias(v1, v4);
694
let _dummy1 = pos.ins().iconst(types::I32, 23);
695
let _v7 = pos.ins().iadd(v1, v1);
696
}
697
assert_eq!(
698
func.to_string(),
699
"function u0:0() fast {\nblock0(v3: i32):\n v0 -> v3\n v2 -> v0\n v4 = iconst.i32 42\n v5 = iadd v0, v0\n v1 -> v5\n v6 = iconst.i32 23\n v7 = iadd v1, v1\n}\n"
700
);
701
}
702
703
#[test]
704
fn cold_blocks() {
705
let mut func = Function::new();
706
{
707
let mut pos = FuncCursor::new(&mut func);
708
709
let block0 = pos.func.dfg.make_block();
710
pos.insert_block(block0);
711
pos.func.layout.set_cold(block0);
712
713
let block1 = pos.func.dfg.make_block();
714
pos.insert_block(block1);
715
pos.func.dfg.append_block_param(block1, types::I32);
716
pos.func.layout.set_cold(block1);
717
}
718
719
assert_eq!(
720
func.to_string(),
721
"function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
722
);
723
}
724
}
725
726