From: Michael Beck Date: Fri, 10 Mar 2006 23:50:39 +0000 (+0000) Subject: instruction mapper implemented X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=5ec0ef3c3a215f5825b430b71787d2c862722d8a;p=libfirm instruction mapper implemented [r7434] --- diff --git a/ir/lower/lower_intrinsics.c b/ir/lower/lower_intrinsics.c index c70425dd5..432e12c23 100644 --- a/ir/lower/lower_intrinsics.c +++ b/ir/lower/lower_intrinsics.c @@ -20,8 +20,10 @@ #include #endif +#include "irop_t.h" #include "irprog_t.h" #include "irnode_t.h" +#include "irprog_t.h" #include "irgwalk.h" #include "ircons.h" #include "irgmod.h" @@ -30,56 +32,81 @@ /** Walker environment */ typedef struct _walker_env { - pmap *map; /**< the intrinsic map. */ - unsigned nr_of_intrinsics; /**< statistics */ + pmap *c_map; /**< The intrinsic call map. */ + unsigned nr_of_intrinsics; /**< statistics */ + const i_instr_record **i_map; /**< The intrinsic instruction map. */ } walker_env_t; /** - * walker: do the call mapping + * walker: call all mapper functions */ -static void call_mapper(ir_node *call, void *env) { +static void call_mapper(ir_node *node, void *env) { walker_env_t *wenv = env; - ir_node *symconst; - pmap_entry *p; - const i_record *r; - entity *ent; + ir_op *op = get_irn_op(node); - if (! is_Call(call)) - return; + if (op == op_Call) { + ir_node *symconst; + pmap_entry *p; + const i_call_record *r; + entity *ent; - symconst = get_Call_ptr(call); - if (get_irn_op(symconst) != op_SymConst || - get_SymConst_kind(symconst) != symconst_addr_ent) - return; + symconst = get_Call_ptr(node); + if (get_irn_op(symconst) != op_SymConst || + get_SymConst_kind(symconst) != symconst_addr_ent) + return; - ent = get_SymConst_entity(symconst); - p = pmap_find(wenv->map, ent); + ent = get_SymConst_entity(symconst); + p = pmap_find(wenv->c_map, ent); - if (p) { - r = p->value; - wenv->nr_of_intrinsics += r->i_mapper(call, r->ctx) ? 1 : 0; + if (p) { + r = p->value; + wenv->nr_of_intrinsics += r->i_mapper(node, r->ctx) ? 1 : 0; + } + } + else { + if (0 <= op->code && op->code < ARR_LEN(wenv->i_map)) { + const i_instr_record *r = wenv->i_map[op->code]; + if (r) { /* we have a mapper */ + wenv->nr_of_intrinsics += r->i_mapper(node, r->ctx) ? 1 : 0; + } + } } } /* Go through all graphs and map calls to intrinsic functions. */ -unsigned lower_intrinsic_calls(const i_record *list, int length) { - int i; - ir_graph *irg; - pmap *map = pmap_create_ex(length); - walker_env_t wenv; - unsigned nr_of_intrinsics = 0; +unsigned lower_intrinsics(const i_record *list, int length) { + int i, n_ops = get_irp_n_opcodes(); + ir_graph *irg; + pmap *c_map = pmap_create_ex(length); + const i_instr_record **i_map; + unsigned nr_of_intrinsics = 0; + walker_env_t wenv; + + /* we use the ir_op generic pointers here */ + NEW_ARR_A(const i_instr_record *, i_map, n_ops); + memset((void *)i_map, 0, sizeof(*i_map) * n_ops); /* fill a map for faster search */ - for (i = length - 1; i >= 0; --i) - pmap_insert(map, list[i].i_ent, (void *)&list[i]); + for (i = length - 1; i >= 0; --i) { + if (list->i_call.kind == INTRINSIC_CALL) { + pmap_insert(c_map, list[i].i_call.i_ent, (void *)&list[i].i_call); + } + else { + ir_op *op = list->i_instr.op; + assert(0 <= op->code && op->code < ARR_LEN(i_map)); - wenv.map = map; + i_map[op->code] = &list->i_instr; + } + } + + wenv.c_map = c_map; + wenv.i_map = i_map; for (i = get_irp_n_irgs() - 1; i >= 0; --i) { irg = get_irp_irg(i); wenv.nr_of_intrinsics = 0; - irg_walk_graph(irg, NULL, call_mapper, map); + irg_walk_graph(irg, NULL, call_mapper, &wenv); if (wenv.nr_of_intrinsics) { /* changes detected */ @@ -93,7 +120,7 @@ unsigned lower_intrinsic_calls(const i_record *list, int length) { nr_of_intrinsics += wenv.nr_of_intrinsics; } } - pmap_destroy(map); + pmap_destroy(c_map); return nr_of_intrinsics; } @@ -142,3 +169,123 @@ int i_mapper_Alloca(ir_node *call, void *ctx) { return 1; } + +#define LMAX(a, b) ((a) > (b) ? (a) : (b)) + +/* A mapper for mapping unsupported instructions to runtime calls. */ +int i_mapper_RuntimeCall(ir_node *node, runtime_rt *rt) { + int i, j, arity, first, n_param, n_res; + long n_proj; + ir_type *mtp; + ir_node *mem, *bl, *call, *addr, *res_proj; + ir_node **in; + ir_graph *irg; + symconst_symbol sym; + + /* check if the result modes match */ + if (get_irn_mode(node) != rt->mode) + return 0; + + arity = get_irn_arity(node); + if (arity <= 0) + return 0; + + mtp = get_entity_type(rt->ent); + n_param = get_method_n_params(mtp); + irg = current_ir_graph; + + mem = get_irn_n(node, 0); + if (get_irn_mode(mem) != mode_M) { + mem = new_r_NoMem(irg); + first = 0; + } + else + first = 1; + + /* check if the modes of the predecessors match the parameter modes */ + if (arity - first != n_param) + return 0; + + for (i = first, j = 0; i < arity; ++i, ++j) { + ir_type *param_tp = get_method_param_type(mtp, j); + ir_node *pred = get_irn_n(node, i); + + if (get_type_mode(param_tp) != get_irn_mode(pred)) + return 0; + } + + n_res = get_method_n_ress(mtp); + + /* step 0: calculate the number of needed Proj's */ + n_proj = 0; + n_proj = LMAX(n_proj, rt->mem_proj_nr + 1); + n_proj = LMAX(n_proj, rt->exc_proj_nr + 1); + n_proj = LMAX(n_proj, rt->exc_mem_proj_nr + 1); + n_proj = LMAX(n_proj, rt->res_proj_nr + 1); + + if (n_proj > 0) { + if (rt->mode != mode_T) /* must be mode_T */ + return 0; + } + else { + if (n_res > 0) + /* must match */ + if (get_type_mode(get_method_res_type(mtp, 0)) != rt->mode) + return 0; + } + + /* ok, when we are here, the number of predecessors match as well as the parameter modes */ + bl = get_nodes_block(node); + + in = NULL; + if (n_param > 0) { + NEW_ARR_A(ir_node *, in, n_param); + for (i = 0; i < n_param; ++i) + in[i] = get_irn_n(node, first + i); + } + + /* step 1: create the call */ + sym.entity_p = rt->ent; + addr = new_r_SymConst(irg, bl, sym, symconst_addr_ent); + call = new_rd_Call(get_irn_dbg_info(node), irg, bl, mem, addr, n_param, in, mtp); + set_irn_pinned(call, get_irn_pinned(node)); + + if (n_res > 0) + res_proj = new_r_Proj(irg, bl, call, mode_T, pn_Call_T_result); + + if (n_proj > 0) { + n_proj += n_res; + + /* we are ready */ + turn_into_tuple(node, n_proj); + + for (i = 0; i < n_proj; ++i) + set_Tuple_pred(node, i, new_r_Bad(irg)); + if (rt->mem_proj_nr > 0) + set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(irg, bl, call, mode_M, pn_Call_M_regular)); + if (rt->exc_proj_nr > 0) + set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(irg, bl, call, mode_X, pn_Call_X_except)); + if (rt->exc_mem_proj_nr > 0) + set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(irg, bl, call, mode_M, pn_Call_M_except)); + + if (rt->res_proj_nr > 0) + for (i = 0; i < n_res; ++i) + set_Tuple_pred(node, rt->res_proj_nr + i, + new_r_Proj(irg, bl, call, get_type_mode(get_method_res_type(mtp, i)), i)); + return 1; + } + else { + /* only one return value supported */ + if (n_res > 0) { + ir_mode *mode = get_type_mode(get_method_res_type(mtp, 0)); + + res_proj = new_r_Proj(irg, bl, call, mode_T, pn_Call_T_result); + res_proj = new_r_Proj(irg, bl, call, mode, 0); + + exchange(node, res_proj); + return 1; + } + } + /* should not happen */ + return 0; +} diff --git a/ir/lower/lower_intrinsics.h b/ir/lower/lower_intrinsics.h index f800cad1c..e413c48e3 100644 --- a/ir/lower/lower_intrinsics.h +++ b/ir/lower/lower_intrinsics.h @@ -24,36 +24,60 @@ /** * An intrinsic mapper function. * - * @param call the Call node + * @param node the IR-node that will be mapped * @param ctx a context * * @return non-zero if the call was mapped */ -typedef int (*i_mapper_func)(ir_node *call, void *ctx); +typedef int (*i_mapper_func)(ir_node *node, void *ctx); + +enum ikind { + INTRINSIC_CALL = 0, /**< the record represents an intrinsic call */ + INTRINSIC_INSTR /**< the record represents an intrinsic instruction */ +}; /** * An intrinsic record. */ -typedef struct _i_record { - entity *i_ent; /**< the entity representing an intrinsic */ +typedef struct _i_call_record { + enum ikind kind; /**< must be INTRINSIC_CALL */ + entity *i_ent; /**< the entity representing an intrinsic call */ + i_mapper_func i_mapper; /**< the mapper function to call */ + void *ctx; /**< mapper context */ +} i_call_record; + +/** + * An intrinsic instruction record. + */ +typedef struct _i_instr_record { + enum ikind kind; /**< must be INTRINSIC_INSTR */ + ir_op *op; /**< the opcode that must be mapped. */ i_mapper_func i_mapper; /**< the mapper function to call */ void *ctx; /**< mapper context */ +} i_instr_record; + +/** + * An intrinsic record. + */ +typedef union _i_record { + i_call_record i_call; + i_instr_record i_instr; } i_record; /** - * Go through all graphs and map calls to intrinsic functions. + * Go through all graphs and map calls to intrinsic functions and instructions. * - * Every call is reported to its mapper function, which is responsible for - * rebuilding the graph. + * Every call or instruction is reported to its mapper function, + * which is responsible for rebuilding the graph. * * current_ir_graph is always set. * * @param list an array of intrinsic map records * @param length the length of the array * - * @return number of found intrinsic calls + * @return number of found intrinsics. */ -unsigned lower_intrinsic_calls(const i_record *list, int length); +unsigned lower_intrinsics(const i_record *list, int length); /** * A mapper for the integer absolute value: inttype abs(inttype v). @@ -71,4 +95,71 @@ int i_mapper_Abs(ir_node *call, void *ctx); */ int i_mapper_Alloca(ir_node *call, void *ctx); +/** + * A runtime routine description. + */ +typedef struct _runtime_rt { + entity *ent; /**< The entity representing the runtime routine. */ + ir_mode *mode; /**< The operation mode of the mapped instruction. */ + long mem_proj_nr; /**< if >= 0, create a memory ProjM() */ + long exc_proj_nr; /**< if >= 0, create a exception ProjX() */ + long exc_mem_proj_nr; /**< if >= 0, create a exception memory ProjM() */ + long res_proj_nr; /**< if >= 0, first result projection number */ +} runtime_rt; + +/** + * A mapper for mapping unsupported instructions to runtime calls. + * Maps a op(arg_0, ..., arg_n) into a call to a runtime function + * rt(arg_0, ..., arg_n). + * + * The mapping is only done, if the modes of all arguments matches the + * modes of rt's argument. + * Further, if op has a memory input, the generated Call uses it, else + * it gets a NoMem. + * The pinned state of the Call will be set to the pinned state of op. + * + * Note that i_mapper_RuntimeCall() must be used with a i_instr_record. + * + * @return 1 if an op was mapped, 0 else + * + * Some examples: + * + * - Maps Div nodes to calls to rt_Div(): + @code + runtime_rt rt_Div = { + ent("int rt_Div(int, int)"), + mode_T, + pn_Div_M, + pn_Div_X_except, + pn_Div_M, + pn_Div_res + }; + i_instr_record map_Div = { + INTRINSIC_INSTR, + op_Div, + i_mapper_RuntimeCall, + &rt_Div + }; + @endcode + * + * - Maps ConvD(F) to calls to rt_Float2Div(): + @code + runtime_rt rt_Float2Double = { + ent("double rt_Float2Div(float)"), + get_type_mode("double"), + -1, + -1, + -1, + -1 + }; + i_instr_record map_Float2Double = { + INTRINSIC_INSTR, + op_Conv, + i_mapper_RuntimeCall, + &rt_Float2Double + }; + @endcode + */ +int i_mapper_RuntimeCall(ir_node *node, runtime_rt *rt); + #endif /* _LOWER_INTRINSICS_H_ */