Commit fd07b149 authored by Christoph Mallon's avatar Christoph Mallon
Browse files

Introduce ia32_Call, which can handle address mode.

[r22609]
parent a0ea0fb4
......@@ -392,6 +392,9 @@ static void ia32_set_frame_offset(ir_node *irn, int bias)
static int ia32_get_sp_bias(const ir_node *node)
{
if (is_ia32_Call(node))
return -(int)get_ia32_call_attr_const(node)->pop;
if (is_ia32_Push(node))
return 4;
......@@ -1889,7 +1892,7 @@ static void ia32_get_call_abi(const void *self, ir_type *method_type,
call_flags.bits.store_args_sequential = 0;
/* call_flags.bits.try_omit_fp not changed: can handle both settings */
call_flags.bits.fp_free = 0; /* the frame pointer is fixed in IA32 */
call_flags.bits.call_has_imm = 1; /* No call immediates, we handle this by ourselves */
call_flags.bits.call_has_imm = 0; /* No call immediates, we handle this by ourselves */
/* set parameter passing style */
be_abi_call_set_flags(abi, call_flags, &ia32_abi_callbacks);
......@@ -2088,16 +2091,13 @@ static const be_execution_unit_t ***ia32_get_allowed_execution_units(
if (is_ia32_irn(irn)) {
ret = get_ia32_exec_units(irn);
}
else if (is_be_node(irn)) {
if (be_is_Call(irn) || be_is_Return(irn)) {
} else if (is_be_node(irn)) {
if (be_is_Return(irn)) {
ret = _units_callret;
}
else if (be_is_Barrier(irn)) {
} else if (be_is_Barrier(irn)) {
ret = _units_dummy;
}
else {
ret = _units_other;
} else {
ret = _units_other;
}
}
else {
......
......@@ -255,7 +255,52 @@ void ia32_emit_source_register(const ir_node *node, int pos)
emit_register(reg, NULL);
}
static void emit_ia32_Immediate(const ir_node *node);
static void ia32_emit_entity(ir_entity *entity, int no_pic_adjust)
{
ident *id;
set_entity_backend_marked(entity, 1);
id = get_entity_ld_ident(entity);
be_emit_ident(id);
if (get_entity_owner(entity) == get_tls_type()) {
if (get_entity_visibility(entity) == visibility_external_allocated) {
be_emit_cstring("@INDNTPOFF");
} else {
be_emit_cstring("@NTPOFF");
}
}
if (!no_pic_adjust && do_pic) {
/* TODO: only do this when necessary */
be_emit_char('-');
be_emit_string(pic_base_label);
}
}
static void emit_ia32_Immediate_no_prefix(const ir_node *node)
{
const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node);
if (attr->symconst != NULL) {
if (attr->sc_sign)
be_emit_char('-');
ia32_emit_entity(attr->symconst, 0);
}
if (attr->symconst == NULL || attr->offset != 0) {
if (attr->symconst != NULL) {
be_emit_irprintf("%+d", attr->offset);
} else {
be_emit_irprintf("0x%X", attr->offset);
}
}
}
static void emit_ia32_Immediate(const ir_node *node)
{
be_emit_char('$');
emit_ia32_Immediate_no_prefix(node);
}
void ia32_emit_8bit_source_register_or_immediate(const ir_node *node, int pos)
{
......@@ -476,6 +521,11 @@ static void ia32_emit_cmp_suffix(int pnc)
be_emit_string(str);
}
typedef enum ia32_emit_mod_t {
EMIT_RESPECT_LS = 1U << 0,
EMIT_ALTERNATE_AM = 1U << 1
} ia32_emit_mod_t;
/**
* fmt parameter output
* ---- ---------------------- ---------------------------------------------
......@@ -495,6 +545,7 @@ static void ia32_emit_cmp_suffix(int pnc)
*
* x starts at 0
* # modifier for %ASx, %D and %S uses ls mode of node to alter register width
* * modifier does not prefix immediates with $, but AM with *
*/
static void ia32_emitf(const ir_node *node, const char *fmt, ...)
{
......@@ -502,8 +553,8 @@ static void ia32_emitf(const ir_node *node, const char *fmt, ...)
va_start(ap, fmt);
for (;;) {
const char *start = fmt;
const ir_mode *mode = NULL;
const char *start = fmt;
ia32_emit_mod_t mod = 0;
while (*fmt != '%' && *fmt != '\n' && *fmt != '\0')
++fmt;
......@@ -523,8 +574,13 @@ static void ia32_emitf(const ir_node *node, const char *fmt, ...)
break;
++fmt;
if (*fmt == '*') {
mod |= EMIT_ALTERNATE_AM;
++fmt;
}
if (*fmt == '#') {
mode = get_ia32_ls_mode(node);
mod |= EMIT_RESPECT_LS;
++fmt;
}
......@@ -536,12 +592,16 @@ static void ia32_emitf(const ir_node *node, const char *fmt, ...)
case 'A': {
switch (*fmt++) {
case 'M':
if (mod & EMIT_ALTERNATE_AM)
be_emit_char('*');
ia32_emit_am(node);
break;
case 'R': {
const arch_register_t *reg = va_arg(ap, const arch_register_t*);
if (get_ia32_op_type(node) == ia32_AddrModeS) {
if (mod & EMIT_ALTERNATE_AM)
be_emit_char('*');
ia32_emit_am(node);
} else {
emit_register(reg, NULL);
......@@ -551,6 +611,8 @@ static void ia32_emitf(const ir_node *node, const char *fmt, ...)
case 'S':
if (get_ia32_op_type(node) == ia32_AddrModeS) {
if (mod & EMIT_ALTERNATE_AM)
be_emit_char('*');
ia32_emit_am(node);
++fmt;
} else {
......@@ -573,12 +635,14 @@ static void ia32_emitf(const ir_node *node, const char *fmt, ...)
pos = *fmt++ - '0';
reg = get_out_reg(node, pos);
emit_register(reg, mode);
emit_register(reg, mod & EMIT_RESPECT_LS ? get_ia32_ls_mode(node) : NULL);
break;
}
case 'I':
emit_ia32_Immediate(node);
if (!(mod & EMIT_ALTERNATE_AM))
be_emit_char('$');
emit_ia32_Immediate_no_prefix(node);
break;
case 'L':
......@@ -613,10 +677,12 @@ emit_S:
pos = *fmt++ - '0';
in = get_irn_n(node, pos);
if (is_ia32_Immediate(in)) {
emit_ia32_Immediate(in);
if (!(mod & EMIT_ALTERNATE_AM))
be_emit_char('$');
emit_ia32_Immediate_no_prefix(in);
} else {
const arch_register_t *reg = get_in_reg(node, pos);
emit_register(reg, mode);
emit_register(reg, mod & EMIT_RESPECT_LS ? get_ia32_ls_mode(node) : NULL);
}
break;
}
......@@ -698,29 +764,6 @@ void ia32_emit_unop(const ir_node *node, int pos)
ia32_emitf(node, fmt);
}
static void ia32_emit_entity(ir_entity *entity, int no_pic_adjust)
{
ident *id;
set_entity_backend_marked(entity, 1);
id = get_entity_ld_ident(entity);
be_emit_ident(id);
if (get_entity_owner(entity) == get_tls_type()) {
if (get_entity_visibility(entity) == visibility_external_allocated) {
be_emit_cstring("@INDNTPOFF");
} else {
be_emit_cstring("@NTPOFF");
}
}
if (!no_pic_adjust && do_pic) {
/* TODO: only do this when necessary */
be_emit_char('-');
be_emit_string(pic_base_label);
}
}
/**
* Emits address mode.
*/
......@@ -1215,25 +1258,6 @@ static void emit_Jmp(const ir_node *node)
}
}
static void emit_ia32_Immediate(const ir_node *node)
{
const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node);
be_emit_char('$');
if (attr->symconst != NULL) {
if (attr->sc_sign)
be_emit_char('-');
ia32_emit_entity(attr->symconst, 0);
}
if (attr->symconst == NULL || attr->offset != 0) {
if (attr->symconst != NULL) {
be_emit_irprintf("%+d", attr->offset);
} else {
be_emit_irprintf("0x%X", attr->offset);
}
}
}
/**
* Emit an inline assembler operand.
*
......@@ -1521,6 +1545,16 @@ static void emit_ia32_Conv_I2I(const ir_node *node)
}
}
/**
* Emits a call
*/
static void emit_ia32_Call(const ir_node *node)
{
/* Special case: Call must not have its immediates prefixed by $, instead
* address mode is prefixed by *. */
ia32_emitf(node, "\tcall %*AS3\n");
}
/*******************************************
* _ _
......@@ -1532,24 +1566,6 @@ static void emit_ia32_Conv_I2I(const ir_node *node)
*
*******************************************/
/**
* Emits a backend call
*/
static void emit_be_Call(const ir_node *node)
{
ir_entity *ent = be_Call_get_entity(node);
be_emit_cstring("\tcall ");
if (ent) {
ia32_emit_entity(ent, 1);
} else {
const arch_register_t *reg = get_in_reg(node, be_pos_Call_ptr);
be_emit_char('*');
emit_register(reg, NULL);
}
be_emit_finish_line_gas(node);
}
/**
* Emits code to increase stack pointer.
*/
......@@ -1795,6 +1811,7 @@ static void ia32_register_emitters(void)
IA32_EMIT2(Conv_I2I8Bit, Conv_I2I);
IA32_EMIT(Asm);
IA32_EMIT(CMov);
IA32_EMIT(Call);
IA32_EMIT(Const);
IA32_EMIT(Conv_FP2FP);
IA32_EMIT(Conv_FP2I);
......@@ -1810,7 +1827,6 @@ static void ia32_register_emitters(void)
IA32_EMIT(SwitchJmp);
/* benode emitter */
BE_EMIT(Call);
BE_EMIT(Copy);
BE_EMIT(CopyKeep);
BE_EMIT(IncSP);
......
......@@ -438,6 +438,22 @@ const ia32_condcode_attr_t *get_ia32_condcode_attr_const(const ir_node *node) {
return cc_attr;
}
ia32_call_attr_t *get_ia32_call_attr(ir_node *node)
{
ia32_attr_t *attr = get_ia32_attr(node);
ia32_call_attr_t *call_attr = CAST_IA32_ATTR(ia32_call_attr_t, attr);
return call_attr;
}
const ia32_call_attr_t *get_ia32_call_attr_const(const ir_node *node)
{
const ia32_attr_t *attr = get_ia32_attr_const(node);
const ia32_call_attr_t *call_attr = CONST_CAST_IA32_ATTR(ia32_call_attr_t, attr);
return call_attr;
}
ia32_copyb_attr_t *get_ia32_copyb_attr(ir_node *node) {
ia32_attr_t *attr = get_ia32_attr(node);
ia32_copyb_attr_t *copyb_attr = CAST_IA32_ATTR(ia32_copyb_attr_t, attr);
......@@ -1095,6 +1111,17 @@ init_ia32_immediate_attributes(ir_node *res, ir_entity *symconst,
attr->offset = offset;
}
void init_ia32_call_attributes(ir_node *const res, unsigned const pop, ir_type *const call_tp)
{
ia32_call_attr_t *attr = get_irn_generic_attr(res);
#ifndef NDEBUG
attr->attr.attr_type |= IA32_ATTR_ia32_call_attr_t;
#endif
attr->pop = pop;
attr->call_tp = call_tp;
}
void
init_ia32_copyb_attributes(ir_node *res, unsigned size) {
ia32_copyb_attr_t *attr = get_irn_generic_attr(res);
......@@ -1203,6 +1230,26 @@ int ia32_compare_condcode_attr(ir_node *a, ir_node *b)
return 0;
}
static int ia32_compare_call_attr(ir_node *a, ir_node *b)
{
const ia32_call_attr_t *attr_a;
const ia32_call_attr_t *attr_b;
if (ia32_compare_nodes_attr(a, b))
return 1;
attr_a = get_ia32_call_attr_const(a);
attr_b = get_ia32_call_attr_const(b);
if (attr_a->pop != attr_b->pop)
return 1;
if (attr_a->call_tp != attr_b->call_tp)
return 1;
return 0;
}
/** Compare node attributes for CopyB nodes. */
static
int ia32_compare_copyb_attr(ir_node *a, ir_node *b)
......
......@@ -86,6 +86,12 @@ const ia32_immediate_attr_t *get_ia32_immediate_attr_const(const ir_node *node);
ia32_condcode_attr_t *get_ia32_condcode_attr(ir_node *node);
const ia32_condcode_attr_t *get_ia32_condcode_attr_const(const ir_node *node);
/**
* Gets the Call node attributes.
*/
ia32_call_attr_t *get_ia32_call_attr(ir_node *node);
const ia32_call_attr_t *get_ia32_call_attr_const(const ir_node *node);
/**
* Gets the CopyB node attributes.
*/
......@@ -454,6 +460,7 @@ void init_ia32_x87_attributes(ir_node *node);
void init_ia32_asm_attributes(ir_node *node);
void init_ia32_immediate_attributes(ir_node *node, ir_entity *symconst,
int symconst_sign, long offset);
void init_ia32_call_attributes(ir_node *res, unsigned pop, ir_type *call_tp);
void init_ia32_copyb_attributes(ir_node *res, unsigned size);
void init_ia32_condcode_attributes(ir_node *res, long pnc);
......
......@@ -82,7 +82,8 @@ typedef enum {
IA32_ATTR_ia32_asm_attr_t = 1 << 2,
IA32_ATTR_ia32_immediate_attr_t = 1 << 3,
IA32_ATTR_ia32_condcode_attr_t = 1 << 4,
IA32_ATTR_ia32_copyb_attr_t = 1 << 5
IA32_ATTR_ia32_copyb_attr_t = 1 << 5,
IA32_ATTR_ia32_call_attr_t = 1 << 6
} ia32_attr_type_t;
#endif
......@@ -141,6 +142,16 @@ struct ia32_attr_t {
};
COMPILETIME_ASSERT(sizeof(struct ia32_attr_data_bitfield) <= 4, attr_bitfield);
/**
* The attributes for a Call node.
*/
typedef struct ia32_call_attr_t ia32_call_attr_t;
struct ia32_call_attr_t {
ia32_attr_t attr; /**< generic attribute */
unsigned pop; /**< number of bytes that get popped by the callee */
ir_type *call_tp; /**< The call type, copied from the original Call node. */
};
/**
* The attributes for nodes with condition code.
*/
......@@ -204,6 +215,7 @@ struct ia32_asm_attr_t {
* the structs (we use them to simulate OO-inheritance) */
union allow_casts_attr_t_ {
ia32_attr_t attr;
ia32_call_attr_t call_attr;
ia32_condcode_attr_t cc_attr;
ia32_copyb_attr_t cpy_attr;
ia32_x87_attr_t x87_attr;
......
......@@ -279,6 +279,9 @@ $custom_init_attr_func = \&ia32_custom_init_attr;
"\tinit_ia32_asm_attributes(res);",
ia32_attr_t =>
"\tinit_ia32_attributes(res, flags, in_reqs, out_reqs, exec_units, n_res);",
ia32_call_attr_t =>
"\tinit_ia32_attributes(res, flags, in_reqs, out_reqs, exec_units, n_res);\n".
"\tinit_ia32_call_attributes(res, pop, call_tp);",
ia32_condcode_attr_t =>
"\tinit_ia32_attributes(res, flags, in_reqs, out_reqs, exec_units, n_res);\n".
"\tinit_ia32_condcode_attributes(res, pnc);",
......@@ -296,6 +299,7 @@ $custom_init_attr_func = \&ia32_custom_init_attr;
%compare_attr = (
ia32_asm_attr_t => "ia32_compare_asm_attr",
ia32_attr_t => "ia32_compare_nodes_attr",
ia32_call_attr_t => "ia32_compare_call_attr",
ia32_condcode_attr_t => "ia32_compare_condcode_attr",
ia32_copyb_attr_t => "ia32_compare_copyb_attr",
ia32_immediate_attr_t => "ia32_compare_immediate_attr",
......@@ -1460,6 +1464,21 @@ Bt => {
modified_flags => $status_flags # only CF is set, but the other flags are undefined
},
Call => {
state => "exc_pinned",
reg_req => {
in => [ "gp", "gp", "none", "gp", "esp", "fpcw", "eax", "ecx", "edx" ],
out => [ "esp", "fpcw", "none", "eax", "ecx", "edx", "vf0", "vf1", "vf2", "vf3", "vf4", "vf5", "vf6", "vf7", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" ]
},
ins => [ "base", "index", "mem", "addr", "stack", "fpcw", "eax", "ecx", "edx" ],
outs => [ "stack:I|S", "fpcw:I", "M", "eax", "ecx", "edx", "vf0", "vf1", "vf2", "vf3", "vf4", "vf5", "vf6", "vf7", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" ],
attr_type => "ia32_call_attr_t",
attr => "unsigned pop, ir_type *call_tp",
am => "source,unary",
units => [ "BRANCH" ],
latency => 4, # random number
},
#-----------------------------------------------------------------------------#
# _____ _____ ______ __ _ _ _ #
# / ____/ ____| ____| / _| | | | | | #
......
......@@ -4210,14 +4210,30 @@ static ir_node *gen_Proj_Quot(ir_node *node) {
panic("No idea how to transform proj->Quot");
}
static ir_node *gen_be_Call(ir_node *node) {
ir_node *res = be_duplicate_node(node);
ir_type *call_tp;
be_node_add_flags(res, -1, arch_irn_flags_modify_flags);
static ir_node *gen_be_Call(ir_node *node)
{
dbg_info *const dbgi = get_irn_dbg_info(node);
ir_graph *const irg = current_ir_graph;
ir_node *const src_block = get_nodes_block(node);
ir_node *const block = be_transform_node(src_block);
ir_node *const src_mem = get_irn_n(node, be_pos_Call_mem);
ir_node *const src_sp = get_irn_n(node, be_pos_Call_sp);
ir_node *const sp = be_transform_node(src_sp);
ir_node *const src_ptr = get_irn_n(node, be_pos_Call_ptr);
ir_node *const noreg = ia32_new_NoReg_gp(env_cg);
ia32_address_mode_t am;
ia32_address_t *const addr = &am.addr;
ir_node * mem;
ir_node * call;
int i;
ir_node * fpcw;
ir_node * eax = noreg;
ir_node * ecx = noreg;
ir_node * edx = noreg;
unsigned const pop = be_Call_get_pop(node);
ir_type *const call_tp = be_Call_get_type(node);
/* Run the x87 simulator if the call returns a float value */
call_tp = be_Call_get_type(node);
if (get_method_n_ress(call_tp) > 0) {
ir_type *const res_type = get_method_res_type(call_tp, 0);
ir_mode *const res_mode = get_type_mode(res_type);
......@@ -4227,7 +4243,41 @@ static ir_node *gen_be_Call(ir_node *node) {
}
}
return res;
/* We do not want be_Call direct calls */
assert(be_Call_get_entity(node) == NULL);
match_arguments(&am, src_block, NULL, src_ptr, src_mem,
match_am | match_immediate);
i = get_irn_arity(node) - 1;
fpcw = be_transform_node(get_irn_n(node, i--));
for (; i >= be_pos_Call_first_arg; --i) {
arch_register_req_t const *const req =
arch_get_register_req(env_cg->arch_env, node, i);
ir_node *const reg_parm = be_transform_node(get_irn_n(node, i));
assert(req->type == arch_register_req_type_limited);
assert(req->cls == &ia32_reg_classes[CLASS_ia32_gp]);
switch (*req->limited) {
case 1 << REG_EAX: assert(eax == noreg); eax = reg_parm; break;
case 1 << REG_ECX: assert(ecx == noreg); ecx = reg_parm; break;
case 1 << REG_EDX: assert(edx == noreg); edx = reg_parm; break;
default: panic("Invalid GP register for register parameter");
}
}
mem = transform_AM_mem(irg, block, src_ptr, src_mem, addr->mem);
call = new_rd_ia32_Call(dbgi, irg, block, addr->base, addr->index, mem,
am.new_op2, sp, fpcw, eax, ecx, edx, pop, call_tp);
set_am_attributes(call, &am);
call = fix_mem_proj(call, &am);
if (get_irn_pinned(node) == op_pin_state_pinned)
set_irn_pinned(call, op_pin_state_pinned);
SET_IA32_ORIG_NODE(call, ia32_get_old_node_name(env_cg, node));
return call;
}
static ir_node *gen_be_IncSP(ir_node *node) {
......@@ -4240,7 +4290,8 @@ static ir_node *gen_be_IncSP(ir_node *node) {
/**
* Transform the Projs from a be_Call.
*/
static ir_node *gen_Proj_be_Call(ir_node *node) {
static ir_node *gen_Proj_be_Call(ir_node *node)
{
ir_node *block = be_transform_node(get_nodes_block(node));
ir_node *call = get_Proj_pred(node);
ir_node *new_call = be_transform_node(call);
......@@ -4252,6 +4303,7 @@ static ir_node *gen_Proj_be_Call(ir_node *node) {
ir_mode *mode = get_irn_mode(node);
ir_node *sse_load;
const arch_register_class_t *cls;
ir_node *res;
/* The following is kinda tricky: If we're using SSE, then we have to
* move the result value of the call in floating point registers to an
......@@ -4270,9 +4322,9 @@ static ir_node *gen_Proj_be_Call(ir_node *node) {
call_res_pred = get_Proj_pred(call_res_new);
}
if (call_res_pred == NULL || be_is_Call(call_res_pred)) {
if (call_res_pred == NULL || is_ia32_Call(call_res_pred)) {
return new_rd_Proj(dbgi, irg, block, new_call, mode_M,
pn_be_Call_M_regular);
n_ia32_Call_mem);
} else {
assert(is_ia32_xLoad(call_res_pred));
return new_rd_Proj(dbgi, irg, block, call_res_pred, mode_M,
......@@ -4319,7 +4371,47 @@ static ir_node *gen_Proj_be_Call(ir_node *node) {
mode = cls->mode;
}
return new_rd_Proj(dbgi, irg, block, new_call, mode, proj);
/* Map from be_Call to ia32_Call proj number */
if (proj == pn_be_Call_sp) {
proj = pn_ia32_Call_stack;
} else if (proj == pn_be_Call_M_regular) {
proj = pn_ia32_Call_M;
} else {
arch_register_req_t const *const req = arch_get_register_req(env_cg->arch_env, node, BE_OUT_POS(proj));
int const n_outs = get_ia32_n_res(new_call);
int i;
assert(proj >= pn_be_Call_first_res);
assert(req->type == arch_register_req_type_limited);
for (i = 0; i < n_outs; ++i) {
arch_register_req_t const *const new_req = get_ia32_out_req(new_call, i);
if (new_req->type != arch_register_req_type_limited ||
new_req->cls != req->cls ||
*new_req->limited != *req->limited)
continue;
proj = i;
break;
}
assert(i < n_outs);
}
res = new_rd_Proj(dbgi, irg, block, new_call, mode, proj);
/* TODO arch_set_irn_register() only operates on Projs, need variant with index */
switch (proj) {
case pn_ia32_Call_stack:
arch_set_irn_register(env_cg->arch_env, res, &ia32_gp_regs[REG_ESP]);
break;