@@ -687,7 +687,143 @@ std::optional<exprt> get_subexpression_at_offset(
687687 const auto offset_bytes = numeric_cast<mp_integer>(offset);
688688
689689 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+ }
691827 else
692828 return get_subexpression_at_offset (expr, *offset_bytes, target_type, ns);
693829}
0 commit comments