Henry Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 1 | """ vec_test_helpers |
| 2 | |
| 3 | This module is for reusable helper functions used by the templates. |
| 4 | """ |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 5 | import collections |
| 6 | import enum |
| 7 | import re |
Henry Herman | a217e14 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 8 | import numpy as np |
Henry Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 9 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 10 | class 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 17 | FLOATSCALAR = enum.auto() |
Henry Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 18 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 19 | 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 Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 24 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 25 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 41 | OperandType.FLOATSCALAR : { |
| 42 | OperandWidth.STANDARD: "vf", |
| 43 | OperandWidth.WIDENING: "vf", |
| 44 | OperandWidth.NARROWING: "wf", |
| 45 | }, |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 46 | } |
Henry Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 47 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 48 | """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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 61 | self.float_np_types = { |
| 62 | 32:np.float32} |
Henry Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 63 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 64 | @property |
| 65 | def op_code(self): |
| 66 | """Return the op_code.""" |
| 67 | if self._op_code is None: |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 68 | raise ValueError("OP CODE was not set.") |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 69 | return self._op_code |
Henry Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 70 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 71 | @op_code.setter |
| 72 | def op_code(self, value): |
| 73 | """Set the op_code""" |
| 74 | self._op_code = value |
Henry Herman | 6f03704 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 75 | |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 76 | def is_floating(self): |
| 77 | """check if a particular op_code is a floating type.""" |
Jieung Kim | c26fc73 | 2021-10-20 07:20:46 +0000 | [diff] [blame] | 78 | if self.op_code[1] == 'm': |
| 79 | return self.op_code[2] == 'f' |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 80 | return self.op_code[1] == 'f' |
| 81 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 82 | @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 Herman | a217e14 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 88 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 89 | @sew.setter |
| 90 | def sew(self, value): |
| 91 | """Set the selected element width.""" |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 92 | if self.is_floating(): |
| 93 | if not (value == 32): |
| 94 | raise ValueError("Invalid SEW") |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 95 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 101 | if self.is_floating(): |
| 102 | raise ValueError("Widening is not supported with this template for floating point values") |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 103 | return self.op_code[1] == 'w' |
| 104 | |
| 105 | def is_narrowing(self): |
| 106 | """Check if a particular op_code is a narrowing type.""" |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 107 | if self.is_floating(): |
| 108 | raise ValueError("Narrowing is not supported with this template for floating point values") |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 109 | return self.op_code[1] == 'n' |
| 110 | |
Henry Herman | 40976a7 | 2021-09-12 07:15:51 +0000 | [diff] [blame] | 111 | def is_destination_mask_register(self): |
| 112 | """Check if a particular op_code has a mask output.""" |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 113 | if self.is_floating(): |
Jieung Kim | c26fc73 | 2021-10-20 07:20:46 +0000 | [diff] [blame] | 114 | 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 Herman | 40976a7 | 2021-09-12 07:15:51 +0000 | [diff] [blame] | 118 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 119 | def is_unsigned(self): |
| 120 | """Check if a particular op_code is a unsigned type.""" |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 121 | if self.is_floating(): |
| 122 | raise ValueError("Invalid unsigned type op code") |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 123 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 127 | if self.is_floating(): |
| 128 | return [32] |
| 129 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 130 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 135 | """Return size of types. |
| 136 | imm is not used for floating point op codes. |
| 137 | """ |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 138 | dest_sew = self.sew |
| 139 | src2_sew = self.sew |
| 140 | src1_sew = self.sew |
| 141 | imm_sew = self.sew |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 142 | if not self.is_floating() and self.is_narrowing(): |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 143 | src2_sew = self.sew * 2 |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 144 | elif not self.is_floating() and self.is_widening(): |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 145 | dest_sew = self.sew * 2 |
| 146 | return dest_sew, src2_sew, src1_sew, imm_sew |
| 147 | |
| 148 | def get_var_types(self): |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 149 | """Return types for an op_code and element width. |
| 150 | imm_type won't be used for floating point values. |
| 151 | """ |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 152 | VarTypes = collections.namedtuple( |
| 153 | "VarTypes", |
| 154 | ('dest_type', 'src2_type', 'src1_type', 'imm_type')) |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 155 | if self.is_floating(): |
Jieung Kim | c26fc73 | 2021-10-20 07:20:46 +0000 | [diff] [blame] | 156 | """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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 164 | 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 Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 173 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 178 | 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 Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 186 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 191 | if not self.is_floating(): |
| 192 | if self.is_widening() or self.is_narrowing(): |
| 193 | return [1, 2, 4] |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 194 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 205 | 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 Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 208 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 212 | if self.is_floating(): |
| 213 | return self.op_code |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 214 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 218 | if self.is_floating(): |
| 219 | raise ValueError("imm is not available.") |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 220 | 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 Herman | a217e14 | 2021-09-01 23:15:40 +0000 | [diff] [blame] | 223 | # Immediate values must be [-16, 15] |
| 224 | return np.linspace(-16, 15, 7, dtype=np.int32) |
| 225 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 226 | def get_np_dest_type(self): |
| 227 | """Return numpy type for destination.""" |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 228 | if self.is_floating(): |
| 229 | return self.float_np_types[self.sew] |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 230 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 240 | if self.is_floating(): |
| 241 | return self.float_np_types[self.sew] |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 242 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 250 | if self.is_floating(): |
| 251 | return self.float_np_types[self.sew] |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 252 | 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 Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 264 | 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 Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 281 | 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 Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 286 | 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 Herman | 40976a7 | 2021-09-12 07:15:51 +0000 | [diff] [blame] | 294 | def pack_dest_mask(self, values): |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 295 | """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 Herman | 40976a7 | 2021-09-12 07:15:51 +0000 | [diff] [blame] | 298 | |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 299 | def cast_to_unsigned(arr): |
Jieung Kim | 535870c | 2021-10-07 06:03:46 +0000 | [diff] [blame] | 300 | """Cast a signed array to an unsigned array. |
| 301 | This should not be called with floating point values. |
| 302 | """ |
Henry Herman | 349c533 | 2021-09-08 08:42:35 +0000 | [diff] [blame] | 303 | 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 | |
| 311 | def to_carr_str(arr): |
| 312 | """Simple function for turn array into comma separated list.""" |
| 313 | return ", ".join(("%s" % x for x in arr)) |