2014-01-19 5 views
18

엄격한 앨리어싱 위반에 대한 내 코드를 확인하려고하는데, 은 엄격한 앨리어싱 규칙을 이해하려고 시도하는 동안 무언가를 놓친 것처럼 보입니다.GCC : 엄격한 앨리어싱 경고의 정확성

#include <stdio.h> 

int main(void) 
{ 
    unsigned long l; 

    l = 0; 

    *((unsigned short *)&l) = 1; 

    printf("%lu\n", l); 

    return 0; 
} 

클래식 및 기본 예제 :

다음 코드를 상상해보십시오. GCC 4.9 (-Wall -fstrict-aliasing -Wstrict-aliasing -O3)로, 실제로는 오류보고 :

dereferencing type-punned pointer will break strict-aliasing rules 

을하지만 다음 잘 컴파일 :

나의 이해에서
#include <stdio.h> 

int main(void) 
{ 
    unsigned long l; 
    unsigned short * sp; 

    l  = 0; 
    sp  = (unsigned short *)&l; 
    *(sp) = 1; 

    printf("%lu\n", l); 

    return 0; 
} 

는 두 번째 예는 구조체 앨리어싱 규칙을 위반하는 것입니다.
그럼 왜 컴파일됩니까? GCC의 정확성 문제입니까, 아니면 엄격한 앨리어싱이 적용된 을 놓쳤습니까? Why are no strict-aliasing warnings generated for this code?

-Wstrict-aliasing=2 또는 -Wstrict-aliasing=3는 차이가 없습니다로 컴파일 :

은 또한 다음과 같은 항목을 발견했다.
그러나 -Wstrict-aliasing=1은 두 번째 예에서 오류를보고합니다.

GCC 문서는 그래서 여기에 무슨 일이 일어나고 있는지

... 레벨 3이 가장 정확한 동안 레벨 1은 정확하고 가장, 그리고 잘못된 반응을 많이 을 생산할 수 있다고? 내 자신의 이해에 문제가 있거나 GCC에 문제가 있습니까?

보너스 질문

난 보통 내 프로젝트에 대한 GCC를 통해 연타/LLVM을 선호하지만 연타 엄격한 앨리어싱에 대한 경고를하지 않는 것 같다.
이유를 아는 사람이 있습니까?
위반을 감지 할 수 없기 때문에 또는 코드를 생성 할 때 규칙을 따르지 않아서입니까?

+0

컴파일러는 두 번째 예제에서 'l'의 값이 일정하다고 가정 할 수 있기 때문에 문제가 발생합니다. – Macmade

+2

에일리어싱 위반이있는 것은 코드 자체를 실행하지 않고도 상황에 따라 탐지하기 어렵다는 것입니다.이것이 제약 조건 위반 (모든 컴파일러에서 감지해야하는 오류)이 아닌 "just"동작이 정의되지 않은 이유입니다.이 문제에 대해 보증 할 수있는 최적화 컴파일러를 작성하는 것이 악몽이기 때문입니다. 그러한 포인터 캐스트로 프로그램하는 유효한 이유는 거의 없으며 중간 '유니온 (union)'을 사용하거나 'unsinged char *'에 캐스트하지 않는 것입니다. –

+1

여기서 -Wstrict-aliasing = 1은 "may", -Wstrict-aliasing = 2는 경고를 "will"으로, -Wstrict-aliasing = 3은 경고를주지 않습니다. – AProgrammer

답변

7

귀하의 이해가 정확합니다. 별칭 분석은 일반적으로 복잡하며이 경우 명백하게 캐스트와 참조 해제 사이에 임시 포인터를 사용하는 것으로 충분합니다. 놀랍게도 GCC 4.8.2는이 코드에서 더 좋은 작업을 수행하며 레벨 1과 마찬가지로 -Wstrict-aliasing=2으로 경고합니다. 따라서 회귀입니다.

clang의 경우 현재 별칭 위반에 대해 경고하는 기능이 없습니다. 그것은 최적화에서 절대적으로 규칙을 이용합니다. 동일한 구조체에 P1 및 P2 지점, 연타 (및 GCC)는 그럼에도 불구하고 값을 리턴하는 경우 예보기 위해서는 직선 C 표준 (N1570 §6.5.2.3 9))

struct t1 { int m; }; 
struct t2 { int m; }; 

int f(struct t1 *p1, struct t2 *p2) { 
    if (p1->m < 0) 
     p2->m = -p2->m; 
    return p1->m; 
} 

에서이 예를 들자면 왜냐하면 p2가 p1의 별칭이 아니므로 이전 부정이 결코 결과에 영향을 미치지 않기 때문입니다. Here's the full example이고 출력은 -fstrict-aliasing입니다. 더 많은 예제를 보려면 here 및 인용 된 What Every C Programmer Should Know About Undefined Behavior; 엄격한 앨리어스 최적화가 입문 게시물의 마지막 주제입니다.

These flags are currently unimplemented; test that we output them anyway.

경고가 the devs are quiet, 구현 될 것입니다,하지만 그들은 제목 (강조 광산)에서 -Wstrict-aliasing=X을 나열 in clang's test suite을 언급하는 경우에 대해서는

은 그래서 어떤 시점에서 일어날 것으로 보인다.

+0

Clang에 대한 메모 주셔서 감사합니다. 어떻게 아십니까? – Macmade

+0

@Macmade는 몇 가지 세부 사항과 참조를 추가했습니다. – tab

+1

+1. 그러나 명확히하기 위해 아마도 "p1-> m"을 "p1-> M"의 초기 값을 되 돌리는 것과 같은 문구로 바꿀 것인가? –

2

O3에서 컴파일러는 을 알기 때문에 다른 유형의 변수가 별칭이 될 수 없다는 것을 알기 때문에, sp은 사용되지 않습니다. 따라서 프로그램에서 sp을 제거하고 오류가 생성되지 않습니다.

l은 알려진 값이므로 printf에서 사용하기 위해 상수로 변환됩니다. 주소가 저장 되었기 때문에 저장소가 남아 있지만 해당 저장소는 아는 한 멀리 쓰지 않습니다. 모든 컴파일러

sp  = (unsigned short *)&l; 

본질적으로 아무 문제가 없습니다

+0

고마워, 그리고 아주 좋은 추측 btw. 하지만 유감스럽게도'sp'를 사용해 보았는데 아직 경고가 없습니다. – Macmade

+0

@Macmade : 그 이유는 l이 알려진 값이고 상수로 변환되어 프로그램에서 제거 되었기 때문입니다. –

+0

음, '휘발성'으로 선언하면 불행히도 아무 것도 바뀌지 않습니다.하지만 저는 @Mehrdad가 올바르게 맞춰 졌다고 생각합니다. 답변을 주셔서 감사합니다.) – Macmade

6

은 단순히 다시 올바른 유형에 포인터를 다시 캐스팅 수도 알고있다.

그것은 잘못 두 가지의 조합이다

*(sp) = 1; 

본질적으로 아무 문제도 없다.

컴파일러는 두 가지를 조합하여 문제가되는 (일반적으로 불가능하고 특별한 경우에는 그렇게 할 수 없음) 컴파일러에게 알려줄 수 없습니다.

그러나

*((unsigned short *)&l) = 1; 

감지 훨씬 쉽게, 따라서 컴파일러는 그렇게하도록 최선을 다하고 있습니다. 상황의

+0

감사합니다. 두 번째 예제는 여전히 규칙을 위반하고 있습니다. 감지되지 않으면? – Macmade

+0

@Macmade : 예, 새로운 변수를 도입해도 갑자기 OK가되지 않습니다. – Mehrdad

+0

그게 내가 생각하고, 감사합니다 :) – Macmade

2

내 해석 : 위반 한 줄에 있기 때문에

  1. *((unsigned short *)&l) = 1;는 잡기 쉽습니다. 그래서 항상 붙잡 혔습니다.

  2. 최적화를 사용하면 두 번째 변형도 포착됩니다. 최적화가 끝난 후에는 첫 번째만큼 간단합니다.

  3. 최적화하지 않으면 기본값보다 lazier 인 -Wstrict-aliasing=1이 캐치합니다. 이것은 디비전이 없어도 캐스트 자체를 위반으로 간주하기 때문입니다 (비속 참조를 삭제하고 볼 수 있음). 이 단순 논리는 빠르지 만 오탐 (false positive)을 유발합니다.

관련 문제