Visual C ++〜不能内联简单的const函数指针调用 [英] Visual C++ ~ Not inlining simple const function pointer calls

查看:114
本文介绍了Visual C ++〜不能内联简单的const函数指针调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

亲爱的StackOverflowers



我有一段简单的代码,我正在Microsoft Visual Studio C ++ 2012上编译:

  int add(int x,int y)
{
return x + y;
}

typedef int(* func_t)(int,int);

class A
{
public:
const static func_t FP;
};

const func_t A :: FP =& add;

int main()
{
int x = 3;
int y = 2;
int z = A :: FP(x,y);
return 0;
}

编译器生成以下代码:

  int main()
{
000000013FBA2430 sub rsp,28h
int x = 3;
int y = 2;
int z = A :: FP(x,y);
000000013FBA2434 mov edx,2
000000013FBA2439 lea ecx,[rdx + 1]
000000013FBA243C call qword ptr [A :: FP(013FBA45C0h)]
return 0;
000000013FBA2442 xor eax,eax
}

优化'(/ Obx标志)和'任何适合'的内联函数扩展。 (/ Ob2 flag)



我想知道为什么编译器不内联这个调用expecially,因为它是const。你是否有任何人有一个想法为什么它不内联,如果可以让编译器内联?



Christian



编辑:现在我正在运行一些测试,并且MSVC无法在以下情况下内联函数指针:



将const指针移出类并使其成为全局。



- 我将const指针移出类,



- 我使指针非常量并在本地移动。

- 当我将返回类型设为void并且没有参数时



Visual Studio不能内联函数指针...

解决方案

问题不在于内联,编译器在每个机会。问题是Visual C ++似乎没有意识到指针变量实际上是一个编译时常数。



测试用例:

  // function_pointer_resolution.cpp:定义控制台应用程序的入口点。 
//

extern void show_int(int);

externCtypedef int binary_int_func(int,int);

externCbinary_int_func sum;
externCbinary_int_func * const sum_ptr = sum;

inline int call(binary_int_func * binary,int a,int b){return(* binary)(a,b); }

template< binary_int_func * binary>
inline int callt(int a,int b){return(* binary)(a,b); }

int main(void)
{
show_int(sum(1,2));
show_int(call(& sum,3,4));
show_int(callt<& sum>(5,6));
show_int((* sum_ptr)(1,7));
show_int(call(sum_ptr,3,8));
// show_int(callt< sum_ptr>(5,9));
return 0;
}

// sum.cpp
externCint sum(int x,int y)
{
return x + y;
}

// show_int.cpp
#include< iostream>

void show_int(int n)
{
std :: cout< n<< std :: endl;
}

函数分为多个编译单元,以更好地控制内联。具体来说,我不想让 show_int 内联,因为它使汇编代码混乱。



麻烦的是有效的代码(注释的行)被Visual C ++拒绝。 G ++没有问题,但Visual C ++抱怨预期的编译时常数表达式。



在启用优化和正常编译语义(无跨模块内联)的情况下,编译器会生成:

  _main PROC; COMDAT 

; 18:show_int(sum(1,2));

push 2
push 1
call _sum
push eax
call?show_int @@ YAXH @ Z; show_int

; 19:show_int(call(& sum,3,4));

push 4
push 3
call _sum
push eax
call?show_int @@ YAXH @ Z; show_int

; 20:show_int(callt& sum>(5,6));

push 6
push 5
call _sum
push eax
call?show_int @@ YAXH @ Z; show_int

; 21:show_int((* sum_ptr)(1,7));

push 7
push 1
call DWORD PTR _sum_ptr
push eax
call?show_int @@ YAXH @ Z; show_int

; 22:show_int(call(sum_ptr,3,8));

push 8
push 3
call DWORD PTR _sum_ptr
push eax
call?show_int @@ YAXH @ Z; show_int
add esp,60; 0000003cH

; 23:// show_int(callt< sum_ptr>(5,9));
; 24:return 0;

xor eax,eax

; 25:}

ret 0
_main ENDP

使用 sum_ptr 和不使用 sum_ptr 之间的巨大差异。使用 sum_ptr 的语句生成一个间接函数调用调用DWORD PTR _sum_ptr ,而所有其他语句生成直接函数调用 call _sum ,即使源代码使用了函数指针。



如果我们现在通过编译function_pointer_resolution.cpp和sum .cpp与 / GL 并链接到 / LTCG ,我们发现编译器内联所有直接调用。间接呼叫保持不变。

  _main PROC; COMDAT 

; 18:show_int(sum(1,2));

push 3
call?show_int @@ YAXH @ Z; show_int

19:show_int(call(& sum,3,4));

push 7
call?show_int @@ YAXH @ Z; show_int

; 20:show_int(callt& sum>(5,6));

push 11; 0000000bH
call?show_int @@ YAXH @ Z; show_int

; 21:show_int((* sum_ptr)(1,7));

push 7
push 1
call DWORD PTR _sum_ptr
push eax
call?show_int @@ YAXH @ Z; show_int

; 22:show_int(call(sum_ptr,3,8));

push 8
push 3
call DWORD PTR _sum_ptr
push eax
call?show_int @@ YAXH @ Z; show_int
add esp,36; 00000024H

; 23:// show_int(callt< sum_ptr>(5,9));
; 24:return 0;

xor eax,eax

25:}

ret 0
_main ENDP

line:是的,编译器通过编译时常量函数指针进行内联调用,只要该函数指针不是从变量中读取的。这种对函数指针的使用已优化:

  call(& sum,3,4); 

但这没有:

 (* sum_ptr)(1,7); 

所有测试都使用Visual C ++ 2010 Service Pack 1运行,为x86上的x86编译。


Microsoft(R)32位C / C ++优化编译器版本16.00.40219.01 for 80x86



Dear StackOverflowers,

I got a simple piece of code which I am compiling on Microsoft Visual Studio C++ 2012:

int add(int x, int y)
{
    return x + y;
}

typedef int (*func_t)(int, int);

class A
{
public:
    const static func_t FP;
};

const func_t A::FP = &add;

int main()
{
 int x = 3;
 int y = 2;
 int z = A::FP(x, y);
 return 0;
}

The compiler generates the following code:

int main()
{
000000013FBA2430  sub         rsp,28h  
int x = 3;
int y = 2;
int z = A::FP(x, y);
000000013FBA2434  mov         edx,2  
000000013FBA2439  lea         ecx,[rdx+1]  
000000013FBA243C  call        qword ptr [A::FP (013FBA45C0h)]  
return 0;
000000013FBA2442  xor         eax,eax
}

I compiled this on the 'Full optimisation' (/Obx flag) and 'Any Suitable' for Inline function Expansion. (/Ob2 flag)

I was wondering why the compiler doesn't inline this call expecially since it's const. Does any of you have an idea why it is not inlined and if it's possible to make the compiler inline it?

Christian

EDIT: I am running some tests now and MSVC fails to inline the function pointers too when:

-I move the const pointer out of the class and make it global.

-I move the const pointer out of the class and make it local in main.

-I make the pointer non-const and move it in locally.

-When I make the return type void and giving it no parameters

I kind start believing Microsoft Visual Studio cannot inline function pointers at all...

解决方案

The problem isn't with inlining, which the compiler does at every opportunity. The problem is that Visual C++ doesn't seem to realize that the pointer variable is actually a compile-time constant.

Test-case:

// function_pointer_resolution.cpp : Defines the entry point for the console application.
//

extern void show_int( int );

extern "C" typedef int binary_int_func( int, int );

extern "C" binary_int_func sum;
extern "C" binary_int_func* const sum_ptr = sum;

inline int call( binary_int_func* binary, int a, int b ) { return (*binary)(a, b); }

template< binary_int_func* binary >
inline int callt( int a, int b ) { return (*binary)(a, b); }

int main( void )
{
    show_int( sum(1, 2) );
    show_int( call(&sum, 3, 4) );
    show_int( callt<&sum>(5, 6) );
    show_int( (*sum_ptr)(1, 7) );
    show_int( call(sum_ptr, 3, 8) );
//  show_int( callt<sum_ptr>(5, 9) );
    return 0;
}

// sum.cpp
extern "C" int sum( int x, int y )
{
    return x + y;
}

// show_int.cpp
#include <iostream>

void show_int( int n )
{
    std::cout << n << std::endl;
}

The functions are separated into multiple compilation units to give better control over inlining. Specifically, I don't want show_int inlined, since it makes the assembly code messy.

The first whiff of trouble is that valid code (the commented line) is rejected by Visual C++. G++ has no problem with it, but Visual C++ complains "expected compile-time constant expression". This is actually a good predictor of all future behavior.

With optimization enabled and normal compilation semantics (no cross-module inlining), the compiler generates:

_main   PROC                        ; COMDAT

; 18   :    show_int( sum(1, 2) );

    push    2
    push    1
    call    _sum
    push    eax
    call    ?show_int@@YAXH@Z           ; show_int

; 19   :    show_int( call(&sum, 3, 4) );

    push    4
    push    3
    call    _sum
    push    eax
    call    ?show_int@@YAXH@Z           ; show_int

; 20   :    show_int( callt<&sum>(5, 6) );

    push    6
    push    5
    call    _sum
    push    eax
    call    ?show_int@@YAXH@Z           ; show_int

; 21   :    show_int( (*sum_ptr)(1, 7) );

    push    7
    push    1
    call    DWORD PTR _sum_ptr
    push    eax
    call    ?show_int@@YAXH@Z           ; show_int

; 22   :    show_int( call(sum_ptr, 3, 8) );

    push    8
    push    3
    call    DWORD PTR _sum_ptr
    push    eax
    call    ?show_int@@YAXH@Z           ; show_int
    add esp, 60                 ; 0000003cH

; 23   :    //show_int( callt<sum_ptr>(5, 9) );
; 24   :    return 0;

    xor eax, eax

; 25   : }

    ret 0
_main   ENDP

There's already a huge difference between using sum_ptr and not using sum_ptr. Statements using sum_ptr generate a indirect function call call DWORD PTR _sum_ptr while all other statements generate a direct function call call _sum, even when the source code used a function pointer.

If we now enable inlining by compiling function_pointer_resolution.cpp and sum.cpp with /GL and linking with /LTCG, we find that the compiler inlines all direct calls. Indirect calls stay as-is.

_main   PROC                        ; COMDAT

; 18   :    show_int( sum(1, 2) );

    push    3
    call    ?show_int@@YAXH@Z           ; show_int

; 19   :    show_int( call(&sum, 3, 4) );

    push    7
    call    ?show_int@@YAXH@Z           ; show_int

; 20   :    show_int( callt<&sum>(5, 6) );

    push    11                  ; 0000000bH
    call    ?show_int@@YAXH@Z           ; show_int

; 21   :    show_int( (*sum_ptr)(1, 7) );

    push    7
    push    1
    call    DWORD PTR _sum_ptr
    push    eax
    call    ?show_int@@YAXH@Z           ; show_int

; 22   :    show_int( call(sum_ptr, 3, 8) );

    push    8
    push    3
    call    DWORD PTR _sum_ptr
    push    eax
    call    ?show_int@@YAXH@Z           ; show_int
    add esp, 36                 ; 00000024H

; 23   :    //show_int( callt<sum_ptr>(5, 9) );
; 24   :    return 0;

    xor eax, eax

; 25   : }

    ret 0
_main   ENDP

Bottom-line: Yes, the compiler does inline calls made through a compile-time constant function pointer, as long as that function pointer is not read from a variable. This use of a function pointer got optimized:

call(&sum, 3, 4);

but this did not:

(*sum_ptr)(1, 7);

All tests run with Visual C++ 2010 Service Pack 1, compiling for x86, hosted on x64.

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86

这篇关于Visual C ++〜不能内联简单的const函数指针调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆