diff --git a/16-add-c-and-cxx-memory-align-investigation.patch b/16-add-c-and-cxx-memory-align-investigation.patch new file mode 100644 index 0000000000000000000000000000000000000000..18af900014fd27d42600c1008a4f63ba5483aca3 --- /dev/null +++ b/16-add-c-and-cxx-memory-align-investigation.patch @@ -0,0 +1,1992 @@ +From 90111bc11daa50d6fe7bec65989156666a1d023b Mon Sep 17 00:00:00 2001 +From: huwei3 +Date: Mon, 5 Dec 2022 17:29:41 +0800 +Subject: [PATCH] add-c-and-cxx-memory-align-investigation + + +diff --git a/docs/c-and-cxx-memory-align-investigation.md b/docs/c-and-cxx-memory-align-investigation.md +new file mode 100644 +index 0000000..e24ef7b +--- /dev/null ++++ b/docs/c-and-cxx-memory-align-investigation.md +@@ -0,0 +1,1977 @@ ++# C/C++内存对齐能力分析报告 ++ ++- 版本号:1.0 ++- 文档编辑:胡伟 ++ - last modify:2022.11.15 ++ ++ ++ ++ ++## 目录 ++ ++[TOC] ++ ++## 引言 ++ ++​ 许多**计算机系统**对**基本数据类型**的可允许地址做出了一些限制,要求某种数据类型的对象的地址必须是某个值k(通常是2、4或8)的倍数,这种**对齐限制**简化了**处理器**和**存储器系统**之间接口的硬件设计^[1]^。 ++ ++​ 一些强制对齐的情况:如果数据未对齐,有些实现多媒体操作的SSE指令就无法正确地工作,任何企图以不满足对齐要求的地址来访问存储器都会导致异常吗,默认的行为是程序终止。^[7]^ ++ ++​ 内存对齐的影响:内存对齐可能会影响内存访问的性能,但是并不是一定会导致的,比如在现代英特尔处理器中,内存访问错位不会影响性能。但是在老版本的Raspberry Pi处理器,内存访问错位导致的性能降低明显^[7]^。 ++ ++​ 但是关于数据对齐,是否会导致硬件错误,有文献表明:无论数据是否对齐,IA32硬件都能正常工作,不过英特尔还是建议要对其数据以提高存储器性能系统的性能^[1]^。由此可见,数据对齐并不一定会影响硬件的正常使用。 ++ ++> 本文档中,代码测试环境:64位ubuntu,CPU信息如下 ++ ++```bash ++huwei@dell:~$ less /proc/cpuinfo ++processor : 0 ++vendor_id : GenuineIntel ++cpu family : 6 ++model : 79 ++model name : Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz ++stepping : 1 ++microcode : 0x1 ++===============省略================ ++``` ++ ++ ++ ++## 1.C/C++标准对内存对齐的要求 ++ ++​ 对齐的定义:要求特定类型的对象位于存储边界上,其地址是字节地址的特定倍数(通常是2、4或8)。 ++ ++​ 笔者对C语言的C99^[2]^、C11^[3]^、C++98^[4]^、C++11^[5]^标准进行了解读,总结如下: ++ ++ ++ ++### 1.1.C99标准 ++ ++> 1.基础类型对齐 ++ ++- 每个有符号/无符号整数类型,都具有相同的对齐要求^[9]^。 ++- 类型 `_Bool` 和==对应于标准有符号整数类型的无符号整数类型是标准无符号整数类型^[2]^。== ++ ++> 2.指针类型对齐,**注意事项**:^[9]^ ++ ++- void指针和`char *`指针具有相同的对齐要求 ++- 类似地,指向兼容类型的合格或非合格版本的指针应具有相同的表示和对齐要求。 ++ - 所有指向**结构类型**的指针都应具有彼此相同的表示和对齐要求。 ++ - 所有指向**联合类型**的指针都应具有彼此相同的表示和对齐要求。 ++ - 指向**其他类型**的指针不需要具有相同的表示或对齐要求。 ++ - 指向**对象**或**不完整类型**的指针可以转换为指向不同对象或不完整类型的指针。 ++- **未定义的行为**: ++ - 两种指针类型之间的转换产生的结果**可能产生不正确的对齐** ++ ++> 3.复合类型的对齐: ++ ++- 每个**复杂类型**具有与恰好包含对应真正类型(real type)的两个元素的**数组类型**相同的表示和对齐要求; ++ - structure或union对象的每个非位域成员都以适合其类型的实现定义的方式对齐。 ++ ++ ++ ++### 1.2.C11标准^[3]^ ++ ++- 1、完整的对象类型(object types )具有对齐要求^[3]^,这对可以分配该类型对象的地址进行了限制。 对齐是实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数。 对象类型对该类型的每个对象都有对齐要求:可以使用 `_Alignas` **关键字**请求**更严格的对齐**。 ++- 2、==基本对齐(fundamental alignment)==由小于或等于所有上下文中实现支持的最大对齐的对齐表示,该对齐等于`_Alignof (max_align_t)`。 ++- 3、==扩展对齐(extended alignment)==由大于 `_Alignof (max_align_t)` 的对齐表示。 ++- 4、对齐方式表示为 `size_t` 类型的值。 有效对齐仅包括由基本类型的 `_Alignof` 表达式返回的值,以及附加的实现定义的值集,这些值可能为空。 每个有效的对齐值(valid alignment value)应是 2 的非负整数幂。 ++- 5、对齐具有从弱到强(weaker to stronger)或更严格的对齐顺序。 ==更严格的对齐具有更大的对齐值==(Stricter alignments have larger alignment values)。 满足对齐要求的地址也满足任何较弱的有效对齐要求。 ++- 6、可以使用 `_Alignof` 表达式查询完整类型的对齐要求。 `char`、`signed char` 和 `unsigned char` 类型应具有**最弱的对齐要求。(the weakest alignment requirement.)** ++- 7、比较对齐(Comparing alignments)是有意义的,并提供了明显的结果: ++ - 当数值相等时,两个对齐是相等的。 ++ - 当数值不相等时,两个对齐是不同的。 ++ - ==当一个对齐**大于**另一个对齐时,它表示**更严格的对齐**。(When an alignment is larger than another it represents a stricter alignment.)== ++ ++ ++ ++### 1.3.C++11标准^[5]^ ++ ++​ C++11中关于对齐^[12]^ ++ ++​ 1、对象类型有对齐要求^[14]^,这对该类型对象可能被分配的地址施加了限制。对齐是一个实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数。对象类型对该类型的每个对象强加了对齐要求;可以使用对齐说明符要求更严格的对齐。 ++ ++​ 2、基本对齐由小于或等于实现在所有上下文中支持的最大对齐表示,最大对齐等于`alignof(std::max_align_t)`。当类型用作完整对象的类型和用作子对象的类型时,类型所需的对齐方式可能不同。^[12]^(例子: ++ ++```cpp ++struct B { long double d; }; ++struct D : virtual B { char c; } ++``` ++ ++当D是一个完整对象的类型时,它将有一个类型为B的子对象,因此它必须对long double进行适当的对齐。如果D作为另一个对象的子对象出现,而另一个对象也将B作为虚拟基类,那么B子对象可能是另一个不同子对象的一部分,从而减少了对D子对象的对齐要求。对齐操作符的结果反映了完全对象情况下类型的对齐要求。 ++ ++​ 3、扩展对齐( extended alignment)由大于`alignof(std::max_align_t)`的对齐表示。是否支持任何扩展对齐以及支持它们的上下文是由实现定义的。**具有扩展对齐要求**的类型是**过对齐类型**。[注意:每个过对齐类型都是或包含应用扩展对齐的类类型(可能通过非静态数据成员)。 ++ ++​ 4、对齐表示为`std::size_t`类型的值。有效的对齐只包括基本类型的对齐表达式返回的值,以及实现定义的额外值集(可能为空)。每个对齐值应为2的非负整数幂。 ++ ++​ 5、队列有一个从弱到强或更严格的顺序。更**严格**的对齐具有更大的对齐值。满足对齐要求的地址也满足任何较弱的有效对齐要求。 ++ ++​ 6、可以使用`alignof`表达式查询完整类型的对齐要求。此外,char类型、signed char类型和unsigned char类型的对齐要求最低。[注意:这使得==字符类型==被用作**对齐内存区域**的**基础类型**。] ++ ++​ 7、比较对齐是有意义的,并提供了明显的结果: ++ ++- 当两个对齐的数值相等时,它们就相等。 ++- 当数值不相等时,两种对齐方式是不同的。 ++- 当一个对齐比另一个对齐**大**时,表示比对**更严格** ++ ++​ 8、注:运行时指针对齐函数可用于在缓冲区内获得对齐指针;库中的对齐存储模板可用于获得对齐存储。 ++ ++​ 9、如果实现不支持在特定上下文中对特定扩展对齐的请求,则该程序是格式不良的。此外,如果==动态存储的运行时分配请求不能满足所请求的对齐,将被视为分配失败。== ++ ++ ++ ++ ++ ++### 1.4.总结 ++ ++- 1、对于基本数据类型,其对齐位数为其`sizeof`的值 ++- 2、对于array类型,其对齐数为其容纳的元素的对齐值 ++- 3、对于struct、class、union类型,其整体的对齐值为其数据成员的最大对齐值和**具体ABI**设置的默认对齐值中的较少值。 ++- 4、函数、堆栈都有具体的对齐值,**取决于具体的ABI规定** ++- 5、编译器实现,可以使用`alignof`等表达式查询完整类型的对齐要求 ++- 6、结构体本身始终与其**成员最大类型**的对齐要求对齐^[7]^ ++ ++
语言标准C99→C11→C++11关于内存对齐的一些新增变化
++ ++| | C99 | C11 | C++11 | ++| ---------- | ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ++| 相关头文件 | 无 | <`stdalign.h`>头文件,查询和指定对象对齐方式 | 同C11 | ++| 函数 | 无 | `aligned_ alloc`函数返回空指针或指向分配空间的指针。 | 新增函数`std::align` | ++| 关键词or宏 | 宏`alignas`和宏`alignof` | 新增:`_Alignas`、`_Alignof`、`alignas`扩展为`_alignas`;宏`alignof`扩展为`_alignof`和`__alignof__` | 新增`alignas`、`alignof`关键词,但取消`_Alignas`和`_Alignof`和`__alignof__` | ++| 其他特性 | `#pragma` | added the `aligned_alloc` | 同C11 | ++ ++ ++ ++ ++ ++## 2.C和C++内存对齐场景分析 ++ ++### 2.1.场景1.基本数据类型 ++ ++#### 2.1.1.整数类 ++ ++> bool【C++】、char、short、int、long、long long ++ ++#### 2.1.2.浮点数类 ++ ++> float、double、long double ++ ++#### 2.1.3.指针类 ++ ++> [pointer] ++ ++### 2.2.场景2.复合类型 ++ ++#### 2.2.1.同类型复合 ++ ++> [array] ++ ++#### 2.2.2.不同类型复合 ++ ++> [Structures],class【C++】、[unions]、enum、[bit-fields] ++ ++- 【注】**其中, ==unions, enumerations、bit-fields==在Fortran的内存对齐支持中无相关场景,我们后边==不进行考虑==** ++ ++### 2.3.场景3.函数边界 ++ ++ ++ ++### 2.4.场景4.stack(堆栈)边界 ++ ++ ++ ++ ++ ++ ++ ++## 3.C/C++的align场景基础实现 ++ ++### 3.1.场景1.基本数据类型 ++ ++> 场景1.基本数据类型 ++ ++- 第1类:整数:`bool【C++】、char、short、int、long、long long`、enum(枚举,与Fortran相差太远,不做深入讨论) ++- 第2类:浮点数:`float、double、long double` ++- 第3类:指针:[pointer] ++ ++ ++ ++#### 例1-C11+`_alignof` ++ ++```c ++//main.c ++#include ++#include ++int main() ++{ ++ char a; ++ short b; ++ int c; ++ long d; ++ long long e; ++ float f; ++ double g; ++ long double h; ++ int * ptr_one; ++ void * ptr_two; ++ ++ printf("%d\n", _Alignof(a) ); ++ printf("%d\n", _Alignof(b) ); ++ printf("%d\n", _Alignof(c) ); ++ printf("%d\n", _Alignof(d) ); ++ printf("%d\n", _Alignof(e) ); ++ printf("%d\n", _Alignof(f) ); ++ printf("%d\n", _Alignof(g) ); ++ printf("%d\n", _Alignof(h) ); ++ printf("%d\n", _Alignof( ptr_one ) ); ++ printf("%d\n", _Alignof( ptr_two ) ); ++ return 0; ++} ++``` ++ ++```bash ++huwei@dell:~$ gcc -std=c11 main.c -w ++huwei@dell:~$ ./a.out ++1 ++2 ++4 ++8 ++8 ++4 ++8 ++16 ++8 ++8 ++ ++``` ++ ++ ++ ++#### 例2-C++11+`alignof` ++ ++```cpp ++//g++ ./demo.cpp -std=c++11 -w ++//.cpp ++#include ++#include ++using namespace std; ++ ++int main() ++{ ++ bool test; ++ char a; ++ short b; ++ int c; ++ long d; ++ long long e; ++ float f; ++ double g; ++ long double h; ++ int * ptr_one; ++ void * ptr_two; ++ ++ printf("%d\n", alignof(test) ); ++ printf("%d\n", alignof(a) ); ++ printf("%d\n", alignof(b) ); ++ printf("%d\n", alignof(c) ); ++ printf("%d\n", alignof(d) ); ++ printf("%d\n", alignof(e) ); ++ printf("%d\n", alignof(f) ); ++ printf("%d\n", alignof(g) ); ++ printf("%d\n", alignof(h) ); ++ printf("%d\n", alignof( ptr_one ) ); ++ printf("%d\n", alignof( ptr_two ) ); ++ printf("%d\n", alignof( int ) ); ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~$ g++ demo.cpp -w ++huwei@dell:~$ ./a.out ++1 ++1 ++2 ++4 ++8 ++8 ++4 ++8 ++16 ++8 ++8 ++4 ++``` ++ ++ ++ ++#### 例3-C++11+`const` ++ ++```cpp ++//g++ ./demo.cpp -std=c++11 -w ++//.cpp ++#include ++#include ++using namespace std; ++ ++int main() ++{ ++ const bool test=false; ++ const char a=1; ++ const short b=1; ++ const int c=2; ++ const long d=3; ++ const long long e=3; ++ const float f=2.3; ++ const double g=1.2; ++ const long double h=2.2; ++ const int * ptr_one=&c; ++ const void * ptr_two=&b; ++ ++ printf("%d\n", alignof(test) ); ++ printf("%d\n", alignof(a) ); ++ printf("%d\n", alignof(b) ); ++ printf("%d\n", alignof(c) ); ++ printf("%d\n", alignof(d) ); ++ printf("%d\n", alignof(e) ); ++ printf("%d\n", alignof(f) ); ++ printf("%d\n", alignof(g) ); ++ printf("%d\n", alignof(h) ); ++ printf("%d\n", alignof( ptr_one ) ); ++ printf("%d\n", alignof( ptr_two ) ); ++ printf("%d\n", alignof( int ) ); ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~$ ./a.out ++1 ++1 ++2 ++4 ++8 ++8 ++4 ++8 ++16 ++8 ++8 ++4 ++``` ++ ++ ++ ++#### 例4-C++11+`static` ++ ++```cpp ++#include ++#include ++using namespace std; ++ ++int main() ++{ ++ static bool test; ++ static char a; ++ static short b; ++ static int c; ++ static long d; ++ static long long e; ++ static float f; ++ static double g; ++ static long double h; ++ static int * ptr_one; ++ static void * ptr_two; ++ ++ printf("%d\n", alignof(test) ); ++ printf("%d\n", alignof(a) ); ++ printf("%d\n", alignof(b) ); ++ printf("%d\n", alignof(c) ); ++ printf("%d\n", alignof(d) ); ++ printf("%d\n", alignof(e) ); ++ printf("%d\n", alignof(f) ); ++ printf("%d\n", alignof(g) ); ++ printf("%d\n", alignof(h) ); ++ printf("%d\n", alignof( ptr_one ) ); ++ printf("%d\n", alignof( ptr_two ) ); ++ printf("%d\n", alignof( int ) ); ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~$ g++ demo.cpp -std=c++11 -w ++huwei@dell:~$ ./a.out ++1 ++1 ++2 ++4 ++8 ++8 ++4 ++8 ++16 ++8 ++8 ++4 ++ ++``` ++ ++ ++ ++ ++ ++#### 例5.C++11+`sizeof` ++ ++```cpp ++//main.cpp ++#include ++#include ++using namespace std; ++ ++int main() ++{ ++ bool test; ++ char a; ++ short b; ++ int c; ++ long d; ++ long long e; ++ float f; ++ double g; ++ long double h; ++ int * ptr_one; ++ void * ptr_two; ++ ++ printf("%d\n", sizeof(test) ); ++ printf("%d\n", sizeof(a) ); ++ printf("%d\n", sizeof(b) ); ++ printf("%d\n", sizeof(c) ); ++ printf("%d\n", sizeof(d) ); ++ printf("%d\n", sizeof(e) ); ++ printf("%d\n", sizeof(f) ); ++ printf("%d\n", sizeof(g) ); ++ printf("%d\n", sizeof(h) ); ++ printf("%d\n", sizeof( ptr_one ) ); ++ printf("%d\n", sizeof( ptr_two ) ); ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~$ g++ demo.cpp -std=c++11 -w ++huwei@dell:~$ ./a.out ++1 ++1 ++2 ++4 ++8 ++8 ++4 ++8 ++16 ++8 ++8 ++``` ++ ++ ++ ++#### 例6.C++11+`__alignof__` ++ ++```cpp ++#include ++#include ++using namespace std; ++ ++int main() ++{ ++ bool test; ++ char a; ++ short b; ++ int c; ++ long d; ++ long long e; ++ float f; ++ double g; ++ long double h; ++ int * ptr_one; ++ void * ptr_two; ++ ++ printf("%d\n", __alignof__(test) ); ++ printf("%d\n", __alignof__(a) ); ++ printf("%d\n", __alignof__(b) ); ++ printf("%d\n", __alignof__(c) ); ++ printf("%d\n", __alignof__(d) ); ++ printf("%d\n", __alignof__(e) ); ++ printf("%d\n", __alignof__(f) ); ++ printf("%d\n", __alignof__(g) ); ++ printf("%d\n", __alignof__(h) ); ++ printf("%d\n", __alignof__( ptr_one ) ); ++ printf("%d\n", __alignof__( ptr_two ) ); ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~$ g++ demo.cpp -std=c++11 -w ++huwei@dell:~$ ./a.out ++1 ++1 ++2 ++4 ++8 ++8 ++4 ++8 ++16 ++8 ++8 ++ ++``` ++ ++ ++ ++#### 例7.C++11+`enum` ++ ++```cpp ++#include ++#include ++using namespace std; ++ ++enum Foo ++{ ++ a, b, c = 10, d, e = 1, f, g = f + c ++}; ++ ++int main() ++{ ++ ++ cout< ++#include ++using namespace std; ++int test; ++int main() ++{ ++ int b=1; ++ cout << alignof(b) < 场景2.复合类型 ++ ++- 第1类:同类型复合:[array] ++- 第2类:不同类型复合:[Structures]、class【C++】、[unions]、[bit-fields](与Fortran相差太远,不做深入讨论) ++ ++#### 例1.C++11+`[array]` ++ ++```cpp ++//demo.cpp ++#include ++#include ++using namespace std; ++ ++int main() ++{ ++ bool first; ++ bool second[3]; ++ int * ptr; ++ int * test[3]; ++ printf("sizeof(first)=%d alignof(first)=%d\n", sizeof(first), alignof(first) ); ++ printf("sizeof(second)=%d alignof(second)=%d\n", sizeof(second), alignof(second) ); ++ printf("sizeof(ptr)=%d alignof(ptr)=%d\n", sizeof(ptr), alignof(ptr) ); ++ printf("sizeof(test)=%d alignof(test)=%d\n", sizeof(test), alignof(test) ); ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~$ g++ demo.cpp -std=c++11 -w ++huwei@dell:~$ ./a.out ++sizeof(first)=1 alignof(first)=1 ++sizeof(second)=3 alignof(second)=1 ++sizeof(ptr)=8 alignof(ptr)=8 ++sizeof(test)=24 alignof(test)=8 ++``` ++ ++ ++ ++ ++ ++#### 例2.C++11+`struct/class`1 ++ ++- 下面代码struct换成class也是一样 ++ ++```cpp ++huwei@dell:~$ cat demo.cpp ++//main.cpp ++#include ++#include ++using namespace std; ++ ++struct S ++{ ++ short f[3]; ++}; ++ ++int main() ++{ ++ cout<< "sizeof(S)=" << sizeof( S ) < ++#include ++using namespace std; ++ ++#pragma pack(push,1) //后边可以改为1,2,4,8 ++struct node ++{ ++ char m1; ++ long m2; ++}; ++#pragma pack(pop) ++ ++ ++int main() ++{ ++ char a; ++ short b; ++ int c; ++ double d[2]; ++ struct node s; ++ printf("a address=%p\n", &a ); ++ printf("b address=%p\n", &b ); ++ printf("c address=%p\n", &c ); ++ printf("d[0] address=%p\n", &( d[0] ) ); ++ printf("d[1] address=%p\n", &( d[1] ) ); ++ printf("s address=%p\n", &s ); ++ printf("s.m2 address=%p\n", &(s.m2) ); ++ ++ ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~/exp$ g++ demo.cpp -g ++huwei@dell:~/exp$ ./a.out ++a address=0x7ffef9233e3d ++b address=0x7ffef9233e3e ++c address=0x7ffef9233e40 ++d[0] address=0x7ffef9233e50 ++d[1] address=0x7ffef9233e58 ++s address=0x7ffef9233e47 ++s.m2 address=0x7ffef9233e48 ++``` ++ ++​ gdb打印栈顶指针 ++ ++```bash ++huwei@dell:~/exp$ g++ demo.cpp -g ++huwei@dell:~/exp$ gdb ./a.out ++========省略======== ++(gdb) b 16 ++Breakpoint 1 at 0x11c4: file demo.cpp, line 21. ++(gdb) b 17 ++Note: breakpoint 1 also set at pc 0x11c4. ++Breakpoint 2 at 0x11c4: file demo.cpp, line 21. ++(gdb) b 18 ++Note: breakpoints 1 and 2 also set at pc 0x11c4. ++Breakpoint 3 at 0x11c4: file demo.cpp, line 21. ++(gdb) r ++Starting program: /home/huwei/exp/a.out ++ ++Breakpoint 1, main () at demo.cpp:21 ++21 printf("a address=%p\n", &a ); ++(gdb) p $sp ++$1 = (void *) 0x7fffffffc420 ++(gdb) disp $sp ++1: $sp = (void *) 0x7fffffffc420 ++(gdb) n ++a address=0x7fffffffc42d ++22 printf("b address=%p\n", &b ); ++1: $sp = (void *) 0x7fffffffc420 ++(gdb) n ++b address=0x7fffffffc42e ++23 printf("c address=%p\n", &c ); ++1: $sp = (void *) 0x7fffffffc420 ++ ++``` ++ ++ ++ ++ ++ ++### :book:宏or关键词 ++ ++### 3.5.`_Alignof`(仅C11) ++ ++ 限制: ++ ++- 1、` _Alignof`运算符不得应用于**函数类型**或**不完整类型**。 ++- 2、` _Alignof`运算符产生其操作数类型的对齐要求。不计算操作数,结果为整数常量。 ++- 3、当**应用于数组类型时,结果是元素类型的对齐要求。** ++ ++```c ++whoway@ubuntu:~$ cat demo.c ++//gcc ./demo.c -std=c11 ++//.c ++#include ++#include ++int main() ++{ ++ int a; ++ long c; ++ ++ printf("_Alignof(char)=%d\n", _Alignof(char) ); ++ printf("_Alignof(short)=%d\n", _Alignof(short) ); ++ printf("_Alignof(int)=%d\n", _Alignof(int) ); ++ printf("_Alignof(long)=%d\n", _Alignof(long) ); ++ printf("_Alignof(long)=%d\n", _Alignof(long long) ); ++ printf("_Alignof(float)=%d\n", _Alignof(float) ); ++ printf("_Alignof(double)=%d\n", _Alignof(double) ); ++ printf("_Alignof(long double)=%d\n", _Alignof(long double) ); ++ return 0; ++} ++ ++/* ++whoway@ubuntu:~$ ./a.out ++_Alignof(char)=1 ++_Alignof(short)=2 ++_Alignof(int)=4 ++_Alignof(long)=8 ++_Alignof(long)=8 ++_Alignof(float)=4 ++_Alignof(double)=8 ++_Alignof(long double)=16 ++*/ ++``` ++ ++ ++ ++### 3.6.`__alignof__`和`__alignof`(仅C11) ++ ++​ `__alignof__`同`__alignof`。 ++ ++​ 关键字`__alignof__`确定函数、对象(object)或类型的对齐要求,或类型通常需要的最小对齐。它的语法就像`sizeof`和C11的 `_Alignof`。有些机器实际上从来不需要对齐;它们允许引用任何数据类型,即使在奇数地址。对于这些机器,`__alignof__`报告GCC给出的数据类型的最小对齐,通常是由目标ABI指定的。 ++ ++- 如果`__alignof__`的操作数是一个左值而不是一个类型,它的值是其类型所需的对齐方式,考虑到被对齐的属性指定的最小对齐方式()。 ++ ++- 如果`__alignof__`表达式的操作数是**一个函数**,该表达式的计算结果为可能由aligned属性指定的函数的对齐方式 ++ ++```c ++//gcc ./demo.c -std=c11 ++//.c ++#include ++#include ++int main() ++{ ++ int a; ++ long c; ++ ++ printf("__alignof__(char)=%d\n", __alignof__(char) ); ++ printf("__alignof__(short)=%d\n", __alignof__(short) ); ++ printf("__alignof__(int)=%d\n", __alignof__(int) ); ++ printf("__alignof__(long)=%d\n", __alignof__(long) ); ++ printf("__alignof__(long)=%d\n", __alignof__(long long) ); ++ printf("__alignof__(float)=%d\n", __alignof__(float) ); ++ printf("__alignof__(double)=%d\n", __alignof__(double) ); ++ printf("__alignof__(long double)=%d\n", __alignof__(long double) ); ++ return 0; ++} ++``` ++ ++```bash ++/* ++whoway@ubuntu:~$ ./a.out ++__alignof__(char)=1 ++__alignof__(short)=2 ++__alignof__(int)=4 ++__alignof__(long)=8 ++__alignof__(long)=8 ++__alignof__(float)=4 ++__alignof__(double)=8 ++__alignof__(long double)=16 ++*/ ++``` ++ ++### 3.7.`_Alignas`(仅C11) ++ ++C11标准文档关于`_Alignas`的规范: ++ ++```c ++_Alignas ( type-name ) //类型名 ++_Alignas ( constant-expression )//常量表达式 ++``` ++ ++​ 完整的**对象类型**具有对齐要求,这些要求限制了该类型对象的分配地址。对齐是一个实现定义的整数值,表示可以分配给给定对象的连续地址之间的字节数。对象类型对该类型的每个对象都施加对齐要求:可以使用`_Alignas`**关键字**请求更严格的对齐。 ++ ++```c ++#include ++ ++int main() ++{ ++ int a; ++ printf( "_Alignof(a)=%d\n" ,_Alignof(a) ); ++ _Alignas(8) int b; ++ printf( "_Alignof(b)=%d\n" ,_Alignof(b) ); ++ return 0; ++} ++ ++``` ++ ++````bash ++huwei@dell:~/exp$ gcc main.c -std=c11 -w ++huwei@dell:~/exp$ ./a.out ++_Alignof(a)=4 ++_Alignof(b)=8 ++```` ++ ++ ++ ++ ++ ++ ++ ++ ++ ++### 3.8.`alignof`(C11/C++11) ++ ++#### 例1-C11 ++ ++```c ++whoway@ubuntu:~$ cat demo.c ++//gcc ./demo.c -std=c11 ++//.c ++#include ++#include ++int main() ++{ ++ int a; ++ long c; ++ ++ printf("alignof(char)=%d\n", alignof(char) ); ++ printf("alignof(short)=%d\n", alignof(short) ); ++ printf("alignof(int)=%d\n", alignof(int) ); ++ printf("alignof(long)=%d\n", alignof(long) ); ++ printf("alignof(long)=%d\n", alignof(long long) ); ++ printf("alignof(float)=%d\n", alignof(float) ); ++ printf("alignof(double)=%d\n", alignof(double) ); ++ printf("alignof(long double)=%d\n", alignof(long double) ); ++ return 0; ++} ++/* ++whoway@ubuntu:~$ ./a.out ++alignof(char)=1 ++alignof(short)=2 ++alignof(int)=4 ++alignof(long)=8 ++alignof(long)=8 ++alignof(float)=4 ++alignof(double)=8 ++alignof(long double)=16 ++*/ ++``` ++ ++ ++ ++#### 例2-C++11^[14,21]^ ++ ++```bash ++#include ++ ++struct Foo ++{ ++ int i; ++ float f; ++ char c; ++}; ++struct alignas(alignof(long double)) Foo2 ++{ ++}; ++struct Empty {}; ++struct alignas(64) Empty64 {}; ++ ++int main() ++{ ++ std::cout << "Alignment of" "\n" ++ "- char : " << alignof(char) << "\n" ++ "- pointer : " << alignof(int*) << "\n" ++ "- class Foo : " << alignof(Foo) << "\n" ++ "- class Foo2 : " << alignof(Foo2) << "\n" ++ "- empty class : " << alignof(Empty) << "\n" ++ "- empty class\n" ++ " with alignas(64): " << alignof(Empty64) << "\n"; ++} ++``` ++ ++```bash ++Alignment of ++- char : 1 ++- pointer : 8 ++- class Foo : 4 ++- class Foo2 : 16 ++- empty class : 1 ++- empty class ++ with alignas(64): 64 ++``` ++ ++ ++ ++ ++ ++### 3.9.关键词`alignas`(C11/C++11)^[21]^ ++ ++```cpp ++#include ++ ++struct Empty {}; ++ ++struct alignas(64) Empty64 {}; ++ ++int main() ++{ ++ std::cout << "Alignment of" "\n" ++ "- empty class : " << alignof(Empty) << "\n" ++ "- empty class\n" ++ " with alignas(64): " << alignof(Empty64) << "\n"; ++} ++``` ++ ++```bash ++huwei@dell:~$ g++ demo.cpp ++huwei@dell:~$ ./a.out ++Alignment of ++- empty class : 1 ++- empty class ++ with alignas(64): 64 ++``` ++ ++ ++ ++ ++ ++### :book:函数 ++ ++### 3.10.`malloc`函数-heap上的对齐 ++ ++​ 我们可以通过malloc()函数在堆区创建一块动态内存空间,例如:`malloc(1);` 在堆区创建1个字节大小的空间,但是实际情况下一般的编译器malloc()创建的数据大小远超过1字节,当然也有不是很完善的平台,直接分配给你没对齐的内存,这就涉及到相关内存对齐问题了^[24]^。 ++ ++​ 但关于malloc函数分配出的内存对齐(像是gcc的编译器^[6]^,他们做的工作很多,malloc直接分配对齐了的内存),受限于具体编译器在后边做的内存管理^[23],[24]^,因而笔者在此不做深入探讨。 ++ ++ ++ ++### 3.11.函数`std::align` ++ ++```cpp ++void* align( std::size_t alignment, ++ std::size_t size, ++ void*& ptr, ++ std::size_t& space ); ++``` ++​ 给定一个指向ptr大小缓冲区的space指针,返回一个按指定字节数对齐的指针,并按alignment用于对齐的字节数size减少space参数。返回第一个对齐的地址。 ++ ++​ 仅当可以将通过给定对齐方式对齐的所需字节数放入缓冲区时,该函数才修改指针。如果缓冲区太小,函数什么也不做并返回空指针. ++ ++​ alignment如果不是 2 的幂, 则行为未定义。 ++ ++#### 例1-C++ ++ ++​ 代码展示如下^[22]^ ++ ++```cpp ++#include ++#include ++ ++template ++struct MyAllocator ++{ ++ char data[N]; ++ void* p; ++ std::size_t sz; ++ MyAllocator() : p(data), sz(N) {} ++ template ++ T* aligned_alloc(std::size_t a = alignof(T)) ++ { ++ if (std::align(a, sizeof(T), p, sz)) ++ { ++ T* result = reinterpret_cast(p); ++ p = (char*)p + sizeof(T); ++ sz -= sizeof(T); ++ return result; ++ } ++ return nullptr; ++ } ++}; ++ ++int main() ++{ ++ MyAllocator<64> a; ++ std::cout << "allocated a.data at " << (void*)a.data ++ << " (" << sizeof a.data << " bytes)\n"; ++ ++ // allocate a char ++ if (char* p = a.aligned_alloc()) { ++ *p = 'a'; ++ std::cout << "allocated a char at " << (void*)p <`< '\n'; ++ } ++ ++ // allocate an int ++ if (int* p = a.aligned_alloc()) { ++ *p = 1; ++ std::cout << "allocated an int at " << (void*)p << '\n'; ++ } ++ ++ // allocate an int, aligned at 32-byte boundary ++ if (int* p = a.aligned_alloc(32)) { ++ *p = 2; ++ std::cout << "allocated an int at " << (void*)p << " (32 byte alignment)\n"; ++ } ++} ++``` ++ ++```bash ++huwei@dell:~/exp$ g++ demo.cpp ++huwei@dell:~/exp$ ./a.out ++allocated a.data at 0x7ffe371a3490 (64 bytes) ++allocated a char at 0x7ffe371a3490 ++allocated an int at 0x7ffe371a3494 ++allocated an int at 0x7ffe371a34a0 (32 byte alignment) ++``` ++ ++ ++ ++ ++ ++### 3.12.伪指令`#pragma pack` ++ ++​ 下面这一系列 `pragma` 控制随后定义的类和联合成员的最大对齐^[17,18,19,20]^。packed属性的主要目的是让编译器更紧凑地使用内存。当它用于变量时,告诉编译器该变量应该有尽可能小的对齐,也就是1字节对齐。当它用于结构体时 ,相当于给该结构体的每个成员加上了packed属性,这时该结构体将占用尽可能少的内存^[20]^。 ++ ++```cpp ++- #pragma pack(arg) (1) ++- #pragma pack() (2) ++- #pragma pack(push) (3) ++- #pragma pack(push, arg) (4) ++- #pragma pack(pop) (5) ++``` ++ ++解释如下:其中*arg*是 2 的次幂,并以字节为单位指定新的对齐方式。 ++ ++- 1)将当前对齐设置为值*arg*。 ++ ++- 2)将当前对齐设置为默认值(由命令行选项指定),取消自定义字节对齐方式。 ++ ++- 3)将当前对齐的值压入内部堆栈。 ++ ++- 4)将当前对齐的值压入内部堆栈,然后将当前对齐设置为值*arg*。 ++ ++- 5)从内部堆栈中弹出顶部条目,然后将当前对齐设置(恢复)为该值。 ++ ++ ++ ++#### 例1-C++11 ++ ++```cpp ++//输出16 ++#include ++using namespace std; ++ ++typedef struct test_32 ++{ ++ char a; ++ char b; ++ double c; ++}test_32; ++ ++int main() ++{ ++ ++ cout< ++using namespace std; ++ ++#pragma pack(4) ++ ++typedef struct test_32 ++{ ++ char a; ++ char b; ++ double c; ++}test_32; ++ ++int main() ++{ ++ ++ cout< ++using namespace std; ++ ++#pragma pack(16) ++ ++typedef struct test_32 ++{ ++ char a; ++ char b; ++ double c; ++}test_32; ++ ++int main() ++{ ++ ++ cout< ++using namespace std; ++ ++struct first ++{ ++ short f[3]; ++}; ++ ++struct __attribute__ ((aligned (8))) second ++{ ++ short f[3]; ++}; ++ ++int main() ++{ ++ cout<< sizeof( first ) < ++using namespace std; ++ ++int main() ++{ ++ char a; ++ char b __attribute__((aligned(8))); ++ cout << sizeof(a) < ++ ++ ++void test() ++{ ++ printf("test"); ++} ++void __attribute__ ((aligned (8)))one() ++{ ++ printf("one"); ++} ++int main() ++{ ++ printf("__alignof__(test)=%d\n", __alignof__(test) ); ++ printf("__alignof__(one)=%d\n", __alignof__(one) ); ++ return 0; ++} ++``` ++ ++```bash ++huwei@dell:~/exp$ gcc main.c -std=c11 -w ++huwei@dell:~/exp$ ./a.out ++__alignof__(test)=1 ++__alignof__(one)=8 ++``` ++ ++##### 例4-C++11 ++ ++```cpp ++#include ++using namespace std; ++ ++int main() ++{ ++ __attribute__((aligned(1))) ++ int a; ++ cout << alignof(a) < `-falign-functions系列` ++ ++```bash ++-falign-functions[=n[:m:[n2[:m2]]]] ++-falign-functions ++-falign-functions=n ++-falign-functions=n:m ++-falign-functions=n:m:n2 ++-falign-functions=n:m:n2:m2 ++``` ++ ++​ 将函数的开头与大于或等于n的下一次幂对齐,最多跳过m-1个字节。这确保了CPU可以在不跨越n字节对齐边界的情况下提取函数的至少前m个字节。**如果未指定m,则默认为n** ++ ++​ 示例:“`-falign-functions=32`”将函数与下一个32字节边界对齐,“`-falign-functions=24`”仅在跳过23字节或更少的情况下与下一条32字节边界对准 ++ ++​ 第二对`n2:m2`值允许您指定二次对齐:`-falign-functions==64:7:32:3`与下一个64字节边界对齐,如果可以跳过6个字节或更少,则对齐;否则,如果可以通过跳过2个字节或更小,则对齐到下一个32字节边界。如果未指定m2,则默认为n2。某些==汇编器==仅在==n为二的幂时==支持此标志;在这种情况下,它被四舍五入。 ++ ++​ ‘`-fno-align-functions`’ and ‘`-falign-functions=1`’是等价的,意味着==函数未对齐==。 ==如果未指定n或n为零,请使用依赖于机器的默认值。==,允许的最大n选项值为65536。 ++ ++> `-flimit-function-alignment` ++ ++如果启用此选项,编译器将==尝试避免不必要的函数过度对齐==。它==试图指示汇编器==按' `-falign-functions` '指定的量进行对齐,但不跳过超过函数大小的字节。(此处,笔者不做探讨) ++ ++ ++ ++##### 例1-C ++ ++```c ++#include ++void test() ++{ ++ printf("test"); ++} ++void one() ++{ ++ printf("one"); ++} ++int main() ++{ ++ printf("__alignof__(test)=%d\n", __alignof__(test) ); ++ printf("address=%p\n", &test); ++ printf("__alignof__(one)=%d\n", __alignof__(one) ); ++ printf("address=%p\n", &one); ++ return 0; ++} ++ ++``` ++ ++```bash ++huwei@dell:~/exp$ gcc main.c -w ++huwei@dell:~/exp$ ./a.out ++__alignof__(test)=1 ++address=0x557031021149 ++__alignof__(one)=1 ++address=0x557031021165 ++huwei@dell:~/exp$ gcc main.c -falign-functions=32 -w ++huwei@dell:~/exp$ ./a.out ++__alignof__(test)=1 ++address=0x55ad45431160 ++__alignof__(one)=1 ++address=0x55ad45431180 ++huwei@dell:~/exp$ gcc main.c -falign-functions=32 -w -flimit-function-alignment ++huwei@dell:~/exp$ ./a.out ++__alignof__(test)=1 ++address=0x5606e9c5a160 ++__alignof__(one)=1 ++address=0x5606e9c5a180 ++huwei@dell:~/exp$ gcc main.c -w -flimit-function-alignment ++huwei@dell:~/exp$ ./a.out ++__alignof__(test)=1 ++address=0x55827a66b149 ++__alignof__(one)=1 ++address=0x55827a66b165 ++``` ++ ++ ++ ++#### 辅助3.堆栈对齐 ++ ++```bash ++-fipa-stack-alignment ++``` ++ ++如果可能,减少调用站点上==的stack 对齐==。==默认情况下启用==。 ++ ++ ++ ++ ++ ++#### 辅助4.其他和硬件相关-以x86为例 ++ ++​ 在X86上,GCC还支持相当的一部分内存对齐扩展选项,展示如下(并非全部): ++ ++```txt ++-malign-double ++-mno-align-stringops ++-mstackrealign ++-mavx256-split-unaligned-load ++-mavx256-split-unaligned-store ++-malign-data=type ++align-stringops ++no-align-stringops ++force_align_arg_pointer ++``` ++ ++​ 其中`-malign-double`和`-mno-align-double`,控制GCC 在两字边界还是一字边界上对齐 `double` , `long double` 和 `long long` 变量。在两个单词的边界上对齐 `double` 变量会产生在奔腾上运行速度更快的代码,但会占用更多内存。**On x86-64,`-malign-double`默认情况下是启用的。** ++ ++​ 其中命令行选项-`malign-double`会使Linux 上的GCC为double类型的数据使用8字节的对齐。这会提高存储器性能,但是在与用4字节对齐方式下编译的库代码链接时,会导致不兼容。 ++ ++​ 其中在x86目标上,`force_align_arg_pointer`。这支持混合使用4字节对齐堆栈运行的传统代码和保留16字节堆栈以实现SSE兼容性的现代代码 ++ ++​ 此外,X86对其中的一部分内存对齐扩展选项,还收到具体的操作系统的影响,如以下选项 ++ ++> x86 **Windows** Options ++ ++| 选项 | 作用 | ++| ------------------------------------- | ------------------------------------------------------------ | ++| `-mpe-aligned-commons` | 此选项适用于Cygwin和==MinGW目标==。它指定在生成代码时应使用PE文件格式的GNU扩展名,该扩展名允许公共变量的正确对齐。如果GCC检测到在配置期间找到的**目标汇编器**支持该功能,则默认情况下启用该功能。 | ++| `-mtarget-align`和`-mno-target-align` | 启用此选项后,GCC指示汇编程序自动对齐指令,以减少分支惩罚,但会牺牲一些代码密度。汇编程序尝试加宽密度指令,以对齐分支目标和调用指令之后的指令。如果前面没有足够的安全密度指令来对齐目标,则不执行加宽。默认值为“`-mtarget-align`”。这些选项不会影响像LOOP这样的自动对齐指令的处理,汇编程序总是通过加宽密度指令或插入NOP指令来对齐这些指令。 | ++ ++ ++ ++##### 例1-C++11 ++ ++> x86的`long double` ++ ++``` ++-m96bit-long-double ++-m128bit-long-double ++``` ++ ++这些开关控制 `long double` 类型的大小。 ++ ++- x86-32应用程序二进制接口将大小指定为96位,因此`-m96bit-long-double`是32位模式下的默认值。 ++- 在x86-64编译器中。`-m128bit-long-double`是默认选项,因为它的ABI指定 `long double` 精度数组在16字节边界上对齐。 ++ ++```bash ++huwei@dell:~$ less /proc/cpuinfo ++processor : 0 ++vendor_id : GenuineIntel ++cpu family : 6 ++model : 79 ++model name : Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz ++stepping : 1 ++microcode : 0x1 ++省略========================== ++``` ++ ++```cpp ++#include ++using namespace std; ++ ++struct S ++{ ++ char c; ++ long double a; ++}; ++ ++ ++int main() ++{ ++ cout<< sizeof( S ) <系统链接器和/或对象文件格式的固有限制。在某些系统上,链接器只能安排变量以一定的最大对齐方式对齐。(对于一些链接器,最大支持对齐可能非常非常小。)如果你的**链接器**只能将变量对齐到最大8字节对齐,那么在`__attribute__`中指定aligned(16)仍然只能为你提供8字节对齐。有关更多信息,请参阅链接器文档 ++ ++​ 2、堆栈变量不受链接限制的影响;GCC可以在任何目标上适当地对齐它们。 ++ ++ ++ ++#### 辅助6.GCC给内存对齐提供的一些宏定义 ++ ++​ GCC还提供了一个特定于目标的宏__BIGGEST_ALIGNMENT__,这是您正在编译的目标机器上任何数据类型使用的最大对齐^[11]^。例如,你可以这样写: ++ ++​ 编译器会自动将声明的变量或字段的对齐方式设置为__BIGGEST_ALIGNMENT__。这样做通常可以提高复制操作的效率,因为在对以这种方式对齐的变量或字段执行复制时,编译器可以使用**任何指令**来复制最大的内存块。请注意__BIGGEST_ALIGNMENT__的值可能会根据命令行选项的不同而改变。 ++ ++​ 在计算机中,为了提高存储的访问性能,通常需要对存储的数据进行对齐处理。例如对于32位的目标机器来说,数据在存储时如果能以4字节(即32位)地址对齐,则存储的访问效率会得到一定程度的提升。下表^[10]^描述了GCC中对待,**目标处理器**中各种对齐方式的主要宏定义,借此,完成对C/C++中基本对齐方式的实现。 ++ ++​ 以==i386系列硬件==为例^[10]^: ++ ++| 宏定义 | 意义(以位为单位对齐) | 备注 | ++| -------------------------- | --------------------------------------------------------- | ------------------------------------------------------------ | ++| `PARM_BOUNDARY` | **函数参数**在堆栈中的对齐位数 | 一般为目**标机器**上==整数==的大小(位数),所有堆栈中的参数至少满足这个对齐要求 | ++| `STACK_BOUNDARY` | **目标机器**提供的**堆栈边界**地址的对齐位数 | 默认值为`PARM_ BOUNDARY` | ++| `PREFERRED_STACK_BOUNDARY` | ==自定义==的比 `STACK_BOUNDARY`数值更大的堆栈边界对齐位数 | 应该大于或等于`STACK BOUNDARY`,默认值为 `STACK BOUNDARY` | ++| `FUNCTION_BOUNDARY` | **函数入口地址**的对齐位数 | | ++| ==`BIGGEST_ALIGNMENT`== | **任何**数据类型都可以要求的一个最大对齐位数 | | ++| `MALLOC_ABI_ALIGNMENT` | malloc函数分配地址的对齐位数 | 默认值为`BITS_PER_WORD` | ++| `ATTRIBUTE_ALIGNED_VALUE` | 为`_attribute__ ((aligned))`对齐属性设置的对齐位数值 | **默认值**为==BIGGEST_ALIGNMENT== | ++| `MINIMUM_ATOMIC_ALIGNMENT` | 最小的对齐数 | 默认值为BITS_PER_UNIT | ++| `BIGGEST_FIELD_ALIGNMENT` | ==成员变量==的最大对齐位数 | | ++| `MAX_STACK_ALIGNMENT` | 最大堆栈对齐位数 | 默认`STACK_BOUNDARY` | ++ ++> - ~/gcc-12.1.0/gcc/config/i386/i386.h ++ ++```c ++/* Allocation boundary (in *bits*) for storing arguments in argument list. */ ++#define PARM_BOUNDARY BITS_PER_WORD ++ ++/* Boundary (in *bits*) on which stack pointer should be aligned. */ ++#define STACK_BOUNDARY (TARGET_64BIT_MS_ABI ? 128 : BITS_PER_WORD) ++ ++/* Stack boundary of the main function guaranteed by OS. */ ++#define MAIN_STACK_BOUNDARY (TARGET_64BIT ? 128 : 32) ++ ++/* Minimum stack boundary. */ ++#define MIN_STACK_BOUNDARY BITS_PER_WORD ++ ++``` ++ ++​ 与前面的示例一样,您可以显式地指定希望编译器对给定变量或结构字段使用的对齐方式(以字节为单位)。或者,您**可以省略对齐因子**,只要求编译器将变量或字段对齐到您正在编译的**目标体系结构**的==默认对齐方式==。默认对齐方式对于**所有标量类型**都足够了,但是对于支持向量操作的目标上的所有向量类型可能还不够。==对于特定的目标ABI,默认对齐方式是固定的==。^[11]^ ++ ++ ++ ++#### 辅助7.GCC关于align的一些内置函数 ++ ++​ 如`__builtin_assume_aligned`、 `__builtin_ia32_palignr` 等 ++ ++ ++ ++ ++ ++## 5.clang对C/C++的内存对齐实现 ++ ++​ LLVM项目中,是以clang前端对C语言和C++语言进行的解析^[13]^。在此处,笔者借助clang来指代LLVM项目中的C/C++编译器。 ++ ++### 5.1.基础实现 ++ ++ 同第3章节,场景和关键词+类+函数 ++ ++ ++ ++### 5.2.辅助能力 ++ ++> Clang 支持了广泛的GCC扩展,在此,笔者不对GCC对C/C++语言标准关于内存对齐的扩展做赘述。 ++ ++ ++ ++#### 辅助1.选项 ++ ++​ 除此以外,clang还扩展了一部分内存对齐扩展选项^[15]^,类似如下(更具体的,请阅读clang用户手册^[13]^) ++ ++```bash ++-f[no-]max-type-align=[number] ++``` ++ ++​ clang在IR中引用了一个新的概念不透明指针,`-f[no-]max-type-align=[number]`选项指示代码生成器在通过**不透明指针**或引用访问内存时,不要强制执行高于给定数字(字节数)的对齐。当直接访问变量或指针类型具有显式的“aligned”属性时,此上限将被忽略^[15]^。 ++ ++​ 该值通常应由系统分配器的属性确定。一些内置类型,特别是向量类型,具有非常高的自然对齐;在处理这些类型的值时,Clang通常希望使用利用这种对齐的指令。然而,许多系统分配器不承诺返回超过8字节或16字节对齐的内存。使用此选项可以限制编译器对可能指向堆的任意指针的对齐方式。 ++ ++​ 此选项不影响ABI的类型对齐;结构和联合的布局以及alignof操作符返回的值保持不变。 ++ ++​ 通过在结构、联合或类型定义上显式地设置“对齐”对齐,可以逐个覆盖此选项。例如: ++ ++```cpp ++#include ++// Make an aligned typedef of the AVX-512 16-int vector type. ++typedef __v16si __aligned_v16si __attribute__((aligned(64))); ++ ++void initialize_vector(__aligned_v16si *v) { ++ // The compiler may assume that ‘v’ is 64-byte aligned, regardless of the ++ // value of -fmax-type-align. ++} ++``` ++ ++ ++ ++#### 辅助2.clang关于内存对齐的内置函数^[16]^ ++ ++​ Clang支持与GCC语法相同的多个内置库函数^[16]^,包括`__builtin_nan`, `__builtin_constant_p`, `__builtin_choose_expr`, `__builtin_types_compatible_p`, `__builtin_assume_aligned`, `__sync_fetch_and_add`但是,clang较之GCC还实现了其他的一些关于内存对齐的内置函数(builtin function) ,比如: ++ ++```cpp ++__builtin_alloca(size_t n) ++``` ++ ++​ `__builtin_alloca`用于在堆栈上动态分配内存。功能终止时,内存将自动释放。 ++ ++```cpp ++__builtin_alloca_with_align(size_t n, size_t align) ++``` ++ ++​ `__builtin_alloca_with_align`用于在堆栈上分配动态内存量。它类似于`__builtin_alloca`,但接受第二个参数,该参数的值是对齐约束,以位为2的幂。 ++ ++​ 此外,Clang提供了支持检查和调整指针和整数对齐的内置程序(Alignment builtins)。这些内置函数可以用来避免依赖于从指针派生的整数的实现定义的算术行为。此外,这些内置程序保留类型信息,与按位运算不同,它们可以对对齐值执行语义检查。 ++ ++```cpp ++Type __builtin_align_up(Type value, size_t alignment); ++Type __builtin_align_down(Type value, size_t alignment); ++bool __builtin_is_aligned(Type value, size_t alignment); ++``` ++ ++​ ` __builtin_align_up`和`__bulltin_alignt_down`返回第一个向上/向下对齐的第二个参数的下一个倍数。如果该值已充分对齐,则返回不变。`__builtin_is_aligned`返回第一个参数是否与第二个参数的倍数对齐。所有这些内置程序都希望对齐以字节数表示。 ++ ++ ++ ++## 6.总结 ++ ++​ C和C++内存对齐能力**主要依赖**于具体的CPU的ABI设计,但是有部分的操作系统接口也会对内存对齐进行影响,典型的如Microsoft Windows。 ++ ++​ **CPU对内存对齐影响的调研结果**:不同的CPU体系结构是影响内存对齐的**主要因素**。 ++ ++​ **OS对内存对齐影响的调研结果**:除了不同**CPU体系结构**影响内存对齐外,不同的OS平台对堆栈对齐做出不同的假设,比如,老版本的Linux和Windows操作系统希望堆栈对齐到4字节边界,现代Linux和Windows操作系统希望堆栈对齐到16字节边界,OSX一直希望堆栈对齐到16字节边界。^[7]^ ++ ++​ 但C和C++内存对齐的场景需求是与具体的ABI无关的,主要细分为如下几大场景: ++ ++> 场景1.基本数据类型 ++ ++- 第1类:整数:`bool【C++】、char、short、int、long、long long`、enum(枚举,与Fortran相差太远,不做深入讨论) ++- 第2类:浮点数:`float、double、long double` ++- 第3类:指针:[pointer] ++ ++> 场景2.复合类型 ++ ++- 第1类:同类型复合:[array] ++- 第2类:不同类型复合:[Structures]、class【C++】、[unions]、[bit-fields](与Fortran相差太远,不做深入讨论) ++ ++> 场景3.函数边界 ++ ++> 场景4.stack(堆栈)边界 ++ ++ ++ ++ ++ ++## 参考资料 ++ ++- [1] CSAPP第3版, 深入理解计算机系统,P168-172 ++- [2] C99 Language standards , ISO/IEC 9899:TC3 ++- [3] C11 Language standards , ISO/IEC 9899:201x ++- [4] C++98 Language standards , Reference number ISO/IEC 14882:1998(E) ++- [5] C++11 Language standards , Reference number ISO/IEC 14882:2011(E) ++- [6] Using the GNU Compiler Collection, For gcc version 12.0.0 (pre-release) ++- [7] Alignment in C Seminar “Effiziente Programmierung in C”,2014, epc-14-haase-svenhendrik-alignmentinc-paper, ++- [8] GCC-12.1.0,[https://ftp.gnu.org/gnu/gcc/gcc-12.1.0/](https://ftp.gnu.org/gnu/gcc/gcc-12.1.0/) ++- [9] P33, ISO/IEC 9899:TC ++- [10] 王亚刚,深入分析GCC,P302 ++- [11] aligned (alignment)-P610面,Using the GNU Compiler Collection, For gcc version 12.0.0 (pre-release) ++- [12] 3.11 Alignment , C++11 Language standards , Reference number ISO/IEC 14882:2011(E) ++- [13] clang16 , Clang Compiler User’s Manualhttps://clang.llvm.org/docs/UsersManual.html ++- [14] https://duckduckgo.com/?sites=cppreference.com&q=align&ia=web ++- [15] https://clang.llvm.org/docs/UsersManual.html#command-line-options ++- [16] https://clang.llvm.org/docs/LanguageExtensions.html?highlight=memory%20align ++- [17] C++23 standard (ISO/IEC 14882:2023) ++- [18] C++20 standard (ISO/IEC 14882:2020) ++- [19] C++17 standard (ISO/IEC 14882:2017) ++- [20] C++14 standard (ISO/IEC 14882:2014) ++- [21] https://en.cppreference.com/w/cpp/preprocessor/impl ++- [22] https://en.cppreference.com/w/cpp/memory/align ++- [23] 侯捷,C++内存管理,https://mooc.study.163.com/smartSpec/detail/1001137001.htm ++- [24] 侯捷,STL源码剖析 ++- [25] 第3卷, 内存管理, 《英特尔体系结构软件开发人员手册》 +-- +2.17.1 + diff --git a/flang.spec b/flang.spec index f4af1fab990eaa48df178d49e59f8ad5504b8f53..80ac5a9247f4f0203bbe562c7a9062d803756b6e 100644 --- a/flang.spec +++ b/flang.spec @@ -2,7 +2,7 @@ Name: flang Version: flang_20210324 -Release: 17 +Release: 18 Summary: Fortran language compiler targeting LLVM License: Apache-2.0 @@ -26,6 +26,7 @@ Patch11: 12-test-interoperability-with-c-c-call-fortran-function.patch Patch12: 13-test-interoperability-with-c-c-call-fortran-global-and-struct.patch Patch13: 14-add-test-cases-for-attribute-declarations-and-specifications-2.patch Patch14: 15-add-test-cases-for-types-2.patch +Patch15: 16-add-c-and-cxx-memory-align-investigation.patch %description Flang depends on a fork of the LLVM project (https://github.com/flang-compiler/classic-flang-llvm-project). The fork made some changes to the upstream LLVM project to support Flang toolchain. Flang cannot build independently for now. @@ -47,6 +48,9 @@ TODO: support build Flang. %changelog +* Fri Dec 9 2022 huwei - flang_20210324-18 +- Add 16-add-c-and-cxx-memory-align-investigation.patch for c and cxx memory align investigation + * Thu Dec 8 2022 xieyihui - flang_20210324-17 - Fix 4-add-test-cases-for-openmp-optimization.patch for add new test cases for OpenMP optimization and modify the test method