/* Proj with the corresponding projnum for the register */
if (get_irn_mode(irn) != mode_T) {
+ assert(pos == 0);
reg = arch_get_irn_register(arch_env, irn);
} else if (is_ia32_irn(irn)) {
reg = get_ia32_out_reg(irn, pos);
return reg;
}
-/**
- * Determine the gnu assembler suffix that indicates a mode
- */
-static char get_mode_suffix(const ir_mode *mode)
-{
- if(mode_is_float(mode)) {
- switch(get_mode_size_bits(mode)) {
- case 32:
- return 's';
- case 64:
- return 'l';
- case 80:
- case 96:
- return 't';
- }
- } else {
- assert(mode_is_int(mode) || mode_is_reference(mode));
- switch(get_mode_size_bits(mode)) {
- case 64:
- return 'q';
- case 32:
- return 'l';
- case 16:
- return 'w';
- case 8:
- return 'b';
- }
- }
- panic("Can't output mode_suffix for %+F\n", mode);
-}
-
/**
* Add a number to a prefix. This number will not be used a second time.
*/
emit_16bit_register(reg);
return;
} else {
- assert(size == 32);
+ assert(mode_is_float(mode) || size == 32);
}
}
be_emit_string(attr->x87[pos]->name);
}
-static
-void ia32_emit_mode_suffix_mode(const ir_mode *mode)
+static void ia32_emit_mode_suffix_mode(const ir_mode *mode)
{
- be_emit_char(get_mode_suffix(mode));
+ if(mode_is_float(mode)) {
+ switch(get_mode_size_bits(mode)) {
+ case 32: be_emit_char('s'); return;
+ case 64: be_emit_char('l'); return;
+ case 80: be_emit_char('t'); return;
+ }
+ } else {
+ assert(mode_is_int(mode) || mode_is_reference(mode));
+ switch(get_mode_size_bits(mode)) {
+ case 64: be_emit_cstring("ll"); return;
+ /* gas docu says q is the suffix but gcc, objdump and icc use
+ ll apparently */
+ case 32: be_emit_char('l'); return;
+ case 16: be_emit_char('w'); return;
+ case 8: be_emit_char('b'); return;
+ }
+ }
+ panic("Can't output mode_suffix for %+F\n", mode);
}
void ia32_emit_mode_suffix(const ir_node *node)
void ia32_emit_x87_mode_suffix(const ir_node *node)
{
ir_mode *mode = get_ia32_ls_mode(node);
- if(mode != NULL)
+ assert(mode != NULL);
+ /* we only need to emit the mode on address mode */
+ if(get_ia32_op_type(node) != ia32_Normal)
ia32_emit_mode_suffix_mode(mode);
}
/**
* Emits registers and/or address mode of a binary operation.
*/
-void ia32_emit_binop(const ir_node *node, int produces_result) {
+void ia32_emit_binop(const ir_node *node) {
const ir_node *right_op = get_irn_n(node, n_ia32_binary_right);
const ir_mode *mode = get_ia32_ls_mode(node);
const arch_register_t *reg_left;
break;
case ia32_AddrModeS:
if(is_ia32_Immediate(right_op)) {
- assert(!produces_result && "Source AM with Const must not produce result");
-
emit_ia32_Immediate(right_op);
be_emit_cstring(", ");
ia32_emit_am(node);
}
}
+static void emit_ia32_IMul(const ir_node *node)
+{
+ ir_node *left = get_irn_n(node, n_ia32_IMul_left);
+ const arch_register_t *out_reg = get_out_reg(node, pn_ia32_IMul_res);
+
+ be_emit_cstring("\timul");
+ ia32_emit_mode_suffix(node);
+ be_emit_char(' ');
+
+ ia32_emit_binop(node);
+
+ /* do we need the 3-address form? */
+ if(is_ia32_NoReg_GP(left) ||
+ get_in_reg(node, n_ia32_IMul_left) != out_reg) {
+ be_emit_cstring(", ");
+ emit_register(out_reg, get_ia32_ls_mode(node));
+ }
+ be_emit_finish_line_gas(node);
+}
+
/*************************************************
* _ _ _
* (_) | | |
if(is_ia32_Sahf(flags)) {
ir_node *cmp = get_irn_n(flags, n_ia32_Sahf_val);
- if(!is_ia32_FucomFnstsw(cmp) || is_ia32_FucompFnstsw(cmp)
- || is_ia32_FucomppFnstsw(cmp)) {
+ if(!(is_ia32_FucomFnstsw(cmp) || is_ia32_FucompFnstsw(cmp)
+ || is_ia32_FucomppFnstsw(cmp) || is_ia32_FtstFnstsw(cmp))) {
cmp = find_original_value(cmp);
assert(is_ia32_FucomFnstsw(cmp) || is_ia32_FucompFnstsw(cmp)
- || is_ia32_FucomppFnstsw(cmp));
+ || is_ia32_FucomppFnstsw(cmp) || is_ia32_FtstFnstsw(cmp));
}
flags_attr = get_ia32_attr_const(cmp);
- if(flags_attr->data.cmp_flipped)
+ if(flags_attr->data.ins_permuted)
pnc = get_mirrored_pnc(pnc);
pnc |= ia32_pn_Cmp_float;
- } else if(is_ia32_Ucomi(flags)) {
+ } else if(is_ia32_Ucomi(flags) || is_ia32_Fucomi(flags)
+ || is_ia32_Fucompi(flags)) {
flags_attr = get_ia32_attr_const(flags);
- if(flags_attr->data.cmp_flipped)
+ if(flags_attr->data.ins_permuted)
pnc = get_mirrored_pnc(pnc);
pnc |= ia32_pn_Cmp_float;
} else {
|| is_ia32_Cmp8Bit(flags) || is_ia32_Test8Bit(flags));
flags_attr = get_ia32_attr_const(flags);
- if(flags_attr->data.cmp_flipped)
+ if(flags_attr->data.ins_permuted)
pnc = get_mirrored_pnc(pnc);
if(flags_attr->data.cmp_unsigned)
pnc |= ia32_pn_Cmp_unsigned;
void ia32_emit_cmp_suffix_node(const ir_node *node,
int flags_pos)
{
- pn_Cmp pnc = get_ia32_pncode(node);
+ const ia32_attr_t *attr = get_ia32_attr_const(node);
+
+ pn_Cmp pnc = get_ia32_condcode(node);
pnc = determine_final_pnc(node, flags_pos, pnc);
+ if(attr->data.ins_permuted) {
+ if(pnc & ia32_pn_Cmp_float) {
+ pnc = get_negated_pnc(pnc, mode_F);
+ } else {
+ pnc = get_negated_pnc(pnc, mode_Iu);
+ }
+ }
+
ia32_emit_cmp_suffix(pnc);
}
const ir_node *proj_false;
const ir_node *block;
const ir_node *next_block;
- pn_Cmp pnc = get_ia32_pncode(node);
+ pn_Cmp pnc = get_ia32_condcode(node);
pnc = determine_final_pnc(node, 0, pnc);
static void emit_ia32_CMov(const ir_node *node)
{
- const arch_register_t *out = arch_get_irn_register(arch_env, node);
+ const ia32_attr_t *attr = get_ia32_attr_const(node);
+ int ins_permuted = attr->data.ins_permuted;
+ const arch_register_t *out = arch_get_irn_register(arch_env, node);
+ pn_Cmp pnc = get_ia32_condcode(node);
const arch_register_t *in_true;
const arch_register_t *in_false;
- pn_Cmp pnc = get_ia32_pncode(node);
pnc = determine_final_pnc(node, n_ia32_CMov_eflags, pnc);
} else if(out == in_true) {
const arch_register_t *tmp;
- /* swap left/right and negate pnc */
- pnc = get_negated_pnc(pnc, mode_Iu);
+ assert(get_ia32_op_type(node) == ia32_Normal);
+
+ ins_permuted = !ins_permuted;
tmp = in_true;
in_true = in_false;
be_emit_finish_line_gas(node);
}
+ if(ins_permuted) {
+ if(pnc & ia32_pn_Cmp_float) {
+ pnc = get_negated_pnc(pnc, mode_F);
+ } else {
+ pnc = get_negated_pnc(pnc, mode_Iu);
+ }
+ }
+
+ /* TODO: handling of Nans isn't correct yet */
+
be_emit_cstring("\tcmov");
ia32_emit_cmp_suffix(pnc);
be_emit_char(' ');
tbl.max_value = pnc > tbl.max_value ? pnc : tbl.max_value;
/* check for default proj */
- if (pnc == get_ia32_pncode(node)) {
+ if (pnc == get_ia32_condcode(node)) {
assert(tbl.defProj == NULL && "found two defProjs at SwitchJmp");
tbl.defProj = proj;
}
if(attr->symconst != NULL) {
ident *id = get_entity_ld_ident(attr->symconst);
- if(attr->attr.data.am_sc_sign)
+ if(attr->sc_sign)
be_emit_char('-');
be_emit_ident(id);
}
}
}
+/**
+ * Emit an inline assembler operand.
+ *
+ * @param node the ia32_ASM node
+ * @param s points to the operand (a %c)
+ *
+ * @return pointer to the first char in s NOT in the current operand
+ */
static const char* emit_asm_operand(const ir_node *node, const char *s)
{
+ const ia32_attr_t *ia32_attr = get_ia32_attr_const(node);
+ const ia32_asm_attr_t *attr = CONST_CAST_IA32_ATTR(ia32_asm_attr_t,
+ ia32_attr);
const arch_register_t *reg;
+ const ia32_asm_reg_t *asm_regs = attr->register_map;
+ const ia32_asm_reg_t *asm_reg;
const char *reg_name;
char c;
char modifier = 0;
int num = -1;
- const ia32_attr_t *attr;
- int n_outs;
int p;
assert(*s == '%');
s += p;
}
+ if(num < 0 || num >= ARR_LEN(asm_regs)) {
+ ir_fprintf(stderr, "Error: Custom assembler references invalid "
+ "input/output (%+F)\n", node);
+ return s;
+ }
+ asm_reg = & asm_regs[num];
+ assert(asm_reg->valid);
+
/* get register */
- attr = get_ia32_attr_const(node);
- n_outs = ARR_LEN(attr->slots);
- if(num < n_outs) {
- reg = get_out_reg(node, num);
+ if(asm_reg->use_input == 0) {
+ reg = get_out_reg(node, asm_reg->inout_pos);
} else {
- ir_node *pred;
- int in = num - n_outs;
- if(in >= get_irn_arity(node)) {
- ir_fprintf(stderr, "Warning: Invalid input %d specified in asm "
- "op (%+F)\n", num, node);
- return s;
- }
- pred = get_irn_n(node, in);
+ ir_node *pred = get_irn_n(node, asm_reg->inout_pos);
+
/* might be an immediate value */
if(is_ia32_Immediate(pred)) {
emit_ia32_Immediate(pred);
return s;
}
- reg = get_in_reg(node, in);
+ reg = get_in_reg(node, asm_reg->inout_pos);
}
if(reg == NULL) {
ir_fprintf(stderr, "Warning: no register assigned for %d asm op "
return s;
}
+ if(asm_reg->memory) {
+ be_emit_char('(');
+ }
+
/* emit it */
- be_emit_char('%');
- switch(modifier) {
- case 0:
- reg_name = arch_register_get_name(reg);
- break;
- case 'b':
- reg_name = ia32_get_mapped_reg_name(isa->regs_8bit, reg);
- break;
- case 'h':
- reg_name = ia32_get_mapped_reg_name(isa->regs_8bit_high, reg);
- break;
- case 'w':
- reg_name = ia32_get_mapped_reg_name(isa->regs_16bit, reg);
- break;
- default:
- panic("Invalid asm op modifier");
+ if(modifier != 0) {
+ be_emit_char('%');
+ switch(modifier) {
+ case 'b':
+ reg_name = ia32_get_mapped_reg_name(isa->regs_8bit, reg);
+ break;
+ case 'h':
+ reg_name = ia32_get_mapped_reg_name(isa->regs_8bit_high, reg);
+ break;
+ case 'w':
+ reg_name = ia32_get_mapped_reg_name(isa->regs_16bit, reg);
+ break;
+ default:
+ panic("Invalid asm op modifier");
+ }
+ be_emit_string(reg_name);
+ } else {
+ emit_register(reg, asm_reg->mode);
+ }
+
+ if(asm_reg->memory) {
+ be_emit_char(')');
}
- be_emit_string(reg_name);
return s;
}
/**
* Emit movsb/w instructions to make mov count divideable by 4
*/
-static void emit_CopyB_prolog(int rem) {
+static void emit_CopyB_prolog(unsigned size) {
be_emit_cstring("\tcld");
be_emit_finish_line_gas(NULL);
- switch(rem) {
+ switch (size) {
case 1:
be_emit_cstring("\tmovsb");
be_emit_finish_line_gas(NULL);
*/
static void emit_ia32_CopyB(const ir_node *node)
{
- int rem = get_ia32_pncode(node);
+ unsigned size = get_ia32_copyb_size(node);
- emit_CopyB_prolog(rem);
+ emit_CopyB_prolog(size);
be_emit_cstring("\trep movsd");
be_emit_finish_line_gas(node);
*/
static void emit_ia32_CopyB_i(const ir_node *node)
{
- int size = get_ia32_pncode(node);
+ unsigned size = get_ia32_copyb_size(node);
emit_CopyB_prolog(size & 0x3);
switch(get_ia32_op_type(node)) {
case ia32_Normal:
ia32_emit_source_register(node, n_ia32_unary_op);
- be_emit_cstring(", ");
- ia32_emit_dest_register(node, 0);
break;
case ia32_AddrModeS:
- ia32_emit_dest_register(node, 0);
- be_emit_cstring(", ");
ia32_emit_am(node);
break;
default:
assert(0 && "unsupported op type for Conv");
}
+ be_emit_cstring(", ");
+ ia32_emit_dest_register(node, 0);
be_emit_finish_line_gas(node);
}
be_emit_finish_line_gas(node);
}
-/**
- * Emits code for an 8Bit Int conversion.
- */
-static void emit_ia32_Conv_I2I8Bit(const ir_node *node)
-{
- emit_ia32_Conv_I2I(node);
-}
-
/*******************************************
* _ _
*/
static void emit_ia32_Const(const ir_node *node)
{
- const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node);
-
- /* a zero? */
- if(attr->symconst == NULL && attr->offset == 0) {
- assert(get_ia32_flags(node) & arch_irn_flags_modify_flags);
- be_emit_cstring("\txorl ");
- ia32_emit_dest_register(node, 0);
- be_emit_cstring(", ");
- ia32_emit_dest_register(node, 0);
- } else {
- be_emit_cstring("\tmovl ");
- emit_ia32_Immediate(node);
- be_emit_cstring(", ");
- ia32_emit_dest_register(node, 0);
- }
+ be_emit_cstring("\tmovl ");
+ emit_ia32_Immediate(node);
+ be_emit_cstring(", ");
+ ia32_emit_dest_register(node, 0);
be_emit_finish_line_gas(node);
}
static void emit_be_Return(const ir_node *node)
{
+ unsigned pop;
be_emit_cstring("\tret");
+
+ pop = be_Return_get_pop(node);
+ if(pop > 0) {
+ be_emit_irprintf(" $%d", pop);
+ }
be_emit_finish_line_gas(node);
}
/* other ia32 emitter functions */
IA32_EMIT(Asm);
IA32_EMIT(CMov);
+ IA32_EMIT(IMul);
IA32_EMIT(SwitchJmp);
IA32_EMIT(CopyB);
IA32_EMIT(CopyB_i);
IA32_EMIT(Conv_FP2I);
IA32_EMIT(Conv_FP2FP);
IA32_EMIT(Conv_I2I);
- IA32_EMIT(Conv_I2I8Bit);
+ IA32_EMIT2(Conv_I2I8Bit, Conv_I2I);
IA32_EMIT(Const);
IA32_EMIT(LdTls);
IA32_EMIT(Minus64Bit);
/**
* Test wether a block should be aligned.
- * For cpus in the P4/Athlon class it is usefull to align jump labels to
+ * For cpus in the P4/Athlon class it is useful to align jump labels to
* 16 bytes. However we should only do that if the alignment nops before the
* label aren't executed more often than we have jumps to the label.
*/
cpu_support cpu = isa->opt_arch;
const be_irg_t *birg = cg->birg;
+ /* write the begin line (used by scripts processing the assembler... */
+ be_emit_write_line();
+ be_emit_cstring("# -- Begin ");
+ be_emit_string(irg_name);
+ be_emit_char('\n');
be_emit_write_line();
+
be_gas_emit_switch_section(GAS_SECTION_TEXT);
be_dbg_method_begin(birg->main_env->db_handle, irg_ent, be_abi_get_stack_layout(birg->abi));
ia32_emit_align_func(cpu);
ia32_emit_function_size(irg_name);
be_dbg_method_end(birg->main_env->db_handle);
+
+ be_emit_cstring("# -- End ");
+ be_emit_string(irg_name);
+ be_emit_char('\n');
+ be_emit_write_line();
+
be_emit_char('\n');
be_emit_write_line();
}