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

Alan-Jowett / ebpf-verifier / 22160684311

18 Feb 2026 09:20PM UTC coverage: 88.256% (+1.1%) from 87.142%
22160684311

push

github

elazarg
lint

Signed-off-by: Elazar Gershuni <elazarg@gmail.com>

78 of 82 new or added lines in 6 files covered. (95.12%)

402 existing lines in 18 files now uncovered.

10731 of 12159 relevant lines covered (88.26%)

837642.51 hits per line

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

95.57
/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/zone_domain.hpp"
12
#include "ir/syntax.hpp"
13
#include "string_constraints.hpp"
14

15
namespace prevail {
16

17
using NumAbsDomain = ZoneDomain;
18

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

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

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

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

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

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

53
std::vector<LinearConstraint> FiniteDomain::assume_signed_64bit_eq(const Variable left_svalue,
8,516✔
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,258✔
59
    if (right_interval <= Interval::nonnegative(64) && !right_interval.is_singleton()) {
8,516✔
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,806✔
63
    }
64
}
17,032✔
65

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

71
    if (const auto rn = right_interval.singleton()) {
3,546✔
72
        const auto left_svalue_interval = eval_interval(left_svalue);
3,334✔
73
        if (auto size = left_svalue_interval.finite_size()) {
3,334✔
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>();
3,819✔
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,546✔
82
            if (lb_match < lb) {
2,546✔
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,407✔
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>();
3,819✔
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,546✔
96
            if (ub_match > ub) {
2,546✔
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,340✔
100
            }
101

102
            if (to_unsigned(lb_match) <= to_unsigned(ub_match)) {
2,546✔
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,164✔
106
                        left_uvalue <= to_unsigned(ub_match)};
12,804✔
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
        }
112
    }
113
    return {};
1,000✔
114
}
7,529✔
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,
16,156✔
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;
8,078✔
124

125
    // Get intervals as 32-bit or 64-bit as appropriate.
126
    left_interval = eval_interval(left_svalue);
16,156✔
127
    right_interval = eval_interval(right_svalue);
16,156✔
128
    if (!is64) {
16,156✔
129
        if ((left_interval <= Interval::nonnegative(32) && right_interval <= Interval::nonnegative(32)) ||
7,806✔
130
            (left_interval <= Interval::negative(32) && right_interval <= Interval::negative(32))) {
6,721✔
131
            is64 = true;
9,209✔
132
            // fallthrough as 64bit, including deduction of relational information
133
        } else {
134
            left_interval = left_interval.truncate_to<int32_t>();
2,934✔
135
            right_interval = right_interval.truncate_to<int32_t>();
2,934✔
136
            // continue as 32bit
137
        }
138
    }
139

140
    if (!left_interval.is_top()) {
16,156✔
141
        left_interval_positive = left_interval & Interval::nonnegative(64);
14,094✔
142
        left_interval_negative = left_interval & Interval::negative(64);
14,094✔
143
    } else {
144
        left_interval = eval_interval(left_uvalue);
2,062✔
145
        if (!left_interval.is_top()) {
2,062✔
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);
180✔
149
            const Number lih_ub =
90✔
150
                left_interval.ub().number() ? left_interval.ub().number()->truncate_to<int64_t>() : -1;
302✔
151
            left_interval_negative = Interval{std::numeric_limits<int64_t>::min(), lih_ub};
180✔
152
        } else {
153
            left_interval_positive = Interval::nonnegative(64);
1,882✔
154
            left_interval_negative = Interval::negative(64);
1,882✔
155
        }
156
    }
157

158
    left_interval = left_interval.truncate_to<int64_t>();
16,156✔
159
    right_interval = right_interval.truncate_to<int64_t>();
16,156✔
160
}
16,156✔
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,770✔
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,885✔
170

171
    // Get intervals as 32-bit or 64-bit as appropriate.
172
    left_interval = eval_interval(left_uvalue);
15,770✔
173
    right_interval = eval_interval(right_uvalue);
15,770✔
174
    if (!is64) {
15,770✔
175
        if ((left_interval <= Interval::nonnegative(32) && right_interval <= Interval::nonnegative(32)) ||
6,789✔
176
            (left_interval <= Interval::unsigned_high(32) && right_interval <= Interval::unsigned_high(32))) {
5,502✔
177
            is64 = true;
9,162✔
178
            // fallthrough as 64bit, including deduction of relational information
179
        } else {
180
            left_interval = left_interval.truncate_to<uint32_t>();
1,964✔
181
            right_interval = right_interval.truncate_to<uint32_t>();
1,964✔
182
            // continue as 32bit
183
        }
184
    }
185

186
    if (!left_interval.is_top()) {
15,770✔
187
        left_interval_low = left_interval & Interval::nonnegative(64);
13,518✔
188
        left_interval_high = left_interval & Interval::unsigned_high(64);
13,518✔
189
    } else {
190
        left_interval = eval_interval(left_svalue);
2,252✔
191
        if (!left_interval.is_top()) {
2,252✔
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>();
180✔
195
            left_interval_high = Interval(left_interval.lb(), -1).truncate_to<uint64_t>();
180✔
196
        } else {
197
            left_interval_low = Interval::nonnegative(64);
2,072✔
198
            left_interval_high = Interval::unsigned_high(64);
2,072✔
199
        }
200
    }
201

202
    left_interval = left_interval.truncate_to<uint64_t>();
15,770✔
203
    right_interval = right_interval.truncate_to<uint64_t>();
15,770✔
204
}
15,770✔
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)) {
868✔
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) &&
775✔
219
               right_interval <= Interval::nonnegative(64)) {
455✔
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
}
2,398✔
228

229
std::vector<LinearConstraint>
230
FiniteDomain::assume_signed_32bit_lt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
674✔
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;
337✔
236

237
    if (right_interval <= Interval::negative(32)) {
674✔
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) &&
584✔
244
               right_interval <= Interval::nonnegative(32)) {
304✔
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();
16✔
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) &&
657✔
255
               eval_interval(right_svalue) <= Interval::signed_int(32)) {
474✔
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 {};
366✔
261
    }
262
}
965✔
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)) {
868✔
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();
728✔
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) &&
141✔
283
               right_interval <= Interval::negative(64)) {
72✔
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
}
4,356✔
294

295
std::vector<LinearConstraint>
296
FiniteDomain::assume_signed_32bit_gt(const bool strict, const Variable left_svalue, const Variable left_uvalue,
676✔
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;
338✔
302

303
    if (right_interval <= Interval::nonnegative(32)) {
676✔
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();
576✔
306
        return {left_svalue >= 0,
288✔
307
                strict ? left_svalue > right_svalue : left_svalue >= right_svalue,
576✔
308
                left_svalue <= left_uvalue,
309
                left_svalue >= left_uvalue,
310
                left_uvalue >= 0,
311
                strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue,
576✔
312
                left_uvalue <= *lpub.number()};
5,472✔
313
    } else if ((left_interval_negative | left_interval_positive) <= Interval::negative(32) &&
101✔
314
               right_interval <= Interval::negative(32)) {
52✔
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,
2✔
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) &&
106✔
321
               eval_interval(right_svalue) <= Interval::signed_int(32)) {
65✔
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
}
3,216✔
329

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

336
    using namespace dsl_syntax;
7,692✔
337

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

345
    if (op == Condition::Op::EQ) {
15,384✔
346
        // Handle svalue == right.
347
        if (is64) {
12,062✔
348
            return assume_signed_64bit_eq(left_svalue, left_uvalue, right_interval, right_svalue, right_uvalue);
8,516✔
349
        } else {
350
            return assume_signed_32bit_eq(left_svalue, left_uvalue, right_interval);
3,546✔
351
        }
352
    }
353

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

357
    const auto llb = left_interval.lb();
3,322✔
358
    const auto lub = left_interval.ub();
3,322✔
359
    const auto rlb = right_interval.lb();
3,322✔
360
    const auto rub = right_interval.ub();
3,322✔
361
    if (!is_lt && (strict ? lub <= rlb : lub < rlb)) {
3,578✔
362
        // Left signed interval is lower than right signed interval.
363
        return {LinearConstraint::false_const()};
170✔
364
    } else if (is_lt && (strict ? llb >= rub : llb > rub)) {
3,838✔
365
        // Left signed interval is higher than right signed interval.
366
        return {LinearConstraint::false_const()};
115✔
367
    }
368
    if (is_lt && (strict ? lub < rlb : lub <= rlb)) {
3,449✔
369
        // Left signed interval is lower than right signed interval.
370
        return {LinearConstraint::true_const()};
190✔
371
    } else if (!is_lt && (strict ? llb > rub : llb >= rub)) {
3,692✔
372
        // Left signed interval is higher than right signed interval.
373
        return {LinearConstraint::true_const()};
115✔
374
    }
375

376
    if (is64) {
3,086✔
377
        if (is_lt) {
1,736✔
378
            return assume_signed_64bit_lt(strict, left_svalue, left_uvalue, left_interval_positive,
434✔
379
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
868✔
380
        } else {
381
            return assume_signed_64bit_gt(strict, left_svalue, left_uvalue, left_interval_positive,
434✔
382
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
868✔
383
        }
384
    } else {
385
        // 32-bit compare.
386
        if (is_lt) {
1,350✔
387
            return assume_signed_32bit_lt(strict, left_svalue, left_uvalue, left_interval_positive,
337✔
388
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
674✔
389
        } else {
390
            return assume_signed_32bit_gt(strict, left_svalue, left_uvalue, left_interval_positive,
338✔
391
                                          left_interval_negative, right_svalue, right_uvalue, right_interval);
676✔
392
        }
393
    }
394
    return {};
395
}
236✔
396

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

403
    using namespace dsl_syntax;
695✔
404

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

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

452
    using namespace dsl_syntax;
245✔
453

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

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

480
    using namespace dsl_syntax;
3,216✔
481

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

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

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

513
    using namespace dsl_syntax;
933✔
514

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

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

535
    using namespace dsl_syntax;
7,885✔
536

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

544
    // Handle uvalue != right.
545
    if (op == Condition::Op::NE) {
15,770✔
546
        if (const auto rn = right_interval.singleton()) {
12,102✔
547
            if (rn == left_interval.zero_extend(is64 ? 64 : 32).lb().number()) {
21,786✔
548
                // "NE lower bound" is equivalent to "GT lower bound".
549
                op = Condition::Op::GT;
7,320✔
550
                right_interval = Interval{left_interval.lb()};
7,320✔
551
            } else if (rn == left_interval.ub().number()) {
8,965✔
552
                // "NE upper bound" is equivalent to "LT upper bound".
553
                op = Condition::Op::LT;
350✔
554
                right_interval = Interval{left_interval.ub()};
350✔
555
            } else {
556
                return {};
4,432✔
557
            }
558
        } else {
559
            return {};
212✔
560
        }
561
    }
562

563
    const bool is_lt = op == Condition::Op::LT || op == Condition::Op::LE;
11,338✔
564
    const bool strict = op == Condition::Op::LT || op == Condition::Op::GT;
11,338✔
565

566
    auto [llb, lub] = left_interval.pair();
11,338✔
567
    auto [rlb, rub] = right_interval.pair();
11,338✔
568
    if (is_lt ? (strict ? llb >= rub : llb > rub) : (strict ? lub <= rlb : lub < rlb)) {
16,160✔
569
        // Left unsigned interval is lower than right unsigned interval.
570
        return {LinearConstraint::false_const()};
2,180✔
571
    }
572
    if (is_lt && (strict ? lub < rlb : lub <= rlb)) {
10,986✔
573
        // Left unsigned interval is lower than right unsigned interval. We still add a
574
        // relationship for use when widening, such as is used in the prime conformance test.
575
        if (is64) {
130✔
576
            return {strict ? left_uvalue < right_uvalue : left_uvalue <= right_uvalue};
270✔
577
        }
578
        return {};
40✔
579
    } else if (!is_lt && (strict ? llb > rub : llb >= rub)) {
10,626✔
580
        // Left unsigned interval is higher than right unsigned interval. We still add a
581
        // relationship for use when widening, such as is used in the prime conformance test.
582
        if (is64) {
158✔
583
            return {strict ? left_uvalue > right_uvalue : left_uvalue >= right_uvalue};
324✔
584
        } else {
585
            return {};
50✔
586
        }
587
    }
588

589
    if (is64) {
10,178✔
590
        if (is_lt) {
7,822✔
591
            return assume_unsigned_64bit_lt(strict, left_svalue, left_uvalue, left_interval_low, left_interval_high,
695✔
592
                                            right_svalue, right_uvalue, right_interval);
1,390✔
593
        } else {
594
            return assume_unsigned_64bit_gt(strict, left_svalue, left_uvalue, left_interval_low, left_interval_high,
3,216✔
595
                                            right_svalue, right_uvalue, right_interval);
6,432✔
596
        }
597
    } else {
598
        if (is_lt) {
2,356✔
599
            return assume_unsigned_32bit_lt(strict, left_svalue, left_uvalue, right_svalue, right_uvalue);
490✔
600
        } else {
601
            return assume_unsigned_32bit_gt(strict, left_svalue, left_uvalue, left_interval_low, left_interval_high,
933✔
602
                                            right_svalue, right_uvalue, right_interval);
1,866✔
603
        }
604
    }
605
}
1,367✔
606

607
/** Linear constraints for a comparison with a constant.
608
 */
609
std::vector<LinearConstraint> FiniteDomain::assume_cst_imm(const Condition::Op op, const bool is64,
29,048✔
610
                                                           const Variable dst_svalue, const Variable dst_uvalue,
611
                                                           const int64_t imm) const {
612
    using namespace dsl_syntax;
14,524✔
613
    using Op = Condition::Op;
14,524✔
614
    switch (op) {
29,048✔
615
    case Op::EQ:
15,060✔
616
    case Op::SGE:
7,530✔
617
    case Op::SLE:
7,530✔
618
    case Op::SGT:
7,530✔
619
    case Op::SLT:
7,530✔
620
        return assume_signed_cst_interval(op, is64, dst_svalue, dst_uvalue, imm, gsl::narrow_cast<uint64_t>(imm));
22,590✔
621
    case Op::SET:
36✔
622
    case Op::NSET: return assume_bit_cst_interval(op, is64, eval_interval(dst_uvalue), Interval{imm});
36✔
623
    case Op::NE:
13,952✔
624
    case Op::GE:
6,976✔
625
    case Op::LE:
6,976✔
626
    case Op::GT:
6,976✔
627
    case Op::LT:
6,976✔
628
        return assume_unsigned_cst_interval(op, is64, dst_svalue, dst_uvalue, imm, gsl::narrow_cast<uint64_t>(imm));
20,928✔
629
    }
630
    return {};
×
631
}
632

633
/** Linear constraint for a numerical comparison between registers.
634
 */
635
std::vector<LinearConstraint> FiniteDomain::assume_cst_reg(const Condition::Op op, const bool is64,
7,212✔
636
                                                           const Variable dst_svalue, const Variable dst_uvalue,
637
                                                           const Variable src_svalue, const Variable src_uvalue) const {
638
    using namespace dsl_syntax;
3,606✔
639
    using Op = Condition::Op;
3,606✔
640
    if (is64) {
7,212✔
641
        switch (op) {
6,264✔
642
        case Op::EQ: {
1,028✔
643
            const Interval src_interval = eval_interval(src_svalue);
1,028✔
644
            if (!src_interval.is_singleton() && src_interval <= Interval::nonnegative(64)) {
1,300✔
645
                return {eq(dst_svalue, src_svalue), eq(dst_uvalue, src_uvalue), eq(dst_svalue, dst_uvalue)};
558✔
646
            } else {
647
                return {eq(dst_svalue, src_svalue), eq(dst_uvalue, src_uvalue)};
3,164✔
648
            }
649
        }
650
        case Op::NE: return {neq(dst_svalue, src_svalue)};
2,590✔
651
        case Op::SGE: return {dst_svalue >= src_svalue};
45✔
652
        case Op::SLE: return {dst_svalue <= src_svalue};
3,675✔
653
        case Op::SGT: return {dst_svalue > src_svalue};
3,670✔
654
        // Note: reverse the test as a workaround strange lookup:
655
        case Op::SLT: return {src_svalue > dst_svalue};
45✔
656
        case Op::SET:
16✔
657
        case Op::NSET: return assume_bit_cst_interval(op, is64, eval_interval(dst_uvalue), eval_interval(src_uvalue));
16✔
658
        case Op::GE:
1,210✔
659
        case Op::LE:
605✔
660
        case Op::GT:
605✔
661
        case Op::LT: return assume_unsigned_cst_interval(op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
1,815✔
662
        }
663
    } else {
664
        switch (op) {
948✔
665
        case Op::EQ:
324✔
666
        case Op::SGE:
162✔
667
        case Op::SLE:
162✔
668
        case Op::SGT:
162✔
669
        case Op::SLT: return assume_signed_cst_interval(op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
486✔
670
        case Op::SET:
16✔
671
        case Op::NSET: return assume_bit_cst_interval(op, is64, eval_interval(dst_uvalue), eval_interval(src_uvalue));
16✔
672
        case Op::NE:
608✔
673
        case Op::GE:
304✔
674
        case Op::LE:
304✔
675
        case Op::GT:
304✔
676
        case Op::LT: return assume_unsigned_cst_interval(op, is64, dst_svalue, dst_uvalue, src_svalue, src_uvalue);
912✔
677
        }
678
    }
679
    assert(false);
×
680
    throw std::exception();
681
}
8,764✔
682

683
void FiniteDomain::assign(const Variable x, const std::optional<LinearExpression>& e) { dom.assign(x, e); }
29,948✔
684
void FiniteDomain::assign(const Variable x, const Variable e) { dom.assign(x, e); }
112,573✔
685
void FiniteDomain::assign(const Variable x, const LinearExpression& e) { dom.assign(x, e); }
74,770✔
686
void FiniteDomain::assign(const Variable x, const int64_t e) { dom.set(x, Interval(e)); }
93,628✔
687

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

700
void FiniteDomain::apply(const ArithBinOp op, const Variable x, const Variable y, const Variable z,
3,516✔
701
                         const int finite_width) {
702
    switch (op) {
3,516✔
703
    case ArithBinOp::ADD: assign(x, LinearExpression(y).plus(z)); break;
4,035✔
704
    case ArithBinOp::SUB: assign(x, LinearExpression(y).subtract(z)); break;
1,125✔
705
    case ArithBinOp::MUL: set(x, eval_interval(y, finite_width) * eval_interval(z, finite_width)); break;
114✔
706
    default: CRAB_ERROR("DBM: unreachable");
×
707
    }
708
}
3,516✔
709

710
void FiniteDomain::apply(const ArithBinOp op, const Variable x, const Variable y, const Number& z,
22,164✔
711
                         const int finite_width) {
712
    switch (op) {
22,164✔
713
    case ArithBinOp::ADD: assign(x, LinearExpression(y).plus(z)); break;
31,353✔
714
    case ArithBinOp::SUB: assign(x, LinearExpression(y).subtract(z)); break;
1,494✔
715
    case ArithBinOp::MUL: set(x, eval_interval(y, finite_width) * Interval{z}); break;
399✔
716
    default: CRAB_ERROR("DBM: unreachable");
×
717
    }
718
}
22,164✔
719

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

732
void FiniteDomain::apply(const SignedArithBinOp op, const Variable x, const Variable y, const Number& k,
238✔
733
                         const int finite_width) {
734
    switch (op) {
238✔
735
    case SignedArithBinOp::SDIV:
12✔
736
        set(x, eval_interval(y, finite_width).sdiv(Interval{read_imm_for_sdiv_or_smod(k, finite_width)}));
12✔
737
        break;
12✔
738
    case SignedArithBinOp::UDIV:
172✔
739
        set(x, eval_interval(y, finite_width).udiv(Interval{read_imm_for_udiv_or_umod(k, finite_width)}));
172✔
740
        break;
172✔
741
    case SignedArithBinOp::SREM:
28✔
742
        set(x, eval_interval(y, finite_width).srem(Interval{read_imm_for_sdiv_or_smod(k, finite_width)}));
28✔
743
        break;
28✔
744
    case SignedArithBinOp::UREM:
26✔
745
        set(x, eval_interval(y, finite_width).urem(Interval{read_imm_for_udiv_or_umod(k, finite_width)}));
26✔
746
        break;
26✔
747
    default: CRAB_ERROR("DBM: unreachable");
×
748
    }
749
}
238✔
750

751
void FiniteDomain::apply(const SignedArithBinOp op, const Variable x, const Variable y, const Variable z,
204✔
752
                         const int finite_width) {
753
    switch (op) {
204✔
754
    case SignedArithBinOp::SDIV: set(x, eval_interval(y, finite_width).sdiv(eval_interval(z, finite_width))); break;
42✔
755
    case SignedArithBinOp::UDIV: set(x, eval_interval(y, finite_width).udiv(eval_interval(z, finite_width))); break;
144✔
756
    case SignedArithBinOp::SREM: set(x, eval_interval(y, finite_width).srem(eval_interval(z, finite_width))); break;
63✔
757
    case SignedArithBinOp::UREM: set(x, eval_interval(y, finite_width).urem(eval_interval(z, finite_width))); break;
57✔
758
    default: CRAB_ERROR("DBM: unreachable");
×
759
    }
760
}
204✔
761

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

770
    Interval xi = Interval::bottom();
7,248✔
771
    switch (op) {
7,248✔
772
    case BitwiseBinOp::AND: xi = yi.bitwise_and(zi); break;
504✔
773
    case BitwiseBinOp::OR: xi = yi.bitwise_or(zi); break;
5,760✔
774
    case BitwiseBinOp::XOR: xi = yi.bitwise_xor(zi); break;
502✔
775
    case BitwiseBinOp::SHL: xi = yi.shl(zi); break;
482✔
776
    case BitwiseBinOp::LSHR: xi = yi.lshr(zi); break;
×
777
    case BitwiseBinOp::ASHR: xi = yi.ashr(zi); break;
×
778
    default: CRAB_ERROR("DBM: unreachable");
×
779
    }
780
    xi.mask_value(finite_width);
7,248✔
781
    set(x, xi);
7,248✔
782
}
7,248✔
783

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

795
    switch (op) {
13,304✔
796
    case BitwiseBinOp::AND: xi = yi.bitwise_and(zi); break;
11,038✔
797
    case BitwiseBinOp::OR: xi = yi.bitwise_or(zi); break;
560✔
798
    case BitwiseBinOp::XOR: xi = yi.bitwise_xor(zi); break;
184✔
799
    case BitwiseBinOp::SHL: xi = yi.shl(zi); break;
1,522✔
800
    case BitwiseBinOp::LSHR: xi = yi.lshr(zi); break;
×
801
    case BitwiseBinOp::ASHR: xi = yi.ashr(zi); break;
×
802
    default: CRAB_ERROR("DBM: unreachable");
×
803
    }
804
    xi.mask_value(finite_width);
13,304✔
805
    set(x, xi);
13,304✔
806
}
13,304✔
807

808
void FiniteDomain::apply(BinOp op, Variable x, Variable y, const Number& z, int finite_width) {
35,706✔
809
    std::visit([&](auto top) { apply(top, x, y, z, finite_width); }, op);
53,559✔
810
}
17,853✔
811

812
void FiniteDomain::apply(BinOp op, Variable x, Variable y, Variable z, int finite_width) {
10,968✔
813
    std::visit([&](auto top) { apply(top, x, y, z, finite_width); }, op);
16,452✔
814
}
5,484✔
815

816
void FiniteDomain::apply(const BinOp op, const Variable x, const Variable y, const Variable z) {
606✔
817
    apply(op, x, y, z, 0);
606✔
818
}
606✔
819

820
void FiniteDomain::overflow_bounds(const Variable lhs, const int finite_width, const bool issigned) {
157,418✔
821
    using namespace dsl_syntax;
78,709✔
822
    const auto interval = eval_interval(lhs);
157,418✔
823
    if (interval.size() >= Interval::unsigned_int(finite_width).size()) {
236,127✔
824
        // Interval covers the full space.
825
        havoc(lhs);
28,772✔
826
        return;
32,602✔
827
    }
828
    if (interval.is_bottom()) {
128,646✔
829
        havoc(lhs);
×
830
        return;
×
831
    }
832
    const Number lb_value = interval.lb().number().value();
192,969✔
833
    const Number ub_value = interval.ub().number().value();
192,969✔
834

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

857
void FiniteDomain::overflow_bounds(const Variable svalue, const Variable uvalue, const int finite_width) {
45,626✔
858
    overflow_bounds(svalue, finite_width, true);
45,626✔
859
    overflow_bounds(uvalue, finite_width, false);
45,626✔
860
}
45,626✔
861

862
void FiniteDomain::apply_signed(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
22,204✔
863
                                const Number& z, const int finite_width) {
864
    apply(op, xs, y, z, finite_width);
22,204✔
865
    if (finite_width) {
22,204✔
866
        assign(xu, xs);
12,100✔
867
        overflow_bounds(xs, xu, finite_width);
12,100✔
868
    }
869
}
22,204✔
870

871
void FiniteDomain::apply_signed(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
2,980✔
872
                                const Variable z, const int finite_width) {
873
    apply(op, xs, y, z, finite_width);
2,980✔
874
    if (finite_width) {
2,980✔
875
        assign(xu, xs);
2,960✔
876
        overflow_bounds(xs, xu, finite_width);
2,960✔
877
    }
878
}
2,980✔
879

880
void FiniteDomain::apply_unsigned(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
13,502✔
881
                                  const Number& z, const int finite_width) {
882
    apply(op, xu, y, z, finite_width);
13,502✔
883
    if (finite_width) {
13,502✔
884
        assign(xs, xu);
13,502✔
885
        overflow_bounds(xs, xu, finite_width);
13,502✔
886
    }
887
}
13,502✔
888

889
void FiniteDomain::apply_unsigned(const BinOp& op, const Variable xs, const Variable xu, const Variable y,
7,382✔
890
                                  const Variable z, const int finite_width) {
891
    apply(op, xu, y, z, finite_width);
7,382✔
892
    if (finite_width) {
7,382✔
893
        assign(xs, xu);
7,382✔
894
        overflow_bounds(xs, xu, finite_width);
7,382✔
895
    }
896
}
7,382✔
897

898
void FiniteDomain::add(const Variable lhs, const Variable op2) { apply_signed(ArithBinOp::ADD, lhs, lhs, lhs, op2, 0); }
×
899
void FiniteDomain::add(const Variable lhs, const Number& op2) { apply_signed(ArithBinOp::ADD, lhs, lhs, lhs, op2, 0); }
9,108✔
900
void FiniteDomain::sub(const Variable lhs, const Variable op2) { apply_signed(ArithBinOp::SUB, lhs, lhs, lhs, op2, 0); }
4✔
901
void FiniteDomain::sub(const Variable lhs, const Number& op2) { apply_signed(ArithBinOp::SUB, lhs, lhs, lhs, op2, 0); }
996✔
902

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

918
void FiniteDomain::neg(const Variable lhss, const Variable lhsu, const int finite_width) {
160✔
919
    apply_signed(ArithBinOp::MUL, lhss, lhsu, lhss, -1, finite_width);
160✔
920
}
160✔
921
void FiniteDomain::mul(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
76✔
922
    apply_signed(ArithBinOp::MUL, lhss, lhsu, lhss, op2, finite_width);
76✔
923
}
76✔
924
void FiniteDomain::mul(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
106✔
925
    apply_signed(ArithBinOp::MUL, lhss, lhsu, lhss, op2, finite_width);
106✔
926
}
106✔
927
void FiniteDomain::sdiv(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
28✔
928
    apply_signed(SignedArithBinOp::SDIV, lhss, lhsu, lhss, op2, finite_width);
28✔
929
}
28✔
930
void FiniteDomain::sdiv(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
12✔
931
    apply_signed(SignedArithBinOp::SDIV, lhss, lhsu, lhss, op2, finite_width);
12✔
932
}
12✔
933
void FiniteDomain::udiv(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
96✔
934
    apply_unsigned(SignedArithBinOp::UDIV, lhss, lhsu, lhsu, op2, finite_width);
96✔
935
}
96✔
936
void FiniteDomain::udiv(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
172✔
937
    apply_unsigned(SignedArithBinOp::UDIV, lhss, lhsu, lhsu, op2, finite_width);
172✔
938
}
172✔
939
void FiniteDomain::srem(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
42✔
940
    apply_signed(SignedArithBinOp::SREM, lhss, lhsu, lhss, op2, finite_width);
42✔
941
}
42✔
942
void FiniteDomain::srem(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
28✔
943
    apply_signed(SignedArithBinOp::SREM, lhss, lhsu, lhss, op2, finite_width);
28✔
944
}
28✔
945
void FiniteDomain::urem(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
38✔
946
    apply_unsigned(SignedArithBinOp::UREM, lhss, lhsu, lhsu, op2, finite_width);
38✔
947
}
38✔
948
void FiniteDomain::urem(const Variable lhss, const Variable lhsu, const Number& op2, const int finite_width) {
26✔
949
    apply_unsigned(SignedArithBinOp::UREM, lhss, lhsu, lhsu, op2, finite_width);
26✔
950
}
26✔
951

952
void FiniteDomain::bitwise_and(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
504✔
953
    apply_unsigned(BitwiseBinOp::AND, lhss, lhsu, lhsu, op2, finite_width);
504✔
954
}
504✔
955
void FiniteDomain::bitwise_and(const Variable lhss, const Variable lhsu, const Number& op2) {
28,836✔
956
    // Skip when the AND is provably a no-op: the mask is (2^n - 1) and both svalue and uvalue
957
    // already fit within [0, mask] with matching intervals.
958
    // Soundness: for any v in [0, 2^n - 1], v & (2^n - 1) = v, so the operation is an identity.
959
    // The entailment guard ensures the svalue==uvalue relational constraint already exists in the
960
    // SplitDBM; without it, skipping would lose the chance to re-establish the relation via
961
    // apply_unsigned's internal assign(svalue, uvalue).
962
    // Skipping preserves relational constraints in the SplitDBM that would otherwise be
963
    // destroyed by the internal havoc().
964
    if (op2 > 0 && (op2 & (op2 + 1)) == 0) {
28,836✔
965
        using namespace dsl_syntax;
13,407✔
966
        const auto mask_interval = Interval{Number{0}, op2};
26,814✔
967
        const auto sinterval = eval_interval(lhss);
26,814✔
968
        const auto uinterval = eval_interval(lhsu);
26,814✔
969
        if (sinterval <= mask_interval && uinterval <= mask_interval && sinterval == uinterval &&
36,482✔
970
            dom.entail(eq(lhss, lhsu))) {
46,150✔
971
            return;
17,798✔
972
        }
973
    }
974
    // Use finite width 64 to make the svalue be set as well as the uvalue.
975
    apply_unsigned(BitwiseBinOp::AND, lhss, lhsu, lhsu, op2, 64);
11,038✔
976
}
977
void FiniteDomain::bitwise_or(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
5,760✔
978
    apply_unsigned(BitwiseBinOp::OR, lhss, lhsu, lhsu, op2, finite_width);
5,760✔
979
}
5,760✔
980
void FiniteDomain::bitwise_or(const Variable lhss, const Variable lhsu, const Number& op2) {
560✔
981
    apply_unsigned(BitwiseBinOp::OR, lhss, lhsu, lhsu, op2, 64);
560✔
982
}
560✔
983
void FiniteDomain::bitwise_xor(const Variable lhss, const Variable lhsu, const Variable op2, const int finite_width) {
502✔
984
    apply_unsigned(BitwiseBinOp::XOR, lhss, lhsu, lhsu, op2, finite_width);
502✔
985
}
502✔
986
void FiniteDomain::bitwise_xor(const Variable lhss, const Variable lhsu, const Number& op2) {
184✔
987
    apply_unsigned(BitwiseBinOp::XOR, lhss, lhsu, lhsu, op2, 64);
184✔
988
}
184✔
989
void FiniteDomain::shl_overflow(const Variable lhss, const Variable lhsu, const Variable op2) {
482✔
990
    apply_unsigned(BitwiseBinOp::SHL, lhss, lhsu, lhsu, op2, 64);
482✔
991
}
482✔
992
void FiniteDomain::shl_overflow(const Variable lhss, const Variable lhsu, const Number& op2) {
1,522✔
993
    apply_unsigned(BitwiseBinOp::SHL, lhss, lhsu, lhsu, op2, 64);
1,522✔
994
}
1,522✔
995

996
void FiniteDomain::shl(const Variable svalue, const Variable uvalue, const int imm, const int finite_width) {
5,612✔
997
    const auto uinterval = eval_interval(uvalue);
5,612✔
998
    if (!uinterval.finite_size()) {
5,612✔
999
        shl_overflow(svalue, uvalue, imm);
1,522✔
1000
        return;
1,522✔
1001
    }
1002
    auto [lb_n, ub_n] = uinterval.pair<uint64_t>();
4,090✔
1003
    const uint64_t uint_max = finite_width == 64 ? uint64_t{std::numeric_limits<uint64_t>::max()}
4,736✔
1004
                                                 : uint64_t{std::numeric_limits<uint32_t>::max()};
646✔
1005
    if (lb_n >> (finite_width - imm) != ub_n >> (finite_width - imm)) {
4,090✔
1006
        // The bits that will be shifted out to the left are different,
1007
        // which means all combinations of remaining bits are possible.
1008
        lb_n = 0;
580✔
1009
        ub_n = uint_max << imm & uint_max;
580✔
1010
    } else {
1011
        // The bits that will be shifted out to the left are identical
1012
        // for all values in the interval, so we can safely shift left
1013
        // to get a new interval.
1014
        lb_n = lb_n << imm & uint_max;
3,510✔
1015
        ub_n = ub_n << imm & uint_max;
3,510✔
1016
    }
1017
    set(uvalue, Interval{lb_n, ub_n});
4,090✔
1018
    assign(svalue, uvalue);
4,090✔
1019
    overflow_bounds(svalue, uvalue, finite_width);
4,090✔
1020
}
1021

1022
void FiniteDomain::lshr(const Variable svalue, const Variable uvalue, const int imm, const int finite_width) {
2,620✔
1023
    const auto uinterval = eval_interval(uvalue);
2,620✔
1024
    if (uinterval.finite_size()) {
2,620✔
1025
        auto [lb_n, ub_n] = uinterval.pair_number();
2,016✔
1026
        if (finite_width == 64) {
2,016✔
1027
            lb_n = lb_n.cast_to<uint64_t>() >> imm;
1,272✔
1028
            ub_n = ub_n.cast_to<uint64_t>() >> imm;
1,272✔
1029
        } else {
1030
            const Number lb_w = lb_n.cast_to_sint(finite_width);
744✔
1031
            const Number ub_w = ub_n.cast_to_sint(finite_width);
744✔
1032
            lb_n = lb_w.cast_to<uint32_t>() >> imm;
744✔
1033
            ub_n = ub_w.cast_to<uint32_t>() >> imm;
744✔
1034

1035
            // The interval must be valid since a signed range crossing 0
1036
            // was earlier converted to a full unsigned range.
1037
            assert(lb_n <= ub_n);
744✔
1038
        }
1039
        set(uvalue, Interval{lb_n, ub_n});
2,016✔
1040
    } else {
1041
        set(uvalue, Interval{0, std::numeric_limits<uint64_t>::max() >> imm});
604✔
1042
    }
1043
    assign(svalue, uvalue);
2,620✔
1044
    overflow_bounds(svalue, uvalue, finite_width);
2,620✔
1045
}
2,620✔
1046

1047
void FiniteDomain::ashr(const Variable svalue, const Variable uvalue, const LinearExpression& right_svalue,
772✔
1048
                        const int finite_width) {
1049
    Interval left_interval = Interval::bottom();
772✔
1050
    Interval right_interval = Interval::bottom();
772✔
1051
    Interval left_interval_positive = Interval::bottom();
772✔
1052
    Interval left_interval_negative = Interval::bottom();
772✔
1053
    get_signed_intervals(finite_width == 64, svalue, uvalue, right_svalue, left_interval, right_interval,
772✔
1054
                         left_interval_positive, left_interval_negative);
1055
    if (const auto sn = right_interval.singleton()) {
772✔
1056
        // The BPF ISA requires masking the imm.
1057
        const int64_t imm = sn->cast_to<int64_t>() & (finite_width - 1);
770✔
1058

1059
        int64_t lb_n = std::numeric_limits<int64_t>::min() >> imm;
770✔
1060
        int64_t ub_n = std::numeric_limits<int64_t>::max() >> imm;
770✔
1061
        if (left_interval.finite_size()) {
770✔
1062
            const auto [lb, ub] = left_interval.pair_number();
770✔
1063
            if (finite_width == 64) {
770✔
1064
                lb_n = lb.cast_to<int64_t>() >> imm;
622✔
1065
                ub_n = ub.cast_to<int64_t>() >> imm;
622✔
1066
            } else {
1067
                const Number lb_w = lb.cast_to_sint(finite_width) >> gsl::narrow<int>(imm);
148✔
1068
                const Number ub_w = ub.cast_to_sint(finite_width) >> gsl::narrow<int>(imm);
148✔
1069
                if (lb_w.cast_to<uint32_t>() <= ub_w.cast_to<uint32_t>()) {
148✔
1070
                    lb_n = lb_w.cast_to<uint32_t>();
32✔
1071
                    ub_n = ub_w.cast_to<uint32_t>();
32✔
1072
                }
1073
            }
1074
        }
1075
        set(svalue, Interval{lb_n, ub_n});
770✔
1076
        assign(uvalue, svalue);
770✔
1077
        overflow_bounds(svalue, uvalue, finite_width);
770✔
1078
    } else {
1079
        havoc(svalue);
2✔
1080
        havoc(uvalue);
2✔
1081
    }
1082
}
772✔
1083

1084
void FiniteDomain::sign_extend(const Variable svalue, const Variable uvalue, const LinearExpression& right_svalue,
2,202✔
1085
                               const int target_width, const int source_width) {
1086
    const Interval right_interval = eval_interval(right_svalue);
2,202✔
1087
    const Interval extended = right_interval.sign_extend(source_width);
2,202✔
1088
    if (extended.is_bottom()) {
2,202✔
1089
        CRAB_ERROR("Sign extension failed ", right_interval, source_width, " becomes bottom");
×
1090
    }
1091
    set(svalue, extended);
2,202✔
1092
    assign(uvalue, svalue);
2,202✔
1093
    overflow_bounds(svalue, uvalue, target_width);
2,202✔
1094
}
2,202✔
1095

1096
} // 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