__built_in_xxx 內建函數

內建函數1

編譯器內部實現的函數。通常以 __builtin 開頭。這些函數主要在編譯器內部使用,主要是爲編譯器服務的。內建函數的主要用途如下。

用來處理變長參數列表;
用來處理程序運行異常;
程序的編譯優化、性能優化;
查看函數運行中的底層信息、堆棧信息等;
C 標準庫函數的內建版本。

常用內建函數

__builtin_return_address(LEVEL)

C 語言函數在調用過程中,會將當前函數的返回地址、寄存器等現場信息保存在堆棧中,然後纔會跳到被調用函數中去執行。當被調用函數執行結束後,根據保存在堆棧中的返回地址,就可以直接返回到原來的函數中繼續執行。

This function returns the return address of the current function, or of one of its callers. The level argument is number
of frames to scan up the call stack. A value of 0 yields the return address of the current function, a value of 1 yields
the return address of the caller of the current function, and so forth.
When inlining the expected behavior is that the function returns the address of the function that is returned to.
To work around this behavior use the noinline function attribute.

The level argument must be a constant integer.
On some machines it may be impossible to determine the return address of any function other than the current one;
in such cases, or when the top of the stack has been reached, this function returns 0 or a random value. In addition,
__builtin_frame_address may be used to determine if the top of the stack has been reached.
__builtin_return_address(LEVEL)
0:返回當前函數的返回地址;
1:返回當前函數調用者的返回地址;
2:返回當前函數調用者的調用者的返回地址;
...

__builtin_frame_address(LEVEL)

在函數調用過程中,有一個“棧幀”的概念。函數每調用一次,都會將當前函數的現場(返回地址、寄存器等)保存在棧中,每一層函數調用都會將各自的現場信息都保存在各自的棧中。這個各自的棧也就是當前函數的棧幀。多層函數調用就會有多個棧幀,每個棧幀裏會保存上一層棧幀的起始地址,這樣各個棧幀就形成了一個調用鏈。很多調試器、內建函數,都是通過回溯函數棧幀調用鏈來獲取函數底層的各種信息的。比如,返回地址、調用關係等。
ARM 處理器使用 FPSP 這兩個寄存器,分別指向當前函數棧幀的起始地址和結束地址。
當函數繼續調用或者返回,這兩個寄存器的值也會發生變化,總是指向當前函數棧幀的起始地址和結束地址。
對於 aarch64 的 APCS 可查看 這篇文章

------
|    | <- fp // 棧幀
------
|    |
------
|    |
------
|    | <- sp // 棧頂
------
|    |
------
|    |
This function is similar to __builtin_return_address, but it returns the address of the function frame rather than
the return address of the function. Calling __builtin_frame_address with a value of 0 yields the frame address of
the current function, a value of 1 yields the frame address of the caller of the current function, and so forth.

The frame is the area on the stack that holds local variables and saved registers. The frame address is normally the
address of the first word pushed on to the stack by the function. However, the exact definition depends upon the
processor and the calling convention. If the processor has a dedicated frame pointer register, and the function has a
frame, then __builtin_frame_address returns the value of the frame pointer register.

__builtin_frame_address(LEVEL)
0:查看當前函數的棧幀地址
1:查看當前函數調用者的棧幀地址
...
#include <stdio.h>

void func(void)
{
        int *p;
        p = __builtin_frame_address(0);
        printf("func frame:%p\n",p);
        p = __builtin_frame_address(1);
        printf("main frame:%p\n",p);
}

int main(void)
{
        int *p;
        p = __builtin_frame_address(0);
        printf("main frame:%p\n",p);
        printf("\n");
        func();
        return 0;
}


main frame:0x40007ff8a0

func frame:0x40007ff880
main frame:0x40007ff8a0


0000000000400548 <func>:
  400548:	a9be7bfd 	stp	x29, x30, [sp, #-32]!
  40054c:	910003fd 	mov	x29, sp
  400550:	f9000fbd 	str	x29, [x29, #24]
  400554:	f00002e0 	adrp	x0, 45f000 <_nl_unload_domain+0x48>
  400558:	91306000 	add	x0, x0, #0xc18
  40055c:	f9400fa1 	ldr	x1, [x29, #24]
  400560:	9400185e 	bl	4066d8 <_IO_printf>
  400564:	f94003a0 	ldr	x0, [x29]
  400568:	f9000fa0 	str	x0, [x29, #24]
  40056c:	f00002e0 	adrp	x0, 45f000 <_nl_unload_domain+0x48>
  400570:	9130a000 	add	x0, x0, #0xc28
  400574:	f9400fa1 	ldr	x1, [x29, #24]
  400578:	94001858 	bl	4066d8 <_IO_printf>
  40057c:	d503201f 	nop
  400580:	a8c27bfd 	ldp	x29, x30, [sp], #32
  400584:	d65f03c0 	ret

__builtin_constant_p(n)

該函數主要用來判斷參數 n 在編譯時是否爲常量,是常量的話,函數返回1;否則函數返回0。
該函數常用於宏定義中,用於編譯優化。一個宏定義,根據宏的參數是常量還是變量,可能實現的方法不一樣。
在內核中經常看到這樣的宏。

#define _dma_cache_sync(addr, sz, dir)        \
do {                            \
    if (__builtin_constant_p(dir)) {          \
        __inline_dma_cache_sync(addr, sz, dir); \
    } else {                       \
        __arc_dma_cache_sync(addr, sz, dir);    \
    } \
} while (0); \

__builtin_expect(exp,c)

在這裏插入圖片描述
這個函數有兩個參數,返回值就是其中一個參數,仍是 exp。這個函數的意義主要就是告訴編譯器:參數 exp 的值爲 c 的可能性很大。然後編譯器可能就會根據這個提示信息,做一些分支預測上的代碼優化。

#include <stdio.h>

int main(void)
{
        int a;
        a = __builtin_expect(3,1);
        printf("a = %d\n",a);
        a = __builtin_expect(3,10);
        printf("a = %d\n",a);
        a = __builtin_expect(3,100);
        printf("a = %d\n",a);
        return 0;
}

a = 3
a = 3
a = 3

實際上 likely unlikely 用的就是這個內建函數實現的。

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

C 標準庫的內建函數

__builtin_memcpy__builtin_puts

內存相關的函數:memcpy 、memset、memcmp
數學函數:log、cos、abs、exp
字符串處理函數:strcat、strcmp、strcpy、strlen
打印函數:printf、scanf、putchar、puts


  1. https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html ↩︎

發佈了67 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章