gcc 源码分析:从IR-RTL 到汇编输出

在完成了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 ();

}

}