Commit 3e79d08e authored by Matthias Braun's avatar Matthias Braun
Browse files

be: Move main backend loop into backends.

Instead of having a generaic main loop in be_main with several callbacks
into the targets, split the code in be_main up into a set of convenience
functions and let the targets implement the main loop itself. This is
nicer because:

- The control flow into and out of the functions in the target becomes
  obvious as no external party is using callbacks anymore.
- Targets can insert custom code at any place between the generic
  passes.
- In the future we can parameterize the generic backend passes directly
  instead of stuffing info into generic isa_if structures.
parent 23f78db8
......@@ -15,6 +15,7 @@
#include "benode.h"
#include "bestack.h"
#include "gen_TEMPLATE_regalloc_if.h"
#include "irprog_t.h"
#include "lower_builtins.h"
#include "lower_calls.h"
#include "panic.h"
......@@ -22,10 +23,9 @@
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
/**
* Transforms the standard firm graph into
* a TEMLPATE firm graph
* Transforms the standard firm graph into a TEMLPATE firm graph
*/
static void TEMPLATE_prepare_graph(ir_graph *irg)
static void TEMPLATE_select_instructions(ir_graph *irg)
{
/* transform nodes into assembler instructions */
be_timer_push(T_CODEGEN);
......@@ -34,24 +34,28 @@ static void TEMPLATE_prepare_graph(ir_graph *irg)
be_dump(DUMP_BE, irg, "code-selection");
}
static void TEMPLATE_generate_code(FILE *output, const char *cup_name)
{
be_begin(output, cup_name);
foreach_irp_irg(i, irg) {
if (!be_step_first(irg))
continue;
/**
* Last touchups and emitting of the generated code of a function.
*/
static void TEMPLATE_emit(ir_graph *irg)
{
/* fix stack entity offsets */
be_fix_stack_nodes(irg, &TEMPLATE_registers[REG_SP]);
TEMPLATE_select_instructions(irg);
/* emit code */
TEMPLATE_emit_function(irg);
}
be_step_schedule(irg);
be_step_regalloc(irg);
static void TEMPLATE_before_ra(ir_graph *irg)
{
(void)irg;
be_fix_stack_nodes(irg, &TEMPLATE_registers[REG_SP]);
TEMPLATE_emit_function(irg);
be_step_last(irg);
}
be_finish();
}
static void TEMPLATE_init(void)
......@@ -65,14 +69,6 @@ static void TEMPLATE_finish(void)
TEMPLATE_free_opcodes();
}
static void TEMPLATE_begin_codegeneration(void)
{
}
static void TEMPLATE_end_codegeneration(void)
{
}
static void TEMPLATE_lower_for_target(void)
{
lower_builtins(0, NULL);
......@@ -147,15 +143,11 @@ static arch_isa_if_t const TEMPLATE_isa_if = {
.init = TEMPLATE_init,
.finish = TEMPLATE_finish,
.get_params = TEMPLATE_get_backend_params,
.generate_code = TEMPLATE_generate_code,
.lower_for_target = TEMPLATE_lower_for_target,
.is_valid_clobber = TEMPLATE_is_valid_clobber,
.begin_codegeneration = TEMPLATE_begin_codegeneration,
.end_codegeneration = TEMPLATE_end_codegeneration,
.new_spill = TEMPLATE_new_spill,
.new_reload = TEMPLATE_new_reload,
.prepare_graph = TEMPLATE_prepare_graph,
.before_ra = TEMPLATE_before_ra,
.emit = TEMPLATE_emit,
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_TEMPLATE)
......
......@@ -109,11 +109,6 @@ static int amd64_get_sp_bias(const ir_node *node)
return 0;
}
static void amd64_before_ra(ir_graph *irg)
{
be_sched_fix_flags(irg, &amd64_reg_classes[CLASS_amd64_flags], NULL, NULL, NULL);
}
static const arch_register_req_t *am_pushpop_base_reqs[] = {
&amd64_single_reg_req_gp_rsp,
&amd64_class_reg_req_gp,
......@@ -496,6 +491,18 @@ static int determine_rbp_input(ir_node *ret)
panic("no rbp input found at %+F", ret);
}
/**
* prepare graph and perform code selection.
*/
static void amd64_select_instructions(ir_graph *irg)
{
be_timer_push(T_CODEGEN);
amd64_transform_graph(irg);
be_timer_pop(T_CODEGEN);
be_dump(DUMP_BE, irg, "code-selection");
}
static void introduce_epilogue(ir_node *ret)
{
const arch_register_t *sp = &amd64_registers[REG_RSP];
......@@ -596,7 +603,7 @@ static void introduce_prologue_epilogue(ir_graph *irg)
/**
* Called immediatly before emit phase.
*/
static void amd64_finish_graph(ir_graph *irg)
static void amd64_finish_and_emit(ir_graph *irg)
{
be_stack_layout_t *stack_layout = be_get_irg_stack_layout(irg);
bool at_begin = stack_layout->sp_relative;
......@@ -620,7 +627,9 @@ static void amd64_finish_graph(ir_graph *irg)
amd64_finish_irg(irg);
/* emit code */
be_timer_push(T_EMIT);
amd64_emit_function(irg);
be_timer_pop(T_EMIT);
}
static void amd64_finish(void)
......@@ -628,26 +637,33 @@ static void amd64_finish(void)
amd64_free_opcodes();
}
static void amd64_begin_codegeneration(void)
static void amd64_generate_code(FILE *output, const char *cup_name)
{
amd64_constants = pmap_create();
}
be_begin(output, cup_name);
static void amd64_end_codegeneration(void)
{
pmap_destroy(amd64_constants);
}
foreach_irp_irg(i, irg) {
if (!be_step_first(irg))
continue;
/**
* prepare graph and perform code selection.
*/
static void amd64_prepare_graph(ir_graph *irg)
{
be_timer_push(T_CODEGEN);
amd64_transform_graph(irg);
be_timer_pop(T_CODEGEN);
amd64_select_instructions(irg);
be_dump(DUMP_BE, irg, "code-selection");
be_step_schedule(irg);
be_timer_push(T_RA_PREPARATION);
be_sched_fix_flags(irg, &amd64_reg_classes[CLASS_amd64_flags], NULL,
NULL, NULL);
be_timer_pop(T_RA_PREPARATION);
be_step_regalloc(irg);
amd64_finish_and_emit(irg);
be_step_last(irg);
}
be_finish();
pmap_destroy(amd64_constants);
}
static void amd64_lower_for_target(void)
......@@ -765,16 +781,12 @@ static arch_isa_if_t const amd64_isa_if = {
.init = amd64_init,
.finish = amd64_finish,
.get_params = amd64_get_backend_params,
.generate_code = amd64_generate_code,
.lower_for_target = amd64_lower_for_target,
.is_valid_clobber = amd64_is_valid_clobber,
.begin_codegeneration = amd64_begin_codegeneration,
.end_codegeneration = amd64_end_codegeneration,
.new_spill = amd64_new_spill,
.new_reload = amd64_new_reload,
.handle_intrinsics = amd64_handle_intrinsics,
.prepare_graph = amd64_prepare_graph,
.before_ra = amd64_before_ra,
.emit = amd64_finish_graph,
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_amd64)
......
......@@ -43,7 +43,7 @@ ir_mode *arm_mode_flags;
/**
* Transforms the standard Firm graph into an ARM firm graph.
*/
static void arm_prepare_graph(ir_graph *irg)
static void arm_select_instructions(ir_graph *irg)
{
/* transform nodes into assembler instructions */
be_timer_push(T_CODEGEN);
......@@ -86,17 +86,6 @@ static ir_node *arm_new_spill(ir_node *value, ir_node *after)
return store;
}
static void arm_emit(ir_graph *irg)
{
arm_finish_graph(irg);
arm_emit_function(irg);
}
static void arm_before_ra(ir_graph *irg)
{
be_sched_fix_flags(irg, &arm_reg_classes[CLASS_arm_flags], NULL, NULL, NULL);
}
static ir_entity *divsi3;
static ir_entity *udivsi3;
static ir_entity *modsi3;
......@@ -173,16 +162,38 @@ static void arm_handle_intrinsics(ir_graph *irg)
irg_walk_graph(irg, handle_intrinsic, NULL, NULL);
}
static void arm_begin_codegeneration(void)
static void arm_generate_code(FILE *output, const char *cup_name)
{
be_gas_emit_types = false;
be_gas_elf_type_char = '%';
be_begin(output, cup_name);
arm_emit_file_prologue();
}
static void arm_end_codegeneration(void)
{
foreach_irp_irg(i, irg) {
if (!be_step_first(irg))
continue;
arm_select_instructions(irg);
be_step_schedule(irg);
be_timer_push(T_RA_PREPARATION);
be_sched_fix_flags(irg, &arm_reg_classes[CLASS_arm_flags], NULL, NULL, NULL);
be_timer_pop(T_RA_PREPARATION);
be_step_regalloc(irg);
be_timer_push(T_EMIT);
arm_finish_graph(irg);
arm_emit_function(irg);
be_timer_pop(T_EMIT);
be_step_last(irg);
}
be_finish();
}
/**
......@@ -272,11 +283,6 @@ static const backend_params *arm_get_libfirm_params(void)
return &arm_backend_params;
}
static void arm_finish(void)
{
arm_free_opcodes();
}
static void arm_init(void)
{
arm_mode_gp = new_int_mode("arm_gp", irma_twos_complement,
......@@ -288,6 +294,11 @@ static void arm_init(void)
arm_init_backend_params();
}
static void arm_finish(void)
{
arm_free_opcodes();
}
static arch_isa_if_t const arm_isa_if = {
.n_registers = N_ARM_REGISTERS,
.registers = arm_registers,
......@@ -298,16 +309,12 @@ static arch_isa_if_t const arm_isa_if = {
.init = arm_init,
.finish = arm_finish,
.get_params = arm_get_libfirm_params,
.generate_code = arm_generate_code,
.lower_for_target = arm_lower_for_target,
.is_valid_clobber = arm_is_valid_clobber,
.begin_codegeneration = arm_begin_codegeneration,
.end_codegeneration = arm_end_codegeneration,
.new_spill = arm_new_spill,
.new_reload = arm_new_reload,
.handle_intrinsics = arm_handle_intrinsics,
.prepare_graph = arm_prepare_graph,
.before_ra = arm_before_ra,
.emit = arm_emit,
};
static const lc_opt_enum_int_items_t arm_fpu_items[] = {
......
......@@ -137,4 +137,17 @@ static inline void be_dump(be_dump_flags_t const mask, ir_graph *const irg, char
dump_ir_graph(irg, suffix);
}
/**
* @defgroup beconvenience Convenience Function for driving code generation.
* @{
*/
void be_begin(FILE *output, const char *cup_name);
void be_finish(void);
bool be_step_first(ir_graph *irg);
void be_step_regalloc(ir_graph *irg);
void be_step_schedule(ir_graph *irg);
void be_step_last(ir_graph *irg);
/** @} */
#endif
......@@ -324,6 +324,11 @@ struct arch_isa_if_t {
*/
const backend_params *(*get_params)(void);
/**
* Generate code for the current firm program.
*/
void (*generate_code)(FILE *output, const char *cup_name);
/**
* lowers current program for target. See the documentation for
* be_lower_for_target() for details.
......@@ -336,17 +341,6 @@ struct arch_isa_if_t {
*/
int (*is_valid_clobber)(const char *clobber);
/**
* Start codegeneration
* @return a new isa instance
*/
void (*begin_codegeneration)(void);
/**
* Free the isa instance.
*/
void (*end_codegeneration)(void);
/**
* mark node as rematerialized
*/
......@@ -377,24 +371,6 @@ struct arch_isa_if_t {
* intrinsics here.
*/
void (*handle_intrinsics)(ir_graph *irg);
/**
* Initialize a graph for codegeneration. Code selection is usually
* performed in this step.
*/
void (*prepare_graph)(ir_graph *irg);
/**
* Called before register allocation.
*/
void (*before_ra)(ir_graph *irg);
/**
* Last step in the backend. Called after register allocation.
* May perform peephole optimizations and legalizations and finally emit
* the code.
*/
void (*emit)(ir_graph *irg);
};
static inline bool arch_irn_is_ignore(const ir_node *irn)
......
......@@ -57,6 +57,7 @@
#include "beemitter.h"
static struct obstack obst;
static be_main_env_t env;
/* options visible for anyone */
be_options_t be_options = {
......@@ -273,7 +274,146 @@ void firm_be_init(void)
be_init_modules();
}
/* Finalize the Firm backend. */
static ir_timer_t *bemain_timer;
/**
* Prepare a backend graph for code generation and initialize its irg
*/
static void initialize_birg(be_irg_t *birg, ir_graph *irg, be_main_env_t *env)
{
/* don't duplicate locals in backend when dumping... */
ir_remove_dump_flags(ir_dump_flag_consts_local);
be_dump(DUMP_INITIAL, irg, "begin");
assure_irg_properties(irg,
IR_GRAPH_PROPERTY_NO_BADS
| IR_GRAPH_PROPERTY_NO_UNREACHABLE_CODE
| IR_GRAPH_PROPERTY_NO_CRITICAL_EDGES
| IR_GRAPH_PROPERTY_MANY_RETURNS);
memset(birg, 0, sizeof(*birg));
birg->main_env = env;
obstack_init(&birg->obst);
irg->be_data = birg;
be_info_init_irg(irg);
birg->lv = be_liveness_new(irg);
/* Verify the initial graph */
if (be_options.do_verify) {
be_timer_push(T_VERIFY);
bool fine = irg_verify(irg);
be_check_verify_result(fine, irg);
be_timer_pop(T_VERIFY);
}
}
static ir_graph *be_prepare_profile(const char *const cup_name)
{
obstack_printf(&obst, "%s.prof", cup_name);
obstack_1grow(&obst, '\0');
const char *prof_filename = obstack_finish(&obst);
bool have_profile = false;
if (be_options.opt_profile_use) {
bool res = ir_profile_read(prof_filename);
if (!res) {
be_warningf(NULL, "could not read profile data '%s'", prof_filename);
} else {
ir_create_execfreqs_from_profile();
ir_profile_free();
have_profile = true;
}
}
ir_graph *prof_init_irg = NULL;
if (be_options.opt_profile_generate)
prof_init_irg = ir_profile_instrument(prof_filename);
if (!have_profile) {
be_timer_push(T_EXECFREQ);
foreach_irp_irg(i, irg) {
ir_estimate_execfreq(irg);
}
be_timer_pop(T_EXECFREQ);
}
return prof_init_irg;
}
void be_begin(FILE *file_handle, const char *cup_name)
{
obstack_init(&obst);
memset(be_asm_constraint_flags, 0, sizeof(be_asm_constraint_flags));
bemain_timer = NULL;
if (be_options.timing) {
bemain_timer = ir_timer_new();
if (ir_timer_enter_high_priority())
be_warningf(NULL, "could not enter high priority mode");
ir_timer_reset_and_start(bemain_timer);
}
if (stat_ev_enabled) {
const char *dot = strrchr(cup_name, '.');
const char *pos = dot ? dot : cup_name + strlen(cup_name);
char *buf = ALLOCAN(char, pos - cup_name + 1);
strncpy(buf, cup_name, pos - cup_name);
buf[pos - cup_name] = '\0';
stat_ev_ctx_push_str("bemain_compilation_unit", cup_name);
}
be_timing = be_options.timing;
/* perform target lowering if it didn't happen yet */
if (get_irp_n_irgs() > 0 && !irg_is_constrained(get_irp_irg(0), IR_GRAPH_CONSTRAINT_TARGET_LOWERED))
be_lower_for_target();
if (be_timing) {
for (be_timer_id_t t = T_FIRST; t < T_LAST+1; ++t) {
be_timers[t] = ir_timer_new();
ir_timer_init_parent(be_timers[t]);
}
}
be_emit_init(file_handle);
memset(&env, 0, sizeof(env));
env.ent_trampoline_map = pmap_create();
env.pic_trampolines_type = new_type_segment(NEW_IDENT("$PIC_TRAMPOLINE_TYPE"), tf_none);
env.ent_pic_symbol_map = pmap_create();
env.pic_symbols_type = new_type_segment(NEW_IDENT("$PIC_SYMBOLS_TYPE"), tf_none);
env.cup_name = cup_name;
be_info_init();
/* First: initialize all birgs */
size_t num_birgs = 0;
/* we might need 1 birg more for instrumentation constructor */
be_irg_t *const birgs = OALLOCN(&obst, be_irg_t, get_irp_n_irgs()+1);
foreach_irp_irg(i, irg) {
ir_entity *entity = get_irg_entity(irg);
if (get_entity_linkage(entity) & IR_LINKAGE_NO_CODEGEN)
continue;
initialize_birg(&birgs[num_birgs++], irg, &env);
if (isa_if->handle_intrinsics)
isa_if->handle_intrinsics(irg);
be_dump(DUMP_INITIAL, irg, "prepared");
}
/* Prepare basicblock profile generation/usage. Note: You should avoid
* introducing new control flow after this point or you won't have profile
* data for the new basic blocks. */
ir_graph *prof_init_irg = be_prepare_profile(cup_name);
if (prof_init_irg != NULL)
initialize_birg(&birgs[num_birgs++], prof_init_irg, &env);
be_gas_begin_compilation_unit(&env);
}
void firm_be_finish(void)
{
finish_isa();
......@@ -322,69 +462,6 @@ float_int_conversion_overflow_style_t be_get_float_int_overflow(void)
return be_get_backend_param()->float_int_overflow;
}
/**
* Initializes the main environment for the backend.
*
* @param env an empty environment
* @param file_handle the file handle where the output will be written to
*/
static be_main_env_t *be_init_env(be_main_env_t *const env,
char const *const compilation_unit_name)
{
obstack_init(&obst);
memset(env, 0, sizeof(*env));
env->ent_trampoline_map = pmap_create();
env->pic_trampolines_type = new_type_segment(NEW_IDENT("$PIC_TRAMPOLINE_TYPE"), tf_none);
env->ent_pic_symbol_map = pmap_create();
env->pic_symbols_type = new_type_segment(NEW_IDENT("$PIC_SYMBOLS_TYPE"), tf_none);
env->cup_name = compilation_unit_name;
isa_if->begin_codegeneration();
memset(be_asm_constraint_flags, 0, sizeof(be_asm_constraint_flags));
return env;
}
/**
* Called when the be_main_env_t can be destroyed.
*/
static void be_done_env(be_main_env_t *env)
{
isa_if->end_codegeneration();
pmap_destroy(env->ent_trampoline_map);
pmap_destroy(env->ent_pic_symbol_map);
free_type(env->pic_trampolines_type);
free_type(env->pic_symbols_type);
obstack_free(&obst, NULL);
}
/**
* Prepare a backend graph for code generation and initialize its irg
*/
static void initialize_birg(be_irg_t *birg, ir_graph *irg, be_main_env_t *env)
{
/* don't duplicate locals in backend when dumping... */
ir_remove_dump_flags(ir_dump_flag_consts_local);
be_dump(DUMP_INITIAL, irg, "begin");
assure_irg_properties(irg,
IR_GRAPH_PROPERTY_NO_BADS
| IR_GRAPH_PROPERTY_NO_UNREACHABLE_CODE
| IR_GRAPH_PROPERTY_NO_CRITICAL_EDGES
| IR_GRAPH_PROPERTY_MANY_RETURNS);
memset(birg, 0, sizeof(*birg));
birg->main_env = env;
obstack_init(&birg->obst);
irg->be_data = birg;
be_info_init_irg(irg);
birg->lv = be_liveness_new(irg);
}
int be_timing;
static const char *get_timer_name(be_timer_id_t id)
......@@ -452,43 +529,13 @@ void be_lower_for_target(void)
}
}
static ir_graph *be_prepare_profile(const char *const cup_name)
{
obstack_printf(&obst, "%s.prof", cup_name);
obstack_1grow(&obst, '\0');
const char *prof_filename = obstack_finish(&obst);
static int cse_setting;
bool have_profile = false;
if (be_options.opt_profile_use) {
bool res = ir_profile_read(prof_filename);
if (!res) {
be_warningf(NULL, "could not read profile data '%s'", prof_filename);
} else {
ir_create_execfreqs_from_profile();