xref: /dragonfly/contrib/gcc-8.0/gcc/ccmp.c (revision ed183f8c)
1 /* Conditional compare related functions
2    Copyright (C) 2014-2018 Free Software Foundation, Inc.
3 
4 This file is part of GCC.
5 
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10 
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3.  If not see
18 <http://www.gnu.org/licenses/>.  */
19 
20 #include "config.h"
21 #include "system.h"
22 #include "coretypes.h"
23 #include "backend.h"
24 #include "target.h"
25 #include "rtl.h"
26 #include "tree.h"
27 #include "gimple.h"
28 #include "memmodel.h"
29 #include "tm_p.h"
30 #include "ssa.h"
31 #include "expmed.h"
32 #include "optabs.h"
33 #include "emit-rtl.h"
34 #include "stor-layout.h"
35 #include "tree-ssa-live.h"
36 #include "tree-outof-ssa.h"
37 #include "cfgexpand.h"
38 #include "ccmp.h"
39 #include "predict.h"
40 
41 /* Check whether T is a simple boolean variable or a SSA name
42    set by a comparison operator in the same basic block.  */
43 static bool
44 ccmp_tree_comparison_p (tree t, basic_block bb)
45 {
46   gimple *g = get_gimple_for_ssa_name (t);
47   tree_code tcode;
48 
49   /* If we have a boolean variable allow it and generate a compare
50      to zero reg when expanding.  */
51   if (!g)
52     return (TREE_CODE (TREE_TYPE (t)) == BOOLEAN_TYPE);
53 
54   /* Check to see if SSA name is set by a comparison operator in
55      the same basic block.  */
56   if (!is_gimple_assign (g))
57     return false;
58   if (bb != gimple_bb (g))
59     return false;
60   tcode = gimple_assign_rhs_code (g);
61   return TREE_CODE_CLASS (tcode) == tcc_comparison;
62 }
63 
64 /* The following functions expand conditional compare (CCMP) instructions.
65    Here is a short description about the over all algorithm:
66      * ccmp_candidate_p is used to identify the CCMP candidate
67 
68      * expand_ccmp_expr is the main entry, which calls expand_ccmp_expr_1
69        to expand CCMP.
70 
71      * expand_ccmp_expr_1 uses a recursive algorithm to expand CCMP.
72        It calls two target hooks gen_ccmp_first and gen_ccmp_next to generate
73        CCMP instructions.
74 	 - gen_ccmp_first expands the first compare in CCMP.
75 	 - gen_ccmp_next expands the following compares.
76 
77        Both hooks return a comparison with the CC register that is equivalent
78        to the value of the gimple comparison.  This is used by the next CCMP
79        and in the final conditional store.
80 
81      * We use cstorecc4 pattern to convert the CCmode intermediate to
82        the integer mode result that expand_normal is expecting.
83 
84    Since the operands of the later compares might clobber CC reg, we do not
85    emit the insns during expand.  We keep the insn sequences in two seq
86 
87      * prep_seq, which includes all the insns to prepare the operands.
88      * gen_seq, which includes all the compare and conditional compares.
89 
90    If all checks OK in expand_ccmp_expr, it emits insns in prep_seq, then
91    insns in gen_seq.  */
92 
93 /* Check whether G is a potential conditional compare candidate.  */
94 static bool
95 ccmp_candidate_p (gimple *g)
96 {
97   tree rhs;
98   tree lhs, op0, op1;
99   gimple *gs0, *gs1;
100   tree_code tcode;
101   basic_block bb;
102 
103   if (!g)
104     return false;
105 
106   rhs = gimple_assign_rhs_to_tree (g);
107   tcode = TREE_CODE (rhs);
108   if (tcode != BIT_AND_EXPR && tcode != BIT_IOR_EXPR)
109     return false;
110 
111   lhs = gimple_assign_lhs (g);
112   op0 = TREE_OPERAND (rhs, 0);
113   op1 = TREE_OPERAND (rhs, 1);
114   bb = gimple_bb (g);
115 
116   if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME)
117       || !has_single_use (lhs))
118     return false;
119 
120   gs0 = get_gimple_for_ssa_name (op0); /* gs0 may be NULL */
121   gs1 = get_gimple_for_ssa_name (op1); /* gs1 may be NULL */
122 
123   if (ccmp_tree_comparison_p (op0, bb) && ccmp_tree_comparison_p (op1, bb))
124     return true;
125   if (ccmp_tree_comparison_p (op0, bb) && ccmp_candidate_p (gs1))
126     return true;
127   if (ccmp_tree_comparison_p (op1, bb) && ccmp_candidate_p (gs0))
128     return true;
129   /* We skip ccmp_candidate_p (gs1) && ccmp_candidate_p (gs0) since
130      there is no way to set and maintain the CC flag on both sides of
131      the logical operator at the same time.  */
132   return false;
133 }
134 
135 /* Extract the comparison we want to do from the tree.  */
136 void
137 get_compare_parts (tree t, int *up, rtx_code *rcode,
138 		   tree *rhs1, tree *rhs2)
139 {
140   tree_code code;
141   gimple *g = get_gimple_for_ssa_name (t);
142   if (g)
143     {
144       *up = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (g)));
145       code = gimple_assign_rhs_code (g);
146       *rcode = get_rtx_code (code, *up);
147       *rhs1 = gimple_assign_rhs1 (g);
148       *rhs2 = gimple_assign_rhs2 (g);
149     }
150   else
151     {
152       /* If g is not a comparison operator create a compare to zero.  */
153       *up = 1;
154       *rcode = NE;
155       *rhs1 = t;
156       *rhs2 = build_zero_cst (TREE_TYPE (t));
157     }
158 }
159 
160 /* PREV is a comparison with the CC register which represents the
161    result of the previous CMP or CCMP.  The function expands the
162    next compare based on G which is ANDed/ORed with the previous
163    compare depending on CODE.
164    PREP_SEQ returns all insns to prepare opearands for compare.
165    GEN_SEQ returns all compare insns.  */
166 static rtx
167 expand_ccmp_next (tree op, tree_code code, rtx prev,
168 		  rtx_insn **prep_seq, rtx_insn **gen_seq)
169 {
170   rtx_code rcode;
171   int unsignedp;
172   tree rhs1, rhs2;
173 
174   get_compare_parts(op, &unsignedp, &rcode, &rhs1, &rhs2);
175   return targetm.gen_ccmp_next (prep_seq, gen_seq, prev, rcode,
176 				rhs1, rhs2, get_rtx_code (code, 0));
177 }
178 
179 /* Expand conditional compare gimple G.  A typical CCMP sequence is like:
180 
181      CC0 = CMP (a, b);
182      CC1 = CCMP (NE (CC0, 0), CMP (e, f));
183      ...
184      CCn = CCMP (NE (CCn-1, 0), CMP (...));
185 
186    hook gen_ccmp_first is used to expand the first compare.
187    hook gen_ccmp_next is used to expand the following CCMP.
188    PREP_SEQ returns all insns to prepare opearand.
189    GEN_SEQ returns all compare insns.  */
190 static rtx
191 expand_ccmp_expr_1 (gimple *g, rtx_insn **prep_seq, rtx_insn **gen_seq)
192 {
193   tree exp = gimple_assign_rhs_to_tree (g);
194   tree_code code = TREE_CODE (exp);
195   basic_block bb = gimple_bb (g);
196 
197   tree op0 = TREE_OPERAND (exp, 0);
198   tree op1 = TREE_OPERAND (exp, 1);
199   gimple *gs0 = get_gimple_for_ssa_name (op0);
200   gimple *gs1 = get_gimple_for_ssa_name (op1);
201   rtx tmp;
202 
203   gcc_assert (code == BIT_AND_EXPR || code == BIT_IOR_EXPR);
204 
205   if (ccmp_tree_comparison_p (op0, bb))
206     {
207       if (ccmp_tree_comparison_p (op1, bb))
208 	{
209 	  int unsignedp0, unsignedp1;
210 	  rtx_code rcode0, rcode1;
211 	  tree logical_op0_rhs1, logical_op0_rhs2;
212 	  tree logical_op1_rhs1, logical_op1_rhs2;
213 	  int speed_p = optimize_insn_for_speed_p ();
214 
215 	  rtx tmp2 = NULL_RTX, ret = NULL_RTX, ret2 = NULL_RTX;
216 	  unsigned cost1 = MAX_COST;
217 	  unsigned cost2 = MAX_COST;
218 
219 	  get_compare_parts (op0, &unsignedp0, &rcode0,
220 			     &logical_op0_rhs1, &logical_op0_rhs2);
221 
222 	  get_compare_parts (op1, &unsignedp1, &rcode1,
223 			     &logical_op1_rhs1, &logical_op1_rhs2);
224 
225 	  rtx_insn *prep_seq_1, *gen_seq_1;
226 	  tmp = targetm.gen_ccmp_first (&prep_seq_1, &gen_seq_1, rcode0,
227 					logical_op0_rhs1, logical_op0_rhs2);
228 	  if (tmp != NULL)
229 	    {
230 	      ret = expand_ccmp_next (op1, code, tmp, &prep_seq_1, &gen_seq_1);
231 	      cost1 = seq_cost (prep_seq_1, speed_p);
232 	      cost1 += seq_cost (gen_seq_1, speed_p);
233 	    }
234 
235 	  /* FIXME: Temporary workaround for PR69619.
236 	     Avoid exponential compile time due to expanding gs0 and gs1 twice.
237 	     If gs0 and gs1 are complex, the cost will be high, so avoid
238 	     reevaluation if above an arbitrary threshold.  */
239 	  rtx_insn *prep_seq_2, *gen_seq_2;
240 	  if (tmp == NULL || cost1 < COSTS_N_INSNS (25))
241 	    tmp2 = targetm.gen_ccmp_first (&prep_seq_2, &gen_seq_2, rcode1,
242 					   logical_op1_rhs1, logical_op1_rhs2);
243 	  if (!tmp && !tmp2)
244 	    return NULL_RTX;
245 	  if (tmp2 != NULL)
246 	    {
247 	      ret2 = expand_ccmp_next (op0, code, tmp2, &prep_seq_2,
248 				       &gen_seq_2);
249 	      cost2 = seq_cost (prep_seq_2, speed_p);
250 	      cost2 += seq_cost (gen_seq_2, speed_p);
251 	    }
252 	  if (cost2 < cost1)
253 	    {
254 	      *prep_seq = prep_seq_2;
255 	      *gen_seq = gen_seq_2;
256 	      return ret2;
257 	    }
258 	  *prep_seq = prep_seq_1;
259 	  *gen_seq = gen_seq_1;
260 	  return ret;
261 	}
262       else
263 	{
264 	  tmp = expand_ccmp_expr_1 (gs1, prep_seq, gen_seq);
265 	  if (!tmp)
266 	    return NULL_RTX;
267 	  return expand_ccmp_next (op0, code, tmp, prep_seq, gen_seq);
268 	}
269     }
270   else
271     {
272       gcc_assert (gimple_assign_rhs_code (gs0) == BIT_AND_EXPR
273                   || gimple_assign_rhs_code (gs0) == BIT_IOR_EXPR);
274       gcc_assert (ccmp_tree_comparison_p (op1, bb));
275       tmp = expand_ccmp_expr_1 (gs0, prep_seq, gen_seq);
276       if (!tmp)
277 	return NULL_RTX;
278       return expand_ccmp_next (op1, code, tmp, prep_seq, gen_seq);
279     }
280 
281   return NULL_RTX;
282 }
283 
284 /* Main entry to expand conditional compare statement G.
285    Return NULL_RTX if G is not a legal candidate or expand fail.
286    Otherwise return the target.  */
287 rtx
288 expand_ccmp_expr (gimple *g, machine_mode mode)
289 {
290   rtx_insn *last;
291   rtx tmp;
292 
293   if (!ccmp_candidate_p (g))
294     return NULL_RTX;
295 
296   last = get_last_insn ();
297 
298   rtx_insn *prep_seq = NULL, *gen_seq = NULL;
299   tmp = expand_ccmp_expr_1 (g, &prep_seq, &gen_seq);
300 
301   if (tmp)
302     {
303       insn_code icode;
304       machine_mode cc_mode = CCmode;
305       rtx_code cmp_code = GET_CODE (tmp);
306 
307 #ifdef SELECT_CC_MODE
308       cc_mode = SELECT_CC_MODE (cmp_code, XEXP (tmp, 0), const0_rtx);
309 #endif
310       icode = optab_handler (cstore_optab, cc_mode);
311       if (icode != CODE_FOR_nothing)
312 	{
313 	  rtx target = gen_reg_rtx (mode);
314 
315 	  emit_insn (prep_seq);
316 	  emit_insn (gen_seq);
317 
318 	  tmp = emit_cstore (target, icode, cmp_code, cc_mode, cc_mode,
319 			     0, XEXP (tmp, 0), const0_rtx, 1, mode);
320 	  if (tmp)
321 	    return tmp;
322 	}
323     }
324   /* Clean up.  */
325   delete_insns_since (last);
326   return NULL_RTX;
327 }
328