xref: /aosp_15_r20/external/emboss/compiler/front_end/synthetics_test.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1# Copyright 2019 Google LLC
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#     https://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
15"""Tests for front_end.synthetics."""
16
17import unittest
18from compiler.front_end import glue
19from compiler.front_end import synthetics
20from compiler.util import error
21from compiler.util import ir_data
22from compiler.util import test_util
23
24
25class SyntheticsTest(unittest.TestCase):
26
27  def _find_attribute(self, field, name):
28    result = None
29    for attribute in field.attribute:
30      if attribute.name.text == name:
31        self.assertIsNone(result)
32        result = attribute
33    self.assertIsNotNone(result)
34    return result
35
36  def _make_ir(self, emb_text):
37    ir, unused_debug_info, errors = glue.parse_emboss_file(
38        "m.emb",
39        test_util.dict_file_reader({"m.emb": emb_text}),
40        stop_before_step="desugar")
41    assert not errors, errors
42    return ir
43
44  def test_nothing_to_do(self):
45    ir = self._make_ir("struct Foo:\n"
46                       "  0 [+1]     UInt      x\n"
47                       "  1 [+1]     UInt:8[]  y\n")
48    self.assertEqual([], synthetics.desugar(ir))
49
50  def test_adds_anonymous_bits_fields(self):
51    ir = self._make_ir("struct Foo:\n"
52                       "  0 [+1]  bits:\n"
53                       "    0 [+4]  Bar   bar\n"
54                       "    4 [+4]  UInt  uint\n"
55                       "  1 [+1]  bits:\n"
56                       "    0 [+4]  Bits  nested_bits\n"
57                       "enum Bar:\n"
58                       "  BAR = 0\n"
59                       "bits Bits:\n"
60                       "  0 [+4]  UInt  uint\n")
61    self.assertEqual([], synthetics.desugar(ir))
62    structure = ir.module[0].type[0].structure
63    # The first field should be the anonymous bits structure.
64    self.assertTrue(structure.field[0].HasField("location"))
65    # Then the aliases generated for those structures.
66    self.assertEqual("bar", structure.field[1].name.name.text)
67    self.assertEqual("uint", structure.field[2].name.name.text)
68    # Then the second anonymous bits.
69    self.assertTrue(structure.field[3].HasField("location"))
70    # Then the alias from the second anonymous bits.
71    self.assertEqual("nested_bits", structure.field[4].name.name.text)
72
73  def test_adds_correct_existence_condition(self):
74    ir = self._make_ir("struct Foo:\n"
75                       "  0 [+1]  bits:\n"
76                       "    0 [+4]  UInt  bar\n")
77    self.assertEqual([], synthetics.desugar(ir))
78    bits_field = ir.module[0].type[0].structure.field[0]
79    alias_field = ir.module[0].type[0].structure.field[1]
80    self.assertEqual("bar", alias_field.name.name.text)
81    self.assertEqual(bits_field.name.name.text,
82                     alias_field.existence_condition.function.args[0].function.
83                     args[0].field_reference.path[0].source_name[-1].text)
84    self.assertEqual(bits_field.name.name.text,
85                     alias_field.existence_condition.function.args[1].function.
86                     args[0].field_reference.path[0].source_name[-1].text)
87    self.assertEqual("bar",
88                     alias_field.existence_condition.function.args[1].function.
89                     args[0].field_reference.path[1].source_name[-1].text)
90    self.assertEqual(
91        ir_data.FunctionMapping.PRESENCE,
92        alias_field.existence_condition.function.args[0].function.function)
93    self.assertEqual(
94        ir_data.FunctionMapping.PRESENCE,
95        alias_field.existence_condition.function.args[1].function.function)
96    self.assertEqual(ir_data.FunctionMapping.AND,
97                     alias_field.existence_condition.function.function)
98
99  def test_adds_correct_read_transform(self):
100    ir = self._make_ir("struct Foo:\n"
101                       "  0 [+1]  bits:\n"
102                       "    0 [+4]  UInt  bar\n")
103    self.assertEqual([], synthetics.desugar(ir))
104    bits_field = ir.module[0].type[0].structure.field[0]
105    alias_field = ir.module[0].type[0].structure.field[1]
106    self.assertEqual("bar", alias_field.name.name.text)
107    self.assertEqual(
108        bits_field.name.name.text,
109        alias_field.read_transform.field_reference.path[0].source_name[-1].text)
110    self.assertEqual(
111        "bar",
112        alias_field.read_transform.field_reference.path[1].source_name[-1].text)
113
114  def test_adds_correct_abbreviation(self):
115    ir = self._make_ir("struct Foo:\n"
116                       "  0 [+1]  bits:\n"
117                       "    0 [+4]  UInt  bar\n"
118                       "    4 [+4]  UInt  baz (qux)\n")
119    self.assertEqual([], synthetics.desugar(ir))
120    bar_alias = ir.module[0].type[0].structure.field[1]
121    baz_alias = ir.module[0].type[0].structure.field[2]
122    self.assertFalse(bar_alias.HasField("abbreviation"))
123    self.assertEqual("qux", baz_alias.abbreviation.text)
124
125  def test_anonymous_bits_sets_correct_is_synthetic(self):
126    ir = self._make_ir("struct Foo:\n"
127                       "  0 [+1]  bits:\n"
128                       "    0 [+4]  UInt  bar (b)\n")
129    self.assertEqual([], synthetics.desugar(ir))
130    bits_field = ir.module[0].type[0].subtype[0].structure.field[0]
131    alias_field = ir.module[0].type[0].structure.field[1]
132    self.assertFalse(alias_field.name.source_location.is_synthetic)
133    self.assertTrue(alias_field.HasField("abbreviation"))
134    self.assertFalse(alias_field.abbreviation.source_location.is_synthetic)
135    self.assertTrue(alias_field.HasField("read_transform"))
136    read_alias = alias_field.read_transform
137    self.assertTrue(read_alias.source_location.is_synthetic)
138    self.assertTrue(
139        read_alias.field_reference.path[0].source_location.is_synthetic)
140    alias_condition = alias_field.existence_condition
141    self.assertTrue(alias_condition.source_location.is_synthetic)
142    self.assertTrue(
143        alias_condition.function.args[0].source_location.is_synthetic)
144    self.assertTrue(bits_field.name.source_location.is_synthetic)
145    self.assertTrue(bits_field.name.name.source_location.is_synthetic)
146    self.assertTrue(bits_field.abbreviation.source_location.is_synthetic)
147
148  def test_adds_text_output_skip_attribute_to_anonymous_bits(self):
149    ir = self._make_ir("struct Foo:\n"
150                       "  0 [+1]  bits:\n"
151                       "    0 [+4]  UInt  bar (b)\n")
152    self.assertEqual([], synthetics.desugar(ir))
153    bits_field = ir.module[0].type[0].structure.field[0]
154    text_output_attribute = self._find_attribute(bits_field, "text_output")
155    self.assertEqual("Skip", text_output_attribute.value.string_constant.text)
156
157  def test_skip_attribute_is_marked_as_synthetic(self):
158    ir = self._make_ir("struct Foo:\n"
159                       "  0 [+1]  bits:\n"
160                       "    0 [+4]  UInt  bar\n")
161    self.assertEqual([], synthetics.desugar(ir))
162    bits_field = ir.module[0].type[0].structure.field[0]
163    attribute = self._find_attribute(bits_field, "text_output")
164    self.assertTrue(attribute.source_location.is_synthetic)
165    self.assertTrue(attribute.name.source_location.is_synthetic)
166    self.assertTrue(attribute.value.source_location.is_synthetic)
167    self.assertTrue(
168        attribute.value.string_constant.source_location.is_synthetic)
169
170  def test_adds_size_in_bytes(self):
171    ir = self._make_ir("struct Foo:\n"
172                       "  1 [+l]  UInt:8[]  bytes\n"
173                       "  0 [+1]  UInt      length (l)\n")
174    self.assertEqual([], synthetics.desugar(ir))
175    structure = ir.module[0].type[0].structure
176    size_in_bytes_field = structure.field[2]
177    max_size_in_bytes_field = structure.field[3]
178    min_size_in_bytes_field = structure.field[4]
179    self.assertEqual("$size_in_bytes", size_in_bytes_field.name.name.text)
180    self.assertEqual(ir_data.FunctionMapping.MAXIMUM,
181                     size_in_bytes_field.read_transform.function.function)
182    self.assertEqual("$max_size_in_bytes",
183                     max_size_in_bytes_field.name.name.text)
184    self.assertEqual(ir_data.FunctionMapping.UPPER_BOUND,
185                     max_size_in_bytes_field.read_transform.function.function)
186    self.assertEqual("$min_size_in_bytes",
187                     min_size_in_bytes_field.name.name.text)
188    self.assertEqual(ir_data.FunctionMapping.LOWER_BOUND,
189                     min_size_in_bytes_field.read_transform.function.function)
190    # The correctness of $size_in_bytes et al are tested much further down
191    # stream, in tests of the generated C++ code.
192
193  def test_adds_size_in_bits(self):
194    ir = self._make_ir("bits Foo:\n"
195                       "  1 [+9]  UInt  hi\n"
196                       "  0 [+1]  Flag  lo\n")
197    self.assertEqual([], synthetics.desugar(ir))
198    structure = ir.module[0].type[0].structure
199    size_in_bits_field = structure.field[2]
200    max_size_in_bits_field = structure.field[3]
201    min_size_in_bits_field = structure.field[4]
202    self.assertEqual("$size_in_bits", size_in_bits_field.name.name.text)
203    self.assertEqual(ir_data.FunctionMapping.MAXIMUM,
204                     size_in_bits_field.read_transform.function.function)
205    self.assertEqual("$max_size_in_bits",
206                     max_size_in_bits_field.name.name.text)
207    self.assertEqual(ir_data.FunctionMapping.UPPER_BOUND,
208                     max_size_in_bits_field.read_transform.function.function)
209    self.assertEqual("$min_size_in_bits",
210                     min_size_in_bits_field.name.name.text)
211    self.assertEqual(ir_data.FunctionMapping.LOWER_BOUND,
212                     min_size_in_bits_field.read_transform.function.function)
213    # The correctness of $size_in_bits et al are tested much further down
214    # stream, in tests of the generated C++ code.
215
216  def test_adds_text_output_skip_attribute_to_size_in_bytes(self):
217    ir = self._make_ir("struct Foo:\n"
218                       "  1 [+l]  UInt:8[]  bytes\n"
219                       "  0 [+1]  UInt      length (l)\n")
220    self.assertEqual([], synthetics.desugar(ir))
221    size_in_bytes_field = ir.module[0].type[0].structure.field[2]
222    self.assertEqual("$size_in_bytes", size_in_bytes_field.name.name.text)
223    text_output_attribute = self._find_attribute(size_in_bytes_field,
224                                                 "text_output")
225    self.assertEqual("Skip", text_output_attribute.value.string_constant.text)
226
227  def test_replaces_next(self):
228    ir = self._make_ir("struct Foo:\n"
229                       "  1     [+2]  UInt:8[]  a\n"
230                       "  $next [+4]  UInt      b\n"
231                       "  $next [+1]  UInt      c\n")
232    self.assertEqual([], synthetics.desugar(ir))
233    offset_of_b = ir.module[0].type[0].structure.field[1].location.start
234    self.assertTrue(offset_of_b.HasField("function"))
235    self.assertEqual(offset_of_b.function.function, ir_data.FunctionMapping.ADDITION)
236    self.assertEqual(offset_of_b.function.args[0].constant.value, "1")
237    self.assertEqual(offset_of_b.function.args[1].constant.value, "2")
238    offset_of_c = ir.module[0].type[0].structure.field[2].location.start
239    self.assertEqual(
240            offset_of_c.function.args[0].function.args[0].constant.value, "1")
241    self.assertEqual(
242            offset_of_c.function.args[0].function.args[1].constant.value, "2")
243    self.assertEqual(offset_of_c.function.args[1].constant.value, "4")
244
245  def test_next_in_first_field(self):
246    ir = self._make_ir("struct Foo:\n"
247                       "  $next [+2]  UInt:8[]  a\n"
248                       "  $next [+4]  UInt      b\n")
249    struct = ir.module[0].type[0].structure
250    self.assertEqual([[
251        error.error("m.emb", struct.field[0].location.start.source_location,
252                    "`$next` may not be used in the first physical field of " +
253                    "a structure; perhaps you meant `0`?"),
254    ]], synthetics.desugar(ir))
255
256  def test_next_in_size(self):
257    ir = self._make_ir("struct Foo:\n"
258                       "  0 [+2]      UInt:8[]  a\n"
259                       "  1 [+$next]  UInt      b\n")
260    struct = ir.module[0].type[0].structure
261    self.assertEqual([[
262        error.error("m.emb", struct.field[1].location.size.source_location,
263                    "`$next` may only be used in the start expression of a " +
264                    "physical field."),
265    ]], synthetics.desugar(ir))
266
267
268if __name__ == "__main__":
269  unittest.main()
270