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 SplitStream
17from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
18
19import re
20
21class C1ParserState:
22  OutsideBlock, InsideCompilationBlock, StartingCfgBlock, InsideCfgBlock = range(4)
23
24  def __init__(self):
25    self.currentState = C1ParserState.OutsideBlock
26    self.lastMethodName = None
27
28def __parseC1Line(c1File, line, lineNo, state, fileName):
29  """ This function is invoked on each line of the output file and returns
30      a triplet which instructs the parser how the line should be handled. If the
31      line is to be included in the current group, it is returned in the first
32      value. If the line starts a new output group, the name of the group is
33      returned in the second value. The third value is only here to make the
34      function prototype compatible with `SplitStream` and is always set to
35      `None` here.
36  """
37  if state.currentState == C1ParserState.StartingCfgBlock:
38    # Previous line started a new 'cfg' block which means that this one must
39    # contain the name of the pass (this is enforced by C1visualizer).
40    if re.match("name\s+\"[^\"]+\"", line):
41      # Extract the pass name, prepend it with the name of the method and
42      # return as the beginning of a new group.
43      state.currentState = C1ParserState.InsideCfgBlock
44      return (None, state.lastMethodName + " " + line.split("\"")[1], None)
45    else:
46      Logger.fail("Expected output group name", fileName, lineNo)
47
48  elif state.currentState == C1ParserState.InsideCfgBlock:
49    if line == "end_cfg":
50      state.currentState = C1ParserState.OutsideBlock
51      return (None, None, None)
52    else:
53      return (line, None, None)
54
55  elif state.currentState == C1ParserState.InsideCompilationBlock:
56    # Search for the method's name. Format: method "<name>"
57    if re.match("method\s+\"[^\"]*\"", line):
58      methodName = line.split("\"")[1].strip()
59      if not methodName:
60        Logger.fail("Empty method name in output", fileName, lineNo)
61
62      m = re.search("isa_features:([\w,-]+)", methodName)
63      if (m):
64        rawFeatures = m.group(1).split(",")
65        # Create a map of features in the form {featureName: isEnabled}.
66        features = {}
67        for rf in rawFeatures:
68          featureName = rf
69          isEnabled = True
70          # A '-' in front of the feature name indicates that the feature wasn't enabled at compile
71          # time.
72          if rf[0] == '-':
73            featureName = rf[1:]
74            isEnabled = False
75          features[featureName] = isEnabled
76
77        c1File.setISAFeatures(features)
78      else:
79        state.lastMethodName = methodName
80    elif line == "end_compilation":
81      state.currentState = C1ParserState.OutsideBlock
82    return (None, None, None)
83
84  else:
85    assert state.currentState == C1ParserState.OutsideBlock
86    if line == "begin_cfg":
87      # The line starts a new group but we'll wait until the next line from
88      # which we can extract the name of the pass.
89      if state.lastMethodName is None:
90        Logger.fail("Expected method header", fileName, lineNo)
91      state.currentState = C1ParserState.StartingCfgBlock
92      return (None, None, None)
93    elif line == "begin_compilation":
94      state.currentState = C1ParserState.InsideCompilationBlock
95      return (None, None, None)
96    else:
97      Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
98
99def ParseC1visualizerStream(fileName, stream):
100  c1File = C1visualizerFile(fileName)
101  state = C1ParserState()
102  fnProcessLine = lambda line, lineNo: __parseC1Line(c1File, line, lineNo, state, fileName)
103  fnLineOutsideChunk = lambda line, lineNo: \
104      Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
105  for passName, passLines, startLineNo, testArch in \
106      SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
107    C1visualizerPass(c1File, passName, passLines, startLineNo + 1)
108  return c1File
109