| # Copyright (C) 2014 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from common.logger import Logger |
| from file_format.common import split_stream |
| from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass |
| |
| import re |
| |
| |
| class C1ParserState: |
| OUTSIDE_BLOCK, INSIDE_COMPILATION_BLOCK, STARTING_CFG_BLOCK, INSIDE_CFG_BLOCK = range(4) |
| |
| def __init__(self): |
| self.current_state = C1ParserState.OUTSIDE_BLOCK |
| self.last_method_name = None |
| |
| |
| def _parse_c1_line(c1_file, line, line_no, state, filename): |
| """ This function is invoked on each line of the output file and returns |
| a triplet which instructs the parser how the line should be handled. If the |
| line is to be included in the current group, it is returned in the first |
| value. If the line starts a new output group, the name of the group is |
| returned in the second value. The third value is only here to make the |
| function prototype compatible with `SplitStream` and is always set to |
| `None` here. |
| """ |
| if state.current_state == C1ParserState.STARTING_CFG_BLOCK: |
| # Previous line started a new 'cfg' block which means that this one must |
| # contain the name of the pass (this is enforced by C1visualizer). |
| if re.match(r'name\s+"[^"]+"', line): |
| # Extract the pass name, prepend it with the name of the method and |
| # return as the beginning of a new group. |
| state.current_state = C1ParserState.INSIDE_CFG_BLOCK |
| return None, state.last_method_name + " " + line.split('"')[1], None |
| else: |
| Logger.fail("Expected output group name", filename, line_no) |
| |
| elif state.current_state == C1ParserState.INSIDE_CFG_BLOCK: |
| if line == "end_cfg": |
| state.current_state = C1ParserState.OUTSIDE_BLOCK |
| return None, None, None |
| else: |
| return line, None, None |
| |
| elif state.current_state == C1ParserState.INSIDE_COMPILATION_BLOCK: |
| # Search for the method's name. Format: method "<name>" |
| if re.match(r'method\s+"[^"]*"', line): |
| method_name = line.split('"')[1].strip() |
| if not method_name: |
| Logger.fail("Empty method name in output", filename, line_no) |
| |
| match = re.search(r"isa_features:([\w,-]+)", method_name) |
| if match: |
| raw_features = match.group(1).split(",") |
| # Create a map of features in the form {feature_name: is_enabled}. |
| features = {} |
| for rf in raw_features: |
| feature_name = rf |
| is_enabled = True |
| # A '-' in front of the feature name indicates that the feature wasn't enabled at compile |
| # time. |
| if rf[0] == "-": |
| feature_name = rf[1:] |
| is_enabled = False |
| features[feature_name] = is_enabled |
| |
| c1_file.set_isa_features(features) |
| |
| # Check what type of read barrier is used |
| match = re.search(r"read_barrier_type:(\w+)", method_name) |
| if match: |
| c1_file.set_read_barrier_type(match.group(1)) |
| |
| else: |
| state.last_method_name = method_name |
| elif line == "end_compilation": |
| state.current_state = C1ParserState.OUTSIDE_BLOCK |
| return None, None, None |
| |
| else: |
| assert state.current_state == C1ParserState.OUTSIDE_BLOCK |
| if line == "begin_cfg": |
| # The line starts a new group but we'll wait until the next line from |
| # which we can extract the name of the pass. |
| if state.last_method_name is None: |
| Logger.fail("Expected method header", filename, line_no) |
| state.current_state = C1ParserState.STARTING_CFG_BLOCK |
| return None, None, None |
| elif line == "begin_compilation": |
| state.current_state = C1ParserState.INSIDE_COMPILATION_BLOCK |
| return None, None, None |
| else: |
| Logger.fail("C1visualizer line not inside a group", filename, line_no) |
| |
| |
| def parse_c1_visualizer_stream(filename, stream): |
| c1_file = C1visualizerFile(filename) |
| state = C1ParserState() |
| |
| def fn_process_line(line, line_no): |
| return _parse_c1_line(c1_file, line, line_no, state, c1_file.base_file_name) |
| |
| def fn_line_outside_chunk(line, line_no): |
| Logger.fail("C1visualizer line not inside a group", c1_file.base_file_name, line_no) |
| |
| for pass_name, pass_lines, start_line_no, test_arch in split_stream(stream, fn_process_line, |
| fn_line_outside_chunk): |
| C1visualizerPass(c1_file, pass_name, pass_lines, start_line_no + 1) |
| return c1_file |