จาก C ถึง Assembly


16

สมมติว่าเรามีโค้ด C ต่อไปนี้สำหรับ avr-8bit:

int v1=1;
int v2=2;
v2=v2+v1;

ฉันคาดว่าถอดแยกชิ้นส่วนต่อไปนี้

ldi r18, 1;
ldi r19, 2;
add r19, r18;

แต่หลังจากที่ฉันวิ่ง:

avr-gcc -mmcu=atmega2560 Test.c -o Test.elf

และ

avr-objdump -S Test.elf > Test.lss

ฉันได้ถอดแยกชิ้นส่วนต่อไปนี้

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0
    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01
    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03
    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02
    add r24, r18
    adc r25, r19
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

มีใครบ้างที่สามารถช่วยให้ฉันเข้าใจผลการถอดประกอบได้

แก้ไข: การใช้ char แอสเซมบลีกลายเป็น:

ldi r24, 0x01
std Y+1, r24
ldi r24, 0x02
std Y+2, r24
ldd r25, Y+2
ldd r24, Y+1
add r24, r25
std Y+2, r24

จะมีคำสั่งมาตรฐานเมื่อใด?

คำตอบ:


20

คำตอบสั้น ๆ : การลงทะเบียนของคุณคือ 8 บิตและค่าของคุณคือ 16 บิต มันจัดการกับมันในสองชิ้น

คำตอบยาว:

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0

เก็บค่า 16 บิต 1 ในการลงทะเบียน 8 บิต r24, r25

    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01

เก็บไว้ที่ตำแหน่งสแต็ค Y + 1, Y + 2

    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0

เก็บค่า 16 บิต 2 ในการลงทะเบียน 8 บิต r24, r25

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

เก็บไว้ที่ตำแหน่งสแต็ค Y + 3, Y + 4

    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02

คัดลอกกลับจากสแต็กไปที่ (r18, r19) และ (r24, r25)

    add r24, r18
    adc r25, r19

เพิ่ม (r18, r19) ไปยัง (r24, r25) รวมถึงการเพิ่มที่สอง

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

เก็บไว้ในกองซ้อน

ในการรับชุดประกอบดั้งเดิมของคุณลองทำสองสิ่ง:

  • ใช้ตัวแปร "char"
  • ใช้ตัวเลือกคอมไพเลอร์ "-O2"

แก้ไข : เหตุผลที่คอมไพเลอร์เก็บตัวแปรไว้ในสแต็กแทนที่จะเก็บไว้ในรีจิสเตอร์เพราะมันถูกจัดเก็บด้วยประเภทการจัดเก็บเริ่มต้น "อัตโนมัติ" มันอาจเพิ่มประสิทธิภาพของพวกเขาเข้าไปลงทะเบียน แต่มันไม่จำเป็นต้องแม้ว่าคุณจะประกาศให้กับ "สมัคร" ระดับการจัดเก็บข้อมูล

แม้ว่านี่จะไม่ใช่ข้อกำหนดที่เข้มงวดของภาษา แต่เป็นพฤติกรรมการคอมไพเลอร์ปกติ หากถึงจุดหนึ่งคุณต้องใช้ที่อยู่ของ v1 มันจะต้องถูกกำหนดให้เป็นที่เก็บข้อมูลและบันทึกไว้ที่นั่นทุกครั้งที่มีการเปลี่ยนแปลง "v1" ดังนั้นเพื่อบันทึกการทำบัญชีว่าควรเก็บไว้ใน V1 หรือไม่หรือในสแต็กมันจะเก็บไว้ในสแต็กและจัดการแต่ละบรรทัดของรหัสแยกกัน


ขอขอบคุณ! ชัดเจนขึ้นแล้ว! โปรดหาการแก้ไขของฉันในคำถาม
DarkCoffee

1
ดูการแก้ไขของฉัน ลอง -O2 เช่นกัน อาจจะ -O3 ถึงแม้ว่าจะสามารถสร้างรหัสที่ใช้งานไม่ได้
pjc50

3
รหัสแบบฝังตัวจำนวนมากที่ฉันทำงานด้วยกำหนดประเภทพิเศษซึ่งมีขนาดเฉพาะเช่น "uint8, uint16, uint32" สำหรับ ints ที่ไม่ได้ลงชื่อ ด้วยวิธีนี้คุณจะรู้ได้อย่างชัดเจนว่าคุณใช้ตัวแปรประเภทใด โดยเฉพาะอย่างยิ่งในขนาดเล็กฝังตัวลงนามลอย "int" ของขนาด / การเซ็นชื่อที่ไม่ได้กำหนดทั้งหมดจะเสียค่าใช้จ่ายรอบ CPU ของคุณที่ดีที่สุดและทำให้เกิดข้อผิดพลาดร้ายแรงที่เลวร้ายที่สุด
John U

คอมไพเลอร์ตัวจริงหยุดทำงานแบบนี้ประมาณ 10-15 ปีที่แล้ว ปัญหาการจัดสรรการลงทะเบียนส่วนใหญ่จะได้รับการแก้ไขและคอมไพเลอร์ก็ประณามได้ดี พวกเขารู้ว่าเมื่อใดที่ตัวแปรต้องอยู่ในสแต็กและเมื่อสามารถอยู่ในรีจิสเตอร์ไม่ว่ามันจะคุ้มค่ากับความพยายามในการย้ายหรือไม่และเมื่อไร การทำบัญชีจะทำในเวลารวบรวมและคอมไพเลอร์เองมีหน่วยความจำกิกะไบต์ ข้อยกเว้นใหญ่คือโหมดแก้ไขข้อผิดพลาดด้วยเหตุผลที่ชัดเจน แต่ทุกอย่างอยู่ในสแต็ก
MSalters

@ pjc50 -O3สามารถผลิตรหัสเสียหรือไม่ [ต้องการอ้างอิง] (และไม่ใช่รหัส C ที่เรียกใช้พฤติกรรมที่ไม่ได้กำหนดและหยุดพักด้วยการตั้งค่าการเพิ่มประสิทธิภาพบางอย่างจะไม่นับ)
marcelm

4

เมื่อฉันพบรหัสตัวอย่างฉันจะให้ความเห็นคำตอบของฉัน - คนอื่น ๆ ได้อธิบายปัญหาแล้ว

รหัสแบบฝังตัวจำนวนมากที่ฉันทำงานด้วยกำหนดประเภทพิเศษซึ่งมีขนาดเฉพาะเช่น "uint8, uint16, uint32" สำหรับ ints ที่ไม่ได้ลงชื่อ ด้วยวิธีนี้คุณจะรู้ได้อย่างชัดเจนว่าตัวแปรชนิดใดที่คุณกำลังเผชิญอยู่ โดยเฉพาะอย่างยิ่งในขนาดเล็กฝังตัวลงนามลอย "int" ของขนาด / การเซ็นชื่อที่ไม่ได้กำหนดทั้งหมดจะเสียค่าใช้จ่ายรอบ CPU ของคุณที่ดีที่สุดและทำให้เกิดข้อผิดพลาดร้ายแรงที่เลวร้ายที่สุด

นี่คือ #defines ปัจจุบันของเรา:

/*
 * Example - the basic data types from our embedded code
 */
typedef unsigned char       uint8;  /*  8 bits */
typedef unsigned short int  uint16; /* 16 bits */
typedef unsigned long int   uint32; /* 32 bits */

typedef char                int8;   /*  8 bits */
typedef short int           int16;  /* 16 bits */
typedef int                 int32;  /* 32 bits */

typedef volatile int8       vint8;  /*  8 bits */
typedef volatile int16      vint16; /* 16 bits */
typedef volatile int32      vint32; /* 32 bits */

typedef volatile uint8      vuint8;  /*  8 bits */
typedef volatile uint16     vuint16; /* 16 bits */
typedef volatile uint32     vuint32; /* 32 bits */

3
ความคิดที่ดี; uint8_t และเพื่อน ๆ เป็นส่วนหนึ่งของมาตรฐาน: stackoverflow.com/questions/16937459/…
pjc50

มีประโยชน์อย่างไร! เราได้รับมรดกผู้ที่มีโปรเจ็กต์ซึ่งเป็น C89 ดังนั้นจึงเป็นการดีที่จะรู้ว่ามีเวอร์ชั่นเป็นทางการ
John U

2

รหัส C ของคุณใช้ตัวแปรจำนวนเต็ม 16 บิต (int) คอมไพเลอร์ไม่สามารถอ่านใจของคุณดังนั้นจึงรวบรวมสิ่งที่อยู่ในไฟล์ต้นฉบับ ดังนั้นถ้าคุณต้องการตัวแปร 8 บิตคุณต้องใช้ประเภทที่เกี่ยวข้อง

เป็นผลให้คุณยังคงได้รับการจัดเก็บค่าในหน่วยความจำ (แม้ว่าจะง่ายขึ้น) ฉันไม่ค่อยดีใน ​​C แต่ IMHO มีบางตัวเลือกในการกำหนดตัวแปรให้กับ register บางตัวหากคุณต้องการให้ตัวแปรบางตัวอยู่ใน register แทนที่จะเป็น RAM สิ่งที่ต้องการ:

register unsigned char VARNAME asm("r3");

โปรดทราบว่าการลงทะเบียนบางอย่างอาจไม่สามารถใช้งานได้สำหรับเทคนิคดังกล่าว

ดังนั้นข้อสรุป? เขียนโปรแกรมของคุณในการชุมนุม พวกเขาจะเล็กลงเร็วขึ้นและง่ายต่อการอ่าน / สนับสนุน


การอ่านง่ายกว่า C?
dext0rb

@ dext0rb - ใช่ แน่นอนถ้าคุณรู้ทั้งสองอย่างดีพอ ถ้าคุณรู้เพียง C ภาษาแอสเซมบลีและภาษาอื่น ๆ จะอ่านยาก
johnfound

ฉันต้องไม่เห็นด้วยกับจุดสุดท้ายโปรแกรมที่เขียนในแอสเซมเบลอร์อ่านยากกว่ามาก เพียงเปรียบเทียบรหัสที่มาที่ระบุด้านบน รหัส C นั้นชัดเจนและสั้นกว่ามากเช่นเดียวกับความตั้งใจ ความแตกต่างนี้เพิ่มขึ้นเมื่อมีการใช้งานโครงสร้างเท่านั้น
soandos

@soandos - รหัส C สั้นกว่าใช่ ชัดเจน? ฉันไม่แน่ใจ. ถ้าเป็นเช่นนั้นคำถามข้างต้นก็ไม่จำเป็นต้องถามเลย ที่จริงแล้วราคาของ "shortness" คือ "blurriness" ของรายละเอียด
johnfound

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