+/* Code to handle alignment inside shorten_branches. */
+
+/* Here is an explanation how the algorithm in align_fuzz can give
+ proper results:
+
+ Call a sequence of instructions beginning with alignment point X
+ and continuing until the next alignment point `block X'. When `X'
+ is used in an expression, it means the alignment value of the
+ alignment point.
+
+ Call the distance between the start of the first insn of block X, and
+ the end of the last insn of block X `IX', for the `inner size of X'.
+ This is clearly the sum of the instruction lengths.
+
+ Likewise with the next alignment-delimited block following X, which we
+ shall call block Y.
+
+ Call the distance between the start of the first insn of block X, and
+ the start of the first insn of block Y `OX', for the `outer size of X'.
+
+ The estimated padding is then OX - IX.
+
+ OX can be safely estimated as
+
+ if (X >= Y)
+ OX = round_up(IX, Y)
+ else
+ OX = round_up(IX, X) + Y - X
+
+ Clearly est(IX) >= real(IX), because that only depends on the
+ instruction lengths, and those being overestimated is a given.
+
+ Clearly round_up(foo, Z) >= round_up(bar, Z) if foo >= bar, so
+ we needn't worry about that when thinking about OX.
+
+ When X >= Y, the alignment provided by Y adds no uncertainty factor
+ for branch ranges starting before X, so we can just round what we have.
+ But when X < Y, we don't know anything about the, so to speak,
+ `middle bits', so we have to assume the worst when aligning up from an
+ address mod X to one mod Y, which is Y - X. */
+
+#ifndef LABEL_ALIGN
+#define LABEL_ALIGN(LABEL) align_labels_log
+#endif
+
+#ifndef LABEL_ALIGN_MAX_SKIP
+#define LABEL_ALIGN_MAX_SKIP (align_labels-1)
+#endif
+
+#ifndef LOOP_ALIGN
+#define LOOP_ALIGN(LABEL) align_loops_log
+#endif
+
+#ifndef LOOP_ALIGN_MAX_SKIP
+#define LOOP_ALIGN_MAX_SKIP (align_loops-1)
+#endif
+
+#ifndef LABEL_ALIGN_AFTER_BARRIER
+#define LABEL_ALIGN_AFTER_BARRIER(LABEL) align_jumps_log
+#endif
+
+#ifndef LABEL_ALIGN_AFTER_BARRIER_MAX_SKIP
+#define LABEL_ALIGN_AFTER_BARRIER_MAX_SKIP (align_jumps-1)
+#endif
+
+#ifndef ADDR_VEC_ALIGN
+int
+final_addr_vec_align (addr_vec)
+ rtx addr_vec;
+{
+ int align = exact_log2 (GET_MODE_SIZE (GET_MODE (PATTERN (addr_vec))));
+
+ if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+ align = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ return align;
+
+}
+#define ADDR_VEC_ALIGN(ADDR_VEC) final_addr_vec_align (ADDR_VEC)
+#endif
+
+#ifndef INSN_LENGTH_ALIGNMENT
+#define INSN_LENGTH_ALIGNMENT(INSN) length_unit_log
+#endif
+
+#define INSN_SHUID(INSN) (uid_shuid[INSN_UID (INSN)])
+
+static int min_labelno, max_labelno;
+
+#define LABEL_TO_ALIGNMENT(LABEL) \
+ (label_align[CODE_LABEL_NUMBER (LABEL) - min_labelno].alignment)
+
+#define LABEL_TO_MAX_SKIP(LABEL) \
+ (label_align[CODE_LABEL_NUMBER (LABEL) - min_labelno].max_skip)
+
+/* For the benefit of port specific code do this also as a function. */
+int
+label_to_alignment (label)
+ rtx label;
+{
+ return LABEL_TO_ALIGNMENT (label);
+}
+
+#ifdef HAVE_ATTR_length
+/* The differences in addresses
+ between a branch and its target might grow or shrink depending on
+ the alignment the start insn of the range (the branch for a forward
+ branch or the label for a backward branch) starts out on; if these
+ differences are used naively, they can even oscillate infinitely.
+ We therefore want to compute a 'worst case' address difference that
+ is independent of the alignment the start insn of the range end
+ up on, and that is at least as large as the actual difference.
+ The function align_fuzz calculates the amount we have to add to the
+ naively computed difference, by traversing the part of the alignment
+ chain of the start insn of the range that is in front of the end insn
+ of the range, and considering for each alignment the maximum amount
+ that it might contribute to a size increase.
+
+ For casesi tables, we also want to know worst case minimum amounts of
+ address difference, in case a machine description wants to introduce
+ some common offset that is added to all offsets in a table.
+ For this purpose, align_fuzz with a growth argument of 0 comuptes the
+ appropriate adjustment. */
+
+
+/* Compute the maximum delta by which the difference of the addresses of
+ START and END might grow / shrink due to a different address for start
+ which changes the size of alignment insns between START and END.
+ KNOWN_ALIGN_LOG is the alignment known for START.
+ GROWTH should be ~0 if the objective is to compute potential code size
+ increase, and 0 if the objective is to compute potential shrink.
+ The return value is undefined for any other value of GROWTH. */
+int
+align_fuzz (start, end, known_align_log, growth)
+ rtx start, end;
+ int known_align_log;
+ unsigned growth;
+{
+ int uid = INSN_UID (start);
+ rtx align_label;
+ int known_align = 1 << known_align_log;
+ int end_shuid = INSN_SHUID (end);
+ int fuzz = 0;
+
+ for (align_label = uid_align[uid]; align_label; align_label = uid_align[uid])
+ {
+ int align_addr, new_align;
+
+ uid = INSN_UID (align_label);
+ align_addr = insn_addresses[uid] - insn_lengths[uid];
+ if (uid_shuid[uid] > end_shuid)
+ break;
+ known_align_log = LABEL_TO_ALIGNMENT (align_label);
+ new_align = 1 << known_align_log;
+ if (new_align < known_align)
+ continue;
+ fuzz += (-align_addr ^ growth) & (new_align - known_align);
+ known_align = new_align;
+ }
+ return fuzz;
+}
+
+/* Compute a worst-case reference address of a branch so that it
+ can be safely used in the presence of aligned labels. Since the
+ size of the branch itself is unknown, the size of the branch is
+ not included in the range. I.e. for a forward branch, the reference
+ address is the end address of the branch as known from the previous
+ branch shortening pass, minus a value to account for possible size
+ increase due to alignment. For a backward branch, it is the start
+ address of the branch as known from the current pass, plus a value
+ to account for possible size increase due to alignment.
+ NB.: Therefore, the maximum offset allowed for backward branches needs
+ to exclude the branch size. */
+int
+insn_current_reference_address (branch)
+ rtx branch;
+{
+ rtx dest;
+ rtx seq = NEXT_INSN (PREV_INSN (branch));
+ int seq_uid = INSN_UID (seq);
+ if (GET_CODE (branch) != JUMP_INSN)
+ /* This can happen for example on the PA; the objective is to know the
+ offset to address something in front of the start of the function.
+ Thus, we can treat it like a backward branch.
+ We assume here that FUNCTION_BOUNDARY / BITS_PER_UNIT is larger than
+ any alignment we'd encounter, so we skip the call to align_fuzz. */
+ return insn_current_address;
+ dest = JUMP_LABEL (branch);
+ /* BRANCH has no proper alignment chain set, so use SEQ. */
+ if (INSN_SHUID (branch) < INSN_SHUID (dest))
+ {
+ /* Forward branch. */
+ return (insn_last_address + insn_lengths[seq_uid]
+ - align_fuzz (seq, dest, length_unit_log, ~0));
+ }
+ else
+ {
+ /* Backward branch. */
+ return (insn_current_address
+ + align_fuzz (dest, seq, length_unit_log, ~0));
+ }
+}
+#endif /* HAVE_ATTR_length */
+\f