From 08dd144ab1b9e92060e40672f5cfebc780605b2d Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 18 Oct 2022 11:07:36 +0000 Subject: [PATCH] Field sensitivity for unions Field-sensitive tracking of unions permits propagating constants even when those do not affect the full width of the union (implying that some bits remain non-constant). --- regression/cbmc/Float22/test.desc | 2 +- .../cbmc/Pointer_Arithmetic19/test.desc | 1 - .../cbmc/array-cell-sensitivity15/test.desc | 2 +- .../cbmc/array-cell-sensitivity8/test.desc | 2 +- regression/cbmc/field-sensitivity9/test.desc | 4 +- regression/cbmc/union18/main.c | 15 ++ regression/cbmc/union18/test.desc | 12 ++ src/goto-symex/field_sensitivity.cpp | 197 ++++++++++++++++-- src/goto-symex/field_sensitivity.h | 56 ++++- src/goto-symex/goto_symex_state.cpp | 13 +- src/goto-symex/symex_assign.cpp | 9 +- src/goto-symex/symex_dead.cpp | 8 +- src/goto-symex/symex_goto.cpp | 2 +- src/util/irep_ids.def | 1 + 14 files changed, 279 insertions(+), 45 deletions(-) create mode 100644 regression/cbmc/union18/main.c create mode 100644 regression/cbmc/union18/test.desc diff --git a/regression/cbmc/Float22/test.desc b/regression/cbmc/Float22/test.desc index 8f1b838d89ca..b7d95a282154 100644 --- a/regression/cbmc/Float22/test.desc +++ b/regression/cbmc/Float22/test.desc @@ -1,4 +1,4 @@ -CORE broken-smt-backend +CORE main.c --floatbv ^EXIT=0$ diff --git a/regression/cbmc/Pointer_Arithmetic19/test.desc b/regression/cbmc/Pointer_Arithmetic19/test.desc index f522ae08324e..62a5ca76c884 100644 --- a/regression/cbmc/Pointer_Arithmetic19/test.desc +++ b/regression/cbmc/Pointer_Arithmetic19/test.desc @@ -1,7 +1,6 @@ CORE new-smt-backend main.c --program-only -ASSERT\(byte_extract_little_endian\(\{ 42, 43 \}, POINTER_OFFSET\(p!0@1#2\), signed int\) == 43\)$ ^EXIT=0$ ^SIGNAL=0$ -- diff --git a/regression/cbmc/array-cell-sensitivity15/test.desc b/regression/cbmc/array-cell-sensitivity15/test.desc index e693ea0aff6d..84c4b927b7ca 100644 --- a/regression/cbmc/array-cell-sensitivity15/test.desc +++ b/regression/cbmc/array-cell-sensitivity15/test.desc @@ -3,7 +3,7 @@ access.c --program-only ^EXIT=0$ ^SIGNAL=0$ -s!0@1#1\.\.n == \{ s!0@1#1\.\.n\[\[0\]\], s!0@1#1\.\.n\[\[1\]\], s!0@1#1\.\.n\[\[2\]\], s!0@1#1\.\.n\[\[3\]\] } WITH \[\(.*\)i!0@1#2:=k!0@1#1\] +s!0@1#2\.\.n == \{ s!0@1#1\.\.n\[\[0\]\], s!0@1#1\.\.n\[\[1\]\], s!0@1#1\.\.n\[\[2\]\], s!0@1#1\.\.n\[\[3\]\] } WITH \[\(.*\)i!0@1#2:=k!0@1#1\] -- byte_update -- diff --git a/regression/cbmc/array-cell-sensitivity8/test.desc b/regression/cbmc/array-cell-sensitivity8/test.desc index 3cb74addf6cf..ca40b6b95e2f 100644 --- a/regression/cbmc/array-cell-sensitivity8/test.desc +++ b/regression/cbmc/array-cell-sensitivity8/test.desc @@ -10,7 +10,7 @@ main::1::array!0@1#2\[\[6\]\] = main::1::array!0@1#2\[\[7\]\] = main::1::array!0@1#2\[\[8\]\] = main::1::array!0@1#2\[\[9\]\] = -main::1::array!0@1#2 =.*byte_extract_little_endian +main::1::array!0@1#3 =.*byte_extract_little_endian main::1::array!0@1#3\[\[0\]\] = main::1::array!0@1#3\[\[1\]\] = main::1::array!0@1#3\[\[2\]\] = diff --git a/regression/cbmc/field-sensitivity9/test.desc b/regression/cbmc/field-sensitivity9/test.desc index 599668c7fe61..656fa006bb52 100644 --- a/regression/cbmc/field-sensitivity9/test.desc +++ b/regression/cbmc/field-sensitivity9/test.desc @@ -1,8 +1,8 @@ CORE test.c --show-vcc -main::1::a1!0@1#1 = -main::1::a2!0@1#1 = +main::1::a1!0@1#2 = +main::1::a2!0@1#2 = main::1::a1!0@1#2\.\.x = main::1::a1!0@1#2\.\.y = main::1::a1!0@1#2\.\.z = diff --git a/regression/cbmc/union18/main.c b/regression/cbmc/union18/main.c new file mode 100644 index 000000000000..16418da0afc9 --- /dev/null +++ b/regression/cbmc/union18/main.c @@ -0,0 +1,15 @@ +#include + +union u_type +{ + int i; + char ch; +}; + +int main() +{ + union u_type u; + + u.ch = 2; + assert(u.ch == 2); +} diff --git a/regression/cbmc/union18/test.desc b/regression/cbmc/union18/test.desc new file mode 100644 index 000000000000..e61cb7256bad --- /dev/null +++ b/regression/cbmc/union18/test.desc @@ -0,0 +1,12 @@ +CORE +main.c + +^Generated 1 VCC\(s\), 0 remaining after simplification$ +^VERIFICATION SUCCESSFUL$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +-- +This should be fully solved by constant propagation when union field-sensitivity +is in place. diff --git a/src/goto-symex/field_sensitivity.cpp b/src/goto-symex/field_sensitivity.cpp index 1ef0da32950a..7575ed544c79 100644 --- a/src/goto-symex/field_sensitivity.cpp +++ b/src/goto-symex/field_sensitivity.cpp @@ -9,8 +9,10 @@ Author: Michael Tautschnig #include "field_sensitivity.h" #include +#include +#include +#include #include -#include #include "goto_symex_state.h" #include "symex_target.h" @@ -26,7 +28,7 @@ exprt field_sensitivityt::apply( if(write) return std::move(ssa_expr); else - return get_fields(ns, state, ssa_expr); + return get_fields(ns, state, ssa_expr, true); } exprt field_sensitivityt::apply( @@ -43,7 +45,7 @@ exprt field_sensitivityt::apply( if(!write && is_ssa_expr(expr)) { - return get_fields(ns, state, to_ssa_expr(expr)); + return get_fields(ns, state, to_ssa_expr(expr), true); } else if( !write && expr.id() == ID_member && @@ -69,7 +71,9 @@ exprt field_sensitivityt::apply( if( is_ssa_expr(member.struct_op()) && (member.struct_op().type().id() == ID_struct || - member.struct_op().type().id() == ID_struct_tag)) + member.struct_op().type().id() == ID_struct_tag || + member.struct_op().type().id() == ID_union || + member.struct_op().type().id() == ID_union_tag)) { // place the entire member expression, not just the struct operand, in an // SSA expression @@ -85,6 +89,40 @@ exprt field_sensitivityt::apply( return std::move(tmp); } } + else if( + !write && (expr.id() == ID_byte_extract_little_endian || + expr.id() == ID_byte_extract_big_endian)) + { + const byte_extract_exprt &be = to_byte_extract_expr(expr); + if( + (be.op().type().id() == ID_union || + be.op().type().id() == ID_union_tag) && + is_ssa_expr(be.op()) && be.offset().is_constant()) + { + const union_typet &type = to_union_type(ns.follow(be.op().type())); + for(const auto &comp : type.components()) + { + ssa_exprt tmp = to_ssa_expr(be.op()); + bool was_l2 = !tmp.get_level_2().empty(); + tmp.remove_level_2(); + const member_exprt member{tmp.get_original_expr(), comp}; + auto recursive_member = + get_subexpression_at_offset(member, be.offset(), be.type(), ns); + if( + recursive_member.has_value() && + (recursive_member->id() == ID_member || + recursive_member->id() == ID_index)) + { + tmp.type() = be.type(); + tmp.set_expression(*recursive_member); + if(was_l2) + return state.rename(std::move(tmp), ns).get(); + else + return std::move(tmp); + } + } + } + } #ifdef ENABLE_ARRAY_FIELD_SENSITIVITY else if(expr.id() == ID_index) { @@ -141,7 +179,7 @@ exprt field_sensitivityt::apply( { // Expand the array and return `{array[0]; array[1]; ...}[index]` exprt expanded_array = - get_fields(ns, state, to_ssa_expr(index.array())); + get_fields(ns, state, to_ssa_expr(index.array()), true); return index_exprt{std::move(expanded_array), index.index()}; } } @@ -154,33 +192,48 @@ exprt field_sensitivityt::apply( exprt field_sensitivityt::get_fields( const namespacet &ns, goto_symex_statet &state, - const ssa_exprt &ssa_expr) const + const ssa_exprt &ssa_expr, + bool disjoined_fields_only) const { - if(ssa_expr.type().id() == ID_struct || ssa_expr.type().id() == ID_struct_tag) + if( + ssa_expr.type().id() == ID_struct || + ssa_expr.type().id() == ID_struct_tag || + (!disjoined_fields_only && (ssa_expr.type().id() == ID_union || + ssa_expr.type().id() == ID_union_tag))) { - const struct_typet &type = to_struct_type(ns.follow(ssa_expr.type())); + const struct_union_typet &type = + to_struct_union_type(ns.follow(ssa_expr.type())); const struct_union_typet::componentst &components = type.components(); - struct_exprt::operandst fields; + exprt::operandst fields; fields.reserve(components.size()); - const exprt &struct_op = ssa_expr.get_original_expr(); + const exprt &compound_op = ssa_expr.get_original_expr(); for(const auto &comp : components) { ssa_exprt tmp = ssa_expr; bool was_l2 = !tmp.get_level_2().empty(); tmp.remove_level_2(); - tmp.set_expression(member_exprt{struct_op, comp.get_name(), comp.type()}); + tmp.set_expression( + member_exprt{compound_op, comp.get_name(), comp.type()}); + exprt field = get_fields(ns, state, tmp, disjoined_fields_only); if(was_l2) { - fields.push_back(state.rename(get_fields(ns, state, tmp), ns).get()); + fields.emplace_back(state.rename(std::move(field), ns).get()); } else - fields.push_back(get_fields(ns, state, tmp)); + fields.emplace_back(std::move(field)); } - return struct_exprt(std::move(fields), ssa_expr.type()); + if( + disjoined_fields_only && (ssa_expr.type().id() == ID_struct || + ssa_expr.type().id() == ID_struct_tag)) + { + return struct_exprt{std::move(fields), ssa_expr.type()}; + } + else + return field_sensitive_ssa_exprt{ssa_expr, std::move(fields)}; } #ifdef ENABLE_ARRAY_FIELD_SENSITIVITY else if( @@ -207,15 +260,19 @@ exprt field_sensitivityt::get_fields( bool was_l2 = !tmp.get_level_2().empty(); tmp.remove_level_2(); tmp.set_expression(index); + exprt element = get_fields(ns, state, tmp, disjoined_fields_only); if(was_l2) { - elements.push_back(state.rename(get_fields(ns, state, tmp), ns).get()); + elements.emplace_back(state.rename(std::move(element), ns).get()); } else - elements.push_back(get_fields(ns, state, tmp)); + elements.emplace_back(std::move(element)); } - return array_exprt(std::move(elements), type); + if(disjoined_fields_only) + return array_exprt(std::move(elements), type); + else + return field_sensitive_ssa_exprt{ssa_expr, std::move(elements)}; } #endif // ENABLE_ARRAY_FIELD_SENSITIVITY else @@ -230,7 +287,7 @@ void field_sensitivityt::field_assignments( symex_targett &target, bool allow_pointer_unsoundness) const { - const exprt lhs_fs = get_fields(ns, state, lhs); + const exprt lhs_fs = get_fields(ns, state, lhs, false); if(lhs != lhs_fs) { @@ -292,10 +349,67 @@ void field_sensitivityt::field_assignments_rec( exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin(); for(const auto &comp : components) { - const exprt member_rhs = - simplify_opt(member_exprt{ssa_rhs, comp.get_name(), comp.type()}, ns); + const exprt member_rhs = apply( + ns, + state, + simplify_opt(member_exprt{ssa_rhs, comp.get_name(), comp.type()}, ns), + false); + const exprt &member_lhs = *fs_it; + + if( + auto fs_ssa = + expr_try_dynamic_cast(member_lhs)) + { + field_assignments_rec( + ns, + state, + fs_ssa->get_object_ssa(), + member_rhs, + target, + allow_pointer_unsoundness); + } + + field_assignments_rec( + ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness); + ++fs_it; + } + } + else if( + ssa_rhs.type().id() == ID_union || ssa_rhs.type().id() == ID_union_tag) + { + const union_typet &type = to_union_type(ns.follow(ssa_rhs.type())); + const struct_union_typet::componentst &components = type.components(); + + PRECONDITION( + components.empty() || + (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size())); + + exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin(); + for(const auto &comp : components) + { + const exprt member_rhs = apply( + ns, + state, + simplify_opt( + make_byte_extract( + ssa_rhs, from_integer(0, c_index_type()), comp.type()), + ns), + false); const exprt &member_lhs = *fs_it; + if( + auto fs_ssa = + expr_try_dynamic_cast(member_lhs)) + { + field_assignments_rec( + ns, + state, + fs_ssa->get_object_ssa(), + member_rhs, + target, + allow_pointer_unsoundness); + } + field_assignments_rec( ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness); ++fs_it; @@ -314,10 +428,27 @@ void field_sensitivityt::field_assignments_rec( exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin(); for(std::size_t i = 0; i < array_size; ++i) { - const exprt index_rhs = simplify_opt( - index_exprt{ssa_rhs, from_integer(i, type->index_type())}, ns); + const exprt index_rhs = apply( + ns, + state, + simplify_opt( + index_exprt{ssa_rhs, from_integer(i, type->index_type())}, ns), + false); const exprt &index_lhs = *fs_it; + if( + auto fs_ssa = + expr_try_dynamic_cast(index_lhs)) + { + field_assignments_rec( + ns, + state, + fs_ssa->get_object_ssa(), + index_rhs, + target, + allow_pointer_unsoundness); + } + field_assignments_rec( ns, state, index_lhs, index_rhs, target, allow_pointer_unsoundness); ++fs_it; @@ -333,6 +464,17 @@ void field_sensitivityt::field_assignments_rec( exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin(); for(const exprt &op : ssa_rhs.operands()) { + if(auto fs_ssa = expr_try_dynamic_cast(*fs_it)) + { + field_assignments_rec( + ns, + state, + fs_ssa->get_object_ssa(), + op, + target, + allow_pointer_unsoundness); + } + field_assignments_rec( ns, state, *fs_it, op, target, allow_pointer_unsoundness); ++fs_it; @@ -344,7 +486,9 @@ void field_sensitivityt::field_assignments_rec( } } -bool field_sensitivityt::is_divisible(const ssa_exprt &expr) const +bool field_sensitivityt::is_divisible( + const ssa_exprt &expr, + bool disjoined_fields_only) const { if(expr.type().id() == ID_struct || expr.type().id() == ID_struct_tag) return true; @@ -360,6 +504,13 @@ bool field_sensitivityt::is_divisible(const ssa_exprt &expr) const } #endif + if( + !disjoined_fields_only && + (expr.type().id() == ID_union || expr.type().id() == ID_union_tag)) + { + return true; + } + return false; } diff --git a/src/goto-symex/field_sensitivity.h b/src/goto-symex/field_sensitivity.h index ffd45b2e0afd..9de75a903ff1 100644 --- a/src/goto-symex/field_sensitivity.h +++ b/src/goto-symex/field_sensitivity.h @@ -9,16 +9,51 @@ Author: Michael Tautschnig #ifndef CPROVER_GOTO_SYMEX_FIELD_SENSITIVITY_H #define CPROVER_GOTO_SYMEX_FIELD_SENSITIVITY_H -#include - #include +#include -class exprt; -class ssa_exprt; class namespacet; class goto_symex_statet; class symex_targett; +class field_sensitive_ssa_exprt : public exprt +{ +public: + field_sensitive_ssa_exprt(const ssa_exprt &ssa, exprt::operandst &&fields) + : exprt(ID_field_sensitive_ssa, ssa.type(), std::move(fields)) + { + add(ID_expression, ssa); + } + + const ssa_exprt &get_object_ssa() const + { + return static_cast(find(ID_expression)); + } +}; + +template <> +inline bool can_cast_expr(const exprt &base) +{ + return base.id() == ID_field_sensitive_ssa; +} + +inline const field_sensitive_ssa_exprt & +to_field_sensitive_ssa_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_field_sensitive_ssa); + const field_sensitive_ssa_exprt &ret = + static_cast(expr); + return ret; +} + +inline field_sensitive_ssa_exprt &to_field_sensitive_ssa_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_field_sensitive_ssa); + field_sensitive_ssa_exprt &ret = + static_cast(expr); + return ret; +} + /// Control granularity of object accesses /// /// [Field sensitivity](\ref field_sensitivityt) is a transformation of the @@ -136,10 +171,14 @@ class field_sensitivityt bool write) const; /// Compute an expression representing the individual components of a - /// field-sensitive SSA representation of \p ssa_expr. + /// field-sensitive SSA representation of \p ssa_expr. When + /// \p disjoined_fields_only is false, the resulting expression must be + /// handled with care as it will have multiple operands but empty `id()`. /// \param ns: a namespace to resolve type symbols/tag types /// \param [in,out] state: symbolic execution state /// \param ssa_expr: the expression to evaluate + /// \param disjoined_fields_only: whether to expand unions (`false`) or not + /// (`true`) /// \return Expanded expression; for example, for a \p ssa_expr of some struct /// type, a `struct_exprt` with each component now being an SSA expression /// is built. @@ -147,17 +186,20 @@ class field_sensitivityt exprt get_fields( const namespacet &ns, goto_symex_statet &state, - const ssa_exprt &ssa_expr) const; + const ssa_exprt &ssa_expr, + bool disjoined_fields_only) const; /// Determine whether \p expr would translate to an atomic SSA expression /// (returns false) or a composite object made of several SSA expressions as /// components (such as a struct with each member becoming an individual SSA /// expression, return true in this case). /// \param expr: the expression to evaluate + /// \param disjoined_fields_only: whether to expand unions (`false`) or not + /// (`true`) /// \return False, if and only if, \p expr would be a single field-sensitive /// SSA expression. NODISCARD - bool is_divisible(const ssa_exprt &expr) const; + bool is_divisible(const ssa_exprt &expr, bool disjoined_fields_only) const; private: const std::size_t max_field_sensitivity_array_size; diff --git a/src/goto-symex/goto_symex_state.cpp b/src/goto-symex/goto_symex_state.cpp index 06f7e304d682..88a9cbe3faaa 100644 --- a/src/goto-symex/goto_symex_state.cpp +++ b/src/goto-symex/goto_symex_state.cpp @@ -372,7 +372,7 @@ bool goto_symex_statet::l2_thread_read_encoding( } // only continue if an indivisible object is being accessed - if(field_sensitivity.is_divisible(expr)) + if(field_sensitivity.is_divisible(expr, true)) return false; const ssa_exprt ssa_l1 = remove_level_2(expr); @@ -508,7 +508,7 @@ goto_symex_statet::write_is_shared_resultt goto_symex_statet::write_is_shared( } // only continue if an indivisible object is being accessed - if(field_sensitivity.is_divisible(expr)) + if(field_sensitivity.is_divisible(expr, true)) return write_is_shared_resultt::NOT_SHARED; if(atomic_section_id != 0) @@ -836,7 +836,7 @@ ssa_exprt goto_symex_statet::declare(ssa_exprt ssa, const namespacet &ns) } // L2 renaming - const exprt fields = field_sensitivity.get_fields(ns, *this, ssa); + exprt fields = field_sensitivity.get_fields(ns, *this, ssa, false); fields.visit_pre([this](const exprt &e) { if(auto l1_symbol = expr_try_dynamic_cast(e)) { @@ -845,6 +845,13 @@ ssa_exprt goto_symex_statet::declare(ssa_exprt ssa, const namespacet &ns) l1_symbol->get_identifier(), field_ssa, fresh_l2_name_provider); CHECK_RETURN(field_generation == 1); } + else if(auto fs_ssa = expr_try_dynamic_cast(e)) + { + const ssa_exprt &ssa = fs_ssa->get_object_ssa(); + const std::size_t field_generation = level2.increase_generation( + ssa.get_identifier(), ssa, fresh_l2_name_provider); + CHECK_RETURN(field_generation == 1); + } }); record_events.push(false); diff --git a/src/goto-symex/symex_assign.cpp b/src/goto-symex/symex_assign.cpp index f73d0a24a6a1..8da10ae2c22b 100644 --- a/src/goto-symex/symex_assign.cpp +++ b/src/goto-symex/symex_assign.cpp @@ -205,7 +205,7 @@ void symex_assignt::assign_non_struct_symbol( current_assignment_type); const ssa_exprt &l1_lhs = assignment.lhs; - if(state.field_sensitivity.is_divisible(l1_lhs)) + if(state.field_sensitivity.is_divisible(l1_lhs, false)) { // Split composite symbol lhs into its components state.field_sensitivity.field_assignments( @@ -218,8 +218,11 @@ void symex_assignt::assign_non_struct_symbol( // Erase the composite symbol from our working state. Note that we need to // have it in the propagation table and the value set while doing the field // assignments, thus we cannot skip putting it in there above. - state.propagation.erase_if_exists(l1_lhs.get_identifier()); - state.value_set.erase_symbol(l1_lhs, ns); + if(state.field_sensitivity.is_divisible(l1_lhs, true)) + { + state.propagation.erase_if_exists(l1_lhs.get_identifier()); + state.value_set.erase_symbol(l1_lhs, ns); + } } } diff --git a/src/goto-symex/symex_dead.cpp b/src/goto-symex/symex_dead.cpp index f30575d2fbf2..d373a46525a0 100644 --- a/src/goto-symex/symex_dead.cpp +++ b/src/goto-symex/symex_dead.cpp @@ -48,7 +48,10 @@ static void remove_l1_object_rec( // identifier is still live. state.drop_l1_name(l1_identifier); } - else if(l1_expr.id() == ID_array || l1_expr.id() == ID_struct) + else if( + l1_expr.type().id() == ID_array || l1_expr.type().id() == ID_struct || + l1_expr.type().id() == ID_struct_tag || l1_expr.type().id() == ID_union || + l1_expr.type().id() == ID_union_tag) { for(const auto &op : l1_expr.operands()) remove_l1_object_rec(state, op, ns); @@ -63,6 +66,7 @@ void goto_symext::symex_dead(statet &state, const symbol_exprt &symbol_expr) : ssa_exprt{symbol_expr}; ssa_exprt ssa = state.rename_ssa(to_rename, ns).get(); - const exprt fields = state.field_sensitivity.get_fields(ns, state, ssa); + const exprt fields = + state.field_sensitivity.get_fields(ns, state, ssa, false); remove_l1_object_rec(state, fields, ns); } diff --git a/src/goto-symex/symex_goto.cpp b/src/goto-symex/symex_goto.cpp index 22b94528e68e..283efe13fb46 100644 --- a/src/goto-symex/symex_goto.cpp +++ b/src/goto-symex/symex_goto.cpp @@ -764,7 +764,7 @@ static void merge_names( } // field sensitivity: only merge on individual fields - if(dest_state.field_sensitivity.is_divisible(ssa)) + if(dest_state.field_sensitivity.is_divisible(ssa, true)) return; // shared variables are renamed on every access anyway, we don't need to diff --git a/src/util/irep_ids.def b/src/util/irep_ids.def index 50c3bfa306db..24c99a81f00b 100644 --- a/src/util/irep_ids.def +++ b/src/util/irep_ids.def @@ -905,6 +905,7 @@ IREP_ID_TWO(overflow_result_minus, overflow_result--) IREP_ID_TWO(overflow_result_mult, overflow_result-*) IREP_ID_TWO(overflow_result_shl, overflow_result-shl) IREP_ID_TWO(overflow_result_unary_minus, overflow_result-unary-) +IREP_ID_ONE(field_sensitive_ssa) // Projects depending on this code base that wish to extend the list of // available ids should provide a file local_irep_ids.def in their source tree