typedef enum ia32_emit_mod_t {
EMIT_RESPECT_LS = 1U << 0,
EMIT_ALTERNATE_AM = 1U << 1,
- EMIT_LONG = 1U << 2
+ EMIT_LONG = 1U << 2,
+ EMIT_HIGH_REG = 1U << 3,
+ EMIT_LOW_REG = 1U << 4
} ia32_emit_mod_t;
/**
* %d signed int signed int
*
* x starts at 0
- * # modifier for %ASx, %D and %S uses ls mode of node to alter register width
+ * # modifier for %ASx, %D, %R, and %S uses ls mode of node to alter register width
* * modifier does not prefix immediates with $, but AM with *
* l modifier for %lu and %ld
+ * > modifier to output high 8bit register (ah, bh)
+ * < modifier to output low 8bit register (al, bl)
*/
static void ia32_emitf(const ir_node *node, const char *fmt, ...)
{
break;
++fmt;
- if (*fmt == '*') {
- mod |= EMIT_ALTERNATE_AM;
- ++fmt;
- }
-
- if (*fmt == '#') {
- mod |= EMIT_RESPECT_LS;
- ++fmt;
- }
-
- if (*fmt == 'l') {
- mod |= EMIT_LONG;
+ while (1) {
+ switch(*fmt) {
+ case '*': mod |= EMIT_ALTERNATE_AM; break;
+ case '#': mod |= EMIT_RESPECT_LS; break;
+ case 'l': mod |= EMIT_LONG; break;
+ case '>': mod |= EMIT_HIGH_REG; break;
+ case '<': mod |= EMIT_LOW_REG; break;
+ default:
+ goto end_of_mods;
+ }
++fmt;
}
+end_of_mods:
switch (*fmt++) {
case '%':
case 'R': {
const arch_register_t *reg = va_arg(ap, const arch_register_t*);
- emit_register(reg, mod & EMIT_RESPECT_LS ? get_ia32_ls_mode(node) : NULL);
+ if (mod & EMIT_HIGH_REG) {
+ emit_8bit_register_high(reg);
+ } else if (mod & EMIT_LOW_REG) {
+ emit_8bit_register(reg);
+ } else {
+ emit_register(reg, mod & EMIT_RESPECT_LS ? get_ia32_ls_mode(node) : NULL);
+ }
break;
}
switch (pnc & 0x0f) {
case pn_Cmp_Uo:
ia32_emitf(node, "\tsetp %#R\n", dreg);
- break;
+ return;
case pn_Cmp_Leg:
ia32_emitf(node, "\tsetnp %#R\n", dreg);
- break;
+ return;
case pn_Cmp_Eq:
case pn_Cmp_Lt:
case pn_Cmp_Le:
+ ia32_emitf(node, "\tset%P %<R\n", pnc, dreg);
+ ia32_emitf(node, "\tsetnp %>R\n", dreg);
+ ia32_emitf(node, "\tandb %>R, %<R\n", dreg, dreg);
+ return;
+
case pn_Cmp_Ug:
case pn_Cmp_Uge:
case pn_Cmp_Ne:
- panic("No handling for set with parity bit yet in ia32_Setcc");
+ ia32_emitf(node, "\tset%P %<R\n", pnc, dreg);
+ ia32_emitf(node, "\tsetp %>R\n", dreg);
+ ia32_emitf(node, "\torb %>R, %<R\n", dreg, dreg);
+ return;
default:
break;
pnc_map_unsigned[pn_Cmp_Lg] = 0x05;
}
+/** Returns the encoding for a pnc field. */
static unsigned char pnc2cc(int pnc)
{
unsigned char cc;
/* end emit routines, all emitters following here should only use the functions
above. */
+typedef enum reg_modifier {
+ REG_LOW = 0,
+ REG_HIGH = 1
+} reg_modifier_t;
+
/** Create a ModR/M byte for src1,src2 registers */
static void bemit_modrr(const arch_register_t *src1,
const arch_register_t *src2)
bemit8(modrm);
}
+/** Create a ModR/M8 byte for src1,src2 registers */
+static void bemit_modrr8(reg_modifier_t high_part1, const arch_register_t *src1,
+ reg_modifier_t high_part2, const arch_register_t *src2)
+{
+ unsigned char modrm = MOD_REG;
+ modrm |= ENC_RM(reg_gp_map[src1->index] + (high_part1 == REG_HIGH ? 4 : 0));
+ modrm |= ENC_REG(reg_gp_map[src2->index] + (high_part2 == REG_HIGH ? 4 : 0));
+ bemit8(modrm);
+}
+
/** Create a ModR/M byte for one register and extension */
static void bemit_modru(const arch_register_t *reg, unsigned ext)
{
bemit8(modrm);
}
+/** Create a ModR/M8 byte for one register */
+static void bemit_modrm8(reg_modifier_t high_part, const arch_register_t *reg)
+{
+ unsigned char modrm = MOD_REG;
+ assert(reg_gp_map[reg->index] < 4);
+ modrm |= ENC_RM(reg_gp_map[reg->index] + (high_part == REG_HIGH ? 4 : 0));
+ modrm |= MOD_REG;
+ bemit8(modrm);
+}
+
/**
* Calculate the size of an signed immediate in bytes.
*
}
}
+/**
+ * binary emitter for setcc.
+ */
+static void bemit_setcc(const ir_node *node)
+{
+ const arch_register_t *dreg = get_out_reg(node, pn_ia32_Setcc_res);
+
+ pn_Cmp pnc = get_ia32_condcode(node);
+ pnc = determine_final_pnc(node, n_ia32_Setcc_eflags, pnc);
+ if (pnc & ia32_pn_Cmp_float) {
+ switch (pnc & 0x0f) {
+ case pn_Cmp_Uo:
+ /* setp <dreg */
+ bemit8(0x0F);
+ bemit8(0x9A);
+ bemit_modrm8(REG_LOW, dreg);
+ return;
+
+ case pn_Cmp_Leg:
+ /* setnp <dreg*/
+ bemit8(0x0F);
+ bemit8(0x9B);
+ bemit_modrm8(REG_LOW, dreg);
+ return;
+
+ case pn_Cmp_Eq:
+ case pn_Cmp_Lt:
+ case pn_Cmp_Le:
+ /* set%PNC <dreg */
+ bemit8(0x0F);
+ bemit8(0x90 | pnc2cc(pnc));
+ bemit_modrm8(REG_LOW, dreg);
+
+ /* setnp >dreg */
+ bemit8(0x0F);
+ bemit8(0x9B);
+ bemit_modrm8(REG_HIGH, dreg);
+
+ /* andb %>dreg, %<dreg */
+ bemit8(0x20);
+ bemit_modrr8(REG_HIGH, dreg, REG_LOW, dreg);
+ return;
+
+ case pn_Cmp_Ug:
+ case pn_Cmp_Uge:
+ case pn_Cmp_Ne:
+ /* set%PNC <dreg */
+ bemit8(0x0F);
+ bemit8(0x90 | pnc2cc(pnc));
+ bemit_modrm8(REG_LOW, dreg);
+
+ /* setp >dreg */
+ bemit8(0x0F);
+ bemit8(0x9A);
+ bemit_modrm8(REG_HIGH, dreg);
+
+ /* orb %>dreg, %<dreg */
+ bemit8(0x08);
+ bemit_modrr8(REG_HIGH, dreg, REG_LOW, dreg);
+ return;
+
+ default:
+ break;
+ }
+ }
+ /* set%PNC <dreg */
+ bemit8(0x0F);
+ bemit8(0x90 | pnc2cc(pnc));
+ bemit_modrm8(REG_LOW, dreg);
+}
+
static void bemit_cmovcc(const ir_node *node)
{
const ia32_attr_t *attr = get_ia32_attr_const(node);
/* TODO: handling of Nans isn't correct yet */
bemit8(0x0F);
- bemit8(0x40 + pnc2cc(pnc));
+ bemit8(0x40 | pnc2cc(pnc));
if (get_ia32_op_type(node) == ia32_Normal) {
bemit_modrr(in_true, out);
} else {
UNOPMEM(incmem, 0xFE, 0)
UNOPMEM(decmem, 0xFE, 1)
-static void bemit_setcc(const ir_node *node)
-{
- pn_Cmp pnc;
-
- bemit8(0x0F);
-
- pnc = get_ia32_condcode(node);
- pnc = determine_final_pnc(node, n_ia32_Setcc_eflags, pnc);
-
- /* TODO: all the special casing for float compares is missing */
- if (pnc & ia32_pn_Cmp_float)
- panic("binary setcc from float compare not implemented yet");
-
- bemit8(0x90 + pnc2cc(pnc));
- bemit_modru(get_out_reg(node, pn_ia32_Setcc_res), 2);
-}
-
static void bemit_ldtls(const ir_node *node)
{
const arch_register_t *out = get_out_reg(node, 0);