@@ -25,6 +25,11 @@ enum IdDef {
2525
2626 Func ( Func ) ,
2727
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+
2833 SpvExtInstImport ( InternedStr ) ,
2934 SpvDebugString ( InternedStr ) ,
3035}
@@ -37,7 +42,7 @@ impl IdDef {
3742 IdDef :: Type ( _) => "a type" . into ( ) ,
3843 IdDef :: Const ( _) => "a constant" . into ( ) ,
3944
40- IdDef :: Func ( _) => "a function" . into ( ) ,
45+ IdDef :: Func ( _) | IdDef :: FuncForwardRef ( _ ) => "a function" . into ( ) ,
4146
4247 IdDef :: SpvExtInstImport ( name) => {
4348 format ! ( "`OpExtInstImport {:?}`" , & cx[ name] )
@@ -114,6 +119,37 @@ impl Module {
114119 // HACK(eddyb) used to quickly check whether an `OpVariable` is global.
115120 let storage_class_function_imm = spv:: Imm :: Short ( wk. StorageClass , wk. Function ) ;
116121
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+
117153 let mut module = {
118154 let [ magic, version, generator_magic, id_bound, reserved_inst_schema] = parser. header ;
119155
@@ -583,6 +619,38 @@ impl Module {
583619 } ) ;
584620 id_defs. insert ( id, IdDef :: Type ( ty) ) ;
585621
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+
586654 Seq :: TypeConstOrGlobalVar
587655 } else if inst_category == spec:: InstructionCategory :: Const || opcode == wk. OpUndef {
588656 let id = inst. result_id . unwrap ( ) ;
@@ -755,19 +823,40 @@ impl Module {
755823 } )
756824 }
757825 } ;
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+ } ;
758834
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+ } ;
771860
772861 current_func_body = Some ( FuncBody { func_id, func, insts : vec ! [ ] } ) ;
773862
@@ -1171,7 +1260,7 @@ impl Module {
11711260 "unsupported use of {} outside `OpExtInst`" ,
11721261 id_def. descr( & cx) ,
11731262 ) ) ) ,
1174- None => local_id_defs
1263+ None | Some ( IdDef :: FuncForwardRef ( _ ) ) => local_id_defs
11751264 . get ( & id)
11761265 . copied ( )
11771266 . ok_or_else ( || invalid ( & format ! ( "undefined ID %{id}" , ) ) ) ,
0 commit comments