程序员iOS程序猿iOS 开发每天分享优质文章

LLVM编译器中的内置(built-in)函数

2019-05-04  本文已影响40人  欧阳大哥2013

什么是built-in 函数?

在一些.h头文件中或者实现代码中经常会看到一些以__builtin_开头的函数声明或者调用,比如下面的头文件#include <secure/_string.h>中的函数定义:

//这里的memcpy函数的由内置函数__builtin___memcpy_chk来实现。
#if __has_builtin(__builtin___memcpy_chk) || defined(__GNUC__)
#undef memcpy
/* void *memcpy(void *dst, const void *src, size_t n) */
#define memcpy(dest, ...) \
        __builtin___memcpy_chk (dest, __VA_ARGS__, __darwin_obsz0 (dest))
#endif

这些__builtin_开头的符号其实是一些编译器内置的函数或者编译优化处理开关等,其作用类似于宏。宏是高级语言用于预编译时进行替换的源代码块,而内置函数则是用于在编译阶段进行替换的机器指令块。因此编译器的这些内置函数其实并不是真实的函数,而只是一段指令块,起到编译时的内联功能。

内置函数和非内置函数的调用的区别

在一些编译器中会对一些标准库的函数实现改用内置函数来代替,可以起到性能优化的作用。因为执行这些函数调用会在编译时变为直接指令块的执行,而不会产生指令跳转、堆栈等相关的操作而引起的函数调用开销(有一些函数直接就有一条对应的机器指令来实现,如果改用普通函数调用势必性能大打折扣)。不同的编译器对内置函数的支持不尽相同,而且对于是否用内置函数来实现标准库函数也没有统一的标准。比如对于GCC来说它所支持的内置函数都在GCC内置函数列表中被定义和声明,这些内置函数大部分也被LLVM编译器所支持。

本文不会介绍所有的内置函数,而是只介绍其中几个特殊的内置函数以及使用方法。熟练使用这些内置函数可以提升程序的运行性能以及扩展一些编程的模式。

 int a, b
 long c;

int ret1= __builtin_types_compatible_p(typeof(a), typeof(b));  //true
int ret2 = __builtin_types_compatible_p(typeof(a), typeof(c)); //false 
int ret3 = __builtin_types_compatible_p(int , const int);  //true
if  (__builtin_types_compatible_p(typeof(a), int))   //true
{
    
}

   int a = 10;
   const int b = 10;
   int ret1  = __builtin_constant_p(10);  //true
   int ret2 = __builtin_constant_p(a);  //false
   int ret3 = __builtin_constant_p(b);  //true
struct S
{
    char m_a;
    long m_b;
};

int offset1 = __builtin_offsetof(struct S, m_a);  //0
int offset2 = __builtin_offsetof(struct S, m_b);  //8

struct S s;
s.m_a = 'a';
s.m_b = 10;
    
char m_a = *(char*)((char*)&s + offset1);   //'a'
long m_b = *(long*)((char*)&s + offset2);  // 10
//这个例子演示一个函数foo。如果是被fout1函数调用则返回1,被其他函数调用时则返回0。

#include <dlfcn.h>

extern int foo();

void fout1()
{
    printf("ret1 = %d\n", foo());    //ret1 = 1
}

void fout2()
{
    printf("ret2 = %d\n", foo());    //ret2= 0
}

int foo()
{
     void *retaddr = __builtin_return_address(0);  //这个返回地址就是调用者函数的某一处的地址。  
    //根据返回地址可以通过dladdr函数获取调用者函数的信息。
    Dl_info dlinfo;
    dladdr(retaddr, &dlinfo);
    if (dlinfo.dli_saddr == fout1)
        return 1;
    else
        return 0;
}

__builtin_return_address()函数的另外一个经典的应用是iOS系统中用ARC进行内存管理时对返回值是OC对象的函数和方法的特殊处理。比如一个函数foo返回一个OC对象时,系统在编译时会对返回的对象调用objc_autoreleaseReturnValue函数,而在调用foo函数时则会在编译时插入如下的三条汇编指令:

//arm64位的指令
bl foo
mov fp, fp    //这条指令看似无意义,其实这是一条特殊标志指令。
bl objc_retainAutoreleasedReturnValue

如果考察objc_autoreleaseReturnValue函数的内部实现就会发现其内部用了__builtin_return_address函数。objc_autoreleaseReturnValue函数通过调用__builtin_return_address(0)返回的地址的内容是否是mov fp,fp来进行特殊的处理。具体原理可以参考这些函数的实现,因为它们都已经开源。

void foo(char *buf)
{
   void *frameaddr =  __builtin_frame_address(0);
  
   //定义栈内存变量,长度为100个字节。
   char local[100];

   int buflen = strlen(buf);   //获取传递进来的缓存字符串的长度。
   if (local + buflen > frameaddr)  //进行栈内存溢出判断。
   {
         ptrinf("可能会出现栈内存溢出");
         return;
   }
   
  strcpy(local, buf);
}

void fooForInt(int a)
{
    printf("int a = %d\n", a);
}

void fooForDouble(double a)
{
    printf("double a=%f\n", a);
}

//如果x的数据类型是整型则使用fooForInt函数,否则使用fooForDouble函数。
#define fooFor(x) __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int), fooForInt(x), fooForDouble(x))

//根据传递进入的参数类型来决定使用哪个具体的函数。
fooFor(10);
fooFor(10.0);

if (__builtin_expect (x, 0))
     foo ();

表示x的值大部分情况下可能为假,因此foo()函数得到执行的机会比较少。这样编译器在编译这段代码时就不会将foo()函数的汇编指令紧挨着if条件跳转指令。再例如:

if (__builtin_expect (x, 1))
     foo ();

表示x的值大部分情况下可能为真,因此foo()函数得到执行的机会比较大。这样编译器在编译这段代码时就会将foo()函数的汇编指令紧挨着if条件跳转指令。

为了简化函数的使用,iOS系统的两个宏fastpath和slowpath来实现这种分支优化判断处理。


#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))

本节参考自:https://blog.csdn.net/jasonchen_gbd/article/details/44948523

//定义一个数组,在接下来的时间中需要对数组进行频繁的写处理,因此可以将数组的内存地址预抓取到高速缓存中去。
int arr[10];
for (int i = 0; i < 10; i++)
{
     __builtin_prefetch(arr+i, 1, 3);
}

//后面会频繁的对数组元素进行写入处理,因此如果不调用预抓取函数的话,每次写操作都是直接对内存地址进行写处理。
//而当使用了高速缓存后,这些写操作可能只是在高速缓存中执行。
for (int i = 0; i < 1000000; i++)
{
     arr[i%10] = i;
}

本节参考自:https://blog.csdn.net/chrysanthemumcao/article/details/8907566


欢迎大家访问欧阳大哥2013的github地址简书地址

上一篇 下一篇

猜你喜欢

热点阅读