Skip to main content
GameDev.net gamedev.net
🔒 Locked

const function pointer optimized?

Started by Ravyne Dec 28, 2006 at 3:32 PM 6 replies 950+ views
Original Post
Ravyne
Ravyne
I'm curious if anyone knows whether a constant function pointer (that is, a function pointer which always points to the same area in memory) can be optimized away? The reason for this rather wierd construct would be for use in self-modifying code. A static code buffer would exist which would be filled with machine code output by an embeddable assembler/compiler (such as SoftWire) which would be specific to a certain use case. The contents of the code buffer would change as the case changes, but the buffer itself would not move. Obviously, using new or malloc would disallow this type of optimization since the buffer would not be at a consistant offset in the application. To get around this, the Code buffer must be a static byte array (or somesuch) within the data section of the executable in the same way that global variables or the string table would be. This code buffer would be over-written with a valid instruction sequence and its address cast as a function pointer for execution. Ultimately, the question comes down to this: Given a consistant location in memory, is the compiler smart enough to treat that memory location, when cast as a function pointer, in such a way as would remove the function pointer indirection? This would essentially make the code buffer equivilant to a regular function who's contents just happen to have changed. Failing that, is it possible to create a function of an arbitrary size, and then overwriting its code with new code to provide the same benefits, namely the loss of the pointer indirection? I'm using C/C++ and I'm aware of the calling convention issues, etc. Just curious to know if the function pointer overhead can be eliminated. Normally I wouldn't worry about it, however my use requires many calls (potentially thousands) to this function per frame while the state of the function changes infrequently (perhaps 2-4 times per frame.) Moving the dynamic behaviour out of call-time and into the state change should prove a win.
throw table_exception("(? ???)? ? ???");
ToohrVyk
ToohrVyk
Ultimately, there is no guarantee about whether your compiler will do this or not. A pedantic compiler might even rant about casting from a non-function pointer to a function pointer and executing data as code. If I remember correctly, however, addresses of globals are treated as compile-time constants by some compilers, and so the optimization might happen. You have no choice but to prototype.

An alternative to this would be to use a function call without a function pointer, and then modifying the contents of the function in memory (that is, casting a function pointer to data pointer, instead of the reverse). This may cause even more rants from the compiler and the OS, though.
asp_
asp_
Yes it will result in a direct call relative to the next instruction instead an indirect call. The problem I see is that you will be jumping into a data section which may not be allowed on some architectures. Also msvc doesn't set the executable flag on the data section your static data block would end up in. You'd have to tweak that manually (linker setting).

Is this "optimization" really worth the hassle and cross platform breaking you'll be doing? I don't think the extra read is really that slow if you're doing it outside of some tight loop.

As for using an already existing function and overwriting it, that sounds even more dangerous for the previous mentioned reasons and I don't think the msvc linker sets the write flag on the code section.
Ravyne
Ravyne
Right, I'm not at all certain at this time whether this will be worthwhile in the end. However, the number of calls vs the number of state changes is what makes me suspect that it may, and why I'm investigating further.

The application this is for is a 2D software rendering library which has a state-based blitter. Additional calculations such as transparancy, translucancy and filtering may be turned on or off for a given set of blits depending on which features that set of blits requires. So, for blits which require no filtering, scaling or transparancy, this results in the fastest possible code path - because there is no need to step around these code paths because they won't exist in the current code buffer. This also demonstrates why these functions may be called so many times (blitting 1000s of sprites) and why state changes are so uncommon (background imagery is usually 1 state, foreground imagery and sprites will usually fit into a handfull of catagory states.) This is the same idea that many of the high-performance software 3D renderers, such as Pix-O-Matic or Codega's, use.

Common function pointers probably would give the best bang-for-the-buck, in terms of speed gain and ease of implimentation, but maintaining blitters for every combination of states would be a nightmare of code duplication. Templates and the optimizer may be some help here, using bool template arguments to wrap functional blocks and relying on the optimizer to remove "if(false) {}" blocks as dead code. This would also help with bloat as only the used state combinations would be generated at compile time.


Is it possible to set the execute flag for only this specific Code buffer? The glaringly obvious security issue in enabling all data to be executed is a little scary, as is enabling any code to be writable.
throw table_exception("(? ???)? ? ???");
nmi
nmi
So you want to have a region in memory that is executed by a direct call, but you want to replace the code in that memory ?

You can write a simple dummy function with an external assembler that reserves enough bytes for the code you may generate in the worst case (e.g. by filling with nops and a return). Then link that function into your executable. The compiler cannot do any tricks since this is an external assembler function, but it will generate the direct call to that function.

At runtime, call a function like mprotect() in linux to make the pages of that function writable, replace the code, the restore the protection. In windows equivalent function is VirtualProtect().

See this example:
#include <windows.h>#include <cstdio>// dummy functionvoid foo(){	printf("test\n");}int main(){	DWORD oldProtection;		// allow writing to the first byte of foo()	VirtualProtect((void*)&foo, 1, PAGE_READWRITE, &oldProtection);		// replace the first byte of foo() by a RET instruction	*(unsigned char*)&foo = 0xc3;		// reset protection to previous value	VirtualProtect((void*)&foo, 1, oldProtection, &oldProtection);	// execute dummy function	foo();		// exit normally	return 0;}


Compiled with MinGW:
gcc -c modify.ccgcc -o modify.exe modify.omodify

The result is, that nothing is printed. If you uncomment the line containing "*(unsigned char*)&foo = 0xc3;", the program will print "test".

And the disassembly of the object file:
00000000 <__Z3foov>:   0:   55                      push   %ebp   1:   89 e5                   mov    %esp,%ebp   3:   83 ec 08                sub    $0x8,%esp   6:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)   d:   e8 00 00 00 00          call   12 <__Z3foov+0x12>  12:   c9                      leave  13:   c3                      ret00000014 <_main>:  14:   55                      push   %ebp  15:   89 e5                   mov    %esp,%ebp  17:   83 ec 18                sub    $0x18,%esp  1a:   83 e4 f0                and    $0xfffffff0,%esp  1d:   b8 00 00 00 00          mov    $0x0,%eax  22:   83 c0 0f                add    $0xf,%eax  25:   83 c0 0f                add    $0xf,%eax  28:   c1 e8 04                shr    $0x4,%eax  2b:   c1 e0 04                shl    $0x4,%eax  2e:   89 45 f8                mov    %eax,0xfffffff8(%ebp)  31:   8b 45 f8                mov    0xfffffff8(%ebp),%eax  34:   e8 00 00 00 00          call   39 <_main+0x25>  39:   e8 00 00 00 00          call   3e <_main+0x2a>  3e:   8d 45 fc                lea    0xfffffffc(%ebp),%eax  41:   89 44 24 0c             mov    %eax,0xc(%esp)  45:   c7 44 24 08 04 00 00    movl   $0x4,0x8(%esp)  4c:   00  4d:   c7 44 24 04 01 00 00    movl   $0x1,0x4(%esp)  54:   00  55:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)  5c:   e8 00 00 00 00          call   61 <_main+0x4d>  61:   83 ec 10                sub    $0x10,%esp  64:   c6 05 00 00 00 00 c3    movb   $0xc3,0x0  6b:   8d 45 fc                lea    0xfffffffc(%ebp),%eax  6e:   89 44 24 0c             mov    %eax,0xc(%esp)  72:   8b 45 fc                mov    0xfffffffc(%ebp),%eax  75:   89 44 24 08             mov    %eax,0x8(%esp)  79:   c7 44 24 04 01 00 00    movl   $0x1,0x4(%esp)  80:   00  81:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)  88:   e8 00 00 00 00          call   8d <_main+0x79>  8d:   83 ec 10                sub    $0x10,%esp  90:   e8 6b ff ff ff          call   0 <__Z3foov>    ; note this is a direct call  95:   b8 00 00 00 00          mov    $0x0,%eax  9a:   c9                      leave  9b:   c3                      ret  9c:   90                      nop  9d:   90                      nop  9e:   90                      nop  9f:   90                      nop


Note that optimisations must be disabled in this case to prevent the compiler to inline the foo() function. That's the reason why you should compile an external assembler function (for instance with NASM) and then link that function to your executable.


As a second option, instead of patching the code of foo(), you may just patch the address of the call to foo(). That's what any linker/loader is doing. The information where those addresses are located should be stored in the object files (or the exe/dll itself). This would allow to allocate memory from the heap, compile the function into that memory, set the protection for that memory, then patch all the calls (to a dummy function) to point to that memory.
Zahlman
Zahlman
Assuming you're not talking about member functions, and that I'm understanding you clearly and thinking straight, "the function pointer overhead" that you're talking about exists for "ordinary" function calls as well. The difference is simply whether the IP is set to a constant value (from the "ordinary" call) or from whatever the variable gets compiled down to (stack value, register contents, or even a constant value, if the compiler is smart enough to prove the const-ness statically). That said, making the pointer value 'const' probably won't help the compiler optimize, if only because it needs to be somewhat conservative, and there exists such a thing as const_cast<>.
ToohrVyk
ToohrVyk
Quote:
Original post by Zahlman
That said, making the pointer value 'const' probably won't help the compiler optimize, if only because it needs to be somewhat conservative, and there exists such a thing as const_cast<>.


Wait, const_cast is allowed to fail on true const objects, isn't it?
Zahlman
Zahlman
Quote:
Original post by ToohrVyk
Quote:
Original post by Zahlman
That said, making the pointer value 'const' probably won't help the compiler optimize, if only because it needs to be somewhat conservative, and there exists such a thing as const_cast<>.


Wait, const_cast is allowed to fail on true const objects, isn't it?


Yeah, it becomes undefined behaviour IIRC. But in general, the const keyword doesn't help the compiler optimize as much as you'd expect it to. Of course, I shouldn't just spread FUD like that... anyone care to fill in some details? :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.