1/*
2 * RISC-V translation routines for the RV64M Standard Extension.
3 *
4 * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
5 * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de
6 *                    Bastian Koppelmann, kbastian@mail.uni-paderborn.de
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2 or later, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22static bool trans_mul(DisasContext *ctx, arg_mul *a)
23{
24    REQUIRE_EXT(ctx, RVM);
25    return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, NULL);
26}
27
28static void gen_mulh(TCGv ret, TCGv s1, TCGv s2)
29{
30    TCGv discard = tcg_temp_new();
31
32    tcg_gen_muls2_tl(discard, ret, s1, s2);
33    tcg_temp_free(discard);
34}
35
36static void gen_mulh_w(TCGv ret, TCGv s1, TCGv s2)
37{
38    tcg_gen_mul_tl(ret, s1, s2);
39    tcg_gen_sari_tl(ret, ret, 32);
40}
41
42static bool trans_mulh(DisasContext *ctx, arg_mulh *a)
43{
44    REQUIRE_EXT(ctx, RVM);
45    return gen_arith_per_ol(ctx, a, EXT_SIGN, gen_mulh, gen_mulh_w, NULL);
46}
47
48static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2)
49{
50    TCGv rl = tcg_temp_new();
51    TCGv rh = tcg_temp_new();
52
53    tcg_gen_mulu2_tl(rl, rh, arg1, arg2);
54    /* fix up for one negative */
55    tcg_gen_sari_tl(rl, arg1, TARGET_LONG_BITS - 1);
56    tcg_gen_and_tl(rl, rl, arg2);
57    tcg_gen_sub_tl(ret, rh, rl);
58
59    tcg_temp_free(rl);
60    tcg_temp_free(rh);
61}
62
63static void gen_mulhsu_w(TCGv ret, TCGv arg1, TCGv arg2)
64{
65    TCGv t1 = tcg_temp_new();
66    TCGv t2 = tcg_temp_new();
67
68    tcg_gen_ext32s_tl(t1, arg1);
69    tcg_gen_ext32u_tl(t2, arg2);
70    tcg_gen_mul_tl(ret, t1, t2);
71    tcg_temp_free(t1);
72    tcg_temp_free(t2);
73    tcg_gen_sari_tl(ret, ret, 32);
74}
75
76static bool trans_mulhsu(DisasContext *ctx, arg_mulhsu *a)
77{
78    REQUIRE_EXT(ctx, RVM);
79    return gen_arith_per_ol(ctx, a, EXT_NONE, gen_mulhsu, gen_mulhsu_w, NULL);
80}
81
82static void gen_mulhu(TCGv ret, TCGv s1, TCGv s2)
83{
84    TCGv discard = tcg_temp_new();
85
86    tcg_gen_mulu2_tl(discard, ret, s1, s2);
87    tcg_temp_free(discard);
88}
89
90static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a)
91{
92    REQUIRE_EXT(ctx, RVM);
93    /* gen_mulh_w works for either sign as input. */
94    return gen_arith_per_ol(ctx, a, EXT_ZERO, gen_mulhu, gen_mulh_w, NULL);
95}
96
97static void gen_div(TCGv ret, TCGv source1, TCGv source2)
98{
99    TCGv temp1, temp2, zero, one, mone, min;
100
101    temp1 = tcg_temp_new();
102    temp2 = tcg_temp_new();
103    zero = tcg_constant_tl(0);
104    one = tcg_constant_tl(1);
105    mone = tcg_constant_tl(-1);
106    min = tcg_constant_tl(1ull << (TARGET_LONG_BITS - 1));
107
108    /*
109     * If overflow, set temp2 to 1, else source2.
110     * This produces the required result of min.
111     */
112    tcg_gen_setcond_tl(TCG_COND_EQ, temp1, source1, min);
113    tcg_gen_setcond_tl(TCG_COND_EQ, temp2, source2, mone);
114    tcg_gen_and_tl(temp1, temp1, temp2);
115    tcg_gen_movcond_tl(TCG_COND_NE, temp2, temp1, zero, one, source2);
116
117    /*
118     * If div by zero, set temp1 to -1 and temp2 to 1 to
119     * produce the required result of -1.
120     */
121    tcg_gen_movcond_tl(TCG_COND_EQ, temp1, source2, zero, mone, source1);
122    tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, temp2);
123
124    tcg_gen_div_tl(ret, temp1, temp2);
125
126    tcg_temp_free(temp1);
127    tcg_temp_free(temp2);
128}
129
130static bool trans_div(DisasContext *ctx, arg_div *a)
131{
132    REQUIRE_EXT(ctx, RVM);
133    return gen_arith(ctx, a, EXT_SIGN, gen_div, NULL);
134}
135
136static void gen_divu(TCGv ret, TCGv source1, TCGv source2)
137{
138    TCGv temp1, temp2, zero, one, max;
139
140    temp1 = tcg_temp_new();
141    temp2 = tcg_temp_new();
142    zero = tcg_constant_tl(0);
143    one = tcg_constant_tl(1);
144    max = tcg_constant_tl(~0);
145
146    /*
147     * If div by zero, set temp1 to max and temp2 to 1 to
148     * produce the required result of max.
149     */
150    tcg_gen_movcond_tl(TCG_COND_EQ, temp1, source2, zero, max, source1);
151    tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, source2);
152    tcg_gen_divu_tl(ret, temp1, temp2);
153
154    tcg_temp_free(temp1);
155    tcg_temp_free(temp2);
156}
157
158static bool trans_divu(DisasContext *ctx, arg_divu *a)
159{
160    REQUIRE_EXT(ctx, RVM);
161    return gen_arith(ctx, a, EXT_ZERO, gen_divu, NULL);
162}
163
164static void gen_rem(TCGv ret, TCGv source1, TCGv source2)
165{
166    TCGv temp1, temp2, zero, one, mone, min;
167
168    temp1 = tcg_temp_new();
169    temp2 = tcg_temp_new();
170    zero = tcg_constant_tl(0);
171    one = tcg_constant_tl(1);
172    mone = tcg_constant_tl(-1);
173    min = tcg_constant_tl(1ull << (TARGET_LONG_BITS - 1));
174
175    /*
176     * If overflow, set temp1 to 0, else source1.
177     * This avoids a possible host trap, and produces the required result of 0.
178     */
179    tcg_gen_setcond_tl(TCG_COND_EQ, temp1, source1, min);
180    tcg_gen_setcond_tl(TCG_COND_EQ, temp2, source2, mone);
181    tcg_gen_and_tl(temp1, temp1, temp2);
182    tcg_gen_movcond_tl(TCG_COND_NE, temp1, temp1, zero, zero, source1);
183
184    /*
185     * If div by zero, set temp2 to 1, else source2.
186     * This avoids a possible host trap, but produces an incorrect result.
187     */
188    tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, source2);
189
190    tcg_gen_rem_tl(temp1, temp1, temp2);
191
192    /* If div by zero, the required result is the original dividend. */
193    tcg_gen_movcond_tl(TCG_COND_EQ, ret, source2, zero, source1, temp1);
194
195    tcg_temp_free(temp1);
196    tcg_temp_free(temp2);
197}
198
199static bool trans_rem(DisasContext *ctx, arg_rem *a)
200{
201    REQUIRE_EXT(ctx, RVM);
202    return gen_arith(ctx, a, EXT_SIGN, gen_rem, NULL);
203}
204
205static void gen_remu(TCGv ret, TCGv source1, TCGv source2)
206{
207    TCGv temp, zero, one;
208
209    temp = tcg_temp_new();
210    zero = tcg_constant_tl(0);
211    one = tcg_constant_tl(1);
212
213    /*
214     * If div by zero, set temp to 1, else source2.
215     * This avoids a possible host trap, but produces an incorrect result.
216     */
217    tcg_gen_movcond_tl(TCG_COND_EQ, temp, source2, zero, one, source2);
218
219    tcg_gen_remu_tl(temp, source1, temp);
220
221    /* If div by zero, the required result is the original dividend. */
222    tcg_gen_movcond_tl(TCG_COND_EQ, ret, source2, zero, source1, temp);
223
224    tcg_temp_free(temp);
225}
226
227static bool trans_remu(DisasContext *ctx, arg_remu *a)
228{
229    REQUIRE_EXT(ctx, RVM);
230    return gen_arith(ctx, a, EXT_ZERO, gen_remu, NULL);
231}
232
233static bool trans_mulw(DisasContext *ctx, arg_mulw *a)
234{
235    REQUIRE_64BIT(ctx);
236    REQUIRE_EXT(ctx, RVM);
237    ctx->ol = MXL_RV32;
238    return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, NULL);
239}
240
241static bool trans_divw(DisasContext *ctx, arg_divw *a)
242{
243    REQUIRE_64BIT(ctx);
244    REQUIRE_EXT(ctx, RVM);
245    ctx->ol = MXL_RV32;
246    return gen_arith(ctx, a, EXT_SIGN, gen_div, NULL);
247}
248
249static bool trans_divuw(DisasContext *ctx, arg_divuw *a)
250{
251    REQUIRE_64BIT(ctx);
252    REQUIRE_EXT(ctx, RVM);
253    ctx->ol = MXL_RV32;
254    return gen_arith(ctx, a, EXT_ZERO, gen_divu, NULL);
255}
256
257static bool trans_remw(DisasContext *ctx, arg_remw *a)
258{
259    REQUIRE_64BIT(ctx);
260    REQUIRE_EXT(ctx, RVM);
261    ctx->ol = MXL_RV32;
262    return gen_arith(ctx, a, EXT_SIGN, gen_rem, NULL);
263}
264
265static bool trans_remuw(DisasContext *ctx, arg_remuw *a)
266{
267    REQUIRE_64BIT(ctx);
268    REQUIRE_EXT(ctx, RVM);
269    ctx->ol = MXL_RV32;
270    return gen_arith(ctx, a, EXT_ZERO, gen_remu, NULL);
271}
272