mirror of
https://github.com/godotengine/godot-angle-static.git
synced 2026-01-03 14:09:33 +03:00
Vulkan: autogen for SPIR-V instruction build and parse
Handwritten SPIR-V instruction parse and build code is replaced with autogenerated functions based on the SPIR-V grammar. Bug: angleproject:4889 Change-Id: I09d724fd944e79c03fe4eadca3ee3e3ef0b49872 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2644721 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Tim Van Patten <timvp@google.com>
This commit is contained in:
committed by
Commit Bot
parent
cc5083e071
commit
6689a54d8a
2
BUILD.gn
2
BUILD.gn
@@ -685,6 +685,8 @@ if (angle_enable_vulkan || angle_enable_metal) {
|
||||
]
|
||||
deps = [
|
||||
":libANGLE_headers",
|
||||
"$angle_root/src/common/spirv:angle_spirv_builder",
|
||||
"$angle_root/src/common/spirv:angle_spirv_parser",
|
||||
"${angle_glslang_dir}:glslang_default_resource_limits_sources",
|
||||
"${angle_glslang_dir}:glslang_lib_sources",
|
||||
"${angle_spirv_headers_dir}:spv_headers",
|
||||
|
||||
14
scripts/code_generation_hashes/SPIR-V_helpers.json
Normal file
14
scripts/code_generation_hashes/SPIR-V_helpers.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"src/common/spirv/gen_spirv_builder_and_parser.py":
|
||||
"02381e3a7cf898265de3ba438b3b6e5e",
|
||||
"src/common/spirv/spirv_instruction_builder_autogen.cpp":
|
||||
"b9c0c27ab2de86955528d4d8ba9be817",
|
||||
"src/common/spirv/spirv_instruction_builder_autogen.h":
|
||||
"9629f02ff30720ec38a9aad0fc5c1503",
|
||||
"src/common/spirv/spirv_instruction_parser_autogen.cpp":
|
||||
"55d28bb4b003dbecf31e162ff996fc4f",
|
||||
"src/common/spirv/spirv_instruction_parser_autogen.h":
|
||||
"68c5746bc94e6b9f891ff2a11869ccd1",
|
||||
"third_party/vulkan-deps/spirv-headers/src/include/spirv/1.0/spirv.core.grammar.json":
|
||||
"a8c4239344b2fc10bfc4ace7ddee1867"
|
||||
}
|
||||
@@ -115,6 +115,8 @@ generators = {
|
||||
'scripts/gen_proc_table.py',
|
||||
'restricted traces':
|
||||
'src/tests/restricted_traces/gen_restricted_traces.py',
|
||||
'SPIR-V helpers':
|
||||
'src/common/spirv/gen_spirv_builder_and_parser.py',
|
||||
'Static builtins':
|
||||
'src/compiler/translator/gen_builtin_symbols.py',
|
||||
'uniform type':
|
||||
|
||||
@@ -70,6 +70,9 @@ class FastVector final
|
||||
void push_back(const value_type &value);
|
||||
void push_back(value_type &&value);
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&... args);
|
||||
|
||||
void pop_back();
|
||||
|
||||
reference front();
|
||||
@@ -287,10 +290,17 @@ ANGLE_INLINE void FastVector<T, N, Storage>::push_back(const value_type &value)
|
||||
|
||||
template <class T, size_t N, class Storage>
|
||||
ANGLE_INLINE void FastVector<T, N, Storage>::push_back(value_type &&value)
|
||||
{
|
||||
emplace_back(std::move(value));
|
||||
}
|
||||
|
||||
template <class T, size_t N, class Storage>
|
||||
template <typename... Args>
|
||||
ANGLE_INLINE void FastVector<T, N, Storage>::emplace_back(Args &&... args)
|
||||
{
|
||||
if (mSize == mReservedSize)
|
||||
ensure_capacity(mSize + 1);
|
||||
mData[mSize++] = std::move(value);
|
||||
mData[mSize++] = std::move(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <class T, size_t N, class Storage>
|
||||
|
||||
29
src/common/spirv/BUILD.gn
Normal file
29
src/common/spirv/BUILD.gn
Normal file
@@ -0,0 +1,29 @@
|
||||
# Copyright 2021 The ANGLE Project Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("../../../gni/angle.gni")
|
||||
|
||||
angle_source_set("angle_spirv_builder") {
|
||||
sources = [
|
||||
"spirv_instruction_builder_autogen.cpp",
|
||||
"spirv_instruction_builder_autogen.h",
|
||||
"spirv_types.h",
|
||||
]
|
||||
deps = [
|
||||
"$angle_root:angle_common",
|
||||
"${angle_spirv_headers_dir}:spv_headers",
|
||||
]
|
||||
}
|
||||
|
||||
angle_source_set("angle_spirv_parser") {
|
||||
sources = [
|
||||
"spirv_instruction_parser_autogen.cpp",
|
||||
"spirv_instruction_parser_autogen.h",
|
||||
"spirv_types.h",
|
||||
]
|
||||
deps = [
|
||||
"$angle_root:angle_common",
|
||||
"${angle_spirv_headers_dir}:spv_headers",
|
||||
]
|
||||
}
|
||||
527
src/common/spirv/gen_spirv_builder_and_parser.py
Executable file
527
src/common/spirv/gen_spirv_builder_and_parser.py
Executable file
@@ -0,0 +1,527 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2021 The ANGLE Project Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
#
|
||||
# gen_spirv_builder_and_parser.py:
|
||||
# Code generation for SPIR-V instruction builder and parser.
|
||||
# NOTE: don't run this script directly. Run scripts/run_code_generation.py.
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
# ANGLE uses SPIR-V 1.0 currently, so there's no reason to generate code for newer instructions.
|
||||
SPIRV_GRAMMAR_FILE = '../../../third_party/vulkan-deps/spirv-headers/src/include/spirv/1.0/spirv.core.grammar.json'
|
||||
|
||||
# The script has two sets of outputs, a header and source file for SPIR-V code generation, and a
|
||||
# header and source file for SPIR-V parsing.
|
||||
SPIRV_BUILDER_FILE = 'spirv_instruction_builder'
|
||||
SPIRV_PARSER_FILE = 'spirv_instruction_parser'
|
||||
|
||||
# The types are either defined in spirv_types.h (to use strong types), or are enums that are
|
||||
# defined by SPIR-V headers.
|
||||
ANGLE_DEFINED_TYPES = [
|
||||
'IdRef', 'IdResult', 'IdResultType', 'IdMemorySemantics', 'IdScope', 'LiteralInteger',
|
||||
'LiteralString', 'LiteralContextDependentNumber', 'LiteralExtInstInteger',
|
||||
'PairLiteralIntegerIdRef', 'PairIdRefLiteralInteger', 'PairIdRefIdRef'
|
||||
]
|
||||
|
||||
HEADER_TEMPLATE = """// GENERATED FILE - DO NOT EDIT.
|
||||
// Generated by {script_name} using data from {data_source_name}.
|
||||
//
|
||||
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// {file_name}_autogen.h:
|
||||
// Functions to {verb} SPIR-V binary for each instruction.
|
||||
|
||||
#ifndef COMMON_SPIRV_{file_name_capitalized}AUTOGEN_H_
|
||||
#define COMMON_SPIRV_{file_name_capitalized}AUTOGEN_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "spirv_types.h"
|
||||
|
||||
namespace angle
|
||||
{{
|
||||
namespace spirv
|
||||
{{
|
||||
{prototype_list}
|
||||
}} // namespace spirv
|
||||
}} // namespace angle
|
||||
|
||||
#endif // COMMON_SPIRV_{file_name_capitalized}AUTOGEN_H_
|
||||
"""
|
||||
|
||||
SOURCE_TEMPLATE = """// GENERATED FILE - DO NOT EDIT.
|
||||
// Generated by {script_name} using data from {data_source_name}.
|
||||
//
|
||||
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// {file_name}_autogen.cpp:
|
||||
// Functions to {verb} SPIR-V binary for each instruction.
|
||||
|
||||
#include "{file_name}_autogen.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace angle
|
||||
{{
|
||||
namespace spirv
|
||||
{{
|
||||
{helper_functions}
|
||||
|
||||
{function_list}
|
||||
}} // namespace spirv
|
||||
}} // namespace angle
|
||||
"""
|
||||
|
||||
BUILDER_HELPER_FUNCTIONS = """namespace
|
||||
{
|
||||
uint32_t MakeLengthOp(size_t length, spv::Op op)
|
||||
{
|
||||
ASSERT(length <= 0xFFFFu);
|
||||
ASSERT(op <= 0xFFFFu);
|
||||
|
||||
return static_cast<uint32_t>(length) << 16 | op;
|
||||
}
|
||||
} // anonymous namespace"""
|
||||
|
||||
PARSER_FIXED_FUNCTIONS_PROTOTYPES = """void GetInstructionOpAndLength(const uint32_t *_instruction,
|
||||
spv::Op *opOut, uint32_t *lengthOut);
|
||||
"""
|
||||
|
||||
PARSER_FIXED_FUNCTIONS = """void GetInstructionOpAndLength(const uint32_t *_instruction,
|
||||
spv::Op *opOut, uint32_t *lengthOut)
|
||||
{
|
||||
constexpr uint32_t kOpMask = 0xFFFFu;
|
||||
*opOut = static_cast<spv::Op>(_instruction[0] & kOpMask);
|
||||
*lengthOut = _instruction[0] >> 16;
|
||||
}
|
||||
"""
|
||||
|
||||
TEMPLATE_BUILDER_FUNCTION_PROTOTYPE = """void Write{op}(std::vector<uint32_t> *blob {param_list})"""
|
||||
TEMPLATE_BUILDER_FUNCTION_BODY = """{{
|
||||
const size_t startSize = blob->size();
|
||||
blob->push_back(0);
|
||||
{push_back_lines}
|
||||
(*blob)[startSize] = MakeLengthOp(blob->size() - startSize, spv::Op{op});
|
||||
}}
|
||||
"""
|
||||
|
||||
TEMPLATE_PARSER_FUNCTION_PROTOTYPE = """void Parse{op}(const uint32_t *_instruction {param_list})"""
|
||||
TEMPLATE_PARSER_FUNCTION_BODY = """{{
|
||||
spv::Op _op;
|
||||
uint32_t _length;
|
||||
GetInstructionOpAndLength(_instruction, &_op, &_length);
|
||||
ASSERT(_op == spv::Op{op});
|
||||
uint32_t _o = 1;
|
||||
{parse_lines}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def load_grammar(grammar_file):
|
||||
with open(grammar_file) as grammar_in:
|
||||
grammar = json.loads(grammar_in.read())
|
||||
|
||||
return grammar
|
||||
|
||||
|
||||
def remove_chars(string, chars):
|
||||
return filter(lambda c: c not in chars, string)
|
||||
|
||||
|
||||
def make_camel_case(name):
|
||||
return name[0].lower() + name[1:]
|
||||
|
||||
|
||||
class Writer:
|
||||
|
||||
def __init__(self):
|
||||
self.path_prefix = os.path.dirname(os.path.realpath(__file__)) + os.path.sep
|
||||
self.grammar = load_grammar(self.path_prefix + SPIRV_GRAMMAR_FILE)
|
||||
|
||||
# If an instruction has a parameter of these types, the instruction is ignored
|
||||
self.unsupported_kinds = set(['LiteralSpecConstantOpInteger'])
|
||||
# If an instruction requires a capability of these kinds, the instruction is ignored
|
||||
self.unsupported_capabilities = set(['Kernel'])
|
||||
# If an instruction requires an extension other than these, the instruction is ignored
|
||||
self.supported_extensions = set([])
|
||||
# List of bit masks. These have 'Mask' added to their typename in SPIR-V headers.
|
||||
self.bit_mask_types = set([])
|
||||
|
||||
# List of generated instructions builder/parser functions so far.
|
||||
self.instruction_builder_prototypes = []
|
||||
self.instruction_builder_impl = []
|
||||
self.instruction_parser_prototypes = [PARSER_FIXED_FUNCTIONS_PROTOTYPES]
|
||||
self.instruction_parser_impl = [PARSER_FIXED_FUNCTIONS]
|
||||
|
||||
def write_builder_and_parser(self):
|
||||
"""Generates four files, a set of header and source files for generating SPIR-V instructions
|
||||
and a set for parsing SPIR-V instructions. Only Vulkan instructions are processed (and not
|
||||
OpenCL for example), and some assumptions are made based on ANGLE's usage (for example that
|
||||
constants always fit in one 32-bit unit, as GLES doesn't support double or 64-bit types).
|
||||
|
||||
Additionally, enums and other parameter 'kinds' are not parsed from the json file, but
|
||||
rather use the definitions from the SPIR-V headers repository and the spirv_types.h file."""
|
||||
|
||||
# Recurse through capabilities and accumulate ones that depend on unsupported ones.
|
||||
self.accumulate_unsupported_capabilities()
|
||||
|
||||
self.find_bit_mask_types()
|
||||
|
||||
for instruction in self.grammar['instructions']:
|
||||
self.generate_instruction_functions(instruction)
|
||||
|
||||
# Write out the files.
|
||||
data_source_base_name = os.path.basename(SPIRV_GRAMMAR_FILE)
|
||||
builder_template_args = {
|
||||
'script_name': sys.argv[0],
|
||||
'data_source_name': data_source_base_name,
|
||||
'file_name': SPIRV_BUILDER_FILE,
|
||||
'file_name_capitalized': remove_chars(SPIRV_BUILDER_FILE.upper(), '_'),
|
||||
'verb': 'generate',
|
||||
'helper_functions': BUILDER_HELPER_FUNCTIONS,
|
||||
'prototype_list': ''.join(self.instruction_builder_prototypes),
|
||||
'function_list': ''.join(self.instruction_builder_impl)
|
||||
}
|
||||
parser_template_args = {
|
||||
'script_name': sys.argv[0],
|
||||
'data_source_name': data_source_base_name,
|
||||
'file_name': SPIRV_PARSER_FILE,
|
||||
'file_name_capitalized': remove_chars(SPIRV_PARSER_FILE.upper(), '_'),
|
||||
'verb': 'parse',
|
||||
'helper_functions': '',
|
||||
'prototype_list': ''.join(self.instruction_parser_prototypes),
|
||||
'function_list': ''.join(self.instruction_parser_impl)
|
||||
}
|
||||
|
||||
with (open(self.path_prefix + SPIRV_BUILDER_FILE + '_autogen.h', 'w')) as f:
|
||||
f.write(HEADER_TEMPLATE.format(**builder_template_args))
|
||||
|
||||
with (open(self.path_prefix + SPIRV_BUILDER_FILE + '_autogen.cpp', 'w')) as f:
|
||||
f.write(SOURCE_TEMPLATE.format(**builder_template_args))
|
||||
|
||||
with (open(self.path_prefix + SPIRV_PARSER_FILE + '_autogen.h', 'w')) as f:
|
||||
f.write(HEADER_TEMPLATE.format(**parser_template_args))
|
||||
|
||||
with (open(self.path_prefix + SPIRV_PARSER_FILE + '_autogen.cpp', 'w')) as f:
|
||||
f.write(SOURCE_TEMPLATE.format(**parser_template_args))
|
||||
|
||||
def requires_unsupported_capability(self, item):
|
||||
depends = item.get('capabilities', [])
|
||||
return any([dep in self.unsupported_capabilities for dep in depends])
|
||||
|
||||
def requires_unsupported_extension(self, item):
|
||||
extensions = item.get('extensions', [])
|
||||
return any([ext not in self.supported_extensions for ext in extensions])
|
||||
|
||||
def accumulate_unsupported_capabilities(self):
|
||||
operand_kinds = self.grammar['operand_kinds']
|
||||
|
||||
# Find the Capability enum
|
||||
for kind in filter(lambda entry: entry['kind'] == 'Capability', operand_kinds):
|
||||
capabilities = kind['enumerants']
|
||||
for capability in capabilities:
|
||||
name = capability['enumerant']
|
||||
# For each capability, see if any of the capabilities they depend on is unsupported.
|
||||
# If so, add the capability to the list of unsupported ones.
|
||||
if self.requires_unsupported_capability(capability):
|
||||
self.unsupported_capabilities.add(name)
|
||||
continue
|
||||
# Do the same for extensions
|
||||
if self.requires_unsupported_extension(capability):
|
||||
self.unsupported_capabilities.add(name)
|
||||
|
||||
def find_bit_mask_types(self):
|
||||
operand_kinds = self.grammar['operand_kinds']
|
||||
|
||||
# Find the BitEnum categories
|
||||
for bitEnumEntry in filter(lambda entry: entry['category'] == 'BitEnum', operand_kinds):
|
||||
self.bit_mask_types.add(bitEnumEntry['kind'])
|
||||
|
||||
def get_operand_name(self, operand):
|
||||
kind = operand['kind']
|
||||
name = operand.get('name')
|
||||
|
||||
# If no name is given, derive the name from the kind
|
||||
if name is None:
|
||||
assert (kind.find(' ') == -1)
|
||||
return make_camel_case(kind)
|
||||
|
||||
quantifier = operand.get('quantifier', '')
|
||||
name = remove_chars(name, "'")
|
||||
|
||||
# First, a number of special-cases for optional lists
|
||||
if quantifier == '*':
|
||||
# For IdRefs, change 'Xyz 1', +\n'Xyz 2', +\n...' to xyzList
|
||||
if kind == 'IdRef':
|
||||
if name.find(' ') != -1:
|
||||
name = name[0:name.find(' ')]
|
||||
return make_camel_case(name) + 'List'
|
||||
|
||||
# Otherwise, it's a pair in the form of 'Xyz, Abc, ...', which is changed to
|
||||
# xyzAbcPairList
|
||||
name = remove_chars(name, " ,.")
|
||||
return make_camel_case(name) + 'PairList'
|
||||
|
||||
# Otherwise, remove invalid characters and make the first letter lower case.
|
||||
name = remove_chars(name, " .,+\n~")
|
||||
name = make_camel_case(name)
|
||||
|
||||
# Make sure the name is not a C++ keyword
|
||||
return 'default_' if name == 'default' else name
|
||||
|
||||
def get_operand_namespace(self, kind):
|
||||
return '' if kind in ANGLE_DEFINED_TYPES else 'spv::'
|
||||
|
||||
def get_operand_type_suffix(self, kind):
|
||||
return 'Mask' if kind in self.bit_mask_types else ''
|
||||
|
||||
def get_kind_cpp_type(self, kind):
|
||||
return self.get_operand_namespace(kind) + kind + self.get_operand_type_suffix(kind)
|
||||
|
||||
def get_operand_type_in_and_out(self, operand):
|
||||
kind = operand['kind']
|
||||
quantifier = operand.get('quantifier', '')
|
||||
|
||||
is_array = quantifier == '*'
|
||||
is_optional = quantifier == '?'
|
||||
cpp_type = self.get_kind_cpp_type(kind)
|
||||
|
||||
if is_array:
|
||||
type_in = 'const ' + cpp_type + 'List &'
|
||||
type_out = cpp_type + 'List *'
|
||||
elif is_optional:
|
||||
type_in = cpp_type + ' *'
|
||||
type_out = cpp_type + ' *'
|
||||
else:
|
||||
type_in = cpp_type
|
||||
type_out = cpp_type + ' *'
|
||||
|
||||
return (type_in, type_out, is_array, is_optional)
|
||||
|
||||
def get_operand_push_back_line(self, operand, operand_name, is_array, is_optional):
|
||||
kind = operand['kind']
|
||||
pre = ''
|
||||
post = ''
|
||||
accessor = '.'
|
||||
item = operand_name
|
||||
item_dereferenced = item
|
||||
if is_optional:
|
||||
# If optional, surround with an if.
|
||||
pre = 'if (' + operand_name + ') {\n'
|
||||
post = '\n}'
|
||||
accessor = '->'
|
||||
item_dereferenced = '*' + item
|
||||
elif is_array:
|
||||
# If array, surround with a loop.
|
||||
pre = 'for (const auto &operand : ' + operand_name + ') {\n'
|
||||
post = '\n}'
|
||||
item = 'operand'
|
||||
item_dereferenced = item
|
||||
|
||||
# Usually the operand is one uint32_t, but it may be pair. Handle the pairs especially.
|
||||
if kind == 'PairLiteralIntegerIdRef':
|
||||
line = 'blob->push_back(' + item + accessor + 'literal);'
|
||||
line += 'blob->push_back(' + item + accessor + 'id);'
|
||||
elif kind == 'PairIdRefLiteralInteger':
|
||||
line = 'blob->push_back(' + item + accessor + 'id);'
|
||||
line += 'blob->push_back(' + item + accessor + 'literal);'
|
||||
elif kind == 'PairIdRefIdRef':
|
||||
line = 'blob->push_back(' + item + accessor + 'id1);'
|
||||
line += 'blob->push_back(' + item + accessor + 'id2);'
|
||||
elif kind == 'LiteralString':
|
||||
line = '{size_t d = blob->size();'
|
||||
line += 'blob->resize(d + strlen(' + item_dereferenced + ') / 4 + 1, 0);'
|
||||
# We currently don't have any big-endian devices in the list of supported platforms.
|
||||
# Literal strings in SPIR-V are stored little-endian (SPIR-V 1.0 Section 2.2.1, Literal
|
||||
# String), so if a big-endian device is to be supported, the string copy here should
|
||||
# be adjusted.
|
||||
line += 'ASSERT(IsLittleEndian());'
|
||||
line += 'strcpy(reinterpret_cast<char *>(blob->data() + d), ' + item_dereferenced + ');}'
|
||||
else:
|
||||
line = 'blob->push_back(' + item_dereferenced + ');'
|
||||
|
||||
return pre + line + post
|
||||
|
||||
def get_operand_parse_line(self, operand, operand_name, is_array, is_optional):
|
||||
kind = operand['kind']
|
||||
pre = ''
|
||||
post = ''
|
||||
accessor = '->'
|
||||
|
||||
if is_optional:
|
||||
# If optional, surround with an if, both checking if argument is provided, and whether
|
||||
# it exists.
|
||||
pre = 'if (' + operand_name + ' && _o < _length) {\n'
|
||||
post = '\n}'
|
||||
elif is_array:
|
||||
# If array, surround with an if and a loop.
|
||||
pre = 'if (' + operand_name + ') {\n'
|
||||
pre += 'while (_o < _length) {\n'
|
||||
post = '\n}}'
|
||||
accessor = '.'
|
||||
|
||||
# Usually the operand is one uint32_t, but it may be pair. Handle the pairs especially.
|
||||
if kind == 'PairLiteralIntegerIdRef':
|
||||
if is_array:
|
||||
line = operand_name + '->emplace_back(' + kind + '{LiteralInteger(_instruction[_o]), IdRef(_instruction[_o + 1])});'
|
||||
line += '_o += 2;'
|
||||
else:
|
||||
line = operand_name + '->literal = LiteralInteger(_instruction[_o++]);'
|
||||
line += operand_name + '->id = IdRef(_instruction[_o++]);'
|
||||
elif kind == 'PairIdRefLiteralInteger':
|
||||
if is_array:
|
||||
line = operand_name + '->emplace_back(' + kind + '{IdRef(_instruction[_o]), LiteralInteger(_instruction[_o + 1])});'
|
||||
line += '_o += 2;'
|
||||
else:
|
||||
line = operand_name + '->id = IdRef(_instruction[_o++]);'
|
||||
line += operand_name + '->literal = LiteralInteger(_instruction[_o++]);'
|
||||
elif kind == 'PairIdRefIdRef':
|
||||
if is_array:
|
||||
line = operand_name + '->emplace_back(' + kind + '{IdRef(_instruction[_o]), IdRef(_instruction[_o + 1])});'
|
||||
line += '_o += 2;'
|
||||
else:
|
||||
line = operand_name + '->id1 = IdRef(_instruction[_o++]);'
|
||||
line += operand_name + '->id2 = IdRef(_instruction[_o++]);'
|
||||
elif kind == 'LiteralString':
|
||||
# The length of string in words is ceil((strlen(str) + 1) / 4). This is equal to
|
||||
# (strlen(str)+1+3) / 4, which is equal to strlen(str)/4+1.
|
||||
assert (not is_array)
|
||||
# See handling of LiteralString in get_operand_push_back_line.
|
||||
line = 'ASSERT(IsLittleEndian());'
|
||||
line += '*' + operand_name + ' = reinterpret_cast<const char *>(&_instruction[_o]);'
|
||||
line += '_o += strlen(*' + operand_name + ') / 4 + 1;'
|
||||
else:
|
||||
if is_array:
|
||||
line = operand_name + '->emplace_back(_instruction[_o++]);'
|
||||
else:
|
||||
line = '*' + operand_name + ' = ' + self.get_kind_cpp_type(
|
||||
kind) + '(_instruction[_o++]);'
|
||||
|
||||
return pre + line + post
|
||||
|
||||
def process_operand(self, operand, cpp_operands_in, cpp_operands_out, cpp_in_parse_lines,
|
||||
cpp_out_push_back_lines):
|
||||
operand_name = self.get_operand_name(operand)
|
||||
type_in, type_out, is_array, is_optional = self.get_operand_type_in_and_out(operand)
|
||||
|
||||
# Make the parameter list
|
||||
cpp_operands_in.append(', ' + type_in + ' ' + operand_name)
|
||||
cpp_operands_out.append(', ' + type_out + ' ' + operand_name)
|
||||
|
||||
# Make the builder body lines
|
||||
cpp_out_push_back_lines.append(
|
||||
self.get_operand_push_back_line(operand, operand_name, is_array, is_optional))
|
||||
|
||||
# Make the parser body lines
|
||||
cpp_in_parse_lines.append(
|
||||
self.get_operand_parse_line(operand, operand_name, is_array, is_optional))
|
||||
|
||||
def generate_instruction_functions(self, instruction):
|
||||
name = instruction['opname']
|
||||
assert (name.startswith('Op'))
|
||||
name = name[2:]
|
||||
|
||||
# Skip if the instruction depends on a capability or extension we aren't interested in
|
||||
if self.requires_unsupported_capability(instruction):
|
||||
return
|
||||
if self.requires_unsupported_extension(instruction):
|
||||
return
|
||||
|
||||
operands = instruction.get('operands', [])
|
||||
|
||||
# Skip if any of the operands are not supported
|
||||
if any([operand['kind'] in self.unsupported_kinds for operand in operands]):
|
||||
return
|
||||
|
||||
cpp_operands_in = []
|
||||
cpp_operands_out = []
|
||||
cpp_in_parse_lines = []
|
||||
cpp_out_push_back_lines = []
|
||||
|
||||
for operand in operands:
|
||||
self.process_operand(operand, cpp_operands_in, cpp_operands_out, cpp_in_parse_lines,
|
||||
cpp_out_push_back_lines)
|
||||
|
||||
# get_operand_parse_line relies on there only being one array parameter, and it being
|
||||
# the last.
|
||||
assert (operand.get('quantifier') != '*' or len(cpp_in_parse_lines) == len(operands))
|
||||
|
||||
if operand['kind'] == 'Decoration':
|
||||
# Special handling of Op*Decorate instructions with a Decoration operand. That
|
||||
# operand always comes last, and implies a number of LiteralIntegers to follow.
|
||||
assert (len(cpp_in_parse_lines) == len(operands))
|
||||
|
||||
decoration_operands = {
|
||||
'name': 'values',
|
||||
'kind': 'LiteralInteger',
|
||||
'quantifier': '*'
|
||||
}
|
||||
self.process_operand(decoration_operands, cpp_operands_in, cpp_operands_out,
|
||||
cpp_in_parse_lines, cpp_out_push_back_lines)
|
||||
|
||||
elif operand['kind'] == 'ImageOperands':
|
||||
# Special handling of OpImage* instructions with an ImageOperands operand. That
|
||||
# operand always comes last, and implies a number of IdRefs to follow with different
|
||||
# meanings based on the bits set in said operand.
|
||||
assert (len(cpp_in_parse_lines) == len(operands))
|
||||
|
||||
image_operands = {'name': 'imageOperandIds', 'kind': 'IdRef', 'quantifier': '*'}
|
||||
self.process_operand(image_operands, cpp_operands_in, cpp_operands_out,
|
||||
cpp_in_parse_lines, cpp_out_push_back_lines)
|
||||
|
||||
# Make the builder prototype body
|
||||
builder_prototype = TEMPLATE_BUILDER_FUNCTION_PROTOTYPE.format(
|
||||
op=name, param_list=''.join(cpp_operands_in))
|
||||
self.instruction_builder_prototypes.append(builder_prototype + ';\n')
|
||||
self.instruction_builder_impl.append(
|
||||
builder_prototype + '\n' + TEMPLATE_BUILDER_FUNCTION_BODY.format(
|
||||
op=name, push_back_lines='\n'.join(cpp_out_push_back_lines)))
|
||||
|
||||
if len(operands) == 0:
|
||||
return
|
||||
|
||||
parser_prototype = TEMPLATE_PARSER_FUNCTION_PROTOTYPE.format(
|
||||
op=name, param_list=''.join(cpp_operands_out))
|
||||
self.instruction_parser_prototypes.append(parser_prototype + ';\n')
|
||||
self.instruction_parser_impl.append(
|
||||
parser_prototype + '\n' + TEMPLATE_PARSER_FUNCTION_BODY.format(
|
||||
op=name, parse_lines='\n'.join(cpp_in_parse_lines)))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# auto_script parameters.
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] == 'inputs':
|
||||
print(SPIRV_GRAMMAR_FILE)
|
||||
elif sys.argv[1] == 'outputs':
|
||||
output_files_base = [SPIRV_BUILDER_FILE, SPIRV_PARSER_FILE]
|
||||
output_files = [
|
||||
'_autogen.'.join(pair)
|
||||
for pair in itertools.product(output_files_base, ['h', 'cpp'])
|
||||
]
|
||||
print(','.join(output_files))
|
||||
else:
|
||||
print('Invalid script parameters')
|
||||
return 1
|
||||
return 0
|
||||
|
||||
writer = Writer()
|
||||
writer.write_builder_and_parser()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
3507
src/common/spirv/spirv_instruction_builder_autogen.cpp
Normal file
3507
src/common/spirv/spirv_instruction_builder_autogen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1121
src/common/spirv/spirv_instruction_builder_autogen.h
Normal file
1121
src/common/spirv/spirv_instruction_builder_autogen.h
Normal file
File diff suppressed because it is too large
Load Diff
4036
src/common/spirv/spirv_instruction_parser_autogen.cpp
Normal file
4036
src/common/spirv/spirv_instruction_parser_autogen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1137
src/common/spirv/spirv_instruction_parser_autogen.h
Normal file
1137
src/common/spirv/spirv_instruction_parser_autogen.h
Normal file
File diff suppressed because it is too large
Load Diff
102
src/common/spirv/spirv_types.h
Normal file
102
src/common/spirv/spirv_types.h
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// spirv_types.h:
|
||||
// Strong types for SPIR-V Ids to prevent mistakes when using the builder and parser APIs.
|
||||
//
|
||||
|
||||
#ifndef COMMON_SPIRV_TYPES_H_
|
||||
#define COMMON_SPIRV_TYPES_H_
|
||||
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
|
||||
#include "common/FastVector.h"
|
||||
|
||||
namespace angle
|
||||
{
|
||||
namespace spirv
|
||||
{
|
||||
template <typename Helper>
|
||||
class BoxedUint32
|
||||
{
|
||||
public:
|
||||
BoxedUint32() : mValue{0} {}
|
||||
explicit BoxedUint32(uint32_t value) : mValue{value} {}
|
||||
template <typename T>
|
||||
T as() const
|
||||
{
|
||||
return T{mValue};
|
||||
}
|
||||
BoxedUint32(const BoxedUint32 &other) = default;
|
||||
BoxedUint32 &operator=(const BoxedUint32 &other) = default;
|
||||
operator uint32_t() const { return mValue.value; }
|
||||
bool operator==(const BoxedUint32 &other) const { return mValue.value == other.mValue.value; }
|
||||
// Applicable to ids, which cannot be 0.
|
||||
bool valid() const { return static_cast<bool>(mValue.value); }
|
||||
|
||||
private:
|
||||
Helper mValue;
|
||||
};
|
||||
|
||||
struct IdRefHelper
|
||||
{
|
||||
spv::Id value;
|
||||
};
|
||||
struct LiteralIntegerHelper
|
||||
{
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
using IdRef = BoxedUint32<IdRefHelper>;
|
||||
// IdResult, IdResultType, IdMemorySemantics and IdScope are all translated as IdRef. This makes
|
||||
// the type verification weaker, but stops the API from becoming tediously verbose.
|
||||
using IdResult = IdRef;
|
||||
using IdResultType = IdRef;
|
||||
using IdMemorySemantics = IdRef;
|
||||
using IdScope = IdRef;
|
||||
using LiteralInteger = BoxedUint32<LiteralIntegerHelper>;
|
||||
using LiteralString = const char *;
|
||||
// Note: In ANGLE's use cases, all literals fit in 32 bits.
|
||||
using LiteralContextDependentNumber = LiteralInteger;
|
||||
// TODO(syoussefi): To be made stronger when generating SPIR-V from the translator.
|
||||
// http://anglebug.com/4889
|
||||
using LiteralExtInstInteger = LiteralInteger;
|
||||
|
||||
struct PairLiteralIntegerIdRef
|
||||
{
|
||||
LiteralInteger literal;
|
||||
IdRef id;
|
||||
};
|
||||
|
||||
struct PairIdRefLiteralInteger
|
||||
{
|
||||
IdRef id;
|
||||
LiteralInteger literal;
|
||||
};
|
||||
|
||||
struct PairIdRefIdRef
|
||||
{
|
||||
IdRef id1;
|
||||
IdRef id2;
|
||||
};
|
||||
|
||||
// Some instructions need 4 components. The drivers uniform struct in ANGLE has 8 fields. A value
|
||||
// of 8 means almost no instruction would end up making dynamic allocations. Notable exceptions are
|
||||
// user-defined structs/blocks and OpEntryPoint.
|
||||
constexpr size_t kFastVectorSize = 8;
|
||||
|
||||
template <typename T>
|
||||
using FastVectorHelper = angle::FastVector<T, kFastVectorSize>;
|
||||
|
||||
using IdRefList = FastVectorHelper<IdRef>;
|
||||
using LiteralIntegerList = FastVectorHelper<LiteralInteger>;
|
||||
using PairLiteralIntegerIdRefList = FastVectorHelper<PairLiteralIntegerIdRef>;
|
||||
using PairIdRefLiteralIntegerList = FastVectorHelper<PairIdRefLiteralInteger>;
|
||||
using PairIdRefIdRefList = FastVectorHelper<PairIdRefIdRef>;
|
||||
|
||||
} // namespace spirv
|
||||
} // namespace angle
|
||||
|
||||
#endif // COMMON_SPIRV_TYPES_H_
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user