ia32_fpu.c 6.83 KB
Newer Older
Christian Würdig's avatar
Christian Würdig committed
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
Christian Würdig's avatar
Christian Würdig committed
4
5
 */

6
7
8
9
10
11
12
13
14
15
/**
 * @file
 * @brief   Handles fpu rounding modes
 * @author  Matthias Braun
 *
 * The problem we deal with here is that the x86 ABI says the user can control
 * the fpu rounding mode, which means that when we do some operations like float
 * to int conversion which are specified as truncation in the C standard we have
 * to spill, change and restore the fpu rounding mode between spills.
 */
16
#include "bearch_ia32_t.h"
17
18
#include "ia32_fpu.h"
#include "ia32_new_nodes.h"
19
#include "ia32_architecture.h"
20
#include "ia32_transform.h"
21
22
23
24
25
26
27
#include "gen_ia32_regalloc_if.h"

#include "ircons.h"
#include "irgwalk.h"
#include "tv.h"
#include "array.h"

28
29
30
31
32
#include "bearch.h"
#include "besched.h"
#include "benode.h"
#include "bestate.h"
#include "bessaconstr.h"
33

34
35
36
static ir_entity *fpcw_round    = NULL;
static ir_entity *fpcw_truncate = NULL;

37
static ir_entity *create_ent(ir_entity **const dst, int value, const char *name)
38
{
39
40
41
	if (!*dst) {
		ir_mode   *const mode = mode_Hu;
		ir_type   *const type = new_type_primitive(mode);
42
		set_type_alignment(type, 4);
43
44
		ir_type   *const glob = get_glob_type();
		ident     *const id   = new_id_from_str(name);
45
46
47
		ir_entity *const ent
			= new_global_entity(glob, id, type, ir_visibility_local,
			                    IR_LINKAGE_CONSTANT | IR_LINKAGE_NO_IDENTITY);
48
49
50
51
52
53
54

		ir_graph *const cnst_irg = get_const_code_irg();
		ir_node  *const cnst     = new_r_Const_long(cnst_irg, mode, value);
		set_atomic_ent_value(ent, cnst);
		*dst = ent;
	}
	return *dst;
55
56
}

57
static ir_node *create_fnstcw(ir_node *const block, ir_node *const frame, ir_node *const noreg, ir_node *const nomem, ir_node *const state)
58
{
59
60
61
62
63
	ir_node *const fnstcw = new_bd_ia32_FnstCW(NULL, block, frame, noreg, nomem, state);
	set_ia32_op_type(fnstcw, ia32_AddrModeD);
	set_ia32_ls_mode(fnstcw, ia32_mode_fpcw);
	set_ia32_frame_use(fnstcw, IA32_FRAME_USE_32BIT);
	return fnstcw;
64
65
}

66
static ir_node *create_fpu_mode_spill(void *const env, ir_node *const state, bool const force, ir_node *const after)
67
{
Matthias Braun's avatar
Matthias Braun committed
68
	(void)env;
69

70
	if (!force && is_ia32_ChangeCW(state))
71
72
		return NULL;

73
74
75
76
77
78
79
80
81
82
83
	ir_node       *spill;
	ir_node *const block = get_nodes_block(state);
	/* Don't spill the fpcw in unsafe mode. */
	if (ia32_cg_config.use_unsafe_floatconv) {
		spill = new_bd_ia32_FnstCWNOP(NULL, block, state);
	} else {
		ir_graph *const irg   = get_irn_irg(state);
		ir_node  *const noreg = ia32_new_NoReg_gp(irg);
		ir_node  *const nomem = get_irg_no_mem(irg);
		ir_node  *const frame = get_irg_frame(irg);
		spill = create_fnstcw(block, frame, noreg, nomem, state);
84
	}
85
86
	sched_add_after(skip_Proj(after), spill);
	return spill;
87
88
}

89
static ir_node *create_fpu_mode_reload(void *const env, ir_node *const state, ir_node *const spill, ir_node *const before, ir_node *const last_state)
90
{
Matthias Braun's avatar
Matthias Braun committed
91
	(void)env;
92
	(void)state;
93

94
95
96
97
98
	ir_node        *reload;
	ir_node  *const block = get_nodes_block(before);
	ir_graph *const irg   = get_irn_irg(block);
	ir_node  *const noreg = ia32_new_NoReg_gp(irg);
	ir_node  *const nomem = get_irg_no_mem(irg);
99
	if (ia32_cg_config.use_unsafe_floatconv) {
100
101
102
103
104
105
106
107
108
109
		reload = new_bd_ia32_FldCW(NULL, block, noreg, noreg, nomem);
		ir_entity *const rounding_mode = spill ?
			create_ent(&fpcw_round,    0xC7F, "_fpcw_round") :
			create_ent(&fpcw_truncate, 0x37F, "_fpcw_truncate");
		set_ia32_am_ent(reload, rounding_mode);
	} else {
		ir_node       *mem;
		ir_node *const frame = get_irg_frame(irg);
		if (spill) {
			mem = spill;
110
		} else {
111
112
113
114
115
116
117
118
119
120
			assert(last_state);
			ir_node *const cwstore = create_fnstcw(block, frame, noreg, nomem, last_state);
			sched_add_before(before, cwstore);

			ir_node *const load = new_bd_ia32_Load(NULL, block, frame, noreg, cwstore);
			set_ia32_op_type(load, ia32_AddrModeS);
			set_ia32_ls_mode(load, mode_Hu);
			set_ia32_frame_use(load, IA32_FRAME_USE_32BIT);
			sched_add_before(before, load);

121
			ir_node *const load_res = be_new_Proj(load, pn_ia32_Load_res);
122
123

			/* TODO: Make the actual mode configurable in ChangeCW. */
124
			ir_node *const or_const = ia32_create_Immediate(irg, 0xC00);
125
126
127
128
129
130
131
132
133
			ir_node *const orn      = new_bd_ia32_Or(NULL, block, noreg, noreg, nomem, load_res, or_const);
			sched_add_before(before, orn);

			ir_node *const store = new_bd_ia32_Store(NULL, block, frame, noreg, nomem, orn);
			set_ia32_op_type(store, ia32_AddrModeD);
			/* Use ia32_mode_gp, as movl has a shorter opcode than movw. */
			set_ia32_ls_mode(store, ia32_mode_gp);
			set_ia32_frame_use(store, IA32_FRAME_USE_32BIT);
			sched_add_before(before, store);
134
			mem = be_new_Proj(store, pn_ia32_Store_M);
135
		}
136

137
		reload = new_bd_ia32_FldCW(NULL, block, frame, noreg, mem);
138
139
	}

140
141
142
143
144
	set_ia32_op_type(reload, ia32_AddrModeS);
	set_ia32_ls_mode(reload, ia32_mode_fpcw);
	set_ia32_frame_use(reload, IA32_FRAME_USE_32BIT);
	arch_set_irn_register(reload, &ia32_registers[REG_FPCW]);
	sched_add_before(before, reload);
145
146
147
148
	return reload;
}

typedef struct collect_fpu_mode_nodes_env_t {
Matthias Braun's avatar
Matthias Braun committed
149
	ir_node **state_nodes;
150
151
} collect_fpu_mode_nodes_env_t;

152
static void collect_fpu_mode_nodes_walker(ir_node *node, void *data)
153
{
154
	if (is_Proj(node) || is_ia32_ChangeCW(node))
155
156
		return;

Matthias Braun's avatar
Matthias Braun committed
157
	collect_fpu_mode_nodes_env_t *env = (collect_fpu_mode_nodes_env_t*)data;
Christoph Mallon's avatar
Christoph Mallon committed
158
	be_foreach_out(node, o) {
159
160
161
162
		const arch_register_t *reg = arch_get_irn_register_out(node, o);
		if (reg != &ia32_registers[REG_FPCW])
			continue;
		ir_node *value = node;
163
164
		if (get_irn_mode(value) == mode_T)
			value = be_get_or_make_Proj_for_pn(node, o);
165
166
		ARR_APP1(ir_node*, env->state_nodes, value);
	}
167
168
}

169
static void rewire_fpu_mode_nodes(ir_graph *irg)
170
171
{
	/* do ssa construction for the fpu modes */
Matthias Braun's avatar
Matthias Braun committed
172
	collect_fpu_mode_nodes_env_t env;
173
174
175
176
177
	env.state_nodes = NEW_ARR_F(ir_node*, 0);
	irg_walk_graph(irg, collect_fpu_mode_nodes_walker, NULL, &env);

	/* nothing needs to be done, in fact we must not continue as for endless
	 * loops noone is using the initial_value and it will point to a bad node
Matthias Braun's avatar
Matthias Braun committed
178
	 * now */
179
	if (ARR_LEN(env.state_nodes) == 0) {
180
181
182
183
		DEL_ARR_F(env.state_nodes);
		return;
	}

Matthias Braun's avatar
Matthias Braun committed
184
	be_ssa_construction_env_t senv;
185
	be_ssa_construction_init(&senv, irg);
186
187
	be_ssa_construction_add_copies(&senv, env.state_nodes,
	                               ARR_LEN(env.state_nodes));
Christoph Mallon's avatar
Christoph Mallon committed
188
189
	arch_register_t const *const reg           = &ia32_registers[REG_FPCW];
	ir_node               *const initial_value = be_get_Start_proj(irg, reg);
190
191
192
	be_ssa_construction_fix_users(&senv, initial_value);

	/* set registers for the phis */
Matthias Braun's avatar
Matthias Braun committed
193
194
	ir_node **phis = be_ssa_construction_get_new_phis(&senv);
	for (size_t i = 0, len = ARR_LEN(phis); i < len; ++i) {
195
		ir_node *phi = phis[i];
196
		arch_set_irn_register(phi, reg);
197
198
199
	}
	be_ssa_construction_destroy(&senv);
	DEL_ARR_F(env.state_nodes);
200

201
	be_invalidate_live_sets(irg);
202
203
}

204
void ia32_setup_fpu_mode(ir_graph *irg)
205
206
{
	/* do ssa construction for the fpu modes */
207
	rewire_fpu_mode_nodes(irg);
208
209

	/* ensure correct fpu mode for operations */
210
	be_assure_state(irg, &ia32_registers[REG_FPCW],
211
	                NULL, create_fpu_mode_spill, create_fpu_mode_reload);
212
}