annotate gcc/internal-fn.c @ 120:f93fa5091070

fix conv1.c
author mir3636
date Thu, 08 Mar 2018 14:53:42 +0900
parents 04ced10e8804
children 84e7813d76e9
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 /* Internal functions.
kono
parents:
diff changeset
2 Copyright (C) 2011-2017 Free Software Foundation, Inc.
kono
parents:
diff changeset
3
kono
parents:
diff changeset
4 This file is part of GCC.
kono
parents:
diff changeset
5
kono
parents:
diff changeset
6 GCC is free software; you can redistribute it and/or modify it under
kono
parents:
diff changeset
7 the terms of the GNU General Public License as published by the Free
kono
parents:
diff changeset
8 Software Foundation; either version 3, or (at your option) any later
kono
parents:
diff changeset
9 version.
kono
parents:
diff changeset
10
kono
parents:
diff changeset
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
kono
parents:
diff changeset
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
kono
parents:
diff changeset
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
kono
parents:
diff changeset
14 for more details.
kono
parents:
diff changeset
15
kono
parents:
diff changeset
16 You should have received a copy of the GNU General Public License
kono
parents:
diff changeset
17 along with GCC; see the file COPYING3. If not see
kono
parents:
diff changeset
18 <http://www.gnu.org/licenses/>. */
kono
parents:
diff changeset
19
kono
parents:
diff changeset
20 #include "config.h"
kono
parents:
diff changeset
21 #include "system.h"
kono
parents:
diff changeset
22 #include "coretypes.h"
kono
parents:
diff changeset
23 #include "backend.h"
kono
parents:
diff changeset
24 #include "target.h"
kono
parents:
diff changeset
25 #include "rtl.h"
kono
parents:
diff changeset
26 #include "tree.h"
kono
parents:
diff changeset
27 #include "gimple.h"
kono
parents:
diff changeset
28 #include "predict.h"
kono
parents:
diff changeset
29 #include "stringpool.h"
kono
parents:
diff changeset
30 #include "tree-vrp.h"
kono
parents:
diff changeset
31 #include "tree-ssanames.h"
kono
parents:
diff changeset
32 #include "expmed.h"
kono
parents:
diff changeset
33 #include "memmodel.h"
kono
parents:
diff changeset
34 #include "optabs.h"
kono
parents:
diff changeset
35 #include "emit-rtl.h"
kono
parents:
diff changeset
36 #include "diagnostic-core.h"
kono
parents:
diff changeset
37 #include "fold-const.h"
kono
parents:
diff changeset
38 #include "internal-fn.h"
kono
parents:
diff changeset
39 #include "stor-layout.h"
kono
parents:
diff changeset
40 #include "dojump.h"
kono
parents:
diff changeset
41 #include "expr.h"
kono
parents:
diff changeset
42 #include "stringpool.h"
kono
parents:
diff changeset
43 #include "attribs.h"
kono
parents:
diff changeset
44 #include "asan.h"
kono
parents:
diff changeset
45 #include "ubsan.h"
kono
parents:
diff changeset
46 #include "recog.h"
kono
parents:
diff changeset
47 #include "builtins.h"
kono
parents:
diff changeset
48 #include "optabs-tree.h"
kono
parents:
diff changeset
49
kono
parents:
diff changeset
50 /* The names of each internal function, indexed by function number. */
kono
parents:
diff changeset
51 const char *const internal_fn_name_array[] = {
kono
parents:
diff changeset
52 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) #CODE,
kono
parents:
diff changeset
53 #include "internal-fn.def"
kono
parents:
diff changeset
54 "<invalid-fn>"
kono
parents:
diff changeset
55 };
kono
parents:
diff changeset
56
kono
parents:
diff changeset
57 /* The ECF_* flags of each internal function, indexed by function number. */
kono
parents:
diff changeset
58 const int internal_fn_flags_array[] = {
kono
parents:
diff changeset
59 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) FLAGS,
kono
parents:
diff changeset
60 #include "internal-fn.def"
kono
parents:
diff changeset
61 0
kono
parents:
diff changeset
62 };
kono
parents:
diff changeset
63
kono
parents:
diff changeset
64 /* Fnspec of each internal function, indexed by function number. */
kono
parents:
diff changeset
65 const_tree internal_fn_fnspec_array[IFN_LAST + 1];
kono
parents:
diff changeset
66
kono
parents:
diff changeset
67 void
kono
parents:
diff changeset
68 init_internal_fns ()
kono
parents:
diff changeset
69 {
kono
parents:
diff changeset
70 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) \
kono
parents:
diff changeset
71 if (FNSPEC) internal_fn_fnspec_array[IFN_##CODE] = \
kono
parents:
diff changeset
72 build_string ((int) sizeof (FNSPEC), FNSPEC ? FNSPEC : "");
kono
parents:
diff changeset
73 #include "internal-fn.def"
kono
parents:
diff changeset
74 internal_fn_fnspec_array[IFN_LAST] = 0;
kono
parents:
diff changeset
75 }
kono
parents:
diff changeset
76
kono
parents:
diff changeset
77 /* Create static initializers for the information returned by
kono
parents:
diff changeset
78 direct_internal_fn. */
kono
parents:
diff changeset
79 #define not_direct { -2, -2, false }
kono
parents:
diff changeset
80 #define mask_load_direct { -1, 2, false }
kono
parents:
diff changeset
81 #define load_lanes_direct { -1, -1, false }
kono
parents:
diff changeset
82 #define mask_store_direct { 3, 2, false }
kono
parents:
diff changeset
83 #define store_lanes_direct { 0, 0, false }
kono
parents:
diff changeset
84 #define unary_direct { 0, 0, true }
kono
parents:
diff changeset
85 #define binary_direct { 0, 0, true }
kono
parents:
diff changeset
86
kono
parents:
diff changeset
87 const direct_internal_fn_info direct_internal_fn_array[IFN_LAST + 1] = {
kono
parents:
diff changeset
88 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) not_direct,
kono
parents:
diff changeset
89 #define DEF_INTERNAL_OPTAB_FN(CODE, FLAGS, OPTAB, TYPE) TYPE##_direct,
kono
parents:
diff changeset
90 #include "internal-fn.def"
kono
parents:
diff changeset
91 not_direct
kono
parents:
diff changeset
92 };
kono
parents:
diff changeset
93
kono
parents:
diff changeset
94 /* ARRAY_TYPE is an array of vector modes. Return the associated insn
kono
parents:
diff changeset
95 for load-lanes-style optab OPTAB, or CODE_FOR_nothing if none. */
kono
parents:
diff changeset
96
kono
parents:
diff changeset
97 static enum insn_code
kono
parents:
diff changeset
98 get_multi_vector_move (tree array_type, convert_optab optab)
kono
parents:
diff changeset
99 {
kono
parents:
diff changeset
100 machine_mode imode;
kono
parents:
diff changeset
101 machine_mode vmode;
kono
parents:
diff changeset
102
kono
parents:
diff changeset
103 gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE);
kono
parents:
diff changeset
104 imode = TYPE_MODE (array_type);
kono
parents:
diff changeset
105 vmode = TYPE_MODE (TREE_TYPE (array_type));
kono
parents:
diff changeset
106
kono
parents:
diff changeset
107 return convert_optab_handler (optab, imode, vmode);
kono
parents:
diff changeset
108 }
kono
parents:
diff changeset
109
kono
parents:
diff changeset
110 /* Expand LOAD_LANES call STMT using optab OPTAB. */
kono
parents:
diff changeset
111
kono
parents:
diff changeset
112 static void
kono
parents:
diff changeset
113 expand_load_lanes_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
kono
parents:
diff changeset
114 {
kono
parents:
diff changeset
115 struct expand_operand ops[2];
kono
parents:
diff changeset
116 tree type, lhs, rhs;
kono
parents:
diff changeset
117 rtx target, mem;
kono
parents:
diff changeset
118
kono
parents:
diff changeset
119 lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
120 rhs = gimple_call_arg (stmt, 0);
kono
parents:
diff changeset
121 type = TREE_TYPE (lhs);
kono
parents:
diff changeset
122
kono
parents:
diff changeset
123 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
124 mem = expand_normal (rhs);
kono
parents:
diff changeset
125
kono
parents:
diff changeset
126 gcc_assert (MEM_P (mem));
kono
parents:
diff changeset
127 PUT_MODE (mem, TYPE_MODE (type));
kono
parents:
diff changeset
128
kono
parents:
diff changeset
129 create_output_operand (&ops[0], target, TYPE_MODE (type));
kono
parents:
diff changeset
130 create_fixed_operand (&ops[1], mem);
kono
parents:
diff changeset
131 expand_insn (get_multi_vector_move (type, optab), 2, ops);
kono
parents:
diff changeset
132 }
kono
parents:
diff changeset
133
kono
parents:
diff changeset
134 /* Expand STORE_LANES call STMT using optab OPTAB. */
kono
parents:
diff changeset
135
kono
parents:
diff changeset
136 static void
kono
parents:
diff changeset
137 expand_store_lanes_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
kono
parents:
diff changeset
138 {
kono
parents:
diff changeset
139 struct expand_operand ops[2];
kono
parents:
diff changeset
140 tree type, lhs, rhs;
kono
parents:
diff changeset
141 rtx target, reg;
kono
parents:
diff changeset
142
kono
parents:
diff changeset
143 lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
144 rhs = gimple_call_arg (stmt, 0);
kono
parents:
diff changeset
145 type = TREE_TYPE (rhs);
kono
parents:
diff changeset
146
kono
parents:
diff changeset
147 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
148 reg = expand_normal (rhs);
kono
parents:
diff changeset
149
kono
parents:
diff changeset
150 gcc_assert (MEM_P (target));
kono
parents:
diff changeset
151 PUT_MODE (target, TYPE_MODE (type));
kono
parents:
diff changeset
152
kono
parents:
diff changeset
153 create_fixed_operand (&ops[0], target);
kono
parents:
diff changeset
154 create_input_operand (&ops[1], reg, TYPE_MODE (type));
kono
parents:
diff changeset
155 expand_insn (get_multi_vector_move (type, optab), 2, ops);
kono
parents:
diff changeset
156 }
kono
parents:
diff changeset
157
kono
parents:
diff changeset
158 static void
kono
parents:
diff changeset
159 expand_ANNOTATE (internal_fn, gcall *)
kono
parents:
diff changeset
160 {
kono
parents:
diff changeset
161 gcc_unreachable ();
kono
parents:
diff changeset
162 }
kono
parents:
diff changeset
163
kono
parents:
diff changeset
164 /* This should get expanded in omp_device_lower pass. */
kono
parents:
diff changeset
165
kono
parents:
diff changeset
166 static void
kono
parents:
diff changeset
167 expand_GOMP_USE_SIMT (internal_fn, gcall *)
kono
parents:
diff changeset
168 {
kono
parents:
diff changeset
169 gcc_unreachable ();
kono
parents:
diff changeset
170 }
kono
parents:
diff changeset
171
kono
parents:
diff changeset
172 /* This should get expanded in omp_device_lower pass. */
kono
parents:
diff changeset
173
kono
parents:
diff changeset
174 static void
kono
parents:
diff changeset
175 expand_GOMP_SIMT_ENTER (internal_fn, gcall *)
kono
parents:
diff changeset
176 {
kono
parents:
diff changeset
177 gcc_unreachable ();
kono
parents:
diff changeset
178 }
kono
parents:
diff changeset
179
kono
parents:
diff changeset
180 /* Allocate per-lane storage and begin non-uniform execution region. */
kono
parents:
diff changeset
181
kono
parents:
diff changeset
182 static void
kono
parents:
diff changeset
183 expand_GOMP_SIMT_ENTER_ALLOC (internal_fn, gcall *stmt)
kono
parents:
diff changeset
184 {
kono
parents:
diff changeset
185 rtx target;
kono
parents:
diff changeset
186 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
187 if (lhs)
kono
parents:
diff changeset
188 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
189 else
kono
parents:
diff changeset
190 target = gen_reg_rtx (Pmode);
kono
parents:
diff changeset
191 rtx size = expand_normal (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
192 rtx align = expand_normal (gimple_call_arg (stmt, 1));
kono
parents:
diff changeset
193 struct expand_operand ops[3];
kono
parents:
diff changeset
194 create_output_operand (&ops[0], target, Pmode);
kono
parents:
diff changeset
195 create_input_operand (&ops[1], size, Pmode);
kono
parents:
diff changeset
196 create_input_operand (&ops[2], align, Pmode);
kono
parents:
diff changeset
197 gcc_assert (targetm.have_omp_simt_enter ());
kono
parents:
diff changeset
198 expand_insn (targetm.code_for_omp_simt_enter, 3, ops);
kono
parents:
diff changeset
199 }
kono
parents:
diff changeset
200
kono
parents:
diff changeset
201 /* Deallocate per-lane storage and leave non-uniform execution region. */
kono
parents:
diff changeset
202
kono
parents:
diff changeset
203 static void
kono
parents:
diff changeset
204 expand_GOMP_SIMT_EXIT (internal_fn, gcall *stmt)
kono
parents:
diff changeset
205 {
kono
parents:
diff changeset
206 gcc_checking_assert (!gimple_call_lhs (stmt));
kono
parents:
diff changeset
207 rtx arg = expand_normal (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
208 struct expand_operand ops[1];
kono
parents:
diff changeset
209 create_input_operand (&ops[0], arg, Pmode);
kono
parents:
diff changeset
210 gcc_assert (targetm.have_omp_simt_exit ());
kono
parents:
diff changeset
211 expand_insn (targetm.code_for_omp_simt_exit, 1, ops);
kono
parents:
diff changeset
212 }
kono
parents:
diff changeset
213
kono
parents:
diff changeset
214 /* Lane index on SIMT targets: thread index in the warp on NVPTX. On targets
kono
parents:
diff changeset
215 without SIMT execution this should be expanded in omp_device_lower pass. */
kono
parents:
diff changeset
216
kono
parents:
diff changeset
217 static void
kono
parents:
diff changeset
218 expand_GOMP_SIMT_LANE (internal_fn, gcall *stmt)
kono
parents:
diff changeset
219 {
kono
parents:
diff changeset
220 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
221 if (!lhs)
kono
parents:
diff changeset
222 return;
kono
parents:
diff changeset
223
kono
parents:
diff changeset
224 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
225 gcc_assert (targetm.have_omp_simt_lane ());
kono
parents:
diff changeset
226 emit_insn (targetm.gen_omp_simt_lane (target));
kono
parents:
diff changeset
227 }
kono
parents:
diff changeset
228
kono
parents:
diff changeset
229 /* This should get expanded in omp_device_lower pass. */
kono
parents:
diff changeset
230
kono
parents:
diff changeset
231 static void
kono
parents:
diff changeset
232 expand_GOMP_SIMT_VF (internal_fn, gcall *)
kono
parents:
diff changeset
233 {
kono
parents:
diff changeset
234 gcc_unreachable ();
kono
parents:
diff changeset
235 }
kono
parents:
diff changeset
236
kono
parents:
diff changeset
237 /* Lane index of the first SIMT lane that supplies a non-zero argument.
kono
parents:
diff changeset
238 This is a SIMT counterpart to GOMP_SIMD_LAST_LANE, used to represent the
kono
parents:
diff changeset
239 lane that executed the last iteration for handling OpenMP lastprivate. */
kono
parents:
diff changeset
240
kono
parents:
diff changeset
241 static void
kono
parents:
diff changeset
242 expand_GOMP_SIMT_LAST_LANE (internal_fn, gcall *stmt)
kono
parents:
diff changeset
243 {
kono
parents:
diff changeset
244 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
245 if (!lhs)
kono
parents:
diff changeset
246 return;
kono
parents:
diff changeset
247
kono
parents:
diff changeset
248 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
249 rtx cond = expand_normal (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
250 machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
kono
parents:
diff changeset
251 struct expand_operand ops[2];
kono
parents:
diff changeset
252 create_output_operand (&ops[0], target, mode);
kono
parents:
diff changeset
253 create_input_operand (&ops[1], cond, mode);
kono
parents:
diff changeset
254 gcc_assert (targetm.have_omp_simt_last_lane ());
kono
parents:
diff changeset
255 expand_insn (targetm.code_for_omp_simt_last_lane, 2, ops);
kono
parents:
diff changeset
256 }
kono
parents:
diff changeset
257
kono
parents:
diff changeset
258 /* Non-transparent predicate used in SIMT lowering of OpenMP "ordered". */
kono
parents:
diff changeset
259
kono
parents:
diff changeset
260 static void
kono
parents:
diff changeset
261 expand_GOMP_SIMT_ORDERED_PRED (internal_fn, gcall *stmt)
kono
parents:
diff changeset
262 {
kono
parents:
diff changeset
263 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
264 if (!lhs)
kono
parents:
diff changeset
265 return;
kono
parents:
diff changeset
266
kono
parents:
diff changeset
267 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
268 rtx ctr = expand_normal (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
269 machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
kono
parents:
diff changeset
270 struct expand_operand ops[2];
kono
parents:
diff changeset
271 create_output_operand (&ops[0], target, mode);
kono
parents:
diff changeset
272 create_input_operand (&ops[1], ctr, mode);
kono
parents:
diff changeset
273 gcc_assert (targetm.have_omp_simt_ordered ());
kono
parents:
diff changeset
274 expand_insn (targetm.code_for_omp_simt_ordered, 2, ops);
kono
parents:
diff changeset
275 }
kono
parents:
diff changeset
276
kono
parents:
diff changeset
277 /* "Or" boolean reduction across SIMT lanes: return non-zero in all lanes if
kono
parents:
diff changeset
278 any lane supplies a non-zero argument. */
kono
parents:
diff changeset
279
kono
parents:
diff changeset
280 static void
kono
parents:
diff changeset
281 expand_GOMP_SIMT_VOTE_ANY (internal_fn, gcall *stmt)
kono
parents:
diff changeset
282 {
kono
parents:
diff changeset
283 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
284 if (!lhs)
kono
parents:
diff changeset
285 return;
kono
parents:
diff changeset
286
kono
parents:
diff changeset
287 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
288 rtx cond = expand_normal (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
289 machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
kono
parents:
diff changeset
290 struct expand_operand ops[2];
kono
parents:
diff changeset
291 create_output_operand (&ops[0], target, mode);
kono
parents:
diff changeset
292 create_input_operand (&ops[1], cond, mode);
kono
parents:
diff changeset
293 gcc_assert (targetm.have_omp_simt_vote_any ());
kono
parents:
diff changeset
294 expand_insn (targetm.code_for_omp_simt_vote_any, 2, ops);
kono
parents:
diff changeset
295 }
kono
parents:
diff changeset
296
kono
parents:
diff changeset
297 /* Exchange between SIMT lanes with a "butterfly" pattern: source lane index
kono
parents:
diff changeset
298 is destination lane index XOR given offset. */
kono
parents:
diff changeset
299
kono
parents:
diff changeset
300 static void
kono
parents:
diff changeset
301 expand_GOMP_SIMT_XCHG_BFLY (internal_fn, gcall *stmt)
kono
parents:
diff changeset
302 {
kono
parents:
diff changeset
303 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
304 if (!lhs)
kono
parents:
diff changeset
305 return;
kono
parents:
diff changeset
306
kono
parents:
diff changeset
307 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
308 rtx src = expand_normal (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
309 rtx idx = expand_normal (gimple_call_arg (stmt, 1));
kono
parents:
diff changeset
310 machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
kono
parents:
diff changeset
311 struct expand_operand ops[3];
kono
parents:
diff changeset
312 create_output_operand (&ops[0], target, mode);
kono
parents:
diff changeset
313 create_input_operand (&ops[1], src, mode);
kono
parents:
diff changeset
314 create_input_operand (&ops[2], idx, SImode);
kono
parents:
diff changeset
315 gcc_assert (targetm.have_omp_simt_xchg_bfly ());
kono
parents:
diff changeset
316 expand_insn (targetm.code_for_omp_simt_xchg_bfly, 3, ops);
kono
parents:
diff changeset
317 }
kono
parents:
diff changeset
318
kono
parents:
diff changeset
319 /* Exchange between SIMT lanes according to given source lane index. */
kono
parents:
diff changeset
320
kono
parents:
diff changeset
321 static void
kono
parents:
diff changeset
322 expand_GOMP_SIMT_XCHG_IDX (internal_fn, gcall *stmt)
kono
parents:
diff changeset
323 {
kono
parents:
diff changeset
324 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
325 if (!lhs)
kono
parents:
diff changeset
326 return;
kono
parents:
diff changeset
327
kono
parents:
diff changeset
328 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
329 rtx src = expand_normal (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
330 rtx idx = expand_normal (gimple_call_arg (stmt, 1));
kono
parents:
diff changeset
331 machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
kono
parents:
diff changeset
332 struct expand_operand ops[3];
kono
parents:
diff changeset
333 create_output_operand (&ops[0], target, mode);
kono
parents:
diff changeset
334 create_input_operand (&ops[1], src, mode);
kono
parents:
diff changeset
335 create_input_operand (&ops[2], idx, SImode);
kono
parents:
diff changeset
336 gcc_assert (targetm.have_omp_simt_xchg_idx ());
kono
parents:
diff changeset
337 expand_insn (targetm.code_for_omp_simt_xchg_idx, 3, ops);
kono
parents:
diff changeset
338 }
kono
parents:
diff changeset
339
kono
parents:
diff changeset
340 /* This should get expanded in adjust_simduid_builtins. */
kono
parents:
diff changeset
341
kono
parents:
diff changeset
342 static void
kono
parents:
diff changeset
343 expand_GOMP_SIMD_LANE (internal_fn, gcall *)
kono
parents:
diff changeset
344 {
kono
parents:
diff changeset
345 gcc_unreachable ();
kono
parents:
diff changeset
346 }
kono
parents:
diff changeset
347
kono
parents:
diff changeset
348 /* This should get expanded in adjust_simduid_builtins. */
kono
parents:
diff changeset
349
kono
parents:
diff changeset
350 static void
kono
parents:
diff changeset
351 expand_GOMP_SIMD_VF (internal_fn, gcall *)
kono
parents:
diff changeset
352 {
kono
parents:
diff changeset
353 gcc_unreachable ();
kono
parents:
diff changeset
354 }
kono
parents:
diff changeset
355
kono
parents:
diff changeset
356 /* This should get expanded in adjust_simduid_builtins. */
kono
parents:
diff changeset
357
kono
parents:
diff changeset
358 static void
kono
parents:
diff changeset
359 expand_GOMP_SIMD_LAST_LANE (internal_fn, gcall *)
kono
parents:
diff changeset
360 {
kono
parents:
diff changeset
361 gcc_unreachable ();
kono
parents:
diff changeset
362 }
kono
parents:
diff changeset
363
kono
parents:
diff changeset
364 /* This should get expanded in adjust_simduid_builtins. */
kono
parents:
diff changeset
365
kono
parents:
diff changeset
366 static void
kono
parents:
diff changeset
367 expand_GOMP_SIMD_ORDERED_START (internal_fn, gcall *)
kono
parents:
diff changeset
368 {
kono
parents:
diff changeset
369 gcc_unreachable ();
kono
parents:
diff changeset
370 }
kono
parents:
diff changeset
371
kono
parents:
diff changeset
372 /* This should get expanded in adjust_simduid_builtins. */
kono
parents:
diff changeset
373
kono
parents:
diff changeset
374 static void
kono
parents:
diff changeset
375 expand_GOMP_SIMD_ORDERED_END (internal_fn, gcall *)
kono
parents:
diff changeset
376 {
kono
parents:
diff changeset
377 gcc_unreachable ();
kono
parents:
diff changeset
378 }
kono
parents:
diff changeset
379
kono
parents:
diff changeset
380 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
381
kono
parents:
diff changeset
382 static void
kono
parents:
diff changeset
383 expand_UBSAN_NULL (internal_fn, gcall *)
kono
parents:
diff changeset
384 {
kono
parents:
diff changeset
385 gcc_unreachable ();
kono
parents:
diff changeset
386 }
kono
parents:
diff changeset
387
kono
parents:
diff changeset
388 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
389
kono
parents:
diff changeset
390 static void
kono
parents:
diff changeset
391 expand_UBSAN_BOUNDS (internal_fn, gcall *)
kono
parents:
diff changeset
392 {
kono
parents:
diff changeset
393 gcc_unreachable ();
kono
parents:
diff changeset
394 }
kono
parents:
diff changeset
395
kono
parents:
diff changeset
396 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
397
kono
parents:
diff changeset
398 static void
kono
parents:
diff changeset
399 expand_UBSAN_VPTR (internal_fn, gcall *)
kono
parents:
diff changeset
400 {
kono
parents:
diff changeset
401 gcc_unreachable ();
kono
parents:
diff changeset
402 }
kono
parents:
diff changeset
403
kono
parents:
diff changeset
404 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
405
kono
parents:
diff changeset
406 static void
kono
parents:
diff changeset
407 expand_UBSAN_PTR (internal_fn, gcall *)
kono
parents:
diff changeset
408 {
kono
parents:
diff changeset
409 gcc_unreachable ();
kono
parents:
diff changeset
410 }
kono
parents:
diff changeset
411
kono
parents:
diff changeset
412 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
413
kono
parents:
diff changeset
414 static void
kono
parents:
diff changeset
415 expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
kono
parents:
diff changeset
416 {
kono
parents:
diff changeset
417 gcc_unreachable ();
kono
parents:
diff changeset
418 }
kono
parents:
diff changeset
419
kono
parents:
diff changeset
420 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
421
kono
parents:
diff changeset
422 static void
kono
parents:
diff changeset
423 expand_ASAN_CHECK (internal_fn, gcall *)
kono
parents:
diff changeset
424 {
kono
parents:
diff changeset
425 gcc_unreachable ();
kono
parents:
diff changeset
426 }
kono
parents:
diff changeset
427
kono
parents:
diff changeset
428 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
429
kono
parents:
diff changeset
430 static void
kono
parents:
diff changeset
431 expand_ASAN_MARK (internal_fn, gcall *)
kono
parents:
diff changeset
432 {
kono
parents:
diff changeset
433 gcc_unreachable ();
kono
parents:
diff changeset
434 }
kono
parents:
diff changeset
435
kono
parents:
diff changeset
436 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
437
kono
parents:
diff changeset
438 static void
kono
parents:
diff changeset
439 expand_ASAN_POISON (internal_fn, gcall *)
kono
parents:
diff changeset
440 {
kono
parents:
diff changeset
441 gcc_unreachable ();
kono
parents:
diff changeset
442 }
kono
parents:
diff changeset
443
kono
parents:
diff changeset
444 /* This should get expanded in the sanopt pass. */
kono
parents:
diff changeset
445
kono
parents:
diff changeset
446 static void
kono
parents:
diff changeset
447 expand_ASAN_POISON_USE (internal_fn, gcall *)
kono
parents:
diff changeset
448 {
kono
parents:
diff changeset
449 gcc_unreachable ();
kono
parents:
diff changeset
450 }
kono
parents:
diff changeset
451
kono
parents:
diff changeset
452 /* This should get expanded in the tsan pass. */
kono
parents:
diff changeset
453
kono
parents:
diff changeset
454 static void
kono
parents:
diff changeset
455 expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
kono
parents:
diff changeset
456 {
kono
parents:
diff changeset
457 gcc_unreachable ();
kono
parents:
diff changeset
458 }
kono
parents:
diff changeset
459
kono
parents:
diff changeset
460 /* This should get expanded in the lower pass. */
kono
parents:
diff changeset
461
kono
parents:
diff changeset
462 static void
kono
parents:
diff changeset
463 expand_FALLTHROUGH (internal_fn, gcall *call)
kono
parents:
diff changeset
464 {
kono
parents:
diff changeset
465 error_at (gimple_location (call),
kono
parents:
diff changeset
466 "invalid use of attribute %<fallthrough%>");
kono
parents:
diff changeset
467 }
kono
parents:
diff changeset
468
kono
parents:
diff changeset
469 /* Return minimum precision needed to represent all values
kono
parents:
diff changeset
470 of ARG in SIGNed integral type. */
kono
parents:
diff changeset
471
kono
parents:
diff changeset
472 static int
kono
parents:
diff changeset
473 get_min_precision (tree arg, signop sign)
kono
parents:
diff changeset
474 {
kono
parents:
diff changeset
475 int prec = TYPE_PRECISION (TREE_TYPE (arg));
kono
parents:
diff changeset
476 int cnt = 0;
kono
parents:
diff changeset
477 signop orig_sign = sign;
kono
parents:
diff changeset
478 if (TREE_CODE (arg) == INTEGER_CST)
kono
parents:
diff changeset
479 {
kono
parents:
diff changeset
480 int p;
kono
parents:
diff changeset
481 if (TYPE_SIGN (TREE_TYPE (arg)) != sign)
kono
parents:
diff changeset
482 {
kono
parents:
diff changeset
483 widest_int w = wi::to_widest (arg);
kono
parents:
diff changeset
484 w = wi::ext (w, prec, sign);
kono
parents:
diff changeset
485 p = wi::min_precision (w, sign);
kono
parents:
diff changeset
486 }
kono
parents:
diff changeset
487 else
kono
parents:
diff changeset
488 p = wi::min_precision (wi::to_wide (arg), sign);
kono
parents:
diff changeset
489 return MIN (p, prec);
kono
parents:
diff changeset
490 }
kono
parents:
diff changeset
491 while (CONVERT_EXPR_P (arg)
kono
parents:
diff changeset
492 && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
kono
parents:
diff changeset
493 && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg, 0))) <= prec)
kono
parents:
diff changeset
494 {
kono
parents:
diff changeset
495 arg = TREE_OPERAND (arg, 0);
kono
parents:
diff changeset
496 if (TYPE_PRECISION (TREE_TYPE (arg)) < prec)
kono
parents:
diff changeset
497 {
kono
parents:
diff changeset
498 if (TYPE_UNSIGNED (TREE_TYPE (arg)))
kono
parents:
diff changeset
499 sign = UNSIGNED;
kono
parents:
diff changeset
500 else if (sign == UNSIGNED && get_range_pos_neg (arg) != 1)
kono
parents:
diff changeset
501 return prec + (orig_sign != sign);
kono
parents:
diff changeset
502 prec = TYPE_PRECISION (TREE_TYPE (arg));
kono
parents:
diff changeset
503 }
kono
parents:
diff changeset
504 if (++cnt > 30)
kono
parents:
diff changeset
505 return prec + (orig_sign != sign);
kono
parents:
diff changeset
506 }
kono
parents:
diff changeset
507 if (TREE_CODE (arg) != SSA_NAME)
kono
parents:
diff changeset
508 return prec + (orig_sign != sign);
kono
parents:
diff changeset
509 wide_int arg_min, arg_max;
kono
parents:
diff changeset
510 while (get_range_info (arg, &arg_min, &arg_max) != VR_RANGE)
kono
parents:
diff changeset
511 {
kono
parents:
diff changeset
512 gimple *g = SSA_NAME_DEF_STMT (arg);
kono
parents:
diff changeset
513 if (is_gimple_assign (g)
kono
parents:
diff changeset
514 && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (g)))
kono
parents:
diff changeset
515 {
kono
parents:
diff changeset
516 tree t = gimple_assign_rhs1 (g);
kono
parents:
diff changeset
517 if (INTEGRAL_TYPE_P (TREE_TYPE (t))
kono
parents:
diff changeset
518 && TYPE_PRECISION (TREE_TYPE (t)) <= prec)
kono
parents:
diff changeset
519 {
kono
parents:
diff changeset
520 arg = t;
kono
parents:
diff changeset
521 if (TYPE_PRECISION (TREE_TYPE (arg)) < prec)
kono
parents:
diff changeset
522 {
kono
parents:
diff changeset
523 if (TYPE_UNSIGNED (TREE_TYPE (arg)))
kono
parents:
diff changeset
524 sign = UNSIGNED;
kono
parents:
diff changeset
525 else if (sign == UNSIGNED && get_range_pos_neg (arg) != 1)
kono
parents:
diff changeset
526 return prec + (orig_sign != sign);
kono
parents:
diff changeset
527 prec = TYPE_PRECISION (TREE_TYPE (arg));
kono
parents:
diff changeset
528 }
kono
parents:
diff changeset
529 if (++cnt > 30)
kono
parents:
diff changeset
530 return prec + (orig_sign != sign);
kono
parents:
diff changeset
531 continue;
kono
parents:
diff changeset
532 }
kono
parents:
diff changeset
533 }
kono
parents:
diff changeset
534 return prec + (orig_sign != sign);
kono
parents:
diff changeset
535 }
kono
parents:
diff changeset
536 if (sign == TYPE_SIGN (TREE_TYPE (arg)))
kono
parents:
diff changeset
537 {
kono
parents:
diff changeset
538 int p1 = wi::min_precision (arg_min, sign);
kono
parents:
diff changeset
539 int p2 = wi::min_precision (arg_max, sign);
kono
parents:
diff changeset
540 p1 = MAX (p1, p2);
kono
parents:
diff changeset
541 prec = MIN (prec, p1);
kono
parents:
diff changeset
542 }
kono
parents:
diff changeset
543 else if (sign == UNSIGNED && !wi::neg_p (arg_min, SIGNED))
kono
parents:
diff changeset
544 {
kono
parents:
diff changeset
545 int p = wi::min_precision (arg_max, UNSIGNED);
kono
parents:
diff changeset
546 prec = MIN (prec, p);
kono
parents:
diff changeset
547 }
kono
parents:
diff changeset
548 return prec + (orig_sign != sign);
kono
parents:
diff changeset
549 }
kono
parents:
diff changeset
550
kono
parents:
diff changeset
551 /* Helper for expand_*_overflow. Set the __imag__ part to true
kono
parents:
diff changeset
552 (1 except for signed:1 type, in which case store -1). */
kono
parents:
diff changeset
553
kono
parents:
diff changeset
554 static void
kono
parents:
diff changeset
555 expand_arith_set_overflow (tree lhs, rtx target)
kono
parents:
diff changeset
556 {
kono
parents:
diff changeset
557 if (TYPE_PRECISION (TREE_TYPE (TREE_TYPE (lhs))) == 1
kono
parents:
diff changeset
558 && !TYPE_UNSIGNED (TREE_TYPE (TREE_TYPE (lhs))))
kono
parents:
diff changeset
559 write_complex_part (target, constm1_rtx, true);
kono
parents:
diff changeset
560 else
kono
parents:
diff changeset
561 write_complex_part (target, const1_rtx, true);
kono
parents:
diff changeset
562 }
kono
parents:
diff changeset
563
kono
parents:
diff changeset
564 /* Helper for expand_*_overflow. Store RES into the __real__ part
kono
parents:
diff changeset
565 of TARGET. If RES has larger MODE than __real__ part of TARGET,
kono
parents:
diff changeset
566 set the __imag__ part to 1 if RES doesn't fit into it. Similarly
kono
parents:
diff changeset
567 if LHS has smaller precision than its mode. */
kono
parents:
diff changeset
568
kono
parents:
diff changeset
569 static void
kono
parents:
diff changeset
570 expand_arith_overflow_result_store (tree lhs, rtx target,
kono
parents:
diff changeset
571 scalar_int_mode mode, rtx res)
kono
parents:
diff changeset
572 {
kono
parents:
diff changeset
573 scalar_int_mode tgtmode
kono
parents:
diff changeset
574 = as_a <scalar_int_mode> (GET_MODE_INNER (GET_MODE (target)));
kono
parents:
diff changeset
575 rtx lres = res;
kono
parents:
diff changeset
576 if (tgtmode != mode)
kono
parents:
diff changeset
577 {
kono
parents:
diff changeset
578 rtx_code_label *done_label = gen_label_rtx ();
kono
parents:
diff changeset
579 int uns = TYPE_UNSIGNED (TREE_TYPE (TREE_TYPE (lhs)));
kono
parents:
diff changeset
580 lres = convert_modes (tgtmode, mode, res, uns);
kono
parents:
diff changeset
581 gcc_assert (GET_MODE_PRECISION (tgtmode) < GET_MODE_PRECISION (mode));
kono
parents:
diff changeset
582 do_compare_rtx_and_jump (res, convert_modes (mode, tgtmode, lres, uns),
kono
parents:
diff changeset
583 EQ, true, mode, NULL_RTX, NULL, done_label,
kono
parents:
diff changeset
584 profile_probability::very_likely ());
kono
parents:
diff changeset
585 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
586 emit_label (done_label);
kono
parents:
diff changeset
587 }
kono
parents:
diff changeset
588 int prec = TYPE_PRECISION (TREE_TYPE (TREE_TYPE (lhs)));
kono
parents:
diff changeset
589 int tgtprec = GET_MODE_PRECISION (tgtmode);
kono
parents:
diff changeset
590 if (prec < tgtprec)
kono
parents:
diff changeset
591 {
kono
parents:
diff changeset
592 rtx_code_label *done_label = gen_label_rtx ();
kono
parents:
diff changeset
593 int uns = TYPE_UNSIGNED (TREE_TYPE (TREE_TYPE (lhs)));
kono
parents:
diff changeset
594 res = lres;
kono
parents:
diff changeset
595 if (uns)
kono
parents:
diff changeset
596 {
kono
parents:
diff changeset
597 rtx mask
kono
parents:
diff changeset
598 = immed_wide_int_const (wi::shifted_mask (0, prec, false, tgtprec),
kono
parents:
diff changeset
599 tgtmode);
kono
parents:
diff changeset
600 lres = expand_simple_binop (tgtmode, AND, res, mask, NULL_RTX,
kono
parents:
diff changeset
601 true, OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
602 }
kono
parents:
diff changeset
603 else
kono
parents:
diff changeset
604 {
kono
parents:
diff changeset
605 lres = expand_shift (LSHIFT_EXPR, tgtmode, res, tgtprec - prec,
kono
parents:
diff changeset
606 NULL_RTX, 1);
kono
parents:
diff changeset
607 lres = expand_shift (RSHIFT_EXPR, tgtmode, lres, tgtprec - prec,
kono
parents:
diff changeset
608 NULL_RTX, 0);
kono
parents:
diff changeset
609 }
kono
parents:
diff changeset
610 do_compare_rtx_and_jump (res, lres,
kono
parents:
diff changeset
611 EQ, true, tgtmode, NULL_RTX, NULL, done_label,
kono
parents:
diff changeset
612 profile_probability::very_likely ());
kono
parents:
diff changeset
613 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
614 emit_label (done_label);
kono
parents:
diff changeset
615 }
kono
parents:
diff changeset
616 write_complex_part (target, lres, false);
kono
parents:
diff changeset
617 }
kono
parents:
diff changeset
618
kono
parents:
diff changeset
619 /* Helper for expand_*_overflow. Store RES into TARGET. */
kono
parents:
diff changeset
620
kono
parents:
diff changeset
621 static void
kono
parents:
diff changeset
622 expand_ubsan_result_store (rtx target, rtx res)
kono
parents:
diff changeset
623 {
kono
parents:
diff changeset
624 if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
kono
parents:
diff changeset
625 /* If this is a scalar in a register that is stored in a wider mode
kono
parents:
diff changeset
626 than the declared mode, compute the result into its declared mode
kono
parents:
diff changeset
627 and then convert to the wider mode. Our value is the computed
kono
parents:
diff changeset
628 expression. */
kono
parents:
diff changeset
629 convert_move (SUBREG_REG (target), res, SUBREG_PROMOTED_SIGN (target));
kono
parents:
diff changeset
630 else
kono
parents:
diff changeset
631 emit_move_insn (target, res);
kono
parents:
diff changeset
632 }
kono
parents:
diff changeset
633
kono
parents:
diff changeset
634 /* Add sub/add overflow checking to the statement STMT.
kono
parents:
diff changeset
635 CODE says whether the operation is +, or -. */
kono
parents:
diff changeset
636
kono
parents:
diff changeset
637 static void
kono
parents:
diff changeset
638 expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
kono
parents:
diff changeset
639 tree arg0, tree arg1, bool unsr_p, bool uns0_p,
kono
parents:
diff changeset
640 bool uns1_p, bool is_ubsan, tree *datap)
kono
parents:
diff changeset
641 {
kono
parents:
diff changeset
642 rtx res, target = NULL_RTX;
kono
parents:
diff changeset
643 tree fn;
kono
parents:
diff changeset
644 rtx_code_label *done_label = gen_label_rtx ();
kono
parents:
diff changeset
645 rtx_code_label *do_error = gen_label_rtx ();
kono
parents:
diff changeset
646 do_pending_stack_adjust ();
kono
parents:
diff changeset
647 rtx op0 = expand_normal (arg0);
kono
parents:
diff changeset
648 rtx op1 = expand_normal (arg1);
kono
parents:
diff changeset
649 scalar_int_mode mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg0));
kono
parents:
diff changeset
650 int prec = GET_MODE_PRECISION (mode);
kono
parents:
diff changeset
651 rtx sgn = immed_wide_int_const (wi::min_value (prec, SIGNED), mode);
kono
parents:
diff changeset
652 bool do_xor = false;
kono
parents:
diff changeset
653
kono
parents:
diff changeset
654 if (is_ubsan)
kono
parents:
diff changeset
655 gcc_assert (!unsr_p && !uns0_p && !uns1_p);
kono
parents:
diff changeset
656
kono
parents:
diff changeset
657 if (lhs)
kono
parents:
diff changeset
658 {
kono
parents:
diff changeset
659 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
660 if (!is_ubsan)
kono
parents:
diff changeset
661 write_complex_part (target, const0_rtx, true);
kono
parents:
diff changeset
662 }
kono
parents:
diff changeset
663
kono
parents:
diff changeset
664 /* We assume both operands and result have the same precision
kono
parents:
diff changeset
665 here (GET_MODE_BITSIZE (mode)), S stands for signed type
kono
parents:
diff changeset
666 with that precision, U for unsigned type with that precision,
kono
parents:
diff changeset
667 sgn for unsigned most significant bit in that precision.
kono
parents:
diff changeset
668 s1 is signed first operand, u1 is unsigned first operand,
kono
parents:
diff changeset
669 s2 is signed second operand, u2 is unsigned second operand,
kono
parents:
diff changeset
670 sr is signed result, ur is unsigned result and the following
kono
parents:
diff changeset
671 rules say how to compute result (which is always result of
kono
parents:
diff changeset
672 the operands as if both were unsigned, cast to the right
kono
parents:
diff changeset
673 signedness) and how to compute whether operation overflowed.
kono
parents:
diff changeset
674
kono
parents:
diff changeset
675 s1 + s2 -> sr
kono
parents:
diff changeset
676 res = (S) ((U) s1 + (U) s2)
kono
parents:
diff changeset
677 ovf = s2 < 0 ? res > s1 : res < s1 (or jump on overflow)
kono
parents:
diff changeset
678 s1 - s2 -> sr
kono
parents:
diff changeset
679 res = (S) ((U) s1 - (U) s2)
kono
parents:
diff changeset
680 ovf = s2 < 0 ? res < s1 : res > s2 (or jump on overflow)
kono
parents:
diff changeset
681 u1 + u2 -> ur
kono
parents:
diff changeset
682 res = u1 + u2
kono
parents:
diff changeset
683 ovf = res < u1 (or jump on carry, but RTL opts will handle it)
kono
parents:
diff changeset
684 u1 - u2 -> ur
kono
parents:
diff changeset
685 res = u1 - u2
kono
parents:
diff changeset
686 ovf = res > u1 (or jump on carry, but RTL opts will handle it)
kono
parents:
diff changeset
687 s1 + u2 -> sr
kono
parents:
diff changeset
688 res = (S) ((U) s1 + u2)
kono
parents:
diff changeset
689 ovf = ((U) res ^ sgn) < u2
kono
parents:
diff changeset
690 s1 + u2 -> ur
kono
parents:
diff changeset
691 t1 = (S) (u2 ^ sgn)
kono
parents:
diff changeset
692 t2 = s1 + t1
kono
parents:
diff changeset
693 res = (U) t2 ^ sgn
kono
parents:
diff changeset
694 ovf = t1 < 0 ? t2 > s1 : t2 < s1 (or jump on overflow)
kono
parents:
diff changeset
695 s1 - u2 -> sr
kono
parents:
diff changeset
696 res = (S) ((U) s1 - u2)
kono
parents:
diff changeset
697 ovf = u2 > ((U) s1 ^ sgn)
kono
parents:
diff changeset
698 s1 - u2 -> ur
kono
parents:
diff changeset
699 res = (U) s1 - u2
kono
parents:
diff changeset
700 ovf = s1 < 0 || u2 > (U) s1
kono
parents:
diff changeset
701 u1 - s2 -> sr
kono
parents:
diff changeset
702 res = u1 - (U) s2
kono
parents:
diff changeset
703 ovf = u1 >= ((U) s2 ^ sgn)
kono
parents:
diff changeset
704 u1 - s2 -> ur
kono
parents:
diff changeset
705 t1 = u1 ^ sgn
kono
parents:
diff changeset
706 t2 = t1 - (U) s2
kono
parents:
diff changeset
707 res = t2 ^ sgn
kono
parents:
diff changeset
708 ovf = s2 < 0 ? (S) t2 < (S) t1 : (S) t2 > (S) t1 (or jump on overflow)
kono
parents:
diff changeset
709 s1 + s2 -> ur
kono
parents:
diff changeset
710 res = (U) s1 + (U) s2
kono
parents:
diff changeset
711 ovf = s2 < 0 ? (s1 | (S) res) < 0) : (s1 & (S) res) < 0)
kono
parents:
diff changeset
712 u1 + u2 -> sr
kono
parents:
diff changeset
713 res = (S) (u1 + u2)
kono
parents:
diff changeset
714 ovf = (U) res < u2 || res < 0
kono
parents:
diff changeset
715 u1 - u2 -> sr
kono
parents:
diff changeset
716 res = (S) (u1 - u2)
kono
parents:
diff changeset
717 ovf = u1 >= u2 ? res < 0 : res >= 0
kono
parents:
diff changeset
718 s1 - s2 -> ur
kono
parents:
diff changeset
719 res = (U) s1 - (U) s2
kono
parents:
diff changeset
720 ovf = s2 >= 0 ? ((s1 | (S) res) < 0) : ((s1 & (S) res) < 0) */
kono
parents:
diff changeset
721
kono
parents:
diff changeset
722 if (code == PLUS_EXPR && uns0_p && !uns1_p)
kono
parents:
diff changeset
723 {
kono
parents:
diff changeset
724 /* PLUS_EXPR is commutative, if operand signedness differs,
kono
parents:
diff changeset
725 canonicalize to the first operand being signed and second
kono
parents:
diff changeset
726 unsigned to simplify following code. */
kono
parents:
diff changeset
727 std::swap (op0, op1);
kono
parents:
diff changeset
728 std::swap (arg0, arg1);
kono
parents:
diff changeset
729 uns0_p = false;
kono
parents:
diff changeset
730 uns1_p = true;
kono
parents:
diff changeset
731 }
kono
parents:
diff changeset
732
kono
parents:
diff changeset
733 /* u1 +- u2 -> ur */
kono
parents:
diff changeset
734 if (uns0_p && uns1_p && unsr_p)
kono
parents:
diff changeset
735 {
kono
parents:
diff changeset
736 insn_code icode = optab_handler (code == PLUS_EXPR ? uaddv4_optab
kono
parents:
diff changeset
737 : usubv4_optab, mode);
kono
parents:
diff changeset
738 if (icode != CODE_FOR_nothing)
kono
parents:
diff changeset
739 {
kono
parents:
diff changeset
740 struct expand_operand ops[4];
kono
parents:
diff changeset
741 rtx_insn *last = get_last_insn ();
kono
parents:
diff changeset
742
kono
parents:
diff changeset
743 res = gen_reg_rtx (mode);
kono
parents:
diff changeset
744 create_output_operand (&ops[0], res, mode);
kono
parents:
diff changeset
745 create_input_operand (&ops[1], op0, mode);
kono
parents:
diff changeset
746 create_input_operand (&ops[2], op1, mode);
kono
parents:
diff changeset
747 create_fixed_operand (&ops[3], do_error);
kono
parents:
diff changeset
748 if (maybe_expand_insn (icode, 4, ops))
kono
parents:
diff changeset
749 {
kono
parents:
diff changeset
750 last = get_last_insn ();
kono
parents:
diff changeset
751 if (profile_status_for_fn (cfun) != PROFILE_ABSENT
kono
parents:
diff changeset
752 && JUMP_P (last)
kono
parents:
diff changeset
753 && any_condjump_p (last)
kono
parents:
diff changeset
754 && !find_reg_note (last, REG_BR_PROB, 0))
kono
parents:
diff changeset
755 add_reg_br_prob_note (last,
kono
parents:
diff changeset
756 profile_probability::very_unlikely ());
kono
parents:
diff changeset
757 emit_jump (done_label);
kono
parents:
diff changeset
758 goto do_error_label;
kono
parents:
diff changeset
759 }
kono
parents:
diff changeset
760
kono
parents:
diff changeset
761 delete_insns_since (last);
kono
parents:
diff changeset
762 }
kono
parents:
diff changeset
763
kono
parents:
diff changeset
764 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
765 unsigned. */
kono
parents:
diff changeset
766 res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab,
kono
parents:
diff changeset
767 op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
768 rtx tem = op0;
kono
parents:
diff changeset
769 /* For PLUS_EXPR, the operation is commutative, so we can pick
kono
parents:
diff changeset
770 operand to compare against. For prec <= BITS_PER_WORD, I think
kono
parents:
diff changeset
771 preferring REG operand is better over CONST_INT, because
kono
parents:
diff changeset
772 the CONST_INT might enlarge the instruction or CSE would need
kono
parents:
diff changeset
773 to figure out we'd already loaded it into a register before.
kono
parents:
diff changeset
774 For prec > BITS_PER_WORD, I think CONST_INT might be more beneficial,
kono
parents:
diff changeset
775 as then the multi-word comparison can be perhaps simplified. */
kono
parents:
diff changeset
776 if (code == PLUS_EXPR
kono
parents:
diff changeset
777 && (prec <= BITS_PER_WORD
kono
parents:
diff changeset
778 ? (CONST_SCALAR_INT_P (op0) && REG_P (op1))
kono
parents:
diff changeset
779 : CONST_SCALAR_INT_P (op1)))
kono
parents:
diff changeset
780 tem = op1;
kono
parents:
diff changeset
781 do_compare_rtx_and_jump (res, tem, code == PLUS_EXPR ? GEU : LEU,
kono
parents:
diff changeset
782 true, mode, NULL_RTX, NULL, done_label,
kono
parents:
diff changeset
783 profile_probability::very_likely ());
kono
parents:
diff changeset
784 goto do_error_label;
kono
parents:
diff changeset
785 }
kono
parents:
diff changeset
786
kono
parents:
diff changeset
787 /* s1 +- u2 -> sr */
kono
parents:
diff changeset
788 if (!uns0_p && uns1_p && !unsr_p)
kono
parents:
diff changeset
789 {
kono
parents:
diff changeset
790 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
791 unsigned. */
kono
parents:
diff changeset
792 res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab,
kono
parents:
diff changeset
793 op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
794 rtx tem = expand_binop (mode, add_optab,
kono
parents:
diff changeset
795 code == PLUS_EXPR ? res : op0, sgn,
kono
parents:
diff changeset
796 NULL_RTX, false, OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
797 do_compare_rtx_and_jump (tem, op1, GEU, true, mode, NULL_RTX, NULL,
kono
parents:
diff changeset
798 done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
799 goto do_error_label;
kono
parents:
diff changeset
800 }
kono
parents:
diff changeset
801
kono
parents:
diff changeset
802 /* s1 + u2 -> ur */
kono
parents:
diff changeset
803 if (code == PLUS_EXPR && !uns0_p && uns1_p && unsr_p)
kono
parents:
diff changeset
804 {
kono
parents:
diff changeset
805 op1 = expand_binop (mode, add_optab, op1, sgn, NULL_RTX, false,
kono
parents:
diff changeset
806 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
807 /* As we've changed op1, we have to avoid using the value range
kono
parents:
diff changeset
808 for the original argument. */
kono
parents:
diff changeset
809 arg1 = error_mark_node;
kono
parents:
diff changeset
810 do_xor = true;
kono
parents:
diff changeset
811 goto do_signed;
kono
parents:
diff changeset
812 }
kono
parents:
diff changeset
813
kono
parents:
diff changeset
814 /* u1 - s2 -> ur */
kono
parents:
diff changeset
815 if (code == MINUS_EXPR && uns0_p && !uns1_p && unsr_p)
kono
parents:
diff changeset
816 {
kono
parents:
diff changeset
817 op0 = expand_binop (mode, add_optab, op0, sgn, NULL_RTX, false,
kono
parents:
diff changeset
818 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
819 /* As we've changed op0, we have to avoid using the value range
kono
parents:
diff changeset
820 for the original argument. */
kono
parents:
diff changeset
821 arg0 = error_mark_node;
kono
parents:
diff changeset
822 do_xor = true;
kono
parents:
diff changeset
823 goto do_signed;
kono
parents:
diff changeset
824 }
kono
parents:
diff changeset
825
kono
parents:
diff changeset
826 /* s1 - u2 -> ur */
kono
parents:
diff changeset
827 if (code == MINUS_EXPR && !uns0_p && uns1_p && unsr_p)
kono
parents:
diff changeset
828 {
kono
parents:
diff changeset
829 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
830 unsigned. */
kono
parents:
diff changeset
831 res = expand_binop (mode, sub_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
832 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
833 int pos_neg = get_range_pos_neg (arg0);
kono
parents:
diff changeset
834 if (pos_neg == 2)
kono
parents:
diff changeset
835 /* If ARG0 is known to be always negative, this is always overflow. */
kono
parents:
diff changeset
836 emit_jump (do_error);
kono
parents:
diff changeset
837 else if (pos_neg == 3)
kono
parents:
diff changeset
838 /* If ARG0 is not known to be always positive, check at runtime. */
kono
parents:
diff changeset
839 do_compare_rtx_and_jump (op0, const0_rtx, LT, false, mode, NULL_RTX,
kono
parents:
diff changeset
840 NULL, do_error, profile_probability::very_unlikely ());
kono
parents:
diff changeset
841 do_compare_rtx_and_jump (op1, op0, LEU, true, mode, NULL_RTX, NULL,
kono
parents:
diff changeset
842 done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
843 goto do_error_label;
kono
parents:
diff changeset
844 }
kono
parents:
diff changeset
845
kono
parents:
diff changeset
846 /* u1 - s2 -> sr */
kono
parents:
diff changeset
847 if (code == MINUS_EXPR && uns0_p && !uns1_p && !unsr_p)
kono
parents:
diff changeset
848 {
kono
parents:
diff changeset
849 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
850 unsigned. */
kono
parents:
diff changeset
851 res = expand_binop (mode, sub_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
852 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
853 rtx tem = expand_binop (mode, add_optab, op1, sgn, NULL_RTX, false,
kono
parents:
diff changeset
854 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
855 do_compare_rtx_and_jump (op0, tem, LTU, true, mode, NULL_RTX, NULL,
kono
parents:
diff changeset
856 done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
857 goto do_error_label;
kono
parents:
diff changeset
858 }
kono
parents:
diff changeset
859
kono
parents:
diff changeset
860 /* u1 + u2 -> sr */
kono
parents:
diff changeset
861 if (code == PLUS_EXPR && uns0_p && uns1_p && !unsr_p)
kono
parents:
diff changeset
862 {
kono
parents:
diff changeset
863 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
864 unsigned. */
kono
parents:
diff changeset
865 res = expand_binop (mode, add_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
866 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
867 do_compare_rtx_and_jump (res, const0_rtx, LT, false, mode, NULL_RTX,
kono
parents:
diff changeset
868 NULL, do_error, profile_probability::very_unlikely ());
kono
parents:
diff changeset
869 rtx tem = op1;
kono
parents:
diff changeset
870 /* The operation is commutative, so we can pick operand to compare
kono
parents:
diff changeset
871 against. For prec <= BITS_PER_WORD, I think preferring REG operand
kono
parents:
diff changeset
872 is better over CONST_INT, because the CONST_INT might enlarge the
kono
parents:
diff changeset
873 instruction or CSE would need to figure out we'd already loaded it
kono
parents:
diff changeset
874 into a register before. For prec > BITS_PER_WORD, I think CONST_INT
kono
parents:
diff changeset
875 might be more beneficial, as then the multi-word comparison can be
kono
parents:
diff changeset
876 perhaps simplified. */
kono
parents:
diff changeset
877 if (prec <= BITS_PER_WORD
kono
parents:
diff changeset
878 ? (CONST_SCALAR_INT_P (op1) && REG_P (op0))
kono
parents:
diff changeset
879 : CONST_SCALAR_INT_P (op0))
kono
parents:
diff changeset
880 tem = op0;
kono
parents:
diff changeset
881 do_compare_rtx_and_jump (res, tem, GEU, true, mode, NULL_RTX, NULL,
kono
parents:
diff changeset
882 done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
883 goto do_error_label;
kono
parents:
diff changeset
884 }
kono
parents:
diff changeset
885
kono
parents:
diff changeset
886 /* s1 +- s2 -> ur */
kono
parents:
diff changeset
887 if (!uns0_p && !uns1_p && unsr_p)
kono
parents:
diff changeset
888 {
kono
parents:
diff changeset
889 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
890 unsigned. */
kono
parents:
diff changeset
891 res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab,
kono
parents:
diff changeset
892 op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
893 int pos_neg = get_range_pos_neg (arg1);
kono
parents:
diff changeset
894 if (code == PLUS_EXPR)
kono
parents:
diff changeset
895 {
kono
parents:
diff changeset
896 int pos_neg0 = get_range_pos_neg (arg0);
kono
parents:
diff changeset
897 if (pos_neg0 != 3 && pos_neg == 3)
kono
parents:
diff changeset
898 {
kono
parents:
diff changeset
899 std::swap (op0, op1);
kono
parents:
diff changeset
900 pos_neg = pos_neg0;
kono
parents:
diff changeset
901 }
kono
parents:
diff changeset
902 }
kono
parents:
diff changeset
903 rtx tem;
kono
parents:
diff changeset
904 if (pos_neg != 3)
kono
parents:
diff changeset
905 {
kono
parents:
diff changeset
906 tem = expand_binop (mode, ((pos_neg == 1) ^ (code == MINUS_EXPR))
kono
parents:
diff changeset
907 ? and_optab : ior_optab,
kono
parents:
diff changeset
908 op0, res, NULL_RTX, false, OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
909 do_compare_rtx_and_jump (tem, const0_rtx, GE, false, mode, NULL,
kono
parents:
diff changeset
910 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
911 }
kono
parents:
diff changeset
912 else
kono
parents:
diff changeset
913 {
kono
parents:
diff changeset
914 rtx_code_label *do_ior_label = gen_label_rtx ();
kono
parents:
diff changeset
915 do_compare_rtx_and_jump (op1, const0_rtx,
kono
parents:
diff changeset
916 code == MINUS_EXPR ? GE : LT, false, mode,
kono
parents:
diff changeset
917 NULL_RTX, NULL, do_ior_label,
kono
parents:
diff changeset
918 profile_probability::even ());
kono
parents:
diff changeset
919 tem = expand_binop (mode, and_optab, op0, res, NULL_RTX, false,
kono
parents:
diff changeset
920 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
921 do_compare_rtx_and_jump (tem, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
922 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
923 emit_jump (do_error);
kono
parents:
diff changeset
924 emit_label (do_ior_label);
kono
parents:
diff changeset
925 tem = expand_binop (mode, ior_optab, op0, res, NULL_RTX, false,
kono
parents:
diff changeset
926 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
927 do_compare_rtx_and_jump (tem, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
928 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
929 }
kono
parents:
diff changeset
930 goto do_error_label;
kono
parents:
diff changeset
931 }
kono
parents:
diff changeset
932
kono
parents:
diff changeset
933 /* u1 - u2 -> sr */
kono
parents:
diff changeset
934 if (code == MINUS_EXPR && uns0_p && uns1_p && !unsr_p)
kono
parents:
diff changeset
935 {
kono
parents:
diff changeset
936 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
937 unsigned. */
kono
parents:
diff changeset
938 res = expand_binop (mode, sub_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
939 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
940 rtx_code_label *op0_geu_op1 = gen_label_rtx ();
kono
parents:
diff changeset
941 do_compare_rtx_and_jump (op0, op1, GEU, true, mode, NULL_RTX, NULL,
kono
parents:
diff changeset
942 op0_geu_op1, profile_probability::even ());
kono
parents:
diff changeset
943 do_compare_rtx_and_jump (res, const0_rtx, LT, false, mode, NULL_RTX,
kono
parents:
diff changeset
944 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
945 emit_jump (do_error);
kono
parents:
diff changeset
946 emit_label (op0_geu_op1);
kono
parents:
diff changeset
947 do_compare_rtx_and_jump (res, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
948 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
949 goto do_error_label;
kono
parents:
diff changeset
950 }
kono
parents:
diff changeset
951
kono
parents:
diff changeset
952 gcc_assert (!uns0_p && !uns1_p && !unsr_p);
kono
parents:
diff changeset
953
kono
parents:
diff changeset
954 /* s1 +- s2 -> sr */
kono
parents:
diff changeset
955 do_signed:
kono
parents:
diff changeset
956 {
kono
parents:
diff changeset
957 insn_code icode = optab_handler (code == PLUS_EXPR ? addv4_optab
kono
parents:
diff changeset
958 : subv4_optab, mode);
kono
parents:
diff changeset
959 if (icode != CODE_FOR_nothing)
kono
parents:
diff changeset
960 {
kono
parents:
diff changeset
961 struct expand_operand ops[4];
kono
parents:
diff changeset
962 rtx_insn *last = get_last_insn ();
kono
parents:
diff changeset
963
kono
parents:
diff changeset
964 res = gen_reg_rtx (mode);
kono
parents:
diff changeset
965 create_output_operand (&ops[0], res, mode);
kono
parents:
diff changeset
966 create_input_operand (&ops[1], op0, mode);
kono
parents:
diff changeset
967 create_input_operand (&ops[2], op1, mode);
kono
parents:
diff changeset
968 create_fixed_operand (&ops[3], do_error);
kono
parents:
diff changeset
969 if (maybe_expand_insn (icode, 4, ops))
kono
parents:
diff changeset
970 {
kono
parents:
diff changeset
971 last = get_last_insn ();
kono
parents:
diff changeset
972 if (profile_status_for_fn (cfun) != PROFILE_ABSENT
kono
parents:
diff changeset
973 && JUMP_P (last)
kono
parents:
diff changeset
974 && any_condjump_p (last)
kono
parents:
diff changeset
975 && !find_reg_note (last, REG_BR_PROB, 0))
kono
parents:
diff changeset
976 add_reg_br_prob_note (last,
kono
parents:
diff changeset
977 profile_probability::very_unlikely ());
kono
parents:
diff changeset
978 emit_jump (done_label);
kono
parents:
diff changeset
979 goto do_error_label;
kono
parents:
diff changeset
980 }
kono
parents:
diff changeset
981
kono
parents:
diff changeset
982 delete_insns_since (last);
kono
parents:
diff changeset
983 }
kono
parents:
diff changeset
984
kono
parents:
diff changeset
985 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
986 unsigned. */
kono
parents:
diff changeset
987 res = expand_binop (mode, code == PLUS_EXPR ? add_optab : sub_optab,
kono
parents:
diff changeset
988 op0, op1, NULL_RTX, false, OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
989
kono
parents:
diff changeset
990 /* If we can prove that one of the arguments (for MINUS_EXPR only
kono
parents:
diff changeset
991 the second operand, as subtraction is not commutative) is always
kono
parents:
diff changeset
992 non-negative or always negative, we can do just one comparison
kono
parents:
diff changeset
993 and conditional jump. */
kono
parents:
diff changeset
994 int pos_neg = get_range_pos_neg (arg1);
kono
parents:
diff changeset
995 if (code == PLUS_EXPR)
kono
parents:
diff changeset
996 {
kono
parents:
diff changeset
997 int pos_neg0 = get_range_pos_neg (arg0);
kono
parents:
diff changeset
998 if (pos_neg0 != 3 && pos_neg == 3)
kono
parents:
diff changeset
999 {
kono
parents:
diff changeset
1000 std::swap (op0, op1);
kono
parents:
diff changeset
1001 pos_neg = pos_neg0;
kono
parents:
diff changeset
1002 }
kono
parents:
diff changeset
1003 }
kono
parents:
diff changeset
1004
kono
parents:
diff changeset
1005 /* Addition overflows if and only if the two operands have the same sign,
kono
parents:
diff changeset
1006 and the result has the opposite sign. Subtraction overflows if and
kono
parents:
diff changeset
1007 only if the two operands have opposite sign, and the subtrahend has
kono
parents:
diff changeset
1008 the same sign as the result. Here 0 is counted as positive. */
kono
parents:
diff changeset
1009 if (pos_neg == 3)
kono
parents:
diff changeset
1010 {
kono
parents:
diff changeset
1011 /* Compute op0 ^ op1 (operands have opposite sign). */
kono
parents:
diff changeset
1012 rtx op_xor = expand_binop (mode, xor_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
1013 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1014
kono
parents:
diff changeset
1015 /* Compute res ^ op1 (result and 2nd operand have opposite sign). */
kono
parents:
diff changeset
1016 rtx res_xor = expand_binop (mode, xor_optab, res, op1, NULL_RTX, false,
kono
parents:
diff changeset
1017 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1018
kono
parents:
diff changeset
1019 rtx tem;
kono
parents:
diff changeset
1020 if (code == PLUS_EXPR)
kono
parents:
diff changeset
1021 {
kono
parents:
diff changeset
1022 /* Compute (res ^ op1) & ~(op0 ^ op1). */
kono
parents:
diff changeset
1023 tem = expand_unop (mode, one_cmpl_optab, op_xor, NULL_RTX, false);
kono
parents:
diff changeset
1024 tem = expand_binop (mode, and_optab, res_xor, tem, NULL_RTX, false,
kono
parents:
diff changeset
1025 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1026 }
kono
parents:
diff changeset
1027 else
kono
parents:
diff changeset
1028 {
kono
parents:
diff changeset
1029 /* Compute (op0 ^ op1) & ~(res ^ op1). */
kono
parents:
diff changeset
1030 tem = expand_unop (mode, one_cmpl_optab, res_xor, NULL_RTX, false);
kono
parents:
diff changeset
1031 tem = expand_binop (mode, and_optab, op_xor, tem, NULL_RTX, false,
kono
parents:
diff changeset
1032 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1033 }
kono
parents:
diff changeset
1034
kono
parents:
diff changeset
1035 /* No overflow if the result has bit sign cleared. */
kono
parents:
diff changeset
1036 do_compare_rtx_and_jump (tem, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
1037 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1038 }
kono
parents:
diff changeset
1039
kono
parents:
diff changeset
1040 /* Compare the result of the operation with the first operand.
kono
parents:
diff changeset
1041 No overflow for addition if second operand is positive and result
kono
parents:
diff changeset
1042 is larger or second operand is negative and result is smaller.
kono
parents:
diff changeset
1043 Likewise for subtraction with sign of second operand flipped. */
kono
parents:
diff changeset
1044 else
kono
parents:
diff changeset
1045 do_compare_rtx_and_jump (res, op0,
kono
parents:
diff changeset
1046 (pos_neg == 1) ^ (code == MINUS_EXPR) ? GE : LE,
kono
parents:
diff changeset
1047 false, mode, NULL_RTX, NULL, done_label,
kono
parents:
diff changeset
1048 profile_probability::very_likely ());
kono
parents:
diff changeset
1049 }
kono
parents:
diff changeset
1050
kono
parents:
diff changeset
1051 do_error_label:
kono
parents:
diff changeset
1052 emit_label (do_error);
kono
parents:
diff changeset
1053 if (is_ubsan)
kono
parents:
diff changeset
1054 {
kono
parents:
diff changeset
1055 /* Expand the ubsan builtin call. */
kono
parents:
diff changeset
1056 push_temp_slots ();
kono
parents:
diff changeset
1057 fn = ubsan_build_overflow_builtin (code, loc, TREE_TYPE (arg0),
kono
parents:
diff changeset
1058 arg0, arg1, datap);
kono
parents:
diff changeset
1059 expand_normal (fn);
kono
parents:
diff changeset
1060 pop_temp_slots ();
kono
parents:
diff changeset
1061 do_pending_stack_adjust ();
kono
parents:
diff changeset
1062 }
kono
parents:
diff changeset
1063 else if (lhs)
kono
parents:
diff changeset
1064 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
1065
kono
parents:
diff changeset
1066 /* We're done. */
kono
parents:
diff changeset
1067 emit_label (done_label);
kono
parents:
diff changeset
1068
kono
parents:
diff changeset
1069 if (lhs)
kono
parents:
diff changeset
1070 {
kono
parents:
diff changeset
1071 if (is_ubsan)
kono
parents:
diff changeset
1072 expand_ubsan_result_store (target, res);
kono
parents:
diff changeset
1073 else
kono
parents:
diff changeset
1074 {
kono
parents:
diff changeset
1075 if (do_xor)
kono
parents:
diff changeset
1076 res = expand_binop (mode, add_optab, res, sgn, NULL_RTX, false,
kono
parents:
diff changeset
1077 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1078
kono
parents:
diff changeset
1079 expand_arith_overflow_result_store (lhs, target, mode, res);
kono
parents:
diff changeset
1080 }
kono
parents:
diff changeset
1081 }
kono
parents:
diff changeset
1082 }
kono
parents:
diff changeset
1083
kono
parents:
diff changeset
1084 /* Add negate overflow checking to the statement STMT. */
kono
parents:
diff changeset
1085
kono
parents:
diff changeset
1086 static void
kono
parents:
diff changeset
1087 expand_neg_overflow (location_t loc, tree lhs, tree arg1, bool is_ubsan,
kono
parents:
diff changeset
1088 tree *datap)
kono
parents:
diff changeset
1089 {
kono
parents:
diff changeset
1090 rtx res, op1;
kono
parents:
diff changeset
1091 tree fn;
kono
parents:
diff changeset
1092 rtx_code_label *done_label, *do_error;
kono
parents:
diff changeset
1093 rtx target = NULL_RTX;
kono
parents:
diff changeset
1094
kono
parents:
diff changeset
1095 done_label = gen_label_rtx ();
kono
parents:
diff changeset
1096 do_error = gen_label_rtx ();
kono
parents:
diff changeset
1097
kono
parents:
diff changeset
1098 do_pending_stack_adjust ();
kono
parents:
diff changeset
1099 op1 = expand_normal (arg1);
kono
parents:
diff changeset
1100
kono
parents:
diff changeset
1101 scalar_int_mode mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg1));
kono
parents:
diff changeset
1102 if (lhs)
kono
parents:
diff changeset
1103 {
kono
parents:
diff changeset
1104 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
1105 if (!is_ubsan)
kono
parents:
diff changeset
1106 write_complex_part (target, const0_rtx, true);
kono
parents:
diff changeset
1107 }
kono
parents:
diff changeset
1108
kono
parents:
diff changeset
1109 enum insn_code icode = optab_handler (negv3_optab, mode);
kono
parents:
diff changeset
1110 if (icode != CODE_FOR_nothing)
kono
parents:
diff changeset
1111 {
kono
parents:
diff changeset
1112 struct expand_operand ops[3];
kono
parents:
diff changeset
1113 rtx_insn *last = get_last_insn ();
kono
parents:
diff changeset
1114
kono
parents:
diff changeset
1115 res = gen_reg_rtx (mode);
kono
parents:
diff changeset
1116 create_output_operand (&ops[0], res, mode);
kono
parents:
diff changeset
1117 create_input_operand (&ops[1], op1, mode);
kono
parents:
diff changeset
1118 create_fixed_operand (&ops[2], do_error);
kono
parents:
diff changeset
1119 if (maybe_expand_insn (icode, 3, ops))
kono
parents:
diff changeset
1120 {
kono
parents:
diff changeset
1121 last = get_last_insn ();
kono
parents:
diff changeset
1122 if (profile_status_for_fn (cfun) != PROFILE_ABSENT
kono
parents:
diff changeset
1123 && JUMP_P (last)
kono
parents:
diff changeset
1124 && any_condjump_p (last)
kono
parents:
diff changeset
1125 && !find_reg_note (last, REG_BR_PROB, 0))
kono
parents:
diff changeset
1126 add_reg_br_prob_note (last,
kono
parents:
diff changeset
1127 profile_probability::very_unlikely ());
kono
parents:
diff changeset
1128 emit_jump (done_label);
kono
parents:
diff changeset
1129 }
kono
parents:
diff changeset
1130 else
kono
parents:
diff changeset
1131 {
kono
parents:
diff changeset
1132 delete_insns_since (last);
kono
parents:
diff changeset
1133 icode = CODE_FOR_nothing;
kono
parents:
diff changeset
1134 }
kono
parents:
diff changeset
1135 }
kono
parents:
diff changeset
1136
kono
parents:
diff changeset
1137 if (icode == CODE_FOR_nothing)
kono
parents:
diff changeset
1138 {
kono
parents:
diff changeset
1139 /* Compute the operation. On RTL level, the addition is always
kono
parents:
diff changeset
1140 unsigned. */
kono
parents:
diff changeset
1141 res = expand_unop (mode, neg_optab, op1, NULL_RTX, false);
kono
parents:
diff changeset
1142
kono
parents:
diff changeset
1143 /* Compare the operand with the most negative value. */
kono
parents:
diff changeset
1144 rtx minv = expand_normal (TYPE_MIN_VALUE (TREE_TYPE (arg1)));
kono
parents:
diff changeset
1145 do_compare_rtx_and_jump (op1, minv, NE, true, mode, NULL_RTX, NULL,
kono
parents:
diff changeset
1146 done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1147 }
kono
parents:
diff changeset
1148
kono
parents:
diff changeset
1149 emit_label (do_error);
kono
parents:
diff changeset
1150 if (is_ubsan)
kono
parents:
diff changeset
1151 {
kono
parents:
diff changeset
1152 /* Expand the ubsan builtin call. */
kono
parents:
diff changeset
1153 push_temp_slots ();
kono
parents:
diff changeset
1154 fn = ubsan_build_overflow_builtin (NEGATE_EXPR, loc, TREE_TYPE (arg1),
kono
parents:
diff changeset
1155 arg1, NULL_TREE, datap);
kono
parents:
diff changeset
1156 expand_normal (fn);
kono
parents:
diff changeset
1157 pop_temp_slots ();
kono
parents:
diff changeset
1158 do_pending_stack_adjust ();
kono
parents:
diff changeset
1159 }
kono
parents:
diff changeset
1160 else if (lhs)
kono
parents:
diff changeset
1161 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
1162
kono
parents:
diff changeset
1163 /* We're done. */
kono
parents:
diff changeset
1164 emit_label (done_label);
kono
parents:
diff changeset
1165
kono
parents:
diff changeset
1166 if (lhs)
kono
parents:
diff changeset
1167 {
kono
parents:
diff changeset
1168 if (is_ubsan)
kono
parents:
diff changeset
1169 expand_ubsan_result_store (target, res);
kono
parents:
diff changeset
1170 else
kono
parents:
diff changeset
1171 expand_arith_overflow_result_store (lhs, target, mode, res);
kono
parents:
diff changeset
1172 }
kono
parents:
diff changeset
1173 }
kono
parents:
diff changeset
1174
kono
parents:
diff changeset
1175 /* Add mul overflow checking to the statement STMT. */
kono
parents:
diff changeset
1176
kono
parents:
diff changeset
1177 static void
kono
parents:
diff changeset
1178 expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
kono
parents:
diff changeset
1179 bool unsr_p, bool uns0_p, bool uns1_p, bool is_ubsan,
kono
parents:
diff changeset
1180 tree *datap)
kono
parents:
diff changeset
1181 {
kono
parents:
diff changeset
1182 rtx res, op0, op1;
kono
parents:
diff changeset
1183 tree fn, type;
kono
parents:
diff changeset
1184 rtx_code_label *done_label, *do_error;
kono
parents:
diff changeset
1185 rtx target = NULL_RTX;
kono
parents:
diff changeset
1186 signop sign;
kono
parents:
diff changeset
1187 enum insn_code icode;
kono
parents:
diff changeset
1188
kono
parents:
diff changeset
1189 done_label = gen_label_rtx ();
kono
parents:
diff changeset
1190 do_error = gen_label_rtx ();
kono
parents:
diff changeset
1191
kono
parents:
diff changeset
1192 do_pending_stack_adjust ();
kono
parents:
diff changeset
1193 op0 = expand_normal (arg0);
kono
parents:
diff changeset
1194 op1 = expand_normal (arg1);
kono
parents:
diff changeset
1195
kono
parents:
diff changeset
1196 scalar_int_mode mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg0));
kono
parents:
diff changeset
1197 bool uns = unsr_p;
kono
parents:
diff changeset
1198 if (lhs)
kono
parents:
diff changeset
1199 {
kono
parents:
diff changeset
1200 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
1201 if (!is_ubsan)
kono
parents:
diff changeset
1202 write_complex_part (target, const0_rtx, true);
kono
parents:
diff changeset
1203 }
kono
parents:
diff changeset
1204
kono
parents:
diff changeset
1205 if (is_ubsan)
kono
parents:
diff changeset
1206 gcc_assert (!unsr_p && !uns0_p && !uns1_p);
kono
parents:
diff changeset
1207
kono
parents:
diff changeset
1208 /* We assume both operands and result have the same precision
kono
parents:
diff changeset
1209 here (GET_MODE_BITSIZE (mode)), S stands for signed type
kono
parents:
diff changeset
1210 with that precision, U for unsigned type with that precision,
kono
parents:
diff changeset
1211 sgn for unsigned most significant bit in that precision.
kono
parents:
diff changeset
1212 s1 is signed first operand, u1 is unsigned first operand,
kono
parents:
diff changeset
1213 s2 is signed second operand, u2 is unsigned second operand,
kono
parents:
diff changeset
1214 sr is signed result, ur is unsigned result and the following
kono
parents:
diff changeset
1215 rules say how to compute result (which is always result of
kono
parents:
diff changeset
1216 the operands as if both were unsigned, cast to the right
kono
parents:
diff changeset
1217 signedness) and how to compute whether operation overflowed.
kono
parents:
diff changeset
1218 main_ovf (false) stands for jump on signed multiplication
kono
parents:
diff changeset
1219 overflow or the main algorithm with uns == false.
kono
parents:
diff changeset
1220 main_ovf (true) stands for jump on unsigned multiplication
kono
parents:
diff changeset
1221 overflow or the main algorithm with uns == true.
kono
parents:
diff changeset
1222
kono
parents:
diff changeset
1223 s1 * s2 -> sr
kono
parents:
diff changeset
1224 res = (S) ((U) s1 * (U) s2)
kono
parents:
diff changeset
1225 ovf = main_ovf (false)
kono
parents:
diff changeset
1226 u1 * u2 -> ur
kono
parents:
diff changeset
1227 res = u1 * u2
kono
parents:
diff changeset
1228 ovf = main_ovf (true)
kono
parents:
diff changeset
1229 s1 * u2 -> ur
kono
parents:
diff changeset
1230 res = (U) s1 * u2
kono
parents:
diff changeset
1231 ovf = (s1 < 0 && u2) || main_ovf (true)
kono
parents:
diff changeset
1232 u1 * u2 -> sr
kono
parents:
diff changeset
1233 res = (S) (u1 * u2)
kono
parents:
diff changeset
1234 ovf = res < 0 || main_ovf (true)
kono
parents:
diff changeset
1235 s1 * u2 -> sr
kono
parents:
diff changeset
1236 res = (S) ((U) s1 * u2)
kono
parents:
diff changeset
1237 ovf = (S) u2 >= 0 ? main_ovf (false)
kono
parents:
diff changeset
1238 : (s1 != 0 && (s1 != -1 || u2 != (U) res))
kono
parents:
diff changeset
1239 s1 * s2 -> ur
kono
parents:
diff changeset
1240 t1 = (s1 & s2) < 0 ? (-(U) s1) : ((U) s1)
kono
parents:
diff changeset
1241 t2 = (s1 & s2) < 0 ? (-(U) s2) : ((U) s2)
kono
parents:
diff changeset
1242 res = t1 * t2
kono
parents:
diff changeset
1243 ovf = (s1 ^ s2) < 0 ? (s1 && s2) : main_ovf (true) */
kono
parents:
diff changeset
1244
kono
parents:
diff changeset
1245 if (uns0_p && !uns1_p)
kono
parents:
diff changeset
1246 {
kono
parents:
diff changeset
1247 /* Multiplication is commutative, if operand signedness differs,
kono
parents:
diff changeset
1248 canonicalize to the first operand being signed and second
kono
parents:
diff changeset
1249 unsigned to simplify following code. */
kono
parents:
diff changeset
1250 std::swap (op0, op1);
kono
parents:
diff changeset
1251 std::swap (arg0, arg1);
kono
parents:
diff changeset
1252 uns0_p = false;
kono
parents:
diff changeset
1253 uns1_p = true;
kono
parents:
diff changeset
1254 }
kono
parents:
diff changeset
1255
kono
parents:
diff changeset
1256 int pos_neg0 = get_range_pos_neg (arg0);
kono
parents:
diff changeset
1257 int pos_neg1 = get_range_pos_neg (arg1);
kono
parents:
diff changeset
1258
kono
parents:
diff changeset
1259 /* s1 * u2 -> ur */
kono
parents:
diff changeset
1260 if (!uns0_p && uns1_p && unsr_p)
kono
parents:
diff changeset
1261 {
kono
parents:
diff changeset
1262 switch (pos_neg0)
kono
parents:
diff changeset
1263 {
kono
parents:
diff changeset
1264 case 1:
kono
parents:
diff changeset
1265 /* If s1 is non-negative, just perform normal u1 * u2 -> ur. */
kono
parents:
diff changeset
1266 goto do_main;
kono
parents:
diff changeset
1267 case 2:
kono
parents:
diff changeset
1268 /* If s1 is negative, avoid the main code, just multiply and
kono
parents:
diff changeset
1269 signal overflow if op1 is not 0. */
kono
parents:
diff changeset
1270 struct separate_ops ops;
kono
parents:
diff changeset
1271 ops.code = MULT_EXPR;
kono
parents:
diff changeset
1272 ops.type = TREE_TYPE (arg1);
kono
parents:
diff changeset
1273 ops.op0 = make_tree (ops.type, op0);
kono
parents:
diff changeset
1274 ops.op1 = make_tree (ops.type, op1);
kono
parents:
diff changeset
1275 ops.op2 = NULL_TREE;
kono
parents:
diff changeset
1276 ops.location = loc;
kono
parents:
diff changeset
1277 res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1278 do_compare_rtx_and_jump (op1, const0_rtx, EQ, true, mode, NULL_RTX,
kono
parents:
diff changeset
1279 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1280 goto do_error_label;
kono
parents:
diff changeset
1281 case 3:
kono
parents:
diff changeset
1282 rtx_code_label *do_main_label;
kono
parents:
diff changeset
1283 do_main_label = gen_label_rtx ();
kono
parents:
diff changeset
1284 do_compare_rtx_and_jump (op0, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
1285 NULL, do_main_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1286 do_compare_rtx_and_jump (op1, const0_rtx, EQ, true, mode, NULL_RTX,
kono
parents:
diff changeset
1287 NULL, do_main_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1288 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
1289 emit_label (do_main_label);
kono
parents:
diff changeset
1290 goto do_main;
kono
parents:
diff changeset
1291 default:
kono
parents:
diff changeset
1292 gcc_unreachable ();
kono
parents:
diff changeset
1293 }
kono
parents:
diff changeset
1294 }
kono
parents:
diff changeset
1295
kono
parents:
diff changeset
1296 /* u1 * u2 -> sr */
kono
parents:
diff changeset
1297 if (uns0_p && uns1_p && !unsr_p)
kono
parents:
diff changeset
1298 {
kono
parents:
diff changeset
1299 uns = true;
kono
parents:
diff changeset
1300 /* Rest of handling of this case after res is computed. */
kono
parents:
diff changeset
1301 goto do_main;
kono
parents:
diff changeset
1302 }
kono
parents:
diff changeset
1303
kono
parents:
diff changeset
1304 /* s1 * u2 -> sr */
kono
parents:
diff changeset
1305 if (!uns0_p && uns1_p && !unsr_p)
kono
parents:
diff changeset
1306 {
kono
parents:
diff changeset
1307 switch (pos_neg1)
kono
parents:
diff changeset
1308 {
kono
parents:
diff changeset
1309 case 1:
kono
parents:
diff changeset
1310 goto do_main;
kono
parents:
diff changeset
1311 case 2:
kono
parents:
diff changeset
1312 /* If (S) u2 is negative (i.e. u2 is larger than maximum of S,
kono
parents:
diff changeset
1313 avoid the main code, just multiply and signal overflow
kono
parents:
diff changeset
1314 unless 0 * u2 or -1 * ((U) Smin). */
kono
parents:
diff changeset
1315 struct separate_ops ops;
kono
parents:
diff changeset
1316 ops.code = MULT_EXPR;
kono
parents:
diff changeset
1317 ops.type = TREE_TYPE (arg1);
kono
parents:
diff changeset
1318 ops.op0 = make_tree (ops.type, op0);
kono
parents:
diff changeset
1319 ops.op1 = make_tree (ops.type, op1);
kono
parents:
diff changeset
1320 ops.op2 = NULL_TREE;
kono
parents:
diff changeset
1321 ops.location = loc;
kono
parents:
diff changeset
1322 res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1323 do_compare_rtx_and_jump (op0, const0_rtx, EQ, true, mode, NULL_RTX,
kono
parents:
diff changeset
1324 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1325 do_compare_rtx_and_jump (op0, constm1_rtx, NE, true, mode, NULL_RTX,
kono
parents:
diff changeset
1326 NULL, do_error, profile_probability::very_unlikely ());
kono
parents:
diff changeset
1327 int prec;
kono
parents:
diff changeset
1328 prec = GET_MODE_PRECISION (mode);
kono
parents:
diff changeset
1329 rtx sgn;
kono
parents:
diff changeset
1330 sgn = immed_wide_int_const (wi::min_value (prec, SIGNED), mode);
kono
parents:
diff changeset
1331 do_compare_rtx_and_jump (op1, sgn, EQ, true, mode, NULL_RTX,
kono
parents:
diff changeset
1332 NULL, done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1333 goto do_error_label;
kono
parents:
diff changeset
1334 case 3:
kono
parents:
diff changeset
1335 /* Rest of handling of this case after res is computed. */
kono
parents:
diff changeset
1336 goto do_main;
kono
parents:
diff changeset
1337 default:
kono
parents:
diff changeset
1338 gcc_unreachable ();
kono
parents:
diff changeset
1339 }
kono
parents:
diff changeset
1340 }
kono
parents:
diff changeset
1341
kono
parents:
diff changeset
1342 /* s1 * s2 -> ur */
kono
parents:
diff changeset
1343 if (!uns0_p && !uns1_p && unsr_p)
kono
parents:
diff changeset
1344 {
kono
parents:
diff changeset
1345 rtx tem, tem2;
kono
parents:
diff changeset
1346 switch (pos_neg0 | pos_neg1)
kono
parents:
diff changeset
1347 {
kono
parents:
diff changeset
1348 case 1: /* Both operands known to be non-negative. */
kono
parents:
diff changeset
1349 goto do_main;
kono
parents:
diff changeset
1350 case 2: /* Both operands known to be negative. */
kono
parents:
diff changeset
1351 op0 = expand_unop (mode, neg_optab, op0, NULL_RTX, false);
kono
parents:
diff changeset
1352 op1 = expand_unop (mode, neg_optab, op1, NULL_RTX, false);
kono
parents:
diff changeset
1353 /* Avoid looking at arg0/arg1 ranges, as we've changed
kono
parents:
diff changeset
1354 the arguments. */
kono
parents:
diff changeset
1355 arg0 = error_mark_node;
kono
parents:
diff changeset
1356 arg1 = error_mark_node;
kono
parents:
diff changeset
1357 goto do_main;
kono
parents:
diff changeset
1358 case 3:
kono
parents:
diff changeset
1359 if ((pos_neg0 ^ pos_neg1) == 3)
kono
parents:
diff changeset
1360 {
kono
parents:
diff changeset
1361 /* If one operand is known to be negative and the other
kono
parents:
diff changeset
1362 non-negative, this overflows always, unless the non-negative
kono
parents:
diff changeset
1363 one is 0. Just do normal multiply and set overflow
kono
parents:
diff changeset
1364 unless one of the operands is 0. */
kono
parents:
diff changeset
1365 struct separate_ops ops;
kono
parents:
diff changeset
1366 ops.code = MULT_EXPR;
kono
parents:
diff changeset
1367 ops.type
kono
parents:
diff changeset
1368 = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
kono
parents:
diff changeset
1369 1);
kono
parents:
diff changeset
1370 ops.op0 = make_tree (ops.type, op0);
kono
parents:
diff changeset
1371 ops.op1 = make_tree (ops.type, op1);
kono
parents:
diff changeset
1372 ops.op2 = NULL_TREE;
kono
parents:
diff changeset
1373 ops.location = loc;
kono
parents:
diff changeset
1374 res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1375 tem = expand_binop (mode, and_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
1376 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1377 do_compare_rtx_and_jump (tem, const0_rtx, EQ, true, mode,
kono
parents:
diff changeset
1378 NULL_RTX, NULL, done_label,
kono
parents:
diff changeset
1379 profile_probability::very_likely ());
kono
parents:
diff changeset
1380 goto do_error_label;
kono
parents:
diff changeset
1381 }
kono
parents:
diff changeset
1382 /* The general case, do all the needed comparisons at runtime. */
kono
parents:
diff changeset
1383 rtx_code_label *do_main_label, *after_negate_label;
kono
parents:
diff changeset
1384 rtx rop0, rop1;
kono
parents:
diff changeset
1385 rop0 = gen_reg_rtx (mode);
kono
parents:
diff changeset
1386 rop1 = gen_reg_rtx (mode);
kono
parents:
diff changeset
1387 emit_move_insn (rop0, op0);
kono
parents:
diff changeset
1388 emit_move_insn (rop1, op1);
kono
parents:
diff changeset
1389 op0 = rop0;
kono
parents:
diff changeset
1390 op1 = rop1;
kono
parents:
diff changeset
1391 do_main_label = gen_label_rtx ();
kono
parents:
diff changeset
1392 after_negate_label = gen_label_rtx ();
kono
parents:
diff changeset
1393 tem = expand_binop (mode, and_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
1394 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1395 do_compare_rtx_and_jump (tem, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
1396 NULL, after_negate_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1397 /* Both arguments negative here, negate them and continue with
kono
parents:
diff changeset
1398 normal unsigned overflow checking multiplication. */
kono
parents:
diff changeset
1399 emit_move_insn (op0, expand_unop (mode, neg_optab, op0,
kono
parents:
diff changeset
1400 NULL_RTX, false));
kono
parents:
diff changeset
1401 emit_move_insn (op1, expand_unop (mode, neg_optab, op1,
kono
parents:
diff changeset
1402 NULL_RTX, false));
kono
parents:
diff changeset
1403 /* Avoid looking at arg0/arg1 ranges, as we might have changed
kono
parents:
diff changeset
1404 the arguments. */
kono
parents:
diff changeset
1405 arg0 = error_mark_node;
kono
parents:
diff changeset
1406 arg1 = error_mark_node;
kono
parents:
diff changeset
1407 emit_jump (do_main_label);
kono
parents:
diff changeset
1408 emit_label (after_negate_label);
kono
parents:
diff changeset
1409 tem2 = expand_binop (mode, xor_optab, op0, op1, NULL_RTX, false,
kono
parents:
diff changeset
1410 OPTAB_LIB_WIDEN);
kono
parents:
diff changeset
1411 do_compare_rtx_and_jump (tem2, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
1412 NULL, do_main_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1413 /* One argument is negative here, the other positive. This
kono
parents:
diff changeset
1414 overflows always, unless one of the arguments is 0. But
kono
parents:
diff changeset
1415 if e.g. s2 is 0, (U) s1 * 0 doesn't overflow, whatever s1
kono
parents:
diff changeset
1416 is, thus we can keep do_main code oring in overflow as is. */
kono
parents:
diff changeset
1417 do_compare_rtx_and_jump (tem, const0_rtx, EQ, true, mode, NULL_RTX,
kono
parents:
diff changeset
1418 NULL, do_main_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1419 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
1420 emit_label (do_main_label);
kono
parents:
diff changeset
1421 goto do_main;
kono
parents:
diff changeset
1422 default:
kono
parents:
diff changeset
1423 gcc_unreachable ();
kono
parents:
diff changeset
1424 }
kono
parents:
diff changeset
1425 }
kono
parents:
diff changeset
1426
kono
parents:
diff changeset
1427 do_main:
kono
parents:
diff changeset
1428 type = build_nonstandard_integer_type (GET_MODE_PRECISION (mode), uns);
kono
parents:
diff changeset
1429 sign = uns ? UNSIGNED : SIGNED;
kono
parents:
diff changeset
1430 icode = optab_handler (uns ? umulv4_optab : mulv4_optab, mode);
kono
parents:
diff changeset
1431 if (icode != CODE_FOR_nothing)
kono
parents:
diff changeset
1432 {
kono
parents:
diff changeset
1433 struct expand_operand ops[4];
kono
parents:
diff changeset
1434 rtx_insn *last = get_last_insn ();
kono
parents:
diff changeset
1435
kono
parents:
diff changeset
1436 res = gen_reg_rtx (mode);
kono
parents:
diff changeset
1437 create_output_operand (&ops[0], res, mode);
kono
parents:
diff changeset
1438 create_input_operand (&ops[1], op0, mode);
kono
parents:
diff changeset
1439 create_input_operand (&ops[2], op1, mode);
kono
parents:
diff changeset
1440 create_fixed_operand (&ops[3], do_error);
kono
parents:
diff changeset
1441 if (maybe_expand_insn (icode, 4, ops))
kono
parents:
diff changeset
1442 {
kono
parents:
diff changeset
1443 last = get_last_insn ();
kono
parents:
diff changeset
1444 if (profile_status_for_fn (cfun) != PROFILE_ABSENT
kono
parents:
diff changeset
1445 && JUMP_P (last)
kono
parents:
diff changeset
1446 && any_condjump_p (last)
kono
parents:
diff changeset
1447 && !find_reg_note (last, REG_BR_PROB, 0))
kono
parents:
diff changeset
1448 add_reg_br_prob_note (last,
kono
parents:
diff changeset
1449 profile_probability::very_unlikely ());
kono
parents:
diff changeset
1450 emit_jump (done_label);
kono
parents:
diff changeset
1451 }
kono
parents:
diff changeset
1452 else
kono
parents:
diff changeset
1453 {
kono
parents:
diff changeset
1454 delete_insns_since (last);
kono
parents:
diff changeset
1455 icode = CODE_FOR_nothing;
kono
parents:
diff changeset
1456 }
kono
parents:
diff changeset
1457 }
kono
parents:
diff changeset
1458
kono
parents:
diff changeset
1459 if (icode == CODE_FOR_nothing)
kono
parents:
diff changeset
1460 {
kono
parents:
diff changeset
1461 struct separate_ops ops;
kono
parents:
diff changeset
1462 int prec = GET_MODE_PRECISION (mode);
kono
parents:
diff changeset
1463 scalar_int_mode hmode, wmode;
kono
parents:
diff changeset
1464 ops.op0 = make_tree (type, op0);
kono
parents:
diff changeset
1465 ops.op1 = make_tree (type, op1);
kono
parents:
diff changeset
1466 ops.op2 = NULL_TREE;
kono
parents:
diff changeset
1467 ops.location = loc;
kono
parents:
diff changeset
1468 if (GET_MODE_2XWIDER_MODE (mode).exists (&wmode)
kono
parents:
diff changeset
1469 && targetm.scalar_mode_supported_p (wmode))
kono
parents:
diff changeset
1470 {
kono
parents:
diff changeset
1471 ops.code = WIDEN_MULT_EXPR;
kono
parents:
diff changeset
1472 ops.type
kono
parents:
diff changeset
1473 = build_nonstandard_integer_type (GET_MODE_PRECISION (wmode), uns);
kono
parents:
diff changeset
1474
kono
parents:
diff changeset
1475 res = expand_expr_real_2 (&ops, NULL_RTX, wmode, EXPAND_NORMAL);
kono
parents:
diff changeset
1476 rtx hipart = expand_shift (RSHIFT_EXPR, wmode, res, prec,
kono
parents:
diff changeset
1477 NULL_RTX, uns);
kono
parents:
diff changeset
1478 hipart = convert_modes (mode, wmode, hipart, uns);
kono
parents:
diff changeset
1479 res = convert_modes (mode, wmode, res, uns);
kono
parents:
diff changeset
1480 if (uns)
kono
parents:
diff changeset
1481 /* For the unsigned multiplication, there was overflow if
kono
parents:
diff changeset
1482 HIPART is non-zero. */
kono
parents:
diff changeset
1483 do_compare_rtx_and_jump (hipart, const0_rtx, EQ, true, mode,
kono
parents:
diff changeset
1484 NULL_RTX, NULL, done_label,
kono
parents:
diff changeset
1485 profile_probability::very_likely ());
kono
parents:
diff changeset
1486 else
kono
parents:
diff changeset
1487 {
kono
parents:
diff changeset
1488 rtx signbit = expand_shift (RSHIFT_EXPR, mode, res, prec - 1,
kono
parents:
diff changeset
1489 NULL_RTX, 0);
kono
parents:
diff changeset
1490 /* RES is low half of the double width result, HIPART
kono
parents:
diff changeset
1491 the high half. There was overflow if
kono
parents:
diff changeset
1492 HIPART is different from RES < 0 ? -1 : 0. */
kono
parents:
diff changeset
1493 do_compare_rtx_and_jump (signbit, hipart, EQ, true, mode,
kono
parents:
diff changeset
1494 NULL_RTX, NULL, done_label,
kono
parents:
diff changeset
1495 profile_probability::very_likely ());
kono
parents:
diff changeset
1496 }
kono
parents:
diff changeset
1497 }
kono
parents:
diff changeset
1498 else if (int_mode_for_size (prec / 2, 1).exists (&hmode)
kono
parents:
diff changeset
1499 && 2 * GET_MODE_PRECISION (hmode) == prec)
kono
parents:
diff changeset
1500 {
kono
parents:
diff changeset
1501 rtx_code_label *large_op0 = gen_label_rtx ();
kono
parents:
diff changeset
1502 rtx_code_label *small_op0_large_op1 = gen_label_rtx ();
kono
parents:
diff changeset
1503 rtx_code_label *one_small_one_large = gen_label_rtx ();
kono
parents:
diff changeset
1504 rtx_code_label *both_ops_large = gen_label_rtx ();
kono
parents:
diff changeset
1505 rtx_code_label *after_hipart_neg = uns ? NULL : gen_label_rtx ();
kono
parents:
diff changeset
1506 rtx_code_label *after_lopart_neg = uns ? NULL : gen_label_rtx ();
kono
parents:
diff changeset
1507 rtx_code_label *do_overflow = gen_label_rtx ();
kono
parents:
diff changeset
1508 rtx_code_label *hipart_different = uns ? NULL : gen_label_rtx ();
kono
parents:
diff changeset
1509
kono
parents:
diff changeset
1510 unsigned int hprec = GET_MODE_PRECISION (hmode);
kono
parents:
diff changeset
1511 rtx hipart0 = expand_shift (RSHIFT_EXPR, mode, op0, hprec,
kono
parents:
diff changeset
1512 NULL_RTX, uns);
kono
parents:
diff changeset
1513 hipart0 = convert_modes (hmode, mode, hipart0, uns);
kono
parents:
diff changeset
1514 rtx lopart0 = convert_modes (hmode, mode, op0, uns);
kono
parents:
diff changeset
1515 rtx signbit0 = const0_rtx;
kono
parents:
diff changeset
1516 if (!uns)
kono
parents:
diff changeset
1517 signbit0 = expand_shift (RSHIFT_EXPR, hmode, lopart0, hprec - 1,
kono
parents:
diff changeset
1518 NULL_RTX, 0);
kono
parents:
diff changeset
1519 rtx hipart1 = expand_shift (RSHIFT_EXPR, mode, op1, hprec,
kono
parents:
diff changeset
1520 NULL_RTX, uns);
kono
parents:
diff changeset
1521 hipart1 = convert_modes (hmode, mode, hipart1, uns);
kono
parents:
diff changeset
1522 rtx lopart1 = convert_modes (hmode, mode, op1, uns);
kono
parents:
diff changeset
1523 rtx signbit1 = const0_rtx;
kono
parents:
diff changeset
1524 if (!uns)
kono
parents:
diff changeset
1525 signbit1 = expand_shift (RSHIFT_EXPR, hmode, lopart1, hprec - 1,
kono
parents:
diff changeset
1526 NULL_RTX, 0);
kono
parents:
diff changeset
1527
kono
parents:
diff changeset
1528 res = gen_reg_rtx (mode);
kono
parents:
diff changeset
1529
kono
parents:
diff changeset
1530 /* True if op0 resp. op1 are known to be in the range of
kono
parents:
diff changeset
1531 halfstype. */
kono
parents:
diff changeset
1532 bool op0_small_p = false;
kono
parents:
diff changeset
1533 bool op1_small_p = false;
kono
parents:
diff changeset
1534 /* True if op0 resp. op1 are known to have all zeros or all ones
kono
parents:
diff changeset
1535 in the upper half of bits, but are not known to be
kono
parents:
diff changeset
1536 op{0,1}_small_p. */
kono
parents:
diff changeset
1537 bool op0_medium_p = false;
kono
parents:
diff changeset
1538 bool op1_medium_p = false;
kono
parents:
diff changeset
1539 /* -1 if op{0,1} is known to be negative, 0 if it is known to be
kono
parents:
diff changeset
1540 nonnegative, 1 if unknown. */
kono
parents:
diff changeset
1541 int op0_sign = 1;
kono
parents:
diff changeset
1542 int op1_sign = 1;
kono
parents:
diff changeset
1543
kono
parents:
diff changeset
1544 if (pos_neg0 == 1)
kono
parents:
diff changeset
1545 op0_sign = 0;
kono
parents:
diff changeset
1546 else if (pos_neg0 == 2)
kono
parents:
diff changeset
1547 op0_sign = -1;
kono
parents:
diff changeset
1548 if (pos_neg1 == 1)
kono
parents:
diff changeset
1549 op1_sign = 0;
kono
parents:
diff changeset
1550 else if (pos_neg1 == 2)
kono
parents:
diff changeset
1551 op1_sign = -1;
kono
parents:
diff changeset
1552
kono
parents:
diff changeset
1553 unsigned int mprec0 = prec;
kono
parents:
diff changeset
1554 if (arg0 != error_mark_node)
kono
parents:
diff changeset
1555 mprec0 = get_min_precision (arg0, sign);
kono
parents:
diff changeset
1556 if (mprec0 <= hprec)
kono
parents:
diff changeset
1557 op0_small_p = true;
kono
parents:
diff changeset
1558 else if (!uns && mprec0 <= hprec + 1)
kono
parents:
diff changeset
1559 op0_medium_p = true;
kono
parents:
diff changeset
1560 unsigned int mprec1 = prec;
kono
parents:
diff changeset
1561 if (arg1 != error_mark_node)
kono
parents:
diff changeset
1562 mprec1 = get_min_precision (arg1, sign);
kono
parents:
diff changeset
1563 if (mprec1 <= hprec)
kono
parents:
diff changeset
1564 op1_small_p = true;
kono
parents:
diff changeset
1565 else if (!uns && mprec1 <= hprec + 1)
kono
parents:
diff changeset
1566 op1_medium_p = true;
kono
parents:
diff changeset
1567
kono
parents:
diff changeset
1568 int smaller_sign = 1;
kono
parents:
diff changeset
1569 int larger_sign = 1;
kono
parents:
diff changeset
1570 if (op0_small_p)
kono
parents:
diff changeset
1571 {
kono
parents:
diff changeset
1572 smaller_sign = op0_sign;
kono
parents:
diff changeset
1573 larger_sign = op1_sign;
kono
parents:
diff changeset
1574 }
kono
parents:
diff changeset
1575 else if (op1_small_p)
kono
parents:
diff changeset
1576 {
kono
parents:
diff changeset
1577 smaller_sign = op1_sign;
kono
parents:
diff changeset
1578 larger_sign = op0_sign;
kono
parents:
diff changeset
1579 }
kono
parents:
diff changeset
1580 else if (op0_sign == op1_sign)
kono
parents:
diff changeset
1581 {
kono
parents:
diff changeset
1582 smaller_sign = op0_sign;
kono
parents:
diff changeset
1583 larger_sign = op0_sign;
kono
parents:
diff changeset
1584 }
kono
parents:
diff changeset
1585
kono
parents:
diff changeset
1586 if (!op0_small_p)
kono
parents:
diff changeset
1587 do_compare_rtx_and_jump (signbit0, hipart0, NE, true, hmode,
kono
parents:
diff changeset
1588 NULL_RTX, NULL, large_op0,
kono
parents:
diff changeset
1589 profile_probability::unlikely ());
kono
parents:
diff changeset
1590
kono
parents:
diff changeset
1591 if (!op1_small_p)
kono
parents:
diff changeset
1592 do_compare_rtx_and_jump (signbit1, hipart1, NE, true, hmode,
kono
parents:
diff changeset
1593 NULL_RTX, NULL, small_op0_large_op1,
kono
parents:
diff changeset
1594 profile_probability::unlikely ());
kono
parents:
diff changeset
1595
kono
parents:
diff changeset
1596 /* If both op0 and op1 are sign (!uns) or zero (uns) extended from
kono
parents:
diff changeset
1597 hmode to mode, the multiplication will never overflow. We can
kono
parents:
diff changeset
1598 do just one hmode x hmode => mode widening multiplication. */
kono
parents:
diff changeset
1599 rtx lopart0s = lopart0, lopart1s = lopart1;
kono
parents:
diff changeset
1600 if (GET_CODE (lopart0) == SUBREG)
kono
parents:
diff changeset
1601 {
kono
parents:
diff changeset
1602 lopart0s = shallow_copy_rtx (lopart0);
kono
parents:
diff changeset
1603 SUBREG_PROMOTED_VAR_P (lopart0s) = 1;
kono
parents:
diff changeset
1604 SUBREG_PROMOTED_SET (lopart0s, uns ? SRP_UNSIGNED : SRP_SIGNED);
kono
parents:
diff changeset
1605 }
kono
parents:
diff changeset
1606 if (GET_CODE (lopart1) == SUBREG)
kono
parents:
diff changeset
1607 {
kono
parents:
diff changeset
1608 lopart1s = shallow_copy_rtx (lopart1);
kono
parents:
diff changeset
1609 SUBREG_PROMOTED_VAR_P (lopart1s) = 1;
kono
parents:
diff changeset
1610 SUBREG_PROMOTED_SET (lopart1s, uns ? SRP_UNSIGNED : SRP_SIGNED);
kono
parents:
diff changeset
1611 }
kono
parents:
diff changeset
1612 tree halfstype = build_nonstandard_integer_type (hprec, uns);
kono
parents:
diff changeset
1613 ops.op0 = make_tree (halfstype, lopart0s);
kono
parents:
diff changeset
1614 ops.op1 = make_tree (halfstype, lopart1s);
kono
parents:
diff changeset
1615 ops.code = WIDEN_MULT_EXPR;
kono
parents:
diff changeset
1616 ops.type = type;
kono
parents:
diff changeset
1617 rtx thisres
kono
parents:
diff changeset
1618 = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1619 emit_move_insn (res, thisres);
kono
parents:
diff changeset
1620 emit_jump (done_label);
kono
parents:
diff changeset
1621
kono
parents:
diff changeset
1622 emit_label (small_op0_large_op1);
kono
parents:
diff changeset
1623
kono
parents:
diff changeset
1624 /* If op0 is sign (!uns) or zero (uns) extended from hmode to mode,
kono
parents:
diff changeset
1625 but op1 is not, just swap the arguments and handle it as op1
kono
parents:
diff changeset
1626 sign/zero extended, op0 not. */
kono
parents:
diff changeset
1627 rtx larger = gen_reg_rtx (mode);
kono
parents:
diff changeset
1628 rtx hipart = gen_reg_rtx (hmode);
kono
parents:
diff changeset
1629 rtx lopart = gen_reg_rtx (hmode);
kono
parents:
diff changeset
1630 emit_move_insn (larger, op1);
kono
parents:
diff changeset
1631 emit_move_insn (hipart, hipart1);
kono
parents:
diff changeset
1632 emit_move_insn (lopart, lopart0);
kono
parents:
diff changeset
1633 emit_jump (one_small_one_large);
kono
parents:
diff changeset
1634
kono
parents:
diff changeset
1635 emit_label (large_op0);
kono
parents:
diff changeset
1636
kono
parents:
diff changeset
1637 if (!op1_small_p)
kono
parents:
diff changeset
1638 do_compare_rtx_and_jump (signbit1, hipart1, NE, true, hmode,
kono
parents:
diff changeset
1639 NULL_RTX, NULL, both_ops_large,
kono
parents:
diff changeset
1640 profile_probability::unlikely ());
kono
parents:
diff changeset
1641
kono
parents:
diff changeset
1642 /* If op1 is sign (!uns) or zero (uns) extended from hmode to mode,
kono
parents:
diff changeset
1643 but op0 is not, prepare larger, hipart and lopart pseudos and
kono
parents:
diff changeset
1644 handle it together with small_op0_large_op1. */
kono
parents:
diff changeset
1645 emit_move_insn (larger, op0);
kono
parents:
diff changeset
1646 emit_move_insn (hipart, hipart0);
kono
parents:
diff changeset
1647 emit_move_insn (lopart, lopart1);
kono
parents:
diff changeset
1648
kono
parents:
diff changeset
1649 emit_label (one_small_one_large);
kono
parents:
diff changeset
1650
kono
parents:
diff changeset
1651 /* lopart is the low part of the operand that is sign extended
kono
parents:
diff changeset
1652 to mode, larger is the other operand, hipart is the
kono
parents:
diff changeset
1653 high part of larger and lopart0 and lopart1 are the low parts
kono
parents:
diff changeset
1654 of both operands.
kono
parents:
diff changeset
1655 We perform lopart0 * lopart1 and lopart * hipart widening
kono
parents:
diff changeset
1656 multiplications. */
kono
parents:
diff changeset
1657 tree halfutype = build_nonstandard_integer_type (hprec, 1);
kono
parents:
diff changeset
1658 ops.op0 = make_tree (halfutype, lopart0);
kono
parents:
diff changeset
1659 ops.op1 = make_tree (halfutype, lopart1);
kono
parents:
diff changeset
1660 rtx lo0xlo1
kono
parents:
diff changeset
1661 = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1662
kono
parents:
diff changeset
1663 ops.op0 = make_tree (halfutype, lopart);
kono
parents:
diff changeset
1664 ops.op1 = make_tree (halfutype, hipart);
kono
parents:
diff changeset
1665 rtx loxhi = gen_reg_rtx (mode);
kono
parents:
diff changeset
1666 rtx tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1667 emit_move_insn (loxhi, tem);
kono
parents:
diff changeset
1668
kono
parents:
diff changeset
1669 if (!uns)
kono
parents:
diff changeset
1670 {
kono
parents:
diff changeset
1671 /* if (hipart < 0) loxhi -= lopart << (bitsize / 2); */
kono
parents:
diff changeset
1672 if (larger_sign == 0)
kono
parents:
diff changeset
1673 emit_jump (after_hipart_neg);
kono
parents:
diff changeset
1674 else if (larger_sign != -1)
kono
parents:
diff changeset
1675 do_compare_rtx_and_jump (hipart, const0_rtx, GE, false, hmode,
kono
parents:
diff changeset
1676 NULL_RTX, NULL, after_hipart_neg,
kono
parents:
diff changeset
1677 profile_probability::even ());
kono
parents:
diff changeset
1678
kono
parents:
diff changeset
1679 tem = convert_modes (mode, hmode, lopart, 1);
kono
parents:
diff changeset
1680 tem = expand_shift (LSHIFT_EXPR, mode, tem, hprec, NULL_RTX, 1);
kono
parents:
diff changeset
1681 tem = expand_simple_binop (mode, MINUS, loxhi, tem, NULL_RTX,
kono
parents:
diff changeset
1682 1, OPTAB_DIRECT);
kono
parents:
diff changeset
1683 emit_move_insn (loxhi, tem);
kono
parents:
diff changeset
1684
kono
parents:
diff changeset
1685 emit_label (after_hipart_neg);
kono
parents:
diff changeset
1686
kono
parents:
diff changeset
1687 /* if (lopart < 0) loxhi -= larger; */
kono
parents:
diff changeset
1688 if (smaller_sign == 0)
kono
parents:
diff changeset
1689 emit_jump (after_lopart_neg);
kono
parents:
diff changeset
1690 else if (smaller_sign != -1)
kono
parents:
diff changeset
1691 do_compare_rtx_and_jump (lopart, const0_rtx, GE, false, hmode,
kono
parents:
diff changeset
1692 NULL_RTX, NULL, after_lopart_neg,
kono
parents:
diff changeset
1693 profile_probability::even ());
kono
parents:
diff changeset
1694
kono
parents:
diff changeset
1695 tem = expand_simple_binop (mode, MINUS, loxhi, larger, NULL_RTX,
kono
parents:
diff changeset
1696 1, OPTAB_DIRECT);
kono
parents:
diff changeset
1697 emit_move_insn (loxhi, tem);
kono
parents:
diff changeset
1698
kono
parents:
diff changeset
1699 emit_label (after_lopart_neg);
kono
parents:
diff changeset
1700 }
kono
parents:
diff changeset
1701
kono
parents:
diff changeset
1702 /* loxhi += (uns) lo0xlo1 >> (bitsize / 2); */
kono
parents:
diff changeset
1703 tem = expand_shift (RSHIFT_EXPR, mode, lo0xlo1, hprec, NULL_RTX, 1);
kono
parents:
diff changeset
1704 tem = expand_simple_binop (mode, PLUS, loxhi, tem, NULL_RTX,
kono
parents:
diff changeset
1705 1, OPTAB_DIRECT);
kono
parents:
diff changeset
1706 emit_move_insn (loxhi, tem);
kono
parents:
diff changeset
1707
kono
parents:
diff changeset
1708 /* if (loxhi >> (bitsize / 2)
kono
parents:
diff changeset
1709 == (hmode) loxhi >> (bitsize / 2 - 1)) (if !uns)
kono
parents:
diff changeset
1710 if (loxhi >> (bitsize / 2) == 0 (if uns). */
kono
parents:
diff changeset
1711 rtx hipartloxhi = expand_shift (RSHIFT_EXPR, mode, loxhi, hprec,
kono
parents:
diff changeset
1712 NULL_RTX, 0);
kono
parents:
diff changeset
1713 hipartloxhi = convert_modes (hmode, mode, hipartloxhi, 0);
kono
parents:
diff changeset
1714 rtx signbitloxhi = const0_rtx;
kono
parents:
diff changeset
1715 if (!uns)
kono
parents:
diff changeset
1716 signbitloxhi = expand_shift (RSHIFT_EXPR, hmode,
kono
parents:
diff changeset
1717 convert_modes (hmode, mode,
kono
parents:
diff changeset
1718 loxhi, 0),
kono
parents:
diff changeset
1719 hprec - 1, NULL_RTX, 0);
kono
parents:
diff changeset
1720
kono
parents:
diff changeset
1721 do_compare_rtx_and_jump (signbitloxhi, hipartloxhi, NE, true, hmode,
kono
parents:
diff changeset
1722 NULL_RTX, NULL, do_overflow,
kono
parents:
diff changeset
1723 profile_probability::very_unlikely ());
kono
parents:
diff changeset
1724
kono
parents:
diff changeset
1725 /* res = (loxhi << (bitsize / 2)) | (hmode) lo0xlo1; */
kono
parents:
diff changeset
1726 rtx loxhishifted = expand_shift (LSHIFT_EXPR, mode, loxhi, hprec,
kono
parents:
diff changeset
1727 NULL_RTX, 1);
kono
parents:
diff changeset
1728 tem = convert_modes (mode, hmode,
kono
parents:
diff changeset
1729 convert_modes (hmode, mode, lo0xlo1, 1), 1);
kono
parents:
diff changeset
1730
kono
parents:
diff changeset
1731 tem = expand_simple_binop (mode, IOR, loxhishifted, tem, res,
kono
parents:
diff changeset
1732 1, OPTAB_DIRECT);
kono
parents:
diff changeset
1733 if (tem != res)
kono
parents:
diff changeset
1734 emit_move_insn (res, tem);
kono
parents:
diff changeset
1735 emit_jump (done_label);
kono
parents:
diff changeset
1736
kono
parents:
diff changeset
1737 emit_label (both_ops_large);
kono
parents:
diff changeset
1738
kono
parents:
diff changeset
1739 /* If both operands are large (not sign (!uns) or zero (uns)
kono
parents:
diff changeset
1740 extended from hmode), then perform the full multiplication
kono
parents:
diff changeset
1741 which will be the result of the operation.
kono
parents:
diff changeset
1742 The only cases which don't overflow are for signed multiplication
kono
parents:
diff changeset
1743 some cases where both hipart0 and highpart1 are 0 or -1.
kono
parents:
diff changeset
1744 For unsigned multiplication when high parts are both non-zero
kono
parents:
diff changeset
1745 this overflows always. */
kono
parents:
diff changeset
1746 ops.code = MULT_EXPR;
kono
parents:
diff changeset
1747 ops.op0 = make_tree (type, op0);
kono
parents:
diff changeset
1748 ops.op1 = make_tree (type, op1);
kono
parents:
diff changeset
1749 tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1750 emit_move_insn (res, tem);
kono
parents:
diff changeset
1751
kono
parents:
diff changeset
1752 if (!uns)
kono
parents:
diff changeset
1753 {
kono
parents:
diff changeset
1754 if (!op0_medium_p)
kono
parents:
diff changeset
1755 {
kono
parents:
diff changeset
1756 tem = expand_simple_binop (hmode, PLUS, hipart0, const1_rtx,
kono
parents:
diff changeset
1757 NULL_RTX, 1, OPTAB_DIRECT);
kono
parents:
diff changeset
1758 do_compare_rtx_and_jump (tem, const1_rtx, GTU, true, hmode,
kono
parents:
diff changeset
1759 NULL_RTX, NULL, do_error,
kono
parents:
diff changeset
1760 profile_probability::very_unlikely ());
kono
parents:
diff changeset
1761 }
kono
parents:
diff changeset
1762
kono
parents:
diff changeset
1763 if (!op1_medium_p)
kono
parents:
diff changeset
1764 {
kono
parents:
diff changeset
1765 tem = expand_simple_binop (hmode, PLUS, hipart1, const1_rtx,
kono
parents:
diff changeset
1766 NULL_RTX, 1, OPTAB_DIRECT);
kono
parents:
diff changeset
1767 do_compare_rtx_and_jump (tem, const1_rtx, GTU, true, hmode,
kono
parents:
diff changeset
1768 NULL_RTX, NULL, do_error,
kono
parents:
diff changeset
1769 profile_probability::very_unlikely ());
kono
parents:
diff changeset
1770 }
kono
parents:
diff changeset
1771
kono
parents:
diff changeset
1772 /* At this point hipart{0,1} are both in [-1, 0]. If they are
kono
parents:
diff changeset
1773 the same, overflow happened if res is non-positive, if they
kono
parents:
diff changeset
1774 are different, overflow happened if res is positive. */
kono
parents:
diff changeset
1775 if (op0_sign != 1 && op1_sign != 1 && op0_sign != op1_sign)
kono
parents:
diff changeset
1776 emit_jump (hipart_different);
kono
parents:
diff changeset
1777 else if (op0_sign == 1 || op1_sign == 1)
kono
parents:
diff changeset
1778 do_compare_rtx_and_jump (hipart0, hipart1, NE, true, hmode,
kono
parents:
diff changeset
1779 NULL_RTX, NULL, hipart_different,
kono
parents:
diff changeset
1780 profile_probability::even ());
kono
parents:
diff changeset
1781
kono
parents:
diff changeset
1782 do_compare_rtx_and_jump (res, const0_rtx, LE, false, mode,
kono
parents:
diff changeset
1783 NULL_RTX, NULL, do_error,
kono
parents:
diff changeset
1784 profile_probability::very_unlikely ());
kono
parents:
diff changeset
1785 emit_jump (done_label);
kono
parents:
diff changeset
1786
kono
parents:
diff changeset
1787 emit_label (hipart_different);
kono
parents:
diff changeset
1788
kono
parents:
diff changeset
1789 do_compare_rtx_and_jump (res, const0_rtx, GE, false, mode,
kono
parents:
diff changeset
1790 NULL_RTX, NULL, do_error,
kono
parents:
diff changeset
1791 profile_probability::very_unlikely ());
kono
parents:
diff changeset
1792 emit_jump (done_label);
kono
parents:
diff changeset
1793 }
kono
parents:
diff changeset
1794
kono
parents:
diff changeset
1795 emit_label (do_overflow);
kono
parents:
diff changeset
1796
kono
parents:
diff changeset
1797 /* Overflow, do full multiplication and fallthru into do_error. */
kono
parents:
diff changeset
1798 ops.op0 = make_tree (type, op0);
kono
parents:
diff changeset
1799 ops.op1 = make_tree (type, op1);
kono
parents:
diff changeset
1800 tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1801 emit_move_insn (res, tem);
kono
parents:
diff changeset
1802 }
kono
parents:
diff changeset
1803 else
kono
parents:
diff changeset
1804 {
kono
parents:
diff changeset
1805 gcc_assert (!is_ubsan);
kono
parents:
diff changeset
1806 ops.code = MULT_EXPR;
kono
parents:
diff changeset
1807 ops.type = type;
kono
parents:
diff changeset
1808 res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
1809 emit_jump (done_label);
kono
parents:
diff changeset
1810 }
kono
parents:
diff changeset
1811 }
kono
parents:
diff changeset
1812
kono
parents:
diff changeset
1813 do_error_label:
kono
parents:
diff changeset
1814 emit_label (do_error);
kono
parents:
diff changeset
1815 if (is_ubsan)
kono
parents:
diff changeset
1816 {
kono
parents:
diff changeset
1817 /* Expand the ubsan builtin call. */
kono
parents:
diff changeset
1818 push_temp_slots ();
kono
parents:
diff changeset
1819 fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE (arg0),
kono
parents:
diff changeset
1820 arg0, arg1, datap);
kono
parents:
diff changeset
1821 expand_normal (fn);
kono
parents:
diff changeset
1822 pop_temp_slots ();
kono
parents:
diff changeset
1823 do_pending_stack_adjust ();
kono
parents:
diff changeset
1824 }
kono
parents:
diff changeset
1825 else if (lhs)
kono
parents:
diff changeset
1826 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
1827
kono
parents:
diff changeset
1828 /* We're done. */
kono
parents:
diff changeset
1829 emit_label (done_label);
kono
parents:
diff changeset
1830
kono
parents:
diff changeset
1831 /* u1 * u2 -> sr */
kono
parents:
diff changeset
1832 if (uns0_p && uns1_p && !unsr_p)
kono
parents:
diff changeset
1833 {
kono
parents:
diff changeset
1834 rtx_code_label *all_done_label = gen_label_rtx ();
kono
parents:
diff changeset
1835 do_compare_rtx_and_jump (res, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
1836 NULL, all_done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1837 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
1838 emit_label (all_done_label);
kono
parents:
diff changeset
1839 }
kono
parents:
diff changeset
1840
kono
parents:
diff changeset
1841 /* s1 * u2 -> sr */
kono
parents:
diff changeset
1842 if (!uns0_p && uns1_p && !unsr_p && pos_neg1 == 3)
kono
parents:
diff changeset
1843 {
kono
parents:
diff changeset
1844 rtx_code_label *all_done_label = gen_label_rtx ();
kono
parents:
diff changeset
1845 rtx_code_label *set_noovf = gen_label_rtx ();
kono
parents:
diff changeset
1846 do_compare_rtx_and_jump (op1, const0_rtx, GE, false, mode, NULL_RTX,
kono
parents:
diff changeset
1847 NULL, all_done_label, profile_probability::very_likely ());
kono
parents:
diff changeset
1848 expand_arith_set_overflow (lhs, target);
kono
parents:
diff changeset
1849 do_compare_rtx_and_jump (op0, const0_rtx, EQ, true, mode, NULL_RTX,
kono
parents:
diff changeset
1850 NULL, set_noovf, profile_probability::very_likely ());
kono
parents:
diff changeset
1851 do_compare_rtx_and_jump (op0, constm1_rtx, NE, true, mode, NULL_RTX,
kono
parents:
diff changeset
1852 NULL, all_done_label, profile_probability::very_unlikely ());
kono
parents:
diff changeset
1853 do_compare_rtx_and_jump (op1, res, NE, true, mode, NULL_RTX, NULL,
kono
parents:
diff changeset
1854 all_done_label, profile_probability::very_unlikely ());
kono
parents:
diff changeset
1855 emit_label (set_noovf);
kono
parents:
diff changeset
1856 write_complex_part (target, const0_rtx, true);
kono
parents:
diff changeset
1857 emit_label (all_done_label);
kono
parents:
diff changeset
1858 }
kono
parents:
diff changeset
1859
kono
parents:
diff changeset
1860 if (lhs)
kono
parents:
diff changeset
1861 {
kono
parents:
diff changeset
1862 if (is_ubsan)
kono
parents:
diff changeset
1863 expand_ubsan_result_store (target, res);
kono
parents:
diff changeset
1864 else
kono
parents:
diff changeset
1865 expand_arith_overflow_result_store (lhs, target, mode, res);
kono
parents:
diff changeset
1866 }
kono
parents:
diff changeset
1867 }
kono
parents:
diff changeset
1868
kono
parents:
diff changeset
1869 /* Expand UBSAN_CHECK_* internal function if it has vector operands. */
kono
parents:
diff changeset
1870
kono
parents:
diff changeset
1871 static void
kono
parents:
diff changeset
1872 expand_vector_ubsan_overflow (location_t loc, enum tree_code code, tree lhs,
kono
parents:
diff changeset
1873 tree arg0, tree arg1)
kono
parents:
diff changeset
1874 {
kono
parents:
diff changeset
1875 int cnt = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
kono
parents:
diff changeset
1876 rtx_code_label *loop_lab = NULL;
kono
parents:
diff changeset
1877 rtx cntvar = NULL_RTX;
kono
parents:
diff changeset
1878 tree cntv = NULL_TREE;
kono
parents:
diff changeset
1879 tree eltype = TREE_TYPE (TREE_TYPE (arg0));
kono
parents:
diff changeset
1880 tree sz = TYPE_SIZE (eltype);
kono
parents:
diff changeset
1881 tree data = NULL_TREE;
kono
parents:
diff changeset
1882 tree resv = NULL_TREE;
kono
parents:
diff changeset
1883 rtx lhsr = NULL_RTX;
kono
parents:
diff changeset
1884 rtx resvr = NULL_RTX;
kono
parents:
diff changeset
1885
kono
parents:
diff changeset
1886 if (lhs)
kono
parents:
diff changeset
1887 {
kono
parents:
diff changeset
1888 optab op;
kono
parents:
diff changeset
1889 lhsr = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
1890 if (!VECTOR_MODE_P (GET_MODE (lhsr))
kono
parents:
diff changeset
1891 || (op = optab_for_tree_code (code, TREE_TYPE (arg0),
kono
parents:
diff changeset
1892 optab_default)) == unknown_optab
kono
parents:
diff changeset
1893 || (optab_handler (op, TYPE_MODE (TREE_TYPE (arg0)))
kono
parents:
diff changeset
1894 == CODE_FOR_nothing))
kono
parents:
diff changeset
1895 {
kono
parents:
diff changeset
1896 if (MEM_P (lhsr))
kono
parents:
diff changeset
1897 resv = make_tree (TREE_TYPE (lhs), lhsr);
kono
parents:
diff changeset
1898 else
kono
parents:
diff changeset
1899 {
kono
parents:
diff changeset
1900 resvr = assign_temp (TREE_TYPE (lhs), 1, 1);
kono
parents:
diff changeset
1901 resv = make_tree (TREE_TYPE (lhs), resvr);
kono
parents:
diff changeset
1902 }
kono
parents:
diff changeset
1903 }
kono
parents:
diff changeset
1904 }
kono
parents:
diff changeset
1905 if (cnt > 4)
kono
parents:
diff changeset
1906 {
kono
parents:
diff changeset
1907 do_pending_stack_adjust ();
kono
parents:
diff changeset
1908 loop_lab = gen_label_rtx ();
kono
parents:
diff changeset
1909 cntvar = gen_reg_rtx (TYPE_MODE (sizetype));
kono
parents:
diff changeset
1910 cntv = make_tree (sizetype, cntvar);
kono
parents:
diff changeset
1911 emit_move_insn (cntvar, const0_rtx);
kono
parents:
diff changeset
1912 emit_label (loop_lab);
kono
parents:
diff changeset
1913 }
kono
parents:
diff changeset
1914 if (TREE_CODE (arg0) != VECTOR_CST)
kono
parents:
diff changeset
1915 {
kono
parents:
diff changeset
1916 rtx arg0r = expand_normal (arg0);
kono
parents:
diff changeset
1917 arg0 = make_tree (TREE_TYPE (arg0), arg0r);
kono
parents:
diff changeset
1918 }
kono
parents:
diff changeset
1919 if (TREE_CODE (arg1) != VECTOR_CST)
kono
parents:
diff changeset
1920 {
kono
parents:
diff changeset
1921 rtx arg1r = expand_normal (arg1);
kono
parents:
diff changeset
1922 arg1 = make_tree (TREE_TYPE (arg1), arg1r);
kono
parents:
diff changeset
1923 }
kono
parents:
diff changeset
1924 for (int i = 0; i < (cnt > 4 ? 1 : cnt); i++)
kono
parents:
diff changeset
1925 {
kono
parents:
diff changeset
1926 tree op0, op1, res = NULL_TREE;
kono
parents:
diff changeset
1927 if (cnt > 4)
kono
parents:
diff changeset
1928 {
kono
parents:
diff changeset
1929 tree atype = build_array_type_nelts (eltype, cnt);
kono
parents:
diff changeset
1930 op0 = uniform_vector_p (arg0);
kono
parents:
diff changeset
1931 if (op0 == NULL_TREE)
kono
parents:
diff changeset
1932 {
kono
parents:
diff changeset
1933 op0 = fold_build1_loc (loc, VIEW_CONVERT_EXPR, atype, arg0);
kono
parents:
diff changeset
1934 op0 = build4_loc (loc, ARRAY_REF, eltype, op0, cntv,
kono
parents:
diff changeset
1935 NULL_TREE, NULL_TREE);
kono
parents:
diff changeset
1936 }
kono
parents:
diff changeset
1937 op1 = uniform_vector_p (arg1);
kono
parents:
diff changeset
1938 if (op1 == NULL_TREE)
kono
parents:
diff changeset
1939 {
kono
parents:
diff changeset
1940 op1 = fold_build1_loc (loc, VIEW_CONVERT_EXPR, atype, arg1);
kono
parents:
diff changeset
1941 op1 = build4_loc (loc, ARRAY_REF, eltype, op1, cntv,
kono
parents:
diff changeset
1942 NULL_TREE, NULL_TREE);
kono
parents:
diff changeset
1943 }
kono
parents:
diff changeset
1944 if (resv)
kono
parents:
diff changeset
1945 {
kono
parents:
diff changeset
1946 res = fold_build1_loc (loc, VIEW_CONVERT_EXPR, atype, resv);
kono
parents:
diff changeset
1947 res = build4_loc (loc, ARRAY_REF, eltype, res, cntv,
kono
parents:
diff changeset
1948 NULL_TREE, NULL_TREE);
kono
parents:
diff changeset
1949 }
kono
parents:
diff changeset
1950 }
kono
parents:
diff changeset
1951 else
kono
parents:
diff changeset
1952 {
kono
parents:
diff changeset
1953 tree bitpos = bitsize_int (tree_to_uhwi (sz) * i);
kono
parents:
diff changeset
1954 op0 = fold_build3_loc (loc, BIT_FIELD_REF, eltype, arg0, sz, bitpos);
kono
parents:
diff changeset
1955 op1 = fold_build3_loc (loc, BIT_FIELD_REF, eltype, arg1, sz, bitpos);
kono
parents:
diff changeset
1956 if (resv)
kono
parents:
diff changeset
1957 res = fold_build3_loc (loc, BIT_FIELD_REF, eltype, resv, sz,
kono
parents:
diff changeset
1958 bitpos);
kono
parents:
diff changeset
1959 }
kono
parents:
diff changeset
1960 switch (code)
kono
parents:
diff changeset
1961 {
kono
parents:
diff changeset
1962 case PLUS_EXPR:
kono
parents:
diff changeset
1963 expand_addsub_overflow (loc, PLUS_EXPR, res, op0, op1,
kono
parents:
diff changeset
1964 false, false, false, true, &data);
kono
parents:
diff changeset
1965 break;
kono
parents:
diff changeset
1966 case MINUS_EXPR:
kono
parents:
diff changeset
1967 if (cnt > 4 ? integer_zerop (arg0) : integer_zerop (op0))
kono
parents:
diff changeset
1968 expand_neg_overflow (loc, res, op1, true, &data);
kono
parents:
diff changeset
1969 else
kono
parents:
diff changeset
1970 expand_addsub_overflow (loc, MINUS_EXPR, res, op0, op1,
kono
parents:
diff changeset
1971 false, false, false, true, &data);
kono
parents:
diff changeset
1972 break;
kono
parents:
diff changeset
1973 case MULT_EXPR:
kono
parents:
diff changeset
1974 expand_mul_overflow (loc, res, op0, op1, false, false, false,
kono
parents:
diff changeset
1975 true, &data);
kono
parents:
diff changeset
1976 break;
kono
parents:
diff changeset
1977 default:
kono
parents:
diff changeset
1978 gcc_unreachable ();
kono
parents:
diff changeset
1979 }
kono
parents:
diff changeset
1980 }
kono
parents:
diff changeset
1981 if (cnt > 4)
kono
parents:
diff changeset
1982 {
kono
parents:
diff changeset
1983 struct separate_ops ops;
kono
parents:
diff changeset
1984 ops.code = PLUS_EXPR;
kono
parents:
diff changeset
1985 ops.type = TREE_TYPE (cntv);
kono
parents:
diff changeset
1986 ops.op0 = cntv;
kono
parents:
diff changeset
1987 ops.op1 = build_int_cst (TREE_TYPE (cntv), 1);
kono
parents:
diff changeset
1988 ops.op2 = NULL_TREE;
kono
parents:
diff changeset
1989 ops.location = loc;
kono
parents:
diff changeset
1990 rtx ret = expand_expr_real_2 (&ops, cntvar, TYPE_MODE (sizetype),
kono
parents:
diff changeset
1991 EXPAND_NORMAL);
kono
parents:
diff changeset
1992 if (ret != cntvar)
kono
parents:
diff changeset
1993 emit_move_insn (cntvar, ret);
kono
parents:
diff changeset
1994 do_compare_rtx_and_jump (cntvar, GEN_INT (cnt), NE, false,
kono
parents:
diff changeset
1995 TYPE_MODE (sizetype), NULL_RTX, NULL, loop_lab,
kono
parents:
diff changeset
1996 profile_probability::very_likely ());
kono
parents:
diff changeset
1997 }
kono
parents:
diff changeset
1998 if (lhs && resv == NULL_TREE)
kono
parents:
diff changeset
1999 {
kono
parents:
diff changeset
2000 struct separate_ops ops;
kono
parents:
diff changeset
2001 ops.code = code;
kono
parents:
diff changeset
2002 ops.type = TREE_TYPE (arg0);
kono
parents:
diff changeset
2003 ops.op0 = arg0;
kono
parents:
diff changeset
2004 ops.op1 = arg1;
kono
parents:
diff changeset
2005 ops.op2 = NULL_TREE;
kono
parents:
diff changeset
2006 ops.location = loc;
kono
parents:
diff changeset
2007 rtx ret = expand_expr_real_2 (&ops, lhsr, TYPE_MODE (TREE_TYPE (arg0)),
kono
parents:
diff changeset
2008 EXPAND_NORMAL);
kono
parents:
diff changeset
2009 if (ret != lhsr)
kono
parents:
diff changeset
2010 emit_move_insn (lhsr, ret);
kono
parents:
diff changeset
2011 }
kono
parents:
diff changeset
2012 else if (resvr)
kono
parents:
diff changeset
2013 emit_move_insn (lhsr, resvr);
kono
parents:
diff changeset
2014 }
kono
parents:
diff changeset
2015
kono
parents:
diff changeset
2016 /* Expand UBSAN_CHECK_ADD call STMT. */
kono
parents:
diff changeset
2017
kono
parents:
diff changeset
2018 static void
kono
parents:
diff changeset
2019 expand_UBSAN_CHECK_ADD (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2020 {
kono
parents:
diff changeset
2021 location_t loc = gimple_location (stmt);
kono
parents:
diff changeset
2022 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2023 tree arg0 = gimple_call_arg (stmt, 0);
kono
parents:
diff changeset
2024 tree arg1 = gimple_call_arg (stmt, 1);
kono
parents:
diff changeset
2025 if (VECTOR_TYPE_P (TREE_TYPE (arg0)))
kono
parents:
diff changeset
2026 expand_vector_ubsan_overflow (loc, PLUS_EXPR, lhs, arg0, arg1);
kono
parents:
diff changeset
2027 else
kono
parents:
diff changeset
2028 expand_addsub_overflow (loc, PLUS_EXPR, lhs, arg0, arg1,
kono
parents:
diff changeset
2029 false, false, false, true, NULL);
kono
parents:
diff changeset
2030 }
kono
parents:
diff changeset
2031
kono
parents:
diff changeset
2032 /* Expand UBSAN_CHECK_SUB call STMT. */
kono
parents:
diff changeset
2033
kono
parents:
diff changeset
2034 static void
kono
parents:
diff changeset
2035 expand_UBSAN_CHECK_SUB (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2036 {
kono
parents:
diff changeset
2037 location_t loc = gimple_location (stmt);
kono
parents:
diff changeset
2038 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2039 tree arg0 = gimple_call_arg (stmt, 0);
kono
parents:
diff changeset
2040 tree arg1 = gimple_call_arg (stmt, 1);
kono
parents:
diff changeset
2041 if (VECTOR_TYPE_P (TREE_TYPE (arg0)))
kono
parents:
diff changeset
2042 expand_vector_ubsan_overflow (loc, MINUS_EXPR, lhs, arg0, arg1);
kono
parents:
diff changeset
2043 else if (integer_zerop (arg0))
kono
parents:
diff changeset
2044 expand_neg_overflow (loc, lhs, arg1, true, NULL);
kono
parents:
diff changeset
2045 else
kono
parents:
diff changeset
2046 expand_addsub_overflow (loc, MINUS_EXPR, lhs, arg0, arg1,
kono
parents:
diff changeset
2047 false, false, false, true, NULL);
kono
parents:
diff changeset
2048 }
kono
parents:
diff changeset
2049
kono
parents:
diff changeset
2050 /* Expand UBSAN_CHECK_MUL call STMT. */
kono
parents:
diff changeset
2051
kono
parents:
diff changeset
2052 static void
kono
parents:
diff changeset
2053 expand_UBSAN_CHECK_MUL (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2054 {
kono
parents:
diff changeset
2055 location_t loc = gimple_location (stmt);
kono
parents:
diff changeset
2056 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2057 tree arg0 = gimple_call_arg (stmt, 0);
kono
parents:
diff changeset
2058 tree arg1 = gimple_call_arg (stmt, 1);
kono
parents:
diff changeset
2059 if (VECTOR_TYPE_P (TREE_TYPE (arg0)))
kono
parents:
diff changeset
2060 expand_vector_ubsan_overflow (loc, MULT_EXPR, lhs, arg0, arg1);
kono
parents:
diff changeset
2061 else
kono
parents:
diff changeset
2062 expand_mul_overflow (loc, lhs, arg0, arg1, false, false, false, true,
kono
parents:
diff changeset
2063 NULL);
kono
parents:
diff changeset
2064 }
kono
parents:
diff changeset
2065
kono
parents:
diff changeset
2066 /* Helper function for {ADD,SUB,MUL}_OVERFLOW call stmt expansion. */
kono
parents:
diff changeset
2067
kono
parents:
diff changeset
2068 static void
kono
parents:
diff changeset
2069 expand_arith_overflow (enum tree_code code, gimple *stmt)
kono
parents:
diff changeset
2070 {
kono
parents:
diff changeset
2071 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2072 if (lhs == NULL_TREE)
kono
parents:
diff changeset
2073 return;
kono
parents:
diff changeset
2074 tree arg0 = gimple_call_arg (stmt, 0);
kono
parents:
diff changeset
2075 tree arg1 = gimple_call_arg (stmt, 1);
kono
parents:
diff changeset
2076 tree type = TREE_TYPE (TREE_TYPE (lhs));
kono
parents:
diff changeset
2077 int uns0_p = TYPE_UNSIGNED (TREE_TYPE (arg0));
kono
parents:
diff changeset
2078 int uns1_p = TYPE_UNSIGNED (TREE_TYPE (arg1));
kono
parents:
diff changeset
2079 int unsr_p = TYPE_UNSIGNED (type);
kono
parents:
diff changeset
2080 int prec0 = TYPE_PRECISION (TREE_TYPE (arg0));
kono
parents:
diff changeset
2081 int prec1 = TYPE_PRECISION (TREE_TYPE (arg1));
kono
parents:
diff changeset
2082 int precres = TYPE_PRECISION (type);
kono
parents:
diff changeset
2083 location_t loc = gimple_location (stmt);
kono
parents:
diff changeset
2084 if (!uns0_p && get_range_pos_neg (arg0) == 1)
kono
parents:
diff changeset
2085 uns0_p = true;
kono
parents:
diff changeset
2086 if (!uns1_p && get_range_pos_neg (arg1) == 1)
kono
parents:
diff changeset
2087 uns1_p = true;
kono
parents:
diff changeset
2088 int pr = get_min_precision (arg0, uns0_p ? UNSIGNED : SIGNED);
kono
parents:
diff changeset
2089 prec0 = MIN (prec0, pr);
kono
parents:
diff changeset
2090 pr = get_min_precision (arg1, uns1_p ? UNSIGNED : SIGNED);
kono
parents:
diff changeset
2091 prec1 = MIN (prec1, pr);
kono
parents:
diff changeset
2092
kono
parents:
diff changeset
2093 /* If uns0_p && uns1_p, precop is minimum needed precision
kono
parents:
diff changeset
2094 of unsigned type to hold the exact result, otherwise
kono
parents:
diff changeset
2095 precop is minimum needed precision of signed type to
kono
parents:
diff changeset
2096 hold the exact result. */
kono
parents:
diff changeset
2097 int precop;
kono
parents:
diff changeset
2098 if (code == MULT_EXPR)
kono
parents:
diff changeset
2099 precop = prec0 + prec1 + (uns0_p != uns1_p);
kono
parents:
diff changeset
2100 else
kono
parents:
diff changeset
2101 {
kono
parents:
diff changeset
2102 if (uns0_p == uns1_p)
kono
parents:
diff changeset
2103 precop = MAX (prec0, prec1) + 1;
kono
parents:
diff changeset
2104 else if (uns0_p)
kono
parents:
diff changeset
2105 precop = MAX (prec0 + 1, prec1) + 1;
kono
parents:
diff changeset
2106 else
kono
parents:
diff changeset
2107 precop = MAX (prec0, prec1 + 1) + 1;
kono
parents:
diff changeset
2108 }
kono
parents:
diff changeset
2109 int orig_precres = precres;
kono
parents:
diff changeset
2110
kono
parents:
diff changeset
2111 do
kono
parents:
diff changeset
2112 {
kono
parents:
diff changeset
2113 if ((uns0_p && uns1_p)
kono
parents:
diff changeset
2114 ? ((precop + !unsr_p) <= precres
kono
parents:
diff changeset
2115 /* u1 - u2 -> ur can overflow, no matter what precision
kono
parents:
diff changeset
2116 the result has. */
kono
parents:
diff changeset
2117 && (code != MINUS_EXPR || !unsr_p))
kono
parents:
diff changeset
2118 : (!unsr_p && precop <= precres))
kono
parents:
diff changeset
2119 {
kono
parents:
diff changeset
2120 /* The infinity precision result will always fit into result. */
kono
parents:
diff changeset
2121 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2122 write_complex_part (target, const0_rtx, true);
kono
parents:
diff changeset
2123 scalar_int_mode mode = SCALAR_INT_TYPE_MODE (type);
kono
parents:
diff changeset
2124 struct separate_ops ops;
kono
parents:
diff changeset
2125 ops.code = code;
kono
parents:
diff changeset
2126 ops.type = type;
kono
parents:
diff changeset
2127 ops.op0 = fold_convert_loc (loc, type, arg0);
kono
parents:
diff changeset
2128 ops.op1 = fold_convert_loc (loc, type, arg1);
kono
parents:
diff changeset
2129 ops.op2 = NULL_TREE;
kono
parents:
diff changeset
2130 ops.location = loc;
kono
parents:
diff changeset
2131 rtx tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
kono
parents:
diff changeset
2132 expand_arith_overflow_result_store (lhs, target, mode, tem);
kono
parents:
diff changeset
2133 return;
kono
parents:
diff changeset
2134 }
kono
parents:
diff changeset
2135
kono
parents:
diff changeset
2136 /* For operations with low precision, if target doesn't have them, start
kono
parents:
diff changeset
2137 with precres widening right away, otherwise do it only if the most
kono
parents:
diff changeset
2138 simple cases can't be used. */
kono
parents:
diff changeset
2139 const int min_precision = targetm.min_arithmetic_precision ();
kono
parents:
diff changeset
2140 if (orig_precres == precres && precres < min_precision)
kono
parents:
diff changeset
2141 ;
kono
parents:
diff changeset
2142 else if ((uns0_p && uns1_p && unsr_p && prec0 <= precres
kono
parents:
diff changeset
2143 && prec1 <= precres)
kono
parents:
diff changeset
2144 || ((!uns0_p || !uns1_p) && !unsr_p
kono
parents:
diff changeset
2145 && prec0 + uns0_p <= precres
kono
parents:
diff changeset
2146 && prec1 + uns1_p <= precres))
kono
parents:
diff changeset
2147 {
kono
parents:
diff changeset
2148 arg0 = fold_convert_loc (loc, type, arg0);
kono
parents:
diff changeset
2149 arg1 = fold_convert_loc (loc, type, arg1);
kono
parents:
diff changeset
2150 switch (code)
kono
parents:
diff changeset
2151 {
kono
parents:
diff changeset
2152 case MINUS_EXPR:
kono
parents:
diff changeset
2153 if (integer_zerop (arg0) && !unsr_p)
kono
parents:
diff changeset
2154 {
kono
parents:
diff changeset
2155 expand_neg_overflow (loc, lhs, arg1, false, NULL);
kono
parents:
diff changeset
2156 return;
kono
parents:
diff changeset
2157 }
kono
parents:
diff changeset
2158 /* FALLTHRU */
kono
parents:
diff changeset
2159 case PLUS_EXPR:
kono
parents:
diff changeset
2160 expand_addsub_overflow (loc, code, lhs, arg0, arg1, unsr_p,
kono
parents:
diff changeset
2161 unsr_p, unsr_p, false, NULL);
kono
parents:
diff changeset
2162 return;
kono
parents:
diff changeset
2163 case MULT_EXPR:
kono
parents:
diff changeset
2164 expand_mul_overflow (loc, lhs, arg0, arg1, unsr_p,
kono
parents:
diff changeset
2165 unsr_p, unsr_p, false, NULL);
kono
parents:
diff changeset
2166 return;
kono
parents:
diff changeset
2167 default:
kono
parents:
diff changeset
2168 gcc_unreachable ();
kono
parents:
diff changeset
2169 }
kono
parents:
diff changeset
2170 }
kono
parents:
diff changeset
2171
kono
parents:
diff changeset
2172 /* For sub-word operations, retry with a wider type first. */
kono
parents:
diff changeset
2173 if (orig_precres == precres && precop <= BITS_PER_WORD)
kono
parents:
diff changeset
2174 {
kono
parents:
diff changeset
2175 int p = MAX (min_precision, precop);
kono
parents:
diff changeset
2176 scalar_int_mode m = smallest_int_mode_for_size (p);
kono
parents:
diff changeset
2177 tree optype = build_nonstandard_integer_type (GET_MODE_PRECISION (m),
kono
parents:
diff changeset
2178 uns0_p && uns1_p
kono
parents:
diff changeset
2179 && unsr_p);
kono
parents:
diff changeset
2180 p = TYPE_PRECISION (optype);
kono
parents:
diff changeset
2181 if (p > precres)
kono
parents:
diff changeset
2182 {
kono
parents:
diff changeset
2183 precres = p;
kono
parents:
diff changeset
2184 unsr_p = TYPE_UNSIGNED (optype);
kono
parents:
diff changeset
2185 type = optype;
kono
parents:
diff changeset
2186 continue;
kono
parents:
diff changeset
2187 }
kono
parents:
diff changeset
2188 }
kono
parents:
diff changeset
2189
kono
parents:
diff changeset
2190 if (prec0 <= precres && prec1 <= precres)
kono
parents:
diff changeset
2191 {
kono
parents:
diff changeset
2192 tree types[2];
kono
parents:
diff changeset
2193 if (unsr_p)
kono
parents:
diff changeset
2194 {
kono
parents:
diff changeset
2195 types[0] = build_nonstandard_integer_type (precres, 0);
kono
parents:
diff changeset
2196 types[1] = type;
kono
parents:
diff changeset
2197 }
kono
parents:
diff changeset
2198 else
kono
parents:
diff changeset
2199 {
kono
parents:
diff changeset
2200 types[0] = type;
kono
parents:
diff changeset
2201 types[1] = build_nonstandard_integer_type (precres, 1);
kono
parents:
diff changeset
2202 }
kono
parents:
diff changeset
2203 arg0 = fold_convert_loc (loc, types[uns0_p], arg0);
kono
parents:
diff changeset
2204 arg1 = fold_convert_loc (loc, types[uns1_p], arg1);
kono
parents:
diff changeset
2205 if (code != MULT_EXPR)
kono
parents:
diff changeset
2206 expand_addsub_overflow (loc, code, lhs, arg0, arg1, unsr_p,
kono
parents:
diff changeset
2207 uns0_p, uns1_p, false, NULL);
kono
parents:
diff changeset
2208 else
kono
parents:
diff changeset
2209 expand_mul_overflow (loc, lhs, arg0, arg1, unsr_p,
kono
parents:
diff changeset
2210 uns0_p, uns1_p, false, NULL);
kono
parents:
diff changeset
2211 return;
kono
parents:
diff changeset
2212 }
kono
parents:
diff changeset
2213
kono
parents:
diff changeset
2214 /* Retry with a wider type. */
kono
parents:
diff changeset
2215 if (orig_precres == precres)
kono
parents:
diff changeset
2216 {
kono
parents:
diff changeset
2217 int p = MAX (prec0, prec1);
kono
parents:
diff changeset
2218 scalar_int_mode m = smallest_int_mode_for_size (p);
kono
parents:
diff changeset
2219 tree optype = build_nonstandard_integer_type (GET_MODE_PRECISION (m),
kono
parents:
diff changeset
2220 uns0_p && uns1_p
kono
parents:
diff changeset
2221 && unsr_p);
kono
parents:
diff changeset
2222 p = TYPE_PRECISION (optype);
kono
parents:
diff changeset
2223 if (p > precres)
kono
parents:
diff changeset
2224 {
kono
parents:
diff changeset
2225 precres = p;
kono
parents:
diff changeset
2226 unsr_p = TYPE_UNSIGNED (optype);
kono
parents:
diff changeset
2227 type = optype;
kono
parents:
diff changeset
2228 continue;
kono
parents:
diff changeset
2229 }
kono
parents:
diff changeset
2230 }
kono
parents:
diff changeset
2231
kono
parents:
diff changeset
2232 gcc_unreachable ();
kono
parents:
diff changeset
2233 }
kono
parents:
diff changeset
2234 while (1);
kono
parents:
diff changeset
2235 }
kono
parents:
diff changeset
2236
kono
parents:
diff changeset
2237 /* Expand ADD_OVERFLOW STMT. */
kono
parents:
diff changeset
2238
kono
parents:
diff changeset
2239 static void
kono
parents:
diff changeset
2240 expand_ADD_OVERFLOW (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2241 {
kono
parents:
diff changeset
2242 expand_arith_overflow (PLUS_EXPR, stmt);
kono
parents:
diff changeset
2243 }
kono
parents:
diff changeset
2244
kono
parents:
diff changeset
2245 /* Expand SUB_OVERFLOW STMT. */
kono
parents:
diff changeset
2246
kono
parents:
diff changeset
2247 static void
kono
parents:
diff changeset
2248 expand_SUB_OVERFLOW (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2249 {
kono
parents:
diff changeset
2250 expand_arith_overflow (MINUS_EXPR, stmt);
kono
parents:
diff changeset
2251 }
kono
parents:
diff changeset
2252
kono
parents:
diff changeset
2253 /* Expand MUL_OVERFLOW STMT. */
kono
parents:
diff changeset
2254
kono
parents:
diff changeset
2255 static void
kono
parents:
diff changeset
2256 expand_MUL_OVERFLOW (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2257 {
kono
parents:
diff changeset
2258 expand_arith_overflow (MULT_EXPR, stmt);
kono
parents:
diff changeset
2259 }
kono
parents:
diff changeset
2260
kono
parents:
diff changeset
2261 /* This should get folded in tree-vectorizer.c. */
kono
parents:
diff changeset
2262
kono
parents:
diff changeset
2263 static void
kono
parents:
diff changeset
2264 expand_LOOP_VECTORIZED (internal_fn, gcall *)
kono
parents:
diff changeset
2265 {
kono
parents:
diff changeset
2266 gcc_unreachable ();
kono
parents:
diff changeset
2267 }
kono
parents:
diff changeset
2268
kono
parents:
diff changeset
2269 /* This should get folded in tree-vectorizer.c. */
kono
parents:
diff changeset
2270
kono
parents:
diff changeset
2271 static void
kono
parents:
diff changeset
2272 expand_LOOP_DIST_ALIAS (internal_fn, gcall *)
kono
parents:
diff changeset
2273 {
kono
parents:
diff changeset
2274 gcc_unreachable ();
kono
parents:
diff changeset
2275 }
kono
parents:
diff changeset
2276
kono
parents:
diff changeset
2277 /* Expand MASK_LOAD call STMT using optab OPTAB. */
kono
parents:
diff changeset
2278
kono
parents:
diff changeset
2279 static void
kono
parents:
diff changeset
2280 expand_mask_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
kono
parents:
diff changeset
2281 {
kono
parents:
diff changeset
2282 struct expand_operand ops[3];
kono
parents:
diff changeset
2283 tree type, lhs, rhs, maskt, ptr;
kono
parents:
diff changeset
2284 rtx mem, target, mask;
kono
parents:
diff changeset
2285 unsigned align;
kono
parents:
diff changeset
2286
kono
parents:
diff changeset
2287 maskt = gimple_call_arg (stmt, 2);
kono
parents:
diff changeset
2288 lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2289 if (lhs == NULL_TREE)
kono
parents:
diff changeset
2290 return;
kono
parents:
diff changeset
2291 type = TREE_TYPE (lhs);
kono
parents:
diff changeset
2292 ptr = build_int_cst (TREE_TYPE (gimple_call_arg (stmt, 1)), 0);
kono
parents:
diff changeset
2293 align = tree_to_shwi (gimple_call_arg (stmt, 1));
kono
parents:
diff changeset
2294 if (TYPE_ALIGN (type) != align)
kono
parents:
diff changeset
2295 type = build_aligned_type (type, align);
kono
parents:
diff changeset
2296 rhs = fold_build2 (MEM_REF, type, gimple_call_arg (stmt, 0), ptr);
kono
parents:
diff changeset
2297
kono
parents:
diff changeset
2298 mem = expand_expr (rhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2299 gcc_assert (MEM_P (mem));
kono
parents:
diff changeset
2300 mask = expand_normal (maskt);
kono
parents:
diff changeset
2301 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2302 create_output_operand (&ops[0], target, TYPE_MODE (type));
kono
parents:
diff changeset
2303 create_fixed_operand (&ops[1], mem);
kono
parents:
diff changeset
2304 create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
kono
parents:
diff changeset
2305 expand_insn (convert_optab_handler (optab, TYPE_MODE (type),
kono
parents:
diff changeset
2306 TYPE_MODE (TREE_TYPE (maskt))),
kono
parents:
diff changeset
2307 3, ops);
kono
parents:
diff changeset
2308 }
kono
parents:
diff changeset
2309
kono
parents:
diff changeset
2310 /* Expand MASK_STORE call STMT using optab OPTAB. */
kono
parents:
diff changeset
2311
kono
parents:
diff changeset
2312 static void
kono
parents:
diff changeset
2313 expand_mask_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
kono
parents:
diff changeset
2314 {
kono
parents:
diff changeset
2315 struct expand_operand ops[3];
kono
parents:
diff changeset
2316 tree type, lhs, rhs, maskt, ptr;
kono
parents:
diff changeset
2317 rtx mem, reg, mask;
kono
parents:
diff changeset
2318 unsigned align;
kono
parents:
diff changeset
2319
kono
parents:
diff changeset
2320 maskt = gimple_call_arg (stmt, 2);
kono
parents:
diff changeset
2321 rhs = gimple_call_arg (stmt, 3);
kono
parents:
diff changeset
2322 type = TREE_TYPE (rhs);
kono
parents:
diff changeset
2323 ptr = build_int_cst (TREE_TYPE (gimple_call_arg (stmt, 1)), 0);
kono
parents:
diff changeset
2324 align = tree_to_shwi (gimple_call_arg (stmt, 1));
kono
parents:
diff changeset
2325 if (TYPE_ALIGN (type) != align)
kono
parents:
diff changeset
2326 type = build_aligned_type (type, align);
kono
parents:
diff changeset
2327 lhs = fold_build2 (MEM_REF, type, gimple_call_arg (stmt, 0), ptr);
kono
parents:
diff changeset
2328
kono
parents:
diff changeset
2329 mem = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2330 gcc_assert (MEM_P (mem));
kono
parents:
diff changeset
2331 mask = expand_normal (maskt);
kono
parents:
diff changeset
2332 reg = expand_normal (rhs);
kono
parents:
diff changeset
2333 create_fixed_operand (&ops[0], mem);
kono
parents:
diff changeset
2334 create_input_operand (&ops[1], reg, TYPE_MODE (type));
kono
parents:
diff changeset
2335 create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt)));
kono
parents:
diff changeset
2336 expand_insn (convert_optab_handler (optab, TYPE_MODE (type),
kono
parents:
diff changeset
2337 TYPE_MODE (TREE_TYPE (maskt))),
kono
parents:
diff changeset
2338 3, ops);
kono
parents:
diff changeset
2339 }
kono
parents:
diff changeset
2340
kono
parents:
diff changeset
2341 static void
kono
parents:
diff changeset
2342 expand_ABNORMAL_DISPATCHER (internal_fn, gcall *)
kono
parents:
diff changeset
2343 {
kono
parents:
diff changeset
2344 }
kono
parents:
diff changeset
2345
kono
parents:
diff changeset
2346 static void
kono
parents:
diff changeset
2347 expand_BUILTIN_EXPECT (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2348 {
kono
parents:
diff changeset
2349 /* When guessing was done, the hints should be already stripped away. */
kono
parents:
diff changeset
2350 gcc_assert (!flag_guess_branch_prob || optimize == 0 || seen_error ());
kono
parents:
diff changeset
2351
kono
parents:
diff changeset
2352 rtx target;
kono
parents:
diff changeset
2353 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2354 if (lhs)
kono
parents:
diff changeset
2355 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2356 else
kono
parents:
diff changeset
2357 target = const0_rtx;
kono
parents:
diff changeset
2358 rtx val = expand_expr (gimple_call_arg (stmt, 0), target, VOIDmode, EXPAND_NORMAL);
kono
parents:
diff changeset
2359 if (lhs && val != target)
kono
parents:
diff changeset
2360 emit_move_insn (target, val);
kono
parents:
diff changeset
2361 }
kono
parents:
diff changeset
2362
kono
parents:
diff changeset
2363 /* IFN_VA_ARG is supposed to be expanded at pass_stdarg. So this dummy function
kono
parents:
diff changeset
2364 should never be called. */
kono
parents:
diff changeset
2365
kono
parents:
diff changeset
2366 static void
kono
parents:
diff changeset
2367 expand_VA_ARG (internal_fn, gcall *)
kono
parents:
diff changeset
2368 {
kono
parents:
diff changeset
2369 gcc_unreachable ();
kono
parents:
diff changeset
2370 }
kono
parents:
diff changeset
2371
kono
parents:
diff changeset
2372 /* Expand the IFN_UNIQUE function according to its first argument. */
kono
parents:
diff changeset
2373
kono
parents:
diff changeset
2374 static void
kono
parents:
diff changeset
2375 expand_UNIQUE (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2376 {
kono
parents:
diff changeset
2377 rtx pattern = NULL_RTX;
kono
parents:
diff changeset
2378 enum ifn_unique_kind kind
kono
parents:
diff changeset
2379 = (enum ifn_unique_kind) TREE_INT_CST_LOW (gimple_call_arg (stmt, 0));
kono
parents:
diff changeset
2380
kono
parents:
diff changeset
2381 switch (kind)
kono
parents:
diff changeset
2382 {
kono
parents:
diff changeset
2383 default:
kono
parents:
diff changeset
2384 gcc_unreachable ();
kono
parents:
diff changeset
2385
kono
parents:
diff changeset
2386 case IFN_UNIQUE_UNSPEC:
kono
parents:
diff changeset
2387 if (targetm.have_unique ())
kono
parents:
diff changeset
2388 pattern = targetm.gen_unique ();
kono
parents:
diff changeset
2389 break;
kono
parents:
diff changeset
2390
kono
parents:
diff changeset
2391 case IFN_UNIQUE_OACC_FORK:
kono
parents:
diff changeset
2392 case IFN_UNIQUE_OACC_JOIN:
kono
parents:
diff changeset
2393 if (targetm.have_oacc_fork () && targetm.have_oacc_join ())
kono
parents:
diff changeset
2394 {
kono
parents:
diff changeset
2395 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2396 rtx target = const0_rtx;
kono
parents:
diff changeset
2397
kono
parents:
diff changeset
2398 if (lhs)
kono
parents:
diff changeset
2399 target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2400
kono
parents:
diff changeset
2401 rtx data_dep = expand_normal (gimple_call_arg (stmt, 1));
kono
parents:
diff changeset
2402 rtx axis = expand_normal (gimple_call_arg (stmt, 2));
kono
parents:
diff changeset
2403
kono
parents:
diff changeset
2404 if (kind == IFN_UNIQUE_OACC_FORK)
kono
parents:
diff changeset
2405 pattern = targetm.gen_oacc_fork (target, data_dep, axis);
kono
parents:
diff changeset
2406 else
kono
parents:
diff changeset
2407 pattern = targetm.gen_oacc_join (target, data_dep, axis);
kono
parents:
diff changeset
2408 }
kono
parents:
diff changeset
2409 else
kono
parents:
diff changeset
2410 gcc_unreachable ();
kono
parents:
diff changeset
2411 break;
kono
parents:
diff changeset
2412 }
kono
parents:
diff changeset
2413
kono
parents:
diff changeset
2414 if (pattern)
kono
parents:
diff changeset
2415 emit_insn (pattern);
kono
parents:
diff changeset
2416 }
kono
parents:
diff changeset
2417
kono
parents:
diff changeset
2418 /* The size of an OpenACC compute dimension. */
kono
parents:
diff changeset
2419
kono
parents:
diff changeset
2420 static void
kono
parents:
diff changeset
2421 expand_GOACC_DIM_SIZE (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2422 {
kono
parents:
diff changeset
2423 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2424
kono
parents:
diff changeset
2425 if (!lhs)
kono
parents:
diff changeset
2426 return;
kono
parents:
diff changeset
2427
kono
parents:
diff changeset
2428 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2429 if (targetm.have_oacc_dim_size ())
kono
parents:
diff changeset
2430 {
kono
parents:
diff changeset
2431 rtx dim = expand_expr (gimple_call_arg (stmt, 0), NULL_RTX,
kono
parents:
diff changeset
2432 VOIDmode, EXPAND_NORMAL);
kono
parents:
diff changeset
2433 emit_insn (targetm.gen_oacc_dim_size (target, dim));
kono
parents:
diff changeset
2434 }
kono
parents:
diff changeset
2435 else
kono
parents:
diff changeset
2436 emit_move_insn (target, GEN_INT (1));
kono
parents:
diff changeset
2437 }
kono
parents:
diff changeset
2438
kono
parents:
diff changeset
2439 /* The position of an OpenACC execution engine along one compute axis. */
kono
parents:
diff changeset
2440
kono
parents:
diff changeset
2441 static void
kono
parents:
diff changeset
2442 expand_GOACC_DIM_POS (internal_fn, gcall *stmt)
kono
parents:
diff changeset
2443 {
kono
parents:
diff changeset
2444 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2445
kono
parents:
diff changeset
2446 if (!lhs)
kono
parents:
diff changeset
2447 return;
kono
parents:
diff changeset
2448
kono
parents:
diff changeset
2449 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2450 if (targetm.have_oacc_dim_pos ())
kono
parents:
diff changeset
2451 {
kono
parents:
diff changeset
2452 rtx dim = expand_expr (gimple_call_arg (stmt, 0), NULL_RTX,
kono
parents:
diff changeset
2453 VOIDmode, EXPAND_NORMAL);
kono
parents:
diff changeset
2454 emit_insn (targetm.gen_oacc_dim_pos (target, dim));
kono
parents:
diff changeset
2455 }
kono
parents:
diff changeset
2456 else
kono
parents:
diff changeset
2457 emit_move_insn (target, const0_rtx);
kono
parents:
diff changeset
2458 }
kono
parents:
diff changeset
2459
kono
parents:
diff changeset
2460 /* This is expanded by oacc_device_lower pass. */
kono
parents:
diff changeset
2461
kono
parents:
diff changeset
2462 static void
kono
parents:
diff changeset
2463 expand_GOACC_LOOP (internal_fn, gcall *)
kono
parents:
diff changeset
2464 {
kono
parents:
diff changeset
2465 gcc_unreachable ();
kono
parents:
diff changeset
2466 }
kono
parents:
diff changeset
2467
kono
parents:
diff changeset
2468 /* This is expanded by oacc_device_lower pass. */
kono
parents:
diff changeset
2469
kono
parents:
diff changeset
2470 static void
kono
parents:
diff changeset
2471 expand_GOACC_REDUCTION (internal_fn, gcall *)
kono
parents:
diff changeset
2472 {
kono
parents:
diff changeset
2473 gcc_unreachable ();
kono
parents:
diff changeset
2474 }
kono
parents:
diff changeset
2475
kono
parents:
diff changeset
2476 /* This is expanded by oacc_device_lower pass. */
kono
parents:
diff changeset
2477
kono
parents:
diff changeset
2478 static void
kono
parents:
diff changeset
2479 expand_GOACC_TILE (internal_fn, gcall *)
kono
parents:
diff changeset
2480 {
kono
parents:
diff changeset
2481 gcc_unreachable ();
kono
parents:
diff changeset
2482 }
kono
parents:
diff changeset
2483
kono
parents:
diff changeset
2484 /* Set errno to EDOM. */
kono
parents:
diff changeset
2485
kono
parents:
diff changeset
2486 static void
kono
parents:
diff changeset
2487 expand_SET_EDOM (internal_fn, gcall *)
kono
parents:
diff changeset
2488 {
kono
parents:
diff changeset
2489 #ifdef TARGET_EDOM
kono
parents:
diff changeset
2490 #ifdef GEN_ERRNO_RTX
kono
parents:
diff changeset
2491 rtx errno_rtx = GEN_ERRNO_RTX;
kono
parents:
diff changeset
2492 #else
kono
parents:
diff changeset
2493 rtx errno_rtx = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno"));
kono
parents:
diff changeset
2494 #endif
kono
parents:
diff changeset
2495 emit_move_insn (errno_rtx,
kono
parents:
diff changeset
2496 gen_int_mode (TARGET_EDOM, GET_MODE (errno_rtx)));
kono
parents:
diff changeset
2497 #else
kono
parents:
diff changeset
2498 gcc_unreachable ();
kono
parents:
diff changeset
2499 #endif
kono
parents:
diff changeset
2500 }
kono
parents:
diff changeset
2501
kono
parents:
diff changeset
2502 /* Expand atomic bit test and set. */
kono
parents:
diff changeset
2503
kono
parents:
diff changeset
2504 static void
kono
parents:
diff changeset
2505 expand_ATOMIC_BIT_TEST_AND_SET (internal_fn, gcall *call)
kono
parents:
diff changeset
2506 {
kono
parents:
diff changeset
2507 expand_ifn_atomic_bit_test_and (call);
kono
parents:
diff changeset
2508 }
kono
parents:
diff changeset
2509
kono
parents:
diff changeset
2510 /* Expand atomic bit test and complement. */
kono
parents:
diff changeset
2511
kono
parents:
diff changeset
2512 static void
kono
parents:
diff changeset
2513 expand_ATOMIC_BIT_TEST_AND_COMPLEMENT (internal_fn, gcall *call)
kono
parents:
diff changeset
2514 {
kono
parents:
diff changeset
2515 expand_ifn_atomic_bit_test_and (call);
kono
parents:
diff changeset
2516 }
kono
parents:
diff changeset
2517
kono
parents:
diff changeset
2518 /* Expand atomic bit test and reset. */
kono
parents:
diff changeset
2519
kono
parents:
diff changeset
2520 static void
kono
parents:
diff changeset
2521 expand_ATOMIC_BIT_TEST_AND_RESET (internal_fn, gcall *call)
kono
parents:
diff changeset
2522 {
kono
parents:
diff changeset
2523 expand_ifn_atomic_bit_test_and (call);
kono
parents:
diff changeset
2524 }
kono
parents:
diff changeset
2525
kono
parents:
diff changeset
2526 /* Expand atomic bit test and set. */
kono
parents:
diff changeset
2527
kono
parents:
diff changeset
2528 static void
kono
parents:
diff changeset
2529 expand_ATOMIC_COMPARE_EXCHANGE (internal_fn, gcall *call)
kono
parents:
diff changeset
2530 {
kono
parents:
diff changeset
2531 expand_ifn_atomic_compare_exchange (call);
kono
parents:
diff changeset
2532 }
kono
parents:
diff changeset
2533
kono
parents:
diff changeset
2534 /* Expand LAUNDER to assignment, lhs = arg0. */
kono
parents:
diff changeset
2535
kono
parents:
diff changeset
2536 static void
kono
parents:
diff changeset
2537 expand_LAUNDER (internal_fn, gcall *call)
kono
parents:
diff changeset
2538 {
kono
parents:
diff changeset
2539 tree lhs = gimple_call_lhs (call);
kono
parents:
diff changeset
2540
kono
parents:
diff changeset
2541 if (!lhs)
kono
parents:
diff changeset
2542 return;
kono
parents:
diff changeset
2543
kono
parents:
diff changeset
2544 expand_assignment (lhs, gimple_call_arg (call, 0), false);
kono
parents:
diff changeset
2545 }
kono
parents:
diff changeset
2546
kono
parents:
diff changeset
2547 /* Expand DIVMOD() using:
kono
parents:
diff changeset
2548 a) optab handler for udivmod/sdivmod if it is available.
kono
parents:
diff changeset
2549 b) If optab_handler doesn't exist, generate call to
kono
parents:
diff changeset
2550 target-specific divmod libfunc. */
kono
parents:
diff changeset
2551
kono
parents:
diff changeset
2552 static void
kono
parents:
diff changeset
2553 expand_DIVMOD (internal_fn, gcall *call_stmt)
kono
parents:
diff changeset
2554 {
kono
parents:
diff changeset
2555 tree lhs = gimple_call_lhs (call_stmt);
kono
parents:
diff changeset
2556 tree arg0 = gimple_call_arg (call_stmt, 0);
kono
parents:
diff changeset
2557 tree arg1 = gimple_call_arg (call_stmt, 1);
kono
parents:
diff changeset
2558
kono
parents:
diff changeset
2559 gcc_assert (TREE_CODE (TREE_TYPE (lhs)) == COMPLEX_TYPE);
kono
parents:
diff changeset
2560 tree type = TREE_TYPE (TREE_TYPE (lhs));
kono
parents:
diff changeset
2561 machine_mode mode = TYPE_MODE (type);
kono
parents:
diff changeset
2562 bool unsignedp = TYPE_UNSIGNED (type);
kono
parents:
diff changeset
2563 optab tab = (unsignedp) ? udivmod_optab : sdivmod_optab;
kono
parents:
diff changeset
2564
kono
parents:
diff changeset
2565 rtx op0 = expand_normal (arg0);
kono
parents:
diff changeset
2566 rtx op1 = expand_normal (arg1);
kono
parents:
diff changeset
2567 rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2568
kono
parents:
diff changeset
2569 rtx quotient, remainder, libfunc;
kono
parents:
diff changeset
2570
kono
parents:
diff changeset
2571 /* Check if optab_handler exists for divmod_optab for given mode. */
kono
parents:
diff changeset
2572 if (optab_handler (tab, mode) != CODE_FOR_nothing)
kono
parents:
diff changeset
2573 {
kono
parents:
diff changeset
2574 quotient = gen_reg_rtx (mode);
kono
parents:
diff changeset
2575 remainder = gen_reg_rtx (mode);
kono
parents:
diff changeset
2576 expand_twoval_binop (tab, op0, op1, quotient, remainder, unsignedp);
kono
parents:
diff changeset
2577 }
kono
parents:
diff changeset
2578
kono
parents:
diff changeset
2579 /* Generate call to divmod libfunc if it exists. */
kono
parents:
diff changeset
2580 else if ((libfunc = optab_libfunc (tab, mode)) != NULL_RTX)
kono
parents:
diff changeset
2581 targetm.expand_divmod_libfunc (libfunc, mode, op0, op1,
kono
parents:
diff changeset
2582 &quotient, &remainder);
kono
parents:
diff changeset
2583
kono
parents:
diff changeset
2584 else
kono
parents:
diff changeset
2585 gcc_unreachable ();
kono
parents:
diff changeset
2586
kono
parents:
diff changeset
2587 /* Wrap the return value (quotient, remainder) within COMPLEX_EXPR. */
kono
parents:
diff changeset
2588 expand_expr (build2 (COMPLEX_EXPR, TREE_TYPE (lhs),
kono
parents:
diff changeset
2589 make_tree (TREE_TYPE (arg0), quotient),
kono
parents:
diff changeset
2590 make_tree (TREE_TYPE (arg1), remainder)),
kono
parents:
diff changeset
2591 target, VOIDmode, EXPAND_NORMAL);
kono
parents:
diff changeset
2592 }
kono
parents:
diff changeset
2593
kono
parents:
diff changeset
2594 /* Expand a call to FN using the operands in STMT. FN has a single
kono
parents:
diff changeset
2595 output operand and NARGS input operands. */
kono
parents:
diff changeset
2596
kono
parents:
diff changeset
2597 static void
kono
parents:
diff changeset
2598 expand_direct_optab_fn (internal_fn fn, gcall *stmt, direct_optab optab,
kono
parents:
diff changeset
2599 unsigned int nargs)
kono
parents:
diff changeset
2600 {
kono
parents:
diff changeset
2601 expand_operand *ops = XALLOCAVEC (expand_operand, nargs + 1);
kono
parents:
diff changeset
2602
kono
parents:
diff changeset
2603 tree_pair types = direct_internal_fn_types (fn, stmt);
kono
parents:
diff changeset
2604 insn_code icode = direct_optab_handler (optab, TYPE_MODE (types.first));
kono
parents:
diff changeset
2605
kono
parents:
diff changeset
2606 tree lhs = gimple_call_lhs (stmt);
kono
parents:
diff changeset
2607 tree lhs_type = TREE_TYPE (lhs);
kono
parents:
diff changeset
2608 rtx lhs_rtx = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
kono
parents:
diff changeset
2609
kono
parents:
diff changeset
2610 /* Do not assign directly to a promoted subreg, since there is no
kono
parents:
diff changeset
2611 guarantee that the instruction will leave the upper bits of the
kono
parents:
diff changeset
2612 register in the state required by SUBREG_PROMOTED_SIGN. */
kono
parents:
diff changeset
2613 rtx dest = lhs_rtx;
kono
parents:
diff changeset
2614 if (GET_CODE (dest) == SUBREG && SUBREG_PROMOTED_VAR_P (dest))
kono
parents:
diff changeset
2615 dest = NULL_RTX;
kono
parents:
diff changeset
2616
kono
parents:
diff changeset
2617 create_output_operand (&ops[0], dest, insn_data[icode].operand[0].mode);
kono
parents:
diff changeset
2618
kono
parents:
diff changeset
2619 for (unsigned int i = 0; i < nargs; ++i)
kono
parents:
diff changeset
2620 {
kono
parents:
diff changeset
2621 tree rhs = gimple_call_arg (stmt, i);
kono
parents:
diff changeset
2622 tree rhs_type = TREE_TYPE (rhs);
kono
parents:
diff changeset
2623 rtx rhs_rtx = expand_normal (rhs);
kono
parents:
diff changeset
2624 if (INTEGRAL_TYPE_P (rhs_type))
kono
parents:
diff changeset
2625 create_convert_operand_from (&ops[i + 1], rhs_rtx,
kono
parents:
diff changeset
2626 TYPE_MODE (rhs_type),
kono
parents:
diff changeset
2627 TYPE_UNSIGNED (rhs_type));
kono
parents:
diff changeset
2628 else
kono
parents:
diff changeset
2629 create_input_operand (&ops[i + 1], rhs_rtx, TYPE_MODE (rhs_type));
kono
parents:
diff changeset
2630 }
kono
parents:
diff changeset
2631
kono
parents:
diff changeset
2632 expand_insn (icode, nargs + 1, ops);
kono
parents:
diff changeset
2633 if (!rtx_equal_p (lhs_rtx, ops[0].value))
kono
parents:
diff changeset
2634 {
kono
parents:
diff changeset
2635 /* If the return value has an integral type, convert the instruction
kono
parents:
diff changeset
2636 result to that type. This is useful for things that return an
kono
parents:
diff changeset
2637 int regardless of the size of the input. If the instruction result
kono
parents:
diff changeset
2638 is smaller than required, assume that it is signed.
kono
parents:
diff changeset
2639
kono
parents:
diff changeset
2640 If the return value has a nonintegral type, its mode must match
kono
parents:
diff changeset
2641 the instruction result. */
kono
parents:
diff changeset
2642 if (GET_CODE (lhs_rtx) == SUBREG && SUBREG_PROMOTED_VAR_P (lhs_rtx))
kono
parents:
diff changeset
2643 {
kono
parents:
diff changeset
2644 /* If this is a scalar in a register that is stored in a wider
kono
parents:
diff changeset
2645 mode than the declared mode, compute the result into its
kono
parents:
diff changeset
2646 declared mode and then convert to the wider mode. */
kono
parents:
diff changeset
2647 gcc_checking_assert (INTEGRAL_TYPE_P (lhs_type));
kono
parents:
diff changeset
2648 rtx tmp = convert_to_mode (GET_MODE (lhs_rtx), ops[0].value, 0);
kono
parents:
diff changeset
2649 convert_move (SUBREG_REG (lhs_rtx), tmp,
kono
parents:
diff changeset
2650 SUBREG_PROMOTED_SIGN (lhs_rtx));
kono
parents:
diff changeset
2651 }
kono
parents:
diff changeset
2652 else if (GET_MODE (lhs_rtx) == GET_MODE (ops[0].value))
kono
parents:
diff changeset
2653 emit_move_insn (lhs_rtx, ops[0].value);
kono
parents:
diff changeset
2654 else
kono
parents:
diff changeset
2655 {
kono
parents:
diff changeset
2656 gcc_checking_assert (INTEGRAL_TYPE_P (lhs_type));
kono
parents:
diff changeset
2657 convert_move (lhs_rtx, ops[0].value, 0);
kono
parents:
diff changeset
2658 }
kono
parents:
diff changeset
2659 }
kono
parents:
diff changeset
2660 }
kono
parents:
diff changeset
2661
kono
parents:
diff changeset
2662 /* Expanders for optabs that can use expand_direct_optab_fn. */
kono
parents:
diff changeset
2663
kono
parents:
diff changeset
2664 #define expand_unary_optab_fn(FN, STMT, OPTAB) \
kono
parents:
diff changeset
2665 expand_direct_optab_fn (FN, STMT, OPTAB, 1)
kono
parents:
diff changeset
2666
kono
parents:
diff changeset
2667 #define expand_binary_optab_fn(FN, STMT, OPTAB) \
kono
parents:
diff changeset
2668 expand_direct_optab_fn (FN, STMT, OPTAB, 2)
kono
parents:
diff changeset
2669
kono
parents:
diff changeset
2670 /* RETURN_TYPE and ARGS are a return type and argument list that are
kono
parents:
diff changeset
2671 in principle compatible with FN (which satisfies direct_internal_fn_p).
kono
parents:
diff changeset
2672 Return the types that should be used to determine whether the
kono
parents:
diff changeset
2673 target supports FN. */
kono
parents:
diff changeset
2674
kono
parents:
diff changeset
2675 tree_pair
kono
parents:
diff changeset
2676 direct_internal_fn_types (internal_fn fn, tree return_type, tree *args)
kono
parents:
diff changeset
2677 {
kono
parents:
diff changeset
2678 const direct_internal_fn_info &info = direct_internal_fn (fn);
kono
parents:
diff changeset
2679 tree type0 = (info.type0 < 0 ? return_type : TREE_TYPE (args[info.type0]));
kono
parents:
diff changeset
2680 tree type1 = (info.type1 < 0 ? return_type : TREE_TYPE (args[info.type1]));
kono
parents:
diff changeset
2681 return tree_pair (type0, type1);
kono
parents:
diff changeset
2682 }
kono
parents:
diff changeset
2683
kono
parents:
diff changeset
2684 /* CALL is a call whose return type and arguments are in principle
kono
parents:
diff changeset
2685 compatible with FN (which satisfies direct_internal_fn_p). Return the
kono
parents:
diff changeset
2686 types that should be used to determine whether the target supports FN. */
kono
parents:
diff changeset
2687
kono
parents:
diff changeset
2688 tree_pair
kono
parents:
diff changeset
2689 direct_internal_fn_types (internal_fn fn, gcall *call)
kono
parents:
diff changeset
2690 {
kono
parents:
diff changeset
2691 const direct_internal_fn_info &info = direct_internal_fn (fn);
kono
parents:
diff changeset
2692 tree op0 = (info.type0 < 0
kono
parents:
diff changeset
2693 ? gimple_call_lhs (call)
kono
parents:
diff changeset
2694 : gimple_call_arg (call, info.type0));
kono
parents:
diff changeset
2695 tree op1 = (info.type1 < 0
kono
parents:
diff changeset
2696 ? gimple_call_lhs (call)
kono
parents:
diff changeset
2697 : gimple_call_arg (call, info.type1));
kono
parents:
diff changeset
2698 return tree_pair (TREE_TYPE (op0), TREE_TYPE (op1));
kono
parents:
diff changeset
2699 }
kono
parents:
diff changeset
2700
kono
parents:
diff changeset
2701 /* Return true if OPTAB is supported for TYPES (whose modes should be
kono
parents:
diff changeset
2702 the same) when the optimization type is OPT_TYPE. Used for simple
kono
parents:
diff changeset
2703 direct optabs. */
kono
parents:
diff changeset
2704
kono
parents:
diff changeset
2705 static bool
kono
parents:
diff changeset
2706 direct_optab_supported_p (direct_optab optab, tree_pair types,
kono
parents:
diff changeset
2707 optimization_type opt_type)
kono
parents:
diff changeset
2708 {
kono
parents:
diff changeset
2709 machine_mode mode = TYPE_MODE (types.first);
kono
parents:
diff changeset
2710 gcc_checking_assert (mode == TYPE_MODE (types.second));
kono
parents:
diff changeset
2711 return direct_optab_handler (optab, mode, opt_type) != CODE_FOR_nothing;
kono
parents:
diff changeset
2712 }
kono
parents:
diff changeset
2713
kono
parents:
diff changeset
2714 /* Return true if load/store lanes optab OPTAB is supported for
kono
parents:
diff changeset
2715 array type TYPES.first when the optimization type is OPT_TYPE. */
kono
parents:
diff changeset
2716
kono
parents:
diff changeset
2717 static bool
kono
parents:
diff changeset
2718 multi_vector_optab_supported_p (convert_optab optab, tree_pair types,
kono
parents:
diff changeset
2719 optimization_type opt_type)
kono
parents:
diff changeset
2720 {
kono
parents:
diff changeset
2721 gcc_assert (TREE_CODE (types.first) == ARRAY_TYPE);
kono
parents:
diff changeset
2722 machine_mode imode = TYPE_MODE (types.first);
kono
parents:
diff changeset
2723 machine_mode vmode = TYPE_MODE (TREE_TYPE (types.first));
kono
parents:
diff changeset
2724 return (convert_optab_handler (optab, imode, vmode, opt_type)
kono
parents:
diff changeset
2725 != CODE_FOR_nothing);
kono
parents:
diff changeset
2726 }
kono
parents:
diff changeset
2727
kono
parents:
diff changeset
2728 #define direct_unary_optab_supported_p direct_optab_supported_p
kono
parents:
diff changeset
2729 #define direct_binary_optab_supported_p direct_optab_supported_p
kono
parents:
diff changeset
2730 #define direct_mask_load_optab_supported_p direct_optab_supported_p
kono
parents:
diff changeset
2731 #define direct_load_lanes_optab_supported_p multi_vector_optab_supported_p
kono
parents:
diff changeset
2732 #define direct_mask_store_optab_supported_p direct_optab_supported_p
kono
parents:
diff changeset
2733 #define direct_store_lanes_optab_supported_p multi_vector_optab_supported_p
kono
parents:
diff changeset
2734
kono
parents:
diff changeset
2735 /* Return true if FN is supported for the types in TYPES when the
kono
parents:
diff changeset
2736 optimization type is OPT_TYPE. The types are those associated with
kono
parents:
diff changeset
2737 the "type0" and "type1" fields of FN's direct_internal_fn_info
kono
parents:
diff changeset
2738 structure. */
kono
parents:
diff changeset
2739
kono
parents:
diff changeset
2740 bool
kono
parents:
diff changeset
2741 direct_internal_fn_supported_p (internal_fn fn, tree_pair types,
kono
parents:
diff changeset
2742 optimization_type opt_type)
kono
parents:
diff changeset
2743 {
kono
parents:
diff changeset
2744 switch (fn)
kono
parents:
diff changeset
2745 {
kono
parents:
diff changeset
2746 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) \
kono
parents:
diff changeset
2747 case IFN_##CODE: break;
kono
parents:
diff changeset
2748 #define DEF_INTERNAL_OPTAB_FN(CODE, FLAGS, OPTAB, TYPE) \
kono
parents:
diff changeset
2749 case IFN_##CODE: \
kono
parents:
diff changeset
2750 return direct_##TYPE##_optab_supported_p (OPTAB##_optab, types, \
kono
parents:
diff changeset
2751 opt_type);
kono
parents:
diff changeset
2752 #include "internal-fn.def"
kono
parents:
diff changeset
2753
kono
parents:
diff changeset
2754 case IFN_LAST:
kono
parents:
diff changeset
2755 break;
kono
parents:
diff changeset
2756 }
kono
parents:
diff changeset
2757 gcc_unreachable ();
kono
parents:
diff changeset
2758 }
kono
parents:
diff changeset
2759
kono
parents:
diff changeset
2760 /* Return true if FN is supported for type TYPE when the optimization
kono
parents:
diff changeset
2761 type is OPT_TYPE. The caller knows that the "type0" and "type1"
kono
parents:
diff changeset
2762 fields of FN's direct_internal_fn_info structure are the same. */
kono
parents:
diff changeset
2763
kono
parents:
diff changeset
2764 bool
kono
parents:
diff changeset
2765 direct_internal_fn_supported_p (internal_fn fn, tree type,
kono
parents:
diff changeset
2766 optimization_type opt_type)
kono
parents:
diff changeset
2767 {
kono
parents:
diff changeset
2768 const direct_internal_fn_info &info = direct_internal_fn (fn);
kono
parents:
diff changeset
2769 gcc_checking_assert (info.type0 == info.type1);
kono
parents:
diff changeset
2770 return direct_internal_fn_supported_p (fn, tree_pair (type, type), opt_type);
kono
parents:
diff changeset
2771 }
kono
parents:
diff changeset
2772
kono
parents:
diff changeset
2773 /* Return true if IFN_SET_EDOM is supported. */
kono
parents:
diff changeset
2774
kono
parents:
diff changeset
2775 bool
kono
parents:
diff changeset
2776 set_edom_supported_p (void)
kono
parents:
diff changeset
2777 {
kono
parents:
diff changeset
2778 #ifdef TARGET_EDOM
kono
parents:
diff changeset
2779 return true;
kono
parents:
diff changeset
2780 #else
kono
parents:
diff changeset
2781 return false;
kono
parents:
diff changeset
2782 #endif
kono
parents:
diff changeset
2783 }
kono
parents:
diff changeset
2784
kono
parents:
diff changeset
2785 #define DEF_INTERNAL_OPTAB_FN(CODE, FLAGS, OPTAB, TYPE) \
kono
parents:
diff changeset
2786 static void \
kono
parents:
diff changeset
2787 expand_##CODE (internal_fn fn, gcall *stmt) \
kono
parents:
diff changeset
2788 { \
kono
parents:
diff changeset
2789 expand_##TYPE##_optab_fn (fn, stmt, OPTAB##_optab); \
kono
parents:
diff changeset
2790 }
kono
parents:
diff changeset
2791 #include "internal-fn.def"
kono
parents:
diff changeset
2792
kono
parents:
diff changeset
2793 /* Routines to expand each internal function, indexed by function number.
kono
parents:
diff changeset
2794 Each routine has the prototype:
kono
parents:
diff changeset
2795
kono
parents:
diff changeset
2796 expand_<NAME> (gcall *stmt)
kono
parents:
diff changeset
2797
kono
parents:
diff changeset
2798 where STMT is the statement that performs the call. */
kono
parents:
diff changeset
2799 static void (*const internal_fn_expanders[]) (internal_fn, gcall *) = {
kono
parents:
diff changeset
2800 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) expand_##CODE,
kono
parents:
diff changeset
2801 #include "internal-fn.def"
kono
parents:
diff changeset
2802 0
kono
parents:
diff changeset
2803 };
kono
parents:
diff changeset
2804
kono
parents:
diff changeset
2805 /* Expand STMT as though it were a call to internal function FN. */
kono
parents:
diff changeset
2806
kono
parents:
diff changeset
2807 void
kono
parents:
diff changeset
2808 expand_internal_call (internal_fn fn, gcall *stmt)
kono
parents:
diff changeset
2809 {
kono
parents:
diff changeset
2810 internal_fn_expanders[fn] (fn, stmt);
kono
parents:
diff changeset
2811 }
kono
parents:
diff changeset
2812
kono
parents:
diff changeset
2813 /* Expand STMT, which is a call to internal function FN. */
kono
parents:
diff changeset
2814
kono
parents:
diff changeset
2815 void
kono
parents:
diff changeset
2816 expand_internal_call (gcall *stmt)
kono
parents:
diff changeset
2817 {
kono
parents:
diff changeset
2818 expand_internal_call (gimple_call_internal_fn (stmt), stmt);
kono
parents:
diff changeset
2819 }
kono
parents:
diff changeset
2820
kono
parents:
diff changeset
2821 void
kono
parents:
diff changeset
2822 expand_PHI (internal_fn, gcall *)
kono
parents:
diff changeset
2823 {
kono
parents:
diff changeset
2824 gcc_unreachable ();
kono
parents:
diff changeset
2825 }