blob: c4699529f90e750769dd84dca8909ad094c464ce [file] [log] [blame]
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +04001/*
2 * Optimizations for Tiny Code Generator for QEMU
3 *
4 * Copyright (c) 2010 Samsung Electronics.
5 * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25
26#include "config.h"
27
28#include <stdlib.h>
29#include <stdio.h>
30
31#include "qemu-common.h"
32#include "tcg-op.h"
33
34#if TCG_TARGET_REG_BITS == 64
35#define CASE_OP_32_64(x) \
36 glue(glue(case INDEX_op_, x), _i32): \
37 glue(glue(case INDEX_op_, x), _i64)
38#else
39#define CASE_OP_32_64(x) \
40 glue(glue(case INDEX_op_, x), _i32)
41#endif
42
Kirill Batuzov22613af2011-07-07 16:37:13 +040043typedef enum {
44 TCG_TEMP_UNDEF = 0,
45 TCG_TEMP_CONST,
46 TCG_TEMP_COPY,
47 TCG_TEMP_HAS_COPY,
48 TCG_TEMP_ANY
49} tcg_temp_state;
50
51struct tcg_temp_info {
52 tcg_temp_state state;
53 uint16_t prev_copy;
54 uint16_t next_copy;
55 tcg_target_ulong val;
56};
57
58static struct tcg_temp_info temps[TCG_MAX_TEMPS];
59
60/* Reset TEMP's state to TCG_TEMP_ANY. If TEMP was a representative of some
61 class of equivalent temp's, a new representative should be chosen in this
62 class. */
63static void reset_temp(TCGArg temp, int nb_temps, int nb_globals)
64{
65 int i;
66 TCGArg new_base = (TCGArg)-1;
67 if (temps[temp].state == TCG_TEMP_HAS_COPY) {
68 for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
69 if (i >= nb_globals) {
70 temps[i].state = TCG_TEMP_HAS_COPY;
71 new_base = i;
72 break;
73 }
74 }
75 for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
76 if (new_base == (TCGArg)-1) {
77 temps[i].state = TCG_TEMP_ANY;
78 } else {
79 temps[i].val = new_base;
80 }
81 }
82 temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
83 temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
84 } else if (temps[temp].state == TCG_TEMP_COPY) {
85 temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
86 temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
87 new_base = temps[temp].val;
88 }
89 temps[temp].state = TCG_TEMP_ANY;
90 if (new_base != (TCGArg)-1 && temps[new_base].next_copy == new_base) {
91 temps[new_base].state = TCG_TEMP_ANY;
92 }
93}
94
95static int op_bits(int op)
96{
97 switch (op) {
98 case INDEX_op_mov_i32:
Kirill Batuzov53108fb2011-07-07 16:37:14 +040099 case INDEX_op_add_i32:
100 case INDEX_op_sub_i32:
101 case INDEX_op_mul_i32:
Kirill Batuzov9a810902011-07-07 16:37:15 +0400102 case INDEX_op_and_i32:
103 case INDEX_op_or_i32:
104 case INDEX_op_xor_i32:
Kirill Batuzov22613af2011-07-07 16:37:13 +0400105 return 32;
106#if TCG_TARGET_REG_BITS == 64
107 case INDEX_op_mov_i64:
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400108 case INDEX_op_add_i64:
109 case INDEX_op_sub_i64:
110 case INDEX_op_mul_i64:
Kirill Batuzov9a810902011-07-07 16:37:15 +0400111 case INDEX_op_and_i64:
112 case INDEX_op_or_i64:
113 case INDEX_op_xor_i64:
Kirill Batuzov22613af2011-07-07 16:37:13 +0400114 return 64;
115#endif
116 default:
117 fprintf(stderr, "Unrecognized operation %d in op_bits.\n", op);
118 tcg_abort();
119 }
120}
121
122static int op_to_movi(int op)
123{
124 switch (op_bits(op)) {
125 case 32:
126 return INDEX_op_movi_i32;
127#if TCG_TARGET_REG_BITS == 64
128 case 64:
129 return INDEX_op_movi_i64;
130#endif
131 default:
132 fprintf(stderr, "op_to_movi: unexpected return value of "
133 "function op_bits.\n");
134 tcg_abort();
135 }
136}
137
138static void tcg_opt_gen_mov(TCGArg *gen_args, TCGArg dst, TCGArg src,
139 int nb_temps, int nb_globals)
140{
141 reset_temp(dst, nb_temps, nb_globals);
142 assert(temps[src].state != TCG_TEMP_COPY);
143 if (src >= nb_globals) {
144 assert(temps[src].state != TCG_TEMP_CONST);
145 if (temps[src].state != TCG_TEMP_HAS_COPY) {
146 temps[src].state = TCG_TEMP_HAS_COPY;
147 temps[src].next_copy = src;
148 temps[src].prev_copy = src;
149 }
150 temps[dst].state = TCG_TEMP_COPY;
151 temps[dst].val = src;
152 temps[dst].next_copy = temps[src].next_copy;
153 temps[dst].prev_copy = src;
154 temps[temps[dst].next_copy].prev_copy = dst;
155 temps[src].next_copy = dst;
156 }
157 gen_args[0] = dst;
158 gen_args[1] = src;
159}
160
161static void tcg_opt_gen_movi(TCGArg *gen_args, TCGArg dst, TCGArg val,
162 int nb_temps, int nb_globals)
163{
164 reset_temp(dst, nb_temps, nb_globals);
165 temps[dst].state = TCG_TEMP_CONST;
166 temps[dst].val = val;
167 gen_args[0] = dst;
168 gen_args[1] = val;
169}
170
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400171static int op_to_mov(int op)
172{
173 switch (op_bits(op)) {
174 case 32:
175 return INDEX_op_mov_i32;
176#if TCG_TARGET_REG_BITS == 64
177 case 64:
178 return INDEX_op_mov_i64;
179#endif
180 default:
181 fprintf(stderr, "op_to_mov: unexpected return value of "
182 "function op_bits.\n");
183 tcg_abort();
184 }
185}
186
187static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y)
188{
189 switch (op) {
190 CASE_OP_32_64(add):
191 return x + y;
192
193 CASE_OP_32_64(sub):
194 return x - y;
195
196 CASE_OP_32_64(mul):
197 return x * y;
198
Kirill Batuzov9a810902011-07-07 16:37:15 +0400199 CASE_OP_32_64(and):
200 return x & y;
201
202 CASE_OP_32_64(or):
203 return x | y;
204
205 CASE_OP_32_64(xor):
206 return x ^ y;
207
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400208 default:
209 fprintf(stderr,
210 "Unrecognized operation %d in do_constant_folding.\n", op);
211 tcg_abort();
212 }
213}
214
215static TCGArg do_constant_folding(int op, TCGArg x, TCGArg y)
216{
217 TCGArg res = do_constant_folding_2(op, x, y);
218#if TCG_TARGET_REG_BITS == 64
219 if (op_bits(op) == 32) {
220 res &= 0xffffffff;
221 }
222#endif
223 return res;
224}
225
Kirill Batuzov22613af2011-07-07 16:37:13 +0400226/* Propagate constants and copies, fold constant expressions. */
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400227static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr,
228 TCGArg *args, TCGOpDef *tcg_op_defs)
229{
Kirill Batuzov22613af2011-07-07 16:37:13 +0400230 int i, nb_ops, op_index, op, nb_temps, nb_globals, nb_call_args;
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400231 const TCGOpDef *def;
232 TCGArg *gen_args;
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400233 TCGArg tmp;
Kirill Batuzov22613af2011-07-07 16:37:13 +0400234 /* Array VALS has an element for each temp.
235 If this temp holds a constant then its value is kept in VALS' element.
236 If this temp is a copy of other ones then this equivalence class'
237 representative is kept in VALS' element.
238 If this temp is neither copy nor constant then corresponding VALS'
239 element is unused. */
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400240
241 nb_temps = s->nb_temps;
242 nb_globals = s->nb_globals;
Kirill Batuzov22613af2011-07-07 16:37:13 +0400243 memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400244
245 nb_ops = tcg_opc_ptr - gen_opc_buf;
246 gen_args = args;
247 for (op_index = 0; op_index < nb_ops; op_index++) {
248 op = gen_opc_buf[op_index];
249 def = &tcg_op_defs[op];
Kirill Batuzov22613af2011-07-07 16:37:13 +0400250 /* Do copy propagation */
251 if (!(def->flags & (TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS))) {
252 assert(op != INDEX_op_call);
253 for (i = def->nb_oargs; i < def->nb_oargs + def->nb_iargs; i++) {
254 if (temps[args[i]].state == TCG_TEMP_COPY) {
255 args[i] = temps[args[i]].val;
256 }
257 }
258 }
259
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400260 /* For commutative operations make constant second argument */
261 switch (op) {
262 CASE_OP_32_64(add):
263 CASE_OP_32_64(mul):
Kirill Batuzov9a810902011-07-07 16:37:15 +0400264 CASE_OP_32_64(and):
265 CASE_OP_32_64(or):
266 CASE_OP_32_64(xor):
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400267 if (temps[args[1]].state == TCG_TEMP_CONST) {
268 tmp = args[1];
269 args[1] = args[2];
270 args[2] = tmp;
271 }
272 break;
273 default:
274 break;
275 }
276
277 /* Simplify expression if possible. */
278 switch (op) {
279 CASE_OP_32_64(add):
280 CASE_OP_32_64(sub):
281 if (temps[args[1]].state == TCG_TEMP_CONST) {
282 /* Proceed with possible constant folding. */
283 break;
284 }
285 if (temps[args[2]].state == TCG_TEMP_CONST
286 && temps[args[2]].val == 0) {
287 if ((temps[args[0]].state == TCG_TEMP_COPY
288 && temps[args[0]].val == args[1])
289 || args[0] == args[1]) {
290 args += 3;
291 gen_opc_buf[op_index] = INDEX_op_nop;
292 } else {
293 gen_opc_buf[op_index] = op_to_mov(op);
294 tcg_opt_gen_mov(gen_args, args[0], args[1],
295 nb_temps, nb_globals);
296 gen_args += 2;
297 args += 3;
298 }
299 continue;
300 }
301 break;
302 CASE_OP_32_64(mul):
303 if ((temps[args[2]].state == TCG_TEMP_CONST
304 && temps[args[2]].val == 0)) {
305 gen_opc_buf[op_index] = op_to_movi(op);
306 tcg_opt_gen_movi(gen_args, args[0], 0, nb_temps, nb_globals);
307 args += 3;
308 gen_args += 2;
309 continue;
310 }
311 break;
Kirill Batuzov9a810902011-07-07 16:37:15 +0400312 CASE_OP_32_64(or):
313 CASE_OP_32_64(and):
314 if (args[1] == args[2]) {
315 if (args[1] == args[0]) {
316 args += 3;
317 gen_opc_buf[op_index] = INDEX_op_nop;
318 } else {
319 gen_opc_buf[op_index] = op_to_mov(op);
320 tcg_opt_gen_mov(gen_args, args[0], args[1], nb_temps,
321 nb_globals);
322 gen_args += 2;
323 args += 3;
324 }
325 continue;
326 }
327 break;
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400328 }
329
Kirill Batuzov22613af2011-07-07 16:37:13 +0400330 /* Propagate constants through copy operations and do constant
331 folding. Constants will be substituted to arguments by register
332 allocator where needed and possible. Also detect copies. */
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400333 switch (op) {
Kirill Batuzov22613af2011-07-07 16:37:13 +0400334 CASE_OP_32_64(mov):
335 if ((temps[args[1]].state == TCG_TEMP_COPY
336 && temps[args[1]].val == args[0])
337 || args[0] == args[1]) {
338 args += 2;
339 gen_opc_buf[op_index] = INDEX_op_nop;
340 break;
341 }
342 if (temps[args[1]].state != TCG_TEMP_CONST) {
343 tcg_opt_gen_mov(gen_args, args[0], args[1],
344 nb_temps, nb_globals);
345 gen_args += 2;
346 args += 2;
347 break;
348 }
349 /* Source argument is constant. Rewrite the operation and
350 let movi case handle it. */
351 op = op_to_movi(op);
352 gen_opc_buf[op_index] = op;
353 args[1] = temps[args[1]].val;
354 /* fallthrough */
355 CASE_OP_32_64(movi):
356 tcg_opt_gen_movi(gen_args, args[0], args[1], nb_temps, nb_globals);
357 gen_args += 2;
358 args += 2;
359 break;
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400360 CASE_OP_32_64(add):
361 CASE_OP_32_64(sub):
362 CASE_OP_32_64(mul):
Kirill Batuzov9a810902011-07-07 16:37:15 +0400363 CASE_OP_32_64(or):
364 CASE_OP_32_64(and):
365 CASE_OP_32_64(xor):
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400366 if (temps[args[1]].state == TCG_TEMP_CONST
367 && temps[args[2]].state == TCG_TEMP_CONST) {
368 gen_opc_buf[op_index] = op_to_movi(op);
369 tmp = do_constant_folding(op, temps[args[1]].val,
370 temps[args[2]].val);
371 tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals);
372 gen_args += 2;
373 args += 3;
374 break;
375 } else {
376 reset_temp(args[0], nb_temps, nb_globals);
377 gen_args[0] = args[0];
378 gen_args[1] = args[1];
379 gen_args[2] = args[2];
380 gen_args += 3;
381 args += 3;
382 break;
383 }
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400384 case INDEX_op_call:
Kirill Batuzov22613af2011-07-07 16:37:13 +0400385 nb_call_args = (args[0] >> 16) + (args[0] & 0xffff);
386 if (!(args[nb_call_args + 1] & (TCG_CALL_CONST | TCG_CALL_PURE))) {
387 for (i = 0; i < nb_globals; i++) {
388 reset_temp(i, nb_temps, nb_globals);
389 }
390 }
391 for (i = 0; i < (args[0] >> 16); i++) {
392 reset_temp(args[i + 1], nb_temps, nb_globals);
393 }
394 i = nb_call_args + 3;
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400395 while (i) {
396 *gen_args = *args;
397 args++;
398 gen_args++;
399 i--;
400 }
401 break;
402 case INDEX_op_set_label:
403 case INDEX_op_jmp:
404 case INDEX_op_br:
405 CASE_OP_32_64(brcond):
Kirill Batuzov22613af2011-07-07 16:37:13 +0400406 memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400407 for (i = 0; i < def->nb_args; i++) {
408 *gen_args = *args;
409 args++;
410 gen_args++;
411 }
412 break;
413 default:
Kirill Batuzov22613af2011-07-07 16:37:13 +0400414 /* Default case: we do know nothing about operation so no
415 propagation is done. We only trash output args. */
416 for (i = 0; i < def->nb_oargs; i++) {
417 reset_temp(args[i], nb_temps, nb_globals);
418 }
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400419 for (i = 0; i < def->nb_args; i++) {
420 gen_args[i] = args[i];
421 }
422 args += def->nb_args;
423 gen_args += def->nb_args;
424 break;
425 }
426 }
427
428 return gen_args;
429}
430
431TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr,
432 TCGArg *args, TCGOpDef *tcg_op_defs)
433{
434 TCGArg *res;
435 res = tcg_constant_folding(s, tcg_opc_ptr, args, tcg_op_defs);
436 return res;
437}