1#!/usr/bin/env python3 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 aliases = inst.get('aliases', []) 165 for alias in aliases: 166 EmitAsStatement(alias) 167 print('\n" Core operand enums') 168 for operand_kind in core["operand_kinds"]: 169 if 'enumerants' in operand_kind: 170 for e in operand_kind['enumerants']: 171 EmitAsEnumerant(e['enumerant']) 172 aliases = e.get('aliases', []) 173 for a in aliases: 174 EmitAsEnumerant(a) 175 176 if args.extinst_glsl_grammar is not None: 177 print('\n" GLSL.std.450 extended instructions') 178 glsl = json.loads(open(args.extinst_glsl_grammar).read()) 179 # These opcodes are really enumerant operands for the OpExtInst 180 # instruction. 181 for inst in glsl["instructions"]: 182 EmitAsEnumerant(inst['opname']) 183 184 if args.extinst_opencl_grammar is not None: 185 print('\n" OpenCL.std extended instructions') 186 opencl = json.loads(open(args.extinst_opencl_grammar).read()) 187 for inst in opencl["instructions"]: 188 EmitAsEnumerant(inst['opname']) 189 190 if args.extinst_debuginfo_grammar is not None: 191 print('\n" DebugInfo extended instructions') 192 debuginfo = json.loads(open(args.extinst_debuginfo_grammar).read()) 193 for inst in debuginfo["instructions"]: 194 EmitAsEnumerant(inst['opname']) 195 print('\n" DebugInfo operand enums') 196 for operand_kind in debuginfo["operand_kinds"]: 197 if 'enumerants' in operand_kind: 198 for e in operand_kind['enumerants']: 199 EmitAsEnumerant(e['enumerant']) 200 201 print('\n" OpSpecConstantOp opcodes') 202 for word in SPEC_CONSTANT_OP_OPCODES.split(' '): 203 stripped = word.strip('\n,') 204 if stripped != "": 205 # Treat as an enumerant, but without the leading "Op" 206 EmitAsEnumerant(stripped[2:]) 207 print(POSTAMBLE) 208 209 210if __name__ == '__main__': 211 main() 212