/*
 * This file is part of libFirm.
 * Copyright (C) 2012 University of Karlsruhe.
 */

/**
 * @file
 * @brief       be transform helper extracted from the ia32 backend.
 * @author      Matthias Braun, Michael Beck
 * @date        14.06.2007
 */
#include "betranshlp.h"

#include "bearch.h"
#include "beirg.h"
#include "belive.h"
#include "benode.h"
#include "beutil.h"
#include "cgana.h"
#include "debug.h"
#include "execfreq_t.h"
#include "heights.h"
#include "irargs_t.h"
#include "ircons_t.h"
#include "iredges_t.h"
#include "irgmod.h"
#include "irgraph_t.h"
#include "irgwalk.h"
#include "irhooks.h"
#include "irnode_t.h"
#include "irnodemap.h"
#include "irop_t.h"
#include "iropt_t.h"
#include "irouts.h"
#include "irtools.h"
#include "panic.h"
#include "pdeq.h"
#include "util.h"
#include "vrp.h"

typedef struct be_transform_env_t {
	deq_t worklist;  /**< worklist of nodes that still need to be transformed */
} be_transform_env_t;

static be_transform_env_t env;

#ifndef NDEBUG
static void be_set_orig_node_rec(ir_node *const node, char const *const name)
{
	if (!is_Proj(node)) {
		char const **const orig = &be_get_info(node)->orig_node;
		if (*orig)
			return;
		*orig = name;
	}
	foreach_irn_in(node, i, in) {
		be_set_orig_node_rec(in, name);
	}
}

static void be_set_orig_node(ir_node *const new_node, ir_node const *const old_node)
{
	if (!is_Proj(old_node)) {
		ir_graph       *const irg  = get_irn_irg(old_node);
		struct obstack *const obst = be_get_be_obst(irg);
		lc_eoprintf(firm_get_arg_env(), obst, "%+F", old_node);
		obstack_1grow(obst, 0);
		char const *const name = (char const*)obstack_finish(obst);
		be_set_orig_node_rec(new_node, name);
	}
}
#endif

void be_set_transformed_node(ir_node *old_node, ir_node *new_node)
{
	set_irn_link(old_node, new_node);
	mark_irn_visited(old_node);
#ifndef NDEBUG
	be_set_orig_node(new_node, old_node);
#endif
}

bool be_is_transformed(const ir_node *node)
{
	return irn_visited(node);
}

ir_node *be_transform_phi(ir_node *node, const arch_register_req_t *req)
{
	ir_node  *block = be_transform_nodes_block(node);
	ir_graph *irg   = get_irn_irg(block);
	dbg_info *dbgi  = get_irn_dbg_info(node);

	/* phi nodes allow loops, so we use the old arguments for now
	 * and fix this later */
	ir_node **ins   = get_irn_in(node);
	int       arity = get_irn_arity(node);
	ir_mode  *mode  = req->cls->mode;
	ir_node  *phi   = new_ir_node(dbgi, irg, block, op_Phi, mode, arity, ins);
	copy_node_attr(irg, node, phi);

	backend_info_t *info = be_get_info(phi);
	info->in_reqs = be_allocate_in_reqs(irg, arity);
	for (int i = 0; i < arity; ++i) {
		info->in_reqs[i] = req;
	}

	arch_set_irn_register_req_out(phi, 0, req);
	be_enqueue_operands(node);

	return phi;
}

void be_set_transform_function(ir_op *op, be_transform_func func)
{
	/* Shouldn't be assigned twice. */
	assert(!op->ops.generic);
	op->ops.generic = (op_func) func;
}

void be_set_transform_proj_function(ir_op *op, be_transform_func func)
{
	op->ops.generic1 = (op_func) func;
}

/**
 * Transform helper for blocks.
 */
static ir_node *transform_block(ir_node *node)
{
	ir_node *const block = exact_copy(node);
	block->node_nr = node->node_nr;

	/* put the preds in the worklist */
	be_enqueue_operands(node);

	return block;
}

static ir_node *transform_end(ir_node *node)
{
	/* Do not transform predecessors yet to keep the pre-transform
	 * phase from visiting all the graph. */
	ir_node *const block   = be_transform_nodes_block(node);
	ir_node *const new_end = exact_copy(node);
	set_nodes_block(new_end, block);

	ir_graph *const irg = get_irn_irg(new_end);
	set_irg_end(irg, new_end);

	be_enqueue_operands(node);

	return new_end;
}

static ir_node *transform_proj(ir_node *node)
{
	ir_node *pred    = get_Proj_pred(node);
	ir_op   *pred_op = get_irn_op(pred);
	be_transform_func *proj_transform
		= (be_transform_func*)pred_op->ops.generic1;
	/* we should have a Proj transformer registered */
#ifdef DEBUG_libfirm
	if (!proj_transform) {
		unsigned const node_pn = get_Proj_num(node);
		if (is_Proj(pred)) {
			unsigned const pred_pn   = get_Proj_num(pred);
			ir_node *const pred_pred = get_Proj_pred(pred);
			panic("no transformer for %+F (%u) -> %+F (%u) -> %+F", node, node_pn, pred, pred_pn, pred_pred);
		} else {
			panic("no transformer for %+F (%u) -> %+F", node, node_pn, pred);
		}
	}
#endif
	return proj_transform(node);
}

ir_node *be_duplicate_node(ir_node *const node)
{
	int       const arity = get_irn_arity(node);
	ir_node **const ins   = ALLOCAN(ir_node*, arity);
	foreach_irn_in(node, i, in) {
		ins[i] = be_transform_node(in);
	}

	ir_node *const block    = be_transform_nodes_block(node);
	ir_node *const new_node = new_similar_node(node, block, ins);

	new_node->node_nr = node->node_nr;
	return new_node;
}

ir_node *be_gen_Proj_default(ir_node *const node)
{
  ir_node *const pred     = get_Proj_pred(node);
  ir_node *const new_pred = be_transform_node(pred);
  unsigned const pn       = get_Proj_num(node);
  return be_new_Proj(new_pred, pn);
}

ir_node *be_transform_node(ir_node *node)
{
	ir_node *new_node;
	if (be_is_transformed(node)) {
		new_node = (ir_node*)get_irn_link(node);
	} else {
#ifdef DEBUG_libfirm
		set_irn_link(node, NULL);
#endif
		mark_irn_visited(node);

		ir_op             *const op        = get_irn_op(node);
		be_transform_func *const transform = (be_transform_func*)op->ops.generic;
#ifdef DEBUG_libfirm
		if (!transform)
			panic("no transformer for %+F", node);
#endif

		new_node = transform(node);
		be_set_transformed_node(node, new_node);
	}
	assert(new_node);
	return new_node;
}

ir_node *be_transform_nodes_block(ir_node const *const node)
{
	ir_node *const block = get_nodes_block(node);
	return be_transform_node(block);
}

void be_enqueue_operands(ir_node *node)
{
	/* put the preds in the worklist */
	foreach_irn_in(node, i, pred) {
		deq_push_pointer_right(&env.worklist, pred);
	}
}

/**
 * Rewire nodes which are potential loops (like Phis) to avoid endless loops.
 */
static void fix_loops(ir_node *node)
{
	if (irn_visited_else_mark(node))
		return;

	bool changed = false;
	if (! is_Block(node)) {
		ir_node *block     = get_nodes_block(node);
		ir_node *new_block = (ir_node*)get_irn_link(block);

		if (new_block != NULL) {
			set_nodes_block(node, new_block);
			block = new_block;
			changed = true;
		}

		fix_loops(block);
	}

	foreach_irn_in(node, i, pred) {
		ir_node *in = pred;
		ir_node *nw = (ir_node*)get_irn_link(in);

		if (nw != NULL && nw != in) {
			set_irn_n(node, i, nw);
			in = nw;
			changed = true;
		}

		fix_loops(in);
	}

	if (changed) {
		identify_remember(node);
	}
}

/**
 * Transforms all nodes. Deletes the old obstack and creates a new one.
 */
static void transform_nodes(ir_graph *irg, arch_pretrans_nodes *pre_transform)
{
	inc_irg_visited(irg);

	deq_init(&env.worklist);

	ir_node *const old_anchor = irg->anchor;
	ir_node *const new_anchor = new_r_Anchor(irg);
	ir_node *const old_end    = get_irg_end(irg);
	irg->anchor = new_anchor;

	/* Pre-transform all anchors (so they are available in the other transform
	 * functions) and put them into the worklist. */
	foreach_irn_in(old_anchor, i, old) {
		ir_node *const nw = be_transform_node(old);
		set_irn_n(new_anchor, i, nw);
	}

	if (pre_transform)
		pre_transform(irg);

	/* process worklist (this should transform all nodes in the graph) */
	while (!deq_empty(&env.worklist)) {
		ir_node *node = deq_pop_pointer_left(ir_node, &env.worklist);
		be_transform_node(node);
	}

	/* Fix loops. */
	inc_irg_visited(irg);
	foreach_irn_in_r(new_anchor, i, n) {
		fix_loops(n);
	}

	deq_free(&env.worklist);
	free_End(old_end);
}

void be_transform_graph(ir_graph *irg, arch_pretrans_nodes *func)
{
	/* create a new obstack */
	struct obstack old_obst = irg->obst;
	obstack_init(&irg->obst);
	irg->last_node_idx = 0;

	free_vrp_data(irg);

	/* create new value table for CSE */
	new_identities(irg);

	/* do the main transformation */
	ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK);
	transform_nodes(irg, func);
	ir_free_resources(irg, IR_RESOURCE_IRN_LINK);

	/* free the old obstack */
	obstack_free(&old_obst, 0);

	/* most analysis info is wrong after transformation */
	be_invalidate_live_chk(irg);
	confirm_irg_properties(irg, IR_GRAPH_PROPERTIES_NONE);

	/* recalculate edges */
	edges_activate(irg);
}

bool be_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	ir_op *op = get_irn_op(node);
	if (op->ops.generic2 == NULL)
		return false;
	upper_bits_clean_func func = (upper_bits_clean_func)op->ops.generic2;
	return func(node, mode);
}

static bool bit_binop_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	return be_upper_bits_clean(get_binop_left(node), mode)
	    && be_upper_bits_clean(get_binop_right(node), mode);
}

static bool mux_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	return be_upper_bits_clean(get_Mux_true(node), mode)
	    && be_upper_bits_clean(get_Mux_false(node), mode);
}

static bool and_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	if (!mode_is_signed(mode)) {
		return be_upper_bits_clean(get_And_left(node), mode)
		    || be_upper_bits_clean(get_And_right(node), mode);
	} else {
		return bit_binop_upper_bits_clean(node, mode);
	}
}

static bool shr_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	if (mode_is_signed(mode)) {
		return false;
	} else {
		const ir_node *right = get_Shr_right(node);
		if (is_Const(right)) {
			long const val = get_Const_long(right);
			if (val >= 32 - (long)get_mode_size_bits(mode))
				return true;
		}
		return be_upper_bits_clean(get_Shr_left(node), mode);
	}
}

static bool shrs_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	return be_upper_bits_clean(get_Shrs_left(node), mode);
}

static bool const_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	long const val = get_Const_long(node);
	if (mode_is_signed(mode)) {
		long    shifted = val >> (get_mode_size_bits(mode)-1);
		return shifted == 0 || shifted == -1;
	} else {
		unsigned long shifted = (unsigned long)val;
		shifted >>= get_mode_size_bits(mode)-1;
		shifted >>= 1;
		return shifted == 0;
	}
}

static bool conv_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	ir_mode       *dest_mode = get_irn_mode(node);
	const ir_node *op        = get_Conv_op(node);
	ir_mode       *src_mode  = get_irn_mode(op);
	if (mode_is_float(src_mode))
		return true;

	unsigned src_bits  = get_mode_size_bits(src_mode);
	unsigned dest_bits = get_mode_size_bits(dest_mode);
	/* downconvs are a nop */
	if (src_bits >= dest_bits)
		return be_upper_bits_clean(op, mode);
	/* upconvs are fine if src is big enough or if sign matches */
	if (src_bits <= get_mode_size_bits(mode)
		&& mode_is_signed(src_mode) == mode_is_signed(mode))
		return true;
	return false;
}

static bool proj_upper_bits_clean(const ir_node *node, ir_mode *mode)
{
	const ir_node *pred = get_Proj_pred(node);
	switch (get_irn_opcode(pred)) {
	case iro_Load: {
		ir_mode *load_mode = get_Load_mode(pred);
		unsigned load_bits = get_mode_size_bits(load_mode);
		if (load_bits > get_mode_size_bits(mode))
			return false;
		if (mode_is_signed(load_mode) != mode_is_signed(mode))
			return false;
		return true;
	}
	default:
		break;
	}
	return false;
}

void be_set_upper_bits_clean_function(ir_op *op, upper_bits_clean_func func)
{
	op->ops.generic2 = (op_func)func;
}

void be_start_transform_setup(void)
{
	ir_clear_opcodes_generic_func();

	be_set_transform_function(op_Block, transform_block);
	be_set_transform_function(op_End,   transform_end);
	be_set_transform_function(op_NoMem, be_duplicate_node);
	be_set_transform_function(op_Pin,   be_duplicate_node);
	be_set_transform_function(op_Proj,  transform_proj);
	be_set_transform_function(op_Sync,  be_duplicate_node);

	be_set_transform_proj_function(op_ASM,    be_gen_Proj_default);
	be_set_transform_proj_function(op_Cond,   be_gen_Proj_default);
	be_set_transform_proj_function(op_Switch, be_gen_Proj_default);

	be_set_upper_bits_clean_function(op_And,   and_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Const, const_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Conv,  conv_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Eor,   bit_binop_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Mux,   mux_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Or,    bit_binop_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Proj,  proj_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Shr,   shr_upper_bits_clean);
	be_set_upper_bits_clean_function(op_Shrs,  shrs_upper_bits_clean);
}

bool be_pattern_is_rotl(ir_node const *const irn_or, ir_node **const left,
                        ir_node **const right)
{
	assert(is_Add(irn_or) || is_Or(irn_or));

	ir_mode *mode = get_irn_mode(irn_or);
	if (!mode_is_int(mode))
		return false;

	ir_node *shl = get_binop_left(irn_or);
	ir_node *shr = get_binop_right(irn_or);
	if (is_Shr(shl)) {
		if (!is_Shl(shr))
			return false;

		ir_node *tmp = shl;
		shl = shr;
		shr = tmp;
	} else if (!is_Shl(shl)) {
		return false;
	} else if (!is_Shr(shr)) {
		return false;
	}

	ir_node *x = get_Shl_left(shl);
	if (x != get_Shr_left(shr))
		return false;

	ir_node *c1 = get_Shl_right(shl);
	ir_node *c2 = get_Shr_right(shr);
	if (is_Const(c1) && is_Const(c2)) {
		ir_tarval *tv1 = get_Const_tarval(c1);
		if (!tarval_is_long(tv1))
			return false;

		ir_tarval *tv2 = get_Const_tarval(c2);
		if (!tarval_is_long(tv2))
			return false;

		if (get_tarval_long(tv1) + get_tarval_long(tv2)
		    != (long) get_mode_size_bits(mode))
			return false;

		*left  = x;
		*right = c1;
		return true;
	}

	/* Note: the obvious rot formulation (a << x) | (a >> (32-x)) gets
	 * transformed to (a << x) | (a >> -x) by transform_node_shift_modulo() */
	if (!ir_is_negated_value(c1, c2))
		return false;

	*left  = x;
	*right = c1;
	return true;
}

void be_map_exc_node_to_runtime_call(ir_node *node, ir_mode *res_mode,
                                     ir_entity *runtime_entity,
                                     long pn_M, long pn_X_regular,
                                     long pn_X_except, long pn_res)
{
	assert(is_memop(node));

	size_t    n_in = get_irn_arity(node)-1;
	ir_node **in   = ALLOCAN(ir_node*, n_in);
	ir_type  *mtp  = get_entity_type(runtime_entity);

	assert(get_method_n_params(mtp) == n_in);
	size_t p = 0;
	foreach_irn_in(node, i, n) {
		if (get_irn_mode(n) == mode_M)
			continue;
		in[p++] = n;
	}
	assert(p == n_in);

	ir_graph *irg   = get_irn_irg(node);
	dbg_info *dbgi  = get_irn_dbg_info(node);
	ir_node  *addr  = new_r_Address(irg, runtime_entity);
	ir_node  *block = get_nodes_block(node);
	ir_node  *mem   = get_memop_mem(node);
	ir_node  *call  = new_rd_Call(dbgi, block, mem, addr, n_in, in, mtp);
	set_irn_pinned(call, get_irn_pinned(node));
	int throws_exception = ir_throws_exception(node);
	ir_set_throws_exception(call, throws_exception);

	assert(pn_M < 2 && pn_res < 2 && pn_X_regular < 4 && pn_X_except < 4);
	int const         n_proj     = 4;
	int               n_operands = 2;
	ir_node   **const tuple_in   = ALLOCAN(ir_node*, n_proj);
	tuple_in[pn_M] = new_r_Proj(call, mode_M, pn_Call_M);
	ir_node *ress = new_r_Proj(call, mode_T, pn_Call_T_result);
	tuple_in[pn_res] = new_r_Proj(ress, res_mode, 0);
	if (throws_exception) {
		tuple_in[pn_X_regular]  = new_r_Proj(call, mode_X, pn_Call_X_regular);
		tuple_in[pn_X_except]   = new_r_Proj(call, mode_X, pn_Call_X_except);
		n_operands             += 2;
	}

	turn_into_tuple(node, n_operands, tuple_in);
}

static ir_heights_t *heights;

/**
 * Check if a node is somehow data dependent on another one.
 * both nodes must be in the same basic block.
 * @param n1 The first node.
 * @param n2 The second node.
 * @return 1, if n1 is data dependent (transitively) on n2, 0 if not.
 */
static int dependent_on(const ir_node *n1, const ir_node *n2)
{
	assert(get_nodes_block(n1) == get_nodes_block(n2));
	return heights_reachable_in_block(heights, n1, n2);
}

struct be_stack_change_t {
	ir_node  *before;
	unsigned  pos;
	ir_node  *after;
};

/**
 * Classical qsort() comparison function behavior:
 *
 *  0 if both elements are equal, no node depend on the other
 * +1 if first depends on second (first is greater)
 * -1 if second depends on first (second is greater)
 */
static int cmp_stack_dependency(const void *c1, const void *c2)
{
	be_stack_change_t const *const s1 = (be_stack_change_t const*)c1;
	be_stack_change_t const *const s2 = (be_stack_change_t const*)c2;

	/* Sort blockwise. */
	ir_node *const b1  = s1->before;
	ir_node *const b2  = s2->before;
	ir_node *const bl1 = get_nodes_block(b1);
	ir_node *const bl2 = get_nodes_block(b2);
	if (bl1 != bl2)
		return get_irn_idx(bl2) - get_irn_idx(bl1);

	/* If one change chain does not produce a new value, it must be the last. */
	ir_node *const n1 = s1->after;
	if (!n1)
		return 1;
	ir_node *const n2 = s2->after;
	if (!n2)
		return -1;

	/* If one change chain is data dependent on the other, it must come later.
	 * The after nodes cannot be dependent on each other, because they are unused.
	 * So compare after of one with before of the other. */
	if (dependent_on(n1, b2))
		return 1;
	if (dependent_on(n2, b1))
		return -1;

	/* The nodes have no depth order, but we need a total order because qsort()
	 * is not stable.
	 *
	 * Additionally, we need to respect transitive dependencies. Consider a
	 * Call a depending on Call b and an independent Call c.
	 * We MUST NOT order c > a and b > c. */
	unsigned h1 = get_irn_height(heights, b1);
	unsigned h2 = get_irn_height(heights, b2);
	if (h1 < h2)
		return 1;
	if (h1 > h2)
		return -1;
	/* Same height, so use a random (but stable) order */
	return get_irn_idx(n2) - get_irn_idx(n1);
}

void be_stack_init(be_stack_env_t *const env)
{
	env->changes = NEW_ARR_F(be_stack_change_t, 0);
}

void be_stack_record_chain(be_stack_env_t *const env, ir_node *const before, unsigned const pos, ir_node *const after)
{
	assert(!after || get_nodes_block(after) == get_nodes_block(before));

	be_stack_change_t const change = { before, pos, after };
	ARR_APP1(be_stack_change_t, env->changes, change);
	/* FIXME: This should be not be necessary, but not keeping the till now unused
	 * stack nodes triggers problems with out edges, because they get deactivated
	 * before be_stack_finish() is called. It should suffice to keep the last
	 * stack producer per block in be_stack_finish(). */
	if (after)
		keep_alive(after);
}

void be_stack_finish(be_stack_env_t *const env)
{
	be_stack_change_t *const changes = env->changes;
	env->changes = NULL;

	unsigned const n_changes = ARR_LEN(changes);
	if (n_changes != 0) {
		/* Order the stack changes according to their data dependencies. */
		ir_graph *const irg = get_irn_irg(changes[0].before);
		heights = heights_new(irg);
		QSORT(changes, n_changes, cmp_stack_dependency);
		heights_free(heights);

		/* Wire the stack change chains within each block, i.e. connect before of
		 * each change to after of its predecessor. */
		ir_node *prev_block = NULL;
		for (unsigned n = n_changes; n-- != 0;) {
			be_stack_change_t const *const c     = &changes[n];
			ir_node                 *const block = get_nodes_block(c->before);
			if (block == prev_block)
				set_irn_n(c[1].before, c[1].pos, c[0].after);
			prev_block = block;
		}
	}

	DEL_ARR_F(changes);
}

ir_entity **be_collect_parameter_entities(ir_graph *const irg)
{
	ir_entity  *const fun_ent    = get_irg_entity(irg);
	ir_type    *const fun_type   = get_entity_type(fun_ent);
	size_t      const n_params   = get_method_n_params(fun_type);
	ir_entity **const params     = XMALLOCNZ(ir_entity*, n_params);
	ir_type    *const frame_type = get_irg_frame_type(irg);
	for (size_t f = get_compound_n_members(frame_type); f-- > 0;) {
		ir_entity *const member = get_compound_member(frame_type, f);
		if (is_parameter_entity(member)) {
			size_t const num = get_entity_parameter_number(member);
			assert(num < n_params);
			if (params[num])
				panic("multiple entities for parameter %u in %+F found", f, irg);
			params[num] = member;
		}
	}
	return params;
}

void be_add_parameter_entity_stores_list(ir_graph *irg, unsigned n_entities,
                                         ir_entity **entities)
{
	if (n_entities == 0)
		return;

	ir_node *const frame       = get_irg_frame(irg);
	ir_node *const initial_mem = get_irg_initial_mem(irg);
	ir_node *const start_block = get_irg_start_block(irg);
	ir_node *const args        = get_irg_args(irg);

	/* all parameter entities left in the frame type require stores.
	 * (The ones passed on the stack have been moved to the arg type) */
	ir_node *first_store = NULL;
	ir_node *mem         = initial_mem;
	for (unsigned i = 0; i < n_entities; ++i) {
		ir_entity *const entity = entities[i];
		ir_type   *const tp     = get_entity_type(entity);
		size_t     const arg    = get_entity_parameter_number(entity);
		ir_node   *const addr   = new_r_Member(start_block, frame, entity);

		if (entity->attr.parameter.is_lowered_doubleword) {
			ir_type *mt          = get_entity_type(get_irg_entity(irg));
			ir_type *param_type0 = get_method_param_type(mt, arg);
			ir_type *param_type1 = get_method_param_type(mt, arg + 1);
			ir_mode *m0          = get_type_mode(param_type0);
			ir_mode *m1          = get_type_mode(param_type1);
			ir_node *val0        = new_r_Proj(args, m0, arg);
			ir_node *val1        = new_r_Proj(args, m1, arg + 1);
			ir_node *store0      = new_r_Store(start_block, mem, addr, val0,
			                                   tp, cons_none);
			ir_node *mem0        = new_r_Proj(store0, mode_M, pn_Store_M);
			size_t   offset      = get_mode_size_bytes(m0);
			ir_mode *mode_ref    = get_irn_mode(addr);
			ir_mode *mode_offs   = get_reference_offset_mode(mode_ref);
			ir_node *cnst        = new_r_Const_long(irg, mode_offs, offset);
			ir_node *next_addr   = new_r_Add(start_block, addr, cnst);
			ir_node *store1      = new_r_Store(start_block, mem0, next_addr, val1,
			                                   tp, cons_none);
			mem = new_r_Proj(store1, mode_M, pn_Store_M);
			if (first_store == NULL)
				first_store = store0;

		} else {
			ir_mode *const mode  = is_compound_type(tp) ? mode_P
			                                            : get_type_mode(tp);
			ir_node *const val   = new_r_Proj(args, mode, arg);
			ir_node *const store = new_r_Store(start_block, mem, addr, val, tp,
			                                   cons_none);
			mem = new_r_Proj(store, mode_M, pn_Store_M);
			if (first_store == NULL)
				first_store = store;
		}
	}

	edges_reroute_except(initial_mem, mem, first_store);
	set_irg_initial_mem(irg, initial_mem);
}

void be_add_parameter_entity_stores(ir_graph *irg)
{
	ir_type    *function_type = get_entity_type(get_irg_entity(irg));
	unsigned    n_parameters  = get_method_n_params(function_type);
	ir_entity **need_stores   = XMALLOCN(ir_entity*, n_parameters);
	unsigned    n_need_stores = 0;
	ir_type    *type          = get_irg_frame_type(irg);


	/* Assume that all parameter entities without an explicit offset set need a
	 * store. */
	for (size_t i = 0, n = get_compound_n_members(type); i < n; ++i) {
		ir_entity *entity = get_compound_member(type, i);
		if (!is_parameter_entity(entity))
			continue;
		if (get_entity_offset(entity) != INVALID_OFFSET)
			continue;

		assert(n_need_stores < n_parameters);
		need_stores[n_need_stores++] = entity;
	}
	be_add_parameter_entity_stores_list(irg, n_need_stores, need_stores);
	free(need_stores);
}

unsigned be_get_n_allocatable_regs(const ir_graph *irg,
                                   const arch_register_class_t *cls)
{
	unsigned *const bs = rbitset_alloca(cls->n_regs);
	be_get_allocatable_regs(irg, cls, bs);
	return rbitset_popcount(bs, cls->n_regs);
}

void be_get_allocatable_regs(ir_graph const *const irg,
                             arch_register_class_t const *const cls,
                             unsigned *const raw_bitset)
{
	be_irg_t *birg             = be_birg_from_irg(irg);
	unsigned *allocatable_regs = birg->allocatable_regs;

	rbitset_clear_all(raw_bitset, cls->n_regs);
	for (unsigned i = 0; i < cls->n_regs; ++i) {
		const arch_register_t *reg = &cls->regs[i];
		if (rbitset_is_set(allocatable_regs, reg->global_index))
			rbitset_set(raw_bitset, i);
	}
}

uint32_t be_get_tv_bits32(ir_tarval *const tv, unsigned const offset)
{
	uint32_t val;
	val  = (uint32_t)get_tarval_sub_bits(tv, offset);
	val |= (uint32_t)get_tarval_sub_bits(tv, offset + 1) <<  8;
	val |= (uint32_t)get_tarval_sub_bits(tv, offset + 2) << 16;
	val |= (uint32_t)get_tarval_sub_bits(tv, offset + 3) << 24;
	return val;
}

ir_node *be_skip_downconv(ir_node *node, bool const single_user)
{
	assert(be_mode_needs_gp_reg(get_irn_mode(node)));
	for (;;) {
		if (single_user && get_irn_n_edges(node) > 1) {
			/* we only want to skip the conv when we're the only user
			 * (because this test is used in the context of address-mode selection
			 *  and we don't want to use address mode for multiple users) */
			break;
		} else if (is_Conv(node)) {
			ir_node *const op       = get_Conv_op(node);
			ir_mode *const src_mode = get_irn_mode(op);
			if (!be_mode_needs_gp_reg(src_mode) || get_mode_size_bits(get_irn_mode(node)) > get_mode_size_bits(src_mode))
				break;
			node = op;
		} else {
			break;
		}
	}
	return node;
}

ir_node *be_skip_sameconv(ir_node *node)
{
	assert(be_mode_needs_gp_reg(get_irn_mode(node)));
	for (;;) {
		if (get_irn_n_edges(node) > 1) {
			/* we only want to skip the conv when we're the only user
			 * (because this test is used in the context of address-mode selection
			 *  and we don't want to use address mode for multiple users) */
			break;
		} else if (is_Conv(node)) {
			ir_node *const op       = get_Conv_op(node);
			ir_mode *const src_mode = get_irn_mode(op);
			if (!be_mode_needs_gp_reg(src_mode) || get_mode_size_bits(get_irn_mode(node)) != get_mode_size_bits(src_mode))
				break;
			node = op;
		} else {
			break;
		}
	}
	return node;
}

bool be_match_immediate(ir_node const *const node, ir_tarval **const tarval_out,
                        ir_entity **const entity_out, unsigned *reloc_kind_out)
{
	unsigned         reloc_kind;
	ir_entity       *entity;
	ir_node   const *cnst;
	if (is_Const(node)) {
		entity     = NULL;
		cnst       = node;
		reloc_kind = 0;
	} else if (is_Address(node)) {
		entity     = get_Address_entity(node);
		cnst       = NULL;
		reloc_kind = 0;
	} else if (be_is_Relocation(node)) {
		entity     = be_get_Relocation_entity(node);
		cnst       = NULL;
		reloc_kind = be_get_Relocation_kind(node);
	} else if (is_Add(node)) {
		ir_node const *l = get_Add_left(node);
		ir_node const *r = get_Add_right(node);
		if (be_is_Relocation(r) || is_Address(r)) {
			ir_node const *tmp = l;
			l = r;
			r = tmp;
		}
		if (!is_Const(r))
			return false;
		cnst = r;
		if (is_Address(l)) {
			entity     = get_Address_entity(l);
			reloc_kind = 0;
		} else if (be_is_Relocation(l)) {
			entity     = be_get_Relocation_entity(l);
			reloc_kind = be_get_Relocation_kind(l);
		} else {
			return false;
		}
	} else {
		return false;
	}

	if (entity && is_tls_entity(entity))
		return false;

	*tarval_out     = cnst ? get_Const_tarval(cnst) : NULL;
	*entity_out     = entity;
	*reloc_kind_out = reloc_kind;
	return true;
}

ir_node *be_make_Sync(ir_node *const block, int const arity, ir_node **const ins)
{
	return
		arity == 1 ? ins[0] :
		new_r_Sync(block, arity, ins);
}

unsigned be_get_out_for_reg(ir_node const *const node, arch_register_t const *const reg)
{
	be_foreach_out(node, o) {
		arch_register_req_t const *const req = arch_get_irn_register_req_out(node, o);
		if (req == reg->single_req)
			return o;
	}
	panic("register requirement not found");
}
