@@ -25,6 +25,11 @@ enum IdDef {
25
25
26
26
Func ( Func ) ,
27
27
28
+ // HACK(eddyb) despite `FuncBody` deferring ID resolution to allow forward
29
+ // references *between* functions, function pointer *constants* need a `Func`
30
+ // long before any `OpFunction`s, so they're pre-defined as dummy imports.
31
+ FuncForwardRef ( Func ) ,
32
+
28
33
SpvExtInstImport ( InternedStr ) ,
29
34
SpvDebugString ( InternedStr ) ,
30
35
}
@@ -37,7 +42,7 @@ impl IdDef {
37
42
IdDef :: Type ( _) => "a type" . into ( ) ,
38
43
IdDef :: Const ( _) => "a constant" . into ( ) ,
39
44
40
- IdDef :: Func ( _) => "a function" . into ( ) ,
45
+ IdDef :: Func ( _) | IdDef :: FuncForwardRef ( _ ) => "a function" . into ( ) ,
41
46
42
47
IdDef :: SpvExtInstImport ( name) => {
43
48
format ! ( "`OpExtInstImport {:?}`" , & cx[ name] )
@@ -114,6 +119,37 @@ impl Module {
114
119
// HACK(eddyb) used to quickly check whether an `OpVariable` is global.
115
120
let storage_class_function_imm = spv:: Imm :: Short ( wk. StorageClass , wk. Function ) ;
116
121
122
+ // HACK(eddyb) used as the `FuncDecl` for an `IdDef::FuncForwardRef`.
123
+ let dummy_decl_for_func_forward_ref = FuncDecl {
124
+ attrs : {
125
+ let mut attrs = AttrSet :: default ( ) ;
126
+ attrs. push_diag (
127
+ & cx,
128
+ Diag :: err ( [ "function ID used as forward reference but never defined" . into ( ) ] ) ,
129
+ ) ;
130
+ attrs
131
+ } ,
132
+ // FIXME(eddyb) this gets simpler w/ disaggregation.
133
+ ret_type : cx. intern ( TypeKind :: SpvInst {
134
+ spv_inst : wk. OpTypeVoid . into ( ) ,
135
+ type_and_const_inputs : [ ] . into_iter ( ) . collect ( ) ,
136
+ } ) ,
137
+ params : [ ] . into_iter ( ) . collect ( ) ,
138
+ def : DeclDef :: Imported ( Import :: LinkName ( cx. intern ( "" ) ) ) ,
139
+ } ;
140
+ // HACK(eddyb) no `PartialEq` on `FuncDecl`.
141
+ let assert_is_dummy_decl_for_func_forward_ref = |decl : & FuncDecl | {
142
+ let [ expected, found] = [ & dummy_decl_for_func_forward_ref, decl] . map (
143
+ |FuncDecl { attrs, ret_type, params, def } | {
144
+ let DeclDef :: Imported ( import) = def else {
145
+ unreachable ! ( ) ;
146
+ } ;
147
+ ( attrs, ret_type, params, import)
148
+ } ,
149
+ ) ;
150
+ assert ! ( expected == found) ;
151
+ } ;
152
+
117
153
let mut module = {
118
154
let [ magic, version, generator_magic, id_bound, reserved_inst_schema] = parser. header ;
119
155
@@ -583,6 +619,38 @@ impl Module {
583
619
} ) ;
584
620
id_defs. insert ( id, IdDef :: Type ( ty) ) ;
585
621
622
+ Seq :: TypeConstOrGlobalVar
623
+ } else if opcode == wk. OpConstantFunctionPointerINTEL {
624
+ use std:: collections:: hash_map:: Entry ;
625
+
626
+ let id = inst. result_id . unwrap ( ) ;
627
+
628
+ let func_id = inst. ids [ 0 ] ;
629
+ let func = match id_defs. entry ( func_id) {
630
+ Entry :: Occupied ( entry) => match entry. get ( ) {
631
+ & IdDef :: FuncForwardRef ( func) => Ok ( func) ,
632
+ id_def => Err ( id_def. descr ( & cx) ) ,
633
+ } ,
634
+ Entry :: Vacant ( entry) => {
635
+ let func =
636
+ module. funcs . define ( & cx, dummy_decl_for_func_forward_ref. clone ( ) ) ;
637
+ entry. insert ( IdDef :: FuncForwardRef ( func) ) ;
638
+ Ok ( func)
639
+ }
640
+ }
641
+ . map_err ( |descr| {
642
+ invalid ( & format ! (
643
+ "unsupported use of {descr} as the `OpConstantFunctionPointerINTEL` operand"
644
+ ) )
645
+ } ) ?;
646
+
647
+ let ct = cx. intern ( ConstDef {
648
+ attrs : mem:: take ( & mut attrs) ,
649
+ ty : result_type. unwrap ( ) ,
650
+ kind : ConstKind :: PtrToFunc ( func) ,
651
+ } ) ;
652
+ id_defs. insert ( id, IdDef :: Const ( ct) ) ;
653
+
586
654
Seq :: TypeConstOrGlobalVar
587
655
} else if inst_category == spec:: InstructionCategory :: Const || opcode == wk. OpUndef {
588
656
let id = inst. result_id . unwrap ( ) ;
@@ -755,19 +823,40 @@ impl Module {
755
823
} )
756
824
}
757
825
} ;
826
+ let decl = FuncDecl {
827
+ attrs : mem:: take ( & mut attrs) ,
828
+ ret_type : func_ret_type,
829
+ params : func_type_param_types
830
+ . map ( |ty| FuncParam { attrs : AttrSet :: default ( ) , ty } )
831
+ . collect ( ) ,
832
+ def,
833
+ } ;
758
834
759
- let func = module. funcs . define (
760
- & cx,
761
- FuncDecl {
762
- attrs : mem:: take ( & mut attrs) ,
763
- ret_type : func_ret_type,
764
- params : func_type_param_types
765
- . map ( |ty| FuncParam { attrs : AttrSet :: default ( ) , ty } )
766
- . collect ( ) ,
767
- def,
768
- } ,
769
- ) ;
770
- id_defs. insert ( func_id, IdDef :: Func ( func) ) ;
835
+ let func = {
836
+ use std:: collections:: hash_map:: Entry ;
837
+
838
+ match id_defs. entry ( func_id) {
839
+ Entry :: Occupied ( mut entry) => match entry. get ( ) {
840
+ & IdDef :: FuncForwardRef ( func) => {
841
+ let decl_slot = & mut module. funcs [ func] ;
842
+ assert_is_dummy_decl_for_func_forward_ref ( decl_slot) ;
843
+ * decl_slot = decl;
844
+
845
+ entry. insert ( IdDef :: Func ( func) ) ;
846
+ Ok ( func)
847
+ }
848
+ id_def => Err ( id_def. descr ( & cx) ) ,
849
+ } ,
850
+ Entry :: Vacant ( entry) => {
851
+ let func = module. funcs . define ( & cx, decl) ;
852
+ entry. insert ( IdDef :: Func ( func) ) ;
853
+ Ok ( func)
854
+ }
855
+ }
856
+ . map_err ( |descr| {
857
+ invalid ( & format ! ( "invalid redefinition of {descr} as a new function" ) )
858
+ } ) ?
859
+ } ;
771
860
772
861
current_func_body = Some ( FuncBody { func_id, func, insts : vec ! [ ] } ) ;
773
862
@@ -1171,7 +1260,7 @@ impl Module {
1171
1260
"unsupported use of {} outside `OpExtInst`" ,
1172
1261
id_def. descr( & cx) ,
1173
1262
) ) ) ,
1174
- None => local_id_defs
1263
+ None | Some ( IdDef :: FuncForwardRef ( _ ) ) => local_id_defs
1175
1264
. get ( & id)
1176
1265
. copied ( )
1177
1266
. ok_or_else ( || invalid ( & format ! ( "undefined ID %{id}" , ) ) ) ,
0 commit comments