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) test = (var_x > 1) test.set_attributes(tag = "test", debug = debug_std) large_add = (var_x + var_y) pre_result = Select( test, 1, large_add, tag = "pre_result", debug = debug_fixed ) result = Conversion(pre_result, precision=output_precision) self.implementation.add_output_signal("vr_out", result) return [self.implementation]
def solve_format_SubSignalSelection(optree, format_solver): """ Dummy legalization of SubSignalSelection operation node """ if optree.get_precision() is None: select_input = optree.get_input(0) input_prec = select_input.get_precision() inf_index = evaluate_cst_graph(optree.get_inf_index(), input_prec_solver=format_solver) sup_index = evaluate_cst_graph(optree.get_sup_index(), input_prec_solver=format_solver) if is_fixed_point(input_prec): frac_size = input_prec.get_frac_size() - inf_index integer_size = input_prec.get_integer_size() - ( input_prec.get_bit_size() - 1 - sup_index) if frac_size + integer_size <= 0: Log.report( Log.Error, "range determined for SubSignalSelection format [{}:{}] has negative size: {}, optree is {}", integer_size, frac_size, integer_size + frac_size, optree) return fixed_point(integer_size, frac_size) else: return ML_StdLogicVectorFormat(sup_index - inf_index + 1) else: return optree.get_precision()
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 expand_build_from_component(self, node): op_list = ((self.expand_node(op), op) for op in node.get_inputs()) result = tuple(op if expanded is None else expanded for (op, expanded) in op_list) Log.report(LOG_LEVEL_EXPAND_VERBOSE, "expanding BuildFromComponent {} into {}", node, result) return result
def search_bound_threshold_mirror(fct, limit, start_point, end_point, precision): """ This function assume that <fct> is monotonic and decreasing search by dichotomy the maximal x, floating-point number in @p precision, such that x >= start_point and x <= end_point and round(fct(x)) >= limit. """ assert precision.round_sollya_object(fct(start_point)) >= limit assert precision.round_sollya_object(fct(end_point)) < limit assert start_point < end_point left_bound = start_point right_bound = end_point while left_bound != right_bound and fp_next(left_bound, precision) != right_bound: mid_point = precision.round_sollya_object( (left_bound + right_bound) / S2, round_mode=sollya.RU) mid_point_img = precision.round_sollya_object(fct(mid_point), round_mode=sollya.RU) if mid_point_img >= limit: left_bound = mid_point elif mid_point_img < limit: right_bound = mid_point else: Log.report( Log.Error, "function must be increasing in search_bound_threshold") return left_bound
def legalize_multi_precision_vector_element_selection(optree): """ legalize a VectorElementSelection @p optree on a vector of multi-precision elements to a single element """ assert isinstance(optree, VectorElementSelection) multi_precision_vector = optree.get_input(0) elt_index = optree.get_input(1) hi_vector = multi_precision_vector.hi lo_vector = multi_precision_vector.lo limb_num = multi_precision_vector.get_precision().get_scalar_format().limb_num if limb_num == 2: component_vectors = [hi_vector, lo_vector] elif limb_num == 3: me_vector = multi_precision_vector.me component_vectors = [hi_vector, me_vector, lo_vector] else: Log.report(Log.Error, "unsupported number of limbs in legalize_multi_precision_vector_element_selection for {}", optree) result = BuildFromComponent( *tuple(VectorElementSelection(vector, elt_index) for vector in component_vectors) ) forward_attributes(optree, result) result.set_precision(optree.precision) return result
def expand_op(self, node, expander_map, arity=2): """ Generic expansion method for 2-operand node """ operands = [node.get_input(i) for i in range(arity)] def wrap_expand(op): """ expand node and returns it if no modification occurs """ expanded_node = self.expand_node(op) return (op, ) if expanded_node is None else expanded_node operands_expansion = [list(wrap_expand(op)) for op in operands] operands_format = [op.precision.get_match_format() for op in operands] result_precision = node.precision.get_match_format() elt_precision = get_elementary_precision(result_precision) try: expansion_key = (result_precision, tuple(operands_format)) expander = expander_map[expansion_key] except KeyError: Log.report( Log.Error, "unable to find multi-precision expander for {}, key is {}", node, str(expansion_key)) new_op = expander(*(sum(operands_expansion, [])), precision=elt_precision) # setting dedicated name to expanded node self.tag_expansion(node, new_op) # forward other attributes for elt in new_op: elt.set_debug(node.get_debug()) elt.set_handle(node.get_handle()) return new_op
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)) shift_amount_precision = fixed_point(3, 0, signed=False) # declaring main input variable var_x = self.implementation.add_input_signal("x", self.input_precision) var_y = self.implementation.add_input_signal("s", shift_amount_precision) cst_8 = Constant(9, precision=ML_Integer) cst_7 = Constant(7, precision=ML_Integer) cst_right_shifted_x = BitLogicRightShift(var_x, cst_8) cst_left_shifted_x = BitLogicLeftShift(var_x, cst_7) dyn_right_shifted_x = BitLogicRightShift(var_x, var_y) dyn_left_shifted_x = BitLogicLeftShift(var_x, var_y) result = cst_right_shifted_x + cst_left_shifted_x + dyn_right_shifted_x + dyn_left_shifted_x # output self.implementation.add_output_signal("vr_out", result) return [self.implementation]
def solve_format_CLZ(optree): """ Legalize CountLeadingZeros precision Args: optree (CountLeadingZeros): input node Returns: ML_Format: legal format for CLZ """ assert isinstance(optree, CountLeadingZeros) op_input = optree.get_input(0) input_precision = op_input.get_precision() if is_fixed_point(input_precision): if input_precision.get_signed(): Log.report(Log.Warning , "signed format in solve_format_CLZ") # +1 for carry overflow int_size = int(sollya.floor(sollya.log2(input_precision.get_bit_size()))) + 1 frac_size = 0 return fixed_point( int_size, frac_size, signed=False ) else: Log.report(Log.Warning , "unsupported format in solve_format_CLZ") return optree.get_precision()
def evaluate_graph_value(optree, input_mapping, memoization_map=None): """ Given the node -> value mapping input_mapping, evaluate optree numerical value """ # initializing memoization_map memoization_map = {} if memoization_map is None else memoization_map # computing values if optree in memoization_map: return memoization_map[optree] elif optree in input_mapping: value = input_mapping[optree] elif is_constant(optree): value = optree.get_value() elif is_typecast(optree): input_value = evaluate_graph_value(optree.get_input(0), input_mapping, memoization_map) value = evaluate_typecast_value(optree, input_value) elif is_conversion(optree): input_value = evaluate_graph_value(optree.get_input(0), input_mapping, memoization_map) value = evaluate_conversion_value(optree, input_value) else: args_interval = tuple( evaluate_graph_value(op, input_mapping, memoization_map) for op in optree.get_inputs() ) value = optree.apply_bare_range_function(args_interval) memoization_map[optree] = value Log.report(LOG_RUNTIME_EVAL_ERROR, "node {} value has been evaluated to: {}", optree.get_tag(), value) return value
def propagate_op(op, stage, retime_map): """ propagate the node op until stage by forwarding it through the pipeline and adding intermediate register at each stage crossed :param op: node to be propagated in the pipeline :type op: ML_Operation :param stage: index of the final pipeline stage :type stage: int :param retime_map: retiming-map to use to store propagation steps :type retime_map: RetimeMap """ op_key = retime_map.get_op_key(op) Log.report(Log.Verbose, " propagating {op} (key={op_key}) to stage {stage}", op=op, op_key=op_key, stage=stage) # look for the latest stage where op is defined current_stage = op_key.attributes.init_stage while retime_map.contains(op_key, current_stage + 1): current_stage += 1 op_src = retime_map.get(op_key, current_stage) while current_stage != stage: # create op instance for <current_stage+1> # remove cycle information prefix on tag if any raw_tag = re.sub("^e[0-9]+_", "", op_key.get_tag() or "empty") op_dst = Signal(tag="e{stage}_{tag}_q".format( tag=raw_tag, stage=(current_stage + 2)), init_stage=current_stage + 1, init_op=op_key, precision=op_key.get_precision(), var_type=Variable.Local) retime_map.add_stage_forward(op_dst, op_src, current_stage) retime_map.set(op_dst, current_stage + 1) # update values for next iteration current_stage += 1 op_src = op_dst
def import_from_file(ref_file): """ import a test summary from a file """ with open(ref_file, "r") as stream: test_map = {} header_line = stream.readline().replace('\n', '') if header_line[0] != "#": Log.report(Log.Error, "failed to read starter char '#' in header \"{}\"", header_line) return None property_list = [ tuple(v.split("=")) for v in header_line.split(" ") if "=" in v ] properties = dict(property_list) ref_format_version = properties["format_version"] if not ref_format_version in TestSummary.format_version_compatible_list: Log.report( Log.Error, "reference format_version={} is not in compatibility list {}", ref_format_version, TestSummary.format_version_compatible_list) for line in stream.readlines(): if line[0] == '#': # skip comment lines continue fields = line.replace('\n', '').split(" ") name = fields[0] test_map[name] = fields[1:] return TestSummary(test_map)
def simplify(self, node): def get_node_input(index): # look for input into simpifield list # and return directly node input if simplified input is None return node.get_input(index) result = None if node in self.memoization_map: return self.memoization_map[node] else: if self.is_simplifiable(node): if isinstance(node, LogicalAnd): result = simplify_logical_and_not(node, self.simplify) elif isinstance(node, LogicalOr): result = simplify_logical_or_not(node, self.simplify) else: raise NotImplementedError elif not is_leaf_node(node): for index, op in enumerate(node.inputs): new_op = self.simplify(op) if new_op != op: node.set_input(index, new_op) if not result is None: Log.report(LOG_LOGICAL_SIMPLIFICATION, "{} has been simplified to {}", node, result) else: # no simplification result = node self.memoization_map[node] = result return result
def generate_dummy_scheme(self): Log.report( Log.Info, "generating MultArray with output precision {precision}".format( precision=self.precision)) acc = None a_inputs = {} b_inputs = {} stage_map = self.instanciate_inputs() stage_index_list = sorted(stage_map.keys()) for stage_id in stage_index_list: # synchronizing pipeline stage if stage_id is None: pass else: while stage_id > self.implementation.get_current_stage(): self.implementation.start_new_stage() operation_list = stage_map[stage_id] for ctor, operand_list in operation_list: new_term = ctor(*tuple(operand_list)) if acc is None: acc = new_term else: acc = Addition(acc, new_term) result = Conversion(acc, precision=self.precision) self.implementation.add_output_signal("result_o", result) return [self.implementation]
def legalize_Select(optree): """ legalize Select operation node by converting if and else inputs to Select output format if the bit sizes do not match """ cond = optree.get_input(0) op0 = optree.get_input(1) op1 = optree.get_input(2) precision = optree.get_precision() if precision is None: Log.report(Log.Error, "None precision for Select:\n{}", optree) if op0.get_precision().get_bit_size() != precision.get_bit_size(): optree.set_input( 1, Conversion( op0, precision = precision ) ) if op1.get_precision().get_bit_size() != precision.get_bit_size(): optree.set_input( 2, Conversion( op1, precision = optree.get_precision() ) ) return optree
def solve_format_Constant(optree): """ Legalize Constant node """ assert isinstance(optree, Constant) value = optree.get_value() if FP_SpecialValue.is_special_value(value): return optree.get_precision() elif not optree.get_precision() is None: # if precision is already set (manually forced), returns it return optree.get_precision() else: # fixed-point format solving frac_size = -1 FRAC_THRESHOLD = 100 # maximum number of frac bit to be tested # TODO: fix for i in range(FRAC_THRESHOLD): if int(value*2**i) == value * 2**i: frac_size = i break if frac_size < 0: Log.report(Log.Error, "value {} is not an integer, from node:\n{}", value, optree) abs_value = abs(value) signed = value < 0 # int_size = max(int(sollya.ceil(sollya.log2(abs_value+2**frac_size))), 0) + (1 if signed else 0) int_size = max(int(sollya.ceil(sollya.log2(abs_value + 1))), 0) + (1 if signed else 0) if frac_size == 0 and int_size == 0: int_size = 1 return fixed_point(int_size, frac_size, signed=signed)
def propagate_op(op, stage, retime_map): op_key = retime_map.get_op_key(op) Log.report( Log.Verbose, " propagating {op} (key={op_key}) to stage {stage}".format( op=op, op_key=op_key, stage=stage)) # look for the latest stage where op is defined current_stage = op_key.attributes.init_stage while retime_map.contains(op_key, current_stage + 1): current_stage += 1 op_src = retime_map.get(op_key, current_stage) while current_stage != stage: # create op instance for <current_stage+1> # remove cycle information prefix on tag if any raw_tag = re.sub("^e[0-9]+_", "", op_key.get_tag() or "empty") op_dst = Signal(tag="e{stage}_{tag}_q".format(tag=raw_tag, stage=(current_stage + 2)), init_stage=current_stage + 1, init_op=op_key, precision=op_key.get_precision(), var_type=Variable.Local) retime_map.add_stage_forward(op_dst, op_src, current_stage) retime_map.set(op_dst, current_stage + 1) # update values for next iteration current_stage += 1 op_src = op_dst
def __call__(self, parser, namespace, values, option_string=None): for level_str in values.split(","): if ":" in level_str: level, sub_level = level_str.split(":") else: level, sub_level = level_str, None Log.enable_level(level, sub_level=sub_level)
def llvm_ir_format(precision): """ Translate from Metalibm precision to string for LLVM-IR format """ try: return { ML_Bool: "i1", v2bool: "<2 x i1>", v4bool: "<4 x i1>", v8bool: "<8 x i1>", ML_Int32: "i32", ML_Int64: "i64", ML_Binary32: "float", ML_Binary64: "double", v2int32: "<2 x i32>", v2int64: "<2 x i64>", v2float32: "<2 x float>", v2float64: "<2 x double>", v4float32: "<4 x float>", v4float64: "<4 x double>", v4int32: "<4 x i32>", v4int64: "<4 x i64>", v8int32: "<8 x i32>", v8float32: "<8 x float>", v8int64: "<8 x i64>", v8float64: "<8 x double>", ML_Int128: "i128", ML_Int256: "i256", }[precision] except KeyError: Log.report(Log.Error, "unknown precision {} in llvm_ir_format".format(precision), error=KeyError)
def propagate_format_to_input(new_format, optree, input_index_list): """ Propgate new_format to @p optree's input whose index is listed in @p input_index_list """ for op_index in input_index_list: op_input = optree.get_input(op_index) if op_input.get_precision() is None: op_input.set_precision(new_format) index_list = does_node_propagate_format(op_input) propagate_format_to_input(new_format, op_input, index_list) elif not test_format_equality(new_format, op_input.get_precision()): if is_constant(op_input): if format_does_fit(op_input, new_format): Log.report( Log.Info, "Simplify Constant Conversion {} to larger Constant: {}" .format(op_input.get_str(display_precision=True), str(new_format))) new_input = op_input.copy() new_input.set_precision(new_format) optree.set_input(op_index, new_input) else: Log.report( Log.Error, "Constant is about to be reduced to a too constrained format: {}" .format(op_input.get_str(display_precision=True))) else: new_input = Conversion(op_input, precision=new_format) optree.set_input(op_index, new_input)
def expand_negation(self, neg_node): """ Expand Negation on multi-component node """ op_input = neg_node.get_input(0) neg_operands = self.expand_node(op_input) Log.report(LOG_LEVEL_EXPAND_VERBOSE, "expanding Negation {} into {}", neg_node, neg_operands) return [Negation(op, precision=op.precision) for op in neg_operands]
def legalize_operation_rec(self, optree): """ """ # looking into memoization map if optree in self.memoization_map: return self.memoization_map[optree] # has the npde been modified ? arg_changed = False if isinstance(optree, ML_LeafNode): pass else: for index, op_input in enumerate(optree.get_inputs()): is_modified, new_node = self.legalize_operation_rec(op_input) if is_modified: optree.set_input(index, new_node) arg_changed = True local_changed, new_optree = self.legalize_single_operation( optree, self.format_solver) if local_changed: forward_attributes(optree, new_optree) Log.report(LOG_LEVEL_LEGALIZE, "legalized {} to {}", optree, new_optree) self.memoization_map[optree] = local_changed, new_optree return (local_changed or arg_changed), new_optree
def fixed_point_position_legalizer(optree, input_prec_solver=default_prec_solver): """ Legalize a FixedPointPosition node to a constant """ assert isinstance(optree, FixedPointPosition) fixed_input = optree.get_input(0) fixed_precision = input_prec_solver(fixed_input) if not is_fixed_point(fixed_precision): Log.report( Log.Error, "in fixed_point_position_legalizer: precision of {} should be fixed-point but is {}" .format(fixed_input, fixed_precision)) position = optree.get_input(1).get_value() align = optree.get_align() value_computation_map = { FixedPointPosition.FromLSBToLSB: position, FixedPointPosition.FromMSBToLSB: fixed_precision.get_bit_size() - 1 - position, FixedPointPosition.FromPointToLSB: fixed_precision.get_frac_size() + position, FixedPointPosition.FromPointToMSB: fixed_precision.get_integer_size() - position } cst_value = value_computation_map[align] # display value Log.report( Log.LogLevel("FixedPoint"), "fixed-point position {tag} has been resolved to {value}".format( tag=optree.get_tag(), value=cst_value)) result = Constant(cst_value, precision=ML_Integer) forward_attributes(optree, result) return result
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(2**-100, high_bound) local_function = function / sollya.x degree = sollya.sup( sollya.guessdegree(local_function, approx_interval, error_bound)) degree_list = range(0, int(degree) + 4, 2) 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 build(self, target, bin_name=None, shared_object=False, link=False, extra_build_opts=[]): """ Build @p self source file for @p target processor Args: target: target processor bin_name(str): name of the binary file (build result) shared_object: build as shared object link: enable/disable link Return: BinaryFile, str (error, stdout) """ build_command = SourceFile.get_build_command( self.path, target, bin_name, shared_object, link, expand_env_var=True, extra_build_opts=extra_build_opts, library_list=self.library_list) Log.report(Log.Info, "Building source with command: {}".format(build_command)) build_result, build_stdout = get_cmd_stdout(build_command) Log.report(Log.Verbose, "building stdout {}\n", build_stdout) if build_result: return None else: return BinaryFile(bin_name, self, shared_object=shared_object, library_deps=self.library_list)
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)) # generating component instantiation before meta-entity scheme lzc_in_width = self.input_precision.get_bit_size() lzc_implementation = self.generate_sub_lzc_component(lzc_in_width) lzc_component = lzc_implementation.get_component_object() # declaring main input variable var_x = self.implementation.add_input_signal("x", self.input_precision) var_x.set_attributes(debug = debug_fixed) lzc_out_width = ML_LeadingZeroCounter.get_lzc_output_width(lzc_in_width) lzc_out_format = ML_StdLogicVectorFormat(lzc_out_width) # input lzc_in = var_x var_x_lzc = Signal( "var_x_lzc", precision=lzc_out_format, var_type=Signal.Local, debug=debug_dec) var_x_lzc = PlaceHolder(var_x_lzc, lzc_component(io_map={"x": lzc_in, "vr_out": var_x_lzc})) # output self.implementation.add_output_signal("vr_out", var_x_lzc) return [self.implementation, lzc_implementation]
def generate_llvm_cst(value, precision, precision_header=True): """ Generate LLVM-IR code string to encode numerical value <value> """ if ML_FP_Format.is_fp_format(precision): if FP_SpecialValue.is_special_value(value): value = copy.copy(value) value.precision = ML_Binary64 mask = ~(2**(ML_Binary64.get_field_size() - precision.get_field_size()) - 1) return "0x{value:x}".format( # special constant must be 64-bit encoded in hexadecimal value=(ML_Binary64.get_integer_coding(value) & mask) ) else: sollya.settings.display = sollya.decimal value_str = str(precision.round_sollya_object(value)) if not "." in value_str: # adding suffix ".0" if numeric value is an integer value_str += ".0" return value_str elif is_std_integer_format(precision): return "{value}".format( # prec="" if not precision_header else llvm_ir_format(precision), value=int(value) ) else: Log.report( Log.Error, "format {} not supported in LLVM-IR generate_llvm_cst", precision )
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 execute_on_function(self, fct, fct_group): """ execute basic-block simplification 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())) bb_list = fct.get_scheme() self.execute_on_optree(bb_list, fct, fct_group)
def execute_pass_list(self, pass_list, inputs, execution_function): inter_values = inputs for pass_object in pass_list: Log.report(LOG_PASS_INFO, "executing pass: {}", pass_object.pass_tag) inter_values = execution_function(self, pass_object, inputs) return inter_values