Strict Aliasing
Table of Contents
1. Strict Aliasing
aliasing 是指编译器总是假设两个指针可能指向同一块内存, 导致无法进行某些优化.
strict aliasing 是指编译器放松一下前面的假设, 认为某些条件下两个指令不会指向同一块内存, 从而能进行优化.
通过 `-O2` 或 `-fstrict-aliasing` 可以打开 strict aliasing. 如果代码不符合 strict aliasing rule, 则会出现一些问题
#include <stdio.h> #include <stdlib.h> #include <string.h> /* no alias */ /* strict alias rule 认为 int 和 short 类型的指令 =不会= 指向同一块内存 */ void test_1(int *a, short *b) { *a = 0x12345678; b[0] = 0xabcd; printf("0x%x\n", *a); } /* no alias */ /* __restrict 告诉编译器 a 不是 alias */ void test_4(int *__restrict a, int *b) { *a = 0x12345678; b[0] = 0xabcd; printf("0x%x\n", *a); } /* may alias */ /* 相同的数据类型 (int/unsiged int/const int) 之间是 alias */ void test_2(int *a, unsigned int *b) { *a = 0x12345678; b[0] = 0xabcd; printf("0x%x\n", *a); } /* may alias */ /* char 类型总是其它任意类型的 alias */ void test_3(int *a, char *b) { *a = 0x12345678; b[0] = 0xa; printf("0x%x\n", *a); } /* may alias */ /* short_may_alias 相当于 char, 它总是其它类型的 alias */ typedef short __attribute__((__may_alias__)) short_may_alias; void test_5(int *a, short_may_alias *b) { *a = 0x12345678; b[0] = 0xabcd; printf("0x%x\n", *a); } int main(int argc, char *argv[]) { int a = 0; test_1(&a, (short *)&a); test_4(&a, &a); test_2(&a, (unsigned int *)&a); test_3(&a, (char *)&a); test_5(&a, (short *)&a); return 0; }
0x12345678 0x12345678 0xabcd 0x1234560a 0x1234abcd
为了避免 strict aliasing 导致问题, 可以:
- 用 `-fno-strict-alising` 关掉 strict aliasing
尽量不要使用指针的 reinterpret cast 造成 type punning, 而是使用 union 或通过 memcpy, 例如
void foo_bad(char* buffer) { int *p = *((int*)buffer); *p = 0x1; } void foo_good(char* buffer) { int tmp = 0x1; memcpy(buffer, &tmp, sizeof(tmp)); }
使用 memcpy/union 不仅能避免 strict aliasing 问题, 还能避免对齐的问题
- 如果实在需要 alias, 只使用 char 或 may_alias 类型
Backlinks
GCC Attribute (GCC Attribute > may_alias): Strict Aliasing
RISC-V Vector Extension
(RISC-V Vector Extension > gcc rvv auto vectorization): 代码使用了 __restrict
关键字, 告诉编译器不需要考虑 a 和 b 有重叠的情况, 参考 Strict Aliasing
slow_unaligned_access (mtune > riscv_tune_param > slow_unaligned_access): 或者通过 may_alias 的指明存在 type punning: