• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Alan-Jowett / ebpf-verifier / 19036509529

02 Nov 2025 09:22PM UTC coverage: 86.936% (-0.5%) from 87.448%
19036509529

push

github

elazarg
Bump external/libbtf from `b674148` to `04281ee`

Bumps [external/libbtf](https://github.com/Alan-Jowett/libbtf) from `b674148` to `04281ee`.
- [Release notes](https://github.com/Alan-Jowett/libbtf/releases)
- [Commits](https://github.com/Alan-Jowett/libbtf/compare/b6741487e...<a class=hub.com/Alan-Jowett/ebpf-verifier/commit/04281ee7a91595911807897b4ca2e7483cf97497">04281ee7a)

---
updated-dependencies:
- dependency-name: external/libbtf
  dependency-version: '04281ee7a91595911807897b4ca2e7483cf97497'
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

9044 of 10403 relevant lines covered (86.94%)

3989157.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.48
/src/crab/finite_domain.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
#include <optional>
4
#include <utility>
5

6
#include "arith/dsl_syntax.hpp"
7
#include "arith/linear_constraint.hpp"
8
#include "arith/variable.hpp"
9
#include "crab/finite_domain.hpp"
10
#include "crab/interval.hpp"
11
#include "crab/split_dbm.hpp"
12
#include "ir/syntax.hpp"
13
#include "string_constraints.hpp"
14

15
namespace prevail {
16

17
using NumAbsDomain = SplitDBM;
18

19
Interval FiniteDomain::eval_interval(const Variable v, const int finite_width) const {
962✔
20
    const Interval span = dom.eval_interval(v);
962✔
21
    return variable_registry->is_unsigned(v) ? span.zero_extend(finite_width) : span.sign_extend(finite_width);
1,443✔
22
}
962✔
23

24
static std::vector<LinearConstraint> assume_bit_cst_interval(Condition::Op op, bool is64, Interval dst_interval,
52✔
25
                                                             Interval src_interval) {
26
    using namespace dsl_syntax;
26✔
27
    using Op = Condition::Op;
26✔
28

29
    const auto dst_n = dst_interval.singleton();
52✔
30
    if (!dst_n || !dst_n.value().fits_cast_to<int64_t>()) {
52✔
31
        return {};
8✔
32
    }
33

34
    const auto src_n = src_interval.singleton();
44✔
35
    if (!src_n || !src_n->fits_cast_to<int64_t>()) {
44✔
36
        return {};
×
37
    }
38
    uint64_t src_int_value = src_n.value().cast_to<uint64_t>();
44✔
39
    if (!is64) {
44✔
40
        src_int_value = gsl::narrow_cast<uint32_t>(src_int_value);
20✔
41
    }
42

43
    bool result;
22✔
44
    switch (op) {
44✔
45
    case Op::SET: result = (dst_n.value().cast_to<uint64_t>() & src_int_value) != 0; break;
22✔
46
    case Op::NSET: result = (dst_n.value().cast_to<uint64_t>() & src_int_value) == 0; break;
22✔
47
    default: throw std::exception();
×
48
    }
49

50
    return {result ? LinearConstraint::true_const() : LinearConstraint::false_const()};
132✔
51
}
118✔
52

53
std::vector<LinearConstraint> FiniteDomain::assume_signed_64bit_eq(const Variable left_svalue,
8,346✔
54
                                                                   const Variable left_uvalue,
55
                                                                   const Interval& right_interval,
56
                                                                   const LinearExpression& right_svalue,
57
                                                                   const LinearExpression& right_uvalue) const {
58
    using namespace dsl_syntax;
4,173✔
59
    if (right_interval <= Interval::nonnegative(64) && !right_interval.is_singleton()) {
16,688✔
60
        return {(left_svalue == right_svalue), (left_uvalue == right_uvalue), eq(left_svalue, left_uvalue)};
×
61
    } else {
62
        return {(left_svalue == right_svalue), (left_uvalue == right_uvalue)};
29,211✔
63
    }
64
}
16,692✔
65

66
std::vector<LinearConstraint> FiniteDomain::assume_signed_32bit_eq(const Variable left_svalue,
3,452✔
67
                                                                   const Variable left_uvalue,
68
                                                                   const Interval& right_interval) const {
69
    using namespace dsl_syntax;
1,726✔
70

71
    if (const auto rn = right_interval.singleton()) {
3,452✔
72
        const auto left_svalue_interval = eval_interval(left_svalue);
3,240✔
73
        if (auto size = left_svalue_interval.finite_size()) {
3,240✔
74
            // Find the lowest 64-bit svalue whose low 32 bits match the singleton.
75

76
            // Get lower bound as a 64-bit value.
77
            int64_t lb = left_svalue_interval.lb().number()->cast_to<int64_t>();
4,904✔
78

79
            // Use the high 32-bits from the left lower bound and the low 32-bits from the right singleton.
80
            // The result might be lower than the lower bound.
81
            const int64_t lb_match = (lb & 0xFFFFFFFF00000000) | (rn->cast_to<int64_t>() & 0xFFFFFFFF);
2,452✔
82
            if (lb_match < lb) {
2,452✔
83
                // The result is lower than the left interval, so try the next higher matching 64-bit value.
84
                // It's ok if this goes higher than the left upper bound.
85
                lb += 0x100000000;
1,358✔
86
            }
87

88
            // Find the highest 64-bit svalue whose low 32 bits match the singleton.
89

90
            // Get upper bound as a 64-bit value.
91
            const int64_t ub = left_svalue_interval.ub().number()->cast_to<int64_t>();
4,904✔
92

93
            // Use the high 32-bits from the left upper bound and the low 32-bits from the right singleton.
94
            // The result might be higher than the upper bound.
95
            const int64_t ub_match = (ub & 0xFFFFFFFF00000000) | (rn->cast_to<int64_t>() & 0xFFFFFFFF);
2,452✔
96
            if (ub_match > ub) {
2,452✔
97
                // The result is higher than the left interval, so try the next lower matching 64-bit value.
98
                // It's ok if this goes lower than the left lower bound.
99
                lb -= 0x100000000;
1,293✔
100
            }
101

102
            if (to_unsigned(lb_match) <= to_unsigned(ub_match)) {
2,452✔
103
                // The interval is also valid when cast to a uvalue, meaning
104
                // both bounds are positive or both are negative.
105
                return {left_svalue >= lb_match, left_svalue <= ub_match, left_uvalue >= to_unsigned(lb_match),
1,117✔
106
                        left_uvalue <= to_unsigned(ub_match)};
12,287✔
107
            } else {
108
                // The interval can only be represented as an svalue.
109
                return {left_svalue >= lb_match, left_svalue <= ub_match};
763✔
110
            }
111
        }
3,240✔
112
    }
4,966✔
113
    return {};
1,000✔
114
}
7,247✔
115

116
// Given left and right values, get the left and right intervals, and also split
117
// the left interval into separate negative and positive intervals.
118
void FiniteDomain::get_signed_intervals(bool is64, const Variable left_svalue, const Variable left_uvalue,
15,826✔
119
                                        const LinearExpression& right_svalue, Interval& left_interval,
120
                                        Interval& right_interval, Interval& left_interval_positive,
121
                                        Interval& left_interval_negative) const {
122

123
    using namespace dsl_syntax;
7,913✔
124

125
    // Get intervals as 32-bit or 64-bit as appropriate.
126
    left_interval = eval_interval(left_svalue);
15,826✔
127
    right_interval = eval_interval(right_svalue);
15,826✔
128
    if (!is64) {
15,826✔
129
        if ((left_interval <= Interval::nonnegative(32) && right_interval <= Interval::nonnegative(32)) ||
15,537✔
130
            (left_interval <= Interval::negative(32) && right_interval <= Interval::negative(32))) {
9,416✔
131
            is64 = true;
9,008✔
132
            // fallthrough as 64bit, including deduction of relational information
133
        } else {
134
            left_interval = left_interval.truncate_to<int32_t>();
2,868✔
135
            right_interval = right_interval.truncate_to<int32_t>();
4,302✔
136
            // continue as 32bit
137
        }
138
    }
139

140
    if (!left_interval.is_top()) {
15,826✔
141
        left_interval_positive = left_interval & Interval::nonnegative(64);
20,670✔
142
        left_interval_negative = left_interval & Interval::negative(64);
27,560✔
143
    } else {
144
        left_interval = eval_interval(left_uvalue);
2,046✔
145
        if (!left_interval.is_top()) {
2,046✔
146
            // The interval is TOP as a signed interval but is represented precisely as an unsigned interval,
147
            // so split into two signed intervals that can be treated separately.
148
            left_interval_positive = left_interval & Interval::nonnegative(64);
270✔
149
            const Number lih_ub =
90✔
150
                left_interval.ub().number() ? left_interval.ub().number()->truncate_to<int64_t>() : -1;
514✔
151
            left_interval_negative = Interval{std::numeric_limits<int64_t>::min(), lih_ub};
360✔
152
        } else {
180✔
153
            left_interval_positive = Interval::nonnegative(64);
1,866✔
154
            left_interval_negative = Interval::negative(64);
2,799✔
155
        }
156
    }
157

158
    left_interval = left_interval.truncate_to<int64_t>();
15,826✔
159
    right_interval = right_interval.truncate_to<int64_t>();
15,826✔
160
}
15,826✔
161

162
// Given left and right values, get the left and right intervals, and also split
163
// the left interval into separate low and high intervals.
164
void FiniteDomain::get_unsigned_intervals(bool is64, const Variable left_svalue, const Variable left_uvalue,
15,382✔
165
                                          const LinearExpression& right_uvalue, Interval& left_interval,
166
                                          Interval& right_interval, Interval& left_interval_low,
167
                                          Interval& left_interval_high) const {
168

169
    using namespace dsl_syntax;
7,691✔
170

171
    // Get intervals as 32-bit or 64-bit as appropriate.
172
    left_interval = eval_interval(left_uvalue);
15,382✔
173
    right_interval = eval_interval(right_uvalue);
15,382✔
174
    if (!is64) {
15,382✔
175
        if ((left_interval <= Interval::nonnegative(32) && right_interval <= Interval::nonnegative(32)) ||
12,906✔
176
            (left_interval <= Interval::unsigned_high(32) && right_interval <= Interval::unsigned_high(32))) {
7,300✔
177
            is64 = true;
8,924✔
178
            // fallthrough as 64bit, including deduction of relational information
179
        } else {
180
            left_interval = left_interval.truncate_to<uint32_t>();
1,926✔
181
            right_interval = right_interval.truncate_to<uint32_t>();
2,889✔
182
            // continue as 32bit
183
        }
184
    }
185

186
    if (!left_interval.is_top()) {
15,382✔
187
        left_interval_low = left_interval & Interval::nonnegative(64);
19,713✔
188
        left_interval_high = left_interval & Interval::unsigned_high(64);
26,284✔
189
    } else {
190
        left_interval = eval_interval(left_svalue);
2,240✔
191
        if (!left_interval.is_top()) {
2,240✔
192
            // The interval is TOP as an unsigned interval but is represented precisely as a signed interval,
193
            // so split into two unsigned intervals that can be treated separately.
194
            left_interval_low = Interval(0, left_interval.ub()).truncate_to<uint64_t>();
455✔
195
            left_interval_high = Interval(left_interval.lb(), -1).truncate_to<uint64_t>();
546✔
196
        } else {
197
            left_interval_low = Interval::nonnegative(64);
2,058✔
198
            left_interval_high = Interval::unsigned_high(64);
3,087✔
199
        }
200
    }
201

202
    left_interval = left_interval.truncate_to<uint64_t>();
15,382✔
203
    right_interval = right_interval.truncate_to<uint64_t>();
15,382✔
204
}
15,382✔
205

206
std::vector<LinearConstraint>
207
FiniteDomain::assume_signed_64bit_lt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
868✔
208
                                     const Interval& left_interval_positive, const Interval& left_interval_negative,
209
                                     const LinearExpression& right_svalue, const LinearExpression& right_uvalue,
210
                                     const Interval& right_interval) const {
211

212
    using namespace dsl_syntax;
434✔
213

214
    if (right_interval <= Interval::negative(64)) {
1,302✔
215
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1].
216
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue, 0 <= left_uvalue,
138✔
217
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
690✔
218
    } else if ((left_interval_negative | left_interval_positive) <= Interval::nonnegative(64) &&
1,870✔
219
               right_interval <= Interval::nonnegative(64)) {
820✔
220
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
221
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue, 0 <= left_uvalue,
90✔
222
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
450✔
223
    } else {
224
        // Interval can only be represented as an svalue.
225
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
1,920✔
226
    }
227
}
1,964✔
228

229
std::vector<LinearConstraint>
230
FiniteDomain::assume_signed_32bit_lt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
670✔
231
                                     const Interval& left_interval_positive, const Interval& left_interval_negative,
232
                                     const LinearExpression& right_svalue, const LinearExpression& right_uvalue,
233
                                     const Interval& right_interval) const {
234

235
    using namespace dsl_syntax;
335✔
236

237
    if (right_interval <= Interval::negative(32)) {
1,005✔
238
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1],
239
        // aka [INT_MAX+1, UINT_MAX].
240
        return {std::numeric_limits<int32_t>::max() < left_uvalue,
49✔
241
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
98✔
242
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
490✔
243
    } else if ((left_interval_negative | left_interval_positive) <= Interval::nonnegative(32) &&
1,438✔
244
               right_interval <= Interval::nonnegative(32)) {
588✔
245
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX]
246
        const auto lpub = left_interval_positive.truncate_to<int32_t>().ub();
24✔
247
        return {left_svalue >= 0,
8✔
248
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue,
16✔
249
                left_svalue <= left_uvalue,
250
                left_svalue >= left_uvalue,
251
                left_uvalue >= 0,
252
                strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
16✔
253
                left_uvalue <= *lpub.number()};
152✔
254
    } else if (eval_interval(left_svalue) <= Interval::signed_int(32) &&
1,870✔
255
               eval_interval(right_svalue) <= Interval::signed_int(32)) {
944✔
256
        // Interval can only be represented as an svalue.
257
        return {strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
582✔
258
    } else {
259
        // We can't directly compare the svalues since they may differ in high order bits.
260
        return {};
362✔
261
    }
262
}
803✔
263

264
std::vector<LinearConstraint>
265
FiniteDomain::assume_signed_64bit_gt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
868✔
266
                                     const Interval& left_interval_positive, const Interval& left_interval_negative,
267
                                     const LinearExpression& right_svalue, const LinearExpression& right_uvalue,
268
                                     const Interval& right_interval) const {
269

270
    using namespace dsl_syntax;
434✔
271

272
    if (right_interval <= Interval::nonnegative(64)) {
1,302✔
273
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
274
        const auto lpub = left_interval_positive.truncate_to<int64_t>().ub();
1,092✔
275
        return {left_svalue >= 0,
364✔
276
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue,
728✔
277
                left_svalue <= left_uvalue,
278
                left_svalue >= left_uvalue,
279
                left_uvalue >= 0,
280
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
728✔
281
                left_uvalue <= *lpub.number()};
6,916✔
282
    } else if ((left_interval_negative | left_interval_positive) <= Interval::negative(64) &&
715✔
283
               right_interval <= Interval::negative(64)) {
142✔
284
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1],
285
        // aka [INT_MAX+1, UINT_MAX].
286
        return {std::numeric_limits<int64_t>::max() < left_uvalue,
1✔
287
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
2✔
288
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
9✔
289
    } else {
290
        // Interval can only be represented as an svalue.
291
        return {strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
414✔
292
    }
293
}
3,558✔
294

295
std::vector<LinearConstraint>
296
FiniteDomain::assume_signed_32bit_gt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
672✔
297
                                     const Interval& left_interval_positive, const Interval& left_interval_negative,
298
                                     const LinearExpression& right_svalue, const LinearExpression& right_uvalue,
299
                                     const Interval& right_interval) const {
300

301
    using namespace dsl_syntax;
336✔
302

303
    if (right_interval <= Interval::nonnegative(32)) {
1,008✔
304
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
305
        const auto lpub = left_interval_positive.truncate_to<int32_t>().ub();
858✔
306
        return {left_svalue >= 0,
286✔
307
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue,
572✔
308
                left_svalue <= left_uvalue,
309
                left_svalue >= left_uvalue,
310
                left_uvalue >= 0,
311
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
572✔
312
                left_uvalue <= *lpub.number()};
5,434✔
313
    } else if ((left_interval_negative | left_interval_positive) <= Interval::negative(32) &&
537✔
314
               right_interval <= Interval::negative(32)) {
102✔
315
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MIN, -1],
316
        // aka [INT_MAX+1, UINT_MAX].
317
        return {left_uvalue >= Number{std::numeric_limits<int32_t>::max()} + 1,
3✔
318
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
2✔
319
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
9✔
320
    } else if (eval_interval(left_svalue) <= Interval::signed_int(32) &&
310✔
321
               eval_interval(right_svalue) <= Interval::signed_int(32)) {
130✔
322
        // Interval can only be represented as an svalue.
323
        return {strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
48✔
324
    } else {
325
        // We can't directly compare the svalues since they may differ in high order bits.
326
        return {};
82✔
327
    }
328
}
2,615✔
329

330
std::vector<LinearConstraint> FiniteDomain::assume_signed_cst_interval(Condition::Op op, bool is64,
15,064✔
331
                                                                       Variable left_svalue, Variable left_uvalue,
332
                                                                       const LinearExpression& right_svalue,
333
                                                                       const LinearExpression& right_uvalue) const {
334

335
    using namespace dsl_syntax;
7,532✔
336

337
    Interval left_interval = Interval::bottom();
15,064✔
338
    Interval right_interval = Interval::bottom();
15,064✔
339
    Interval left_interval_positive = Interval::bottom();
15,064✔
340
    Interval left_interval_negative = Interval::bottom();
15,064✔
341
    get_signed_intervals(is64, left_svalue, left_uvalue, right_svalue, left_interval, right_interval,
15,064✔
342
                         left_interval_positive, left_interval_negative);
343

344
    if (op == Condition::Op::EQ) {
15,064✔
345
        // Handle svalue == right.
346
        if (is64) {
11,798✔
347
            return assume_signed_64bit_eq(left_svalue, left_uvalue, right_interval, right_svalue, right_uvalue);
8,346✔
348
        } else {
349
            return assume_signed_32bit_eq(left_svalue, left_uvalue, right_interval);
3,452✔
350
        }
351
    }
352

353
    const bool is_lt = op == Condition::Op::SLT || op == Condition::Op::SLE;
3,266✔
354
    bool strict = op == Condition::Op::SLT || op == Condition::Op::SGT;
3,266✔
355

356
    auto llb = left_interval.lb();
3,266✔
357
    auto lub = left_interval.ub();
3,266✔
358
    auto rlb = right_interval.lb();
3,266✔
359
    auto rub = right_interval.ub();
3,266✔
360
    if (!is_lt && (strict ? lub <= rlb : lub < rlb)) {
3,831✔
361
        // Left signed interval is lower than right signed interval.
362
        return {LinearConstraint::false_const()};
130✔
363
    } else if (is_lt && (strict ? llb >= rub : llb > rub)) {
3,456✔
364
        // Left signed interval is higher than right signed interval.
365
        return {LinearConstraint::false_const()};
95✔
366
    }
367
    if (is_lt && (strict ? lub < rlb : lub <= rlb)) {
3,739✔
368
        // Left signed interval is lower than right signed interval.
369
        return {LinearConstraint::true_const()};
150✔
370
    } else if (!is_lt && (strict ? llb > rub : llb >= rub)) {
3,350✔
371
        // Left signed interval is higher than right signed interval.
372
        return {LinearConstraint::true_const()};
95✔
373
    }
374

375
    if (is64) {
3,078✔
376
        if (is_lt) {
1,736✔
377
            return assume_signed_64bit_lt(strict, left_svalue, left_uvalue, left_interval_positive,
434✔
378
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
868✔
379
        } else {
380
            return assume_signed_64bit_gt(strict, left_svalue, left_uvalue, left_interval_positive,
434✔
381
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
868✔
382
        }
383
    } else {
384
        // 32-bit compare.
385
        if (is_lt) {
1,342✔
386
            return assume_signed_32bit_lt(strict, left_svalue, left_uvalue, left_interval_positive,
335✔
387
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
670✔
388
        } else {
389
            return assume_signed_32bit_gt(strict, left_svalue, left_uvalue, left_interval_positive,
336✔
390
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
672✔
391
        }
392
    }
393
    return {};
394
}
39,481✔
395

396
std::vector<LinearConstraint>
397
FiniteDomain::assume_unsigned_64bit_lt(bool strict, Variable left_svalue, Variable left_uvalue,
1,382✔
398
                                       const Interval& left_interval_low, const Interval& left_interval_high,
399
                                       const LinearExpression& right_svalue, const LinearExpression& right_uvalue,
400
                                       const Interval& right_interval) const {
401

402
    using namespace dsl_syntax;
691✔
403

404
    auto rub = right_interval.ub();
1,382✔
405
    auto lllb = left_interval_low.truncate_to<uint64_t>().lb();
2,073✔
406
    if (right_interval <= Interval::nonnegative(64) && (strict ? lllb >= rub : lllb > rub)) {
2,391✔
407
        // The high interval is out of range.
408
        if (auto lsubn = eval_interval(left_svalue).ub().number()) {
×
409
            return {left_uvalue >= 0, (strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue),
×
410
                    left_uvalue <= *lsubn, left_svalue >= 0};
×
411
        } else {
412
            return {left_uvalue >= 0, (strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue),
×
413
                    left_svalue >= 0};
×
414
        }
×
415
    }
416
    auto lhlb = left_interval_high.truncate_to<uint64_t>().lb();
2,073✔
417
    if (right_interval <= Interval::unsigned_high(64) && (strict ? lhlb >= rub : lhlb > rub)) {
2,074✔
418
        // The high interval is out of range.
419
        if (auto lsubn = eval_interval(left_svalue).ub().number()) {
12✔
420
            return {left_uvalue >= 0, (strict ? left_uvalue < *rub.number() : left_uvalue <= *rub.number()),
6✔
421
                    left_uvalue <= *lsubn, left_svalue >= 0};
24✔
422
        } else {
423
            return {left_uvalue >= 0, (strict ? left_uvalue < *rub.number() : left_uvalue <= *rub.number()),
×
424
                    left_svalue >= 0};
×
425
        }
4✔
426
    }
427
    if (right_interval <= Interval::signed_int(64)) {
2,067✔
428
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
429
        auto llub = left_interval_low.truncate_to<uint64_t>().ub();
1,656✔
430
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
1,104✔
431
                left_uvalue <= *llub.number(), 0 <= left_svalue,
1,104✔
432
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
7,728✔
433
    } else if (left_interval_low.is_bottom() && right_interval <= Interval::unsigned_high(64)) {
827✔
434
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MAX+1, UINT_MAX].
435
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
2✔
436
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
10✔
437
    } else if ((left_interval_low | left_interval_high) == Interval::unsigned_int(64)) {
544✔
438
        // Interval can only be represented as a uvalue, and was TOP before.
439
        return {strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
60✔
440
    } else {
441
        // Interval can only be represented as a uvalue.
442
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
1,008✔
443
    }
444
}
8,283✔
445

446
std::vector<LinearConstraint> FiniteDomain::assume_unsigned_32bit_lt(const bool strict, const Variable left_svalue,
490✔
447
                                                                     const Variable left_uvalue,
448
                                                                     const LinearExpression& right_svalue,
449
                                                                     const LinearExpression& right_uvalue) const {
450

451
    using namespace dsl_syntax;
245✔
452

453
    if (eval_interval(left_uvalue) <= Interval::nonnegative(32) &&
1,738✔
454
        eval_interval(right_uvalue) <= Interval::nonnegative(32)) {
1,026✔
455
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT32_MAX].
456
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
262✔
457
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
1,310✔
458
    } else if (eval_interval(left_svalue) <= Interval::negative(32) &&
686✔
459
               eval_interval(right_svalue) <= Interval::negative(32)) {
232✔
460
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT32_MIN, -1].
461
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue,
2✔
462
                strict ? left_svalue < right_svalue : left_svalue <= right_svalue};
10✔
463
    } else if (eval_interval(left_uvalue) <= Interval::unsigned_int(32) &&
810✔
464
               eval_interval(right_uvalue) <= Interval::unsigned_int(32)) {
490✔
465
        // Interval can only be represented as a uvalue.
466
        return {0 <= left_uvalue, strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
400✔
467
    } else {
468
        // We can't directly compare the uvalues since they may differ in high order bits.
469
        return {};
126✔
470
    }
471
}
1,224✔
472

473
std::vector<LinearConstraint>
474
FiniteDomain::assume_unsigned_64bit_gt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
6,366✔
475
                                       const Interval& left_interval_low, const Interval& left_interval_high,
476
                                       const LinearExpression& right_svalue, const LinearExpression& right_uvalue,
477
                                       const Interval& right_interval) const {
478

479
    using namespace dsl_syntax;
3,183✔
480

481
    const auto rlb = right_interval.lb();
6,366✔
482
    const auto llub = left_interval_low.truncate_to<uint64_t>().ub();
9,549✔
483
    const auto lhlb = left_interval_high.truncate_to<uint64_t>().lb();
9,549✔
484

485
    if (right_interval <= Interval::nonnegative(64) && (strict ? llub <= rlb : llub < rlb)) {
9,657✔
486
        // The low interval is out of range.
487
        return {strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
16✔
488
                *lhlb.number() == std::numeric_limits<uint64_t>::max() ? left_uvalue == *lhlb.number()
46✔
489
                                                                       : left_uvalue >= *lhlb.number(),
6✔
490
                left_svalue < 0};
72✔
491
    } else if (right_interval <= Interval::unsigned_high(64)) {
9,525✔
492
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MAX+1, UINT_MAX].
493
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
4✔
494
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
18✔
495
    } else if ((left_interval_low | left_interval_high) <= Interval::nonnegative(64) &&
18,579✔
496
               right_interval <= Interval::nonnegative(64)) {
11,774✔
497
        // Interval can be represented as both an svalue and a uvalue since it fits in [0, INT_MAX].
498
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
5,178✔
499
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
23,301✔
500
    } else {
501
        // Interval can only be represented as a uvalue.
502
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue};
4,672✔
503
    }
504
}
22,317✔
505

506
std::vector<LinearConstraint>
507
FiniteDomain::assume_unsigned_32bit_gt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
1,862✔
508
                                       const Interval& left_interval_low, const Interval& left_interval_high,
509
                                       const LinearExpression& right_svalue, const LinearExpression& right_uvalue,
510
                                       const Interval& right_interval) const {
511

512
    using namespace dsl_syntax;
931✔
513

514
    if (right_interval <= Interval::unsigned_high(32)) {
2,793✔
515
        // Interval can be represented as both an svalue and a uvalue since it fits in [INT_MAX+1, UINT_MAX].
516
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
8✔
517
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue};
36✔
518
    } else if (eval_interval(left_uvalue) <= Interval::unsigned_int(32) &&
6,894✔
519
               eval_interval(right_uvalue) <= Interval::unsigned_int(32)) {
4,518✔
520
        // Interval can only be represented as a uvalue.
521
        return {0 <= left_uvalue, strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue};
5,200✔
522
    } else {
523
        // We can't directly compare the uvalues since they may differ in high order bits.
524
        return {};
554✔
525
    }
526
}
3,924✔
527

528
std::vector<LinearConstraint> FiniteDomain::assume_unsigned_cst_interval(Condition::Op op, bool is64,
15,382✔
529
                                                                         Variable left_svalue, Variable left_uvalue,
530
                                                                         const LinearExpression& right_svalue,
531
                                                                         const LinearExpression& right_uvalue) const {
532

533
    using namespace dsl_syntax;
7,691✔
534

535
    Interval left_interval = Interval::bottom();
15,382✔
536
    Interval right_interval = Interval::bottom();
15,382✔
537
    Interval left_interval_low = Interval::bottom();
15,382✔
538
    Interval left_interval_high = Interval::bottom();
15,382✔
539
    get_unsigned_intervals(is64, left_svalue, left_uvalue, right_uvalue, left_interval, right_interval,
15,382✔
540
                           left_interval_low, left_interval_high);
541

542
    // Handle uvalue != right.
543
    if (op == Condition::Op::NE) {
15,382✔
544
        if (auto rn = right_interval.singleton()) {
11,828✔
545
            if (rn == left_interval.zero_extend(is64 ? 64 : 32).lb().number()) {
30,659✔
546
                // "NE lower bound" is equivalent to "GT lower bound".
547
                op = Condition::Op::GT;
7,064✔
548
                right_interval = Interval{left_interval.lb()};
14,128✔
549
            } else if (rn == left_interval.ub().number()) {
9,104✔
550
                // "NE upper bound" is equivalent to "LT upper bound".
551
                op = Condition::Op::LT;
350✔
552
                right_interval = Interval{left_interval.ub()};
700✔
553
            } else {
554
                return {};
4,202✔
555
            }
556
        } else {
557
            return {};
212✔
558
        }
11,828✔
559
    }
560

561
    const bool is_lt = op == Condition::Op::LT || op == Condition::Op::LE;
10,968✔
562
    bool strict = op == Condition::Op::LT || op == Condition::Op::GT;
10,968✔
563

564
    auto [llb, lub] = left_interval.pair();
16,452✔
565
    auto [rlb, rub] = right_interval.pair();
16,452✔
566
    if (is_lt ? (strict ? llb >= rub : llb > rub) : (strict ? lub <= rlb : lub < rlb)) {
11,795✔
567
        // Left unsigned interval is lower than right unsigned interval.
568
        return {LinearConstraint::false_const()};
1,550✔
569
    }
570
    if (is_lt && (strict ? lub < rlb : lub <= rlb)) {
10,829✔
571
        // Left unsigned interval is lower than right unsigned interval. We still add a
572
        // relationship for use when widening, such as is used in the prime conformance test.
573
        if (is64) {
114✔
574
            return {strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
246✔
575
        }
576
        return {};
32✔
577
    } else if (!is_lt && (strict ? llb > rub : llb >= rub)) {
14,129✔
578
        // Left unsigned interval is higher than right unsigned interval. We still add a
579
        // relationship for use when widening, such as is used in the prime conformance test.
580
        if (is64) {
134✔
581
            return {strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue};
276✔
582
        } else {
583
            return {};
42✔
584
        }
585
    }
586

587
    if (is64) {
10,100✔
588
        if (is_lt) {
7,748✔
589
            return assume_unsigned_64bit_lt(strict, left_svalue, left_uvalue, left_interval_low, left_interval_high,
691✔
590
                                            right_svalue, right_uvalue, right_interval);
1,382✔
591
        } else {
592
            return assume_unsigned_64bit_gt(strict, left_svalue, left_uvalue, left_interval_low, left_interval_high,
3,183✔
593
                                            right_svalue, right_uvalue, right_interval);
6,366✔
594
        }
595
    } else {
596
        if (is_lt) {
2,352✔
597
            return assume_unsigned_32bit_lt(strict, left_svalue, left_uvalue, right_svalue, right_uvalue);
490✔
598
        } else {
599
            return assume_unsigned_32bit_gt(strict, left_svalue, left_uvalue, left_interval_low, left_interval_high,
931✔
600
                                            right_svalue, right_uvalue, right_interval);
1,862✔
601
        }
602
    }
603
}
50,304✔
604

605
/** Linear constraints for a comparison with a constant.
606
 */
607
std::vector<LinearConstraint> FiniteDomain::assume_cst_imm(const Condition::Op op, const bool is64,
28,450✔
608
                                                           const Variable dst_svalue, const Variable dst_uvalue,
609
                                                           const int64_t imm) const {
610
    using namespace dsl_syntax;
14,225✔
611
    using Op = Condition::Op;
14,225✔
612
    switch (op) {
28,450✔
613
    case Op::EQ:
14,766✔
614
    case Op::SGE:
7,383✔
615
    case Op::SLE:
7,383✔
616
    case Op::SGT:
7,383✔
617
    case Op::SLT:
7,383✔
618
        return assume_signed_cst_interval(op, is64, dst_svalue, dst_uvalue, imm, gsl::narrow_cast<uint64_t>(imm));
14,766✔
619
    case Op::SET:
28✔
620
    case Op::NSET: return assume_bit_cst_interval(op, is64, eval_interval(dst_uvalue), Interval{imm});
70✔
621
    case Op::NE:
13,656✔
622
    case Op::GE:
6,828✔
623
    case Op::LE:
6,828✔
624
    case Op::GT:
6,828✔
625
    case Op::LT:
6,828✔
626
        return assume_unsigned_cst_interval(op, is64, dst_svalue, dst_uvalue, imm, gsl::narrow_cast<uint64_t>(imm));
13,656✔
627
    }
628
    return {};
×
629
}
630

631
/** Linear constraint for a numerical comparison between registers.
632
 */
633
std::vector<LinearConstraint> FiniteDomain::assume_cst_reg(const Condition::Op op, const bool is64,
6,966✔
634
                                                           const Variable dst_svalue, const Variable dst_uvalue,
635
                                                           const Variable src_svalue, const Variable src_uvalue) const {
636
    using namespace dsl_syntax;
3,483✔
637
    using Op = Condition::Op;
3,483✔
638
    if (is64) {
6,966✔
639
        switch (op) {
6,074✔
640
        case Op::EQ: {
976✔
641
            const Interval src_interval = eval_interval(src_svalue);
976✔
642
            if (!src_interval.is_singleton() && src_interval <= Interval::nonnegative(64)) {
1,248✔
643
                return {eq(dst_svalue, src_svalue), eq(dst_uvalue, src_uvalue), eq(dst_svalue, dst_uvalue)};
558✔
644
            } else {
645
                return {eq(dst_svalue, src_svalue), eq(dst_uvalue, src_uvalue)};
2,982✔
646
            }
647
        }
976✔
648
        case Op::NE: return {neq(dst_svalue, src_svalue)};
2,460✔
649
        case Op::SGE: return {dst_svalue >= src_svalue};
35✔
650
        case Op::SLE: return {dst_svalue <= src_svalue};
3,665✔
651
        case Op::SGT: return {dst_svalue > src_svalue};
3,660✔
652
        // Note: reverse the test as a workaround strange lookup:
653
        case Op::SLT: return {src_svalue > dst_svalue};
35✔
654
        case Op::SET:
12✔
655
        case Op::NSET: return assume_bit_cst_interval(op, is64, eval_interval(dst_uvalue), eval_interval(src_uvalue));
30✔
656
        case Op::GE:
1,144✔
657
        case Op::LE:
572✔
658
        case Op::GT:
572✔
659
        case Op::LT: return assume_unsigned_cst_interval(op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
1,144✔
660
        }
661
    } else {
662
        switch (op) {
892✔
663
        case Op::EQ:
298✔
664
        case Op::SGE:
149✔
665
        case Op::SLE:
149✔
666
        case Op::SGT:
149✔
667
        case Op::SLT: return assume_signed_cst_interval(op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
298✔
668
        case Op::SET:
12✔
669
        case Op::NSET: return assume_bit_cst_interval(op, is64, eval_interval(dst_uvalue), eval_interval(src_uvalue));
30✔
670
        case Op::NE:
582✔
671
        case Op::GE:
291✔
672
        case Op::LE:
291✔
673
        case Op::GT:
291✔
674
        case Op::LT: return assume_unsigned_cst_interval(op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
582✔
675
        }
676
    }
677
    assert(false);
×
678
    throw std::exception();
679
}
8,622✔
680

681
void FiniteDomain::assign(const Variable x, const std::optional<LinearExpression>& e) { dom.assign(x, e); }
114,996✔
682
void FiniteDomain::assign(const Variable x, const Variable e) { dom.assign(x, e); }
146,724✔
683
void FiniteDomain::assign(const Variable x, const LinearExpression& e) { dom.assign(x, e); }
98,336✔
684
void FiniteDomain::assign(const Variable x, const int64_t e) { dom.set(x, Interval(e)); }
136,140✔
685

686
// As defined in the BPF ISA specification, the immediate value of an unsigned modulo and division is treated
687
// differently depending on the width:
688
// * for 32 bit, as a 32-bit unsigned integer
689
// * for 64 bit, as a 32-bit (not 64 bit) signed integer
690
static Number read_imm_for_udiv_or_umod(const Number& imm, const int width) {
190✔
691
    assert(width == 32 || width == 64);
190✔
692
    if (width == 32) {
190✔
693
        return Number{imm.cast_to<uint32_t>()};
36✔
694
    }
695
    return Number{imm.cast_to<int32_t>()};
154✔
696
}
697

698
void FiniteDomain::apply(const ArithBinOp op, const Variable x, const Variable y, const Variable z,
3,476✔
699
                         const int finite_width) {
700
    switch (op) {
3,476✔
701
    case ArithBinOp::ADD: assign(x, LinearExpression(y).plus(z)); break;
3,996✔
702
    case ArithBinOp::SUB: assign(x, LinearExpression(y).subtract(z)); break;
1,110✔
703
    case ArithBinOp::MUL: set(x, eval_interval(y, finite_width) * eval_interval(z, finite_width)); break;
216✔
704
    default: CRAB_ERROR("DBM: unreachable");
×
705
    }
706
}
3,476✔
707

708
void FiniteDomain::apply(const ArithBinOp op, const Variable x, const Variable y, const Number& z,
21,976✔
709
                         const int finite_width) {
710
    switch (op) {
21,976✔
711
    case ArithBinOp::ADD: assign(x, LinearExpression(y).plus(z)); break;
31,098✔
712
    case ArithBinOp::SUB: assign(x, LinearExpression(y).subtract(z)); break;
1,488✔
713
    case ArithBinOp::MUL: set(x, eval_interval(y, finite_width) * Interval{z}); break;
756✔
714
    default: CRAB_ERROR("DBM: unreachable");
×
715
    }
716
}
21,976✔
717

718
// As defined in the BPF ISA specification, the immediate value of a signed modulo and division is treated
719
// differently depending on the width:
720
// * for 32 bit, as a 32-bit signed integer
721
// * for 64 bit, as a 64-bit signed integer
722
static Number read_imm_for_sdiv_or_smod(const Number& imm, const int width) {
48✔
723
    assert(width == 32 || width == 64);
48✔
724
    if (width == 32) {
48✔
725
        return Number{imm.cast_to<int32_t>()};
16✔
726
    }
727
    return Number{imm.cast_to<int64_t>()};
32✔
728
}
729

730
void FiniteDomain::apply(const SignedArithBinOp op, const Variable x, const Variable y, const Number& k,
238✔
731
                         const int finite_width) {
732
    switch (op) {
238✔
733
    case SignedArithBinOp::SDIV:
16✔
734
        set(x, eval_interval(y, finite_width).sdiv(Interval{read_imm_for_sdiv_or_smod(k, finite_width)}));
40✔
735
        break;
16✔
736
    case SignedArithBinOp::UDIV:
168✔
737
        set(x, eval_interval(y, finite_width).udiv(Interval{read_imm_for_udiv_or_umod(k, finite_width)}));
420✔
738
        break;
168✔
739
    case SignedArithBinOp::SREM:
32✔
740
        set(x, eval_interval(y, finite_width).srem(Interval{read_imm_for_sdiv_or_smod(k, finite_width)}));
80✔
741
        break;
32✔
742
    case SignedArithBinOp::UREM:
22✔
743
        set(x, eval_interval(y, finite_width).urem(Interval{read_imm_for_udiv_or_umod(k, finite_width)}));
55✔
744
        break;
22✔
745
    default: CRAB_ERROR("DBM: unreachable");
×
746
    }
747
}
238✔
748

749
void FiniteDomain::apply(const SignedArithBinOp op, const Variable x, const Variable y, const Variable z,
164✔
750
                         const int finite_width) {
751
    switch (op) {
164✔
752
    case SignedArithBinOp::SDIV: set(x, eval_interval(y, finite_width).sdiv(eval_interval(z, finite_width))); break;
48✔
753
    case SignedArithBinOp::UDIV: set(x, eval_interval(y, finite_width).udiv(eval_interval(z, finite_width))); break;
264✔
754
    case SignedArithBinOp::SREM: set(x, eval_interval(y, finite_width).srem(eval_interval(z, finite_width))); break;
90✔
755
    case SignedArithBinOp::UREM: set(x, eval_interval(y, finite_width).urem(eval_interval(z, finite_width))); break;
90✔
756
    default: CRAB_ERROR("DBM: unreachable");
×
757
    }
758
}
164✔
759

760
void FiniteDomain::apply(BitwiseBinOp op, Variable x, Variable y, Variable z, int finite_width) {
7,212✔
761
    Interval yi = dom.eval_interval(y);
7,212✔
762
    Interval zi = dom.eval_interval(z);
7,212✔
763
    if (op == BitwiseBinOp::SHL || op == BitwiseBinOp::LSHR || op == BitwiseBinOp::ASHR) {
7,212✔
764
        zi.mask_shift_count(finite_width);
482✔
765
    }
766

767
    Interval xi = Interval::bottom();
7,212✔
768
    switch (op) {
7,212✔
769
    case BitwiseBinOp::AND: xi = yi.bitwise_and(zi); break;
738✔
770
    case BitwiseBinOp::OR: xi = yi.bitwise_or(zi); break;
8,622✔
771
    case BitwiseBinOp::XOR: xi = yi.bitwise_xor(zi); break;
735✔
772
    case BitwiseBinOp::SHL: xi = yi.shl(zi); break;
723✔
773
    case BitwiseBinOp::LSHR: xi = yi.lshr(zi); break;
×
774
    case BitwiseBinOp::ASHR: xi = yi.ashr(zi); break;
×
775
    default: CRAB_ERROR("DBM: unreachable");
×
776
    }
777
    xi.mask_value(finite_width);
7,212✔
778
    set(x, xi);
7,212✔
779
}
14,424✔
780

781
// Apply a bitwise operator to a uvalue.
782
void FiniteDomain::apply(BitwiseBinOp op, Variable x, Variable y, const Number& k, int finite_width) {
30,702✔
783
    // Convert to intervals and perform the operation
784
    Interval yi = dom.eval_interval(y);
30,702✔
785
    Interval zi{k.cast_to<uint64_t>()};
30,702✔
786
    if (op == BitwiseBinOp::SHL || op == BitwiseBinOp::LSHR || op == BitwiseBinOp::ASHR) {
30,702✔
787
        zi.mask_shift_count(finite_width);
1,520✔
788
    }
789
    Interval xi = Interval::top();
30,702✔
790

791
    switch (op) {
30,702✔
792
    case BitwiseBinOp::AND: xi = yi.bitwise_and(zi); break;
42,669✔
793
    case BitwiseBinOp::OR: xi = yi.bitwise_or(zi); break;
834✔
794
    case BitwiseBinOp::XOR: xi = yi.bitwise_xor(zi); break;
270✔
795
    case BitwiseBinOp::SHL: xi = yi.shl(zi); break;
2,280✔
796
    case BitwiseBinOp::LSHR: xi = yi.lshr(zi); break;
×
797
    case BitwiseBinOp::ASHR: xi = yi.ashr(zi); break;
×
798
    default: CRAB_ERROR("DBM: unreachable");
×
799
    }
800
    xi.mask_value(finite_width);
30,702✔
801
    set(x, xi);
30,702✔
802
}
61,404✔
803

804
void FiniteDomain::apply(BinOp op, Variable x, Variable y, const Number& z, int finite_width) {
52,916✔
805
    std::visit([&](auto top) { apply(top, x, y, z, finite_width); }, op);
79,374✔
806
}
26,458✔
807

808
void FiniteDomain::apply(BinOp op, Variable x, Variable y, Variable z, int finite_width) {
10,852✔
809
    std::visit([&](auto top) { apply(top, x, y, z, finite_width); }, op);
16,278✔
810
}
5,426✔
811

812
void FiniteDomain::apply(const BinOp op, const Variable x, const Variable y, const Variable z) {
604✔
813
    apply(op, x, y, z, 0);
604✔
814
}
604✔
815

816
void FiniteDomain::overflow_bounds(Variable lhs, int finite_width, bool issigned) {
190,738✔
817
    using namespace dsl_syntax;
95,369✔
818
    auto interval = eval_interval(lhs);
190,738✔
819
    if (interval.size() >= Interval::unsigned_int(finite_width).size()) {
286,107✔
820
        // Interval covers the full space.
821
        havoc(lhs);
28,712✔
822
        return;
28,712✔
823
    }
824
    if (interval.is_bottom()) {
162,026✔
825
        havoc(lhs);
×
826
        return;
×
827
    }
828
    Number lb_value = interval.lb().number().value();
324,052✔
829
    Number ub_value = interval.ub().number().value();
324,052✔
830

831
    // Compute the interval, taking overflow into account.
832
    // For a signed result, we need to ensure the signed and unsigned results match
833
    // so for a 32-bit operation, 0x80000000 should be a positive 64-bit number not
834
    // a sign extended negative one.
835
    Number lb = lb_value.zero_extend(finite_width);
162,026✔
836
    Number ub = ub_value.zero_extend(finite_width);
162,026✔
837
    if (issigned) {
162,026✔
838
        lb = lb.truncate_to<int64_t>();
63,838✔
839
        ub = ub.truncate_to<int64_t>();
63,838✔
840
    }
841
    if (lb > ub) {
162,026✔
842
        // Range wraps in the middle, so we cannot represent as an unsigned interval.
843
        havoc(lhs);
3,830✔
844
        return;
3,830✔
845
    }
846
    auto new_interval = Interval{lb, ub};
395,490✔
847
    if (new_interval != interval) {
158,196✔
848
        // Update the variable, which will lose any relationships to other variables.
849
        dom.set(lhs, new_interval);
1,740✔
850
    }
851
}
279,411✔
852

853
void FiniteDomain::overflow_bounds(const Variable svalue, const Variable uvalue, const int finite_width) {
62,744✔
854
    overflow_bounds(svalue, finite_width, true);
62,744✔
855
    overflow_bounds(uvalue, finite_width, false);
62,744✔
856
}
62,744✔
857

858
void FiniteDomain::apply_signed(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
22,024✔
859
                                const Number& z, const int finite_width) {
860
    apply(op, xs, y, z, finite_width);
22,024✔
861
    if (finite_width) {
22,024✔
862
        assign(xu, xs);
11,992✔
863
        overflow_bounds(xs, xu, finite_width);
11,992✔
864
    }
865
}
22,024✔
866

867
void FiniteDomain::apply_signed(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
2,918✔
868
                                const Variable z, const int finite_width) {
869
    apply(op, xs, y, z, finite_width);
2,918✔
870
    if (finite_width) {
2,918✔
871
        assign(xu, xs);
2,900✔
872
        overflow_bounds(xs, xu, finite_width);
2,900✔
873
    }
874
}
2,918✔
875

876
void FiniteDomain::apply_unsigned(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
30,892✔
877
                                  const Number& z, const int finite_width) {
878
    apply(op, xu, y, z, finite_width);
30,892✔
879
    if (finite_width) {
30,892✔
880
        assign(xs, xu);
30,892✔
881
        overflow_bounds(xs, xu, finite_width);
30,892✔
882
    }
883
}
30,892✔
884

885
void FiniteDomain::apply_unsigned(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
7,330✔
886
                                  const Variable z, const int finite_width) {
887
    apply(op, xu, y, z, finite_width);
7,330✔
888
    if (finite_width) {
7,330✔
889
        assign(xs, xu);
7,330✔
890
        overflow_bounds(xs, xu, finite_width);
7,330✔
891
    }
892
}
7,330✔
893

894
void FiniteDomain::add(const Variable lhs, const Variable op2) { apply_signed(ArithBinOp::ADD, lhs, lhs, lhs, op2, 0); }
×
895
void FiniteDomain::add(const Variable lhs, const Number& op2) { apply_signed(ArithBinOp::ADD, lhs, lhs, lhs, op2, 0); }
9,040✔
896
void FiniteDomain::sub(const Variable lhs, const Variable op2) { apply_signed(ArithBinOp::SUB, lhs, lhs, lhs, op2, 0); }
4✔
897
void FiniteDomain::sub(const Variable lhs, const Number& op2) { apply_signed(ArithBinOp::SUB, lhs, lhs, lhs, op2, 0); }
992✔
898

899
// Add/subtract with overflow are both signed and unsigned. We can use either one of the two to compute the
900
// result before adjusting for overflow, though if one is top we want to use the other to retain precision.
901
void FiniteDomain::add_overflow(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
1,460✔
902
    apply_signed(ArithBinOp::ADD, lhss, lhsu, !eval_interval(lhss).is_top() ? lhss : lhsu, op2, finite_width);
1,843✔
903
}
1,460✔
904
void FiniteDomain::add_overflow(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
11,692✔
905
    apply_signed(ArithBinOp::ADD, lhss, lhsu, !eval_interval(lhss).is_top() ? lhss : lhsu, op2, finite_width);
16,682✔
906
}
11,692✔
907
void FiniteDomain::sub_overflow(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
4✔
908
    apply_signed(ArithBinOp::SUB, lhss, lhsu, !eval_interval(lhss).is_top() ? lhss : lhsu, op2, finite_width);
4✔
909
}
4✔
910
void FiniteDomain::sub_overflow(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
×
911
    apply_signed(ArithBinOp::SUB, lhss, lhsu, !eval_interval(lhss).is_top() ? lhss : lhsu, op2, finite_width);
×
912
}
×
913

914
void FiniteDomain::neg(const Variable lhss, const Variable lhsu, const int finite_width) {
152✔
915
    apply_signed(ArithBinOp::MUL, lhss, lhsu, lhss, -1, finite_width);
152✔
916
}
152✔
917
void FiniteDomain::mul(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
72✔
918
    apply_signed(ArithBinOp::MUL, lhss, lhsu, lhss, op2, finite_width);
72✔
919
}
72✔
920
void FiniteDomain::mul(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
100✔
921
    apply_signed(ArithBinOp::MUL, lhss, lhsu, lhss, op2, finite_width);
100✔
922
}
100✔
923
void FiniteDomain::sdiv(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
16✔
924
    apply_signed(SignedArithBinOp::SDIV, lhss, lhsu, lhss, op2, finite_width);
16✔
925
}
16✔
926
void FiniteDomain::sdiv(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
16✔
927
    apply_signed(SignedArithBinOp::SDIV, lhss, lhsu, lhss, op2, finite_width);
16✔
928
}
16✔
929
void FiniteDomain::udiv(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
88✔
930
    apply_unsigned(SignedArithBinOp::UDIV, lhss, lhsu, lhsu, op2, finite_width);
88✔
931
}
88✔
932
void FiniteDomain::udiv(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
168✔
933
    apply_unsigned(SignedArithBinOp::UDIV, lhss, lhsu, lhsu, op2, finite_width);
168✔
934
}
168✔
935
void FiniteDomain::srem(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
30✔
936
    apply_signed(SignedArithBinOp::SREM, lhss, lhsu, lhss, op2, finite_width);
30✔
937
}
30✔
938
void FiniteDomain::srem(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
32✔
939
    apply_signed(SignedArithBinOp::SREM, lhss, lhsu, lhss, op2, finite_width);
32✔
940
}
32✔
941
void FiniteDomain::urem(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
30✔
942
    apply_unsigned(SignedArithBinOp::UREM, lhss, lhsu, lhsu, op2, finite_width);
30✔
943
}
30✔
944
void FiniteDomain::urem(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
22✔
945
    apply_unsigned(SignedArithBinOp::UREM, lhss, lhsu, lhsu, op2, finite_width);
22✔
946
}
22✔
947

948
void FiniteDomain::bitwise_and(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
492✔
949
    apply_unsigned(BitwiseBinOp::AND, lhss, lhsu, lhsu, op2, finite_width);
492✔
950
}
492✔
951
void FiniteDomain::bitwise_and(const Variable lhss, const Variable lhsu, const Number& op2) {
28,446✔
952
    // Use finite width 64 to make the svalue be set as well as the uvalue.
953
    apply_unsigned(BitwiseBinOp::AND, lhss, lhsu, lhsu, op2, 64);
28,446✔
954
}
28,446✔
955
void FiniteDomain::bitwise_or(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
5,748✔
956
    apply_unsigned(BitwiseBinOp::OR, lhss, lhsu, lhsu, op2, finite_width);
5,748✔
957
}
5,748✔
958
void FiniteDomain::bitwise_or(const Variable lhss, const Variable lhsu, const Number& op2) {
556✔
959
    apply_unsigned(BitwiseBinOp::OR, lhss, lhsu, lhsu, op2, 64);
556✔
960
}
556✔
961
void FiniteDomain::bitwise_xor(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
490✔
962
    apply_unsigned(BitwiseBinOp::XOR, lhss, lhsu, lhsu, op2, finite_width);
490✔
963
}
490✔
964
void FiniteDomain::bitwise_xor(const Variable lhss, const Variable lhsu, const Number& op2) {
180✔
965
    apply_unsigned(BitwiseBinOp::XOR, lhss, lhsu, lhsu, op2, 64);
180✔
966
}
180✔
967
void FiniteDomain::shl_overflow(const Variable lhss, const Variable lhsu, const Variable op2) {
482✔
968
    apply_unsigned(BitwiseBinOp::SHL, lhss, lhsu, lhsu, op2, 64);
482✔
969
}
482✔
970
void FiniteDomain::shl_overflow(const Variable lhss, const Variable lhsu, const Number& op2) {
1,520✔
971
    apply_unsigned(BitwiseBinOp::SHL, lhss, lhsu, lhsu, op2, 64);
1,520✔
972
}
1,520✔
973

974
void FiniteDomain::shl(const Variable svalue, const Variable uvalue, const int imm, const int finite_width) {
5,600✔
975
    const auto uinterval = eval_interval(uvalue);
5,600✔
976
    if (!uinterval.finite_size()) {
5,600✔
977
        shl_overflow(svalue, uvalue, imm);
1,520✔
978
        return;
1,520✔
979
    }
980
    auto [lb_n, ub_n] = uinterval.pair<uint64_t>();
4,080✔
981
    const uint64_t uint_max = finite_width == 64 ? uint64_t{std::numeric_limits<uint64_t>::max()}
4,724✔
982
                                                 : uint64_t{std::numeric_limits<uint32_t>::max()};
644✔
983
    if (lb_n >> (finite_width - imm) != ub_n >> (finite_width - imm)) {
4,080✔
984
        // The bits that will be shifted out to the left are different,
985
        // which means all combinations of remaining bits are possible.
986
        lb_n = 0;
580✔
987
        ub_n = uint_max << imm & uint_max;
580✔
988
    } else {
989
        // The bits that will be shifted out to the left are identical
990
        // for all values in the interval, so we can safely shift left
991
        // to get a new interval.
992
        lb_n = lb_n << imm & uint_max;
3,500✔
993
        ub_n = ub_n << imm & uint_max;
3,500✔
994
    }
995
    set(uvalue, Interval{lb_n, ub_n});
4,080✔
996
    assign(svalue, uvalue);
4,080✔
997
    overflow_bounds(svalue, uvalue, finite_width);
4,080✔
998
}
5,600✔
999

1000
void FiniteDomain::lshr(const Variable svalue, const Variable uvalue, int imm, int finite_width) {
2,604✔
1001
    const auto uinterval = eval_interval(uvalue);
2,604✔
1002
    if (uinterval.finite_size()) {
2,604✔
1003
        auto [lb_n, ub_n] = uinterval.pair_number();
2,000✔
1004
        if (finite_width == 64) {
2,000✔
1005
            lb_n = lb_n.cast_to<uint64_t>() >> imm;
1,260✔
1006
            ub_n = ub_n.cast_to<uint64_t>() >> imm;
1,260✔
1007
        } else {
1008
            const Number lb_w = lb_n.cast_to_sint(finite_width);
740✔
1009
            const Number ub_w = ub_n.cast_to_sint(finite_width);
740✔
1010
            lb_n = lb_w.cast_to<uint32_t>() >> imm;
740✔
1011
            ub_n = ub_w.cast_to<uint32_t>() >> imm;
740✔
1012

1013
            // The interval must be valid since a signed range crossing 0
1014
            // was earlier converted to a full unsigned range.
1015
            assert(lb_n <= ub_n);
740✔
1016
        }
740✔
1017
        set(uvalue, Interval{lb_n, ub_n});
6,000✔
1018
    } else {
2,000✔
1019
        set(uvalue, Interval{0, std::numeric_limits<uint64_t>::max() >> imm});
1,208✔
1020
    }
1021
    assign(svalue, uvalue);
2,604✔
1022
    overflow_bounds(svalue, uvalue, finite_width);
2,604✔
1023
}
2,604✔
1024

1025
void FiniteDomain::ashr(const Variable svalue, const Variable uvalue, const LinearExpression& right_svalue,
762✔
1026
                        int finite_width) {
1027
    Interval left_interval = Interval::bottom();
762✔
1028
    Interval right_interval = Interval::bottom();
762✔
1029
    Interval left_interval_positive = Interval::bottom();
762✔
1030
    Interval left_interval_negative = Interval::bottom();
762✔
1031
    get_signed_intervals(finite_width == 64, svalue, uvalue, right_svalue, left_interval, right_interval,
762✔
1032
                         left_interval_positive, left_interval_negative);
1033
    if (auto sn = right_interval.singleton()) {
762✔
1034
        // The BPF ISA requires masking the imm.
1035
        const int64_t imm = sn->cast_to<int64_t>() & (finite_width - 1);
760✔
1036

1037
        int64_t lb_n = std::numeric_limits<int64_t>::min() >> imm;
760✔
1038
        int64_t ub_n = std::numeric_limits<int64_t>::max() >> imm;
760✔
1039
        if (left_interval.finite_size()) {
760✔
1040
            const auto [lb, ub] = left_interval.pair_number();
760✔
1041
            if (finite_width == 64) {
760✔
1042
                lb_n = lb.cast_to<int64_t>() >> imm;
616✔
1043
                ub_n = ub.cast_to<int64_t>() >> imm;
616✔
1044
            } else {
1045
                const Number lb_w = lb.cast_to_sint(finite_width) >> gsl::narrow<int>(imm);
216✔
1046
                const Number ub_w = ub.cast_to_sint(finite_width) >> gsl::narrow<int>(imm);
216✔
1047
                if (lb_w.cast_to<uint32_t>() <= ub_w.cast_to<uint32_t>()) {
144✔
1048
                    lb_n = lb_w.cast_to<uint32_t>();
28✔
1049
                    ub_n = ub_w.cast_to<uint32_t>();
28✔
1050
                }
1051
            }
144✔
1052
        }
760✔
1053
        set(svalue, Interval{lb_n, ub_n});
760✔
1054
        assign(uvalue, svalue);
760✔
1055
        overflow_bounds(svalue, uvalue, finite_width);
760✔
1056
    } else {
1057
        havoc(svalue);
2✔
1058
        havoc(uvalue);
2✔
1059
    }
762✔
1060
}
1,905✔
1061

1062
void FiniteDomain::sign_extend(const Variable svalue, const Variable uvalue, const LinearExpression& right_svalue,
2,186✔
1063
                               const int target_width, const int source_width) {
1064
    const Interval right_interval = eval_interval(right_svalue);
2,186✔
1065
    const Interval extended = right_interval.sign_extend(source_width);
2,186✔
1066
    if (extended.is_bottom()) {
2,186✔
1067
        CRAB_ERROR("Sign extension failed ", right_interval, source_width, " becomes bottom");
×
1068
    }
1069
    set(svalue, extended);
2,186✔
1070
    assign(uvalue, svalue);
2,186✔
1071
    overflow_bounds(svalue, uvalue, target_width);
2,186✔
1072
}
3,279✔
1073

1074
} // namespace prevail
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc