1#!/usr/bin/env python 2# Copyright (c) 2016 Google Inc. 3 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Generates Vim syntax rules for SPIR-V assembly (.spvasm) files""" 16 17import json 18 19PREAMBLE="""" Vim syntax file 20" Language: spvasm 21" Generated by SPIRV-Tools 22 23if version < 600 24 syntax clear 25elseif exists("b:current_syntax") 26 finish 27endif 28 29syn case match 30""" 31 32POSTAMBLE=""" 33 34syntax keyword spvasmTodo TODO FIXME contained 35 36syn match spvasmIdNumber /%\d\+\>/ 37 38" The assembler treats the leading minus sign as part of the number token. 39" This applies to integers, and to floats below. 40syn match spvasmNumber /-\?\<\d\+\>/ 41 42" Floating point literals. 43" In general, C++ requires at least digit in the mantissa, and the 44" floating point is optional. This applies to both the regular decimal float 45" case and the hex float case. 46 47" First case: digits before the optional decimal, no trailing digits. 48syn match spvasmFloat /-\?\d\+\.\?\(e[+-]\d\+\)\?/ 49" Second case: optional digits before decimal, trailing digits 50syn match spvasmFloat /-\?\d*\.\d\+\(e[+-]\d\+\)\?/ 51 52" First case: hex digits before the optional decimal, no trailing hex digits. 53syn match spvasmFloat /-\?0[xX]\\x\+\.\?p[-+]\d\+/ 54" Second case: optional hex digits before decimal, trailing hex digits 55syn match spvasmFloat /-\?0[xX]\\x*\.\\x\+p[-+]\d\+/ 56 57syn match spvasmComment /;.*$/ contains=spvasmTodo 58syn region spvasmString start=/"/ skip=/\\\\"/ end=/"/ 59syn match spvasmId /%[a-zA-Z_][a-zA-Z_0-9]*/ 60 61" Highlight unknown constants and statements as errors 62syn match spvasmError /[a-zA-Z][a-zA-Z_0-9]*/ 63 64 65if version >= 508 || !exists("did_c_syn_inits") 66 if version < 508 67 let did_c_syn_inits = 1 68 command -nargs=+ HiLink hi link <args> 69 else 70 command -nargs=+ HiLink hi def link <args> 71 endif 72 73 HiLink spvasmStatement Statement 74 HiLink spvasmNumber Number 75 HiLink spvasmComment Comment 76 HiLink spvasmString String 77 HiLink spvasmFloat Float 78 HiLink spvasmConstant Constant 79 HiLink spvasmIdNumber Identifier 80 HiLink spvasmId Identifier 81 HiLink spvasmTodo Todo 82 83 delcommand HiLink 84endif 85 86let b:current_syntax = "spvasm" 87""" 88 89# This list is taken from the description of OpSpecConstantOp in SPIR-V 1.1. 90# TODO(dneto): Propose that this information be embedded in the grammar file. 91SPEC_CONSTANT_OP_OPCODES = """ 92 OpSConvert, OpFConvert 93 OpSNegate, OpNot 94 OpIAdd, OpISub 95 OpIMul, OpUDiv, OpSDiv, OpUMod, OpSRem, OpSMod 96 OpShiftRightLogical, OpShiftRightArithmetic, OpShiftLeftLogical 97 OpBitwiseOr, OpBitwiseXor, OpBitwiseAnd 98 OpVectorShuffle, OpCompositeExtract, OpCompositeInsert 99 OpLogicalOr, OpLogicalAnd, OpLogicalNot, 100 OpLogicalEqual, OpLogicalNotEqual 101 OpSelect 102 OpIEqual, OpINotEqual 103 OpULessThan, OpSLessThan 104 OpUGreaterThan, OpSGreaterThan 105 OpULessThanEqual, OpSLessThanEqual 106 OpUGreaterThanEqual, OpSGreaterThanEqual 107 108 OpQuantizeToF16 109 110 OpConvertFToS, OpConvertSToF 111 OpConvertFToU, OpConvertUToF 112 OpUConvert 113 OpConvertPtrToU, OpConvertUToPtr 114 OpGenericCastToPtr, OpPtrCastToGeneric 115 OpBitcast 116 OpFNegate 117 OpFAdd, OpFSub 118 OpFMul, OpFDiv 119 OpFRem, OpFMod 120 OpAccessChain, OpInBoundsAccessChain 121 OpPtrAccessChain, OpInBoundsPtrAccessChain""" 122 123 124def EmitAsStatement(name): 125 """Emits the given name as a statement token""" 126 print('syn keyword spvasmStatement', name) 127 128 129def EmitAsEnumerant(name): 130 """Emits the given name as an named operand token""" 131 print('syn keyword spvasmConstant', name) 132 133 134def main(): 135 """Parses arguments, then generates the Vim syntax rules for SPIR-V assembly 136 on stdout.""" 137 import argparse 138 parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') 139 parser.add_argument('--spirv-core-grammar', metavar='<path>', 140 type=str, required=True, 141 help='input JSON grammar file for core SPIR-V ' 142 'instructions') 143 parser.add_argument('--extinst-glsl-grammar', metavar='<path>', 144 type=str, required=False, default=None, 145 help='input JSON grammar file for GLSL extended ' 146 'instruction set') 147 parser.add_argument('--extinst-opencl-grammar', metavar='<path>', 148 type=str, required=False, default=None, 149 help='input JSON grammar file for OpenGL extended ' 150 'instruction set') 151 parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>', 152 type=str, required=False, default=None, 153 help='input JSON grammar file for DebugInfo extended ' 154 'instruction set') 155 args = parser.parse_args() 156 157 # Generate the syntax rules. 158 print(PREAMBLE) 159 160 core = json.loads(open(args.spirv_core_grammar).read()) 161 print('\n" Core instructions') 162 for inst in core["instructions"]: 163 EmitAsStatement(inst['opname']) 164 print('\n" Core operand enums') 165 for operand_kind in core["operand_kinds"]: 166 if 'enumerants' in operand_kind: 167 for e in operand_kind['enumerants']: 168 EmitAsEnumerant(e['enumerant']) 169 170 if args.extinst_glsl_grammar is not None: 171 print('\n" GLSL.std.450 extended instructions') 172 glsl = json.loads(open(args.extinst_glsl_grammar).read()) 173 # These opcodes are really enumerant operands for the OpExtInst 174 # instruction. 175 for inst in glsl["instructions"]: 176 EmitAsEnumerant(inst['opname']) 177 178 if args.extinst_opencl_grammar is not None: 179 print('\n" OpenCL.std extended instructions') 180 opencl = json.loads(open(args.extinst_opencl_grammar).read()) 181 for inst in opencl["instructions"]: 182 EmitAsEnumerant(inst['opname']) 183 184 if args.extinst_debuginfo_grammar is not None: 185 print('\n" DebugInfo extended instructions') 186 debuginfo = json.loads(open(args.extinst_debuginfo_grammar).read()) 187 for inst in debuginfo["instructions"]: 188 EmitAsEnumerant(inst['opname']) 189 print('\n" DebugInfo operand enums') 190 for operand_kind in debuginfo["operand_kinds"]: 191 if 'enumerants' in operand_kind: 192 for e in operand_kind['enumerants']: 193 EmitAsEnumerant(e['enumerant']) 194 195 print('\n" OpSpecConstantOp opcodes') 196 for word in SPEC_CONSTANT_OP_OPCODES.split(' '): 197 stripped = word.strip('\n,') 198 if stripped != "": 199 # Treat as an enumerant, but without the leading "Op" 200 EmitAsEnumerant(stripped[2:]) 201 print(POSTAMBLE) 202 203 204if __name__ == '__main__': 205 main() 206