理解try/catch语句的Clang CFG



我试图通过查看其转储的输出来理解Clang的CFG,但我不清楚try/catch语句在CFG中是如何表示的。

考虑一下这个小片段:

int func(int x);
int func2(int x) {
try {
return func(x);
} catch(...) {
return 0;
}
}

转储的CFG如下:

$ clang++ -Xclang -analyze -Xclang -analyzer-checker=debug.DumpCFG -fsyntax-only test.cpp
int func2(int x)
[B4 (ENTRY)]
Succs (1): B3
[B1]
T: try ...
Succs (1): B2
[B2]
catch (...):
1: catch (...) {
[B2.3]}
2: 0
3: return [B2.2];
Preds (1): B1
Succs (1): B0
[B3]
1: func
2: [B3.1] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(int))
3: x
4: [B3.3] (ImplicitCastExpr, LValueToRValue, int)
5: [B3.2]([B3.4])
6: return [B3.5];
Preds (1): B4
Succs (1): B0
[B0 (EXIT)]
Preds (2): B2 B3

我不明白B1基本块是如何链接到其他块的。入口块似乎直接跳转到包含try{}语句主体的B3。然后,B3将退出块作为其唯一的后续块。因此,B1B2似乎与函数的主流程没有关联。

在这种情况下,我必须如何解释CFG?

clang对try语句的CFG支持并不完整。

12年前,clang曾在每个函数调用中为这些异常块添加一条边。但是https://github.com/llvm/llvm-project/commit/04c6851cd6053c638e68bf1d7b99dda14ea267fb以构建性能的名义取消了这一点(异常不仅对人类来说很难推理(,这就是今天的情况:https://github.com/llvm/llvm-project/blob/d677a7cb056b17145a50ec8ca2ab6d5f4c494749/clang/lib/Analysis/CFG.cpp#L2636

有一个bool可以打开这些边缘,但它没有连接到任何叮当作响的命令行标志。

因此,这些用于try语句的CFG块大多不被使用。它们将catch块作为后继块,而可达代码分析只是将它们视为额外的根(本质上假设try块中的某些东西总是有可能抛出(。(参见中提到的"CXXTryStmt"https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/AnalysisBasedWarnings.cpp)

为try语句向CFG块添加边所做的一件事是trytry主体中的显式throw

% cat foo.cc
int func() {
try {
throw 0;
} catch(...) {
return 0;
}
}
% clang++ -Xclang -analyze -Xclang -analyzer-checker=debug.DumpCFG -fsyntax-only foo.cc
int func()
[B4 (ENTRY)]
Succs (1): B3
[B1]
T: try ...
Preds (1): B3
Succs (1): B2
[B2]
catch (...):
1: catch (...) {
[B2.3]}
2: 0
3: return [B2.2];
Preds (1): B1
Succs (1): B0
[B3]
1: 0
2: throw [B3.1]
Preds (1): B4
Succs (1): B1
[B0 (EXIT)]
Preds (1): B2

最新更新