xref: /aosp_15_r20/art/tools/checker/match/file.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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