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 xml.parsers.expat
29
30from mako.template import Template
31from util import *
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 "dev/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 12: return ${item.get_prop(prop, 12)};
84   case 11: return ${item.get_prop(prop, 11)};
85   case 9: return ${item.get_prop(prop, 9)};
86   case 8: return ${item.get_prop(prop, 8)};
87   case 7:
88%if item.get_prop(prop, 7.5) == item.get_prop(prop, 7):
89      return ${item.get_prop(prop, 7)};
90%else:
91      if (devinfo->is_haswell) {
92         return ${item.get_prop(prop, 7.5)};
93      } else {
94         return ${item.get_prop(prop, 7)};
95      }
96%endif
97   case 6: return ${item.get_prop(prop, 6)};
98   case 5: return ${item.get_prop(prop, 5)};
99   case 4:
100%if item.get_prop(prop, 4.5) == item.get_prop(prop, 4):
101      return ${item.get_prop(prop, 4)};
102%else:
103      if (devinfo->is_g4x) {
104         return ${item.get_prop(prop, 4.5)};
105      } else {
106         return ${item.get_prop(prop, 4)};
107      }
108%endif
109   default:
110      unreachable("Invalid hardware generation");
111   }
112}
113%endif
114</%def>
115
116#ifdef __cplusplus
117extern "C" {
118#endif
119% for _, container in sorted(containers.items(), key=itemgetter(0)):
120
121/* ${container.name} */
122
123${emit_per_gen_prop_func(container, 'length')}
124
125% for _, field in sorted(container.fields.items(), key=itemgetter(0)):
126
127/* ${container.name}::${field.name} */
128
129${emit_per_gen_prop_func(field, 'bits')}
130
131${emit_per_gen_prop_func(field, 'start')}
132
133% endfor
134% endfor
135
136#ifdef __cplusplus
137}
138#endif
139
140#endif /* ${guard} */""", output_encoding='utf-8')
141
142class Gen(object):
143
144    def __init__(self, z):
145        # Convert potential "major.minor" string
146        self.tenx = int(float(z) * 10)
147
148    def __lt__(self, other):
149        return self.tenx < other.tenx
150
151    def __hash__(self):
152        return hash(self.tenx)
153
154    def __eq__(self, other):
155        return self.tenx == other.tenx
156
157    def prefix(self, token):
158        gen = self.tenx
159
160        if gen % 10 == 0:
161            gen //= 10
162
163        if token[0] == '_':
164            token = token[1:]
165
166        return 'GEN{}_{}'.format(gen, token)
167
168class Container(object):
169
170    def __init__(self, name):
171        self.name = name
172        self.token_name = safe_name(name)
173        self.length_by_gen = {}
174        self.fields = {}
175
176    def add_gen(self, gen, xml_attrs):
177        assert isinstance(gen, Gen)
178        if 'length' in xml_attrs:
179            self.length_by_gen[gen] = xml_attrs['length']
180
181    def get_field(self, field_name, create=False):
182        key = to_alphanum(field_name)
183        if key not in self.fields:
184            if create:
185                self.fields[key] = Field(self, field_name)
186            else:
187                return None
188        return self.fields[key]
189
190    def has_prop(self, prop):
191        if prop == 'length':
192            return bool(self.length_by_gen)
193        else:
194            raise ValueError('Invalid property: "{0}"'.format(prop))
195
196    def iter_prop(self, prop):
197        if prop == 'length':
198            return self.length_by_gen.items()
199        else:
200            raise ValueError('Invalid property: "{0}"'.format(prop))
201
202    def get_prop(self, prop, gen):
203        if not isinstance(gen, Gen):
204            gen = Gen(gen)
205
206        if prop == 'length':
207            return self.length_by_gen.get(gen, 0)
208        else:
209            raise ValueError('Invalid property: "{0}"'.format(prop))
210
211class Field(object):
212
213    def __init__(self, container, name):
214        self.name = name
215        self.token_name = safe_name('_'.join([container.name, self.name]))
216        self.bits_by_gen = {}
217        self.start_by_gen = {}
218
219    def add_gen(self, gen, xml_attrs):
220        assert isinstance(gen, Gen)
221        start = int(xml_attrs['start'])
222        end = int(xml_attrs['end'])
223        self.start_by_gen[gen] = start
224        self.bits_by_gen[gen] = 1 + end - start
225
226    def has_prop(self, prop):
227        return True
228
229    def iter_prop(self, prop):
230        if prop == 'bits':
231            return self.bits_by_gen.items()
232        elif prop == 'start':
233            return self.start_by_gen.items()
234        else:
235            raise ValueError('Invalid property: "{0}"'.format(prop))
236
237    def get_prop(self, prop, gen):
238        if not isinstance(gen, Gen):
239            gen = Gen(gen)
240
241        if prop == 'bits':
242            return self.bits_by_gen.get(gen, 0)
243        elif prop == 'start':
244            return self.start_by_gen.get(gen, 0)
245        else:
246            raise ValueError('Invalid property: "{0}"'.format(prop))
247
248class XmlParser(object):
249
250    def __init__(self, containers):
251        self.parser = xml.parsers.expat.ParserCreate()
252        self.parser.StartElementHandler = self.start_element
253        self.parser.EndElementHandler = self.end_element
254
255        self.gen = None
256        self.containers = containers
257        self.container_stack = []
258        self.container_stack.append(None)
259
260    def parse(self, filename):
261        with open(filename, 'rb') as f:
262            self.parser.ParseFile(f)
263
264    def start_element(self, name, attrs):
265        if name == 'genxml':
266            self.gen = Gen(attrs['gen'])
267        elif name in ('instruction', 'struct', 'register'):
268            if name == 'instruction' and 'engine' in attrs:
269                engines = set(attrs['engine'].split('|'))
270                if not engines & self.engines:
271                    self.container_stack.append(None)
272                    return
273            self.start_container(attrs)
274        elif name == 'group':
275            self.container_stack.append(None)
276        elif name == 'field':
277            self.start_field(attrs)
278        else:
279            pass
280
281    def end_element(self, name):
282        if name == 'genxml':
283            self.gen = None
284        elif name in ('instruction', 'struct', 'register', 'group'):
285            self.container_stack.pop()
286        else:
287            pass
288
289    def start_container(self, attrs):
290        assert self.container_stack[-1] is None
291        name = attrs['name']
292        if name not in self.containers:
293            self.containers[name] = Container(name)
294        self.container_stack.append(self.containers[name])
295        self.container_stack[-1].add_gen(self.gen, attrs)
296
297    def start_field(self, attrs):
298        if self.container_stack[-1] is None:
299            return
300
301        field_name = attrs.get('name', None)
302        if not field_name:
303            return
304
305        self.container_stack[-1].get_field(field_name, True).add_gen(self.gen, attrs)
306
307def parse_args():
308    p = argparse.ArgumentParser()
309    p.add_argument('-o', '--output', type=str,
310                   help="If OUTPUT is unset or '-', then it defaults to '/dev/stdout'")
311    p.add_argument('--cpp-guard', type=str,
312                   help='If unset, then CPP_GUARD is derived from OUTPUT.')
313    p.add_argument('--engines', nargs='?', type=str, default='render',
314                   help="Comma-separated list of engines whose instructions should be parsed (default: %(default)s)")
315    p.add_argument('xml_sources', metavar='XML_SOURCE', nargs='+')
316
317    pargs = p.parse_args()
318
319    if pargs.output in (None, '-'):
320        pargs.output = '/dev/stdout'
321
322    if pargs.cpp_guard is None:
323        pargs.cpp_guard = os.path.basename(pargs.output).upper().replace('.', '_')
324
325    return pargs
326
327def main():
328    pargs = parse_args()
329
330    engines = pargs.engines.split(',')
331    valid_engines = [ 'render', 'blitter', 'video' ]
332    if set(engines) - set(valid_engines):
333        print("Invalid engine specified, valid engines are:\n")
334        for e in valid_engines:
335            print("\t%s" % e)
336        sys.exit(1)
337
338    # Maps name => Container
339    containers = {}
340
341    for source in pargs.xml_sources:
342        p = XmlParser(containers)
343        p.engines = set(engines)
344        p.parse(source)
345
346    with open(pargs.output, 'wb') as f:
347        f.write(TEMPLATE.render(containers=containers, guard=pargs.cpp_guard))
348
349if __name__ == '__main__':
350    main()
351