X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ir%2Fbe%2Fsparc%2Fsparc_emitter.c;h=07eef6137e5e6d277a69580e5753c5526c247b7c;hb=1b63e1946e0e454a59ddc6eb7df8fe3bc372a10e;hp=fcab9f0461f66e8c05fdc2519833c106f38fe224;hpb=65a52a96e8ab7ed601d7f98c516d37c46b674b4a;p=libfirm diff --git a/ir/be/sparc/sparc_emitter.c b/ir/be/sparc/sparc_emitter.c index fcab9f046..07eef6137 100644 --- a/ir/be/sparc/sparc_emitter.c +++ b/ir/be/sparc/sparc_emitter.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1995-2008 University of Karlsruhe. All right reserved. + * Copyright (C) 1995-2010 University of Karlsruhe. All right reserved. * * This file is part of libFirm. * @@ -51,17 +51,10 @@ #include "gen_sparc_emitter.h" #include "sparc_nodes_attr.h" #include "sparc_new_nodes.h" +#include "gen_sparc_regalloc_if.h" -#define SNPRINTF_BUF_LEN 128 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;) -/** - * attribute of SAVE node which follows immediatelly after the START node - * we need this to correct all offsets since SPARC expects - * some reserved stack space after the stackpointer - */ -const sparc_save_attr_t *save_attr; - /** * Returns the register at in position pos. */ @@ -116,25 +109,46 @@ static const arch_register_t *get_out_reg(const ir_node *node, int pos) return reg; } -/************************************************************* - * _ _ __ _ _ - * (_) | | / _| | | | | - * _ __ _ __ _ _ __ | |_| |_ | |__ ___| |_ __ ___ _ __ - * | '_ \| '__| | '_ \| __| _| | '_ \ / _ \ | '_ \ / _ \ '__| - * | |_) | | | | | | | |_| | | | | | __/ | |_) | __/ | - * | .__/|_| |_|_| |_|\__|_| |_| |_|\___|_| .__/ \___|_| - * | | | | - * |_| |_| - *************************************************************/ +static bool is_valid_immediate(int32_t value) +{ + return -4096 <= value && value < 4096; +} void sparc_emit_immediate(const ir_node *node) { - // TODO: make sure it's a valid simm13 ? - const sparc_attr_t *attr = get_sparc_attr_const(node); + const sparc_attr_t *attr = get_sparc_attr_const(node); + ir_entity *entity = attr->immediate_value_entity; - assert(!(attr->immediate_value < -4096 || attr->immediate_value > 4096)); + if (entity == NULL) { + int32_t value = attr->immediate_value; + assert(is_valid_immediate(value)); + be_emit_irprintf("%d", value); + } else { + be_emit_cstring("%lo("); + be_gas_emit_entity(entity); + if (attr->immediate_value != 0) { + be_emit_irprintf("%+d", attr->immediate_value); + } + be_emit_char(')'); + } +} - be_emit_irprintf("%d", attr->immediate_value); +void sparc_emit_high_immediate(const ir_node *node) +{ + const sparc_attr_t *attr = get_sparc_attr_const(node); + ir_entity *entity = attr->immediate_value_entity; + + be_emit_cstring("%hi("); + if (entity == NULL) { + uint32_t value = (uint32_t) attr->immediate_value; + be_emit_irprintf("0x%X", value); + } else { + be_gas_emit_entity(entity); + if (attr->immediate_value != 0) { + be_emit_irprintf("%+d", attr->immediate_value); + } + } + be_emit_char(')'); } void sparc_emit_source_register(const ir_node *node, int pos) @@ -167,18 +181,58 @@ void sparc_emit_reg_or_imm(const ir_node *node, int pos) } } +static bool is_stack_pointer_relative(const ir_node *node) +{ + const arch_register_t *sp = &sparc_gp_regs[REG_SP]; + return (is_sparc_St(node) && get_in_reg(node, n_sparc_St_ptr) == sp) + || (is_sparc_Ld(node) && get_in_reg(node, n_sparc_Ld_ptr) == sp); +} + /** * emit SP offset */ -void sparc_emit_offset(const ir_node *node) +void sparc_emit_offset(const ir_node *node, int offset_node_pos) { const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node); - assert(attr->base.is_load_store); - if (attr->offset > 0) - be_emit_irprintf("+%ld", attr->offset); + if (attr->is_reg_reg) { + assert(!attr->is_frame_entity); + assert(attr->base.immediate_value == 0); + assert(attr->base.immediate_value_entity == NULL); + be_emit_char('+'); + sparc_emit_source_register(node, offset_node_pos); + } else if (attr->is_frame_entity) { + int32_t offset = attr->base.immediate_value; + /* bad hack: the real stack stuff is behind the always-there spill + * space for the register window and stack */ + if (is_stack_pointer_relative(node)) + offset += SPARC_MIN_STACKSIZE; + if (offset != 0) { + assert(is_valid_immediate(offset)); + be_emit_irprintf("%+ld", offset); + } + } else if (attr->base.immediate_value != 0 + || attr->base.immediate_value_entity != NULL) { + be_emit_char('+'); + sparc_emit_immediate(node); + } } +void sparc_emit_float_load_store_mode(const ir_node *node) +{ + const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node); + ir_mode *mode = attr->load_store_mode; + int bits = get_mode_size_bits(mode); + + assert(mode_is_float(mode)); + + switch (bits) { + case 32: return; + case 64: be_emit_char('d'); return; + case 128: be_emit_char('q'); return; + } + panic("invalid flaot load/store mode %+F", mode); +} /** * Emit load mode char @@ -186,19 +240,19 @@ void sparc_emit_offset(const ir_node *node) void sparc_emit_load_mode(const ir_node *node) { const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node); - ir_mode *mode = attr->load_store_mode; - int bits = get_mode_size_bits(mode); - bool is_signed = mode_is_signed(mode); - - if (bits == 16) { - be_emit_string(is_signed ? "sh" : "uh"); - } else if (bits == 8) { - be_emit_string(is_signed ? "sb" : "ub"); - } else if (bits == 64) { - be_emit_string("d"); - } else { - assert(bits == 32); - } + ir_mode *mode = attr->load_store_mode; + int bits = get_mode_size_bits(mode); + bool is_signed = mode_is_signed(mode); + + if (bits == 16) { + be_emit_string(is_signed ? "sh" : "uh"); + } else if (bits == 8) { + be_emit_string(is_signed ? "sb" : "ub"); + } else if (bits == 64) { + be_emit_char('d'); + } else { + assert(bits == 32); + } } /** @@ -207,18 +261,70 @@ void sparc_emit_load_mode(const ir_node *node) void sparc_emit_store_mode(const ir_node *node) { const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node); - ir_mode *mode = attr->load_store_mode; - int bits = get_mode_size_bits(mode); + ir_mode *mode = attr->load_store_mode; + int bits = get_mode_size_bits(mode); + + if (bits == 16) { + be_emit_string("h"); + } else if (bits == 8) { + be_emit_string("b"); + } else if (bits == 64) { + be_emit_char('d'); + } else { + assert(bits == 32); + } +} + +/** + * emit integer signed/unsigned prefix char + */ +void sparc_emit_mode_sign_prefix(const ir_node *node) +{ + ir_mode *mode = get_irn_mode(node); + bool is_signed = mode_is_signed(mode); + be_emit_string(is_signed ? "s" : "u"); +} + +static void emit_fp_suffix(const ir_mode *mode) +{ + unsigned bits = get_mode_size_bits(mode); + assert(mode_is_float(mode)); + + if (bits == 32) { + be_emit_char('s'); + } else if (bits == 64) { + be_emit_char('d'); + } else if (bits == 128) { + be_emit_char('q'); + } else { + panic("invalid FP mode"); + } +} - if (bits == 16) { - be_emit_string("h"); - } else if (bits == 8) { - be_emit_string("b"); - } else if (bits == 64) { - be_emit_string("d"); - } else { - assert(bits == 32); - } +void sparc_emit_fp_conv_source(const ir_node *node) +{ + const sparc_fp_conv_attr_t *attr = get_sparc_fp_conv_attr_const(node); + emit_fp_suffix(attr->src_mode); +} + +void sparc_emit_fp_conv_destination(const ir_node *node) +{ + const sparc_fp_conv_attr_t *attr = get_sparc_fp_conv_attr_const(node); + emit_fp_suffix(attr->dest_mode); +} + +/** + * emits the FP mode suffix char + */ +void sparc_emit_fp_mode_suffix(const ir_node *node) +{ + const sparc_fp_attr_t *attr = get_sparc_fp_attr_const(node); + emit_fp_suffix(attr->fp_mode); +} + +static ir_node *get_jump_target(const ir_node *jump) +{ + return get_irn_link(jump); } /** @@ -226,7 +332,7 @@ void sparc_emit_store_mode(const ir_node *node) */ static void sparc_emit_cfop_target(const ir_node *node) { - ir_node *block = get_irn_link(node); + ir_node *block = get_jump_target(node); be_gas_emit_block_name(block); } @@ -238,17 +344,6 @@ static void sparc_emit_entity(ir_entity *entity) be_gas_emit_entity(entity); } -/*********************************************************************************** - * _ __ _ - * (_) / _| | | - * _ __ ___ __ _ _ _ __ | |_ _ __ __ _ _ __ ___ _____ _____ _ __| | __ - * | '_ ` _ \ / _` | | '_ \ | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / - * | | | | | | (_| | | | | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < - * |_| |_| |_|\__,_|_|_| |_| |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_\ - * - ***********************************************************************************/ - - /** * Emits code for stack space management */ @@ -275,12 +370,11 @@ static void emit_be_IncSP(const ir_node *irn) } /** - * emits code for save instruction - * and sets the current save_attr pointer + * emits code for save instruction with min. required stack space */ static void emit_sparc_Save(const ir_node *irn) { - save_attr = get_sparc_save_attr_const(irn); + const sparc_save_attr_t *save_attr = get_sparc_save_attr_const(irn); be_emit_cstring("\tsave "); sparc_emit_source_register(irn, 0); be_emit_irprintf(", %d, ", -save_attr->initial_stacksize); @@ -289,30 +383,67 @@ static void emit_sparc_Save(const ir_node *irn) } /** - * emits code to load hi 22 bit of a constant + * emits code for mulh */ -static void emit_sparc_HiImm(const ir_node *irn) +static void emit_sparc_Mulh(const ir_node *irn) { - const sparc_attr_t *attr = get_sparc_attr_const(irn); - be_emit_cstring("\tsethi "); - be_emit_irprintf("%%hi(%d), ", attr->immediate_value); + be_emit_cstring("\t"); + sparc_emit_mode_sign_prefix(irn); + be_emit_cstring("mul "); + + sparc_emit_source_register(irn, 0); + be_emit_cstring(", "); + sparc_emit_reg_or_imm(irn, 1); + be_emit_cstring(", "); sparc_emit_dest_register(irn, 0); be_emit_finish_line_gas(irn); -} -/** - * emits code to load lo 10bits of a constant - */ -static void emit_sparc_LoImm(const ir_node *irn) -{ - const sparc_attr_t *attr = get_sparc_attr_const(irn); - be_emit_cstring("\tor "); - sparc_emit_source_register(irn, 0); - be_emit_irprintf(", %%lo(%d), ", attr->immediate_value); + // our result is in the y register now + // we just copy it to the assigned target reg + be_emit_cstring("\tmov %y, "); sparc_emit_dest_register(irn, 0); be_emit_finish_line_gas(irn); } +static void fill_delay_slot(void) +{ + be_emit_cstring("\tnop\n"); + be_emit_write_line(); +} + +static void emit_sparc_Div(const ir_node *node, bool is_signed) +{ + /* can we get the delay count of the wr instruction somewhere? */ + unsigned wry_delay_count = 3; + unsigned i; + + be_emit_cstring("\twr "); + sparc_emit_source_register(node, 0); + be_emit_cstring(", 0, %y"); + be_emit_finish_line_gas(node); + + for (i = 0; i < wry_delay_count; ++i) { + fill_delay_slot(); + } + + be_emit_irprintf("\t%s ", is_signed ? "sdiv" : "udiv"); + sparc_emit_source_register(node, 1); + be_emit_cstring(", "); + sparc_emit_reg_or_imm(node, 2); + be_emit_cstring(", "); + sparc_emit_dest_register(node, 0); + be_emit_finish_line_gas(node); +} + +static void emit_sparc_SDiv(const ir_node *node) +{ + emit_sparc_Div(node, true); +} + +static void emit_sparc_UDiv(const ir_node *node) +{ + emit_sparc_Div(node, false); +} /** * Emits code for return node @@ -329,28 +460,29 @@ static void emit_be_Return(const ir_node *irn) /** * Emits code for Call node */ -static void emit_be_Call(const ir_node *irn) +static void emit_sparc_Call(const ir_node *node) { - ir_entity *entity = be_Call_get_entity(irn); + const sparc_attr_t *attr = get_sparc_attr_const(node); + ir_entity *entity = attr->immediate_value_entity; + be_emit_cstring("\tcall "); if (entity != NULL) { - be_emit_cstring("\tcall "); sparc_emit_entity(entity); + if (attr->immediate_value != 0) { + be_emit_irprintf("%+d", attr->immediate_value); + } be_emit_cstring(", 0"); - be_emit_finish_line_gas(irn); - be_emit_cstring("\tnop"); - be_emit_pad_comment(); - be_emit_cstring("/* TODO: use delay slot */\n"); } else { - be_emit_cstring("\tnop\n"); - be_emit_pad_comment(); - be_emit_cstring("/* TODO: Entity == NULL */\n"); - be_emit_finish_line_gas(irn); + int last = get_irn_arity(node); + sparc_emit_source_register(node, last-1); } + be_emit_finish_line_gas(node); + + fill_delay_slot(); } /** - * TODO: check if this is correct + * Emit code for Perm node */ static void emit_be_Perm(const ir_node *irn) { @@ -380,77 +512,160 @@ static void emit_be_Perm(const ir_node *irn) } /** - * Emit a SymConst. + * TODO: not really tested but seems to work with memperm_arity == 1 */ -static void emit_sparc_SymConst(const ir_node *irn) +static void emit_be_MemPerm(const ir_node *node) { - const sparc_symconst_attr_t *attr = get_sparc_symconst_attr_const(irn); + int i; + int memperm_arity; + int sp_change = 0; + ir_graph *irg = get_irn_irg(node); + be_stack_layout_t *layout = be_get_irg_stack_layout(irg); - //sethi %hi(const32),%reg - //or %reg,%lo(const32),%reg + /* this implementation only works with frame pointers currently */ + assert(layout->sp_relative == false); - be_emit_cstring("\tsethi %hi("); - be_gas_emit_entity(attr->entity); - be_emit_cstring("), "); - sparc_emit_dest_register(irn, 0); - be_emit_cstring("\n "); + /* TODO: this implementation is slower than necessary. + The longterm goal is however to avoid the memperm node completely */ - // TODO: could be combined with the following load/store instruction - be_emit_cstring("\tor "); - sparc_emit_dest_register(irn, 0); - be_emit_cstring(", %lo("); - be_gas_emit_entity(attr->entity); - be_emit_cstring("), "); - sparc_emit_dest_register(irn, 0); - be_emit_finish_line_gas(irn); -} + memperm_arity = be_get_MemPerm_entity_arity(node); + // we use our local registers - so this is limited to 8 inputs ! + if (memperm_arity > 8) + panic("memperm with more than 8 inputs not supported yet"); + + be_emit_irprintf("\tsub %%sp, %d, %%sp", memperm_arity*4); + be_emit_finish_line_gas(node); + for (i = 0; i < memperm_arity; ++i) { + ir_entity *entity = be_get_MemPerm_in_entity(node, i); + int offset = be_get_stack_entity_offset(layout, entity, 0); + + /* spill register */ + be_emit_irprintf("\tst %%l%d, [%%sp%+d]", i, sp_change + SPARC_MIN_STACKSIZE); + be_emit_finish_line_gas(node); + + /* load from entity */ + be_emit_irprintf("\tld [%%fp%+d], %%l%d", offset, i); + be_emit_finish_line_gas(node); + sp_change += 4; + } + + for (i = memperm_arity-1; i >= 0; --i) { + ir_entity *entity = be_get_MemPerm_out_entity(node, i); + int offset = be_get_stack_entity_offset(layout, entity, 0); + + sp_change -= 4; + + /* store to new entity */ + be_emit_irprintf("\tst %%l%d, [%%fp%+d]", i, offset); + be_emit_finish_line_gas(node); + /* restore register */ + be_emit_irprintf("\tld [%%sp%+d], %%l%d", sp_change + SPARC_MIN_STACKSIZE, i); + be_emit_finish_line_gas(node); + } + + be_emit_irprintf("\tadd %%sp, %d, %%sp", memperm_arity*4); + be_emit_finish_line_gas(node); + + assert(sp_change == 0); +} /** * Emits code for FrameAddr fix */ -static void emit_sparc_FrameAddr(const ir_node *irn) +static void emit_sparc_FrameAddr(const ir_node *node) { - const sparc_symconst_attr_t *attr = get_irn_generic_attr_const(irn); + const sparc_attr_t *attr = get_sparc_attr_const(node); // no need to fix offset as we are adressing via the framepointer - if (attr->fp_offset >= 0) { + if (attr->immediate_value >= 0) { be_emit_cstring("\tadd "); - sparc_emit_source_register(irn, 0); + sparc_emit_source_register(node, 0); be_emit_cstring(", "); - be_emit_irprintf("%ld", attr->fp_offset + save_attr->initial_stacksize); + be_emit_irprintf("%ld", attr->immediate_value); } else { be_emit_cstring("\tsub "); - sparc_emit_source_register(irn, 0); + sparc_emit_source_register(node, 0); be_emit_cstring(", "); - be_emit_irprintf("%ld", -attr->fp_offset); + be_emit_irprintf("%ld", -attr->immediate_value); } be_emit_cstring(", "); - sparc_emit_dest_register(irn, 0); - be_emit_finish_line_gas(irn); + sparc_emit_dest_register(node, 0); + be_emit_finish_line_gas(node); } +static const char *get_icc_unsigned(pn_Cmp pnc) +{ + switch (pnc) { + case pn_Cmp_False: return "bn"; + case pn_Cmp_Eq: return "be"; + case pn_Cmp_Lt: return "blu"; + case pn_Cmp_Le: return "bleu"; + case pn_Cmp_Gt: return "bgu"; + case pn_Cmp_Ge: return "bgeu"; + case pn_Cmp_Lg: return "bne"; + case pn_Cmp_Leg: return "ba"; + default: panic("Cmp has unsupported pnc"); + } +} + +static const char *get_icc_signed(pn_Cmp pnc) +{ + switch (pnc) { + case pn_Cmp_False: return "bn"; + case pn_Cmp_Eq: return "be"; + case pn_Cmp_Lt: return "bl"; + case pn_Cmp_Le: return "ble"; + case pn_Cmp_Gt: return "bg"; + case pn_Cmp_Ge: return "bge"; + case pn_Cmp_Lg: return "bne"; + case pn_Cmp_Leg: return "ba"; + default: panic("Cmp has unsupported pnc"); + } +} + +static const char *get_fcc(pn_Cmp pnc) +{ + switch (pnc) { + case pn_Cmp_False: return "fbn"; + case pn_Cmp_Eq: return "fbe"; + case pn_Cmp_Lt: return "fbl"; + case pn_Cmp_Le: return "fble"; + case pn_Cmp_Gt: return "fbg"; + case pn_Cmp_Ge: return "fbge"; + case pn_Cmp_Lg: return "fblg"; + case pn_Cmp_Leg: return "fbo"; + case pn_Cmp_Uo: return "fbu"; + case pn_Cmp_Ue: return "fbue"; + case pn_Cmp_Ul: return "fbul"; + case pn_Cmp_Ule: return "fbule"; + case pn_Cmp_Ug: return "fbug"; + case pn_Cmp_Uge: return "fbuge"; + case pn_Cmp_Ne: return "fbne"; + case pn_Cmp_True: return "fba"; + case pn_Cmp_max: + break; + } + panic("invalid pnc"); +} + +typedef const char* (*get_cc_func)(pn_Cmp pnc); /** * Emits code for Branch */ -static void emit_sparc_Branch(const ir_node *irn) +static void emit_sparc_branch(const ir_node *node, get_cc_func get_cc) { + const sparc_jmp_cond_attr_t *attr = get_sparc_jmp_cond_attr_const(node); + pn_Cmp pnc = attr->pnc; + const ir_node *proj_true = NULL; + const ir_node *proj_false = NULL; const ir_edge_t *edge; - const ir_node *proj_true = NULL; - const ir_node *proj_false = NULL; - const ir_node *block; - const ir_node *next_block; - ir_node *op1 = get_irn_n(irn, 0); - const char *suffix; - int proj_num = get_sparc_jmp_cond_proj_num(irn); - const sparc_cmp_attr_t *cmp_attr = get_irn_generic_attr_const(op1); - // bool is_signed = !cmp_attr->is_unsigned; - - assert(is_sparc_Cmp(op1) || is_sparc_Tst(op1)); - - foreach_out_edge(irn, edge) { + const ir_node *block; + const ir_node *next_block; + + foreach_out_edge(node, edge) { ir_node *proj = get_edge_src_irn(edge); long nr = get_Proj_proj(proj); if (nr == pn_Cond_true) { @@ -460,51 +675,36 @@ static void emit_sparc_Branch(const ir_node *irn) } } - if (cmp_attr->ins_permuted) { - proj_num = get_mirrored_pnc(proj_num); - } - /* for now, the code works for scheduled and non-schedules blocks */ - block = get_nodes_block(irn); + block = get_nodes_block(node); /* we have a block schedule */ next_block = get_irn_link(block); - assert(proj_num != pn_Cmp_False); - assert(proj_num != pn_Cmp_True); - if (get_irn_link(proj_true) == next_block) { /* exchange both proj's so the second one can be omitted */ const ir_node *t = proj_true; proj_true = proj_false; proj_false = t; - proj_num = get_negated_pnc(proj_num, mode_Iu); - } - - - switch (proj_num) { - case pn_Cmp_Eq: suffix = "e"; break; - case pn_Cmp_Lt: suffix = "l"; break; - case pn_Cmp_Le: suffix = "le"; break; - case pn_Cmp_Gt: suffix = "g"; break; - case pn_Cmp_Ge: suffix = "ge"; break; - case pn_Cmp_Lg: suffix = "ne"; break; - case pn_Cmp_Leg: suffix = "a"; break; - default: panic("Cmp has unsupported pnc"); + if (is_sparc_fbfcc(node)) { + pnc = get_negated_pnc(pnc, mode_F); + } else { + pnc = get_negated_pnc(pnc, mode_Iu); + } } /* emit the true proj */ - be_emit_irprintf("\tb%s ", suffix); + be_emit_cstring("\t"); + be_emit_string(get_cc(pnc)); + be_emit_char(' '); sparc_emit_cfop_target(proj_true); be_emit_finish_line_gas(proj_true); - be_emit_cstring("\tnop"); - be_emit_pad_comment(); - be_emit_cstring("/* TODO: use delay slot */\n"); + fill_delay_slot(); if (get_irn_link(proj_false) == next_block) { - be_emit_cstring("\t/* false-fallthrough to "); + be_emit_cstring("\t/* fallthrough to "); sparc_emit_cfop_target(proj_false); be_emit_cstring(" */"); be_emit_finish_line_gas(proj_false); @@ -512,15 +712,26 @@ static void emit_sparc_Branch(const ir_node *irn) be_emit_cstring("\tba "); sparc_emit_cfop_target(proj_false); be_emit_finish_line_gas(proj_false); - be_emit_cstring("\tnop\t\t/* TODO: use delay slot */\n"); - be_emit_finish_line_gas(proj_false); + fill_delay_slot(); } } +static void emit_sparc_Bicc(const ir_node *node) +{ + const sparc_jmp_cond_attr_t *attr = get_sparc_jmp_cond_attr_const(node); + bool is_unsigned = attr->is_unsigned; + emit_sparc_branch(node, is_unsigned ? get_icc_unsigned : get_icc_signed); +} + +static void emit_sparc_fbfcc(const ir_node *node) +{ + emit_sparc_branch(node, get_fcc); +} + /** * emit Jmp (which actually is a branch always (ba) instruction) */ -static void emit_sparc_Jmp(const ir_node *node) +static void emit_sparc_Ba(const ir_node *node) { ir_node *block, *next_block; @@ -533,7 +744,7 @@ static void emit_sparc_Jmp(const ir_node *node) be_emit_cstring("\tba "); sparc_emit_cfop_target(node); be_emit_finish_line_gas(node); - be_emit_cstring("\tnop\t\t/* TODO: use delay slot */\n"); + fill_delay_slot(); } else { be_emit_cstring("\t/* fallthrough to "); sparc_emit_cfop_target(node); @@ -542,28 +753,124 @@ static void emit_sparc_Jmp(const ir_node *node) be_emit_finish_line_gas(node); } +static void emit_jump_table(const ir_node *node) +{ + const sparc_switch_jmp_attr_t *attr = get_sparc_switch_jmp_attr_const(node); + long switch_min = LONG_MAX; + long switch_max = LONG_MIN; + long default_pn = attr->default_proj_num; + ir_entity *entity = attr->jump_table; + ir_node *default_block = NULL; + unsigned length; + const ir_edge_t *edge; + unsigned i; + ir_node **table; + + /* go over all proj's and collect them */ + foreach_out_edge(node, edge) { + ir_node *proj = get_edge_src_irn(edge); + long pn = get_Proj_proj(proj); + + /* check for default proj */ + if (pn == default_pn) { + assert(default_block == NULL); /* more than 1 default_pn? */ + default_block = get_jump_target(proj); + } else { + switch_min = pn < switch_min ? pn : switch_min; + switch_max = pn > switch_max ? pn : switch_max; + } + } + length = (unsigned long) (switch_max - switch_min) + 1; + assert(switch_min < LONG_MAX || switch_max > LONG_MIN); + + table = XMALLOCNZ(ir_node*, length); + foreach_out_edge(node, edge) { + ir_node *proj = get_edge_src_irn(edge); + long pn = get_Proj_proj(proj); + if (pn == default_pn) + continue; + + table[pn - switch_min] = get_jump_target(proj); + } + + /* emit table */ + be_gas_emit_switch_section(GAS_SECTION_RODATA); + be_emit_cstring("\t.align 4\n"); + be_gas_emit_entity(entity); + be_emit_cstring(":\n"); + for (i = 0; i < length; ++i) { + ir_node *block = table[i]; + if (block == NULL) + block = default_block; + be_emit_cstring("\t.long "); + be_gas_emit_block_name(block); + be_emit_char('\n'); + be_emit_write_line(); + } + be_gas_emit_switch_section(GAS_SECTION_TEXT); + + xfree(table); +} + +static void emit_sparc_SwitchJmp(const ir_node *node) +{ + be_emit_cstring("\tjmp "); + sparc_emit_source_register(node, 0); + be_emit_finish_line_gas(node); + fill_delay_slot(); + + emit_jump_table(node); +} + +static void emit_fmov(const ir_node *node, const arch_register_t *src_reg, + const arch_register_t *dst_reg) +{ + be_emit_cstring("\tfmov "); + be_emit_string(arch_register_get_name(src_reg)); + be_emit_cstring(", "); + be_emit_string(arch_register_get_name(dst_reg)); + be_emit_finish_line_gas(node); +} + +static const arch_register_t *get_next_fp_reg(const arch_register_t *reg) +{ + unsigned index = reg->index; + assert(reg == &sparc_fp_regs[index]); + index++; + assert(index < N_sparc_fp_REGS); + return &sparc_fp_regs[index]; +} + /** * emit copy node */ -static void emit_be_Copy(const ir_node *irn) +static void emit_be_Copy(const ir_node *node) { - ir_mode *mode = get_irn_mode(irn); + ir_mode *mode = get_irn_mode(node); + const arch_register_t *src_reg = get_in_reg(node, 0); + const arch_register_t *dst_reg = get_out_reg(node, 0); - if (get_in_reg(irn, 0) == get_out_reg(irn, 0)) { - /* omitted Copy */ + if (src_reg == dst_reg) return; - } if (mode_is_float(mode)) { - panic("emit_be_Copy: move not supported for FP"); + unsigned bits = get_mode_size_bits(mode); + int n = bits > 32 ? bits > 64 ? 3 : 1 : 0; + int i; + emit_fmov(node, src_reg, dst_reg); + for (i = 0; i < n; ++i) { + src_reg = get_next_fp_reg(src_reg); + dst_reg = get_next_fp_reg(dst_reg); + emit_fmov(node, src_reg, dst_reg); + } } else if (mode_is_data(mode)) { be_emit_cstring("\tmov "); - sparc_emit_source_register(irn, 0); + sparc_emit_source_register(node, 0); be_emit_cstring(", "); - sparc_emit_dest_register(irn, 0); - be_emit_finish_line_gas(irn); + sparc_emit_dest_register(node, 0); + be_emit_finish_line_gas(node); } else { - panic("emit_be_Copy: move not supported for this mode"); + panic("emit_be_Copy: invalid mode"); } } @@ -576,8 +883,6 @@ static void emit_nothing(const ir_node *irn) (void) irn; } - - /** * type of emitter function */ @@ -597,47 +902,34 @@ static inline void set_emitter(ir_op *op, emit_func sparc_emit_node) */ static void sparc_register_emitters(void) { - /* first clear the generic function pointer for all ops */ clear_irp_opcodes_generic_func(); - /* register all emitter functions defined in spec */ sparc_register_spec_emitters(); /* custom emitter */ - set_emitter(op_be_IncSP, emit_be_IncSP); - set_emitter(op_be_Return, emit_be_Return); - set_emitter(op_be_Call, emit_be_Call); - set_emitter(op_sparc_FrameAddr, emit_sparc_FrameAddr); - set_emitter(op_sparc_Branch, emit_sparc_Branch); - set_emitter(op_sparc_SymConst, emit_sparc_SymConst); - set_emitter(op_sparc_Jmp, emit_sparc_Jmp); - set_emitter(op_sparc_Save, emit_sparc_Save); - - set_emitter(op_sparc_HiImm, emit_sparc_HiImm); - set_emitter(op_sparc_LoImm, emit_sparc_LoImm); - - set_emitter(op_be_Copy, emit_be_Copy); - set_emitter(op_be_CopyKeep, emit_be_Copy); - - set_emitter(op_be_Perm, emit_be_Perm); - -/* - set_emitter(op_arm_B, emit_arm_B); - set_emitter(op_arm_CopyB, emit_arm_CopyB); - set_emitter(op_arm_fpaConst, emit_arm_fpaConst); - set_emitter(op_arm_fpaDbl2GP, emit_arm_fpaDbl2GP); - set_emitter(op_arm_LdTls, emit_arm_LdTls); - set_emitter(op_arm_SwitchJmp, emit_arm_SwitchJmp); - set_emitter(op_be_MemPerm, emit_be_MemPerm); - -*/ - /* no need to emit anything for the following nodes */ - set_emitter(op_Phi, emit_nothing); - set_emitter(op_be_Keep, emit_nothing); - set_emitter(op_be_Start, emit_nothing); - set_emitter(op_be_Barrier, emit_nothing); - + set_emitter(op_be_Copy, emit_be_Copy); + set_emitter(op_be_CopyKeep, emit_be_Copy); + set_emitter(op_be_IncSP, emit_be_IncSP); + set_emitter(op_be_MemPerm, emit_be_MemPerm); + set_emitter(op_be_Perm, emit_be_Perm); + set_emitter(op_be_Return, emit_be_Return); + set_emitter(op_sparc_Ba, emit_sparc_Ba); + set_emitter(op_sparc_Bicc, emit_sparc_Bicc); + set_emitter(op_sparc_Call, emit_sparc_Call); + set_emitter(op_sparc_fbfcc, emit_sparc_fbfcc); + set_emitter(op_sparc_FrameAddr, emit_sparc_FrameAddr); + set_emitter(op_sparc_Mulh, emit_sparc_Mulh); + set_emitter(op_sparc_Save, emit_sparc_Save); + set_emitter(op_sparc_SDiv, emit_sparc_SDiv); + set_emitter(op_sparc_SwitchJmp, emit_sparc_SwitchJmp); + set_emitter(op_sparc_UDiv, emit_sparc_UDiv); + + /* no need to emit anything for the following nodes */ + set_emitter(op_be_Barrier, emit_nothing); + set_emitter(op_be_Keep, emit_nothing); + set_emitter(op_be_Start, emit_nothing); + set_emitter(op_Phi, emit_nothing); } /** @@ -645,15 +937,15 @@ static void sparc_register_emitters(void) */ static void sparc_emit_node(const ir_node *node) { - ir_op *op = get_irn_op(node); + ir_op *op = get_irn_op(node); if (op->ops.generic) { emit_func func = (emit_func) op->ops.generic; be_dbg_set_dbg_info(get_irn_dbg_info(node)); (*func) (node); } else { - panic("Error: No emit handler for node %+F (graph %+F)\n", - node, current_ir_graph); + panic("No emit handler for node %+F (graph %+F)\n", node, + current_ir_graph); } } @@ -731,13 +1023,14 @@ void sparc_gen_routine(const sparc_code_gen_t *cg, ir_graph *irg) ir_node *last_block = NULL; ir_entity *entity = get_irg_entity(irg); int i, n; + (void) cg; be_gas_elf_type_char = '#'; be_gas_object_file_format = OBJECT_FILE_FORMAT_ELF_SPARC; /* register all emitter functions */ sparc_register_emitters(); - be_dbg_method_begin(entity, be_abi_get_stack_layout(be_get_irg_abi(cg->irg))); + be_dbg_method_begin(entity); /* create the block schedule. For now, we don't need it earlier. */ blk_sched = be_create_block_schedule(irg); @@ -763,9 +1056,6 @@ void sparc_gen_routine(const sparc_code_gen_t *cg, ir_graph *irg) last_block = block; } - - //irg_walk_blkwise_graph(irg, NULL, sparc_gen_block, NULL); - // emit function epilog sparc_emit_func_epilog(irg); }