함수에서 구조체를 반환 할 때 가능한 GCC 버그


133

O'Neill의 PCG PRNG를 구현하는 동안 GCC에서 버그를 발견했다고 생각합니다. ( Godbolt의 컴파일러 탐색기의 초기 코드 )

승산 후, oldstateMULTIPLIER(RDI에 저장된 결과), GCC는 해당 결과를 추가하지 않는다 INCREMENTmovabs'ing, INCREMENT다음 rand32_ret.state의 반환 값으로서 사용 도착하는 대신 RDX 할

최소한의 재현 가능한 예 ( Compiler Explorer ) :

#include <stdint.h>

struct retstruct {
    uint32_t a;
    uint64_t b;
};

struct retstruct fn(uint64_t input)
{
    struct retstruct ret;

    ret.a = 0;
    ret.b = input * 11111111111 + 111111111111;

    return ret;
}

생성 된 어셈블리 (GCC 9.2, x86_64, -O3) :

fn:
  movabs rdx, 11111111111     # multiplier constant (doesn't fit in imm32)
  xor eax, eax                # ret.a = 0
  imul rdi, rdx
  movabs rdx, 111111111111    # add constant; one more 1 than multiplier
     # missing   add rdx, rdi   # ret.b=... that we get with clang or older gcc
  ret
# returns RDX:RAX = constant 111111111111 : 0
# independent of input RDI, and not using the imul result it just computed

흥미롭게도 두 멤버를 uint64_t로 변경하는 것처럼 첫 번째 멤버로 uint64_t를 갖도록 구조체를 수정 하면 올바른 코드가 생성 됩니다.

x86-64 System V는 사소한 복사가 가능한 RDX : RAX에서 16 바이트보다 작은 구조체를 반환합니다. 이 경우 RAX의 절반이 정렬을위한 패딩이거나 좁은 유형일 .b때 두 번째 멤버가 RDX에 .a있습니다. ( 어쨌든 sizeof(retstruct)16입니다; 우리는 사용하지 않으므로 __attribute__((packed))alignof (uint64_t) = 8을 존중합니다.)

이 코드에 GCC가 "잘못된"어셈블리를 생성 할 수있는 정의되지 않은 동작이 포함되어 있습니까?

그렇지 않은 경우 https://gcc.gnu.org/bugzilla/ 에보고되어야합니다.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Samuel Liew

답변:


102

여기에 UB가 보이지 않습니다. 유형이 부호가 없으므로 부호가있는 UB는 불가능하며 이상한 것은 없습니다. (그리고 서명하더라도 UB와 같은 오버플로 UB를 유발 하지 않는 입력에 대해 올바른 출력을 생성해야합니다 rdi=1). GCC의 C ++ 프론트 엔드에서도 깨졌습니다.

또한 GCC8.2 는 AArch64 및 RISC-V에 대해 올바르게 컴파일합니다 ( 상수를 생성 madd한 후의 명령어 movk또는 RISC-V mul을 사용하여 상수를로드 한 후 추가). GCC가 찾은 것이 UB라면, 일반적으로 비슷한 너비와 레지스터 너비를 가진 ISA를 찾아서 다른 ISA에 대한 코드를 깨뜨릴 것으로 기대합니다.

Clang도 올바르게 컴파일합니다.

이것은 GCC 5에서 6으로의 회귀 인 것으로 보입니다. GCC5.4 컴파일은 6.1 이상에서는 정확하지 않습니다. ( 고드 볼트 ).

질문에서 MCVE를 사용하여 GCC의 버그질라 에이를보고 할 수 있습니다 .

패딩을 포함하는 구조체의 x86-64 System V struct-return 처리에서 버그 인 것 같습니다. 그것은 인라인 할 때와 auint64_t로 확장 할 때 왜 패딩을 피할 수 있는지 설명합니다 .



11
@vitorhnn에 고정 된 것 같습니다 master.
SS Anne

19

이것은 trunk/ 에 수정되었습니다 master.

관련 커밋 은 다음과 같습니다 .

그리고 이것은 문제를 해결하기 위한 패치 입니다.

패치의 주석을 기반으로 reload_combine_recognize_pattern함수가 USE insns 를 조정하려고했습니다 .


14

이 코드에 GCC가 "잘못된"어셈블리를 생성 할 수있는 정의되지 않은 동작이 포함되어 있습니까?

질문에 제시된 코드의 동작은 C99 이상의 C 언어 표준과 관련하여 잘 정의되어 있습니다. 특히 C는 함수가 제한없이 구조 값을 반환하도록 허용합니다.


2
GCC는 함수의 독립형 정의를 생성합니다. 다른 함수와 함께 번역 단위로 컴파일 할 때 실행되는지 여부에 관계없이 우리 가보고있는 것입니다. 실제로 __attribute__((noinline))번역 단위로 컴파일하고 LTO없이 연결하거나 -fPIC모든 전역 기호가 (기본적으로) 인터 포저 블 가능하므로 호출자에게 인라인 될 수 없음을 의미 하는 컴파일을 통해 실제로 사용하지 않고도 쉽게 테스트 할 수 있습니다. 그러나 실제로 문제는 발신자에 관계없이 생성 된 asm을 보면 감지 할 수 있습니다.
Peter Cordes

@PeterCordes는 충분히 공정하지만, Godbolt에서 그 세부 사항이 나에게서 변경되었다고 확신합니다.
John Bollinger

질문의 버전 1은 답변 할 때 질문 자체의 상태와 같이 번역 단위의 기능만으로 Godbolt에 연결되었습니다. 나는 당신이 볼 수있는 모든 개정 사항이나 의견을 확인하지 않았습니다. 다리 아래의 물이지만 소스를 사용할 때 독립형 asm 정의가 깨 졌다는 주장은 없다고 생각 __attribute__((noinline))합니다. (이것은 GCC 정확성 버그가 놀라운 방식이 아니라 충격적 일 것입니다). 아마도 결과를 인쇄하는 테스트 호출자를 만드는 것에 대해서만 언급되었을 것입니다.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.