1# Copyright (C) 2014 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#   http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from common.logger import Logger
16from file_format.common import split_stream
17from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
18
19import re
20
21
22class C1ParserState:
23  OUTSIDE_BLOCK, INSIDE_COMPILATION_BLOCK, STARTING_CFG_BLOCK, INSIDE_CFG_BLOCK = range(4)
24
25  def __init__(self):
26    self.current_state = C1ParserState.OUTSIDE_BLOCK
27    self.last_method_name = None
28
29
30def _parse_c1_line(c1_file, line, line_no, state, filename):
31  """ This function is invoked on each line of the output file and returns
32      a triplet which instructs the parser how the line should be handled. If the
33      line is to be included in the current group, it is returned in the first
34      value. If the line starts a new output group, the name of the group is
35      returned in the second value. The third value is only here to make the
36      function prototype compatible with `SplitStream` and is always set to
37      `None` here.
38  """
39  if state.current_state == C1ParserState.STARTING_CFG_BLOCK:
40    # Previous line started a new 'cfg' block which means that this one must
41    # contain the name of the pass (this is enforced by C1visualizer).
42    if re.match(r'name\s+"[^"]+"', line):
43      # Extract the pass name, prepend it with the name of the method and
44      # return as the beginning of a new group.
45      state.current_state = C1ParserState.INSIDE_CFG_BLOCK
46      return None, state.last_method_name + " " + line.split('"')[1], None
47    else:
48      Logger.fail("Expected output group name", filename, line_no)
49
50  elif state.current_state == C1ParserState.INSIDE_CFG_BLOCK:
51    if line == "end_cfg":
52      state.current_state = C1ParserState.OUTSIDE_BLOCK
53      return None, None, None
54    else:
55      return line, None, None
56
57  elif state.current_state == C1ParserState.INSIDE_COMPILATION_BLOCK:
58    # Search for the method's name. Format: method "<name>"
59    if re.match(r'method\s+"[^"]*"', line):
60      method_name = line.split('"')[1].strip()
61      if not method_name:
62        Logger.fail("Empty method name in output", filename, line_no)
63
64      match = re.search(r"isa_features:([\w,-]+)", method_name)
65      if match:
66        raw_features = match.group(1).split(",")
67        # Create a map of features in the form {feature_name: is_enabled}.
68        features = {}
69        for rf in raw_features:
70          feature_name = rf
71          is_enabled = True
72          # A '-' in front of the feature name indicates that the feature wasn't enabled at compile
73          # time.
74          if rf[0] == "-":
75            feature_name = rf[1:]
76            is_enabled = False
77          features[feature_name] = is_enabled
78
79        c1_file.set_isa_features(features)
80
81      # Check what type of read barrier is used
82      match = re.search(r"read_barrier_type:(\w+)", method_name)
83      if match:
84        c1_file.set_read_barrier_type(match.group(1))
85
86      else:
87        state.last_method_name = method_name
88    elif line == "end_compilation":
89      state.current_state = C1ParserState.OUTSIDE_BLOCK
90    return None, None, None
91
92  else:
93    assert state.current_state == C1ParserState.OUTSIDE_BLOCK
94    if line == "begin_cfg":
95      # The line starts a new group but we'll wait until the next line from
96      # which we can extract the name of the pass.
97      if state.last_method_name is None:
98        Logger.fail("Expected method header", filename, line_no)
99      state.current_state = C1ParserState.STARTING_CFG_BLOCK
100      return None, None, None
101    elif line == "begin_compilation":
102      state.current_state = C1ParserState.INSIDE_COMPILATION_BLOCK
103      return None, None, None
104    else:
105      Logger.fail("C1visualizer line not inside a group", filename, line_no)
106
107
108def parse_c1_visualizer_stream(filename, stream):
109  c1_file = C1visualizerFile(filename)
110  state = C1ParserState()
111
112  def fn_process_line(line, line_no):
113    return _parse_c1_line(c1_file, line, line_no, state, c1_file.base_file_name)
114
115  def fn_line_outside_chunk(line, line_no):
116    Logger.fail("C1visualizer line not inside a group", c1_file.base_file_name, line_no)
117
118  for pass_name, pass_lines, start_line_no, test_arch in split_stream(stream, fn_process_line,
119                                                                      fn_line_outside_chunk):
120    C1visualizerPass(c1_file, pass_name, pass_lines, start_line_no + 1)
121  return c1_file
122