Commit ad8c4178 authored by Michael Beck's avatar Michael Beck
Browse files

Removed the callee/caller saved flag from register specification.

The callee/caller saved information is not constant accross different
ABI's, so don't make it constant. Instead, all BE that still use beabi
provide a callback now.
This allows to implement support for x64_64/Win32 and is a necessary step
for the combined x86 BE.
parent d0c092c7
...@@ -61,47 +61,48 @@ $mode_fp = "mode_E"; # mode used by floatingpoint registers ...@@ -61,47 +61,48 @@ $mode_fp = "mode_E"; # mode used by floatingpoint registers
# #
# register types: # register types:
# 0 - no special type # 0 - no special type
# 1 - caller save (register must be saved by the caller of a function) # 1 - ignore (do not assign this register)
# 2 - callee save (register must be saved by the called function) # 2 - emitter can choose an arbitrary register of this class
# 4 - ignore (do not assign this register) # 4 - the register is a virtual one
# 8 - register represents a state
# NOTE: Last entry of each class is the largest Firm-Mode a register can hold # NOTE: Last entry of each class is the largest Firm-Mode a register can hold
%reg_classes = ( %reg_classes = (
gp => [ gp => [
{ name => "r0", type => 1 }, { name => "r0" },
{ name => "r1", type => 1 }, { name => "r1" },
{ name => "r2", type => 1 }, { name => "r2" },
{ name => "r3", type => 1 }, { name => "r3" },
{ name => "r4", type => 1 }, { name => "r4" },
{ name => "r5", type => 1 }, { name => "r5" },
{ name => "r6", type => 1 }, { name => "r6" },
{ name => "r7", type => 2 }, { name => "r7" },
{ name => "r8", type => 2 }, { name => "r8" },
{ name => "r9", type => 2 }, { name => "r9" },
{ name => "r10", type => 2 }, { name => "r10" },
{ name => "r11", type => 2 }, { name => "r11" },
{ name => "r12", type => 2 }, { name => "r12" },
{ name => "r13", type => 2 }, { name => "r13" },
{ name => "sp", realname => "r14", type => 4 }, # stackpointer { name => "sp", realname => "r14", type => 1 }, # stackpointer
{ name => "bp", realname => "r15", type => 4 }, # basepointer { name => "bp", realname => "r15", type => 1 }, # basepointer
{ mode => $mode_gp } { mode => $mode_gp }
], ],
fp => [ fp => [
{ name => "f0", type => 1 }, { name => "f0" },
{ name => "f1", type => 1 }, { name => "f1" },
{ name => "f2", type => 1 }, { name => "f2" },
{ name => "f3", type => 1 }, { name => "f3" },
{ name => "f4", type => 1 }, { name => "f4" },
{ name => "f5", type => 1 }, { name => "f5" },
{ name => "f6", type => 1 }, { name => "f6" },
{ name => "f7", type => 1 }, { name => "f7" },
{ name => "f8", type => 1 }, { name => "f8" },
{ name => "f9", type => 1 }, { name => "f9" },
{ name => "f10", type => 1 }, { name => "f10" },
{ name => "f11", type => 1 }, { name => "f11" },
{ name => "f12", type => 1 }, { name => "f12" },
{ name => "f13", type => 1 }, { name => "f13" },
{ name => "f14", type => 1 }, { name => "f14" },
{ name => "f15", type => 1 }, { name => "f15" },
{ mode => $mode_fp } { mode => $mode_fp }
] ]
); );
......
...@@ -351,6 +351,50 @@ static int TEMPLATE_is_valid_clobber(const char *clobber) ...@@ -351,6 +351,50 @@ static int TEMPLATE_is_valid_clobber(const char *clobber)
return 0; return 0;
} }
/**
* Check if the given register is callee or caller save.
*/
static int TEMPLATE_register_saved_by(const arch_register_t *reg, int callee)
{
if (callee) {
/* check for callee saved */
if (reg->reg_class == &TEMPLATE_reg_classes[CLASS_TEMPLATE_gp]) {
switch (reg->index) {
case REG_GP_R7:
case REG_GP_R8:
case REG_GP_R9:
case REG_GP_R10:
case REG_GP_R11:
case REG_GP_R12:
case REG_GP_R13:
return 1;
default:
return 0;
}
}
} else {
/* check for caller saved */
if (reg->reg_class == &TEMPLATE_reg_classes[CLASS_TEMPLATE_gp]) {
switch (reg->index) {
case REG_GP_R0:
case REG_GP_R1:
case REG_GP_R2:
case REG_GP_R3:
case REG_GP_R4:
case REG_GP_R5:
case REG_GP_R6:
return 1;
default:
return 0;
}
} else if (reg->reg_class == &TEMPLATE_reg_classes[CLASS_TEMPLATE_fp]) {
/* all FP registers are caller save */
return 1;
}
}
return 0;
}
const arch_isa_if_t TEMPLATE_isa_if = { const arch_isa_if_t TEMPLATE_isa_if = {
TEMPLATE_init, TEMPLATE_init,
TEMPLATE_lower_for_target, TEMPLATE_lower_for_target,
...@@ -373,6 +417,7 @@ const arch_isa_if_t TEMPLATE_isa_if = { ...@@ -373,6 +417,7 @@ const arch_isa_if_t TEMPLATE_isa_if = {
TEMPLATE_after_ra, TEMPLATE_after_ra,
TEMPLATE_finish_irg, TEMPLATE_finish_irg,
TEMPLATE_emit_routine, TEMPLATE_emit_routine,
TEMPLATE_register_saved_by,
}; };
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_TEMPLATE) BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_TEMPLATE)
......
...@@ -55,55 +55,53 @@ $arch = "amd64"; ...@@ -55,55 +55,53 @@ $arch = "amd64";
# #
# register types: # register types:
$normal = 0; # no special type $normal = 0; # no special type
$caller_save = 1; # caller save (register must be saved by the caller of a function) $ignore = 1; # ignore (do not assign this register)
$callee_save = 2; # callee save (register must be saved by the called function) $arbitrary = 2; # emitter can choose an arbitrary register of this class
$ignore = 4; # ignore (do not assign this register) $virtual = 4; # the register is a virtual one
$arbitrary = 8; # emitter can choose an arbitrary register of this class $state = 8; # register represents a state
$virtual = 16; # the register is a virtual one
$state = 32; # register represents a state
# NOTE: Last entry of each class is the largest Firm-Mode a register can hold # NOTE: Last entry of each class is the largest Firm-Mode a register can hold
%reg_classes = ( %reg_classes = (
gp => [ gp => [
{ name => "rax", type => $caller_save }, { name => "rax" },
{ name => "rcx", type => $caller_save }, { name => "rcx" },
{ name => "rdx", type => $caller_save }, { name => "rdx" },
{ name => "rsi", type => $caller_save }, { name => "rsi" },
{ name => "rdi", type => $caller_save }, { name => "rdi" },
{ name => "rbx", type => $callee_save }, { name => "rbx" },
{ name => "rbp", type => $callee_save }, { name => "rbp" },
{ name => "rsp", type => 4 }, # stackpointer? { name => "rsp", type => $ignore }, # stackpointer?
{ name => "r8", type => $caller_save }, { name => "r8" },
{ name => "r9", type => $caller_save }, { name => "r9" },
{ name => "r10", type => $caller_save }, { name => "r10" },
{ name => "r11", type => $caller_save }, { name => "r11" },
{ name => "r12", type => $callee_save }, { name => "r12" },
{ name => "r13", type => $callee_save }, { name => "r13" },
{ name => "r14", type => $callee_save }, { name => "r14" },
{ name => "r15", type => $callee_save }, { name => "r15" },
# { name => "gp_NOREG", type => $ignore }, # we need a dummy register for NoReg nodes # { name => "gp_NOREG", type => $ignore }, # we need a dummy register for NoReg nodes
{ mode => "mode_Lu" } { mode => "mode_Lu" }
], ],
# fp => [ # fp => [
# { name => "xmm0", type => $caller_save }, # { name => "xmm0" },
# { name => "xmm1", type => $caller_save }, # { name => "xmm1" },
# { name => "xmm2", type => $caller_save }, # { name => "xmm2" },
# { name => "xmm3", type => $caller_save }, # { name => "xmm3" },
# { name => "xmm4", type => $caller_save }, # { name => "xmm4" },
# { name => "xmm5", type => $caller_save }, # { name => "xmm5" },
# { name => "xmm6", type => $caller_save }, # { name => "xmm6" },
# { name => "xmm7", type => $caller_save }, # { name => "xmm7" },
# { name => "xmm8", type => $caller_save }, # { name => "xmm8" },
# { name => "xmm9", type => $caller_save }, # { name => "xmm9" },
# { name => "xmm10", type => $caller_save }, # { name => "xmm10" },
# { name => "xmm11", type => $caller_save }, # { name => "xmm11" },
# { name => "xmm12", type => $caller_save }, # { name => "xmm12" },
# { name => "xmm13", type => $caller_save }, # { name => "xmm13" },
# { name => "xmm14", type => $caller_save }, # { name => "xmm14" },
# { name => "xmm15", type => $caller_save }, # { name => "xmm15" },
# { mode => "mode_D" } # { mode => "mode_D" }
# ] # ]
flags => [ flags => [
{ name => "eflags", type => 0 }, { name => "eflags" },
{ mode => "mode_Iu", flags => "manual_ra" } { mode => "mode_Iu", flags => "manual_ra" }
], ],
); );
......
...@@ -531,6 +531,45 @@ static int amd64_is_valid_clobber(const char *clobber) ...@@ -531,6 +531,45 @@ static int amd64_is_valid_clobber(const char *clobber)
return 0; return 0;
} }
static int amd64_register_saved_by(const arch_register_t *reg, int callee)
{
if (callee) {
/* check for callee saved */
if (reg->reg_class == &amd64_reg_classes[CLASS_amd64_gp]) {
switch (reg->index) {
case REG_GP_RBX:
case REG_GP_RBP:
case REG_GP_R12:
case REG_GP_R13:
case REG_GP_R14:
case REG_GP_R15:
return 1;
default:
return 0;
}
}
} else {
/* check for caller saved */
if (reg->reg_class == &amd64_reg_classes[CLASS_amd64_gp]) {
switch (reg->index) {
case REG_GP_RAX:
case REG_GP_RCX:
case REG_GP_RDX:
case REG_GP_RSI:
case REG_GP_RDI:
case REG_GP_R8:
case REG_GP_R9:
case REG_GP_R10:
case REG_GP_R11:
return 1;
default:
return 0;
}
}
}
return 0;
}
const arch_isa_if_t amd64_isa_if = { const arch_isa_if_t amd64_isa_if = {
amd64_init, amd64_init,
amd64_lower_for_target, amd64_lower_for_target,
...@@ -553,6 +592,7 @@ const arch_isa_if_t amd64_isa_if = { ...@@ -553,6 +592,7 @@ const arch_isa_if_t amd64_isa_if = {
amd64_after_ra, amd64_after_ra,
amd64_finish_irg, amd64_finish_irg,
amd64_gen_routine, amd64_gen_routine,
amd64_register_saved_by,
}; };
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_amd64) BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_amd64)
......
...@@ -625,6 +625,7 @@ const arch_isa_if_t arm_isa_if = { ...@@ -625,6 +625,7 @@ const arch_isa_if_t arm_isa_if = {
arm_after_ra, arm_after_ra,
arm_finish_irg, arm_finish_irg,
arm_gen_routine, arm_gen_routine,
NULL, /* register_saved_by */
}; };
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_arm) BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_arm)
......
...@@ -536,7 +536,7 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp) ...@@ -536,7 +536,7 @@ static ir_node *adjust_call(be_abi_irg_t *env, ir_node *irn, ir_node *curr_sp)
* checking */ * checking */
continue; continue;
} }
if (destroy_all_regs || (reg->type & arch_register_type_caller_save)) { if (destroy_all_regs || arch_register_is_caller_save(arch_env, reg)) {
if (!(reg->type & arch_register_type_ignore)) { if (!(reg->type & arch_register_type_ignore)) {
ARR_APP1(const arch_register_t*, destroyed_regs, reg); ARR_APP1(const arch_register_t*, destroyed_regs, reg);
} }
...@@ -1274,7 +1274,7 @@ static ir_node *create_be_return(be_abi_irg_t *env, ir_node *irn, ir_node *bl, ...@@ -1274,7 +1274,7 @@ static ir_node *create_be_return(be_abi_irg_t *env, ir_node *irn, ir_node *bl,
/* Add uses of the callee save registers. */ /* Add uses of the callee save registers. */
foreach_pmap(env->regs, ent) { foreach_pmap(env->regs, ent) {
const arch_register_t *reg = (const arch_register_t*)ent->key; const arch_register_t *reg = (const arch_register_t*)ent->key;
if (reg->type & (arch_register_type_callee_save | arch_register_type_ignore)) if ((reg->type & arch_register_type_ignore) || arch_register_is_callee_save(arch_env, reg))
pmap_insert(reg_map, ent->key, ent->value); pmap_insert(reg_map, ent->key, ent->value);
} }
...@@ -1787,7 +1787,7 @@ static void modify_irg(ir_graph *irg) ...@@ -1787,7 +1787,7 @@ static void modify_irg(ir_graph *irg)
const arch_register_class_t *cls = &arch_env->register_classes[i]; const arch_register_class_t *cls = &arch_env->register_classes[i];
for (j = 0; j < cls->n_regs; ++j) { for (j = 0; j < cls->n_regs; ++j) {
const arch_register_t *reg = &cls->regs[j]; const arch_register_t *reg = &cls->regs[j];
if (reg->type & (arch_register_type_callee_save | arch_register_type_state)) { if ((reg->type & arch_register_type_state) || arch_register_is_callee_save(arch_env, reg)) {
pmap_insert(env->regs, (void *) reg, NULL); pmap_insert(env->regs, (void *) reg, NULL);
} }
} }
......
...@@ -58,24 +58,18 @@ ENUM_BITSET(arch_register_class_flags_t) ...@@ -58,24 +58,18 @@ ENUM_BITSET(arch_register_class_flags_t)
typedef enum arch_register_type_t { typedef enum arch_register_type_t {
arch_register_type_none = 0, arch_register_type_none = 0,
/** The register must be saved by the caller upon a function call. It thus
* can be overwritten in the called function. */
arch_register_type_caller_save = 1U << 0,
/** The register must be saved by the caller upon a function call. It thus
* can be overwritten in the called function. */
arch_register_type_callee_save = 1U << 1,
/** Do not consider this register when allocating. */ /** Do not consider this register when allocating. */
arch_register_type_ignore = 1U << 2, arch_register_type_ignore = 1U << 0,
/** The emitter can choose an arbitrary register. The register fulfills any /** The emitter can choose an arbitrary register. The register fulfills any
* register constraints as long as the register class matches */ * register constraints as long as the register class matches */
arch_register_type_joker = 1U << 3, arch_register_type_joker = 1U << 1,
/** This is just a virtual register. Virtual registers fulfill any register /** This is just a virtual register. Virtual registers fulfill any register
* constraints as long as the register class matches. It is a allowed to * constraints as long as the register class matches. It is a allowed to
* have multiple definitions for the same virtual register at a point */ * have multiple definitions for the same virtual register at a point */
arch_register_type_virtual = 1U << 4, arch_register_type_virtual = 1U << 2,
/** The register represents a state that should be handled by bestate /** The register represents a state that should be handled by bestate
* code */ * code */
arch_register_type_state = 1U << 5, arch_register_type_state = 1U << 3,
} arch_register_type_t; } arch_register_type_t;
ENUM_BITSET(arch_register_type_t) ENUM_BITSET(arch_register_type_t)
...@@ -588,6 +582,11 @@ struct arch_isa_if_t { ...@@ -588,6 +582,11 @@ struct arch_isa_if_t {
* The code generator must also be de-allocated here. * The code generator must also be de-allocated here.
*/ */
void (*emit)(ir_graph *irg); void (*emit)(ir_graph *irg);
/**
* Checks if the given register is callee/caller saved.
*/
int (*register_saved_by)(const arch_register_t *reg, int callee);
}; };
#define arch_env_done(env) ((env)->impl->done(env)) #define arch_env_done(env) ((env)->impl->done(env))
...@@ -728,6 +727,30 @@ static inline const arch_register_req_t **arch_get_in_register_reqs( ...@@ -728,6 +727,30 @@ static inline const arch_register_req_t **arch_get_in_register_reqs(
return info->in_reqs; return info->in_reqs;
} }
/**
* Check if the given register is callee save, ie. will be save by the callee.
*/
static inline bool arch_register_is_callee_save(
const arch_env_t *arch_env,
const arch_register_t *reg)
{
if (arch_env->impl->register_saved_by)
return arch_env->impl->register_saved_by(reg, /*callee=*/1);
return false;
}
/**
* Check if the given register is caller save, ie. must be save by the caller.
*/
static inline bool arch_register_is_caller_save(
const arch_env_t *arch_env,
const arch_register_t *reg)
{
if (arch_env->impl->register_saved_by)
return arch_env->impl->register_saved_by(reg, /*callee=*/0);
return false;
}
/** /**
* Iterate over all values defined by an instruction. * Iterate over all values defined by an instruction.
* Only looks at values in a certain register class where the requirements * Only looks at values in a certain register class where the requirements
......
...@@ -2118,6 +2118,46 @@ static const backend_params *ia32_get_libfirm_params(void) ...@@ -2118,6 +2118,46 @@ static const backend_params *ia32_get_libfirm_params(void)
return &p; return &p;
} }
/**
* Check if the given register is callee or caller save.
*/
static int ia32_register_saved_by(const arch_register_t *reg, int callee)
{
if (callee) {
/* check for callee saved */
if (reg->reg_class == &ia32_reg_classes[CLASS_ia32_gp]) {
switch (reg->index) {
case REG_GP_EBX:
case REG_GP_ESI:
case REG_GP_EDI:
case REG_GP_EBP:
return 1;
default:
return 0;
}
}
} else {
/* check for caller saved */
if (reg->reg_class == &ia32_reg_classes[CLASS_ia32_gp]) {
switch (reg->index) {
case REG_GP_EDX:
case REG_GP_ECX:
case REG_GP_EAX:
return 1;
default:
return 0;
}
} else if (reg->reg_class == &ia32_reg_classes[CLASS_ia32_xmm]) {
/* all XMM registers are caller save */
return reg->index != REG_XMM_NOREG;
} else if (reg->reg_class == &ia32_reg_classes[CLASS_ia32_vfp]) {
/* all VFP registers are caller save */
return reg->index != REG_VFP_NOREG;
}
}
return 0;
}
static const lc_opt_enum_int_items_t gas_items[] = { static const lc_opt_enum_int_items_t gas_items[] = {
{ "elf", OBJECT_FILE_FORMAT_ELF }, { "elf", OBJECT_FILE_FORMAT_ELF },
{ "mingw", OBJECT_FILE_FORMAT_COFF }, { "mingw", OBJECT_FILE_FORMAT_COFF },
...@@ -2175,6 +2215,7 @@ const arch_isa_if_t ia32_isa_if = { ...@@ -2175,6 +2215,7 @@ const arch_isa_if_t ia32_isa_if = {
ia32_after_ra, /* after register allocation hook */ ia32_after_ra, /* after register allocation hook */
ia32_finish, /* called before codegen */ ia32_finish, /* called before codegen */
ia32_emit, /* emit && done */ ia32_emit, /* emit && done */
ia32_register_saved_by,
}; };
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_ia32) BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_ia32)
......
...@@ -6,22 +6,20 @@ $arch = "ia32"; ...@@ -6,22 +6,20 @@ $arch = "ia32";
# register types: # register types:
$normal = 0; # no special type $normal = 0; # no special type
$caller_save = 1; # caller save (register must be saved by the caller of a function) $ignore = 1; # ignore (do not assign this register)
$callee_save = 2; # callee save (register must be saved by the called function) $arbitrary = 2; # emitter can choose an arbitrary register of this class
$ignore = 4; # ignore (do not assign this register) $virtual = 4; # the register is a virtual one
$arbitrary = 8; # emitter can choose an arbitrary register of this class $state = 8; # register represents a state
$virtual = 16; # the register is a virtual one
$state = 32; # register represents a state
# NOTE: Last entry of each class is the largest Firm-Mode a register can hold # NOTE: Last entry of each class is the largest Firm-Mode a register can hold
%reg_classes = ( %reg_classes = (
gp => [ gp => [
{ name => "edx", type => $caller_save }, { name => "edx" },
{ name => "ecx", type => $caller_save }, { name => "ecx" },
{ name => "eax", type => $caller_save }, { name => "eax" },
{ name => "ebx", type => $callee_save }, { name => "ebx" },
{ name => "esi", type => $callee_save }, { name => "esi" },
{ name => "edi", type => $callee_save }, { name => "edi" },
{ name => "ebp", type => $callee_save }, { name => "ebp" },
{ name => "esp", type => $ignore }, { name => "esp", type => $ignore },
{ name => "gp_NOREG", type => $ignore | $arbitrary | $virtual }, # we need a dummy register for NoReg nodes { name => "gp_NOREG", type => $ignore | $arbitrary | $virtual }, # we need a dummy register for NoReg nodes
{ mode => "mode_Iu" } { mode => "mode_Iu" }
...@@ -38,26 +36,26 @@ $state = 32; # register represents a state ...@@ -38,26 +36,26 @@ $state = 32; # register represents a state
{ mode => "mode_E", flags => "manual_ra" } { mode => "mode_E", flags => "manual_ra" }
], ],
xmm => [ xmm => [
{ name => "xmm0", type => $caller_save }, { name => "xmm0" },
{ name => "xmm1", type => $caller_save }, { name => "xmm1" },
{ name => "xmm2", type => $caller_save }, { name => "xmm2" },
{ name => "xmm3", type => $caller_save }, { name => "xmm3" },
{ name => "xmm4", type => $caller_save }, { name => "xmm4" },
{ name => "xmm5", type => $caller_save }, { name => "xmm5" },
{ name => "xmm6", type => $caller_save }, { name => "xmm6" },
{ name => "xmm7", type => $caller_save }, { name => "xmm7" },
{ name => "xmm_NOREG", type => $ignore | $virtual }, # we need a dummy register for NoReg nodes { name => "xmm_NOREG", type => $ignore | $virtual }, # we need a dummy register for NoReg nodes
{ mode => "mode_E" } { mode => "mode_E" }
], ],
vfp => [ vfp => [
{ name => "vf0", type => $caller_save },