void split_quadword_operands (rtx * operands, rtx * low, int n ATTRIBUTE_UNUSED) { int i; /* Split operands. */ low[0] = low[1] = low[2] = 0; for (i = 0; i < 3; i++) { if (low[i]) /* it's already been figured out */; else if (GET_CODE (operands[i]) == MEM && (GET_CODE (XEXP (operands[i], 0)) == POST_INC)) { rtx addr = XEXP (operands[i], 0); operands[i] = low[i] = gen_rtx_MEM (SImode, addr); if (which_alternative == 0 && i == 0) { addr = XEXP (operands[i], 0); operands[i+1] = low[i+1] = gen_rtx_MEM (SImode, addr); } } else { low[i] = operand_subword (operands[i], 0, 0, DImode); operands[i] = operand_subword (operands[i], 1, 0, DImode); } } }
/* Support function to determine the return address of the function 'count' frames back up the stack. */ rtx lm32_return_addr_rtx (int count, rtx frame) { rtx r; if (count == 0) { /* *mjs* This test originally used leaf_function_p (), we now use the regs_ever_live test which I *think* is more accurate. */ if (!df_regs_ever_live_p(RA_REGNUM)) { r = gen_rtx_REG (Pmode, RA_REGNUM); } else { r = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, frame, GEN_INT(- 2 * UNITS_PER_WORD))); set_mem_alias_set (r, get_frame_alias_set ()); } } else if (flag_omit_frame_pointer) r = NULL_RTX; else { r = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, frame, GEN_INT(- 2 * UNITS_PER_WORD))); set_mem_alias_set (r, get_frame_alias_set ()); } return r; }
static void nds32_emit_mem_move_block (int base_regno, int count, rtx *dst_base_reg, rtx *dst_mem, rtx *src_base_reg, rtx *src_mem, bool update_base_reg_p) { rtx new_base_reg; emit_insn (nds32_expand_load_multiple (base_regno, count, *src_base_reg, *src_mem, update_base_reg_p, &new_base_reg)); if (update_base_reg_p) { *src_base_reg = new_base_reg; *src_mem = gen_rtx_MEM (SImode, *src_base_reg); } emit_insn (nds32_expand_store_multiple (base_regno, count, *dst_base_reg, *dst_mem, update_base_reg_p, &new_base_reg)); if (update_base_reg_p) { *dst_base_reg = new_base_reg; *dst_mem = gen_rtx_MEM (SImode, *dst_base_reg); } }
/* Support function to determine the return address of the function 'count' frames back up the stack. */ rtx lm32_return_addr_rtx (int count, rtx frame) { rtx r; if (count == 0) { if (!df_regs_ever_live_p (RA_REGNUM)) r = gen_rtx_REG (Pmode, RA_REGNUM); else { r = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, frame, GEN_INT (-2 * UNITS_PER_WORD))); set_mem_alias_set (r, get_frame_alias_set ()); } } else if (flag_omit_frame_pointer) r = NULL_RTX; else { r = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, frame, GEN_INT (-2 * UNITS_PER_WORD))); set_mem_alias_set (r, get_frame_alias_set ()); } return r; }
/* Generate and emit RTL to save or restore callee save registers. */ static void expand_save_restore (struct lm32_frame_info *info, int op) { unsigned int reg_save_mask = info->reg_save_mask; int regno; HOST_WIDE_INT offset; rtx insn; /* Callee saves are below locals and above outgoing arguments. */ offset = info->args_size + info->callee_size; for (regno = 0; regno <= 31; regno++) { if ((reg_save_mask & (1 << regno)) != 0) { rtx offset_rtx; rtx mem; offset_rtx = GEN_INT (offset); if (satisfies_constraint_K (offset_rtx)) { mem = gen_rtx_MEM (word_mode, gen_rtx_PLUS (Pmode, stack_pointer_rtx, offset_rtx)); } else { /* r10 is caller saved so it can be used as a temp reg. */ rtx r10; r10 = gen_rtx_REG (word_mode, 10); insn = emit_move_insn (r10, offset_rtx); if (op == 0) RTX_FRAME_RELATED_P (insn) = 1; insn = emit_add (r10, r10, stack_pointer_rtx); if (op == 0) RTX_FRAME_RELATED_P (insn) = 1; mem = gen_rtx_MEM (word_mode, r10); } if (op == 0) insn = emit_move_insn (mem, gen_rtx_REG (word_mode, regno)); else insn = emit_move_insn (gen_rtx_REG (word_mode, regno), mem); /* only prologue instructions which set the sp fp or save a register should be marked as frame related. */ if (op == 0) RTX_FRAME_RELATED_P (insn) = 1; offset -= UNITS_PER_WORD; } } }
static void moxie_setup_incoming_varargs (cumulative_args_t cum_v, machine_mode mode ATTRIBUTE_UNUSED, tree type ATTRIBUTE_UNUSED, int *pretend_size, int no_rtl) { CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); int regno; int regs = 8 - *cum; *pretend_size = regs < 0 ? 0 : GET_MODE_SIZE (SImode) * regs; if (no_rtl) return; for (regno = *cum; regno < 8; regno++) { rtx reg = gen_rtx_REG (SImode, regno); rtx slot = gen_rtx_PLUS (Pmode, gen_rtx_REG (SImode, ARG_POINTER_REGNUM), GEN_INT (UNITS_PER_WORD * (3 + (regno-2)))); emit_move_insn (gen_rtx_MEM (SImode, slot), reg); } }
rtx nds32_expand_store_multiple (int base_regno, int count, rtx base_addr, rtx basemem, bool update_base_reg_p, rtx *update_base_reg) { int par_index; int offset; int start_idx; rtx result; rtx new_addr, mem, reg; if (count == 1) { reg = gen_rtx_REG (SImode, base_regno); if (update_base_reg_p) { *update_base_reg = gen_reg_rtx (SImode); return gen_unaligned_store_update_base_w (*update_base_reg, base_addr, reg); } else return gen_unaligned_store_w (gen_rtx_MEM (SImode, base_addr), reg); } /* Create the pattern that is presented in nds32-multiple.md. */ if (update_base_reg_p) { result = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1)); start_idx = 1; } else { result = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); start_idx = 0; } if (update_base_reg_p) { offset = count * 4; new_addr = plus_constant (Pmode, base_addr, offset); *update_base_reg = gen_reg_rtx (SImode); XVECEXP (result, 0, 0) = gen_rtx_SET (*update_base_reg, new_addr); } for (par_index = 0; par_index < count; par_index++) { offset = par_index * 4; /* 4-byte for storing data to memory. */ new_addr = plus_constant (Pmode, base_addr, offset); mem = adjust_automodify_address_nv (basemem, SImode, new_addr, offset); reg = gen_rtx_REG (SImode, base_regno + par_index); XVECEXP (result, 0, par_index + start_idx) = gen_rtx_SET (mem, reg); } return result; }
static void i386_pe_mark_dllimport (tree decl) { const char *oldname; char *newname; tree idp; rtx rtlname, newrtl; rtx symref; rtlname = XEXP (DECL_RTL (decl), 0); if (GET_CODE (rtlname) == SYMBOL_REF) oldname = XSTR (rtlname, 0); else if (GET_CODE (rtlname) == MEM && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF) oldname = XSTR (XEXP (rtlname, 0), 0); else abort (); if (i386_pe_dllexport_name_p (oldname)) { error ("%qs declared as both exported to and imported from a DLL", IDENTIFIER_POINTER (DECL_NAME (decl))); return; } else if (i386_pe_dllimport_name_p (oldname)) { /* Already done, but do a sanity check to prevent assembler errors. */ /* APPLE LOCAL begin mainline 2005-10-12 */ if (!DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl) || !DECL_DLLIMPORT_P (decl)) { error ("%Jfailure in redeclaration of '%D': dllimport'd " "symbol lacks external linkage.", decl, decl); abort(); } /* APPLE LOCAL end mainline 2005-10-12 */ return; } newname = alloca (strlen (DLL_IMPORT_PREFIX) + strlen (oldname) + 1); sprintf (newname, "%s%s", DLL_IMPORT_PREFIX, oldname); /* We pass newname through get_identifier to ensure it has a unique address. RTL processing can sometimes peek inside the symbol ref and compare the string's addresses to see if two symbols are identical. */ idp = get_identifier (newname); symref = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp)); SYMBOL_REF_DECL (symref) = decl; newrtl = gen_rtx_MEM (Pmode,symref); XEXP (DECL_RTL (decl), 0) = newrtl; /* APPLE LOCAL begin mainline 2005-10-12 */ DECL_DLLIMPORT_P (decl) = 1; /* APPLE LOCAL end mainline 2005-10-12 */ }
static void nds32_emit_post_inc_load_store (rtx reg, rtx base_reg, enum machine_mode mode, bool load_p) { gcc_assert (GET_MODE (reg) == mode); gcc_assert (GET_MODE (base_reg) == Pmode); /* Do not gen (set (reg) (mem (post_inc (reg)))) directly here since it may not recognize by gcc, so let gcc combine it at auto_inc_dec pass. */ if (load_p) emit_move_insn (reg, gen_rtx_MEM (mode, base_reg)); else emit_move_insn (gen_rtx_MEM (mode, base_reg), reg); emit_move_insn (base_reg, plus_constant(Pmode, base_reg, GET_MODE_SIZE (mode))); }
/* Generate and emit RTL to save or restore callee save registers */ static void expand_save_restore (struct lm32_frame_info *info, int op) { unsigned int reg_save_mask = info->reg_save_mask; int regno; HOST_WIDE_INT offset; rtx insn; /* Callee saves are below locals and above outgoing arguments */ offset = info->args_size + info->callee_size; for (regno = 0; regno <= 31; regno++) { if ((reg_save_mask & (1 << regno)) != 0) { if (op == 0) { insn = emit_move_insn (gen_rtx_MEM (word_mode, gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset))), gen_rtx_REG (word_mode, regno)); } else { insn = emit_move_insn (gen_rtx_REG (word_mode, regno), gen_rtx_MEM (word_mode, gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset)))); } /* only prologue instructions which set the sp fp or save a register should be marked as frame related */ if (op==0) RTX_FRAME_RELATED_P (insn) = 1; offset -= UNITS_PER_WORD; } } }
static rtx moxie_static_chain (const_tree ARG_UNUSED (fndecl_or_type), bool incoming_p) { rtx addr, mem; if (incoming_p) addr = plus_constant (Pmode, arg_pointer_rtx, 2 * UNITS_PER_WORD); else addr = plus_constant (Pmode, stack_pointer_rtx, -UNITS_PER_WORD); mem = gen_rtx_MEM (Pmode, addr); MEM_NOTRAP_P (mem) = 1; return mem; }
static void expand_one_error_var (tree var) { enum machine_mode mode = DECL_MODE (var); rtx x; if (mode == BLKmode) x = gen_rtx_MEM (BLKmode, const0_rtx); else if (mode == VOIDmode) x = const0_rtx; else x = gen_reg_rtx (mode); SET_DECL_RTL (var, x); }
static void lm32_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int *pretend_size, int no_rtl) { int first_anon_arg; tree fntype; int stdarg_p; fntype = TREE_TYPE (current_function_decl); stdarg_p = (TYPE_ARG_TYPES (fntype) != 0 && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)); if (stdarg_p) first_anon_arg = *cum + LM32_FIRST_ARG_REG; else { /* this is the common case, we have been passed details setup for the last named argument, we want to skip over the registers, if any used in passing this named paramter in order to determine which is the first registers used to pass anonymous arguments */ int size; if (mode==BLKmode) size = int_size_in_bytes (type); else size = GET_MODE_SIZE (mode); first_anon_arg = *cum + LM32_FIRST_ARG_REG + ((size + UNITS_PER_WORD - 1) / UNITS_PER_WORD); } if ((first_anon_arg < (LM32_FIRST_ARG_REG + LM32_NUM_ARG_REGS)) && !no_rtl) { int first_reg_offset = first_anon_arg; int size = LM32_FIRST_ARG_REG + LM32_NUM_ARG_REGS - first_anon_arg; rtx regblock; regblock = gen_rtx_MEM (BLKmode, plus_constant (arg_pointer_rtx, FIRST_PARM_OFFSET (0))); move_block_from_reg (first_reg_offset, regblock, size); *pretend_size = size * UNITS_PER_WORD; } }
static void i386_pe_mark_dllimport (tree decl) { const char *oldname; char *newname; tree idp; rtx rtlname, newrtl; rtx symref; rtlname = XEXP (DECL_RTL (decl), 0); if (GET_CODE (rtlname) == MEM) rtlname = XEXP (rtlname, 0); gcc_assert (GET_CODE (rtlname) == SYMBOL_REF); oldname = XSTR (rtlname, 0); if (i386_pe_dllexport_name_p (oldname)) { error ("%qs declared as both exported to and imported from a DLL", IDENTIFIER_POINTER (DECL_NAME (decl))); return; } else if (i386_pe_dllimport_name_p (oldname)) { /* Already done, but do a sanity check to prevent assembler errors. */ gcc_assert (DECL_EXTERNAL (decl) && TREE_PUBLIC (decl) && DECL_DLLIMPORT_P (decl)); return; } newname = alloca (strlen (DLL_IMPORT_PREFIX) + strlen (oldname) + 1); sprintf (newname, "%s%s", DLL_IMPORT_PREFIX, oldname); /* We pass newname through get_identifier to ensure it has a unique address. RTL processing can sometimes peek inside the symbol ref and compare the string's addresses to see if two symbols are identical. */ idp = get_identifier (newname); symref = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp)); SET_SYMBOL_REF_DECL (symref, decl); newrtl = gen_rtx_MEM (Pmode,symref); XEXP (DECL_RTL (decl), 0) = newrtl; DECL_DLLIMPORT_P (decl) = 1; }
static rtx moxie_static_chain (const_tree fndecl, bool incoming_p) { rtx addr, mem; if (!DECL_STATIC_CHAIN (fndecl)) return NULL; if (incoming_p) addr = plus_constant (arg_pointer_rtx, 2 * UNITS_PER_WORD); else addr = plus_constant (stack_pointer_rtx, -UNITS_PER_WORD); mem = gen_rtx_MEM (Pmode, addr); MEM_NOTRAP_P (mem) = 1; return mem; }
static void expand_one_stack_var_at (tree decl, HOST_WIDE_INT offset) { HOST_WIDE_INT align; rtx x; /* If this fails, we've overflowed the stack frame. Error nicely? */ gcc_assert (offset == trunc_int_for_mode (offset, Pmode)); x = plus_constant (virtual_stack_vars_rtx, offset); x = gen_rtx_MEM (DECL_MODE (decl), x); /* Set alignment we actually gave this decl. */ offset -= frame_phase; align = offset & -offset; align *= BITS_PER_UNIT; if (align > STACK_BOUNDARY || align == 0) align = STACK_BOUNDARY; DECL_ALIGN (decl) = align; DECL_USER_ALIGN (decl) = 0; set_mem_attributes (x, decl, true); SET_DECL_RTL (decl, x); }
static bool nds32_expand_movmemsi_unroll (rtx dstmem, rtx srcmem, rtx total_bytes, rtx alignment) { rtx dst_base_reg, src_base_reg; rtx tmp_reg; int maximum_bytes; int maximum_bytes_per_inst; int maximum_regs; int start_regno; int i, inst_num; HOST_WIDE_INT remain_bytes, remain_words; bool align_to_4_bytes = (INTVAL (alignment) & 3) == 0; bool align_to_2_bytes = (INTVAL (alignment) & 1) == 0; /* Because reduced-set regsiters has few registers (r0~r5, r6~10, r15, r28~r31, where 'r15' and 'r28~r31' cannot be used for register allocation), using 8 registers (32 bytes) for moving memory block may easily consume all of them. It makes register allocation/spilling hard to work. So we only allow maximum=4 registers (16 bytes) for moving memory block under reduced-set registers. */ if (TARGET_REDUCED_REGS) { maximum_regs = 4; maximum_bytes = 64; start_regno = 2; } else { /* $r25 is $tp so we use up to 8 registers. */ maximum_regs = 8; maximum_bytes = 160; start_regno = 16; } maximum_bytes_per_inst = maximum_regs * UNITS_PER_WORD; /* 1. Total_bytes is integer for sure. 2. Alignment is integer for sure. 3. Maximum 4 or 10 registers and up to 4 instructions, 4 * 4 * 4 = 64 bytes, 8 * 4 * 10 = 160 bytes. 4. The dstmem cannot be volatile memory access. 5. The srcmem cannot be volatile memory access. 6. Known shared alignment not align to 4 byte in v3m since lmw/smw *NOT* support unalign access with v3m configure. */ if (GET_CODE (total_bytes) != CONST_INT || GET_CODE (alignment) != CONST_INT || INTVAL (total_bytes) > maximum_bytes || MEM_VOLATILE_P (dstmem) || MEM_VOLATILE_P (srcmem) || (TARGET_ISA_V3M && !align_to_4_bytes)) return false; dst_base_reg = copy_to_mode_reg (SImode, XEXP (dstmem, 0)); src_base_reg = copy_to_mode_reg (SImode, XEXP (srcmem, 0)); remain_bytes = INTVAL (total_bytes); /* Do not update base address for last lmw/smw pair. */ inst_num = ((INTVAL (total_bytes) + (maximum_bytes_per_inst - 1)) / maximum_bytes_per_inst) - 1; for (i = 0; i < inst_num; i++) { nds32_emit_mem_move_block (start_regno, maximum_regs, &dst_base_reg, &dstmem, &src_base_reg, &srcmem, true); } remain_bytes -= maximum_bytes_per_inst * inst_num; remain_words = remain_bytes / UNITS_PER_WORD; remain_bytes = remain_bytes - (remain_words * UNITS_PER_WORD); if (remain_words != 0) { if (remain_bytes != 0) nds32_emit_mem_move_block (start_regno, remain_words, &dst_base_reg, &dstmem, &src_base_reg, &srcmem, true); else { /* Do not update address if no further byte to move. */ if (remain_words == 1) { /* emit move instruction if align to 4 byte and only 1 word to move. */ if (align_to_4_bytes) nds32_emit_mem_move (srcmem, dstmem, SImode, 0); else { tmp_reg = gen_reg_rtx (SImode); emit_insn ( gen_unaligned_load_w (tmp_reg, gen_rtx_MEM (SImode, src_base_reg))); emit_insn ( gen_unaligned_store_w (gen_rtx_MEM (SImode, dst_base_reg), tmp_reg)); } } else nds32_emit_mem_move_block (start_regno, remain_words, &dst_base_reg, &dstmem, &src_base_reg, &srcmem, false); } } switch (remain_bytes) { case 3: case 2: { if (align_to_2_bytes) nds32_emit_mem_move (srcmem, dstmem, HImode, 0); else { nds32_emit_mem_move (srcmem, dstmem, QImode, 0); nds32_emit_mem_move (srcmem, dstmem, QImode, 1); } if (remain_bytes == 3) nds32_emit_mem_move (srcmem, dstmem, QImode, 2); break; } case 1: nds32_emit_mem_move (srcmem, dstmem, QImode, 0); break; case 0: break; default: gcc_unreachable (); } /* Successfully create patterns, return true. */ return true; }
static bool nds32_expand_setmem_unroll (rtx dstmem, rtx size, rtx value, rtx align ATTRIBUTE_UNUSED, rtx expected_align ATTRIBUTE_UNUSED, rtx expected_size ATTRIBUTE_UNUSED) { unsigned maximum_regs, maximum_bytes, start_regno, regno; rtx value4word; rtx dst_base_reg, new_base_reg; unsigned HOST_WIDE_INT remain_bytes, remain_words, prepare_regs, fill_per_smw; unsigned HOST_WIDE_INT real_size; if (TARGET_REDUCED_REGS) { maximum_regs = 4; maximum_bytes = 64; start_regno = 2; } else { maximum_regs = 8; maximum_bytes = 128; start_regno = 16; } real_size = UINTVAL (size) & GET_MODE_MASK(SImode); if (!(CONST_INT_P (size) && real_size <= maximum_bytes)) return false; remain_bytes = real_size; gcc_assert (GET_MODE (value) == QImode || CONST_INT_P (value)); value4word = nds32_gen_dup_4_byte_to_word_value (value); prepare_regs = remain_bytes / UNITS_PER_WORD; dst_base_reg = copy_to_mode_reg (SImode, XEXP (dstmem, 0)); if (prepare_regs > maximum_regs) prepare_regs = maximum_regs; fill_per_smw = prepare_regs * UNITS_PER_WORD; regno = start_regno; switch (prepare_regs) { case 2: default: { rtx reg0 = gen_rtx_REG (SImode, regno); rtx reg1 = gen_rtx_REG (SImode, regno+1); unsigned last_regno = start_regno + prepare_regs - 1; emit_move_insn (reg0, value4word); emit_move_insn (reg1, value4word); rtx regd = gen_rtx_REG (DImode, regno); regno += 2; /* Try to utilize movd44! */ while (regno <= last_regno) { if ((regno + 1) <=last_regno) { rtx reg = gen_rtx_REG (DImode, regno); emit_move_insn (reg, regd); regno += 2; } else { rtx reg = gen_rtx_REG (SImode, regno); emit_move_insn (reg, reg0); regno += 1; } } break; } case 1: { rtx reg = gen_rtx_REG (SImode, regno++); emit_move_insn (reg, value4word); } break; case 0: break; } if (fill_per_smw) for (;remain_bytes >= fill_per_smw;remain_bytes -= fill_per_smw) { emit_insn (nds32_expand_store_multiple (start_regno, prepare_regs, dst_base_reg, dstmem, true, &new_base_reg)); dst_base_reg = new_base_reg; dstmem = gen_rtx_MEM (SImode, dst_base_reg); } remain_words = remain_bytes / UNITS_PER_WORD; if (remain_words) { emit_insn (nds32_expand_store_multiple (start_regno, remain_words, dst_base_reg, dstmem, true, &new_base_reg)); dst_base_reg = new_base_reg; dstmem = gen_rtx_MEM (SImode, dst_base_reg); } remain_bytes = remain_bytes - (remain_words * UNITS_PER_WORD); if (remain_bytes) { value = simplify_gen_subreg (QImode, value4word, SImode, subreg_lowpart_offset(QImode, SImode)); int offset = 0; for (;remain_bytes;--remain_bytes, ++offset) { nds32_emit_load_store (value, dstmem, QImode, offset, false); } } return true; }
rtx fr30_move_double (rtx * operands) { rtx src = operands[1]; rtx dest = operands[0]; enum rtx_code src_code = GET_CODE (src); enum rtx_code dest_code = GET_CODE (dest); enum machine_mode mode = GET_MODE (dest); rtx val; start_sequence (); if (dest_code == REG) { if (src_code == REG) { int reverse = (REGNO (dest) == REGNO (src) + 1); /* We normally copy the low-numbered register first. However, if the first register of operand 0 is the same as the second register of operand 1, we must copy in the opposite order. */ emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, reverse, TRUE, mode), operand_subword (src, reverse, TRUE, mode))); emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, !reverse, TRUE, mode), operand_subword (src, !reverse, TRUE, mode))); } else if (src_code == MEM) { rtx addr = XEXP (src, 0); int dregno = REGNO (dest); rtx dest0 = operand_subword (dest, 0, TRUE, mode);; rtx dest1 = operand_subword (dest, 1, TRUE, mode);; rtx new_mem; gcc_assert (GET_CODE (addr) == REG); /* Copy the address before clobbering it. See PR 34174. */ emit_insn (gen_rtx_SET (SImode, dest1, addr)); emit_insn (gen_rtx_SET (VOIDmode, dest0, adjust_address (src, SImode, 0))); emit_insn (gen_rtx_SET (SImode, dest1, plus_constant (dest1, UNITS_PER_WORD))); new_mem = gen_rtx_MEM (SImode, dest1); MEM_COPY_ATTRIBUTES (new_mem, src); emit_insn (gen_rtx_SET (VOIDmode, dest1, new_mem)); } else if (src_code == CONST_INT || src_code == CONST_DOUBLE) { rtx words[2]; split_double (src, &words[0], &words[1]); emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, 0, TRUE, mode), words[0])); emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, 1, TRUE, mode), words[1])); } } else if (src_code == REG && dest_code == MEM) { rtx addr = XEXP (dest, 0); rtx src0; rtx src1; gcc_assert (GET_CODE (addr) == REG); src0 = operand_subword (src, 0, TRUE, mode); src1 = operand_subword (src, 1, TRUE, mode); emit_move_insn (adjust_address (dest, SImode, 0), src0); if (REGNO (addr) == STACK_POINTER_REGNUM || REGNO (addr) == FRAME_POINTER_REGNUM) emit_insn (gen_rtx_SET (VOIDmode, adjust_address (dest, SImode, UNITS_PER_WORD), src1)); else { rtx new_mem; rtx scratch_reg_r0 = gen_rtx_REG (SImode, 0); /* We need a scratch register to hold the value of 'address + 4'. We use r0 for this purpose. It is used for example for long jumps and is already marked to not be used by normal register allocation. */ emit_insn (gen_movsi_internal (scratch_reg_r0, addr)); emit_insn (gen_addsi_small_int (scratch_reg_r0, scratch_reg_r0, GEN_INT (UNITS_PER_WORD))); new_mem = gen_rtx_MEM (SImode, scratch_reg_r0); MEM_COPY_ATTRIBUTES (new_mem, dest); emit_move_insn (new_mem, src1); emit_insn (gen_blockage ()); } } else /* This should have been prevented by the constraints on movdi_insn. */ gcc_unreachable (); val = get_insns (); end_sequence (); return val; }
rtx fr30_move_double (rtx * operands) { rtx src = operands[1]; rtx dest = operands[0]; enum rtx_code src_code = GET_CODE (src); enum rtx_code dest_code = GET_CODE (dest); enum machine_mode mode = GET_MODE (dest); rtx val; start_sequence (); if (dest_code == REG) { if (src_code == REG) { int reverse = (REGNO (dest) == REGNO (src) + 1); /* We normally copy the low-numbered register first. However, if the first register of operand 0 is the same as the second register of operand 1, we must copy in the opposite order. */ emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, reverse, TRUE, mode), operand_subword (src, reverse, TRUE, mode))); emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, !reverse, TRUE, mode), operand_subword (src, !reverse, TRUE, mode))); } else if (src_code == MEM) { rtx addr = XEXP (src, 0); int dregno = REGNO (dest); rtx dest0; rtx dest1; rtx new_mem; /* If the high-address word is used in the address, we must load it last. Otherwise, load it first. */ int reverse = (refers_to_regno_p (dregno, dregno + 1, addr, 0) != 0); gcc_assert (GET_CODE (addr) == REG); dest0 = operand_subword (dest, reverse, TRUE, mode); dest1 = operand_subword (dest, !reverse, TRUE, mode); if (reverse) { emit_insn (gen_rtx_SET (VOIDmode, dest1, adjust_address (src, SImode, 0))); emit_insn (gen_rtx_SET (SImode, dest0, gen_rtx_REG (SImode, REGNO (addr)))); emit_insn (gen_rtx_SET (SImode, dest0, plus_constant (dest0, UNITS_PER_WORD))); new_mem = gen_rtx_MEM (SImode, dest0); MEM_COPY_ATTRIBUTES (new_mem, src); emit_insn (gen_rtx_SET (VOIDmode, dest0, new_mem)); } else { emit_insn (gen_rtx_SET (VOIDmode, dest0, adjust_address (src, SImode, 0))); emit_insn (gen_rtx_SET (SImode, dest1, gen_rtx_REG (SImode, REGNO (addr)))); emit_insn (gen_rtx_SET (SImode, dest1, plus_constant (dest1, UNITS_PER_WORD))); new_mem = gen_rtx_MEM (SImode, dest1); MEM_COPY_ATTRIBUTES (new_mem, src); emit_insn (gen_rtx_SET (VOIDmode, dest1, new_mem)); } } else if (src_code == CONST_INT || src_code == CONST_DOUBLE) { rtx words[2]; split_double (src, &words[0], &words[1]); emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, 0, TRUE, mode), words[0])); emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, 1, TRUE, mode), words[1])); } } else if (src_code == REG && dest_code == MEM) { rtx addr = XEXP (dest, 0); rtx src0; rtx src1; gcc_assert (GET_CODE (addr) == REG); src0 = operand_subword (src, 0, TRUE, mode); src1 = operand_subword (src, 1, TRUE, mode); emit_insn (gen_rtx_SET (VOIDmode, adjust_address (dest, SImode, 0), src0)); if (REGNO (addr) == STACK_POINTER_REGNUM || REGNO (addr) == FRAME_POINTER_REGNUM) emit_insn (gen_rtx_SET (VOIDmode, adjust_address (dest, SImode, UNITS_PER_WORD), src1)); else { rtx new_mem; /* We need a scratch register to hold the value of 'address + 4'. We ought to allow gcc to find one for us, but for now, just push one of the source registers. */ emit_insn (gen_movsi_push (src0)); emit_insn (gen_movsi_internal (src0, addr)); emit_insn (gen_addsi_small_int (src0, src0, GEN_INT (UNITS_PER_WORD))); new_mem = gen_rtx_MEM (SImode, src0); MEM_COPY_ATTRIBUTES (new_mem, dest); emit_insn (gen_rtx_SET (VOIDmode, new_mem, src1)); emit_insn (gen_movsi_pop (src0)); } } else /* This should have been prevented by the constraints on movdi_insn. */ gcc_unreachable (); val = get_insns (); end_sequence (); return val; }
void init_caller_save () { char *first_obj = (char *) oballoc (0); rtx addr_reg; int offset; rtx address; int i, j; /* First find all the registers that we need to deal with and all the modes that they can have. If we can't find a mode to use, we can't have the register live over calls. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) { if (call_used_regs[i] && ! call_fixed_regs[i]) { for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++) { regno_save_mode[i][j] = choose_hard_reg_mode (i, j); if (regno_save_mode[i][j] == VOIDmode && j == 1) { call_fixed_regs[i] = 1; SET_HARD_REG_BIT (call_fixed_reg_set, i); } } } else regno_save_mode[i][1] = VOIDmode; } /* The following code tries to approximate the conditions under which we can easily save and restore a register without scratch registers or other complexities. It will usually work, except under conditions where the validity of an insn operand is dependent on the address offset. No such cases are currently known. We first find a typical offset from some BASE_REG_CLASS register. This address is chosen by finding the first register in the class and by finding the smallest power of two that is a valid offset from that register in every mode we will use to save registers. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (TEST_HARD_REG_BIT (reg_class_contents[(int) BASE_REG_CLASS], i)) break; if (i == FIRST_PSEUDO_REGISTER) abort (); addr_reg = gen_rtx_REG (Pmode, i); for (offset = 1 << (HOST_BITS_PER_INT / 2); offset; offset >>= 1) { address = gen_rtx_PLUS (Pmode, addr_reg, GEN_INT (offset)); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (regno_save_mode[i][1] != VOIDmode && ! strict_memory_address_p (regno_save_mode[i][1], address)) break; if (i == FIRST_PSEUDO_REGISTER) break; } /* If we didn't find a valid address, we must use register indirect. */ if (offset == 0) address = addr_reg; /* Next we try to form an insn to save and restore the register. We see if such an insn is recognized and meets its constraints. */ start_sequence (); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++) if (regno_save_mode[i][j] != VOIDmode) { rtx mem = gen_rtx_MEM (regno_save_mode[i][j], address); rtx reg = gen_rtx_REG (regno_save_mode[i][j], i); rtx savepat = gen_rtx_SET (VOIDmode, mem, reg); rtx restpat = gen_rtx_SET (VOIDmode, reg, mem); rtx saveinsn = emit_insn (savepat); rtx restinsn = emit_insn (restpat); int ok; reg_save_code[i][j] = recog_memoized (saveinsn); reg_restore_code[i][j] = recog_memoized (restinsn); /* Now extract both insns and see if we can meet their constraints. */ ok = (reg_save_code[i][j] != -1 && reg_restore_code[i][j] != -1); if (ok) { insn_extract (saveinsn); ok = constrain_operands (reg_save_code[i][j], 1); insn_extract (restinsn); ok &= constrain_operands (reg_restore_code[i][j], 1); } if (! ok) { regno_save_mode[i][j] = VOIDmode; if (j == 1) { call_fixed_regs[i] = 1; SET_HARD_REG_BIT (call_fixed_reg_set, i); } } } end_sequence (); obfree (first_obj); }
void expand_builtin_cilk_detach (tree exp) { rtx_insn *insn; tree fptr = get_frame_arg (exp); if (fptr == NULL_TREE) return; tree parent = cilk_dot (fptr, CILK_TI_FRAME_PARENT, 0); tree worker = cilk_dot (fptr, CILK_TI_FRAME_WORKER, 0); tree tail = cilk_arrow (worker, CILK_TI_WORKER_TAIL, 1); rtx wreg = expand_expr (worker, NULL_RTX, Pmode, EXPAND_NORMAL); if (GET_CODE (wreg) != REG) wreg = copy_to_reg (wreg); rtx preg = expand_expr (parent, NULL_RTX, Pmode, EXPAND_NORMAL); /* TMP <- WORKER.TAIL *TMP <- PARENT TMP <- TMP + 1 WORKER.TAIL <- TMP */ HOST_WIDE_INT worker_tail_offset = tree_to_shwi (DECL_FIELD_OFFSET (cilk_trees[CILK_TI_WORKER_TAIL])) + tree_to_shwi (DECL_FIELD_BIT_OFFSET (cilk_trees[CILK_TI_WORKER_TAIL])) / BITS_PER_UNIT; rtx tmem0 = gen_rtx_MEM (Pmode, plus_constant (Pmode, wreg, worker_tail_offset)); set_mem_attributes (tmem0, tail, 0); MEM_NOTRAP_P (tmem0) = 1; gcc_assert (MEM_VOLATILE_P (tmem0)); rtx treg = copy_to_mode_reg (Pmode, tmem0); rtx tmem1 = gen_rtx_MEM (Pmode, treg); set_mem_attributes (tmem1, TREE_TYPE (TREE_TYPE (tail)), 0); MEM_NOTRAP_P (tmem1) = 1; emit_move_insn (tmem1, preg); emit_move_insn (treg, plus_constant (Pmode, treg, GET_MODE_SIZE (Pmode))); /* There is a release barrier (st8.rel, membar #StoreStore, sfence, lwsync, etc.) between the two stores. On x86 normal volatile stores have proper semantics; the sfence would only be needed for nontemporal stores (which we could generate using the storent optab, for no benefit in this case). The predicate may return false even for a REG if this is the limited release operation that only stores 0. */ enum insn_code icode = direct_optab_handler (sync_lock_release_optab, Pmode); if (icode != CODE_FOR_nothing && insn_data[icode].operand[1].predicate (treg, Pmode) && (insn = GEN_FCN (icode) (tmem0, treg)) != NULL_RTX) emit_insn (insn); else emit_move_insn (tmem0, treg); /* The memory barrier inserted above should not prevent the load of flags from being moved before the stores, but in practice it does because it is implemented with unspec_volatile. In-order RISC machines should explicitly load flags earlier. */ tree flags = cilk_dot (fptr, CILK_TI_FRAME_FLAGS, 0); expand_expr (build2 (MODIFY_EXPR, void_type_node, flags, build2 (BIT_IOR_EXPR, TREE_TYPE (flags), flags, build_int_cst (TREE_TYPE (flags), CILK_FRAME_DETACHED))), const0_rtx, VOIDmode, EXPAND_NORMAL); }
/* Functions to expand load_multiple and store_multiple. They are auxiliary extern functions to help create rtx template. Check nds32-multiple.md file for the patterns. */ rtx nds32_expand_load_multiple (int base_regno, int count, rtx base_addr, rtx basemem, bool update_base_reg_p, rtx *update_base_reg) { int par_index; int offset; int start_idx; rtx result; rtx new_addr, mem, reg; /* Generate a unaligned load to prevent load instruction pull out from parallel, and then it will generate lwi, and lose unaligned acces */ if (count == 1) { reg = gen_rtx_REG (SImode, base_regno); if (update_base_reg_p) { *update_base_reg = gen_reg_rtx (SImode); return gen_unaligned_load_update_base_w (*update_base_reg, reg, base_addr); } else return gen_unaligned_load_w (reg, gen_rtx_MEM (SImode, base_addr)); } /* Create the pattern that is presented in nds32-multiple.md. */ if (update_base_reg_p) { result = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1)); start_idx = 1; } else { result = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); start_idx = 0; } if (update_base_reg_p) { offset = count * 4; new_addr = plus_constant (Pmode, base_addr, offset); *update_base_reg = gen_reg_rtx (SImode); XVECEXP (result, 0, 0) = gen_rtx_SET (*update_base_reg, new_addr); } for (par_index = 0; par_index < count; par_index++) { offset = par_index * 4; /* 4-byte for loading data to each register. */ new_addr = plus_constant (Pmode, base_addr, offset); mem = adjust_automodify_address_nv (basemem, SImode, new_addr, offset); reg = gen_rtx_REG (SImode, base_regno + par_index); XVECEXP (result, 0, (par_index + start_idx)) = gen_rtx_SET (reg, mem); } return result; }