@@ -24,6 +24,11 @@ enum IdDef {
2424
2525 Func ( Func ) ,
2626
27+ // HACK(eddyb) despite `FuncBody` deferring ID resolution to allow forward
28+ // references *between* functions, function pointer *constants* need a `Func`
29+ // long before any `OpFunction`s, so they're pre-defined as dummy imports.
30+ FuncForwardRef ( Func ) ,
31+
2732 SpvExtInstImport ( InternedStr ) ,
2833 SpvDebugString ( InternedStr ) ,
2934}
@@ -36,7 +41,7 @@ impl IdDef {
3641 IdDef :: Type ( _) => "a type" . into ( ) ,
3742 IdDef :: Const ( _) => "a constant" . into ( ) ,
3843
39- IdDef :: Func ( _) => "a function" . into ( ) ,
44+ IdDef :: Func ( _) | IdDef :: FuncForwardRef ( _ ) => "a function" . into ( ) ,
4045
4146 IdDef :: SpvExtInstImport ( name) => {
4247 format ! ( "`OpExtInstImport {:?}`" , & cx[ name] )
@@ -113,6 +118,37 @@ impl Module {
113118 // HACK(eddyb) used to quickly check whether an `OpVariable` is global.
114119 let storage_class_function_imm = spv:: Imm :: Short ( wk. StorageClass , wk. Function ) ;
115120
121+ // HACK(eddyb) used as the `FuncDecl` for an `IdDef::FuncForwardRef`.
122+ let dummy_decl_for_func_forward_ref = FuncDecl {
123+ attrs : {
124+ let mut attrs = AttrSet :: default ( ) ;
125+ attrs. push_diag (
126+ & cx,
127+ Diag :: err ( [ "function ID used as forward reference but never defined" . into ( ) ] ) ,
128+ ) ;
129+ attrs
130+ } ,
131+ // FIXME(eddyb) this gets simpler w/ disaggregation.
132+ ret_type : cx. intern ( TypeKind :: SpvInst {
133+ spv_inst : wk. OpTypeVoid . into ( ) ,
134+ type_and_const_inputs : [ ] . into_iter ( ) . collect ( ) ,
135+ } ) ,
136+ params : [ ] . into_iter ( ) . collect ( ) ,
137+ def : DeclDef :: Imported ( Import :: LinkName ( cx. intern ( "" ) ) ) ,
138+ } ;
139+ // HACK(eddyb) no `PartialEq` on `FuncDecl`.
140+ let assert_is_dummy_decl_for_func_forward_ref = |decl : & FuncDecl | {
141+ let [ expected, found] = [ & dummy_decl_for_func_forward_ref, decl] . map (
142+ |FuncDecl { attrs, ret_type, params, def } | {
143+ let DeclDef :: Imported ( import) = def else {
144+ unreachable ! ( ) ;
145+ } ;
146+ ( attrs, ret_type, params, import)
147+ } ,
148+ ) ;
149+ assert ! ( expected == found) ;
150+ } ;
151+
116152 let mut module = {
117153 let [ magic, version, generator_magic, id_bound, reserved_inst_schema] = parser. header ;
118154
@@ -582,6 +618,38 @@ impl Module {
582618 } ) ;
583619 id_defs. insert ( id, IdDef :: Type ( ty) ) ;
584620
621+ Seq :: TypeConstOrGlobalVar
622+ } else if opcode == wk. OpConstantFunctionPointerINTEL {
623+ use std:: collections:: hash_map:: Entry ;
624+
625+ let id = inst. result_id . unwrap ( ) ;
626+
627+ let func_id = inst. ids [ 0 ] ;
628+ let func = match id_defs. entry ( func_id) {
629+ Entry :: Occupied ( entry) => match entry. get ( ) {
630+ & IdDef :: FuncForwardRef ( func) => Ok ( func) ,
631+ id_def => Err ( id_def. descr ( & cx) ) ,
632+ } ,
633+ Entry :: Vacant ( entry) => {
634+ let func =
635+ module. funcs . define ( & cx, dummy_decl_for_func_forward_ref. clone ( ) ) ;
636+ entry. insert ( IdDef :: FuncForwardRef ( func) ) ;
637+ Ok ( func)
638+ }
639+ }
640+ . map_err ( |descr| {
641+ invalid ( & format ! (
642+ "unsupported use of {descr} as the `OpConstantFunctionPointerINTEL` operand"
643+ ) )
644+ } ) ?;
645+
646+ let ct = cx. intern ( ConstDef {
647+ attrs : mem:: take ( & mut attrs) ,
648+ ty : result_type. unwrap ( ) ,
649+ kind : ConstKind :: PtrToFunc ( func) ,
650+ } ) ;
651+ id_defs. insert ( id, IdDef :: Const ( ct) ) ;
652+
585653 Seq :: TypeConstOrGlobalVar
586654 } else if inst_category == spec:: InstructionCategory :: Const || opcode == wk. OpUndef {
587655 let id = inst. result_id . unwrap ( ) ;
@@ -754,19 +822,40 @@ impl Module {
754822 } )
755823 }
756824 } ;
825+ let decl = FuncDecl {
826+ attrs : mem:: take ( & mut attrs) ,
827+ ret_type : func_ret_type,
828+ params : func_type_param_types
829+ . map ( |ty| FuncParam { attrs : AttrSet :: default ( ) , ty } )
830+ . collect ( ) ,
831+ def,
832+ } ;
757833
758- let func = module. funcs . define (
759- & cx,
760- FuncDecl {
761- attrs : mem:: take ( & mut attrs) ,
762- ret_type : func_ret_type,
763- params : func_type_param_types
764- . map ( |ty| FuncParam { attrs : AttrSet :: default ( ) , ty } )
765- . collect ( ) ,
766- def,
767- } ,
768- ) ;
769- id_defs. insert ( func_id, IdDef :: Func ( func) ) ;
834+ let func = {
835+ use std:: collections:: hash_map:: Entry ;
836+
837+ match id_defs. entry ( func_id) {
838+ Entry :: Occupied ( mut entry) => match entry. get ( ) {
839+ & IdDef :: FuncForwardRef ( func) => {
840+ let decl_slot = & mut module. funcs [ func] ;
841+ assert_is_dummy_decl_for_func_forward_ref ( decl_slot) ;
842+ * decl_slot = decl;
843+
844+ entry. insert ( IdDef :: Func ( func) ) ;
845+ Ok ( func)
846+ }
847+ id_def => Err ( id_def. descr ( & cx) ) ,
848+ } ,
849+ Entry :: Vacant ( entry) => {
850+ let func = module. funcs . define ( & cx, decl) ;
851+ entry. insert ( IdDef :: Func ( func) ) ;
852+ Ok ( func)
853+ }
854+ }
855+ . map_err ( |descr| {
856+ invalid ( & format ! ( "invalid redefinition of {descr} as a new function" ) )
857+ } ) ?
858+ } ;
770859
771860 current_func_body = Some ( FuncBody { func_id, func, insts : vec ! [ ] } ) ;
772861
@@ -1170,7 +1259,7 @@ impl Module {
11701259 "unsupported use of {} outside `OpExtInst`" ,
11711260 id_def. descr( & cx) ,
11721261 ) ) ) ,
1173- None => local_id_defs
1262+ None | Some ( IdDef :: FuncForwardRef ( _ ) ) => local_id_defs
11741263 . get ( & id)
11751264 . copied ( )
11761265 . ok_or_else ( || invalid ( & format ! ( "undefined ID %{id}" , ) ) ) ,
0 commit comments