1#encoding=utf-8
2# Copyright © 2017 Intel Corporation
3
4# Permission is hereby granted, free of charge, to any person obtaining a copy
5# of this software and associated documentation files (the "Software"), to deal
6# in the Software without restriction, including without limitation the rights
7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8# copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10
11# The above copyright notice and this permission notice shall be included in
12# all copies or substantial portions of the Software.
13
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20# SOFTWARE.
21
22from __future__ import (
23    absolute_import, division, print_function, unicode_literals
24)
25
26import argparse
27import os
28import sys
29import xml.parsers.expat
30
31from mako.template import Template
32
33TEMPLATE = Template("""\
34<%!
35from operator import itemgetter
36%>\
37/*
38 * Copyright © 2017 Intel Corporation
39 *
40 * Permission is hereby granted, free of charge, to any person obtaining a
41 * copy of this software and associated documentation files (the "Software"),
42 * to deal in the Software without restriction, including without limitation
43 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
44 * and/or sell copies of the Software, and to permit persons to whom the
45 * Software is furnished to do so, subject to the following conditions:
46 *
47 * The above copyright notice and this permission notice (including the next
48 * paragraph) shall be included in all copies or substantial portions of the
49 * Software.
50 *
51 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
54 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
56 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
57 * IN THE SOFTWARE.
58 */
59
60/* THIS FILE HAS BEEN GENERATED, DO NOT HAND EDIT.
61 *
62 * Sizes of bitfields in genxml instructions, structures, and registers.
63 */
64
65#ifndef ${guard}
66#define ${guard}
67
68#include <stdint.h>
69
70#include "common/gen_device_info.h"
71#include "util/macros.h"
72
73<%def name="emit_per_gen_prop_func(item, prop)">
74%if item.has_prop(prop):
75% for gen, value in sorted(item.iter_prop(prop), reverse=True):
76#define ${gen.prefix(item.token_name)}_${prop}  ${value}
77% endfor
78
79static inline uint32_t ATTRIBUTE_PURE
80${item.token_name}_${prop}(const struct gen_device_info *devinfo)
81{
82   switch (devinfo->gen) {
83   case 10: return ${item.get_prop(prop, 10)};
84   case 9: return ${item.get_prop(prop, 9)};
85   case 8: return ${item.get_prop(prop, 8)};
86   case 7:
87      if (devinfo->is_haswell) {
88         return ${item.get_prop(prop, 7.5)};
89      } else {
90         return ${item.get_prop(prop, 7)};
91      }
92   case 6: return ${item.get_prop(prop, 6)};
93   case 5: return ${item.get_prop(prop, 5)};
94   case 4:
95      if (devinfo->is_g4x) {
96         return ${item.get_prop(prop, 4.5)};
97      } else {
98         return ${item.get_prop(prop, 4)};
99      }
100   default:
101      unreachable("Invalid hardware generation");
102   }
103}
104%endif
105</%def>
106
107#ifdef __cplusplus
108extern "C" {
109#endif
110% for _, container in sorted(containers.iteritems(), key=itemgetter(0)):
111
112/* ${container.name} */
113
114${emit_per_gen_prop_func(container, 'length')}
115
116% for _, field in sorted(container.fields.iteritems(), key=itemgetter(0)):
117
118/* ${container.name}::${field.name} */
119
120${emit_per_gen_prop_func(field, 'bits')}
121
122${emit_per_gen_prop_func(field, 'start')}
123
124% endfor
125% endfor
126
127#ifdef __cplusplus
128}
129#endif
130
131#endif /* ${guard} */""", output_encoding='utf-8')
132
133def to_alphanum(name):
134    substitutions = {
135        ' ': '',
136        '/': '',
137        '[': '',
138        ']': '',
139        '(': '',
140        ')': '',
141        '-': '',
142        ':': '',
143        '.': '',
144        ',': '',
145        '=': '',
146        '>': '',
147        '#': '',
148        'α': 'alpha',
149        '&': '',
150        '*': '',
151        '"': '',
152        '+': '',
153        '\'': '',
154    }
155
156    for i, j in substitutions.items():
157        name = name.replace(i, j)
158
159    return name
160
161def safe_name(name):
162    name = to_alphanum(name)
163    if not name[0].isalpha():
164        name = '_' + name
165    return name
166
167class Gen(object):
168
169    def __init__(self, z):
170        # Convert potential "major.minor" string
171        self.tenx = int(float(z) * 10)
172
173    def __lt__(self, other):
174        return self.tenx < other.tenx
175
176    def __hash__(self):
177        return hash(self.tenx)
178
179    def __eq__(self, other):
180        return self.tenx == other.tenx
181
182    def prefix(self, token):
183        gen = self.tenx
184
185        if gen % 10 == 0:
186            gen //= 10
187
188        if token[0] == '_':
189            token = token[1:]
190
191        return 'GEN{}_{}'.format(gen, token)
192
193class Container(object):
194
195    def __init__(self, name):
196        self.name = name
197        self.token_name = safe_name(name)
198        self.length_by_gen = {}
199        self.fields = {}
200
201    def add_gen(self, gen, xml_attrs):
202        assert isinstance(gen, Gen)
203        if 'length' in xml_attrs:
204            self.length_by_gen[gen] = xml_attrs['length']
205
206    def get_field(self, field_name, create=False):
207        if field_name not in self.fields:
208            if create:
209                self.fields[field_name] = Field(self, field_name)
210            else:
211                return None
212        return self.fields[field_name]
213
214    def has_prop(self, prop):
215        if prop == 'length':
216            return bool(self.length_by_gen)
217        else:
218            raise ValueError('Invalid property: "{0}"'.format(prop))
219
220    def iter_prop(self, prop):
221        if prop == 'length':
222            return self.length_by_gen.iteritems()
223        else:
224            raise ValueError('Invalid property: "{0}"'.format(prop))
225
226    def get_prop(self, prop, gen):
227        if not isinstance(gen, Gen):
228            gen = Gen(gen)
229
230        if prop == 'length':
231            return self.length_by_gen.get(gen, 0)
232        else:
233            raise ValueError('Invalid property: "{0}"'.format(prop))
234
235class Field(object):
236
237    def __init__(self, container, name):
238        self.name = name
239        self.token_name = safe_name('_'.join([container.name, self.name]))
240        self.bits_by_gen = {}
241        self.start_by_gen = {}
242
243    def add_gen(self, gen, xml_attrs):
244        assert isinstance(gen, Gen)
245        start = int(xml_attrs['start'])
246        end = int(xml_attrs['end'])
247        self.start_by_gen[gen] = start
248        self.bits_by_gen[gen] = 1 + end - start
249
250    def has_prop(self, prop):
251        return True
252
253    def iter_prop(self, prop):
254        if prop == 'bits':
255            return self.bits_by_gen.iteritems()
256        elif prop == 'start':
257            return self.start_by_gen.iteritems()
258        else:
259            raise ValueError('Invalid property: "{0}"'.format(prop))
260
261    def get_prop(self, prop, gen):
262        if not isinstance(gen, Gen):
263            gen = Gen(gen)
264
265        if prop == 'bits':
266            return self.bits_by_gen.get(gen, 0)
267        elif prop == 'start':
268            return self.start_by_gen.get(gen, 0)
269        else:
270            raise ValueError('Invalid property: "{0}"'.format(prop))
271
272class XmlParser(object):
273
274    def __init__(self, containers):
275        self.parser = xml.parsers.expat.ParserCreate()
276        self.parser.StartElementHandler = self.start_element
277        self.parser.EndElementHandler = self.end_element
278
279        self.gen = None
280        self.containers = containers
281        self.container = None
282
283    def parse(self, filename):
284        with open(filename) as f:
285            self.parser.ParseFile(f)
286
287    def start_element(self, name, attrs):
288        if name == 'genxml':
289            self.gen = Gen(attrs['gen'])
290        elif name in ('instruction', 'struct', 'register'):
291            self.start_container(attrs)
292        elif name == 'field':
293            self.start_field(attrs)
294        else:
295            pass
296
297    def end_element(self, name):
298        if name == 'genxml':
299            self.gen = None
300        elif name in ('instruction', 'struct', 'register'):
301            self.container = None
302        else:
303            pass
304
305    def start_container(self, attrs):
306        assert self.container is None
307        name = attrs['name']
308        if name not in self.containers:
309            self.containers[name] = Container(name)
310        self.container = self.containers[name]
311        self.container.add_gen(self.gen, attrs)
312
313    def start_field(self, attrs):
314        if self.container is None:
315            return
316
317        field_name = attrs.get('name', None)
318        if not field_name:
319            return
320
321        self.container.get_field(field_name, True).add_gen(self.gen, attrs)
322
323def parse_args():
324    p = argparse.ArgumentParser()
325    p.add_argument('-o', '--output', type=str,
326                   help="If OUTPUT is unset or '-', then it defaults to '/dev/stdout'")
327    p.add_argument('--cpp-guard', type=str,
328                   help='If unset, then CPP_GUARD is derived from OUTPUT.')
329    p.add_argument('xml_sources', metavar='XML_SOURCE', nargs='+')
330
331    pargs = p.parse_args()
332
333    if pargs.output in (None, '-'):
334        pargs.output = '/dev/stdout'
335
336    if pargs.cpp_guard is None:
337        pargs.cpp_guard = os.path.basename(pargs.output).upper().replace('.', '_')
338
339    return pargs
340
341def main():
342    pargs = parse_args()
343
344    # Maps name => Container
345    containers = {}
346
347    for source in pargs.xml_sources:
348        XmlParser(containers).parse(source)
349
350    with open(pargs.output, 'wb') as f:
351        f.write(TEMPLATE.render(containers=containers, guard=pargs.cpp_guard))
352
353if __name__ == '__main__':
354    main()
355