Why are pointers to inline functions allowed?

前端 未结 5 1037
别跟我提以往
别跟我提以往 2021-01-31 02:12

I have two questions:

1) Why are pointers to inline functions allowed in C++? I have read that the code of inline functions just gets copied to the function call stateme

5条回答
  •  忘了有多久
    2021-01-31 02:26

    Apart from the already said point that an inline function need not actually be inlined (and many functions without inline are inlined by modern compilers), it's also entirely conceivable to inline a call through a function pointer. Example:

    #include 
    
    int foo(int (*fun)(int), int x) {
      return fun(x);
    }
    int succ(int n) {
      return n+1;
    }
    int main() {
      int c=0;
      for (int i=0; i<10000; ++i) {
        c += foo(succ, i);
      }
      std::cout << c << std::endl;
    }
    

    Here, foo(succ, i) could as a whole be inlined to just i+1. And indeed that seems to happen: g++ -O3 -S produces code for the foo and succ functions

    _Z3fooPFiiEi:
    .LFB998:
        .cfi_startproc
        movq    %rdi, %rax
        movl    %esi, %edi
        jmp *%rax
        .cfi_endproc
    .LFE998:
        .size   _Z3fooPFiiEi, .-_Z3fooPFiiEi
        .p2align 4,,15
        .globl  _Z4succi
        .type   _Z4succi, @function
    _Z4succi:
    .LFB999:
        .cfi_startproc
        leal    1(%rdi), %eax
        ret
        .cfi_endproc
    

    But then it generates code for main which never refers to either of these, instead just includes a new specialised _GLOBAL__sub_I__Z3fooPFiiEi:

    .LFE999:
        .size   _Z4succi, .-_Z4succi
        .section    .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function
    main:
    .LFB1000:
        .cfi_startproc
        movdqa  .LC1(%rip), %xmm4
        xorl    %eax, %eax
        pxor    %xmm1, %xmm1
        movdqa  .LC0(%rip), %xmm0
        movdqa  .LC2(%rip), %xmm3
        jmp .L5
        .p2align 4,,10
        .p2align 3
    .L8:
        movdqa  %xmm2, %xmm0
    .L5:
        movdqa  %xmm0, %xmm2
        addl    $1, %eax
        paffffd   %xmm3, %xmm0
        cmpl    $2500, %eax
        paffffd   %xmm0, %xmm1
        paffffd   %xmm4, %xmm2
        jne .L8
        movdqa  %xmm1, %xmm5
        subq    $24, %rsp
        .cfi_def_cfa_offset 32
        movl    $_ZSt4cout, %edi
        psrldq  $8, %xmm5
        paffffd   %xmm5, %xmm1
        movdqa  %xmm1, %xmm6
        psrldq  $4, %xmm6
        paffffd   %xmm6, %xmm1
        movdqa  %xmm1, %xmm7
        movd    %xmm7, 12(%rsp)
        movl    12(%rsp), %esi
        call    _ZNSolsEi
        movq    %rax, %rdi
        call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        xorl    %eax, %eax
        addq    $24, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
    .LFE1000:
        .size   main, .-main
        .p2align 4,,15
        .type   _GLOBAL__sub_I__Z3fooPFiiEi, @function
    _GLOBAL__sub_I__Z3fooPFiiEi:
    .LFB1007:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $_ZStL8__ioinit, %edi
        call    _ZNSt8ios_base4InitC1Ev
        movl    $__dso_handle, %edx
        movl    $_ZStL8__ioinit, %esi
        movl    $_ZNSt8ios_base4InitD1Ev, %edi
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        jmp __cxa_atexit
        .cfi_endproc
    .LFE1007:
        .size   _GLOBAL__sub_I__Z3fooPFiiEi, .-_GLOBAL__sub_I__Z3fooPFiiEi
        .section    .init_array,"aw"
        .align 8
        .quad   _GLOBAL__sub_I__Z3fooPFiiEi
        .local  _ZStL8__ioinit
        .comm   _ZStL8__ioinit,1,1
    

    So in this case the actual program does not even contain a function pointer pointing to succ – the compiler has found out that this pointer would always refer to the same function anyway, and was therefore able to eliminate the entire thing without changing the behaviour. This can improve performance a lot, when you often call small functions through function pointers. Which is quite a widespread technique in functional languages; compilers for languages like O'Caml and Haskell make great use of this kind of optimisation.


    Disclaimer: my assembly skills are close to nonexistent. I might well be talking rubbish here.

提交回复
热议问题