@@ -360,6 +360,7 @@ module ControlFlow {
360360 class Split = SplitImpl ;
361361 class FinallySplit = FinallySplitting:: FinallySplitImpl ;
362362 class ExceptionHandlerSplit = ExceptionHandlerSplitting:: ExceptionHandlerSplitImpl ;
363+ class BooleanSplit = BooleanSplitting:: BooleanSplitImpl ;
363364 }
364365
365366 class BasicBlock = BBs:: BasicBlock ;
@@ -3204,6 +3205,275 @@ module ControlFlow {
32043205 }
32053206 }
32063207
3208+ module BooleanSplitting {
3209+ private import PreBasicBlocks
3210+ private import PreSsa
3211+
3212+ /** A sub-classification of Boolean splits. */
3213+ abstract class BooleanSplitSubKind extends TBooleanSplitSubKind {
3214+ /**
3215+ * Holds if the branch taken by condition `cb1` should be recorded in
3216+ * this split, and the recorded value determines the branch taken by a
3217+ * later condition `cb2`, possibly inverted.
3218+ *
3219+ * For example, in
3220+ *
3221+ * ```
3222+ * var b = GetB();
3223+ * if (b)
3224+ * Console.WriteLine("b is true");
3225+ * if (!b)
3226+ * Console.WriteLine("b is false");
3227+ * ```
3228+ *
3229+ * the branch taken in the condition on line 2 can be recorded, and the
3230+ * recorded value will detmine the branch taken in the condition on line 4.
3231+ */
3232+ abstract predicate correlatesConditions ( ConditionBlock cb1 , ConditionBlock cb2 , boolean inverted ) ;
3233+
3234+ /** Holds if control flow element `cfe` starts a split of this kind. */
3235+ predicate startsSplit ( ControlFlowElement cfe ) {
3236+ this .correlatesConditions ( any ( ConditionBlock cb | cb .getLastElement ( ) = cfe ) , _, _)
3237+ }
3238+
3239+ /** Gets the callable that this Boolean split kind belongs to. */
3240+ abstract Callable getEnclosingCallable ( ) ;
3241+
3242+ /** Gets a textual representation of this Boolean split kind. */
3243+ abstract string toString ( ) ;
3244+
3245+ /** Gets the location of this Boolean split kind. */
3246+ abstract Location getLocation ( ) ;
3247+ }
3248+
3249+ /**
3250+ * A Boolean split that records the value of a Boolean SSA variable.
3251+ *
3252+ * For example, in
3253+ *
3254+ * ```
3255+ * var b = GetB();
3256+ * if (b)
3257+ * Console.WriteLine("b is true");
3258+ * if (!b)
3259+ * Console.WriteLine("b is false");
3260+ * ```
3261+ *
3262+ * there is a Boolean split on the SSA variable for `b` at line 1.
3263+ */
3264+ class SsaBooleanSplitSubKind extends BooleanSplitSubKind , TSsaBooleanSplitSubKind {
3265+ private PreSsa:: Definition def ;
3266+
3267+ SsaBooleanSplitSubKind ( ) {
3268+ this = TSsaBooleanSplitSubKind ( def )
3269+ }
3270+
3271+ /**
3272+ * Holds if condition `cb` is a read of the SSA variable in this split.
3273+ */
3274+ private predicate defCondition ( ConditionBlock cb ) {
3275+ exists ( LocalScopeVariableRead read1 , LocalScopeVariableRead read2 |
3276+ firstReadSameVar ( def , read1 ) |
3277+ adjacentReadPairSameVar * ( read1 , read2 ) and
3278+ read2 = cb .getLastElement ( )
3279+ )
3280+ }
3281+
3282+ /**
3283+ * Holds if condition `cb` is a read of the SSA variable in this split,
3284+ * and `cb` can be reached from `read` without passing through another
3285+ * condition that reads the same SSA variable.
3286+ */
3287+ private predicate defConditionReachableFromRead ( ConditionBlock cb , LocalScopeVariableRead read ) {
3288+ this .defCondition ( cb ) and
3289+ read = cb .getLastElement ( )
3290+ or
3291+ exists ( LocalScopeVariableRead mid |
3292+ this .defConditionReachableFromRead ( cb , mid ) |
3293+ adjacentReadPairSameVar ( read , mid ) and
3294+ not this .defCondition ( read )
3295+ )
3296+ }
3297+
3298+ /**
3299+ * Holds if condition `cb` is a read of the SSA variable in this split,
3300+ * and `cb` can be reached from the SSA definition without passing through
3301+ * another condition that reads the same SSA variable.
3302+ */
3303+ private predicate firstDefCondition ( ConditionBlock cb ) {
3304+ exists ( LocalScopeVariableRead read |
3305+ this .defConditionReachableFromRead ( cb , read ) |
3306+ firstReadSameVar ( def , read )
3307+ )
3308+ }
3309+
3310+ override predicate correlatesConditions ( ConditionBlock cb1 , ConditionBlock cb2 , boolean inverted ) {
3311+ this .firstDefCondition ( cb1 ) and
3312+ exists ( LocalScopeVariableRead read1 , LocalScopeVariableRead read2 |
3313+ read1 = cb1 .getLastElement ( ) and
3314+ adjacentReadPairSameVar + ( read1 , read2 ) and
3315+ read2 = cb2 .getLastElement ( ) and
3316+ inverted = false
3317+ )
3318+ }
3319+
3320+ override Callable getEnclosingCallable ( ) {
3321+ result = def .getVariable ( ) .getCallable ( )
3322+ }
3323+
3324+ override string toString ( ) {
3325+ result = def .getVariable ( ) .toString ( )
3326+ }
3327+
3328+ override Location getLocation ( ) {
3329+ result = def .getLocation ( )
3330+ }
3331+ }
3332+
3333+ /**
3334+ * A split for elements that can reach a condition where this split determines
3335+ * the Boolean value that the condition evaluates to. For example, in
3336+ *
3337+ * ```
3338+ * if (b)
3339+ * Console.WriteLine("b is true");
3340+ * if (!b)
3341+ * Console.WriteLine("b is false");
3342+ * ```
3343+ *
3344+ * all control flow nodes on line 2 and line 3 have two splits: one representing
3345+ * that the condition on line 1 took the `true` branch, and one representing that
3346+ * the condition on line 1 took the `false` branch.
3347+ */
3348+ class BooleanSplitImpl extends SplitImpl , TBooleanSplit {
3349+ private BooleanSplitSubKind kind ;
3350+ private boolean branch ;
3351+
3352+ BooleanSplitImpl ( ) {
3353+ this = TBooleanSplit ( kind , branch )
3354+ }
3355+
3356+ /** Gets the kind of this Boolean split. */
3357+ BooleanSplitSubKind getSubKind ( ) { result = kind }
3358+
3359+ /** Gets the branch taken in this split. */
3360+ boolean getBranch ( ) { result = branch }
3361+
3362+ override string toString ( ) {
3363+ exists ( int line |
3364+ line = kind .getLocation ( ) .getStartLine ( ) and
3365+ result = kind .toString ( ) + " (line " + line + "): " + branch .toString ( )
3366+ )
3367+ }
3368+ }
3369+
3370+ private class BooleanSplitKind extends SplitKind , TBooleanSplitKind {
3371+ private BooleanSplitSubKind kind ;
3372+
3373+ BooleanSplitKind ( ) {
3374+ this = TBooleanSplitKind ( kind )
3375+ }
3376+
3377+ /** Gets the sub kind of this Boolean split kind. */
3378+ BooleanSplitSubKind getSubKind ( ) { result = kind }
3379+
3380+ override int getListOrder ( ) {
3381+ exists ( Callable c , int r |
3382+ c = kind .getEnclosingCallable ( ) |
3383+ result = r + 1 and // start the ordering from 2
3384+ kind = rank [ r ] ( BooleanSplitSubKind kind0 |
3385+ kind0 .getEnclosingCallable ( ) = c and
3386+ kind0 .startsSplit ( _) |
3387+ kind0 order by kind0 .getLocation ( ) .getStartLine ( ) , kind0 .getLocation ( ) .getStartColumn ( )
3388+ )
3389+ )
3390+ }
3391+
3392+ override string toString ( ) { result = kind .toString ( ) }
3393+ }
3394+
3395+ private class BooleanSplitInternal extends SplitInternal , BooleanSplitImpl {
3396+ override BooleanSplitKind getKind ( ) {
3397+ result .getSubKind ( ) = this .getSubKind ( )
3398+ }
3399+
3400+ override predicate hasEntry ( ControlFlowElement pred , ControlFlowElement succ , Completion c ) {
3401+ succ = succ ( pred , c ) and
3402+ this .getSubKind ( ) .startsSplit ( pred ) and
3403+ c = any ( BooleanCompletion bc | bc .getOuterValue ( ) = this .getBranch ( ) )
3404+ }
3405+
3406+ private ConditionBlock getACorrelatedCondition ( boolean inverted ) {
3407+ this .getSubKind ( ) .correlatesConditions ( _, result , inverted )
3408+ }
3409+
3410+ /**
3411+ * Holds if this split applies to basic block `bb`, where the the last
3412+ * element of `bb` can have completion `c`.
3413+ */
3414+ private predicate appliesToBlock ( PreBasicBlock bb , Completion c ) {
3415+ this .appliesTo ( bb ) and
3416+ exists ( ControlFlowElement last |
3417+ last = bb .getLastElement ( ) |
3418+ ( exists ( succ ( last , c ) ) or exists ( succExit ( last , c ) ) ) and
3419+ // Respect the value recorded in this split for all correlated conditions
3420+ forall ( boolean inverted |
3421+ bb = this .getACorrelatedCondition ( inverted ) |
3422+ c instanceof BooleanCompletion
3423+ implies
3424+ c = any ( BooleanCompletion bc | bc .getInnerValue ( ) = this .getBranch ( ) .booleanXor ( inverted ) )
3425+ )
3426+ )
3427+ }
3428+
3429+ /**
3430+ * Holds if basic block `bb` can reach a condition correlated with the value
3431+ * recorded in this split.
3432+ */
3433+ private predicate canReachCorrelatedCondition ( PreBasicBlock bb ) {
3434+ bb = this .getACorrelatedCondition ( _)
3435+ or
3436+ exists ( PreBasicBlock mid |
3437+ this .canReachCorrelatedCondition ( mid ) |
3438+ bb = mid .getAPredecessor ( )
3439+ )
3440+ }
3441+
3442+ override predicate hasExit ( ControlFlowElement pred , ControlFlowElement succ , Completion c ) {
3443+ exists ( PreBasicBlock bb |
3444+ this .appliesToBlock ( bb , c ) |
3445+ pred = bb .getLastElement ( ) and
3446+ succ = succ ( pred , c ) and
3447+ // Exit this split if we can no longer reach a correlated condition
3448+ not this .canReachCorrelatedCondition ( succ )
3449+ )
3450+ }
3451+
3452+ override predicate hasExit ( ControlFlowElement pred , Completion c ) {
3453+ exists ( PreBasicBlock bb |
3454+ this .appliesToBlock ( bb , c ) |
3455+ pred = bb .getLastElement ( ) and
3456+ exists ( succExit ( pred , c ) )
3457+ )
3458+ }
3459+
3460+ override predicate hasSuccessor ( ControlFlowElement pred , ControlFlowElement succ , Completion c ) {
3461+ exists ( PreBasicBlock bb , Completion c0 |
3462+ this .appliesToBlock ( bb , c0 ) |
3463+ pred = bb .getAnElement ( ) and
3464+ succ = succ ( pred , c ) and
3465+ (
3466+ pred = bb .getLastElement ( )
3467+ implies
3468+ // We must still be able to reach a correlated condition to stay in this split
3469+ this .canReachCorrelatedCondition ( succ ) and
3470+ c = c0
3471+ )
3472+ )
3473+ }
3474+ }
3475+ }
3476+
32073477 /**
32083478 * A set of control flow node splits. The set is represented by a list of splits,
32093479 * ordered by ascending rank.
@@ -3512,17 +3782,30 @@ module ControlFlow {
35123782 )
35133783 }
35143784
3785+ cached
3786+ newtype TBooleanSplitSubKind =
3787+ TSsaBooleanSplitSubKind ( PreSsa:: Definition def )
3788+
35153789 cached
35163790 newtype TSplitKind =
35173791 TFinallySplitKind ( )
35183792 or
35193793 TExceptionHandlerSplitKind ( )
3794+ or
3795+ TBooleanSplitKind ( BooleanSplitting:: BooleanSplitSubKind kind ) {
3796+ kind .startsSplit ( _)
3797+ }
35203798
35213799 cached
35223800 newtype TSplit =
35233801 TFinallySplit ( FinallySplitting:: FinallySplitType type )
35243802 or
35253803 TExceptionHandlerSplit ( ExceptionClass ec )
3804+ or
3805+ TBooleanSplit ( BooleanSplitting:: BooleanSplitSubKind kind , boolean branch ) {
3806+ kind .startsSplit ( _) and
3807+ ( branch = true or branch = false )
3808+ }
35263809
35273810 cached
35283811 newtype TSplits =
0 commit comments