itsource

gcc - O0은 여전히 "미사용" 코드를 최적화합니다.그것을 변경할 수 있는 컴파일 플래그가 있습니까?

mycopycode 2022. 8. 15. 21:36
반응형

gcc - O0은 여전히 "미사용" 코드를 최적화합니다.그것을 변경할 수 있는 컴파일 플래그가 있습니까?

이 질문에서 언급했듯이 gcc는 삭제되고 있습니다(네,-O0의 코드 행 )는, 「」를 참조해 주세요._mm_div_ss(s1, s2);아마 결과가 저장되지 않았기 때문일 것입니다.단, 로 인해 부동소수점 예외가 트리거되어 SIGFPE가 상승합니다.이것은 콜이 삭제되면 발생할 수 없습니다.

질문:코드를 그대로 컴파일하기 위해 gcc에 전달하는 플래그가 하나 또는 여러 개 있습니까?생각 중인데...fno-remove-unused가 없는 되지 않는 사용하는?이상적으로는 소스 코드를 변경할 필요가 없는 컴파일러 플래그입니다만, 지원되지 않는 경우 대신 사용할 gcc 속성/pragma가 있습니까?

내가 시도한 것:

$ gcc --help=optimizers | grep -i remove

결과가 없습니다.

$ gcc --help=optimizers | grep -i unused

결과가 없습니다.

또한 모든 데드 코드/제거 플래그를 명시적으로 사용 불가능으로 설정합니다. 사용하지 않는 코드에 대한 경고는 없습니다.

$ gcc -O0 -msse2 -Wall -Wextra -pedantic -Winline \
     -fno-dce -fno-dse -fno-tree-dce \
     -fno-tree-dse -fno-tree-fre -fno-compare-elim -fno-gcse  \
     -fno-gcse-after-reload -fno-gcse-las -fno-rerun-cse-after-loop \
     -fno-tree-builtin-call-dce -fno-tree-cselim a.c
a.c: In function ‘main’:
a.c:25:5: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
     __m128 s1, s2;
     ^
$

소스 프로그램

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <xmmintrin.h>

static void sigaction_sfpe(int signal, siginfo_t *si, void *arg)
{
    printf("%d,%d,%d\n", signal, si!=NULL?1:0, arg!=NULL?1:0);
    printf("inside SIGFPE handler\nexit now.\n");
    exit(1);
}

int main()
{
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sigaction_sfpe;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGFPE, &sa, NULL);

    _mm_setcsr(0x00001D80);

    __m128 s1, s2;
    s1 = _mm_set_ps(1.0, 1.0, 1.0, 1.0);
    s2 = _mm_set_ps(0.0, 0.0, 0.0, 0.0);
    _mm_div_ss(s1, s2);

    printf("done (no error).\n");

    return 0;
}

위의 프로그램을 컴파일하면

$ ./a.out
done (no error).

회선 변경

_mm_div_ss(s1, s2);

로.

s2 = _mm_div_ss(s1, s2); // add "s2 = "

그럼 예상되는 결과가 생성됩니다.

$ ./a.out
inside SIGFPE handler

자세한 내용을 편집하십시오.

은 '아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아.__always_inline___mm_div_ss 정의.

$ cat t.c
int
div(int b)
{
    return 1/b;
}

int main()
{
    div(0);
    return 0;
}


$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$  

(경고나 오류 없음)

$ ./t.out
Floating point exception
$

vs 이하(함수 속성 이외에는 동일)

$ cat t.c
__inline int __attribute__((__always_inline__))
div(int b)
{
    return 1/b;
}

int main()
{
    div(0);
    return 0;
}

$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$   

(경고나 오류 없음)

$ ./t.out
$

속성 " " " 추가__warn_unused_result__: at도도도도 at at at at at at at at at at at at at at at at at at at at at 。

$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
t.c: In function ‘main’:
t.c:9:5: warning: ignoring return value of ‘div’, declared with attribute warn_unused_result [-Wunused-result]
     div(0);
     ^

편집:

gcc 메일링 리스트에 대한 논의입니다.결국, 모든 것이 의도한 대로 작동하고 있다고 생각합니다.

gcc가 지정된 명령을 내보내지 않는 이유는 무엇입니까?

컴파일러는 표준에서 지정한 관찰 가능한 동작을 가져야 하는 코드를 생성합니다.관찰할 수 없는 것은 프로그램의 동작(지정된 대로)을 변경하지 않으므로 원하는 대로 변경할 수 있습니다.

어떻게 그것을 두들겨서 복종시킬 수 있는가?

요령은 컴파일러가 특정 코드의 동작을 실제로 관찰할 수 있다고 믿게 하는 것입니다.

이는 마이크로벤치마크에서 자주 발생하는 문제이므로 Google벤치마크가 이를 어떻게 해결하는지 살펴보는 것이 좋습니다.입수처:

template <class Tp>
inline void DoNotOptimize(Tp const& value) {
    asm volatile("" : : "g"(value) : "memory");
}

구문의 자세한 내용은 지루합니다.이것을 알기 위해서, 다음의 내용만을 알면 됩니다.

  • "g"(value) 알리다value스테이트먼트에 대한 입력으로 사용됩니다.
  • "memory" 시 입니다.

따라서 코드를 다음과 같이 변경할 수 있습니다.

asm volatile("" : : : "memory");

__m128 result = _mm_div_ss(s1, s2);

asm volatile("" : : "g"(result) : );

어떤 점:

  • 컴파일러가 검토하도록 강요하다s1그리고.s2초기화와 사용 사이에 변경되었을 수 있습니다.
  • 컴파일러가 조작의 결과가 사용되는 것을 고려하도록 합니다.

플래그는 불필요하며, 어떤 최적화 레벨에서도 동작합니다(https://gcc.godbolt.org/의 -O3에서 테스트했습니다).

GCC는 여기서 아무것도 "최적화"하지 않습니다.쓸모없는 코드를 생성하지 않을 뿐입니다.컴파일러가 생성해야 하는 순수한 형태의 코드가 있으며, 그 변경은 "최적화"라는 매우 일반적인 착각으로 보입니다.그런 건 없어요.

컴파일러는 코드가 의미하는 바를 나타내는 데이터 구조를 만들고, 그 데이터 구조에 변환을 적용하여 어셈블러를 생성한 후 명령으로 컴파일합니다."최적화" 없이 컴파일러를 실행하면 컴파일러가 코드 생성에 필요한 최소한의 노력만 할 수 있습니다.

이 경우 스테이트먼트 전체가 아무 것도 하지 않고 바로 폐기되므로(인라인과 빌트인의 의미를 확장한 후) 사용할 수 없습니다.a/b;, 다른 점은 글쓰기가a/b;에 대해 경고를 발하다statement with no effect한편, 빌트인은 같은 경고로 처리되지 않을 수 있습니다).이것은 최적화가 아닙니다.컴파일러는 실제로 의미 없는 스테이트먼트에 의미를 부여하기 위해 여분의 노력을 들인 다음 이 스테이트먼트의 결과를 저장하기 위해 임시 변수를 조작해야 합니다.

최적화를 무효로 하는 플래그가 아니라 비관적인 플래그를 찾고 있습니다.컴파일러 개발자는 이러한 플래그를 구현하는 데 시간을 낭비하지 않는다고 생각합니다.만우절 농담 말고는 말이야

나는 에 대해 전문가가 아니다.gcc내부적인 문제이지만, 최적화 패스에 의한 데드 코드 삭제가 문제가 아닌 것 같습니다.컴파일러는 이 코드를 생성할 생각도 하지 않을 가능성이 높습니다.

이 예제를 컴파일러 고유의 기능에서 단순한 오래된 추가 기능으로 줄여 보겠습니다.

int foo(int num) {
    num + 77;
    return num + 15;
}

생성된 코드 없음:

foo(int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     eax, DWORD PTR [rbp-4]
        add     eax, 15
        pop     rbp
        ret

오퍼랜드 중 하나에 부작용이 있는 경우 해당 오퍼랜드만 평가됩니다.그래도 어셈블리에 추가는 없습니다.

그러나 이 결과를 (사용하지 않는) 변수에 저장하면 컴파일러는 추가할 코드를 생성해야 합니다.

int foo(int num) {
  int baz = num + 77;
  return num + 15;
}

어셈블리:

foo(int):
    push    rbp
    mov     rbp, rsp
    mov     DWORD PTR [rbp-20], edi
    mov     eax, DWORD PTR [rbp-20]
    add     eax, 77
    mov     DWORD PTR [rbp-4], eax
    mov     eax, DWORD PTR [rbp-20]
    add     eax, 15
    pop     rbp
    ret

다음은 추측일 뿐이지만, 컴파일러 구축에 대한 제 경험상 나중에 이 코드를 삭제하는 것보다 사용하지 않는 표현에 대한 코드를 생성하지 않는 것이 더 자연스럽습니다.

내가 추천하는 것은 당신의 의도를 분명히 하고, 표현의 결과를 에 넣는 것이다.volatile 변수(따라서 옵티마이저에 의해 비휘발성)입니다.

@Matthieu M은 값을 미리 계산하는 것을 막는 것은 충분하지 않다고 지적했다.신호를 것 의 것을 된 방법을 아마도, "이러한 방법").volatile인라인 어셈블리).

언급URL : https://stackoverflow.com/questions/39832958/gcc-o0-still-optimizes-out-unused-code-is-there-a-compile-flag-to-change-tha

반응형