1/*
2 * RISC-V translation routines for the RV64 Zacas Standard Extension.
3 *
4 * Copyright (c) 2020-2023 PLCT Lab
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2 or later, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#define REQUIRE_ZACAS(ctx) do {          \
20    if (!ctx->cfg_ptr->ext_zacas) {      \
21        return false;                     \
22    }                                     \
23} while (0)
24
25static bool trans_amocas_w(DisasContext *ctx, arg_amocas_w *a)
26{
27    REQUIRE_ZACAS(ctx);
28    return gen_cmpxchg(ctx, a, MO_ALIGN | MO_TESL);
29}
30
31static TCGv_i64 get_gpr_pair(DisasContext *ctx, int reg_num)
32{
33    TCGv_i64 t;
34
35    assert(get_ol(ctx) == MXL_RV32);
36
37    if (reg_num == 0) {
38        return tcg_constant_i64(0);
39    }
40
41    t = tcg_temp_new_i64();
42    tcg_gen_concat_tl_i64(t, cpu_gpr[reg_num], cpu_gpr[reg_num + 1]);
43    return t;
44}
45
46static void gen_set_gpr_pair(DisasContext *ctx, int reg_num, TCGv_i64 t)
47{
48    assert(get_ol(ctx) == MXL_RV32);
49
50    if (reg_num != 0) {
51#ifdef TARGET_RISCV32
52        tcg_gen_extr_i64_i32(cpu_gpr[reg_num], cpu_gpr[reg_num + 1], t);
53#else
54        tcg_gen_ext32s_i64(cpu_gpr[reg_num], t);
55        tcg_gen_sari_i64(cpu_gpr[reg_num + 1], t, 32);
56#endif
57
58        if (get_xl_max(ctx) == MXL_RV128) {
59            tcg_gen_sari_tl(cpu_gprh[reg_num], cpu_gpr[reg_num], 63);
60            tcg_gen_sari_tl(cpu_gprh[reg_num + 1], cpu_gpr[reg_num + 1], 63);
61        }
62    }
63}
64
65static bool gen_cmpxchg64(DisasContext *ctx, arg_atomic *a, MemOp mop)
66{
67    /*
68     * Encodings with odd numbered registers specified in rs2 and rd are
69     * reserved.
70     */
71    if ((a->rs2 | a->rd) & 1) {
72        return false;
73    }
74
75    TCGv_i64 dest = get_gpr_pair(ctx, a->rd);
76    TCGv src1 = get_address(ctx, a->rs1, 0);
77    TCGv_i64 src2 = get_gpr_pair(ctx, a->rs2);
78
79    decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO);
80    tcg_gen_atomic_cmpxchg_i64(dest, src1, dest, src2, ctx->mem_idx, mop);
81
82    gen_set_gpr_pair(ctx, a->rd, dest);
83    return true;
84}
85
86static bool trans_amocas_d(DisasContext *ctx, arg_amocas_d *a)
87{
88    REQUIRE_ZACAS(ctx);
89    switch (get_ol(ctx)) {
90    case MXL_RV32:
91        return gen_cmpxchg64(ctx, a, MO_ALIGN | MO_TEUQ);
92    case MXL_RV64:
93    case MXL_RV128:
94        return gen_cmpxchg(ctx, a, MO_ALIGN | MO_TEUQ);
95    default:
96        g_assert_not_reached();
97    }
98}
99
100static bool trans_amocas_q(DisasContext *ctx, arg_amocas_q *a)
101{
102    REQUIRE_ZACAS(ctx);
103    REQUIRE_64BIT(ctx);
104
105    /*
106     * Encodings with odd numbered registers specified in rs2 and rd are
107     * reserved.
108     */
109    if ((a->rs2 | a->rd) & 1) {
110        return false;
111    }
112
113#ifdef TARGET_RISCV64
114    TCGv_i128 dest = tcg_temp_new_i128();
115    TCGv src1 = get_address(ctx, a->rs1, 0);
116    TCGv_i128 src2 = tcg_temp_new_i128();
117    TCGv_i64 src2l = get_gpr(ctx, a->rs2, EXT_NONE);
118    TCGv_i64 src2h = get_gpr(ctx, a->rs2 == 0 ? 0 : a->rs2 + 1, EXT_NONE);
119    TCGv_i64 destl = get_gpr(ctx, a->rd, EXT_NONE);
120    TCGv_i64 desth = get_gpr(ctx, a->rd == 0 ? 0 : a->rd + 1, EXT_NONE);
121
122    tcg_gen_concat_i64_i128(src2, src2l, src2h);
123    tcg_gen_concat_i64_i128(dest, destl, desth);
124    decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO);
125    tcg_gen_atomic_cmpxchg_i128(dest, src1, dest, src2, ctx->mem_idx,
126                                (MO_ALIGN | MO_TEUO));
127
128    tcg_gen_extr_i128_i64(destl, desth, dest);
129
130    if (a->rd != 0) {
131        gen_set_gpr(ctx, a->rd, destl);
132        gen_set_gpr(ctx, a->rd + 1, desth);
133    }
134#endif
135
136    return true;
137}
138