|  | /* | 
|  | * Optimizations for Tiny Code Generator for QEMU | 
|  | * | 
|  | * Copyright (c) 2010 Samsung Electronics. | 
|  | * Contributed by Kirill Batuzov <batuzovk@ispras.ru> | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
|  | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | * THE SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include "qemu-common.h" | 
|  | #include "tcg-op.h" | 
|  |  | 
|  | #define CASE_OP_32_64(x)                        \ | 
|  | glue(glue(case INDEX_op_, x), _i32):    \ | 
|  | glue(glue(case INDEX_op_, x), _i64) | 
|  |  | 
|  | typedef enum { | 
|  | TCG_TEMP_UNDEF = 0, | 
|  | TCG_TEMP_CONST, | 
|  | TCG_TEMP_COPY, | 
|  | } tcg_temp_state; | 
|  |  | 
|  | struct tcg_temp_info { | 
|  | tcg_temp_state state; | 
|  | uint16_t prev_copy; | 
|  | uint16_t next_copy; | 
|  | tcg_target_ulong val; | 
|  | tcg_target_ulong mask; | 
|  | }; | 
|  |  | 
|  | static struct tcg_temp_info temps[TCG_MAX_TEMPS]; | 
|  |  | 
|  | /* Reset TEMP's state to TCG_TEMP_UNDEF.  If TEMP only had one copy, remove | 
|  | the copy flag from the left temp.  */ | 
|  | static void reset_temp(TCGArg temp) | 
|  | { | 
|  | if (temps[temp].state == TCG_TEMP_COPY) { | 
|  | if (temps[temp].prev_copy == temps[temp].next_copy) { | 
|  | temps[temps[temp].next_copy].state = TCG_TEMP_UNDEF; | 
|  | } else { | 
|  | temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy; | 
|  | temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy; | 
|  | } | 
|  | } | 
|  | temps[temp].state = TCG_TEMP_UNDEF; | 
|  | temps[temp].mask = -1; | 
|  | } | 
|  |  | 
|  | /* Reset all temporaries, given that there are NB_TEMPS of them.  */ | 
|  | static void reset_all_temps(int nb_temps) | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < nb_temps; i++) { | 
|  | temps[i].state = TCG_TEMP_UNDEF; | 
|  | temps[i].mask = -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int op_bits(TCGOpcode op) | 
|  | { | 
|  | const TCGOpDef *def = &tcg_op_defs[op]; | 
|  | return def->flags & TCG_OPF_64BIT ? 64 : 32; | 
|  | } | 
|  |  | 
|  | static TCGOpcode op_to_mov(TCGOpcode op) | 
|  | { | 
|  | switch (op_bits(op)) { | 
|  | case 32: | 
|  | return INDEX_op_mov_i32; | 
|  | case 64: | 
|  | return INDEX_op_mov_i64; | 
|  | default: | 
|  | fprintf(stderr, "op_to_mov: unexpected return value of " | 
|  | "function op_bits.\n"); | 
|  | tcg_abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static TCGOpcode op_to_movi(TCGOpcode op) | 
|  | { | 
|  | switch (op_bits(op)) { | 
|  | case 32: | 
|  | return INDEX_op_movi_i32; | 
|  | case 64: | 
|  | return INDEX_op_movi_i64; | 
|  | default: | 
|  | fprintf(stderr, "op_to_movi: unexpected return value of " | 
|  | "function op_bits.\n"); | 
|  | tcg_abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static TCGArg find_better_copy(TCGContext *s, TCGArg temp) | 
|  | { | 
|  | TCGArg i; | 
|  |  | 
|  | /* If this is already a global, we can't do better. */ | 
|  | if (temp < s->nb_globals) { | 
|  | return temp; | 
|  | } | 
|  |  | 
|  | /* Search for a global first. */ | 
|  | for (i = temps[temp].next_copy ; i != temp ; i = temps[i].next_copy) { | 
|  | if (i < s->nb_globals) { | 
|  | return i; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If it is a temp, search for a temp local. */ | 
|  | if (!s->temps[temp].temp_local) { | 
|  | for (i = temps[temp].next_copy ; i != temp ; i = temps[i].next_copy) { | 
|  | if (s->temps[i].temp_local) { | 
|  | return i; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Failure to find a better representation, return the same temp. */ | 
|  | return temp; | 
|  | } | 
|  |  | 
|  | static bool temps_are_copies(TCGArg arg1, TCGArg arg2) | 
|  | { | 
|  | TCGArg i; | 
|  |  | 
|  | if (arg1 == arg2) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (temps[arg1].state != TCG_TEMP_COPY | 
|  | || temps[arg2].state != TCG_TEMP_COPY) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (i = temps[arg1].next_copy ; i != arg1 ; i = temps[i].next_copy) { | 
|  | if (i == arg2) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static void tcg_opt_gen_mov(TCGContext *s, int op_index, TCGArg *gen_args, | 
|  | TCGOpcode old_op, TCGArg dst, TCGArg src) | 
|  | { | 
|  | TCGOpcode new_op = op_to_mov(old_op); | 
|  | tcg_target_ulong mask; | 
|  |  | 
|  | s->gen_opc_buf[op_index] = new_op; | 
|  |  | 
|  | reset_temp(dst); | 
|  | mask = temps[src].mask; | 
|  | if (TCG_TARGET_REG_BITS > 32 && new_op == INDEX_op_mov_i32) { | 
|  | /* High bits of the destination are now garbage.  */ | 
|  | mask |= ~0xffffffffull; | 
|  | } | 
|  | temps[dst].mask = mask; | 
|  |  | 
|  | assert(temps[src].state != TCG_TEMP_CONST); | 
|  |  | 
|  | if (s->temps[src].type == s->temps[dst].type) { | 
|  | if (temps[src].state != TCG_TEMP_COPY) { | 
|  | temps[src].state = TCG_TEMP_COPY; | 
|  | temps[src].next_copy = src; | 
|  | temps[src].prev_copy = src; | 
|  | } | 
|  | temps[dst].state = TCG_TEMP_COPY; | 
|  | temps[dst].next_copy = temps[src].next_copy; | 
|  | temps[dst].prev_copy = src; | 
|  | temps[temps[dst].next_copy].prev_copy = dst; | 
|  | temps[src].next_copy = dst; | 
|  | } | 
|  |  | 
|  | gen_args[0] = dst; | 
|  | gen_args[1] = src; | 
|  | } | 
|  |  | 
|  | static void tcg_opt_gen_movi(TCGContext *s, int op_index, TCGArg *gen_args, | 
|  | TCGOpcode old_op, TCGArg dst, TCGArg val) | 
|  | { | 
|  | TCGOpcode new_op = op_to_movi(old_op); | 
|  | tcg_target_ulong mask; | 
|  |  | 
|  | s->gen_opc_buf[op_index] = new_op; | 
|  |  | 
|  | reset_temp(dst); | 
|  | temps[dst].state = TCG_TEMP_CONST; | 
|  | temps[dst].val = val; | 
|  | mask = val; | 
|  | if (TCG_TARGET_REG_BITS > 32 && new_op == INDEX_op_mov_i32) { | 
|  | /* High bits of the destination are now garbage.  */ | 
|  | mask |= ~0xffffffffull; | 
|  | } | 
|  | temps[dst].mask = mask; | 
|  |  | 
|  | gen_args[0] = dst; | 
|  | gen_args[1] = val; | 
|  | } | 
|  |  | 
|  | static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y) | 
|  | { | 
|  | uint64_t l64, h64; | 
|  |  | 
|  | switch (op) { | 
|  | CASE_OP_32_64(add): | 
|  | return x + y; | 
|  |  | 
|  | CASE_OP_32_64(sub): | 
|  | return x - y; | 
|  |  | 
|  | CASE_OP_32_64(mul): | 
|  | return x * y; | 
|  |  | 
|  | CASE_OP_32_64(and): | 
|  | return x & y; | 
|  |  | 
|  | CASE_OP_32_64(or): | 
|  | return x | y; | 
|  |  | 
|  | CASE_OP_32_64(xor): | 
|  | return x ^ y; | 
|  |  | 
|  | case INDEX_op_shl_i32: | 
|  | return (uint32_t)x << (y & 31); | 
|  |  | 
|  | case INDEX_op_shl_i64: | 
|  | return (uint64_t)x << (y & 63); | 
|  |  | 
|  | case INDEX_op_shr_i32: | 
|  | return (uint32_t)x >> (y & 31); | 
|  |  | 
|  | case INDEX_op_trunc_shr_i32: | 
|  | case INDEX_op_shr_i64: | 
|  | return (uint64_t)x >> (y & 63); | 
|  |  | 
|  | case INDEX_op_sar_i32: | 
|  | return (int32_t)x >> (y & 31); | 
|  |  | 
|  | case INDEX_op_sar_i64: | 
|  | return (int64_t)x >> (y & 63); | 
|  |  | 
|  | case INDEX_op_rotr_i32: | 
|  | return ror32(x, y & 31); | 
|  |  | 
|  | case INDEX_op_rotr_i64: | 
|  | return ror64(x, y & 63); | 
|  |  | 
|  | case INDEX_op_rotl_i32: | 
|  | return rol32(x, y & 31); | 
|  |  | 
|  | case INDEX_op_rotl_i64: | 
|  | return rol64(x, y & 63); | 
|  |  | 
|  | CASE_OP_32_64(not): | 
|  | return ~x; | 
|  |  | 
|  | CASE_OP_32_64(neg): | 
|  | return -x; | 
|  |  | 
|  | CASE_OP_32_64(andc): | 
|  | return x & ~y; | 
|  |  | 
|  | CASE_OP_32_64(orc): | 
|  | return x | ~y; | 
|  |  | 
|  | CASE_OP_32_64(eqv): | 
|  | return ~(x ^ y); | 
|  |  | 
|  | CASE_OP_32_64(nand): | 
|  | return ~(x & y); | 
|  |  | 
|  | CASE_OP_32_64(nor): | 
|  | return ~(x | y); | 
|  |  | 
|  | CASE_OP_32_64(ext8s): | 
|  | return (int8_t)x; | 
|  |  | 
|  | CASE_OP_32_64(ext16s): | 
|  | return (int16_t)x; | 
|  |  | 
|  | CASE_OP_32_64(ext8u): | 
|  | return (uint8_t)x; | 
|  |  | 
|  | CASE_OP_32_64(ext16u): | 
|  | return (uint16_t)x; | 
|  |  | 
|  | case INDEX_op_ext32s_i64: | 
|  | return (int32_t)x; | 
|  |  | 
|  | case INDEX_op_ext32u_i64: | 
|  | return (uint32_t)x; | 
|  |  | 
|  | case INDEX_op_muluh_i32: | 
|  | return ((uint64_t)(uint32_t)x * (uint32_t)y) >> 32; | 
|  | case INDEX_op_mulsh_i32: | 
|  | return ((int64_t)(int32_t)x * (int32_t)y) >> 32; | 
|  |  | 
|  | case INDEX_op_muluh_i64: | 
|  | mulu64(&l64, &h64, x, y); | 
|  | return h64; | 
|  | case INDEX_op_mulsh_i64: | 
|  | muls64(&l64, &h64, x, y); | 
|  | return h64; | 
|  |  | 
|  | case INDEX_op_div_i32: | 
|  | /* Avoid crashing on divide by zero, otherwise undefined.  */ | 
|  | return (int32_t)x / ((int32_t)y ? : 1); | 
|  | case INDEX_op_divu_i32: | 
|  | return (uint32_t)x / ((uint32_t)y ? : 1); | 
|  | case INDEX_op_div_i64: | 
|  | return (int64_t)x / ((int64_t)y ? : 1); | 
|  | case INDEX_op_divu_i64: | 
|  | return (uint64_t)x / ((uint64_t)y ? : 1); | 
|  |  | 
|  | case INDEX_op_rem_i32: | 
|  | return (int32_t)x % ((int32_t)y ? : 1); | 
|  | case INDEX_op_remu_i32: | 
|  | return (uint32_t)x % ((uint32_t)y ? : 1); | 
|  | case INDEX_op_rem_i64: | 
|  | return (int64_t)x % ((int64_t)y ? : 1); | 
|  | case INDEX_op_remu_i64: | 
|  | return (uint64_t)x % ((uint64_t)y ? : 1); | 
|  |  | 
|  | default: | 
|  | fprintf(stderr, | 
|  | "Unrecognized operation %d in do_constant_folding.\n", op); | 
|  | tcg_abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static TCGArg do_constant_folding(TCGOpcode op, TCGArg x, TCGArg y) | 
|  | { | 
|  | TCGArg res = do_constant_folding_2(op, x, y); | 
|  | if (op_bits(op) == 32) { | 
|  | res &= 0xffffffff; | 
|  | } | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static bool do_constant_folding_cond_32(uint32_t x, uint32_t y, TCGCond c) | 
|  | { | 
|  | switch (c) { | 
|  | case TCG_COND_EQ: | 
|  | return x == y; | 
|  | case TCG_COND_NE: | 
|  | return x != y; | 
|  | case TCG_COND_LT: | 
|  | return (int32_t)x < (int32_t)y; | 
|  | case TCG_COND_GE: | 
|  | return (int32_t)x >= (int32_t)y; | 
|  | case TCG_COND_LE: | 
|  | return (int32_t)x <= (int32_t)y; | 
|  | case TCG_COND_GT: | 
|  | return (int32_t)x > (int32_t)y; | 
|  | case TCG_COND_LTU: | 
|  | return x < y; | 
|  | case TCG_COND_GEU: | 
|  | return x >= y; | 
|  | case TCG_COND_LEU: | 
|  | return x <= y; | 
|  | case TCG_COND_GTU: | 
|  | return x > y; | 
|  | default: | 
|  | tcg_abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool do_constant_folding_cond_64(uint64_t x, uint64_t y, TCGCond c) | 
|  | { | 
|  | switch (c) { | 
|  | case TCG_COND_EQ: | 
|  | return x == y; | 
|  | case TCG_COND_NE: | 
|  | return x != y; | 
|  | case TCG_COND_LT: | 
|  | return (int64_t)x < (int64_t)y; | 
|  | case TCG_COND_GE: | 
|  | return (int64_t)x >= (int64_t)y; | 
|  | case TCG_COND_LE: | 
|  | return (int64_t)x <= (int64_t)y; | 
|  | case TCG_COND_GT: | 
|  | return (int64_t)x > (int64_t)y; | 
|  | case TCG_COND_LTU: | 
|  | return x < y; | 
|  | case TCG_COND_GEU: | 
|  | return x >= y; | 
|  | case TCG_COND_LEU: | 
|  | return x <= y; | 
|  | case TCG_COND_GTU: | 
|  | return x > y; | 
|  | default: | 
|  | tcg_abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool do_constant_folding_cond_eq(TCGCond c) | 
|  | { | 
|  | switch (c) { | 
|  | case TCG_COND_GT: | 
|  | case TCG_COND_LTU: | 
|  | case TCG_COND_LT: | 
|  | case TCG_COND_GTU: | 
|  | case TCG_COND_NE: | 
|  | return 0; | 
|  | case TCG_COND_GE: | 
|  | case TCG_COND_GEU: | 
|  | case TCG_COND_LE: | 
|  | case TCG_COND_LEU: | 
|  | case TCG_COND_EQ: | 
|  | return 1; | 
|  | default: | 
|  | tcg_abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Return 2 if the condition can't be simplified, and the result | 
|  | of the condition (0 or 1) if it can */ | 
|  | static TCGArg do_constant_folding_cond(TCGOpcode op, TCGArg x, | 
|  | TCGArg y, TCGCond c) | 
|  | { | 
|  | if (temps[x].state == TCG_TEMP_CONST && temps[y].state == TCG_TEMP_CONST) { | 
|  | switch (op_bits(op)) { | 
|  | case 32: | 
|  | return do_constant_folding_cond_32(temps[x].val, temps[y].val, c); | 
|  | case 64: | 
|  | return do_constant_folding_cond_64(temps[x].val, temps[y].val, c); | 
|  | default: | 
|  | tcg_abort(); | 
|  | } | 
|  | } else if (temps_are_copies(x, y)) { | 
|  | return do_constant_folding_cond_eq(c); | 
|  | } else if (temps[y].state == TCG_TEMP_CONST && temps[y].val == 0) { | 
|  | switch (c) { | 
|  | case TCG_COND_LTU: | 
|  | return 0; | 
|  | case TCG_COND_GEU: | 
|  | return 1; | 
|  | default: | 
|  | return 2; | 
|  | } | 
|  | } else { | 
|  | return 2; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Return 2 if the condition can't be simplified, and the result | 
|  | of the condition (0 or 1) if it can */ | 
|  | static TCGArg do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) | 
|  | { | 
|  | TCGArg al = p1[0], ah = p1[1]; | 
|  | TCGArg bl = p2[0], bh = p2[1]; | 
|  |  | 
|  | if (temps[bl].state == TCG_TEMP_CONST | 
|  | && temps[bh].state == TCG_TEMP_CONST) { | 
|  | uint64_t b = ((uint64_t)temps[bh].val << 32) | (uint32_t)temps[bl].val; | 
|  |  | 
|  | if (temps[al].state == TCG_TEMP_CONST | 
|  | && temps[ah].state == TCG_TEMP_CONST) { | 
|  | uint64_t a; | 
|  | a = ((uint64_t)temps[ah].val << 32) | (uint32_t)temps[al].val; | 
|  | return do_constant_folding_cond_64(a, b, c); | 
|  | } | 
|  | if (b == 0) { | 
|  | switch (c) { | 
|  | case TCG_COND_LTU: | 
|  | return 0; | 
|  | case TCG_COND_GEU: | 
|  | return 1; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (temps_are_copies(al, bl) && temps_are_copies(ah, bh)) { | 
|  | return do_constant_folding_cond_eq(c); | 
|  | } | 
|  | return 2; | 
|  | } | 
|  |  | 
|  | static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) | 
|  | { | 
|  | TCGArg a1 = *p1, a2 = *p2; | 
|  | int sum = 0; | 
|  | sum += temps[a1].state == TCG_TEMP_CONST; | 
|  | sum -= temps[a2].state == TCG_TEMP_CONST; | 
|  |  | 
|  | /* Prefer the constant in second argument, and then the form | 
|  | op a, a, b, which is better handled on non-RISC hosts. */ | 
|  | if (sum > 0 || (sum == 0 && dest == a2)) { | 
|  | *p1 = a2; | 
|  | *p2 = a1; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool swap_commutative2(TCGArg *p1, TCGArg *p2) | 
|  | { | 
|  | int sum = 0; | 
|  | sum += temps[p1[0]].state == TCG_TEMP_CONST; | 
|  | sum += temps[p1[1]].state == TCG_TEMP_CONST; | 
|  | sum -= temps[p2[0]].state == TCG_TEMP_CONST; | 
|  | sum -= temps[p2[1]].state == TCG_TEMP_CONST; | 
|  | if (sum > 0) { | 
|  | TCGArg t; | 
|  | t = p1[0], p1[0] = p2[0], p2[0] = t; | 
|  | t = p1[1], p1[1] = p2[1], p2[1] = t; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Propagate constants and copies, fold constant expressions. */ | 
|  | static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, | 
|  | TCGArg *args, TCGOpDef *tcg_op_defs) | 
|  | { | 
|  | int nb_ops, op_index, nb_temps, nb_globals; | 
|  | TCGArg *gen_args; | 
|  |  | 
|  | /* Array VALS has an element for each temp. | 
|  | If this temp holds a constant then its value is kept in VALS' element. | 
|  | If this temp is a copy of other ones then the other copies are | 
|  | available through the doubly linked circular list. */ | 
|  |  | 
|  | nb_temps = s->nb_temps; | 
|  | nb_globals = s->nb_globals; | 
|  | reset_all_temps(nb_temps); | 
|  |  | 
|  | nb_ops = tcg_opc_ptr - s->gen_opc_buf; | 
|  | gen_args = args; | 
|  | for (op_index = 0; op_index < nb_ops; op_index++) { | 
|  | TCGOpcode op = s->gen_opc_buf[op_index]; | 
|  | const TCGOpDef *def = &tcg_op_defs[op]; | 
|  | tcg_target_ulong mask, partmask, affected; | 
|  | int nb_oargs, nb_iargs, nb_args, i; | 
|  | TCGArg tmp; | 
|  |  | 
|  | if (op == INDEX_op_call) { | 
|  | *gen_args++ = tmp = *args++; | 
|  | nb_oargs = tmp >> 16; | 
|  | nb_iargs = tmp & 0xffff; | 
|  | nb_args = nb_oargs + nb_iargs + def->nb_cargs; | 
|  | } else { | 
|  | nb_oargs = def->nb_oargs; | 
|  | nb_iargs = def->nb_iargs; | 
|  | nb_args = def->nb_args; | 
|  | } | 
|  |  | 
|  | /* Do copy propagation */ | 
|  | for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { | 
|  | if (temps[args[i]].state == TCG_TEMP_COPY) { | 
|  | args[i] = find_better_copy(s, args[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* For commutative operations make constant second argument */ | 
|  | switch (op) { | 
|  | CASE_OP_32_64(add): | 
|  | CASE_OP_32_64(mul): | 
|  | CASE_OP_32_64(and): | 
|  | CASE_OP_32_64(or): | 
|  | CASE_OP_32_64(xor): | 
|  | CASE_OP_32_64(eqv): | 
|  | CASE_OP_32_64(nand): | 
|  | CASE_OP_32_64(nor): | 
|  | CASE_OP_32_64(muluh): | 
|  | CASE_OP_32_64(mulsh): | 
|  | swap_commutative(args[0], &args[1], &args[2]); | 
|  | break; | 
|  | CASE_OP_32_64(brcond): | 
|  | if (swap_commutative(-1, &args[0], &args[1])) { | 
|  | args[2] = tcg_swap_cond(args[2]); | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(setcond): | 
|  | if (swap_commutative(args[0], &args[1], &args[2])) { | 
|  | args[3] = tcg_swap_cond(args[3]); | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(movcond): | 
|  | if (swap_commutative(-1, &args[1], &args[2])) { | 
|  | args[5] = tcg_swap_cond(args[5]); | 
|  | } | 
|  | /* For movcond, we canonicalize the "false" input reg to match | 
|  | the destination reg so that the tcg backend can implement | 
|  | a "move if true" operation.  */ | 
|  | if (swap_commutative(args[0], &args[4], &args[3])) { | 
|  | args[5] = tcg_invert_cond(args[5]); | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(add2): | 
|  | swap_commutative(args[0], &args[2], &args[4]); | 
|  | swap_commutative(args[1], &args[3], &args[5]); | 
|  | break; | 
|  | CASE_OP_32_64(mulu2): | 
|  | CASE_OP_32_64(muls2): | 
|  | swap_commutative(args[0], &args[2], &args[3]); | 
|  | break; | 
|  | case INDEX_op_brcond2_i32: | 
|  | if (swap_commutative2(&args[0], &args[2])) { | 
|  | args[4] = tcg_swap_cond(args[4]); | 
|  | } | 
|  | break; | 
|  | case INDEX_op_setcond2_i32: | 
|  | if (swap_commutative2(&args[1], &args[3])) { | 
|  | args[5] = tcg_swap_cond(args[5]); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Simplify expressions for "shift/rot r, 0, a => movi r, 0", | 
|  | and "sub r, 0, a => neg r, a" case.  */ | 
|  | switch (op) { | 
|  | CASE_OP_32_64(shl): | 
|  | CASE_OP_32_64(shr): | 
|  | CASE_OP_32_64(sar): | 
|  | CASE_OP_32_64(rotl): | 
|  | CASE_OP_32_64(rotr): | 
|  | if (temps[args[1]].state == TCG_TEMP_CONST | 
|  | && temps[args[1]].val == 0) { | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); | 
|  | args += 3; | 
|  | gen_args += 2; | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(sub): | 
|  | { | 
|  | TCGOpcode neg_op; | 
|  | bool have_neg; | 
|  |  | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | /* Proceed with possible constant folding. */ | 
|  | break; | 
|  | } | 
|  | if (op == INDEX_op_sub_i32) { | 
|  | neg_op = INDEX_op_neg_i32; | 
|  | have_neg = TCG_TARGET_HAS_neg_i32; | 
|  | } else { | 
|  | neg_op = INDEX_op_neg_i64; | 
|  | have_neg = TCG_TARGET_HAS_neg_i64; | 
|  | } | 
|  | if (!have_neg) { | 
|  | break; | 
|  | } | 
|  | if (temps[args[1]].state == TCG_TEMP_CONST | 
|  | && temps[args[1]].val == 0) { | 
|  | s->gen_opc_buf[op_index] = neg_op; | 
|  | reset_temp(args[0]); | 
|  | gen_args[0] = args[0]; | 
|  | gen_args[1] = args[2]; | 
|  | args += 3; | 
|  | gen_args += 2; | 
|  | continue; | 
|  | } | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(xor): | 
|  | CASE_OP_32_64(nand): | 
|  | if (temps[args[1]].state != TCG_TEMP_CONST | 
|  | && temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].val == -1) { | 
|  | i = 1; | 
|  | goto try_not; | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(nor): | 
|  | if (temps[args[1]].state != TCG_TEMP_CONST | 
|  | && temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].val == 0) { | 
|  | i = 1; | 
|  | goto try_not; | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(andc): | 
|  | if (temps[args[2]].state != TCG_TEMP_CONST | 
|  | && temps[args[1]].state == TCG_TEMP_CONST | 
|  | && temps[args[1]].val == -1) { | 
|  | i = 2; | 
|  | goto try_not; | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(orc): | 
|  | CASE_OP_32_64(eqv): | 
|  | if (temps[args[2]].state != TCG_TEMP_CONST | 
|  | && temps[args[1]].state == TCG_TEMP_CONST | 
|  | && temps[args[1]].val == 0) { | 
|  | i = 2; | 
|  | goto try_not; | 
|  | } | 
|  | break; | 
|  | try_not: | 
|  | { | 
|  | TCGOpcode not_op; | 
|  | bool have_not; | 
|  |  | 
|  | if (def->flags & TCG_OPF_64BIT) { | 
|  | not_op = INDEX_op_not_i64; | 
|  | have_not = TCG_TARGET_HAS_not_i64; | 
|  | } else { | 
|  | not_op = INDEX_op_not_i32; | 
|  | have_not = TCG_TARGET_HAS_not_i32; | 
|  | } | 
|  | if (!have_not) { | 
|  | break; | 
|  | } | 
|  | s->gen_opc_buf[op_index] = not_op; | 
|  | reset_temp(args[0]); | 
|  | gen_args[0] = args[0]; | 
|  | gen_args[1] = args[i]; | 
|  | args += 3; | 
|  | gen_args += 2; | 
|  | continue; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Simplify expression for "op r, a, const => mov r, a" cases */ | 
|  | switch (op) { | 
|  | CASE_OP_32_64(add): | 
|  | CASE_OP_32_64(sub): | 
|  | CASE_OP_32_64(shl): | 
|  | CASE_OP_32_64(shr): | 
|  | CASE_OP_32_64(sar): | 
|  | CASE_OP_32_64(rotl): | 
|  | CASE_OP_32_64(rotr): | 
|  | CASE_OP_32_64(or): | 
|  | CASE_OP_32_64(xor): | 
|  | CASE_OP_32_64(andc): | 
|  | if (temps[args[1]].state != TCG_TEMP_CONST | 
|  | && temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].val == 0) { | 
|  | goto do_mov3; | 
|  | } | 
|  | break; | 
|  | CASE_OP_32_64(and): | 
|  | CASE_OP_32_64(orc): | 
|  | CASE_OP_32_64(eqv): | 
|  | if (temps[args[1]].state != TCG_TEMP_CONST | 
|  | && temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].val == -1) { | 
|  | goto do_mov3; | 
|  | } | 
|  | break; | 
|  | do_mov3: | 
|  | if (temps_are_copies(args[0], args[1])) { | 
|  | s->gen_opc_buf[op_index] = INDEX_op_nop; | 
|  | } else { | 
|  | tcg_opt_gen_mov(s, op_index, gen_args, op, args[0], args[1]); | 
|  | gen_args += 2; | 
|  | } | 
|  | args += 3; | 
|  | continue; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Simplify using known-zero bits. Currently only ops with a single | 
|  | output argument is supported. */ | 
|  | mask = -1; | 
|  | affected = -1; | 
|  | switch (op) { | 
|  | CASE_OP_32_64(ext8s): | 
|  | if ((temps[args[1]].mask & 0x80) != 0) { | 
|  | break; | 
|  | } | 
|  | CASE_OP_32_64(ext8u): | 
|  | mask = 0xff; | 
|  | goto and_const; | 
|  | CASE_OP_32_64(ext16s): | 
|  | if ((temps[args[1]].mask & 0x8000) != 0) { | 
|  | break; | 
|  | } | 
|  | CASE_OP_32_64(ext16u): | 
|  | mask = 0xffff; | 
|  | goto and_const; | 
|  | case INDEX_op_ext32s_i64: | 
|  | if ((temps[args[1]].mask & 0x80000000) != 0) { | 
|  | break; | 
|  | } | 
|  | case INDEX_op_ext32u_i64: | 
|  | mask = 0xffffffffU; | 
|  | goto and_const; | 
|  |  | 
|  | CASE_OP_32_64(and): | 
|  | mask = temps[args[2]].mask; | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | and_const: | 
|  | affected = temps[args[1]].mask & ~mask; | 
|  | } | 
|  | mask = temps[args[1]].mask & mask; | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(andc): | 
|  | /* Known-zeros does not imply known-ones.  Therefore unless | 
|  | args[2] is constant, we can't infer anything from it.  */ | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | mask = ~temps[args[2]].mask; | 
|  | goto and_const; | 
|  | } | 
|  | /* But we certainly know nothing outside args[1] may be set. */ | 
|  | mask = temps[args[1]].mask; | 
|  | break; | 
|  |  | 
|  | case INDEX_op_sar_i32: | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | tmp = temps[args[2]].val & 31; | 
|  | mask = (int32_t)temps[args[1]].mask >> tmp; | 
|  | } | 
|  | break; | 
|  | case INDEX_op_sar_i64: | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | tmp = temps[args[2]].val & 63; | 
|  | mask = (int64_t)temps[args[1]].mask >> tmp; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case INDEX_op_shr_i32: | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | tmp = temps[args[2]].val & 31; | 
|  | mask = (uint32_t)temps[args[1]].mask >> tmp; | 
|  | } | 
|  | break; | 
|  | case INDEX_op_shr_i64: | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | tmp = temps[args[2]].val & 63; | 
|  | mask = (uint64_t)temps[args[1]].mask >> tmp; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case INDEX_op_trunc_shr_i32: | 
|  | mask = (uint64_t)temps[args[1]].mask >> args[2]; | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(shl): | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | tmp = temps[args[2]].val & (TCG_TARGET_REG_BITS - 1); | 
|  | mask = temps[args[1]].mask << tmp; | 
|  | } | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(neg): | 
|  | /* Set to 1 all bits to the left of the rightmost.  */ | 
|  | mask = -(temps[args[1]].mask & -temps[args[1]].mask); | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(deposit): | 
|  | mask = deposit64(temps[args[1]].mask, args[3], args[4], | 
|  | temps[args[2]].mask); | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(or): | 
|  | CASE_OP_32_64(xor): | 
|  | mask = temps[args[1]].mask | temps[args[2]].mask; | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(setcond): | 
|  | case INDEX_op_setcond2_i32: | 
|  | mask = 1; | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(movcond): | 
|  | mask = temps[args[3]].mask | temps[args[4]].mask; | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(ld8u): | 
|  | mask = 0xff; | 
|  | break; | 
|  | CASE_OP_32_64(ld16u): | 
|  | mask = 0xffff; | 
|  | break; | 
|  | case INDEX_op_ld32u_i64: | 
|  | mask = 0xffffffffu; | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(qemu_ld): | 
|  | { | 
|  | TCGMemOp mop = args[nb_oargs + nb_iargs]; | 
|  | if (!(mop & MO_SIGN)) { | 
|  | mask = (2ULL << ((8 << (mop & MO_SIZE)) - 1)) - 1; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* 32-bit ops generate 32-bit results.  For the result is zero test | 
|  | below, we can ignore high bits, but for further optimizations we | 
|  | need to record that the high bits contain garbage.  */ | 
|  | partmask = mask; | 
|  | if (!(def->flags & TCG_OPF_64BIT)) { | 
|  | mask |= ~(tcg_target_ulong)0xffffffffu; | 
|  | partmask &= 0xffffffffu; | 
|  | affected &= 0xffffffffu; | 
|  | } | 
|  |  | 
|  | if (partmask == 0) { | 
|  | assert(nb_oargs == 1); | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); | 
|  | args += nb_args; | 
|  | gen_args += 2; | 
|  | continue; | 
|  | } | 
|  | if (affected == 0) { | 
|  | assert(nb_oargs == 1); | 
|  | if (temps_are_copies(args[0], args[1])) { | 
|  | s->gen_opc_buf[op_index] = INDEX_op_nop; | 
|  | } else if (temps[args[1]].state != TCG_TEMP_CONST) { | 
|  | tcg_opt_gen_mov(s, op_index, gen_args, op, args[0], args[1]); | 
|  | gen_args += 2; | 
|  | } else { | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, | 
|  | args[0], temps[args[1]].val); | 
|  | gen_args += 2; | 
|  | } | 
|  | args += nb_args; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Simplify expression for "op r, a, 0 => movi r, 0" cases */ | 
|  | switch (op) { | 
|  | CASE_OP_32_64(and): | 
|  | CASE_OP_32_64(mul): | 
|  | CASE_OP_32_64(muluh): | 
|  | CASE_OP_32_64(mulsh): | 
|  | if ((temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].val == 0)) { | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); | 
|  | args += 3; | 
|  | gen_args += 2; | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Simplify expression for "op r, a, a => mov r, a" cases */ | 
|  | switch (op) { | 
|  | CASE_OP_32_64(or): | 
|  | CASE_OP_32_64(and): | 
|  | if (temps_are_copies(args[1], args[2])) { | 
|  | if (temps_are_copies(args[0], args[1])) { | 
|  | s->gen_opc_buf[op_index] = INDEX_op_nop; | 
|  | } else { | 
|  | tcg_opt_gen_mov(s, op_index, gen_args, op, | 
|  | args[0], args[1]); | 
|  | gen_args += 2; | 
|  | } | 
|  | args += 3; | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Simplify expression for "op r, a, a => movi r, 0" cases */ | 
|  | switch (op) { | 
|  | CASE_OP_32_64(andc): | 
|  | CASE_OP_32_64(sub): | 
|  | CASE_OP_32_64(xor): | 
|  | if (temps_are_copies(args[1], args[2])) { | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], 0); | 
|  | gen_args += 2; | 
|  | args += 3; | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Propagate constants through copy operations and do constant | 
|  | folding.  Constants will be substituted to arguments by register | 
|  | allocator where needed and possible.  Also detect copies. */ | 
|  | switch (op) { | 
|  | CASE_OP_32_64(mov): | 
|  | if (temps_are_copies(args[0], args[1])) { | 
|  | args += 2; | 
|  | s->gen_opc_buf[op_index] = INDEX_op_nop; | 
|  | break; | 
|  | } | 
|  | if (temps[args[1]].state != TCG_TEMP_CONST) { | 
|  | tcg_opt_gen_mov(s, op_index, gen_args, op, args[0], args[1]); | 
|  | gen_args += 2; | 
|  | args += 2; | 
|  | break; | 
|  | } | 
|  | /* Source argument is constant.  Rewrite the operation and | 
|  | let movi case handle it. */ | 
|  | args[1] = temps[args[1]].val; | 
|  | /* fallthrough */ | 
|  | CASE_OP_32_64(movi): | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], args[1]); | 
|  | gen_args += 2; | 
|  | args += 2; | 
|  | break; | 
|  |  | 
|  | CASE_OP_32_64(not): | 
|  | CASE_OP_32_64(neg): | 
|  | CASE_OP_32_64(ext8s): | 
|  | CASE_OP_32_64(ext8u): | 
|  | CASE_OP_32_64(ext16s): | 
|  | CASE_OP_32_64(ext16u): | 
|  | case INDEX_op_ext32s_i64: | 
|  | case INDEX_op_ext32u_i64: | 
|  | if (temps[args[1]].state == TCG_TEMP_CONST) { | 
|  | tmp = do_constant_folding(op, temps[args[1]].val, 0); | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); | 
|  | gen_args += 2; | 
|  | args += 2; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | case INDEX_op_trunc_shr_i32: | 
|  | if (temps[args[1]].state == TCG_TEMP_CONST) { | 
|  | tmp = do_constant_folding(op, temps[args[1]].val, args[2]); | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); | 
|  | gen_args += 2; | 
|  | args += 3; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | CASE_OP_32_64(add): | 
|  | CASE_OP_32_64(sub): | 
|  | CASE_OP_32_64(mul): | 
|  | CASE_OP_32_64(or): | 
|  | CASE_OP_32_64(and): | 
|  | CASE_OP_32_64(xor): | 
|  | CASE_OP_32_64(shl): | 
|  | CASE_OP_32_64(shr): | 
|  | CASE_OP_32_64(sar): | 
|  | CASE_OP_32_64(rotl): | 
|  | CASE_OP_32_64(rotr): | 
|  | CASE_OP_32_64(andc): | 
|  | CASE_OP_32_64(orc): | 
|  | CASE_OP_32_64(eqv): | 
|  | CASE_OP_32_64(nand): | 
|  | CASE_OP_32_64(nor): | 
|  | CASE_OP_32_64(muluh): | 
|  | CASE_OP_32_64(mulsh): | 
|  | CASE_OP_32_64(div): | 
|  | CASE_OP_32_64(divu): | 
|  | CASE_OP_32_64(rem): | 
|  | CASE_OP_32_64(remu): | 
|  | if (temps[args[1]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | tmp = do_constant_folding(op, temps[args[1]].val, | 
|  | temps[args[2]].val); | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); | 
|  | gen_args += 2; | 
|  | args += 3; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | CASE_OP_32_64(deposit): | 
|  | if (temps[args[1]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].state == TCG_TEMP_CONST) { | 
|  | tmp = deposit64(temps[args[1]].val, args[3], args[4], | 
|  | temps[args[2]].val); | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); | 
|  | gen_args += 2; | 
|  | args += 5; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | CASE_OP_32_64(setcond): | 
|  | tmp = do_constant_folding_cond(op, args[1], args[2], args[3]); | 
|  | if (tmp != 2) { | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); | 
|  | gen_args += 2; | 
|  | args += 4; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | CASE_OP_32_64(brcond): | 
|  | tmp = do_constant_folding_cond(op, args[0], args[1], args[2]); | 
|  | if (tmp != 2) { | 
|  | if (tmp) { | 
|  | reset_all_temps(nb_temps); | 
|  | s->gen_opc_buf[op_index] = INDEX_op_br; | 
|  | gen_args[0] = args[3]; | 
|  | gen_args += 1; | 
|  | } else { | 
|  | s->gen_opc_buf[op_index] = INDEX_op_nop; | 
|  | } | 
|  | args += 4; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | CASE_OP_32_64(movcond): | 
|  | tmp = do_constant_folding_cond(op, args[1], args[2], args[5]); | 
|  | if (tmp != 2) { | 
|  | if (temps_are_copies(args[0], args[4-tmp])) { | 
|  | s->gen_opc_buf[op_index] = INDEX_op_nop; | 
|  | } else if (temps[args[4-tmp]].state == TCG_TEMP_CONST) { | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, | 
|  | args[0], temps[args[4-tmp]].val); | 
|  | gen_args += 2; | 
|  | } else { | 
|  | tcg_opt_gen_mov(s, op_index, gen_args, op, | 
|  | args[0], args[4-tmp]); | 
|  | gen_args += 2; | 
|  | } | 
|  | args += 6; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | case INDEX_op_add2_i32: | 
|  | case INDEX_op_sub2_i32: | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[3]].state == TCG_TEMP_CONST | 
|  | && temps[args[4]].state == TCG_TEMP_CONST | 
|  | && temps[args[5]].state == TCG_TEMP_CONST) { | 
|  | uint32_t al = temps[args[2]].val; | 
|  | uint32_t ah = temps[args[3]].val; | 
|  | uint32_t bl = temps[args[4]].val; | 
|  | uint32_t bh = temps[args[5]].val; | 
|  | uint64_t a = ((uint64_t)ah << 32) | al; | 
|  | uint64_t b = ((uint64_t)bh << 32) | bl; | 
|  | TCGArg rl, rh; | 
|  |  | 
|  | if (op == INDEX_op_add2_i32) { | 
|  | a += b; | 
|  | } else { | 
|  | a -= b; | 
|  | } | 
|  |  | 
|  | /* We emit the extra nop when we emit the add2/sub2.  */ | 
|  | assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); | 
|  |  | 
|  | rl = args[0]; | 
|  | rh = args[1]; | 
|  | tcg_opt_gen_movi(s, op_index, &gen_args[0], | 
|  | op, rl, (uint32_t)a); | 
|  | tcg_opt_gen_movi(s, ++op_index, &gen_args[2], | 
|  | op, rh, (uint32_t)(a >> 32)); | 
|  | gen_args += 4; | 
|  | args += 6; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | case INDEX_op_mulu2_i32: | 
|  | if (temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[3]].state == TCG_TEMP_CONST) { | 
|  | uint32_t a = temps[args[2]].val; | 
|  | uint32_t b = temps[args[3]].val; | 
|  | uint64_t r = (uint64_t)a * b; | 
|  | TCGArg rl, rh; | 
|  |  | 
|  | /* We emit the extra nop when we emit the mulu2.  */ | 
|  | assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); | 
|  |  | 
|  | rl = args[0]; | 
|  | rh = args[1]; | 
|  | tcg_opt_gen_movi(s, op_index, &gen_args[0], | 
|  | op, rl, (uint32_t)r); | 
|  | tcg_opt_gen_movi(s, ++op_index, &gen_args[2], | 
|  | op, rh, (uint32_t)(r >> 32)); | 
|  | gen_args += 4; | 
|  | args += 4; | 
|  | break; | 
|  | } | 
|  | goto do_default; | 
|  |  | 
|  | case INDEX_op_brcond2_i32: | 
|  | tmp = do_constant_folding_cond2(&args[0], &args[2], args[4]); | 
|  | if (tmp != 2) { | 
|  | if (tmp) { | 
|  | do_brcond_true: | 
|  | reset_all_temps(nb_temps); | 
|  | s->gen_opc_buf[op_index] = INDEX_op_br; | 
|  | gen_args[0] = args[5]; | 
|  | gen_args += 1; | 
|  | } else { | 
|  | do_brcond_false: | 
|  | s->gen_opc_buf[op_index] = INDEX_op_nop; | 
|  | } | 
|  | } else if ((args[4] == TCG_COND_LT || args[4] == TCG_COND_GE) | 
|  | && temps[args[2]].state == TCG_TEMP_CONST | 
|  | && temps[args[3]].state == TCG_TEMP_CONST | 
|  | && temps[args[2]].val == 0 | 
|  | && temps[args[3]].val == 0) { | 
|  | /* Simplify LT/GE comparisons vs zero to a single compare | 
|  | vs the high word of the input.  */ | 
|  | do_brcond_high: | 
|  | reset_all_temps(nb_temps); | 
|  | s->gen_opc_buf[op_index] = INDEX_op_brcond_i32; | 
|  | gen_args[0] = args[1]; | 
|  | gen_args[1] = args[3]; | 
|  | gen_args[2] = args[4]; | 
|  | gen_args[3] = args[5]; | 
|  | gen_args += 4; | 
|  | } else if (args[4] == TCG_COND_EQ) { | 
|  | /* Simplify EQ comparisons where one of the pairs | 
|  | can be simplified.  */ | 
|  | tmp = do_constant_folding_cond(INDEX_op_brcond_i32, | 
|  | args[0], args[2], TCG_COND_EQ); | 
|  | if (tmp == 0) { | 
|  | goto do_brcond_false; | 
|  | } else if (tmp == 1) { | 
|  | goto do_brcond_high; | 
|  | } | 
|  | tmp = do_constant_folding_cond(INDEX_op_brcond_i32, | 
|  | args[1], args[3], TCG_COND_EQ); | 
|  | if (tmp == 0) { | 
|  | goto do_brcond_false; | 
|  | } else if (tmp != 1) { | 
|  | goto do_default; | 
|  | } | 
|  | do_brcond_low: | 
|  | reset_all_temps(nb_temps); | 
|  | s->gen_opc_buf[op_index] = INDEX_op_brcond_i32; | 
|  | gen_args[0] = args[0]; | 
|  | gen_args[1] = args[2]; | 
|  | gen_args[2] = args[4]; | 
|  | gen_args[3] = args[5]; | 
|  | gen_args += 4; | 
|  | } else if (args[4] == TCG_COND_NE) { | 
|  | /* Simplify NE comparisons where one of the pairs | 
|  | can be simplified.  */ | 
|  | tmp = do_constant_folding_cond(INDEX_op_brcond_i32, | 
|  | args[0], args[2], TCG_COND_NE); | 
|  | if (tmp == 0) { | 
|  | goto do_brcond_high; | 
|  | } else if (tmp == 1) { | 
|  | goto do_brcond_true; | 
|  | } | 
|  | tmp = do_constant_folding_cond(INDEX_op_brcond_i32, | 
|  | args[1], args[3], TCG_COND_NE); | 
|  | if (tmp == 0) { | 
|  | goto do_brcond_low; | 
|  | } else if (tmp == 1) { | 
|  | goto do_brcond_true; | 
|  | } | 
|  | goto do_default; | 
|  | } else { | 
|  | goto do_default; | 
|  | } | 
|  | args += 6; | 
|  | break; | 
|  |  | 
|  | case INDEX_op_setcond2_i32: | 
|  | tmp = do_constant_folding_cond2(&args[1], &args[3], args[5]); | 
|  | if (tmp != 2) { | 
|  | do_setcond_const: | 
|  | tcg_opt_gen_movi(s, op_index, gen_args, op, args[0], tmp); | 
|  | gen_args += 2; | 
|  | } else if ((args[5] == TCG_COND_LT || args[5] == TCG_COND_GE) | 
|  | && temps[args[3]].state == TCG_TEMP_CONST | 
|  | && temps[args[4]].state == TCG_TEMP_CONST | 
|  | && temps[args[3]].val == 0 | 
|  | && temps[args[4]].val == 0) { | 
|  | /* Simplify LT/GE comparisons vs zero to a single compare | 
|  | vs the high word of the input.  */ | 
|  | do_setcond_high: | 
|  | s->gen_opc_buf[op_index] = INDEX_op_setcond_i32; | 
|  | reset_temp(args[0]); | 
|  | temps[args[0]].mask = 1; | 
|  | gen_args[0] = args[0]; | 
|  | gen_args[1] = args[2]; | 
|  | gen_args[2] = args[4]; | 
|  | gen_args[3] = args[5]; | 
|  | gen_args += 4; | 
|  | } else if (args[5] == TCG_COND_EQ) { | 
|  | /* Simplify EQ comparisons where one of the pairs | 
|  | can be simplified.  */ | 
|  | tmp = do_constant_folding_cond(INDEX_op_setcond_i32, | 
|  | args[1], args[3], TCG_COND_EQ); | 
|  | if (tmp == 0) { | 
|  | goto do_setcond_const; | 
|  | } else if (tmp == 1) { | 
|  | goto do_setcond_high; | 
|  | } | 
|  | tmp = do_constant_folding_cond(INDEX_op_setcond_i32, | 
|  | args[2], args[4], TCG_COND_EQ); | 
|  | if (tmp == 0) { | 
|  | goto do_setcond_high; | 
|  | } else if (tmp != 1) { | 
|  | goto do_default; | 
|  | } | 
|  | do_setcond_low: | 
|  | reset_temp(args[0]); | 
|  | temps[args[0]].mask = 1; | 
|  | s->gen_opc_buf[op_index] = INDEX_op_setcond_i32; | 
|  | gen_args[0] = args[0]; | 
|  | gen_args[1] = args[1]; | 
|  | gen_args[2] = args[3]; | 
|  | gen_args[3] = args[5]; | 
|  | gen_args += 4; | 
|  | } else if (args[5] == TCG_COND_NE) { | 
|  | /* Simplify NE comparisons where one of the pairs | 
|  | can be simplified.  */ | 
|  | tmp = do_constant_folding_cond(INDEX_op_setcond_i32, | 
|  | args[1], args[3], TCG_COND_NE); | 
|  | if (tmp == 0) { | 
|  | goto do_setcond_high; | 
|  | } else if (tmp == 1) { | 
|  | goto do_setcond_const; | 
|  | } | 
|  | tmp = do_constant_folding_cond(INDEX_op_setcond_i32, | 
|  | args[2], args[4], TCG_COND_NE); | 
|  | if (tmp == 0) { | 
|  | goto do_setcond_low; | 
|  | } else if (tmp == 1) { | 
|  | goto do_setcond_const; | 
|  | } | 
|  | goto do_default; | 
|  | } else { | 
|  | goto do_default; | 
|  | } | 
|  | args += 6; | 
|  | break; | 
|  |  | 
|  | case INDEX_op_call: | 
|  | if (!(args[nb_oargs + nb_iargs + 1] | 
|  | & (TCG_CALL_NO_READ_GLOBALS | TCG_CALL_NO_WRITE_GLOBALS))) { | 
|  | for (i = 0; i < nb_globals; i++) { | 
|  | reset_temp(i); | 
|  | } | 
|  | } | 
|  | goto do_reset_output; | 
|  |  | 
|  | default: | 
|  | do_default: | 
|  | /* Default case: we know nothing about operation (or were unable | 
|  | to compute the operation result) so no propagation is done. | 
|  | We trash everything if the operation is the end of a basic | 
|  | block, otherwise we only trash the output args.  "mask" is | 
|  | the non-zero bits mask for the first output arg.  */ | 
|  | if (def->flags & TCG_OPF_BB_END) { | 
|  | reset_all_temps(nb_temps); | 
|  | } else { | 
|  | do_reset_output: | 
|  | for (i = 0; i < nb_oargs; i++) { | 
|  | reset_temp(args[i]); | 
|  | /* Save the corresponding known-zero bits mask for the | 
|  | first output argument (only one supported so far). */ | 
|  | if (i == 0) { | 
|  | temps[args[i]].mask = mask; | 
|  | } | 
|  | } | 
|  | } | 
|  | for (i = 0; i < nb_args; i++) { | 
|  | gen_args[i] = args[i]; | 
|  | } | 
|  | args += nb_args; | 
|  | gen_args += nb_args; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return gen_args; | 
|  | } | 
|  |  | 
|  | TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr, | 
|  | TCGArg *args, TCGOpDef *tcg_op_defs) | 
|  | { | 
|  | TCGArg *res; | 
|  | res = tcg_constant_folding(s, tcg_opc_ptr, args, tcg_op_defs); | 
|  | return res; | 
|  | } |