def __init__(self): self._entry = None self._exit = None self._blocks = CustomVector() self._numBlockID = -1 self._indirectGotoBlock = None # special block to contain collective # dispatch for indirect gotos self._syntheticDeclStmts = []
def __init__(self): self._succ = None self._block = None self._badCFG = False self._cfg = CFG() self._sv = CustomVector() # For stmt self._breakJumpTarget = BlockScopePosPair() self._continueJumpTarget = None self._saveBreak = CustomVector() self._saveBlock = CustomVector() self._saveSucc = CustomVector() self._saveContinue = CustomVector() # switch stmt self._defaultCaseBlock = None self._switchTerminatedBlock = None self._switchExclusivelyCovered = None self._switchCond = None # labels self._labels = {} self._backPatchBlocks = CustomVector() self._addressTakenLabels = CustomVector() # for graph self.graph = GraphBuilder() self.curNode = -1
def __init__(self, blockID, C, CFGparent): # Set of statements in the basic block, list of CFGElement self._elements = C # TODO: label = stmt() self._label = None # The terminator for a basic block that indicates the type of control-flow # that accours between a bloack and its successors. self._terminator = None # LoopTarget- some blocks are used to represent the "loop edge" to the start # of a loop from within the loop body. This stmt will be refer to the loop stmt # for such blocks (and be null otherwise) self._loopTarget = None self._blockID = blockID # Predecessors/Sucessors - Keep track of the predecessor / sucessor CFG Blocks self._preds = CustomVector() self._succs = CustomVector() # This shit makes no sense # self._preds.push_back(C) # self._succs.push_back(C) # NoReturn - this bit is set when basic block contains a function call # that is attributed as 'noreturn'. In that case, control cannot technically # ever proceed past this block. All such blocks will have a immediate successor: # the exit block. This allows them to be easily reached from the exit block and # using this bit quickly recognized without scanning the contents of the block self._hasNoReturnElement = False # Parent CFG that owns the CFGBlock self._parent = CFGparent # Control do stmt self._doBodyBlock = False
def createBlock(self): """Creates a new block in the CFG. The CFG owns the block. Returns ------- :obj: `CFGBlock` """ first_block = False if not (self.front) and not (self.back): first_block = True # Create a new block self._numBlockID += 1 # Instantiate the elements of the block elements = CustomVector() B = CFGBlock(self._numBlockID, elements, self) # Insert the block self._blocks.push_back(B) if (first_block): self._entry = self._exit = B return self._blocks.back()
] adjoint_snapshots = [ CustomVecHandle('%s/adjoint_snap%d.pkl' % (out_dir, i), scale=np.pi) for i in mr.range(10) ] # Create random snapshot data nx = 50 ny = 30 nz = 20 x = np.linspace(0, 1, nx) y = np.logspace(1, 2, ny) z = np.linspace(0, 1, nz)**2 if parallel.is_rank_zero(): for snap in direct_snapshots + adjoint_snapshots: snap.put(CustomVector([x, y, z], np.random.random((nx, ny, nz)))) parallel.barrier() # Compute and save balanced POD modes my_BPOD = mr.BPODHandles(inner_product) my_BPOD.sanity_check(direct_snapshots[0]) sing_vals, L_sing_vecs, R_sing_vecs = my_BPOD.compute_decomp( direct_snapshots, adjoint_snapshots) # Choose modes so that BPOD projection has less than 10% error sing_vals_norm = sing_vals / np.sum(sing_vals) num_modes = np.nonzero(np.cumsum(sing_vals_norm) > 0.9)[0][0] + 1 mode_nums = list(mr.range(num_modes)) direct_modes = [ CustomVecHandle('%s/direct_mode%d.pkl' % (out_dir, i)) for i in mode_nums ]
class CFG: def __init__(self): self._entry = None self._exit = None self._blocks = CustomVector() self._numBlockID = -1 self._indirectGotoBlock = None # special block to contain collective # dispatch for indirect gotos self._syntheticDeclStmts = [] def createBlock(self): """Creates a new block in the CFG. The CFG owns the block. Returns ------- :obj: `CFGBlock` """ first_block = False if not (self.front) and not (self.back): first_block = True # Create a new block self._numBlockID += 1 # Instantiate the elements of the block elements = CustomVector() B = CFGBlock(self._numBlockID, elements, self) # Insert the block self._blocks.push_back(B) if (first_block): self._entry = self._exit = B return self._blocks.back() def buildCFG(self, declaration, statement): """ Builds a CFG from an AST :param declaration: DeclDecorator :param statement: StmtDecorator :return: CFG """ builder = CFGBuilder() return builder.buildCFG(declaration, statement) def setEntry(self, block): """ Set the entry block of the CFG. This is typically used only during the CFG construction. :param block: CFGBlock :return: None """ self._entry = block def setIndirectGotoBlock(self, block): """ Set the block used for indirect goto jumps. This is typically used only during CFG construction. :param block: CFGBlock :return: block """ self._indirectGotoBlock = block # # Block Iterators # @property def front(self): return self._blocks.front() @property def back(self): return self._blocks.back() def begin(self): return self._blocks.begin() def rbegin(self): return self._blocks.rbegin() def getEntry(self): return self._entry def getExit(self): return self._exit def getIndirectGotoBlock(self): return self._indirectGotoBlock def addSyntheticDeclStmt(self, synthetic, source): """ Records a synthetic DeclStmt and the DeclStmt it was constructed from. The CFG uses synthetic DeclStmt when a single AST DeclStmt contains multiple decls :return: """ assert synthetic.isSingleDeclaration( ), "Can handle single declaration only" self._syntheticDeclStmts.append([source, synthetic]) def synthetic_stmt_begin(self): """ Iterates over synthetic DeclStmts in the CFG Each element is a [synthetic statement, source statement] pair :return: [synthetic statement, source statement] """ for key, value in self._syntheticDeclStmts.iteritems(): yield [key, value] # # CFG Introspection # def getNumBlocIDs(self): """ Returns the total number of BlockIDs allocated (start at 1) :return: int """ return self._numBlockID def size(self): """ Returns the total number of CFGBlocks within the CFG :return: int """ return self._numBlockID def printer(self): output = "" for b in self._blocks.begin(): output += b.printer() output += '=>Entry: ' + str(self._entry.getBlockID()) + '\n' output += '<=Exit: ' + str(self._exit.getBlockID()) + '\n' return output
class CFGBuilder: def __init__(self): self._succ = None self._block = None self._badCFG = False self._cfg = CFG() self._sv = CustomVector() # For stmt self._breakJumpTarget = BlockScopePosPair() self._continueJumpTarget = None self._saveBreak = CustomVector() self._saveBlock = CustomVector() self._saveSucc = CustomVector() self._saveContinue = CustomVector() # switch stmt self._defaultCaseBlock = None self._switchTerminatedBlock = None self._switchExclusivelyCovered = None self._switchCond = None # labels self._labels = {} self._backPatchBlocks = CustomVector() self._addressTakenLabels = CustomVector() # for graph self.graph = GraphBuilder() self.curNode = -1 def addPrefix(self, ch): self.graph.prefixString.append(ch) def removePrefix(self): self.graph.prefixString = self.graph.prefixString[:-1] def buildCFG(self, Decl, statement=None): """ Constructs a CFG from an AST (a Stmt). The ownership of the returned CFG is transferred to the caller. Parameters ---------- statement : :obj:`statement` This is the AST, and can represent any arbitrary statement. For a single expression or a function body (compound statement). Returns ------- :obj:`CFG` A cfg object. None If the CFG construction fails. """ self.graph.prefixString = [] if not statement: return None # print self.graph # print self.curNode # Create an empty block that will serve as the exit block for the CFG. # Is the first block added to the CFG and registered as exit block self._succ = self.createBlock() # Exit block is empty (None) create the next blocks lazily self._block = None # Visit the statements and create the CFG B = self.accepts(statement) # if self._badCFG: # return None # if B: # self._succ = B # backpatch the gotos whose label -> block mappings we didn't know when we # encountered them for e in self._backPatchBlocks.begin(): b = e.block g = b.getTerminator() try: li = self._labels[g.getLabel()] except KeyError: continue self.addSucessor(b, li.block) # Add successor to the Indirect Goto Dispatch block (if we have one) b = self._cfg.getIndirectGotoBlock() if (b is not None): for l in self._addressTakenLabels.begin(): # Look the target block try: li = self._labels[l] except KeyError: # If there is no target block that contains label, then we are looking # at an incomplete AST. Handle this by not registering a successor continue self.addSucessor(b, li.block) self._cfg.setEntry(self.createBlock()) self.graph.printGraph() return self._cfg def createBlock(self, add_sucessor=True): """Used to lazily create blocks that are connected to the current (global) succesor. Parameters ---------- add_sucessor : bool Variable that indicates whether or not a sucessor is added. Returns ------- :obj:`CFGBlock` Returns the CFG block object. """ B = self._cfg.createBlock() if add_sucessor and self._succ: self.addSucessor(B, self._succ) return B def addSucessor(self, B, S, IsReachable=True): B.addSuccessor(CFGBlock.AdjacentBlock(S, IsReachable)) def accepts(self, S): if not S: return return self.visit(S) def visit(self, S=None): """Visit - Walk the subtree of a statement and add extra blocks for ternary operators, &&, and ||. We also process "," and DeclStmts (which may contain nested control-flow). Paramaters ---------- S : Cursor to the statement in the AST. Returns ------- :obj:`CFGBlock` Returns the CFG block object. """ if not S: return None kind = S.kind() if (type_stmt.COMPOUND_STMT == kind): # self.graph.addNode(S) return self.visitCompoundStmt(S) elif (type_stmt.DECL_STMT == kind): return self.visitDeclStmt(S) elif (type_stmt.IF_STMT == kind): return self.visitIfStmt(S) elif (type_stmt.BINARY_OPERATOR == kind): return self.visitBinaryOperator(S) elif (type_stmt.COMPOUND_ASSIGMENT_OP == kind): return self.visitCompoundAssignmentOp(S) elif (type_stmt.FOR_STMT == kind): return self.visitForStmt(S) elif (type_stmt.NULL_STMT == kind): return self.visitNullStmt() elif (type_stmt.WHILE_STMT == kind): return self.visitWhileStmt(S) elif (type_stmt.IMPL_CAST_EXPR == kind): return self.visitImplicitCastExpr(S) elif (type_stmt.CSTYLE_CAST_EXPR == kind): return self.visitCstyleCastExpr(S) elif (type_stmt.SWITCH_STMT == kind): return self.visitSwitchStmt(S) elif (type_stmt.CASE_STMT == kind): return self.visitCaseStmt(S) elif (type_stmt.BREAK_STMT == kind): return self.visitBreakStmt(S) elif (type_stmt.DEFAULT_STMT == kind): return self.visitDefaultStmt(S) elif (type_stmt.ADDR_LABEL_EXPR == kind): return self.visitAddrLabelExpr(S) elif (type_stmt.CALL_EXPR == kind): return self.visitCallExpr(S) elif (type_stmt.DO_STMT == kind): return self.visitDoStmt(S) elif (type_stmt.CONTINUE_STMT == kind): return self.visitContinueStmt(S) elif (type_stmt.GOTO_STMT == kind): return self.visitGoToStmt(S) elif (type_stmt.LABEL_STMT == kind): return self.visitLabelStmt(S) elif (type_stmt.CONDITIONAL_OPERATOR == kind or type_stmt.BINARY_CONDITIONAL_OPERATOR == kind): return self.visitConditionalOperator(S) elif (type_stmt.RETURN_STMT == kind): return self.visitReturnStmt(S) elif (type_stmt.PAREN_EXPR == kind): return self.visitParenExpr(S) elif (type_stmt.MEMBER_REF_EXPR == kind): return self.visitMemberRefExpr(S) elif (type_stmt.INDIRECT_GOTO_STMT == kind): return self.visitIndirectGoToStmt(S) elif (type_stmt.STMT_EXPR == kind): return self.visitStmtExpr(S) elif (type_stmt.COMPOUND_LITERAL_EXPR == kind): return self.visitCompoundLiteralExpr(S) elif (type_stmt.UNARY_OPERATOR == kind): self.visitUnaryStmt(S) elif (type_stmt.DECL_REF_EXPR == kind): # print S.printer() self.graph.addVariables(self.curNode, S.value()) elif (type_stmt.INTEGER_LITERAL == kind): self.graph.addConstant(self.curNode, S.value()) elif (type_stmt.CHARACTER_LITERAL == kind): self.graph.addConstant(self.curNode, S.value()) elif (type_stmt.FLOATING_LITERAL == kind): self.graph.addConstant(self.curNode, S.value()) elif (type_stmt.STRING_LITERAL == kind): self.graph.addConstant(self.curNode, S.value()) elif (type_stmt.VAR_DECL == kind): for v in S.value(): print v self.graph.addVariables(self.curNode, v) # elif (type_stmt.BLOCK_EXPR == kind): # return self.visitBlockExpr(S) # elif (type_stmt.LAMBDA_EXPR == kind): # return self.visitLambdaExpr(S) else: return self.visitStmt(S) def visitCompoundLiteralExpr(self, S): # self.autoCreateBlock() # self.appendStmt(self._block, S) se = S.getSubExpr() if (se is not None): return self.accepts(se) def visitStmtExpr(self, S): """ Utility method to handle (nested) statements expressions (a GCC extension) :param S: Statement :return: visit """ # self.autoCreateBlock() # self.appendStmt(self._block, S) return self.visitCompoundStmt(S.getSubStmt()) def visitIndirectGoToStmt(self, I): # Lazily create the indirect-goto dispatch block if there isn't one already IBlock = self._cfg.getIndirectGotoBlock() if (IBlock is None): IBlock = self.createBlock(False) self._cfg.setIndirectGotoBlock(IBlock) # IndirectGoto is a control-flow statement. Thus we stop processing the current # block and create a new one if (self._badCFG): return None self._block = self.createBlock(False) self._block.setTerminator(I) self.addSucessor(self._block, IBlock) return self.accepts(I.getTarget()) def visitMemberRefExpr(self, M): # self.autoCreateBlock() # self.appendStmt(self._block, M) return self.visit(M.getBase()) def visitParenExpr(self, P): # self.autoCreateBlock() # self.appendStmt(self._block, P) return self.visit(P.getSubExpr()) def visitReturnStmt(self, R): """If we were in the middle of a block we stop processing that block. Notes ----- If a 'return' appears in the middle of a block, this means that the code afterwards is DEAD (unreachable). We still keep a basic block for that code; a simple 'mark-and-sweep' from the entry block will be able to resport such dead blocks. """ # Create the new block self.graph.addFeature(self.curNode, 'return') # Add the return statement to the block. This may create a new blocks if R contains # control-flow (shor-circuit operations) return self.visitStmt(R) def visitNoRecurse(self, E): # self.autoCreateBlock() # self.appendStmt(self._block, E) return None # def visitBlockExpr(self, E): # lastBlock = self.visitNoRecurse(E) def visitConditionalOperator(self, C): if (C.kind() == type_stmt.BINARY_CONDITIONAL_OPERATOR): opaqueValue = C.getOpaqueValue() else: opaqueValue = None trueExpr = C.getTrueExpr() falseExpr = C.getFalseExpr() cond = C.getCond() self.accepts(opaqueValue) self.accepts(cond) self.accepts(trueExpr) self.accepts(falseExpr) def visitLabelStmt(self, L): # Get the block of the labeled statement. Add it to our map (self._labels) self.accepts(L.getSubStmt()) labelBlock = self._block if not labelBlock: labelBlock = self.createBlock( ) # This can happen when the body is empty self._labels[L.value()] = BlockScopePosPair(labelBlock) # labels partition blocks, so this is the end of the basic block we were # processing (L is the blocks label). Because this is label (and we have # already processed the substatement) there is no extra control-flow to worry # about labelBlock.setLabel(L) if self._badCFG: return None # We set Block to NULL to allow lazy creation of a new block (if necessary); self._block = None # This block is now the implicit successor of other blocks self._succ = labelBlock return labelBlock def visitGoToStmt(self, G): """Goto is a control-flow statement. Thus we stop processing the current block and create a new one.""" if self._badCFG: return None self._block = self.createBlock(False) self._block.setTerminator(G) # If we already know the mapping to the label block add the sucessor now try: b = self._labels[G.getLabel()] except KeyError: b = None if not b: # We will need to backpatch this block later. self._backPatchBlocks.push_back(BlockScopePosPair(self._block)) else: self.addSucessor(self._block, b.block) return self._block def visitContinueStmt(self, C): """"continue" is a control-flow statement. Thus we stop processing the current block.""" self.graph.addFeature(self.curNode, 'continue') def visitDoStmt(self, D): loopSuccessor = None # "do ... while" is a control-flow statement. Thus we stop processing the current # block prevNode = self.graph.lastNode cond = D.getCond() body = D.getBody() self.accepts(cond) self.accepts(body) # TODO: REVISAR Y MEJORAR def visitCallExpr(self, C): """Compute de callee type. TODO ---- calleType = C.getCalle().getType() #TODO TODO If this is a call to a no-return function, this stops the block here bool """ params = C.getParams() callee = C.getCallee() for param in params: self.accepts(param) return self.accepts(C.getCallee()) def visitAddrLabelExpr(self, A): # self._addressTakenLabels.push_back(A.getLabel()) # self.autoCreateBlock() # self.appendStmt(self._block, A) return self._block def visitStmt(self, S): # self.autoCreateBlock() # self.appendStmt(self._block, S) # I don't want the index of the array subscript in the CFG # if(S.kind() == type_stmt.ARRAY_SUBSCRIPT_EXPR): # return self.accepts(S.getSubExpr()) return self.visitChildren(S) def visitChildren(self, S): B = self._block # Visit the children in their reverse order so that they appear in left-to-right (natural) # order in the CFG childrens = S.get_children() it = childrens.begin() for c in it: # if(self.isStmt(c)): R = self.visit(c) # if R: # B = R return B def visitCstyleCastExpr(self, S): # self.autoCreateBlock() # self.appendStmt(self._block, S) if (S.getSubExpr() and hasattr(S.getSubExpr(), 'value')): self.graph.addVariables(self.curNode, S.getSubExpr().value()) return self.accepts(S.getSubExpr()) def visitImplicitCastExpr(self, S): # self.autoCreateBlock() # self.appendStmt(self._block, S) # print S.getSubExpr().kind() return self.accepts(S.getSubExpr()) def isStmt(self, S): type = S.kind() if (type == type_stmt.COMPOUND_STMT or type == type_stmt.IF_STMT or type == type_stmt.FOR_STMT or type == type_stmt.DECL_STMT): return True else: return False def visitCompoundStmt(self, compoundStatement): """visitCompoundStmt - when a compound statement is reached visit the stmts inside it. :param S: StmtDecorator :return: CFGBlock """ # creating a binding to block object lastBlock = self._block # iterating through the compound statement elements = compoundStatement.child_iterator() for e in elements: # lastNode = self.graph.lastNode curNode = self.graph.createNode('data2') self.curNode = curNode if (len(e.printer()) < 40): self.graph.addString(self.curNode, e.printer()) newBlock = self.accepts(e) # if newBlock: # lastBlock = newBlock # if self._badCFG: # return None return lastBlock def visitDeclStmt(self, DS): # TODO: check if the declaration is label, if so elude it (return block) # If the DS refers to a single declaration if DS.isSingleDeclaration(): self.visitDeclSubExpr(DS) return elements = DS.child_iterator() for e in elements: newStmt = SyntheticDeclStmt(DS.get_cursor(), e) B = self.visitDeclSubExpr(newStmt) def visitDeclSubExpr(self, DS): """Utility method to add block-level expressions for DeclStmts and initializers in them :param DS: StmtDecorator :return: """ assert DS.isSingleDeclaration(), "Can handle single declarations only" VD = DS.getSingleDeclaration() # print VD.kind() init = None # print VD.printer() # if not VD: # init = VD.getInit() # self.accepts(DS) if (VD is None): return childs = VD.get_children().begin() val = VD.value() if (len(val) > 1): self.graph.addVariables(self.curNode, val[0]) for child in childs: print child self.accepts(child) def visitIfStmt(self, if_stmt): """ We may see an if stmt in the middle of a basic block, or it may be the first statement we are processing. In either case, we create a new basic block. First, we create the blocks for the then...else statements, and then we create the block containing the if stmt. If we were in the middle of a block, we stop processing that block. That block is then the implicit successor for the then and else clauses. """ # The block we were processing is now finished. Make it the successor block ifNode = self.curNode # print if_stmt.printer() self.graph.addFeature(ifNode, "icn") cond = if_stmt.getCond() then = if_stmt.getThen() elseBody = if_stmt.getElse() self.graph.addString(ifNode, cond.printer()[:40]) self.accepts(cond) self.addPrefix('if') self.accepts(then) self.removePrefix() self.accepts(elseBody) def visitLogicalOperator(self, bOperator, stmt=None, trueBlock=None, falseBlock=None): # Introspect the RHS. if it is a nested logical operation, we recursively build te # CFG using this funcrion. Otherwise, resort to default CFG construction behaviour # print bOperator.value() # self.graph.addOperator(self.curNode, bOperator.value()) rhs = bOperator.getRHS() self.accepts(rhs) lhs = bOperator.getLHS() self.accepts(lhs) # def tryEvaluateBool(self, S): # """ Try and evaluate the Stmt and return 0 or 1 if we can evaluate to know value # otherwise return -1. # """ # # TODO: Build options # if S.kind() is type_stmt.BINARY_OPERATOR: # bop = S # if bop.isLogicalOp(): # # Todo: comprobar si esta en cache # result = self.evaluateAsBooleanCondition(S) # TODO # # Todo: guardar en cache # return result # else: # # For 'x & 0' and 'x * 0' we can determine that the value is # # always false # if(bop.value() == '*' or bop.value() == '&'): # # If either operand is 0 value must be false # if(bop.getLHS.kind() == type_stmt.INTEGER_LITERAL and bop.getLHS.value() == 0): # return TryResult(False) # if(bop.getHS.kind() == type_stmt.INTEGER_LITERAL and bop.getRHS.value() == 0): # return TryResult(False) # return self.evaluateAsBooleanCondition(S) # # def evaluateAsBooleanCondition(self, expression): # if expression.kind() is type_stmt.BINARY_OPERATOR: # bop = expression # if bop.isLogicalOp(): # lhs = self.tryEvaluateBool(bop.getLHS()) # if lhs.isKnown(): # # We were able to evaluate the LHS, see if we can get away with not # # evaluating the RHS: '0 && X' => 0, '1 || X' => 1 # if lhs.isTrue() and bop.value() == '||': # return lhs.isTrue() # rhs = self.tryEvaluateBool(bop.getRHS()) # if rhs.isKnown(): # if bop.value() == '||': # return lhs.isTrue() or rhs.isTrue() # else: # return lhs.isTrue() and rhs.isTrue() # else: # rhs = self.tryEvaluateBool(bop.getRHS()) # if rhs.isKnown(): # # We can't evaluate the LHS; however, sometimes the result # # is determined by RHS: 'X && 0' => 0, 'X || 1' => 1 # if rhs.isTrue() and bop.value() == '||': # return rhs.isTrue() # else: # # bopRes = self.checkIncorrectLogicOperator(bop) # TODO: HACERLA # # if(bopRes.isKnown()): # return TryResult() # bopRes.isTrue() FIXME # return TryResult() # elif bop.value() == '==' or bop.value() == '!=': # #bopRes = self.checkIncorrectEqualityOperator(bop) # # if (bopRes.isKnown()): # return TryResult() # bopRes.isTrue() FIXME # elif bop.value() == '<' or bop.value() == '>' or bop.value() == '>=' or bop.value() == '<=': # #bopRes = self.checkIncorrectRelationaloperator(bop) # # if(bopRes.isKnown()): # # bopRes.isTrue() #FIXME: De momento lo todomo todo como uknown, hay que hacerla bien # return TryResult() # # result = None # # if(expression.EvaluateAsBooleanCondition(result) == True): # TODO(Optional) # # return result # return TryResult() def visitBinaryOperator(self, B): # && or || self.graph.addString(self.curNode, B.printer()) if (B.value() is not None): self.graph.addOperator(self.curNode, B.value()) # print B.value() if B.isLogicalOp(): return self.visitLogicalOperator(B) if B.value() == ',': # self.autoCreateBlock() # self.appendStmt(self._block, B) self.accepts(B.getRHS()) return self.accepts(B.getLHS()) if B.isAssignmentOp(): # self.autoCreateBlock() # self.appendStmt(self._block, B) self.graph.inLHS = True self.accepts(B.getLHS()) self.graph.inLHS = False return self.accepts(B.getRHS()) # TODO: ALWAYS ADD # self.autoCreateBlock() # self.appendStmt(self._block, B) # print B.getLHS().getSubExpr().value() numOfOps = B.getLen() if (numOfOps > 1): oldNode = self.curNode rhNode = self.graph.createNode('graph') self.curNode = rhNode rBlock = self.accepts(B.getRHS()) lhNode = self.graph.createNode('data2') self.curNode = lhNode lBlock = self.accepts(B.getLHS()) self.graph.addLink(oldNode, rhNode) self.graph.addLink(oldNode, lhNode) else: rBlock = self.accepts(B.getRHS()) lBlock = self.accepts(B.getLHS()) def visitCompoundAssignmentOp(self, C): # TODO: ALWAYS ADD # self.autoCreateBlock() # self.appendStmt(self._block, C) self.graph.addOperator(self.curNode, C.specific_kind()) rBlock = self.accepts(C.getRHS()) lBlock = self.accepts(C.getLHS()) # If visiting RHS causes us to finish 'Block' eg: the RHS is a StmtExpr # containing a DoStmt, and the LHS doesn't create a new block, the we should # return RBlock. Otherwise we'll incorrectly recur Null if lBlock: return lBlock else: return rBlock def visitForStmt(self, F): # 'For' is a control flow statement, thus we stop processing the current block # i = F.getInit() # print i.printer() cond = F.getCond() body = F.getBody() inc = F.getInc() # self.accepts(i) # self.graph.createNode('data2', 'cn') # self.accepts(cond) # self.addPrefix('lp') # self.accepts(body) # self.accepts(inc) # self.removePrefix() # if i: # self._block = self.createBlock() # initBlock = self.accepts(i) condNode = self.graph.createNode('data2') self.graph.addFeature(condNode, 'cn') self.accepts(i) self.accepts(cond) self.addPrefix('lp') IncNode = self.graph.createNode('data2') self.curNode = IncNode self.accepts(inc) self.accepts(body) self.removePrefix() return None def visitNullStmt(self): return None def visitWhileStmt(self, W): whileCnNode = self.curNode cond = W.getCond() body = W.getBody() self.graph.addFeature(whileCnNode, 'cn') self.graph.addFeature(whileCnNode, 'loop') self.accepts(cond) self.addPrefix('lp') self.accepts(body) self.removePrefix() return None # switchFlag = 0 def visitSwitchStmt(self, terminator): # 'Switch' is a control-flow statment. Thus we stop processing the current block tmBody = terminator.getBody() tmCond = terminator.getCond() self.graph.addFeature(self.curNode, 'switch') self.accepts(tmCond) self.addPrefix('sb') self.accepts(tmBody) self.removePrefix() # def tryEvaluate(self, S): # return self.evaluateAsRValue(S) # # def evaluateAsRValue(self, expr): # field = self.fastEvaluateAsRValue(expr)[1] # isConst = field is not False # if isConst: # return self.fastEvaluateAsRValue(expr) # return[False, False] # # def fastEvaluateAsRValue(self, expr): # # Fast evaluation of integer literals, since we sometimes see files # # containing vast quantities of these # if expr.kind() is type_stmt.INTEGER_LITERAL: # return [True, expr.value()] # # This case should be rare # if expr.kind() is type_stmt.NULL_STMT: # return [True, False] # # TODO: FOR ARRAY TYPES # return[False, False] # # # caseExit = 0 def visitCaseStmt(self, CS): # CaseStmts are essentially labels, so they are the first statement in a block sub = CS.getSubStmt() val = CS.value() self.graph.addFeature(self.curNode, val) self.graph.addFeature(self.curNode, 'case') self.accepts(sub) def visitUnaryStmt(self, S): self.graph.addOperator(self.curNode, S.value()) self.visit(S.getOperand()) # def shouldAddCase(self, switchExclusivelyCovered, switchCond, CS): # if not switchCond: # return True # addCase = False # if not switchExclusivelyCovered: # if type(switchCond) is int: # # Evaluate the LHS of the case value # lhsInt = self.evaluateAsRValue(CS.getLHS()) # TODO # condInt = switchCond # if condInt == lhsInt: # addCase = True # self._switchExclusivelyCovered = True # elif condInt > lhsInt: # rhs = CS.getRHS() # if rhs: # # Evaluate the RHS of the case value # v2 = self.evaluateKnownConstInt(rhs) # if v2 >= condInt: # addCase = True # self._switchExclusivelyCovered = True # else: # addCase = True # return addCase def evaluateKnownConstInt(self, S): """ Call EvaluateAsRValue and return the folded integer. This must be called on an expression that constant folds to an integer """ result = self.evaluateAsRValue(S) if result[0]: return result[1] def visitBreakStmt(self, B): # Break is a control-flow stmt. Thus we stop processsing the current block self.graph.addFeature(self.curNode, 'break') # Now create a new block that ends with the break stmt # return self._block def visitDefaultStmt(self, terminator): substmt = terminator.getSubStmt() def appendStmt(self, CFGBlock, stmt): """Interface to CFGBlock - adding CFGElements.""" CFGBlock.appendStmt(stmt)
def print_succs(self): l = CustomVector() for e in self._succs.begin(): l.push_back(e.getReachableBlock().getBlockID()) l.printer()
class CFGBlock: class AdjacentBlock: """This class represents a potential adjacent block in the CFG. It encodes whether or ont the block is actually reachable, or can be proved to be tribially unreachable. For some cases it allows one to encode scenarios where a block was substituted because the original (now alternate) block is unreachable.""" class Kind(Enum): AB_Normal = 0 AB_Unreachable = 1 AB_Alternate = 2 def __init__(self, CFGBlock, isReachable=None, alternateBlock=None): self._reachableBlock = None self._unreachableBlock = [None, None] if isReachable and not (alternateBlock): self.reachable(CFGBlock, isReachable) elif not (isReachable) and alternateBlock: self.alternate(CFGBlock, alternateBlock) def reachable(self, block, isReachable): if isReachable: self._reachableBlock = block else: self._reachableBlock = None # Inicialization of _unreachableBlock if not isReachable: self._unreachableBlock[0] = block else: self._unreachableBlock[0] = None if block and isReachable: self._unreachableBlock[1] = self.Kind.AB_Normal else: self._unreachableBlock[1] = self.Kind.AB_Unreachable def alternate(self, block, alternateBlock): self._reachableBlock = block if block == alternateBlock: self._unreachableBlock[0] = None self._unreachableBlock[1] = self.Kind.AB_Alternate else: self._unreachableBlock[0] = alternateBlock self._unreachableBlock[1] = self.Kind.AB_Normal def getReachableBlock(self): """Get the reachable block if one exists.""" return self._reachableBlock def getPossiblyUnreachableBlock(self): """Get potentially unreachable block.""" return self._unreachableBlock def isReachable(self): kind = self._unreachableBlock[1] return kind is self.Kind.AB_Normal or kind is self.Kind.AB_Alternate def __init__(self, blockID, C, CFGparent): # Set of statements in the basic block, list of CFGElement self._elements = C # TODO: label = stmt() self._label = None # The terminator for a basic block that indicates the type of control-flow # that accours between a bloack and its successors. self._terminator = None # LoopTarget- some blocks are used to represent the "loop edge" to the start # of a loop from within the loop body. This stmt will be refer to the loop stmt # for such blocks (and be null otherwise) self._loopTarget = None self._blockID = blockID # Predecessors/Sucessors - Keep track of the predecessor / sucessor CFG Blocks self._preds = CustomVector() self._succs = CustomVector() # This shit makes no sense # self._preds.push_back(C) # self._succs.push_back(C) # NoReturn - this bit is set when basic block contains a function call # that is attributed as 'noreturn'. In that case, control cannot technically # ever proceed past this block. All such blocks will have a immediate successor: # the exit block. This allows them to be easily reached from the exit block and # using this bit quickly recognized without scanning the contents of the block self._hasNoReturnElement = False # Parent CFG that owns the CFGBlock self._parent = CFGparent # Control do stmt self._doBodyBlock = False def front(self): return self._elements.front() def back(self): return self._elements.back() def begin(self): return self._elements.begin() def rbegin(self): return self._elements.rbegin() def size(self): return self._elements.size() def empty(self): return self._elements.empty() def removeElement(self, index): self._elements.popAtIndex(index) # CFG ITERATORS def pred_begin(self): return self._preds.begin() def preds_rbegin(self): return self._preds.rbegin() def succs_begin(self): return self._succs.begin() def succs_rbegin(self): return self._succs.rbegin() def succs_size(self): return self._succs.size() def succs_empty(self): return self._succs.empty() def preds_size(self): return self._preds.size() def preds_empty(self): return self._preds.empty() def removeSucc(self, index): return self._succs.popAtIndex(index) def removeAllSuccs(self): for s in range(self._succs.size()): self._succs.pop_back() def removePred(self, index): return self._preds.popAtIndex(index) # MANIPULATION OF BLOCK CONTENTS def setTerminator(self, term): self._terminator = term def setLabel(self, statement): self._label = statement def setDoBodyBlock(self): self._doBodyBlock = True def isDoBodyBlock(self): return self._doBodyBlock def setLoopTarget(self, stmtLoopTarjet): self._loopTarget = stmtLoopTarjet def setHasNoReturnElement(self): self._hasNoReturnElement = True def getTerminator(self): return self._terminator def getLoopTarjet(self): return self._loopTarget def getLabel(self): return self._label def hasNoReturnElement(self): return self._hasNoReturnElement def getBlockID(self): return self._blockID def getParent(self): return self._parent # TODO: METODOS DE SALIDA POR PANTALLA def print_preds(self): l = CustomVector() for e in self._preds.begin(): l.push_back(e.getReachableBlock().getBlockID()) l.printer() def print_succs(self): l = CustomVector() for e in self._succs.begin(): l.push_back(e.getReachableBlock().getBlockID()) l.printer() def printer(self): """ Pretty-print a block :return: String """ output = "" # Printing the block ID output += colored("[B" + str(self._blockID) + "]\n", "red") # Printing the statements i = 0 for e in self._elements.rbegin(): output += str(i) + ": " + e.printer() + "\n" i += 1 # Printing predecessors for e in self._preds.begin(): if (e.getReachableBlock() is not None): output += colored( "Preds " + "(" + str(self._preds.size()) + ")" + ": " + "B" + str(e.getReachableBlock().getBlockID()) + "\n", "cyan") # Printing predecessors for e in self._succs.begin(): if (e.getReachableBlock() is not None): output += colored( "Succs " + "(" + str(self._succs.size()) + ")" + ": " + "B" + str(e.getReachableBlock().getBlockID()) + "\n", "magenta") output += "\n" return output def addSuccessor(self, succ): """Adds a (potentially unreachable) sucessor block to the current block. Parameters ---------- succ : :obj:`AdjacentBlock` """ # The block is reachable B = succ.getReachableBlock() if B: B._preds.push_back( self.AdjacentBlock(self, isReachable=succ.isReachable())) # The block is unreachable unreachableB = succ.getPossiblyUnreachableBlock() if unreachableB[0]: unreachableB[0]._preds.push_back( self.AdjacentBlock(self, isReachable=False)) # Adding the block as a sucessor self._succs.push_back(succ) def appendStmt(self, stmt): self._elements.push_back(stmt)