สับสนวิธีเข้าถึงตัวแปร C ++ ภายในโค้ดแอสเซมบลี

การเรียนเรื่องการประกอบและการเขียนโปรแกรมทั้งหมดเสร็จสิ้นใน Visual Studio โดยใช้คำสั่ง _asm นี่คือเทมเพลตสำหรับงานที่ฉันกำลังทำอยู่:

void main ()
    {
    const unsigned char c1 (0x11);
    const unsigned char c2 (0x22);
    const unsigned char c3 (0x33);
    const unsigned char c4 (0x44);
    unsigned long i1 (0);
    _asm
        {
        **CODE HERE**
        }
     cout << "result is " << hex << i1 << endl;
    }

คำแนะนำ: ภายในบล็อกที่แสดงด้วยคีย์เวิร์ด _asm ให้เพิ่มโค้ดเพื่อใส่ค่า c1, c2, c3 และ c4 (แต่ละ 8 บิต) ลงในรีจิสเตอร์ eax โดยที่ c1 กำลังโหลดที่จุดสูงสุด (จุดสิ้นสุดที่สำคัญที่สุด) และ c4 ที่ ต่ำสุด ใส่ผลลัพธ์ 32 บิตลงใน i1

ฉันจะเข้าถึงตัวแปรที่ประกาศใน C++ ได้อย่างไร ฉันคิดว่าไม่มี 'ตัวแปร' ในแอสเซมบลี แล้วฉันจะเข้าถึงสิ่งเหล่านี้ได้อย่างไร


person DJ Kermit    schedule 24.01.2020    source แหล่งที่มา
comment
คุณเพียงแค่ เข้าถึงด้วยชื่อ คุณไม่ได้เขียนแอสเซมบลีบริสุทธิ์ คุณกำลังเขียนแอสเซมบลีแบบฝัง MSVC   -  person Miles Budnek    schedule 24.01.2020
comment
asm ของคุณสามารถใช้ประโยชน์จากความจริงที่ว่ามันเป็นค่าคงที่ได้หรือไม่ และทำ mov eax, (c1<<24) | (c2<<16) | ... ด้วยคำสั่งเดียวที่คำนวณทันทีในเวลาคอมไพล์หรือไม่ หรือคุณต้องปล่อยให้ MSVC เก็บไว้ในหน่วยความจำแล้ว mov al, c1 และ shift? (หรือวิธีอื่นอีกมากมายที่ไม่เกี่ยวข้องกับการเขียนการลงทะเบียนบางส่วน)   -  person Peter Cordes    schedule 24.01.2020
comment
ไปทางอื่น: วิธีย้ายไบต์ของตัวแปรในแอสเซมบลี   -  person Peter Cordes    schedule 12.02.2020


คำตอบ (1)


ตัวอย่างการเพิ่มตัวเลขเหล่านั้นด้วย Visual Studio

#include <iostream>

int main( )
{
    const unsigned char c1 (0x11);
    const unsigned char c2 (0x22);
    const unsigned char c3 (0x33);
    const unsigned char c4 (0x44);
    unsigned long i1 (0);
    _asm
    {
            xor     eax,eax
            xor     ecx,ecx         ;xor and mov could be replaced with movzx ecx,c1
            mov     cl,c1
            add     eax,ecx
            mov     cl,c2
            add     eax,ecx
            mov     cl,c3
            add     eax,ecx
            mov     cl,c4
            add     eax,ecx
            mov     i1,eax
    }
    std::cout << "result is " << std::hex << i1 << std::endl;
    return 0;
}

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

person rcgldr    schedule 25.01.2020
comment
วิธีนี้จะคำนวณ c1+c2+c3+c4 โดยไม่บรรจุไบต์เหล่านั้นเป็น dword เดียวเป็น c1:c2:c3:c4 นี่คือตัวอย่างวิธีเข้าถึงตัวแปรโดยไม่ต้องทำการบ้านของ OP (คุณสามารถใช้ movzx ecx, c1 เพื่อหลีกเลี่ยง ECX ที่ xor เป็นศูนย์ก่อน) - person Peter Cordes; 25.01.2020
comment
@PeterCordes - แล้วจำนวนนาฬิกาหรือ uops สำหรับ |xor ecx|mov cl,c1| เจอ |movzx ecx,c1| ? สูญเสียการแก้ไขที่อธิบายโค้ด ขณะนี้ได้รับการแก้ไขแล้ว - person rcgldr; 25.01.2020
comment
movzx r32, m8 เป็น single-uop ไม่ใช่แม้แต่ micro-fused (จัดการอย่างเต็มที่โดยพอร์ตโหลด) บน Intel มาเป็นเวลานาน เช่นเดียวกับ Zen 1 และ Zen 2 ตาม uops.info เวลาแฝงเดียวกันกับโหลด mov แบบ 32 บิต มีค่าใช้จ่ายขนาดโค้ดเพิ่มเติม 1 ไบต์ต่อการโหลด แต่การทำให้แต่ละโหลดเขียนการลงทะเบียนแบบเต็มจะเป็นการลบการพึ่งพาอนุกรมระหว่างโหลด และความจำเป็นในการรวม uop - person Peter Cordes; 25.01.2020
comment
การ Xor-zeroing จะหลีกเลี่ยงการลงโทษบางส่วนในการลงทะเบียน (เช่น P6-family หรือ SnB รุ่นแรกๆ) สำหรับการเขียน cl และการอ่าน ecx (เว้นแต่จะมีการขัดจังหวะเกิดขึ้นระหว่างการทำให้เป็นศูนย์และ mov r8,m8 ดังนั้นให้บันทึก/กู้คืน reg ที่จะทำลายสิ่งนั้น) แต่ในยุคสมัยใหม่ Intel (HSW และใหม่กว่าซึ่งไม่เปลี่ยนชื่อ cl แยกจาก ECX) mov cl, mem มีค่าใช้จ่าย 1 ไมโครฟิวส์ โหลด+ผสาน uop มันผ่านส่วนหน้าเป็น uop เดียว แต่ในด้านหลัง ALU uop ต้องดำเนินการเพื่อรวมผลลัพธ์การโหลดเข้ากับ ECX ก่อน add จึงสามารถอ่านได้ ดังนั้นจึงต้องใช้พื้นที่มากขึ้นใน RS และมีเวลาแฝงมากขึ้น (ในเชนอิสระแบบสั้น) - person Peter Cordes; 25.01.2020
comment
หลักทั่วไป: ใช้ mov r8, ... เฉพาะในกรณีที่คุณต้องการ ต้องการ รวมเป็นไบต์ต่ำของค่าก่อนหน้า มิฉะนั้น ให้ใช้รูปแบบคล้าย RISC ของโหลดที่ขยายเป็นศูนย์หรือขยายสัญญาณ (CPU บางตัว (เช่น AMD Bulldozer อาจจะใช่หรือไม่) จำเป็นต้องมี uop เพิ่มเติมเพื่อเซ็นชื่อและขยายไบต์หรือโหลดคำ ส่วน Intel / AMD ล่าสุดก็โหลด movsx เพียงอย่างเดียวในพอร์ตโหลดเช่นกัน) อาจมีกรณีที่บันทึกโค้ดได้ 1 ไบต์ ขนาดจะมีประโยชน์มากกว่า เช่น ในโค้ดแบบเย็นซึ่งประสิทธิภาพของตัวเองไม่สำคัญ หรือหากคุณทราบ CPU เป้าหมายของคุณว่าการทำงานแบบรีจิบางส่วนทำงานได้ดี - person Peter Cordes; 25.01.2020
comment
BTW หากคุณเพียงต้องการยกตัวอย่างการใช้ชื่อ var ในไวยากรณ์ asm แบบอินไลน์ ฉันคิดว่ามีรายการซ้ำที่มีอยู่ซึ่งถามเช่นนั้น ดังนั้นเราจึงสามารถปิดคำถามนี้เป็นการซ้ำซ้อนได้ - person Peter Cordes; 25.01.2020
comment
@PeterCordes - ฉันได้เพิ่มความคิดเห็นในคำตอบของฉันแทนที่จะเปลี่ยนรหัสเพื่อให้ความคิดเห็นของคุณยังคงนำไปใช้ ใน 486 xor+mov คือ 2 รอบ movzx คือ 3 รอบ ณ จุดหนึ่งหลังจาก 486 movzx ก็เร็วขึ้น แต่ฉันไม่รู้ว่าเมื่อใด เนื่องจากการเพิ่มไม่ใช่เป้าหมายที่นี่ ฉันจึงไม่ได้คิดที่จะปรับโค้ดตัวอย่างของฉันให้เหมาะสม และฉันพยายามสร้างรูปแบบของ mov + เพิ่มเหมือนกันสำหรับคำสั่งทั้ง 4 คู่ - person rcgldr; 25.01.2020
comment
ยุติธรรมเพียงพอ แม้ว่าในปี 2020 ฉันไม่คิดว่าจะมีใครสนใจการปรับแต่ง 486 หรือ P5 ตามลำดับ (โดยที่ movzx ก็ช้าเช่นกัน) อย่างไรก็ตาม การลงทะเบียนบางส่วน (และกะ) เป็นวิธีที่มีประสิทธิภาพมากที่สุดในการทำงานของ OP ให้สำเร็จบน CPU ส่วนใหญ่ ยกเว้นตระกูล P6 ที่แผงลอยการผสาน reg บางส่วนนั้นร้ายแรงจริงๆ แต่มันยังคงใช้งานได้และง่าย ดังนั้นบางที mov ตัวอย่างการโหลดอาจช่วยพวกเขาได้ บน Intel HSW/SKL นั้น movzx + shl + 3x mov และอีก 2 กะจะใช้งานได้ โดยโหลดลงใน AL เท่านั้น การโหลด AH และ AL อาจจะแย่ลงหากปัญหาคอขวดของส่วนหน้า (AH รวม uop ต้องออกด้วยตัวเอง) - person Peter Cordes; 25.01.2020
comment
เพื่อพูดเล่น: ฉันรู้ว่าคุณไม่ได้เลือกชื่อตัวแปร แต่การใช้โค้ดเช่น cl,c1 เป็นเพียงการถามถึงปัญหา ชื่อทั้งสองนี้อาจสับสนได้ง่ายทั้งนี้ขึ้นอยู่กับแบบอักษร - person David Wohlferd; 25.01.2020
comment
@DavidWohhlferd - ในกรณีนี้ ควรชัดเจนว่าการเคลื่อนไหวนั้นมาจากหน่วยความจำเพื่อลงทะเบียน นี่เป็นเพียงตัวอย่างที่แสดงไวยากรณ์สำหรับการเข้าถึงตัวแปรโลคัลด้วยแอสเซมบลี การกำหนดจริงคือการแพ็ค 4 ไบต์แยกกันให้เป็นจำนวนเต็มเดียว - person rcgldr; 26.01.2020
comment
ตัวพิมพ์เล็ก L และหมายเลข 1 อาจแยกแยะได้ยาก ทั้งนี้ขึ้นอยู่กับแบบอักษร การพิมพ์อันหนึ่งเมื่อคุณหมายถึงอีกอันจะทำได้ง่าย และมองเห็นได้ยากในระหว่างการตรวจด้วยสายตา ตัวอย่างเช่น mov cl,cl จะประกอบกัน แต่อาจนำไปสู่การเกาหัวอย่างรุนแรง เมื่อไม่ได้ย้ายค่าจาก c1 ในแบบที่คุณคาดหวัง เนื่องจากโดยทั่วไปชื่อตัวแปรจะอยู่ภายใต้การควบคุมของโปรแกรมเมอร์ การใช้ uc1 สำหรับอักขระที่ไม่ได้ลงชื่อ #1 อาจหลีกเลี่ยงความสับสนได้ - person David Wohlferd; 26.01.2020
comment
ตกลง ฉันสามารถเข้าถึงได้โดยใช้ชื่อภายในโค้ดแอสเซมบลีใช่ไหม - person DJ Kermit; 26.01.2020