From: Matthias Braun Date: Sat, 2 Jun 2007 20:46:46 +0000 (+0000) Subject: we can parse lots of assembler constraints now (but not all yet), and emit immediates... X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=774486f37ed015066db8a5a12492d924b25bfc1d;p=libfirm we can parse lots of assembler constraints now (but not all yet), and emit immediates in asm nodes [r14275] --- diff --git a/ir/be/ia32/bearch_ia32.c b/ir/be/ia32/bearch_ia32.c index f683bba62..d4276b926 100644 --- a/ir/be/ia32/bearch_ia32.c +++ b/ir/be/ia32/bearch_ia32.c @@ -1767,7 +1767,8 @@ int ia32_to_appear_in_schedule(void *block_env, const ir_node *irn) { if(is_ia32_NoReg_GP(irn) || is_ia32_NoReg_VFP(irn) || is_ia32_NoReg_XMM(irn) || is_ia32_Unknown_GP(irn) || is_ia32_Unknown_XMM(irn) - || is_ia32_Unknown_VFP(irn) || is_ia32_ChangeCW(irn)) + || is_ia32_Unknown_VFP(irn) || is_ia32_ChangeCW(irn) + || is_ia32_Immediate(irn)) return 0; return 1; diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index 9986a803f..b28ff444e 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -224,6 +224,7 @@ char *get_unique_label(char *buf, size_t buflen, const char *prefix) { #undef be_emit_cstring #define be_emit_cstring(env,x) { be_emit_string_len(env->emit, x, sizeof(x)-1); } #define be_emit_ident(env,i) be_emit_ident(env->emit,i) +#define be_emit_tarval(env,tv) be_emit_tarval(env->emit,tv) #define be_emit_write_line(env) be_emit_write_line(env->emit) #define be_emit_finish_line_gas(env,n) be_emit_finish_line_gas(env->emit,n) #define be_emit_pad_comment(env) be_emit_pad_comment(env->emit) @@ -267,7 +268,7 @@ void ia32_emit_immediate(ia32_emit_env_t *env, const ir_node *node) switch(get_ia32_immop_type(node)) { case ia32_ImmConst: tv = get_ia32_Immop_tarval(node); - be_emit_tarval(env->emit, tv); + be_emit_tarval(env, tv); return; case ia32_ImmSymConst: ent = get_ia32_Immop_symconst(node); @@ -275,6 +276,7 @@ void ia32_emit_immediate(ia32_emit_env_t *env, const ir_node *node) id = get_entity_ld_ident(ent); be_emit_ident(env, id); return; + case ia32_ImmAsm: case ia32_ImmNone: break; } @@ -1311,6 +1313,25 @@ void emit_Jmp(ia32_emit_env_t *env, const ir_node *node) { be_emit_finish_line_gas(env, node); } +static +void emit_ia32_Immediate(ia32_emit_env_t *env, const ir_node *node) +{ + ia32_attr_t *attr = get_ia32_attr(node); + + if(attr->am_sc != NULL) { + ident *id = get_entity_ld_ident(attr->am_sc); + + if(attr->data.am_sc_sign) + be_emit_char(env, '-'); + be_emit_ident(env, id); + } + if(attr->cnst_val.tv != NULL) { + if(attr->am_sc != NULL) + be_emit_char(env, '+'); + be_emit_tarval(env, attr->cnst_val.tv); + } +} + static const char* emit_asm_operand(ia32_emit_env_t *env, const ir_node *node, const char *s) @@ -1375,12 +1396,19 @@ const char* emit_asm_operand(ia32_emit_env_t *env, const ir_node *node, if(num < n_outs) { reg = get_out_reg(env, node, num); } else { - int in = num - n_outs; + ir_node *pred; + int in = num - n_outs; if(in >= get_irn_arity(node)) { ir_fprintf(stderr, "Warning: Invalid input %d specified in asm " "op (%+F)\n", num, node); return s; } + pred = get_irn_n(node, in); + /* might be an immediate value */ + if(is_ia32_Immediate(pred)) { + emit_ia32_Immediate(env, pred); + return s; + } reg = get_in_reg(env, node, in); } if(reg == NULL) { diff --git a/ir/be/ia32/ia32_new_nodes.c b/ir/be/ia32/ia32_new_nodes.c index 4cfea6123..977670059 100644 --- a/ir/be/ia32/ia32_new_nodes.c +++ b/ir/be/ia32/ia32_new_nodes.c @@ -132,6 +132,7 @@ static int ia32_dump_node(ir_node *n, FILE *F, dump_reason_t reason) { ir_mode *mode = NULL; int bad = 0; int i, n_res, am_flav, flags; + const ia32_attr_t *attr = get_ia32_attr_const(n); const arch_register_req_t **reqs; const arch_register_t **slots; @@ -239,6 +240,10 @@ static int ia32_dump_node(ir_node *n, FILE *F, dump_reason_t reason) { case ia32_ImmSymConst: fprintf(F, "SymConst"); break; + case ia32_ImmAsm: + fprintf(F, "Asm '%s'\n", + get_id_str(attr->cnst_val.asm_text)); + break; default: fprintf(F, "unknown (%d)", get_ia32_immop_type(n)); break; @@ -422,6 +427,11 @@ ia32_attr_t *get_ia32_attr(const ir_node *node) { return (ia32_attr_t *)get_irn_generic_attr((ir_node *)node); } +const ia32_attr_t *get_ia32_attr_const(const ir_node *node) { + assert(is_ia32_irn(node) && "need ia32 node to get ia32 attributes"); + return (const ia32_attr_t*) get_irn_generic_attr_const(node); +} + /** * Gets the type of an ia32 node. */ @@ -1207,6 +1217,9 @@ int ia32_compare_attr(ia32_attr_t *a, ia32_attr_t *b) { if (a->data.imm_tp == ia32_ImmSymConst && a->cnst_val.sc != b->cnst_val.sc) return 1; + if(a->data.imm_tp == ia32_ImmAsm + && a->cnst_val.asm_text != b->cnst_val.asm_text) + return 1; if (a->data.am_flavour != b->data.am_flavour || a->data.am_scale != b->data.am_scale diff --git a/ir/be/ia32/ia32_new_nodes.h b/ir/be/ia32/ia32_new_nodes.h index 9e4b2d884..c3cce8c25 100644 --- a/ir/be/ia32/ia32_new_nodes.h +++ b/ir/be/ia32/ia32_new_nodes.h @@ -52,6 +52,8 @@ int ia32_has_x87_register(const ir_node *n); * Returns the attributes of an ia32 node. */ ia32_attr_t *get_ia32_attr(const ir_node *node); +const ia32_attr_t *get_ia32_attr_const(const ir_node *node); + /** * Gets the type of an ia32 node. diff --git a/ir/be/ia32/ia32_nodes_attr.h b/ir/be/ia32/ia32_nodes_attr.h index 1e8b0265d..4742d79f7 100644 --- a/ir/be/ia32/ia32_nodes_attr.h +++ b/ir/be/ia32/ia32_nodes_attr.h @@ -44,7 +44,8 @@ typedef enum { typedef enum { ia32_ImmNone = 0, ia32_ImmConst = 1, - ia32_ImmSymConst = 2 + ia32_ImmSymConst = 2, + ia32_ImmAsm = 3 } ia32_immop_type_t; typedef enum { diff --git a/ir/be/ia32/ia32_spec.pl b/ir/be/ia32/ia32_spec.pl index 64e5a2985..68c196355 100644 --- a/ir/be/ia32/ia32_spec.pl +++ b/ir/be/ia32/ia32_spec.pl @@ -290,12 +290,13 @@ $fpcw_flags = [ "FP_IM", "FP_DM", "FP_ZM", "FP_OM", "FP_UM", "FP_PM", %nodes = ( -#Immediate => { -# irn_flags => "i", -# reg_req => { out => [ "NoReg_GP" ] }, -# mode => $mode_gp, -# attr_type => "ia32_imm_t", -#}, +Immediate => { + state => "pinned", + op_flags => "c", + irn_flags => "I", + reg_req => { out => [ "gp_NOREG" ] }, + mode => $mode_gp, +}, Asm => { mode => "mode_T", diff --git a/ir/be/ia32/ia32_transform.c b/ir/be/ia32/ia32_transform.c index 97268ff9b..353c72288 100644 --- a/ir/be/ia32/ia32_transform.c +++ b/ir/be/ia32/ia32_transform.c @@ -2392,6 +2392,320 @@ static ir_node *gen_Conv(ia32_transform_env_t *env, ir_node *node) { return res; } +static +ir_node *try_create_Immediate(ia32_transform_env_t *env, ir_node *node, + unsigned immediate_max) +{ + int minus = 0; + tarval *offset = NULL; + int offset_sign = 0; + ir_entity *symconst_ent = NULL; + int symconst_sign = 0; + ir_mode *mode; + ir_node *cnst = NULL; + ir_node *symconst = NULL; + ir_node *res; + ir_graph *irg; + dbg_info *dbgi; + ir_node *block; + ia32_attr_t *attr; + + mode = get_irn_mode(node); + if(!mode_is_int(mode) && !mode_is_character(mode) && + !mode_is_reference(mode)) { + return NULL; + } + + if(is_Minus(node)) { + minus = 1; + node = get_Minus_op(node); + } + + if(is_Const(node)) { + cnst = node; + symconst = NULL; + offset_sign = minus; + } else if(is_SymConst(node)) { + cnst = NULL; + symconst = node; + symconst_sign = minus; + } else if(is_Add(node)) { + ir_node *left = get_Add_left(node); + ir_node *right = get_Add_right(node); + if(is_Const(left) && is_SymConst(right)) { + cnst = left; + symconst = right; + symconst_sign = minus; + offset_sign = minus; + } else if(is_SymConst(left) && is_Const(right)) { + cnst = right; + symconst = left; + symconst_sign = minus; + offset_sign = minus; + } + } else if(is_Sub(node)) { + ir_node *left = get_Add_left(node); + ir_node *right = get_Add_right(node); + if(is_Const(left) && is_SymConst(right)) { + cnst = left; + symconst = right; + symconst_sign = !minus; + offset_sign = minus; + } else if(is_SymConst(left) && is_Const(right)) { + cnst = right; + symconst = left; + symconst_sign = minus; + offset_sign = !minus; + } + } else { + return NULL; + } + + if(cnst != NULL) { + tarval *tv; + tarval *tvu; + long val; + + tv = get_Const_tarval(cnst); + if(!tarval_is_long(tv)) { + ir_fprintf(stderr, "Optimisation Warning: tarval from %+F is not a " + "long?\n", cnst); + return NULL; + } + + tvu = tarval_convert_to(tv, mode_Iu); + val = get_tarval_long(tvu); + if(val > immediate_max) + return NULL; + + offset = tvu; + } + if(symconst != NULL) { + if(immediate_max != 0xffffffff) { + /* we need full 32bits for symconsts */ + return NULL; + } + + if(get_SymConst_kind(symconst) != symconst_addr_ent) + return NULL; + symconst_ent = get_SymConst_entity(symconst); + } + + irg = env->irg; + dbgi = get_irn_dbg_info(node); + block = get_irg_start_block(irg); + res = new_rd_ia32_Immediate(dbgi, irg, block); + arch_set_irn_register(env->cg->arch_env, res, &ia32_gp_regs[REG_GP_NOREG]); + + /* make sure we don't schedule stuff before the barrier */ + add_irn_dep(res, get_irg_frame(irg)); + + /* misuse some fields for now... */ + attr = get_ia32_attr(res); + attr->am_sc = symconst_ent; + attr->data.am_sc_sign = symconst_sign; + if(offset_sign && offset != NULL) { + offset = tarval_neg(offset); + } + attr->cnst_val.tv = offset; + attr->data.imm_tp = ia32_ImmConst; + + return res; +} + +typedef struct constraint_t constraint_t; +struct constraint_t { + const arch_register_req_t *req; + unsigned immediate_possible; + unsigned immediate_max; +}; + +static +void parse_asm_constraint(ia32_transform_env_t *env, ir_node *node, + constraint_t *constraint, const char *c, int is_in) +{ + int immediate_possible = 0; + unsigned immediate_max = 0xffffffff; + unsigned limited = 0; + const arch_register_class_t *cls = NULL; + ir_graph *irg; + struct obstack *obst; + arch_register_req_t *req; + unsigned *limited_ptr; + + printf("Constraint: %s\n", c); + + while(*c != 0) { + switch(*c) { + case ' ': + case '\t': + case '\n': + break; + + case 'a': + assert(cls == NULL || + (cls == &ia32_reg_classes[CLASS_ia32_gp] && limited != 0)); + cls = &ia32_reg_classes[CLASS_ia32_gp]; + limited |= 1 << REG_EAX; + break; + case 'b': + assert(cls == NULL || + (cls == &ia32_reg_classes[CLASS_ia32_gp] && limited != 0)); + cls = &ia32_reg_classes[CLASS_ia32_gp]; + limited |= 1 << REG_EBX; + break; + case 'c': + assert(cls == NULL || + (cls == &ia32_reg_classes[CLASS_ia32_gp] && limited != 0)); + cls = &ia32_reg_classes[CLASS_ia32_gp]; + limited |= 1 << REG_ECX; + break; + case 'd': + assert(cls == NULL || + (cls == &ia32_reg_classes[CLASS_ia32_gp] && limited != 0)); + cls = &ia32_reg_classes[CLASS_ia32_gp]; + limited |= 1 << REG_EDX; + break; + case 'D': + assert(cls == NULL || + (cls == &ia32_reg_classes[CLASS_ia32_gp] && limited != 0)); + cls = &ia32_reg_classes[CLASS_ia32_gp]; + limited |= 1 << REG_EDI; + break; + case 'S': + assert(cls == NULL || + (cls == &ia32_reg_classes[CLASS_ia32_gp] && limited != 0)); + cls = &ia32_reg_classes[CLASS_ia32_gp]; + limited |= 1 << REG_ESI; + break; + + case 'R': + case 'r': + case 'p': + assert(cls == NULL); + cls = &ia32_reg_classes[CLASS_ia32_gp]; + break; + + case 'f': + case 't': + case 'u': + assert(cls == NULL); + cls = &ia32_reg_classes[CLASS_ia32_vfp]; + break; + + case 'x': + assert(cls == NULL); + cls = &ia32_reg_classes[CLASS_ia32_xmm]; + break; + + case 'I': + assert(!immediate_possible); + immediate_possible = 1; + immediate_max = 31; + break; + case 'J': + assert(!immediate_possible); + immediate_possible = 1; + immediate_max = 63; + break; + case 'n': + case 'i': + assert(!immediate_possible); + immediate_possible = 1; + break; + case 'M': + assert(!immediate_possible); + immediate_possible = 1; + immediate_max = 3; + break; + case 'N': + assert(!immediate_possible); + immediate_possible = 1; + immediate_max = 0xff; + break; + + case 'g': + assert(!immediate_possible && cls == NULL); + immediate_possible = 1; + cls = &ia32_reg_classes[CLASS_ia32_gp]; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* TODO */ + assert(0 && "other_same not implemented yet"); + break; + + case 'E': /* no float consts yet */ + case 'F': /* no float consts yet */ + case 's': /* makes no sense on x86 */ + case 'X': /* we can't support that in firm */ + case 'm': + case 'o': + case 'V': + case '<': /* no autodecrement on x86 */ + case '>': /* no autoincrement on x86 */ + case 'C': /* sse constant not supported yet */ + case 'G': /* 80387 constant not supported yet */ + case 'y': /* we don't support mmx registers yet */ + case 'Z': /* not available in 32 bit mode */ + case 'e': /* not available in 32 bit mode */ + case 'K': /* gcc docu is cryptic */ + case 'L': /* gcc docu is cryptic */ + assert(0); + break; + default: + assert(0); + break; + } + ++c; + } + + if(immediate_possible && cls == NULL) { + cls = &ia32_reg_classes[CLASS_ia32_gp]; + } + assert(!immediate_possible || cls == &ia32_reg_classes[CLASS_ia32_gp]); + assert(cls != NULL); + + if(immediate_possible) { + assert(is_in && "imeediates make no sense for output constraints"); + printf("Immediate possible 0-%x\n", immediate_max); + } + /* todo: check types (no float input on 'r' constrainted in and such... */ + + irg = env->irg; + obst = get_irg_obstack(irg); + + if(limited != 0) { + req = obstack_alloc(obst, sizeof(req[0]) + sizeof(unsigned)); + limited_ptr = (unsigned*) (req+1); + } else { + req = obstack_alloc(obst, sizeof(req[0])); + } + memset(req, 0, sizeof(req[0])); + + if(limited != 0) { + req->type = arch_register_req_type_limited; + *limited_ptr = limited; + req->limited = limited_ptr; + } else { + req->type = arch_register_req_type_normal; + } + req->cls = cls; + + constraint->req = req; + constraint->immediate_possible = immediate_possible; + constraint->immediate_max = immediate_max; +} + static ir_node *gen_ASM(ia32_transform_env_t *env, ir_node *node) { @@ -2402,6 +2716,8 @@ ir_node *gen_ASM(ia32_transform_env_t *env, ir_node *node) ir_node **in; ir_node *res; int out_arity; + int n_outs; + int n_clobbers; ia32_attr_t *attr; const arch_register_req_t **out_reqs; const arch_register_req_t **in_reqs; @@ -2414,44 +2730,75 @@ ir_node *gen_ASM(ia32_transform_env_t *env, ir_node *node) /* transform inputs */ arity = get_irn_arity(node); in = alloca(arity * sizeof(in[0])); - for(i = 0; i < arity; ++i) { - ir_node *pred = get_irn_n(node, i); - ir_node *transformed = transform_node(env, pred); - - in[i] = transformed; - } + memset(in, 0, arity * sizeof(in[0])); - out_arity = get_ASM_n_output_constraints(node) + get_ASM_n_clobbers(node); - res = new_rd_ia32_Asm(dbgi, irg, block, arity, in, out_arity); + n_outs = get_ASM_n_output_constraints(node); + n_clobbers = get_ASM_n_clobbers(node); + out_arity = n_outs + n_clobbers; /* construct register constraints */ obst = get_irg_obstack(irg); out_reqs = obstack_alloc(obst, out_arity * sizeof(out_reqs[0])); for(i = 0; i < out_arity; ++i) { - arch_register_req_t *req = obstack_alloc(obst, sizeof(req[0])); - memset(req, 0, sizeof(req[0])); + const char *c; + constraint_t parsed_constr; - /* TODO: parse constraints */ - req->type = arch_register_req_type_normal; - req->cls = &ia32_reg_classes[CLASS_ia32_gp]; - out_reqs[i] = req; + if(i < n_outs) { + const ir_asm_constraint *constraint; + constraint = & get_ASM_output_constraints(node) [i]; + c = get_id_str(constraint->constraint); + } else { + ident *glob_id = get_ASM_clobbers(node) [i - n_outs]; + c = get_id_str(glob_id); + } + parse_asm_constraint(env, node, &parsed_constr, c, 0); + out_reqs[i] = parsed_constr.req; } - set_ia32_out_req_all(res, out_reqs); in_reqs = obstack_alloc(obst, arity * sizeof(in_reqs[0])); for(i = 0; i < arity; ++i) { - arch_register_req_t *req = obstack_alloc(obst, sizeof(req[0])); - memset(req, 0, sizeof(req[0])); + const ir_asm_constraint *constraint; + ident *constr_id; + const char *c; + constraint_t parsed_constr; + + constraint = & get_ASM_input_constraints(node) [i]; + constr_id = constraint->constraint; + c = get_id_str(constr_id); + parse_asm_constraint(env, node, &parsed_constr, c, 1); + in_reqs[i] = parsed_constr.req; + + if(parsed_constr.immediate_possible) { + ir_node *pred = get_irn_n(node, i); + ir_node *immediate + = try_create_Immediate(env, pred, parsed_constr.immediate_max); + + if(immediate != NULL) { + in[i] = immediate; + } + } + } + + /* transform inputs */ + for(i = 0; i < arity; ++i) { + ir_node *pred; + ir_node *transformed; + + if(in[i] != NULL) + continue; - /* TODO: parse constraints */ - req->type = arch_register_req_type_normal; - req->cls = &ia32_reg_classes[CLASS_ia32_gp]; - in_reqs[i] = req; + pred = get_irn_n(node, i); + transformed = transform_node(env, pred); + in[i] = transformed; } - set_ia32_in_req_all(res, in_reqs); + + res = new_rd_ia32_Asm(dbgi, irg, block, arity, in, out_arity); attr = get_ia32_attr(res); attr->cnst_val.asm_text = get_ASM_text(node); + attr->data.imm_tp = ia32_ImmAsm; + set_ia32_out_req_all(res, out_reqs); + set_ia32_in_req_all(res, in_reqs); SET_IA32_ORIG_NODE(res, ia32_get_old_node_name(env->cg, node)); diff --git a/ir/be/test/asm_test.c b/ir/be/test/asm_test.c index d6c369656..27aff797a 100644 --- a/ir/be/test/asm_test.c +++ b/ir/be/test/asm_test.c @@ -36,7 +36,8 @@ static inline int mov(int val) int main() { //sincostest(0.5); - //outb(123, 42); + outb(123, 42); + outb(12345, 42); - return mov(0); + return mov(0) + inb(12345) + inb(123); }