编译器 __built_in_xxx 内建函数

内建函数1

内建函数是编译器内部实现的函数,通常以 __builtin 开头。这些函数主要在编译器内部使用,其主要是为编译器服务的。内建函数的主要用途如下。

用来处理变长参数列表;
用来处理程序运行异常;
程序的编译优化、性能优化;
查看函数运行中的底层信息、堆栈信息等;
C标准库函数的内建版本。

常用内建函数

__builtin_return_address(LEVEL)

C语言函数在调用过程中,x86上会将当前函数的返回地址、寄存器等现场信息保存在堆栈中,然后才会跳到被调用函数中去执行。当被调用函数执行结束后,根据保存在堆栈中的返回地址,就可以直接返回到原来的函数中继续执行。

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 可查看 这篇文章

// 以 aarch64 为例
+----+ <-- high address
|    | 
+----+ <- 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。
该函数常用于宏定义中,用于编译优化。一个宏定义,根据宏的参数是常量还是变量,可能实现的方法不一样。
在 Linux 内核中经常看到这样的宏。

#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 ↩︎

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值