Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/write.rs
3052 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 or debug tags are present.
263
let indent = if func.rel_srclocs().is_empty() && func.debug_tags.is_empty() {
264
4
265
} else {
266
36
267
};
268
269
func_w.write_block_header(w, func, block, indent)?;
270
for a in func.dfg.block_params(block).iter().cloned() {
271
write_value_aliases(w, aliases, a, indent)?;
272
}
273
274
for inst in func.layout.block_insts(block) {
275
func_w.write_instruction(w, func, aliases, inst, indent)?;
276
}
277
278
Ok(())
279
}
280
281
//----------------------------------------------------------------------
282
//
283
// Instructions
284
285
// Should `inst` be printed with a type suffix?
286
//
287
// Polymorphic instructions may need a suffix indicating the value of the controlling type variable
288
// if it can't be trivially inferred.
289
//
290
fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
291
let inst_data = &func.dfg.insts[inst];
292
let constraints = inst_data.opcode().constraints();
293
294
if !constraints.is_polymorphic() {
295
return None;
296
}
297
298
// If the controlling type variable can be inferred from the type of the designated value input
299
// operand, we don't need the type suffix.
300
if constraints.use_typevar_operand() {
301
let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
302
let def_block = match func.dfg.value_def(ctrl_var) {
303
ValueDef::Result(instr, _) => func.layout.inst_block(instr),
304
ValueDef::Param(block, _) => Some(block),
305
ValueDef::Union(..) => None,
306
};
307
if def_block.is_some() && def_block == func.layout.inst_block(inst) {
308
return None;
309
}
310
}
311
312
let rtype = func.dfg.ctrl_typevar(inst);
313
assert!(
314
!rtype.is_invalid(),
315
"Polymorphic instruction must produce a result"
316
);
317
Some(rtype)
318
}
319
320
/// Write out any aliases to the given target, including indirect aliases
321
fn write_value_aliases(
322
w: &mut dyn Write,
323
aliases: &SecondaryMap<Value, Vec<Value>>,
324
target: Value,
325
indent: usize,
326
) -> fmt::Result {
327
let mut todo_stack = vec![target];
328
while let Some(target) = todo_stack.pop() {
329
for &a in &aliases[target] {
330
writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
331
todo_stack.push(a);
332
}
333
}
334
335
Ok(())
336
}
337
338
fn write_instruction(
339
w: &mut dyn Write,
340
func: &Function,
341
aliases: &SecondaryMap<Value, Vec<Value>>,
342
inst: Inst,
343
mut indent: usize,
344
) -> fmt::Result {
345
// Prefix containing source location, encoding, and value locations.
346
let mut s = String::with_capacity(16);
347
348
// Source location goes first.
349
let srcloc = func.srcloc(inst);
350
if !srcloc.is_default() {
351
write!(s, "{srcloc} ")?;
352
}
353
354
// Write out any debug tags.
355
write_debug_tags(w, &func, inst, &mut indent)?;
356
357
// Write out prefix and indent the instruction.
358
write!(w, "{s:indent$}")?;
359
360
// Write out the result values, if any.
361
let mut has_results = false;
362
for r in func.dfg.inst_results(inst) {
363
if !has_results {
364
has_results = true;
365
write!(w, "{r}")?;
366
} else {
367
write!(w, ", {r}")?;
368
}
369
if let Some(f) = &func.dfg.facts[*r] {
370
write!(w, " ! {f}")?;
371
}
372
}
373
if has_results {
374
write!(w, " = ")?;
375
}
376
377
// Then the opcode, possibly with a '.type' suffix.
378
let opcode = func.dfg.insts[inst].opcode();
379
380
match type_suffix(func, inst) {
381
Some(suf) => write!(w, "{opcode}.{suf}")?,
382
None => write!(w, "{opcode}")?,
383
}
384
385
write_operands(w, &func.dfg, inst)?;
386
writeln!(w)?;
387
388
// Value aliases come out on lines after the instruction defining the referent.
389
for r in func.dfg.inst_results(inst) {
390
write_value_aliases(w, aliases, *r, indent)?;
391
}
392
Ok(())
393
}
394
395
/// Write the operands of `inst` to `w` with a prepended space.
396
pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
397
let pool = &dfg.value_lists;
398
let jump_tables = &dfg.jump_tables;
399
let exception_tables = &dfg.exception_tables;
400
use crate::ir::instructions::InstructionData::*;
401
let ctrl_ty = dfg.ctrl_typevar(inst);
402
match dfg.insts[inst] {
403
AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
404
AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
405
LoadNoOffset { flags, arg, .. } => write!(w, "{flags} {arg}"),
406
StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
407
Unary { arg, .. } => write!(w, " {arg}"),
408
UnaryImm { imm, .. } => write!(w, " {}", {
409
let mut imm = imm;
410
if ctrl_ty.bits() != 0 {
411
imm = imm.sign_extend_from_width(ctrl_ty.bits());
412
}
413
imm
414
}),
415
UnaryIeee16 { imm, .. } => write!(w, " {imm}"),
416
UnaryIeee32 { imm, .. } => write!(w, " {imm}"),
417
UnaryIeee64 { imm, .. } => write!(w, " {imm}"),
418
UnaryGlobalValue { global_value, .. } => write!(w, " {global_value}"),
419
UnaryConst {
420
constant_handle, ..
421
} => write!(w, " {constant_handle}"),
422
Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
423
BinaryImm8 { arg, imm, .. } => write!(w, " {arg}, {imm}"),
424
BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, {
425
let mut imm = imm;
426
if ctrl_ty.bits() != 0 {
427
imm = imm.sign_extend_from_width(ctrl_ty.bits());
428
}
429
imm
430
}),
431
Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
432
MultiAry { ref args, .. } => {
433
if args.is_empty() {
434
write!(w, "")
435
} else {
436
write!(w, " {}", DisplayValues(args.as_slice(pool)))
437
}
438
}
439
NullAry { .. } => write!(w, " "),
440
TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
441
Shuffle { imm, args, .. } => {
442
let data = dfg.immediates.get(imm).expect(
443
"Expected the shuffle mask to already be inserted into the immediates table",
444
);
445
write!(w, " {}, {}, {}", args[0], args[1], data)
446
}
447
IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
448
IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, {
449
let mut imm = imm;
450
if ctrl_ty.bits() != 0 {
451
imm = imm.sign_extend_from_width(ctrl_ty.bits());
452
}
453
imm
454
}),
455
IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code),
456
FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
457
Jump { destination, .. } => {
458
write!(w, " {}", destination.display(pool))
459
}
460
Brif {
461
arg,
462
blocks: [block_then, block_else],
463
..
464
} => {
465
write!(w, " {}, {}", arg, block_then.display(pool))?;
466
write!(w, ", {}", block_else.display(pool))
467
}
468
BranchTable { arg, table, .. } => {
469
write!(w, " {}, {}", arg, jump_tables[table].display(pool))
470
}
471
Call {
472
func_ref, ref args, ..
473
} => {
474
write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))?;
475
write_user_stack_map_entries(w, dfg, inst)
476
}
477
CallIndirect {
478
sig_ref, ref args, ..
479
} => {
480
let args = args.as_slice(pool);
481
write!(
482
w,
483
" {}, {}({})",
484
sig_ref,
485
args[0],
486
DisplayValues(&args[1..])
487
)?;
488
write_user_stack_map_entries(w, dfg, inst)
489
}
490
TryCall {
491
func_ref,
492
ref args,
493
exception,
494
..
495
} => {
496
write!(
497
w,
498
" {}({}), {}",
499
func_ref,
500
DisplayValues(args.as_slice(pool)),
501
exception_tables[exception].display(pool),
502
)
503
}
504
TryCallIndirect {
505
ref args,
506
exception,
507
..
508
} => {
509
let args = args.as_slice(pool);
510
write!(
511
w,
512
" {}({}), {}",
513
args[0],
514
DisplayValues(&args[1..]),
515
exception_tables[exception].display(pool),
516
)
517
}
518
FuncAddr { func_ref, .. } => write!(w, " {func_ref}"),
519
StackLoad {
520
stack_slot, offset, ..
521
} => write!(w, " {stack_slot}{offset}"),
522
StackStore {
523
arg,
524
stack_slot,
525
offset,
526
..
527
} => write!(w, " {arg}, {stack_slot}{offset}"),
528
DynamicStackLoad {
529
dynamic_stack_slot, ..
530
} => write!(w, " {dynamic_stack_slot}"),
531
DynamicStackStore {
532
arg,
533
dynamic_stack_slot,
534
..
535
} => write!(w, " {arg}, {dynamic_stack_slot}"),
536
Load {
537
flags, arg, offset, ..
538
} => write!(w, "{flags} {arg}{offset}"),
539
Store {
540
flags,
541
args,
542
offset,
543
..
544
} => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
545
Trap { code, .. } => write!(w, " {code}"),
546
CondTrap { arg, code, .. } => write!(w, " {arg}, {code}"),
547
ExceptionHandlerAddress { block, imm, .. } => write!(w, " {block}, {imm}"),
548
}?;
549
550
let mut sep = " ; ";
551
for arg in dfg.inst_values(inst) {
552
if let ValueDef::Result(src, _) = dfg.value_def(arg) {
553
let imm = match dfg.insts[src] {
554
UnaryImm { imm, .. } => {
555
let mut imm = imm;
556
if dfg.ctrl_typevar(src).bits() != 0 {
557
imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits());
558
}
559
imm.to_string()
560
}
561
UnaryIeee16 { imm, .. } => imm.to_string(),
562
UnaryIeee32 { imm, .. } => imm.to_string(),
563
UnaryIeee64 { imm, .. } => imm.to_string(),
564
UnaryConst {
565
constant_handle,
566
opcode: Opcode::F128const,
567
} => Ieee128::try_from(dfg.constants.get(constant_handle))
568
.expect("16-byte f128 constant")
569
.to_string(),
570
UnaryConst {
571
constant_handle, ..
572
} => constant_handle.to_string(),
573
_ => continue,
574
};
575
write!(w, "{sep}{arg} = {imm}")?;
576
sep = ", ";
577
}
578
}
579
Ok(())
580
}
581
582
fn write_debug_tags(
583
w: &mut dyn Write,
584
func: &Function,
585
inst: Inst,
586
indent: &mut usize,
587
) -> fmt::Result {
588
let tags = func.debug_tags.get(inst);
589
if !tags.is_empty() {
590
let tags = tags
591
.iter()
592
.map(|tag| format!("{tag}"))
593
.collect::<Vec<_>>()
594
.join(", ");
595
let s = format!("<{tags}> ");
596
write!(w, "{s}")?;
597
*indent = indent.saturating_sub(s.len());
598
}
599
Ok(())
600
}
601
602
fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
603
let entries = match dfg.user_stack_map_entries(inst) {
604
None => return Ok(()),
605
Some(es) => es,
606
};
607
write!(w, ", stack_map=[")?;
608
let mut need_comma = false;
609
for entry in entries {
610
if need_comma {
611
write!(w, ", ")?;
612
}
613
write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?;
614
need_comma = true;
615
}
616
write!(w, "]")?;
617
Ok(())
618
}
619
620
/// Displayable slice of values.
621
struct DisplayValues<'a>(&'a [Value]);
622
623
impl<'a> fmt::Display for DisplayValues<'a> {
624
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
625
for (i, val) in self.0.iter().enumerate() {
626
if i == 0 {
627
write!(f, "{val}")?;
628
} else {
629
write!(f, ", {val}")?;
630
}
631
}
632
Ok(())
633
}
634
}
635
636
#[cfg(test)]
637
mod tests {
638
use crate::cursor::{Cursor, CursorPosition, FuncCursor};
639
use crate::ir::types;
640
use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
641
use alloc::string::ToString;
642
643
#[test]
644
fn basic() {
645
let mut f = Function::new();
646
assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
647
648
f.name = UserFuncName::testcase("foo");
649
assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
650
651
f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0));
652
assert_eq!(
653
f.to_string(),
654
"function %foo() fast {\n ss0 = explicit_slot 4\n}\n"
655
);
656
657
let block = f.dfg.make_block();
658
f.layout.append_block(block);
659
assert_eq!(
660
f.to_string(),
661
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n"
662
);
663
664
f.dfg.append_block_param(block, types::I8);
665
assert_eq!(
666
f.to_string(),
667
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
668
);
669
670
f.dfg.append_block_param(block, types::F32.by(4).unwrap());
671
assert_eq!(
672
f.to_string(),
673
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
674
);
675
676
{
677
let mut cursor = FuncCursor::new(&mut f);
678
cursor.set_position(CursorPosition::After(block));
679
cursor.ins().return_(&[])
680
};
681
assert_eq!(
682
f.to_string(),
683
"function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n"
684
);
685
686
let mut f = Function::new();
687
f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2));
688
assert_eq!(
689
f.to_string(),
690
"function u0:0() fast {\n ss0 = explicit_slot 4, align = 4\n}\n"
691
);
692
}
693
694
#[test]
695
fn aliases() {
696
use crate::ir::InstBuilder;
697
698
let mut func = Function::new();
699
{
700
let block0 = func.dfg.make_block();
701
let mut pos = FuncCursor::new(&mut func);
702
pos.insert_block(block0);
703
704
// make some detached values for change_to_alias
705
let v0 = pos.func.dfg.append_block_param(block0, types::I32);
706
let v1 = pos.func.dfg.append_block_param(block0, types::I32);
707
let v2 = pos.func.dfg.append_block_param(block0, types::I32);
708
pos.func.dfg.detach_block_params(block0);
709
710
// alias to a param--will be printed at beginning of block defining param
711
let v3 = pos.func.dfg.append_block_param(block0, types::I32);
712
pos.func.dfg.change_to_alias(v0, v3);
713
714
// alias to an alias--should print attached to alias, not ultimate target
715
pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
716
717
// alias to a result--will be printed after instruction producing result
718
let _dummy0 = pos.ins().iconst(types::I32, 42);
719
let v4 = pos.ins().iadd(v0, v0);
720
pos.func.dfg.change_to_alias(v1, v4);
721
let _dummy1 = pos.ins().iconst(types::I32, 23);
722
let _v7 = pos.ins().iadd(v1, v1);
723
}
724
assert_eq!(
725
func.to_string(),
726
"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"
727
);
728
}
729
730
#[test]
731
fn cold_blocks() {
732
let mut func = Function::new();
733
{
734
let mut pos = FuncCursor::new(&mut func);
735
736
let block0 = pos.func.dfg.make_block();
737
pos.insert_block(block0);
738
pos.func.layout.set_cold(block0);
739
740
let block1 = pos.func.dfg.make_block();
741
pos.insert_block(block1);
742
pos.func.dfg.append_block_param(block1, types::I32);
743
pos.func.layout.set_cold(block1);
744
}
745
746
assert_eq!(
747
func.to_string(),
748
"function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
749
);
750
}
751
}
752
753