ฉันสังเกตเห็นว่าไม่มีคำถามดังกล่าวดังนั้นที่นี่:
คุณมีเคล็ดลับทั่วไปสำหรับการเล่นกอล์ฟในรหัสเครื่องหรือไม่? หากเคล็ดลับนี้ใช้กับสภาพแวดล้อมบางอย่างหรือการประชุมที่โทรมาเท่านั้นโปรดระบุในคำตอบของคุณ
กรุณาเพียงหนึ่งเคล็ดลับต่อคำตอบ (ดูที่นี่ )
ฉันสังเกตเห็นว่าไม่มีคำถามดังกล่าวดังนั้นที่นี่:
คุณมีเคล็ดลับทั่วไปสำหรับการเล่นกอล์ฟในรหัสเครื่องหรือไม่? หากเคล็ดลับนี้ใช้กับสภาพแวดล้อมบางอย่างหรือการประชุมที่โทรมาเท่านั้นโปรดระบุในคำตอบของคุณ
กรุณาเพียงหนึ่งเคล็ดลับต่อคำตอบ (ดูที่นี่ )
คำตอบ:
mov- ระดับกลางมีราคาแพงสำหรับค่าคงที่นี่อาจชัดเจน แต่ฉันจะยังคงวางไว้ที่นี่ โดยทั่วไปแล้วจะคิดออกเกี่ยวกับการเป็นตัวแทนระดับบิตของจำนวนเมื่อคุณต้องการเริ่มต้นค่า
eaxด้วย0:b8 00 00 00 00 mov $0x0,%eax
ควรย่อให้สั้นลง ( เพื่อประสิทธิภาพเช่นเดียวกับขนาดรหัส )
31 c0 xor %eax,%eax
eaxด้วย-1:b8 ff ff ff ff mov $-1,%eax
สามารถตัดให้สั้นลง
31 c0 xor %eax,%eax
48 dec %eax
หรือ
83 c8 ff or $-1,%eax
หรือมากกว่าโดยทั่วไปค่าที่ขยายเพิ่ม 8 บิตสามารถสร้างได้ใน 3 ไบต์ด้วยpush -12(2 ไบต์) / pop %eax(1 ไบต์) สิ่งนี้ใช้ได้กับการลงทะเบียน 64 บิตโดยไม่มีส่วนนำหน้า REX เพิ่มเติม push/ popdefault operand-size = 64
6a f3 pushq $0xfffffffffffffff3
5d pop %rbp
หรือได้รับค่าคงที่เป็นที่รู้จักในการลงทะเบียนคุณสามารถสร้างค่าคงที่ใกล้เคียงอื่นโดยใช้lea 123(%eax), %ecx(3 ไบต์) สิ่งนี้มีประโยชน์ถ้าคุณต้องการการลงทะเบียนแบบ zeroed และค่าคงที่; xor-zero (2 bytes) + lea-disp8(3 bytes)
31 c0 xor %eax,%eax
8d 48 0c lea 0xc(%eax),%ecx
ดูเพิ่มเติมตั้งค่าบิตทั้งหมดใน CPU register เป็น 1 อย่างมีประสิทธิภาพ
decเช่นxor eax, eax; dec eax
push imm8/ pop regคือ 3 ไบต์และยอดเยี่ยมสำหรับค่าคงที่ 64 บิตใน x86-64 โดยที่dec/ incเป็น 2 ไบต์ และpush r64/ pop 64(2 ไบต์) สามารถแทนที่ 3 ไบต์mov r64, r64(3 ไบต์ด้วย REX) ดูเพิ่มเติมตั้งค่าบิตทั้งหมดใน CPU register เป็น 1 ได้อย่างมีประสิทธิภาพสำหรับสิ่งที่ต้องการlea eax, [rcx-1]ค่าที่รู้จักในeax(เช่นถ้าต้องการ registered zeroed และค่าคงที่อื่นเพียงแค่ใช้ LEA แทน push / pop
ในหลายกรณีคำแนะนำที่อิงกับแอคคูเลเตอร์ (เช่นคำสั่งที่ใช้(R|E)AXเป็นตัวถูกดำเนินการปลายทาง) จะมีขนาด 1 ไบต์สั้นกว่าคำแนะนำกรณีทั่วไป ดูคำถามนี้ใน StackOverflow
al, imm8กรณีพิเศษเช่นor al, 0x20/ sub al, 'a'/ cmp al, 'z'-'a'/ ja .non_alphabeticเป็น 2 ไบต์แต่ละแทน 3. การใช้alข้อมูลตัวละครยังช่วยให้และlodsb / หรือ stosbหรือใช้alเพื่อทดสอบบางอย่างเกี่ยวกับไบต์ต่ำของ EAX เช่นlodsd/ test al, 1/ setnz clทำให้ cl = 1 หรือ 0 เป็นเลขคี่ / คู่ แต่ในกรณีที่หายากที่คุณจำเป็นต้องมี 32 บิตทันทีแล้วแน่ใจop eax, imm32เหมือนในคำตอบของความเข้มของสีที่สำคัญของฉัน
ภาษาของคำตอบของคุณคือ asm (จริง ๆ แล้วรหัสเครื่อง) ดังนั้นให้ถือว่าเป็นส่วนหนึ่งของโปรแกรมที่เขียนด้วย asm ไม่ใช่ C-compiled-for-x86 ฟังก์ชั่นของคุณไม่จำเป็นต้องโทรออกได้ง่ายจาก C กับแบบแผนการโทรมาตรฐานใด ๆ นั่นเป็นโบนัสที่ดีถ้ามันไม่ทำให้คุณเสียค่าใช้จ่ายเพิ่ม
ในโปรแกรม asm ล้วนเป็นเรื่องปกติสำหรับฟังก์ชั่นตัวช่วยบางอย่างที่จะใช้แบบแผนการโทรที่สะดวกสำหรับพวกเขาและสำหรับผู้โทร ฟังก์ชั่นดังกล่าวจัดทำเอกสารแผนการประชุมที่เรียกว่า
ในชีวิตจริงแม้โปรแกรม asm จะทำ (ฉันคิดว่า) มีแนวโน้มที่จะใช้แบบแผนการเรียกที่สอดคล้องกันสำหรับฟังก์ชั่นส่วนใหญ่ ใน code-golf คุณกำลังปรับอึออกจากฟังก์ชั่นเดียวดังนั้นจึงเป็นสิ่งสำคัญ / พิเศษ
เพื่อทดสอบการทำงานของคุณจากโปรแกรม C, สามารถเขียนเสื้อคลุมที่ args ทำให้ในสถานที่ที่เหมาะสมจะช่วยประหยัด / คืนค่าลงทะเบียนพิเศษใด ๆ ที่คุณข่มขี่และทำให้ค่าส่งกลับเข้าไปe/raxถ้ามันไม่ได้มีอยู่แล้ว
การกำหนดให้ DF (แฟล็กทิศทางสตริงสำหรับlods/ stos/ ฯลฯ ) ให้ชัดเจน (ขึ้นไป) เมื่อมีการโทร / ret เป็นเรื่องปกติ ปล่อยให้มันไม่ได้กำหนดไว้ในการโทร / ret จะเป็น ok ต้องการให้ล้างหรือตั้งค่าในรายการ แต่ปล่อยให้มันแก้ไขเมื่อคุณกลับมาจะแปลก
การส่งคืนค่า FP ใน x87 st0นั้นสมเหตุสมผล แต่การกลับมาst3พร้อมกับขยะในการลงทะเบียน x87 อื่น ๆ นั้นไม่ใช่ ผู้เรียกจะต้องล้างค่าสแต็ก x87 แม้จะกลับมาst0พร้อมกับรีจิสเตอร์สแต็กที่ไม่ว่างเปล่าที่สูงขึ้นก็จะเป็นที่น่าสงสัย (เว้นแต่คุณจะส่งคืนค่าหลายค่า)
callดังนั้นจึง[rsp]เป็นที่อยู่ผู้ส่งของคุณ คุณสามารถหลีกเลี่ยงcall/ retบน x86 โดยใช้ลิงค์ลงทะเบียนเช่นlea rbx, [ret_addr]/ jmp functionและกลับด้วยjmp rbxแต่นั่นไม่ใช่ "สมเหตุสมผล" นั่นไม่ได้มีประสิทธิภาพเท่ากับการโทร / รับดังนั้นจึงไม่ใช่สิ่งที่คุณจะพบได้ในรหัสจริงกรณีชายแดน: การเขียนฟังก์ชั่นที่ผลิตลำดับในอาร์เรย์ที่กำหนด 2 องค์ประกอบแรกเป็น args ฉันเลือกให้ผู้โทรจัดเก็บการเริ่มต้นของลำดับลงในอาร์เรย์และเพียงแค่ส่งตัวชี้ไปยังอาร์เรย์ นี่คือการดัดข้อกำหนดของคำถาม ผมถือว่าการ args บรรจุเข้าxmm0สำหรับmovlps [rdi], xmm0ซึ่งยังจะเป็นเรียกประชุมแปลก
การเรียกใช้ระบบ OS X ทำได้ ( CF=0หมายถึงไม่มีข้อผิดพลาด): มันถือว่าเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะใช้การลงทะเบียนค่าสถานะเป็นค่าส่งคืนบูลีน? .
เงื่อนไขใด ๆ ที่สามารถตรวจสอบได้ด้วย JCC หนึ่งรายการนั้นสมเหตุสมผลอย่างสมบูรณ์แบบโดยเฉพาะอย่างยิ่งหากคุณสามารถเลือกเงื่อนไขที่มีความเกี่ยวข้องกับความหมายของปัญหาได้ (เช่นฟังก์ชั่นการเปรียบเทียบอาจตั้งค่าสถานะดังนั้นjneจะต้องดำเนินการหากไม่เท่ากับ)
char) เพื่อเป็นสัญญาณหรือศูนย์ขยายเป็น 32 หรือ 64 บิตนี่ไม่ใช่เหตุผลอันสมควร; การใช้movzxหรือmovsx เพื่อหลีกเลี่ยงการชะลอการลงทะเบียนบางส่วนเป็นเรื่องปกติใน x86 asm ที่ทันสมัย ในความเป็นจริงเสียงดังกราว / LLVM แล้วทำให้รหัสที่ขึ้นอยู่กับส่วนขยายที่ไม่มีเอกสารไป x86-64 System V เรียกประชุม: args แคบกว่า 32 บิตจะเข้าสู่ระบบหรือศูนย์ขยายไปถึง 32 บิตโดยผู
คุณสามารถจัดทำเอกสาร / อธิบายส่วนขยายถึง 64 บิตโดยการเขียนuint64_tหรือint64_tในต้นแบบของคุณหากคุณต้องการ เช่นเพื่อให้คุณสามารถใช้loopคำสั่งซึ่งใช้ทั้ง 64 บิตของ RCX เว้นแต่ว่าคุณใช้คำนำหน้าขนาดที่อยู่เพื่อแทนที่ขนาดลงไปที่ 32 บิต ECX (ใช่จริงๆขนาดที่อยู่ไม่ได้ถูกดำเนินการขนาด)
โปรดทราบว่าlongเป็นประเภท 32 บิตใน Windows 64 บิต ABI และLinux x32 ABIเท่านั้น เป็นที่ชัดเจนและสั้นกว่าชนิดuint64_tunsigned long long
ของ Windows 32 บิต__fastcall, แนะนำแล้วโดยคำตอบอื่น : args จำนวนเต็มในและecxedx
x86-64 ระบบ V : ผ่าน args จำนวนมากในการลงทะเบียนและมีการลงทะเบียนการโทรที่ถูกปิดกั้นจำนวนมากซึ่งคุณสามารถใช้ได้โดยไม่ต้องใช้คำนำหน้า REX ที่สำคัญกว่านั้นคือมันถูกเลือกให้คอมไพเลอร์อินไลน์memcpyหรือ memset rep movsbได้อย่างง่ายดาย: args จำนวนเต็ม / ตัวชี้ 6 ตัวแรกจะถูกส่งผ่านใน RDI, RSI, RDX, RCX, R8, R9
หากฟังก์ชั่นของคุณใช้lodsd/ stosdภายในลูปที่รันrcxเวลา (พร้อมloopคำสั่ง) คุณสามารถพูดว่า "callable จาก C เช่นเดียวint foo(int *rdi, const int *rsi, int dummy, uint64_t len)กับ x86-64 System V call Convention" ตัวอย่างเช่น: chromakey
32 บิต GCC regparm: มีจำนวนเต็มใน EAX , ECX, EDX, ส่งคืนเป็น EAX (หรือ EDX: EAX) มีหาเรื่องครั้งแรกในการลงทะเบียนเช่นเดียวกับค่าตอบแทนที่ช่วยให้การเพิ่มประสิทธิภาพบางอย่างเช่นกรณีนี้ด้วยตัวอย่างโทรและต้นแบบที่มีคุณลักษณะฟังก์ชั่น และแน่นอนว่า AL / EAX นั้นพิเศษสำหรับคำแนะนำบางอย่าง
Linux x32 ABI ใช้ตัวชี้แบบ 32 บิตในโหมดยาวดังนั้นคุณสามารถบันทึกคำนำหน้า REX เมื่อทำการแก้ไขตัวชี้ ( ตัวอย่างเช่นการใช้ตัวพิมพ์ ) คุณยังคงสามารถใช้ขนาดที่อยู่ 64- บิตได้เว้นแต่คุณจะมีเลขจำนวนเต็มลบ 32- ศูนย์ในการลงทะเบียน (ดังนั้นมันจะเป็นค่าที่ไม่ได้ลงชื่อขนาดใหญ่ถ้าคุณทำ[rdi + rdx])
โปรดทราบว่าpush rsp/ pop raxคือ 2 ไบต์และเทียบเท่าmov rax,rspดังนั้นคุณยังสามารถคัดลอกการลงทะเบียน 64- บิตเต็มใน 2 ไบต์
ret 16; พวกเขาไม่ได้ปรากฏอยู่กลับผลักดันอาร์เรย์แล้ว/push rcx retผู้เรียกจะต้องทราบขนาดอาร์เรย์หรือบันทึก RSP ไว้ที่อื่นนอกสแต็กเพื่อค้นหาตัวเอง
ใช้การเข้ารหัสแบบสั้นกรณีพิเศษสำหรับ AL / AX / EAX และแบบฟอร์มสั้นอื่น ๆ และคำแนะนำแบบไบต์เดียว
ตัวอย่างสมมติโหมด 32/64 บิตโดยที่ขนาดตัวถูกดำเนินการเริ่มต้นคือ 32 บิต คำนำหน้าขนาดตัวถูกดำเนินการเปลี่ยนคำสั่งเป็น AX แทน EAX (หรือย้อนกลับในโหมด 16 บิต)
inc/decลงทะเบียน (อื่น ๆ กว่า 8 บิต): /inc eax dec ebp(ไม่ใช่ x86-64: 0x4xไบต์ opcode ถูกนำมาใช้ใหม่เป็นคำนำหน้า REX ดังนั้นจึงinc r/m32เป็นการเข้ารหัสเท่านั้น)
8 บิตinc blคือ 2 ไบต์ใช้inc r/m8opcode + ModR / M ถูกดำเนินการเข้ารหัส ดังนั้นใช้inc ebxเพื่อเพิ่มblถ้ามันปลอดภัย (เช่นหากคุณไม่ต้องการผลลัพธ์ ZF ในกรณีที่ไบต์บนอาจไม่ใช่ศูนย์)
scasd: e/rdi+=4ต้องการให้ register register ไปยังหน่วยความจำที่สามารถอ่านได้ บางครั้งมีประโยชน์แม้ว่าคุณจะไม่สนใจผลลัพธ์ของธง (เช่นcmp eax,[rdi]/ rdi+=4) และในโหมด 64- บิตscasbสามารถทำงานเป็น 1 ไบต์inc rdiถ้า lodsb หรือ stosb ไม่มีประโยชน์
xchg eax, r32: นี่คือที่ 0x90 NOP xchg eax,eaxมาจาก: ตัวอย่าง: จัดเรียงรีจิสเตอร์อีก 3 ตัวโดยมีสองxchgคำสั่งใน a cdq/ idivloop สำหรับ GCD ใน 8 ไบต์ซึ่งคำสั่งส่วนใหญ่เป็นไบต์เดียวรวมถึงการใช้inc ecx/ loopแทนtest ecx,ecx/jnz
cdq: ลงชื่อขยาย EAX ไปยัง EDX: EAX, เช่นการคัดลอก EAX บิตสูงไปยัง EDX ทุกบิต หากต้องการสร้างศูนย์ที่รู้จักกันว่าไม่เป็นลบหรือรับ 0 / -1 เพื่อเพิ่ม / ย่อยหรือปิดบังด้วย x86 ประวัติศาสตร์บทเรียน: cltqเทียบmovslqและ AT & T กับ Intel cdqeจำสำหรับเรื่องนี้และที่เกี่ยวข้อง
lodsb / d : like mov eax, [rsi]/ rsi += 4ไม่มีธงการอุดตัน (สมมติว่า DF มีความชัดเจนซึ่งมาตรฐานการประชุมที่จำเป็นต้องมีในรายการฟังก์ชั่น) นอกจากนี้ยัง stosb / d บางครั้ง scas และ movs / cmps บ่อยครั้งมากขึ้น
push/ pop reg. เช่นในโหมด 64- บิตpush rsp/ pop rdiเป็น 2 ไบต์ แต่mov rdi, rspต้องการคำนำหน้า REX และ 3 ไบต์
xlatbมีอยู่ แต่ไม่ค่อยมีประโยชน์ ตารางการค้นหาขนาดใหญ่เป็นสิ่งที่ควรหลีกเลี่ยง ฉันไม่เคยพบการใช้งานสำหรับ AAA / DAA หรือคำแนะนำในการบรรจุแบบ BCD หรือ 2-ASCII อื่น ๆ
1 ไบต์lahf/ sahfไม่ค่อยมีประโยชน์ คุณสามารถ lahf / and ah, 1เป็นทางเลือกแทนsetc ahแต่โดยทั่วไปแล้วจะไม่มีประโยชน์
และสำหรับ CF โดยเฉพาะsbb eax,eaxจะต้องมีค่า 0 / -1 หรือแม้กระทั่งที่ไม่มีเอกสาร แต่รองรับ 1 ไบต์salc(ตั้งค่า AL จาก Carry)ซึ่งทำได้อย่างมีประสิทธิภาพsbb al,alโดยไม่ส่งผลกระทบต่อแฟล็ก (ถูกลบใน x86-64) ผมใช้ SALC ในAppreciation ผู้ใช้ท้าทาย # 1: เดนนิส♦
1-byte cmc/ clc/ stc(flip ("complement"), clear หรือ set CF) ไม่ค่อยมีประโยชน์แม้ว่าฉันจะพบว่ามีการใช้งานcmcในการเพิ่มความแม่นยำเพิ่มเติมด้วยฐาน 10 ^ 9 หากต้องการตั้งค่า / ล้าง CF โดยไม่มีเงื่อนไขมักจะจัดให้สิ่งนั้นเกิดขึ้นโดยเป็นส่วนหนึ่งของคำสั่งอื่นเช่นxor eax,eaxล้าง CF และ EAX ไม่มีคำแนะนำที่เทียบเท่าสำหรับแฟล็กเงื่อนไขอื่น ๆ เพียง DF (ทิศทางสตริง) และ IF (อินเตอร์รัปต์) ธงพกเป็นพิเศษสำหรับคำแนะนำมากมาย; กะตั้งมันadc al, 0สามารถเพิ่มลงใน AL ใน 2 ไบต์และฉันกล่าวถึงก่อนหน้านี้ SALC ที่ไม่มีเอกสาร
std/cldไม่ค่อยดูเหมือนมันคุ้มค่า โดยเฉพาะอย่างยิ่งในรหัส 32 บิตจะดีกว่าที่จะใช้decกับตัวชี้และ a movหรือแหล่งหน่วยความจำตัวถูกดำเนินการกับคำสั่ง ALU แทนการตั้งค่า DF ดังนั้นlodsb/ stosbไปลงแทนที่จะขึ้น โดยปกติหากคุณต้องการลงทั้งหมดคุณยังคงมีตัวชี้อื่น ๆ เพิ่มขึ้นดังนั้นคุณต้องมีมากกว่าหนึ่งตัวstdและcldในทั้งฟังก์ชันเพื่อใช้lods/ stosสำหรับทั้งคู่ ให้ใช้คำสั่งสตริงแทนทิศทางที่สูงขึ้น (ข้อกำหนดการโทรมาตรฐานรับประกัน DF = 0 ในรายการฟังก์ชันดังนั้นคุณสามารถสมมติได้ฟรีโดยไม่ต้องใช้cld)
ในต้นฉบับ 8086, ขวานเป็นคนที่พิเศษมาก: คำแนะนำชอบlodsb/ stosb, cbw, mul/ divและอื่น ๆ ใช้งานได้โดยปริยาย ยังคงเป็นกรณีของแน่นอน; x86 ปัจจุบันยังไม่ได้ลด opcodes ใด ๆ ของ 8086 (อย่างน้อยก็ไม่ได้มีเอกสารที่เป็นทางการ) แต่ภายหลังซีพียูได้เพิ่มคำแนะนำใหม่ที่ให้วิธีการที่ดีขึ้น / มีประสิทธิภาพมากขึ้นโดยไม่ต้องคัดลอกหรือสลับไปยัง AX ก่อน (หรือถึง EAX ในโหมด 32 บิต)
เช่น 8086 ขาดการเพิ่มเติมในภายหลังเช่นmovsx/ movzxเพื่อโหลดหรือย้าย + เครื่องหมายขยายหรือ 2 และ 3 ตัวถูกดำเนินการimul cx, bx, 1234ที่ไม่ได้ผลครึ่งปีสูงและไม่มีตัวถูกดำเนินการโดยปริยาย
นอกจากนี้8086 ของคอขวดหลักคือการเรียนการสอนสามารถดึงข้อมูลเพื่อเพิ่มประสิทธิภาพสำหรับรหัสขนาดเป็นสิ่งสำคัญสำหรับการทำงานกลับมาแล้ว นักออกแบบ ISA 8086 (Stephen Morse)ใช้พื้นที่การเข้ารหัส opcode เป็นจำนวนมากในกรณีพิเศษสำหรับ AX / AL รวมถึง opcodes AX / AL พิเศษสำหรับ E-AXU สำหรับคำแนะนำ ALU พื้นฐานทันทีเพียงแค่ opcode + ทันที ไม่มี ModR / M ไบต์ 2 ไบต์add/sub/and/or/xor/cmp/test/... AL,imm8หรือAX,imm16หรือ (ในโหมด 32 EAX,imm32บิต)
แต่ไม่มีกรณีพิเศษEAX,imm8ดังนั้นการเข้ารหัส ModR / M ปกติของadd eax,4จึงสั้นกว่า
สมมุติว่าถ้าคุณจะทำงานกับข้อมูลบางอย่างคุณจะต้องใช้มันใน AX / AL ดังนั้นการสลับการลงทะเบียนกับ AX นั้นเป็นสิ่งที่คุณอาจต้องการทำบางทีอาจจะมากกว่าการคัดลอก register ไปยัง AX ด้วยซ้ำmov.
ทุกอย่างเกี่ยวกับการเข้ารหัสคำสั่ง 8086 รองรับกระบวนทัศน์นี้ตั้งแต่คำสั่งเช่นlodsb/wไปจนถึงการเข้ารหัสกรณีพิเศษทั้งหมดสำหรับ EAX ทันทีจนถึงการใช้งานโดยปริยายแม้กระทั่งการคูณ / หาร
อย่าถูกพาตัวไป ไม่ใช่การชนะโดยอัตโนมัติในการสลับทุกอย่างเป็น EAX โดยเฉพาะถ้าคุณต้องการใช้ทันทีด้วยการลงทะเบียนแบบ 32 บิตแทนที่จะเป็น 8 บิต หรือถ้าคุณต้องการ interleave การดำเนินงานกับหลายตัวแปรในการลงทะเบียนในครั้งเดียว หรือหากคุณกำลังใช้คำแนะนำกับการลงทะเบียน 2 รายการไม่สามารถทำได้ทันที
แต่โปรดจำไว้เสมอ: ฉันกำลังทำอะไรที่จะสั้นลงใน EAX / AL หรือไม่? ฉันสามารถจัดเรียงใหม่เพื่อให้ฉันมีสิ่งนี้ในอัลหรือฉันกำลังใช้ประโยชน์จากอัลที่ดีกว่ากับสิ่งที่ฉันใช้มันไปแล้ว
ผสมผสานการทำงาน 8 บิตและ 32 บิตอย่างอิสระเพื่อใช้ประโยชน์เมื่อใดก็ตามที่ปลอดภัย (คุณไม่จำเป็นต้องดำเนินการลงทะเบียนเต็มรูปแบบหรืออะไรก็ตาม)
cdqมีประโยชน์สำหรับdivความต้องการเป็นศูนย์edxในหลายกรณี
cdqก่อนที่จะลงนามdivหากคุณรู้ว่าเงินปันผลของคุณต่ำกว่า 2 ^ 31 (เช่นไม่เป็นลบเมื่อถือว่าเป็นลงชื่อ) หรือถ้าคุณใช้มันก่อนที่จะตั้งค่าeaxที่อาจมีขนาดใหญ่ โดยปกติ (นอก code-golf) คุณจะใช้cdqเป็นค่าติดตั้งidivและxor edx,edxก่อนdiv
fastcallแบบแผนแพลตฟอร์ม x86 มีการเรียกประชุมจำนวนมาก คุณควรใช้ผู้ที่ผ่านพารามิเตอร์ในการลงทะเบียน บน x86_64 พารามิเตอร์สองสามตัวแรกจะถูกส่งผ่านไปยังการลงทะเบียนดังนั้นจึงไม่มีปัญหา บนแพลตฟอร์ม 32 บิตการเรียกใช้เริ่มต้น ( cdecl) ส่งผ่านพารามิเตอร์ในสแต็กซึ่งไม่ดีสำหรับการเล่นกอล์ฟการเข้าถึงพารามิเตอร์บนสแต็กต้องใช้คำแนะนำที่ยาว
เมื่อใช้fastcallบนแพลตฟอร์ม 32 บิต 2 พารามิเตอร์แรกมักจะผ่านในและecx edxหากฟังก์ชันของคุณมี 3 พารามิเตอร์คุณอาจลองนำไปใช้กับแพลตฟอร์ม 64 บิต
ต้นแบบฟังก์ชัน C สำหรับfastcallการประชุม (นำมาจากคำตอบตัวอย่างนี้ ):
extern int __fastcall SwapParity(int value); // MSVC
extern int __attribute__((fastcall)) SwapParity(int value); // GNU
0100 81C38000 ADD BX,0080
0104 83EB80 SUB BX,-80
ให้เพิ่ม -128 แทนการลบ 128
< 128เป็น<= 127ลดขนาดของตัวถูกดำเนินการทันทีcmpหรือgcc มักชอบจัดเรียงใหม่ เปรียบเทียบเพื่อลดขนาดแม้ว่าจะไม่ใช่ -129 กับ -128
mul(จากนั้นinc/ decเพื่อรับ +1 / -1 เช่นเดียวกับศูนย์)คุณสามารถเป็นศูนย์ eax และ edx โดยคูณด้วยศูนย์ในการลงทะเบียนที่สาม
xor ebx, ebx ; 2B ebx = 0
mul ebx ; 2B eax=edx = 0
inc ebx ; 1B ebx=1
จะส่งผลให้ EAX, EDX และ EBX ทั้งหมดเป็นศูนย์ในเวลาเพียงสี่ไบต์ คุณสามารถเป็นศูนย์ EAX และ EDX ในสามไบต์:
xor eax, eax
cdq
แต่จากจุดเริ่มต้นนั้นคุณจะไม่สามารถลงทะเบียน zeroed ครั้งที่ 3 ในอีกหนึ่งไบต์หรือลงทะเบียน +1 หรือ -1 ในอีก 2 ไบต์ ให้ใช้เทคนิคของ mul แทน
ตัวอย่างเช่นกรณีการใช้งาน: เชื่อมโยงตัวเลขฟีโบนักชีในไบนารี
โปรดทราบว่าหลังจากLOOPวนรอบเสร็จสิ้น ECX จะเป็นศูนย์และสามารถใช้เป็นศูนย์ EDX และ EAX คุณไม่จำเป็นต้องสร้างศูนย์แรกด้วยxorเสมอไป
เราสามารถสันนิษฐานได้ว่าซีพียูอยู่ในสถานะเริ่มต้นที่เป็นที่รู้จักและจัดทำเป็นเอกสารตามแพลตฟอร์มและระบบปฏิบัติการ
ตัวอย่างเช่น:
DOS http://www.fysnet.net/yourhelp.htm
Linux x86 ELF http://asm.sourceforge.net/articles/startup.html
_startในการเข้า ใช่มันเป็นเกมที่ยุติธรรมที่จะใช้ประโยชน์จากสิ่งนั้นถ้าคุณกำลังเขียนโปรแกรมแทนที่จะเป็นฟังก์ชั่น ฉันไม่ได้ในสุดขีด Fibonacci (ในการปฏิบัติการเชื่อมโยงแบบไดนามิก, ld.so ก่อนจะวิ่งกระโดดที่คุณ_startและไม่ขยะลาในทะเบียน แต่คงเป็นเพียงรหัสของคุณ.)
ในการเพิ่มหรือลบ 1 ให้ใช้หนึ่งไบต์incหรือdecคำสั่งที่เล็กกว่าคำสั่งเพิ่มและคำสั่งย่อยแบบหลายไบต์
inc/dec r32พร้อมหมายเลขลงทะเบียนที่เข้ารหัสใน opcode ดังนั้นinc ebxเป็น 1 ไบต์ แต่inc blเป็น 2 ยังคงมีขนาดเล็กกว่าของหลักสูตรสำหรับการลงทะเบียนอื่นที่ไม่ใช่add bl, 1 alนอกจากนี้โปรดทราบว่าinc/ decปล่อย CF ไม่ได้รับการแก้ไข แต่อัพเดตธงอื่น ๆ
lea สำหรับคณิตศาสตร์นี่อาจเป็นหนึ่งในสิ่งแรก ๆ ที่เรียนรู้เกี่ยวกับ x86 แต่ฉันปล่อยไว้ที่นี่เพื่อเป็นการเตือน leaสามารถใช้ในการคูณด้วย 2, 3, 4, 5, 8, หรือ 9 และเพิ่มออฟเซ็ต
ตัวอย่างเช่นการคำนวณebx = 9*eax + 3ในคำสั่งเดียว (ในโหมด 32 บิต):
8d 5c c0 03 lea 0x3(%eax,%eax,8),%ebx
นี่มันไม่มีออฟเซ็ต:
8d 1c c0 lea (%eax,%eax,8),%ebx
ว้าว! แน่นอนleaสามารถใช้ในการทำคณิตศาสตร์เช่นebx = edx + 8*eax + 3การคำนวณการจัดทำดัชนีอาร์เรย์
lea eax, [rcx + 13]เป็นรุ่นที่ไม่มีส่วนเสริมสำหรับโหมด 64 บิต ขนาดตัวถูกดำเนินการ 32 บิต (สำหรับผลลัพธ์) และขนาดที่อยู่ 64 บิต (สำหรับอินพุต)
คำสั่งวนซ้ำและสตริงมีขนาดเล็กกว่าลำดับการเรียนการสอนทางเลือก ส่วนใหญ่จะเป็นประโยชน์loop <label>ซึ่งมีขนาดเล็กกว่าสองคำแนะนำลำดับdec ECXและjnz <label>และlodsbมีขนาดเล็กกว่าและmov al,[esi]inc si
mov ขนาดเล็กลงในทะเบียนที่ต่ำกว่าทันทีหากคุณรู้ว่าบิตส่วนบนของรีจิสเตอร์เป็น 0 คุณสามารถใช้คำสั่งสั้นลงเพื่อย้ายรีจิสเตอร์เข้าสู่รีจิสเตอร์ล่างทันที
b8 0a 00 00 00 mov $0xa,%eax
กับ
b0 0a mov $0xa,%al
push/ popสำหรับ imm8 ถึงศูนย์บิตบนมอบเครดิตให้กับ Peter Cordes xor/ movคือ 4 ไบต์ แต่push/ popเป็น 3 เท่านั้น!
6a 0a push $0xa
58 pop %eax
mov al, 0xaเป็นสิ่งที่ดีถ้าคุณไม่ต้องการขยายศูนย์เต็ม reg แต่ถ้าคุณทำเช่นนั้น xor / mov คือ 4 ไบต์เทียบกับ 3 สำหรับการกด imm8 / pop หรือleaจากค่าคงที่อื่นที่ทราบ ซึ่งอาจเป็นประโยชน์เมื่อใช้ร่วมกับmulการลงทะเบียน 3 ศูนย์ใน 4 ไบต์หรือcdqหากคุณต้องการค่าคงที่จำนวนมาก
[0x80..0xFF]ซึ่งไม่สามารถแทนค่าได้เป็น imm8 แบบขยายสัญญาณ หรือถ้าคุณอยู่แล้วทราบไบต์บนเช่นmov cl, 0x10หลังจากloopการเรียนการสอนเพราะวิธีเดียวที่จะไม่กระโดดคือเมื่อมันทำloop rcx=0(ฉันเดาว่าคุณพูดแบบนี้ แต่ตัวอย่างของคุณใช้xor) คุณสามารถใช้ไบต์ต่ำของการลงทะเบียนสำหรับสิ่งอื่นตราบใดที่สิ่งอื่นทำให้มันกลับเป็นศูนย์ (หรืออะไรก็ตาม) เมื่อคุณทำเสร็จแล้ว เช่นโปรแกรม Fibonacci ของฉันยังคง-1024อยู่ใน ebx และใช้ bl
xchg eax, r32) เช่นmov bl, 10/ dec bl/ jnzเพื่อให้รหัสของคุณไม่สนใจไบต์สูงของ RBX
หลังจากคำแนะนำเกี่ยวกับการคำนวณทางคณิตศาสตร์มากมายตั้งค่าสถานะพกพา (ไม่ได้ลงชื่อ) และตั้งค่าสถานะโอเวอร์โฟลว์ (ลงนาม) โดยอัตโนมัติ ( ข้อมูลเพิ่มเติม ) การตั้งค่าสถานะการตั้งค่าสถานะและการตั้งค่าสถานะเป็นศูนย์หลังจากการดำเนินการทางคณิตศาสตร์และตรรกะมากมาย สามารถใช้สำหรับการแยกย่อยตามเงื่อนไข
ตัวอย่าง:
d1 f8 sar %eax
ZF ถูกกำหนดโดยคำสั่งนี้ดังนั้นเราจึงสามารถใช้มันเพื่อการแยกทางแบบมีเงื่อนไข
test al,1นั้น คุณมักจะไม่ได้รับฟรี (หรือand al,1เพื่อสร้างจำนวนเต็ม 0/1 ขึ้นอยู่กับเลขคี่ / คู่)
test/ cmp" ดังนั้นนั่นจะเป็นมือใหม่ที่ค่อนข้างดี x86 แต่ก็ยังคุ้มค่ากับการลงคะแนน
นี่ไม่ใช่เฉพาะ x86 แต่เป็นเคล็ดลับการประกอบเริ่มต้นที่ใช้กันอย่างแพร่หลาย หากคุณรู้ว่าขณะที่ลูปจะทำงานอย่างน้อยหนึ่งครั้งให้เขียนลูปเป็นลูปที่ทำในขณะที่การตรวจสอบสภาพลูปที่ปลายมักบันทึกคำสั่งการกระโดดแบบ 2 ไบต์ ในกรณีพิเศษคุณอาจจะสามารถใช้งานloopได้
do{}while()สำนวนธรรมชาติวนซ้ำในแอสเซมบลี (โดยเฉพาะอย่างยิ่งสำหรับประสิทธิภาพ) โปรดทราบว่า 2 ไบต์jecxz/ jrcxzก่อนที่ลูปจะทำงานได้ดีมากloopในการจัดการ "ต้องใช้เวลาเป็นศูนย์" เคส "อย่างมีประสิทธิภาพ" (บน CPU ที่หายากซึ่งloopไม่ได้ช้า) jecxzยังสามารถใช้งานได้ในลูปเพื่อใช้ awhile(ecx){}โดยมีjmpที่ด้านล่าง
ระบบวี x86 ใช้สแต็คและระบบวีใช้ x86-64 rdi, rsi, rdx, rcxฯลฯ สำหรับป้อนพารามิเตอร์และraxเป็นค่าตอบแทน แต่มันเป็นอย่างดีที่เหมาะสมที่จะใช้เรียกประชุมของคุณเอง __fastcallใช้ecxและedxเป็นพารามิเตอร์การป้อนข้อมูลและคอมไพเลอร์อื่น ๆ / ระบบปฏิบัติการที่ใช้การประชุมของตัวเอง ใช้สแต็กและสิ่งที่ลงทะเบียนเป็นอินพุต / เอาต์พุตเมื่อสะดวก
ตัวอย่าง: ตัวนับไบต์ซ้ำโดยใช้หลักการเรียกที่ชาญฉลาดสำหรับโซลูชัน 1 ไบต์
Meta: การเขียนป้อนข้อมูลเพื่อลงทะเบียน , การเขียนออกไปลงทะเบียน
แหล่งข้อมูลอื่น ๆ : บันทึกของ Agner Fog เกี่ยวกับการเรียกประชุม
int 0x80ต้องมีการตั้งค่ามากมาย
int 0x80ในรหัส 32 บิตหรือรหัสsyscall64 บิตที่จะเรียกใช้sys_writeเป็นวิธีที่ดีเท่านั้น มันเป็นสิ่งที่ฉันใช้สำหรับสุดขีด Fibonacci ในรหัส 64 บิตเพื่อให้คุณสามารถ__NR_write = 1 = STDOUT_FILENO mov eax, ediหรือถ้าจำนวนไบต์บนของ EAX เป็นศูนย์จะเป็นmov al, 4รหัส 32 บิต คุณสามารถcall printfหรือputsฉันเดาและเขียนคำตอบ "x86 asm สำหรับ Linux + glibc" ฉันคิดว่ามันสมเหตุสมผลที่จะไม่นับพื้นที่เข้าร่วม PLT หรือ GOT หรือรหัสห้องสมุดเอง
char*bufและสร้างสตริงในนั้นด้วยการจัดรูปแบบด้วยตนเอง เช่นนี้(เหมาะสำหรับความเร็วอย่างเชื่องช้า) asm FizzBuzzที่ฉันได้รับข้อมูลสตริงเข้าสู่การลงทะเบียนแล้วเก็บไว้ด้วยmovเพราะสตริงนั้นสั้นและยาวคงที่
CMOVccและการตั้งเงื่อนไขSETccนี่เป็นคำเตือนเพิ่มเติมให้กับฉันเอง แต่มีคำแนะนำการตั้งค่าตามเงื่อนไขและมีคำแนะนำการย้ายตามเงื่อนไขอยู่ในโปรเซสเซอร์ P6 (Pentium Pro) หรือใหม่กว่า มีคำแนะนำมากมายที่ขึ้นอยู่กับการตั้งค่าสถานะหนึ่งใน EFLAGS
cmovมี opcode ขนาด 2 ไบต์ ( 0F 4x +ModR/M) ดังนั้นจึงมีขนาดต่ำสุด 3 ไบต์ แต่แหล่งที่มาคือ r / m32 ดังนั้นคุณสามารถโหลดแบบมีเงื่อนไขใน 3 ไบต์ อื่น ๆ กว่ากิ่งจะเป็นประโยชน์ในกรณีมากกว่าsetcc cmovccยังคงพิจารณาชุดคำสั่งทั้งหมดไม่ใช่แค่พื้นฐาน 386 คำแนะนำ (แม้ว่าคำสั่ง SSE2 และ BMI / BMI2 มีขนาดใหญ่มากซึ่งไม่ค่อยมีประโยชน์ rorx eax, ecx, 326 ไบต์ยาวกว่า mov + ror ดีสำหรับการแสดงไม่ใช่กอล์ฟเว้นแต่ว่า POPCNT หรือ PDEP จะช่วยประหยัดเกาะได้มากมาย)
setccขอบคุณฉันได้เพิ่ม
jmpไบต์ด้วยการจัดเรียง if / then มากกว่า if / then / elseนี่เป็นพื้นฐานที่สำคัญมากเพียงแค่คิดว่าฉันจะโพสต์สิ่งนี้เป็นสิ่งที่ควรพิจารณาเมื่อเล่นกอล์ฟ ยกตัวอย่างเช่นพิจารณารหัสตรงไปตรงมาต่อไปนี้เพื่อถอดรหัสตัวเลขฐานสิบหก:
cmp $'A', %al
jae .Lletter
sub $'0', %al
jmp .Lprocess
.Lletter:
sub $('A'-10), %al
.Lprocess:
movzbl %al, %eax
...
สิ่งนี้สามารถย่อให้สั้นลงได้สองไบต์โดยให้ตัวพิมพ์เล็กและใหญ่ตกลงมาเป็นตัวพิมพ์เล็ก:
cmp $'A', %al
jb .digit
sub $('A'-'0'-10), %eax
.digit:
sub $'0', %eax
movzbl %al, %eax
...
subเวลาแฝงที่เพิ่มบนเส้นทางวิกฤติสำหรับกรณีหนึ่งไม่ได้เป็นส่วนหนึ่งของห่วงโซ่อ้างอิงแบบวนซ้ำ (เช่นที่นี่ซึ่งแต่ละอินพุตหลักมีความเป็นอิสระ ) แต่ฉันเดาว่า +1 BTW ตัวอย่างของคุณมีการเพิ่มประสิทธิภาพพลาดเฉพาะกิจการ: ถ้าคุณกำลังจะต้องมีmovzxที่สิ้นสุดอยู่แล้วจากนั้นใช้sub $imm, %alไม่ได้ EAX ที่จะใช้ประโยชน์จากการที่ไม่มีการเข้ารหัส modrm 2 op $imm, %alไบต์ของ
cmpโดยการทำsub $'A'-10, %al; jae .was_alpha; add $('A'-10)-'0'. (ฉันคิดว่าฉันมีเหตุผลที่ถูกต้อง) โปรดทราบว่า'A'-10 > '9'ไม่มีความกำกวม การลบการแก้ไขสำหรับตัวอักษรจะตัดทศนิยมหลัก ดังนั้นนี่จึงปลอดภัยถ้าเราสมมติว่าอินพุตของเรานั้นเป็นเลขฐานสิบหกที่ถูกต้องเช่นเดียวกับที่คุณทำ
คุณสามารถดึงข้อมูลวัตถุตามลำดับจากสแต็กได้โดยตั้งค่า esi เป็น esp และดำเนินการตามลำดับของ lodsd / xchg reg, eax
pop eax/ pop edx/ ... หากคุณจำเป็นต้องปล่อยให้พวกเขาในกองคุณสามารถpushให้พวกเขาทั้งหมดกลับมาหลังจากที่จะเรียกคืน ESP ยังคง 2 mov esi,espไบต์ต่อวัตถุโดยไม่จำเป็นต้อง หรือคุณหมายถึงวัตถุ 4 ไบต์ในรหัส 64 บิตที่popจะได้รับ 8 ไบต์? BTW คุณสามารถใช้popเพื่อวนลูปบัฟเฟอร์ด้วยประสิทธิภาพที่ดีกว่าlodsdเช่นสำหรับการเพิ่มความแม่นยำสูงใน Extreme Fibonacci
ในการคัดลอกการลงทะเบียน 64- บิตให้ใช้push rcx; pop rdxแทนที่จะเป็น mov3
ขนาดตัวถูกดำเนินการเริ่มต้นของ push / pop คือ 64- บิตโดยไม่จำเป็นต้องมีคำนำหน้า REX
51 push rcx
5a pop rdx
vs.
48 89 ca mov rdx,rcx
(คำนำหน้าขนาดตัวถูกดำเนินการสามารถแทนที่ขนาด push / pop เป็น 16 บิต แต่ขนาดตัวถูกดำเนินการ push-pop 32 บิตไม่สามารถเข้ารหัสในโหมด 64 บิตแม้กับ REX.W = 0)
หากการลงทะเบียนอย่างใดอย่างหนึ่งหรือทั้งสองอย่างเป็นr8.. r15ให้ใช้movเพราะการพุชและ / หรือป๊อปจะต้องใช้คำนำหน้า REX กรณีที่เลวร้ายที่สุดสิ่งนี้จะเสียจริงถ้าทั้งคู่ต้องการคำนำหน้า REX เห็นได้ชัดว่าคุณควรหลีกเลี่ยง r8..r15 ต่อไปในรหัสกอล์ฟ
คุณสามารถให้แหล่งข้อมูลของคุณอ่านได้มากขึ้นในขณะที่พัฒนาด้วยแมโคร NASMนี้ เพียงจำไว้ว่ามันทำตามขั้นตอนใน 8 ไบต์ด้านล่าง RSP (ในพื้นที่สีแดงใน x86-64 System V) แต่ภายใต้สภาวะปกติมันเป็นการแทนที่สำหรับ 64- บิตmov r64,r64หรือmov r64, -128..127
; mov %1, %2 ; use this macro to copy 64-bit registers in 2 bytes (no REX prefix)
%macro MOVE 2
push %2
pop %1
%endmacro
ตัวอย่าง:
MOVE rax, rsi ; 2 bytes (push + pop)
MOVE rbp, rdx ; 2 bytes (push + pop)
mov ecx, edi ; 2 bytes. 32-bit operand size doesn't need REX prefixes
MOVE r8, r10 ; 4 bytes, don't use
mov r8, r10 ; 3 bytes, REX prefix has W=1 and the bits for reg and r/m being high
xchg eax, edi ; 1 byte (special xchg-with-accumulator opcodes)
xchg rax, rdi ; 2 bytes (REX.W + that)
xchg ecx, edx ; 2 bytes (normal xchg + modrm)
xchg rcx, rdx ; 3 bytes (normal REX + xchg + modrm)
xchgส่วนหนึ่งของตัวอย่างเป็นเพราะบางครั้งคุณจำเป็นต้องได้รับค่าลงใน EAX หรือ Rax และไม่เกี่ยวกับการดูแลรักษาชุดเก่า แต่ดัน / ป๊อปไม่ได้ช่วยให้คุณแลกเปลี่ยนได้จริง
push 200; pop edx- 3 ไบต์สำหรับการเริ่มต้น