diff --git a/chb/astinterface/ASTInterfaceBasicBlock.py b/chb/astinterface/ASTInterfaceBasicBlock.py index f59181a7..374a692f 100644 --- a/chb/astinterface/ASTInterfaceBasicBlock.py +++ b/chb/astinterface/ASTInterfaceBasicBlock.py @@ -192,10 +192,16 @@ def ast_switch_condition( ''' def assembly_ast(self, astree: "ASTInterface") -> AST.ASTStmt: - instrs: List[AST.ASTInstruction] = [] - for (a, i) in sorted(self.instructions.items(), key=lambda p: p[0]): - instrs.extend(i.assembly_ast(astree)) - return astree.mk_instr_sequence(instrs) + # A block with control flow of its own is emitted as fragments rather + # than as a single instruction sequence, so that the condition governing + # a fragment is kept in the low-level ast instead of being flattened + # away with it. + if self.basicblock.has_control_flow(): + self.basicblock.partition_control_flow() + return self.fragmented_assembly_ast(astree) + + return self.linear_assembly_ast( + astree, sorted(self.instructions.values(), key = lambda p:p.iaddr)) def ast_fragment( self, astree: "ASTInterface", frag: "BasicBlockFragment") -> AST.ASTStmt: @@ -208,7 +214,7 @@ def ast_fragment( cinstr = theninstrs[0] brcond = cinstr.ast_cc_condition(astree) if brcond is None: - chklogger.logger.warning( + chklogger.logger.error( "No instruction predicate expression found at address %s", cinstr.iaddr) brcond = astree.mk_temp_lval_expression() @@ -224,19 +230,55 @@ def ast_fragment( instrs = [self.get_instruction(i.iaddr) for i in frag.linear] return self.linear_ast(astree, instrs) - def fragmented_ast(self, astree: "ASTInterface") -> AST.ASTStmt: - + def assembly_ast_fragment( + self, + astree: "ASTInterface", + frag: "BasicBlockFragment") -> AST.ASTStmt: + if not frag.is_predicated: + instrs = [self.get_instruction(i.iaddr) for i in frag.linear] + return self.linear_assembly_ast(astree, instrs) + + theninstrs = [self.get_instruction(i.iaddr) for i in frag.thenbranch] + elseinstrs = [self.get_instruction(i.iaddr) for i in frag.elsebranch] + thenstmt = self.linear_assembly_block_ast(astree, theninstrs) + elsestmt = self.linear_assembly_block_ast(astree, elseinstrs) + spans = [(i.iaddr, i.bytestring) for i in theninstrs + elseinstrs] + cinstr = theninstrs[0] + brcond = cinstr.assembly_ast_cc_condition(astree) + if brcond is None: + chklogger.logger.error( + "No low-level instruction predicate expression found at " + + "address %s", + cinstr.iaddr) + return self.linear_assembly_ast(astree, theninstrs + elseinstrs) + + instrcount = len(theninstrs) + len(elseinstrs) + ifstmt = astree.mk_branch( + brcond, thenstmt, elsestmt, cinstr.iaddr, predicated=instrcount) + astree.add_stmt_span(ifstmt.locationid, spans) + return ifstmt + + def fragmented_ast( + self, + astree: "ASTInterface", + ll: bool = False) -> AST.ASTStmt: if len(self.basicblock.partition) == 0: raise UF.CHBError("Error in fragmented ast") stmts: List[AST.ASTStmt] = [] for (a, bf) in sorted(self.basicblock.partition.items()): - stmt = self.ast_fragment(astree, bf) + if ll: + stmt = self.assembly_ast_fragment(astree, bf) + else: + stmt = self.ast_fragment(astree, bf) stmts.append(stmt) return astree.mk_block(stmts) + def fragmented_assembly_ast(self, astree: "ASTInterface") -> AST.ASTStmt: + return self.fragmented_ast(astree, ll=True) + def ast(self, astree: "ASTInterface") -> AST.ASTStmt: if self.is_trampoline: return self.trampoline_ast(astree) @@ -253,22 +295,32 @@ def ast(self, astree: "ASTInterface") -> AST.ASTStmt: def linear_block_ast( self, astree: "ASTInterface", - instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt: - instrs: List[AST.ASTInstruction] = [] - for i in instritems: - instrs.extend(i.ast(astree)) - instrseq = astree.mk_instr_sequence(instrs) - return astree.mk_block([instrseq]) + instritems: List[ASTInterfaceInstruction], + ll: bool = False) -> AST.ASTStmt: + return astree.mk_block([self.linear_ast(astree, instritems, ll=ll)]) def linear_ast( self, astree: "ASTInterface", - instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt: + instritems: List[ASTInterfaceInstruction], + ll: bool = False) -> AST.ASTStmt: instrs: List[AST.ASTInstruction] = [] for i in instritems: - instrs.extend(i.ast(astree)) + instrs.extend(i.assembly_ast(astree) if ll else i.ast(astree)) return astree.mk_instr_sequence(instrs) + def linear_assembly_block_ast( + self, + astree: "ASTInterface", + instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt: + return self.linear_block_ast(astree, instritems, ll=True) + + def linear_assembly_ast( + self, + astree: "ASTInterface", + instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt: + return self.linear_ast(astree, instritems, ll=True) + def trampoline_block_ast( self, role: str, diff --git a/chb/astinterface/ASTInterfaceInstruction.py b/chb/astinterface/ASTInterfaceInstruction.py index 552ff052..c503f374 100644 --- a/chb/astinterface/ASTInterfaceInstruction.py +++ b/chb/astinterface/ASTInterfaceInstruction.py @@ -151,6 +151,13 @@ def ast_cc_condition(self, astree: "ASTInterface") -> Optional[AST.ASTExpr]: self.ast_cc_condition_prov(astree) return self.hl_ast_cc_condition + def assembly_ast_cc_condition( + self, + astree: "ASTInterface") -> Optional[AST.ASTExpr]: + if self.ll_ast_cc_condition is None: + self.ast_cc_condition_prov(astree) + return self.ll_ast_cc_condition + def ast_switch_condition( self, astree: "ASTInterface") -> Optional[AST.ASTExpr]: