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

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/ 에보고되어야합니다.



답변

여기에 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로 확장 할 때 왜 패딩을 피할 수 있는지 설명합니다 .


답변

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

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

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

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


답변

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

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


답변