Bir işlevden yapı döndürürken olası GCC hatası


133

O'Neill'in PCG PRNG'sini uygularken GCC'de bir hata bulduğuma inanıyorum. ( Godbolt'un Derleyici Gezgini'nde başlangıç ​​kodu )

Çarparak sonra oldstategöre MULTIPLIER(RDI saklanan sonuç), GCC ile bu sonucu eklemez INCREMENTmovabs'ing, INCREMENTdaha sonra rand32_ret.state dönüş değeri olarak kullanılan alır, bunun yerine, RDX'e

Minimum tekrarlanabilir örnek ( Derleyici Gezgini ):

#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;
}

Oluşturulan montaj (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

İlk eleman olarak İlginçtir, yapı değiştirerek uint64_t olması doğru kodu üretir yaptığı gibi, uint64_t olmak hem üyelerini değişen

x86-64 System V, önemsiz bir şekilde kopyalanabildiklerinde RDX: RAX'ta 16 bayttan küçük yapıları döndürür. Bu durumda 2. üye RDX'tedir, çünkü RAX'in yüksek yarısı hizalama veya.b zaman .adaha dar bir türüdür. ( sizeof(retstruct)her iki durumda da 16'dır; kullanmıyoruz, __attribute__((packed))bu yüzden alignof'a saygı duyuyor (uint64_t) = 8.)

Bu kod, GCC'nin "yanlış" derleme vermesine izin verecek tanımlanmamış bir davranış içeriyor mu?

Değilse, bu https://gcc.gnu.org/bugzilla/ adresinde bildirilmelidir.


Yorumlar uzun tartışmalar için değildir; bu sohbet sohbete taşındı .
Samuel Liew

Yanıtlar:


102

Burada hiç UB görmüyorum; tipleriniz imzasızdır, bu nedenle imzalı taşma UB imkansızdır ve garip bir şey yoktur. (Ve imzalanmış olsa bile, taşma UB'ye neden olmayan girişler için doğru çıkışlar üretmek zorunda kalacaktı , örneğinrdi=1 ). GCC'nin C ++ ön ucu ile de kırıldı.

Ayrıca, GCC8.2 , AArch64 ve RISC-V için doğru şekilde derler ( sabitleri oluşturmak için maddkullandıktan sonra bir yönerge movkveya RISC-V mul ve sabitleri yükledikten sonra ekleme). GCC'nin bulduğu UB olsaydı, genellikle onu bulmasını ve diğer ISA'lar için de kodunuzu kırmasını, en azından benzer tip genişliklere ve kayıt genişliklerine sahip olmasını beklerdik.

Clang ayrıca doğru bir şekilde derler.

Bu GCC 5 ila 6 arasında bir gerileme gibi görünmektedir; GCC5.4 derlemeleri doğru, 6.1 ve sonrası değil. ( Godbolt) ).

Sorunuzdaki MCVE'yi kullanarak GCC'nin bugzilla'sında bunu bildirebilirsiniz .

Gerçekten x86-64 Sistem V yapı-dönüş işlemede, belki de dolgu içeren yapılarda bir hata gibi görünüyor. Bu neden satır içi ve auint64_t (dolgudan kaçınarak) genişletirken neden çalışır .



11
@vitorhnn Görünüşe göre düzeltildi master.
SS Anne


14

Bu kod, GCC'nin "yanlış" derleme vermesine izin verecek tanımlanmamış bir davranış içeriyor mu?

Soruda sunulan kodun davranışı, C99 ve daha sonraki C dil standartlarına göre iyi tanımlanmıştır. Özellikle C, fonksiyonların kısıtlama olmaksızın yapı değerlerini döndürmesine izin verir.


2
GCC, işlevin bağımsız bir tanımını üretir; diğer işlevlerle birlikte bir çeviri biriminde derlediğinizde çalışıp çalışmadığına bakılmaksızın, baktığımız şey budur. __attribute__((noinline))Bir çeviri biriminde tek başına derleyerek ve LTO olmadan bağlantı kurarak veya -fPICtüm global sembollerin (varsayılan olarak) araya getirilebileceği anlamına gelen derleme yaparak arayanlar için satır içine alınamayacak şekilde kolayca kullanmadan test edebilirsiniz. Ama gerçekten sorun arayanlar ne olursa olsun, üretilen asm'a bakmakla tespit edilebilir.
Peter Cordes

Yeterince adil, @PeterCordes, ancak bu detayın Godbolt'ta altımdan değiştirildiğinden oldukça eminim.
John Bollinger

Sorunun 1. sürümü Godbolt ile bağlantı kurarken sorunun kendisi gibi bir çeviri birimindeki işleviyle bağlantılıdır. Bakabileceğiniz tüm düzeltmeleri veya yorumları kontrol etmedim. Köprünün altındaki su, ancak tek başına asm tanımının sadece kaynak kullanıldığında bozulduğu iddiası olduğunu düşünmüyorum __attribute__((noinline)). (Bu bir GCC doğruluk hatasının nasıl olması şaşırtıcı değil, şok edici olurdu). Muhtemelen bu sadece sonucu yazdıran bir test arayan yapmak için bahsedildi.
Peter Cordes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.