在完成了IR-RTL的優化與寄存器分配后
就來到匯編代碼的輸出:
實現如下:
class pass_final : public rtl_opt_pass
{
public:
pass_final (gcc::context *ctxt)
: rtl_opt_pass (pass_data_final, ctxt)
{}
? /* opt_pass methods: */
unsigned int execute (function *) final override
{
return rest_of_handle_final ();
}
}; // class pass_final
} // anon namespace
rest_of_handle_final (void)
{
const char *fnname = get_fnname_from_decl (current_function_decl);
? /* Turn debug markers into notes if the var-tracking pass has not
been invoked. ?*/
if (!flag_var_tracking && MAY_HAVE_DEBUG_MARKER_INSNS)
delete_vta_debug_insns (false);
? assemble_start_function (current_function_decl, fnname);
rtx_insn *first = get_insns ();
int seen = 0;
final_start_function_1 (&first, asm_out_file, &seen, optimize);
final_1 (first, asm_out_file, seen, optimize);
if (flag_ipa_ra
&& !lookup_attribute ("noipa", DECL_ATTRIBUTES (current_function_decl))
/* Functions with naked attributes are supported only with basic asm
statements in the body, thus for supported use cases the information
on clobbered registers is not available. ?*/
&& !lookup_attribute ("naked", DECL_ATTRIBUTES (current_function_decl)))
collect_fn_hard_reg_usage ();
final_end_function ();
? /* The IA-64 ".handlerdata" directive must be issued before the ".endp"
directive that closes the procedure descriptor. ?Similarly, for x64 SEH.
Otherwise it's not strictly necessary, but it doesn't hurt either. ?*/
output_function_exception_table (crtl->has_bb_partition ? 1 : 0);
? assemble_end_function (current_function_decl, fnname);
? /* Free up reg info memory. ?*/
free_reg_info ();
? if (! quiet_flag)
fflush (asm_out_file);
? /* Note that for those inline functions where we don't initially
know for certain that we will be generating an out-of-line copy,
the first invocation of this routine (rest_of_compilation) will
skip over this code by doing a `goto exit_rest_of_compilation;'.
Later on, wrapup_global_declarations will (indirectly) call
rest_of_compilation again for those inline functions that need
to have out-of-line copies generated. ?During that call, we
*will* be routed past here. ?*/
? timevar_push (TV_SYMOUT);
if (!DECL_IGNORED_P (current_function_decl))
debug_hooks->function_decl (current_function_decl);
timevar_pop (TV_SYMOUT);
? /* Release the blocks that are linked to DECL_INITIAL() to free the memory. ?*/
DECL_INITIAL (current_function_decl) = error_mark_node;
? if (DECL_STATIC_CONSTRUCTOR (current_function_decl)
&& targetm.have_ctors_dtors)
targetm.asm_out.constructor (XEXP (DECL_RTL (current_function_decl), 0),
decl_init_priority_lookup
(current_function_decl));
if (DECL_STATIC_DESTRUCTOR (current_function_decl)
&& targetm.have_ctors_dtors)
targetm.asm_out.destructor (XEXP (DECL_RTL (current_function_decl), 0),
decl_fini_priority_lookup
(current_function_decl));
return 0;
}
static void
final_1 (rtx_insn *first, FILE *file, int seen, int optimize_p)
{
rtx_insn *insn, *next;
? /* Used for -dA dump. ?*/
basic_block *start_to_bb = NULL;
basic_block *end_to_bb = NULL;
int bb_map_size = 0;
int bb_seqn = 0;
? last_ignored_compare = 0;
? init_recog ();
? CC_STATUS_INIT;
? if (flag_debug_asm)
{
basic_block bb;
? ? ? bb_map_size = get_max_uid () + 1;
start_to_bb = XCNEWVEC (basic_block, bb_map_size);
end_to_bb = XCNEWVEC (basic_block, bb_map_size);
? ? ? /* There is no cfg for a thunk. ?*/
if (!cfun->is_thunk)
FOR_EACH_BB_REVERSE_FN (bb, cfun)
{
start_to_bb[INSN_UID (BB_HEAD (bb))] = bb;
end_to_bb[INSN_UID (BB_END (bb))] = bb;
}
}
? /* Output the insns. ?*/
for (insn = first; insn;)
{
if (HAVE_ATTR_length)
{
if ((unsigned) INSN_UID (insn) >= INSN_ADDRESSES_SIZE ())
{
/* This can be triggered by bugs elsewhere in the compiler if
new insns are created after init_insn_lengths is called. ?*/
gcc_assert (NOTE_P (insn));
insn_current_address = -1;
}
else
insn_current_address = INSN_ADDRESSES (INSN_UID (insn));
/* final can be seen as an iteration of shorten_branches that
does nothing (since a fixed point has already been reached). ?*/
insn_last_address = insn_current_address;
}
? ? ? dump_basic_block_info (file, insn, start_to_bb, end_to_bb,
bb_map_size, &bb_seqn);
insn = final_scan_insn (insn, file, optimize_p, 0, &seen);
}
? maybe_output_next_view (&seen);
? if (flag_debug_asm)
{
free (start_to_bb);
free (end_to_bb);
}
? /* Remove CFI notes, to avoid compare-debug failures. ?*/
for (insn = first; insn; insn = next)
{
next = NEXT_INSN (insn);
if (NOTE_P (insn)
&& (NOTE_KIND (insn) == NOTE_INSN_CFI
|| NOTE_KIND (insn) == NOTE_INSN_CFI_LABEL))
delete_insn (insn);
}
}
rtx_insn *
final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p,
int nopeepholes, int *seen)
{
static int *enclosing_seen;
static int recursion_counter;
? gcc_assert (seen || recursion_counter);
gcc_assert (!recursion_counter || !seen || seen == enclosing_seen);
? if (!recursion_counter++)
enclosing_seen = seen;
else if (!seen)
seen = enclosing_seen;
? rtx_insn *ret = final_scan_insn_1 (insn, file, optimize_p, nopeepholes, seen);
? if (!--recursion_counter)
enclosing_seen = NULL;
? return ret;
}
static rtx_insn *
final_scan_insn_1 (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED,
int nopeepholes ATTRIBUTE_UNUSED, int *seen)
{
rtx_insn *next;
rtx_jump_table_data *table;
? insn_counter++;
? /* Ignore deleted insns. ?These can occur when we split insns (due to a
template of "#") while not optimizing. ?*/
if (insn->deleted ())
return NEXT_INSN (insn);
? switch (GET_CODE (insn))
{
case NOTE:
switch (NOTE_KIND (insn))
{
case NOTE_INSN_DELETED:
case NOTE_INSN_UPDATE_SJLJ_CONTEXT:
break;
?? ?case NOTE_INSN_SWITCH_TEXT_SECTIONS:
maybe_output_next_view (seen);
?? ? ?output_function_exception_table (0);
?? ? ?if (targetm.asm_out.unwind_emit)
targetm.asm_out.unwind_emit (asm_out_file, insn);
?? ? ?in_cold_section_p = !in_cold_section_p;
?? ? ?gcc_checking_assert (in_cold_section_p);
if (in_cold_section_p)
cold_function_name
= clone_function_name (current_function_decl, "cold");
?? ? ?if (dwarf2out_do_frame ())
{
dwarf2out_switch_text_section ();
if (!dwarf2_debug_info_emitted_p (current_function_decl)
&& !DECL_IGNORED_P (current_function_decl))
debug_hooks->switch_text_section ();
}
else if (!DECL_IGNORED_P (current_function_decl))
debug_hooks->switch_text_section ();
if (DECL_IGNORED_P (current_function_decl) && last_linenum
&& last_filename)
debug_hooks->set_ignored_loc (last_linenum, last_columnnum,
last_filename);
?? ? ?switch_to_section (current_function_section ());
targetm.asm_out.function_switched_text_sections (asm_out_file,
current_function_decl,
in_cold_section_p);
/* Emit a label for the split cold section. ?Form label name by
suffixing "cold" to the original function's name. ?*/
if (in_cold_section_p)
{
#ifdef ASM_DECLARE_COLD_FUNCTION_NAME
ASM_DECLARE_COLD_FUNCTION_NAME (asm_out_file,
IDENTIFIER_POINTER
(cold_function_name),
current_function_decl);
#else
ASM_OUTPUT_LABEL (asm_out_file,
IDENTIFIER_POINTER (cold_function_name));
#endif
if (dwarf2out_do_frame ()
&& cfun->fde->dw_fde_second_begin != NULL)
ASM_OUTPUT_LABEL (asm_out_file, cfun->fde->dw_fde_second_begin);
}
break;
?? ?case NOTE_INSN_BASIC_BLOCK:
if (need_profile_function)
{
profile_function (asm_out_file);
need_profile_function = false;
}
?? ? ?if (targetm.asm_out.unwind_emit)
targetm.asm_out.unwind_emit (asm_out_file, insn);
?? ? ?break;
?? ?case NOTE_INSN_EH_REGION_BEG:
ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LEHB",
NOTE_EH_HANDLER (insn));
break;
?? ?case NOTE_INSN_EH_REGION_END:
ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LEHE",
NOTE_EH_HANDLER (insn));
break;
?? ?case NOTE_INSN_PROLOGUE_END:
targetm.asm_out.function_end_prologue (file);
profile_after_prologue (file);
?? ? ?if ((*seen & (SEEN_EMITTED | SEEN_NOTE)) == SEEN_NOTE)
{
*seen |= SEEN_EMITTED;
force_source_line = true;
}
else
*seen |= SEEN_NOTE;
?? ? ?break;
?? ?case NOTE_INSN_EPILOGUE_BEG:
if (!DECL_IGNORED_P (current_function_decl))
(*debug_hooks->begin_epilogue) (last_linenum, last_filename);
targetm.asm_out.function_begin_epilogue (file);
break;
?? ?case NOTE_INSN_CFI:
dwarf2out_emit_cfi (NOTE_CFI (insn));
break;
?? ?case NOTE_INSN_CFI_LABEL:
ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LCFI",
NOTE_LABEL_NUMBER (insn));
break;
?? ?case NOTE_INSN_FUNCTION_BEG:
if (need_profile_function)
{
profile_function (asm_out_file);
need_profile_function = false;
}
?? ? ?app_disable ();
if (!DECL_IGNORED_P (current_function_decl))
debug_hooks->end_prologue (last_linenum, last_filename);
?? ? ?if ((*seen & (SEEN_EMITTED | SEEN_NOTE)) == SEEN_NOTE)
{
*seen |= SEEN_EMITTED;
force_source_line = true;
}
else
*seen |= SEEN_NOTE;
?? ? ?break;
?? ?case NOTE_INSN_BLOCK_BEG:
if (debug_info_level >= DINFO_LEVEL_NORMAL
|| dwarf_debuginfo_p ()
|| write_symbols == VMS_DEBUG)
{
int n = BLOCK_NUMBER (NOTE_BLOCK (insn));
?? ? ? ? ?app_disable ();
++block_depth;
high_block_linenum = last_linenum;
?? ? ? ? ?/* Output debugging info about the symbol-block beginning. ?*/
if (!DECL_IGNORED_P (current_function_decl))
debug_hooks->begin_block (last_linenum, n, NOTE_BLOCK (insn));
?? ? ? ? ?/* Mark this block as output. ?*/
TREE_ASM_WRITTEN (NOTE_BLOCK (insn)) = 1;
BLOCK_IN_COLD_SECTION_P (NOTE_BLOCK (insn)) = in_cold_section_p;
}
break;
?? ?case NOTE_INSN_BLOCK_END:
maybe_output_next_view (seen);
?? ? ?if (debug_info_level >= DINFO_LEVEL_NORMAL
|| dwarf_debuginfo_p ()
|| write_symbols == VMS_DEBUG)
{
int n = BLOCK_NUMBER (NOTE_BLOCK (insn));
?? ? ? ? ?app_disable ();
?? ? ? ? ?/* End of a symbol-block. ?*/
--block_depth;
gcc_assert (block_depth >= 0);
?? ? ? ? ?if (!DECL_IGNORED_P (current_function_decl))
debug_hooks->end_block (high_block_linenum, n);
gcc_assert (BLOCK_IN_COLD_SECTION_P (NOTE_BLOCK (insn))
== in_cold_section_p);
}
break;
?? ?case NOTE_INSN_DELETED_LABEL:
/* Emit the label. ?We may have deleted the CODE_LABEL because
the label could be proved to be unreachable, though still
referenced (in the form of having its address taken. ?*/
ASM_OUTPUT_DEBUG_LABEL (file, "L", CODE_LABEL_NUMBER (insn));
break;
?? ?case NOTE_INSN_DELETED_DEBUG_LABEL:
/* Similarly, but need to use different namespace for it. ?*/
if (CODE_LABEL_NUMBER (insn) != -1)
ASM_OUTPUT_DEBUG_LABEL (file, "LDL", CODE_LABEL_NUMBER (insn));
break;
?? ?case NOTE_INSN_VAR_LOCATION:
if (!DECL_IGNORED_P (current_function_decl))
{
debug_hooks->var_location (insn);
set_next_view_needed (seen);
}
break;
?? ?case NOTE_INSN_BEGIN_STMT:
gcc_checking_assert (cfun->debug_nonbind_markers);
if (!DECL_IGNORED_P (current_function_decl)
&& notice_source_line (insn, NULL))
{
output_source_line:
(*debug_hooks->source_line) (last_linenum, last_columnnum,
last_filename, last_discriminator,
true);
clear_next_view_needed (seen);
}
break;
?? ?case NOTE_INSN_INLINE_ENTRY:
gcc_checking_assert (cfun->debug_nonbind_markers);
if (!DECL_IGNORED_P (current_function_decl)
&& notice_source_line (insn, NULL))
{
(*debug_hooks->inline_entry) (LOCATION_BLOCK
(NOTE_MARKER_LOCATION (insn)));
goto output_source_line;
}
break;
?? ?default:
gcc_unreachable ();
break;
}
break;
? ? case BARRIER:
break;
? ? case CODE_LABEL:
/* The target port might emit labels in the output function for
some insn, e.g. sh.cc output_branchy_insn. ?*/
if (CODE_LABEL_NUMBER (insn) <= max_labelno)
{
align_flags alignment = LABEL_TO_ALIGNMENT (insn);
if (alignment.levels[0].log && NEXT_INSN (insn))
{
#ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
/* Output both primary and secondary alignment. ?*/
ASM_OUTPUT_MAX_SKIP_ALIGN (file, alignment.levels[0].log,
alignment.levels[0].maxskip);
ASM_OUTPUT_MAX_SKIP_ALIGN (file, alignment.levels[1].log,
alignment.levels[1].maxskip);
#else
#ifdef ASM_OUTPUT_ALIGN_WITH_NOP
ASM_OUTPUT_ALIGN_WITH_NOP (file, alignment.levels[0].log);
#else
ASM_OUTPUT_ALIGN (file, alignment.levels[0].log);
#endif
#endif
}
}
CC_STATUS_INIT;
? ? ? if (!DECL_IGNORED_P (current_function_decl) && LABEL_NAME (insn))
debug_hooks->label (as_a <rtx_code_label *> (insn));
? ? ? app_disable ();
? ? ? /* If this label is followed by a jump-table, make sure we put
the label in the read-only section. ?Also possibly write the
label and jump table together. ?*/
table = jump_table_for_label (as_a <rtx_code_label *> (insn));
if (table)
{
#if defined(ASM_OUTPUT_ADDR_VEC) || defined(ASM_OUTPUT_ADDR_DIFF_VEC)
/* In this case, the case vector is being moved by the
target, so don't output the label at all. ?Leave that
to the back end macros. ?*/
#else
if (! JUMP_TABLES_IN_TEXT_SECTION)
{
int log_align;
?? ? ? ? ?switch_to_section (targetm.asm_out.function_rodata_section
(current_function_decl,
jumptable_relocatable ()));
#ifdef ADDR_VEC_ALIGN
log_align = ADDR_VEC_ALIGN (table);
#else
log_align = exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT);
#endif
ASM_OUTPUT_ALIGN (file, log_align);
}
else
switch_to_section (current_function_section ());
#ifdef ASM_OUTPUT_CASE_LABEL
ASM_OUTPUT_CASE_LABEL (file, "L", CODE_LABEL_NUMBER (insn), table);
#else
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (insn));
#endif
#endif
break;
}
if (LABEL_ALT_ENTRY_P (insn))
output_alternate_entry_point (file, insn);
else
targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (insn));
break;
? ? default:
{
rtx body = PATTERN (insn);
int insn_code_number;
const char *templ;
bool is_stmt, *is_stmt_p;
?? ?if (MAY_HAVE_DEBUG_MARKER_INSNS && cfun->debug_nonbind_markers)
{
is_stmt = false;
is_stmt_p = NULL;
}
else
is_stmt_p = &is_stmt;
?? ?/* Reset this early so it is correct for ASM statements. ?*/
current_insn_predicate = NULL_RTX;
?? ?/* An INSN, JUMP_INSN or CALL_INSN.
First check for special kinds that recog doesn't recognize. ?*/
?? ?if (GET_CODE (body) == USE /* These are just declarations. ?*/
|| GET_CODE (body) == CLOBBER)
break;
?? ?/* Detect insns that are really jump-tables
and output them as such. ?*/
? ? ? ? if (JUMP_TABLE_DATA_P (insn))
{
#if !(defined(ASM_OUTPUT_ADDR_VEC) || defined(ASM_OUTPUT_ADDR_DIFF_VEC))
int vlen, idx;
#endif
?? ? ? ?if (! JUMP_TABLES_IN_TEXT_SECTION)
switch_to_section (targetm.asm_out.function_rodata_section
(current_function_decl,
jumptable_relocatable ()));
else
switch_to_section (current_function_section ());
?? ? ? ?app_disable ();
#if defined(ASM_OUTPUT_ADDR_VEC) || defined(ASM_OUTPUT_ADDR_DIFF_VEC)
if (GET_CODE (body) == ADDR_VEC)
{
#ifdef ASM_OUTPUT_ADDR_VEC
ASM_OUTPUT_ADDR_VEC (PREV_INSN (insn), body);
#else
gcc_unreachable ();
#endif
}
else
{
#ifdef ASM_OUTPUT_ADDR_DIFF_VEC
ASM_OUTPUT_ADDR_DIFF_VEC (PREV_INSN (insn), body);
#else
gcc_unreachable ();
#endif
}
#else
vlen = XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC);
for (idx = 0; idx < vlen; idx++)
{
if (GET_CODE (body) == ADDR_VEC)
{
#ifdef ASM_OUTPUT_ADDR_VEC_ELT
ASM_OUTPUT_ADDR_VEC_ELT
(file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0)));
#else
gcc_unreachable ();
#endif
}
else
{
#ifdef ASM_OUTPUT_ADDR_DIFF_ELT
ASM_OUTPUT_ADDR_DIFF_ELT
(file,
body,
CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)),
CODE_LABEL_NUMBER (XEXP (XEXP (body, 0), 0)));
#else
gcc_unreachable ();
#endif
}
}
#ifdef ASM_OUTPUT_CASE_END
ASM_OUTPUT_CASE_END (file,
CODE_LABEL_NUMBER (PREV_INSN (insn)),
insn);
#endif
#endif
?? ? ? ?switch_to_section (current_function_section ());
?? ? ? ?if (debug_variable_location_views
&& !DECL_IGNORED_P (current_function_decl))
debug_hooks->var_location (insn);
?? ? ? ?break;
}
/* Output this line note if it is the first or the last line
note in a row. ?*/
if (!DECL_IGNORED_P (current_function_decl)
&& notice_source_line (insn, is_stmt_p))
{
if (flag_verbose_asm)
asm_show_source (last_filename, last_linenum);
(*debug_hooks->source_line) (last_linenum, last_columnnum,
last_filename, last_discriminator,
is_stmt);
clear_next_view_needed (seen);
}
else
maybe_output_next_view (seen);
?? ?gcc_checking_assert (!DEBUG_INSN_P (insn));
?? ?if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == ASM_INPUT)
body = XVECEXP (body, 0, 0);
?? ?if (GET_CODE (body) == ASM_INPUT)
{
const char *string = XSTR (body, 0);
?? ? ? ?/* There's no telling what that did to the condition codes. ?*/
CC_STATUS_INIT;
?? ? ? ?if (string[0])
{
expanded_location loc;
?? ??? ?app_enable ();
loc = expand_location (ASM_INPUT_SOURCE_LOCATION (body));
if (*loc.file && loc.line)
fprintf (asm_out_file, "%s %i \"%s\" 1\n",
ASM_COMMENT_START, loc.line, loc.file);
fprintf (asm_out_file, "\t%s\n", string);
#if HAVE_AS_LINE_ZERO
if (*loc.file && loc.line)
fprintf (asm_out_file, "%s 0 \"\" 2\n", ASM_COMMENT_START);
#endif
}
break;
}
?? ?/* Detect `asm' construct with operands. ?*/
if (asm_noperands (body) >= 0)
{
unsigned int noperands = asm_noperands (body);
rtx *ops = XALLOCAVEC (rtx, noperands);
const char *string;
location_t loc;
expanded_location expanded;
?? ? ? ?/* There's no telling what that did to the condition codes. ?*/
CC_STATUS_INIT;
?? ? ? ?/* Get out the operand values. ?*/
string = decode_asm_operands (body, ops, NULL, NULL, NULL, &loc);
/* Inhibit dying on what would otherwise be compiler bugs. ?*/
insn_noperands = noperands;
this_is_asm_operands = insn;
expanded = expand_location (loc);
#ifdef FINAL_PRESCAN_INSN
FINAL_PRESCAN_INSN (insn, ops, insn_noperands);
#endif
?? ? ? ?/* Output the insn using them. ?*/
if (string[0])
{
app_enable ();
if (expanded.file && expanded.line)
fprintf (asm_out_file, "%s %i \"%s\" 1\n",
ASM_COMMENT_START, expanded.line, expanded.file);
output_asm_insn (string, ops);
#if HAVE_AS_LINE_ZERO
if (expanded.file && expanded.line)
fprintf (asm_out_file, "%s 0 \"\" 2\n", ASM_COMMENT_START);
#endif
}
?? ? ? ?if (targetm.asm_out.final_postscan_insn)
targetm.asm_out.final_postscan_insn (file, insn, ops,
insn_noperands);
?? ? ? ?this_is_asm_operands = 0;
break;
}
?? ?app_disable ();
?? ?if (rtx_sequence *seq = dyn_cast <rtx_sequence *> (body))
{
/* A delayed-branch sequence */
int i;
?? ? ? ?final_sequence = seq;
?? ? ? ?/* The first insn in this SEQUENCE might be a JUMP_INSN that will
force the restoration of a comparison that was previously
thought unnecessary. ?If that happens, cancel this sequence
and cause that insn to be restored. ?*/
?? ? ? ?next = final_scan_insn (seq->insn (0), file, 0, 1, seen);
if (next != seq->insn (1))
{
final_sequence = 0;
return next;
}
?? ? ? ?for (i = 1; i < seq->len (); i++)
{
rtx_insn *insn = seq->insn (i);
rtx_insn *next = NEXT_INSN (insn);
/* We loop in case any instruction in a delay slot gets
split. ?*/
do
insn = final_scan_insn (insn, file, 0, 1, seen);
while (insn != next);
}
#ifdef DBR_OUTPUT_SEQEND
DBR_OUTPUT_SEQEND (file);
#endif
final_sequence = 0;
?? ? ? ?/* If the insn requiring the delay slot was a CALL_INSN, the
insns in the delay slot are actually executed before the
called function. ?Hence we don't preserve any CC-setting
actions in these insns and the CC must be marked as being
clobbered by the function. ?*/
if (CALL_P (seq->insn (0)))
{
CC_STATUS_INIT;
}
break;
}
?? ?/* We have a real machine instruction as rtl. ?*/
?? ?body = PATTERN (insn);
?? ?/* Do machine-specific peephole optimizations if desired. ?*/
?? ?if (HAVE_peephole && optimize_p && !flag_no_peephole && !nopeepholes)
{
rtx_insn *next = peephole (insn);
/* When peepholing, if there were notes within the peephole,
emit them before the peephole. ?*/
if (next != 0 && next != NEXT_INSN (insn))
{
rtx_insn *note, *prev = PREV_INSN (insn);
?? ??? ?for (note = NEXT_INSN (insn); note != next;
note = NEXT_INSN (note))
final_scan_insn (note, file, optimize_p, nopeepholes, seen);
?? ??? ?/* Put the notes in the proper position for a later
rescan. ?For example, the SH target can do this
when generating a far jump in a delayed branch
sequence. ?*/
note = NEXT_INSN (insn);
SET_PREV_INSN (note) = prev;
SET_NEXT_INSN (prev) = note;
SET_NEXT_INSN (PREV_INSN (next)) = insn;
SET_PREV_INSN (insn) = PREV_INSN (next);
SET_NEXT_INSN (insn) = next;
SET_PREV_INSN (next) = insn;
}
?? ? ? ?/* PEEPHOLE might have changed this. ?*/
body = PATTERN (insn);
}
?? ?/* Try to recognize the instruction.
If successful, verify that the operands satisfy the
constraints for the instruction. ?Crash if they don't,
since `reload' should have changed them so that they do. ?*/
?? ?insn_code_number = recog_memoized (insn);
cleanup_subreg_operands (insn);
?? ?/* Dump the insn in the assembly for debugging (-dAP).
If the final dump is requested as slim RTL, dump slim
RTL to the assembly file also. ?*/
if (flag_dump_rtl_in_asm)
{
print_rtx_head = ASM_COMMENT_START;
if (! (dump_flags & TDF_SLIM))
print_rtl_single (asm_out_file, insn);
else
dump_insn_slim (asm_out_file, insn);
print_rtx_head = "";
}
?? ?if (! constrain_operands_cached (insn, 1))
fatal_insn_not_found (insn);
?? ?/* Some target machines need to prescan each insn before
it is output. ?*/
#ifdef FINAL_PRESCAN_INSN
FINAL_PRESCAN_INSN (insn, recog_data.operand, recog_data.n_operands);
#endif
?? ?if (targetm.have_conditional_execution ()
&& GET_CODE (PATTERN (insn)) == COND_EXEC)
current_insn_predicate = COND_EXEC_TEST (PATTERN (insn));
?? ?current_output_insn = debug_insn = insn;
?? ?/* Find the proper template for this insn. ?*/
?templ = get_insn_template (insn_code_number, insn);
?? ?/* If the C code returns 0, it means that it is a jump insn
which follows a deleted test insn, and that test insn
needs to be reinserted. ?*/
if (templ == 0)
{
rtx_insn *prev;
?? ? ? ?gcc_assert (prev_nonnote_insn (insn) == last_ignored_compare);
?? ? ? ?/* We have already processed the notes between the setter and
the user. ?Make sure we don't process them again, this is
particularly important if one of the notes is a block
scope note or an EH note. ?*/
for (prev = insn;
prev != last_ignored_compare;
prev = PREV_INSN (prev))
{
if (NOTE_P (prev))
delete_insn (prev);?? ?/* Use delete_note. ?*/
}
?? ? ? ?return prev;
}
?? ?/* If the template is the string "#", it means that this insn must
be split. ?*/
if (templ[0] == '#' && templ[1] == '\0')
{
rtx_insn *new_rtx = try_split (body, insn, 0);
?? ? ? ?/* If we didn't split the insn, go away. ?*/
if (new_rtx == insn && PATTERN (new_rtx) == body)
fatal_insn ("could not split insn", insn);
?? ? ? ?/* If we have a length attribute, this instruction should have
been split in shorten_branches, to ensure that we would have
valid length info for the splitees. ?*/
gcc_assert (!HAVE_ATTR_length);
?? ? ? ?return new_rtx;
}
?? ?/* ??? This will put the directives in the wrong place if
get_insn_template outputs assembly directly. ?However calling it
before get_insn_template breaks if the insns is split. ?*/
if (targetm.asm_out.unwind_emit_before_insn
&& targetm.asm_out.unwind_emit)
targetm.asm_out.unwind_emit (asm_out_file, insn);
?? ?rtx_call_insn *call_insn = dyn_cast <rtx_call_insn *> (insn);
if (call_insn != NULL)
{
rtx x = call_from_call_insn (call_insn);
x = XEXP (x, 0);
if (x && MEM_P (x) && GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
{
tree t;
x = XEXP (x, 0);
t = SYMBOL_REF_DECL (x);
if (t)
assemble_external (t);
}
}
?? ?/* Output assembler code from the template. ?*/
output_asm_insn (templ, recog_data.operand);
?? ?/* Some target machines need to postscan each insn after
it is output. ?*/
if (targetm.asm_out.final_postscan_insn)
targetm.asm_out.final_postscan_insn (file, insn, recog_data.operand,
recog_data.n_operands);
?? ?if (!targetm.asm_out.unwind_emit_before_insn
&& targetm.asm_out.unwind_emit)
targetm.asm_out.unwind_emit (asm_out_file, insn);
?? ?/* Let the debug info back-end know about this call. ?We do this only
after the instruction has been emitted because labels that may be
created to reference the call instruction must appear after it. ?*/
if ((debug_variable_location_views || call_insn != NULL)
&& !DECL_IGNORED_P (current_function_decl))
debug_hooks->var_location (insn);
?? ?current_output_insn = debug_insn = 0;
}
}
return NEXT_INSN (insn);
}
const char *
get_insn_template (int code, rtx_insn *insn)
{
switch (insn_data[code].output_format)
{
case INSN_OUTPUT_FORMAT_SINGLE:
return insn_data[code].output.single;
case INSN_OUTPUT_FORMAT_MULTI:
return insn_data[code].output.multi[which_alternative];
case INSN_OUTPUT_FORMAT_FUNCTION:
gcc_assert (insn);
return (*insn_data[code].output.function) (recog_data.operand, insn);
? ? default:
gcc_unreachable ();
}
}