Commit e719e0dd authored by Matthias Braun's avatar Matthias Braun
Browse files

amd64: do not use beabi anymore

parent 661a5d5e
/*
* This file is part of libFirm.
* Copyright (C) 2012 University of Karlsruhe.
*/
/**
* @file
* @brief calling convention helpers
* @author Matthias Braun
*/
#include "be_t.h"
#include "beirg.h"
#include "amd64_cconv.h"
#include "irmode.h"
#include "irgwalk.h"
#include "typerep.h"
#include "xmalloc.h"
#include "util.h"
#include "error.h"
#include "gen_amd64_regalloc_if.h"
#include "bitfiddle.h"
static const unsigned ignore_regs[] = {
REG_RSP,
REG_EFLAGS,
};
static const arch_register_t* const param_regs[] = {
&amd64_registers[REG_RDI],
&amd64_registers[REG_RSI],
&amd64_registers[REG_RDX],
&amd64_registers[REG_RCX],
&amd64_registers[REG_R8],
&amd64_registers[REG_R9],
};
static const arch_register_t* const result_regs[] = {
&amd64_registers[REG_RAX],
&amd64_registers[REG_RDX],
};
static const unsigned caller_saves[] = {
REG_RAX,
REG_RCX,
REG_RDX,
REG_RSI,
REG_RDI,
REG_R8,
REG_R9,
REG_R10,
REG_R11,
};
static unsigned default_caller_saves[BITSET_SIZE_ELEMS(N_AMD64_REGISTERS)];
static const unsigned callee_saves[] = {
REG_RBX,
REG_RBP,
REG_R12,
REG_R13,
REG_R14,
REG_R15,
};
static unsigned default_callee_saves[BITSET_SIZE_ELEMS(N_AMD64_REGISTERS)];
static void check_omit_fp(ir_node *node, void *env)
{
/* omit-fp is not possible if:
* - we have allocations on the stack
*/
if (is_Alloc(node) || is_Free(node)) {
bool *can_omit_fp = (bool*) env;
*can_omit_fp = false;
}
}
amd64_cconv_t *amd64_decide_calling_convention(ir_type *function_type,
ir_graph *irg)
{
bool omit_fp = false;
if (irg != NULL) {
omit_fp = be_options.omit_fp;
if (omit_fp == true) {
irg_walk_graph(irg, check_omit_fp, NULL, &omit_fp);
}
}
mtp_additional_properties mtp
= get_method_additional_properties(function_type);
unsigned *caller_saves = rbitset_malloc(N_AMD64_REGISTERS);
unsigned *callee_saves = rbitset_malloc(N_AMD64_REGISTERS);
if (mtp & mtp_property_returns_twice)
panic("amd64: returns_twice calling convention NIY");
rbitset_copy(caller_saves, default_caller_saves, N_AMD64_REGISTERS);
rbitset_copy(callee_saves, default_callee_saves, N_AMD64_REGISTERS);
if (!omit_fp) {
rbitset_clear(callee_saves, REG_RBP);
}
/* determine how parameters are passed */
size_t n_params = get_method_n_params(function_type);
size_t regnum = 0;
reg_or_stackslot_t *params = XMALLOCNZ(reg_or_stackslot_t, n_params);
size_t n_param_regs = ARRAY_SIZE(param_regs);
unsigned stack_offset = 0;
for (size_t i = 0; i < n_params; ++i) {
ir_type *param_type = get_method_param_type(function_type,i);
if (is_compound_type(param_type))
panic("amd64: compound arguments NIY");
if (get_method_calling_convention(function_type) & cc_compound_ret)
panic("amd64: compound return NIY");
ir_mode *mode = get_type_mode(param_type);
int bits = get_mode_size_bits(mode);
reg_or_stackslot_t *param = &params[i];
if (regnum < n_param_regs) {
const arch_register_t *reg = param_regs[regnum];
param->reg = reg;
param->req = reg->single_req;
param->reg_offset = regnum;
++regnum;
} else {
param->type = param_type;
param->offset = stack_offset;
/* increase offset by at least AMD64_REGISTER_SIZE bytes so
* everything is aligned */
stack_offset += bits > 8 * AMD64_REGISTER_SIZE ? bits / 8 : AMD64_REGISTER_SIZE;
continue;
}
}
unsigned n_param_regs_used = regnum;
/* determine how results are passed */
size_t n_results = get_method_n_ress(function_type);
unsigned n_reg_results = 0;
reg_or_stackslot_t *results = XMALLOCNZ(reg_or_stackslot_t, n_results);
unsigned res_regnum = 0;
size_t n_result_regs = ARRAY_SIZE(result_regs);
for (size_t i = 0; i < n_results; ++i) {
ir_type *result_type = get_method_res_type(function_type, i);
ir_mode *result_mode = get_type_mode(result_type);
reg_or_stackslot_t *result = &results[i];
if (mode_is_float(result_mode)) {
panic("amd64: float return NIY");
} else {
if (res_regnum >= n_result_regs) {
panic("Too many results");
} else {
const arch_register_t *reg = result_regs[res_regnum++];
result->req = reg->single_req;
result->reg_offset = i;
rbitset_clear(caller_saves, reg->global_index);
++n_reg_results;
}
}
}
amd64_cconv_t *cconv = XMALLOCZ(amd64_cconv_t);
cconv->parameters = params;
cconv->param_stack_size = stack_offset;
cconv->n_param_regs = n_param_regs_used;
cconv->results = results;
cconv->omit_fp = omit_fp;
cconv->caller_saves = caller_saves;
cconv->callee_saves = callee_saves;
cconv->n_reg_results = n_reg_results;
if (irg != NULL) {
be_irg_t *birg = be_birg_from_irg(irg);
size_t n_ignores = ARRAY_SIZE(ignore_regs);
struct obstack *obst = &birg->obst;
birg->allocatable_regs = rbitset_obstack_alloc(obst, N_AMD64_REGISTERS);
rbitset_set_all(birg->allocatable_regs, N_AMD64_REGISTERS);
for (size_t r = 0; r < n_ignores; ++r) {
rbitset_clear(birg->allocatable_regs, ignore_regs[r]);
}
if (!omit_fp)
rbitset_clear(birg->allocatable_regs, REG_RBP);
}
return cconv;
}
void amd64_free_calling_convention(amd64_cconv_t *cconv)
{
free(cconv->parameters);
free(cconv->results);
free(cconv->caller_saves);
free(cconv->callee_saves);
free(cconv);
}
void amd64_cconv_init(void)
{
for (size_t i = 0; i < ARRAY_SIZE(caller_saves); ++i) {
rbitset_set(default_caller_saves, caller_saves[i]);
}
for (size_t i = 0; i < ARRAY_SIZE(callee_saves); ++i) {
rbitset_set(default_callee_saves, callee_saves[i]);
}
}
/*
* This file is part of libFirm.
* Copyright (C) 2012 University of Karlsruhe.
*/
/**
* @file
* @brief support functions for calling conventions
* @author Matthias Braun
*/
#ifndef FIRM_BE_AMD64_AMD64_CCONV_H
#define FIRM_BE_AMD64_AMD64_CCONV_H
#include "firm_types.h"
#include "bearch_amd64_t.h"
#include "be_types.h"
#include "benode.h"
#include "gen_amd64_regalloc_if.h"
/** information about a single parameter or result */
typedef struct reg_or_stackslot_t
{
const arch_register_req_t *req; /**< if != NULL, register requirements
for this parameter (or the first
part of it). */
const arch_register_t *reg;
unsigned reg_offset;
ir_type *type; /**< indicates that an entity of the specific
type is needed */
unsigned offset; /**< if transmitted via stack, the offset for
this parameter. */
ir_entity *entity; /**< entity in frame type */
} reg_or_stackslot_t;
/** The calling convention info for one call site. */
typedef struct amd64_cconv_t
{
bool omit_fp; /**< do not use frame pointer (and no
save/restore) */
reg_or_stackslot_t *parameters; /**< parameter info. */
unsigned param_stack_size; /**< stack size for parameters */
unsigned n_param_regs; /**< number of values passed in a
register */
reg_or_stackslot_t *results; /**< result info. */
unsigned n_reg_results;
unsigned *caller_saves; /**< bitset of caller saved registers */
unsigned *callee_saves; /**< bitset of callee saved registers */
} amd64_cconv_t;
/**
* Determine how function parameters and return values are passed.
* Decides what goes to register or to stack and what stack offsets/
* datatypes are used.
*
* @param function_type the type of the caller/callee function
* @param caller true for convention for the caller, false for callee
*/
amd64_cconv_t *amd64_decide_calling_convention(ir_type *function_type,
ir_graph *irg);
/**
* free memory used by a amd64_cconv_t
*/
void amd64_free_calling_convention(amd64_cconv_t *cconv);
void amd64_cconv_init(void);
#endif
......@@ -196,8 +196,6 @@ ENUM_BITSET(amd64_emit_mod_t)
static void amd64_emit_immediate(const amd64_imm_t *const imm)
{
if (imm->symconst != NULL) {
if (imm->sc_sign)
be_emit_char('-');
be_gas_emit_entity(imm->symconst);
}
if (imm->symconst == NULL || imm->offset != 0) {
......@@ -209,6 +207,17 @@ static void amd64_emit_immediate(const amd64_imm_t *const imm)
}
}
static void amd64_emit_am(const arch_register_t *const base,
const amd64_imm_t *const imm)
{
if (base == NULL) {
amd64_emit_immediate(imm);
return;
}
emit_register(base);
}
void amd64_emitf(ir_node const *const node, char const *fmt, ...)
{
va_list ap;
......@@ -255,6 +264,20 @@ end_of_mods:
be_emit_char('%');
break;
case 'A':
switch (*fmt++) {
case 'M': {
amd64_attr_t const *const attr = get_amd64_attr_const(node);
arch_register_t const *const base
= arch_get_irn_register_in(node, 0);
amd64_emit_am(base, &attr->imm);
break;
}
default:
goto unknown;
}
break;
case 'C': {
amd64_attr_t const *const attr = get_amd64_attr_const(node);
amd64_emit_immediate(&attr->imm);
......@@ -492,26 +515,15 @@ static void emit_amd64_LoadZ(const ir_node *node)
}
}
/**
* Emits code for a call.
*/
static void emit_be_Call(const ir_node *node)
static void emit_amd64_Call(const ir_node *node)
{
ir_entity *entity = be_Call_get_entity(node);
/* %eax/%rax is used in AMD64 to pass the number of vector parameters for
* variable argument counts */
if (get_method_variadicity (be_Call_get_type((ir_node *) node))) {
/* But this still is a hack... */
amd64_emitf(node, "xor %%rax, %%rax");
}
const amd64_attr_t *attr = get_amd64_attr_const(node);
if (entity) {
amd64_emitf(node, "call %E", entity);
} else {
be_emit_pad_comment();
be_emit_cstring("/* FIXME: call NULL entity?! */\n");
}
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);
}
/**
......@@ -576,7 +588,7 @@ static void emit_be_IncSP(const ir_node *node)
}
}
static void emit_be_Start(const ir_node *node)
static void emit_amd64_Start(const ir_node *node)
{
ir_graph *irg = get_irn_irg(node);
ir_type *frame_type = get_irg_frame_type(irg);
......@@ -587,10 +599,7 @@ static void emit_be_Start(const ir_node *node)
}
}
/**
* Emits code for a return.
*/
static void emit_be_Return(const ir_node *node)
static void emit_amd64_Return(const ir_node *node)
{
ir_graph *irg = get_irn_irg(node);
ir_type *frame_type = get_irg_frame_type(irg);
......@@ -616,21 +625,20 @@ static void amd64_register_emitters(void)
/* register all emitter functions defined in spec */
amd64_register_spec_emitters();
be_set_emitter(op_amd64_FrameAddr, emit_amd64_FrameAddr);
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_SwitchJmp, emit_amd64_SwitchJmp);
be_set_emitter(op_be_Call, emit_be_Call);
be_set_emitter(op_be_Copy, emit_be_Copy);
be_set_emitter(op_be_CopyKeep, emit_be_Copy);
be_set_emitter(op_be_IncSP, emit_be_IncSP);
be_set_emitter(op_be_Perm, emit_be_Perm);
be_set_emitter(op_be_Return, emit_be_Return);
be_set_emitter(op_be_Start, emit_be_Start);
be_set_emitter(op_Phi, be_emit_nothing);
be_set_emitter(op_be_Keep, be_emit_nothing);
be_set_emitter(op_amd64_Call, emit_amd64_Call);
be_set_emitter(op_amd64_FrameAddr, emit_amd64_FrameAddr);
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_Return, emit_amd64_Return);
be_set_emitter(op_amd64_Start, emit_amd64_Start);
be_set_emitter(op_amd64_SwitchJmp, emit_amd64_SwitchJmp);
be_set_emitter(op_be_Copy, emit_be_Copy);
be_set_emitter(op_be_CopyKeep, emit_be_Copy);
be_set_emitter(op_be_IncSP, emit_be_IncSP);
be_set_emitter(op_be_Keep, be_emit_nothing);
be_set_emitter(op_be_Perm, emit_be_Perm);
be_set_emitter(op_Phi, be_emit_nothing);
}
/**
......
......@@ -178,8 +178,7 @@ static int cmp_amd64_attr_SymConst(const ir_node *a, const ir_node *b)
static int cmp_imm(const amd64_imm_t *const imm0, const amd64_imm_t *const imm1)
{
return imm0->offset != imm1->offset || imm0->sc_sign != imm1->sc_sign
|| imm0->symconst != imm1->symconst;
return imm0->offset != imm1->offset || imm0->symconst != imm1->symconst;
}
/** Compare common amd64 node attributes. */
......
......@@ -26,7 +26,6 @@ typedef enum {
typedef struct amd64_imm_t {
int64_t offset;
bool sc_sign;
ir_entity *symconst;
} amd64_imm_t;
......
......@@ -9,7 +9,7 @@ $arch = "amd64";
{ name => "rdi", dwarf => 5 },
{ name => "rbx", dwarf => 3 },
{ name => "rbp", dwarf => 6 },
{ name => "rsp", dwarf => 7, type => "ignore" }, # stackpointer
{ name => "rsp", dwarf => 7 },
{ name => "r8", dwarf => 8 },
{ name => "r9", dwarf => 9 },
{ name => "r10", dwarf => 10 },
......@@ -18,28 +18,8 @@ $arch = "amd64";
{ name => "r13", dwarf => 13 },
{ name => "r14", dwarf => 14 },
{ name => "r15", dwarf => 15 },
# { name => "gp_NOREG", type => "ignore" }, # we need a dummy register for NoReg nodes
{ mode => "mode_Lu" }
],
# fp => [
# { name => "xmm0", dwarf => 17 },
# { name => "xmm1", dwarf => 18 },
# { name => "xmm2", dwarf => 19 },
# { name => "xmm3", dwarf => 20 },
# { name => "xmm4", dwarf => 21 },
# { name => "xmm5", dwarf => 22 },
# { name => "xmm6", dwarf => 23 },
# { name => "xmm7", dwarf => 24 },
# { name => "xmm8", dwarf => 25 },
# { name => "xmm9", dwarf => 26 },
# { name => "xmm10", dwarf => 27 },
# { name => "xmm11", dwarf => 28 },
# { name => "xmm12", dwarf => 29 },
# { name => "xmm13", dwarf => 30 },
# { name => "xmm14", dwarf => 31 },
# { name => "xmm15", dwarf => 32 },
# { mode => "mode_D" }
# ]
flags => [
{ name => "eflags", dwarf => 49 },
{ mode => "mode_Iu", flags => "manual_ra" }
......@@ -82,14 +62,30 @@ $default_copy_attr = "amd64_copy_attr";
);
%nodes = (
Push => {
PushAM => {
op_flags => [ "uses_memory" ],
state => "exc_pinned",
reg_req => { in => [ "gp", "gp", "none", "gp", "rsp" ], out => [ "rsp:I|S", "none" ] },
ins => [ "base", "index", "mem", "val", "stack" ],
emit => "push %S0",
reg_req => { in => [ "gp", "none", "rsp" ], out => [ "rsp:I|S", "none" ] },
ins => [ "base", "mem", "stack" ],
outs => [ "stack", "M" ],
am => "source,unary",
attr => "amd64_insn_mode_t insn_mode, int64_t offset, ir_entity *symconst",
init_attr => "attr->imm.offset = offset;\n"
. "attr->imm.symconst = symconst;\n"
. "attr->data.insn_mode = insn_mode;\n",
emit => "push %AM",
},
PopAM => {
op_flags => [ "uses_memory" ],
state => "exc_pinned",
reg_req => { in => [ "gp", "none", "rsp" ], out => [ "rsp:I|S", "none" ] },
ins => [ "base", "mem", "stack" ],
outs => [ "stack", "M" ],
attr => "amd64_insn_mode_t insn_mode, int64_t offset, ir_entity *symconst",
init_attr => "attr->imm.offset = offset;\n"
. "attr->imm.symconst = symconst;\n"
. "attr->data.insn_mode = insn_mode;\n",
emit => "pop %AM",
},
Add => {
......@@ -269,9 +265,8 @@ Xor0 => {
Const => {
op_flags => [ "constlike" ],
attr => "amd64_insn_mode_t insn_mode, int64_t offset, bool sc_sign, ir_entity *symconst",
attr => "amd64_insn_mode_t insn_mode, int64_t offset, ir_entity *symconst",
init_attr => "attr->imm.offset = offset;\n"
. "attr->imm.sc_sign = sc_sign;\n"
. "attr->imm.symconst = symconst;\n"
. "attr->data.insn_mode = insn_mode;\n",
reg_req => { out => [ "gp" ] },
......@@ -383,4 +378,24 @@ SwitchJmp => {
attr => "const ir_switch_table *table, ir_entity *table_entity",
},
Call => {
state => "exc_pinned",
arity => "variable",
out_arity => "variable",
},
Start => {
state => "pinned",
out_arity => "variable",
ins => [],
},
Return => {
state => "pinned",
op_flags => [ "cfopcode" ],
arity => "variable",
reg_req => { out => [ "none" ] },
mode => "mode_X",
},
);
This diff is collapsed.
......@@ -12,6 +12,7 @@
#include "ircons.h"
#include "irgmod.h"
#include "irdump.h"
#include "lower_builtins.h"
#include "lower_calls.h"
#include "debug.h"
#include "error.h"
......@@ -38,6 +39,7 @@
#include "gen_amd64_regalloc_if.h"
#include "amd64_transform.h"
#include "amd64_emitter.h"
#include "amd64_cconv.h"
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
......@@ -67,24 +69,24 @@ static ir_entity *amd64_get_frame_entity(const ir_node *node)
*/
static void amd64_set_frame_offset(ir_node *irn, int offset)
{
if (is_amd64_FrameAddr(irn)) {
if (is_amd64_FrameAddr(irn) || is_amd64_Store(irn) || is_amd64_LoadS(irn)
|| is_amd64_LoadZ(irn)) {
amd64_SymConst_attr_t *attr = get_amd64_SymConst_attr(irn);
attr->fp_offset += offset;
} else if (is_amd64_Store(irn)) {
amd64_SymConst_attr_t *attr = get_amd64_SymConst_attr(irn);
attr->fp_offset += offset;
} else if (is_amd64_LoadS(irn) || is_amd64_LoadZ(irn)) {
amd64_SymConst_attr_t *attr = get_amd64_SymConst_attr(irn);
attr->fp_offset += offset;
}
}
static int amd64_get_sp_bias(const ir_node *irn)
{
(void) irn;
if (is_amd64_Start(irn)) {
ir_graph *irg = get_irn_irg(irn);
ir_type *frame_type = get_irg_frame_type(irg);
return get_type_size_bytes(frame_type);
} else if (is_amd64_Return(irn)) {
ir_graph *irg = get_irn_irg(irn);
ir_type *frame_type = get_irg_frame_type(irg);
return -(int)get_type_size_bytes(frame_type);
}
return 0;
}
......@@ -143,6 +145,117 @@ static void transform_Spill(ir_node *node)
exchange(node, store);
}
static ir_node *create_push(ir_node *node, ir_node *schedpoint, ir_node *sp, ir_node *mem, ir_entity *ent)
{
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *block = get_nodes_block(node);
ir_graph *irg = get_irn_irg(node);
ir_node *frame = get_irg_frame(irg);
ir_node *push
= new_bd_amd64_PushAM(dbgi, block, frame, mem, sp, INSN_MODE_64, 0, ent);
sched_add_before(schedpoint, push);
return push;
}
static ir_node *create_pop(ir_node *node, ir_node *schedpoint, ir_node *sp, ir_entity *ent)
{
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *block = get_nodes_block(node);
ir_graph *irg = get_irn_irg(node);
ir_node *frame = get_irg_frame(irg);
ir_node *pop = new_bd_amd64_PopAM(dbgi, block, frame, get_irg_no_mem(irg),
sp, INSN_MODE_64, 0, ent);
sched_add_before(schedpoint, pop);
return pop;