blob: 32f928f98052013cbc4933f1696251409e607fc0 [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
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +040034#define CASE_OP_32_64(x) \
35 glue(glue(case INDEX_op_, x), _i32): \
36 glue(glue(case INDEX_op_, x), _i64)
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +040037
Kirill Batuzov22613af2011-07-07 16:37:13 +040038typedef enum {
39 TCG_TEMP_UNDEF = 0,
40 TCG_TEMP_CONST,
41 TCG_TEMP_COPY,
42 TCG_TEMP_HAS_COPY,
43 TCG_TEMP_ANY
44} tcg_temp_state;
45
46struct tcg_temp_info {
47 tcg_temp_state state;
48 uint16_t prev_copy;
49 uint16_t next_copy;
50 tcg_target_ulong val;
51};
52
53static struct tcg_temp_info temps[TCG_MAX_TEMPS];
54
55/* Reset TEMP's state to TCG_TEMP_ANY. If TEMP was a representative of some
56 class of equivalent temp's, a new representative should be chosen in this
57 class. */
58static void reset_temp(TCGArg temp, int nb_temps, int nb_globals)
59{
60 int i;
61 TCGArg new_base = (TCGArg)-1;
62 if (temps[temp].state == TCG_TEMP_HAS_COPY) {
63 for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
64 if (i >= nb_globals) {
65 temps[i].state = TCG_TEMP_HAS_COPY;
66 new_base = i;
67 break;
68 }
69 }
70 for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
71 if (new_base == (TCGArg)-1) {
72 temps[i].state = TCG_TEMP_ANY;
73 } else {
74 temps[i].val = new_base;
75 }
76 }
77 temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
78 temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
79 } else if (temps[temp].state == TCG_TEMP_COPY) {
80 temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
81 temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
82 new_base = temps[temp].val;
83 }
84 temps[temp].state = TCG_TEMP_ANY;
85 if (new_base != (TCGArg)-1 && temps[new_base].next_copy == new_base) {
86 temps[new_base].state = TCG_TEMP_ANY;
87 }
88}
89
Richard Henderson8399ad52011-08-17 14:11:45 -070090static int op_bits(enum TCGOpcode op)
Kirill Batuzov22613af2011-07-07 16:37:13 +040091{
Richard Henderson8399ad52011-08-17 14:11:45 -070092 const TCGOpDef *def = &tcg_op_defs[op];
93 return def->flags & TCG_OPF_64BIT ? 64 : 32;
Kirill Batuzov22613af2011-07-07 16:37:13 +040094}
95
96static int op_to_movi(int op)
97{
98 switch (op_bits(op)) {
99 case 32:
100 return INDEX_op_movi_i32;
Kirill Batuzov22613af2011-07-07 16:37:13 +0400101 case 64:
102 return INDEX_op_movi_i64;
Kirill Batuzov22613af2011-07-07 16:37:13 +0400103 default:
104 fprintf(stderr, "op_to_movi: unexpected return value of "
105 "function op_bits.\n");
106 tcg_abort();
107 }
108}
109
Blue Swirle31b0a72011-08-06 13:58:47 +0000110static void tcg_opt_gen_mov(TCGContext *s, TCGArg *gen_args, TCGArg dst,
111 TCGArg src, int nb_temps, int nb_globals)
Kirill Batuzov22613af2011-07-07 16:37:13 +0400112{
113 reset_temp(dst, nb_temps, nb_globals);
114 assert(temps[src].state != TCG_TEMP_COPY);
Blue Swirle31b0a72011-08-06 13:58:47 +0000115 /* Don't try to copy if one of temps is a global or either one
116 is local and another is register */
117 if (src >= nb_globals && dst >= nb_globals &&
118 tcg_arg_is_local(s, src) == tcg_arg_is_local(s, dst)) {
Kirill Batuzov22613af2011-07-07 16:37:13 +0400119 assert(temps[src].state != TCG_TEMP_CONST);
120 if (temps[src].state != TCG_TEMP_HAS_COPY) {
121 temps[src].state = TCG_TEMP_HAS_COPY;
122 temps[src].next_copy = src;
123 temps[src].prev_copy = src;
124 }
125 temps[dst].state = TCG_TEMP_COPY;
126 temps[dst].val = src;
127 temps[dst].next_copy = temps[src].next_copy;
128 temps[dst].prev_copy = src;
129 temps[temps[dst].next_copy].prev_copy = dst;
130 temps[src].next_copy = dst;
131 }
132 gen_args[0] = dst;
133 gen_args[1] = src;
134}
135
136static void tcg_opt_gen_movi(TCGArg *gen_args, TCGArg dst, TCGArg val,
137 int nb_temps, int nb_globals)
138{
139 reset_temp(dst, nb_temps, nb_globals);
140 temps[dst].state = TCG_TEMP_CONST;
141 temps[dst].val = val;
142 gen_args[0] = dst;
143 gen_args[1] = val;
144}
145
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400146static int op_to_mov(int op)
147{
148 switch (op_bits(op)) {
149 case 32:
150 return INDEX_op_mov_i32;
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400151 case 64:
152 return INDEX_op_mov_i64;
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400153 default:
154 fprintf(stderr, "op_to_mov: unexpected return value of "
155 "function op_bits.\n");
156 tcg_abort();
157 }
158}
159
160static TCGArg do_constant_folding_2(int op, TCGArg x, TCGArg y)
161{
162 switch (op) {
163 CASE_OP_32_64(add):
164 return x + y;
165
166 CASE_OP_32_64(sub):
167 return x - y;
168
169 CASE_OP_32_64(mul):
170 return x * y;
171
Kirill Batuzov9a810902011-07-07 16:37:15 +0400172 CASE_OP_32_64(and):
173 return x & y;
174
175 CASE_OP_32_64(or):
176 return x | y;
177
178 CASE_OP_32_64(xor):
179 return x ^ y;
180
Kirill Batuzov55c09752011-07-07 16:37:16 +0400181 case INDEX_op_shl_i32:
182 return (uint32_t)x << (uint32_t)y;
183
Kirill Batuzov55c09752011-07-07 16:37:16 +0400184 case INDEX_op_shl_i64:
185 return (uint64_t)x << (uint64_t)y;
Kirill Batuzov55c09752011-07-07 16:37:16 +0400186
187 case INDEX_op_shr_i32:
188 return (uint32_t)x >> (uint32_t)y;
189
Kirill Batuzov55c09752011-07-07 16:37:16 +0400190 case INDEX_op_shr_i64:
191 return (uint64_t)x >> (uint64_t)y;
Kirill Batuzov55c09752011-07-07 16:37:16 +0400192
193 case INDEX_op_sar_i32:
194 return (int32_t)x >> (int32_t)y;
195
Kirill Batuzov55c09752011-07-07 16:37:16 +0400196 case INDEX_op_sar_i64:
197 return (int64_t)x >> (int64_t)y;
Kirill Batuzov55c09752011-07-07 16:37:16 +0400198
199 case INDEX_op_rotr_i32:
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700200 x = ((uint32_t)x << (32 - y)) | ((uint32_t)x >> y);
Kirill Batuzov55c09752011-07-07 16:37:16 +0400201 return x;
202
Kirill Batuzov55c09752011-07-07 16:37:16 +0400203 case INDEX_op_rotr_i64:
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700204 x = ((uint64_t)x << (64 - y)) | ((uint64_t)x >> y);
Kirill Batuzov55c09752011-07-07 16:37:16 +0400205 return x;
Kirill Batuzov55c09752011-07-07 16:37:16 +0400206
207 case INDEX_op_rotl_i32:
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700208 x = ((uint32_t)x << y) | ((uint32_t)x >> (32 - y));
Kirill Batuzov55c09752011-07-07 16:37:16 +0400209 return x;
210
Kirill Batuzov55c09752011-07-07 16:37:16 +0400211 case INDEX_op_rotl_i64:
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700212 x = ((uint64_t)x << y) | ((uint64_t)x >> (64 - y));
Kirill Batuzov55c09752011-07-07 16:37:16 +0400213 return x;
Kirill Batuzov55c09752011-07-07 16:37:16 +0400214
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700215 CASE_OP_32_64(not):
Kirill Batuzova640f032011-07-07 16:37:17 +0400216 return ~x;
217
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700218 CASE_OP_32_64(ext8s):
Kirill Batuzova640f032011-07-07 16:37:17 +0400219 return (int8_t)x;
220
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700221 CASE_OP_32_64(ext16s):
Kirill Batuzova640f032011-07-07 16:37:17 +0400222 return (int16_t)x;
223
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700224 CASE_OP_32_64(ext8u):
Kirill Batuzova640f032011-07-07 16:37:17 +0400225 return (uint8_t)x;
226
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700227 CASE_OP_32_64(ext16u):
Kirill Batuzova640f032011-07-07 16:37:17 +0400228 return (uint16_t)x;
229
Kirill Batuzova640f032011-07-07 16:37:17 +0400230 case INDEX_op_ext32s_i64:
231 return (int32_t)x;
232
233 case INDEX_op_ext32u_i64:
234 return (uint32_t)x;
Kirill Batuzova640f032011-07-07 16:37:17 +0400235
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400236 default:
237 fprintf(stderr,
238 "Unrecognized operation %d in do_constant_folding.\n", op);
239 tcg_abort();
240 }
241}
242
243static TCGArg do_constant_folding(int op, TCGArg x, TCGArg y)
244{
245 TCGArg res = do_constant_folding_2(op, x, y);
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400246 if (op_bits(op) == 32) {
247 res &= 0xffffffff;
248 }
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400249 return res;
250}
251
Kirill Batuzov22613af2011-07-07 16:37:13 +0400252/* Propagate constants and copies, fold constant expressions. */
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400253static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr,
254 TCGArg *args, TCGOpDef *tcg_op_defs)
255{
Kirill Batuzov22613af2011-07-07 16:37:13 +0400256 int i, nb_ops, op_index, op, nb_temps, nb_globals, nb_call_args;
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400257 const TCGOpDef *def;
258 TCGArg *gen_args;
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400259 TCGArg tmp;
Kirill Batuzov22613af2011-07-07 16:37:13 +0400260 /* Array VALS has an element for each temp.
261 If this temp holds a constant then its value is kept in VALS' element.
262 If this temp is a copy of other ones then this equivalence class'
263 representative is kept in VALS' element.
264 If this temp is neither copy nor constant then corresponding VALS'
265 element is unused. */
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400266
267 nb_temps = s->nb_temps;
268 nb_globals = s->nb_globals;
Kirill Batuzov22613af2011-07-07 16:37:13 +0400269 memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400270
271 nb_ops = tcg_opc_ptr - gen_opc_buf;
272 gen_args = args;
273 for (op_index = 0; op_index < nb_ops; op_index++) {
274 op = gen_opc_buf[op_index];
275 def = &tcg_op_defs[op];
Kirill Batuzov22613af2011-07-07 16:37:13 +0400276 /* Do copy propagation */
277 if (!(def->flags & (TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS))) {
278 assert(op != INDEX_op_call);
279 for (i = def->nb_oargs; i < def->nb_oargs + def->nb_iargs; i++) {
280 if (temps[args[i]].state == TCG_TEMP_COPY) {
281 args[i] = temps[args[i]].val;
282 }
283 }
284 }
285
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400286 /* For commutative operations make constant second argument */
287 switch (op) {
288 CASE_OP_32_64(add):
289 CASE_OP_32_64(mul):
Kirill Batuzov9a810902011-07-07 16:37:15 +0400290 CASE_OP_32_64(and):
291 CASE_OP_32_64(or):
292 CASE_OP_32_64(xor):
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400293 if (temps[args[1]].state == TCG_TEMP_CONST) {
294 tmp = args[1];
295 args[1] = args[2];
296 args[2] = tmp;
297 }
298 break;
299 default:
300 break;
301 }
302
303 /* Simplify expression if possible. */
304 switch (op) {
305 CASE_OP_32_64(add):
306 CASE_OP_32_64(sub):
Kirill Batuzov55c09752011-07-07 16:37:16 +0400307 CASE_OP_32_64(shl):
308 CASE_OP_32_64(shr):
309 CASE_OP_32_64(sar):
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700310 CASE_OP_32_64(rotl):
311 CASE_OP_32_64(rotr):
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400312 if (temps[args[1]].state == TCG_TEMP_CONST) {
313 /* Proceed with possible constant folding. */
314 break;
315 }
316 if (temps[args[2]].state == TCG_TEMP_CONST
317 && temps[args[2]].val == 0) {
318 if ((temps[args[0]].state == TCG_TEMP_COPY
319 && temps[args[0]].val == args[1])
320 || args[0] == args[1]) {
321 args += 3;
322 gen_opc_buf[op_index] = INDEX_op_nop;
323 } else {
324 gen_opc_buf[op_index] = op_to_mov(op);
Blue Swirle31b0a72011-08-06 13:58:47 +0000325 tcg_opt_gen_mov(s, gen_args, args[0], args[1],
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400326 nb_temps, nb_globals);
327 gen_args += 2;
328 args += 3;
329 }
330 continue;
331 }
332 break;
333 CASE_OP_32_64(mul):
334 if ((temps[args[2]].state == TCG_TEMP_CONST
335 && temps[args[2]].val == 0)) {
336 gen_opc_buf[op_index] = op_to_movi(op);
337 tcg_opt_gen_movi(gen_args, args[0], 0, nb_temps, nb_globals);
338 args += 3;
339 gen_args += 2;
340 continue;
341 }
342 break;
Kirill Batuzov9a810902011-07-07 16:37:15 +0400343 CASE_OP_32_64(or):
344 CASE_OP_32_64(and):
345 if (args[1] == args[2]) {
346 if (args[1] == args[0]) {
347 args += 3;
348 gen_opc_buf[op_index] = INDEX_op_nop;
349 } else {
350 gen_opc_buf[op_index] = op_to_mov(op);
Blue Swirle31b0a72011-08-06 13:58:47 +0000351 tcg_opt_gen_mov(s, gen_args, args[0], args[1], nb_temps,
Kirill Batuzov9a810902011-07-07 16:37:15 +0400352 nb_globals);
353 gen_args += 2;
354 args += 3;
355 }
356 continue;
357 }
358 break;
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400359 }
360
Kirill Batuzov22613af2011-07-07 16:37:13 +0400361 /* Propagate constants through copy operations and do constant
362 folding. Constants will be substituted to arguments by register
363 allocator where needed and possible. Also detect copies. */
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400364 switch (op) {
Kirill Batuzov22613af2011-07-07 16:37:13 +0400365 CASE_OP_32_64(mov):
366 if ((temps[args[1]].state == TCG_TEMP_COPY
367 && temps[args[1]].val == args[0])
368 || args[0] == args[1]) {
369 args += 2;
370 gen_opc_buf[op_index] = INDEX_op_nop;
371 break;
372 }
373 if (temps[args[1]].state != TCG_TEMP_CONST) {
Blue Swirle31b0a72011-08-06 13:58:47 +0000374 tcg_opt_gen_mov(s, gen_args, args[0], args[1],
Kirill Batuzov22613af2011-07-07 16:37:13 +0400375 nb_temps, nb_globals);
376 gen_args += 2;
377 args += 2;
378 break;
379 }
380 /* Source argument is constant. Rewrite the operation and
381 let movi case handle it. */
382 op = op_to_movi(op);
383 gen_opc_buf[op_index] = op;
384 args[1] = temps[args[1]].val;
385 /* fallthrough */
386 CASE_OP_32_64(movi):
387 tcg_opt_gen_movi(gen_args, args[0], args[1], nb_temps, nb_globals);
388 gen_args += 2;
389 args += 2;
390 break;
Kirill Batuzova640f032011-07-07 16:37:17 +0400391 CASE_OP_32_64(not):
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700392 CASE_OP_32_64(ext8s):
393 CASE_OP_32_64(ext8u):
394 CASE_OP_32_64(ext16s):
395 CASE_OP_32_64(ext16u):
Kirill Batuzova640f032011-07-07 16:37:17 +0400396 case INDEX_op_ext32s_i64:
397 case INDEX_op_ext32u_i64:
Kirill Batuzova640f032011-07-07 16:37:17 +0400398 if (temps[args[1]].state == TCG_TEMP_CONST) {
399 gen_opc_buf[op_index] = op_to_movi(op);
400 tmp = do_constant_folding(op, temps[args[1]].val, 0);
401 tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals);
402 gen_args += 2;
403 args += 2;
404 break;
405 } else {
406 reset_temp(args[0], nb_temps, nb_globals);
407 gen_args[0] = args[0];
408 gen_args[1] = args[1];
409 gen_args += 2;
410 args += 2;
411 break;
412 }
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400413 CASE_OP_32_64(add):
414 CASE_OP_32_64(sub):
415 CASE_OP_32_64(mul):
Kirill Batuzov9a810902011-07-07 16:37:15 +0400416 CASE_OP_32_64(or):
417 CASE_OP_32_64(and):
418 CASE_OP_32_64(xor):
Kirill Batuzov55c09752011-07-07 16:37:16 +0400419 CASE_OP_32_64(shl):
420 CASE_OP_32_64(shr):
421 CASE_OP_32_64(sar):
Richard Henderson25c4d9c2011-08-17 14:11:46 -0700422 CASE_OP_32_64(rotl):
423 CASE_OP_32_64(rotr):
Kirill Batuzov53108fb2011-07-07 16:37:14 +0400424 if (temps[args[1]].state == TCG_TEMP_CONST
425 && temps[args[2]].state == TCG_TEMP_CONST) {
426 gen_opc_buf[op_index] = op_to_movi(op);
427 tmp = do_constant_folding(op, temps[args[1]].val,
428 temps[args[2]].val);
429 tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals);
430 gen_args += 2;
431 args += 3;
432 break;
433 } else {
434 reset_temp(args[0], nb_temps, nb_globals);
435 gen_args[0] = args[0];
436 gen_args[1] = args[1];
437 gen_args[2] = args[2];
438 gen_args += 3;
439 args += 3;
440 break;
441 }
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400442 case INDEX_op_call:
Kirill Batuzov22613af2011-07-07 16:37:13 +0400443 nb_call_args = (args[0] >> 16) + (args[0] & 0xffff);
444 if (!(args[nb_call_args + 1] & (TCG_CALL_CONST | TCG_CALL_PURE))) {
445 for (i = 0; i < nb_globals; i++) {
446 reset_temp(i, nb_temps, nb_globals);
447 }
448 }
449 for (i = 0; i < (args[0] >> 16); i++) {
450 reset_temp(args[i + 1], nb_temps, nb_globals);
451 }
452 i = nb_call_args + 3;
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400453 while (i) {
454 *gen_args = *args;
455 args++;
456 gen_args++;
457 i--;
458 }
459 break;
460 case INDEX_op_set_label:
461 case INDEX_op_jmp:
462 case INDEX_op_br:
463 CASE_OP_32_64(brcond):
Kirill Batuzov22613af2011-07-07 16:37:13 +0400464 memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400465 for (i = 0; i < def->nb_args; i++) {
466 *gen_args = *args;
467 args++;
468 gen_args++;
469 }
470 break;
471 default:
Kirill Batuzov22613af2011-07-07 16:37:13 +0400472 /* Default case: we do know nothing about operation so no
473 propagation is done. We only trash output args. */
474 for (i = 0; i < def->nb_oargs; i++) {
475 reset_temp(args[i], nb_temps, nb_globals);
476 }
Kirill Batuzov8f2e8c02011-07-07 16:37:12 +0400477 for (i = 0; i < def->nb_args; i++) {
478 gen_args[i] = args[i];
479 }
480 args += def->nb_args;
481 gen_args += def->nb_args;
482 break;
483 }
484 }
485
486 return gen_args;
487}
488
489TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr,
490 TCGArg *args, TCGOpDef *tcg_op_defs)
491{
492 TCGArg *res;
493 res = tcg_constant_folding(s, tcg_opc_ptr, args, tcg_op_defs);
494 return res;
495}