ทำไมคอมไพเลอร์ GCC จึงไม่ใช้รหัสบางอย่าง


9

ฉันไม่สามารถเข้าใจว่าทำไม GCC คอมไพเลอร์ตัดส่วนของรหัสของฉันในขณะที่มันรักษารหัสเดียวกันในละแวก?

รหัส C:

#define setb_SYNCO do{(PORTA|= (1<<0));} while(0);

ISR(INT0_vect){
    unsigned char i;

    i = 10;
    while(i>0)i--;   // first pause - omitted

    setb_SYNCO;
    setb_GATE;
    i=30;
    clrb_SYNCO;
    while(i>0)i--;  // second pause - preserved
    clrb_GATE;
}

ส่วนที่สอดคล้องกันของ LSS (ไฟล์แอสเซมเบลอร์ที่สร้างโดยคอมไพเลอร์):

ISR(INT0_vect){
  a4:   1f 92           push    r1
  a6:   0f 92           push    r0
  a8:   0f b6           in  r0, 0x3f    ; 63
  aa:   0f 92           push    r0
  ac:   11 24           eor r1, r1
  ae:   8f 93           push    r24
    unsigned char i;

    i = 10;
    while(i>0)i--;

    setb_SYNCO;
  b0:   d8 9a           sbi 0x1b, 0 ; 27
    setb_GATE;
  b2:   d9 9a           sbi 0x1b, 1 ; 27
    i=30;
    clrb_SYNCO;
  b4:   d8 98           cbi 0x1b, 0 ; 27
  b6:   8e e1           ldi r24, 0x1E   ; 30
  b8:   81 50           subi    r24, 0x01   ; 1
    while(i>0)i--;
  ba:   f1 f7           brne    .-4         ; 0xb8 <__vector_1+0x14>
    clrb_GATE;
  bc:   d9 98           cbi 0x1b, 1 ; 27
}
  be:   8f 91           pop r24
  c0:   0f 90           pop r0
  c2:   0f be           out 0x3f, r0    ; 63
  c4:   0f 90           pop r0
  c6:   1f 90           pop r1
  c8:   18 95           reti

ฉันสามารถสันนิษฐานได้ว่าคอมไพเลอร์คิดว่ารหัสนั้นหลอกตาและตัดมันออก แต่ทำไมมันถึงรักษารหัสเดียวกันในตอนท้ายของรหัส?

มีคำแนะนำในการคอมไพเลอร์เพื่อป้องกันจากการเพิ่มประสิทธิภาพดังกล่าว?


1
คุณสามารถบอกคอมไพเลอร์ว่าไม่ปรับฟังก์ชั่นเดียวให้เหมาะสมอาจจะคุ้มค่าที่จะลองใช้วิธีนี้กับ ISR ของคุณ ดูคำถามนี้ใน stackoverflow
Vladimir Cravero

2
เฮ้โรมันฉันเพิ่มแท็ก "c" ในคำถามของคุณเพื่อลบ atmega ฉันต้องลบหนึ่งแท็กเนื่องจากมีข้อ จำกัด (ห้า) และเมื่อถามคำถามที่เกี่ยวข้องกับรหัสการเพิ่มชื่อภาษาเป็นแท็กดีมากเพราะรหัสทั้งหมด (Q & As) ได้รับการเน้น
Vladimir Cravero

5
โดยทั่วไปแล้วภาษาระดับสูงกว่า (เช่น C) ได้รับการออกแบบอย่างชัดเจนเพื่อไม่ให้ผูกกับความสัมพันธ์แบบ 1: 1 กับแอสเซมบลีที่เกิดขึ้น หากคุณจำเป็นต้องนับคำแนะนำเพื่อให้ได้เวลาที่ถูกต้องคุณจะต้องพึ่งพาการชุมนุม (เช่นคำตอบบางข้อ) จุดรวมของภาษาระดับสูงคือคอมไพเลอร์มีอิสระในการช่วยให้โค้ดของคุณแข็งแกร่งขึ้นเร็วขึ้นกว่า แต่ก่อน รายละเอียดเช่นการจัดสรรการลงทะเบียนและการคาดการณ์สาขาจะดีกว่ามากในคอมไพเลอร์ ... ยกเว้นในบางครั้งเช่นนี้ที่คุณโปรแกรมเมอร์รู้คำแนะนำที่คุณต้องการ
Cort Ammon

5
คำถามที่ดีกว่าคือทำไม GCC จึงไม่ปรับลูปทั้งสองให้เหมาะสม
Ilmari Karonen

5
Gcc เปิดใช้การวนรอบแรกแล้วสังเกตว่ารหัสที่เกี่ยวข้องนั้นไม่มีประโยชน์ ด้วยขนาดวนวน 30 การคลี่คลายจะโง่และ gcc ไม่ทำ ในระดับการเพิ่มประสิทธิภาพที่สูงขึ้นทั้งสองจะถูกปรับให้เหมาะสม
Marc Glisse

คำตอบ:


9

เนื่องจากในความคิดเห็นหนึ่งคุณระบุว่า "ขีด จำกัด ของ CPU แต่ละอันมีค่า" ฉันแนะนำให้ใช้ชุดประกอบแบบอินไลน์เพื่อทำให้ความล่าช้าของคุณวนซ้ำตามที่คุณต้องการ การแก้ปัญหานี้เหนือกว่าความหลากหลายvolatileหรือ-O0เพราะมันชัดเจนว่าเจตนาของคุณคืออะไร

unsigned char i = 10;
__asm__ volatile ( "loop: subi    %0, 0x01\n\t"
                   "      brne    loop"
                   : "+rm" (i)
                   : /* no inputs */
                   : /* no dirty registers to decleare*/);

นั่นควรทำเคล็ดลับ สิ่งที่ระเหยได้คือการบอกคอมไพเลอร์ "ฉันรู้ว่านี่ไม่ได้ทำอะไรเลยเพียงแค่เก็บไว้และเชื่อใจฉัน" คำว่า "งบ" สามอันนั้นค่อนข้างอธิบายตนเองคุณสามารถใช้รีจิสเตอร์แทน r24 ได้ฉันเชื่อว่าคอมไพเลอร์ชอบการลงทะเบียนต่ำดังนั้นคุณอาจต้องการใช้อันสูง หลังจากครั้งแรกที่:คุณควรจะแสดงรายการตัวแปร c เอาท์พุท (อ่านและเขียน) และไม่มีหลังจากที่สอง:คุณควรจะแสดงรายการตัวแปร c (input) (ronly) อีกครั้งไม่มีและพารามิเตอร์ที่สามคือรายการที่คั่นด้วยเครื่องหมายจุลภาค ในกรณีนี้ r24 ฉันไม่แน่ใจว่าคุณควรรวมการลงทะเบียนสถานะด้วยหรือไม่เนื่องจากการZEROเปลี่ยนแปลงการตั้งค่าสถานะของหลักสูตรฉันไม่ได้รวมไว้ด้วย

แก้ไขคำตอบที่แก้ไขตามที่ขอ OP บางบันทึก

"+rm"ก่อนที่จะ(i)หมายความว่าคุณจะให้คอมไพเลอร์ตัดสินใจที่จะวางฉันในเอมอรีหรือในR egister เป็นสิ่งที่ดีในกรณีส่วนใหญ่เนื่องจากคอมไพเลอร์สามารถเพิ่มประสิทธิภาพได้ดีขึ้นถ้ามันฟรี ในกรณีของคุณฉันเชื่อว่าคุณต้องการเก็บเฉพาะข้อ จำกัด r เพื่อบังคับให้ฉันลงทะเบียน


ดูเหมือนว่านี่เป็นสิ่งที่ฉันต้องการจริงๆ แต่คุณสามารถแก้ไขคำตอบเพื่อยอมรับcตัวแปรใด ๆแทนที่จะเป็นตัวอักษรที่10ฉันพูดถึงในคำตอบเดิม ฉันกำลังพยายามอ่านคู่มือ GCC เกี่ยวกับการใช้asm construction ที่เหมาะสมแต่ตอนนี้ค่อนข้างคลุมเครือสำหรับฉัน ฉันจะขอบคุณมาก!
Roman Matveev

1
@RomanMatveev แก้ไขตามที่คุณร้องขอ
Vladimir Cravero

13

คุณสามารถลองทำลูปจริงทำอะไรบางอย่าง ขณะที่มันแปลว่าคอมไพเลอร์พูดอย่างถูกต้องว่า "วงนี้ไม่ทำอะไรเลย - ฉันจะกำจัดมัน"

ดังนั้นคุณสามารถลองสร้างฉันใช้บ่อย:

int i;
for (i = 0; i < 10; i++) {
    asm volatile ("nop");
}

หมายเหตุ: ไม่ใช่เป้าหมายทั้งหมดสำหรับคอมไพเลอร์ gcc ใช้ไวยากรณ์แอสเซมบลีไลน์เดียวกัน - คุณอาจต้องปรับแต่งมันสำหรับเป้าหมายของคุณ


วิธีแก้ปัญหาของคุณดูดีกว่าของฉันมาก ... บางทีของฉันจะดีกว่าเมื่อต้องการจำนวนรอบที่แม่นยำ ฉันหมายความว่าทั้งหมดสำหรับสิ่งที่ไม่รับประกันว่าจะรวบรวมในวิธีที่แน่นอนมันได้หรือไม่
Vladimir Cravero

8
ความจริงง่ายๆคือการใช้ C หมายความว่าคุณไม่สามารถรับประกันรอบ หากคุณต้องการการนับรอบที่แม่นยำ ASM เป็นหนทางเดียวที่จะไป ฉันหมายความว่าคุณจะได้รับระยะเวลาที่แตกต่างกันกับวง C ถ้าคุณมี -funroll ลูปที่เปิดใช้งานกว่าถ้าคุณทำไม่ได้ ฯลฯ
Majenko

ใช่นั่นคือสิ่งที่ฉันคิด เมื่อทำ HW ล่าช้าด้วยค่า i ที่สูงพอ (100 หรือมากกว่า) ฉันเดาว่าโซลูชันของคุณให้ผลลัพธ์ที่เหมือนกันเกือบทุกประการพร้อมกับเพิ่มความสามารถในการอ่าน
Vladimir Cravero

6

ใช่คุณสามารถสันนิษฐานได้ว่า หากคุณประกาศตัวแปร i ว่าระเหยได้คุณจะต้องบอกคอมไพเลอร์ว่าอย่าปรับให้เหมาะสมกับ i


1
นั่นไม่ใช่ความจริงทั้งหมดในความคิดของฉัน
Vladimir Cravero

1
@VladimirCravero คุณหมายถึงอะไรโดยพูดอย่างนั้น? คุณช่วยให้ชัดเจนขึ้นได้ไหม
Roman Matveev

2
สิ่งที่ฉันหมายถึงคือฉันจะไม่แน่ใจเกี่ยวกับสิ่งที่คอมไพเลอร์ทำ การประกาศความแปรปรวนของตัวแปรบอกคอมไพเลอร์ว่ามันอาจเปลี่ยนแปลงที่อื่นดังนั้นจึงควรทำในขณะนั้น
Vladimir Cravero

1
@ Roman Matveev register unsigned char volatile i __asm__("r1");อาจจะ?
a3f

2
การประกาศแบบiระเหยแก้ทุกสิ่ง สิ่งนี้รับประกันโดยมาตรฐาน C 5.1.2.3 คอมไพเลอร์ที่สอดคล้องกันจะต้องไม่ปรับลูปเหล่านั้นให้เหมาะสมหากiมีความผันผวน โชคดีที่ GCC เป็นคอมไพเลอร์ที่ได้มาตรฐาน น่าเสียดายที่มีคอมไพเลอร์ C หลายตัวที่ไม่เป็นไปตามมาตรฐาน แต่ก็ไม่เกี่ยวข้องกับคำถามนี้ ไม่มีความจำเป็นอย่างยิ่งสำหรับแอสเซมเบลอร์แอสเซมเบลอร์
Lundin

1

หลังจากลูปแรกiเป็นค่าคงที่ การเริ่มต้นiและลูปไม่ได้ทำอะไรนอกจากสร้างค่าคงที่ ไม่มีอะไรในมาตรฐานที่ระบุว่าการวนซ้ำนี้จะต้องรวบรวมตามสภาพ มาตรฐานไม่ได้พูดอะไรเกี่ยวกับเวลาเช่นกัน รหัสที่คอมไพล์แล้วจะต้องทำตัวราวกับว่ามีการวนซ้ำอยู่ คุณไม่สามารถบอกได้อย่างน่าเชื่อถือว่าการเพิ่มประสิทธิภาพนี้ดำเนินการภายใต้มาตรฐาน (ไม่นับเวลา)

ควรลบลูปที่สองด้วย ฉันคิดว่าเป็นข้อผิดพลาด (หรือการเพิ่มประสิทธิภาพที่หายไป) ว่าไม่ใช่ หลังจากวนiเป็นศูนย์คงที่ รหัสควรถูกแทนที่ด้วยการตั้งค่าiเป็นศูนย์

ผมคิดว่า GCC ช่วยให้iรอบหมดจดสำหรับเหตุผลที่คุณทำการ (ทึบแสง) iการเข้าถึงพอร์ตอาจส่งผลกระทบต่อ

ใช้

asm volatile ("nop");

เพื่อหลอกลวง GCC ให้เชื่อว่าการวนซ้ำทำอะไรบางอย่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.