This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH v3] [MIPS] GAS: Fix Loongson3 LLSC errata
- From: YunQiang Su <syq at debian dot org>
- To: binutils at sourceware dot org
- Cc: paul dot hua dot gm at gmail dot com
- Date: Sat, 5 Jan 2019 23:34:50 +0800
- Subject: [PATCH v3] [MIPS] GAS: Fix Loongson3 LLSC errata
From: Paul Hua <paul.hua.gm@gmail.com>
In some older Loongson 3A1000 processors there is a LL/SC errata that
can cause the CPU to deadlock occasionally. The details are very
complicated. We find a way to workaround this errata by
1) add a sync before ll/lld instruction.
2) if there is an branch instruction between ll/sc pair, insert a
sync at the target.
This patch also add a configure options
--enable-mips-fix-loongson3-llsc=[yes|no] to enable fix-loongson3-llsc
by config.
v1 -> v2:
Regenerate configure file with the same version fo autotools,
to make the patch shorter.
v2 -> v3:
Insert a sync at the target of the branch between ll/sc pair.
gas/
* NEWS: Mention -m[no-]fix-loongson3-llsc.
* configure.ac: Add --enable-mips-fix-loongson3-llsc.
Define DEFAULT_MIPS_FIX_LOONGSON3_LLSC.
* config.in: Regenerated.
* configure: Likewise.
* config/tc-mips.c (sync_insn, mips_fix_loongson3_llsc):
New variables.
(options): New OPTION_FIX_LOONGSON3_LLSC,
OPTION_NO_FIX_LOONGSON3_LLSC.
(md_longopts): Add -m[no-]fix-loongson3-llsc.
(md_begin): Initialize sync insn.
(fix_loongson3_llsc): New.
(append_insn): Call fix_loongson3_llsc.
(md_parse_option): Handle OPTION_FIX_LOONGSON3_LLSC,
OPTION_NO_FIX_LOONGSON3_LLSC.
(md_show_usage): Display -m[no-]fix-loongson3-llsc.
* doc/c-mips.texi: Document -m[no-]fix-loongson3-llsc,
--enable-mips-fix-loongson3-llsc=[yes|no].
---
gas/NEWS | 4 ++
gas/config.in | 3 ++
gas/config/tc-mips.c | 123 ++++++++++++++++++++++++++++++++++++++++++-
gas/configure | 23 ++++++++
gas/configure.ac | 18 +++++++
gas/doc/c-mips.texi | 7 +++
6 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/gas/NEWS b/gas/NEWS
index d3a1e5ed..a4cefe1d 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,4 +1,8 @@
-*- text -*-
+* For MIPS, Add -m[no-]fix-loongson3-llsc option to fix (or not) Loongson3 LLSC
+ Errata. Add a --enable-mips-fix-loongson3-llsc=[yes|no] configure time option
+ to set the default behavior. Set the default if the configure option is not used
+ to "no".
* Add -mvexwig=[0|1] option to x86 assembler to control encoding of
VEX.W-ignored (WIG) VEX instructions.
diff --git a/gas/config.in b/gas/config.in
index 9c15a070..8a8a4c05 100644
--- a/gas/config.in
+++ b/gas/config.in
@@ -50,6 +50,9 @@
/* Define to 1 if you want to generate x86 relax relocations by default. */
#undef DEFAULT_GENERATE_X86_RELAX_RELOCATIONS
+/* Define to 1 if you want to fix Loongson3 LLSC Errata by default. */
+#undef DEFAULT_MIPS_FIX_LOONGSON3_LLSC
+
/* Define to 1 if you want to generate GNU x86 used ISA and feature properties
by default. */
#undef DEFAULT_X86_USED_NOTE
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index ae559042..682594d7 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -141,6 +141,12 @@ struct mips_cl_insn
extension. */
unsigned long insn_opcode;
+ /* The name if this is an label */
+ char label[16];
+
+ /* The target label name if this is an branch */
+ char target[16];
+
/* The frag that contains the instruction. */
struct frag *frag;
@@ -786,12 +792,15 @@ static int mips_debug = 0;
/* The maximum number of NOPs needed for any purpose. */
#define MAX_NOPS 4
+/* The maximum range of context length of ll/sc */
+#define MAX_LLSC_RANGE 20
+
/* A list of previous instructions, with index 0 being the most recent.
We need to look back MAX_NOPS instructions when filling delay slots
or working around processor errata. We need to look back one
instruction further if we're thinking about using history[0] to
fill a branch delay slot. */
-static struct mips_cl_insn history[1 + MAX_NOPS];
+static struct mips_cl_insn history[1 + MAX_NOPS + MAX_LLSC_RANGE];
/* Arrays of operands for each instruction. */
#define MAX_OPERANDS 6
@@ -808,6 +817,9 @@ static struct mips_cl_insn mips16_nop_insn;
static struct mips_cl_insn micromips_nop16_insn;
static struct mips_cl_insn micromips_nop32_insn;
+/* Sync instructions used by insert sync. */
+static struct mips_cl_insn sync_insn;
+
/* The appropriate nop for the current mode. */
#define NOP_INSN (mips_opts.mips16 \
? &mips16_nop_insn \
@@ -943,6 +955,9 @@ static bfd_boolean mips_fix_cn63xxp1;
static bfd_boolean mips_fix_r5900;
static bfd_boolean mips_fix_r5900_explicit;
+/* ...likewise -mfix-loongson3-llsc. */
+static bfd_boolean mips_fix_loongson3_llsc = DEFAULT_MIPS_FIX_LOONGSON3_LLSC;
+
/* We don't relax branches by default, since this causes us to expand
`la .l2 - .l1' if there's a branch between .l1 and .l2, because we
fail to compute the offset before expanding the macro to the most
@@ -1482,6 +1497,8 @@ enum options
OPTION_NO_FIX_24K,
OPTION_FIX_RM7000,
OPTION_NO_FIX_RM7000,
+ OPTION_FIX_LOONGSON3_LLSC,
+ OPTION_NO_FIX_LOONGSON3_LLSC,
OPTION_FIX_LOONGSON2F_JUMP,
OPTION_NO_FIX_LOONGSON2F_JUMP,
OPTION_FIX_LOONGSON2F_NOP,
@@ -1628,6 +1645,8 @@ struct option md_longopts[] =
{"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
{"no-fix-7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
{"mno-fix7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
+ {"mfix-loongson3-llsc", no_argument, NULL, OPTION_FIX_LOONGSON3_LLSC},
+ {"mno-fix-loongson3-llsc", no_argument, NULL, OPTION_NO_FIX_LOONGSON3_LLSC},
{"mfix-loongson2f-jump", no_argument, NULL, OPTION_FIX_LOONGSON2F_JUMP},
{"mno-fix-loongson2f-jump", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_JUMP},
{"mfix-loongson2f-nop", no_argument, NULL, OPTION_FIX_LOONGSON2F_NOP},
@@ -3678,6 +3697,10 @@ md_begin (void)
nop_insn.insn_opcode = LOONGSON2F_NOP_INSN;
nop_insn.fixed_p = 1;
}
+ if (sync_insn.insn_mo == NULL && strcmp (name, "sync") == 0)
+ {
+ create_insn (&sync_insn, mips_opcodes + i);
+ }
++i;
}
while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name));
@@ -6859,6 +6882,76 @@ fix_loongson2f (struct mips_cl_insn * ip)
fix_loongson2f_jump (ip);
}
+/* Fix loongson3 llsc errata: Insert sync before ll/lld. */
+static void
+fix_loongson3_llsc (struct mips_cl_insn * ip)
+{
+ gas_assert (!HAVE_CODE_COMPRESSION);
+
+ /* if is an local label and the insn is not sync,
+ * look forward that whether an branch between ll/sc jump to here
+ * if so, insert a sync
+ * */
+ if(seg_info (now_seg)->label_list
+ && 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);
+ for(unsigned long i=0; i<lookback; i++){
+ if(streq (history[i].insn_mo->name, "ll") || streq (history[i].insn_mo->name, "lld"))
+ break;
+ if(streq (history[i].insn_mo->name, "sc") || streq (history[i].insn_mo->name, "scd")){
+ unsigned long j;
+ for(j=i+1; j<lookback; j++){
+ if(streq (history[i].insn_mo->name, "ll") || streq (history[i].insn_mo->name, "lld"))
+ break;
+ if(delayed_branch_p(&history[j])){
+ if(streq(history[j].target, label_name)){
+ add_fixed_insn (&sync_insn);
+ insert_into_history (0, 1, &sync_insn);
+ i = lookback;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* 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")){
+ unsigned long lookback = ARRAY_SIZE(history)-1;
+ for(unsigned long i=0; i<lookback; i++){
+ if(streq (history[i].insn_mo->name, "ll") || streq (history[i].insn_mo->name, "lld"))
+ break;
+ if(delayed_branch_p(&history[i])){
+ unsigned long j;
+ for(j=i+1; j<lookback; j++){
+ if(streq (history[j].insn_mo->name, "ll") || streq (history[i].insn_mo->name, "lld"))
+ break;
+ }
+ for(; j<lookback; j++){
+ if(history[j].label[0] != '\0'
+ && streq(history[j].label, history[i].target)
+ && strcmp(history[j+1].insn_mo->name, "sync")!=0){
+ add_fixed_insn (&sync_insn);
+ insert_into_history (++j, 1, &sync_insn);
+ }
+ }
+ }
+ }
+ }
+
+ /* Skip if there is a sync before ll/lld. */
+ if ((strcmp (ip->insn_mo->name, "ll") == 0
+ || strcmp (ip->insn_mo->name, "lld") == 0)
+ && (strcmp (history[0].insn_mo->name, "sync") != 0))
+ {
+ add_fixed_insn (&sync_insn);
+ insert_into_history (0, 1, &sync_insn);
+ }
+}
+
/* IP is a branch that has a delay slot, and we need to fill it
automatically. Return true if we can do that by swapping IP
with the previous instruction.
@@ -7316,6 +7409,15 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
fix_loongson2f (ip);
+ ip->target[0] = '\0';
+ if (offset_expr.X_op == O_symbol)
+ strncpy(ip->target, S_GET_NAME(offset_expr.X_add_symbol), 15);
+ ip->label[0] = '\0';
+ if (seg_info (now_seg)->label_list)
+ strncpy(ip->label, S_GET_NAME(seg_info (now_seg)->label_list->label), 15);
+ if (mips_fix_loongson3_llsc && !HAVE_CODE_COMPRESSION)
+ fix_loongson3_llsc (ip);
+
file_ase_mips16 |= mips_opts.mips16;
file_ase_micromips |= mips_opts.micromips;
@@ -14731,6 +14833,14 @@ md_parse_option (int c, const char *arg)
mips_fix_rm7000 = 0;
break;
+ case OPTION_FIX_LOONGSON3_LLSC:
+ mips_fix_loongson3_llsc = TRUE;
+ break;
+
+ case OPTION_NO_FIX_LOONGSON3_LLSC:
+ mips_fix_loongson3_llsc = FALSE;
+ break;
+
case OPTION_FIX_LOONGSON2F_JUMP:
mips_fix_loongson2f_jump = TRUE;
break;
@@ -20140,9 +20250,20 @@ MIPS options:\n\
fprintf (stream, _("\
-minsn32 only generate 32-bit microMIPS instructions\n\
-mno-insn32 generate all microMIPS instructions\n"));
+#if DEFAULT_MIPS_FIX_LOONGSON3_LLSC
+ fprintf (stream, _("\
+-mfix-loongson3-llsc work around Loongson3 LL/SC errata, default\n\
+-mno-fix-loongson3-llsc disable work around Loongson3 LL/SC errata\n"));
+#else
+ fprintf (stream, _("\
+-mfix-loongson3-llsc work around Loongson3 LL/SC errata\n\
+-mno-fix-loongson3-llsc disable work around Loongson3 LL/SC errata, default\n"));
+#endif
fprintf (stream, _("\
-mfix-loongson2f-jump work around Loongson2F JUMP instructions\n\
-mfix-loongson2f-nop work around Loongson2F NOP errata\n\
+-mfix-loongson3-llsc work around Loongson3 LL/SC errata\n\
+-mno-fix-loongson3-llsc disable work around Loongson3 LL/SC errata\n\
-mfix-vr4120 work around certain VR4120 errata\n\
-mfix-vr4130 work around VR4130 mflo/mfhi errata\n\
-mfix-24k insert a nop after ERET and DERET instructions\n\
diff --git a/gas/configure b/gas/configure
index 27b6e8e8..13732153 100755
--- a/gas/configure
+++ b/gas/configure
@@ -809,6 +809,7 @@ enable_x86_relax_relocations
enable_elf_stt_common
enable_generate_build_notes
enable_x86_used_note
+enable_mips_fix_loongson3_llsc
enable_werror
enable_build_warnings
with_cpu
@@ -1471,6 +1472,8 @@ Optional Features:
generate GNU Build notes if none are provided by the
input
--enable-x86-used-note generate GNU x86 used ISA and feature properties
+ --enable-mips-fix-loongson3-llsc
+ enable MIPS fix Loongson3 LLSC errata
--enable-werror treat compile warnings as errors
--enable-build-warnings enable build-time compiler warnings
--disable-nls do not use Native Language Support
@@ -12125,6 +12128,17 @@ if test "${enable_x86_used_note+set}" = set; then :
esac
fi
+# Decide if the MIPS assembler should default to enable MIPS fix Loongson3
+# LLSC errata.
+ac_default_mips_fix_loongson3_llsc=unset
+# Provide a configuration option to override the default.
+# Check whether --enable-mips-fix-loongson3-llsc was given.
+if test "${enable_mips_fix_loongson3_llsc+set}" = set; then :
+ enableval=$enable_mips_fix_loongson3_llsc; case "${enableval}" in
+ yes) ac_default_mips_fix_loongson3_llsc=1 ;;
+ no) ac_default_mips_fix_loongson3_llsc=0 ;;
+esac
+fi
using_cgen=no
@@ -13102,6 +13116,15 @@ cat >>confdefs.h <<_ACEOF
_ACEOF
+if test ${ac_default_mips_fix_loongson3_llsc} = unset; then
+ ac_default_mips_fix_loongson3_llsc=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_MIPS_FIX_LOONGSON3_LLSC $ac_default_mips_fix_loongson3_llsc
+_ACEOF
+
+
if test x$ac_default_compressed_debug_sections = xyes ; then
$as_echo "#define DEFAULT_FLAG_COMPRESS_DEBUG 1" >>confdefs.h
diff --git a/gas/configure.ac b/gas/configure.ac
index 2fe9f786..55d48d4a 100644
--- a/gas/configure.ac
+++ b/gas/configure.ac
@@ -125,6 +125,17 @@ AC_ARG_ENABLE(x86-used-note,
no) ac_default_generate_x86_used_note=0 ;;
esac])dnl
+# Decide if the MIPS assembler should default to enable MIPS fix Loongson3
+# LLSC errata.
+ac_default_mips_fix_loongson3_llsc=unset
+# Provide a configuration option to override the default.
+AC_ARG_ENABLE(mips-fix-loongson3-llsc,
+ AS_HELP_STRING([--enable-mips-fix-loongson3-llsc],
+ [enable MIPS fix Loongson3 LLSC errata]),
+[case "${enableval}" in
+ yes) ac_default_mips_fix_loongson3_llsc=1 ;;
+ no) ac_default_mips_fix_loongson3_llsc=0 ;;
+esac])dnl
using_cgen=no
@@ -663,6 +674,13 @@ AC_DEFINE_UNQUOTED(DEFAULT_X86_USED_NOTE,
[Define to 1 if you want to generate GNU x86 used ISA and feature
properties by default.])
+if test ${ac_default_mips_fix_loongson3_llsc} = unset; then
+ ac_default_mips_fix_loongson3_llsc=0
+fi
+AC_DEFINE_UNQUOTED(DEFAULT_MIPS_FIX_LOONGSON3_LLSC,
+ $ac_default_mips_fix_loongson3_llsc,
+ [Define to 1 if you want to fix Loongson3 LLSC Errata by default.])
+
if test x$ac_default_compressed_debug_sections = xyes ; then
AC_DEFINE(DEFAULT_FLAG_COMPRESS_DEBUG, 1, [Define if you want compressed debug sections by default.])
fi
diff --git a/gas/doc/c-mips.texi b/gas/doc/c-mips.texi
index 81228030..1ef289a0 100644
--- a/gas/doc/c-mips.texi
+++ b/gas/doc/c-mips.texi
@@ -308,6 +308,13 @@ Replace nops by @code{or at,at,zero} to work around the Loongson2F
deadlock. The issue has been solved in later Loongson2F batches, but
this fix has no side effect to them.
+@item -mfix-loongson3-llsc
+@itemx -mno-fix-loongson3-llsc
+Insert @samp{sync} before @samp{ll} and @samp{lld} to work around
+Loongson3 LLSC errata. Without it, under extrame cases, the CPU might
+deadlock. The default can be controlled by the
+@option{--enable-mips-fix-loongson3-llsc=[yes|no]} configure option.
+
@item -mfix-vr4120
@itemx -mno-fix-vr4120
Insert nops to work around certain VR4120 errata. This option is
--
2.20.1