Skip to content

Commit 5d1a8e1

Browse files
authored
Color dead BasicBlocks red in CFG output (sorbet#3927)
* Color dead BasicBlocks red in CFG output This makes debugging dead code errors in the CFG a little easier, especially on otherwise large CFGs. * test/testdata/**/*.cfg.exp * test/testdata/**/*.cfg-raw.exp * stog //test/cli:update_phases
1 parent 49e90fc commit 5d1a8e1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1214
-2
lines changed

cfg/CFG.cc

+8-2
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,16 @@ string CFG::toString(const core::GlobalState &gs) const {
216216
auto text = basicBlock->toString(gs, *this);
217217
auto lines = absl::StrSplit(text, "\n");
218218

219+
// whole block red if whole block is dead
220+
auto color = basicBlock->firstDeadInstructionIdx == 0 ? "red" : "black";
219221
fmt::format_to(
220222
buf,
221223
" \"bb{}_{}\" [\n"
224+
" color = {};\n"
222225
" label = \"{}\\l\"\n"
223226
" ];\n\n"
224227
" \"bb{}_{}\" -> \"bb{}_{}\" [style=\"bold\"];\n",
225-
symbolName, basicBlock->id,
228+
symbolName, basicBlock->id, color,
226229
fmt::map_join(lines.begin(), lines.end(), "\\l", [](auto line) -> string { return absl::CEscape(line); }),
227230
symbolName, basicBlock->id, symbolName, basicBlock->bexit.thenb->id);
228231

@@ -249,13 +252,16 @@ string CFG::showRaw(core::Context ctx) const {
249252
auto text = basicBlock->showRaw(ctx, *this);
250253
auto lines = absl::StrSplit(text, "\n");
251254

255+
// whole block red if whole block is dead
256+
auto color = basicBlock->firstDeadInstructionIdx == 0 ? "red" : "black";
252257
fmt::format_to(
253258
buf,
254259
" \"bb{}_{}\" [\n"
260+
" color = {};\n"
255261
" label = \"{}\\l\"\n"
256262
" ];\n\n"
257263
" \"bb{}_{}\" -> \"bb{}_{}\" [style=\"bold\"];\n",
258-
symbolName, basicBlock->id,
264+
symbolName, basicBlock->id, color,
259265
fmt::map_join(lines.begin(), lines.end(), "\\l", [](auto line) -> string { return absl::CEscape(line); }),
260266
symbolName, basicBlock->id, symbolName, basicBlock->bexit.thenb->id);
261267

test/cli/phases/phases.out

+4
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,13 @@ subgraph "cluster_::<Class:<root>>#<static-init>" {
198198
"bb::<Class:<root>>#<static-init>_1" [shape = parallelogram];
199199

200200
"bb::<Class:<root>>#<static-init>_0" [
201+
color = black;
201202
label = "block[id=0, rubyBlockId=0]()\l<self>: T.class_of(<root>) = cast(<self>: NilClass, AppliedType {\l klass = <S <C <U <root>>> $1>\l targs = [\l <C <U <AttachedClass>>> = SelfTypeParam(<S <C <U <root>>> $1><C <U <AttachedClass>>>)\l ]\l});\l<returnMethodTemp>$2: Integer(1) = 1\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: Integer(1)\l<unconditional>\l"
202203
];
203204

204205
"bb::<Class:<root>>#<static-init>_0" -> "bb::<Class:<root>>#<static-init>_1" [style="bold"];
205206
"bb::<Class:<root>>#<static-init>_1" [
207+
color = black;
206208
label = "block[id=1, rubyBlockId=0]()\l<unconditional>\l"
207209
];
208210

@@ -224,11 +226,13 @@ subgraph "cluster_::<Class:<root>>#<static-init>" {
224226
"bb::<Class:<root>>#<static-init>_1" [shape = parallelogram];
225227

226228
"bb::<Class:<root>>#<static-init>_0" [
229+
color = black;
227230
label = "block[id=0]()\lBinding {\l&nbsp;bind = VariableUseSite {\l&nbsp;&nbsp;variable = <U <self>>,\l&nbsp;&nbsp;type = T.class_of(<root>),\l&nbsp;},\l&nbsp;value = Cast {\l&nbsp;&nbsp;cast = T.cast,\l&nbsp;&nbsp;value = VariableUseSite {\l&nbsp;&nbsp;&nbsp;variable = <U <self>>,\l&nbsp;&nbsp;&nbsp;type = NilClass,\l&nbsp;&nbsp;},\l&nbsp;&nbsp;type = T.class_of(<root>),\l&nbsp;},\l}\lBinding {\l&nbsp;bind = VariableUseSite {\l&nbsp;&nbsp;variable = <U <returnMethodTemp>>$2,\l&nbsp;&nbsp;type = Integer(1),\l&nbsp;},\l&nbsp;value = Literal { value = Integer(1) },\l}\lBinding {\l&nbsp;bind = VariableUseSite {\l&nbsp;&nbsp;variable = <U <finalReturn>>,\l&nbsp;&nbsp;type = T.noreturn,\l&nbsp;},\l&nbsp;value = Return {\l&nbsp;&nbsp;what = VariableUseSite {\l&nbsp;&nbsp;&nbsp;variable = <U <returnMethodTemp>>$2,\l&nbsp;&nbsp;&nbsp;type = Integer(1),\l&nbsp;&nbsp;},\l&nbsp;},\l}\lVariableUseSite { variable = <U <unconditional>> }\l"
228231
];
229232

230233
"bb::<Class:<root>>#<static-init>_0" -> "bb::<Class:<root>>#<static-init>_1" [style="bold"];
231234
"bb::<Class:<root>>#<static-init>_1" [
235+
color = black;
232236
label = "block[id=1]()\lVariableUseSite { variable = <U <unconditional>> }\l"
233237
];
234238

test/testdata/cfg/array.rb.cfg.exp

+16
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ subgraph "cluster_::<Class:<root>>#<static-init>" {
66
"bb::<Class:<root>>#<static-init>_1" [shape = parallelogram];
77

88
"bb::<Class:<root>>#<static-init>_0" [
9+
color = black;
910
label = "block[id=0, rubyBlockId=0]()\l<self>: T.class_of(<root>) = cast(<self>: NilClass, AppliedType {\l klass = <S <C <U <root>>> $1>\l targs = [\l <C <U <AttachedClass>>> = SelfTypeParam(<S <C <U <root>>> $1><C <U <AttachedClass>>>)\l ]\l});\l<cfgAlias>$5: T.class_of(<Magic>) = alias <C <Magic>>\l<cfgAlias>$7: T.class_of(TestArray) = alias <C TestArray>\l<statTemp>$3: Sorbet::Private::Static::Void = <cfgAlias>$5: T.class_of(<Magic>).<define-top-class-or-module>(<cfgAlias>$7: T.class_of(TestArray))\l<cfgAlias>$10: T.class_of(Sorbet::Private::Static) = alias <C Static>\l<cfgAlias>$12: T.class_of(TestArray) = alias <C TestArray>\l<statTemp>$8: Sorbet::Private::Static::Void = <cfgAlias>$10: T.class_of(Sorbet::Private::Static).keep_for_ide(<cfgAlias>$12: T.class_of(TestArray))\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: NilClass\l<unconditional>\l"
1011
];
1112

1213
"bb::<Class:<root>>#<static-init>_0" -> "bb::<Class:<root>>#<static-init>_1" [style="bold"];
1314
"bb::<Class:<root>>#<static-init>_1" [
15+
color = black;
1416
label = "block[id=1, rubyBlockId=0]()\l<unconditional>\l"
1517
];
1618

@@ -24,11 +26,13 @@ subgraph "cluster_::TestArray#an_int" {
2426
"bb::TestArray#an_int_1" [shape = parallelogram];
2527

2628
"bb::TestArray#an_int_0" [
29+
color = black;
2730
label = "block[id=0, rubyBlockId=0]()\l<self>: TestArray = cast(<self>: NilClass, TestArray);\l<returnMethodTemp>$2: Integer(0) = 0\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: Integer(0)\l<unconditional>\l"
2831
];
2932

3033
"bb::TestArray#an_int_0" -> "bb::TestArray#an_int_1" [style="bold"];
3134
"bb::TestArray#an_int_1" [
35+
color = black;
3236
label = "block[id=1, rubyBlockId=0]()\l<unconditional>\l"
3337
];
3438

@@ -42,11 +46,13 @@ subgraph "cluster_::TestArray#a_string" {
4246
"bb::TestArray#a_string_1" [shape = parallelogram];
4347

4448
"bb::TestArray#a_string_0" [
49+
color = black;
4550
label = "block[id=0, rubyBlockId=0]()\l<self>: TestArray = cast(<self>: NilClass, TestArray);\l<returnMethodTemp>$2: String(\"str\") = \"str\"\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: String(\"str\")\l<unconditional>\l"
4651
];
4752

4853
"bb::TestArray#a_string_0" -> "bb::TestArray#a_string_1" [style="bold"];
4954
"bb::TestArray#a_string_1" [
55+
color = black;
5056
label = "block[id=1, rubyBlockId=0]()\l<unconditional>\l"
5157
];
5258

@@ -60,11 +66,13 @@ subgraph "cluster_::TestArray#test_arrays" {
6066
"bb::TestArray#test_arrays_1" [shape = parallelogram];
6167

6268
"bb::TestArray#test_arrays_0" [
69+
color = black;
6370
label = "block[id=0, rubyBlockId=0]()\l<self>: TestArray = cast(<self>: NilClass, TestArray);\l<magic>$4: T.class_of(<Magic>) = alias <C <Magic>>\l<statTemp>$3: [] = <magic>$4: T.class_of(<Magic>).<build-array>()\l<arrayTemp>$6: Integer(1) = 1\l<arrayTemp>$7: Integer(2) = 2\l<magic>$8: T.class_of(<Magic>) = alias <C <Magic>>\l<statTemp>$5: [Integer(1), Integer(2)] = <magic>$8: T.class_of(<Magic>).<build-array>(<arrayTemp>$6: Integer(1), <arrayTemp>$7: Integer(2))\l<arrayTemp>$9: Integer = <self>: TestArray.an_int()\l<arrayTemp>$11: String = <self>: TestArray.a_string()\l<magic>$14: T.class_of(<Magic>) = alias <C <Magic>>\l<arrayTemp>$13: [] = <magic>$14: T.class_of(<Magic>).<build-array>()\l<magic>$15: T.class_of(<Magic>) = alias <C <Magic>>\l<returnMethodTemp>$2: [Integer, String, []] = <magic>$15: T.class_of(<Magic>).<build-array>(<arrayTemp>$9: Integer, <arrayTemp>$11: String, <arrayTemp>$13: [])\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: [Integer, String, []]\l<unconditional>\l"
6471
];
6572

6673
"bb::TestArray#test_arrays_0" -> "bb::TestArray#test_arrays_1" [style="bold"];
6774
"bb::TestArray#test_arrays_1" [
75+
color = black;
6876
label = "block[id=1, rubyBlockId=0]()\l<unconditional>\l"
6977
];
7078

@@ -78,45 +86,53 @@ subgraph "cluster_::<Class:TestArray>#<static-init>" {
7886
"bb::<Class:TestArray>#<static-init>_1" [shape = parallelogram];
7987

8088
"bb::<Class:TestArray>#<static-init>_0" [
89+
color = black;
8190
label = "block[id=0, rubyBlockId=0]()\l<self>: T.class_of(TestArray) = cast(<self>: NilClass, AppliedType {\l klass = <S <C <U TestArray>> $1>\l targs = [\l <C <U <AttachedClass>>> = SelfTypeParam(<S <C <U TestArray>> $1><C <U <AttachedClass>>>)\l ]\l});\l<cfgAlias>$5: T.class_of(Sorbet::Private::Static) = alias <C Static>\l<block-pre-call-temp>$7: Sorbet::Private::Static::Void = <cfgAlias>$5: T.class_of(Sorbet::Private::Static).sig(<self>: T.class_of(TestArray))\l<selfRestore>$8: T.class_of(TestArray) = <self>\l<unconditional>\l"
8291
];
8392

8493
"bb::<Class:TestArray>#<static-init>_0" -> "bb::<Class:TestArray>#<static-init>_2" [style="bold"];
8594
"bb::<Class:TestArray>#<static-init>_1" [
95+
color = black;
8696
label = "block[id=1, rubyBlockId=0]()\l<unconditional>\l"
8797
];
8898

8999
"bb::<Class:TestArray>#<static-init>_1" -> "bb::<Class:TestArray>#<static-init>_1" [style="bold"];
90100
"bb::<Class:TestArray>#<static-init>_2" [
101+
color = black;
91102
label = "block[id=2, rubyBlockId=1](<self>: T.class_of(TestArray), <block-pre-call-temp>$7: Sorbet::Private::Static::Void, <selfRestore>$8: T.class_of(TestArray))\louterLoops: 1\l<block-call>: NilClass\l"
92103
];
93104

94105
"bb::<Class:TestArray>#<static-init>_2" -> "bb::<Class:TestArray>#<static-init>_5" [style="bold"];
95106
"bb::<Class:TestArray>#<static-init>_2" -> "bb::<Class:TestArray>#<static-init>_3" [style="tapered"];
96107

97108
"bb::<Class:TestArray>#<static-init>_3" [
109+
color = black;
98110
label = "block[id=3, rubyBlockId=0](<block-pre-call-temp>$7: Sorbet::Private::Static::Void, <selfRestore>$8: T.class_of(TestArray))\l<statTemp>$3: Sorbet::Private::Static::Void = Solve<<block-pre-call-temp>$7, sig>\l<self>: T.class_of(TestArray) = <selfRestore>$8\l<cfgAlias>$18: T.class_of(Sorbet::Private::Static) = alias <C Static>\l<block-pre-call-temp>$20: Sorbet::Private::Static::Void = <cfgAlias>$18: T.class_of(Sorbet::Private::Static).sig(<self>: T.class_of(TestArray))\l<selfRestore>$21: T.class_of(TestArray) = <self>\l<unconditional>\l"
99111
];
100112

101113
"bb::<Class:TestArray>#<static-init>_3" -> "bb::<Class:TestArray>#<static-init>_6" [style="bold"];
102114
"bb::<Class:TestArray>#<static-init>_5" [
115+
color = black;
103116
label = "block[id=5, rubyBlockId=1](<self>: T.class_of(TestArray), <block-pre-call-temp>$7: Sorbet::Private::Static::Void, <selfRestore>$8: T.class_of(TestArray))\louterLoops: 1\l<self>: T::Private::Methods::DeclBuilder = loadSelf\l<cfgAlias>$14: T.class_of(Integer) = alias <C Integer>\l<blockReturnTemp>$11: T::Private::Methods::DeclBuilder = <self>: T::Private::Methods::DeclBuilder.returns(<cfgAlias>$14: T.class_of(Integer))\l<blockReturnTemp>$15: T.noreturn = blockreturn<sig> <blockReturnTemp>$11: T::Private::Methods::DeclBuilder\l<unconditional>\l"
104117
];
105118

106119
"bb::<Class:TestArray>#<static-init>_5" -> "bb::<Class:TestArray>#<static-init>_2" [style="bold"];
107120
"bb::<Class:TestArray>#<static-init>_6" [
121+
color = black;
108122
label = "block[id=6, rubyBlockId=2](<self>: T.class_of(TestArray), <block-pre-call-temp>$20: Sorbet::Private::Static::Void, <selfRestore>$21: T.class_of(TestArray))\louterLoops: 1\l<block-call>: NilClass\l"
109123
];
110124

111125
"bb::<Class:TestArray>#<static-init>_6" -> "bb::<Class:TestArray>#<static-init>_9" [style="bold"];
112126
"bb::<Class:TestArray>#<static-init>_6" -> "bb::<Class:TestArray>#<static-init>_7" [style="tapered"];
113127

114128
"bb::<Class:TestArray>#<static-init>_7" [
129+
color = black;
115130
label = "block[id=7, rubyBlockId=0](<block-pre-call-temp>$20: Sorbet::Private::Static::Void, <selfRestore>$21: T.class_of(TestArray))\l<statTemp>$16: Sorbet::Private::Static::Void = Solve<<block-pre-call-temp>$20, sig>\l<self>: T.class_of(TestArray) = <selfRestore>$21\l<cfgAlias>$32: T.class_of(T::Sig) = alias <C Sig>\l<cfgAlias>$34: T.class_of(T) = alias <C T>\l<statTemp>$29: T.class_of(TestArray) = <self>: T.class_of(TestArray).extend(<cfgAlias>$32: T.class_of(T::Sig))\l<cfgAlias>$37: T.class_of(Sorbet::Private::Static) = alias <C Static>\l<statTemp>$39: Symbol(:an_int) = :an_int\l<statTemp>$40: Symbol(:normal) = :normal\l<statTemp>$35: Symbol(:an_int) = <cfgAlias>$37: T.class_of(Sorbet::Private::Static).keep_def(<self>: T.class_of(TestArray), <statTemp>$39: Symbol(:an_int), <statTemp>$40: Symbol(:normal))\l<cfgAlias>$43: T.class_of(Sorbet::Private::Static) = alias <C Static>\l<statTemp>$45: Symbol(:a_string) = :a_string\l<statTemp>$46: Symbol(:normal) = :normal\l<statTemp>$41: Symbol(:a_string) = <cfgAlias>$43: T.class_of(Sorbet::Private::Static).keep_def(<self>: T.class_of(TestArray), <statTemp>$45: Symbol(:a_string), <statTemp>$46: Symbol(:normal))\l<cfgAlias>$49: T.class_of(Sorbet::Private::Static) = alias <C Static>\l<statTemp>$51: Symbol(:test_arrays) = :test_arrays\l<statTemp>$52: Symbol(:normal) = :normal\l<statTemp>$47: Symbol(:test_arrays) = <cfgAlias>$49: T.class_of(Sorbet::Private::Static).keep_def(<self>: T.class_of(TestArray), <statTemp>$51: Symbol(:test_arrays), <statTemp>$52: Symbol(:normal))\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: NilClass\l<unconditional>\l"
116131
];
117132

118133
"bb::<Class:TestArray>#<static-init>_7" -> "bb::<Class:TestArray>#<static-init>_1" [style="bold"];
119134
"bb::<Class:TestArray>#<static-init>_9" [
135+
color = black;
120136
label = "block[id=9, rubyBlockId=2](<self>: T.class_of(TestArray), <block-pre-call-temp>$20: Sorbet::Private::Static::Void, <selfRestore>$21: T.class_of(TestArray))\louterLoops: 1\l<self>: T::Private::Methods::DeclBuilder = loadSelf\l<cfgAlias>$27: T.class_of(String) = alias <C String>\l<blockReturnTemp>$24: T::Private::Methods::DeclBuilder = <self>: T::Private::Methods::DeclBuilder.returns(<cfgAlias>$27: T.class_of(String))\l<blockReturnTemp>$28: T.noreturn = blockreturn<sig> <blockReturnTemp>$24: T::Private::Methods::DeclBuilder\l<unconditional>\l"
121137
];
122138

test/testdata/cfg/block_in_deadcode.rb.cfg.exp

+7
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,33 @@ subgraph "cluster_::Object#foo" {
66
"bb::Object#foo_1" [shape = parallelogram];
77

88
"bb::Object#foo_0" [
9+
color = black;
910
label = "block[id=0, rubyBlockId=0]()\l<self>: Object = cast(<self>: NilClass, Object);\l<block-pre-call-temp>$4: Sorbet::Private::Static::Void = <self>: Object.outer()\l<unconditional>\l"
1011
];
1112

1213
"bb::Object#foo_0" -> "bb::Object#foo_2" [style="bold"];
1314
"bb::Object#foo_1" [
15+
color = black;
1416
label = "block[id=1, rubyBlockId=0](<self>)\l<statTemp>$11 = <self>\l<block-pre-call-temp>$12 = <statTemp>$11.inner()\l<unconditional>\l"
1517
];
1618

1719
"bb::Object#foo_1" -> "bb::Object#foo_1" [style="bold"];
1820
"bb::Object#foo_2" [
21+
color = black;
1922
label = "block[id=2, rubyBlockId=1](<self>: Object, <block-pre-call-temp>$4: Sorbet::Private::Static::Void)\louterLoops: 1\l<block-call>: NilClass\l"
2023
];
2124

2225
"bb::Object#foo_2" -> "bb::Object#foo_5" [style="bold"];
2326
"bb::Object#foo_2" -> "bb::Object#foo_3" [style="tapered"];
2427

2528
"bb::Object#foo_3" [
29+
color = black;
2630
label = "block[id=3, rubyBlockId=0](<self>: Object, <block-pre-call-temp>$4: Sorbet::Private::Static::Void)\l<returnMethodTemp>$2: T.untyped = Solve<<block-pre-call-temp>$4, outer>\l<self>: Object = <self>\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: T.untyped\l<unconditional>\l"
2731
];
2832

2933
"bb::Object#foo_3" -> "bb::Object#foo_1" [style="bold"];
3034
"bb::Object#foo_5" [
35+
color = black;
3136
label = "block[id=5, rubyBlockId=1](<self>: Object)\louterLoops: 1\l<self>: Object = loadSelf\l<statTemp>$9: T.noreturn = return <returnTemp>$10: NilClass\l<unconditional>\l"
3237
];
3338

@@ -41,11 +46,13 @@ subgraph "cluster_::<Class:<root>>#<static-init>" {
4146
"bb::<Class:<root>>#<static-init>_1" [shape = parallelogram];
4247

4348
"bb::<Class:<root>>#<static-init>_0" [
49+
color = black;
4450
label = "block[id=0, rubyBlockId=0]()\l<self>: T.class_of(<root>) = cast(<self>: NilClass, AppliedType {\l klass = <S <C <U <root>>> $1>\l targs = [\l <C <U <AttachedClass>>> = SelfTypeParam(<S <C <U <root>>> $1><C <U <AttachedClass>>>)\l ]\l});\l<cfgAlias>$4: T.class_of(Sorbet::Private::Static) = alias <C Static>\l<statTemp>$6: Symbol(:foo) = :foo\l<statTemp>$7: Symbol(:normal) = :normal\l<returnMethodTemp>$2: Symbol(:foo) = <cfgAlias>$4: T.class_of(Sorbet::Private::Static).keep_def(<self>: T.class_of(<root>), <statTemp>$6: Symbol(:foo), <statTemp>$7: Symbol(:normal))\l<finalReturn>: T.noreturn = return <returnMethodTemp>$2: Symbol(:foo)\l<unconditional>\l"
4551
];
4652

4753
"bb::<Class:<root>>#<static-init>_0" -> "bb::<Class:<root>>#<static-init>_1" [style="bold"];
4854
"bb::<Class:<root>>#<static-init>_1" [
55+
color = black;
4956
label = "block[id=1, rubyBlockId=0]()\l<unconditional>\l"
5057
];
5158

0 commit comments

Comments
 (0)