How to get current address of last executed instruction

Sep 7, 2011 at 6:40pm
Recently, i was searching for a way to find out if the function was really inlined by the compiler. I thought that this approach could work:
1
2
3
4
5
6
7
8
9
10
11
static inline result_t InlineFunction(param_t arg1, param_t arg2)
{
          void *address = getCS_IP();
          if (approximatelyEqual(currentAddress, (void *) &InlineFunction)) {
                  // Function was inlined 
          } else {
                  // Function was not inlined
          }

          // Function code
}

But i don't know how to get the address of the last executed instruction. I don't know if this is possible to get using c (but i think that it can be done via assembly). I am looking for a solution, which will work with gcc compiler and gas assembly.

P.S. I am just curious about this.

Any ideas are appreciated )
Sep 7, 2011 at 7:00pm
I think you can declare a label and convert it to void pointer.. Though why not just look at generated asm?
Sep 7, 2011 at 7:04pm
but i think that it can be done via assembly
Not as far as I know. At least, not with x86 Assembly. You could do
1
2
3
call label
label:
pop eax
but that would just get you the value of ip at 'label'.
At the very least, I figure you'd need a custom calling convention that implicitly passes some kind of information about the stack or the program counter just before the call, so you have something to work with.
Sep 7, 2011 at 8:48pm
helios wrote:
I figure you'd need a custom calling convention that implicitly passes some
kind of information about the stack or the program counter just before the call

You could also pass that information explicitly. Example on windows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <windows.h>
#include <iostream>

using namespace std;

DWORD get_esp()
{
    HANDLE   thread;
    CONTEXT context;

    thread = GetCurrentThread();

    context.ContextFlags = CONTEXT_CONTROL;

    GetThreadContext(thread, &context);

    CloseHandle(thread);

    return context.Esp;
}

/* inline */ void f(DWORD esp)
{
    if (esp == get_esp())
        cout <<     "inlined!" << endl;
    else
        cout << "not inlined!" << endl;
}

int main()
{
    f(get_esp());

    return 0;
}

Useful links:

http://msdn.microsoft.com/en-us/library/ms683182(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/ms679362(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/ms679284(v=vs.85).aspx
Last edited on Sep 7, 2011 at 9:07pm
Sep 12, 2011 at 8:55pm
I think you can declare a label and convert it to void pointer..

I've didn't knew that it's possible ))

Useful links:

Thanks for the links. Using similar stuff in linux system, it becomes possible to do all i need without assembly.
Sep 14, 2011 at 8:43pm
Using all what was wrote above i wrote the following code to determine in runtime whether the function was inlined by the compiler.
Here's code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//inliner.h
#ifndef _INLINER2_H_
#define _INLINER2_H_

#include <cstdio>

extern void *callerFrameAddress;

static inline void forcedInlineFunction(void *params) __attribute__((always_inline));
static inline void inlineFunction(void *params);
static inline void impossibleInlineFunction(void *params);
static 		  void notInlineFunction(void *params);

#define WrapperFor_forcedInlineFunction(x)		(callerFrameAddress = __builtin_frame_address(0), forcedInlineFunction(x))
#define WrapperFor_inlineFunction(x)			(callerFrameAddress = __builtin_frame_address(0), inlineFunction(x))
#define WrapperFor_impossibleInlineFunction(x)	(callerFrameAddress = __builtin_frame_address(0), impossibleInlineFunction(x))
#define WrapperFor_notInlineFunction(x)			(callerFrameAddress = __builtin_frame_address(0), notInlineFunction(x))

#define PrintTraceData()	//printf("[%s] : Caller address [%p], current frame address [%p], caller frame address [%p]\n", __FUNCTION__, \ 
							//__builtin_return_address(0), __builtin_frame_address(0), __builtin_frame_address(1))

#define IsInlined()			(callerFrameAddress == __builtin_frame_address(0))
#define PrintInlineStatus()	printf("[%s] %s inlined\n", __FUNCTION__, (IsInlined()) ? "was" : "was not")

static inline void forcedInlineFunction(void *params)
{
	PrintTraceData();
	PrintInlineStatus();
}

static inline void inlineFunction(void *params)
{
	PrintTraceData();
	PrintInlineStatus();
}

static inline void impossibleInlineFunction(void *params)
{
	PrintTraceData();
	PrintInlineStatus();
	for (int i = 0; i < 100; i++);
}

static void notInlineFunction(void *params)
{
	PrintTraceData();
	PrintInlineStatus();
}

//void * __builtin_frame_address (unsigned int level);
//void * __builtin_return_address (unsigned int level);
//void * __builtin_extract_return_address (void *addr);
//http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

#undef PrintTraceData
#undef IsInlined
#undef PrintInlineStatus

#endif // _INLINER2_H_ 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// inliner.cpp
#include <iostream>

#include "inliner.h"

void *callerFrameAddress = NULL;

int main(void)
{
	WrapperFor_forcedInlineFunction(NULL);
	WrapperFor_notInlineFunction(NULL);
	WrapperFor_inlineFunction(NULL);
	WrapperFor_impossibleInlineFunction(NULL);
	WrapperFor_forcedInlineFunction(NULL);
	WrapperFor_notInlineFunction(NULL);
	WrapperFor_inlineFunction(NULL);
	WrapperFor_impossibleInlineFunction(NULL);
	return 0;
}


I compiled it with gcc compiler gcc version 4.5.1 20101208 [gcc-4_5-branch revision 167585] (SUSE Linux) with the make targets:
1
2
3
4
5
inliner_lst:
	g++ -c -g -Wa,-a,-ad inliner.cpp -o prog.o > prog.lst

inliner:
	g++ inliner.cpp -o prog


As the result i've got next messages:
1
2
3
4
5
6
7
8
[forcedInlineFunction] was inlined
[notInlineFunction] was not inlined
[inlineFunction] was not inlined
[impossibleInlineFunction] was not inlined
[forcedInlineFunction] was inlined
[notInlineFunction] was not inlined
[inlineFunction] was not inlined
[impossibleInlineFunction] was not inlined


And this messages matched the real situation about inlines. However i don't see any reasons why the compiler didn't inlined the [inlineFunction]. As for me this is very strange.

It would be very interesting to hear your opinion about how i check whether the function was inlined and that you check this implementation on your platform
Last edited on Sep 14, 2011 at 8:43pm
Sep 14, 2011 at 8:50pm
My guess is that using PrintTraceData prompts the compiler to decide not to inline unless required by the programmer.
Sep 14, 2011 at 9:12pm
My guess is that using PrintTraceData prompts the compiler to decide not to inline unless required by the programmer.


when i commented all code of inlineFunction and changed wrapper call to
 
#define WrapperFor_inlineFunction(x)			inlineFunction(x) //(callerFrameAddress = __builtin_frame_address(0), inlineFunction(x)) 

it also didn't inlined function
Sep 14, 2011 at 9:16pm
Did you verify through the disassembly or by running the code?
Sep 14, 2011 at 9:29pm
disassembly

1
2
3
4
5
  12:inliner.cpp   **** 	WrapperFor_inlineFunction(NULL);
 194              		.loc 2 12 0
 195 00eb C7042400 		movl	$0, (%esp)
 195      000000
 196 00f2 E809FFFF 		call	_ZL14inlineFunctionPv
Sep 14, 2011 at 10:03pm
You're not compiling with optimizations enabled, so inlining will normally not happen. Compile with -O2 or -O3.
Even so, the compiler will only inline if the gains appear to outweigh the costs. The fact that all functions are called twice each combined with the fact that inlining provides no advantage here other than saving the call itself are probably the reasons why the compiler decides against inlining in this case.
Sep 15, 2011 at 7:55pm
You're not compiling with optimizations enabled, so inlining will normally not happen.
It seems that it is the reason why the function was not inlined.

With optimization levels 1 and 2 i've got next results:
1
2
3
4
5
6
7
8
[forcedInlineFunction] was inlined
[notInlineFunction] was not inlined
[inlineFunction] was inlined
[impossibleInlineFunction] was inlined
[forcedInlineFunction] was inlined
[notInlineFunction] was not inlined
[inlineFunction] was inlined
[impossibleInlineFunction] was inlined


And with the third level, even the function which i didn't declared inlined was inlined by the compiler (I've also checked assembly code and really was inlined))):
1
2
3
4
5
6
7
8
[forcedInlineFunction] was inlined
[notInlineFunction] was inlined
[inlineFunction] was inlined
[impossibleInlineFunction] was inlined
[forcedInlineFunction] was inlined
[notInlineFunction] was inlined
[inlineFunction] was inlined
[impossibleInlineFunction] was inlined


So it seems that compiler directives affect on the result code very much. I'm wondering, are there any standart of c++ on optimization levels or compiler directives (and their affects), or they are compiler dependent and there is no any standart on this.
Sep 15, 2011 at 9:14pm
No, the standard defines syntax and semantics. Optimization is part of pragmatics, which is beyond its scope.
Topic archived. No new replies allowed.