lower_copyb.c 6 KB
Newer Older
Michael Beck's avatar
Michael Beck committed
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
Michael Beck's avatar
Michael Beck committed
4
5
6
7
 */

/**
 * @file
8
9
 * @brief   Lower small CopyB nodes into a series of Load/Store nodes
 * @author  Michael Beck, Matthias Braun, Manuel Mohr
Michael Beck's avatar
Michael Beck committed
10
 */
Michael Beck's avatar
Michael Beck committed
11
#include "adt/list.h"
Michael Beck's avatar
Michael Beck committed
12
13
14
#include "ircons.h"
#include "lowering.h"
#include "irprog_t.h"
15
#include "irgwalk.h"
Michael Beck's avatar
Michael Beck committed
16
17
#include "irnode_t.h"
#include "type_t.h"
18
#include "irgmod.h"
Matthias Braun's avatar
Matthias Braun committed
19
#include "panic.h"
20
#include "be.h"
21
#include "util.h"
22

23
24
25
26
static unsigned max_small_size; /**< The maximum size of a CopyB node
                                     so that it is regarded as 'small'. */
static unsigned min_large_size; /**< The minimum size of a CopyB node
                                     so that it is regarded as 'large'. */
27
static unsigned native_mode_bytes; /**< The size of the native mode in bytes. */
28
29
static bool allow_misalignments; /**< Whether backend can handle misaligned
                                      loads and stores. */
30

Michael Beck's avatar
Michael Beck committed
31
typedef struct walk_env {
32
	ir_node **copybs; /**< The list of CopyB nodes. */
Michael Beck's avatar
Michael Beck committed
33
} walk_env_t;
34

35
static ir_mode *get_ir_mode(unsigned mode_bytes)
36
{
37
	switch (mode_bytes) {
38
	case 1:  return mode_Bu;
39
40
41
42
43
	case 2:  return mode_Hu;
	case 4:  return mode_Iu;
	case 8:  return mode_Lu;
	default:
		panic("unexpected mode size requested in copyb lowering");
Michael Beck's avatar
Michael Beck committed
44
45
46
47
	}
}

/**
48
 * Turn a small CopyB node into a series of Load/Store nodes.
Michael Beck's avatar
Michael Beck committed
49
 */
50
static void lower_small_copyb_node(ir_node *irn)
51
{
52
	ir_graph *irg        = get_irn_irg(irn);
53
54
55
56
57
	ir_node  *block      = get_nodes_block(irn);
	ir_type  *tp         = get_CopyB_type(irn);
	ir_node  *addr_src   = get_CopyB_src(irn);
	ir_node  *addr_dst   = get_CopyB_dst(irn);
	ir_node  *mem        = get_CopyB_mem(irn);
58
	ir_mode  *mode_ref   = get_irn_mode(addr_src);
59
60
61
	unsigned  mode_bytes = allow_misalignments ? native_mode_bytes
	                                           : get_type_alignment(tp);
	unsigned  size       = get_type_size(tp);
62
63
	unsigned  offset     = 0;

Michael Beck's avatar
Michael Beck committed
64
	while (offset < size) {
65
		ir_mode *mode = get_ir_mode(mode_bytes);
Michael Beck's avatar
Michael Beck committed
66
		for (; offset + mode_bytes <= size; offset += mode_bytes) {
67
			ir_mode *mode_ref_int = get_reference_offset_mode(mode_ref);
68

69
			/* construct offset */
70
			ir_node *addr_const = new_r_Const_long(irg, mode_ref_int, offset);
71
			ir_node *add        = new_r_Add(block, addr_src, addr_const);
72

73
			ir_node *load     = new_r_Load(block, mem, add, mode, tp, cons_none);
74
75
			ir_node *load_res = new_r_Proj(load, mode, pn_Load_res);
			ir_node *load_mem = new_r_Proj(load, mode_M, pn_Load_M);
76

77
			ir_node *addr_const2 = new_r_Const_long(irg, mode_ref_int, offset);
78
			ir_node *add2        = new_r_Add(block, addr_dst, addr_const2);
79

80
			ir_node *store     = new_r_Store(block, load_mem, add2, load_res,
81
			                                 tp, cons_none);
82
			ir_node *store_mem = new_r_Proj(store, mode_M, pn_Store_M);
83
84
85
86
87

			mem = store_mem;
		}

		mode_bytes /= 2;
Michael Beck's avatar
Michael Beck committed
88
	}
89

90
	exchange(irn, mem);
91
92
}

Andreas Zwinkau's avatar
Andreas Zwinkau committed
93
static ir_type *get_memcpy_methodtype(void)
94
{
95
	ir_type *tp          = new_type_method(3, 1, false);
Manuel Mohr's avatar
Manuel Mohr committed
96
	ir_mode *size_t_mode = get_ir_mode(native_mode_bytes);
97
98
99

	set_method_param_type(tp, 0, get_type_for_mode(mode_P));
	set_method_param_type(tp, 1, get_type_for_mode(mode_P));
100
	set_method_param_type(tp, 2, get_type_for_mode(size_t_mode));
101
102
103
104
105
	set_method_res_type  (tp, 0, get_type_for_mode(mode_P));

	return tp;
}

106
static ir_node *get_memcpy_address(ir_graph *irg)
107
108
109
{
	ident     *id  = new_id_from_str("memcpy");
	ir_type   *mt  = get_memcpy_methodtype();
110
	ir_entity *ent = create_compilerlib_entity(id, mt);
111

112
	return new_r_Address(irg, ent);
113
114
115
116
117
118
119
120
121
122
123
124
125
126
}

/**
 * Turn a large CopyB node into a memcpy call.
 */
static void lower_large_copyb_node(ir_node *irn)
{
	ir_graph *irg      = get_irn_irg(irn);
	ir_node  *block    = get_nodes_block(irn);
	dbg_info *dbgi     = get_irn_dbg_info(irn);
	ir_node  *mem      = get_CopyB_mem(irn);
	ir_node  *addr_src = get_CopyB_src(irn);
	ir_node  *addr_dst = get_CopyB_dst(irn);
	ir_type  *copyb_tp = get_CopyB_type(irn);
127
	unsigned  size     = get_type_size(copyb_tp);
128

129
	ir_node  *callee      = get_memcpy_address(irg);
130
131
	ir_type  *call_tp     = get_memcpy_methodtype();
	ir_mode  *mode_size_t = get_ir_mode(native_mode_bytes);
132
133
	ir_node  *size_cnst   = new_r_Const_long(irg, mode_size_t, size);
	ir_node  *in[]        = { addr_dst, addr_src, size_cnst };
134
	ir_node  *call        = new_rd_Call(dbgi, block, mem, callee, ARRAY_SIZE(in), in, call_tp);
135
	ir_node  *call_mem    = new_r_Proj(call, mode_M, pn_Call_M);
136

137
	exchange(irn, call_mem);
138
139
}

140
static void lower_copyb_node(ir_node *irn)
141
142
{
	ir_type *tp   = get_CopyB_type(irn);
143
	unsigned size = get_type_size(tp);
144
145

	if (size <= max_small_size)
146
		lower_small_copyb_node(irn);
147
148
149
	else if (size >= min_large_size)
		lower_large_copyb_node(irn);
	else
150
		panic("CopyB of invalid size");
151
152
}

Michael Beck's avatar
Michael Beck committed
153
/**
154
 * Post-Walker: find CopyB nodes.
Michael Beck's avatar
Michael Beck committed
155
 */
156
157
static void find_copyb_nodes(ir_node *irn, void *ctx)
{
158
	if (!is_CopyB(irn))
Michael Beck's avatar
Michael Beck committed
159
160
		return;

161
	ir_type *tp = get_CopyB_type(irn);
Michael Beck's avatar
Michael Beck committed
162
163
164
	if (get_type_state(tp) != layout_fixed)
		return;

165
	unsigned size         = get_type_size(tp);
166
	bool     medium_sized = max_small_size < size && size < min_large_size;
167
168
	if (medium_sized)
		return; /* Nothing to do for medium-sized CopyBs. */
Michael Beck's avatar
Michael Beck committed
169

170
	/* Okay, either small or large CopyB, so link it in and lower it later. */
171
	walk_env_t *env = (walk_env_t*)ctx;
172
	ARR_APP1(ir_node*, env->copybs, irn);
Michael Beck's avatar
Michael Beck committed
173
174
}

175
176
void lower_CopyB(ir_graph *irg, unsigned max_small_sz, unsigned min_large_sz,
                 int allow_misaligns)
177
{
178
179
	const backend_params *bparams = be_get_backend_param();

180
	assert(max_small_sz < min_large_sz && "CopyB size ranges must not overlap");
181

182
183
184
185
	max_small_size      = max_small_sz;
	min_large_size      = min_large_sz;
	native_mode_bytes   = bparams->machine_size / 8;
	allow_misalignments = allow_misaligns;
186

187
	walk_env_t env = { .copybs = NEW_ARR_F(ir_node*, 0) };
Michael Beck's avatar
Michael Beck committed
188
189
	irg_walk_graph(irg, NULL, find_copyb_nodes, &env);

190
	bool changed = false;
191
192
	for (size_t i = 0, n = ARR_LEN(env.copybs); i != n; ++i) {
		lower_copyb_node(env.copybs[i]);
193
		changed = true;
Michael Beck's avatar
Michael Beck committed
194
	}
195
196
	confirm_irg_properties(irg, changed ? IR_GRAPH_PROPERTIES_CONTROL_FLOW
	                                    : IR_GRAPH_PROPERTIES_ALL);
197

198
	DEL_ARR_F(env.copybs);
Michael Beck's avatar
Michael Beck committed
199
}