1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""
17A tool to check atomic memory order format.
18"""
19
20import re
21import sys
22
23if len(sys.argv) < 2:
24    sys.exit("File name to be checked was not specified")
25FILE = sys.argv[1]
26
27MAX_OFFSET = 3
28EMPTY_MEMORY_ORDER = "empty"
29PARAMETERIZED_MEMORY_ORDER = "parameterized"
30IGNORED_PREFIXES = ["ATOMIC_STORE", "ATOMIC_LOAD", "ATOMIC_FETCH_ADD",
31                    "ATOMIC_FETCH_SUB", "ATOMIC_CAS_WEAK"]
32
33
34def obtain_memory_order(parsed_line: str) -> []:
35    memory_orders = list()
36    while True:
37        parsed_res = re.search(r"memory_order_(\w+)", parsed_line)
38        if parsed_res:
39            memory_order = parsed_res.group(1)
40            memory_orders.append(memory_order)
41            parsed_line = parsed_line.replace(memory_order, "", 1)
42        else:
43            break
44    return memory_orders
45
46
47def parse_file() -> ():
48    atomic_comments = dict()
49    atomic_operations = dict()
50    cur_parser_label = 0
51    with open(FILE) as parsed_file:
52        for index, line in enumerate(parsed_file.readlines(), 1):
53            res = re.search(r"// Atomic with (\w+) order reason: (.+)", line)
54            if res:
55                memory_order = res.group(1)
56                atomic_comments[index] = memory_order
57
58            # TODO: Support compare_exchange_strong|compare_exchange_weak
59            res = re.search(r"(.*)(\.|->)(store|load|fetch_add|fetch_sub|"
60                            r"fetch_or|fetch_xor|fetch_and)\((.+)", line)
61            if res:
62                prefix = res.group(1)
63                is_ignored = False
64                for ignored_prefix in IGNORED_PREFIXES:
65                    if ignored_prefix in prefix:
66                        is_ignored = True
67                        continue
68                if is_ignored:
69                    continue
70                if cur_parser_label:
71                    atomic_operations[cur_parser_label] = [EMPTY_MEMORY_ORDER]
72                    cur_parser_label = 0
73                rest_str = res.group(4)
74                memory_order = obtain_memory_order(rest_str)
75                if memory_order:
76                    atomic_operations[index] = memory_order
77                else:
78                    cur_parser_label = index
79            else:
80                if cur_parser_label:
81                    memory_order = obtain_memory_order(line)
82                    if memory_order:
83                        atomic_operations[cur_parser_label] = memory_order
84                        cur_parser_label = 0
85                    else:
86                        if index - cur_parser_label >= MAX_OFFSET:
87                            atomic_operations[cur_parser_label] = \
88                                [EMPTY_MEMORY_ORDER]
89                            cur_parser_label = 0
90    if cur_parser_label:
91        atomic_operations[cur_parser_label] = [EMPTY_MEMORY_ORDER]
92    return atomic_comments, atomic_operations
93
94
95def process_results(atomic_comments: dict, atomic_operations: dict) -> int:
96    exit_code = 0
97    for index, memory_orders in atomic_operations.items():
98        for memory_order in memory_orders:
99            is_commented = False
100            for i in reversed(range(index - MAX_OFFSET, index)):
101                commented_memory_order = atomic_comments.get(i)
102                if memory_order == commented_memory_order or \
103                        memory_order == EMPTY_MEMORY_ORDER and \
104                        commented_memory_order == PARAMETERIZED_MEMORY_ORDER:
105                    is_commented = True
106                    if commented_memory_order == PARAMETERIZED_MEMORY_ORDER:
107                        memory_order = PARAMETERIZED_MEMORY_ORDER
108                    break
109            if not is_commented:
110                print("File {}:{}: reason for specific memory order was not "
111                      "specified for atomic operation".format(FILE, index))
112                exit_code = 1
113            else:
114                if memory_order == EMPTY_MEMORY_ORDER:
115                    print("File {}:{}: memory order was not specified for "
116                          "atomic operation".format(FILE, index))
117                    exit_code = 1
118    return exit_code
119
120
121if __name__ == "__main__":
122    COMMENTS, OPERATIONS = parse_file()
123    sys.exit(process_results(COMMENTS, OPERATIONS))
124