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
'itsource' 카테고리의 다른 글
Android 애플리케이션의 빌드/버전 번호는 어떻게 얻을 수 있습니까? (0) | 2022.08.15 |
---|---|
visual studio 2017 스파 템플릿 vue js 누락 (0) | 2022.08.15 |
Linux 플랫폼 드라이버와 일반 디바이스 드라이버의 차이점은 무엇입니까? (0) | 2022.08.15 |
C의 함수 포인터에 대한 typedef 이해 (0) | 2022.08.15 |
스프링 부트:Embedded Servlet Container Factory 빈이 없어 Embedded Web Application Context를 시작할 수 없습니다. (0) | 2022.08.15 |