gen_pack_header.py revision 9f464c52
1#encoding=utf-8
2
3from __future__ import (
4    absolute_import, division, print_function, unicode_literals
5)
6import argparse
7import ast
8import xml.parsers.expat
9import re
10import sys
11import copy
12import textwrap
13
14license =  """/*
15 * Copyright (C) 2016 Intel Corporation
16 *
17 * Permission is hereby granted, free of charge, to any person obtaining a
18 * copy of this software and associated documentation files (the "Software"),
19 * to deal in the Software without restriction, including without limitation
20 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21 * and/or sell copies of the Software, and to permit persons to whom the
22 * Software is furnished to do so, subject to the following conditions:
23 *
24 * The above copyright notice and this permission notice (including the next
25 * paragraph) shall be included in all copies or substantial portions of the
26 * Software.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
31 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
34 * IN THE SOFTWARE.
35 */
36"""
37
38pack_header = """%(license)s
39
40/* Instructions, enums and structures for %(platform)s.
41 *
42 * This file has been generated, do not hand edit.
43 */
44
45#ifndef %(guard)s
46#define %(guard)s
47
48#include <stdio.h>
49#include <stdint.h>
50#include <stdbool.h>
51#include <assert.h>
52#include <math.h>
53
54#ifndef __gen_validate_value
55#define __gen_validate_value(x)
56#endif
57
58#ifndef __gen_field_functions
59#define __gen_field_functions
60
61#ifdef NDEBUG
62#define NDEBUG_UNUSED __attribute__((unused))
63#else
64#define NDEBUG_UNUSED
65#endif
66
67union __gen_value {
68   float f;
69   uint32_t dw;
70};
71
72static inline uint64_t
73__gen_mbo(uint32_t start, uint32_t end)
74{
75   return (~0ull >> (64 - (end - start + 1))) << start;
76}
77
78static inline uint64_t
79__gen_uint(uint64_t v, uint32_t start, NDEBUG_UNUSED uint32_t end)
80{
81   __gen_validate_value(v);
82
83#ifndef NDEBUG
84   const int width = end - start + 1;
85   if (width < 64) {
86      const uint64_t max = (1ull << width) - 1;
87      assert(v <= max);
88   }
89#endif
90
91   return v << start;
92}
93
94static inline uint64_t
95__gen_sint(int64_t v, uint32_t start, uint32_t end)
96{
97   const int width = end - start + 1;
98
99   __gen_validate_value(v);
100
101#ifndef NDEBUG
102   if (width < 64) {
103      const int64_t max = (1ll << (width - 1)) - 1;
104      const int64_t min = -(1ll << (width - 1));
105      assert(min <= v && v <= max);
106   }
107#endif
108
109   const uint64_t mask = ~0ull >> (64 - width);
110
111   return (v & mask) << start;
112}
113
114static inline uint64_t
115__gen_offset(uint64_t v, NDEBUG_UNUSED uint32_t start, NDEBUG_UNUSED uint32_t end)
116{
117   __gen_validate_value(v);
118#ifndef NDEBUG
119   uint64_t mask = (~0ull >> (64 - (end - start + 1))) << start;
120
121   assert((v & ~mask) == 0);
122#endif
123
124   return v;
125}
126
127static inline uint32_t
128__gen_float(float v)
129{
130   __gen_validate_value(v);
131   return ((union __gen_value) { .f = (v) }).dw;
132}
133
134static inline uint64_t
135__gen_sfixed(float v, uint32_t start, uint32_t end, uint32_t fract_bits)
136{
137   __gen_validate_value(v);
138
139   const float factor = (1 << fract_bits);
140
141#ifndef NDEBUG
142   const float max = ((1 << (end - start)) - 1) / factor;
143   const float min = -(1 << (end - start)) / factor;
144   assert(min <= v && v <= max);
145#endif
146
147   const int64_t int_val = llroundf(v * factor);
148   const uint64_t mask = ~0ull >> (64 - (end - start + 1));
149
150   return (int_val & mask) << start;
151}
152
153static inline uint64_t
154__gen_ufixed(float v, uint32_t start, NDEBUG_UNUSED uint32_t end, uint32_t fract_bits)
155{
156   __gen_validate_value(v);
157
158   const float factor = (1 << fract_bits);
159
160#ifndef NDEBUG
161   const float max = ((1 << (end - start + 1)) - 1) / factor;
162   const float min = 0.0f;
163   assert(min <= v && v <= max);
164#endif
165
166   const uint64_t uint_val = llroundf(v * factor);
167
168   return uint_val << start;
169}
170
171#ifndef __gen_address_type
172#error #define __gen_address_type before including this file
173#endif
174
175#ifndef __gen_user_data
176#error #define __gen_combine_address before including this file
177#endif
178
179#undef NDEBUG_UNUSED
180
181#endif
182
183"""
184
185def to_alphanum(name):
186    substitutions = {
187        ' ': '',
188        '/': '',
189        '[': '',
190        ']': '',
191        '(': '',
192        ')': '',
193        '-': '',
194        ':': '',
195        '.': '',
196        ',': '',
197        '=': '',
198        '>': '',
199        '#': '',
200        'α': 'alpha',
201        '&': '',
202        '*': '',
203        '"': '',
204        '+': '',
205        '\'': '',
206    }
207
208    for i, j in substitutions.items():
209        name = name.replace(i, j)
210
211    return name
212
213def safe_name(name):
214    name = to_alphanum(name)
215    if not name[0].isalpha():
216        name = '_' + name
217
218    return name
219
220def num_from_str(num_str):
221    if num_str.lower().startswith('0x'):
222        return int(num_str, base=16)
223
224    assert not num_str.startswith('0'), 'octals numbers not allowed'
225    return int(num_str)
226
227class Field(object):
228    ufixed_pattern = re.compile(r"u(\d+)\.(\d+)")
229    sfixed_pattern = re.compile(r"s(\d+)\.(\d+)")
230
231    def __init__(self, parser, attrs):
232        self.parser = parser
233        if "name" in attrs:
234            self.name = safe_name(attrs["name"])
235        self.start = int(attrs["start"])
236        self.end = int(attrs["end"])
237        self.type = attrs["type"]
238
239        assert self.start <= self.end, \
240               'field {} has end ({}) < start ({})'.format(self.name, self.end,
241                                                           self.start)
242        if self.type == 'bool':
243            assert self.end == self.start, \
244                   'bool field ({}) is too wide'.format(self.name)
245
246        if "prefix" in attrs:
247            self.prefix = attrs["prefix"]
248        else:
249            self.prefix = None
250
251        if "default" in attrs:
252            # Base 0 recognizes 0x, 0o, 0b prefixes in addition to decimal ints.
253            self.default = int(attrs["default"], base=0)
254        else:
255            self.default = None
256
257        ufixed_match = Field.ufixed_pattern.match(self.type)
258        if ufixed_match:
259            self.type = 'ufixed'
260            self.fractional_size = int(ufixed_match.group(2))
261
262        sfixed_match = Field.sfixed_pattern.match(self.type)
263        if sfixed_match:
264            self.type = 'sfixed'
265            self.fractional_size = int(sfixed_match.group(2))
266
267    def is_builtin_type(self):
268        builtins =  [ 'address', 'bool', 'float', 'ufixed',
269                      'offset', 'sfixed', 'offset', 'int', 'uint', 'mbo' ]
270        return self.type in builtins
271
272    def is_struct_type(self):
273        return self.type in self.parser.structs
274
275    def is_enum_type(self):
276        return self.type in self.parser.enums
277
278    def emit_template_struct(self, dim):
279        if self.type == 'address':
280            type = '__gen_address_type'
281        elif self.type == 'bool':
282            type = 'bool'
283        elif self.type == 'float':
284            type = 'float'
285        elif self.type == 'ufixed':
286            type = 'float'
287        elif self.type == 'sfixed':
288            type = 'float'
289        elif self.type == 'uint' and self.end - self.start > 32:
290            type = 'uint64_t'
291        elif self.type == 'offset':
292            type = 'uint64_t'
293        elif self.type == 'int':
294            type = 'int32_t'
295        elif self.type == 'uint':
296            type = 'uint32_t'
297        elif self.is_struct_type():
298            type = 'struct ' + self.parser.gen_prefix(safe_name(self.type))
299        elif self.is_enum_type():
300            type = 'enum ' + self.parser.gen_prefix(safe_name(self.type))
301        elif self.type == 'mbo':
302            return
303        else:
304            print("#error unhandled type: %s" % self.type)
305            return
306
307        print("   %-36s %s%s;" % (type, self.name, dim))
308
309        prefix = ""
310        if self.values and self.default is None:
311            if self.prefix:
312                prefix = self.prefix + "_"
313
314        for value in self.values:
315            print("#define %-40s %d" % (prefix + value.name, value.value))
316
317class Group(object):
318    def __init__(self, parser, parent, start, count, size):
319        self.parser = parser
320        self.parent = parent
321        self.start = start
322        self.count = count
323        self.size = size
324        self.fields = []
325
326    def emit_template_struct(self, dim):
327        if self.count == 0:
328            print("   /* variable length fields follow */")
329        else:
330            if self.count > 1:
331                dim = "%s[%d]" % (dim, self.count)
332
333            for field in self.fields:
334                field.emit_template_struct(dim)
335
336    class DWord:
337        def __init__(self):
338            self.size = 32
339            self.fields = []
340            self.address = None
341
342    def collect_dwords(self, dwords, start, dim):
343        for field in self.fields:
344            if isinstance(field, Group):
345                if field.count == 1:
346                    field.collect_dwords(dwords, start + field.start, dim)
347                else:
348                    for i in range(field.count):
349                        field.collect_dwords(dwords,
350                                             start + field.start + i * field.size,
351                                             "%s[%d]" % (dim, i))
352                continue
353
354            index = (start + field.start) // 32
355            if not index in dwords:
356                dwords[index] = self.DWord()
357
358            clone = copy.copy(field)
359            clone.start = clone.start + start
360            clone.end = clone.end + start
361            clone.dim = dim
362            dwords[index].fields.append(clone)
363
364            if field.type == "address":
365                # assert dwords[index].address == None
366                dwords[index].address = field
367
368            # Coalesce all the dwords covered by this field. The two cases we
369            # handle are where multiple fields are in a 64 bit word (typically
370            # and address and a few bits) or where a single struct field
371            # completely covers multiple dwords.
372            while index < (start + field.end) // 32:
373                if index + 1 in dwords and not dwords[index] == dwords[index + 1]:
374                    dwords[index].fields.extend(dwords[index + 1].fields)
375                dwords[index].size = 64
376                dwords[index + 1] = dwords[index]
377                index = index + 1
378
379    def collect_dwords_and_length(self):
380        dwords = {}
381        self.collect_dwords(dwords, 0, "")
382
383        # Determine number of dwords in this group. If we have a size, use
384        # that, since that'll account for MBZ dwords at the end of a group
385        # (like dword 8 on BDW+ 3DSTATE_HS). Otherwise, use the largest dword
386        # index we've seen plus one.
387        if self.size > 0:
388            length = self.size // 32
389        elif dwords:
390            length = max(dwords.keys()) + 1
391        else:
392            length = 0
393
394        return (dwords, length)
395
396    def emit_pack_function(self, dwords, length):
397        for index in range(length):
398            # Handle MBZ dwords
399            if not index in dwords:
400                print("")
401                print("   dw[%d] = 0;" % index)
402                continue
403
404            # For 64 bit dwords, we aliased the two dword entries in the dword
405            # dict it occupies. Now that we're emitting the pack function,
406            # skip the duplicate entries.
407            dw = dwords[index]
408            if index > 0 and index - 1 in dwords and dw == dwords[index - 1]:
409                continue
410
411            # Special case: only one field and it's a struct at the beginning
412            # of the dword. In this case we pack directly into the
413            # destination. This is the only way we handle embedded structs
414            # larger than 32 bits.
415            if len(dw.fields) == 1:
416                field = dw.fields[0]
417                name = field.name + field.dim
418                if field.is_struct_type() and field.start % 32 == 0:
419                    print("")
420                    print("   %s_pack(data, &dw[%d], &values->%s);" %
421                          (self.parser.gen_prefix(safe_name(field.type)), index, name))
422                    continue
423
424            # Pack any fields of struct type first so we have integer values
425            # to the dword for those fields.
426            field_index = 0
427            for field in dw.fields:
428                if isinstance(field, Field) and field.is_struct_type():
429                    name = field.name + field.dim
430                    print("")
431                    print("   uint32_t v%d_%d;" % (index, field_index))
432                    print("   %s_pack(data, &v%d_%d, &values->%s);" %
433                          (self.parser.gen_prefix(safe_name(field.type)), index, field_index, name))
434                    field_index = field_index + 1
435
436            print("")
437            dword_start = index * 32
438            if dw.address == None:
439                address_count = 0
440            else:
441                address_count = 1
442
443            if dw.size == 32 and dw.address == None:
444                v = None
445                print("   dw[%d] =" % index)
446            elif len(dw.fields) > address_count:
447                v = "v%d" % index
448                print("   const uint%d_t %s =" % (dw.size, v))
449            else:
450                v = "0"
451
452            field_index = 0
453            non_address_fields = []
454            for field in dw.fields:
455                if field.type != "mbo":
456                    name = field.name + field.dim
457
458                if field.type == "mbo":
459                    non_address_fields.append("__gen_mbo(%d, %d)" % \
460                        (field.start - dword_start, field.end - dword_start))
461                elif field.type == "address":
462                    pass
463                elif field.type == "uint":
464                    non_address_fields.append("__gen_uint(values->%s, %d, %d)" % \
465                        (name, field.start - dword_start, field.end - dword_start))
466                elif field.is_enum_type():
467                    non_address_fields.append("__gen_uint(values->%s, %d, %d)" % \
468                        (name, field.start - dword_start, field.end - dword_start))
469                elif field.type == "int":
470                    non_address_fields.append("__gen_sint(values->%s, %d, %d)" % \
471                        (name, field.start - dword_start, field.end - dword_start))
472                elif field.type == "bool":
473                    non_address_fields.append("__gen_uint(values->%s, %d, %d)" % \
474                        (name, field.start - dword_start, field.end - dword_start))
475                elif field.type == "float":
476                    non_address_fields.append("__gen_float(values->%s)" % name)
477                elif field.type == "offset":
478                    non_address_fields.append("__gen_offset(values->%s, %d, %d)" % \
479                        (name, field.start - dword_start, field.end - dword_start))
480                elif field.type == 'ufixed':
481                    non_address_fields.append("__gen_ufixed(values->%s, %d, %d, %d)" % \
482                        (name, field.start - dword_start, field.end - dword_start, field.fractional_size))
483                elif field.type == 'sfixed':
484                    non_address_fields.append("__gen_sfixed(values->%s, %d, %d, %d)" % \
485                        (name, field.start - dword_start, field.end - dword_start, field.fractional_size))
486                elif field.is_struct_type():
487                    non_address_fields.append("__gen_uint(v%d_%d, %d, %d)" % \
488                        (index, field_index, field.start - dword_start, field.end - dword_start))
489                    field_index = field_index + 1
490                else:
491                    non_address_fields.append("/* unhandled field %s, type %s */\n" % \
492                                              (name, field.type))
493
494            if non_address_fields:
495                print(" |\n".join("      " + f for f in non_address_fields) + ";")
496
497            if dw.size == 32:
498                if dw.address:
499                    print("   dw[%d] = __gen_combine_address(data, &dw[%d], values->%s, %s);" % (index, index, dw.address.name + field.dim, v))
500                continue
501
502            if dw.address:
503                v_address = "v%d_address" % index
504                print("   const uint64_t %s =\n      __gen_combine_address(data, &dw[%d], values->%s, %s);" %
505                      (v_address, index, dw.address.name + field.dim, v))
506                if len(dw.fields) > address_count:
507                    print("   dw[%d] = %s;" % (index, v_address))
508                    print("   dw[%d] = (%s >> 32) | (%s >> 32);" % (index + 1, v_address, v))
509                    continue
510                else:
511                    v = v_address
512            print("   dw[%d] = %s;" % (index, v))
513            print("   dw[%d] = %s >> 32;" % (index + 1, v))
514
515class Value(object):
516    def __init__(self, attrs):
517        self.name = safe_name(attrs["name"])
518        self.value = ast.literal_eval(attrs["value"])
519
520class Parser(object):
521    def __init__(self):
522        self.parser = xml.parsers.expat.ParserCreate()
523        self.parser.StartElementHandler = self.start_element
524        self.parser.EndElementHandler = self.end_element
525
526        self.instruction = None
527        self.structs = {}
528        # Set of enum names we've seen.
529        self.enums = set()
530        self.registers = {}
531
532    def gen_prefix(self, name):
533        if name[0] == "_":
534            return 'GEN%s%s' % (self.gen, name)
535        return 'GEN%s_%s' % (self.gen, name)
536
537    def gen_guard(self):
538        return self.gen_prefix("PACK_H")
539
540    def start_element(self, name, attrs):
541        if name == "genxml":
542            self.platform = attrs["name"]
543            self.gen = attrs["gen"].replace('.', '')
544            print(pack_header % {'license': license, 'platform': self.platform, 'guard': self.gen_guard()})
545        elif name in ("instruction", "struct", "register"):
546            if name == "instruction":
547                self.instruction = safe_name(attrs["name"])
548                self.length_bias = int(attrs["bias"])
549                if "engine" in attrs:
550                    self.instruction_engines = set(attrs["engine"].split('|'))
551                else:
552                    # When an instruction doesn't have the engine specified,
553                    # it is considered to be for all engines, so 'None' is used
554                    # to signify that the instruction belongs to all engines.
555                    self.instruction_engines = None
556            elif name == "struct":
557                self.struct = safe_name(attrs["name"])
558                self.structs[attrs["name"]] = 1
559            elif name == "register":
560                self.register = safe_name(attrs["name"])
561                self.reg_num = num_from_str(attrs["num"])
562                self.registers[attrs["name"]] = 1
563            if "length" in attrs:
564                self.length = int(attrs["length"])
565                size = self.length * 32
566            else:
567                self.length = None
568                size = 0
569            self.group = Group(self, None, 0, 1, size)
570
571        elif name == "group":
572            group = Group(self, self.group,
573                          int(attrs["start"]), int(attrs["count"]), int(attrs["size"]))
574            self.group.fields.append(group)
575            self.group = group
576        elif name == "field":
577            self.group.fields.append(Field(self, attrs))
578            self.values = []
579        elif name == "enum":
580            self.values = []
581            self.enum = safe_name(attrs["name"])
582            self.enums.add(attrs["name"])
583            if "prefix" in attrs:
584                self.prefix = safe_name(attrs["prefix"])
585            else:
586                self.prefix= None
587        elif name == "value":
588            self.values.append(Value(attrs))
589
590    def end_element(self, name):
591        if name  == "instruction":
592            self.emit_instruction()
593            self.instruction = None
594            self.group = None
595        elif name == "struct":
596            self.emit_struct()
597            self.struct = None
598            self.group = None
599        elif name == "register":
600            self.emit_register()
601            self.register = None
602            self.reg_num = None
603            self.group = None
604        elif name == "group":
605            self.group = self.group.parent
606        elif name  == "field":
607            self.group.fields[-1].values = self.values
608        elif name  == "enum":
609            self.emit_enum()
610            self.enum = None
611        elif name == "genxml":
612            print('#endif /* %s */' % self.gen_guard())
613
614    def emit_template_struct(self, name, group):
615        print("struct %s {" % self.gen_prefix(name))
616        group.emit_template_struct("")
617        print("};\n")
618
619    def emit_pack_function(self, name, group):
620        name = self.gen_prefix(name)
621        print(textwrap.dedent("""\
622            static inline void
623            %s_pack(__attribute__((unused)) __gen_user_data *data,
624                  %s__attribute__((unused)) void * restrict dst,
625                  %s__attribute__((unused)) const struct %s * restrict values)
626            {""") % (name, ' ' * len(name), ' ' * len(name), name))
627
628        (dwords, length) = group.collect_dwords_and_length()
629        if length:
630            # Cast dst to make header C++ friendly
631            print("   uint32_t * restrict dw = (uint32_t * restrict) dst;")
632
633            group.emit_pack_function(dwords, length)
634
635        print("}\n")
636
637    def emit_instruction(self):
638        name = self.instruction
639        if self.instruction_engines and not self.instruction_engines & self.engines:
640            return
641
642        if not self.length is None:
643            print('#define %-33s %6d' %
644                  (self.gen_prefix(name + "_length"), self.length))
645        print('#define %-33s %6d' %
646              (self.gen_prefix(name + "_length_bias"), self.length_bias))
647
648        default_fields = []
649        for field in self.group.fields:
650            if not isinstance(field, Field):
651                continue
652            if field.default is None:
653                continue
654            default_fields.append("   .%-35s = %6d" % (field.name, field.default))
655
656        if default_fields:
657            print('#define %-40s\\' % (self.gen_prefix(name + '_header')))
658            print(",  \\\n".join(default_fields))
659            print('')
660
661        self.emit_template_struct(self.instruction, self.group)
662
663        self.emit_pack_function(self.instruction, self.group)
664
665    def emit_register(self):
666        name = self.register
667        if not self.reg_num is None:
668            print('#define %-33s 0x%04x' %
669                  (self.gen_prefix(name + "_num"), self.reg_num))
670
671        if not self.length is None:
672            print('#define %-33s %6d' %
673                  (self.gen_prefix(name + "_length"), self.length))
674
675        self.emit_template_struct(self.register, self.group)
676        self.emit_pack_function(self.register, self.group)
677
678    def emit_struct(self):
679        name = self.struct
680        if not self.length is None:
681            print('#define %-33s %6d' %
682                  (self.gen_prefix(name + "_length"), self.length))
683
684        self.emit_template_struct(self.struct, self.group)
685        self.emit_pack_function(self.struct, self.group)
686
687    def emit_enum(self):
688        print('enum %s {' % self.gen_prefix(self.enum))
689        for value in self.values:
690            if self.prefix:
691                name = self.prefix + "_" + value.name
692            else:
693                name = value.name
694            print('   %-36s = %6d,' % (name.upper(), value.value))
695        print('};\n')
696
697    def parse(self, filename):
698        file = open(filename, "rb")
699        self.parser.ParseFile(file)
700        file.close()
701
702def parse_args():
703    p = argparse.ArgumentParser()
704    p.add_argument('xml_source', metavar='XML_SOURCE',
705                   help="Input xml file")
706    p.add_argument('--engines', nargs='?', type=str, default='render',
707                   help="Comma-separated list of engines whose instructions should be parsed (default: %(default)s)")
708
709    pargs = p.parse_args()
710
711    if pargs.engines is None:
712        print("No engines specified")
713        sys.exit(1)
714
715    return pargs
716
717def main():
718    pargs = parse_args()
719
720    input_file = pargs.xml_source
721    engines = pargs.engines.split(',')
722    valid_engines = [ 'render', 'blitter', 'video' ]
723    if set(engines) - set(valid_engines):
724        print("Invalid engine specified, valid engines are:\n")
725        for e in valid_engines:
726            print("\t%s" % e)
727        sys.exit(1)
728
729    p = Parser()
730    p.engines = set(engines)
731    p.parse(input_file)
732
733if __name__ == '__main__':
734    main()
735