Pointer Aliasing and Strict Aliasing Rule

October 27, 2019
C/C++

Pointer Aliasing

在 C++ 中,多個 pointeres 指向同個記憶體位置,我們稱為 Pointer Aliasing

  • 此時兩個 pointer 的權限都一樣,可以修改甚至刪除空間
  • 但是若其中一個 pointer 被 delete 後,可能會造成其他 pointers 成為 Dangling Pointer
  • 因為 aliasing 的錯誤使用很可能無法在 compile time 發現,可能會造成輸出的結果錯誤
  • ex., undefine behavior,像是用 double pointer 去 deference 一個 int
    int *pA = new int(4);   // new a space
    int *pB = pA;           // Both of them store same address
    
    *pB = 10;               // Alias pointer can also make modification
    cout << *pA << endl;    // 10
    delete pA;
    
    cout << *pB << endl;    // xx

Dangling Pointer

    int *pA = nullptr;
    {
    	int n = 20;
    	pA = &n;
    	cout << *pA << endl;   // 20
    }
    
    // pA becomes a dangling pointer
    cout << *pA << endl;       // xx

Example

一個說明 aliasing 如何影響 compiler 優化的例子:

    // Case 1 
    void foo(int *a, int *b, int *res) {
    	for (int i = 0; i < 10; i++) {
    		*res += *a + *b;
    	}
    }
    
    // Case 2
    // Compiler tends to do so..
    // to load *a and *b only once instead of each iteration
    void foo(int *a, int *b, int *res) {
    	int temp = *a + *b;
    	for (int i = 0; i < 10; i++) {
    		*res += temp;
    	}
    }
    
    // 事實上,Compiler 會預設可能有 pointer aliasing,所以不會執行這種優化
    // 因為下面這個例子,在每個 iteration 都會改變 val 的值,所以無法提到迴圈外
    foo(&val, &val, &val);

    // Case 1 
    void foo(int *a, int *b, int *res) {
    	for (int i = 0; i < 10; i++) {
    		*res += *a + *b;
    	}
    }
    
    // Case 2
    // Compiler tends to do so..
    // to load *a and *b only once instead of each iteration
    void foo(int *a, int *b, int *res) {
    	int temp = *a + *b;
    	for (int i = 0; i < 10; i++) {
    		*res += temp;
    	}
    }
    
    // However, if we called, results of case 1 and 2 differs.
    foo(&val, &val, &val);
    
    // 因為 a, b, res 都是 aliasing, 所以每個 iteration 後,值都會改變
  • restrict 可以用來表示某個 pointer 沒有 aliasing,由 programmer 自行決定
  • Strick Aliasing 並無法改變 foo() 能否套用優化

Strict Aliasing Rules

strict aliasing rule 有一些假定且預設 programmer 會遵守,compiler 才可以套用更多優化

  • -O2 之後的 compilation level 預設執行 strict aliasing rule
  • 這也是為什麼有時候 -O1-O2 的執行結果有可能不同 (ex., )
    // cast result differs in different levels of compilation
    double d = 3.4;
    unsigned int *p = (unsigned int *)(&d);
  • CFLAGS=-fno-strict-aliasing 可以取消 strict aliasing rule
  • 不允許建立與原先型別不同的 alias,ex., 用 double pointer 去存取 int

Dereferencing a cast of a variable from one type of pointer to a different type is usually in violation of the strict aliasing rule.

Assumptions that Strict Aliasing is enabled

  • Compiler 會假定不同型別的 int *foodouble *bar 一定不會 refer 到同個位址
  • 如此 programmer 就要負責避免可能產生不同型別的 alias (ex., 亂作 casing)
  • 這個設計主要的目的是擴展優化在在某些情境下可以執行,而不是消極認定 aliasing 的可能性
    void foo(int *a, int *b, long *res) {
    	int temp = *a + *b;
    	for (int i = 0; i < 10; i++) {
    		*res += temp;
    	}
    }
    
    // strict aliasing warning
    // long *res can't be alias of int* a, *b
    foo(&val, &val, &val);  

References

Pointers in C, Part III: The Strict Aliasing Rule - Approxion

What is Strict Aliasing and Why do we Care?

Understanding Strict Aliasing

Strict Aliasing

comments powered by Disqus