This commit is contained in:
Taus
2026-04-21 16:19:00 +00:00
committed by yoff
parent e3155ea544
commit 1af415bec3
11 changed files with 112 additions and 15 deletions

View File

@@ -0,0 +1,16 @@
/**
* Checks that every timer annotation has a corresponding CFG node.
*/
import python
import TimerUtils
import OldCfgImpl
private module Utils = EvalOrderCfgUtils<OldCfg>;
private import Utils::CfgTests
from TimerAnnotation ann
where annotationWithoutCfgNode(ann)
select ann, "Annotation in $@ has no CFG node", ann.getTestFunction(),
ann.getTestFunction().getName()

View File

@@ -0,0 +1,18 @@
/**
* New-CFG version of AnnotationHasCfgNode.
*
* Checks that every timer annotation has a corresponding CFG node.
*/
import python
import TimerUtils
import NewCfgImpl
private module Utils = EvalOrderCfgUtils<NewCfg>;
private import Utils::CfgTests
from TimerAnnotation ann
where annotationWithoutCfgNode(ann)
select ann, "Annotation in $@ has no CFG node", ann.getTestFunction(),
ann.getTestFunction().getName()

View File

@@ -0,0 +1 @@
| test_if.py:51:9:51:9 | IntegerLiteral | $@ in $@ has no consecutive successor (expected 6) | test_if.py:51:15:51:15 | IntegerLiteral | Timestamp 5 | test_if.py:43:1:43:31 | Function test_if_elif_else_first | test_if_elif_else_first |

View File

@@ -14,9 +14,12 @@ private class NewBasicBlock = CfgImpl::BasicBlock;
/** New (shared) CFG implementation of the evaluation-order signature. */
module NewCfg implements EvalOrderCfgSig {
class CfgNode instanceof NewControlFlowNode {
// Only include the unique representative node for each AST node,
// filtering out synthetic before/after/entry/exit/additional nodes.
CfgNode() { NewControlFlowNode.super.injects(_) }
// Use the post-order representative for each AST node: the "after" node.
// For simple leaf nodes this is the merged before/after node. For
// post-order expressions this is the TAstNode. For pre-order expressions
// (and/or/not/ternary) this uses an AfterValueNode, which places the
// expression after its operands — matching the timer test expectations.
CfgNode() { NewControlFlowNode.super.isAfter(_) }
string toString() { result = NewControlFlowNode.super.toString() }

View File

@@ -0,0 +1,18 @@
/**
* New-CFG version of NoBasicBlock.
*
* Checks that every annotated CFG node belongs to a basic block.
*/
import python
import TimerUtils
import NewCfgImpl
private module Utils = EvalOrderCfgUtils<NewCfg>;
private import Utils
private import Utils::CfgTests
from CfgNode n, TestFunction f
where noBasicBlock(n, f)
select n, "CFG node in $@ does not belong to any basic block", f, f.getName()

View File

@@ -0,0 +1,16 @@
/**
* Checks that every annotated CFG node belongs to a basic block.
*/
import python
import TimerUtils
import OldCfgImpl
private module Utils = EvalOrderCfgUtils<OldCfg>;
private import Utils
private import Utils::CfgTests
from CfgNode n, TestFunction f
where noBasicBlock(n, f)
select n, "CFG node in $@ does not belong to any basic block", f, f.getName()

View File

@@ -92,7 +92,7 @@ class TimerAnnotation extends TTimerAnnotation {
abstract Expr getAnnotatedExpr();
/** Gets the enclosing annotation expression (the `BinaryExpr` or `Call`). */
abstract Expr getExpr();
abstract Expr getTimerExpr();
/** Holds if this is a dead-code annotation (`t.dead[n]`). */
predicate isDead() { this instanceof DeadTimerAnnotation }
@@ -100,9 +100,9 @@ class TimerAnnotation extends TTimerAnnotation {
/** Holds if this is a never-evaluated annotation (`t.never`). */
predicate isNever() { this instanceof NeverTimerAnnotation }
string toString() { result = this.getExpr().toString() }
string toString() { result = this.getAnnotatedExpr().toString() }
Location getLocation() { result = this.getExpr().getLocation() }
Location getLocation() { result = this.getAnnotatedExpr().getLocation() }
}
/** A matmul-based timer annotation: `expr @ t[n]`. */
@@ -119,7 +119,7 @@ class MatmulTimerAnnotation extends TMatmulAnnotation, TimerAnnotation {
override Expr getAnnotatedExpr() { result = annotated }
override BinaryExpr getExpr() { result.getLeft() = annotated }
override BinaryExpr getTimerExpr() { result.getLeft() = annotated }
}
/** A call-based timer annotation: `t(expr, n)`. */
@@ -136,7 +136,7 @@ class CallTimerAnnotation extends TCallAnnotation, TimerAnnotation {
override Expr getAnnotatedExpr() { result = annotated }
override Call getExpr() { result.getArg(0) = annotated }
override Call getTimerExpr() { result.getArg(0) = annotated }
}
/** A dead-code timer annotation: `expr @ t.dead[n]`. */
@@ -153,7 +153,7 @@ class DeadTimerAnnotation extends TDeadAnnotation, TimerAnnotation {
override Expr getAnnotatedExpr() { result = annotated }
override BinaryExpr getExpr() { result.getLeft() = annotated }
override BinaryExpr getTimerExpr() { result.getLeft() = annotated }
}
/** A never-evaluated annotation: `expr @ t.never`. */
@@ -169,7 +169,7 @@ class NeverTimerAnnotation extends TNeverAnnotation, TimerAnnotation {
override Expr getAnnotatedExpr() { result = annotated }
override BinaryExpr getExpr() { result.getLeft() = annotated }
override BinaryExpr getTimerExpr() { result.getLeft() = annotated }
}
/**
@@ -240,7 +240,7 @@ module EvalOrderCfgUtils<EvalOrderCfgSig Input> {
class TimerCfgNode extends CfgNode {
private TimerAnnotation annot;
TimerCfgNode() { annot.getExpr() = this.getNode() }
TimerCfgNode() { annot.getAnnotatedExpr() = this.getNode() }
/** Gets a timestamp value from this annotation. */
int getATimestamp() { result = annot.getATimestamp() }
@@ -322,7 +322,7 @@ module EvalOrderCfgUtils<EvalOrderCfgSig Input> {
private predicate hasNestedScopeAnnotation(TestFunction f) {
exists(TimerAnnotation a |
a.getTestFunction() = f and
a.getExpr().getScope() != f
a.getAnnotatedExpr().getScope() != f
)
}
@@ -335,7 +335,7 @@ module EvalOrderCfgUtils<EvalOrderCfgSig Input> {
not ann.isDead() and
a = ann.getATimestamp() and
not exists(TimerCfgNode x, TimerCfgNode y |
ann.getExpr() = x.getNode() and
ann.getAnnotatedExpr() = x.getNode() and
nextTimerAnnotation(x, y) and
(a + 1) = y.getATimestamp()
) and
@@ -354,7 +354,7 @@ module EvalOrderCfgUtils<EvalOrderCfgSig Input> {
*/
predicate neverReachable(NeverTimerAnnotation ann) {
exists(CfgNode n, Scope s |
n.getNode() = ann.getExpr() and
n.getNode() = ann.getAnnotatedExpr() and
s = n.getScope() and
(
// Reachable via inter-block path (includes same block)
@@ -417,6 +417,27 @@ module EvalOrderCfgUtils<EvalOrderCfgSig Input> {
minB = min(b.getATimestamp()) and
maxA >= minB
}
/**
* Holds if CFG node `n` in test function `f` does not belong to any basic block.
*/
predicate noBasicBlock(CfgNode n, TestFunction f) {
n.getScope() = f and
not exists(n.getBasicBlock())
}
/**
* Holds if non-dead annotation `ann` has no corresponding CFG node.
*/
predicate annotationWithoutCfgNode(TimerAnnotation ann) {
not ann.isDead() and
not ann.isNever() and
not exists(CfgNode n | n.getNode() = ann.getAnnotatedExpr())
}
predicate annotationWithCfgNode(TimerAnnotation ann) {
exists(CfgNode n | n.getNode() = ann.getAnnotatedExpr())
}
}
}
@@ -427,7 +448,7 @@ module EvalOrderCfgUtils<EvalOrderCfgSig Input> {
predicate isTimerMechanism(Expr e, TestFunction f) {
exists(TimerAnnotation a |
a.getTestFunction() = f and
e = a.getExpr().getASubExpression*()
e = a.getTimerExpr().getASubExpression*()
)
}