1 /* brig-cmp-inst-handler.cc -- brig cmp instruction handling
2    Copyright (C) 2016-2020 Free Software Foundation, Inc.
3    Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
4    for General Processor Tech.
5 
6 This file is part of GCC.
7 
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
12 
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with GCC; see the file COPYING3.  If not see
20 <http://www.gnu.org/licenses/>.  */
21 
22 #include "brig-code-entry-handler.h"
23 #include "diagnostic.h"
24 #include "tree-pretty-print.h"
25 #include "print-tree.h"
26 #include "brig-util.h"
27 #include "convert.h"
28 
29 size_t
operator ()(const BrigBase * base)30 brig_cmp_inst_handler::operator () (const BrigBase *base)
31 {
32   const BrigInstBase *inst_base = (const BrigInstBase *) base;
33   const BrigInstCmp *inst = (const BrigInstCmp *) base;
34 
35   tree cmp_type = get_tree_expr_type_for_hsa_type (inst->sourceType);
36 
37   /* The destination type to convert the comparison result to.  */
38   tree dest_type = gccbrig_tree_type_for_hsa_type (inst_base->type);
39 
40   const bool is_fp16_dest
41     = (inst_base->type & BRIG_TYPE_BASE_MASK) == BRIG_TYPE_F16;
42   const bool is_boolean_dest
43     = (inst_base->type & BRIG_TYPE_BASE_MASK) == BRIG_TYPE_B1;
44 
45   bool is_int_cmp = VECTOR_TYPE_P (cmp_type)
46 		      ? INTEGRAL_TYPE_P (TREE_TYPE (cmp_type))
47 		      : INTEGRAL_TYPE_P (cmp_type);
48 
49   /* The type for the GENERIC comparison.  It should match the
50      input operand width for vector comparisons, a boolean
51      otherwise.  */
52   tree result_type = get_comparison_result_type (cmp_type);
53 
54   /* Save the result as a boolean and extend/convert it to the
55      wanted destination type.  */
56   tree expr = NULL_TREE;
57 
58   std::vector<tree> operands = build_operands (*inst_base);
59 
60   switch (inst->compare)
61     {
62     case BRIG_COMPARE_SEQ:
63     case BRIG_COMPARE_EQ:
64       expr = build2 (EQ_EXPR, result_type, operands[1], operands[2]);
65       break;
66     case BRIG_COMPARE_SNE:
67     case BRIG_COMPARE_NE:
68       expr = build2 (NE_EXPR, result_type, operands[1], operands[2]);
69 
70       if (!is_int_cmp)
71 	expr = build2 (BIT_AND_EXPR, TREE_TYPE (expr),
72 		       expr,
73 		       build2 (ORDERED_EXPR, result_type, operands[1],
74 			       operands[2]));
75       break;
76     case BRIG_COMPARE_SLT:
77     case BRIG_COMPARE_LT:
78       expr = build2 (LT_EXPR, result_type, operands[1], operands[2]);
79       break;
80     case BRIG_COMPARE_SLE:
81     case BRIG_COMPARE_LE:
82       expr = build2 (LE_EXPR, result_type, operands[1], operands[2]);
83       break;
84     case BRIG_COMPARE_SGT:
85     case BRIG_COMPARE_GT:
86       expr = build2 (GT_EXPR, result_type, operands[1], operands[2]);
87       break;
88     case BRIG_COMPARE_SGE:
89     case BRIG_COMPARE_GE:
90       expr = build2 (GE_EXPR, result_type, operands[1], operands[2]);
91       break;
92     case BRIG_COMPARE_SEQU:
93     case BRIG_COMPARE_EQU:
94       expr = build2 (UNEQ_EXPR, result_type, operands[1], operands[2]);
95       break;
96     case BRIG_COMPARE_SNEU:
97     case BRIG_COMPARE_NEU:
98       expr = build2 (NE_EXPR, result_type, operands[1], operands[2]);
99       break;
100     case BRIG_COMPARE_SLTU:
101     case BRIG_COMPARE_LTU:
102       expr = build2 (UNLT_EXPR, result_type, operands[1], operands[2]);
103       break;
104     case BRIG_COMPARE_SLEU:
105     case BRIG_COMPARE_LEU:
106       expr = build2 (UNLE_EXPR, result_type, operands[1], operands[2]);
107       break;
108     case BRIG_COMPARE_SGTU:
109     case BRIG_COMPARE_GTU:
110       expr = build2 (UNGT_EXPR, result_type, operands[1], operands[2]);
111       break;
112     case BRIG_COMPARE_SGEU:
113     case BRIG_COMPARE_GEU:
114       expr = build2 (UNGE_EXPR, result_type, operands[1], operands[2]);
115       break;
116     case BRIG_COMPARE_SNUM:
117     case BRIG_COMPARE_NUM:
118       expr = build2 (ORDERED_EXPR, result_type, operands[1], operands[2]);
119       break;
120     case BRIG_COMPARE_SNAN:
121     case BRIG_COMPARE_NAN:
122       expr = build2 (UNORDERED_EXPR, result_type, operands[1], operands[2]);
123       break;
124     default:
125       break;
126     }
127 
128   if (expr == NULL_TREE)
129     gcc_unreachable ();
130 
131   if (is_fp16_dest)
132     {
133       expr = convert_to_real (brig_to_generic::s_fp32_type, expr);
134     }
135   else if (VECTOR_TYPE_P (dest_type) && ANY_INTEGRAL_TYPE_P (dest_type)
136 	   && !is_boolean_dest
137 	   && (inst->sourceType & BRIG_TYPE_BASE_MASK) != BRIG_TYPE_F16)
138     {
139       /* In later gcc versions, the output of comparison is not
140 	 all ones for vectors like still in 4.9.1.  We need to use
141 	 an additional VEC_COND_EXPR to produce the all ones 'true' value
142 	 required by HSA.
143 	 VEC_COND_EXPR <a == b, { -1, -1, -1, -1 }, { 0, 0, 0, 0 }>; */
144 
145       tree all_ones
146 	= build_vector_from_val (dest_type,
147 			       build_minus_one_cst (TREE_TYPE (dest_type)));
148       tree all_zeroes
149 	= build_vector_from_val (dest_type,
150 			       build_zero_cst (TREE_TYPE (dest_type)));
151       expr = build3 (VEC_COND_EXPR, dest_type, expr, all_ones, all_zeroes);
152     }
153   else if (INTEGRAL_TYPE_P (dest_type) && !is_boolean_dest)
154     {
155       /* We need to produce the all-ones pattern for the width of the whole
156 	 resulting integer type.  Use back and forth shifts for propagating
157 	 the lower 1.  */
158       tree signed_type = signed_type_for (dest_type);
159       tree signed_result = convert_to_integer (signed_type, expr);
160 
161       size_t result_width = int_size_in_bytes (dest_type) * BITS_PER_UNIT;
162 
163       tree shift_amount_cst
164 	= build_int_cstu (signed_type, result_width - 1);
165 
166       tree shift_left_result
167 	= build2 (LSHIFT_EXPR, signed_type, signed_result, shift_amount_cst);
168 
169       expr = build2 (RSHIFT_EXPR, signed_type, shift_left_result,
170 		     shift_amount_cst);
171     }
172   else if (SCALAR_FLOAT_TYPE_P (dest_type))
173     {
174       expr = convert_to_real (dest_type, expr);
175     }
176   else if (VECTOR_TYPE_P (dest_type)
177 	   && (inst->sourceType & BRIG_TYPE_BASE_MASK) == BRIG_TYPE_F16)
178     {
179       /* Because F16 comparison is emulated as an F32 comparison with S32
180 	 results, we must now truncate the result vector to S16s so it
181 	 fits to the destination register.  We can build the target vector
182 	 type from the f16 storage type (unsigned ints).  */
183       expr = m_parent.m_cf->add_temp_var ("wide_cmp_result", expr);
184       tree_stl_vec wide_elements;
185       tree_stl_vec shrunk_elements;
186       m_parent.m_cf->unpack (expr, wide_elements);
187       for (size_t i = 0; i < wide_elements.size (); ++i)
188 	{
189 	  tree wide = wide_elements.at (i);
190 	  shrunk_elements.push_back
191 	    (convert_to_integer (short_integer_type_node, wide));
192 	}
193       expr = m_parent.m_cf->pack (shrunk_elements);
194     }
195   build_output_assignment (*inst_base, operands[0], expr);
196 
197   return base->byteCount;
198 }
199