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