1*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2014 The Android Open Source Project 2*795d594fSAndroid Build Coastguard Worker# 3*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*795d594fSAndroid Build Coastguard Worker# 7*795d594fSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*795d594fSAndroid Build Coastguard Worker# 9*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*795d594fSAndroid Build Coastguard Worker# limitations under the License. 14*795d594fSAndroid Build Coastguard Worker 15*795d594fSAndroid Build Coastguard Workerfrom collections import namedtuple 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Workerfrom common.immutables import ImmutableDict 18*795d594fSAndroid Build Coastguard Workerfrom common.logger import Logger 19*795d594fSAndroid Build Coastguard Workerfrom file_format.checker.struct import TestStatement 20*795d594fSAndroid Build Coastguard Workerfrom match.line import match_lines, evaluate_line 21*795d594fSAndroid Build Coastguard Worker 22*795d594fSAndroid Build Coastguard WorkerMatchScope = namedtuple("MatchScope", ["start", "end"]) 23*795d594fSAndroid Build Coastguard WorkerMatchInfo = namedtuple("MatchInfo", ["scope", "variables"]) 24*795d594fSAndroid Build Coastguard Worker 25*795d594fSAndroid Build Coastguard Worker 26*795d594fSAndroid Build Coastguard Workerclass MatchFailedException(Exception): 27*795d594fSAndroid Build Coastguard Worker def __init__(self, statement, line_no, variables): 28*795d594fSAndroid Build Coastguard Worker self.statement = statement 29*795d594fSAndroid Build Coastguard Worker self.line_no = line_no 30*795d594fSAndroid Build Coastguard Worker self.variables = variables 31*795d594fSAndroid Build Coastguard Worker 32*795d594fSAndroid Build Coastguard Worker 33*795d594fSAndroid Build Coastguard Workerclass BadStructureException(Exception): 34*795d594fSAndroid Build Coastguard Worker def __init__(self, msg, line_no): 35*795d594fSAndroid Build Coastguard Worker self.msg = msg 36*795d594fSAndroid Build Coastguard Worker self.line_no = line_no 37*795d594fSAndroid Build Coastguard Worker 38*795d594fSAndroid Build Coastguard Worker 39*795d594fSAndroid Build Coastguard Workerclass IfStack: 40*795d594fSAndroid Build Coastguard Worker """ 41*795d594fSAndroid Build Coastguard Worker The purpose of this class is to keep track of which branch the cursor is in. 42*795d594fSAndroid Build Coastguard Worker This will let us know if the line read by the cursor should be processed or not. 43*795d594fSAndroid Build Coastguard Worker Furthermore, this class contains the methods to handle the CHECK-[IF, ELIF, ELSE, FI] 44*795d594fSAndroid Build Coastguard Worker statements, and consequently update the stack with new information. 45*795d594fSAndroid Build Coastguard Worker 46*795d594fSAndroid Build Coastguard Worker The following elements can appear on the stack: 47*795d594fSAndroid Build Coastguard Worker - BRANCH_TAKEN: a branch is taken if its condition evaluates to true and 48*795d594fSAndroid Build Coastguard Worker its parent branch was also previously taken. 49*795d594fSAndroid Build Coastguard Worker - BRANCH_NOT_TAKEN_YET: the branch's parent was taken, but this branch wasn't as its 50*795d594fSAndroid Build Coastguard Worker condition did not evaluate to true. 51*795d594fSAndroid Build Coastguard Worker - BRANCH_NOT_TAKEN: a branch is not taken when its parent was either NotTaken or NotTakenYet. 52*795d594fSAndroid Build Coastguard Worker It doesn't matter if the condition would evaluate to true, that's not even checked. 53*795d594fSAndroid Build Coastguard Worker 54*795d594fSAndroid Build Coastguard Worker CHECK-IF is the only instruction that pushes a new element on the stack. CHECK-ELIF 55*795d594fSAndroid Build Coastguard Worker and CHECK-ELSE will update the top of the stack to keep track of what's been seen. 56*795d594fSAndroid Build Coastguard Worker That means that we can check if the line currently pointed to by the cursor should be 57*795d594fSAndroid Build Coastguard Worker processed just by looking at the top of the stack. 58*795d594fSAndroid Build Coastguard Worker CHECK-FI will pop the last element. 59*795d594fSAndroid Build Coastguard Worker 60*795d594fSAndroid Build Coastguard Worker `BRANCH_TAKEN`, `BRANCH_NOT_TAKEN`, `BRANCH_NOT_TAKEN_YET` are implemented as positive integers. 61*795d594fSAndroid Build Coastguard Worker Negated values of `BRANCH_TAKEN` and `BRANCH_NOT_TAKEN` may be appear; `-BRANCH_TAKEN` and 62*795d594fSAndroid Build Coastguard Worker `-BRANCH_NOT_TAKEN` have the same meaning as `BRANCH_TAKEN` and `BRANCH_NOT_TAKEN` 63*795d594fSAndroid Build Coastguard Worker (respectively), but they indicate that we went past the ELSE branch. Knowing that, we can 64*795d594fSAndroid Build Coastguard Worker output a precise error message if the user creates a malformed branching structure. 65*795d594fSAndroid Build Coastguard Worker """ 66*795d594fSAndroid Build Coastguard Worker 67*795d594fSAndroid Build Coastguard Worker BRANCH_TAKEN, BRANCH_NOT_TAKEN, BRANCH_NOT_TAKEN_YET = range(1, 4) 68*795d594fSAndroid Build Coastguard Worker 69*795d594fSAndroid Build Coastguard Worker def __init__(self): 70*795d594fSAndroid Build Coastguard Worker self.stack = [] 71*795d594fSAndroid Build Coastguard Worker 72*795d594fSAndroid Build Coastguard Worker def can_execute(self): 73*795d594fSAndroid Build Coastguard Worker """ 74*795d594fSAndroid Build Coastguard Worker Returns true if we're not in any branch, or the branch we're 75*795d594fSAndroid Build Coastguard Worker currently in was taken. 76*795d594fSAndroid Build Coastguard Worker """ 77*795d594fSAndroid Build Coastguard Worker if self._is_empty(): 78*795d594fSAndroid Build Coastguard Worker return True 79*795d594fSAndroid Build Coastguard Worker return abs(self._peek()) == IfStack.BRANCH_TAKEN 80*795d594fSAndroid Build Coastguard Worker 81*795d594fSAndroid Build Coastguard Worker def handle(self, statement, variables): 82*795d594fSAndroid Build Coastguard Worker """ 83*795d594fSAndroid Build Coastguard Worker This function is invoked if the cursor is pointing to a 84*795d594fSAndroid Build Coastguard Worker CHECK-[IF, ELIF, ELSE, FI] line. 85*795d594fSAndroid Build Coastguard Worker """ 86*795d594fSAndroid Build Coastguard Worker variant = statement.variant 87*795d594fSAndroid Build Coastguard Worker if variant is TestStatement.Variant.IF: 88*795d594fSAndroid Build Coastguard Worker self._if(statement, variables) 89*795d594fSAndroid Build Coastguard Worker elif variant is TestStatement.Variant.ELIF: 90*795d594fSAndroid Build Coastguard Worker self._elif(statement, variables) 91*795d594fSAndroid Build Coastguard Worker elif variant is TestStatement.Variant.ELSE: 92*795d594fSAndroid Build Coastguard Worker self._else(statement) 93*795d594fSAndroid Build Coastguard Worker else: 94*795d594fSAndroid Build Coastguard Worker assert variant is TestStatement.Variant.FI 95*795d594fSAndroid Build Coastguard Worker self._fi(statement) 96*795d594fSAndroid Build Coastguard Worker 97*795d594fSAndroid Build Coastguard Worker def eof(self): 98*795d594fSAndroid Build Coastguard Worker """ 99*795d594fSAndroid Build Coastguard Worker The last line the cursor points to is always EOF. 100*795d594fSAndroid Build Coastguard Worker """ 101*795d594fSAndroid Build Coastguard Worker if not self._is_empty(): 102*795d594fSAndroid Build Coastguard Worker raise BadStructureException("Missing CHECK-FI", -1) 103*795d594fSAndroid Build Coastguard Worker 104*795d594fSAndroid Build Coastguard Worker def _is_empty(self): 105*795d594fSAndroid Build Coastguard Worker return not self.stack 106*795d594fSAndroid Build Coastguard Worker 107*795d594fSAndroid Build Coastguard Worker def _if(self, statement, variables): 108*795d594fSAndroid Build Coastguard Worker if not self._is_empty() and abs(self._peek()) in [IfStack.BRANCH_NOT_TAKEN, 109*795d594fSAndroid Build Coastguard Worker IfStack.BRANCH_NOT_TAKEN_YET]: 110*795d594fSAndroid Build Coastguard Worker self._push(IfStack.BRANCH_NOT_TAKEN) 111*795d594fSAndroid Build Coastguard Worker elif evaluate_line(statement, variables): 112*795d594fSAndroid Build Coastguard Worker self._push(IfStack.BRANCH_TAKEN) 113*795d594fSAndroid Build Coastguard Worker else: 114*795d594fSAndroid Build Coastguard Worker self._push(IfStack.BRANCH_NOT_TAKEN_YET) 115*795d594fSAndroid Build Coastguard Worker 116*795d594fSAndroid Build Coastguard Worker def _elif(self, statement, variables): 117*795d594fSAndroid Build Coastguard Worker if self._is_empty(): 118*795d594fSAndroid Build Coastguard Worker raise BadStructureException("CHECK-ELIF must be after CHECK-IF or CHECK-ELIF", 119*795d594fSAndroid Build Coastguard Worker statement.line_no) 120*795d594fSAndroid Build Coastguard Worker if self._peek() < 0: 121*795d594fSAndroid Build Coastguard Worker raise BadStructureException("CHECK-ELIF cannot be after CHECK-ELSE", statement.line_no) 122*795d594fSAndroid Build Coastguard Worker if self._peek() == IfStack.BRANCH_TAKEN: 123*795d594fSAndroid Build Coastguard Worker self._set_last(IfStack.BRANCH_NOT_TAKEN) 124*795d594fSAndroid Build Coastguard Worker elif self._peek() == IfStack.BRANCH_NOT_TAKEN_YET: 125*795d594fSAndroid Build Coastguard Worker if evaluate_line(statement, variables): 126*795d594fSAndroid Build Coastguard Worker self._set_last(IfStack.BRANCH_TAKEN) 127*795d594fSAndroid Build Coastguard Worker # else, the CHECK-ELIF condition is False, so do nothing: the last element on the stack is 128*795d594fSAndroid Build Coastguard Worker # already set to BRANCH_NOT_TAKEN_YET. 129*795d594fSAndroid Build Coastguard Worker else: 130*795d594fSAndroid Build Coastguard Worker assert self._peek() == IfStack.BRANCH_NOT_TAKEN 131*795d594fSAndroid Build Coastguard Worker 132*795d594fSAndroid Build Coastguard Worker def _else(self, statement): 133*795d594fSAndroid Build Coastguard Worker if self._is_empty(): 134*795d594fSAndroid Build Coastguard Worker raise BadStructureException("CHECK-ELSE must be after CHECK-IF or CHECK-ELIF", 135*795d594fSAndroid Build Coastguard Worker statement.line_no) 136*795d594fSAndroid Build Coastguard Worker if self._peek() < 0: 137*795d594fSAndroid Build Coastguard Worker raise BadStructureException("Consecutive CHECK-ELSE statements", statement.line_no) 138*795d594fSAndroid Build Coastguard Worker if self._peek() in [IfStack.BRANCH_TAKEN, IfStack.BRANCH_NOT_TAKEN]: 139*795d594fSAndroid Build Coastguard Worker # Notice that we're setting -BRANCH_NOT_TAKEN rather that BRANCH_NOT_TAKEN as we went past the 140*795d594fSAndroid Build Coastguard Worker # ELSE branch. 141*795d594fSAndroid Build Coastguard Worker self._set_last(-IfStack.BRANCH_NOT_TAKEN) 142*795d594fSAndroid Build Coastguard Worker else: 143*795d594fSAndroid Build Coastguard Worker assert self._peek() == IfStack.BRANCH_NOT_TAKEN_YET 144*795d594fSAndroid Build Coastguard Worker # Setting -BRANCH_TAKEN rather BRANCH_TAKEN for the same reason. 145*795d594fSAndroid Build Coastguard Worker self._set_last(-IfStack.BRANCH_TAKEN) 146*795d594fSAndroid Build Coastguard Worker 147*795d594fSAndroid Build Coastguard Worker def _fi(self, statement): 148*795d594fSAndroid Build Coastguard Worker if self._is_empty(): 149*795d594fSAndroid Build Coastguard Worker raise BadStructureException("CHECK-FI does not have a matching CHECK-IF", statement.line_no) 150*795d594fSAndroid Build Coastguard Worker self.stack.pop() 151*795d594fSAndroid Build Coastguard Worker 152*795d594fSAndroid Build Coastguard Worker def _peek(self): 153*795d594fSAndroid Build Coastguard Worker assert not self._is_empty() 154*795d594fSAndroid Build Coastguard Worker return self.stack[-1] 155*795d594fSAndroid Build Coastguard Worker 156*795d594fSAndroid Build Coastguard Worker def _push(self, element): 157*795d594fSAndroid Build Coastguard Worker self.stack.append(element) 158*795d594fSAndroid Build Coastguard Worker 159*795d594fSAndroid Build Coastguard Worker def _set_last(self, element): 160*795d594fSAndroid Build Coastguard Worker self.stack[-1] = element 161*795d594fSAndroid Build Coastguard Worker 162*795d594fSAndroid Build Coastguard Worker 163*795d594fSAndroid Build Coastguard Workerdef find_matching_line(statement, c1_pass, scope, variables, exclude_lines=[]): 164*795d594fSAndroid Build Coastguard Worker """ Finds the first line in `c1_pass` which matches `statement`. 165*795d594fSAndroid Build Coastguard Worker 166*795d594fSAndroid Build Coastguard Worker Scan only lines numbered between `scope.start` and `scope.end` and not on the 167*795d594fSAndroid Build Coastguard Worker `excludeLines` list. 168*795d594fSAndroid Build Coastguard Worker 169*795d594fSAndroid Build Coastguard Worker Returns the index of the `c1Pass` line matching the statement and variables 170*795d594fSAndroid Build Coastguard Worker values after the match. 171*795d594fSAndroid Build Coastguard Worker 172*795d594fSAndroid Build Coastguard Worker Raises MatchFailedException if no such `c1Pass` line can be found. 173*795d594fSAndroid Build Coastguard Worker """ 174*795d594fSAndroid Build Coastguard Worker for i in range(scope.start, scope.end): 175*795d594fSAndroid Build Coastguard Worker if i in exclude_lines: 176*795d594fSAndroid Build Coastguard Worker continue 177*795d594fSAndroid Build Coastguard Worker new_variables = match_lines(statement, c1_pass.body[i], variables) 178*795d594fSAndroid Build Coastguard Worker if new_variables is not None: 179*795d594fSAndroid Build Coastguard Worker return MatchInfo(MatchScope(i, i), new_variables) 180*795d594fSAndroid Build Coastguard Worker raise MatchFailedException(statement, scope.start, variables) 181*795d594fSAndroid Build Coastguard Worker 182*795d594fSAndroid Build Coastguard Worker 183*795d594fSAndroid Build Coastguard Workerclass ExecutionState(object): 184*795d594fSAndroid Build Coastguard Worker def __init__(self, c1_pass, variables={}): 185*795d594fSAndroid Build Coastguard Worker self.cursor = 0 186*795d594fSAndroid Build Coastguard Worker self.c1_pass = c1_pass 187*795d594fSAndroid Build Coastguard Worker self.c1_length = len(c1_pass.body) 188*795d594fSAndroid Build Coastguard Worker self.variables = ImmutableDict(variables) 189*795d594fSAndroid Build Coastguard Worker self.dag_queue = [] 190*795d594fSAndroid Build Coastguard Worker self.not_queue = [] 191*795d594fSAndroid Build Coastguard Worker self.if_stack = IfStack() 192*795d594fSAndroid Build Coastguard Worker self.last_variant = None 193*795d594fSAndroid Build Coastguard Worker 194*795d594fSAndroid Build Coastguard Worker def move_cursor(self, match): 195*795d594fSAndroid Build Coastguard Worker assert self.cursor <= match.scope.end 196*795d594fSAndroid Build Coastguard Worker 197*795d594fSAndroid Build Coastguard Worker # Handle any pending NOT statements before moving the cursor 198*795d594fSAndroid Build Coastguard Worker self.handle_not_queue(MatchScope(self.cursor, match.scope.start)) 199*795d594fSAndroid Build Coastguard Worker 200*795d594fSAndroid Build Coastguard Worker self.cursor = match.scope.end + 1 201*795d594fSAndroid Build Coastguard Worker self.variables = match.variables 202*795d594fSAndroid Build Coastguard Worker 203*795d594fSAndroid Build Coastguard Worker def handle_dag_queue(self, scope): 204*795d594fSAndroid Build Coastguard Worker """ Attempts to find matching `c1Pass` lines for a group of DAG statements. 205*795d594fSAndroid Build Coastguard Worker 206*795d594fSAndroid Build Coastguard Worker Statements are matched in the list order and variable values propagated. Only 207*795d594fSAndroid Build Coastguard Worker lines in `scope` are scanned and each line can only match one statement. 208*795d594fSAndroid Build Coastguard Worker 209*795d594fSAndroid Build Coastguard Worker Returns the range of `c1Pass` lines covered by this group (min/max of matching 210*795d594fSAndroid Build Coastguard Worker line numbers) and the variable values after the match of the last statement. 211*795d594fSAndroid Build Coastguard Worker 212*795d594fSAndroid Build Coastguard Worker Raises MatchFailedException when a statement cannot be satisfied. 213*795d594fSAndroid Build Coastguard Worker """ 214*795d594fSAndroid Build Coastguard Worker if not self.dag_queue: 215*795d594fSAndroid Build Coastguard Worker return 216*795d594fSAndroid Build Coastguard Worker 217*795d594fSAndroid Build Coastguard Worker matched_lines = [] 218*795d594fSAndroid Build Coastguard Worker variables = self.variables 219*795d594fSAndroid Build Coastguard Worker 220*795d594fSAndroid Build Coastguard Worker for statement in self.dag_queue: 221*795d594fSAndroid Build Coastguard Worker assert statement.variant == TestStatement.Variant.DAG 222*795d594fSAndroid Build Coastguard Worker match = find_matching_line(statement, self.c1_pass, scope, variables, matched_lines) 223*795d594fSAndroid Build Coastguard Worker variables = match.variables 224*795d594fSAndroid Build Coastguard Worker assert match.scope.start == match.scope.end 225*795d594fSAndroid Build Coastguard Worker assert match.scope.start not in matched_lines 226*795d594fSAndroid Build Coastguard Worker matched_lines.append(match.scope.start) 227*795d594fSAndroid Build Coastguard Worker 228*795d594fSAndroid Build Coastguard Worker match = MatchInfo(MatchScope(min(matched_lines), max(matched_lines)), variables) 229*795d594fSAndroid Build Coastguard Worker self.dag_queue = [] 230*795d594fSAndroid Build Coastguard Worker self.move_cursor(match) 231*795d594fSAndroid Build Coastguard Worker 232*795d594fSAndroid Build Coastguard Worker def handle_not_queue(self, scope): 233*795d594fSAndroid Build Coastguard Worker """ Verifies that none of the given NOT statements matches a line inside 234*795d594fSAndroid Build Coastguard Worker the given `scope` of `c1Pass` lines. 235*795d594fSAndroid Build Coastguard Worker 236*795d594fSAndroid Build Coastguard Worker Raises MatchFailedException if a statement matches a line in the scope. 237*795d594fSAndroid Build Coastguard Worker """ 238*795d594fSAndroid Build Coastguard Worker for statement in self.not_queue: 239*795d594fSAndroid Build Coastguard Worker assert statement.variant == TestStatement.Variant.NOT 240*795d594fSAndroid Build Coastguard Worker for i in range(scope.start, scope.end): 241*795d594fSAndroid Build Coastguard Worker if match_lines(statement, self.c1_pass.body[i], self.variables) is not None: 242*795d594fSAndroid Build Coastguard Worker raise MatchFailedException(statement, i, self.variables) 243*795d594fSAndroid Build Coastguard Worker self.not_queue = [] 244*795d594fSAndroid Build Coastguard Worker 245*795d594fSAndroid Build Coastguard Worker def handle_eof(self): 246*795d594fSAndroid Build Coastguard Worker """ EOF marker always moves the cursor to the end of the file.""" 247*795d594fSAndroid Build Coastguard Worker match = MatchInfo(MatchScope(self.c1_length, self.c1_length), None) 248*795d594fSAndroid Build Coastguard Worker self.move_cursor(match) 249*795d594fSAndroid Build Coastguard Worker 250*795d594fSAndroid Build Coastguard Worker def handle_in_order(self, statement): 251*795d594fSAndroid Build Coastguard Worker """ Single in-order statement. Find the first line that matches and move 252*795d594fSAndroid Build Coastguard Worker the cursor to the subsequent line. 253*795d594fSAndroid Build Coastguard Worker 254*795d594fSAndroid Build Coastguard Worker Raises MatchFailedException if no such line can be found. 255*795d594fSAndroid Build Coastguard Worker """ 256*795d594fSAndroid Build Coastguard Worker scope = MatchScope(self.cursor, self.c1_length) 257*795d594fSAndroid Build Coastguard Worker match = find_matching_line(statement, self.c1_pass, scope, self.variables) 258*795d594fSAndroid Build Coastguard Worker self.move_cursor(match) 259*795d594fSAndroid Build Coastguard Worker 260*795d594fSAndroid Build Coastguard Worker def handle_next_line(self, statement): 261*795d594fSAndroid Build Coastguard Worker """ Single next-line statement. Test if the current line matches and move 262*795d594fSAndroid Build Coastguard Worker the cursor to the next line if it does. 263*795d594fSAndroid Build Coastguard Worker 264*795d594fSAndroid Build Coastguard Worker Raises MatchFailedException if the current line does not match. 265*795d594fSAndroid Build Coastguard Worker """ 266*795d594fSAndroid Build Coastguard Worker if self.last_variant not in [TestStatement.Variant.IN_ORDER, TestStatement.Variant.NEXT_LINE]: 267*795d594fSAndroid Build Coastguard Worker raise BadStructureException("A next-line statement can only be placed " 268*795d594fSAndroid Build Coastguard Worker "after an in-order statement or another next-line statement.", 269*795d594fSAndroid Build Coastguard Worker statement.line_no) 270*795d594fSAndroid Build Coastguard Worker 271*795d594fSAndroid Build Coastguard Worker scope = MatchScope(self.cursor, self.cursor + 1) 272*795d594fSAndroid Build Coastguard Worker match = find_matching_line(statement, self.c1_pass, scope, self.variables) 273*795d594fSAndroid Build Coastguard Worker self.move_cursor(match) 274*795d594fSAndroid Build Coastguard Worker 275*795d594fSAndroid Build Coastguard Worker def handle_eval(self, statement): 276*795d594fSAndroid Build Coastguard Worker """ Evaluates the statement in the current context. 277*795d594fSAndroid Build Coastguard Worker 278*795d594fSAndroid Build Coastguard Worker Raises MatchFailedException if the expression evaluates to False. 279*795d594fSAndroid Build Coastguard Worker """ 280*795d594fSAndroid Build Coastguard Worker if not evaluate_line(statement, self.variables): 281*795d594fSAndroid Build Coastguard Worker raise MatchFailedException(statement, self.cursor, self.variables) 282*795d594fSAndroid Build Coastguard Worker 283*795d594fSAndroid Build Coastguard Worker def handle(self, statement): 284*795d594fSAndroid Build Coastguard Worker variant = None if statement is None else statement.variant 285*795d594fSAndroid Build Coastguard Worker 286*795d594fSAndroid Build Coastguard Worker if variant in [TestStatement.Variant.IF, 287*795d594fSAndroid Build Coastguard Worker TestStatement.Variant.ELIF, 288*795d594fSAndroid Build Coastguard Worker TestStatement.Variant.ELSE, 289*795d594fSAndroid Build Coastguard Worker TestStatement.Variant.FI]: 290*795d594fSAndroid Build Coastguard Worker self.if_stack.handle(statement, self.variables) 291*795d594fSAndroid Build Coastguard Worker return 292*795d594fSAndroid Build Coastguard Worker 293*795d594fSAndroid Build Coastguard Worker if variant is None: 294*795d594fSAndroid Build Coastguard Worker self.if_stack.eof() 295*795d594fSAndroid Build Coastguard Worker 296*795d594fSAndroid Build Coastguard Worker if not self.if_stack.can_execute(): 297*795d594fSAndroid Build Coastguard Worker return 298*795d594fSAndroid Build Coastguard Worker 299*795d594fSAndroid Build Coastguard Worker # First non-DAG statement always triggers execution of any preceding 300*795d594fSAndroid Build Coastguard Worker # DAG statements. 301*795d594fSAndroid Build Coastguard Worker if variant is not TestStatement.Variant.DAG: 302*795d594fSAndroid Build Coastguard Worker self.handle_dag_queue(MatchScope(self.cursor, self.c1_length)) 303*795d594fSAndroid Build Coastguard Worker 304*795d594fSAndroid Build Coastguard Worker if variant is None: 305*795d594fSAndroid Build Coastguard Worker self.handle_eof() 306*795d594fSAndroid Build Coastguard Worker elif variant is TestStatement.Variant.IN_ORDER: 307*795d594fSAndroid Build Coastguard Worker self.handle_in_order(statement) 308*795d594fSAndroid Build Coastguard Worker elif variant is TestStatement.Variant.NEXT_LINE: 309*795d594fSAndroid Build Coastguard Worker self.handle_next_line(statement) 310*795d594fSAndroid Build Coastguard Worker elif variant is TestStatement.Variant.DAG: 311*795d594fSAndroid Build Coastguard Worker self.dag_queue.append(statement) 312*795d594fSAndroid Build Coastguard Worker elif variant is TestStatement.Variant.NOT: 313*795d594fSAndroid Build Coastguard Worker self.not_queue.append(statement) 314*795d594fSAndroid Build Coastguard Worker else: 315*795d594fSAndroid Build Coastguard Worker assert variant is TestStatement.Variant.EVAL 316*795d594fSAndroid Build Coastguard Worker self.handle_eval(statement) 317*795d594fSAndroid Build Coastguard Worker 318*795d594fSAndroid Build Coastguard Worker self.last_variant = variant 319*795d594fSAndroid Build Coastguard Worker 320*795d594fSAndroid Build Coastguard Worker 321*795d594fSAndroid Build Coastguard Workerdef match_test_case( 322*795d594fSAndroid Build Coastguard Worker test_case, 323*795d594fSAndroid Build Coastguard Worker c1_pass, 324*795d594fSAndroid Build Coastguard Worker instruction_set_features, 325*795d594fSAndroid Build Coastguard Worker read_barrier_type): 326*795d594fSAndroid Build Coastguard Worker """ Runs a test case against a C1visualizer graph dump. 327*795d594fSAndroid Build Coastguard Worker 328*795d594fSAndroid Build Coastguard Worker Raises MatchFailedException when a statement cannot be satisfied. 329*795d594fSAndroid Build Coastguard Worker """ 330*795d594fSAndroid Build Coastguard Worker assert test_case.name == c1_pass.name 331*795d594fSAndroid Build Coastguard Worker 332*795d594fSAndroid Build Coastguard Worker initial_variables = { 333*795d594fSAndroid Build Coastguard Worker "ISA_FEATURES": instruction_set_features, 334*795d594fSAndroid Build Coastguard Worker "READ_BARRIER_TYPE": read_barrier_type 335*795d594fSAndroid Build Coastguard Worker } 336*795d594fSAndroid Build Coastguard Worker state = ExecutionState(c1_pass, initial_variables) 337*795d594fSAndroid Build Coastguard Worker test_statements = test_case.statements + [None] 338*795d594fSAndroid Build Coastguard Worker for statement in test_statements: 339*795d594fSAndroid Build Coastguard Worker state.handle(statement) 340*795d594fSAndroid Build Coastguard Worker 341*795d594fSAndroid Build Coastguard Worker 342*795d594fSAndroid Build Coastguard Workerdef match_files(checker_file, c1_file, target_arch, debuggable_mode, print_cfg): 343*795d594fSAndroid Build Coastguard Worker for test_case in checker_file.test_cases: 344*795d594fSAndroid Build Coastguard Worker if test_case.test_arch not in [None, target_arch]: 345*795d594fSAndroid Build Coastguard Worker continue 346*795d594fSAndroid Build Coastguard Worker if test_case.for_debuggable != debuggable_mode: 347*795d594fSAndroid Build Coastguard Worker continue 348*795d594fSAndroid Build Coastguard Worker 349*795d594fSAndroid Build Coastguard Worker # TODO: Currently does not handle multiple occurrences of the same group 350*795d594fSAndroid Build Coastguard Worker # name, e.g. when a pass is run multiple times. It will always try to 351*795d594fSAndroid Build Coastguard Worker # match a check group against the first output group of the same name. 352*795d594fSAndroid Build Coastguard Worker c1_pass = c1_file.find_pass(test_case.name) 353*795d594fSAndroid Build Coastguard Worker if c1_pass is None: 354*795d594fSAndroid Build Coastguard Worker with open(c1_file.full_file_name) as cfg_file: 355*795d594fSAndroid Build Coastguard Worker Logger.log("".join(cfg_file), Logger.Level.ERROR) 356*795d594fSAndroid Build Coastguard Worker Logger.fail("Test case not found in the CFG file", 357*795d594fSAndroid Build Coastguard Worker c1_file.full_file_name, test_case.start_line_no, test_case.name) 358*795d594fSAndroid Build Coastguard Worker 359*795d594fSAndroid Build Coastguard Worker Logger.start_test(test_case.name) 360*795d594fSAndroid Build Coastguard Worker try: 361*795d594fSAndroid Build Coastguard Worker match_test_case( 362*795d594fSAndroid Build Coastguard Worker test_case, 363*795d594fSAndroid Build Coastguard Worker c1_pass, 364*795d594fSAndroid Build Coastguard Worker c1_file.instruction_set_features, 365*795d594fSAndroid Build Coastguard Worker c1_file.read_barrier_type) 366*795d594fSAndroid Build Coastguard Worker Logger.test_passed() 367*795d594fSAndroid Build Coastguard Worker except MatchFailedException as e: 368*795d594fSAndroid Build Coastguard Worker line_no = c1_pass.start_line_no + e.line_no 369*795d594fSAndroid Build Coastguard Worker if e.statement.variant == TestStatement.Variant.NOT: 370*795d594fSAndroid Build Coastguard Worker msg = "NOT statement matched line {}" 371*795d594fSAndroid Build Coastguard Worker else: 372*795d594fSAndroid Build Coastguard Worker msg = "Statement could not be matched starting from line {}" 373*795d594fSAndroid Build Coastguard Worker msg = msg.format(line_no) 374*795d594fSAndroid Build Coastguard Worker if print_cfg: 375*795d594fSAndroid Build Coastguard Worker with open(c1_file.full_file_name) as cfg_file: 376*795d594fSAndroid Build Coastguard Worker Logger.log("".join(cfg_file), Logger.Level.ERROR) 377*795d594fSAndroid Build Coastguard Worker Logger.test_failed(msg, e.statement, e.variables) 378