@@ -687,7 +687,143 @@ std::optional<exprt> get_subexpression_at_offset(
687
687
const auto offset_bytes = numeric_cast<mp_integer>(offset);
688
688
689
689
if (!offset_bytes.has_value ())
690
- return {};
690
+ {
691
+ // offset is not a constant; try to see whether it is a multiple of a
692
+ // constant, or a sum that involves a multiple of a constant
693
+ if (auto array_type = type_try_dynamic_cast<array_typet>(expr.type ()))
694
+ {
695
+ const auto target_size_bits = pointer_offset_bits (target_type, ns);
696
+ const auto elem_size_bits =
697
+ pointer_offset_bits (array_type->element_type (), ns);
698
+
699
+ // no arrays of zero-, or unknown-sized elements, or ones where elements
700
+ // have a bit-width that isn't a multiple of bytes
701
+ if (
702
+ !target_size_bits.has_value () || !elem_size_bits.has_value () ||
703
+ *elem_size_bits <= 0 ||
704
+ *elem_size_bits % config.ansi_c .char_width != 0 ||
705
+ *target_size_bits != *elem_size_bits)
706
+ {
707
+ return {};
708
+ }
709
+
710
+ // if we have an offset C + x (where C is a constant) we can try to
711
+ // recurse by first looking at the member at offset C
712
+ if (
713
+ offset.id () == ID_plus && offset.operands ().size () == 2 &&
714
+ (to_multi_ary_expr (offset).op0 ().is_constant () ||
715
+ to_multi_ary_expr (offset).op1 ().is_constant ()))
716
+ {
717
+ const plus_exprt &offset_plus = to_plus_expr (offset);
718
+ const auto &const_factor = numeric_cast_v<mp_integer>(to_constant_expr (
719
+ offset_plus.op0 ().is_constant () ? offset_plus.op0 ()
720
+ : offset_plus.op1 ()));
721
+ const exprt &other_factor = offset_plus.op0 ().is_constant ()
722
+ ? offset_plus.op1 ()
723
+ : offset_plus.op0 ();
724
+
725
+ auto expr_at_offset_C =
726
+ get_subexpression_at_offset (expr, const_factor, target_type, ns);
727
+
728
+ if (
729
+ expr_at_offset_C.has_value () && expr_at_offset_C->id () == ID_index &&
730
+ to_index_expr (*expr_at_offset_C).index ().is_zero ())
731
+ {
732
+ return get_subexpression_at_offset (
733
+ to_index_expr (*expr_at_offset_C).array (),
734
+ other_factor,
735
+ target_type,
736
+ ns);
737
+ }
738
+ }
739
+
740
+ // give up if the offset expression isn't of the form K * i or i * K
741
+ // (where K is a constant)
742
+ if (
743
+ offset.id () != ID_mult || offset.operands ().size () != 2 ||
744
+ (!to_multi_ary_expr (offset).op0 ().is_constant () &&
745
+ !to_multi_ary_expr (offset).op1 ().is_constant ()))
746
+ {
747
+ return {};
748
+ }
749
+
750
+ const mult_exprt &offset_mult = to_mult_expr (offset);
751
+ const auto &const_factor = numeric_cast_v<mp_integer>(to_constant_expr (
752
+ offset_mult.op0 ().is_constant () ? offset_mult.op0 ()
753
+ : offset_mult.op1 ()));
754
+ const exprt &other_factor =
755
+ offset_mult.op0 ().is_constant () ? offset_mult.op1 () : offset_mult.op0 ();
756
+
757
+ if (const_factor % (*elem_size_bits / config.ansi_c .char_width ) != 0 )
758
+ return {};
759
+
760
+ exprt index = mult_exprt{
761
+ other_factor,
762
+ from_integer (
763
+ const_factor / (*elem_size_bits / config.ansi_c .char_width ),
764
+ other_factor.type ())};
765
+
766
+ return get_subexpression_at_offset (
767
+ index_exprt{
768
+ expr,
769
+ typecast_exprt::conditional_cast (index, array_type->index_type ())},
770
+ 0 ,
771
+ target_type,
772
+ ns);
773
+ }
774
+ else if (
775
+ auto struct_tag_type =
776
+ type_try_dynamic_cast<struct_tag_typet>(expr.type ()))
777
+ {
778
+ // If the offset expression is of the form K * i or i * K (where K is a
779
+ // constant) and the first component of the struct is an array we will
780
+ // recurse on that member.
781
+ const auto &components = ns.follow_tag (*struct_tag_type).components ();
782
+ if (
783
+ !components.empty () &&
784
+ can_cast_type<array_typet>(components.front ().type ()) &&
785
+ offset.id () == ID_mult && offset.operands ().size () == 2 &&
786
+ (to_multi_ary_expr (offset).op0 ().is_constant () ||
787
+ to_multi_ary_expr (offset).op1 ().is_constant ()))
788
+ {
789
+ return get_subexpression_at_offset (
790
+ member_exprt{expr, components.front ()}, offset, target_type, ns);
791
+ }
792
+ // if we have an offset C + x (where C is a constant) we can try to
793
+ // recurse by first looking at the member at offset C
794
+ else if (
795
+ offset.id () == ID_plus && offset.operands ().size () == 2 &&
796
+ (to_multi_ary_expr (offset).op0 ().is_constant () ||
797
+ to_multi_ary_expr (offset).op1 ().is_constant ()))
798
+ {
799
+ const plus_exprt &offset_plus = to_plus_expr (offset);
800
+ const auto &const_factor = numeric_cast_v<mp_integer>(to_constant_expr (
801
+ offset_plus.op0 ().is_constant () ? offset_plus.op0 ()
802
+ : offset_plus.op1 ()));
803
+ const exprt &other_factor = offset_plus.op0 ().is_constant ()
804
+ ? offset_plus.op1 ()
805
+ : offset_plus.op0 ();
806
+
807
+ auto expr_at_offset_C =
808
+ get_subexpression_at_offset (expr, const_factor, target_type, ns);
809
+
810
+ if (
811
+ expr_at_offset_C.has_value () && expr_at_offset_C->id () == ID_index &&
812
+ to_index_expr (*expr_at_offset_C).index ().is_zero ())
813
+ {
814
+ return get_subexpression_at_offset (
815
+ to_index_expr (*expr_at_offset_C).array (),
816
+ other_factor,
817
+ target_type,
818
+ ns);
819
+ }
820
+ }
821
+
822
+ return {};
823
+ }
824
+ else
825
+ return {};
826
+ }
691
827
else
692
828
return get_subexpression_at_offset (expr, *offset_bytes, target_type, ns);
693
829
}
0 commit comments