แครี่บิต, ข้อ จำกัด ของ GAS

ฉันกำลังเขียนแอสเซมบลียาวเพิ่มเติมในแอสเซมบลีแบบอินไลน์ของ GAS

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

ถ้าฉันเชี่ยวชาญฉันก็ทำได้:

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");
}

เยี่ยมเลย ใช้งานได้ดี ตอนนี้หากฉันพยายามสรุปให้อนุญาตเทมเพลตแบบอินไลน์ และปล่อยให้คอมไพเลอร์ทำงานไม่ว่าจะยาวเท่าใด ...

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");
};

มันใช้งานไม่ได้ ฉันไม่รับประกันว่าจะมีการแพร่กระจายบิตพกพา (CB) ไม่มีการสงวนไว้ระหว่างบรรทัด asm แรกและบรรทัดที่สอง อาจเป็นตรรกะเนื่องจากการเพิ่มลูป i และ "ลบ" สิ่ง CB I ควรมีข้อจำกัด GAS เพื่ออนุรักษ์ CB ผ่านการเรียก ASM สองครั้ง ขออภัย ฉันไม่พบข้อมูลดังกล่าว

มีความคิดอะไรบ้าง?

ขอบคุณเมอร์ซี่!

ป.ล. ฉันเขียนฟังก์ชันของฉันใหม่เพื่อลบอุดมการณ์ 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 ให้ (โหมด GCC Debug)

แอป

    addq  %rdx, %rax; 

NO_APP

    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

แอป

    adcq  %rcx, %rax; 

NO_APP

ดังที่เราอ่านได้ว่ามีส่วนเสริมเพิ่มเติม ซึ่งจะทำลายการแพร่กระจายของ CB


person Timocafé    schedule 30.11.2012    source แหล่งที่มา
comment
มันน่าจะช่วยได้ถ้าคุณแสดงผลลัพธ์ asm ที่คอมไพล์แล้วของฟังก์ชัน KA_add ของคุณให้เราดู เพราะอาจมีบางอย่างที่อยู่ระหว่างบรรทัด asm ของคุณที่กำลังเปลี่ยนแครี่บิตหรืออะไรบางอย่าง   -  person Tony The Lion    schedule 30.11.2012
comment
การวนซ้ำในโค้ดแอสเซมเบลอร์จะยอมรับได้หรือคุณยังคงคลี่ออกต่อไป (หากคุณตกลงโดยคลายการวนซ้ำตามจำนวนคงที่เท่านั้น [boost preprocessor Library](www.boost.org/libs/preprocessor/) อาจเป็นไปได้ ถูกนำมาใช้)?   -  person user786653    schedule 03.12.2012
comment
บิต Carry Flag (CF) ถูกตั้งค่าไว้หรือไม่ เมื่อจำเป็นต้องมีการพกพาสำหรับ addq ของคุณ?   -  person Tony The Lion    schedule 03.12.2012
comment
ปัจจุบัน ฉันแก้ไข pb ด้วยโซลูชัน boost_pp ซึ่งใช้งานได้ แต่อ่าน/แก้ไขข้อบกพร่อง/บำรุงรักษาได้ยากตามหมายเหตุ (user786653) ไม่ใช่ Tony การเพิ่มนี้จะรีเซ็ตเป็น 0 ที่ CB ได้รับจาก ASM แบบอินไลน์ครั้งแรก ดังนั้น adcq ของฉันจึงไร้ประโยชน์ แย่มาก -_-   -  person Timocafé    schedule 03.12.2012


คำตอบ (1)


ฉันไม่เห็นวิธีใดที่จะบอกคอมไพเลอร์อย่างชัดเจนว่าต้องสร้างโค้ดลูปโดยไม่มีคำแนะนำที่ส่งผลต่อแฟล็ก C

คุณสามารถทำได้อย่างแน่นอน - ใช้ lea เพื่อนับที่อยู่อาเรย์ขึ้นไป dec เพื่อนับการวนซ้ำลง และทดสอบ Z สำหรับเงื่อนไขสิ้นสุด ด้วยวิธีนี้ ไม่มีอะไรในลูปยกเว้นผลรวมอาร์เรย์จริงจะเปลี่ยนแฟล็ก C

คุณจะต้องทำสิ่งที่ด้วยตนเอง เช่น:

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");

สำหรับโค้ดร้อน การวนซ้ำแบบแน่นนั้นไม่เหมาะสมที่สุด ประการแรกคำแนะนำมีการพึ่งพาและมีคำแนะนำต่อการวนซ้ำมากกว่าลำดับ adc แบบอินไลน์ / ไม่ได้โรล ลำดับที่ดีกว่าจะเป็นเช่น (%rbp resp. %rsi มีที่อยู่เริ่มต้นสำหรับอาร์เรย์ต้นทางและอาร์เรย์เป้าหมาย):

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 

และวนซ้ำเฉพาะบล็อกดังกล่าวเท่านั้น ข้อดีก็คือโหลดถูกบล็อก และคุณจะจัดการกับจำนวนลูป/เงื่อนไขการยกเลิกเพียงครั้งเดียวเท่านั้น

พูดตามตรงแล้ว ฉันจะพยายามไม่ทำให้ความกว้างบิต ทั่วไป โดยเฉพาะอย่างยิ่ง "เรียบร้อย" แต่เป็นโค้ดที่คลี่กรณีพิเศษอย่างชัดเจน เช่น พูด ความกว้างบิตของกำลังสอง แทนที่จะเพิ่มข้อความแฟล็ก / ตัวสร้างให้กับอินสแตนซ์เทมเพลตที่ไม่ได้รับการปรับให้เหมาะสมเพื่อบอกผู้ใช้ว่า "ใช้กำลังสอง" หรือไม่

person FrankH.    schedule 07.12.2012
comment
จะดีกว่าไหมถ้าจัดเก็บแฟล็กรีจิสเตอร์ไว้ที่จุดเริ่มต้นของฟังก์ชันและกู้คืนก่อนส่งคืน PUSHF/POPF และ SAHF/LAHF สามารถทำได้ - person Daniel Kamil Kozar; 07.12.2012
comment
จริง - แต่อย่างใดอย่างหนึ่งเหล่านี้หมายถึงการเข้าถึงหน่วยความจำมากขึ้น ทำหนึ่งครั้งต่อการวนซ้ำที่แน่นหนาซึ่งมีค่าใช้จ่ายสูง การใช้ lea / inc / dec คุณสามารถหลีกเลี่ยงได้ - person FrankH.; 08.12.2012