+/* (Un)reverse the line_entry list starting from H. */
+
+static struct line_entry *
+reverse_line_entry_list (struct line_entry *h)
+{
+ struct line_entry *p = NULL, *e, *n;
+
+ for (e = h; e; e = n)
+ {
+ n = e->next;
+ e->next = p;
+ p = e;
+ }
+ return p;
+}
+
+/* Compute the view for E based on the previous entry P. If we
+ introduce an (undefined) view symbol for P, and H is given (P must
+ be the tail in this case), introduce view symbols for earlier list
+ entries as well, until one of them is constant. */
+
+static void
+set_or_check_view (struct line_entry *e, struct line_entry *p,
+ struct line_entry *h)
+{
+ expressionS viewx;
+
+ memset (&viewx, 0, sizeof (viewx));
+ viewx.X_unsigned = 1;
+
+ /* First, compute !(E->label > P->label), to tell whether or not
+ we're to reset the view number. If we can't resolve it to a
+ constant, keep it symbolic. */
+ if (!p || (e->loc.view == force_reset_view && force_reset_view))
+ {
+ viewx.X_op = O_constant;
+ viewx.X_add_number = 0;
+ viewx.X_add_symbol = NULL;
+ viewx.X_op_symbol = NULL;
+ }
+ else
+ {
+ viewx.X_op = O_gt;
+ viewx.X_add_number = 0;
+ viewx.X_add_symbol = e->label;
+ viewx.X_op_symbol = p->label;
+ resolve_expression (&viewx);
+ if (viewx.X_op == O_constant)
+ viewx.X_add_number = !viewx.X_add_number;
+ else
+ {
+ viewx.X_add_symbol = make_expr_symbol (&viewx);
+ viewx.X_add_number = 0;
+ viewx.X_op_symbol = NULL;
+ viewx.X_op = O_logical_not;
+ }
+ }
+
+ if (S_IS_DEFINED (e->loc.view) && symbol_constant_p (e->loc.view))
+ {
+ expressionS *value = symbol_get_value_expression (e->loc.view);
+ /* We can't compare the view numbers at this point, because in
+ VIEWX we've only determined whether we're to reset it so
+ far. */
+ if (viewx.X_op == O_constant)
+ {
+ if (!value->X_add_number != !viewx.X_add_number)
+ as_bad (_("view number mismatch"));
+ }
+ /* Record the expression to check it later. It is the result of
+ a logical not, thus 0 or 1. We just add up all such deferred
+ expressions, and resolve it at the end. */
+ else if (!value->X_add_number)
+ {
+ symbolS *deferred = make_expr_symbol (&viewx);
+ if (view_assert_failed)
+ {
+ expressionS chk;
+
+ memset (&chk, 0, sizeof (chk));
+ chk.X_unsigned = 1;
+ chk.X_op = O_add;
+ chk.X_add_number = 0;
+ chk.X_add_symbol = view_assert_failed;
+ chk.X_op_symbol = deferred;
+ deferred = make_expr_symbol (&chk);
+ }
+ view_assert_failed = deferred;
+ }
+ }
+
+ if (viewx.X_op != O_constant || viewx.X_add_number)
+ {
+ expressionS incv;
+
+ if (!p->loc.view)
+ {
+ p->loc.view = symbol_temp_make ();
+ gas_assert (!S_IS_DEFINED (p->loc.view));
+ }
+
+ memset (&incv, 0, sizeof (incv));
+ incv.X_unsigned = 1;
+ incv.X_op = O_symbol;
+ incv.X_add_symbol = p->loc.view;
+ incv.X_add_number = 1;
+
+ if (viewx.X_op == O_constant)
+ {
+ gas_assert (viewx.X_add_number == 1);
+ viewx = incv;
+ }
+ else
+ {
+ viewx.X_add_symbol = make_expr_symbol (&viewx);
+ viewx.X_add_number = 0;
+ viewx.X_op_symbol = make_expr_symbol (&incv);
+ viewx.X_op = O_multiply;
+ }
+ }
+
+ if (!S_IS_DEFINED (e->loc.view))
+ {
+ symbol_set_value_expression (e->loc.view, &viewx);
+ S_SET_SEGMENT (e->loc.view, expr_section);
+ symbol_set_frag (e->loc.view, &zero_address_frag);
+ }
+
+ /* Define and attempt to simplify any earlier views needed to
+ compute E's. */
+ if (h && p && p->loc.view && !S_IS_DEFINED (p->loc.view))
+ {
+ struct line_entry *h2;
+ /* Reverse the list to avoid quadratic behavior going backwards
+ in a single-linked list. */
+ struct line_entry *r = reverse_line_entry_list (h);
+
+ gas_assert (r == p);
+ /* Set or check views until we find a defined or absent view. */
+ do
+ {
+ /* Do not define the head of a (sub?)segment view while
+ handling others. It would be defined too early, without
+ regard to the last view of other subsegments.
+ set_or_check_view will be called for every head segment
+ that needs it. */
+ if (r == h)
+ break;
+ set_or_check_view (r, r->next, NULL);
+ }
+ while (r->next && r->next->loc.view && !S_IS_DEFINED (r->next->loc.view)
+ && (r = r->next));
+
+ /* Unreverse the list, so that we can go forward again. */
+ h2 = reverse_line_entry_list (p);
+ gas_assert (h2 == h);
+
+ /* Starting from the last view we just defined, attempt to
+ simplify the view expressions, until we do so to P. */
+ do
+ {
+ /* The head view of a subsegment may remain undefined while
+ handling other elements, before it is linked to the last
+ view of the previous subsegment. */
+ if (r == h)
+ continue;
+ gas_assert (S_IS_DEFINED (r->loc.view));
+ resolve_expression (symbol_get_value_expression (r->loc.view));
+ }
+ while (r != p && (r = r->next));
+
+ /* Now that we've defined and computed all earlier views that might
+ be needed to compute E's, attempt to simplify it. */
+ resolve_expression (symbol_get_value_expression (e->loc.view));
+ }
+}
+