diff mbox series

[11/20] target/loongarch: Add fixed point atomic instruction translation

Message ID 1624881885-31692-12-git-send-email-gaosong@loongson.cn (mailing list archive)
State New, archived
Headers show
Series Add LoongArch linux-user emulation support | expand

Commit Message

gaosong June 28, 2021, 12:04 p.m. UTC
This patch implement fixed point atomic instruction translation.

This includes:
- LL.{W/D}, SC.{W/D}
- AM{SWAP/ADD/AND/OR/XOR/MAX/MIN}[_DB].{W/D}
- AM{MAX/MIN}[_DB].{WU/DU}

Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/insns.decode |  44 ++++++++++++
 target/loongarch/instmap.h    |   2 +
 target/loongarch/trans.inc.c  | 155 ++++++++++++++++++++++++++++++++++++++++++
 target/loongarch/translate.c  |  54 +++++++++++++++
 4 files changed, 255 insertions(+)
diff mbox series

Patch

diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode
index c5c08c8..beb2e4a 100644
--- a/target/loongarch/insns.decode
+++ b/target/loongarch/insns.decode
@@ -219,3 +219,47 @@  stle_b           0011 10000111 11100 ..... ..... .....    @fmt_rdrjrk
 stle_h           0011 10000111 11101 ..... ..... .....    @fmt_rdrjrk
 stle_w           0011 10000111 11110 ..... ..... .....    @fmt_rdrjrk
 stle_d           0011 10000111 11111 ..... ..... .....    @fmt_rdrjrk
+
+#
+# Fixed point atomic instruction
+#
+ll_w             0010 0000 .............. ..... .....     @fmt_rdrjsi14
+sc_w             0010 0001 .............. ..... .....     @fmt_rdrjsi14
+ll_d             0010 0010 .............. ..... .....     @fmt_rdrjsi14
+sc_d             0010 0011 .............. ..... .....     @fmt_rdrjsi14
+amswap_w         0011 10000110 00000 ..... ..... .....    @fmt_rdrjrk
+amswap_d         0011 10000110 00001 ..... ..... .....    @fmt_rdrjrk
+amadd_w          0011 10000110 00010 ..... ..... .....    @fmt_rdrjrk
+amadd_d          0011 10000110 00011 ..... ..... .....    @fmt_rdrjrk
+amand_w          0011 10000110 00100 ..... ..... .....    @fmt_rdrjrk
+amand_d          0011 10000110 00101 ..... ..... .....    @fmt_rdrjrk
+amor_w           0011 10000110 00110 ..... ..... .....    @fmt_rdrjrk
+amor_d           0011 10000110 00111 ..... ..... .....    @fmt_rdrjrk
+amxor_w          0011 10000110 01000 ..... ..... .....    @fmt_rdrjrk
+amxor_d          0011 10000110 01001 ..... ..... .....    @fmt_rdrjrk
+ammax_w          0011 10000110 01010 ..... ..... .....    @fmt_rdrjrk
+ammax_d          0011 10000110 01011 ..... ..... .....    @fmt_rdrjrk
+ammin_w          0011 10000110 01100 ..... ..... .....    @fmt_rdrjrk
+ammin_d          0011 10000110 01101 ..... ..... .....    @fmt_rdrjrk
+ammax_wu         0011 10000110 01110 ..... ..... .....    @fmt_rdrjrk
+ammax_du         0011 10000110 01111 ..... ..... .....    @fmt_rdrjrk
+ammin_wu         0011 10000110 10000 ..... ..... .....    @fmt_rdrjrk
+ammin_du         0011 10000110 10001 ..... ..... .....    @fmt_rdrjrk
+amswap_db_w      0011 10000110 10010 ..... ..... .....    @fmt_rdrjrk
+amswap_db_d      0011 10000110 10011 ..... ..... .....    @fmt_rdrjrk
+amadd_db_w       0011 10000110 10100 ..... ..... .....    @fmt_rdrjrk
+amadd_db_d       0011 10000110 10101 ..... ..... .....    @fmt_rdrjrk
+amand_db_w       0011 10000110 10110 ..... ..... .....    @fmt_rdrjrk
+amand_db_d       0011 10000110 10111 ..... ..... .....    @fmt_rdrjrk
+amor_db_w        0011 10000110 11000 ..... ..... .....    @fmt_rdrjrk
+amor_db_d        0011 10000110 11001 ..... ..... .....    @fmt_rdrjrk
+amxor_db_w       0011 10000110 11010 ..... ..... .....    @fmt_rdrjrk
+amxor_db_d       0011 10000110 11011 ..... ..... .....    @fmt_rdrjrk
+ammax_db_w       0011 10000110 11100 ..... ..... .....    @fmt_rdrjrk
+ammax_db_d       0011 10000110 11101 ..... ..... .....    @fmt_rdrjrk
+ammin_db_w       0011 10000110 11110 ..... ..... .....    @fmt_rdrjrk
+ammin_db_d       0011 10000110 11111 ..... ..... .....    @fmt_rdrjrk
+ammax_db_wu      0011 10000111 00000 ..... ..... .....    @fmt_rdrjrk
+ammax_db_du      0011 10000111 00001 ..... ..... .....    @fmt_rdrjrk
+ammin_db_wu      0011 10000111 00010 ..... ..... .....    @fmt_rdrjrk
+ammin_db_du      0011 10000111 00011 ..... ..... .....    @fmt_rdrjrk
diff --git a/target/loongarch/instmap.h b/target/loongarch/instmap.h
index b6ddb7d..ea5396e 100644
--- a/target/loongarch/instmap.h
+++ b/target/loongarch/instmap.h
@@ -92,6 +92,8 @@  enum {
     LA_OPC_LD_HU     = (0x0A9 << 22),
     LA_OPC_LD_WU     = (0x0AA << 22),
 
+    LA_OPC_LL_W      = (0x20 << 24),
+    LA_OPC_LL_D      = (0x22 << 24),
     LA_OPC_LDPTR_W   = (0x24 << 24),
     LA_OPC_STPTR_W   = (0x25 << 24),
     LA_OPC_LDPTR_D   = (0x26 << 24),
diff --git a/target/loongarch/trans.inc.c b/target/loongarch/trans.inc.c
index 9880a7f..3f1e64f 100644
--- a/target/loongarch/trans.inc.c
+++ b/target/loongarch/trans.inc.c
@@ -1178,3 +1178,158 @@  static bool trans_stle_d(DisasContext *ctx, arg_stle_d *a)
 }
 
 #undef DECL_ARG
+
+/* Fixed point atomic instruction translation */
+static bool trans_ll_w(DisasContext *ctx, arg_ll_w *a)
+{
+    gen_loongarch_ld(ctx, LA_OPC_LL_W, a->rd, a->rj, a->si14 << 2);
+    return true;
+}
+
+static bool trans_sc_w(DisasContext *ctx, arg_sc_w *a)
+{
+    gen_loongarch_st_cond(ctx, a->rd, a->rj, a->si14 << 2, MO_TESL, false);
+    return true;
+}
+
+static bool trans_ll_d(DisasContext *ctx, arg_ll_d *a)
+{
+    gen_loongarch_ld(ctx, LA_OPC_LL_D, a->rd, a->rj, a->si14 << 2);
+    return true;
+}
+
+static bool trans_sc_d(DisasContext *ctx, arg_sc_d *a)
+{
+    gen_loongarch_st_cond(ctx, a->rd, a->rj, a->si14 << 2, MO_TEQ, false);
+    return true;
+}
+
+#define TRANS_AM_W(name, op)                                      \
+static bool trans_ ## name(DisasContext *ctx, arg_ ## name * a)   \
+{                                                                 \
+    if ((a->rd != 0) && ((a->rj == a->rd) || (a->rk == a->rd))) { \
+        printf("%s: warning, register equal\n", __func__);        \
+        return false;                                             \
+    }                                                             \
+    int mem_idx = ctx->mem_idx;                                   \
+    TCGv addr = tcg_temp_new();                                   \
+    TCGv val = tcg_temp_new();                                    \
+    TCGv ret = tcg_temp_new();                                    \
+                                                                  \
+    gen_load_gpr(addr, a->rj);                                    \
+    gen_load_gpr(val, a->rk);                                     \
+    tcg_gen_atomic_##op##_tl(ret, addr, val, mem_idx, MO_TESL |   \
+                            ctx->default_tcg_memop_mask);         \
+    gen_store_gpr(ret, a->rd);                                    \
+                                                                  \
+    tcg_temp_free(addr);                                          \
+    tcg_temp_free(val);                                           \
+    tcg_temp_free(ret);                                           \
+    return true;                                                  \
+}
+#define TRANS_AM_D(name, op)                                      \
+static bool trans_ ## name(DisasContext *ctx, arg_ ## name * a)   \
+{                                                                 \
+    if ((a->rd != 0) && ((a->rj == a->rd) || (a->rk == a->rd))) { \
+        printf("%s: warning, register equal\n", __func__);        \
+        return false;                                             \
+    }                                                             \
+    int mem_idx = ctx->mem_idx;                                   \
+    TCGv addr = tcg_temp_new();                                   \
+    TCGv val = tcg_temp_new();                                    \
+    TCGv ret = tcg_temp_new();                                    \
+                                                                  \
+    gen_load_gpr(addr, a->rj);                                    \
+    gen_load_gpr(val, a->rk);                                     \
+    tcg_gen_atomic_##op##_tl(ret, addr, val, mem_idx, MO_TEQ |    \
+                            ctx->default_tcg_memop_mask);         \
+    gen_store_gpr(ret, a->rd);                                    \
+                                                                  \
+    tcg_temp_free(addr);                                          \
+    tcg_temp_free(val);                                           \
+    tcg_temp_free(ret);                                           \
+    return true;                                                  \
+}
+#define TRANS_AM(name, op)   \
+    TRANS_AM_W(name##_w, op) \
+    TRANS_AM_D(name##_d, op)
+TRANS_AM(amswap, xchg)      /* trans_amswap_w, trans_amswap_d */
+TRANS_AM(amadd, fetch_add)  /* trans_amadd_w, trans_amadd_d   */
+TRANS_AM(amand, fetch_and)  /* trans_amand_w, trans_amand_d   */
+TRANS_AM(amor, fetch_or)    /* trans_amor_w, trans_amor_d     */
+TRANS_AM(amxor, fetch_xor)  /* trans_amxor_w, trans_amxor_d   */
+TRANS_AM(ammax, fetch_smax) /* trans_ammax_w, trans_ammax_d   */
+TRANS_AM(ammin, fetch_smin) /* trans_ammin_w, trans_ammin_d   */
+TRANS_AM_W(ammax_wu, fetch_umax)    /* trans_ammax_wu */
+TRANS_AM_D(ammax_du, fetch_umax)    /* trans_ammax_du */
+TRANS_AM_W(ammin_wu, fetch_umin)    /* trans_ammin_wu */
+TRANS_AM_D(ammin_du, fetch_umin)    /* trans_ammin_du */
+#undef TRANS_AM
+#undef TRANS_AM_W
+#undef TRANS_AM_D
+
+#define TRANS_AM_DB_W(name, op)                                   \
+static bool trans_ ## name(DisasContext *ctx, arg_ ## name * a)   \
+{                                                                 \
+    if ((a->rd != 0) && ((a->rj == a->rd) || (a->rk == a->rd))) { \
+        printf("%s: warning, register equal\n", __func__);        \
+        return false;                                             \
+    }                                                             \
+    int mem_idx = ctx->mem_idx;                                   \
+    TCGv addr = tcg_temp_new();                                   \
+    TCGv val = tcg_temp_new();                                    \
+    TCGv ret = tcg_temp_new();                                    \
+                                                                  \
+    gen_loongarch_sync(0x10);                                     \
+    gen_load_gpr(addr, a->rj);                                    \
+    gen_load_gpr(val, a->rk);                                     \
+    tcg_gen_atomic_##op##_tl(ret, addr, val, mem_idx, MO_TESL |   \
+                            ctx->default_tcg_memop_mask);         \
+    gen_store_gpr(ret, a->rd);                                    \
+                                                                  \
+    tcg_temp_free(addr);                                          \
+    tcg_temp_free(val);                                           \
+    tcg_temp_free(ret);                                           \
+    return true;                                                  \
+}
+#define TRANS_AM_DB_D(name, op)                                   \
+static bool trans_ ## name(DisasContext *ctx, arg_ ## name * a)   \
+{                                                                 \
+    if ((a->rd != 0) && ((a->rj == a->rd) || (a->rk == a->rd))) { \
+        printf("%s: warning, register equal\n", __func__);        \
+        return false;                                             \
+    }                                                             \
+    int mem_idx = ctx->mem_idx;                                   \
+    TCGv addr = tcg_temp_new();                                   \
+    TCGv val = tcg_temp_new();                                    \
+    TCGv ret = tcg_temp_new();                                    \
+                                                                  \
+    gen_loongarch_sync(0x10);                                     \
+    gen_load_gpr(addr, a->rj);                                    \
+    gen_load_gpr(val, a->rk);                                     \
+    tcg_gen_atomic_##op##_tl(ret, addr, val, mem_idx, MO_TEQ |    \
+                            ctx->default_tcg_memop_mask);         \
+    gen_store_gpr(ret, a->rd);                                    \
+                                                                  \
+    tcg_temp_free(addr);                                          \
+    tcg_temp_free(val);                                           \
+    tcg_temp_free(ret);                                           \
+    return true;                                                  \
+}
+#define TRANS_AM_DB(name, op)      \
+    TRANS_AM_DB_W(name##_db_w, op) \
+    TRANS_AM_DB_D(name##_db_d, op)
+TRANS_AM_DB(amswap, xchg)      /* trans_amswap_db_w, trans_amswap_db_d */
+TRANS_AM_DB(amadd, fetch_add)  /* trans_amadd_db_w, trans_amadd_db_d   */
+TRANS_AM_DB(amand, fetch_and)  /* trans_amand_db_w, trans_amand_db_d   */
+TRANS_AM_DB(amor, fetch_or)    /* trans_amor_db_w, trans_amor_db_d     */
+TRANS_AM_DB(amxor, fetch_xor)  /* trans_amxor_db_w, trans_amxor_db_d   */
+TRANS_AM_DB(ammax, fetch_smax) /* trans_ammax_db_w, trans_ammax_db_d   */
+TRANS_AM_DB(ammin, fetch_smin) /* trans_ammin_db_w, trans_ammin_db_d   */
+TRANS_AM_DB_W(ammax_db_wu, fetch_umax)    /* trans_ammax_db_wu */
+TRANS_AM_DB_D(ammax_db_du, fetch_umax)    /* trans_ammax_db_du */
+TRANS_AM_DB_W(ammin_db_wu, fetch_umin)    /* trans_ammin_db_wu */
+TRANS_AM_DB_D(ammin_db_du, fetch_umin)    /* trans_ammin_db_du */
+#undef TRANS_AM_DB
+#undef TRANS_AM_DB_W
+#undef TRANS_AM_DB_D
diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c
index d9d2f1a..bf14507 100644
--- a/target/loongarch/translate.c
+++ b/target/loongarch/translate.c
@@ -1181,6 +1181,21 @@  fail:
     tcg_temp_free(t1);
 }
 
+#define OP_LD_ATOMIC(insn, fname)                                    \
+static inline void op_ld_##insn(TCGv ret, TCGv arg1, int mem_idx,    \
+                                DisasContext *ctx)                   \
+{                                                                    \
+    TCGv t0 = tcg_temp_new();                                        \
+    tcg_gen_mov_tl(t0, arg1);                                        \
+    tcg_gen_qemu_##fname(ret, arg1, ctx->mem_idx);                   \
+    tcg_gen_st_tl(t0, cpu_env, offsetof(CPULoongArchState, lladdr)); \
+    tcg_gen_st_tl(ret, cpu_env, offsetof(CPULoongArchState, llval)); \
+    tcg_temp_free(t0);                                               \
+}
+OP_LD_ATOMIC(ll, ld32s);
+OP_LD_ATOMIC(lld, ld64);
+#undef OP_LD_ATOMIC
+
 /* loongarch load */
 static void gen_loongarch_ld(DisasContext *ctx, uint32_t opc,
                              int rd, int base, int offset)
@@ -1226,6 +1241,14 @@  static void gen_loongarch_ld(DisasContext *ctx, uint32_t opc,
         tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_UB);
         gen_store_gpr(t0, rd);
         break;
+    case LA_OPC_LL_W:
+        op_ld_ll(t0, t0, mem_idx, ctx);
+        gen_store_gpr(t0, rd);
+        break;
+    case LA_OPC_LL_D:
+        op_ld_lld(t0, t0, mem_idx, ctx);
+        gen_store_gpr(t0, rd);
+        break;
     }
     tcg_temp_free(t0);
 }
@@ -1264,6 +1287,37 @@  static void gen_loongarch_st(DisasContext *ctx, uint32_t opc, int rd,
     tcg_temp_free(t1);
 }
 
+/* loongarch st cond */
+static void gen_loongarch_st_cond(DisasContext *ctx, int rd, int base,
+                                  int offset, MemOp tcg_mo, bool eva)
+{
+    TCGv t0 = tcg_temp_new();
+    TCGv addr = tcg_temp_new();
+    TCGv val = tcg_temp_new();
+    TCGLabel *l1 = gen_new_label();
+    TCGLabel *done = gen_new_label();
+
+    /* compare the address against that of the preceding LL */
+    gen_base_offset_addr(ctx, addr, base, offset);
+    tcg_gen_brcond_tl(TCG_COND_EQ, addr, cpu_lladdr, l1);
+    tcg_gen_movi_tl(t0, 0);
+    gen_store_gpr(t0, rd);
+    tcg_gen_br(done);
+
+    gen_set_label(l1);
+    /* generate cmpxchg */
+    gen_load_gpr(val, rd);
+    tcg_gen_atomic_cmpxchg_tl(t0, cpu_lladdr, cpu_llval, val,
+                              eva ? LOONGARCH_HFLAG_UM : ctx->mem_idx, tcg_mo);
+    tcg_gen_setcond_tl(TCG_COND_EQ, t0, t0, cpu_llval);
+    gen_store_gpr(t0, rd);
+
+    gen_set_label(done);
+    tcg_temp_free(t0);
+    tcg_temp_free(addr);
+    tcg_temp_free(val);
+}
+
 static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
 {
 }