@@ -1220,6 +1220,139 @@ module Make<LocationSig Location, InputSig<Location> Input> {
1220
1220
1221
1221
/** Holds if the `i`th node of basic block `bb` evaluates this guard. */
1222
1222
predicate hasCfgNode ( BasicBlock bb , int i ) ;
1223
+
1224
+ /** Gets the location of this guard. */
1225
+ Location getLocation ( ) ;
1226
+ }
1227
+
1228
+ /**
1229
+ * A logical operation guard.
1230
+ *
1231
+ * Guards are automatically lifted to logical operations, as dictacted
1232
+ * by the `lift` predicate. For example, logical conjunctions can be
1233
+ * lifted as follows:
1234
+ *
1235
+ * ```csharp
1236
+ * // Example 1
1237
+ * if (x == "safe" && x == "safe")
1238
+ * {
1239
+ * sink(x); // guarded
1240
+ * }
1241
+ * else
1242
+ * {
1243
+ * sink(x); // unguarded
1244
+ * }
1245
+ *
1246
+ * // Example 2
1247
+ * if (x == "safe" && x != "safe2")
1248
+ * {
1249
+ * sink(x); // guarded
1250
+ * }
1251
+ * else
1252
+ * {
1253
+ * sink(x); // unguarded
1254
+ * }
1255
+ *
1256
+ * // Example 3
1257
+ * if (x != "safe" && x == "safe2")
1258
+ * {
1259
+ * sink(x); // guarded
1260
+ * }
1261
+ * else
1262
+ * {
1263
+ * sink(x); // unguarded
1264
+ * }
1265
+ *
1266
+ * // Example 4
1267
+ * if (x != "safe1" && x != "safe2")
1268
+ * {
1269
+ * sink(x); // unguarded
1270
+ * }
1271
+ * else
1272
+ * {
1273
+ * sink(x); // guarded
1274
+ * }
1275
+ * ```
1276
+ *
1277
+ * Examples 1--3 should normally be handled via the control-flow graph (CFG)
1278
+ * implementation, but they are included for completeness.
1279
+ *
1280
+ * Example 4, which does not come for free just from the CFG, requires that
1281
+ * logical conjunctions be modeled post-order in the CFG, as they will
1282
+ * otherwise not dominate their `else` branch.
1283
+ *
1284
+ * Similarly, logical disjunctions can be lifted as follows:
1285
+ *
1286
+ * ```csharp
1287
+ * // Example 1
1288
+ * if (x == "safe1" || x == "safe2")
1289
+ * {
1290
+ * sink(x); // guarded
1291
+ * }
1292
+ * else
1293
+ * {
1294
+ * sink(x); // unguarded
1295
+ * }
1296
+ *
1297
+ * // Example 2
1298
+ * if (x == "safe" || x != "safe2")
1299
+ * {
1300
+ * sink(x); // unguarded
1301
+ * }
1302
+ * else
1303
+ * {
1304
+ * sink(x); // guarded
1305
+ * }
1306
+ *
1307
+ * // Example 3
1308
+ * if (x != "safe" || x == "safe2")
1309
+ * {
1310
+ * sink(x); // unguarded
1311
+ * }
1312
+ * else
1313
+ * {
1314
+ * sink(x); // guarded
1315
+ * }
1316
+ *
1317
+ * // Example 4
1318
+ * if (x != "safe" || x != "safe")
1319
+ * {
1320
+ * sink(x); // unguarded
1321
+ * }
1322
+ * else
1323
+ * {
1324
+ * sink(x); // guarded
1325
+ * }
1326
+ * ```
1327
+ *
1328
+ * Again, Examples 1--3 should normally be handled via the control-flow graph
1329
+ * (CFG) implementation, while Example 4 requires that logical disjunctions be
1330
+ * modeled post-order in the CFG.
1331
+ */
1332
+ class LogicalOperationGuard extends Guard {
1333
+ /** Gets the `i`th operand of this logical operation. */
1334
+ Guard getOperand ( int i ) ;
1335
+
1336
+ /**
1337
+ * Holds if this logical operation guards `branch`, provided that the `i`th
1338
+ * operand guards its `operandBranch`. The string `id` is used to encode
1339
+ * the bit-pattern of the operands.
1340
+ *
1341
+ * Example: If this operand is a Boolean conjunction, then the following lift
1342
+ * table should be used:
1343
+ *
1344
+ * | id | i | operandBranch | branch |
1345
+ * |---------------|---|---------------|--------|
1346
+ * | "false,false" | 0 | false | false |
1347
+ * | "false,false" | 1 | false | false |
1348
+ * | "false,true" | 0 | false | true |
1349
+ * | "false,true" | 1 | true | true |
1350
+ * | "true,false" | 0 | true | true |
1351
+ * | "true,false" | 1 | false | true |
1352
+ * | "true,true" | 0 | true | true |
1353
+ * | "true,true" | 1 | true | true |
1354
+ */
1355
+ predicate lift ( string id , int i , boolean operandBranch , boolean branch ) ;
1223
1356
}
1224
1357
1225
1358
/** Holds if `guard` controls block `bb` upon evaluating to `branch`. */
@@ -1681,6 +1814,26 @@ module Make<LocationSig Location, InputSig<Location> Input> {
1681
1814
DfInput:: Guard g , Definition def , boolean branch , State state
1682
1815
) {
1683
1816
guardChecks ( g , DfInput:: getARead ( def ) , branch , state )
1817
+ or
1818
+ exists ( int last |
1819
+ logicalOperationGuardChecksSsaDef ( g , _, last , def , branch , state ) and
1820
+ last = strictcount ( int i | exists ( g .( DfInput:: LogicalOperationGuard ) .getOperand ( i ) ) ) - 1
1821
+ )
1822
+ }
1823
+
1824
+ pragma [ nomagic]
1825
+ private predicate logicalOperationGuardChecksSsaDef (
1826
+ DfInput:: LogicalOperationGuard op , string id , int i , Definition def , boolean branch ,
1827
+ State state
1828
+ ) {
1829
+ exists ( boolean operandBranch |
1830
+ op .lift ( id , i , operandBranch , branch ) and
1831
+ guardChecksSsaDef ( op .getOperand ( i ) , def , operandBranch , state )
1832
+ |
1833
+ i = 0
1834
+ or
1835
+ logicalOperationGuardChecksSsaDef ( op , id , i - 1 , def , branch , state )
1836
+ )
1684
1837
}
1685
1838
1686
1839
/** Gets a node that is safely guarded by the given guard check. */
0 commit comments