return reg;
}
+static bool is_valid_immediate(int32_t value)
+{
+ return -4096 <= value && value < 4096;
+}
+
void sparc_emit_immediate(const ir_node *node)
{
- int32_t value = get_sparc_attr_const(node)->immediate_value;
- assert(-4096 <= value && value < 4096);
- be_emit_irprintf("%d", value);
+ const sparc_attr_t *attr = get_sparc_attr_const(node);
+ ir_entity *entity = attr->immediate_value_entity;
+
+ 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(')');
+ }
}
void sparc_emit_high_immediate(const ir_node *node)
{
- uint32_t value = (uint32_t) get_sparc_attr_const(node)->immediate_value;
- be_emit_irprintf("%%hi(0x%X)", value);
+ 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)
/**
* 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);
- long offset = attr->offset;
-
- /* 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) {
- be_emit_irprintf("%+ld", 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);
}
}
emit_fp_suffix(attr->fp_mode);
}
+static ir_node *get_jump_target(const ir_node *jump)
+{
+ return get_irn_link(jump);
+}
+
/**
* Returns the target label for a control flow 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);
}
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? */
be_emit_finish_line_gas(node);
for (i = 0; i < wry_delay_count; ++i) {
- be_emit_cstring("\tnop");
- be_emit_finish_line_gas(node);
+ fill_delay_slot();
}
be_emit_irprintf("\t%s ", is_signed ? "sdiv" : "udiv");
sparc_emit_source_register(node, 1);
be_emit_cstring(", ");
- sparc_emit_source_register(node, 2);
+ 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)
{
- (void) node;
- /* aehm we would need an aditional register for an sra instruction to
- * compute the upper bits... Just panic for now */
- //emit_sparc_Div(node, true);
- panic("signed div is wrong");
+ emit_sparc_Div(node, true);
}
static void emit_sparc_UDiv(const ir_node *node)
be_emit_cstring("\tcall ");
if (entity != NULL) {
sparc_emit_entity(entity);
+ if (attr->immediate_value != 0) {
+ be_emit_irprintf("%+d", attr->immediate_value);
+ }
be_emit_cstring(", 0");
} else {
int last = get_irn_arity(node);
}
be_emit_finish_line_gas(node);
- /* fill delay slot */
- be_emit_cstring("\tnop");
- be_emit_finish_line_gas(node);
+ fill_delay_slot();
}
/**
assert(sp_change == 0);
}
-/**
- * Emit a SymConst.
- */
-static void emit_sparc_SymConst(const ir_node *irn)
-{
- const sparc_symconst_attr_t *attr = get_sparc_symconst_attr_const(irn);
-
- //sethi %hi(const32),%reg
- //or %reg,%lo(const32),%reg
-
- 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: 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);
-}
-
/**
* 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);
+ 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)
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/* fallthrough to ");
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();
}
}
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);
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)
{
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_SymConst, emit_sparc_SymConst);
+ 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 */