blob: 2212151528eff172a4f6af3d06786d89b59df6d5 [file] [log] [blame]
Henry Herman6f037042021-09-01 23:15:40 +00001""" vec_test_helpers
2
3This module is for reusable helper functions used by the templates.
4"""
Henry Herman349c5332021-09-08 08:42:35 +00005import collections
6import enum
7import re
Henry Hermana217e142021-09-01 23:15:40 +00008import numpy as np
Henry Herman6f037042021-09-01 23:15:40 +00009
Henry Herman349c5332021-09-08 08:42:35 +000010class VecTemplateHelper:
11 """Given op and sew provide template with necessary parameters."""
12 class OperandType(enum.Enum):
13 """RISC-V V operand type options."""
14 VECTOR = enum.auto()
15 SCALAR = enum.auto()
16 IMMEDIATE = enum.auto()
Jieung Kim535870c2021-10-07 06:03:46 +000017 FLOATSCALAR = enum.auto()
Henry Herman6f037042021-09-01 23:15:40 +000018
Henry Herman349c5332021-09-08 08:42:35 +000019 class OperandWidth(enum.Enum):
20 """RISC-V V operand width type option."""
21 STANDARD = enum.auto()
22 WIDENING = enum.auto()
23 NARROWING = enum.auto()
Henry Herman6f037042021-09-01 23:15:40 +000024
Henry Herman349c5332021-09-08 08:42:35 +000025 mnemonic_suffix = {
26 OperandType.VECTOR : {
27 OperandWidth.STANDARD: "vv",
28 OperandWidth.WIDENING: "vv",
29 OperandWidth.NARROWING: "wv",
30 },
31 OperandType.SCALAR : {
32 OperandWidth.STANDARD: "vx",
33 OperandWidth.WIDENING: "vx",
34 OperandWidth.NARROWING: "wx",
35 },
36 OperandType.IMMEDIATE : {
37 OperandWidth.STANDARD: "vi",
38 OperandWidth.WIDENING: "vi",
39 OperandWidth.NARROWING: "wi",
40 },
Jieung Kim535870c2021-10-07 06:03:46 +000041 OperandType.FLOATSCALAR : {
42 OperandWidth.STANDARD: "vf",
43 OperandWidth.WIDENING: "vf",
44 OperandWidth.NARROWING: "wf",
45 },
Henry Herman349c5332021-09-08 08:42:35 +000046 }
Henry Herman6f037042021-09-01 23:15:40 +000047
Henry Herman349c5332021-09-08 08:42:35 +000048 """Helper class for providing params for use in templates"""
49 def __init__(self, op_code, sew=None):
50 self._op_code = op_code
51 self._sew = sew
52 self.force_unsigned = False
53 self.signed_np_types = {
54 8:np.int8,
55 16:np.int16,
56 32:np.int32}
57 self.unsigned_np_types = {
58 8:np.uint8,
59 16:np.uint16,
60 32:np.uint32}
Jieung Kim535870c2021-10-07 06:03:46 +000061 self.float_np_types = {
62 32:np.float32}
Henry Herman6f037042021-09-01 23:15:40 +000063
Henry Herman349c5332021-09-08 08:42:35 +000064 @property
65 def op_code(self):
66 """Return the op_code."""
67 if self._op_code is None:
Jieung Kim535870c2021-10-07 06:03:46 +000068 raise ValueError("OP CODE was not set.")
Henry Herman349c5332021-09-08 08:42:35 +000069 return self._op_code
Henry Herman6f037042021-09-01 23:15:40 +000070
Henry Herman349c5332021-09-08 08:42:35 +000071 @op_code.setter
72 def op_code(self, value):
73 """Set the op_code"""
74 self._op_code = value
Henry Herman6f037042021-09-01 23:15:40 +000075
Jieung Kim535870c2021-10-07 06:03:46 +000076 def is_floating(self):
77 """check if a particular op_code is a floating type."""
Jieung Kimc26fc732021-10-20 07:20:46 +000078 if self.op_code[1] == 'm':
79 return self.op_code[2] == 'f'
Jieung Kim535870c2021-10-07 06:03:46 +000080 return self.op_code[1] == 'f'
81
Henry Herman349c5332021-09-08 08:42:35 +000082 @property
83 def sew(self):
84 """Return the selected element width."""
85 if self._sew is None:
86 raise ValueError("SEW was not set.")
87 return self._sew
Henry Hermana217e142021-09-01 23:15:40 +000088
Henry Herman349c5332021-09-08 08:42:35 +000089 @sew.setter
90 def sew(self, value):
91 """Set the selected element width."""
Jieung Kim535870c2021-10-07 06:03:46 +000092 if self.is_floating():
93 if not (value == 32):
94 raise ValueError("Invalid SEW")
Henry Herman349c5332021-09-08 08:42:35 +000095 if not value in (8, 16, 32):
96 raise ValueError("Invalid SEW")
97 self._sew = value
98
99 def is_widening(self):
100 """Check if a particular op_code is a widening type."""
Jieung Kim535870c2021-10-07 06:03:46 +0000101 if self.is_floating():
102 raise ValueError("Widening is not supported with this template for floating point values")
Henry Herman349c5332021-09-08 08:42:35 +0000103 return self.op_code[1] == 'w'
104
105 def is_narrowing(self):
106 """Check if a particular op_code is a narrowing type."""
Jieung Kim535870c2021-10-07 06:03:46 +0000107 if self.is_floating():
108 raise ValueError("Narrowing is not supported with this template for floating point values")
Henry Herman349c5332021-09-08 08:42:35 +0000109 return self.op_code[1] == 'n'
110
Henry Herman40976a72021-09-12 07:15:51 +0000111 def is_destination_mask_register(self):
112 """Check if a particular op_code has a mask output."""
Jieung Kim535870c2021-10-07 06:03:46 +0000113 if self.is_floating():
Jieung Kimc26fc732021-10-20 07:20:46 +0000114 comparison_ops = ('vmfeq', 'vmfle', 'vmflt', 'vmfne', 'vmfgt', 'vmfge')
115 else:
116 comparison_ops = ('vmseq', 'vmsne', 'vmsltu', 'vmsleu', 'vmsle', 'vmsgtu', 'vmsgt')
117 return self.op_code in comparison_ops
Henry Herman40976a72021-09-12 07:15:51 +0000118
Henry Herman349c5332021-09-08 08:42:35 +0000119 def is_unsigned(self):
120 """Check if a particular op_code is a unsigned type."""
Jieung Kim535870c2021-10-07 06:03:46 +0000121 if self.is_floating():
122 raise ValueError("Invalid unsigned type op code")
Henry Herman349c5332021-09-08 08:42:35 +0000123 return self.op_code[-1] == 'u'
124
125 def get_sews(self):
126 """Given an op_code return a list of valid element widths."""
Jieung Kim535870c2021-10-07 06:03:46 +0000127 if self.is_floating():
128 return [32]
129
Henry Herman349c5332021-09-08 08:42:35 +0000130 if self.is_widening() or self.is_narrowing():
131 return [8, 16]
132 return [8, 16, 32]
133
134 def get_sew_sizes(self):
Jieung Kim535870c2021-10-07 06:03:46 +0000135 """Return size of types.
136 imm is not used for floating point op codes.
137 """
Henry Herman349c5332021-09-08 08:42:35 +0000138 dest_sew = self.sew
139 src2_sew = self.sew
140 src1_sew = self.sew
141 imm_sew = self.sew
Jieung Kim535870c2021-10-07 06:03:46 +0000142 if not self.is_floating() and self.is_narrowing():
Henry Herman349c5332021-09-08 08:42:35 +0000143 src2_sew = self.sew * 2
Jieung Kim535870c2021-10-07 06:03:46 +0000144 elif not self.is_floating() and self.is_widening():
Henry Herman349c5332021-09-08 08:42:35 +0000145 dest_sew = self.sew * 2
146 return dest_sew, src2_sew, src1_sew, imm_sew
147
148 def get_var_types(self):
Jieung Kim535870c2021-10-07 06:03:46 +0000149 """Return types for an op_code and element width.
150 imm_type won't be used for floating point values.
151 """
Henry Herman349c5332021-09-08 08:42:35 +0000152 VarTypes = collections.namedtuple(
153 "VarTypes",
154 ('dest_type', 'src2_type', 'src1_type', 'imm_type'))
Jieung Kim535870c2021-10-07 06:03:46 +0000155 if self.is_floating():
Jieung Kimc26fc732021-10-20 07:20:46 +0000156 """dest_type is defined as int32_t instead of float
157 to use "set_bit_in_dest_mask" (in softrvv/include/softrvv_internal.h)
158 , which has bitwise operations in it.
159 """
160 if self.is_destination_mask_register():
161 var_types = VarTypes("int32_t", "float", "float", "float")
162 else:
163 var_types = VarTypes("float", "float", "float", "float")
Jieung Kim535870c2021-10-07 06:03:46 +0000164 else:
165 type_fmt = "%sint%d_t"
166 sign_type = "u" if self.is_unsigned() or self.force_unsigned else ""
167 dest_sew, src2_sew, src1_sew, imm_sew = self.get_sew_sizes()
168 dest_type = type_fmt % (sign_type, dest_sew)
169 src1_type = type_fmt % (sign_type, src1_sew)
170 src2_type = type_fmt % (sign_type, src2_sew)
171 imm_type = type_fmt % (sign_type, imm_sew)
172 var_types = VarTypes(dest_type, src2_type, src1_type, imm_type)
Henry Herman349c5332021-09-08 08:42:35 +0000173 return var_types
174
175 def get_mnemonic(self, operand_type):
176 """Generate the correct mnemonic given a opcode and operand type."""
177 operand_width = self.OperandWidth.STANDARD
Jieung Kim535870c2021-10-07 06:03:46 +0000178 if self.is_floating():
179 if operand_type == self.OperandType.SCALAR:
180 operand_type = self.OperandType.FLOATSCALAR
181 else:
182 if self.is_narrowing():
183 operand_width = self.OperandWidth.NARROWING
184 elif self.is_widening():
185 operand_width = self.OperandWidth.WIDENING
Henry Herman349c5332021-09-08 08:42:35 +0000186 op_suffix = self.mnemonic_suffix[operand_type][operand_width]
187 return "%s.%s" % (self.op_code, op_suffix)
188
189 def get_lmuls(self):
190 """Given an op_code return an iterable of valid lmuls."""
Jieung Kim535870c2021-10-07 06:03:46 +0000191 if not self.is_floating():
192 if self.is_widening() or self.is_narrowing():
193 return [1, 2, 4]
Henry Herman349c5332021-09-08 08:42:35 +0000194 return [1, 2, 4, 8]
195
196 @staticmethod
197 def get_sew_from_dtype(dtype):
198 """Extract the selected element width from a data type."""
199 match = re.match(r"[a-z]+(?P<sew>[\d]+)", dtype)
200 return int(match['sew'])
201
202 def get_softrvv_template_data_type(self):
203 """Return types """
204 var_types = self.get_var_types()
Jieung Kim535870c2021-10-07 06:03:46 +0000205 if not self.is_floating():
206 if self.is_narrowing() or self.is_widening():
207 return "%s, %s" % (var_types.dest_type, var_types.src2_type)
Henry Herman349c5332021-09-08 08:42:35 +0000208 return var_types.src1_type
209
210 def get_ref_opcode(self):
211 """Return the name of the reference code in the softrvv library."""
Jieung Kim535870c2021-10-07 06:03:46 +0000212 if self.is_floating():
213 return self.op_code
Henry Herman349c5332021-09-08 08:42:35 +0000214 return self.op_code[:-1] if self.is_unsigned() else self.op_code
215
216 def get_imms(self):
217 """Return a list of valid immediate values for a op code."""
Jieung Kim535870c2021-10-07 06:03:46 +0000218 if self.is_floating():
219 raise ValueError("imm is not available.")
Henry Herman349c5332021-09-08 08:42:35 +0000220 if self.op_code in ['vsll', 'vsrl', 'vsra', 'vnsrl', 'vnsra']:
221 # Left and right shift immediates must be [0,31]
222 return np.linspace(0, 31, 8, dtype=np.int32)
Henry Hermana217e142021-09-01 23:15:40 +0000223 # Immediate values must be [-16, 15]
224 return np.linspace(-16, 15, 7, dtype=np.int32)
225
Henry Herman349c5332021-09-08 08:42:35 +0000226 def get_np_dest_type(self):
227 """Return numpy type for destination."""
Jieung Kim535870c2021-10-07 06:03:46 +0000228 if self.is_floating():
229 return self.float_np_types[self.sew]
Henry Herman349c5332021-09-08 08:42:35 +0000230 if self.force_unsigned:
231 types = self.unsigned_np_types
232 else:
233 types = self.signed_np_types
234 if self.is_widening():
235 return types[self.sew * 2]
236 return types[self.sew]
237
238 def get_np_src1_type(self):
239 """Return numpy type for src1."""
Jieung Kim535870c2021-10-07 06:03:46 +0000240 if self.is_floating():
241 return self.float_np_types[self.sew]
Henry Herman349c5332021-09-08 08:42:35 +0000242 if self.force_unsigned:
243 types = self.unsigned_np_types
244 else:
245 types = self.signed_np_types
246 return types[self.sew]
247
248 def get_np_src2_type(self):
249 """Return numpy type for src2."""
Jieung Kim535870c2021-10-07 06:03:46 +0000250 if self.is_floating():
251 return self.float_np_types[self.sew]
Henry Herman349c5332021-09-08 08:42:35 +0000252 if self.force_unsigned:
253 types = self.unsigned_np_types
254 else:
255 types = self.signed_np_types
256 if self.is_narrowing():
257 return types[self.sew * 2]
258 return types[self.sew]
259
260 def get_test_inputs(self, n=5, allow_zero=True): # pylint: disable=invalid-name
261 """Return test inputs."""
262 src1_np_type = self.get_np_src1_type()
263 src2_np_type = self.get_np_src2_type()
Jieung Kim535870c2021-10-07 06:03:46 +0000264 src2_np_type = self.get_np_src2_type()
265 # Return test inputs for floating points.
266 if self.is_floating():
267 type_info = np.finfo(src1_np_type)
268 src1_data = np.random.uniform(
269 type_info.min, type_info.max, n).astype(src1_np_type)
270 rs1 = self.get_np_src1_type()(np.random.uniform(
271 type_info.min, type_info.max))
272 type_info = np.finfo(src2_np_type)
273 src2_data = np.random.uniform(
274 type_info.min, type_info.max, n).astype(src2_np_type)
275 if not allow_zero:
276 src2_data[src2_data==0] = 1
277 rs1 = 1.0 if rs1 == 0.0 else rs1
278 return src2_data, src1_data, rs1
279
280 # Return test inputs for integers.
Henry Herman349c5332021-09-08 08:42:35 +0000281 type_info = np.iinfo(src1_np_type)
282 src1_data = np.random.randint(
283 type_info.min, type_info.max, n).astype(src1_np_type)
284 rs1 = self.get_np_src1_type()(np.random.randint(
285 type_info.min, type_info.max))
Henry Herman349c5332021-09-08 08:42:35 +0000286 type_info = np.iinfo(src2_np_type)
287 src2_data = np.random.randint(
288 type_info.min, type_info.max, n).astype(src2_np_type)
289 if not allow_zero:
290 src2_data[src2_data==0] = 1
291 rs1 = 1 if rs1 == 0 else rs1
292 return src2_data, src1_data, rs1
293
Henry Herman40976a72021-09-12 07:15:51 +0000294 def pack_dest_mask(self, values):
Jieung Kim535870c2021-10-07 06:03:46 +0000295 """Pack values into a single destination register."""
296 dest_type = self.get_np_dest_type()
297 return np.packbits(dest_type(values), bitorder='little')
Henry Herman40976a72021-09-12 07:15:51 +0000298
Henry Herman349c5332021-09-08 08:42:35 +0000299def cast_to_unsigned(arr):
Jieung Kim535870c2021-10-07 06:03:46 +0000300 """Cast a signed array to an unsigned array.
301 This should not be called with floating point values.
302 """
Henry Herman349c5332021-09-08 08:42:35 +0000303 udtypes = {np.int8:np.uint8,
304 np.int16:np.uint16,
305 np.int32:np.uint32,
306 np.int64:np.uint64}
307 if not arr.dtype.type in udtypes.keys():
308 raise TypeError
309 return arr.astype(udtypes[arr.dtype.type])
310
311def to_carr_str(arr):
312 """Simple function for turn array into comma separated list."""
313 return ", ".join(("%s" % x for x in arr))