summaryrefslogtreecommitdiff
path: root/gcc/brig/brigfrontend/brig-cmp-inst-handler.cc
blob: 5494d09eb8267c71c1da93079c9cb6e4f113a259 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* brig-cmp-inst-handler.cc -- brig cmp instruction handling
   Copyright (C) 2016-2019 Free Software Foundation, Inc.
   Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
   for General Processor Tech.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "brig-code-entry-handler.h"
#include "diagnostic.h"
#include "tree-pretty-print.h"
#include "print-tree.h"
#include "brig-util.h"
#include "convert.h"

size_t
brig_cmp_inst_handler::operator () (const BrigBase *base)
{
  const BrigInstBase *inst_base = (const BrigInstBase *) base;
  const BrigInstCmp *inst = (const BrigInstCmp *) base;

  tree cmp_type = get_tree_expr_type_for_hsa_type (inst->sourceType);

  /* The destination type to convert the comparison result to.  */
  tree dest_type = gccbrig_tree_type_for_hsa_type (inst_base->type);

  const bool is_fp16_dest
    = (inst_base->type & BRIG_TYPE_BASE_MASK) == BRIG_TYPE_F16;
  const bool is_boolean_dest
    = (inst_base->type & BRIG_TYPE_BASE_MASK) == BRIG_TYPE_B1;

  bool is_int_cmp = VECTOR_TYPE_P (cmp_type)
		      ? INTEGRAL_TYPE_P (TREE_TYPE (cmp_type))
		      : INTEGRAL_TYPE_P (cmp_type);

  /* The type for the GENERIC comparison.  It should match the
     input operand width for vector comparisons, a boolean
     otherwise.  */
  tree result_type = get_comparison_result_type (cmp_type);

  /* Save the result as a boolean and extend/convert it to the
     wanted destination type.  */
  tree expr = NULL_TREE;

  std::vector<tree> operands = build_operands (*inst_base);

  switch (inst->compare)
    {
    case BRIG_COMPARE_SEQ:
    case BRIG_COMPARE_EQ:
      expr = build2 (EQ_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SNE:
    case BRIG_COMPARE_NE:
      expr = build2 (NE_EXPR, result_type, operands[1], operands[2]);

      if (!is_int_cmp)
	expr = build2 (BIT_AND_EXPR, TREE_TYPE (expr),
		       expr,
		       build2 (ORDERED_EXPR, result_type, operands[1],
			       operands[2]));
      break;
    case BRIG_COMPARE_SLT:
    case BRIG_COMPARE_LT:
      expr = build2 (LT_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SLE:
    case BRIG_COMPARE_LE:
      expr = build2 (LE_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SGT:
    case BRIG_COMPARE_GT:
      expr = build2 (GT_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SGE:
    case BRIG_COMPARE_GE:
      expr = build2 (GE_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SEQU:
    case BRIG_COMPARE_EQU:
      expr = build2 (UNEQ_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SNEU:
    case BRIG_COMPARE_NEU:
      expr = build2 (NE_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SLTU:
    case BRIG_COMPARE_LTU:
      expr = build2 (UNLT_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SLEU:
    case BRIG_COMPARE_LEU:
      expr = build2 (UNLE_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SGTU:
    case BRIG_COMPARE_GTU:
      expr = build2 (UNGT_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SGEU:
    case BRIG_COMPARE_GEU:
      expr = build2 (UNGE_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SNUM:
    case BRIG_COMPARE_NUM:
      expr = build2 (ORDERED_EXPR, result_type, operands[1], operands[2]);
      break;
    case BRIG_COMPARE_SNAN:
    case BRIG_COMPARE_NAN:
      expr = build2 (UNORDERED_EXPR, result_type, operands[1], operands[2]);
      break;
    default:
      break;
    }

  if (expr == NULL_TREE)
    gcc_unreachable ();

  if (is_fp16_dest)
    {
      expr = convert_to_real (brig_to_generic::s_fp32_type, expr);
    }
  else if (VECTOR_TYPE_P (dest_type) && ANY_INTEGRAL_TYPE_P (dest_type)
	   && !is_boolean_dest
	   && (inst->sourceType & BRIG_TYPE_BASE_MASK) != BRIG_TYPE_F16)
    {
      /* In later gcc versions, the output of comparison is not
	 all ones for vectors like still in 4.9.1.  We need to use
	 an additional VEC_COND_EXPR to produce the all ones 'true' value
	 required by HSA.
	 VEC_COND_EXPR <a == b, { -1, -1, -1, -1 }, { 0, 0, 0, 0 }>; */

      tree all_ones
	= build_vector_from_val (dest_type,
			       build_minus_one_cst (TREE_TYPE (dest_type)));
      tree all_zeroes
	= build_vector_from_val (dest_type,
			       build_zero_cst (TREE_TYPE (dest_type)));
      expr = build3 (VEC_COND_EXPR, dest_type, expr, all_ones, all_zeroes);
    }
  else if (INTEGRAL_TYPE_P (dest_type) && !is_boolean_dest)
    {
      /* We need to produce the all-ones pattern for the width of the whole
	 resulting integer type.  Use back and forth shifts for propagating
	 the lower 1.  */
      tree signed_type = signed_type_for (dest_type);
      tree signed_result = convert_to_integer (signed_type, expr);

      size_t result_width = int_size_in_bytes (dest_type) * BITS_PER_UNIT;

      tree shift_amount_cst
	= build_int_cstu (signed_type, result_width - 1);

      tree shift_left_result
	= build2 (LSHIFT_EXPR, signed_type, signed_result, shift_amount_cst);

      expr = build2 (RSHIFT_EXPR, signed_type, shift_left_result,
		     shift_amount_cst);
    }
  else if (SCALAR_FLOAT_TYPE_P (dest_type))
    {
      expr = convert_to_real (dest_type, expr);
    }
  else if (VECTOR_TYPE_P (dest_type)
	   && (inst->sourceType & BRIG_TYPE_BASE_MASK) == BRIG_TYPE_F16)
    {
      /* Because F16 comparison is emulated as an F32 comparison with S32
	 results, we must now truncate the result vector to S16s so it
	 fits to the destination register.  We can build the target vector
	 type from the f16 storage type (unsigned ints).  */
      expr = m_parent.m_cf->add_temp_var ("wide_cmp_result", expr);
      tree_stl_vec wide_elements;
      tree_stl_vec shrunk_elements;
      m_parent.m_cf->unpack (expr, wide_elements);
      for (size_t i = 0; i < wide_elements.size (); ++i)
	{
	  tree wide = wide_elements.at (i);
	  shrunk_elements.push_back
	    (convert_to_integer (short_integer_type_node, wide));
	}
      expr = m_parent.m_cf->pack (shrunk_elements);
    }
  build_output_assignment (*inst_base, operands[0], expr);

  return base->byteCount;
}