@@ -1371,16 +1371,27 @@ impl<'a> AssemblyTranslator<'a> {
13711371 return ;
13721372 } ;
13731373
1374- let type_id = self . module_builder . resolve_type_id ( result_type) ;
1375- match self . builder . function_parameter ( type_id) {
1376- Ok ( parameter_id) => {
1377- self . module_builder . bind_result_id ( result_id, parameter_id) ;
1378- self . module_builder
1379- . note_numeric_result_type ( parameter_id, type_id) ;
1380- self . record_function_param ( ) ;
1381- }
1382- Err ( error) => self . emit_builder_error ( error, opcode_pos) ,
1383- }
1374+ let Some ( selected_function) = self . builder . selected_function ( ) else {
1375+ self . emit_builder_error ( BuildError :: DetachedFunctionParameter , opcode_pos) ;
1376+ return ;
1377+ } ;
1378+
1379+ // Use bind_typed_result to allocate IDs from the module_builder's counter,
1380+ // rather than Builder::function_parameter() which uses Builder's separate
1381+ // next_id counter and can produce duplicate IDs.
1382+ let ( type_id, result_id) = self
1383+ . module_builder
1384+ . bind_typed_result ( result_type, result_id) ;
1385+ let inst = dr:: Instruction :: new (
1386+ spirv:: Op :: FunctionParameter ,
1387+ Some ( type_id) ,
1388+ Some ( result_id) ,
1389+ vec ! [ ] ,
1390+ ) ;
1391+ self . builder . module_mut ( ) . functions [ selected_function]
1392+ . parameters
1393+ . push ( inst) ;
1394+ self . record_function_param ( ) ;
13841395 }
13851396
13861397 fn translate_label ( & mut self , instruction : & ParsedInstruction < ' a > ) {
@@ -6245,4 +6256,95 @@ OpFunctionEnd"#;
62456256 & dr:: Operand :: LiteralBit32 ( 42.0_f32 . to_bits( ) )
62466257 ) ;
62476258 }
6259+
6260+ #[ test]
6261+ fn function_parameter_gets_unique_id_from_function ( ) {
6262+ // Regression test: OpFunctionParameter must get a different result ID
6263+ // than its parent OpFunction. Previously, the assembler used Builder's
6264+ // internal next_id counter (via function_parameter()) which was
6265+ // independent of the module_builder's counter, causing collisions.
6266+ let source = [
6267+ "%void = OpTypeVoid" ,
6268+ "%uint = OpTypeInt 32 0" ,
6269+ "%fn_type = OpTypeFunction %void %uint" ,
6270+ "OpMemoryModel Logical GLSL450" ,
6271+ "%func = OpFunction %void None %fn_type" ,
6272+ "%param = OpFunctionParameter %uint" ,
6273+ "%entry = OpLabel" ,
6274+ "OpReturn" ,
6275+ "OpFunctionEnd" ,
6276+ ] ;
6277+ let parsed: Vec < _ > = source
6278+ . into_iter ( )
6279+ . map ( |line| {
6280+ parse_instruction ( line)
6281+ . unwrap_or_else ( |err| panic ! ( "failed to parse '{line}': {err:?}" ) )
6282+ } )
6283+ . collect ( ) ;
6284+ let refs: Vec < _ > = parsed. iter ( ) . collect ( ) ;
6285+ let module = assemble_instructions ( & refs) . expect ( "assemble instructions" ) ;
6286+ let function = module. functions . first ( ) . expect ( "function" ) ;
6287+ let func_id = function. def . as_ref ( ) . unwrap ( ) . result_id . unwrap ( ) ;
6288+ let param = function. parameters . first ( ) . expect ( "parameter" ) ;
6289+ let param_id = param. result_id . unwrap ( ) ;
6290+ assert_ne ! (
6291+ func_id, param_id,
6292+ "OpFunction and OpFunctionParameter must have different result IDs, \
6293+ but both got {func_id}"
6294+ ) ;
6295+ }
6296+
6297+ #[ test]
6298+ fn multiple_functions_with_parameters_have_unique_ids ( ) {
6299+ // Ensure that across multiple functions, all result IDs are unique.
6300+ let source = [
6301+ "%void = OpTypeVoid" ,
6302+ "%uint = OpTypeInt 32 0" ,
6303+ "%fn_type = OpTypeFunction %void %uint" ,
6304+ "OpMemoryModel Logical GLSL450" ,
6305+ "%func1 = OpFunction %void None %fn_type" ,
6306+ "%param1 = OpFunctionParameter %uint" ,
6307+ "%entry1 = OpLabel" ,
6308+ "OpReturn" ,
6309+ "OpFunctionEnd" ,
6310+ "%func2 = OpFunction %void None %fn_type" ,
6311+ "%param2 = OpFunctionParameter %uint" ,
6312+ "%entry2 = OpLabel" ,
6313+ "OpReturn" ,
6314+ "OpFunctionEnd" ,
6315+ ] ;
6316+ let parsed: Vec < _ > = source
6317+ . into_iter ( )
6318+ . map ( |line| {
6319+ parse_instruction ( line)
6320+ . unwrap_or_else ( |err| panic ! ( "failed to parse '{line}': {err:?}" ) )
6321+ } )
6322+ . collect ( ) ;
6323+ let refs: Vec < _ > = parsed. iter ( ) . collect ( ) ;
6324+ let module = assemble_instructions ( & refs) . expect ( "assemble instructions" ) ;
6325+ let mut all_ids = std:: collections:: HashSet :: new ( ) ;
6326+ for function in & module. functions {
6327+ let func_id = function. def . as_ref ( ) . unwrap ( ) . result_id . unwrap ( ) ;
6328+ assert ! (
6329+ all_ids. insert( func_id) ,
6330+ "Duplicate function ID: {func_id}"
6331+ ) ;
6332+ for param in & function. parameters {
6333+ let param_id = param. result_id . unwrap ( ) ;
6334+ assert ! (
6335+ all_ids. insert( param_id) ,
6336+ "Duplicate parameter ID: {param_id}"
6337+ ) ;
6338+ }
6339+ for block in & function. blocks {
6340+ if let Some ( label) = & block. label {
6341+ let label_id = label. result_id . unwrap ( ) ;
6342+ assert ! (
6343+ all_ids. insert( label_id) ,
6344+ "Duplicate label ID: {label_id}"
6345+ ) ;
6346+ }
6347+ }
6348+ }
6349+ }
62486350}
0 commit comments