def transform(self): '''To apply loop transformations on the annotated code''' # parse the code to get the AST stmts = parser.getParser(self.line_no).parse(self.module_body_code) if isinstance(stmts[0], ast.TransformStmt) and stmts[0].stmt is None: # transform the enclosed annot_body_code annotated_stmts = parser.getParser(self.line_no).parse( self.annot_body_code) if len(annotated_stmts) == 1: annotated_stmt = annotated_stmts[0] else: annotated_stmt = ast.CompStmt(annotated_stmts[0]) stmts[0].stmt = annotated_stmt # apply transformations t = transformation.Transformation(self.perf_params, self.verbose, self.language, self.tinfo) transformed_stmts = t.transform(stmts) # generate code for the transformed ASTs indent = ' ' * self.indent_size extra_indent = ' ' cgen = codegen.CodeGen(self.language) transformed_code = '\n' for s in transformed_stmts: transformed_code += cgen.generate(s, indent, extra_indent) # return the transformed code return transformed_code
def createForLoop(self, index_id, lbound_exp, ubound_exp, stride_exp, loop_body): ''' Generate a for-loop statement based on the given loop structure information: for (index_id = lbound_exp; index_id <= ubound_exp; index_id = index_id + stride_exp) loop_body ''' init_exp = None test_exp = None iter_exp = None if lbound_exp: init_exp = ast.BinOpExp(index_id.replicate(), lbound_exp.replicate(), ast.BinOpExp.EQ_ASGN) if ubound_exp: test_exp = ast.BinOpExp(index_id.replicate(), ubound_exp.replicate(), ast.BinOpExp.LE) if stride_exp: while isinstance(stride_exp, ast.ParenthExp): stride_exp = stride_exp.exp it = ast.BinOpExp(index_id.replicate(), stride_exp.replicate(), ast.BinOpExp.ADD) iter_exp = ast.BinOpExp(index_id.replicate(), it, ast.BinOpExp.EQ_ASGN) if not isinstance(loop_body, ast.CompStmt): loop_body = ast.CompStmt([loop_body]) return ast.ForStmt(init_exp, test_exp, iter_exp, loop_body.replicate())
def convertToASTs(self, tile_level, loop_info_table, node=None): '''To generate a sequence of ASTs that correspond to this sequence of "simple" loops''' if node == None: node = self.loops if isinstance(node, tuple): iname, subnodes = node tsize_name, titer_name, st_exp = loop_info_table[iname] id = ast.IdentExp(iname) lb = ast.IdentExp(titer_name) tmp = ast.BinOpExp(ast.IdentExp(tsize_name), ast.ParenthExp(st_exp), ast.BinOpExp.SUB) ub = ast.BinOpExp(lb, ast.ParenthExp(tmp), ast.BinOpExp.ADD) st = st_exp lbody = ast.CompStmt( self.convertToASTs(tile_level, loop_info_table, subnodes)) return self.ast_util.createForLoop(id, lb, ub, st, lbody) elif isinstance(node, list): return [ self.convertToASTs(tile_level, loop_info_table, n) for n in node ] else: return node
def __normalizeStmt(self, stmt): """ * To change the format of all for-loops to a fixed form as described below: for (<id> = <exp>; <id> <= <exp>; <id> += <exp>) { <stmts> } * To change the format of all if-statements to a fixed form as described below: if (<exp>) { <stmts> } else { <stmts> } * To remove meaningless scopings inside each compound statement. """ if isinstance(stmt, ast.ExpStmt): return stmt elif isinstance(stmt, ast.CompStmt): stmt.stmts = [self.__normalizeStmt(s) for s in stmt.stmts] while len(stmt.stmts) == 1 and isinstance(stmt.stmts[0], ast.CompStmt): stmt.stmts = stmt.stmts[0].stmts return stmt elif isinstance(stmt, ast.IfStmt): stmt.true_stmt = self.__normalizeStmt(stmt.true_stmt) if not isinstance(stmt.true_stmt, ast.CompStmt): stmt.true_stmt = ast.CompStmt([stmt.true_stmt]) if stmt.false_stmt: stmt.false_stmt = self.__normalizeStmt(stmt.false_stmt) if not isinstance(stmt.false_stmt, ast.CompStmt): stmt.false_stmt = ast.CompStmt([stmt.false_stmt]) return stmt elif isinstance(stmt, ast.ForStmt): stmt.stmt = self.__normalizeStmt(stmt.stmt) (id, lb, ub, st, bod) = self.ast_util.getForLoopInfo(stmt) if not isinstance(bod, ast.CompStmt): bod = ast.CompStmt([bod]) stmt = self.ast_util.createForLoop(id, lb, ub, st, bod) return stmt else: err( "orio.module.ortil.semant internal error:OrTil: unknown type of statement: %s" % stmt.__class__.__name__ )
def __normalizeStmt(self, stmt): """ * To enforce all loops to have a fixed loop form: for (id = lb; id <= ub; id += st) { bod } * To enforce all if-statements to have a fixed if-statement form: if (test) { true-case } else { false-case } * To remove all meaningless scopings inside each compound statement. """ if isinstance(stmt, ast.ExpStmt): return stmt elif isinstance(stmt, ast.CompStmt): stmt.stmts = [self.__normalizeStmt(s) for s in stmt.stmts] while len(stmt.stmts) == 1 and isinstance(stmt.stmts[0], ast.CompStmt): stmt.stmts = stmt.stmts[0].stmts return stmt elif isinstance(stmt, ast.IfStmt): stmt.true_stmt = self.__normalizeStmt(stmt.true_stmt) if not isinstance(stmt.true_stmt, ast.CompStmt): stmt.true_stmt = ast.CompStmt([stmt.true_stmt]) if stmt.false_stmt: stmt.false_stmt = self.__normalizeStmt(stmt.false_stmt) if not isinstance(stmt.false_stmt, ast.CompStmt): stmt.false_stmt = ast.CompStmt([stmt.false_stmt]) return stmt elif isinstance(stmt, ast.ForStmt): stmt.stmt = self.__normalizeStmt(stmt.stmt) (id, lb, ub, st, bod) = self.ast_util.getForLoopInfo(stmt) if not isinstance(bod, ast.CompStmt): bod = ast.CompStmt([bod]) stmt = self.ast_util.createForLoop(id, lb, ub, st, bod) return stmt else: err("orio.module.tilic.semant internal error: unknown type of statement: %s" % stmt.__class__.__name__)
def __startTiling(self, stmt, tile_level, int_vars): '''To apply loop-tiling transformation on each of loop''' # expression statement if isinstance(stmt, ast.ExpStmt): return stmt # compound statement elif isinstance(stmt, ast.CompStmt): tstmts = [] for s in stmt.stmts: ts = self.__startTiling(s, tile_level, int_vars) if isinstance(ts, ast.CompStmt): tstmts.extend(ts.stmts) else: tstmts.append(ts) stmt.stmts = tstmts return stmt # if statement elif isinstance(stmt, ast.IfStmt): stmt.true_stmt = self.__startTiling(stmt.true_stmt, tile_level, int_vars) if stmt.false_stmt: stmt.false_stmt = self.__startTiling(stmt.false_stmt, tile_level, int_vars) return stmt # for loop statement elif isinstance(stmt, ast.ForStmt): # apply loop tiling on this loop tiling_results = self.__tile(stmt, tile_level, [], [], None, int_vars) # return the tiled AST t_stmts = [] for is_tiled, stmts in tiling_results: if self.use_boundary_tiling and not is_tiled: new_tile_level = min(tile_level - 1, self.recursive_tile_level) if new_tile_level > 0: stmts = [ self.__startTiling(s, new_tile_level, int_vars) for s in stmts ] t_stmts.extend(stmts) tiled_ast = ast.CompStmt(t_stmts) # return the tiled AST return tiled_ast # unknown statement else: err('orio.module.ortil.transformation internal error: unknown type of statement: %s' % stmt.__class__.__name__)
def __unroll(self, stmt, unroll_factor_table): """ To apply loop unrolling on the fully rectangularly tiled loop. Assumption: the given loop is perfectly nested, and all loops are fully rectangularly tiled. """ # expression statement if isinstance(stmt, ast.ExpStmt): return stmt # compound statement elif isinstance(stmt, ast.CompStmt): ustmts = [] for s in stmt.stmts: us = self.__unroll(s, unroll_factor_table) if isinstance(us, ast.CompStmt): ustmts.extend(us.stmts) else: ustmts.append(us) stmt.stmts = ustmts return stmt # if statement elif isinstance(stmt, ast.IfStmt): return stmt # for loop statement elif isinstance(stmt, ast.ForStmt): # extract this loop structure id, _, _, _, lbody = self.ast_util.getForLoopInfo(stmt) # recursively unroll its loop body ubody = self.__unroll(lbody, unroll_factor_table) # unroll the loop body ufactor = unroll_factor_table[id.name] ustmts = [] for i in range(0, ufactor): s = ubody.replicate() s = self.__addIdentWithConstant(s, id.name, i) if isinstance(s, ast.CompStmt): ustmts.extend(s.stmts) else: ustmts.append(s) # return the unrolled body return ast.CompStmt(ustmts) # unknown statement else: err("orio.module.ortildriver.transformation internal error: unknown type of statement: %s" % stmt.__class__.__name__)
def __startTiling(self, stmt): '''To apply tiling transformation on the top-level loop statement''' # expression statement if isinstance(stmt, ast.ExpStmt): return ([], stmt) # compound statement elif isinstance(stmt, ast.CompStmt): int_vars = [] tstmts = [] for s in stmt.stmts: ivars, ts = self.__startTiling(s) int_vars.extend(ivars) if isinstance(ts, ast.CompStmt): tstmts.extend(ts.stmts) else: tstmts.append(ts) stmt.stmts = tstmts return (int_vars, stmt) # if statement elif isinstance(stmt, ast.IfStmt): int_vars = [] ivars, ts = self.__startTiling(stmt.true_stmt) int_vars.extend(ivars) stmt.true_stmt = ts if stmt.false_stmt: ivars, ts = self.__startTiling(stmt.false_stmt) int_vars.extend(ivars) stmt.false_stmt = ts return (int_vars, stmt) # loop statement elif isinstance(stmt, ast.ForStmt): int_vars, tstmts = self.__tile(stmt) if len(tstmts) > 1: tiled_stmt = ast.CompStmt(tstmts) else: tiled_stmt = tstmts[0] return (int_vars, tiled_stmt) # unknown statement else: err('orio.module.tilic.transformation internal error: unknown type of statement: %s' % stmt.__class__.__name__)
def transform(self): '''To apply loop transformations on the annotated code''' # parse the code to get the AST stmts = parser.getParser(self.line_no).parse(self.module_body_code) if isinstance(stmts[0], ast.TransformStmt) and stmts[0].stmt is None: # transform the enclosed annot_body_code annotated_stmts = parser.getParser(self.line_no).parse( self.annot_body_code) if len(annotated_stmts) == 1: annotated_stmt = annotated_stmts[0] else: annotated_stmt = ast.CompStmt(annotated_stmts[0]) stmts[0].stmt = annotated_stmt # apply transformations t = transformation.Transformation(self.perf_params, self.verbose, self.language, self.tinfo) transformed_stmts = t.transform(stmts) # generate code for the transformed ASTs indent = ' ' * self.indent_size extra_indent = ' ' cgen = codegen.CodeGen(self.language) transformed_code = '\n' for s in transformed_stmts: transformed_code += cgen.generate(s, indent, extra_indent) # Example on applying another visitor, e.g., for analysis #exampleVisitor = astvisitors.ExampleVisitor() #exampleVisitor.visit(transformed_stmts) # Count operations visitor opsVisitor = astvisitors.CountingVisitor() opsVisitor.visit(transformed_stmts) debug(str(opsVisitor), level=3) # CFG if True: try: from orio.module.loop.cfg import CFGGraph cfg = CFGGraph(transformed_stmts) except Exception, e: err('[module.loop.loop] cannot construct CFG: ', e)
def createForLoop(self, id, lb, ub, st, bod): ''' Generate a loop statement: for (id = lb; id <= ub; id = id + st) bod ''' init = None test = None iter = None if lb: init = ast.BinOpExp(id.replicate(), lb.replicate(), ast.BinOpExp.EQ_ASGN) if ub: test = ast.BinOpExp(id.replicate(), ub.replicate(), ast.BinOpExp.LE) if st: i = ast.BinOpExp(id.replicate(), st.replicate(), ast.BinOpExp.ADD) iter = ast.BinOpExp(id.replicate(), i, ast.BinOpExp.EQ_ASGN) bod = bod.replicate() if not isinstance(bod, ast.CompStmt): bod = ast.CompStmt([bod]) return ast.ForStmt(init, test, iter, bod)
def p_compound_statement(p): 'compound_statement : LBRACE statement_list_opt RBRACE' p[0] = ast.CompStmt(p[2], p.lineno(1) + __start_line_no - 1)
def p_compound_statement(p): "compound_statement : LBRACE statement_list_opt RBRACE" p[0] = ast.CompStmt(p[2])
def __tile(self, stmt, tile_level, outer_loop_infos, preceding_stmts, lbound_info, int_vars): '''Apply tiling on the given statement''' # complain if the tiling level is not a positive integer if not isinstance(tile_level, int) or tile_level <= 0: err('orio.module.ortil.transformation internal error: invalid tiling level: %s' % tile_level) # cannot handle a directly nested compound statement if isinstance(stmt, ast.CompStmt): err('orio.module.ortil.transformation internal error: unexpected compound statement directly nested inside ' + 'another compound statement') # to handle the given expression statement or if statement if isinstance(stmt, ast.ExpStmt) or isinstance(stmt, ast.IfStmt): preceding_stmts = preceding_stmts[:] if preceding_stmts: is_tiled, last_stmts = preceding_stmts.pop() if is_tiled: preceding_stmts.append((is_tiled, last_stmts)) preceding_stmts.append((False, [stmt])) else: preceding_stmts.append((False, last_stmts + [stmt])) else: preceding_stmts.append((False, [stmt])) return preceding_stmts # to tile the given for-loop statement if isinstance(stmt, ast.ForStmt): # check if this loop is already tiled and whether it's fully tiled this_fully_tiled = stmt.fully_tiled # extract loop structure information this_linfo = self.ast_util.getForLoopInfo(stmt) this_id, this_lb_exp, this_ub_exp, this_st_exp, this_lbody = this_linfo this_iname = this_id.name # get information about the (extended) tiled outer loops outer_loop_inames = [i for i, _ in outer_loop_infos] loop_info_table = dict(outer_loop_infos) n_outer_loop_infos = outer_loop_infos + [(this_iname, this_linfo)] n_outer_loop_inames = [i for i, _ in n_outer_loop_infos] n_loop_info_table = dict(n_outer_loop_infos) # prepare loop bounds information (for iterating full rectangular tiles) need_prolog = False need_epilog = False rect_lb_exp = this_lb_exp rect_ub_exp = this_ub_exp if lbound_info: lb_name, ub_name, need_prolog, need_epilog, need_tiled_loop = lbound_info rect_lb_exp = ast.IdentExp(lb_name) rect_ub_exp = ast.IdentExp(ub_name) if not need_tiled_loop: err('orio.module.ortil.transformation internal error: unexpected case where generation of the orio.main.' + 'rectangular tiled loop is needed') # get explicit loop-bound scanning code t = self.__getLoopBoundScanningStmts(this_lbody.stmts, tile_level, n_outer_loop_inames, n_loop_info_table) scan_stmts, lbound_info_seq, ivars = t # update the newly declared integer variables int_vars.extend(ivars) # initialize the resulting statements res_stmts = preceding_stmts[:] # generate the prolog code if need_prolog: ub = ast.BinOpExp(rect_lb_exp, ast.ParenthExp(this_st_exp), ast.BinOpExp.SUB) prolog_code = self.ast_util.createForLoop( this_id, this_lb_exp, ub, this_st_exp, this_lbody) if res_stmts: is_tiled, last_stmts = res_stmts.pop() if is_tiled: res_stmts.append((is_tiled, last_stmts)) res_stmts.append((False, [prolog_code])) else: res_stmts.append((False, last_stmts + [prolog_code])) else: res_stmts.append((False, [prolog_code])) # start generating the orio.main.rectangularly tiled code # (note: the body of the tiled code may contain if-statement branches, # each needed to be recursively transformed) # example of the resulting processed statements: # s1; if (t-exp) {s2; s3;} else s4; # is represented as the following list: # [s1, t-exp, [s2, s3], [s4]] contain_loop = False processed_stmts = [] if_branches = [ processed_stmts ] # a container for storing list of if-branch statements for s, binfo in zip(this_lbody.stmts, lbound_info_seq): # check if one of the enclosed statements is a loop if isinstance(s, ast.ForStmt): contain_loop = True # perform transformation on each if-branch statements n_if_branches = [] for p_stmts in if_branches: # replicate the statement (just to be safe) s = s.replicate() # this is NOT a loop statement with bound expressions that are functions of # outer loop iterators if binfo == None: n_p_stmts = self.__tile(s, tile_level, n_outer_loop_infos, p_stmts, binfo, int_vars) while len(p_stmts) > 0: p_stmts.pop() p_stmts.extend(n_p_stmts) n_if_branches.append(p_stmts) continue # (optimization) special handling for one-time loop --> remove the if's true # condition (i.e., lb<ub) since it will never be executed. _, _, _, _, need_tiled_loop = binfo if not need_tiled_loop: if p_stmts: is_tiled, last_stmts = p_stmts.pop() if is_tiled: p_stmts.append((is_tiled, last_stmts)) p_stmts.append((False, [s])) else: p_stmts.append((False, last_stmts + [s])) else: p_stmts.append((False, [s])) n_if_branches.append(p_stmts) continue # (optimization) recursively feed in the last processed statement only, and # leave the other preceeding statements untouched --> for reducing code size if len(p_stmts) > 0: p = p_stmts.pop() last_p_stmts = [p] else: last_p_stmts = [] # perform a recursion to further tile this rectangularly tiled loop n_p_stmts = self.__tile(s.replicate(), tile_level, n_outer_loop_infos, last_p_stmts, binfo, int_vars) # compute the processed statements for both true and false conditions true_p_stmts = n_p_stmts false_p_stmts = last_p_stmts if false_p_stmts: is_tiled, last_stmts = false_p_stmts.pop() if is_tiled: false_p_stmts.append((is_tiled, last_stmts)) false_p_stmts.append((False, [s])) else: false_p_stmts.append((False, last_stmts + [s])) else: false_p_stmts.append((False, [s])) # create two sets of if-branch statements lbn, ubn, _, _, _ = binfo test_exp = ast.BinOpExp(ast.IdentExp(lbn), ast.IdentExp(ubn), ast.BinOpExp.LT) p_stmts.append(test_exp) p_stmts.append(true_p_stmts) p_stmts.append(false_p_stmts) n_if_branches.append(true_p_stmts) n_if_branches.append(false_p_stmts) # update the if-branch statements if_branches = n_if_branches # combine the loop-bound scanning statements lbody_stmts = [] lbody_stmts.extend(scan_stmts) # convert the processed statements into AST lbody_stmts.extend( self.__convertToASTs(processed_stmts, tile_level, contain_loop, n_outer_loop_inames, n_loop_info_table, int_vars)) # generate the orio.main.rectangularly tiled code lbody = ast.CompStmt(lbody_stmts) iname = self.__getTileIterName(this_iname, tile_level) tname = self.__getTileSizeName(this_iname, tile_level) tiled_code = self.__getInterTileLoop(iname, tname, rect_lb_exp, rect_ub_exp, this_st_exp, lbody) res_stmts.append((True, [tiled_code])) # mark the loop if it's a loop iterating the full rectangular tiles self.__labelFullCoreTiledLoop(tiled_code, n_outer_loop_inames) # generate the cleanup code (the epilog is already fused) if not this_fully_tiled: lb = ast.IdentExp( self.__getTileIterName(this_iname, tile_level)) cleanup_code = self.ast_util.createForLoop( this_id, lb, this_ub_exp, this_st_exp, this_lbody) res_stmts.append((False, [cleanup_code])) # return the resulting statements return res_stmts # unknown statement err('orio.module.ortil.transformation internal error: unknown type of statement: %s' % stmt.__class__.__name__)
def __convertToASTs(self, dstmts, tile_level, contain_loop, loop_inames, loop_info_table, int_vars): ''' To recursively convert the given list, containing processed statements and possibly if-branching statements, to AST. A sample of the given list is as follows: [s1, t-exp, [s2, s3], [s4]] which represents the following AST: s1; if (t-exp) {s2; s3;} else s4; ''' # initialize the list of ASTs asts = [] # iterating over each list element i = 0 while i < len(dstmts): s = dstmts[i] # if it's an AST if isinstance(s, tuple): is_tiled, stmts = s stmts = [s.replicate() for s in stmts] # already tiled; no need to enclose with an inter-tile loop nest if is_tiled: asts.extend(stmts) # need to enclose with a single-level inter-tile loop nest elif contain_loop: # add single-level tiled loop rev_inames = loop_inames[:] rev_inames.reverse() l = ast.CompStmt(stmts) for iname in rev_inames: tname = self.__getTileSizeName(iname, tile_level) lb_exp = ast.IdentExp( self.__getTileIterName(iname, tile_level)) _, _, _, st_exp, _ = loop_info_table[iname] lbody = l l = self.__getIntraTileLoop(iname, tname, lb_exp, st_exp, lbody) l.fully_tiled = True # to recursively tile all boundary tiles if self.use_boundary_tiling: new_tile_level = min(tile_level - 1, self.recursive_tile_level) if new_tile_level > 0: l = self.__startTiling(l, new_tile_level, int_vars) # insert the tiled loop to the AST list asts.append(l) # need to enclose with a multilevel inter-tile loop nest else: lbody = ast.CompStmt(stmts) st_exps = [] for iname in loop_inames: _, _, _, st_exp, _ = loop_info_table[iname] st_exps.append(st_exp) l = self.__getMultiLevelTileLoop(tile_level, loop_inames, st_exps, lbody) asts.append(l) # increment index i += 1 # if it's an if-statement's test expression else: if not isinstance(s, ast.BinOpExp): err('orio.module.ortil.transformation internal error: a test expression is expected' ) # generate AST for the true statement t1 = self.__convertToASTs(dstmts[i + 1], tile_level, contain_loop, loop_inames, loop_info_table, int_vars) # generate AST for the false statement t2 = self.__convertToASTs(dstmts[i + 2], tile_level, contain_loop, loop_inames, loop_info_table, int_vars) # generate AST for the if-statement test_exp = s.replicate() true_stmt = ast.CompStmt(t1) false_stmt = ast.CompStmt(t2) asts.append(ast.IfStmt(test_exp, true_stmt, false_stmt)) # increment index i += 3 # return the list of ASTs return asts
def p_compound_statement(p): "compound_statement : LBRACE statement_list_opt RBRACE" p[0] = ast.CompStmt(p[2], getLineNumber(p.lineno(1)))
def p_compound_statement(p): "compound_statement : LBRACE statement_list_opt RBRACE" p[0] = ast.CompStmt(p[2], line_no=str(p.lineno(1) + __start_line_no - 1))