blob: 20323cf6f5d5a0b58a8717e9cbacee37ea2b16a3 [file] [log] [blame]
#!/usr/bin/python3 -i
#
# Copyright (c) 2021 The Khronos Group Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Spencer Fricke <s.fricke@samsung.com>
import os,re,sys,string,json
import xml.etree.ElementTree as etree
from generator import *
from collections import namedtuple
from common_codegen import *
# This is a workaround to use a Python 2.7 and 3.x compatible syntax
from io import open
class SpirvGrammarHelperOutputGeneratorOptions(GeneratorOptions):
def __init__(self,
conventions = None,
filename = None,
directory = '.',
genpath = None,
apiname = 'vulkan',
profile = None,
versions = '.*',
emitversions = '.*',
defaultExtensions = 'vulkan',
addExtensions = None,
removeExtensions = None,
emitExtensions = None,
emitSpirv = None,
sortProcedure = regSortFeatures,
genFuncPointers = True,
protectFile = True,
protectFeature = True,
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
indentFuncProto = True,
indentFuncPointer = False,
alignFuncParam = 48,
expandEnumerants = False,
grammar = None):
GeneratorOptions.__init__(self,
conventions = conventions,
filename = filename,
directory = directory,
genpath = genpath,
apiname = apiname,
profile = profile,
versions = versions,
emitversions = emitversions,
defaultExtensions = defaultExtensions,
addExtensions = addExtensions,
removeExtensions = removeExtensions,
emitExtensions = emitExtensions,
emitSpirv = emitSpirv,
sortProcedure = sortProcedure)
self.genFuncPointers = genFuncPointers
self.protectFile = protectFile
self.protectFeature = protectFeature
self.apicall = apicall
self.apientry = apientry
self.apientryp = apientryp
self.indentFuncProto = indentFuncProto
self.indentFuncPointer = indentFuncPointer
self.alignFuncParam = alignFuncParam
self.expandEnumerants = expandEnumerants
self.grammar = grammar
#
# SpirvGrammarHelperOutputGenerator - Generate SPIR-V grammar helper
# for SPIR-V opcodes, enums, etc
class SpirvGrammarHelperOutputGenerator(OutputGenerator):
def __init__(self,
errFile = sys.stderr,
warnFile = sys.stderr,
diagFile = sys.stdout):
OutputGenerator.__init__(self, errFile, warnFile, diagFile)
self.headerFile = False # Header file generation flag
self.sourceFile = False # Source file generation flag
self.atomicsOps = []
self.groupOps = []
self.memoryScopeParam = [[] for i in range(5)]
self.executionScopeParam = [[] for i in range(5)]
# Lots of switch statements share same ending
self.commonBoolSwitch = ''' found = true;
break;
default:
break;
}
return found;
}
'''
#
# Called at beginning of processing as file is opened
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
self.parseGrammar(genOpts.grammar)
self.headerFile = (genOpts.filename == 'spirv_grammar_helper.h')
self.sourceFile = (genOpts.filename == 'spirv_grammar_helper.cpp')
if not self.headerFile and not self.sourceFile:
print("Error: Output Filenames have changed, update generator source.\n")
sys.exit(1)
# File Comment
file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
file_comment += '// See spirv_gramar_generator.py for modifications\n'
write(file_comment, file=self.outFile)
# Copyright Statement
copyright = ''
copyright += '\n'
copyright += '/***************************************************************************\n'
copyright += ' *\n'
copyright += ' * Copyright (c) 2021 The Khronos Group Inc.\n'
copyright += ' *\n'
copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
copyright += ' * you may not use this file except in compliance with the License.\n'
copyright += ' * You may obtain a copy of the License at\n'
copyright += ' *\n'
copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
copyright += ' *\n'
copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
copyright += ' * See the License for the specific language governing permissions and\n'
copyright += ' * limitations under the License.\n'
copyright += ' *\n'
copyright += ' * Author: Spencer Fricke <s.fricke@samsung.com>\n'
copyright += ' *\n'
copyright += ' * This file is related to anything that is found in the SPIR-V grammar\n'
copyright += ' * file found in the SPIRV-Headers. Mainly used for SPIR-V util functions.\n'
copyright += ' *\n'
copyright += ' ****************************************************************************/\n'
write(copyright, file=self.outFile)
if self.sourceFile:
write('#include "spirv_grammar_helper.h"', file=self.outFile)
write('#include <spirv/unified1/spirv.hpp>', file=self.outFile)
elif self.headerFile:
write('#pragma once', file=self.outFile)
write('#include <cstdint>', file=self.outFile)
write('', file=self.outFile)
#
# Write generated file content to output file
def endFile(self):
write(self.atomicOperation(), file=self.outFile)
write(self.groupOperation(), file=self.outFile)
write(self.scopeHelper(), file=self.outFile)
# Finish processing in superclass
OutputGenerator.endFile(self)
#
# Takes the SPIR-V Grammar JSON and parses it
# Emulates the gen*() functions the vk.xml calls
#
# In the future, IF more then this generator wants to use the grammar
# it would be better to move the file opening to lvl_genvk.py
def parseGrammar(self, grammar):
with open(grammar, 'r') as jsonFile:
data = json.load(jsonFile)
instructions = data['instructions']
operandKinds = data['operand_kinds']
# Build list from json of all capabilities that are only for kernel
# This needs to be done before loop instructions
kernelCapability = ['Kernel']
for operandKind in operandKinds:
if operandKind['kind'] == 'Capability':
for enum in operandKind['enumerants']:
if 'capabilities' in enum and len(enum['capabilities']) == 1 and enum['capabilities'][0] == 'Kernel':
kernelCapability.append(enum['enumerant'])
for instruction in instructions:
opname = instruction['opname']
if 'capabilities' in instruction and len(instruction['capabilities']) == 1 and instruction['capabilities'][0] in kernelCapability:
continue # If just 'Kernel' then op is ment for OpenCL
if instruction['class'] == 'Atomic':
self.atomicsOps.append(opname)
if instruction['class'] == 'Non-Uniform':
self.groupOps.append(opname)
if 'operands' in instruction:
for index, operand in enumerate(instruction['operands']):
# some instructions have both types of IdScope
# OpReadClockKHR has the wrong 'name' as 'Scope'
if operand['kind'] == 'IdScope':
if operand['name'] == '\'Execution\'' or operand['name'] == '\'Scope\'':
self.executionScopeParam[index + 1].append(opname)
elif operand['name'] == '\'Memory\'':
self.memoryScopeParam[index + 1].append(opname)
else:
print("Error: unknown operand {} not handled correctly\n".format(opname))
sys.exit(1)
#
# Generate functions for numeric based functions
def atomicOperation(self):
output = ''
if self.headerFile:
output += 'bool AtomicOperation(uint32_t opcode);\n'
elif self.sourceFile:
output += '// Any non supported operation will be covered with VUID 01090\n'
output += 'bool AtomicOperation(uint32_t opcode) {\n'
output += ' bool found = false;\n'
output += ' switch (opcode) {\n'
for f in self.atomicsOps:
output += ' case spv::{}:\n'.format(f)
output += self.commonBoolSwitch
return output;
#
# Generate functions for numeric based functions
def groupOperation(self):
output = ''
if self.headerFile:
output += 'bool GroupOperation(uint32_t opcode);\n'
elif self.sourceFile:
output += '// Any non supported operation will be covered with VUID 01090\n'
output += 'bool GroupOperation(uint32_t opcode) {\n'
output += ' bool found = false;\n'
output += ' switch (opcode) {\n'
for f in self.groupOps:
output += ' case spv::{}:\n'.format(f)
output += self.commonBoolSwitch
return output;
#
# Generate functions for scope id
def scopeHelper(self):
output = ''
if self.headerFile:
output += 'uint32_t MemoryScopeParam(uint32_t opcode);\n'
output += 'uint32_t ExecutionScopeParam(uint32_t opcode);\n'
elif self.sourceFile:
output += '// Return paramater position of memory scope ID or zero if there is none\n'
output += 'uint32_t MemoryScopeParam(uint32_t opcode) {\n'
output += ' uint32_t position = 0;\n'
output += ' switch (opcode) {\n'
for index, operands in enumerate(self.memoryScopeParam):
for operand in operands:
output += ' case spv::{}:\n'.format(operand)
if len(operands) != 0:
output += ' return {};\n'.format(index)
output +=''' break;
default:
break;
}
return position;
}\n
'''
output += '// Return paramater position of execution scope ID or zero if there is none\n'
output += 'uint32_t ExecutionScopeParam(uint32_t opcode) {\n'
output += ' uint32_t position = 0;\n'
output += ' switch (opcode) {\n'
for index, operands in enumerate(self.executionScopeParam):
for operand in operands:
output += ' case spv::{}:\n'.format(operand)
if len(operands) != 0:
output += ' return {};\n'.format(index)
output +=''' break;
default:
break;
}
return position;
}\n
'''
return output;