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 => {
......
......@@ -20,7 +20,7 @@
/**
* @file
* @brief The codegenerator (transform FIRM into arm FIRM)
* @author Oliver Richter, Tobias Gneist, Michael Beck
* @author Matthias Braun, Oliver Richter, Tobias Gneist, Michael Beck
* @version $Id$
*/
#include "config.h"
......@@ -42,13 +42,16 @@
#include "../beirg.h"
#include "../beutil.h"
#include "../betranshlp.h"
#include "bearch_arm_t.h"
#include "../beabihelper.h"
#include "../beabi.h"
#include "bearch_arm_t.h"
#include "arm_nodes_attr.h"
#include "arm_transform.h"
#include "arm_optimize.h"
#include "arm_new_nodes.h"
#include "arm_map_regs.h"
#include "arm_cconv.h"
#include "gen_arm_regalloc_if.h"
......@@ -59,13 +62,20 @@ DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
/** hold the current code generator during transformation */
static arm_code_gen_t *env_cg;
static inline int mode_needs_gp_reg(ir_mode *mode)
static const arch_register_t *sp_reg = &arm_gp_regs[REG_SP];
static ir_mode *mode_gp;
static beabi_helper_env_t *abihelper;
static calling_convention_t *cconv = NULL;
static pmap *node_to_stack;
static bool mode_needs_gp_reg(ir_mode *mode)
{
return mode_is_int(mode) || mode_is_reference(mode);
}
/**
* Creates a possible DAG for an constant.
* create firm graph for a constant
*/
static ir_node *create_const_graph_value(dbg_info *dbgi, ir_node *block,
unsigned int value)
......@@ -74,6 +84,12 @@ static ir_node *create_const_graph_value(dbg_info *dbgi, ir_node *block,
arm_vals v, vn;
int cnt;
/* We only have 8 bit immediates. So we possibly have to combine several
* operations to construct the desired value.
*
* we can either create the value by adding bits to 0 or by removing bits
* from an register with all bits set. Try which alternative needs fewer
* operations */
arm_gen_vals_from_word(value, &v);
arm_gen_vals_from_word(~value, &vn);
......@@ -206,13 +222,11 @@ static ir_node *gen_Conv(ir_node *node)
if (mode_is_float(dst_mode)) {
/* from float to float */
return new_bd_arm_fpaMvf(dbg, block, new_op, dst_mode);
}
else {
} else {
/* from float to int */
return new_bd_arm_fpaFix(dbg, block, new_op, dst_mode);
}
}
else {
} else {
/* from int to float */
return new_bd_arm_fpaFlt(dbg, block, new_op, dst_mode);
}
......@@ -401,8 +415,7 @@ static ir_node *gen_Add(ir_node *node)
} else if (USE_VFP(env_cg->isa)) {
assert(mode != mode_E && "IEEE Extended FP not supported");
panic("VFP not supported yet");
}
else {
} else {
panic("Softfloat not supported yet");
}
} else {
......@@ -454,12 +467,10 @@ static ir_node *gen_Mul(ir_node *node)
return new_bd_arm_fpaMuf_i(dbg, block, new_op1, mode, get_arm_imm_value(new_op2));
#endif
return new_bd_arm_fpaMuf(dbg, block, new_op1, new_op2, mode);
}
else if (USE_VFP(env_cg->isa)) {
} else if (USE_VFP(env_cg->isa)) {
assert(mode != mode_E && "IEEE Extended FP not supported");
panic("VFP not supported yet");
}
else {
} else {
panic("Softfloat not supported yet");
}
}
......@@ -467,12 +478,6 @@ static ir_node *gen_Mul(ir_node *node)
return new_bd_arm_Mul(dbg, block, new_op1, new_op2);
}
/**
* Creates an ARM floating point Div.
*
* @param env The transformation environment
* @return the created arm fDiv node
*/
static ir_node *gen_Quot(ir_node *node)
{
ir_node *block = be_transform_node(get_nodes_block(node));
......@@ -497,51 +502,29 @@ static ir_node *gen_Quot(ir_node *node)
} else if (USE_VFP(env_cg->isa)) {
assert(mode != mode_E && "IEEE Extended FP not supported");
panic("VFP not supported yet");
}
else {
} else {
panic("Softfloat not supported yet");
}
}
/**
* Creates an ARM And.
*
* @return the created arm And node
*/
static ir_node *gen_And(ir_node *node)
{
return gen_int_binop(node, MATCH_COMMUTATIVE | MATCH_SIZE_NEUTRAL,
new_bd_arm_And_reg, new_bd_arm_And_imm);
}
/**
* Creates an ARM Orr.
*
* @param env The transformation environment
* @return the created arm Or node
*/
static ir_node *gen_Or(ir_node *node)
{
return gen_int_binop(node, MATCH_COMMUTATIVE | MATCH_SIZE_NEUTRAL,
new_bd_arm_Or_reg, new_bd_arm_Or_imm);
}
/**
* Creates an ARM Eor.
*
* @return the created arm Eor node
*/
static ir_node *gen_Eor(ir_node *node)
{
return gen_int_binop(node, MATCH_COMMUTATIVE | MATCH_SIZE_NEUTRAL,
new_bd_arm_Eor_reg, new_bd_arm_Eor_imm);
}
/**
* Creates an ARM Sub.
*
* @return the created arm Sub node
*/
static ir_node *gen_Sub(ir_node *node)
{
ir_node *block = be_transform_node(get_nodes_block(node));
......@@ -622,41 +605,21 @@ static ir_node *make_shift(ir_node *node, match_flags_t flags,
shift_modifier);
}
/**
* Creates an ARM Shl.
*
* @return the created ARM Shl node
*/
static ir_node *gen_Shl(ir_node *node)
{
return make_shift(node, MATCH_SIZE_NEUTRAL, ARM_SHF_LSL_REG);
}
/**
* Creates an ARM Shr.
*
* @return the created ARM Shr node
*/
static ir_node *gen_Shr(ir_node *node)
{
return make_shift(node, MATCH_NONE, ARM_SHF_LSR_REG);
}
/**
* Creates an ARM Shrs.
*
* @return the created ARM Shrs node
*/
static ir_node *gen_Shrs(ir_node *node)
{
return make_shift(node, MATCH_NONE, ARM_SHF_ASR_REG);
}
/**
* Creates an ARM Ror.
*
* @return the created ARM Ror node
*/
static ir_node *gen_Ror(ir_node *node, ir_node *op1, ir_node *op2)
{
ir_node *block = be_transform_node(get_nodes_block(node));
......@@ -668,13 +631,6 @@ static ir_node *gen_Ror(ir_node *node, ir_node *op1, ir_node *op2)
ARM_SHF_ROR_REG);
}
/**
* Creates an ARM Rol.
*
* @return the created ARM Rol node
*
* Note: there is no Rol on arm, we have to use Ror
*/
static ir_node *gen_Rol(ir_node *node, ir_node *op1, ir_node *op2)
{
ir_node *block = be_transform_node(get_nodes_block(node));
......@@ -682,16 +638,12 @@ static ir_node *gen_Rol(ir_node *node, ir_node *op1, ir_node *op2)
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *new_op2 = be_transform_node(op2);
/* Note: there is no Rol on arm, we have to use Ror */
new_op2 = new_bd_arm_Rsb_imm(dbgi, block, new_op2, 32, 0);
return new_bd_arm_Mov_reg_shift_reg(dbgi, block, new_op1, new_op2,
ARM_SHF_ROR_REG);
}
/**
* Creates an ARM ROR from a Firm Rotl.
*
* @return the created ARM Ror node
*/
static ir_node *gen_Rotl(ir_node *node)
{
ir_node *rotate = NULL;
......@@ -751,11 +703,6 @@ static ir_node *gen_Rotl(ir_node *node)
return rotate;
}
/**
* Transforms a Not node.
*
* @return the created ARM Not node
*/
static ir_node *gen_Not(ir_node *node)
{
ir_node *block = be_transform_node(get_nodes_block(node));
......@@ -768,12 +715,6 @@ static ir_node *gen_Not(ir_node *node)
return new_bd_arm_Mvn_reg(dbgi, block, new_op);
}
/**
* Transforms an Abs node.
*
* @param env The transformation environment
* @return the created ARM Abs node
*/
static ir_node *gen_Abs(ir_node *node)
{
ir_node *block = be_transform_node(get_nodes_block(node));
......@@ -784,13 +725,12 @@ static ir_node *gen_Abs(ir_node *node)
if (mode_is_float(mode)) {
env_cg->have_fp_insn = 1;
if (USE_FPA(env_cg->