|
ESBMC
Efficient SMT-based Context-Bounded Model Checker
|
Files | |
| expr_kinds.inc | |
| guard_seq.h | |
| irep2.h | |
| Classes and definitions for non-stringy internal representation. | |
| irep2_content_hash.cpp | |
| irep2_content_hash.h | |
| irep2_crc.cpp | |
| irep2_dispatch.h | |
| irep2_expr.cpp | |
| irep2_expr.h | |
| irep2_guard.cpp | |
| irep2_guard.h | |
| irep2_type.cpp | |
| irep2_type.h | |
| irep2_utils.cpp | |
| irep2_utils.h | |
| simplification_check.cpp | |
| simplification_check.h | |
| type_kinds.inc | |
irep2 is ESBMC's typed, intrusively reference-counted, copy-on-write internal representation for expressions (expr2t) and types (type2t). It is the data structure every frontend lowers to, every transformation rewrites, and every backend (symex, SMT, goto2c) consumes. It replaces the older "stringy" irept for the verification pipeline; conversions live in util/irep/migrate.{h,cpp}.
Generated boilerplate. The full list of IR nodes lives in two per-family manifest files:
Each entry is IREP2_EXPR(kind, "pretty_name") / IREP2_TYPE(kind, "pretty_name"). Consumers (the expr_ids / type_ids enums, the per-kind forward declarations, the is_* / to_* / try_to_* predicate generators, the pretty-name tables) #include the matching .inc with a redefining macro.
| File | Contents |
|---|---|
| irep2.h | Base classes irep2t, type2t, expr2t; the irep_container smart pointer (alias expr2tc / type2tc); the make_irep factory; function_ref; hash_combine; checked-cast helpers. Declares the cmp/lt/clone/etc. virtual entry points; the switch-on-id dispatchers themselves live in irep2_expr.cpp / irep2_type.cpp (and irep2_crc.cpp for crc). |
| expr_kinds.inc / type_kinds.inc | Manifest of node kinds in declaration order. Single source of truth. |
| irep2_type.h / irep2_type.cpp | Concrete type classes (bool_type2t, signedbv_type2t, array_type2t, pointer_type2t, struct_type2t, ...). Each kind inherits directly from type2t and owns its fields. Free family helpers in the same header (struct_union_members, array_or_vector_subtype, ...) provide uniform field access when the caller doesn't care which specific kind it is. |
| irep2_expr.h / irep2_expr.cpp | Concrete expression classes (constant_int2t, symbol2t, add2t, if2t, code_assign2t, ...). Each kind inherits directly from expr2t. |
| irep2_utils.h | Inline predicates and helpers (is_bv_type, is_number_type, is_scalar_type, simplification helpers). |
| irep2_dispatch.h | Generic generic_*<K> helpers that walk a kind's K::fields tuple via std::apply to implement cmp/lt/tostring/get_sub_expr/foreach_operand uniformly, plus the per-field-type overloads they invoke. Switch dispatchers on expr2t/type2t route to these. (crc and clone walk the same K::fields tuple from irep2_crc.cpp / irep2_expr.cpp / irep2_type.cpp.) |
| irep2_crc.cpp | Iterative postorder CRC traversal and per-field CRC operations. Kept in one translation unit with internal linkage so the compiler sees and optimises the complete CRC path. |
| irep2_utils.cpp | Definitions for the predicates and dispatch-catalogue overloads declared in irep2_utils.h and irep2_dispatch.h. |
| irep2_guard.{h,cpp} / guard_seq.h | Path-condition guard container guard2tc — an expr2tc carrying a flat conjunct list — and guard_seq, the immer::vector-backed, oldest-first immutable conjunct sequence it stores (O(1) copy for the deep guard chains symex builds). |
| CMakeLists.txt | Builds the irep2 static library; links fmt, bigint, and immer, and adds Boost's include path (irep2 headers reach boost/program_options.hpp transitively through config.h). |
Each concrete kind inherits directly from expr2t (or type2t, with a small set of shared *_data intermediates on the type side) and declares its fields plus two static class members:
The generic helpers in irep2_dispatch.h walk K::fields via std::apply to implement cmp, lt, tostring, get_sub_expr, and foreach_operand once for every kind; clone (irep2_expr.cpp / irep2_type.cpp) and do_crc (irep2_crc.cpp) walk the same tuple. The switch-on-id dispatchers on expr2t / type2t (driven by the expr_kinds.inc / type_kinds.inc X-macro manifests) route each call to the matching generic_*<K>.
Net effect: adding a new node is a single-line manifest entry plus a concrete class with fields / field_names. Comparison, hashing, pretty-printing, and operand iteration are generated by the dispatchers for free.
The fields tuple is canonical for the kind's primary constructor argument order, after the type slot (so K(type, f1, f2, ...) matches K::fields = make_tuple(&K::f1, &K::f2, ...) — or make_tuple(&expr2t::type, &K::f1, &K::f2, ...) for kinds where the type is itself part of the structural identity). get_sub_expr(i) indexes into this tuple after skipping non-expr2tc fields, so the tuple order is observable to callers.
Notes:
This section gives the shape of the in-memory term, not a parseable language — irep2 has no surface syntax, parser, or evaluator. The node hierarchy is an inductive data type, and the productions below describe its abstract syntax: the structure walked by get_sub_expr, the comparison/crc dispatchers, and clone.
The two manifests (expr_kinds.inc / type_kinds.inc) plus each kind's fields tuple define that grammar. The productions below are the structure; the Reference tables that follow enumerate every terminal in prose with its semantics.
Notation. ::= is a production, | alternation, ( … ) grouping, { x } zero-or-more, [ x ] optional, "lit" a terminal keyword (the identifier as it appears in the .inc manifest). Lower-case words are non-terminals; leaf terminals (nat, name, int-lit, …) are defined at the end. ; … is a comment. Every expression carries a result type, written «T» after the keyword: «type» when the type is a free operand, or a concrete type when the kind fixes it («bool» for predicates, «empty» for statements, «pointer» for address_of). Operand lists appear in fields-tuple order — the order get_sub_expr and the dispatchers walk.
code stores argument_names parallel to its argument types and an ellipsis flag (the ...). struct/union additionally carry member_pretty_names parallel to member (elided above). An array whose size_is_infinite flag is set has size infinite and a nil array_size; a non-constant array_size is a dynamically-sized array.
Constants:
Symbols and casts:
Control, comparison and logical:
Bitwise and arithmetic:
IEEE-754 floating point:
Bit-level utilities and overflow checks:
Pointers and the memory model:
Aggregate access and update:
Quantifiers, Python predicates and CHERI:
Side effects:
GOTO statements (code_*):
Leaf terminals:
Notes:
Each example shows the source, the C++ construction with the generated *2tc factories, and the resulting tree in the grammar notation above. Assume type2tc i32 = signedbv_type2tc(32); throughout.
Leaves — a variable and a literal.
Arithmetic — x + 2. add takes its result type plus two operands:
Comparison + ternary — y > 2 ? y : 0. Relations have boolean result and take only their two operands (no explicit type):
Struct member — p.y for struct point { int x; int y; }:
Array and pointers — a[i], &x, *p. Note address_of2tc takes the pointee type as its first argument; the node's own type is the pointer to it:
Statements — a function body. code_* nodes nest inside a code_block; their type is empty:
Seeing the IR for real code. esbmc file.c --goto-functions-only dumps the GOTO program, where the same code_* / expression nodes print in a compact concrete syntax. For
the listing (comments and p's nondet-init trimmed) is:
Reading it back to the grammar: ASSIGN y=x + 2; is code_assign(symbol "y", add(symbol "x", constant_int 2)); p.y is member(symbol "p", "y"); a[(signed long int)i] is index(symbol "a", typecast(symbol "i")); and the IF … GOTO is the conditional branch carried on the GOTO instruction itself — not a code_goto expression (see the grammar note above).
Listed in type_kinds.inc (one IREP2_TYPE row per kind); declared in irep2_type.h.
| Kind | Description |
|---|---|
| bool | Boolean type. No payload. |
| empty | "Void" — used for void pointers, void function returns, statements. |
| symbol | Placeholder/symbolic type used while linking, or for recursive references inside structs/arrays. |
| struct | C struct and C++ class data. Carries member types, names, pretty names, struct name, and packed flag. |
| union | C union. Same field shape as struct (members, names, packed, ...). |
| code | Function type: argument types, argument names, return type, ellipsis flag. |
| array | Fixed, infinite, or dynamically-sized array. Holds subtype, array_size, and size_is_infinite. |
| vector | SIMD-style fixed-size vector. Same backing data as array but distinct semantics. |
| pointer | Pointer to subtype; optional CHERI capability flag. |
| unsignedbv | Unsigned bitvector of given width. |
| signedbv | Signed bitvector of given width. |
| fixedbv | Fixed-point bitvector — total width split into integer and fraction bits. |
| floatbv | IEEE-754 floating-point — fraction-bit and exponent-bit counts. |
| complex | C _Complex — stores only its element subtype; the SMT tuple lowering synthesises the (real, imag) 2-tuple view at the boundary via struct_union_members / struct_union_member_names. |
| cpp_name | C++ qualified name with template arguments; used transiently by the C++ frontend. |
Listed in expr_kinds.inc (one IREP2_EXPR row per kind); declared in irep2_expr.h. Every expression has a type and an expr_id. Statement-shaped nodes (the code_* family) carry empty type — they appear inside GOTO programs rather than inside an expression tree.
| Kind | Description |
|---|---|
| constant_int | Arbitrary-precision integer (BigInt), clipped to the bitvector type's width on simplification. |
| constant_fixedbv | Fixed-point literal (fixedbvt). |
| constant_floatbv | IEEE-754 floating-point literal (ieee_floatt). |
| constant_bool | Boolean literal (true / false). |
| constant_string | String literal (stored as an irep_idt). |
| constant_struct | Struct literal — one operand per member, in declaration order. |
| constant_union | Union literal — single active member and value. |
| constant_array | Array literal — one operand per element. |
| constant_vector | Vector literal — same shape as constant_array. |
| constant_array_of | Array filled uniformly with one value (cheap, scalable). |
| Kind | Description |
|---|---|
| symbol | Reference to a named program variable (by irep_idt); type-checked against the symbol table. |
| typecast | Value-preserving conversion (e.g. int↔float, widening, narrowing). |
| bitcast | Reinterpret bits of one type as another type of equal width (no value conversion). |
| nearbyint | Round float to nearest integer per a rounding mode. |
| Kind | Description |
|---|---|
| if | If-then-else expression: cond ? a : b. |
| equality | a == b. Boolean result. |
| notequal | a != b. Boolean result. |
| lessthan | a < b. Boolean result. |
| greaterthan | a > b. Boolean result. |
| lessthanequal | a <= b. Boolean result. |
| greaterthanequal | a >= b. Boolean result. |
| cmp_three_way | C++20 a <=> b; result is the appropriate comparison-category struct. |
| Kind | Description |
|---|---|
| not | Logical negation !a. |
| and | Logical and a && b. |
| or | Logical or a \|\| b. |
| xor | Logical xor. |
| implies | Logical implication a -> b. |
| Kind | Description |
|---|---|
| bitand | Bitwise AND. |
| bitor | Bitwise OR. |
| bitxor | Bitwise XOR. |
| bitnot | Bitwise NOT. |
| shl | Logical shift left. |
| lshr | Logical shift right (zero-fill). |
| ashr | Arithmetic shift right (sign-fill). |
| Kind | Description |
|---|---|
| neg | Arithmetic negation. |
| abs | Absolute value. |
| add | Addition. |
| sub | Subtraction. |
| mul | Multiplication. |
| div | Division (semantics follow operand type). |
| modulus | Remainder / modulus. |
Distinct from the integer ops because they carry a rounding mode and obey IEEE semantics (NaN, infinities, signed zero).
| Kind | Description |
|---|---|
| ieee_add | IEEE addition with rounding mode. |
| ieee_sub | IEEE subtraction. |
| ieee_mul | IEEE multiplication. |
| ieee_div | IEEE division. |
| ieee_fma | IEEE fused multiply-add (x*y)+z rounded once. |
| ieee_sqrt | IEEE square root. |
| isnan | True iff operand is NaN. |
| isinf | True iff operand is ±infinity. |
| isnormal | True iff operand is a normal float. |
| isfinite | True iff operand is finite (not NaN or infinity). |
| signbit | Sign bit of the operand (int32 result). |
| Kind | Description |
|---|---|
| popcount | Population count (number of set bits). |
| bswap | Byte-swap (endianness reversal). |
| concat | Concatenate two unsigned bitvectors into a wider one. |
| extract | Slice bits [upper:lower] out of a bitvector. |
| Kind | Description |
|---|---|
| overflow | True iff the wrapped add/sub/mul overflows its operand width. |
| overflow_cast | True iff casting the operand to a narrower bitvector overflows. |
| overflow_neg | True iff negating the operand overflows (e.g. INT_MIN). |
| Kind | Description |
|---|---|
| address_of | &expr — produces a pointer to its operand. |
| dereference | *ptr — expanded by symex into an if-then-else over candidate objects. |
| same_object | True iff two pointers refer to the same memory object. |
| pointer_offset | Byte offset component of a pointer. |
| pointer_object | Object-identifier component of a pointer. |
| pointer_capability | CHERI capability metadata of a pointer. |
| valid_object | True iff a pointer's target object is live (not freed, in scope). |
| invalid_pointer | True iff a pointer is structurally invalid. |
| null_object | Sentinel used by pointer analysis to represent the null target. |
| dynamic_object | Sentinel for a heap-allocated object (used by pointer analysis). |
| dynamic_size | Allocated size of a dynamic object (looked up from a model array at symex). |
| deallocated_obj | True iff a pointer's target has been freed (use-after-free probe). |
| races_check | Data-race check predicate used by the concurrent model checker. |
| object_descriptor | (Root object, offset, alignment) triple used during pointer reasoning. |
| unknown | Pointer-analysis sentinel: target is unknown. |
| invalid | Pointer-analysis sentinel: pointer is invalid. |
| Kind | Description |
|---|---|
| index | Element access a[i] on an array or vector. |
| member | Field access s.field on a struct/union. |
| member_ref | C++ reference-style member access. |
| ptr_mem | C++ pointer-to-member access obj.*pm. |
| with | Functional update — produce a new aggregate with one element/field replaced. |
| byte_extract | Extract a byte from a value at a runtime offset (used in byte-level memory model). |
| byte_update | Update a byte at a runtime offset, producing a new value. |
| Kind | Description |
|---|---|
| forall | Universal quantification ∀ sym. predicate. |
| exists | Existential quantification ∃ sym. predicate. |
Emitted by the Python frontend; lowered later in the pipeline.
| Kind | Description |
|---|---|
| isinstance | Python isinstance(value, type). |
| hasattr | Python hasattr(obj, attr). |
| isnone | Python value is None (and equivalents). |
| Kind | Description |
|---|---|
| capability_base | Base address of a CHERI capability. |
| capability_top | Top address (one past the last accessible byte) of a CHERI capability. |
| Kind | Description |
|---|---|
| sideeffect | Catch-all for effectful operations during frontend lowering: malloc/alloca, nondet allocation, embedded function calls. Carries an allockind discriminator. Most are flattened before GOTO. |
These appear as instructions inside a GOTO program rather than inside an ordinary expression tree. Their type is empty unless noted.
| Kind | Description |
|---|---|
| code_block | Sequence of statements. |
| code_assign | target = source. |
| code_decl | Declaration of a local with a given type and name (starts its lifetime). |
| code_dead | End of a local's lifetime — symex stops trusting reads through it. |
| code_return | return expr; (or return; if operand is nil). |
| code_skip | No-op. |
| code_goto | Unconditional jump to a label (irep_idt). Conditional gotos live on the GOTO instruction itself. |
| code_function_call | Function call ret = func(args). |
| code_free | free(ptr) — releases a heap object. |
| code_printf | Captured printf/format call (preserves the format string). |
| code_expression | A statement consisting of a single expression evaluated for its side effects. |
| code_comma | C comma operator — evaluate left, then right; result is right. |
| code_asm | Inline assembly (the string is preserved; semantics are usually opaque). |
These mirror the legacy structured codet statements so a frontend can emit IREP2 bodies; they are not yet built by any pipeline path (see the Structured-statement kinds note above).
| Kind | Description |
|---|---|
| code_ifthenelse | if (cond) then_case [else else_case]. else_case is nil when absent. |
| code_while | while (cond) body. |
| code_for | for (init; cond; iter) body. init/cond/iter are each nil when absent. |
| code_switch | switch (value) body. |
| code_break | break;. |
| code_continue | continue;. |
| code_label | A labelled statement: label: code. |
| Kind | Description |
|---|---|
| code_cpp_delete | C++ delete expr;. |
| code_cpp_del_array | C++ delete[] expr;. |
| code_cpp_throw | throw expr; with a list of types being thrown. |
| code_cpp_catch | try/catch: a list of catchable types plus, for the source-level form, the try block and per-type handler blocks as operands (empty on the post-goto-convert CATCH marker). |