From 1f4718ba6a9a7d199227989d30b87131dafa3f3c Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Mon, 3 Feb 2025 15:11:25 +1100 Subject: [PATCH 1/8] print statements --- src/mk_graph.rs | 57 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/mk_graph.rs b/src/mk_graph.rs index 2c57e3e..98d3a50 100644 --- a/src/mk_graph.rs +++ b/src/mk_graph.rs @@ -5,7 +5,7 @@ use std::{ io::{self, Write}, }; -use dot_writer::{Attributes, Color, DotWriter, Scope, Style}; +use dot_writer::{Attributes, Color, DotWriter, Scope, Shape, Style}; extern crate rustc_middle; use rustc_middle::ty::TyCtxt; @@ -14,7 +14,7 @@ extern crate stable_mir; use rustc_session::config::{OutFileName, OutputType}; extern crate rustc_session; -use stable_mir::mir::{BasicBlock, ConstOperand, Operand, Place, TerminatorKind, UnwindAction}; +use stable_mir::mir::{BasicBlock, ConstOperand, Operand, Place, Statement, StatementKind, TerminatorKind, UnwindAction}; use stable_mir::ty::Ty; use crate::{ @@ -51,6 +51,7 @@ impl SmirJson<'_> { let mut graph = writer.digraph(); graph.set_label(&self.name[..]); + graph.node_attributes().set_shape(Shape::Rectangle); let func_map: HashMap = self .functions @@ -87,17 +88,21 @@ impl SmirJson<'_> { let name = &item.symbol_name; let this_block = block_name(name, node_id); let mut n = cluster.node_named(&this_block); + + let mut label_strs: Vec = b.statements.iter().map(|s| render_stmt(s)).collect(); // TODO: render statements and terminator as text label (with line breaks) // switch on terminator kind, add inner and out-edges according to terminator use TerminatorKind::*; match &b.terminator.kind { Goto { target } => { - n.set_label("Goto"); + label_strs.push("Goto".to_string()); + n.set_label(&label_strs.join("\\l")); drop(n); // so we can borrow `cluster` again below cluster.edge(&this_block, block_name(name, *target)); } SwitchInt { discr: _, targets } => { - n.set_label("SwitchInt"); + label_strs.push("SwitchInt".to_string()); + n.set_label(&label_strs.join("\\l")); drop(n); // so we can borrow `cluster` again below for (d, t) in targets.clone().branches() { cluster @@ -114,23 +119,28 @@ impl SmirJson<'_> { .set_label("other"); } Resume {} => { - n.set_label("Resume"); + label_strs.push("Resume".to_string()); + n.set_label(&label_strs.join("\\l")); } Abort {} => { - n.set_label("Abort"); + label_strs.push("Abort".to_string()); + n.set_label(&label_strs.join("\\l")); } Return {} => { - n.set_label("Return"); + label_strs.push("Return".to_string()); + n.set_label(&label_strs.join("\\l")); } Unreachable {} => { - n.set_label("Unreachable"); + label_strs.push("Unreachable".to_string()); + n.set_label(&label_strs.join("\\l")); } TerminatorKind::Drop { place, target, unwind, } => { - n.set_label(&format!("Drop {}", show_place(place))); + label_strs.push(format!("Drop {}", show_place(place))); + n.set_label(&label_strs.join("\\l")); drop(n); if let UnwindAction::Cleanup(t) = unwind { cluster @@ -147,7 +157,8 @@ impl SmirJson<'_> { target, unwind, } => { - n.set_label("Call()"); + label_strs.push("Call".to_string()); + n.set_label(&label_strs.join("\\l")); drop(n); if let UnwindAction::Cleanup(t) = unwind { cluster @@ -167,7 +178,8 @@ impl SmirJson<'_> { // Code for that is therefore separated into its own second function below. } Assert { target, .. } => { - n.set_label("Assert"); + label_strs.push(format!("Assert {}", "...")); + n.set_label(&label_strs.join("\\l")); drop(n); cluster.edge(&this_block, block_name(name, *target)); } @@ -176,7 +188,8 @@ impl SmirJson<'_> { unwind, .. } => { - n.set_label("Inline ASM"); + label_strs.push("Inline ASM".to_string()); + n.set_label(&label_strs.join("\\l")); drop(n); if let Some(t) = destination { cluster.edge(&this_block, block_name(name, *t)); @@ -364,3 +377,23 @@ fn block_name(function_name: &String, id: usize) -> String { function_name.hash(&mut h); format!("X{:x}_{}", h.finish(), id) } + +fn render_stmt(s: &Statement) -> String { + use StatementKind::*; + match &s.kind { + Assign(p, _v) => format!("{} <- {}", show_place(&p), ""), + FakeRead(_cause, p) => format!("Fake-Read {}", show_place(&p)), + SetDiscriminant { place, variant_index: _ } => format!("set discriminant {}({})", show_place(&place), "..."), + Deinit(p) => format!("Deinit {}", show_place(&p)), + StorageLive(l) => format!("Storage Live _{}", &l), + StorageDead(l) => format!("Storage Dead _{}", &l), + Retag(_retag_kind, p) => format!("Retag {}", show_place(&p)), + PlaceMention(p) => format!("Mention {}", show_place(&p)), + AscribeUserType {place, projections: _, variance: _} => + format!("Ascribe {}: {}, {}", show_place(&place), "proj", "variance"), + Coverage(_) => format!("Coverage"), + Intrinsic(_intr) => format!("Intrinsic {}", "non-diverging-intrinsic"), + ConstEvalCounter{} => "ConstEvalCounter".to_string(), + Nop{}=> "Nop".to_string(), + } +} From 182d877ae7b35d5cc0fd732f7b56526f2e00f6f3 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Mon, 3 Feb 2025 15:23:30 +1100 Subject: [PATCH 2/8] avoid explicit node drop by reversing order, colours --- src/mk_graph.rs | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/mk_graph.rs b/src/mk_graph.rs index 98d3a50..34aeb24 100644 --- a/src/mk_graph.rs +++ b/src/mk_graph.rs @@ -77,8 +77,10 @@ impl SmirJson<'_> { MonoItemKind::MonoItemFn { name, body, id: _ } => { let mut c = graph.cluster(); c.set_label(&name_lines(&name)); + c.set_style(Style::Filled); if is_unqualified(&name) { - c.set_style(Style::Filled); + c.set_color(Color::PaleGreen); + } else { c.set_color(Color::LightGrey); } @@ -87,7 +89,6 @@ impl SmirJson<'_> { |cluster: &mut Scope<'_, '_>, node_id: usize, b: &BasicBlock| { let name = &item.symbol_name; let this_block = block_name(name, node_id); - let mut n = cluster.node_named(&this_block); let mut label_strs: Vec = b.statements.iter().map(|s| render_stmt(s)).collect(); // TODO: render statements and terminator as text label (with line breaks) @@ -96,14 +97,10 @@ impl SmirJson<'_> { match &b.terminator.kind { Goto { target } => { label_strs.push("Goto".to_string()); - n.set_label(&label_strs.join("\\l")); - drop(n); // so we can borrow `cluster` again below cluster.edge(&this_block, block_name(name, *target)); } SwitchInt { discr: _, targets } => { label_strs.push("SwitchInt".to_string()); - n.set_label(&label_strs.join("\\l")); - drop(n); // so we can borrow `cluster` again below for (d, t) in targets.clone().branches() { cluster .edge(&this_block, block_name(name, t)) @@ -120,19 +117,15 @@ impl SmirJson<'_> { } Resume {} => { label_strs.push("Resume".to_string()); - n.set_label(&label_strs.join("\\l")); } Abort {} => { label_strs.push("Abort".to_string()); - n.set_label(&label_strs.join("\\l")); } Return {} => { label_strs.push("Return".to_string()); - n.set_label(&label_strs.join("\\l")); } Unreachable {} => { label_strs.push("Unreachable".to_string()); - n.set_label(&label_strs.join("\\l")); } TerminatorKind::Drop { place, @@ -140,8 +133,6 @@ impl SmirJson<'_> { unwind, } => { label_strs.push(format!("Drop {}", show_place(place))); - n.set_label(&label_strs.join("\\l")); - drop(n); if let UnwindAction::Cleanup(t) = unwind { cluster .edge(&this_block, block_name(name, *t)) @@ -158,8 +149,6 @@ impl SmirJson<'_> { unwind, } => { label_strs.push("Call".to_string()); - n.set_label(&label_strs.join("\\l")); - drop(n); if let UnwindAction::Cleanup(t) = unwind { cluster .edge(&this_block, block_name(name, *t)) @@ -179,8 +168,6 @@ impl SmirJson<'_> { } Assert { target, .. } => { label_strs.push(format!("Assert {}", "...")); - n.set_label(&label_strs.join("\\l")); - drop(n); cluster.edge(&this_block, block_name(name, *target)); } InlineAsm { @@ -189,8 +176,6 @@ impl SmirJson<'_> { .. } => { label_strs.push("Inline ASM".to_string()); - n.set_label(&label_strs.join("\\l")); - drop(n); if let Some(t) = destination { cluster.edge(&this_block, block_name(name, *t)); } @@ -202,6 +187,8 @@ impl SmirJson<'_> { } } } + let mut n = cluster.node_named(&this_block); + n.set_label(&label_strs.join("\\l")); }; let process_blocks = From e823d0cb14569181f4e184ba2377b7cc812345da Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Mon, 3 Feb 2025 15:42:01 +1100 Subject: [PATCH 3/8] break long cluster names at 26 char and whitespace: --- src/mk_graph.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mk_graph.rs b/src/mk_graph.rs index 34aeb24..7e4c49e 100644 --- a/src/mk_graph.rs +++ b/src/mk_graph.rs @@ -345,8 +345,8 @@ fn function_string(f: FnSymType) -> String { fn name_lines(name: &str) -> String { name.split_inclusive(" ") - .flat_map(|s| s.split_inclusive("::")) - .map(|s| s.to_string()) + .flat_map(|s| s.as_bytes().chunks(25)) + .map(|bs| core::str::from_utf8(bs).unwrap().to_string()) .collect::>() .join("\\n") } From dc45306e1fde02a4ed7837e730e3eaf7c4f14165 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Thu, 6 Feb 2025 10:54:43 +1100 Subject: [PATCH 4/8] clippy and fmt --- src/mk_graph.rs | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/mk_graph.rs b/src/mk_graph.rs index 7e4c49e..764d128 100644 --- a/src/mk_graph.rs +++ b/src/mk_graph.rs @@ -14,7 +14,10 @@ extern crate stable_mir; use rustc_session::config::{OutFileName, OutputType}; extern crate rustc_session; -use stable_mir::mir::{BasicBlock, ConstOperand, Operand, Place, Statement, StatementKind, TerminatorKind, UnwindAction}; +use stable_mir::mir::{ + BasicBlock, ConstOperand, Operand, Place, Statement, StatementKind, TerminatorKind, + UnwindAction, +}; use stable_mir::ty::Ty; use crate::{ @@ -90,7 +93,8 @@ impl SmirJson<'_> { let name = &item.symbol_name; let this_block = block_name(name, node_id); - let mut label_strs: Vec = b.statements.iter().map(|s| render_stmt(s)).collect(); + let mut label_strs: Vec = + b.statements.iter().map(render_stmt).collect(); // TODO: render statements and terminator as text label (with line breaks) // switch on terminator kind, add inner and out-edges according to terminator use TerminatorKind::*; @@ -368,19 +372,25 @@ fn block_name(function_name: &String, id: usize) -> String { fn render_stmt(s: &Statement) -> String { use StatementKind::*; match &s.kind { - Assign(p, _v) => format!("{} <- {}", show_place(&p), ""), - FakeRead(_cause, p) => format!("Fake-Read {}", show_place(&p)), - SetDiscriminant { place, variant_index: _ } => format!("set discriminant {}({})", show_place(&place), "..."), - Deinit(p) => format!("Deinit {}", show_place(&p)), + Assign(p, _v) => format!("{} <- {}", show_place(p), ""), + FakeRead(_cause, p) => format!("Fake-Read {}", show_place(p)), + SetDiscriminant { + place, + variant_index: _, + } => format!("set discriminant {}({})", show_place(place), "..."), + Deinit(p) => format!("Deinit {}", show_place(p)), StorageLive(l) => format!("Storage Live _{}", &l), StorageDead(l) => format!("Storage Dead _{}", &l), - Retag(_retag_kind, p) => format!("Retag {}", show_place(&p)), - PlaceMention(p) => format!("Mention {}", show_place(&p)), - AscribeUserType {place, projections: _, variance: _} => - format!("Ascribe {}: {}, {}", show_place(&place), "proj", "variance"), - Coverage(_) => format!("Coverage"), + Retag(_retag_kind, p) => format!("Retag {}", show_place(p)), + PlaceMention(p) => format!("Mention {}", show_place(p)), + AscribeUserType { + place, + projections: _, + variance: _, + } => format!("Ascribe {}: {}, {}", show_place(place), "proj", "variance"), + Coverage(_) => "Coverage".to_string(), Intrinsic(_intr) => format!("Intrinsic {}", "non-diverging-intrinsic"), - ConstEvalCounter{} => "ConstEvalCounter".to_string(), - Nop{}=> "Nop".to_string(), + ConstEvalCounter {} => "ConstEvalCounter".to_string(), + Nop {} => "Nop".to_string(), } } From 83516653c6e413c046c87fbd4d0610ce1227ac68 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Thu, 6 Feb 2025 11:16:40 +1100 Subject: [PATCH 5/8] Replace stale section about tests in README.md by current integration test description --- tests/test_basic.rs | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 tests/test_basic.rs diff --git a/tests/test_basic.rs b/tests/test_basic.rs deleted file mode 100644 index a0c6a87..0000000 --- a/tests/test_basic.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod common; -use common::*; -use smir_pretty::{print_all_items_verbose, stable_mir_driver}; - -#[test] -fn test_pretty_print() { - stable_mir_driver( - &vec![ - "rustc".into(), - get_resource_path(vec!["tests", "resources", "println.rs"]), - ], - print_all_items_verbose, - ); -} From 2f4644d58819556a40b17d09c862f68388b35bf8 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Thu, 6 Feb 2025 11:17:04 +1100 Subject: [PATCH 6/8] replace all mentions of `smir_pretty`, including the crate name --- .github/workflows/test.yml | 4 ++-- Cargo.lock | 10 ++++----- Cargo.toml | 2 +- README.md | 43 +++++++++++++------------------------- src/main.rs | 2 +- src/printer.rs | 2 +- 6 files changed, 25 insertions(+), 38 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 345dd65..d46a369 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: with: toolchain: nightly-2024-11-29 # Hardcoded version, same as is in the build.rs - - name: 'Build smir_pretty' # rustfmt documentation claims it is unstable on code that doesn't build + - name: 'Build stable-mir-json' # rustfmt documentation claims it is unstable on code that doesn't build run: | cargo build -vv @@ -60,7 +60,7 @@ jobs: with: toolchain: nightly-2024-11-29 # Hardcoded version, same as is in the build.rs - - name: 'Build smir_pretty' + - name: 'Build stable-mir-json' run: | # Warning check should be redundant since code-quality runs first RUSTFLAGS='--deny warnings' cargo build -vv diff --git a/Cargo.lock b/Cargo.lock index a0fda01..ed4b811 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ dependencies = [ ] [[package]] -name = "smir_pretty" +name = "stable-mir-pretty" version = "0.1.0" dependencies = [ "dot-writer", @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -90,6 +90,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" diff --git a/Cargo.toml b/Cargo.toml index ac52992..d0e519c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "smir_pretty" +name = "stable-mir-pretty" version = "0.1.0" edition = "2021" diff --git a/README.md b/README.md index 684c938..7a5b338 100644 --- a/README.md +++ b/README.md @@ -42,38 +42,25 @@ To ensure code quality, all code is required to pass `cargo clippy` and `cargo f ## Tests -### Running the Tests - -To run the tests, do the following: - -```shell -make generate_ui_tests -``` +Integration tests for `stable-mir-pretty` consist of compiling a number of (small) +programs with the wrapper compiler, and checking the output against expected JSON +data ("golden" tests). -This will generate four outputs: +The tests are stored [in `src/tests/integration/programs`](./src/tests/integration/programs). -| Path | Comment | -| --- | --- | -| `deps/rust/tests/ui/upstream` | Upstream `rustc` test outputs | -| `deps/rust/tests_ui_upstream.log` | Upstream test log | -| `deps/rust/tests/ui/smir` | `smir_pretty` test outputs (including `.smir.json` files) | -| `deps/rust/tests_ui_smir.log` | `smir_pretty` test log | +To compensate for any non-determinism in the output, the JSON file is first processed +to sort the array contents and remove data which changes with dependencies (such as +the crate hash suffix in the symbol names). -### Test Rationale +The JSON post-processing is performed [with `jq` using the script in `src/tests/integration/normalise-filter.jq`](./src/tests/integration/normalise-filter.jq). -Since this crate is a Stable MIR serialization tool, there are two main features we are interested in: +Some tests have non-deterministic output and are therefore expected to fail. +These tests are stored [in `src/tests/integration/failing`](./src/tests/integration/failing). -1. the serialization facilities should be stable (i.e. not crash) -2. the serialized output should be correct - -Since this tool is currently in its early stages, it is hard to test (2). -However, to test (1) and to make progress towards (2), we currently do the following: - -1. in the rustc test suite, we gather all of the run-pass tests, i.e., tests where the compiler is able to generate a binary _and_ subsequently execute the binary such that it exits successfully -2. we extract the test runner invocation from the `x.py test` command -3. we execute the test runner with upstream `rustc` against the test inputs from (1) --- this gives us a baseline on which tests should pass/fail -4. we re-execute the test runner but use our wrapper binary against the test inputs from (1) --- this generates the corresponding `.smir.json` files and shows us where any regressions occur +### Running the Tests +To run the tests, do the following: -**NOTE:** In order to speed up test time, we setup the test runner, by default, such that it skips codegen and compiler-generated binary execution. -**NOTE:** Points (1,4) also means that our test _outputs_ from this phase can become test _inputs_ for KMIR. +```shell +make integration-test +``` diff --git a/src/main.rs b/src/main.rs index ea72d5c..1cd2e11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ pub mod driver; pub mod printer; use driver::stable_mir_driver; use printer::emit_smir; -use smir_pretty::mk_graph::emit_dotfile; +use stable_mir_pretty::mk_graph::emit_dotfile; fn main() { let mut args: Vec = env::args().collect(); diff --git a/src/printer.rs b/src/printer.rs index a851334..c3d66ca 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -605,7 +605,7 @@ fn collect_ty(val_collector: &mut InternedValueCollector, val: stable_mir::ty::T // HACK: std::fmt::Arguments has escaping bounds and will error if trying to get the layout. // We will just ban producing the layout for now see, this issue for more info - // https://github.com/runtimeverification/smir_pretty/issues/27 + // https://github.com/runtimeverification/stable-mir-json/issues/27 let maybe_layout = match val.kind() { stable_mir::ty::TyKind::RigidTy(Adt(AdtDef(def_id_stable), _)) => { let def_id_internal = rustc_internal::internal(val_collector.tcx, def_id_stable); From ec038b3b045fbe7272f1ae22cbc4ce84608bb6b1 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Thu, 6 Feb 2025 11:23:31 +1100 Subject: [PATCH 7/8] use underscore in crate name --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed4b811..ac721b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ dependencies = [ ] [[package]] -name = "stable-mir-pretty" +name = "stable_mir_pretty" version = "0.1.0" dependencies = [ "dot-writer", diff --git a/Cargo.toml b/Cargo.toml index d0e519c..088f98d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "stable-mir-pretty" +name = "stable_mir_pretty" version = "0.1.0" edition = "2021" From 340d5b47616f0f3378694e174c248897f94272c8 Mon Sep 17 00:00:00 2001 From: Jost Berthold Date: Thu, 6 Feb 2025 13:59:15 +1100 Subject: [PATCH 8/8] more statement printing, refactoring to use a trait --- src/mk_graph.rs | 193 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 143 insertions(+), 50 deletions(-) diff --git a/src/mk_graph.rs b/src/mk_graph.rs index 764d128..cbc4425 100644 --- a/src/mk_graph.rs +++ b/src/mk_graph.rs @@ -15,10 +15,10 @@ use rustc_session::config::{OutFileName, OutputType}; extern crate rustc_session; use stable_mir::mir::{ - BasicBlock, ConstOperand, Operand, Place, Statement, StatementKind, TerminatorKind, - UnwindAction, + AggregateKind, BasicBlock, ConstOperand, Mutability, NonDivergingIntrinsic, NullOp, Operand, + Place, Rvalue, Statement, StatementKind, TerminatorKind, UnwindAction, }; -use stable_mir::ty::Ty; +use stable_mir::ty::{IndexedVal, Ty}; use crate::{ printer::{collect_smir, FnSymType, SmirJson}, @@ -103,8 +103,8 @@ impl SmirJson<'_> { label_strs.push("Goto".to_string()); cluster.edge(&this_block, block_name(name, *target)); } - SwitchInt { discr: _, targets } => { - label_strs.push("SwitchInt".to_string()); + SwitchInt { discr, targets } => { + label_strs.push(format!("SwitchInt {}", discr.label())); for (d, t) in targets.clone().branches() { cluster .edge(&this_block, block_name(name, t)) @@ -136,7 +136,7 @@ impl SmirJson<'_> { target, unwind, } => { - label_strs.push(format!("Drop {}", show_place(place))); + label_strs.push(format!("Drop {}", place.label())); if let UnwindAction::Cleanup(t) = unwind { cluster .edge(&this_block, block_name(name, *t)) @@ -160,7 +160,7 @@ impl SmirJson<'_> { .set_label("Cleanup"); } if let Some(t) = target { - let dest = show_place(destination); + let dest = destination.label(); cluster .edge(&this_block, block_name(name, *t)) .attributes() @@ -170,8 +170,24 @@ impl SmirJson<'_> { // The call edge has to be drawn outside the cluster, outside this function (cluster borrows &mut graph)! // Code for that is therefore separated into its own second function below. } - Assert { target, .. } => { - label_strs.push(format!("Assert {}", "...")); + Assert { + cond, + expected, + msg: _, + target, + unwind, + } => { + label_strs.push(format!( + "Assert {} == {}", + cond.label(), + expected + )); + if let UnwindAction::Cleanup(t) = unwind { + cluster + .edge(&this_block, block_name(name, *t)) + .attributes() + .set_label("Cleanup"); + } cluster.edge(&this_block, block_name(name, *target)); } InlineAsm { @@ -192,6 +208,7 @@ impl SmirJson<'_> { } } let mut n = cluster.node_named(&this_block); + label_strs.push("".to_string()); n.set_label(&label_strs.join("\\l")); }; @@ -252,24 +269,16 @@ impl SmirJson<'_> { } Operand::Copy(place) => graph.edge( &this_block, - format!( - "{}: {}", - &this_block, - show_place(place) - ), + format!("{}: {}", &this_block, place.label()), ), Operand::Move(place) => graph.edge( &this_block, - format!( - "{}: {}", - &this_block, - show_place(place) - ), + format!("{}: {}", &this_block, place.label()), ), }; let arg_str = args .iter() - .map(show_op) + .map(|op| op.label()) .collect::>() .join(","); e.attributes().set_label(&arg_str); @@ -315,26 +324,6 @@ impl SmirJson<'_> { } } -fn show_op(op: &Operand) -> String { - match op { - Operand::Constant(ConstOperand { const_, .. }) => format!("const :: {}", const_.ty()), - Operand::Copy(place) => show_place(place), - Operand::Move(place) => show_place(place), - } -} - -fn show_place(p: &Place) -> String { - format!( - "_{}{}", - p.local, - if !p.projection.is_empty() { - "(...)" - } else { - "" - } - ) -} - fn is_unqualified(name: &str) -> bool { !name.contains("::") } @@ -372,25 +361,129 @@ fn block_name(function_name: &String, id: usize) -> String { fn render_stmt(s: &Statement) -> String { use StatementKind::*; match &s.kind { - Assign(p, _v) => format!("{} <- {}", show_place(p), ""), - FakeRead(_cause, p) => format!("Fake-Read {}", show_place(p)), + Assign(p, v) => format!("{} <- {}", p.label(), v.label()), + FakeRead(_cause, p) => format!("Fake-Read {}", p.label()), SetDiscriminant { place, - variant_index: _, - } => format!("set discriminant {}({})", show_place(place), "..."), - Deinit(p) => format!("Deinit {}", show_place(p)), + variant_index, + } => format!( + "set discriminant {}({})", + place.label(), + variant_index.to_index() + ), + Deinit(p) => format!("Deinit {}", p.label()), StorageLive(l) => format!("Storage Live _{}", &l), StorageDead(l) => format!("Storage Dead _{}", &l), - Retag(_retag_kind, p) => format!("Retag {}", show_place(p)), - PlaceMention(p) => format!("Mention {}", show_place(p)), + Retag(_retag_kind, p) => format!("Retag {}", p.label()), + PlaceMention(p) => format!("Mention {}", p.label()), AscribeUserType { place, - projections: _, + projections, variance: _, - } => format!("Ascribe {}: {}, {}", show_place(place), "proj", "variance"), + } => format!("Ascribe {}.{}", place.label(), projections.base), Coverage(_) => "Coverage".to_string(), - Intrinsic(_intr) => format!("Intrinsic {}", "non-diverging-intrinsic"), + Intrinsic(intr) => format!("Intr: {}", intr.label()), ConstEvalCounter {} => "ConstEvalCounter".to_string(), Nop {} => "Nop".to_string(), } } + +/// Rendering things as part of graph node labels +trait GraphLabelString { + fn label(&self) -> String; +} + +impl GraphLabelString for Operand { + fn label(&self) -> String { + match &self { + Operand::Constant(ConstOperand { const_, .. }) => format!("const :: {}", const_.ty()), + Operand::Copy(place) => place.label(), + Operand::Move(place) => place.label(), + } + } +} + +impl GraphLabelString for Place { + fn label(&self) -> String { + format!( + "_{}{}", + &self.local, + if !&self.projection.is_empty() { + "(...)" + } else { + "" + } + ) + } +} + +impl GraphLabelString for AggregateKind { + fn label(&self) -> String { + use AggregateKind::*; + use Mutability::*; + match &self { + Array(_ty) => "Array".to_string(), + Tuple {} => "Tuple".to_string(), + Adt(_, _, _, _, _) => "Adt".to_string(), // (AdtDef, VariantIdx, GenericArgs, Option, Option), + Closure(_, _) => "Closure".to_string(), // (ClosureDef, GenericArgs), + Coroutine(_, _, _) => "Coroutine".to_string(), // (CoroutineDef, GenericArgs, Movability), + // CoroutineClosure{} => "CoroutineClosure".to_string(), // (CoroutineClosureDef, GenericArgs), + RawPtr(ty, Mut) => format!("*mut ({})", ty), + RawPtr(ty, Not) => format!("*({})", ty), + } + } +} + +impl GraphLabelString for Rvalue { + fn label(&self) -> String { + use Rvalue::*; + match &self { + AddressOf(kind, p) => format!("&{:?} {}", kind, p.label()), + Aggregate(kind, operands) => { + let os: Vec = operands.iter().map(|op| op.label()).collect(); + format!("{} ({})", kind.label(), os.join(", ")) + } + BinaryOp(binop, op1, op2) => format!("{:?}({}, {})", binop, op1.label(), op2.label()), + Cast(kind, op, _ty) => format!("Cast-{:?} {}", kind, op.label()), + CheckedBinaryOp(binop, op1, op2) => { + format!("chkd-{:?}({}, {})", binop, op1.label(), op2.label()) + } + CopyForDeref(p) => format!("CopyForDeref({})", p.label()), + Discriminant(p) => format!("Discriminant({})", p.label()), + Len(p) => format!("Len({})", p.label()), + Ref(region, borrowkind, p) => { + format!("{:?} ({:?}): {}", region.kind, borrowkind, p.label()) + } + Repeat(op, _ty_const) => format!("Repeat {}", op.label()), + ShallowInitBox(op, _ty) => format!("ShallowInitBox({})", op.label()), + ThreadLocalRef(_item) => "ThreadLocalRef".to_string(), + NullaryOp(nullop, ty) => format!("{} :: {}", nullop.label(), ty), + UnaryOp(unop, op) => format!("{:?}({})", unop, op.label()), + Use(op) => format!("Use({})", op.label()), + } + } +} + +impl GraphLabelString for NullOp { + fn label(&self) -> String { + match &self { + NullOp::OffsetOf(_vec) => "OffsetOf(..)".to_string(), + other => format!("{:?}", other), + } + } +} + +impl GraphLabelString for NonDivergingIntrinsic { + fn label(&self) -> String { + use NonDivergingIntrinsic::*; + match &self { + Assume(op) => format!("Assume {}", op.label()), + CopyNonOverlapping(c) => format!( + "CopyNonOverlapping: {} <- {}({}))", + c.dst.label(), + c.src.label(), + c.count.label() + ), + } + } +}