Hi there,
I am experimenting with Clang’s relatively new dataflow analysis framework to implement a simple sign analysis. I really appreciate the framework as it nicely follows the lattice theory, so it was very easy to advance for the first few test cases.
Then I wrote the following test case, which I wanted to pass in a TDD fashion.
std::string Code = R"(
int foo();
void fun() {
int a = foo();
if (a > 0) {
(void)0;
(void)1;
// [[p]]
}
}
)";
RunDataflow(Code, UnorderedElementsAre(
Pair("p", HoldsSignLattice(UnorderedElementsAre(
Pair(Var("a"), Positive())
)))
));
I ran into a problem, that during the transfer function how to tell which branch did we take? Alas, when we analyze the (void)0; statement, it is not trivial to tell that the condition of the if is evaluated as true of false. The only thing I could do is to save the condition expression a < 0 in the previous call to transfer (i.e in the Pred node of the CFG) and then in later tranfer calls I could query if the flow condition implies that condition. This had to be done redundantly for the second statement as well in the very same basic block.
Other dataflow frameworks provide special callbacks for edge transfers in the CFG, one notable example is the Java SpotBugs ReturnPathType analysis (see edgeTransfer). I am proposing here (and request comments) to introduce a similar concept to the Clang dataflow framework. On top of this general concept, I think, a special edge transfer is a “branch” transfer where the callback provides the condition and the boolean value of the condition.
void branchTransfer(bool Branch, const Stmt *S,
SignPropagationLattice &Vars, Environment &Env);
This “transfer” function shall be called once at the beginning of each basic block which has a direct predecessor that is a conditional statement. To demonstrate the idea, I’ve created a prototype patch, please take a look at ⚙ D133698 [clang][dataflow] SignAnalysis, edgeTransfer, branchTransfer