Sedikit membawa, kendala GAS

Saya sedang menulis tambahan panjang perakitan di perakitan inline GAS,

template <std::size_t NumBits>
void inline KA_add(vli<NumBits> & x, vli<NumBits> const& y);

Jika saya berspesialisasi, saya dapat melakukan:

template <>
void inline KA_add<128>(vli<128> & x, vli<128> const& y){
     asm("addq  %2, %0; adcq  %3, %1;" :"+r"(x[0]),"+r"(x[1]):"g"(y[0]),"g"(y[1]):"cc");
}

Bagus itu berfungsi, sekarang jika saya mencoba menggeneralisasi untuk mengizinkan sebaris templat, dan membiarkan kompiler saya bekerja untuk jangka waktu berapa pun ...

template <std::size_t NumBits>
void inline KA_add(vli<NumBits> & x, vli<NumBits> const& y){
    asm("addq  %1, %0;" :"+r"(x[0]):"g"(y[0]):"cc");
    for(int i(1); i < vli<NumBits>::numwords;++i)
        asm("adcq  %1, %0;" :"+r"(x[i]):"g"(y[i]):"cc");
};

Ya, itu tidak berhasil. Saya tidak punya jaminan bahwa carry bit (CB) disebarkan. Ini tidak menghemat antara garis asm pertama dan yang kedua. Ini mungkin logis karena loop menambah i dan "menghapus" hal CB I, harus ada batasan GAS untuk melestarikan CB selama dua panggilan ASM. Sayangnya saya tidak menemukan informasi seperti itu.

Ada ide ?

Terima kasih, Merci!

PS Saya menulis ulang fungsi saya untuk menghilangkan ideologi C++

template <std::size_t NumBits>
inline void KA_add_test(boost::uint64_t* x, boost::uint64_t const* y){
    asm ("addq  %1, %0;" :"+r"(x[0]):"g"(y[0]):"cc");
        for(int i(1); i < vli<NumBits>::numwords;++i)
            asm ("adcq  %1, %0;" :"+r"(x[i]):"g"(y[i]):"cc");
};

Asm memberi (mode Debug GCC),

APLIKASI

    addq  %rdx, %rax; 

TIDAK_APLIKASI

    movq    -24(%rbp), %rdx
    movq    %rax, (%rdx)

.LBB94: .loc 9 55 0

    movl    $1, -4(%rbp)
    jmp     .L323 

.L324:

    .loc 9 56 0

    movl    -4(%rbp), %eax
    cltq  
    salq    $3, %rax
    movq    %rax, %rdx
    addq    -24(%rbp), %rdx <----------------- Break the carry bit
    movl    -4(%rbp), %eax
    cltq  
    salq    $3, %rax
    addq    -32(%rbp), %rax
    movq    (%rax), %rcx
    movq    (%rdx), %rax

APLIKASI

    adcq  %rcx, %rax; 

TIDAK_APLIKASI

Seperti yang bisa kita baca, ada tambahan tambahan yang merusak propagasi CB


person Timocafé    schedule 30.11.2012    source sumber
comment
Mungkin akan membantu jika Anda menunjukkan kepada kami keluaran asm yang dikompilasi dari fungsi KA_add Anda, karena kemungkinan ada sesuatu di antara baris asm Anda yang mengubah bit carry atau semacamnya.   -  person Tony The Lion    schedule 30.11.2012
comment
Apakah perulangan dalam kode assembler dapat diterima atau apakah Anda benar-benar terus membuka gulungannya (jika Anda setuju dengan hanya membuka gulungan hingga jumlah iterasi yang tetap, [pustaka praprosesor boost](www.boost.org/libs/preprocessor/) mungkin bisa digunakan)?   -  person user786653    schedule 03.12.2012
comment
Apakah bit Carry Flag (CF) disetel, kapan carry diperlukan untuk addq Anda?   -  person Tony The Lion    schedule 03.12.2012
comment
Saat ini, saya memecahkan pb dengan solusi boost_pp, ini berfungsi tetapi sulit untuk membaca/men-debug/mempertahankan sebagai komentar (user786653). Tidak Tony, penambahan ini disetel ulang ke 0 yang diperoleh CB oleh ASM inline pertama. Jadi adcq saya menjadi tidak berguna, sayang sekali -_-   -  person Timocafé    schedule 03.12.2012


Jawaban (1)


Saya tidak melihat cara untuk secara eksplisit memberi tahu kompiler bahwa kode loop harus dibuat tanpa instruksi yang mempengaruhi flag C.

Hal ini tentu mungkin dilakukan - gunakan lea untuk menghitung alamat array ke atas, dec untuk menghitung loop ke bawah dan uji Z untuk kondisi akhir. Dengan begitu, tidak ada apa pun dalam loop kecuali jumlah array aktual yang mengubah tanda C.

Anda harus melakukan hal manual, seperti:

long long tmp; // hold a register

__asm__("0:
    movq (%1), %0
    lea 8(%1), %1
    adcq  %0, (%2)
    lea 8(%2), %2
    dec %3
    jnz 0b"
    : "=r"(tmp)
    : "m"(&x[0]), "m"(&y[0]), "r"(vli<NumBits>::numwords)
    : "cc", "memory");

Untuk kode panas, loop ketat tidaklah optimal; pertama, instruksi memiliki ketergantungan, dan ada lebih banyak instruksi per iterasi daripada urutan adc yang disisipkan/dibuka. Urutan yang lebih baik adalah seperti (%rbp resp. %rsi memiliki alamat awal untuk array sumber dan target):

0:

lea  64(%rbp), %r13
lea  64(%rsi), %r14
movq   (%rbp), %rax
movq  8(%rbp), %rdx
adcq   (%rsi), %rax
movq 16(%rbp), %rcx
adcq  8(%rsi), %rdx
movq 24(%rbp), %r8
adcq 16(%rsi), %rcx
movq 32(%rbp), %r9
adcq 24(%rsi), %r8
movq 40(%rbp), %r10
adcq 32(%rsi), %r9
movq 48(%rbp), %r11
adcq 40(%rsi), %r10
movq 56(%rbp), %r12
adcq 48(%rsi), %r10
movq %rax,   (%rsi)
adcq 56(%rsi), %r10
movq %rdx,  8(%rsi)
movq %rcx, 16(%rsi)
movq %r8,  24(%rsi)
movq %r13, %rbp     // next src
movq %r9,  32(%rsi)
movq %r10, 40(%rsi)
movq %r11, 48(%rsi)
movq %r12, 56(%rsi)
movq %r14, %rsi     // next tgt
dec  %edi           // use counter % 8 (doing 8 words / iteration)
jnz 0b              // loop again if not yet zero 

dan hanya berputar di sekitar blok tersebut. Keuntungannya adalah beban diblokir, dan Anda hanya akan menangani jumlah loop/kondisi penghentian satu kali saja.

Sejujurnya, saya akan mencoba untuk tidak membuat lebar bit umum menjadi "rapi", melainkan kode khusus yang secara eksplisit membuka gulungannya untuk, katakanlah, lebar bit pangkat dua. Daripada menambahkan pesan tanda/konstruktor ke contoh template yang tidak dioptimalkan yang memberi tahu pengguna "gunakan kekuatan dua"?

person FrankH.    schedule 07.12.2012
comment
Bukankah lebih mudah untuk menyimpan register flag di awal fungsi dan memulihkannya sebelum kembali? PUSHF/POPF dan SAHF/LAHF dapat melakukan hal itu. - person Daniel Kamil Kozar; 07.12.2012
comment
Benar - namun keduanya berarti lebih banyak akses memori; dilakukan sekali per iterasi yang ketat, itu mahal. Menggunakan lea / inc / dec Anda dapat menghindarinya. - person FrankH.; 08.12.2012