#
# register types:
# 0 - no special type
-# 1 - caller save (register must be saved by the caller of a function)
-# 2 - callee save (register must be saved by the called function)
-# 4 - ignore (do not assign this register)
+# 1 - ignore (do not assign this register)
+# 2 - emitter can choose an arbitrary register of this class
+# 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
%reg_classes = (
gp => [
- { name => "r0", type => 1 },
- { name => "r1", type => 1 },
- { name => "r2", type => 1 },
- { name => "r3", type => 1 },
- { name => "r4", type => 1 },
- { name => "r5", type => 1 },
- { name => "r6", type => 1 },
- { name => "r7", type => 2 },
- { name => "r8", type => 2 },
- { name => "r9", type => 2 },
- { name => "r10", type => 2 },
- { name => "r11", type => 2 },
- { name => "r12", type => 2 },
- { name => "r13", type => 2 },
- { name => "sp", realname => "r14", type => 4 }, # stackpointer
- { name => "bp", realname => "r15", type => 4 }, # basepointer
+ { name => "r0" },
+ { name => "r1" },
+ { name => "r2" },
+ { name => "r3" },
+ { name => "r4" },
+ { name => "r5" },
+ { name => "r6" },
+ { name => "r7" },
+ { name => "r8" },
+ { name => "r9" },
+ { name => "r10" },
+ { name => "r11" },
+ { name => "r12" },
+ { name => "r13" },
+ { name => "sp", realname => "r14", type => 1 }, # stackpointer
+ { name => "bp", realname => "r15", type => 1 }, # basepointer
{ mode => $mode_gp }
],
fp => [
- { name => "f0", type => 1 },
- { name => "f1", type => 1 },
- { name => "f2", type => 1 },
- { name => "f3", type => 1 },
- { name => "f4", type => 1 },
- { name => "f5", type => 1 },
- { name => "f6", type => 1 },
- { name => "f7", type => 1 },
- { name => "f8", type => 1 },
- { name => "f9", type => 1 },
- { name => "f10", type => 1 },
- { name => "f11", type => 1 },
- { name => "f12", type => 1 },
- { name => "f13", type => 1 },
- { name => "f14", type => 1 },
- { name => "f15", type => 1 },
+ { name => "f0" },
+ { name => "f1" },
+ { name => "f2" },
+ { name => "f3" },
+ { name => "f4" },
+ { name => "f5" },
+ { name => "f6" },
+ { name => "f7" },
+ { name => "f8" },
+ { name => "f9" },
+ { name => "f10" },
+ { name => "f11" },
+ { name => "f12" },
+ { name => "f13" },
+ { name => "f14" },
+ { name => "f15" },
{ mode => $mode_fp }
]
);
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 = {
TEMPLATE_init,
TEMPLATE_lower_for_target,
TEMPLATE_after_ra,
TEMPLATE_finish_irg,
TEMPLATE_emit_routine,
+ TEMPLATE_register_saved_by,
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_TEMPLATE)
#
# register types:
$normal = 0; # no special type
-$caller_save = 1; # caller save (register must be saved by the caller of a function)
-$callee_save = 2; # callee save (register must be saved by the called function)
-$ignore = 4; # ignore (do not assign this register)
-$arbitrary = 8; # emitter can choose an arbitrary register of this class
-$virtual = 16; # the register is a virtual one
-$state = 32; # register represents a state
+$ignore = 1; # ignore (do not assign this register)
+$arbitrary = 2; # emitter can choose an arbitrary register of this class
+$virtual = 4; # the register is a virtual one
+$state = 8; # register represents a state
# NOTE: Last entry of each class is the largest Firm-Mode a register can hold
%reg_classes = (
gp => [
- { name => "rax", type => $caller_save },
- { name => "rcx", type => $caller_save },
- { name => "rdx", type => $caller_save },
- { name => "rsi", type => $caller_save },
- { name => "rdi", type => $caller_save },
- { name => "rbx", type => $callee_save },
- { name => "rbp", type => $callee_save },
- { name => "rsp", type => 4 }, # stackpointer?
- { name => "r8", type => $caller_save },
- { name => "r9", type => $caller_save },
- { name => "r10", type => $caller_save },
- { name => "r11", type => $caller_save },
- { name => "r12", type => $callee_save },
- { name => "r13", type => $callee_save },
- { name => "r14", type => $callee_save },
- { name => "r15", type => $callee_save },
+ { name => "rax" },
+ { name => "rcx" },
+ { name => "rdx" },
+ { name => "rsi" },
+ { name => "rdi" },
+ { name => "rbx" },
+ { name => "rbp" },
+ { name => "rsp", type => $ignore }, # stackpointer?
+ { name => "r8" },
+ { name => "r9" },
+ { name => "r10" },
+ { name => "r11" },
+ { name => "r12" },
+ { name => "r13" },
+ { name => "r14" },
+ { name => "r15" },
# { name => "gp_NOREG", type => $ignore }, # we need a dummy register for NoReg nodes
{ mode => "mode_Lu" }
],
# fp => [
-# { name => "xmm0", type => $caller_save },
-# { name => "xmm1", type => $caller_save },
-# { name => "xmm2", type => $caller_save },
-# { name => "xmm3", type => $caller_save },
-# { name => "xmm4", type => $caller_save },
-# { name => "xmm5", type => $caller_save },
-# { name => "xmm6", type => $caller_save },
-# { name => "xmm7", type => $caller_save },
-# { name => "xmm8", type => $caller_save },
-# { name => "xmm9", type => $caller_save },
-# { name => "xmm10", type => $caller_save },
-# { name => "xmm11", type => $caller_save },
-# { name => "xmm12", type => $caller_save },
-# { name => "xmm13", type => $caller_save },
-# { name => "xmm14", type => $caller_save },
-# { name => "xmm15", type => $caller_save },
+# { name => "xmm0" },
+# { name => "xmm1" },
+# { name => "xmm2" },
+# { name => "xmm3" },
+# { name => "xmm4" },
+# { name => "xmm5" },
+# { name => "xmm6" },
+# { name => "xmm7" },
+# { name => "xmm8" },
+# { name => "xmm9" },
+# { name => "xmm10" },
+# { name => "xmm11" },
+# { name => "xmm12" },
+# { name => "xmm13" },
+# { name => "xmm14" },
+# { name => "xmm15" },
# { mode => "mode_D" }
# ]
flags => [
- { name => "eflags", type => 0 },
+ { name => "eflags" },
{ mode => "mode_Iu", flags => "manual_ra" }
],
);
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 = {
amd64_init,
amd64_lower_for_target,
amd64_after_ra,
amd64_finish_irg,
amd64_gen_routine,
+ amd64_register_saved_by,
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_amd64)
arm_after_ra,
arm_finish_irg,
arm_gen_routine,
+ NULL, /* register_saved_by */
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_arm)
* checking */
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)) {
ARR_APP1(const arch_register_t*, destroyed_regs, reg);
}
/* Add uses of the callee save registers. */
foreach_pmap(env->regs, ent) {
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);
}
const arch_register_class_t *cls = &arch_env->register_classes[i];
for (j = 0; j < cls->n_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);
}
}
typedef enum arch_register_type_t {
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. */
- arch_register_type_ignore = 1U << 2,
+ arch_register_type_ignore = 1U << 0,
/** The emitter can choose an arbitrary register. The register fulfills any
* 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
* constraints as long as the register class matches. It is a allowed to
* 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
* code */
- arch_register_type_state = 1U << 5,
+ arch_register_type_state = 1U << 3,
} arch_register_type_t;
ENUM_BITSET(arch_register_type_t)
* The code generator must also be de-allocated here.
*/
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))
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.
* Only looks at values in a certain register class where the requirements
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[] = {
{ "elf", OBJECT_FILE_FORMAT_ELF },
{ "mingw", OBJECT_FILE_FORMAT_COFF },
ia32_after_ra, /* after register allocation hook */
ia32_finish, /* called before codegen */
ia32_emit, /* emit && done */
+ ia32_register_saved_by,
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_ia32)
# register types:
$normal = 0; # no special type
-$caller_save = 1; # caller save (register must be saved by the caller of a function)
-$callee_save = 2; # callee save (register must be saved by the called function)
-$ignore = 4; # ignore (do not assign this register)
-$arbitrary = 8; # emitter can choose an arbitrary register of this class
-$virtual = 16; # the register is a virtual one
-$state = 32; # register represents a state
+$ignore = 1; # ignore (do not assign this register)
+$arbitrary = 2; # emitter can choose an arbitrary register of this class
+$virtual = 4; # the register is a virtual one
+$state = 8; # register represents a state
# NOTE: Last entry of each class is the largest Firm-Mode a register can hold
%reg_classes = (
gp => [
- { name => "edx", type => $caller_save },
- { name => "ecx", type => $caller_save },
- { name => "eax", type => $caller_save },
- { name => "ebx", type => $callee_save },
- { name => "esi", type => $callee_save },
- { name => "edi", type => $callee_save },
- { name => "ebp", type => $callee_save },
+ { name => "edx" },
+ { name => "ecx" },
+ { name => "eax" },
+ { name => "ebx" },
+ { name => "esi" },
+ { name => "edi" },
+ { name => "ebp" },
{ name => "esp", type => $ignore },
{ name => "gp_NOREG", type => $ignore | $arbitrary | $virtual }, # we need a dummy register for NoReg nodes
{ mode => "mode_Iu" }
{ mode => "mode_E", flags => "manual_ra" }
],
xmm => [
- { name => "xmm0", type => $caller_save },
- { name => "xmm1", type => $caller_save },
- { name => "xmm2", type => $caller_save },
- { name => "xmm3", type => $caller_save },
- { name => "xmm4", type => $caller_save },
- { name => "xmm5", type => $caller_save },
- { name => "xmm6", type => $caller_save },
- { name => "xmm7", type => $caller_save },
+ { name => "xmm0" },
+ { name => "xmm1" },
+ { name => "xmm2" },
+ { name => "xmm3" },
+ { name => "xmm4" },
+ { name => "xmm5" },
+ { name => "xmm6" },
+ { name => "xmm7" },
{ name => "xmm_NOREG", type => $ignore | $virtual }, # we need a dummy register for NoReg nodes
{ mode => "mode_E" }
],
vfp => [
- { name => "vf0", type => $caller_save },
- { name => "vf1", type => $caller_save },
- { name => "vf2", type => $caller_save },
- { name => "vf3", type => $caller_save },
- { name => "vf4", type => $caller_save },
- { name => "vf5", type => $caller_save },
- { name => "vf6", type => $caller_save },
- { name => "vf7", type => $caller_save },
+ { name => "vf0" },
+ { name => "vf1" },
+ { name => "vf2" },
+ { name => "vf3" },
+ { name => "vf4" },
+ { name => "vf5" },
+ { name => "vf6" },
+ { name => "vf7" },
{ name => "vfp_NOREG", type => $ignore | $arbitrary | $virtual }, # we need a dummy register for NoReg nodes
{ mode => "mode_E" }
],
my @types;
if ($t & 1) {
- push(@types, "arch_register_type_caller_save");
- }
-
- if ($t & 2) {
- push(@types, "arch_register_type_callee_save");
- }
-
- if ($t & 4) {
push(@types, "arch_register_type_ignore");
}
- if ($t & 8) {
+ if ($t & 2) {
push(@types, "arch_register_type_joker");
}
- if ($t & 16) {
+ if ($t & 4) {
push(@types, "arch_register_type_virtual");
}
- if ($t & 32) {
+ if ($t & 8) {
push(@types, "arch_register_type_state");
}
sparc_after_ra,
sparc_finish,
sparc_emit_routine,
+ NULL, /* register_saved_by */
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_sparc);