Commit 114a3dc1 authored by Matthias Braun's avatar Matthias Braun Committed by Tobias Rapp
Browse files

Rewrite amd64 code selection

parent d9607aa8
......@@ -8,6 +8,7 @@
* @brief emit assembler for a backend graph
*/
#include <limits.h>
#include <inttypes.h>
#include "be_t.h"
#include "error.h"
......@@ -54,21 +55,6 @@ static void amd64_emit_insn_mode_suffix(amd64_insn_mode_t mode)
be_emit_char(c);
}
static void amd64_emit_mode_suffix(const ir_mode *mode)
{
assert(mode_is_int(mode) || mode_is_reference(mode));
char c;
switch (get_mode_size_bits(mode)) {
case 8: c = 'b'; break;
case 16: c = 'w'; break;
case 32: c = 'l'; break;
case 64: c = 'q'; break;
default:
panic("Can't output mode_suffix for %+F", mode);
}
be_emit_char(c);
}
static const char *get_8bit_name(const arch_register_t *reg)
{
switch (reg->index) {
......@@ -144,21 +130,6 @@ static void emit_register(const arch_register_t *reg)
be_emit_string(reg->name);
}
static void emit_register_mode(const arch_register_t *reg, const ir_mode *mode)
{
const char *name;
switch (get_mode_size_bits(mode)) {
case 8: name = get_8bit_name(reg); break;
case 16: name = get_16bit_name(reg); break;
case 32: name = get_32bit_name(reg); break;
case 64: name = reg->name; break;
default:
panic("invalid mode");
}
be_emit_char('%');
be_emit_string(name);
}
static void emit_register_insn_mode(const arch_register_t *reg,
amd64_insn_mode_t mode)
{
......@@ -177,7 +148,6 @@ static void emit_register_insn_mode(const arch_register_t *reg,
typedef enum amd64_emit_mod_t {
EMIT_NONE = 0,
EMIT_RESPECT_LS = 1U << 0,
EMIT_IGNORE_MODE = 1U << 1,
} amd64_emit_mod_t;
ENUM_BITSET(amd64_emit_mod_t)
......@@ -188,14 +158,29 @@ static void amd64_emit_immediate(const amd64_movimm_attr_t *const imm)
if (entity != NULL) {
be_gas_emit_entity(entity);
if (imm->offset != 0)
be_emit_irprintf("%+ld", imm->offset);
be_emit_irprintf("%+" PRId64, imm->offset);
} else {
be_emit_irprintf("0x%lX", imm->offset);
be_emit_irprintf("0x%" PRIX64, imm->offset);
}
}
static void amd64_emit_am(const ir_node *const node,
const amd64_am_info_t *const am)
static void amd64_emit_am_immediate(const amd64_am_info_t *const am)
{
be_emit_char('$');
if (am->entity != NULL) {
be_gas_emit_entity(am->entity);
}
if (am->entity == NULL || am->offset != 0) {
if (am->entity != NULL) {
be_emit_irprintf("%+" PRId32, am->offset);
} else {
be_emit_irprintf("0x%" PRIX32, am->offset);
}
}
}
static void amd64_emit_addr(const ir_node *const node,
const amd64_am_info_t *const am)
{
ir_entity *entity = am->entity;
if (entity != NULL) {
......@@ -213,9 +198,9 @@ static void amd64_emit_am(const ir_node *const node,
if (offset != 0 || (entity == NULL && base_input == NO_INPUT
&& index_input == NO_INPUT)) {
if (entity != NULL) {
be_emit_irprintf("%+d", offset);
be_emit_irprintf("%+" PRId32, offset);
} else {
be_emit_irprintf("%d", offset);
be_emit_irprintf("%" PRId32, offset);
}
}
......@@ -244,6 +229,71 @@ static void amd64_emit_am(const ir_node *const node,
}
}
static void amd64_emit_am(const ir_node *const node)
{
amd64_attr_t const *const attr = get_amd64_attr_const(node);
switch (attr->data.op_mode) {
case AMD64_MODE_REG_IMM: {
amd64_emit_am_immediate(&attr->am);
be_emit_cstring(", ");
const arch_register_t *reg = arch_get_irn_register_in(node, 0);
emit_register_insn_mode(reg, attr->data.insn_mode);
return;
}
case AMD64_MODE_REG_REG: {
const arch_register_t *reg0 = arch_get_irn_register_in(node, 0);
const arch_register_t *reg1 = arch_get_irn_register_in(node, 1);
emit_register_insn_mode(reg1, attr->data.insn_mode);
be_emit_cstring(", ");
emit_register_insn_mode(reg0, attr->data.insn_mode);
return;
}
case AMD64_MODE_LOAD_REG: {
amd64_emit_addr(node, &attr->am);
be_emit_cstring(", ");
const arch_register_t *reg
= arch_get_irn_register_in(node, attr->am.reg_input);
emit_register_insn_mode(reg, attr->data.insn_mode);
return;
}
case AMD64_MODE_LOAD:
amd64_emit_addr(node, &attr->am);
return;
case AMD64_MODE_REG: {
const arch_register_t *reg
= arch_get_irn_register_in(node, attr->am.reg_input);
emit_register_insn_mode(reg, attr->data.insn_mode);
return;
}
}
panic("invalid op_mode");
}
static void emit_shiftop(const ir_node *const node)
{
amd64_attr_t const *const attr = get_amd64_attr_const(node);
switch (attr->data.op_mode) {
case AMD64_MODE_REG_IMM: {
amd64_emit_am_immediate(&attr->am);
be_emit_cstring(", ");
const arch_register_t *reg = arch_get_irn_register_in(node, 0);
emit_register_insn_mode(reg, attr->data.insn_mode);
return;
}
case AMD64_MODE_REG_REG: {
const arch_register_t *reg0 = arch_get_irn_register_in(node, 0);
const arch_register_t *reg1 = arch_get_irn_register_in(node, 1);
emit_register_insn_mode(reg1, INSN_MODE_8);
be_emit_cstring(", ");
emit_register_insn_mode(reg0, attr->data.insn_mode);
return;
}
case AMD64_MODE_LOAD_REG:
break;
}
panic("invalid op_mode for shiftop");
}
void amd64_emitf(ir_node const *const node, char const *fmt, ...)
{
va_list ap;
......@@ -274,7 +324,6 @@ void amd64_emitf(ir_node const *const node, char const *fmt, ...)
amd64_emit_mod_t mod = EMIT_NONE;
for (;;) {
switch (*fmt) {
case '#': mod |= EMIT_RESPECT_LS; break;
case '^': mod |= EMIT_IGNORE_MODE; break;
default:
goto end_of_mods;
......@@ -292,13 +341,14 @@ end_of_mods:
case 'A':
switch (*fmt++) {
case 'M': {
amd64_attr_t const *const attr = get_amd64_attr_const(node);
amd64_emit_am(node, &attr->am);
case 'M':
amd64_emit_am(node);
break;
default: {
amd64_attr_t const *const attr = get_amd64_attr_const(node);
amd64_emit_addr(node, &attr->am);
--fmt;
}
default:
goto unknown;
}
break;
......@@ -346,15 +396,16 @@ emit_R:
emit_register(reg);
} else {
amd64_attr_t const *const attr = get_amd64_attr_const(node);
if (mod & EMIT_RESPECT_LS) {
emit_register_mode(reg, attr->ls_mode);
} else {
emit_register_insn_mode(reg, attr->data.insn_mode);
}
emit_register_insn_mode(reg, attr->data.insn_mode);
}
break;
case 'S': {
if (*fmt == 'O') {
++fmt;
emit_shiftop(node);
break;
}
int pos;
if ('0' <= *fmt && *fmt <= '9') {
pos = *fmt++ - '0';
......@@ -367,11 +418,7 @@ emit_R:
case 'M': {
amd64_attr_t const *const attr = get_amd64_attr_const(node);
if (mod & EMIT_RESPECT_LS) {
amd64_emit_mode_suffix(attr->ls_mode);
} else {
amd64_emit_insn_mode_suffix(attr->data.insn_mode);
}
amd64_emit_insn_mode_suffix(attr->data.insn_mode);
break;
}
......@@ -393,19 +440,6 @@ emit_R:
break;
}
case 'c': {
amd64_attr_t const *const attr = get_amd64_attr_const(node);
ir_mode *mode = attr->ls_mode;
if (get_mode_size_bits(mode) == 64)
break;
if (get_mode_size_bits(mode) == 32 && !mode_is_signed(mode)
&& attr->data.insn_mode == INSN_MODE_32)
break;
be_emit_char(mode_is_signed(mode) ? 's' : 'z');
amd64_emit_mode_suffix(mode);
break;
}
default:
unknown:
panic("unknown format conversion");
......@@ -499,30 +533,19 @@ static void emit_amd64_Jcc(const ir_node *irn)
}
}
static void emit_amd64_LoadZ(const ir_node *node)
static void emit_amd64_Movz(const ir_node *node)
{
const amd64_attr_t *attr = get_amd64_attr_const(node);
switch (attr->data.insn_mode) {
case INSN_MODE_8: amd64_emitf(node, "movzbq %AM, %^D0"); break;
case INSN_MODE_16: amd64_emitf(node, "movzwq %AM, %^D0"); break;
case INSN_MODE_8: amd64_emitf(node, "movzbq %A, %^D0"); break;
case INSN_MODE_16: amd64_emitf(node, "movzwq %A, %^D0"); break;
case INSN_MODE_32:
case INSN_MODE_64: amd64_emitf(node, "mov%M %AM, %D0"); break;
case INSN_MODE_64: amd64_emitf(node, "mov%M %A, %D0"); break;
default:
panic("invalid insn mode");
}
}
static void emit_amd64_Call(const ir_node *node)
{
const amd64_attr_t *attr = get_amd64_attr_const(node);
be_emit_cstring("\tcall *");
int arity = get_irn_arity(node);
const arch_register_t *reg = arch_get_irn_register_in(node, arity-1);
emit_register_insn_mode(reg, attr->data.insn_mode);
be_emit_finish_line_gas(node);
}
/**
* emit copy node
*/
......@@ -613,10 +636,9 @@ static void amd64_register_emitters(void)
/* register all emitter functions defined in spec */
amd64_register_spec_emitters();
be_set_emitter(op_amd64_Call, emit_amd64_Call);
be_set_emitter(op_amd64_Jcc, emit_amd64_Jcc);
be_set_emitter(op_amd64_Jmp, emit_amd64_Jmp);
be_set_emitter(op_amd64_LoadZ, emit_amd64_LoadZ);
be_set_emitter(op_amd64_Movz, emit_amd64_Movz);
be_set_emitter(op_amd64_Return, emit_amd64_Return);
be_set_emitter(op_amd64_Start, emit_amd64_Start);
be_set_emitter(op_amd64_SwitchJmp, emit_amd64_SwitchJmp);
......
......@@ -22,14 +22,16 @@
* @brief This file implements functions to finalize the irg for emit.
*/
#include "amd64_finish.h"
#include "amd64_new_nodes.h"
#include "amd64_nodes_attr.h"
#include "bearch.h"
#include "benode.h"
#include "besched.h"
#include "debug.h"
#include "error.h"
#include "amd64_nodes_attr.h"
#include "gen_amd64_new_nodes.h"
#include "irgwalk.h"
#include "util.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
......@@ -46,6 +48,71 @@ static unsigned get_first_same(arch_register_req_t const *const req)
panic("same position not found");
}
static bool try_swap_inputs(ir_node *node)
{
/* commutative operation, just switch the inputs */
if (is_amd64_Add(node) || is_amd64_And(node) || is_amd64_Or(node)
|| is_amd64_Xor(node) || is_amd64_IMul(node)) {
/* TODO: support Cmp input swapping */
ir_node *in0 = get_irn_n(node, 0);
ir_node *in1 = get_irn_n(node, 1);
set_irn_n(node, 0, in1);
set_irn_n(node, 1, in0);
return true;
}
return false;
}
static ir_node *amd64_turn_back_am(ir_node *node)
{
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *block = get_nodes_block(node);
amd64_attr_t *attr = get_amd64_attr(node);
amd64_am_info_t new_am = attr->am;
ir_node *load_in[3];
int load_arity = 0;
if (attr->am.base_input != NO_INPUT && attr->am.base_input != RIP_INPUT) {
new_am.base_input = load_arity;
load_in[load_arity++] = get_irn_n(node, attr->am.base_input);
}
if (attr->am.index_input != NO_INPUT) {
new_am.index_input = load_arity;
load_in[load_arity++] = get_irn_n(node, attr->am.index_input);
}
assert(attr->am.mem_input != NO_INPUT);
new_am.mem_input = load_arity;
load_in[load_arity++] = get_irn_n(node, attr->am.mem_input);
ir_node *load = new_bd_amd64_Movz(dbgi, block, load_arity, load_in,
attr->data.insn_mode, AMD64_MODE_LOAD,
new_am);
ir_node *load_res = new_r_Proj(load, mode_Lu, pn_amd64_Movz_res);
/* change operation */
ir_node *new_in[2];
new_in[0] = get_irn_n(node, attr->am.reg_input);
new_in[1] = load_res;
set_irn_in(node, ARRAY_SIZE(new_in), new_in);
attr->data.op_mode = AMD64_MODE_REG_REG;
attr->am.base_input = NO_INPUT;
attr->am.index_input = NO_INPUT;
/* rewire mem-proj */
foreach_out_edge(node, edge) {
ir_node *out = get_edge_src_irn(edge);
if (get_irn_mode(out) == mode_M) {
set_Proj_pred(out, load);
set_Proj_proj(out, pn_amd64_Movz_M);
break;
}
}
if (sched_is_scheduled(node))
sched_add_before(node, load);
return load_res;
}
/**
* Insert copies for all amd64 nodes where the should_be_same requirement is
* not fulfilled.
......@@ -68,6 +135,34 @@ static void assure_should_be_same_requirements(ir_node *const node)
if (in_reg == out_reg)
continue;
/* test if any other input is using the out register */
for (int i2 = 0, arity = get_irn_arity(node); i2 < arity; ++i2) {
const arch_register_t *reg = arch_get_irn_register_in(node, i2);
if (reg == out_reg && (unsigned)i2 != same_pos) {
if (!is_amd64_irn(node))
panic("Can't fulfill should_be_same on non-amd64 node");
/* see what role this register has */
const amd64_attr_t *attr = get_amd64_attr_const(node);
if (attr->data.op_mode == AMD64_MODE_LOAD
|| attr->data.op_mode == AMD64_MODE_REG
|| attr->data.op_mode == AMD64_MODE_REG_IMM) {
panic("unexpected op_mode");
} else if (attr->data.op_mode == AMD64_MODE_REG_REG) {
swap:;
bool res = try_swap_inputs(node);
if (res)
return;
panic("couldn't swap inputs of %+F", node);
} else {
assert(attr->data.op_mode == AMD64_MODE_LOAD_REG);
/* extract load into an own instruction */
ir_node *res = amd64_turn_back_am(node);
arch_set_irn_register(res, out_reg);
goto swap;
}
}
}
ir_node *const block = get_nodes_block(node);
ir_node *const copy = be_new_Copy(block, in_node);
......
......@@ -10,6 +10,7 @@
* assembler irg.
*/
#include <stdlib.h>
#include <inttypes.h>
#include "error.h"
#include "irprog_t.h"
......@@ -28,12 +29,6 @@
#include "amd64_new_nodes.h"
#include "gen_amd64_regalloc_if.h"
void set_amd64_ls_mode(ir_node *node, ir_mode *mode)
{
amd64_attr_t *attr = get_amd64_attr(node);
attr->ls_mode = mode;
}
/**
* Dumper interface for dumping amd64 nodes in vcg.
* @param F the output file
......@@ -60,14 +55,30 @@ static void amd64_dump_node(FILE *F, const ir_node *n, dump_reason_t reason)
break;
case dump_node_nodeattr_txt:
/* TODO: dump some attributes which should show up */
/* in node name in dump (e.g. consts or the like) */
break;
case dump_node_info_txt:
arch_dump_reqs_and_registers(F, n);
const amd64_attr_t *attr = get_amd64_attr_const(n);
fputs("size = ", F);
switch (attr->data.insn_mode) {
case INSN_MODE_8: fputs("8\n", F); break;
case INSN_MODE_16: fputs("16\n", F); break;
case INSN_MODE_32: fputs("32\n", F); break;
case INSN_MODE_64: fputs("64\n", F); break;
}
fputs("mode = ", F);
switch (attr->data.op_mode) {
case AMD64_MODE_REG_REG: fputs("reg+reg\n", F); break;
case AMD64_MODE_REG_IMM: fputs("reg+imm\n", F); break;
case AMD64_MODE_LOAD_REG: fputs("load+reg\n", F); break;
default: fputs("unknown\n", F); break;
}
fprintf(F, "reg input: %d\n", attr->am.reg_input);
fprintf(F, "base input: %d\n", attr->am.base_input);
fprintf(F, "index input: %d\n", attr->am.index_input);
ir_fprintf(F, "am imm: %+F%+" PRId32 "\n", attr->am.entity,
attr->am.offset);
break;
}
}
......@@ -186,8 +197,7 @@ static int cmp_amd64_attr(const ir_node *a, const ir_node *b)
const amd64_attr_t *attr_b = get_amd64_attr_const(b);
return cmp_am(&attr_a->am, &attr_b->am)
|| attr_a->data.insn_mode != attr_b->data.insn_mode
|| attr_a->data.needs_frame_ent != attr_b->data.needs_frame_ent
|| attr_a->ls_mode != attr_b->ls_mode;
|| attr_a->data.needs_frame_ent != attr_b->data.needs_frame_ent;
}
static int cmp_amd64_movimm_attr(const ir_node *const a,
......
......@@ -13,11 +13,6 @@
#include <stdint.h>
#include "amd64_nodes_attr.h"
/**
* Sets the input mode of the node.
*/
void set_amd64_ls_mode(ir_node *n, ir_mode *mode);
/**
* Returns the attributes of an amd64 node.
*/
......
......@@ -38,28 +38,37 @@ typedef enum {
AMD64_SEGMENT_GS,
} amd64_segment_selector_t;
typedef enum {
AMD64_MODE_LOAD,
AMD64_MODE_REG,
AMD64_MODE_REG_REG,
AMD64_MODE_REG_IMM,
AMD64_MODE_LOAD_REG,
} amd64_op_mode_t;
enum {
NO_INPUT = 0xFF,
RIP_INPUT = 0xFE, /* can be used as base_input for PIC code */
};
typedef struct amd64_am_info_t {
int64_t offset;
int32_t offset;
ir_entity *entity;
uint8_t base_input;
uint8_t index_input;
uint8_t mem_input;
unsigned log_scale : 2; /* 0, 1, 2, 3 (giving scale 1, 2, 4, 8) */
ENUMBF(amd64_segment_selector_t) segment : 4;
unsigned reg_input : 4;
} amd64_am_info_t;
struct amd64_attr_t
{
except_attr exc; /**< the exception attribute. MUST be the first one. */
ir_mode *ls_mode; /**< Stores the "input" mode */
struct amd64_attr_data_bitfield {
bool has_am_info : 1;
bool needs_frame_ent : 1;
bool needs_frame_ent : 1;
ENUMBF(amd64_insn_mode_t) insn_mode : 2;
ENUMBF(amd64_op_mode_t) op_mode : 3;
} data;
amd64_am_info_t am;
};
......
......@@ -74,9 +74,8 @@ PushAM => {
outs => [ "stack", "M" ],
attr => "amd64_insn_mode_t insn_mode, amd64_am_info_t am",
init_attr => "attr->data.insn_mode = insn_mode;\n"
."\tattr->am = am;\n"
."\tattr->data.has_am_info = true;\n",
emit => "push%M %AM",
."\tattr->am = am;\n",
emit => "push%M %A",
},
PopAM => {
......@@ -87,35 +86,37 @@ PopAM => {
outs => [ "stack", "M" ],
attr => "amd64_insn_mode_t insn_mode, amd64_am_info_t am",
init_attr => "attr->data.insn_mode = insn_mode;\n"
."\tattr->am = am;\n"
."\tattr->data.has_am_info = true;\n",
emit => "pop%M %AM",
."\tattr->am = am;\n",
emit => "pop%M %A",
},
Add => {
irn_flags => [ "rematerializable" ],
state => "exc_pinned",
attr => "amd64_insn_mode_t insn_mode",
init_attr => "attr->data.insn_mode = insn_mode;",
reg_req => { in => [ "gp", "gp" ], out => [ "in_r1 !in_r2" ] },
ins => [ "left", "right" ],
outs => [ "res" ],
emit => "add%M %S1, %D0",
mode => $mode_gp,
modified_flags => 1,
irn_flags => [ "rematerializable" ],
state => "exc_pinned",
reg_req => { out => [ "gp", "flags", "none" ] },
arity => "variable",
outs => [ "res", "flags", "M" ],
attr => "amd64_insn_mode_t insn_mode, amd64_op_mode_t op_mode, amd64_am_info_t am",
init_attr => "attr->data.insn_mode = insn_mode;\n"
."\tattr->data.op_mode = op_mode;\n"
."\tattr->am = am;\n",
emit => "add%M %AM",
modified_flags => $status_flags,
},
And => {
irn_flags => [ "rematerializable" ],
state => "exc_pinned",
attr => "amd64_insn_mode_t insn_mode",
init_attr => "attr->data.insn_mode = insn_mode;",
reg_req => { in => [ "gp", "gp" ], out => [ "in_r1 !in_r2" ] },
ins => [ "left", "right" ],
outs => [ "res" ],
emit => "and%M %S1, %D0",
mode => $mode_gp,
modified_flags => 1,
irn_flags => [ "rematerializable" ],
state => "exc_pinned",
reg_req => { out => [ "gp", "flags", "none" ] },
arity => "variable",
outs => [ "res", "flags", "M" ],
attr => "amd64_insn_mode_t insn_mode, amd64_op_mode_t op_mode, amd64_am_info_t am",
init_attr => "attr->data.insn_mode = insn_mode;\n"
."\tattr->data.op_mode = op_mode;\n"
."\tattr->am = am;\n",
emit => "and%M %AM",
modified_flags => $status_flags,