genxml: Add 3DSTATE_DEPTH_BOUNDS instruction.
[mesa.git] / src / intel / genxml / gen_pack_header.py
old mode 100755 (executable)
new mode 100644 (file)
index 75c4f26..118cc63
@@ -1,12 +1,18 @@
-#!/usr/bin/env python3
+#encoding=utf-8
 
+from __future__ import (
+    absolute_import, division, print_function, unicode_literals
+)
+import argparse
+import ast
 import xml.parsers.expat
 import re
 import sys
 import copy
+import textwrap
 
 license =  """/*
- * Copyright © 2016 Intel Corporation
+ * Copyright (C) 2016 Intel Corporation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -36,10 +42,14 @@ pack_header = """%(license)s
  * This file has been generated, do not hand edit.
  */
 
-#pragma once
+#ifndef %(guard)s
+#define %(guard)s
 
 #include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
 #include <assert.h>
+#include <math.h>
 
 #ifndef __gen_validate_value
 #define __gen_validate_value(x)
@@ -48,6 +58,12 @@ pack_header = """%(license)s
 #ifndef __gen_field_functions
 #define __gen_field_functions
 
+#ifdef NDEBUG
+#define NDEBUG_UNUSED __attribute__((unused))
+#else
+#define NDEBUG_UNUSED
+#endif
+
 union __gen_value {
    float f;
    uint32_t dw;
@@ -60,11 +76,11 @@ __gen_mbo(uint32_t start, uint32_t end)
 }
 
 static inline uint64_t
-__gen_uint(uint64_t v, uint32_t start, uint32_t end)
+__gen_uint(uint64_t v, uint32_t start, NDEBUG_UNUSED uint32_t end)
 {
    __gen_validate_value(v);
 
-#if DEBUG
+#ifndef NDEBUG
    const int width = end - start + 1;
    if (width < 64) {
       const uint64_t max = (1ull << width) - 1;
@@ -82,7 +98,7 @@ __gen_sint(int64_t v, uint32_t start, uint32_t end)
 
    __gen_validate_value(v);
 
-#if DEBUG
+#ifndef NDEBUG
    if (width < 64) {
       const int64_t max = (1ll << (width - 1)) - 1;
       const int64_t min = -(1ll << (width - 1));
@@ -96,10 +112,10 @@ __gen_sint(int64_t v, uint32_t start, uint32_t end)
 }
 
 static inline uint64_t
-__gen_offset(uint64_t v, uint32_t start, uint32_t end)
+__gen_offset(uint64_t v, NDEBUG_UNUSED uint32_t start, NDEBUG_UNUSED uint32_t end)
 {
    __gen_validate_value(v);
-#if DEBUG
+#ifndef NDEBUG
    uint64_t mask = (~0ull >> (64 - (end - start + 1))) << start;
 
    assert((v & ~mask) == 0);
@@ -122,32 +138,32 @@ __gen_sfixed(float v, uint32_t start, uint32_t end, uint32_t fract_bits)
 
    const float factor = (1 << fract_bits);
 
-#if DEBUG
+#ifndef NDEBUG
    const float max = ((1 << (end - start)) - 1) / factor;
    const float min = -(1 << (end - start)) / factor;
    assert(min <= v && v <= max);
 #endif
 
-   const int32_t int_val = roundf(v * factor);
+   const int64_t int_val = llroundf(v * factor);
    const uint64_t mask = ~0ull >> (64 - (end - start + 1));
 
    return (int_val & mask) << start;
 }
 
 static inline uint64_t
-__gen_ufixed(float v, uint32_t start, uint32_t end, uint32_t fract_bits)
+__gen_ufixed(float v, uint32_t start, NDEBUG_UNUSED uint32_t end, uint32_t fract_bits)
 {
    __gen_validate_value(v);
 
    const float factor = (1 << fract_bits);
 
-#if DEBUG
+#ifndef NDEBUG
    const float max = ((1 << (end - start + 1)) - 1) / factor;
    const float min = 0.0f;
    assert(min <= v && v <= max);
 #endif
 
-   const uint32_t uint_val = roundf(v * factor);
+   const uint64_t uint_val = llroundf(v * factor);
 
    return uint_val << start;
 }
@@ -160,6 +176,8 @@ __gen_ufixed(float v, uint32_t start, uint32_t end, uint32_t fract_bits)
 #error #define __gen_combine_address before including this file
 #endif
 
+#undef NDEBUG_UNUSED
+
 #endif
 
 """
@@ -179,7 +197,6 @@ def to_alphanum(name):
         '=': '',
         '>': '',
         '#': '',
-        'α': 'alpha',
         '&': '',
         '*': '',
         '"': '',
@@ -194,14 +211,21 @@ def to_alphanum(name):
 
 def safe_name(name):
     name = to_alphanum(name)
-    if not str.isalpha(name[0]):
+    if not name[0].isalpha():
         name = '_' + name
 
     return name
 
-class Field:
-    ufixed_pattern = re.compile("u(\d+)\.(\d+)")
-    sfixed_pattern = re.compile("s(\d+)\.(\d+)")
+def num_from_str(num_str):
+    if num_str.lower().startswith('0x'):
+        return int(num_str, base=16)
+
+    assert not num_str.startswith('0'), 'octals numbers not allowed'
+    return int(num_str)
+
+class Field(object):
+    ufixed_pattern = re.compile(r"u(\d+)\.(\d+)")
+    sfixed_pattern = re.compile(r"s(\d+)\.(\d+)")
 
     def __init__(self, parser, attrs):
         self.parser = parser
@@ -211,13 +235,21 @@ class Field:
         self.end = int(attrs["end"])
         self.type = attrs["type"]
 
+        assert self.start <= self.end, \
+               'field {} has end ({}) < start ({})'.format(self.name, self.end,
+                                                           self.start)
+        if self.type == 'bool':
+            assert self.end == self.start, \
+                   'bool field ({}) is too wide'.format(self.name)
+
         if "prefix" in attrs:
             self.prefix = attrs["prefix"]
         else:
             self.prefix = None
 
         if "default" in attrs:
-            self.default = int(attrs["default"])
+            # Base 0 recognizes 0x, 0o, 0b prefixes in addition to decimal ints.
+            self.default = int(attrs["default"], base=0)
         else:
             self.default = None
 
@@ -231,6 +263,17 @@ class Field:
             self.type = 'sfixed'
             self.fractional_size = int(sfixed_match.group(2))
 
+    def is_builtin_type(self):
+        builtins =  [ 'address', 'bool', 'float', 'ufixed',
+                      'offset', 'sfixed', 'offset', 'int', 'uint', 'mbo' ]
+        return self.type in builtins
+
+    def is_struct_type(self):
+        return self.type in self.parser.structs
+
+    def is_enum_type(self):
+        return self.type in self.parser.enums
+
     def emit_template_struct(self, dim):
         if self.type == 'address':
             type = '__gen_address_type'
@@ -250,25 +293,27 @@ class Field:
             type = 'int32_t'
         elif self.type == 'uint':
             type = 'uint32_t'
-        elif self.type in self.parser.structs:
+        elif self.is_struct_type():
             type = 'struct ' + self.parser.gen_prefix(safe_name(self.type))
+        elif self.is_enum_type():
+            type = 'enum ' + self.parser.gen_prefix(safe_name(self.type))
         elif self.type == 'mbo':
             return
         else:
             print("#error unhandled type: %s" % self.type)
+            return
 
         print("   %-36s %s%s;" % (type, self.name, dim))
 
-        if len(self.values) > 0 and self.default == None:
+        prefix = ""
+        if self.values and self.default is None:
             if self.prefix:
                 prefix = self.prefix + "_"
-            else:
-                prefix = ""
 
         for value in self.values:
             print("#define %-40s %d" % (prefix + value.name, value.value))
 
-class Group:
+class Group(object):
     def __init__(self, parser, parent, start, count, size):
         self.parser = parser
         self.parent = parent
@@ -295,7 +340,7 @@ class Group:
 
     def collect_dwords(self, dwords, start, dim):
         for field in self.fields:
-            if type(field) is Group:
+            if isinstance(field, Group):
                 if field.count == 1:
                     field.collect_dwords(dwords, start + field.start, dim)
                 else:
@@ -330,7 +375,7 @@ class Group:
                 dwords[index + 1] = dwords[index]
                 index = index + 1
 
-    def emit_pack_function(self, start):
+    def collect_dwords_and_length(self):
         dwords = {}
         self.collect_dwords(dwords, 0, "")
 
@@ -340,9 +385,14 @@ class Group:
         # index we've seen plus one.
         if self.size > 0:
             length = self.size // 32
-        else:
+        elif dwords:
             length = max(dwords.keys()) + 1
+        else:
+            length = 0
 
+        return (dwords, length)
+
+    def emit_pack_function(self, dwords, length):
         for index in range(length):
             # Handle MBZ dwords
             if not index in dwords:
@@ -364,7 +414,7 @@ class Group:
             if len(dw.fields) == 1:
                 field = dw.fields[0]
                 name = field.name + field.dim
-                if field.type in self.parser.structs and field.start % 32 == 0:
+                if field.is_struct_type() and field.start % 32 == 0:
                     print("")
                     print("   %s_pack(data, &dw[%d], &values->%s);" %
                           (self.parser.gen_prefix(safe_name(field.type)), index, name))
@@ -374,7 +424,7 @@ class Group:
             # to the dword for those fields.
             field_index = 0
             for field in dw.fields:
-                if type(field) is Field and field.type in self.parser.structs:
+                if isinstance(field, Field) and field.is_struct_type():
                     name = field.name + field.dim
                     print("")
                     print("   uint32_t v%d_%d;" % (index, field_index))
@@ -399,69 +449,74 @@ class Group:
                 v = "0"
 
             field_index = 0
+            non_address_fields = []
             for field in dw.fields:
                 if field.type != "mbo":
                     name = field.name + field.dim
 
                 if field.type == "mbo":
-                    s = "__gen_mbo(%d, %d)" % \
-                        (field.start - dword_start, field.end - dword_start)
+                    non_address_fields.append("__gen_mbo(%d, %d)" % \
+                        (field.start - dword_start, field.end - dword_start))
                 elif field.type == "address":
-                    s = None
+                    pass
                 elif field.type == "uint":
-                    s = "__gen_uint(values->%s, %d, %d)" % \
-                        (name, field.start - dword_start, field.end - dword_start)
+                    non_address_fields.append("__gen_uint(values->%s, %d, %d)" % \
+                        (name, field.start - dword_start, field.end - dword_start))
+                elif field.is_enum_type():
+                    non_address_fields.append("__gen_uint(values->%s, %d, %d)" % \
+                        (name, field.start - dword_start, field.end - dword_start))
                 elif field.type == "int":
-                    s = "__gen_sint(values->%s, %d, %d)" % \
-                        (name, field.start - dword_start, field.end - dword_start)
+                    non_address_fields.append("__gen_sint(values->%s, %d, %d)" % \
+                        (name, field.start - dword_start, field.end - dword_start))
                 elif field.type == "bool":
-                    s = "__gen_uint(values->%s, %d, %d)" % \
-                        (name, field.start - dword_start, field.end - dword_start)
+                    non_address_fields.append("__gen_uint(values->%s, %d, %d)" % \
+                        (name, field.start - dword_start, field.end - dword_start))
                 elif field.type == "float":
-                    s = "__gen_float(values->%s)" % name
+                    non_address_fields.append("__gen_float(values->%s)" % name)
                 elif field.type == "offset":
-                    s = "__gen_offset(values->%s, %d, %d)" % \
-                        (name, field.start - dword_start, field.end - dword_start)
+                    non_address_fields.append("__gen_offset(values->%s, %d, %d)" % \
+                        (name, field.start - dword_start, field.end - dword_start))
                 elif field.type == 'ufixed':
-                    s = "__gen_ufixed(values->%s, %d, %d, %d)" % \
-                        (name, field.start - dword_start, field.end - dword_start, field.fractional_size)
+                    non_address_fields.append("__gen_ufixed(values->%s, %d, %d, %d)" % \
+                        (name, field.start - dword_start, field.end - dword_start, field.fractional_size))
                 elif field.type == 'sfixed':
-                    s = "__gen_sfixed(values->%s, %d, %d, %d)" % \
-                        (name, field.start - dword_start, field.end - dword_start, field.fractional_size)
-                elif field.type in self.parser.structs:
-                    s = "__gen_uint(v%d_%d, %d, %d)" % \
-                        (index, field_index, field.start - dword_start, field.end - dword_start)
+                    non_address_fields.append("__gen_sfixed(values->%s, %d, %d, %d)" % \
+                        (name, field.start - dword_start, field.end - dword_start, field.fractional_size))
+                elif field.is_struct_type():
+                    non_address_fields.append("__gen_uint(v%d_%d, %d, %d)" % \
+                        (index, field_index, field.start - dword_start, field.end - dword_start))
                     field_index = field_index + 1
                 else:
-                    print("/* unhandled field %s, type %s */\n" % (name, field.type))
-                    s = None
+                    non_address_fields.append("/* unhandled field %s, type %s */\n" % \
+                                              (name, field.type))
 
-                if not s == None:
-                    if field == dw.fields[-1]:
-                        print("      %s;" % s)
-                    else:
-                        print("      %s |" % s)
+            if non_address_fields:
+                print(" |\n".join("      " + f for f in non_address_fields) + ";")
 
             if dw.size == 32:
                 if dw.address:
-                    print("   dw[%d] = __gen_combine_address(data, &dw[%d], values->%s, %s);" % (index, index, dw.address.name, v))
+                    print("   dw[%d] = __gen_combine_address(data, &dw[%d], values->%s, %s);" % (index, index, dw.address.name + field.dim, v))
                 continue
 
             if dw.address:
                 v_address = "v%d_address" % index
                 print("   const uint64_t %s =\n      __gen_combine_address(data, &dw[%d], values->%s, %s);" %
-                      (v_address, index, dw.address.name, v))
-                v = v_address
-
+                      (v_address, index, dw.address.name + field.dim, v))
+                if len(dw.fields) > address_count:
+                    print("   dw[%d] = %s;" % (index, v_address))
+                    print("   dw[%d] = (%s >> 32) | (%s >> 32);" % (index + 1, v_address, v))
+                    continue
+                else:
+                    v = v_address
             print("   dw[%d] = %s;" % (index, v))
             print("   dw[%d] = %s >> 32;" % (index + 1, v))
 
-class Value:
+class Value(object):
     def __init__(self, attrs):
         self.name = safe_name(attrs["name"])
-        self.value = int(attrs["value"])
+        self.value = ast.literal_eval(attrs["value"])
 
-class Parser:
+class Parser(object):
     def __init__(self):
         self.parser = xml.parsers.expat.ParserCreate()
         self.parser.StartElementHandler = self.start_element
@@ -469,25 +524,41 @@ class Parser:
 
         self.instruction = None
         self.structs = {}
+        # Set of enum names we've seen.
+        self.enums = set()
+        self.registers = {}
+
+    def gen_prefix(self, name):
+        if name[0] == "_":
+            return 'GEN%s%s' % (self.gen, name)
+        return 'GEN%s_%s' % (self.gen, name)
+
+    def gen_guard(self):
+        return self.gen_prefix("PACK_H")
 
     def start_element(self, name, attrs):
         if name == "genxml":
             self.platform = attrs["name"]
             self.gen = attrs["gen"].replace('.', '')
-            print(pack_header % {'license': license, 'platform': self.platform})
-        elif name == "instruction":
-            self.instruction = safe_name(attrs["name"])
-            self.length_bias = int(attrs["bias"])
-            if "length" in attrs:
-                self.length = int(attrs["length"])
-                size = self.length * 32
-            else:
-                self.length = None
-                size = 0
-            self.group = Group(self, None, 0, 1, size)
-        elif name == "struct":
-            self.struct = safe_name(attrs["name"])
-            self.structs[attrs["name"]] = 1
+            print(pack_header % {'license': license, 'platform': self.platform, 'guard': self.gen_guard()})
+        elif name in ("instruction", "struct", "register"):
+            if name == "instruction":
+                self.instruction = safe_name(attrs["name"])
+                self.length_bias = int(attrs["bias"])
+                if "engine" in attrs:
+                    self.instruction_engines = set(attrs["engine"].split('|'))
+                else:
+                    # When an instruction doesn't have the engine specified,
+                    # it is considered to be for all engines, so 'None' is used
+                    # to signify that the instruction belongs to all engines.
+                    self.instruction_engines = None
+            elif name == "struct":
+                self.struct = safe_name(attrs["name"])
+                self.structs[attrs["name"]] = 1
+            elif name == "register":
+                self.register = safe_name(attrs["name"])
+                self.reg_num = num_from_str(attrs["num"])
+                self.registers[attrs["name"]] = 1
             if "length" in attrs:
                 self.length = int(attrs["length"])
                 size = self.length * 32
@@ -507,6 +578,7 @@ class Parser:
         elif name == "enum":
             self.values = []
             self.enum = safe_name(attrs["name"])
+            self.enums.add(attrs["name"])
             if "prefix" in attrs:
                 self.prefix = safe_name(attrs["prefix"])
             else:
@@ -519,10 +591,15 @@ class Parser:
             self.emit_instruction()
             self.instruction = None
             self.group = None
-        elif name  == "struct":
+        elif name == "struct":
             self.emit_struct()
             self.struct = None
             self.group = None
+        elif name == "register":
+            self.emit_register()
+            self.register = None
+            self.reg_num = None
+            self.group = None
         elif name == "group":
             self.group = self.group.parent
         elif name  == "field":
@@ -530,12 +607,8 @@ class Parser:
         elif name  == "enum":
             self.emit_enum()
             self.enum = None
-
-    def gen_prefix(self, name):
-        if name[0] == "_":
-            return 'GEN%s%s' % (self.gen, name)
-        else:
-            return 'GEN%s_%s' % (self.gen, name)
+        elif name == "genxml":
+            print('#endif /* %s */' % self.gen_guard())
 
     def emit_template_struct(self, name, group):
         print("struct %s {" % self.gen_prefix(name))
@@ -544,31 +617,40 @@ class Parser:
 
     def emit_pack_function(self, name, group):
         name = self.gen_prefix(name)
-        print("static inline void\n%s_pack(__gen_user_data *data, void * restrict dst,\n%sconst struct %s * restrict values)\n{" %
-              (name, ' ' * (len(name) + 6), name))
+        print(textwrap.dedent("""\
+            static inline void
+            %s_pack(__attribute__((unused)) __gen_user_data *data,
+                  %s__attribute__((unused)) void * restrict dst,
+                  %s__attribute__((unused)) const struct %s * restrict values)
+            {""") % (name, ' ' * len(name), ' ' * len(name), name))
 
-        # Cast dst to make header C++ friendly
-        print("   uint32_t * restrict dw = (uint32_t * restrict) dst;")
+        (dwords, length) = group.collect_dwords_and_length()
+        if length:
+            # Cast dst to make header C++ friendly
+            print("   uint32_t * restrict dw = (uint32_t * restrict) dst;")
 
-        group.emit_pack_function(0)
+            group.emit_pack_function(dwords, length)
 
         print("}\n")
 
     def emit_instruction(self):
         name = self.instruction
-        if not self.length == None:
-            print('#define %-33s %4d' %
+        if self.instruction_engines and not self.instruction_engines & self.engines:
+            return
+
+        if not self.length is None:
+            print('#define %-33s %6d' %
                   (self.gen_prefix(name + "_length"), self.length))
-        print('#define %-33s %4d' %
+        print('#define %-33s %6d' %
               (self.gen_prefix(name + "_length_bias"), self.length_bias))
 
         default_fields = []
         for field in self.group.fields:
-            if not type(field) is Field:
+            if not isinstance(field, Field):
                 continue
-            if field.default == None:
+            if field.default is None:
                 continue
-            default_fields.append("   .%-35s = %4d" % (field.name, field.default))
+            default_fields.append("   .%-35s = %6d" % (field.name, field.default))
 
         if default_fields:
             print('#define %-40s\\' % (self.gen_prefix(name + '_header')))
@@ -579,35 +661,73 @@ class Parser:
 
         self.emit_pack_function(self.instruction, self.group)
 
+    def emit_register(self):
+        name = self.register
+        if not self.reg_num is None:
+            print('#define %-33s 0x%04x' %
+                  (self.gen_prefix(name + "_num"), self.reg_num))
+
+        if not self.length is None:
+            print('#define %-33s %6d' %
+                  (self.gen_prefix(name + "_length"), self.length))
+
+        self.emit_template_struct(self.register, self.group)
+        self.emit_pack_function(self.register, self.group)
+
     def emit_struct(self):
         name = self.struct
-        if not self.length == None:
-            print('#define %-33s %4d' %
+        if not self.length is None:
+            print('#define %-33s %6d' %
                   (self.gen_prefix(name + "_length"), self.length))
 
         self.emit_template_struct(self.struct, self.group)
         self.emit_pack_function(self.struct, self.group)
 
     def emit_enum(self):
-        print('/* enum %s */' % self.gen_prefix(self.enum))
+        print('enum %s {' % self.gen_prefix(self.enum))
         for value in self.values:
             if self.prefix:
                 name = self.prefix + "_" + value.name
             else:
                 name = value.name
-            print('#define %-36s %4d' % (name.upper(), value.value))
-        print('')
+            print('   %-36s = %6d,' % (name.upper(), value.value))
+        print('};\n')
 
     def parse(self, filename):
         file = open(filename, "rb")
         self.parser.ParseFile(file)
         file.close()
 
-if len(sys.argv) < 2:
-    print("No input xml file specified")
-    sys.exit(1)
+def parse_args():
+    p = argparse.ArgumentParser()
+    p.add_argument('xml_source', metavar='XML_SOURCE',
+                   help="Input xml file")
+    p.add_argument('--engines', nargs='?', type=str, default='render',
+                   help="Comma-separated list of engines whose instructions should be parsed (default: %(default)s)")
+
+    pargs = p.parse_args()
+
+    if pargs.engines is None:
+        print("No engines specified")
+        sys.exit(1)
+
+    return pargs
+
+def main():
+    pargs = parse_args()
+
+    input_file = pargs.xml_source
+    engines = pargs.engines.split(',')
+    valid_engines = [ 'render', 'blitter', 'video' ]
+    if set(engines) - set(valid_engines):
+        print("Invalid engine specified, valid engines are:\n")
+        for e in valid_engines:
+            print("\t%s" % e)
+        sys.exit(1)
 
-input_file = sys.argv[1]
+    p = Parser()
+    p.engines = set(engines)
+    p.parse(input_file)
 
-p = Parser()
-p.parse(input_file)
+if __name__ == '__main__':
+    main()