| /* Subroutines used for code generation on Ubicom IP2022 |
| Communications Controller. |
| Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. |
| Contributed by Red Hat, Inc and Ubicom, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "real.h" |
| #include "insn-config.h" |
| #include "conditions.h" |
| #include "insn-flags.h" |
| #include "output.h" |
| #include "insn-attr.h" |
| #include "insn-addr.h" |
| #include "flags.h" |
| #include "reload.h" |
| #include "tree.h" |
| #include "expr.h" |
| #include "optabs.h" |
| #include "toplev.h" |
| #include "obstack.h" |
| #include "function.h" |
| #include "recog.h" |
| #include "tm_p.h" |
| #include "target.h" |
| #include "target-def.h" |
| #include "basic-block.h" |
| |
| /* There are problems with 'frame_pointer_needed'. If we force it |
| on, we either end up not eliminating uses of FP, which results in |
| SPILL register failures or we may end up with calculation errors in |
| the stack offsets. Isolate the decision process into a simple macro. */ |
| #define CHAIN_FRAMES (frame_pointer_needed || FRAME_POINTER_REQUIRED) |
| |
| static int ip2k_naked_function_p (tree); |
| #ifdef IP2K_MD_REORG_PASS |
| static void mdr_resequence_xy_yx (rtx); |
| static void mdr_pres_replace_and_recurse (rtx, rtx, rtx); |
| static void mdr_propagate_reg_equivs_sequence (rtx, rtx, rtx); |
| static void mdr_propagate_reg_equivs (rtx); |
| static int track_dp_reload (rtx , rtx *, int , int); |
| static void mdr_try_dp_reload_elim (rtx); |
| static void mdr_try_move_dp_reload (rtx); |
| static void mdr_try_move_pushes (rtx); |
| static void mdr_try_propagate_clr_sequence (rtx, unsigned int); |
| static void mdr_try_propagate_clr (rtx); |
| static void mdr_try_propagate_move_sequence (rtx, rtx, rtx); |
| static void mdr_try_propagate_move (rtx); |
| static void mdr_try_remove_redundant_insns (rtx); |
| static int track_w_reload (rtx, rtx *, int , int); |
| static void mdr_try_wreg_elim (rtx); |
| #endif /* IP2K_MD_REORG_PASS */ |
| static void ip2k_reorg (void); |
| static int ip2k_check_can_adjust_stack_ref (rtx, int); |
| static void ip2k_adjust_stack_ref (rtx *, int); |
| static int ip2k_xexp_not_uses_reg_for_mem (rtx, unsigned int); |
| static tree ip2k_handle_progmem_attribute (tree *, tree, tree, int, bool *); |
| static tree ip2k_handle_fndecl_attribute (tree *, tree, tree, int, bool *); |
| static bool ip2k_rtx_costs (rtx, int, int, int *); |
| static int ip2k_address_cost (rtx); |
| static void ip2k_init_libfuncs (void); |
| |
| const struct attribute_spec ip2k_attribute_table[]; |
| |
| |
| /* Initialize the GCC target structure. */ |
| #undef TARGET_ASM_ALIGNED_HI_OP |
| #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" |
| |
| #undef TARGET_ASM_FUNCTION_PROLOGUE |
| #define TARGET_ASM_FUNCTION_PROLOGUE function_prologue |
| |
| #undef TARGET_ASM_FUNCTION_EPILOGUE |
| #define TARGET_ASM_FUNCTION_EPILOGUE function_epilogue |
| |
| #undef TARGET_ASM_UNIQUE_SECTION |
| #define TARGET_ASM_UNIQUE_SECTION unique_section |
| |
| #undef TARGET_ATTRIBUTE_TABLE |
| #define TARGET_ATTRIBUTE_TABLE ip2k_attribute_table |
| |
| #undef TARGET_RTX_COSTS |
| #define TARGET_RTX_COSTS ip2k_rtx_costs |
| #undef TARGET_ADDRESS_COST |
| #define TARGET_ADDRESS_COST ip2k_address_cost |
| |
| #undef TARGET_MACHINE_DEPENDENT_REORG |
| #define TARGET_MACHINE_DEPENDENT_REORG ip2k_reorg |
| |
| #undef TARGET_INIT_LIBFUNCS |
| #define TARGET_INIT_LIBFUNCS ip2k_init_libfuncs |
| |
| struct gcc_target targetm = TARGET_INITIALIZER; |
| |
| /* Prologue/Epilogue size in words. */ |
| static int prologue_size; |
| static int epilogue_size; |
| |
| /* compare and test instructions for the IP2K are materialized by |
| the conditional branch that uses them. This is because conditional |
| branches are skips over unconditional branches. */ |
| rtx ip2k_compare_operands[3]; /* Additional operands for condition code. */ |
| int ip2k_test_flag; /* Indicates Z, WREG contain condition code |
| information. */ |
| |
| /* Some ip2k patterns push a byte onto the stack and then access |
| SP-relative addresses. Since reload doesn't know about these |
| pushes, we must track them internally with a %< (push) or %> (pop) |
| indicator. */ |
| static int ip2k_stack_delta; |
| |
| /* Track if or how far our ip2k reorganization pass has run. */ |
| int ip2k_reorg_in_progress = 0; |
| int ip2k_reorg_completed = 0; |
| int ip2k_reorg_split_dimode = 0; |
| int ip2k_reorg_split_simode = 0; |
| int ip2k_reorg_split_himode = 0; |
| int ip2k_reorg_split_qimode = 0; |
| int ip2k_reorg_merge_qimode = 0; |
| |
| /* Set up local allocation order. */ |
| |
| void |
| ip2k_init_local_alloc (int *rao) |
| { |
| static const int alloc_order[] = REG_ALLOC_ORDER; |
| |
| memcpy (rao, alloc_order, sizeof (alloc_order)); |
| } |
| |
| /* Returns the number of bytes of arguments automatically |
| popped when returning from a subroutine call. |
| FUNDECL is the declaration node of the function (as a tree), |
| FUNTYPE is the data type of the function (as a tree), |
| or for a library call it is an identifier node for the subroutine name. |
| SIZE is the number of bytes of arguments passed on the stack. */ |
| |
| int |
| ip2k_return_pops_args (tree fundecl ATTRIBUTE_UNUSED, tree funtype, int size) |
| { |
| if (TREE_CODE (funtype) == IDENTIFIER_NODE) |
| return size; |
| |
| if (TYPE_ARG_TYPES (funtype) == NULL_TREE |
| || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node)) |
| return size; |
| |
| return 0; |
| } |
| |
| /* Return nonzero if FUNC is a naked function. */ |
| |
| static int |
| ip2k_naked_function_p (tree func) |
| { |
| tree a; |
| |
| if (TREE_CODE (func) != FUNCTION_DECL) |
| abort (); |
| |
| a = lookup_attribute ("naked", DECL_ATTRIBUTES (func)); |
| return a != NULL_TREE; |
| } |
| |
| /* Output function prologue. */ |
| void |
| function_prologue (FILE *file, HOST_WIDE_INT size) |
| { |
| int leaf_func_p; |
| int main_p; |
| int reg; |
| rtx operands[2]; |
| |
| prologue_size = epilogue_size = 0; |
| |
| if (ip2k_naked_function_p (current_function_decl)) |
| { |
| fprintf (file, "/* prologue: naked */\n"); |
| return; |
| } |
| |
| leaf_func_p = leaf_function_p (); |
| main_p = MAIN_NAME_P (DECL_NAME (current_function_decl)); |
| |
| /* For now, we compute all these facts about the function, but don't |
| take any action based on the information. */ |
| |
| prologue_size = 0; |
| fprintf (file, "/* prologue: frame size=" HOST_WIDE_INT_PRINT_DEC " */\n", |
| size); |
| |
| /* Unless we're a leaf we need to save the return PC. */ |
| |
| if (! leaf_func_p) |
| { |
| OUT_AS1 (push, calll); |
| OUT_AS1 (push, callh); |
| prologue_size += 4; |
| } |
| |
| /* We need to save the old FP and set the new FP pointing at the |
| stack location where the old one is saved. Note that because of |
| post-decrement addressing, the SP is off-by-one after the |
| push, so we harvest the SP address BEFORE we push the MSBs of |
| the FP. */ |
| if (CHAIN_FRAMES) |
| { |
| OUT_AS1 (push, REG_FP+1); /* Save old LSBs. */ |
| OUT_AS2 (mov, w, spl); |
| OUT_AS2 (mov, REG_FP+1, w); /* SPL -> FPL */ |
| |
| OUT_AS2 (mov, w, sph); /* Freeze SP MSBs */ |
| OUT_AS1 (push, REG_FP); /* Save old MSBs */ |
| OUT_AS2 (mov, REG_FP, w); /* SPH -> FPH */ |
| prologue_size += 12; |
| } |
| |
| for (reg = (CHAIN_FRAMES) ? (REG_FP - 1) : (REG_FP + 1); |
| reg > 0; --reg) |
| { |
| if (regs_ever_live[reg] && ! call_used_regs[reg]) |
| { |
| fprintf (file, "\t" AS1 (push,%s) "\n", reg_names[reg]); |
| prologue_size += 2; |
| } |
| } |
| |
| if (size) |
| { |
| operands[0] = GEN_INT (size); |
| |
| switch (size & 0xff) |
| { |
| case 0: |
| break; |
| case 1: |
| OUT_AS1 (dec, spl); |
| prologue_size += 2; |
| break; |
| default: |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS2 (sub, spl, w); |
| prologue_size += 4; |
| } |
| |
| switch (size & 0xff00) |
| { |
| case 0: |
| break; |
| case 0x100: |
| OUT_AS1 (dec, sph); |
| prologue_size += 2; |
| break; |
| default: |
| if ((size & 0xff) != ((size >> 8) & 0xff)) |
| OUT_AS2 (mov, w, %H0); /* Otherwise W has value we want. */ |
| OUT_AS2 (sub, sph, w); |
| prologue_size += 4; |
| } |
| } |
| |
| /* XXX - change this to use the carry-propagating subtract trick. */ |
| if (flag_stack_check) |
| { |
| OUT_AS2 (mov, w, sph); |
| OUT_AS2 (cmp, w, #%%hi8data(_end)); |
| OUT_AS1 (sc, ); /* C == 0 -> hi8(edata) < sph */ |
| OUT_AS1 (page, 1f); |
| OUT_AS1 (jmp, 1f); |
| OUT_AS1 (sz, ); /* Z == 1 -> look at low byte */ |
| OUT_AS1 (page,0f); |
| OUT_AS1 (jmp,0f); /* sp < edata, so raise stack fault */ |
| OUT_AS2 (mov, w, spl); |
| OUT_AS2 (cmp, w, #%%lo8data(_end)); |
| OUT_AS1 (sc,); /* C==1 -> lo8(edata) >= spl */ |
| OUT_AS1 (page,1f); |
| OUT_AS1 (jmp,1f); |
| OUT_AS1 (0:,); |
| output_asm_insn ("push\t$ff", operands); |
| OUT_AS1 (system,); |
| OUT_AS1 (1:, ); |
| prologue_size += 30; |
| } |
| } |
| |
| /* Output function epilogue. */ |
| void |
| function_epilogue (FILE *file, HOST_WIDE_INT size) |
| { |
| int leaf_func_p; |
| int reg,savelimit; |
| rtx operands[2]; /* Dummy used by OUT_ASn */ |
| int args_locals_size = current_function_args_size; |
| int saved_regs_p = 0; |
| int need_ret = 1; |
| |
| /* Use this opportunity to reset the reorg flags! */ |
| ip2k_reorg_in_progress = 0; |
| ip2k_reorg_completed = 0; |
| ip2k_reorg_split_dimode = 0; |
| ip2k_reorg_split_simode = 0; |
| ip2k_reorg_split_himode = 0; |
| ip2k_reorg_split_qimode = 0; |
| ip2k_reorg_merge_qimode = 0; |
| |
| if (ip2k_naked_function_p (current_function_decl)) |
| { |
| fprintf (file, "/* epilogue: naked */\n"); |
| return; |
| } |
| |
| leaf_func_p = leaf_function_p (); |
| epilogue_size = 0; |
| fprintf (file, "/* epilogue: frame size=" HOST_WIDE_INT_PRINT_DEC " */\n", |
| size); |
| |
| savelimit = (CHAIN_FRAMES) ? REG_FP : (REG_FP + 2); |
| for (reg = 0; reg < savelimit; reg++) |
| if (regs_ever_live[reg] && ! call_used_regs[reg]) |
| { |
| saved_regs_p = 1; |
| break; |
| } |
| |
| if (size) |
| { |
| if (leaf_func_p && !CHAIN_FRAMES && !saved_regs_p |
| && current_function_pops_args) |
| args_locals_size = current_function_args_size + size; |
| else |
| { |
| operands[0] = GEN_INT (size); |
| |
| switch (size & 0xff) |
| { |
| default: |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS2 (add, spl, w); |
| epilogue_size += 4; |
| /* fall-through */ |
| case 0: |
| break; |
| case 1: |
| OUT_AS1 (inc, spl); |
| epilogue_size += 2; |
| } |
| |
| switch (size & 0xff00) |
| { |
| default: |
| if ((size & 0xff) != ((size >> 8) & 0xff)) |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (add, sph, w); |
| epilogue_size += 4; |
| /* fall-through */ |
| case 0: |
| break; |
| case 0x100: |
| OUT_AS1 (inc, sph); |
| epilogue_size += 2; |
| } |
| } |
| } |
| |
| for (reg = 0; reg < savelimit; reg++) |
| { |
| if (regs_ever_live[reg] && ! call_used_regs[reg]) |
| { |
| fprintf (file, "\t" AS1 (pop,%s) "\n", reg_names[reg]); |
| prologue_size += 2; |
| } |
| } |
| |
| if (CHAIN_FRAMES |
| && ! (current_function_pops_args |
| && current_function_args_size >= 2 |
| && current_function_args_size < 0x100)) |
| { |
| OUT_AS1 (pop, REG_FP); |
| OUT_AS1 (pop, REG_FP+1); |
| epilogue_size += 4; |
| } |
| |
| if (! leaf_func_p) |
| { |
| if (current_function_pops_args |
| && current_function_args_size >= 2 |
| && current_function_args_size < 0x100) |
| { |
| if (current_function_args_size == 2) |
| { |
| if (CHAIN_FRAMES) |
| { |
| OUT_AS1 (page, __fp_pop2_args_ret); |
| OUT_AS1 (jmp, __fp_pop2_args_ret); |
| } |
| else |
| { |
| OUT_AS1 (page, __pop2_args_ret); |
| OUT_AS1 (jmp, __pop2_args_ret); |
| } |
| epilogue_size += 4; |
| } |
| else |
| { |
| operands[0] = GEN_INT (current_function_args_size); |
| OUT_AS2 (mov, w, %L0); |
| if (CHAIN_FRAMES) |
| { |
| OUT_AS1 (page, __fp_pop_args_ret); |
| OUT_AS1 (jmp, __fp_pop_args_ret); |
| } |
| else |
| { |
| OUT_AS1 (page, __pop_args_ret); |
| OUT_AS1 (jmp, __pop_args_ret); |
| } |
| epilogue_size += 6; |
| } |
| need_ret = 0; |
| } |
| else |
| { |
| OUT_AS1 (pop, callh); |
| OUT_AS1 (pop, calll); |
| epilogue_size += 4; |
| } |
| } |
| else |
| { |
| if (current_function_pops_args |
| && args_locals_size >= 2 |
| && args_locals_size < 0x100) |
| { |
| if (args_locals_size == 2) |
| { |
| if (CHAIN_FRAMES) |
| { |
| OUT_AS1 (page, __leaf_fp_pop2_args_ret); |
| OUT_AS1 (jmp, __leaf_fp_pop2_args_ret); |
| epilogue_size += 4; |
| need_ret = 0; |
| } |
| } |
| else |
| { |
| operands[0] = GEN_INT (args_locals_size); |
| if (CHAIN_FRAMES) |
| { |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS1 (page, __leaf_fp_pop_args_ret); |
| OUT_AS1 (jmp, __leaf_fp_pop_args_ret); |
| epilogue_size += 6; |
| need_ret = 0; |
| } |
| } |
| } |
| } |
| |
| if (current_function_pops_args && args_locals_size && need_ret) |
| { |
| operands[0] = GEN_INT (args_locals_size); |
| |
| switch (args_locals_size & 0xff) |
| { |
| default: |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS2 (add, spl, w); |
| epilogue_size += 4; |
| /* fall-through */ |
| |
| case 0: |
| break; |
| |
| case 1: |
| OUT_AS1 (inc, spl); |
| epilogue_size += 2; |
| } |
| |
| switch (args_locals_size & 0xff00) |
| { |
| default: |
| if ((args_locals_size & 0xff) != ((args_locals_size >> 8) & 0xff)) |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (add, sph, w); |
| epilogue_size += 4; |
| /* fall-through */ |
| |
| case 0: |
| break; |
| |
| case 0x100: |
| OUT_AS1 (inc, sph); |
| epilogue_size += 2; |
| } |
| } |
| |
| if (need_ret) |
| { |
| OUT_AS1 (ret,); |
| epilogue_size += 2; |
| } |
| |
| fprintf (file, "/* epilogue end (size=%d) */\n", epilogue_size); |
| } |
| |
| /* Return the difference between the registers after the function |
| prologue. |
| |
| Stack Frame grows down: |
| |
| ARGUMENTS |
| <------ AP ($102:$103) |
| RETURN PC (unless leaf function) |
| SAVEDFP (if needed) |
| <------ FP [HARD_FRAME_POINTER] ($FD:$FE) |
| SAVED REGS |
| <------ VFP [$100:$101] |
| STACK ALLOCATION |
| <------ SP ($6:$7) */ |
| int |
| ip2k_init_elim_offset (int from, int to) |
| { |
| int leaf_func_p = leaf_function_p (); |
| int no_saved_pc = leaf_func_p |
| || ip2k_naked_function_p (current_function_decl); |
| int offset; |
| int reg; |
| int reglimit; |
| |
| if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| return get_frame_size () + 1; |
| |
| if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) |
| return (CHAIN_FRAMES ? 2 : 0) + (no_saved_pc ? 0 : 2); |
| |
| /* Count all the registers we had to preserve. */ |
| |
| reglimit = CHAIN_FRAMES ? REG_FP : (REG_FP + 2); |
| for (offset = 0,reg = 0; reg < reglimit; ++reg) |
| { |
| if ((regs_ever_live[reg] && ! call_used_regs[reg])) |
| { |
| ++offset; |
| } |
| } |
| |
| if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) |
| return -offset; |
| |
| if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| /* Add in the stack-local variables. */ |
| return offset + get_frame_size () + 1; |
| |
| if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| /* Add stack-locals plus saved FP and PC. */ |
| return offset + get_frame_size () + 1 |
| + (CHAIN_FRAMES ? 2 : 0) + (no_saved_pc ? 0 : 2); |
| |
| abort (); /* Unanticipated elimination. */ |
| } |
| |
| /* Return nonzero if X (an RTX) is a legitimate memory address on the target |
| machine for a memory operand of mode MODE. */ |
| |
| int |
| legitimate_address_p (enum machine_mode mode, rtx x, int strict) |
| { |
| int off; |
| |
| if (GET_CODE (x) == SUBREG) |
| x = SUBREG_REG (x); |
| |
| switch (GET_CODE (x)) |
| { |
| case REG: |
| /* IP allows indirection without offset - only okay if |
| we don't require access to multiple bytes. */ |
| if (REGNO (x) == REG_IP) |
| return (GET_MODE_SIZE (mode) == 1) ? 'R' : 0; |
| |
| /* We can indirect through DP or SP register. */ |
| if (strict ? REG_OK_FOR_BASE_STRICT_P (x) |
| : REG_OK_FOR_BASE_NOSTRICT_P (x)) |
| return 'S'; |
| break; |
| |
| case PLUS: |
| /* Offsets from DP or SP are legal in the range 0..127 */ |
| { |
| rtx op1, op2; |
| |
| op1 = XEXP (x, 0); |
| op2 = XEXP (x, 1); |
| |
| if (REG_P (op2) && ! REG_P (op1)) |
| { |
| rtx tmp = op1; |
| op1 = op2; |
| op2 = tmp; |
| } |
| |
| /* Don't let anything but R+I through.. */ |
| if (! REG_P (op1) |
| || REG_P (op2) |
| || GET_CODE (op2) != CONST_INT) |
| return 0; |
| |
| switch (REGNO (op1)) |
| { |
| case REG_DP: /* only 0..127 displacement */ |
| case REG_SP: |
| off = 2 * GET_MODE_SIZE (mode); |
| if (! off) |
| off = 1; |
| |
| if (INTVAL (op2) < 0 || INTVAL (op2) > (128 - off)) |
| return 0; /* Positive must be small enough that after |
| splitting all pieces are addressed. */ |
| return 'S'; /* Safe displacement. */ |
| |
| case REG_IP: |
| if (GET_MODE_SIZE (mode) <= 1 && INTVAL (op2) == 0) |
| return (GET_MODE_SIZE (mode) == 1) ? 'R' : 0; |
| return 0; |
| |
| case REG_AP: |
| case REG_FP: |
| case REG_VFP: |
| default: |
| if (strict || ! REG_OK_FOR_BASE_NOSTRICT_P (op1)) |
| return 0; /* Allow until reload. */ |
| |
| return 'S'; |
| } |
| } |
| break; |
| |
| case CONST: |
| case SYMBOL_REF: |
| /* We always allow references to things in code space. */ |
| return is_regfile_address (x) ? 0 : 'C'; |
| |
| case LABEL_REF: |
| return 'L'; |
| |
| default: |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| /* Is ADDR mode dependent? */ |
| int |
| ip2k_mode_dependent_address (rtx addr) |
| { |
| switch (GET_CODE (addr)) |
| { |
| case POST_INC: |
| case POST_DEC: |
| case PRE_INC: |
| case PRE_DEC: |
| return 1; |
| |
| case REG: |
| return (REGNO (addr) == REG_IP); /* Can't do IP displaced addresses. */ |
| |
| default: |
| return 0; /* Assume no dependency. */ |
| } |
| } |
| |
| /* Attempts to replace X with a valid |
| memory address for an operand of mode MODE. */ |
| |
| rtx |
| legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, |
| enum machine_mode mode ATTRIBUTE_UNUSED, rtx scratch) |
| { |
| rtx reg; |
| |
| /* You might think that we could split up a symbolic address by |
| adding the HIGH 8 bits and doing a displacement off the dp. But |
| because we only have 7 bits of offset, that doesn't actually |
| help. So only constant displacements are likely to obtain an |
| advantage. */ |
| |
| if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0)) |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && ! CONST_OK_FOR_LETTER_P (INTVAL (XEXP (x, 1)), 'K')) |
| { |
| int offset = INTVAL (XEXP (x, 1)); |
| |
| reg = scratch ? scratch : gen_reg_rtx (Pmode); |
| |
| emit_insn (gen_rtx_SET (VOIDmode, reg, |
| gen_rtx_PLUS (Pmode, XEXP (x, 0), |
| GEN_INT (offset & 0xffc0)))); |
| x = gen_rtx_PLUS (Pmode, reg, GEN_INT (offset & 0x3f)); |
| } |
| |
| return x; /* We don't have any other tricks. */ |
| } |
| |
| /* Determine if X is a 'data' address or a code address. All static |
| data and stack variables reside in data memory. Only code is believed |
| to be in PRAM or FLASH. */ |
| int |
| is_regfile_address (rtx x) |
| { |
| while (1) |
| switch (GET_CODE (x)) |
| { |
| case SYMBOL_REF: |
| return ! SYMBOL_REF_FUNCTION_P (x); /* Declared as function. */ |
| case CONST: |
| case PLUS: |
| x = XEXP (x, 0); |
| break; |
| case CONST_INT: |
| case REG: |
| case SUBREG: |
| return 1; |
| case LABEL_REF: |
| return 0; |
| default: |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| /* Output ADDR to FILE as address. */ |
| |
| void |
| print_operand_address (FILE *file, rtx addr) |
| { |
| switch (GET_CODE (addr)) |
| { |
| case SUBREG: |
| addr = alter_subreg (&addr); |
| /* fall-through */ |
| |
| case REG: |
| fprintf (file, "(%s)", |
| REGNO (addr) == REG_DP ? "DP" |
| : REGNO (addr) == REG_SP ? "SP" |
| : REGNO (addr) == REG_IP ? "IP" |
| : REGNO (addr) == REG_VFP ? "VFP" /* Should never see this */ |
| : REGNO (addr) == REG_AP ? "AP" /* or this, either. */ |
| : reg_names[REGNO (addr)]); |
| break; |
| |
| case PRE_DEC: |
| case POST_INC: |
| abort (); |
| break; |
| |
| case CONST: |
| addr = XEXP (addr, 0); |
| print_operand_address (file, XEXP (addr, 0)); |
| fprintf (file, "+"); |
| print_operand_address (file, XEXP (addr, 1)); |
| return; |
| |
| case LO_SUM: |
| if (is_regfile_address (XEXP (addr, 1))) |
| fprintf (file, "%%lo8data("); |
| else |
| fprintf (file, "%%lo8insn("); |
| print_operand_address (file, XEXP (addr, 1)); |
| fprintf (file, ")"); |
| print_operand_address (file, XEXP (addr, 0)); |
| break; |
| |
| case PLUS: /* Ought to be stack or dp references. */ |
| if (XEXP (addr, 1) == const0_rtx |
| && GET_CODE (XEXP (addr, 0)) == PLUS) |
| { |
| print_operand_address (file, XEXP (addr, 0)); |
| return; |
| } |
| |
| if (! REG_P (XEXP (addr, 0)) || REGNO (XEXP (addr, 0)) != REG_IP) |
| print_operand_address (file, XEXP (addr, 1)); /* const */ |
| print_operand_address (file, XEXP (addr, 0)); /* (reg) */ |
| break; |
| |
| case HIGH: |
| if (is_regfile_address (XEXP (addr, 0))) |
| fprintf (file, "%%hi8data("); |
| else |
| fprintf (file, "%%hi8insn("); |
| output_addr_const (file, XEXP (addr, 0)); |
| fprintf (file, ")"); |
| break; |
| |
| default: |
| output_addr_const (file, addr); |
| } |
| } |
| |
| |
| /* Output X as assembler operand to file FILE. */ |
| |
| void |
| print_operand (FILE *file, rtx x, int code) |
| { |
| int abcd = 0; |
| unsigned long value; |
| |
| switch (code) |
| { |
| case '<': /* Push */ |
| ip2k_stack_delta++; |
| return; |
| |
| case '>': /* Pop */ |
| ip2k_stack_delta--; |
| return; |
| |
| case 'A': |
| case 'B': |
| case 'C': |
| case 'D': |
| abcd = code - 'A'; |
| break; |
| |
| case 'H': |
| abcd = 0; |
| break; |
| |
| case 'L': |
| abcd = 1; |
| break; |
| |
| case 'S': |
| case 'T': |
| case 'U': |
| case 'V': |
| case 'W': |
| case 'X': |
| case 'Y': |
| case 'Z': |
| abcd = code - 'S'; |
| |
| default: |
| break; |
| } |
| |
| if (ip2k_short_operand (x, GET_MODE (x)) |
| && ip2k_address_uses_reg_p (x, REG_SP)) |
| /* An SP-relative address needs to account for interior stack |
| pushes that reload didn't know about when it calculated the |
| stack offset. */ |
| abcd += ip2k_stack_delta; |
| |
| switch (GET_CODE (x)) |
| { |
| case SUBREG: |
| x = alter_subreg (&x); |
| /* fall-through */ |
| |
| case REG: |
| fprintf (file, reg_names[true_regnum (x) + abcd]); |
| break; |
| |
| case CONST_INT: |
| switch (code) |
| { |
| case 'x': |
| fprintf (file, "$%x", (int)(INTVAL (x) & 0xffff)); |
| break; |
| |
| case 'b': |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); /* bit selector */ |
| break; |
| |
| case 'e': /* "1 << n" - e.g. "exp" */ |
| fprintf (file, "#%d", 1 << INTVAL (x)); |
| break; |
| |
| case 'A': |
| case 'B': |
| case 'C': |
| case 'D': |
| value = INTVAL (x); |
| value >>= 8 * (3 - abcd); |
| value &= 0xff; |
| |
| fprintf (file, "#%ld", value); |
| break; |
| |
| case 'H': |
| fprintf (file, "#%d", (int)((INTVAL (x) >> 8) & 0xff)); |
| break; |
| |
| case 'L': |
| fprintf (file, "#%d", (int)(INTVAL (x) & 0xff)); |
| break; |
| |
| case 'S': |
| case 'T': |
| case 'U': |
| case 'V': |
| case 'W': |
| case 'X': |
| case 'Y': |
| case 'Z': |
| value = ((unsigned long long)INTVAL (x)) >> (8 * (7 - abcd)) & 0xff; |
| fprintf (file, "#%ld", value); |
| break; |
| |
| default: |
| fprintf (file, "#" HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); |
| } |
| break; |
| |
| case SYMBOL_REF: |
| case LABEL_REF: |
| case CODE_LABEL: |
| case CONST: |
| switch (code) |
| { |
| case 'A': |
| case 'B': |
| case 'C': |
| case 'D': |
| case 'S': |
| case 'T': |
| case 'U': |
| case 'V': |
| case 'W': |
| case 'X': |
| case 'Y': |
| case 'Z': |
| abort (); /* Probably an error. */ |
| break; |
| |
| case 'H': |
| fprintf (file, "#%s(", |
| is_regfile_address (x) ? "%hi8data" |
| : "%hi8insn"); |
| print_operand_address (file, x); |
| fputc (')', file); |
| break; |
| |
| case 'L': |
| fprintf (file, "#%s(", |
| is_regfile_address (x) ? "%lo8data" |
| : "%lo8insn"); |
| print_operand_address (file, x); |
| fputc (')', file); |
| break; |
| |
| default: |
| print_operand_address (file, x); |
| } |
| break; |
| |
| case MEM: |
| { |
| rtx addr = XEXP (x, 0); |
| |
| if (GET_CODE (addr) == SUBREG) |
| addr = alter_subreg (&x); |
| |
| if (CONSTANT_P (addr) && abcd) |
| { |
| fputc ('(', file); |
| print_operand_address (file, addr); |
| fprintf (file, ")+%d", abcd); |
| } |
| else if (abcd) |
| { |
| switch (GET_CODE (addr)) |
| { |
| case PLUS: |
| abcd += INTVAL (XEXP (addr, 1)); |
| |
| /* Worry about (plus (plus (reg DP) (const_int 10)) |
| (const_int 0)) */ |
| if (GET_CODE (XEXP (addr, 0)) == PLUS) |
| { |
| addr = XEXP (addr, 0); |
| abcd += INTVAL (XEXP (addr, 1)); |
| } |
| |
| fprintf (file, "%d", abcd); |
| print_operand_address (file, XEXP (addr, 0)); |
| break; |
| |
| case REG: |
| default: |
| fprintf (file, "%d", abcd); |
| print_operand_address (file, addr); |
| } |
| } |
| else if (GET_CODE (addr) == REG |
| && (REGNO (addr) == REG_DP || REGNO (addr) == REG_SP)) |
| { |
| fprintf (file, "0"); |
| print_operand_address (file, addr); |
| } |
| else |
| print_operand_address (file, addr); |
| } |
| break; |
| |
| case CONST_DOUBLE: |
| /* Is this an integer or a floating point value? */ |
| if (GET_MODE (x) == VOIDmode) |
| { |
| switch (code) |
| { |
| case 'S': |
| case 'T': |
| case 'U': |
| case 'V': |
| value = CONST_DOUBLE_HIGH (x); |
| value >>= 8 * (3 - abcd); |
| value &= 0xff; |
| |
| fprintf (file, "#%ld", value); |
| break; |
| |
| case 'W': |
| case 'X': |
| case 'Y': |
| case 'Z': |
| value = CONST_DOUBLE_LOW (x); |
| value >>= 8 * (7 - abcd); |
| value &= 0xff; |
| |
| fprintf (file, "#%ld", value); |
| break; |
| } |
| |
| } |
| else |
| { |
| REAL_VALUE_TYPE rv; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| REAL_VALUE_TO_TARGET_SINGLE (rv, value); |
| fprintf (file, "0x%lx", value); |
| } |
| break; |
| |
| default: |
| fatal_insn ("bad operand", x); |
| } |
| } |
| |
| /* Remember the operands for the compare. */ |
| const char * |
| ip2k_set_compare (rtx x, rtx y) |
| { |
| ip2k_compare_operands[0] = x; |
| ip2k_compare_operands[1] = y; |
| return ""; |
| } |
| |
| /* Emit the code for sCOND instructions. */ |
| const char * |
| ip2k_gen_sCOND (rtx insn ATTRIBUTE_UNUSED, enum rtx_code code, rtx dest) |
| { |
| #define operands ip2k_compare_operands |
| enum machine_mode mode; |
| |
| operands[2] = dest; |
| |
| mode = GET_MODE (operands[0]); |
| if ((mode != QImode) && (mode != HImode) |
| && (mode != SImode) && (mode != DImode)) |
| mode = GET_MODE (operands[1]); |
| |
| /* We have a fast path for a specific type of QImode compare. We ought |
| to extend this for larger cases too but that wins less frequently and |
| introduces a lot of complexity. */ |
| if (mode == QImode |
| && !rtx_equal_p (operands[0], operands[2]) |
| && !rtx_equal_p (operands[1], operands[2]) |
| && (! REG_P (operands[2]) |
| || (ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[2]), 1) |
| && ip2k_xexp_not_uses_reg_p (operands[1], |
| REGNO (operands[2]), 1)))) |
| { |
| OUT_AS1 (clr, %2); |
| if (immediate_operand (operands[1], QImode) |
| && ((INTVAL (operands[1]) & 0xff) == 0xff)) |
| { |
| if (code == EQ) |
| OUT_AS2 (incsnz, w, %0); |
| else |
| OUT_AS2 (incsz, w, %0); |
| } |
| else if (immediate_operand (operands[1], QImode) |
| && ((INTVAL (operands[1]) & 0xff) == 0x01)) |
| { |
| if (code == EQ) |
| OUT_AS2 (decsnz, w, %0); |
| else |
| OUT_AS2 (decsz, w, %0); |
| } |
| else if (ip2k_compare_operands[1] == const0_rtx) |
| { |
| OUT_AS2 (mov, w, %0); |
| if (code == EQ) |
| OUT_AS1 (snz,); |
| else |
| OUT_AS1 (sz,); |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %0); |
| if (code == EQ) |
| OUT_AS2 (csne, w, %1); |
| else |
| OUT_AS2 (cse, w, %1); |
| } |
| OUT_AS1 (inc, %2); |
| } |
| else |
| { |
| if (ip2k_compare_operands[1] == const0_rtx) |
| { |
| switch (mode) |
| { |
| case QImode: |
| OUT_AS2 (mov, w, %0); |
| break; |
| |
| case HImode: |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (or, w, %L0); |
| break; |
| |
| case SImode: |
| OUT_AS2 (mov, w, %A0); |
| OUT_AS2 (or, w, %B0); |
| OUT_AS2 (or, w, %C0); |
| OUT_AS2 (or, w, %D0); |
| break; |
| |
| case DImode: |
| OUT_AS2 (mov, w, %S0); |
| OUT_AS2 (or, w, %T0); |
| OUT_AS2 (or, w, %U0); |
| OUT_AS2 (or, w, %V0); |
| OUT_AS2 (or, w, %W0); |
| OUT_AS2 (or, w, %X0); |
| OUT_AS2 (or, w, %Y0); |
| OUT_AS2 (or, w, %Z0); |
| break; |
| |
| default: |
| abort (); |
| } |
| } |
| else |
| { |
| switch (mode) |
| { |
| case QImode: |
| OUT_AS2 (mov, w, %1); |
| OUT_AS2 (cmp, w, %0); |
| break; |
| |
| case HImode: |
| OUT_AS2 (mov, w, %H1); |
| OUT_AS2 (cmp, w, %H0); |
| OUT_AS1 (sz,); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %L1); |
| OUT_AS2 (cmp, w, %L0); |
| OUT_AS1 (2:,); |
| break; |
| |
| case SImode: |
| if (code == EQ) |
| { |
| OUT_AS2 (mov, w, #1); |
| OUT_AS2 (mov, mulh, w); |
| } |
| else |
| OUT_AS1 (clr, mulh); |
| OUT_AS2 (mov, w, %A1); |
| OUT_AS2 (cse, w, %A0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %B1); |
| OUT_AS2 (cse, w, %B0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %C1); |
| OUT_AS2 (cse, w, %C0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %D1); |
| OUT_AS2 (cse, w, %D0); |
| OUT_AS1 (2:,); |
| if (code == EQ) |
| OUT_AS1 (dec, mulh); |
| else |
| OUT_AS1 (inc, mulh); |
| OUT_AS2 (mov, w, mulh); |
| OUT_AS2 (mov, %2, w); |
| return ""; |
| |
| case DImode: |
| if (code == EQ) |
| { |
| OUT_AS2 (mov, w, #1); |
| OUT_AS2 (mov, mulh, w); |
| } |
| else |
| OUT_AS1 (clr, mulh); |
| OUT_AS2 (mov, w, %S1); |
| OUT_AS2 (cse, w, %S0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %T1); |
| OUT_AS2 (cse, w, %T0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %U1); |
| OUT_AS2 (cse, w, %U0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %V1); |
| OUT_AS2 (cse, w, %V0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %W1); |
| OUT_AS2 (cse, w, %W0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %X1); |
| OUT_AS2 (cse, w, %X0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %Y1); |
| OUT_AS2 (cse, w, %Y0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %Z1); |
| OUT_AS2 (cse, w, %Z0); |
| OUT_AS1 (2:,); |
| if (code == EQ) |
| OUT_AS1 (dec, mulh); |
| else |
| OUT_AS1 (inc, mulh); |
| OUT_AS2 (mov, w, mulh); |
| OUT_AS2 (mov, %2, w); |
| return ""; |
| |
| default: |
| abort (); |
| } |
| } |
| OUT_AS2 (mov, w, #0); |
| if (code == EQ) |
| OUT_AS1 (snz,); |
| else |
| OUT_AS1 (sz,); |
| OUT_AS1 (inc, wreg); |
| OUT_AS2 (mov, %2, w); |
| } |
| |
| return ""; |
| #undef operands |
| } |
| |
| const char * |
| ip2k_gen_signed_comp_branch (rtx insn, enum rtx_code code, rtx label) |
| { |
| #define operands ip2k_compare_operands |
| enum machine_mode mode; |
| int can_use_skip = 0; |
| rtx ninsn; |
| |
| operands[2] = label; |
| |
| mode = GET_MODE (operands[0]); |
| if ((mode != QImode) && (mode != HImode) |
| && (mode != SImode) && (mode != DImode)) |
| mode = GET_MODE (operands[1]); |
| |
| /* Look for situations where we can just skip the next instruction instead |
| of skipping and then branching! */ |
| ninsn = next_real_insn (insn); |
| if (ninsn |
| && (recog_memoized (ninsn) >= 0) |
| && get_attr_skip (ninsn) == SKIP_YES) |
| { |
| rtx skip_tgt = next_nonnote_insn (next_real_insn (insn)); |
| |
| /* The first situation is where the target of the jump is one insn |
| after the jump insn and the insn being jumped is only one machine |
| opcode long. */ |
| if (label == skip_tgt) |
| can_use_skip = 1; |
| else |
| { |
| /* If our skip target is in fact a code label then we ignore the |
| label and move onto the next useful instruction. Nothing we do |
| here has any effect on the use of skipping instructions. */ |
| if (GET_CODE (skip_tgt) == CODE_LABEL) |
| skip_tgt = next_nonnote_insn (skip_tgt); |
| |
| /* The second situation is where we have something of the form: |
| |
| test_condition |
| skip_conditional |
| page/jump label |
| |
| optional_label (this may or may not exist): |
| skippable_insn |
| page/jump label |
| |
| In this case we can eliminate the first "page/jump label". */ |
| if (GET_CODE (skip_tgt) == JUMP_INSN) |
| { |
| rtx set = single_set (skip_tgt); |
| if (GET_CODE (XEXP (set, 0)) == PC |
| && GET_CODE (XEXP (set, 1)) == LABEL_REF |
| && label == JUMP_LABEL (skip_tgt)) |
| can_use_skip = 2; |
| } |
| } |
| } |
| |
| /* gcc is a little braindead and does some rather stateful things while |
| inspecting attributes - we have to put this state back to what it's |
| supposed to be. */ |
| extract_constrain_insn_cached (insn); |
| |
| if (ip2k_compare_operands[1] == const0_rtx) /* These are easier. */ |
| { |
| switch (code) |
| { |
| case LT: |
| if (can_use_skip) |
| { |
| OUT_AS2 (sb, %0, 7); |
| } |
| else |
| { |
| OUT_AS2 (snb, %0, 7); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GT: |
| switch (mode) |
| { |
| case DImode: |
| OUT_AS2 (rl, w, %S0); |
| OUT_AS2 (mov, w, %S0); |
| OUT_AS2 (or, w, %T0); |
| OUT_AS2 (or, w, %U0); |
| OUT_AS2 (or, w, %V0); |
| OUT_AS2 (or, w, %W0); |
| OUT_AS2 (or, w, %X0); |
| OUT_AS2 (or, w, %Y0); |
| OUT_AS2 (or, w, %Z0); |
| OUT_AS1 (snz, ); |
| OUT_AS2 (setb, status, 0); |
| OUT_AS2 (sb, status, 0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case SImode: |
| OUT_AS2 (rl, w, %A0); |
| OUT_AS2 (mov, w, %A0); |
| OUT_AS2 (or, w, %B0); |
| OUT_AS2 (or, w, %C0); |
| OUT_AS2 (or, w, %D0); |
| OUT_AS1 (snz, ); |
| OUT_AS2 (setb, status, 0); |
| OUT_AS2 (sb, status, 0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case HImode: |
| OUT_AS2 (rl, w, %H0); |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (or, w, %L0); |
| OUT_AS1 (snz, ); |
| OUT_AS2 (setb, status, 0); |
| OUT_AS2 (sb, status, 0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case QImode: |
| OUT_AS2 (mov, w, %0); /* Will just do "sb w, 7". */ |
| OUT_AS1 (snz, ); |
| OUT_AS2 (setb, wreg, 7); |
| OUT_AS2 (sb, wreg, 7); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case LE: |
| switch (mode) |
| { |
| case DImode: |
| OUT_AS2 (mov, w, %S0); |
| OUT_AS2 (or, w, %T0); |
| OUT_AS2 (or, w, %U0); |
| OUT_AS2 (or, w, %V0); |
| OUT_AS2 (or, w, %W0); |
| OUT_AS2 (or, w, %X0); |
| OUT_AS2 (or, w, %Y0); |
| OUT_AS2 (or, w, %Z0); /* Z is correct. */ |
| OUT_AS1 (sz, ); |
| OUT_AS2 (snb, %S0, 7); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case SImode: |
| OUT_AS2 (mov, w, %A0); |
| OUT_AS2 (or, w, %B0); |
| OUT_AS2 (or, w, %C0); |
| OUT_AS2 (or, w, %D0); /* Z is correct. */ |
| OUT_AS1 (sz, ); |
| OUT_AS2 (snb, %A0, 7); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case HImode: |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (or, w, %L0); |
| OUT_AS1 (sz, ); |
| OUT_AS2 (snb, %H0, 7); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case QImode: |
| OUT_AS2 (mov, w, %0); /* Will just do "sb w, 7". */ |
| OUT_AS1 (sz, ); |
| OUT_AS2 (snb, wreg, 7); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case GE: |
| if (can_use_skip) |
| { |
| OUT_AS2 (snb, %0, 7); |
| } |
| else |
| { |
| OUT_AS2 (sb, %0, 7); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| default: |
| abort (); |
| } |
| return ""; |
| } |
| |
| /* signed compares are out of line because we can't get |
| the hardware to compute the overflow for us. */ |
| |
| switch (mode) |
| { |
| case QImode: |
| OUT_AS1 (push, %1%<); |
| OUT_AS1 (push, %0%>); |
| OUT_AS1 (page, __cmpqi2); |
| OUT_AS1 (call, __cmpqi2); |
| break; |
| |
| case HImode: |
| OUT_AS1 (push, %L1%<); |
| OUT_AS1 (push, %H1%<); |
| OUT_AS1 (push, %L0%<); |
| OUT_AS1 (push, %H0%>%>%>); |
| OUT_AS1 (page, __cmphi2); |
| OUT_AS1 (call, __cmphi2); |
| break; |
| |
| case SImode: |
| OUT_AS1 (push, %D1%<); |
| OUT_AS1 (push, %C1%<); |
| OUT_AS1 (push, %B1%<); |
| OUT_AS1 (push, %A1%<); |
| OUT_AS1 (push, %D0%<); |
| OUT_AS1 (push, %C0%<); |
| OUT_AS1 (push, %B0%<); |
| OUT_AS1 (push, %A0%>%>%>%>%>%>%>); |
| OUT_AS1 (page, __cmpsi2); |
| OUT_AS1 (call, __cmpsi2); |
| break; |
| |
| case DImode: |
| if (GET_CODE (operands[0]) == MEM |
| && true_regnum (XEXP (operands[0], 0)) == REG_DP) |
| { |
| OUT_AS1 (push, %Z1%<); |
| OUT_AS1 (push, %Y1%<); |
| OUT_AS1 (push, %X1%<); |
| OUT_AS1 (push, %W1%<); |
| OUT_AS1 (push, %V1%<); |
| OUT_AS1 (push, %U1%<); |
| OUT_AS1 (push, %T1%<); |
| OUT_AS1 (push, %S1%>%>%>%>%>%>%>); |
| OUT_AS1 (page, __cmpdi2_dp); |
| OUT_AS1 (call, __cmpdi2_dp); |
| } |
| else |
| { |
| OUT_AS1 (push, %Z1%<); |
| OUT_AS1 (push, %Y1%<); |
| OUT_AS1 (push, %X1%<); |
| OUT_AS1 (push, %W1%<); |
| OUT_AS1 (push, %V1%<); |
| OUT_AS1 (push, %U1%<); |
| OUT_AS1 (push, %T1%<); |
| OUT_AS1 (push, %S1%<); |
| OUT_AS1 (push, %Z0%<); |
| OUT_AS1 (push, %Y0%<); |
| OUT_AS1 (push, %X0%<); |
| OUT_AS1 (push, %W0%<); |
| OUT_AS1 (push, %V0%<); |
| OUT_AS1 (push, %U0%<); |
| OUT_AS1 (push, %T0%<); |
| OUT_AS1 (push, %S0%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>); |
| OUT_AS1 (page, __cmpdi2); |
| OUT_AS1 (call, __cmpdi2); |
| } |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| switch (code) |
| { |
| case LT: |
| if (can_use_skip) |
| { |
| OUT_AS2 (cse, w, #0); |
| } |
| else |
| { |
| OUT_AS2 (csne, w, #0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GT: |
| if (can_use_skip) |
| { |
| OUT_AS2 (cse, w, #2); |
| } |
| else |
| { |
| OUT_AS2 (csne, w, #2); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case LE: |
| if (can_use_skip) |
| { |
| OUT_AS2 (snb, wreg, 1); |
| } |
| else |
| { |
| OUT_AS2 (sb, wreg, 1); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GE: |
| if (can_use_skip) |
| { |
| OUT_AS2 (csne, w, #0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, #0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| default: |
| abort (); |
| } |
| return ""; |
| #undef operands |
| } |
| |
| const char * |
| ip2k_gen_unsigned_comp_branch (rtx insn, enum rtx_code code, rtx label) |
| { |
| #define operands ip2k_compare_operands |
| enum machine_mode mode; |
| int imm_sub = 0; |
| int imm_cmp = 0; |
| int can_use_skip = 0; |
| rtx ninsn; |
| HOST_WIDE_INT const_low; |
| HOST_WIDE_INT const_high; |
| |
| operands[2] = label; |
| |
| mode = GET_MODE (operands[0]); |
| if ((mode != QImode) && (mode != HImode) && (mode != SImode) |
| && (mode != DImode)) |
| { |
| mode = GET_MODE (operands[1]); |
| } |
| |
| /* Look for situations where we can just skip the next instruction instead |
| of skipping and then branching! */ |
| ninsn = next_real_insn (insn); |
| if (ninsn |
| && (recog_memoized (ninsn) >= 0) |
| && get_attr_skip (ninsn) == SKIP_YES) |
| { |
| rtx skip_tgt = next_nonnote_insn (next_real_insn (insn)); |
| |
| /* The first situation is where the target of the jump is one insn |
| after the jump insn and the insn being jumped is only one machine |
| opcode long. */ |
| if (label == skip_tgt) |
| can_use_skip = 1; |
| else |
| { |
| /* If our skip target is in fact a code label then we ignore the |
| label and move onto the next useful instruction. Nothing we do |
| here has any effect on the use of skipping instructions. */ |
| if (GET_CODE (skip_tgt) == CODE_LABEL) |
| skip_tgt = next_nonnote_insn (skip_tgt); |
| |
| /* The second situation is where we have something of the form: |
| |
| test_condition |
| skip_conditional |
| page/jump label |
| |
| optional_label (this may or may not exist): |
| skippable_insn |
| page/jump label |
| |
| In this case we can eliminate the first "page/jump label". */ |
| if (GET_CODE (skip_tgt) == JUMP_INSN) |
| { |
| rtx set = single_set (skip_tgt); |
| if (GET_CODE (XEXP (set, 0)) == PC |
| && GET_CODE (XEXP (set, 1)) == LABEL_REF |
| && label == JUMP_LABEL (skip_tgt)) |
| can_use_skip = 2; |
| } |
| } |
| } |
| |
| /* gcc is a little braindead and does some rather stateful things while |
| inspecting attributes - we have to put this state back to what it's |
| supposed to be. */ |
| extract_constrain_insn_cached (insn); |
| |
| if (ip2k_compare_operands[1] == const0_rtx) |
| { |
| switch (code) |
| { |
| case LEU: |
| code = EQ; /* Nothing is LTU 0. */ |
| goto zero; |
| |
| case GTU: |
| code = NE; /* Anything nonzero is GTU. */ |
| /* fall-through */ |
| |
| case EQ: |
| case NE: /* Test all the bits, result in |
| Z AND WREG. */ |
| zero: |
| switch (mode) |
| { |
| case DImode: |
| OUT_AS2 (mov, w, %S0); |
| OUT_AS2 (or, w, %T0); |
| OUT_AS2 (or, w, %U0); |
| OUT_AS2 (or, w, %V0); |
| OUT_AS2 (or, w, %W0); |
| OUT_AS2 (or, w, %X0); |
| OUT_AS2 (or, w, %Y0); |
| OUT_AS2 (or, w, %Z0); |
| break; |
| |
| case SImode: |
| OUT_AS2 (mov, w, %A0); |
| OUT_AS2 (or, w, %B0); |
| OUT_AS2 (or, w, %C0); |
| OUT_AS2 (or, w, %D0); |
| break; |
| |
| case HImode: |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (or, w, %L0); |
| break; |
| |
| case QImode: |
| OUT_AS2 (mov, w, %0); |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| if (can_use_skip) |
| { |
| if (code == EQ) |
| OUT_AS1 (sz, ); |
| else |
| OUT_AS1 (snz, ); |
| } |
| else |
| { |
| if (code == EQ) |
| OUT_AS1 (snz,); |
| else |
| OUT_AS1 (sz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GEU: |
| /* Always succeed. */ |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case LTU: |
| /* Always fail. */ |
| break; |
| |
| default: |
| abort (); |
| } |
| return ""; |
| } |
| |
| /* Look at whether we have a constant as one of our operands. If we do |
| and it's in the position that we use to subtract from during our |
| normal optimized comparison concept then we have to shuffle things |
| around! */ |
| if (mode != QImode) |
| { |
| if ((immediate_operand (operands[1], GET_MODE (operands[1])) |
| && ((code == LEU) || (code == GTU))) |
| || (immediate_operand (operands[0], GET_MODE (operands[0])) |
| && ((code == LTU) || (code == GEU)))) |
| { |
| imm_sub = 1; |
| } |
| } |
| |
| /* Same as above - look if we have a constant that we can compare |
| for equality or non-equality. If we know this then we can look |
| for common value eliminations. Note that we want to ensure that |
| any immediate value is operand 1 to simplify the code later! */ |
| if ((code == EQ) || (code == NE)) |
| { |
| imm_cmp = immediate_operand (operands[1], GET_MODE (operands[1])); |
| if (! imm_cmp) |
| { |
| imm_cmp = immediate_operand (operands[0], GET_MODE (operands[0])); |
| if (imm_cmp) |
| { |
| rtx tmp = operands[1]; |
| operands[1] = operands[0]; |
| operands[0] = tmp; |
| } |
| } |
| } |
| |
| switch (mode) |
| { |
| case QImode: |
| switch (code) |
| { |
| case EQ: |
| if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0xff)) |
| OUT_AS2 (incsnz, w, %0); |
| else if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0x01)) |
| OUT_AS2 (decsnz, w, %0); |
| else |
| { |
| OUT_AS2 (mov, w, %1); |
| OUT_AS2 (csne, w, %0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case NE: |
| if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0xff)) |
| OUT_AS2 (incsz, w, %0); |
| else if (imm_cmp && ((INTVAL (operands[1]) & 0xff) == 0x01)) |
| OUT_AS2 (decsz, w, %0); |
| else |
| { |
| OUT_AS2 (mov, w, %1); |
| OUT_AS2 (cse, w, %0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case GTU: |
| OUT_AS2 (mov, w, %0); |
| OUT_AS2 (cmp, w, %1); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case GEU: |
| OUT_AS2 (mov, w, %1); |
| OUT_AS2 (cmp, w, %0); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case LTU: |
| OUT_AS2 (mov, w, %1); |
| OUT_AS2 (cmp, w, %0); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| case LEU: |
| OUT_AS2 (mov, w, %0); |
| OUT_AS2 (cmp, w, %1); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case HImode: |
| switch (code) |
| { |
| case EQ: |
| { |
| unsigned char h = 0, l = 1; |
| |
| if (imm_cmp) |
| { |
| h = (INTVAL (operands[1]) >> 8) & 0xff; |
| l = INTVAL (operands[1]) & 0xff; |
| |
| if ((h == 0xff) && (l == 0xff)) |
| { |
| /* We should be able to do the following, but the |
| IP2k simulator doesn't like it and we get a load |
| of failures in gcc-c-torture. */ |
| OUT_AS2 (incsnz, w, %L0); |
| OUT_AS2 (incsz, w, %H0); |
| /* OUT_AS1 (skip,); Should have this */ |
| OUT_AS1 (page, 1f);/* Shouldn't need this! */ |
| OUT_AS1 (jmp, 1f); /* Shouldn't need this either. */ |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS1 (1:,); |
| break; |
| } |
| else if (h == 0) |
| { |
| if (l == 1) |
| OUT_AS2 (dec, w, %L0); |
| else |
| { |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS2 (sub, w, %L1); |
| } |
| OUT_AS2 (or, w, %H0); |
| OUT_AS1 (snz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| } |
| else if (l == 0) |
| { |
| if (h == 1) |
| OUT_AS2 (dec, w, %H0); |
| else |
| { |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (sub, w, %H1); |
| } |
| OUT_AS2 (or, w, %L0); |
| OUT_AS1 (snz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| } |
| } |
| |
| OUT_AS2 (mov, w, %H1); |
| OUT_AS2 (cse, w, %H0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| if (! imm_cmp || (h != l)) |
| OUT_AS2 (mov, w, %L1); |
| OUT_AS2 (csne, w, %L0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS1 (2:,); |
| } |
| break; |
| |
| case NE: |
| { |
| unsigned char h = 0, l = 1; |
| |
| if (imm_cmp) |
| { |
| h = (INTVAL (operands[1]) >> 8) & 0xff; |
| l = INTVAL (operands[1]) & 0xff; |
| |
| if ((h == 0xff) && (l == 0xff)) |
| { |
| OUT_AS2 (incsnz, w, %L0); |
| OUT_AS2 (incsz, w, %H0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| } |
| else if (h == 0) |
| { |
| if (l == 1) |
| OUT_AS2 (dec, w, %L0); |
| else |
| { |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS2 (sub, w, %L1); |
| } |
| OUT_AS2 (or, w, %H0); |
| OUT_AS1 (sz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| } |
| else if (l == 0) |
| { |
| if (h == 1) |
| OUT_AS2 (dec, w, %H0); |
| else |
| { |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (sub, w, %H1); |
| } |
| OUT_AS2 (or, w, %L0); |
| OUT_AS1 (sz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| break; |
| } |
| } |
| |
| OUT_AS2 (mov, w, %H1); |
| if (imm_cmp && (h == l)) |
| { |
| OUT_AS2 (csne, w, %H0); |
| OUT_AS2 (cse, w, %L0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %H0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS2 (mov, w, %L1); |
| OUT_AS2 (cse, w, %L0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GTU: |
| if (imm_sub) |
| { |
| /* > 0xffff never succeeds! */ |
| if ((INTVAL (operands[1]) & 0xffff) != 0xffff) |
| { |
| operands[3] = GEN_INT (INTVAL (operands[1]) + 1); |
| OUT_AS2 (mov, w, %L3); |
| OUT_AS2 (sub, w, %L0); |
| OUT_AS2 (mov, w, %H3); |
| OUT_AS2 (subc, w, %H0); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS2 (sub, w, %L1); |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (subc, w, %H1); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GEU: |
| if (imm_sub) |
| { |
| if (INTVAL (operands[0]) == 0) |
| { |
| OUT_AS2 (mov, w, %H1); |
| OUT_AS2 (or, w, %L1); |
| OUT_AS1 (snz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (INTVAL (operands[0]) - 1); |
| OUT_AS2 (mov, w, %L3); |
| OUT_AS2 (sub, w, %L1); |
| OUT_AS2 (mov, w, %H3); |
| OUT_AS2 (subc, w, %H1); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %L1); |
| OUT_AS2 (sub, w, %L0); |
| OUT_AS2 (mov, w, %H1); |
| OUT_AS2 (subc, w, %H0); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case LTU: |
| if (imm_sub) |
| { |
| if (INTVAL (operands[0]) == 0) |
| { |
| OUT_AS2 (mov, w, %H1); |
| OUT_AS2 (or, w, %L1); |
| OUT_AS1 (sz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (INTVAL (operands[0]) - 1); |
| OUT_AS2 (mov, w, %L3); |
| OUT_AS2 (sub, w, %L1); |
| OUT_AS2 (mov, w, %H3); |
| OUT_AS2 (subc, w, %H1); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %L1); |
| OUT_AS2 (sub, w, %L0); |
| OUT_AS2 (mov, w, %H1); |
| OUT_AS2 (subc, w, %H0); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case LEU: |
| if (imm_sub) |
| { |
| if ((INTVAL (operands[1]) & 0xffff) == 0xffff) |
| { |
| /* <= 0xffff always succeeds. */ |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (INTVAL (operands[1]) + 1); |
| OUT_AS2 (mov, w, %L3); |
| OUT_AS2 (sub, w, %L0); |
| OUT_AS2 (mov, w, %H3); |
| OUT_AS2 (subc, w, %H0); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %L0); |
| OUT_AS2 (sub, w, %L1); |
| OUT_AS2 (mov, w, %H0); |
| OUT_AS2 (subc, w, %H1); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case SImode: |
| switch (code) |
| { |
| case EQ: |
| { |
| unsigned char a = 0, b = 1, c = 2, d = 3; |
| |
| if (imm_cmp) |
| { |
| a = (INTVAL (operands[1]) >> 24) & 0xff; |
| b = (INTVAL (operands[1]) >> 16) & 0xff; |
| c = (INTVAL (operands[1]) >> 8) & 0xff; |
| d = INTVAL (operands[1]) & 0xff; |
| } |
| |
| OUT_AS2 (mov, w, %A1); |
| if (imm_cmp && (b == a)) |
| { |
| OUT_AS2 (csne, w, %A0); |
| OUT_AS2 (cse, w, %B0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %A0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %B1); |
| OUT_AS2 (cse, w, %B0); |
| } |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| if (! imm_cmp || (c != b)) |
| OUT_AS2 (mov, w, %C1); |
| OUT_AS2 (cse, w, %C0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| if (! imm_cmp || (d != c)) |
| OUT_AS2 (mov, w, %D1); |
| OUT_AS2 (csne, w, %D0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS1 (2:,); |
| } |
| break; |
| |
| case NE: |
| { |
| unsigned char a = 0, b = 1, c = 2, d = 3; |
| |
| if (imm_cmp) |
| { |
| a = (INTVAL (operands[1]) >> 24) & 0xff; |
| b = (INTVAL (operands[1]) >> 16) & 0xff; |
| c = (INTVAL (operands[1]) >> 8) & 0xff; |
| d = INTVAL (operands[1]) & 0xff; |
| } |
| |
| OUT_AS2 (mov, w, %A1); |
| if (imm_cmp && (b == a)) |
| { |
| OUT_AS2 (csne, w, %A0); |
| OUT_AS2 (cse, w, %B0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %A0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS2 (mov, w, %B1); |
| OUT_AS2 (cse, w, %B0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| if (! imm_cmp || (c != b)) |
| OUT_AS2 (mov, w, %C1); |
| if (imm_cmp && (d == c)) |
| { |
| OUT_AS2 (csne, w, %C0); |
| OUT_AS2 (cse, w, %D0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %C0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS2 (mov, w, %D1); |
| OUT_AS2 (cse, w, %D0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GTU: |
| if (imm_sub) |
| { |
| /* > 0xffffffff never succeeds! */ |
| if ((unsigned HOST_WIDE_INT)(INTVAL (operands[1]) & 0xffffffff) |
| != 0xffffffff) |
| { |
| operands[3] = GEN_INT (INTVAL (operands[1]) + 1); |
| OUT_AS2 (mov, w, %D3); |
| OUT_AS2 (sub, w, %D0); |
| OUT_AS2 (mov, w, %C3); |
| OUT_AS2 (subc, w, %C0); |
| OUT_AS2 (mov, w, %B3); |
| OUT_AS2 (subc, w, %B0); |
| OUT_AS2 (mov, w, %A3); |
| OUT_AS2 (subc, w, %A0); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %D0); |
| OUT_AS2 (sub, w, %D1); |
| OUT_AS2 (mov, w, %C0); |
| OUT_AS2 (subc, w, %C1); |
| OUT_AS2 (mov, w, %B0); |
| OUT_AS2 (subc, w, %B1); |
| OUT_AS2 (mov, w, %A0); |
| OUT_AS2 (subc, w, %A1); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GEU: |
| if (imm_sub) |
| { |
| if (INTVAL (operands[0]) == 0) |
| { |
| OUT_AS2 (mov, w, %A1); |
| OUT_AS2 (or, w, %B1); |
| OUT_AS2 (or, w, %C1); |
| OUT_AS2 (or, w, %D1); |
| OUT_AS1 (snz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (INTVAL (operands[0]) - 1); |
| OUT_AS2 (mov, w, %D3); |
| OUT_AS2 (sub, w, %D1); |
| OUT_AS2 (mov, w, %C3); |
| OUT_AS2 (subc, w, %C1); |
| OUT_AS2 (mov, w, %B3); |
| OUT_AS2 (subc, w, %B1); |
| OUT_AS2 (mov, w, %A3); |
| OUT_AS2 (subc, w, %A1); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %D1); |
| OUT_AS2 (sub, w, %D0); |
| OUT_AS2 (mov, w, %C1); |
| OUT_AS2 (subc, w, %C0); |
| OUT_AS2 (mov, w, %B1); |
| OUT_AS2 (subc, w, %B0); |
| OUT_AS2 (mov, w, %A1); |
| OUT_AS2 (subc, w, %A0); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case LTU: |
| if (imm_sub) |
| { |
| if (INTVAL (operands[0]) == 0) |
| { |
| OUT_AS2 (mov, w, %A1); |
| OUT_AS2 (or, w, %B1); |
| OUT_AS2 (or, w, %C1); |
| OUT_AS2 (or, w, %D1); |
| OUT_AS1 (sz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (INTVAL (operands[0]) - 1); |
| OUT_AS2 (mov, w, %D3); |
| OUT_AS2 (sub, w, %D1); |
| OUT_AS2 (mov, w, %C3); |
| OUT_AS2 (subc, w, %C1); |
| OUT_AS2 (mov, w, %B3); |
| OUT_AS2 (subc, w, %B1); |
| OUT_AS2 (mov, w, %A3); |
| OUT_AS2 (subc, w, %A1); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %D1); |
| OUT_AS2 (sub, w, %D0); |
| OUT_AS2 (mov, w, %C1); |
| OUT_AS2 (subc, w, %C0); |
| OUT_AS2 (mov, w, %B1); |
| OUT_AS2 (subc, w, %B0); |
| OUT_AS2 (mov, w, %A1); |
| OUT_AS2 (subc, w, %A0); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case LEU: |
| if (imm_sub) |
| { |
| if ((unsigned HOST_WIDE_INT)(INTVAL (operands[1]) & 0xffffffff) |
| == 0xffffffff) |
| { |
| /* <= 0xffffffff always succeeds. */ |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (INTVAL (operands[1]) + 1); |
| OUT_AS2 (mov, w, %D3); |
| OUT_AS2 (sub, w, %D0); |
| OUT_AS2 (mov, w, %C3); |
| OUT_AS2 (subc, w, %C0); |
| OUT_AS2 (mov, w, %B3); |
| OUT_AS2 (subc, w, %B0); |
| OUT_AS2 (mov, w, %A3); |
| OUT_AS2 (subc, w, %A0); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %D0); |
| OUT_AS2 (sub, w, %D1); |
| OUT_AS2 (mov, w, %C0); |
| OUT_AS2 (subc, w, %C1); |
| OUT_AS2 (mov, w, %B0); |
| OUT_AS2 (subc, w, %B1); |
| OUT_AS2 (mov, w, %A0); |
| OUT_AS2 (subc, w, %A1); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case DImode: |
| if (GET_CODE (operands[1]) == CONST_INT) |
| { |
| const_low = INTVAL (operands[1]); |
| const_high = (const_low >= 0) - 1; |
| } |
| else if (GET_CODE (operands[1]) == CONST_DOUBLE) |
| { |
| const_low = CONST_DOUBLE_LOW (operands[1]); |
| const_high = CONST_DOUBLE_HIGH (operands[1]); |
| } |
| switch (code) |
| { |
| case EQ: |
| { |
| unsigned char s = 0, t = 1, u = 2, v = 3; |
| unsigned char w = 4, x = 5, y = 6, z = 7; |
| if (optimize_size) |
| { |
| if (GET_CODE (operands[0]) == MEM |
| && true_regnum (XEXP (operands[0], 0)) == REG_DP) |
| { |
| OUT_AS1 (push, %Z1%<); |
| OUT_AS1 (push, %Y1%<); |
| OUT_AS1 (push, %X1%<); |
| OUT_AS1 (push, %W1%<); |
| OUT_AS1 (push, %V1%<); |
| OUT_AS1 (push, %U1%<); |
| OUT_AS1 (push, %T1%<); |
| OUT_AS1 (push, %S1%>%>%>%>%>%>%>); |
| OUT_AS1 (page, __cmpdi2_dp); |
| OUT_AS1 (call, __cmpdi2_dp); |
| OUT_AS2 (csne, w, #1); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| OUT_AS1 (push, %Z1%<); |
| OUT_AS1 (push, %Y1%<); |
| OUT_AS1 (push, %X1%<); |
| OUT_AS1 (push, %W1%<); |
| OUT_AS1 (push, %V1%<); |
| OUT_AS1 (push, %U1%<); |
| OUT_AS1 (push, %T1%<); |
| OUT_AS1 (push, %S1%<); |
| OUT_AS1 (push, %Z0%<); |
| OUT_AS1 (push, %Y0%<); |
| OUT_AS1 (push, %X0%<); |
| OUT_AS1 (push, %W0%<); |
| OUT_AS1 (push, %V0%<); |
| OUT_AS1 (push, %U0%<); |
| OUT_AS1 (push, %T0%<); |
| OUT_AS1 (push, %S0%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>); |
| OUT_AS1 (page, __cmpdi2); |
| OUT_AS1 (call, __cmpdi2); |
| OUT_AS2 (csne, w, #1); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| if (imm_cmp) |
| { |
| s = (const_high >> 24) & 0xff; |
| t = (const_high >> 16) & 0xff; |
| u = (const_high >> 8) & 0xff; |
| v = const_high & 0xff; |
| w = (const_low >> 24) & 0xff; |
| x = (const_low >> 16) & 0xff; |
| y = (const_low >> 8) & 0xff; |
| z = const_low & 0xff; |
| } |
| |
| OUT_AS2 (mov, w, %S1); |
| if (imm_cmp && (s == t)) |
| { |
| OUT_AS2 (csne, w, %S0); |
| OUT_AS2 (cse, w, %T0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %S0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %T1); |
| OUT_AS2 (cse, w, %T0); |
| } |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| |
| OUT_AS2 (mov, w, %U1); |
| if (imm_cmp && (u == v)) |
| { |
| OUT_AS2 (csne, w, %U0); |
| OUT_AS2 (cse, w, %V0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %U0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %V1); |
| OUT_AS2 (cse, w, %V0); |
| } |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| |
| OUT_AS2 (mov, w, %W1); |
| if (imm_cmp && (w == x)) |
| { |
| OUT_AS2 (csne, w, %W0); |
| OUT_AS2 (cse, w, %X0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %W0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| OUT_AS2 (mov, w, %X1); |
| OUT_AS2 (cse, w, %X0); |
| } |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| |
| if (! imm_cmp || (x != y)) |
| OUT_AS2 (mov, w, %Y1); |
| OUT_AS2 (cse, w, %Y0); |
| OUT_AS1 (page, 2f); |
| OUT_AS1 (jmp, 2f); |
| if (! imm_cmp || (z != y)) |
| OUT_AS2 (mov, w, %Z1); |
| OUT_AS2 (csne, w, %Z0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS1 (2:,); |
| } |
| } |
| break; |
| |
| case NE: |
| { |
| unsigned char s = 0, t = 1, u = 2, v = 3; |
| unsigned char w = 4, x = 5, y = 6, z = 7; |
| |
| if (optimize_size) |
| { |
| if (GET_CODE (operands[0]) == MEM |
| && true_regnum (XEXP (operands[0], 0)) == REG_DP) |
| { |
| OUT_AS1 (push, %Z1%<); |
| OUT_AS1 (push, %Y1%<); |
| OUT_AS1 (push, %X1%<); |
| OUT_AS1 (push, %W1%<); |
| OUT_AS1 (push, %V1%<); |
| OUT_AS1 (push, %U1%<); |
| OUT_AS1 (push, %T1%<); |
| OUT_AS1 (push, %S1%>%>%>%>%>%>%>); |
| OUT_AS1 (page, __cmpdi2_dp); |
| OUT_AS1 (call, __cmpdi2_dp); |
| OUT_AS2 (cse, w, #1); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| OUT_AS1 (push, %Z1%<); |
| OUT_AS1 (push, %Y1%<); |
| OUT_AS1 (push, %X1%<); |
| OUT_AS1 (push, %W1%<); |
| OUT_AS1 (push, %V1%<); |
| OUT_AS1 (push, %U1%<); |
| OUT_AS1 (push, %T1%<); |
| OUT_AS1 (push, %S1%<); |
| OUT_AS1 (push, %Z0%<); |
| OUT_AS1 (push, %Y0%<); |
| OUT_AS1 (push, %X0%<); |
| OUT_AS1 (push, %W0%<); |
| OUT_AS1 (push, %V0%<); |
| OUT_AS1 (push, %U0%<); |
| OUT_AS1 (push, %T0%<); |
| OUT_AS1 (push, %S0%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>); |
| OUT_AS1 (page, __cmpdi2); |
| OUT_AS1 (call, __cmpdi2); |
| OUT_AS2 (cse, w, #1); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| if (imm_cmp) |
| { |
| s = (const_high >> 24) & 0xff; |
| t = (const_high >> 16) & 0xff; |
| u = (const_high >> 8) & 0xff; |
| v = const_high & 0xff; |
| w = (const_low >> 24) & 0xff; |
| x = (const_low >> 16) & 0xff; |
| y = (const_low >> 8) & 0xff; |
| z = const_low & 0xff; |
| } |
| |
| OUT_AS2 (mov, w, %S1); |
| if (imm_cmp && (s == t)) |
| { |
| OUT_AS2 (csne, w, %S0); |
| OUT_AS2 (cse, w, %T0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %S0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS2 (mov, w, %T1); |
| OUT_AS2 (cse, w, %T0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| |
| OUT_AS2 (mov, w, %U1); |
| if (imm_cmp && (u == v)) |
| { |
| OUT_AS2 (csne, w, %U0); |
| OUT_AS2 (cse, w, %V0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %U0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS2 (mov, w, %V1); |
| OUT_AS2 (cse, w, %V0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| |
| OUT_AS2 (mov, w, %W1); |
| if (imm_cmp && (w == x)) |
| { |
| OUT_AS2 (csne, w, %W0); |
| OUT_AS2 (cse, w, %X0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %W0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS2 (mov, w, %X1); |
| OUT_AS2 (cse, w, %X0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| |
| if (! imm_cmp || (y != x)) |
| OUT_AS2 (mov, w, %Y1); |
| if (imm_cmp && (z == y)) |
| { |
| OUT_AS2 (csne, w, %Y0); |
| OUT_AS2 (cse, w, %Z0); |
| } |
| else |
| { |
| OUT_AS2 (cse, w, %Y0); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| OUT_AS2 (mov, w, %Z1); |
| OUT_AS2 (cse, w, %Z0); |
| } |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| break; |
| |
| case GTU: |
| if (imm_sub) |
| { |
| /* > 0xffffffffffffffff never suceeds! */ |
| if (((const_high & 0xffffffff) != 0xffffffff) |
| || ((const_low & 0xffffffff) != 0xffffffff)) |
| { |
| operands[3] = GEN_INT (const_low + 1); |
| operands[4] = GEN_INT (const_high |
| + (INTVAL (operands[3]) ? 0 : 1)); |
| OUT_AS2 (mov, w, %D3); |
| OUT_AS2 (sub, w, %Z0); |
| OUT_AS2 (mov, w, %C3); |
| OUT_AS2 (subc, w, %Y0); |
| OUT_AS2 (mov, w, %B3); |
| OUT_AS2 (subc, w, %X0); |
| OUT_AS2 (mov, w, %A3); |
| OUT_AS2 (subc, w, %W0); |
| OUT_AS2 (mov, w, %D4); |
| OUT_AS2 (subc, w, %V0); |
| OUT_AS2 (mov, w, %C4); |
| OUT_AS2 (subc, w, %U0); |
| OUT_AS2 (mov, w, %B4); |
| OUT_AS2 (subc, w, %T0); |
| OUT_AS2 (mov, w, %A4); |
| OUT_AS2 (subc, w, %S0); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %Z0); |
| OUT_AS2 (sub, w, %Z1); |
| OUT_AS2 (mov, w, %Y0); |
| OUT_AS2 (subc, w, %Y1); |
| OUT_AS2 (mov, w, %X0); |
| OUT_AS2 (subc, w, %X1); |
| OUT_AS2 (mov, w, %W0); |
| OUT_AS2 (subc, w, %W1); |
| OUT_AS2 (mov, w, %V0); |
| OUT_AS2 (subc, w, %V1); |
| OUT_AS2 (mov, w, %U0); |
| OUT_AS2 (subc, w, %U1); |
| OUT_AS2 (mov, w, %T0); |
| OUT_AS2 (subc, w, %T1); |
| OUT_AS2 (mov, w, %S0); |
| OUT_AS2 (subc, w, %S1); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case GEU: |
| if (imm_sub) |
| { |
| HOST_WIDE_INT const_low0; |
| HOST_WIDE_INT const_high0; |
| |
| if (GET_CODE (operands[0]) == CONST_INT) |
| { |
| const_low0 = INTVAL (operands[0]); |
| const_high0 = (const_low >= 0) - 1; |
| } |
| else if (GET_CODE (operands[0]) == CONST_DOUBLE) |
| { |
| const_low0 = CONST_DOUBLE_LOW (operands[0]); |
| const_high0 = CONST_DOUBLE_HIGH (operands[0]); |
| } |
| |
| if (const_high0 == 0 && const_low0 == 0) |
| { |
| OUT_AS2 (mov, w, %S1); |
| OUT_AS2 (or, w, %T1); |
| OUT_AS2 (or, w, %U1); |
| OUT_AS2 (or, w, %V1); |
| OUT_AS2 (or, w, %W1); |
| OUT_AS2 (or, w, %X1); |
| OUT_AS2 (or, w, %Y1); |
| OUT_AS2 (or, w, %Z1); |
| OUT_AS1 (snz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (const_low0 - 1); |
| operands[4] = GEN_INT (const_high0 - (const_low0 ? 1 : 0)); |
| OUT_AS2 (mov, w, %D3); |
| OUT_AS2 (sub, w, %Z1); |
| OUT_AS2 (mov, w, %C3); |
| OUT_AS2 (subc, w, %Y1); |
| OUT_AS2 (mov, w, %B3); |
| OUT_AS2 (subc, w, %X1); |
| OUT_AS2 (mov, w, %A3); |
| OUT_AS2 (subc, w, %W1); |
| OUT_AS2 (mov, w, %D4); |
| OUT_AS2 (subc, w, %V1); |
| OUT_AS2 (mov, w, %C4); |
| OUT_AS2 (subc, w, %U1); |
| OUT_AS2 (mov, w, %B4); |
| OUT_AS2 (subc, w, %T1); |
| OUT_AS2 (mov, w, %A4); |
| OUT_AS2 (subc, w, %S1); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %Z1); |
| OUT_AS2 (sub, w, %Z0); |
| OUT_AS2 (mov, w, %Y1); |
| OUT_AS2 (subc, w, %Y0); |
| OUT_AS2 (mov, w, %X1); |
| OUT_AS2 (subc, w, %X0); |
| OUT_AS2 (mov, w, %W1); |
| OUT_AS2 (subc, w, %W0); |
| OUT_AS2 (mov, w, %V1); |
| OUT_AS2 (subc, w, %V0); |
| OUT_AS2 (mov, w, %U1); |
| OUT_AS2 (subc, w, %U0); |
| OUT_AS2 (mov, w, %T1); |
| OUT_AS2 (subc, w, %T0); |
| OUT_AS2 (mov, w, %S1); |
| OUT_AS2 (subc, w, %S0); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case LTU: |
| if (imm_sub) |
| { |
| HOST_WIDE_INT const_low0; |
| HOST_WIDE_INT const_high0; |
| |
| if (GET_CODE (operands[0]) == CONST_INT) |
| { |
| const_low0 = INTVAL (operands[0]); |
| const_high0 = (const_low >= 0) - 1; |
| } |
| else if (GET_CODE (operands[0]) == CONST_DOUBLE) |
| { |
| const_low0 = CONST_DOUBLE_LOW (operands[0]); |
| const_high0 = CONST_DOUBLE_HIGH (operands[0]); |
| } |
| |
| if (const_high0 == 0 && const_low0 == 0) |
| { |
| OUT_AS2 (mov, w, %S1); |
| OUT_AS2 (or, w, %T1); |
| OUT_AS2 (or, w, %U1); |
| OUT_AS2 (or, w, %V1); |
| OUT_AS2 (or, w, %W1); |
| OUT_AS2 (or, w, %X1); |
| OUT_AS2 (or, w, %Y1); |
| OUT_AS2 (or, w, %Z1); |
| OUT_AS1 (sz,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| else |
| { |
| operands[3] = GEN_INT (const_low0 - 1); |
| operands[4] = GEN_INT (const_high0 - (const_low0 ? 1 : 0)); |
| OUT_AS2 (mov, w, %D3); |
| OUT_AS2 (sub, w, %Z1); |
| OUT_AS2 (mov, w, %C3); |
| OUT_AS2 (subc, w, %Y1); |
| OUT_AS2 (mov, w, %B3); |
| OUT_AS2 (subc, w, %X1); |
| OUT_AS2 (mov, w, %A3); |
| OUT_AS2 (subc, w, %W1); |
| OUT_AS2 (mov, w, %D4); |
| OUT_AS2 (subc, w, %V1); |
| OUT_AS2 (mov, w, %C4); |
| OUT_AS2 (subc, w, %U1); |
| OUT_AS2 (mov, w, %B4); |
| OUT_AS2 (subc, w, %T1); |
| OUT_AS2 (mov, w, %A4); |
| OUT_AS2 (subc, w, %S1); |
| OUT_AS1 (snc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| } |
| else |
| { |
| OUT_AS2 (mov, w, %Z1); |
| OUT_AS2 (sub, w, %Z0); |
| OUT_AS2 (mov, w, %Y1); |
| OUT_AS2 (subc, w, %Y0); |
| OUT_AS2 (mov, w, %X1); |
| OUT_AS2 (subc, w, %X0); |
| OUT_AS2 (mov, w, %W1); |
| OUT_AS2 (subc, w, %W0); |
| OUT_AS2 (mov, w, %V1); |
| OUT_AS2 (subc, w, %V0); |
| OUT_AS2 (mov, w, %U1); |
| OUT_AS2 (subc, w, %U0); |
| OUT_AS2 (mov, w, %T1); |
| OUT_AS2 (subc, w, %T0); |
| OUT_AS2 (mov, w, %S1); |
| OUT_AS2 (subc, w, %S0); |
| OUT_AS1 (sc,); |
| OUT_AS1 (page, %2); |
| OUT_AS1 (jmp, %2); |
| } |
| break; |
| |
| case LEU: |
| if (imm_sub) |
| { |
| if (((const_high & 0xffffffff) == 0xffffffff) |
| && ((const_low & 0xffffffff) == 0xffffffff)) |
| { |
| /* <= 0xffffffffffffffff always suceeds. */ |
|