@@ -86,6 +86,11 @@ fn get_uses(insn: &Instruction) -> Vec<PseudoId> {
8686 }
8787 }
8888
89+ // Indirect call target (function pointer)
90+ if let Some ( indirect) = insn. indirect_target {
91+ uses. push ( indirect) ;
92+ }
93+
8994 uses
9095}
9196
@@ -507,4 +512,74 @@ mod tests {
507512
508513 assert_eq ! ( func. blocks[ 0 ] . insns[ 1 ] . op, Opcode :: Store ) ;
509514 }
515+
516+ #[ test]
517+ fn test_indirect_call_target_is_use ( ) {
518+ // Test that get_uses() includes indirect_target for function pointer calls.
519+ // This prevents DCE from eliminating the instruction that computes
520+ // the function pointer before an indirect call.
521+ let types = TypeTable :: new ( 64 ) ;
522+ let call_insn = Instruction :: call_indirect (
523+ Some ( PseudoId ( 0 ) ) , // return value target
524+ PseudoId ( 5 ) , // func_addr (the function pointer)
525+ vec ! [ PseudoId ( 1 ) , PseudoId ( 2 ) ] , // args
526+ vec ! [ types. int_id, types. int_id] , // arg_types
527+ types. int_id ,
528+ 32 ,
529+ ) ;
530+
531+ let uses = get_uses ( & call_insn) ;
532+
533+ // Verify indirect_target is in the uses list
534+ assert ! (
535+ uses. contains( & PseudoId ( 5 ) ) ,
536+ "get_uses should include indirect_target"
537+ ) ;
538+ // Also verify call arguments are in uses
539+ assert ! ( uses. contains( & PseudoId ( 1 ) ) ) ;
540+ assert ! ( uses. contains( & PseudoId ( 2 ) ) ) ;
541+ }
542+
543+ #[ test]
544+ fn test_indirect_call_keeps_func_ptr_live ( ) {
545+ // Full DCE test: verify an indirect call keeps its function pointer live.
546+ let types = TypeTable :: new ( 64 ) ;
547+ let mut func = Function :: new ( "test" , types. int_id ) ;
548+
549+ func. add_pseudo ( Pseudo :: reg ( PseudoId ( 0 ) , 0 ) ) ; // result
550+ func. add_pseudo ( Pseudo :: reg ( PseudoId ( 1 ) , 1 ) ) ; // func pointer
551+ func. add_pseudo ( Pseudo :: val ( PseudoId ( 2 ) , 42 ) ) ; // arg
552+
553+ let mut bb = BasicBlock :: new ( BasicBlockId ( 0 ) ) ;
554+ bb. add_insn ( Instruction :: new ( Opcode :: Entry ) ) ;
555+
556+ // %1 = load the function pointer (this should stay live)
557+ let mut load = Instruction :: new ( Opcode :: Load ) ;
558+ load. target = Some ( PseudoId ( 1 ) ) ;
559+ load. src = vec ! [ PseudoId ( 2 ) ] ; // load from some address
560+ load. typ = Some ( types. pointer_to ( types. int_id ) ) ;
561+ load. size = 64 ;
562+ bb. add_insn ( load) ;
563+
564+ // %0 = call_indirect %1(%2)
565+ let call = Instruction :: call_indirect (
566+ Some ( PseudoId ( 0 ) ) ,
567+ PseudoId ( 1 ) , // indirect through %1
568+ vec ! [ PseudoId ( 2 ) ] , // args
569+ vec ! [ types. int_id] , // arg_types
570+ types. int_id ,
571+ 32 ,
572+ ) ;
573+ bb. add_insn ( call) ;
574+
575+ bb. add_insn ( Instruction :: ret ( Some ( PseudoId ( 0 ) ) ) ) ;
576+ func. add_block ( bb) ;
577+ func. entry = BasicBlockId ( 0 ) ;
578+
579+ let changed = run ( & mut func) ;
580+
581+ // The load instruction that defines %1 (func pointer) should NOT be
582+ // eliminated because %1 is used as indirect_target in the call
583+ assert ! ( !changed || func. blocks[ 0 ] . insns[ 1 ] . op == Opcode :: Load ) ;
584+ }
510585}
0 commit comments