Commit 6fa4cdf1 authored by Matthias Braun's avatar Matthias Braun
Browse files

change intrinsic lowering API

- You can now construct the lookup table separately and perform the
  lowering selectively on irgs (instead of the whole program at once)
  while reusing the lookup table.
- Simplified API a bit.
- Remove i_mapper_RuntimeCall. All users are simpler by doing the
  transformations directly instead of filling in runtime_rt
  structures...
parent fbf1c649
......@@ -113,11 +113,9 @@ FIRM_API void lower_mux(ir_graph *irg, lower_mux_callback *cb_func);
* An intrinsic mapper function.
*
* @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 *node, void *ctx);
typedef int (i_mapper_func)(ir_node *node);
/** kind of an instruction record
* @see #i_record */
......@@ -132,9 +130,7 @@ enum ikind {
typedef struct i_call_record {
enum ikind kind; /**< must be INTRINSIC_CALL */
ir_entity *i_ent; /**< the entity representing an intrinsic call */
i_mapper_func i_mapper; /**< the mapper function to call */
void *ctx; /**< mapper context */
void *link; /**< used in the construction algorithm, must be NULL */
i_mapper_func *i_mapper; /**< the mapper function to call */
} i_call_record;
/**
......@@ -143,9 +139,7 @@ typedef struct i_call_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 */
void *link; /**< used in the construction algorithm, must be NULL */
i_mapper_func *i_mapper; /**< the mapper function to call */
} i_instr_record;
/**
......@@ -157,20 +151,33 @@ typedef union i_record {
i_instr_record i_instr; /**< used for isnstruction records */
} i_record;
typedef struct ir_intrinsics_map ir_intrinsics_map;
/**
* Go through all graphs and map calls to intrinsic functions and instructions.
*
* Every call or instruction is reported to its mapper function,
* which is responsible for rebuilding the graph.
* Create a new intrinsic lowering map which can be used by ir_lower_intrinsics.
*
* @param list an array of intrinsic map records
* @param length the length of the array
* @param part_block_used set to true if part_block() must be using during lowering
* @param part_block_used set to 1 if part_block() is used by one of the
* lowering functions.
*/
FIRM_API ir_intrinsics_map *ir_create_intrinsics_map(i_record *list,
size_t length,
int part_block_used);
/**
* Frees resources occupied by an intrisics_map created by
* ir_create_intrinsics_map().
*/
FIRM_API void ir_free_intrinsics_map(ir_intrinsics_map *map);
/**
* Go through all graphs and map calls to intrinsic functions and instructions.
*
* @return number of found intrinsics.
* Every call or instruction is reported to its mapper function,
* which is responsible for rebuilding the graph.
*/
FIRM_API size_t lower_intrinsics(i_record *list, size_t length,
int part_block_used);
FIRM_API void ir_lower_intrinsics(ir_graph *irg, ir_intrinsics_map *map);
/**
* A mapper for the integer/float absolute value: type abs(type v).
......@@ -178,7 +185,7 @@ FIRM_API size_t lower_intrinsics(i_record *list, size_t length,
*
* @return always 1
*/
FIRM_API int i_mapper_abs(ir_node *call, void *ctx);
FIRM_API int i_mapper_abs(ir_node *call);
/**
* A mapper for the integer byte swap value: type bswap(type v).
......@@ -186,196 +193,196 @@ FIRM_API int i_mapper_abs(ir_node *call, void *ctx);
*
* @return always 1
*/
FIRM_API int i_mapper_bswap(ir_node *call, void *ctx);
FIRM_API int i_mapper_bswap(ir_node *call);
/**
* A mapper for the floating point sqrt(v): floattype sqrt(floattype v);
*
* @return 1 if the sqrt call was removed, 0 else.
*/
FIRM_API int i_mapper_sqrt(ir_node *call, void *ctx);
FIRM_API int i_mapper_sqrt(ir_node *call);
/**
* A mapper for the floating point cbrt(v): floattype sqrt(floattype v);
*
* @return 1 if the cbrt call was removed, 0 else.
*/
FIRM_API int i_mapper_cbrt(ir_node *call, void *ctx);
FIRM_API int i_mapper_cbrt(ir_node *call);
/**
* A mapper for the floating point pow(a, b): floattype pow(floattype a, floattype b);
*
* @return 1 if the pow call was removed, 0 else.
*/
FIRM_API int i_mapper_pow(ir_node *call, void *ctx);
FIRM_API int i_mapper_pow(ir_node *call);
/**
* A mapper for the floating point exp(a): floattype exp(floattype a);
*
* @return 1 if the exp call was removed, 0 else.
*/
FIRM_API int i_mapper_exp(ir_node *call, void *ctx);
FIRM_API int i_mapper_exp(ir_node *call);
/**
* A mapper for the floating point exp2(a): floattype exp2(floattype a);
*
* @return 1 if the exp call was removed, 0 else.
*/
FIRM_API int i_mapper_exp2(ir_node *call, void *ctx);
FIRM_API int i_mapper_exp2(ir_node *call);
/**
* A mapper for the floating point exp10(a): floattype exp10(floattype a);
*
* @return 1 if the exp call was removed, 0 else.
*/
FIRM_API int i_mapper_exp10(ir_node *call, void *ctx);
FIRM_API int i_mapper_exp10(ir_node *call);
/**
* A mapper for the floating point log(a): floattype log(floattype a);
*
* @return 1 if the log call was removed, 0 else.
*/
FIRM_API int i_mapper_log(ir_node *call, void *ctx);
FIRM_API int i_mapper_log(ir_node *call);
/**
* A mapper for the floating point log(a): floattype log(floattype a);
*
* @return 1 if the log call was removed, 0 else.
*/
FIRM_API int i_mapper_log2(ir_node *call, void *ctx);
FIRM_API int i_mapper_log2(ir_node *call);
/**
* A mapper for the floating point log(a): floattype log(floattype a);
*
* @return 1 if the log call was removed, 0 else.
*/
FIRM_API int i_mapper_log10(ir_node *call, void *ctx);
FIRM_API int i_mapper_log10(ir_node *call);
/**
* A mapper for the floating point sin(a): floattype sin(floattype a);
*
* @return 1 if the sin call was removed, 0 else.
*/
FIRM_API int i_mapper_sin(ir_node *call, void *ctx);
FIRM_API int i_mapper_sin(ir_node *call);
/**
* A mapper for the floating point sin(a): floattype cos(floattype a);
*
* @return 1 if the cos call was removed, 0 else.
*/
FIRM_API int i_mapper_cos(ir_node *call, void *ctx);
FIRM_API int i_mapper_cos(ir_node *call);
/**
* A mapper for the floating point tan(a): floattype tan(floattype a);
*
* @return 1 if the tan call was removed, 0 else.
*/
FIRM_API int i_mapper_tan(ir_node *call, void *ctx);
FIRM_API int i_mapper_tan(ir_node *call);
/**
* A mapper for the floating point asin(a): floattype asin(floattype a);
*
* @return 1 if the asin call was removed, 0 else.
*/
FIRM_API int i_mapper_asin(ir_node *call, void *ctx);
FIRM_API int i_mapper_asin(ir_node *call);
/**
* A mapper for the floating point acos(a): floattype acos(floattype a);
*
* @return 1 if the tan call was removed, 0 else.
*/
FIRM_API int i_mapper_acos(ir_node *call, void *ctx);
FIRM_API int i_mapper_acos(ir_node *call);
/**
* A mapper for the floating point atan(a): floattype atan(floattype a);
*
* @return 1 if the atan call was removed, 0 else.
*/
FIRM_API int i_mapper_atan(ir_node *call, void *ctx);
FIRM_API int i_mapper_atan(ir_node *call);
/**
* A mapper for the floating point sinh(a): floattype sinh(floattype a);
*
* @return 1 if the sinh call was removed, 0 else.
*/
FIRM_API int i_mapper_sinh(ir_node *call, void *ctx);
FIRM_API int i_mapper_sinh(ir_node *call);
/**
* A mapper for the floating point cosh(a): floattype cosh(floattype a);
*
* @return 1 if the cosh call was removed, 0 else.
*/
FIRM_API int i_mapper_cosh(ir_node *call, void *ctx);
FIRM_API int i_mapper_cosh(ir_node *call);
/**
* A mapper for the floating point tanh(a): floattype tanh(floattype a);
*
* @return 1 if the tanh call was removed, 0 else.
*/
FIRM_API int i_mapper_tanh(ir_node *call, void *ctx);
FIRM_API int i_mapper_tanh(ir_node *call);
/**
* A mapper for the strcmp-Function: inttype strcmp(char pointer a, char pointer b);
*
* @return 1 if the strcmp call was removed, 0 else.
*/
FIRM_API int i_mapper_strcmp(ir_node *call, void *ctx);
FIRM_API int i_mapper_strcmp(ir_node *call);
/**
* A mapper for the strncmp-Function: inttype strncmp(char pointer a, char pointer b, inttype len);
*
* @return 1 if the strncmp call was removed, 0 else.
*/
FIRM_API int i_mapper_strncmp(ir_node *call, void *ctx);
FIRM_API int i_mapper_strncmp(ir_node *call);
/**
* A mapper for the strcpy-Function: char pointer strcpy(char pointer a, char pointer b);
*
* @return 1 if the strcpy call was removed, 0 else.
*/
FIRM_API int i_mapper_strcpy(ir_node *call, void *ctx);
FIRM_API int i_mapper_strcpy(ir_node *call);
/**
* A mapper for the strlen-Function: inttype strlen(char pointer a);
*
* @return 1 if the strlen call was removed, 0 else.
*/
FIRM_API int i_mapper_strlen(ir_node *call, void *ctx);
FIRM_API int i_mapper_strlen(ir_node *call);
/**
* A mapper for the memcpy-Function: void pointer memcpy(void pointer d, void pointer s, inttype c);
*
* @return 1 if the memcpy call was removed, 0 else.
*/
FIRM_API int i_mapper_memcpy(ir_node *call, void *ctx);
FIRM_API int i_mapper_memcpy(ir_node *call);
/**
* A mapper for the mempcpy-Function: void pointer mempcpy(void pointer d, void pointer s, inttype c);
*
* @return 1 if the mempcpy call was removed, 0 else.
*/
FIRM_API int i_mapper_mempcpy(ir_node *call, void *ctx);
FIRM_API int i_mapper_mempcpy(ir_node *call);
/**
* A mapper for the memmove-Function: void pointer memmove(void pointer d, void pointer s, inttype c);
*
* @return 1 if the memmove call was removed, 0 else.
*/
FIRM_API int i_mapper_memmove(ir_node *call, void *ctx);
FIRM_API int i_mapper_memmove(ir_node *call);
/**
* A mapper for the memset-Function: void pointer memset(void pointer d, inttype C, inttype len);
*
* @return 1 if the memset call was removed, 0 else.
*/
FIRM_API int i_mapper_memset(ir_node *call, void *ctx);
FIRM_API int i_mapper_memset(ir_node *call);
/**
* A mapper for the strncmp-Function: inttype memcmp(void pointer a, void pointer b, inttype len);
*
* @return 1 if the strncmp call was removed, 0 else.
*/
FIRM_API int i_mapper_memcmp(ir_node *call, void *ctx);
FIRM_API int i_mapper_memcmp(ir_node *call);
/**
* A mapper for the alloca() function: pointer alloca(inttype size)
......@@ -383,81 +390,7 @@ FIRM_API int i_mapper_memcmp(ir_node *call, void *ctx);
*
* @return always 1
*/
FIRM_API int i_mapper_alloca(ir_node *call, void *ctx);
/**
* A runtime routine description.
*/
typedef struct runtime_rt {
ir_entity *ent; /**< The entity representing the runtime routine. */
ir_mode *mode; /**< The operation mode of the mapped instruction. */
ir_mode *res_mode; /**< The result mode of the mapped instruction or NULL. */
long mem_proj_nr; /**< if >= 0, create a memory ProjM() */
long regular_proj_nr; /**< if >= 0, create a regular ProjX() */
long exc_proj_nr; /**< if >= 0, create a exception ProjX() */
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 signed Div nodes to calls to rt_Div():
@code
runtime_rt rt_Div = {
ent("int rt_Div(int, int)"),
mode_T,
mode_Is,
pn_Div_M,
pn_Div_X_regular,
pn_Div_X_except,
pn_Div_M,
pn_Div_res
};
i_instr_record map_Div = {
INTRINSIC_INSTR,
op_Div,
i_mapper_RuntimeCall,
&rt_Div,
NULL
};
@endcode
*
* - Maps ConvD(F) to calls to rt_Float2Div():
@code
runtime_rt rt_Float2Double = {
ent("double rt_Float2Div(float)"),
get_type_mode("double"),
NULL,
-1,
-1,
-1,
-1,
-1
};
i_instr_record map_Float2Double = {
INTRINSIC_INSTR,
op_Conv,
i_mapper_RuntimeCall,
&rt_Float2Double,
NULL
};
@endcode
*/
FIRM_API int i_mapper_RuntimeCall(ir_node *node, runtime_rt *rt);
FIRM_API int i_mapper_alloca(ir_node *call);
/** @} */
......
......@@ -37,6 +37,7 @@
#include "belistsched.h"
#include "beflags.h"
#include "bestack.h"
#include "betranshlp.h"
#include "bearch_arm_t.h"
......@@ -238,128 +239,75 @@ static void arm_before_ra(ir_graph *irg)
be_sched_fix_flags(irg, &arm_reg_classes[CLASS_arm_flags], NULL, NULL);
}
static ir_entity *divsi3;
static ir_entity *udivsi3;
static ir_entity *modsi3;
static ir_entity *umodsi3;
static void handle_intrinsic(ir_node *node, void *data)
{
(void)data;
if (is_Div(node)) {
ir_mode *mode = get_Div_resmode(node);
if (get_mode_arithmetic(mode) == irma_twos_complement) {
ir_entity *entity = mode_is_signed(mode) ? divsi3 : udivsi3;
be_map_exc_node_to_runtime_call(node, mode, entity, pn_Div_M,
pn_Div_X_regular, pn_Div_X_except,
pn_Div_res);
}
}
if (is_Mod(node)) {
ir_mode *mode = get_Mod_resmode(node);
assert(get_mode_arithmetic(mode) == irma_twos_complement);
ir_entity *entity = mode_is_signed(mode) ? modsi3 : umodsi3;
be_map_exc_node_to_runtime_call(node, mode, entity, pn_Mod_M,
pn_Mod_X_regular, pn_Mod_X_except,
pn_Mod_res);
}
}
static void arm_create_runtime_entities(void)
{
if (divsi3 != NULL)
return;
ir_type *int_tp = get_type_for_mode(mode_Is);
ir_type *uint_tp = get_type_for_mode(mode_Iu);
ir_type *tp_divsi3 = new_type_method(2, 1);
set_method_param_type(tp_divsi3, 0, int_tp);
set_method_param_type(tp_divsi3, 1, int_tp);
set_method_res_type(tp_divsi3, 0, int_tp);
divsi3 = create_compilerlib_entity(new_id_from_str("__divsi3"), tp_divsi3);
ir_type *tp_udivsi3 = new_type_method(2, 1);
set_method_param_type(tp_udivsi3, 0, uint_tp);
set_method_param_type(tp_udivsi3, 1, uint_tp);
set_method_res_type(tp_udivsi3, 0, uint_tp);
udivsi3 = create_compilerlib_entity(new_id_from_str("__udivsi3"), tp_udivsi3);
ir_type *tp_modsi3 = new_type_method(2, 1);
set_method_param_type(tp_modsi3, 0, int_tp);
set_method_param_type(tp_modsi3, 1, int_tp);
set_method_res_type(tp_modsi3, 0, int_tp);
modsi3 = create_compilerlib_entity(new_id_from_str("__modsi3"), tp_modsi3);
ir_type *tp_umodsi3 = new_type_method(2, 1);
set_method_param_type(tp_umodsi3, 0, uint_tp);
set_method_param_type(tp_umodsi3, 1, uint_tp);
set_method_res_type(tp_umodsi3, 0, uint_tp);
umodsi3 = create_compilerlib_entity(new_id_from_str("__umodsi3"), tp_umodsi3);
}
/**
* Maps all intrinsic calls that the backend support
* and map all instructions the backend did not support
* to runtime calls.
*/
static void arm_handle_intrinsics(void)
static void arm_handle_intrinsics(ir_graph *irg)
{
ir_type *tp, *int_tp, *uint_tp;
i_record records[8];
int n_records = 0;
runtime_rt rt_iDiv, rt_uDiv, rt_iMod, rt_uMod;
#define ID(x) new_id_from_chars(x, sizeof(x)-1)
int_tp = get_type_for_mode(mode_Is);
uint_tp = get_type_for_mode(mode_Iu);
/* ARM has neither a signed div instruction ... */
{
i_instr_record *map_Div = &records[n_records++].i_instr;
tp = new_type_method(2, 1);
set_method_param_type(tp, 0, int_tp);
set_method_param_type(tp, 1, int_tp);
set_method_res_type(tp, 0, int_tp);
rt_iDiv.ent = new_entity(get_glob_type(), ID("__divsi3"), tp);
set_entity_ld_ident(rt_iDiv.ent, ID("__divsi3"));
rt_iDiv.mode = mode_T;
rt_iDiv.res_mode = mode_Is;
rt_iDiv.mem_proj_nr = pn_Div_M;
rt_iDiv.regular_proj_nr = pn_Div_X_regular;
rt_iDiv.exc_proj_nr = pn_Div_X_except;
rt_iDiv.res_proj_nr = pn_Div_res;
add_entity_linkage(rt_iDiv.ent, IR_LINKAGE_CONSTANT);
set_entity_visibility(rt_iDiv.ent, ir_visibility_external);
map_Div->kind = INTRINSIC_INSTR;
map_Div->op = op_Div;
map_Div->i_mapper = (i_mapper_func)i_mapper_RuntimeCall;
map_Div->ctx = &rt_iDiv;
}
/* ... nor an unsigned div instruction ... */
{
i_instr_record *map_Div = &records[n_records++].i_instr;
tp = new_type_method(2, 1);
set_method_param_type(tp, 0, uint_tp);
set_method_param_type(tp, 1, uint_tp);
set_method_res_type(tp, 0, uint_tp);
rt_uDiv.ent = new_entity(get_glob_type(), ID("__udivsi3"), tp);
set_entity_ld_ident(rt_uDiv.ent, ID("__udivsi3"));
rt_uDiv.mode = mode_T;
rt_uDiv.res_mode = mode_Iu;
rt_uDiv.mem_proj_nr = pn_Div_M;
rt_uDiv.regular_proj_nr = pn_Div_X_regular;
rt_uDiv.exc_proj_nr = pn_Div_X_except;
rt_uDiv.res_proj_nr = pn_Div_res;
set_entity_visibility(rt_uDiv.ent, ir_visibility_external);
map_Div->kind = INTRINSIC_INSTR;
map_Div->op = op_Div;
map_Div->i_mapper = (i_mapper_func)i_mapper_RuntimeCall;
map_Div->ctx = &rt_uDiv;
}
/* ... nor a signed mod instruction ... */
{
i_instr_record *map_Mod = &records[n_records++].i_instr;
tp = new_type_method(2, 1);
set_method_param_type(tp, 0, int_tp);
set_method_param_type(tp, 1, int_tp);
set_method_res_type(tp, 0, int_tp);
rt_iMod.ent = new_entity(get_glob_type(), ID("__modsi3"), tp);
set_entity_ld_ident(rt_iMod.ent, ID("__modsi3"));
rt_iMod.mode = mode_T;
rt_iMod.res_mode = mode_Is;
rt_iMod.mem_proj_nr = pn_Mod_M;
rt_iMod.regular_proj_nr = pn_Mod_X_regular;
rt_iMod.exc_proj_nr = pn_Mod_X_except;
rt_iMod.res_proj_nr = pn_Mod_res;
set_entity_visibility(rt_iMod.ent, ir_visibility_external);
map_Mod->kind = INTRINSIC_INSTR;
map_Mod->op = op_Mod;
map_Mod->i_mapper = (i_mapper_func)i_mapper_RuntimeCall;
map_Mod->ctx = &rt_iMod;
}
/* ... nor an unsigned mod. */
{
i_instr_record *map_Mod = &records[n_records++].i_instr;
tp = new_type_method(2, 1);
set_method_param_type(tp, 0, uint_tp);
set_method_param_type(tp, 1, uint_tp);
set_method_res_type(tp, 0, uint_tp);
rt_uMod.ent = new_entity(get_glob_type(), ID("__umodsi3"), tp);
set_entity_ld_ident(rt_uMod.ent, ID("__umodsi3"));
rt_uMod.mode = mode_T;
rt_uMod.res_mode = mode_Iu;
rt_uMod.mem_proj_nr = pn_Mod_M;
rt_uMod.regular_proj_nr = pn_Mod_X_regular;
rt_uMod.exc_proj_nr = pn_Mod_X_except;
rt_uMod.res_proj_nr = pn_Mod_res;
set_entity_visibility(rt_uMod.ent, ir_visibility_external);
map_Mod->kind = INTRINSIC_INSTR;
map_Mod->op = op_Mod;
map_Mod->i_mapper = (i_mapper_func)i_mapper_RuntimeCall;
map_Mod->ctx = &rt_uMod;
}
if (n_records > 0)
lower_intrinsics(records, n_records, /*part_block_used=*/0);
arm_create_runtime_entities();
irg_walk_graph(irg, handle_intrinsic, NULL, NULL);
}
extern const arch_isa_if_t arm_isa_if;
......
......@@ -462,7 +462,7 @@ struct arch_isa_if_t {
* Called directly after initialization. Backend should handle all
* intrinsics here.
*/
void (*handle_intrinsics)(void);
void (*handle_intrinsics)(ir_graph *irg);
/**
* Called, when the graph is being normalized.
......@@ -488,8 +488,6 @@ struct arch_isa_if_t {
};
#define arch_env_end_codegeneration(env) ((env)->impl->end_codegeneration(env))
#define arch_env_handle_intrinsics(env) \
do { if((env)->impl->handle_intrinsics != NULL) (env)->impl->handle_intrinsics(); } while(0)
#define arch_env_get_call_abi(env,tp,abi) ((env)->impl->get_call_abi((tp), (abi)))
#define arch_env_mark_remat(env,node) \
do { if ((env)->impl->mark_remat != NULL) (env)->impl->mark_remat((node)); } while(0)
......
......@@ -482,6 +482,7 @@ static void be_main_loop(FILE *file_handle, const char *cup_name)
be_main_env_t env;
be_init_env(&env, cup_name);
be_info_init();
arch_env_t *arch_env = env.arch_env;
be_emit_init(file_handle);
be_gas_begin_compilation_unit(&env);
......@@ -497,9 +498,9 @@ static void be_main_loop(FILE *file_handle, const char *cup_name)
if (get_entity_linkage(entity) & IR_LINKAGE_NO_CODEGEN)
continue;
initialize_birg(&birgs[num_birgs++], irg, &env);
if (arch_env->impl->handle_intrinsics)
arch_env->impl->handle_intrinsics(irg);
}