def generate_approx_poly_near_zero(self, function, high_bound, error_bound, variable): """ Generate polynomial approximation scheme """ error_function = lambda p, f, ai, mod, t: sollya.dirtyinfnorm( p - f, ai) # Some issues encountered when 0 is one of the interval bound # so we use a symetric interval around it approx_interval = Interval(-high_bound, high_bound) local_function = function / sollya.x degree = sollya.sup( sollya.guessdegree(local_function, approx_interval, error_bound)) degree_list = range(0, int(degree) + 1, 1) poly_object, approx_error = Polynomial.build_from_approximation_with_error( function / sollya.x, degree_list, [1] + [self.precision] * (len(degree_list) - 1), approx_interval, sollya.absolute, error_function=error_function) Log.report( Log.Info, "approximation poly: {}\n with error {}".format( poly_object, approx_error)) poly_scheme = Multiplication( variable, PolynomialSchemeEvaluator.generate_horner_scheme( poly_object, variable, self.precision)) return poly_scheme, approx_error
def phi_node_insertion(bbg): """ Perform first phase of SSA translation: insert phi-node where required """ for v in bbg.variable_list: F = set() # set of bb where phi-node were added W = set() # set of bb which contains definition of v try: def_list = bbg.variable_defs[v] except KeyError as e: Log.report(Log.Error, "variable {} has no defs", v, error=e) for bb in def_list: #bb = bb_map[op] W.add(bb) while W: # non-empty set are evaluated to True x = W.pop() try: df = bbg.dominance_frontier_map[x] except KeyError: Log.report(LOG_LEVEL_GEN_BB_VERBOSE, "could not find dominance frontier for {}", x.get_tag()) df = [] for y in df: # Log.report(LOG_LEVEL_GEN_BB_VERBOSE, "y: {}", y) if not y in F: # add phi-node x <- at entry of y # TODO: manage more than 2 predecessor for v phi_fct = PhiNode(v, v, None, v, None) y.push(phi_fct) # adding phi funciton to bb map bbg.bb_map[phi_fct] = y F.add(y) if not y in def_list: W.add(y)
def generate_scheme(self): """ main scheme generation """ Log.report(Log.Info, "width parameter is {}".format(self.width)) int_size = 3 frac_size = self.width - int_size input_precision = fixed_point(int_size, frac_size) output_precision = fixed_point(int_size, frac_size) # declaring main input variable var_x = self.implementation.add_input_signal("x", input_precision) var_y = self.implementation.add_input_signal("y", input_precision) var_x.set_attributes(debug = debug_fixed) var_y.set_attributes(debug = debug_fixed) sub = var_x - var_y c = Constant(0) self.implementation.start_new_stage() #pre_result = Select( # c > sub, # c, # sub #) pre_result = Max(0, sub) self.implementation.start_new_stage() result = Conversion(pre_result + var_x, precision=output_precision) self.implementation.add_output_signal("vr_out", result) return [self.implementation]
def build_dominator_map(bbg): """ Build a dict which associates to each node N a list of nodes which dominate N. (by definition this list contains at least N, since each node dominates itself) """ predominance_map_list = build_predominance_map_list(bbg.cfg_edges) # building dominator map for each node dominator_map = {} for bb in bbg.bb_list.inputs: dominator_map[bb] = set(bbg.bb_list.get_inputs()) dominator_map[bbg.root] = set([bbg.root]) while True: change = False # dom(n) = fix point of intersect(dom(x), x in predecessor(n)) U {n} for bb in predominance_map_list: dom = set.union( set([bb]), set.intersection(*tuple( dominator_map[pred] for pred in predominance_map_list[bb]))) if not bb in dominator_map: Log.report(Log.Error, "following bb was not in dominator_map: {}", bb) if dom != dominator_map[bb]: change = True dominator_map[bb] = dom Log.report(LOG_LEVEL_GEN_BB_VERBOSE, "bb {}'s dominator list is {}", bb.get_tag(), [dom.get_tag() for dom in dominator_map[bb]]) if not change: break return dominator_map
def test_cp_eval_optree(optree, combinatorial): """ Evaluate critical path for Test operation class """ if optree.specifier is Test.IsZero: # floating-point is zero comparison is assumed to be equivalent # to a AND reduction on exponent + mantissa size operand = optree.get_input(0) base_precision = operand.get_precision().get_base_format() return CriticalPath(optree, tree_cp_eval_value( base_precision.get_exponent_size() + base_precision.get_mantissa_size(), TimingModel.AND_LEVEL), combinatorial=combinatorial) elif optree.specifier in [ Test.IsNaN, Test.IsInfty, Test.IsPositiveInfty, Test.IsNegativeInfty ]: # floating-point is nan/(+/-)infty test is assumed to be equivalent # to a AND reduction on exponent and an OR reduction on mantissa # plus neglected terms operand = optree.get_input(0) base_precision = operand.get_precision().get_base_format() return CriticalPath( optree, TimingModel.AND_LEVEL + max( tree_cp_eval_value(base_precision.get_exponent_size(), TimingModel.AND_LEVEL), tree_cp_eval_value(base_precision.get_mantissa_size(), TimingModel.OR_LEVEL)), combinatorial=combinatorial) else: Log.report(Log.Error, "unknown Test specifier in test_cp_eval_optree: {}", optree)
def __init__(self, arg_template=None): """ Initialize """ # building default arg_template if necessary arg_template = SubComponentInstance.get_default_args() if \ arg_template is None else arg_template # initializing I/O precision self.width = arg_template.width precision = arg_template.precision io_precisions = [precision] * 2 Log.report( Log.Info, "generating Adaptative Entity with width={}".format(self.width)) # initializing base class ML_EntityBasis.__init__(self, base_name="adaptative_design", arg_template=arg_template) self.accuracy = arg_template.accuracy self.precision = arg_template.precision int_size = 3 frac_size = 7 self.input_precision = fixed_point(int_size, frac_size) self.output_precision = fixed_point(int_size, frac_size)
def generate_scheme(self): self.var_mapping = {} for var_index in range(self.arity): # FIXME: maximal arity is 4 var_tag = ["x", "y", "z", "t"][var_index] self.var_mapping[var_tag] = self.implementation.add_input_variable( var_tag, self.get_input_precision(var_index), interval=self.input_intervals[var_index]) self.function_expr = function_parser(self.function_expr_str, self.var_mapping) Log.report(Log.Info, "evaluating function range") evaluate_range(self.function_expr, update_interval=True) Log.report( LOG_VERBOSE_FUNCTION_EXPR, "scheme is: \n{}", self.function_expr.get_str(depth=None, display_interval=True)) # defined copy map to avoid copying input Variables copy_map = dict((var, var) for var in self.var_mapping.items()) function_expr_copy = self.function_expr.copy(copy_map) result, scheme = self.instanciate_graph(function_expr_copy, expand_div=self.expand_div) scheme.add(Return(result, precision=self.precision)) return scheme
def generate_scheme(self): """ main scheme generation """ Log.report(Log.Info, "input_precision is {}".format(self.input_precision)) Log.report(Log.Info, "output_precision is {}".format(self.output_precision)) # declaring main input variable var_x = self.implementation.add_input_signal("x", self.input_precision) var_y = self.implementation.add_input_signal("y", self.input_precision) var_x.set_attributes(debug=debug_fixed) var_y.set_attributes(debug=debug_fixed) self.implementation.start_new_stage() add = var_x + var_y self.implementation.start_new_stage() sub = add - var_y self.implementation.start_new_stage() pre_result = sub - var_x self.implementation.start_new_stage() post_result = pre_result + var_x result = Conversion(pre_result, precision=self.output_precision) self.implementation.add_output_signal("vr_out", result) return [self.implementation]
def flush_bb_stack(self): """ Pop all basic-blocks from the stack except the last (function enttry point) """ # flush all but last bb (which is root/main) while len(self.current_bb_stack) > 1: assert self.pop_current_bb() Log.report(LOG_LEVEL_GEN_BB_VERBOSE, "bb_stack after flush: ", [bb.get_tag() for bb in self.current_bb_stack])
def elab(self, entity_name=None): # TODO/FIXME: a single file supported for now output_file = self.source_file_list[0] modelsim_elab_cmd = "vlib work && vcom -2008 {}".format(output_file) Log.report(Log.Info, "elaboration command:\n{}".format(modelsim_elab_cmd)) elab_result = subprocess.call(modelsim_elab_cmd, shell=True) Log.report(Log.Info, "elaboration result:{}".format(elab_result)) return elab_result
def execute(self, optree): Log.report(Log.Info, "executing PassDumpWithStages") print( optree.get_str(depth=None, display_precision=True, memoization_map={}, custom_callback=lambda op: " [S={}] ".format( op.attributes.init_stage)))
def execute_on_function(self, fct, fct_group): """ execute generic lowering on function <fct> from group <fct_group> """ Log.report(LOG_LOWERING_INFO, "executing pass {} on fct {}".format( self.pass_tag, fct.get_name())) fct_scheme = fct.get_scheme() lowered_scheme = self.execute_on_graph(fct_scheme) fct.set_scheme(lowered_scheme)
def evaluate_typecast_value(optree, value): assert isinstance(optree, TypeCast) input_format = optree.get_input(0).get_precision() output_format = optree.get_precision() input_value = input_format.get_integer_coding(value) output_value = output_format.get_value_from_integer_coding(input_value, base=None) Log.report(LOG_RUNTIME_EVAL_ERROR, "value={}, input_value= {}, output_value={}", value, input_value, output_value) return output_value
def get_memoization_value(self, node, *args): node_key = self.get_memoization_key(node, *args) try: return self.memoization_map[node_key] except KeyError: Log.report(Log.Error, "unable to found key {} in {} memoization map", node_key, self)
def execute_on_function(self, fct, fct_group): """ execute basic-block generation pass on function @p fct from function-group @p fct_group """ Log.report(LOG_LEVEL_GEN_BB_INFO, "executing pass {} on fct {}".format( self.pass_tag, fct.get_name())) op_graph = fct.get_scheme() top_bb_list = self.execute_on_graph(op_graph) fct.set_scheme(top_bb_list)
def register_pass(self, pass_object, pass_dep=PassDependency(), pass_slot=None): Log.report( LOG_PASS_INFO, "PassScheduler: registering pass {} at {}".format( pass_object, pass_slot)) self.pass_map[pass_slot].append(PassWrapper(pass_object, pass_dep))
def elab(self, entity_name): # TODO/FIXME: a single file supported for now output_file = self.source_file_list[0] ghdl_elab_cmd = "ghdl -c --ieee=synopsys --std=08 {} -e {} ".format(output_file, entity_name) Log.report(Log.Info, "elaboration command:\n{}".format(ghdl_elab_cmd)) elab_result = subprocess.call(ghdl_elab_cmd, shell=True) Log.report(Log.Info, "elaboration result:{}".format(elab_result)) return elab_result
def execute_on_function(self, fct, fct_group): Log.report(Log.Info, "executing pass {} on fct {}".format( self.pass_tag, fct.get_name())) optree = fct.get_scheme() memoization_map = {} new_scheme = self.execute_on_optree(optree, fct, fct_group, memoization_map) if not new_scheme is None: fct.set_scheme(new_scheme)
def dadda_4to2_reduction(previous_bit_heap): """ BitHeap Wallace reduction using 4:2 compressors """ next_bit_heap = BitHeap() carry_bit_heap = BitHeap() max_count = previous_bit_heap.max_count() new_count = int(math.ceil(max_count / 2.0)) # each step reduce the height of the bit heap at at most # new_count. However it is not necessary to reduce over it while previous_bit_heap.max_count() > 0: bit_list, w = previous_bit_heap.pop_lower_bits(4) # if a carry from this weight exists, we must try to # accumulate it if carry_bit_heap.bit_count(w) > 0: cin = carry_bit_heap.pop_bit(w) else: cin = None if len(bit_list) == 0: if cin: next_bit_heap.insert(w, cin) elif len(bit_list) == 1: next_bit_heap.insert_bit(w, bit_list[0]) if cin: next_bit_heap.insert_bit(w, cin) elif (0 if cin is None else 1) + previous_bit_heap.bit_count(w) + len( bit_list) + next_bit_heap.bit_count(w) <= new_count: Log.report(Log.Verbose, "dropping bits without compression") # drop every bit in next stage if not cin is None: next_bit_heap.insert_bit(w, cin) for b in bit_list: next_bit_heap.insert_bit(w, b) elif len(bit_list) == 2: if cin is None: for b in bit_list: next_bit_heap.insert_bit(w, b) else: b_wp1, b_w = comp_3to2(bit_list[0], bit_list[1], cin) next_bit_heap.insert_bit(w + 1, b_wp1) next_bit_heap.insert_bit(w, b_w) cin = None elif len(bit_list) == 3: if cin: next_bit_heap.insert_bit(w, cin) b_wp1, b_w = comp_3to2(bit_list[0], bit_list[1], bit_list[2]) next_bit_heap.insert_bit(w + 1, b_wp1) next_bit_heap.insert_bit(w, b_w) else: assert len(bit_list) == 4 cout, b_wp1, b_w = comp_4to2(cin, bit_list[0], bit_list[1], bit_list[2], bit_list[3]) next_bit_heap.insert_bit(w + 1, b_wp1) next_bit_heap.insert_bit(w, b_w) carry_bit_heap.insert_bit(w + 1, cout) # flush carry-bit heap while carry_bit_heap.max_count() > 0: bit_list, w = carry_bit_heap.pop_lower_bits(1) next_bit_heap.insert_bit(w, bit_list[0]) return next_bit_heap
def expand_node(self, node): """ If node @p node is a multi-precision node, expands to a list of scalar element, ordered from most to least significant """ if node in self.memoization_map: return self.memoization_map[node] else: if not (multi_element_output(node) or multi_element_inputs(node)): if not is_leaf_node(node): # recursive processing of node's input for index, op in enumerate(node.get_inputs()): op_input = self.expand_node(op) if not op_input is None: reconstructed_input = self.reconstruct_from_transformed( op, op_input) node.set_input(index, reconstructed_input) result = (node, ) elif isinstance(node, Variable): result = self.expand_var(node) elif isinstance(node, Constant): result = self.expand_cst(node) elif isinstance(node, Addition): result = self.expand_add(node) elif isinstance(node, Multiplication): result = self.expand_mul(node) elif isinstance(node, Subtraction): result = self.expand_sub(node) elif isinstance(node, FusedMultiplyAdd): result = self.expand_fma(node) elif isinstance(node, Conversion): result = self.expand_conversion(node) elif isinstance(node, Negation): result = self.expand_negation(node) elif isinstance(node, BuildFromComponent): result = self.expand_build_from_component(node) elif isinstance(node, ComponentSelection): result = self.expand_component_selection(node) elif is_subnormalize_op(node): result = self.expand_subnormalize(node) else: if is_leaf_node(node): pass else: # recursive processing of node's input for index, op in enumerate(node.get_inputs()): op_input = self.expand_node(op) if not op_input is None: reconstructed_input = self.reconstruct_from_transformed( op, op_input) node.set_input(index, reconstructed_input) # no modification result = None if result is None: Log.report(LOG_LEVEL_EXPAND_VERBOSE, "expansion is None for {}", node) self.memoization_map[node] = result return result
def update_def_var(node, var, new_var): """ Update @p var which should be the variable defined by @p and replace it by @p vp """ # TODO: manage sub-assignation cases assert isinstance(node, (ReferenceAssign, PhiNode)) assert node.get_input(0) is var assert not new_var is None Log.report(LOG_LEVEL_GEN_BB_VERBOSE, "updating var def in {} from {} to {}", node, var, new_var) node.set_input(0, new_var)
def dominator_map(self): """ dict associating to each node the least of nodes which dominate it """ if self._dominator_map is None: self._dominator_map = build_dominator_map(self) for bb in self._dominator_map: Log.report(LOG_LEVEL_GEN_BB_VERBOSE, "bb {}'s dominator: {}", bb.get_tag(), [dom.get_tag() for dom in self._dominator_map[bb]]) return self._dominator_map
def add_output_variable(self, name, output_node): output_var = Variable(name, precision=output_node.get_precision(), var_type=Variable.Output) output_assign = ReferenceAssign(output_var, output_node) if name in self.output_map: Log.report(Log.error, "pre-existing name {} in output_map".format(name)) self.output_map[name] = output_assign
def legalize_node(self, node): """ return the expansion of @p node when possible else None """ def wrap_expansion(node, transformed_node): return node if transformed_node is None else transformed_node def general_get_scalar_format(node_format): """ Overload of get_scalar_format which works for both vector and scalar formats """ if node_format.is_vector_format(): return node_format.get_scalar_format() return node_format if node in self.memoization_map: return self.memoization_map[node] elif not is_virtual_bool_node(node): result = None elif isinstance(node, Comparison) or isinstance( node, LogicalAnd) or isinstance(node, LogicalOr): scalar_bit_size = max( general_get_scalar_format( node.get_input(0).get_precision()).get_bit_size(), general_get_scalar_format( node.get_input(1).get_precision()).get_bit_size()) legalized_format = VECTOR_TYPE_MAP[ML_Bool][scalar_bit_size][ node.get_precision().get_vector_size()] Log.report(LOG_VERBOSE_VBOOL_LEGALIZATION, "legalizing format of {} from {} to {}", node, node.get_precision(), legalized_format) node.set_attributes(precision=legalized_format) result = node elif isinstance(node, LogicalNot) or (isinstance( node, Test) and node.specifier in EXPANDABLE_TEST_LIST): scalar_bit_size = general_get_scalar_format( node.get_input(0).get_precision()).get_bit_size() legalized_format = VECTOR_TYPE_MAP[ML_Bool][scalar_bit_size][ node.get_precision().get_vector_size()] Log.report(LOG_VERBOSE_VBOOL_LEGALIZATION, "legalizing format of {} from {} to {}", node, node.get_precision(), legalized_format) node.set_attributes(precision=legalized_format) result = node elif isinstance(node, VectorAssembling): scalar_bit_size = max( general_get_scalar_format( node.get_input(0).get_precision()).get_bit_size(), general_get_scalar_format( node.get_input(1).get_precision()).get_bit_size()) legalized_format = VECTOR_TYPE_MAP[ML_Bool][scalar_bit_size][ node.get_precision().get_vector_size()] node.set_attributes(precision=legalized_format) result = node else: result = None self.memoization_map[node] = result return result
def add_stage_forward(self, op_dst, op_src, stage): Log.report( Log.Verbose, " adding stage forward {op_src} to {op_dst} @ stage {stage}". format(op_src=op_src, op_dst=op_dst, stage=stage)) if not stage in self.stage_forward: self.stage_forward[stage] = [] self.stage_forward[stage].append(ReferenceAssign(op_dst, op_src)) self.pre_statement.add(op_src)
def run(self, simulation_time, debug=False, exit_after_test=True): debug_cmd = "do {debug_file};".format(debug_file=self.debug_file) if debug else "" debug_cmd += " exit;" if exit_after_test else "" # simulation modelsim_run_cmd = "vsim -c work.testbench -do \"run {test_delay} ns; {debug_cmd}\"".format( debug_cmd=debug_cmd, test_delay=simulation_time) Log.report(Log.Info, "simulation command:\n{}".format(modelsim_run_cmd)) sim_result = subprocess.call(modelsim_run_cmd, shell=True) Log.report(Log.Info, "simulation result:{}".format(sim_result)) return sim_result
def llvm_ir_format(precision): """ Translate from Metalibm precision to string for LLVM-IR format """ if is_pointer_format(precision): return "{}*".format(llvm_ir_format(precision.get_data_precision())) elif precision in LLVM_IR_DATA_FORMAT_MAP: return LLVM_IR_DATA_FORMAT_MAP[precision] else: Log.report(Log.Error, "unknown precision {} in llvm_ir_format".format(precision), error=KeyError)
def successors(self): last_op = self.get_input(-1) if not isinstance(last_op, ControlFlowOperation): Log.report( Log.Info, "last operation of BB is not a ControlFlowOperation: BB is {}", self) return [] else: return last_op.destination_list
def execute_on_fct_group(self, fct_group): Log.report( Log.Info, "executing pass {} on fct group {}".format(self.pass_tag, fct_group)) def local_fct_apply(group, fct): return self.execute_on_function(fct, group) return fct_group.apply_to_all_functions(local_fct_apply)
def execute_on_function(self, fct, fct_group): """ Execute SSA translation pass on function @p fct from function-group @p fct_group """ Log.report(LOG_LEVEL_GEN_BB_INFO, "executing pass {} on fct {}".format( self.pass_tag, fct.get_name())) optree = fct.get_scheme() assert isinstance(optree, BasicBlockList) bb_root = optree.get_input(0) bbg = BasicBlockGraph(bb_root, optree) phi_node_insertion(bbg) variable_renaming(bbg)