}
\f
+/* Return true if labels in asm operands BODY are LABEL_REFs. */
+
+static bool
+asm_labels_ok (rtx body)
+{
+ rtx asmop;
+ int i;
+
+ asmop = extract_asm_operands (body);
+ if (asmop == NULL_RTX)
+ return true;
+
+ for (i = 0; i < ASM_OPERANDS_LABEL_LENGTH (asmop); i++)
+ if (GET_CODE (ASM_OPERANDS_LABEL (asmop, i)) != LABEL_REF)
+ return false;
+
+ return true;
+}
+
/* Check that X is an insn-body for an `asm' with operands
and that the operands mentioned in it are legitimate. */
const char **constraints;
int i;
+ if (!asm_labels_ok (x))
+ return 0;
+
/* Post-reload, be more strict with things. */
if (reload_completed)
{
/* Oops, the caller has made X no longer canonical.
Let's redo the changes in the correct order. */
rtx tem = XEXP (x, 0);
- validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
- validate_change (insn, &XEXP (x, 1), tem, 1);
+ validate_unshare_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
+ validate_unshare_change (insn, &XEXP (x, 1), tem, 1);
return true;
}
else
(GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode) -
offset);
+ gcc_assert (GET_MODE_PRECISION (wanted_mode)
+ == GET_MODE_BITSIZE (wanted_mode));
pos %= GET_MODE_BITSIZE (wanted_mode);
newmem = adjust_address_nv (XEXP (x, 0), wanted_mode, offset);
return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode
|| mode == VOIDmode)
&& (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
- && LEGITIMATE_CONSTANT_P (op));
+ && targetm.legitimate_constant_p (mode == VOIDmode
+ ? GET_MODE (op)
+ : mode, op));
/* Except for certain constants with VOIDmode, already checked for,
OP's mode must match MODE if MODE specifies a mode. */
&& (GET_MODE (op) == mode || mode == VOIDmode
|| GET_MODE (op) == VOIDmode)
&& (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
- && LEGITIMATE_CONSTANT_P (op));
+ && targetm.legitimate_constant_p (mode == VOIDmode
+ ? GET_MODE (op)
+ : mode, op));
}
/* Returns 1 if OP is an operand that is a CONST_INT. */
nonmemory_operand (rtx op, enum machine_mode mode)
{
if (CONSTANT_P (op))
- {
- /* Don't accept CONST_INT or anything similar
- if the caller wants something floating. */
- if (GET_MODE (op) == VOIDmode && mode != VOIDmode
- && GET_MODE_CLASS (mode) != MODE_INT
- && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
- return 0;
-
- if (CONST_INT_P (op)
- && mode != VOIDmode
- && trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
- return 0;
-
- return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode
- || mode == VOIDmode)
- && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
- && LEGITIMATE_CONSTANT_P (op));
- }
+ return immediate_operand (op, mode);
if (GET_MODE (op) != mode && mode != VOIDmode)
return 0;
case PRE_MODIFY:
case POST_MODIFY:
if (strchr (recog_data.constraints[opno], '<') == NULL
- || strchr (recog_data.constraints[opno], '>')
+ && strchr (recog_data.constraints[opno], '>')
== NULL)
return 0;
break;
return 0;
}
-/* Return 1 iff OPERAND (assumed to be a REG rtx)
+/* Return true iff OPERAND (assumed to be a REG rtx)
is a hard reg in class CLASS when its regno is offset by OFFSET
and changed to mode MODE.
If REG occupies multiple hard regs, all of them must be in CLASS. */
-int
-reg_fits_class_p (rtx operand, enum reg_class cl, int offset,
+bool
+reg_fits_class_p (const_rtx operand, reg_class_t cl, int offset,
enum machine_mode mode)
{
int regno = REGNO (operand);
if (cl == NO_REGS)
- return 0;
+ return false;
- return (regno < FIRST_PSEUDO_REGISTER
+ return (HARD_REGISTER_NUM_P (regno)
&& in_hard_reg_set_p (reg_class_contents[(int) cl],
mode, regno + offset));
}
}
else
{
- rtx last = split_insn (insn);
- if (last)
+ if (split_insn (insn))
{
- /* The split sequence may include barrier, but the
- BB boundary we are interested in will be set to
- previous one. */
-
- while (BARRIER_P (last))
- last = PREV_INSN (last);
SET_BIT (blocks, bb->index);
changed = true;
}
/* While scanning basic block BB, we found a match of length MATCH_LEN,
starting at INSN. Perform the replacement, removing the old insns and
- replacing them with ATTEMPT. Returns the last insn emitted. */
+ replacing them with ATTEMPT. Returns the last insn emitted, or NULL
+ if the replacement is rejected. */
static rtx
peep2_attempt (basic_block bb, rtx insn, int match_len, rtx attempt)
{
int i;
- rtx last, note, before_try, x;
+ rtx last, eh_note, as_note, before_try, x;
+ rtx old_insn, new_insn;
bool was_call = false;
+ /* If we are splitting an RTX_FRAME_RELATED_P insn, do not allow it to
+ match more than one insn, or to be split into more than one insn. */
+ old_insn = peep2_insn_data[peep2_current].insn;
+ if (RTX_FRAME_RELATED_P (old_insn))
+ {
+ bool any_note = false;
+ rtx note;
+
+ if (match_len != 0)
+ return NULL;
+
+ /* Look for one "active" insn. I.e. ignore any "clobber" insns that
+ may be in the stream for the purpose of register allocation. */
+ if (active_insn_p (attempt))
+ new_insn = attempt;
+ else
+ new_insn = next_active_insn (attempt);
+ if (next_active_insn (new_insn))
+ return NULL;
+
+ /* We have a 1-1 replacement. Copy over any frame-related info. */
+ RTX_FRAME_RELATED_P (new_insn) = 1;
+
+ /* Allow the backend to fill in a note during the split. */
+ for (note = REG_NOTES (new_insn); note ; note = XEXP (note, 1))
+ switch (REG_NOTE_KIND (note))
+ {
+ case REG_FRAME_RELATED_EXPR:
+ case REG_CFA_DEF_CFA:
+ case REG_CFA_ADJUST_CFA:
+ case REG_CFA_OFFSET:
+ case REG_CFA_REGISTER:
+ case REG_CFA_EXPRESSION:
+ case REG_CFA_RESTORE:
+ case REG_CFA_SET_VDRAP:
+ any_note = true;
+ break;
+ default:
+ break;
+ }
+
+ /* If the backend didn't supply a note, copy one over. */
+ if (!any_note)
+ for (note = REG_NOTES (old_insn); note ; note = XEXP (note, 1))
+ switch (REG_NOTE_KIND (note))
+ {
+ case REG_FRAME_RELATED_EXPR:
+ case REG_CFA_DEF_CFA:
+ case REG_CFA_ADJUST_CFA:
+ case REG_CFA_OFFSET:
+ case REG_CFA_REGISTER:
+ case REG_CFA_EXPRESSION:
+ case REG_CFA_RESTORE:
+ case REG_CFA_SET_VDRAP:
+ add_reg_note (new_insn, REG_NOTE_KIND (note), XEXP (note, 0));
+ any_note = true;
+ break;
+ default:
+ break;
+ }
+
+ /* If there still isn't a note, make sure the unwind info sees the
+ same expression as before the split. */
+ if (!any_note)
+ {
+ rtx old_set, new_set;
+
+ /* The old insn had better have been simple, or annotated. */
+ old_set = single_set (old_insn);
+ gcc_assert (old_set != NULL);
+
+ new_set = single_set (new_insn);
+ if (!new_set || !rtx_equal_p (new_set, old_set))
+ add_reg_note (new_insn, REG_FRAME_RELATED_EXPR, old_set);
+ }
+
+ /* Copy prologue/epilogue status. This is required in order to keep
+ proper placement of EPILOGUE_BEG and the DW_CFA_remember_state. */
+ maybe_copy_prologue_epilogue_insn (old_insn, new_insn);
+ }
+
/* If we are splitting a CALL_INSN, look for the CALL_INSN
in SEQ and copy our CALL_INSN_FUNCTION_USAGE and other
cfg-related call notes. */
for (i = 0; i <= match_len; ++i)
{
int j;
- rtx old_insn, new_insn, note;
+ rtx note;
j = peep2_buf_position (peep2_current + i);
old_insn = peep2_insn_data[j].insn;
break;
}
- i = peep2_buf_position (peep2_current + match_len);
+ /* If we matched any instruction that had a REG_ARGS_SIZE, then
+ move those notes over to the new sequence. */
+ as_note = NULL;
+ for (i = match_len; i >= 0; --i)
+ {
+ int j = peep2_buf_position (peep2_current + i);
+ old_insn = peep2_insn_data[j].insn;
+
+ as_note = find_reg_note (old_insn, REG_ARGS_SIZE, NULL);
+ if (as_note)
+ break;
+ }
- note = find_reg_note (peep2_insn_data[i].insn, REG_EH_REGION, NULL_RTX);
+ i = peep2_buf_position (peep2_current + match_len);
+ eh_note = find_reg_note (peep2_insn_data[i].insn, REG_EH_REGION, NULL_RTX);
/* Replace the old sequence with the new. */
last = emit_insn_after_setloc (attempt,
delete_insn_chain (insn, peep2_insn_data[i].insn, false);
/* Re-insert the EH_REGION notes. */
- if (note || (was_call && nonlocal_goto_handler_labels))
+ if (eh_note || (was_call && nonlocal_goto_handler_labels))
{
edge eh_edge;
edge_iterator ei;
if (eh_edge->flags & (EDGE_EH | EDGE_ABNORMAL_CALL))
break;
- if (note)
- copy_reg_eh_region_note_backward (note, last, before_try);
+ if (eh_note)
+ copy_reg_eh_region_note_backward (eh_note, last, before_try);
if (eh_edge)
for (x = last; x != before_try; x = PREV_INSN (x))
peep2_do_cleanup_cfg |= purge_dead_edges (bb);
}
+ /* Re-insert the ARGS_SIZE notes. */
+ if (as_note)
+ fixup_args_size_notes (before_try, last, INTVAL (XEXP (as_note, 0)));
+
/* If we generated a jump instruction, it won't have
JUMP_LABEL set. Recompute after we're done. */
for (x = last; x != before_try; x = PREV_INSN (x))
if (peep2_current_count == MAX_INSNS_PER_PEEP2)
return false;
- /* If an insn has RTX_FRAME_RELATED_P set, peephole substitution would lose
- the REG_FRAME_RELATED_EXPR that is attached. */
+ /* If an insn has RTX_FRAME_RELATED_P set, do not allow it to be matched with
+ any other pattern, lest it change the semantics of the frame info. */
if (RTX_FRAME_RELATED_P (insn))
{
/* Let the buffer drain first. */
if (peep2_current_count > 0)
return false;
- /* Step over the insn then return true without adding the insn
- to the buffer; this will cause us to process the next
- insn. */
- df_simulate_one_insn_forwards (bb, insn, live);
- return true;
+ /* Now the insn will be the only thing in the buffer. */
}
pos = peep2_buf_position (peep2_current + peep2_current_count);
attempt = peephole2_insns (PATTERN (head), head, &match_len);
if (attempt != NULL)
{
- rtx last;
- last = peep2_attempt (bb, head, match_len, attempt);
- peep2_update_life (bb, match_len, last, PREV_INSN (attempt));
- }
- else
- {
- /* If no match, advance the buffer by one insn. */
- peep2_current = peep2_buf_position (peep2_current + 1);
- peep2_current_count--;
+ rtx last = peep2_attempt (bb, head, match_len, attempt);
+ if (last)
+ {
+ peep2_update_life (bb, match_len, last, PREV_INSN (attempt));
+ continue;
+ }
}
+
+ /* No match: advance the buffer by one insn. */
+ peep2_current = peep2_buf_position (peep2_current + 1);
+ peep2_current_count--;
}
}
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_df_finish | TODO_verify_rtl_sharing |
- TODO_dump_func /* todo_flags_finish */
+ 0 /* todo_flags_finish */
}
};
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ 0 /* todo_flags_finish */
}
};
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ 0 /* todo_flags_finish */
}
};
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ 0 /* todo_flags_finish */
}
};
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_verify_flow |
- TODO_dump_func /* todo_flags_finish */
+ TODO_verify_flow /* todo_flags_finish */
}
};
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func | TODO_verify_rtl_sharing /* todo_flags_finish */
+ TODO_verify_rtl_sharing /* todo_flags_finish */
}
};