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

handle arm ABI in arm transform phase

[r27726]
parent b4bdc4fe
/*
* Copyright (C) 1995-2008 University of Karlsruhe. All right reserved.
*
* This file is part of libFirm.
*
* This file may be distributed and/or modified under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation and appearing in the file LICENSE.GPL included in the
* packaging of this file.
*
* Licensees holding valid libFirm Professional Edition licenses may use
* this file in accordance with the libFirm Commercial License.
* Agreement provided with the Software.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE.
*/
/**
* @file
* @brief calling convention helpers
* @author Matthias Braun
* @version $Id$
*/
#include "config.h"
#include "arm_cconv.h"
#include "irmode.h"
#include "typerep.h"
#include "xmalloc.h"
#include "error.h"
calling_convention_t *decide_calling_convention(ir_type *function_type)
{
int stack_offset = 0;
reg_or_stackslot_t *params;
reg_or_stackslot_t *results;
int n_param_regs
= sizeof(param_regs)/sizeof(param_regs[0]);
int n_result_regs
= sizeof(result_regs)/sizeof(result_regs[0]);
int n_params;
int n_results;
int i;
int regnum;
calling_convention_t *cconv;
/* determine how parameters are passed */
n_params = get_method_n_params(function_type);
regnum = 0;
params = XMALLOCNZ(reg_or_stackslot_t, n_params);
for (i = 0; i < n_params; ++i) {
ir_type *param_type = get_method_param_type(function_type,i);
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->reg0 = reg;
} else {
param->type = param_type;
param->offset = stack_offset;
/* increase offset 4 bytes so everything is aligned */
stack_offset += 4;
continue;
}
/* we might need a 2nd 32bit component (for 64bit or double values) */
if (bits > 32) {
if (bits > 64)
panic("only 32 and 64bit modes supported in arm backend");
if (regnum < n_param_regs) {
const arch_register_t *reg = param_regs[regnum++];
param->reg1 = reg;
} else {
ir_mode *mode = param_regs[0]->reg_class->mode;
ir_type *type = get_type_for_mode(mode);
param->type = type;
param->offset = stack_offset;
stack_offset += 4;
}
}
}
n_results = get_method_n_ress(function_type);
regnum = 0;
results = XMALLOCNZ(reg_or_stackslot_t, n_results);
for (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 (get_mode_size_bits(result_mode) > 32) {
panic("Results with more than 32bits not supported by arm backend yet");
}
if (regnum >= n_result_regs) {
panic("Too many results for arm backend");
} else {
const arch_register_t *reg = result_regs[regnum++];
result->reg0 = reg;
}
}
cconv = XMALLOCZ(calling_convention_t);
cconv->parameters = params;
cconv->param_stack_size = stack_offset;
cconv->results = results;
return cconv;
}
void free_calling_convention(calling_convention_t *cconv)
{
free(cconv->parameters);
free(cconv->results);
free(cconv);
}
/*
* Copyright (C) 1995-2008 University of Karlsruhe. All right reserved.
*
* This file is part of libFirm.
*
* This file may be distributed and/or modified under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation and appearing in the file LICENSE.GPL included in the
* packaging of this file.
*
* Licensees holding valid libFirm Professional Edition licenses may use
* this file in accordance with the libFirm Commercial License.
* Agreement provided with the Software.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE.
*/
/**
* @file
* @brief support functions for calling conventions
* @author Matthias Braun
* @version $Id$
*/
#ifndef FIRM_BE_ARM_ARM_CCONV_H
#define FIRM_BE_ARM_ARM_CCONV_H
#include "firm_types.h"
#include "../be_types.h"
#include "gen_arm_regalloc_if.h"
static const arch_register_t *const callee_saves[] = {
&arm_gp_regs[REG_R4],
&arm_gp_regs[REG_R5],
&arm_gp_regs[REG_R6],
&arm_gp_regs[REG_R7],
&arm_gp_regs[REG_R8],
&arm_gp_regs[REG_R9],
&arm_gp_regs[REG_R10],
&arm_gp_regs[REG_R11],
&arm_gp_regs[REG_LR],
};
static const arch_register_t *const caller_saves[] = {
&arm_gp_regs[REG_R0],
&arm_gp_regs[REG_R1],
&arm_gp_regs[REG_R2],
&arm_gp_regs[REG_R3],
&arm_gp_regs[REG_LR]
};
static const arch_register_t* const param_regs[] = {
&arm_gp_regs[REG_R0],
&arm_gp_regs[REG_R1],
&arm_gp_regs[REG_R2],
&arm_gp_regs[REG_R3]
};
static const arch_register_t* const result_regs[] = {
&arm_gp_regs[REG_R0],
&arm_gp_regs[REG_R1],
&arm_gp_regs[REG_R2],
&arm_gp_regs[REG_R3]
};
/** information about a single parameter or result */
typedef struct reg_or_stackslot_t
{
const arch_register_t *reg0;
const arch_register_t *reg1;
ir_type *type; /**< indicates that an entity of the specific
type is needed */
int offset;
ir_entity *entity; /**< entity in frame type */
} reg_or_stackslot_t;
typedef struct calling_convention_t
{
reg_or_stackslot_t *parameters;
int param_stack_size;
reg_or_stackslot_t *results;
} calling_convention_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.
*/
calling_convention_t *decide_calling_convention(ir_type *function_type);
/**
* free memory used by a calling_convention_t
*/
void free_calling_convention(calling_convention_t *cconv);
#endif
......@@ -67,12 +67,12 @@ const char *arm_get_fpa_imm_name(long imm_value)
static bool arm_has_symconst_attr(const ir_node *node)
{
return is_arm_SymConst(node) || is_arm_FrameAddr(node);
return is_arm_SymConst(node) || is_arm_FrameAddr(node) || is_arm_Bl(node);
}
static bool has_load_store_attr(const ir_node *node)
{
return is_arm_Ldr(node) || is_arm_Str(node);
return is_arm_Ldr(node) || is_arm_Str(node) || is_arm_LinkLdrPC(node);
}
static bool has_shifter_operand(const ir_node *node)
......@@ -80,7 +80,7 @@ static bool has_shifter_operand(const ir_node *node)
return is_arm_Add(node) || is_arm_And(node) || is_arm_Or(node)
|| is_arm_Eor(node) || is_arm_Bic(node) || is_arm_Sub(node)
|| is_arm_Rsb(node) || is_arm_Mov(node) || is_arm_Mvn(node)
|| is_arm_Cmp(node) || is_arm_Tst(node);
|| is_arm_Cmp(node) || is_arm_Tst(node) || is_arm_LinkMovPC(node);
}
static bool has_cmp_attr(const ir_node *node)
......@@ -215,7 +215,7 @@ const arm_attr_t *get_arm_attr_const(const ir_node *node)
static bool has_symconst_attr(const ir_node *node)
{
return is_arm_SymConst(node) || is_arm_FrameAddr(node);
return is_arm_SymConst(node) || is_arm_FrameAddr(node) || is_arm_Bl(node);
}
arm_SymConst_attr_t *get_arm_SymConst_attr(ir_node *node)
......
......@@ -309,6 +309,46 @@ Abs => {
mode => $mode_gp,
},
# mov lr, pc\n mov pc, XXX -- This combination is used for calls to function
# pointers
LinkMovPC => {
state => "exc_pinned",
arity => "variable",
out_arity => "variable",
attr_type => "arm_shifter_operand_t",
attr => "arm_shift_modifier_t shift_modifier, unsigned char immediate_value, unsigned char immediate_rot",
custominit => "init_arm_shifter_operand(res, immediate_value, shift_modifier, immediate_rot);\n".
"\tarch_irn_add_flags(res, arch_irn_flags_modify_flags);",
emit => ". mov lr, pc\n".
". mov pc, %SO",
mode => "mode_T",
},
# mov lr, pc\n ldr pc, XXX -- This combination is used for calls to function
# pointers
LinkLdrPC => {
state => "exc_pinned",
arity => "variable",
out_arity => "variable",
attr_type => "arm_load_store_attr_t",
attr => "ir_mode *ls_mode, ir_entity *entity, int entity_sign, long offset, bool is_frame_entity",
custominit => "arch_irn_add_flags(res, arch_irn_flags_modify_flags);",
emit => ". mov lr, pc\n".
". ldr pc, %SO",
mode => "mode_T",
},
Bl => {
state => "exc_pinned",
arity => "variable",
out_arity => "variable",
attr_type => "arm_SymConst_attr_t",
attr => "ir_entity *entity, int symconst_offset",
custominit => "arch_irn_add_flags(res, arch_irn_flags_modify_flags);",
emit => '. bl %SC',
mode => "mode_T",
},
# this node produces ALWAYS an empty (tempary) gp reg and cannot be CSE'd
EmptyReg => {
op_flags => "c",
......@@ -539,44 +579,24 @@ fpaFix => {
emit => '. fix %D0, %S0',
},
fpaCmfBra => {
op_flags => "L|X|Y",
state => "pinned",
mode => "mode_T",
attr => "pn_Cmp pnc",
init_attr => "\tset_arm_CondJmp_pnc(res, pnc);",
reg_req => { in => [ "fpa", "fpa" ], out => [ "none", "none"] },
attr_type => "arm_CondJmp_attr_t",
},
fpaCnfBra => {
op_flags => "L|X|Y",
state => "pinned",
mode => "mode_T",
attr => "int pnc",
init_attr => "\tset_arm_CondJmp_pnc(res, pnc);",
reg_req => { in => [ "fpa", "fpa" ], out => [ "none", "none"] },
attr_type => "arm_CondJmp_attr_t",
},
fpaCmfeBra => {
op_flags => "L|X|Y",
state => "pinned",
mode => "mode_T",
attr => "int pnc",
init_attr => "\tset_arm_CondJmp_pnc(res, pnc);",
reg_req => { in => [ "fpa", "fpa" ], out => [ "none", "none"] },
attr_type => "arm_CondJmp_attr_t",
},
fpaCnfeBra => {
op_flags => "L|X|Y",
state => "pinned",
mode => "mode_T",
attr => "int pnc",
init_attr => "\tset_arm_CondJmp_pnc(res, pnc);",
reg_req => { in => [ "fpa", "fpa" ], out => [ "none", "none"] },
attr_type => "arm_CondJmp_attr_t",
Cmf => {
irn_flags => "R|F",
mode => $mode_flags,
attr_type => "arm_cmp_attr_t",
attr => "bool ins_permuted",
init_attr => "init_arm_cmp_attr(res, ins_permuted, false);",
reg_req => { in => [ "fpa", "fpa" ], out => [ "flags" ] },
emit => '. cmf %S0, %S1',
},
Cmfe => {
irn_flags => "R|F",
mode => $mode_flags,
attr_type => "arm_cmp_attr_t",
attr => "bool ins_permuted",
init_attr => "init_arm_cmp_attr(res, ins_permuted, false);",
reg_req => { in => [ "fpa", "fpa" ], out => [ "flags" ] },
emit => '. cmfe %S0, %S1',
},
fpaLdf => {
......
This diff is collapsed.
......@@ -72,6 +72,7 @@
static arch_irn_class_t arm_classify(const ir_node *irn)
{
(void) irn;
/* TODO: we should mark reload/spill instructions and classify them here */
return 0;
}
......@@ -119,7 +120,7 @@ static void arm_set_stack_bias(ir_node *irn, int bias)
static int arm_get_sp_bias(const ir_node *irn)
{
/* We don't have any nodes changing the stack pointer.
TODO: we probably want to support post-/pre increment/decrement later */
We probably want to support post-/pre increment/decrement later */
(void) irn;
return 0;
}
......@@ -200,10 +201,9 @@ static void arm_before_ra(void *self)
static void transform_Reload(ir_node *node)
{
ir_graph *irg = get_irn_irg(node);
ir_node *block = get_nodes_block(node);
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *ptr = get_irg_frame(irg);
ir_node *ptr = get_irn_n(node, be_pos_Reload_frame);
ir_node *mem = get_irn_n(node, be_pos_Reload_mem);
ir_mode *mode = get_irn_mode(node);
ir_entity *entity = be_get_frame_entity(node);
......@@ -227,10 +227,9 @@ static void transform_Reload(ir_node *node)
static void transform_Spill(ir_node *node)
{
ir_graph *irg = get_irn_irg(node);
ir_node *block = get_nodes_block(node);
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *ptr = get_irg_frame(irg);
ir_node *ptr = get_irn_n(node, be_pos_Spill_frame);
ir_node *mem = new_NoMem();
ir_node *val = get_irn_n(node, be_pos_Spill_val);
ir_mode *mode = get_irn_mode(val);
......@@ -288,205 +287,13 @@ static void arm_emit_and_done(void *self)
free(self);
}
/**
* Move a double floating point value into an integer register.
* Place the move operation into block bl.
*
* Handle some special cases here:
* 1.) A constant: simply split into two
* 2.) A load: simply split into two
*/
static ir_node *convert_dbl_to_int(ir_node *bl, ir_node *arg, ir_node *mem,
ir_node **resH, ir_node **resL)
{
if (is_Const(arg)) {
tarval *tv = get_Const_tarval(arg);
unsigned v;
/* get the upper 32 bits */
v = get_tarval_sub_bits(tv, 7);
v = (v << 8) | get_tarval_sub_bits(tv, 6);
v = (v << 8) | get_tarval_sub_bits(tv, 5);
v = (v << 8) | get_tarval_sub_bits(tv, 4);
*resH = new_Const_long(mode_Is, v);
/* get the lower 32 bits */
v = get_tarval_sub_bits(tv, 3);
v = (v << 8) | get_tarval_sub_bits(tv, 2);
v = (v << 8) | get_tarval_sub_bits(tv, 1);
v = (v << 8) | get_tarval_sub_bits(tv, 0);
*resL = new_Const_long(mode_Is, v);
} else if (is_Load(skip_Proj(arg))) {
/* FIXME: handling of low/high depends on LE/BE here */
panic("Unimplemented convert_dbl_to_int() case");
}
else {
ir_node *conv;
conv = new_bd_arm_fpaDbl2GP(NULL, bl, arg, mem);
/* move high/low */
*resL = new_r_Proj(conv, mode_Is, pn_arm_fpaDbl2GP_low);
*resH = new_r_Proj(conv, mode_Is, pn_arm_fpaDbl2GP_high);
mem = new_r_Proj(conv, mode_M, pn_arm_fpaDbl2GP_M);
}
return mem;
}
/**
* Move a single floating point value into an integer register.
* Place the move operation into block bl.
*
* Handle some special cases here:
* 1.) A constant: simply move
* 2.) A load: simply load
*/
static ir_node *convert_sng_to_int(ir_node *bl, ir_node *arg)
{
(void) bl;
if (is_Const(arg)) {
tarval *tv = get_Const_tarval(arg);
unsigned v;
/* get the lower 32 bits */
v = get_tarval_sub_bits(tv, 3);
v = (v << 8) | get_tarval_sub_bits(tv, 2);
v = (v << 8) | get_tarval_sub_bits(tv, 1);
v = (v << 8) | get_tarval_sub_bits(tv, 0);
return new_Const_long(mode_Is, v);
}
panic("Unimplemented convert_sng_to_int() case");
}
/**
* Convert the arguments of a call to support the
* ARM calling convention of general purpose AND floating
* point arguments.
*/
static void handle_calls(ir_node *call, void *env)
{
arm_code_gen_t *cg = env;
int i, j, n, size, idx, flag, n_param, n_res, first_variadic;
ir_type *mtp, *new_mtd, *new_tp[5];
ir_node *new_in[5], **in;
ir_node *bl;
if (! is_Call(call))
return;
/* check, if we need conversions */
n = get_Call_n_params(call);
mtp = get_Call_type(call);
assert(get_method_n_params(mtp) == n);
/* it's always enough to handle the first 4 parameters */
if (n > 4)
n = 4;
flag = size = idx = 0;
bl = get_nodes_block(call);
for (i = 0; i < n; ++i) {
ir_type *param_tp = get_method_param_type(mtp, i);
if (is_compound_type(param_tp)) {
/* an aggregate parameter: bad case */
assert(0);
}
else {
/* a primitive parameter */
ir_mode *mode = get_type_mode(param_tp);
if (mode_is_float(mode)) {
if (get_mode_size_bits(mode) > 32) {
ir_node *mem = get_Call_mem(call);
/* Beware: ARM wants the high part first */
size += 2 * 4;
new_tp[idx] = cg->int_tp;
new_tp[idx+1] = cg->int_tp;
mem = convert_dbl_to_int(bl, get_Call_param(call, i), mem, &new_in[idx], &new_in[idx+1]);
idx += 2;
set_Call_mem(call, mem);
}
else {
size += 4;
new_tp[idx] = cg->int_tp;
new_in[idx] = convert_sng_to_int(bl, get_Call_param(call, i));
++idx;
}
flag = 1;
}
else {
size += 4;
new_tp[idx] = param_tp;
new_in[idx] = get_Call_param(call, i);
++idx;
}
}
if (size >= 16)
break;
}
/* if flag is NOT set, no need to translate the method type */
if (! flag)
return;
/* construct a new method type */
n = i;
n_param = get_method_n_params(mtp) - n + idx;
n_res = get_method_n_ress(mtp);
new_mtd = new_d_type_method(n_param, n_res, get_type_dbg_info(mtp));
for (i = 0; i < idx; ++i)
set_method_param_type(new_mtd, i, new_tp[i]);
for (i = n, j = idx; i < get_method_n_params(mtp); ++i)
set_method_param_type(new_mtd, j++, get_method_param_type(mtp, i));
for (i = 0; i < n_res; ++i)
set_method_res_type(new_mtd, i, get_method_res_type(mtp, i));
set_method_calling_convention(new_mtd, get_method_calling_convention(mtp));
first_variadic = get_method_first_variadic_param_index(mtp);
if (first_variadic >= 0)
set_method_first_variadic_param_index(new_mtd, first_variadic);
if (is_lowered_type(mtp)) {
mtp = get_associated_type(mtp);
}
set_lowered_type(mtp, new_mtd);
set_Call_type(call, new_mtd);
/* calculate new in array of the Call */
NEW_ARR_A(ir_node *, in, n_param + 2);
for (i = 0; i < idx; ++i)
in[2 + i] = new_in[i];
for (i = n, j = idx; i < get_method_n_params(mtp); ++i)
in[2 + j++] = get_Call_param(call, i);
in[0] = get_Call_mem(call);
in[1] = get_Call_ptr(call);
/* finally, change the call inputs */
set_irn_in(call, n_param + 2, in);
}
/**
* Handle graph transformations before the abi converter does its work.
*/
static void arm_before_abi(void *self)
{
arm_code_gen_t *cg = self;
irg_walk_graph(cg->irg, NULL, handle_calls, cg);
}
/* forward */
static void *arm_cg_init(ir_graph *irg);
static const arch_code_generator_if_t arm_code_gen_if = {
arm_cg_init,
NULL, /* get_pic_base */
arm_before_abi, /* before abi introduce */
NULL, /* before abi introduce */
arm_prepare_graph,
NULL, /* spill */
arm_before_ra, /* before register allocation hook */
......@@ -667,7 +474,7 @@ static arm_isa_t arm_isa_template = {
NULL, /* main environment */
7, /* spill costs */
5, /* reload costs */
false, /* no custom abi handling */
true, /* we do have custo