From f8cc397573bbd7bbcb488b9f107bdfa963ac7a4e Mon Sep 17 00:00:00 2001 From: David Neto Date: Fri, 16 Dec 2016 15:32:56 -0500 Subject: [PATCH] Generate spvasm.vim Generate a vim syntax file for SPIR-V assembly. Copy the resulting spvasm.vim into your $HOME/.vim/syntax directory to get syntax highlighting in Vim. Also, suggest that the grammar file include information about what opcodes can be used in OpSpecConstantOp. --- CHANGES | 2 + README.md | 10 ++ source/CMakeLists.txt | 20 ++++ utils/generate_vim_syntax.py | 192 +++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+) create mode 100755 utils/generate_vim_syntax.py diff --git a/CHANGES b/CHANGES index e9f2c8eed..352db8faf 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Revision history for SPIRV-Tools v2016.7-dev 2016-12-13 + - Add build target spirv-tools-vimsyntax to generate spvasm.vim, a SPIR-V + assembly syntax file for Vim. v2016.6 2016-12-13 - Published the C++ interface for assembling, disassembling, validation, and diff --git a/README.md b/README.md index 9ec348e3b..6fc589dfe 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,13 @@ Currently supported optimizations: For the latest list with detailed documentation, please refer to [`include/spirv-tools/optimizer.hpp`](include/spirv-tools/optimizer.hpp). +### Extras + +* [Utility filters](#utility-filters) +* Build target `spirv-tools-vimsyntax` generates file `spvasm.vim`. + Copy that file into your `$HOME/.vim/syntax` directory to get SPIR-V assembly syntax + highlighting in Vim. This build target is not built by default. + ## Source code The SPIR-V Tools are maintained by members of the The Khronos Group Inc., @@ -332,6 +339,9 @@ tests. ## Future Work +_See the [projects pages](https://github.com/KhronosGroup/SPIRV-Tools/projects) +for more information._ + ### Assembler and disassembler * The disassembler could emit helpful annotations in comments. For example: diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 3a6b9a240..67e712e29 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -13,6 +13,7 @@ # limitations under the License. set(GRAMMAR_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_grammar_tables.py") +set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_syntax.py") set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py") # macro() definitions are used in the following because we need to append .inc @@ -34,6 +35,22 @@ macro(spvtools_core_tables VERSION) list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE}) endmacro(spvtools_core_tables) +macro(spvtools_vimsyntax VERSION CLVERSION) + set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${VERSION}/spirv.core.grammar.json") + set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${VERSION}/extinst.glsl.std.450.grammar.json") + set(OPENCL_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst-1.0.opencl.std.grammar.json") + set(VIMSYNTAX_FILE "${spirv-tools_BINARY_DIR}/spvasm.vim") + add_custom_command(OUTPUT ${VIMSYNTAX_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${VIMSYNTAX_PROCESSING_SCRIPT} + --spirv-core-grammar=${GRAMMAR_JSON_FILE} + --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} + --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE} + >${VIMSYNTAX_FILE} + DEPENDS ${VIMSYNTAX_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} + ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} + COMMENT "Generate spvasm.vim: Vim syntax file for SPIR-V assembly.") +endmacro(spvtools_vimsyntax) + macro(spvtools_glsl_tables VERSION) set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${VERSION}/spirv.core.grammar.json") set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${VERSION}/extinst.glsl.std.450.grammar.json") @@ -67,6 +84,9 @@ spvtools_core_tables("1.1") spvtools_opencl_tables("1.0") spvtools_glsl_tables("1.0") +spvtools_vimsyntax("1.1" "1.0") +add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) + # Extract the list of known generators from the SPIR-V XML registry file. set(GENERATOR_INC_FILE ${spirv-tools_BINARY_DIR}/generators.inc) diff --git a/utils/generate_vim_syntax.py b/utils/generate_vim_syntax.py new file mode 100755 index 000000000..837afa62a --- /dev/null +++ b/utils/generate_vim_syntax.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# Copyright (c) 2016 Google 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. +"""Generates Vim syntax rules for SPIR-V assembly (.spvasm) files""" + +from __future__ import print_function + +import json + +PREAMBLE="""" Vim syntax file +" Language: spvasm +" Generated by SPIRV-Tools + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn case match +""" + +POSTAMBLE=""" + +syntax keyword spvasmTodo TODO FIXME contained + +syn match spvasmIdNumber /%\d\+\>/ + +" The assembler treats the leading minus sign as part of the number token. +" This applies to integers, and to floats below. +syn match spvasmNumber /-\?\<\d\+\>/ + +" Floating point literals. +" In general, C++ requires at least digit in the mantissa, and the +" floating point is optional. This applies to both the regular decimal float +" case and the hex float case. + +" First case: digits before the optional decimal, no trailing digits. +syn match spvasmFloat /-\?\d\+\.\?\(e[+-]\d\+\)\?/ +" Second case: optional digits before decimal, trailing digits +syn match spvasmFloat /-\?\d*\.\d\+\(e[+-]\d\+\)\?/ + +" First case: hex digits before the optional decimal, no trailing hex digits. +syn match spvasmFloat /-\?0[xX]\\x\+\.\?p[-+]\d\+/ +" Second case: optional hex digits before decimal, trailing hex digits +syn match spvasmFloat /-\?0[xX]\\x*\.\\x\+p[-+]\d\+/ + +syn match spvasmComment /;.*$/ contains=spvasmTodo +syn region spvasmString start=/"/ skip=/\\\\"/ end=/"/ +syn match spvasmId /%[a-zA-Z_][a-zA-Z_0-9]*/ + +" Highlight unknown constants and statements as errors +syn match spvasmError /[a-zA-Z][a-zA-Z_0-9]*/ + + +if version >= 508 || !exists("did_c_syn_inits") + if version < 508 + let did_c_syn_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + HiLink spvasmStatement Statement + HiLink spvasmNumber Number + HiLink spvasmComment Comment + HiLink spvasmString String + HiLink spvasmFloat Float + HiLink spvasmConstant Constant + HiLink spvasmIdNumber Identifier + HiLink spvasmId Identifier + HiLink spvasmTodo Todo + + delcommand HiLink +endif + +let b:current_syntax = "spvasm" +""" + +# This list is taken from the description of OpSpecConstantOp in SPIR-V 1.1. +# TODO(dneto): Propose that this information be embedded in the grammar file. +SPEC_CONSTANT_OP_OPCODES = """ + OpSConvert, OpFConvert + OpSNegate, OpNot + OpIAdd, OpISub + OpIMul, OpUDiv, OpSDiv, OpUMod, OpSRem, OpSMod + OpShiftRightLogical, OpShiftRightArithmetic, OpShiftLeftLogical + OpBitwiseOr, OpBitwiseXor, OpBitwiseAnd + OpVectorShuffle, OpCompositeExtract, OpCompositeInsert + OpLogicalOr, OpLogicalAnd, OpLogicalNot, + OpLogicalEqual, OpLogicalNotEqual + OpSelect + OpIEqual, OpINotEqual + OpULessThan, OpSLessThan + OpUGreaterThan, OpSGreaterThan + OpULessThanEqual, OpSLessThanEqual + OpUGreaterThanEqual, OpSGreaterThanEqual + + OpQuantizeToF16 + + OpConvertFToS, OpConvertSToF + OpConvertFToU, OpConvertUToF + OpUConvert + OpConvertPtrToU, OpConvertUToPtr + OpGenericCastToPtr, OpPtrCastToGeneric + OpBitcast + OpFNegate + OpFAdd, OpFSub + OpFMul, OpFDiv + OpFRem, OpFMod + OpAccessChain, OpInBoundsAccessChain + OpPtrAccessChain, OpInBoundsPtrAccessChain""" + + +def EmitAsStatement(name): + """Emits the given name as a statement token""" + print('syn keyword spvasmStatement', name) + + +def EmitAsEnumerant(name): + """Emits the given name as an named operand token""" + print('syn keyword spvasmConstant', name) + + +def main(): + """Parses arguments, then generates the Vim syntax rules for SPIR-V assembly + on stdout.""" + import argparse + parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') + parser.add_argument('--spirv-core-grammar', metavar='', + type=str, required=True, + help='input JSON grammar file for core SPIR-V ' + 'instructions') + parser.add_argument('--extinst-glsl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for GLSL extended ' + 'instruction set') + parser.add_argument('--extinst-opencl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for OpenGL extended ' + 'instruction set') + args = parser.parse_args() + + # Generate the syntax rules. + print(PREAMBLE) + + core = json.loads(open(args.spirv_core_grammar).read()) + print('\n" Core instructions') + for inst in core["instructions"]: + EmitAsStatement(inst['opname']) + print('\n" Core operand enums') + for operand_kind in core["operand_kinds"]: + if 'enumerants' in operand_kind: + for e in operand_kind['enumerants']: + EmitAsEnumerant(e['enumerant']) + + if args.extinst_glsl_grammar is not None: + print('\n" GLSL.std.450 extended instructions') + glsl = json.loads(open(args.extinst_glsl_grammar).read()) + # These opcodes are really enumerant operands for the OpExtInst + # instruction. + for inst in glsl["instructions"]: + EmitAsEnumerant(inst['opname']) + + if args.extinst_opencl_grammar is not None: + print('\n" OpenCL.std extended instructions') + opencl = json.loads(open(args.extinst_opencl_grammar).read()) + for inst in opencl["instructions"]: + EmitAsEnumerant(inst['opname']) + + print('\n" OpSpecConstantOp opcodes') + for word in SPEC_CONSTANT_OP_OPCODES.split(' '): + stripped = word.strip('\n,') + if stripped != "": + # Treat as an enumerant, but without the leading "Op" + EmitAsEnumerant(stripped[2:]) + print(POSTAMBLE) + + +if __name__ == '__main__': + main()