+/* Write out an alternative form of line and address skips using
+ DW_LNS_fixed_advance_pc opcodes. This uses more space than the default
+ line and address information, but it is required if linker relaxation
+ could change the code offsets. The following two routines *must* be
+ kept in sync. */
+
+static int
+size_fixed_inc_line_addr (int line_delta, addressT addr_delta)
+{
+ int len = 0;
+
+ /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. */
+ if (line_delta != INT_MAX)
+ len = 1 + sizeof_leb128 (line_delta, 1);
+
+ if (addr_delta > 50000)
+ {
+ /* DW_LNS_extended_op */
+ len += 1 + sizeof_leb128 (sizeof_address + 1, 0);
+ /* DW_LNE_set_address */
+ len += 1 + sizeof_address;
+ }
+ else
+ /* DW_LNS_fixed_advance_pc */
+ len += 3;
+
+ if (line_delta == INT_MAX)
+ /* DW_LNS_extended_op + DW_LNE_end_sequence */
+ len += 3;
+ else
+ /* DW_LNS_copy */
+ len += 1;
+
+ return len;
+}
+
+static void
+emit_fixed_inc_line_addr (int line_delta, addressT addr_delta, fragS *frag,
+ char *p, int len)
+{
+ expressionS *pexp;
+ segT line_seg;
+ char *end = p + len;
+
+ /* Line number sequences cannot go backward in addresses. This means
+ we've incorrectly ordered the statements in the sequence. */
+ gas_assert ((offsetT) addr_delta >= 0);
+
+ /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. */
+ if (line_delta != INT_MAX)
+ {
+ *p++ = DW_LNS_advance_line;
+ p += output_leb128 (p, line_delta, 1);
+ }
+
+ pexp = symbol_get_value_expression (frag->fr_symbol);
+ line_seg = subseg_get (".debug_line", 0);
+
+ /* The DW_LNS_fixed_advance_pc opcode has a 2-byte operand so it can
+ advance the address by at most 64K. Linker relaxation (without
+ which this function would not be used) could change the operand by
+ an unknown amount. If the address increment is getting close to
+ the limit, just reset the address. */
+ if (addr_delta > 50000)
+ {
+ symbolS *to_sym;
+ expressionS exp;
+
+ gas_assert (pexp->X_op = O_subtract);
+ to_sym = pexp->X_add_symbol;
+
+ *p++ = DW_LNS_extended_op;
+ p += output_leb128 (p, sizeof_address + 1, 0);
+ *p++ = DW_LNE_set_address;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = to_sym;
+ exp.X_add_number = 0;
+ subseg_change (line_seg, 0);
+ emit_expr_fix (&exp, sizeof_address, frag, p);
+ p += sizeof_address;
+ }
+ else
+ {
+ *p++ = DW_LNS_fixed_advance_pc;
+ subseg_change (line_seg, 0);
+ emit_expr_fix (pexp, 2, frag, p);
+ p += 2;
+ }
+
+ if (line_delta == INT_MAX)
+ {
+ *p++ = DW_LNS_extended_op;
+ *p++ = 1;
+ *p++ = DW_LNE_end_sequence;
+ }
+ else
+ *p++ = DW_LNS_copy;
+
+ gas_assert (p == end);
+}
+