@@ -9,6 +9,7 @@ mod table;
99mod utils;
1010
1111use std:: {
12+ collections:: BTreeMap ,
1213 env, fs,
1314 io:: Write ,
1415 path:: { Path , PathBuf } ,
@@ -128,78 +129,134 @@ fn main() {
128129 original
129130 } ;
130131
131- // (import_name, file_key, op, url)
132- // import_name: canonical OpExtInstImport string (case-sensitive, per SPIRV-Tools)
133- // file_key: grammar filename stem (lowercase), may differ from import_name
134- let extended_instruction_sets = [
135- ( "GLSL.std.450" , "glsl.std.450" , "GLOp" , "https://registry.khronos.org/SPIR-V/specs/unified1/GLSL.std.450.html" ) ,
136- ( "OpenCL.std" , "opencl.std.100" , "CLOp" , "https://registry.khronos.org/SPIR-V/specs/unified1/OpenCL.ExtendedInstructionSet.100.html" ) ,
137- ( "NonSemantic.DebugPrintF" , "nonsemantic.debugprintf" , "DebugPrintFOp" , "https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.DebugPrintf.html" ) ,
138- ] ;
139- let extended_instruction_sets = extended_instruction_sets. map ( |( ext, file_key, op, url) | {
140- let grammar: structs:: ExtInstSetGrammar = serde_json:: from_str (
141- & std:: fs:: read_to_string ( autogen_src_dir. join ( format ! (
142- "external/SPIRV-Headers/include/spirv/unified1/extinst.{file_key}.grammar.json" ,
143- ) ) )
144- . unwrap ( ) ,
145- )
146- . unwrap ( ) ;
147- ( ext, file_key, op, url, grammar)
148- } ) ;
132+ // Automatically discover all extended instruction sets from SPIRV-Headers
133+ let extinst_dir = autogen_src_dir. join ( "external/SPIRV-Headers/include/spirv/unified1" ) ;
134+ let extended_instruction_sets: BTreeMap < String , structs:: ExtInstSetGrammar > =
135+ fs:: read_dir ( & extinst_dir)
136+ . unwrap ( )
137+ . filter_map ( |entry| {
138+ let entry = entry. unwrap ( ) ;
139+ let filename = entry. file_name ( ) . to_string_lossy ( ) . into_owned ( ) ;
140+ let key = filename
141+ . strip_prefix ( "extinst." )
142+ . and_then ( |s| s. strip_suffix ( ".grammar.json" ) ) ?;
143+ let grammar: structs:: ExtInstSetGrammar =
144+ serde_json:: from_str ( & fs:: read_to_string ( entry. path ( ) ) . unwrap ( ) ) . unwrap ( ) ;
145+ Some ( ( key. to_owned ( ) , grammar) )
146+ } )
147+ . collect ( ) ;
149148
150- // SPIR-V header
151- write_formatted ( & autogen_src_dir. join ( "../spirv/autogen_spirv.rs" ) , {
152- let core = header:: gen_spirv_header ( & grammar) ;
153- let extended_instruction_sets =
154- extended_instruction_sets
155- . iter ( )
156- . map ( |( ext, _file_key, op, url, grammar) | {
157- header:: gen_opcodes (
158- op,
159- grammar,
160- & format ! ( "[{}]({}) extended instruction opcode" , ext, url) ,
161- )
162- . to_string ( )
163- } ) ;
164- format ! (
165- "{}\n {}" ,
166- core,
167- extended_instruction_sets. collect:: <Vec <_>>( ) . join( "\n " )
168- )
169- } ) ;
149+ // Canonical OpExtInstImport name strings for known extended instruction sets.
150+ // Grammar filenames don't carry these, and the casing/separators are not
151+ // derivable (e.g. "OpenCL.std" vs filename "opencl.std.100"). Names are
152+ // case-sensitive and must match exactly what SPIR-V binaries contain.
153+ // Reference: https://github.com/KhronosGroup/SPIRV-Tools/blob/main/source/ext_inst.cpp
154+ let canonical_import_names: BTreeMap < & str , & str > = BTreeMap :: from ( [
155+ ( "arm.motion-engine.100" , "Arm.MotionEngine.100" ) ,
156+ ( "debuginfo" , "DebugInfo" ) ,
157+ ( "glsl.std.450" , "GLSL.std.450" ) ,
158+ ( "nonsemantic.clspvreflection" , "NonSemantic.ClspvReflection" ) ,
159+ ( "nonsemantic.debugbreak" , "NonSemantic.DebugBreak" ) ,
160+ ( "nonsemantic.debugprintf" , "NonSemantic.DebugPrintf" ) ,
161+ (
162+ "nonsemantic.shader.debuginfo.100" ,
163+ "NonSemantic.Shader.DebugInfo.100" ,
164+ ) ,
165+ ( "nonsemantic.vkspreflection" , "NonSemantic.VkspReflection" ) ,
166+ ( "opencl.debuginfo.100" , "OpenCL.DebugInfo.100" ) ,
167+ ( "opencl.std.100" , "OpenCL.std" ) ,
168+ ( "spv-amd-gcn-shader" , "SPV_AMD_gcn_shader" ) ,
169+ ( "spv-amd-shader-ballot" , "SPV_AMD_shader_ballot" ) ,
170+ (
171+ "spv-amd-shader-explicit-vertex-parameter" ,
172+ "SPV_AMD_shader_explicit_vertex_parameter" ,
173+ ) ,
174+ (
175+ "spv-amd-shader-trinary-minmax" ,
176+ "SPV_AMD_shader_trinary_minmax" ,
177+ ) ,
178+ ( "tosa.001000.1" , "TOSA.001000.1" ) ,
179+ ] ) ;
170180
171- // Derive module and variant names for extensions with operand kinds
172- let ext_info: Vec < _ > = extended_instruction_sets
173- . iter ( )
174- . map ( |( _ext, file_key, _, _, grammar) | {
175- let module_name = file_key. replace ( [ '.' , '-' ] , "_" ) ;
176- let variant_name: String = file_key
181+ // Derive module, variant, and op names for each extended instruction set
182+ struct ExtInstInfo {
183+ /// Lowercased grammar filename key, e.g. "glsl.std.450"
184+ file_key : String ,
185+ /// Canonical OpExtInstImport name, e.g. "GLSL.std.450"
186+ import_name : String ,
187+ /// Module name: dots/hyphens replaced with underscores, e.g. "glsl_std_450"
188+ module_name : String ,
189+ /// PascalCase variant name, e.g. "GlslStd450", "NonsemanticDebugprintf"
190+ variant_name : String ,
191+ /// Op enum name in the spirv crate, e.g. "GlslStd450Op"
192+ op_name : String ,
193+ /// UPPER_CASE table name prefix, e.g. "GLSL_STD_450_INSTRUCTION"
194+ table_name : String ,
195+ /// Whether this extension defines its own operand kinds
196+ has_operand_kinds : bool ,
197+ grammar : structs:: ExtInstSetGrammar ,
198+ }
199+ let extended_instruction_sets: Vec < ExtInstInfo > = extended_instruction_sets
200+ . into_iter ( )
201+ . map ( |( key, grammar) | {
202+ let import_name = canonical_import_names
203+ . get ( key. as_str ( ) )
204+ . unwrap_or_else ( || panic ! ( "unknown extended instruction set {:?}, add its canonical OpExtInstImport name to the mapping" , key) )
205+ . to_string ( ) ;
206+ let module_name = key. replace ( [ '.' , '-' ] , "_" ) ;
207+ let variant_name: String = key
177208 . split ( [ '.' , '-' ] )
178209 . flat_map ( |part| {
179210 let mut chars = part. chars ( ) ;
180- // Uppercase the first character
181211 chars. next ( ) . unwrap ( ) . to_uppercase ( ) . chain ( chars)
182212 } )
183213 . collect ( ) ;
214+ let op_name = format ! ( "{variant_name}Op" ) ;
215+ let table_name = format ! ( "{}_INSTRUCTION" , module_name. to_uppercase( ) ) ;
184216 let has_operand_kinds = !grammar. operand_kinds . is_empty ( ) ;
185- ( module_name, variant_name, has_operand_kinds)
217+ ExtInstInfo {
218+ file_key : key,
219+ import_name,
220+ module_name,
221+ variant_name,
222+ op_name,
223+ table_name,
224+ has_operand_kinds,
225+ grammar,
226+ }
186227 } )
187228 . collect ( ) ;
188229
230+ // SPIR-V header
231+ write_formatted ( & autogen_src_dir. join ( "../spirv/autogen_spirv.rs" ) , {
232+ let core = header:: gen_spirv_header ( & grammar) ;
233+ let ext_opcodes = extended_instruction_sets
234+ . iter ( )
235+ . map ( |ext| {
236+ header:: gen_opcodes (
237+ & ext. op_name ,
238+ & ext. grammar ,
239+ & format ! (
240+ "[{}](https://github.com/KhronosGroup/SPIRV-Headers/blob/main/include/spirv/unified1/extinst.{}.grammar.json) extended instruction opcode" ,
241+ ext. import_name, ext. file_key,
242+ ) ,
243+ )
244+ . to_string ( )
245+ } ) ;
246+ format ! ( "{}\n {}" , core, ext_opcodes. collect:: <Vec <_>>( ) . join( "\n " ) )
247+ } ) ;
248+
189249 // Wrapper variants for core OperandKind: only extensions with operand kinds
190- let ext_wrapper_variants: Vec < ( & str , & str ) > = ext_info
250+ let ext_wrapper_variants: Vec < ( & str , & str ) > = extended_instruction_sets
191251 . iter ( )
192- . filter ( |( _ , _ , has ) | * has )
193- . map ( |( module , variant , _ ) | ( variant . as_str ( ) , module . as_str ( ) ) )
252+ . filter ( |ext| ext . has_operand_kinds )
253+ . map ( |ext | ( ext . variant_name . as_str ( ) , ext . module_name . as_str ( ) ) )
194254 . collect ( ) ;
195255
196- // Collect ExtInstOp variant info: (variant_name, op_name) for all extended sets
256+ // All extension variants for ExtInstOp enum
197257 let ext_inst_variants: Vec < ( & str , & str ) > = extended_instruction_sets
198258 . iter ( )
199- . map ( |( _, _, op, _, _) | {
200- let variant = op. strip_suffix ( "Op" ) . unwrap_or ( op) ;
201- ( variant, * op)
202- } )
259+ . map ( |ext| ( ext. variant_name . as_str ( ) , ext. op_name . as_str ( ) ) )
203260 . collect ( ) ;
204261
205262 // Instruction table
@@ -219,30 +276,28 @@ fn main() {
219276 ) ,
220277 ) ;
221278 // Extended instruction sets
222- for (
223- ( ext, _file_key, spirv_op, _, ext_grammar) ,
224- ( module_name, variant_name, _has_operand_kinds) ,
225- ) in extended_instruction_sets. iter ( ) . zip ( & ext_info)
226- {
227- let autogen_file = format ! ( "autogen_{module_name}.rs" ) ;
228- let table_name = format ! ( "{}_INSTRUCTION" , module_name. to_uppercase( ) ) ;
229- let ext_variant_name = spirv_op. strip_suffix ( "Op" ) . unwrap_or ( spirv_op) ;
279+ for ext in & extended_instruction_sets {
280+ let autogen_file = format ! ( "autogen_{}.rs" , ext. module_name) ;
230281 write_formatted (
231282 & autogen_src_dir. join ( format ! ( "../rspirv/grammar/{autogen_file}" ) ) ,
232283 table:: gen_ext_instruction_file (
233- & ext_grammar . operand_kinds ,
234- & ext_grammar . instructions ,
235- spirv_op ,
236- ext_variant_name ,
237- & table_name,
238- variant_name,
284+ & ext . grammar . operand_kinds ,
285+ & ext . grammar . instructions ,
286+ & ext . op_name ,
287+ & ext . variant_name ,
288+ & ext . table_name ,
289+ & ext . variant_name ,
239290 ) ,
240291 ) ;
241292 tables. push_str ( & format ! (
242- "pub mod {module_name} {{ use super::*; include!(\" {autogen_file}\" ); }}\n \
243- pub use {module_name}::{table_name}_TABLE;\n "
293+ "pub mod {} {{ use super::*; include!(\" {autogen_file}\" ); }}\n \
294+ pub use {}::{}_TABLE;\n ",
295+ ext. module_name, ext. module_name, ext. table_name,
296+ ) ) ;
297+ table_lookup. push_str ( & format ! (
298+ "\" {}\" => &{}_TABLE,\n " ,
299+ ext. import_name, ext. table_name,
244300 ) ) ;
245- table_lookup. push_str ( & format ! ( "\" {ext}\" => &{table_name}_TABLE,\n " ) ) ;
246301 }
247302 write_formatted (
248303 & autogen_src_dir. join ( "../rspirv/grammar/autogen_tables.rs" ) ,
0 commit comments