| #!/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""" |
| |
| 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 <args> |
| else |
| command -nargs=+ HiLink hi def link <args> |
| 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='<path>', |
| type=str, required=True, |
| help='input JSON grammar file for core SPIR-V ' |
| 'instructions') |
| parser.add_argument('--extinst-glsl-grammar', metavar='<path>', |
| type=str, required=False, default=None, |
| help='input JSON grammar file for GLSL extended ' |
| 'instruction set') |
| parser.add_argument('--extinst-opencl-grammar', metavar='<path>', |
| type=str, required=False, default=None, |
| help='input JSON grammar file for OpenGL extended ' |
| 'instruction set') |
| parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>', |
| type=str, required=False, default=None, |
| help='input JSON grammar file for DebugInfo 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']) |
| |
| if args.extinst_debuginfo_grammar is not None: |
| print('\n" DebugInfo extended instructions') |
| debuginfo = json.loads(open(args.extinst_debuginfo_grammar).read()) |
| for inst in debuginfo["instructions"]: |
| EmitAsEnumerant(inst['opname']) |
| print('\n" DebugInfo operand enums') |
| for operand_kind in debuginfo["operand_kinds"]: |
| if 'enumerants' in operand_kind: |
| for e in operand_kind['enumerants']: |
| EmitAsEnumerant(e['enumerant']) |
| |
| 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() |