aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
Diffstat (limited to 'gas')
-rw-r--r--gas/ChangeLog7
-rw-r--r--gas/config/tc-mips.c49
2 files changed, 51 insertions, 5 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 601fb50..d146fe3 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,10 @@
+2020-02-28 YunQiang Su <syq@debian.org>
+
+ PR gas/25539
+ * config/tc-mips.c (fix_loongson3_llsc): Compare label value
+ to handle multi-labels.
+ (has_label_name): New.
+
2020-02-26 Matthew Malcomson <matthew.malcomson@arm.com>
* config/tc-arm.c (enum pred_instruction_type): Remove
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 6244d8a..a00c69b 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -812,6 +812,9 @@ static int mips_debug = 0;
fill a branch delay slot. */
static struct mips_cl_insn history[1 + MAX_NOPS + MAX_LLSC_RANGE];
+/* The maximum number of LABELS detect for the same address. */
+#define MAX_LABELS_SAME 10
+
/* Arrays of operands for each instruction. */
#define MAX_OPERANDS 6
struct mips_operand_array
@@ -6901,7 +6904,21 @@ fix_loongson2f (struct mips_cl_insn * ip)
fix_loongson2f_jump (ip);
}
-/* Fix loongson3 llsc errata: Insert sync before ll/lld. */
+static bfd_boolean
+has_label_name (const char *arr[], size_t len ,const char *s)
+{
+ unsigned long i;
+ for (i = 0; i < len; i++)
+ {
+ if (!arr[i])
+ return FALSE;
+ if (streq (arr[i], s))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Fix loongson3 llsc errata: Insert sync before ll/lld. */
static void
fix_loongson3_llsc (struct mips_cl_insn * ip)
@@ -6915,10 +6932,30 @@ fix_loongson3_llsc (struct mips_cl_insn * ip)
&& S_IS_LOCAL (seg_info (now_seg)->label_list->label)
&& (strcmp (ip->insn_mo->name, "sync") != 0))
{
- const char *label_name = S_GET_NAME (seg_info (now_seg)->label_list->label);
- unsigned long lookback = ARRAY_SIZE (history);
unsigned long i;
+ valueT label_value;
+ const char *label_names[MAX_LABELS_SAME];
+ const char *label_name;
+
+ label_name = S_GET_NAME (seg_info (now_seg)->label_list->label);
+ label_names[0] = label_name;
+ struct insn_label_list *llist = seg_info (now_seg)->label_list;
+ label_value = S_GET_VALUE (llist->label);
+ for (i = 1; i < MAX_LABELS_SAME; i++)
+ {
+ llist = llist->next;
+ if (!llist)
+ break;
+ if (S_GET_VALUE (llist->label) == label_value)
+ label_names[i] = S_GET_NAME (llist->label);
+ else
+ break;
+ }
+ for (; i < MAX_LABELS_SAME; i++)
+ label_names[i] = NULL;
+
+ unsigned long lookback = ARRAY_SIZE (history);
for (i = 0; i < lookback; i++)
{
if (streq (history[i].insn_mo->name, "ll")
@@ -6938,7 +6975,9 @@ fix_loongson3_llsc (struct mips_cl_insn * ip)
if (delayed_branch_p (&history[j]))
{
- if (streq (history[j].target, label_name))
+ if (has_label_name (label_names,
+ MAX_LABELS_SAME,
+ history[j].target))
{
add_fixed_insn (&sync_insn);
insert_into_history (0, 1, &sync_insn);
@@ -6952,7 +6991,7 @@ fix_loongson3_llsc (struct mips_cl_insn * ip)
}
/* If we find a sc, we look forward to look for an branch insn,
and see whether it jump back and out of ll/sc. */
- else if (streq(ip->insn_mo->name, "sc") || streq(ip->insn_mo->name, "scd"))
+ else if (streq (ip->insn_mo->name, "sc") || streq (ip->insn_mo->name, "scd"))
{
unsigned long lookback = ARRAY_SIZE (history) - 1;
unsigned long i;