def _BinOp(t, symbols, inferred_symbols): # Operations that require a function call if t.op.__class__.__name__ in cppunparse.CPPUnparser.funcops: separator, func = cppunparse.CPPUnparser.funcops[ t.op.__class__.__name__] # get the type of left and right operands for type inference type_left = _dispatch(t.left, symbols, inferred_symbols) type_right = _dispatch(t.right, symbols, inferred_symbols) # infer type and returns return dtypes.result_type_of(type_left, type_right) # Special case for integer power elif t.op.__class__.__name__ == 'Pow': if (isinstance(t.right, (ast.Num, ast.Constant)) and int(t.right.n) == t.right.n and t.right.n >= 0): if t.right.n != 0: type_left = _dispatch(t.left, symbols, inferred_symbols) for i in range(int(t.right.n) - 1): _dispatch(t.left, symbols, inferred_symbols) return dtypes.result_type_of(type_left, dtypes.typeclass(np.uint32)) else: type_left = _dispatch(t.left, symbols, inferred_symbols) type_right = _dispatch(t.right, symbols, inferred_symbols) return dtypes.result_type_of(type_left, type_right) else: # get left and right types for type inference type_left = _dispatch(t.left, symbols, inferred_symbols) type_right = _dispatch(t.right, symbols, inferred_symbols) return dtypes.result_type_of(type_left, type_right)
def _Call(t, symbols, inferred_symbols): inf_type = _dispatch(t.func, symbols, inferred_symbols) # Dispatch the arguments and determine their types arg_types = [_dispatch(e, symbols, inferred_symbols) for e in t.args] for e in t.keywords: _dispatch(e, symbols, inferred_symbols) # If the function symbol is known, always return the defined type if inf_type: return inf_type # In case of a typeless math function, determine the return type based on the arguments name = dace.frontend.python.astutils.rname(t) module = name[:name.rfind('.')] if module == 'math': return dtypes.result_type_of(arg_types[0], *arg_types) # Reading from an Intel channel returns the channel type if name == 'read_channel_intel': return arg_types[0] if name in ('abs', 'log'): return arg_types[0] # dtypes (dace.int32, np.float64) can be used as functions inf_type = _infer_dtype(t) if inf_type: return inf_type # In any other case simply return None return None
def _Constant(t, symbols, inferred_symbols): # String value if isinstance(t.value, (str, bytes)): return dtypes.pointer(dtypes.int8) # Numeric value return dtypes.result_type_of(dtypes.typeclass(type(t.value)), dtypes.typeclass(np.min_scalar_type(t.value).name))
def _Compare(self, t): if len(t.ops) != 1: # This includes things like a < b <= c raise NotImplementedError( 'Multiple comparisons at once not implemented') lhs = t.left op = t.ops[0] rhs = t.comparators[0] self.assert_type_compatibility(*self.infer(lhs, rhs)) if not isinstance(self.infer(t)[0], (dtypes.vector, dtypes.pointer)): return super()._Compare(t) if op.__class__ not in util.COMPARE_TO_SVE: raise NotImplementedError('Comparator not supported') self.write('{}({}, '.format(util.COMPARE_TO_SVE[op.__class__], self.pred_name)) lhs_type, rhs_type = self.infer(lhs, rhs) res_type = dtypes.result_type_of(lhs_type, rhs_type) self.dispatch_expect(lhs, res_type) self.write(', ') self.dispatch_expect(rhs, res_type) self.write(')')
def _IfExp(t, symbols, inferred_symbols): type_test = _dispatch(t.test, symbols, inferred_symbols) type_body = _dispatch(t.body, symbols, inferred_symbols) type_orelse = _dispatch(t.orelse, symbols, inferred_symbols) res_type = dtypes.result_type_of(type_body, type_orelse) if isinstance(type_test, dtypes.vector) and not isinstance(res_type, (dtypes.vector, dtypes.pointer)): # If we test on a vector, the result should be a vector aswell # so we can do a selection based on the test predicate res_type = dtypes.vector(res_type, type_test.veclen) return res_type
def _IfExp(self, t): if util.only_scalars_involed(self.get_defined_symbols(), t.test, t.body, t.orelse): return super()._IfExp(t) if_type, else_type = self.infer(t.body, t.orelse) res_type = dtypes.result_type_of(if_type, else_type) if not isinstance(res_type, dtypes.vector): res_type = dtypes.vector(res_type, -1) self.write('svsel(') self.dispatch_expect(t.test, dtypes.vector(dace.bool, -1)) self.write(', ') self.dispatch_expect(t.body, res_type) self.write(', ') self.dispatch_expect(t.orelse, res_type) self.write(')')
def _BinOp(self, t): lhs_type, rhs_type = self.infer(t.left, t.right) res_type = dtypes.result_type_of(lhs_type, rhs_type) if not isinstance(res_type, (dtypes.vector, dtypes.pointer)): return super()._BinOp(t) if t.op.__class__ not in util.BIN_OP_TO_SVE: raise NotImplementedError( f'Binary operation {t.op.__class__.__name__} not implemented') op_name = util.BIN_OP_TO_SVE[t.op.__class__] self.write('{}_x({}, '.format(op_name, self.pred_name)) self.dispatch_expect(t.left, res_type) self.write(', ') self.dispatch_expect(t.right, res_type) self.write(')')
def new_symbols(self, sdfg, state, symbols) -> Dict[str, dtypes.typeclass]: from dace.codegen.tools.type_inference import infer_expr_type result = {} # Add map params for p, rng in zip(self._map.params, self._map.range): result[p] = dtypes.result_type_of(infer_expr_type(rng[0], symbols), infer_expr_type(rng[1], symbols)) # Add dynamic inputs dyn_inputs = set(c for c in self.in_connectors if not c.startswith('IN_')) # TODO: Get connector type from connector for e in state.in_edges(self): if e.dst_conn in dyn_inputs: result[e.dst_conn] = sdfg.arrays[e.data.data].dtype return result
def _Call(t, symbols, inferred_symbols): inf_type = _dispatch(t.func, symbols, inferred_symbols) # Dispatch the arguments and determine their types arg_types = [_dispatch(e, symbols, inferred_symbols) for e in t.args] for e in t.keywords: _dispatch(e, symbols, inferred_symbols) # If the function symbol is known, always return the defined type if inf_type: return inf_type # In case of a typeless math function, determine the return type based on the arguments name = dace.frontend.python.astutils.rname(t) module = name[:name.rfind('.')] if module == 'math': return dtypes.result_type_of(arg_types[0], *arg_types) # In any other case simply return None return None
def _IfExp(t, symbols, inferred_symbols): _dispatch(t.test, symbols, inferred_symbols) type_body = _dispatch(t.body, symbols, inferred_symbols) type_orelse = _dispatch(t.orelse, symbols, inferred_symbols) return dtypes.result_type_of(type_body, type_orelse)
def _Num(t, symbols, inferred_symbols): # get the minimum between the minimum type needed to represent this number and the corresponding default data types # e.g., if num=1, then it will be represented by using the default integer type (int32 if C data types are used) return dtypes.result_type_of( dtypes.typeclass(type(t.n)), dtypes.typeclass(np.min_scalar_type(t.n).name))
def _NameConstant(t, symbols, inferred_symbols): return dtypes.result_type_of( dtypes.typeclass(type(t.value)), dtypes.typeclass(np.min_scalar_type(t.value).name))