i386 (x86-32) মেশিন কোড, 8 বাইট (স্বাক্ষরবিহীন 9B)
আমাদের যদি ইনপুটটিতে হ্যান্ডেল করতে হয় তবে + 1 বি b = 0
।
amd64 (x86-64) মেশিন কোড, 9 বাইট (স্বাক্ষরবিহীন জন্য 10 বি , বা 64 বি পূর্ণসংখ্যার জন্য 14 বি 13 বি স্বাক্ষরিত বা স্বাক্ষরযুক্ত)
এমডি 64 এ স্বাক্ষরিত হওয়ার জন্য 10 9 বি যা ইনপুট = 0 দিয়ে ব্রেক হয়
ইনপুটগুলি 32 বিট-নন-শূন্য স্বাক্ষরিত পূর্ণসংখ্যা eax
এবং ecx
। আউটপুট ইন eax
।
## 32bit code, signed integers: eax, ecx
08048420 <gcd0>:
8048420: 99 cdq ; shorter than xor edx,edx
8048421: f7 f9 idiv ecx
8048423: 92 xchg edx,eax ; there's a one-byte encoding for xchg eax,r32. So this is shorter but slower than a mov
8048424: 91 xchg ecx,eax ; eax = divisor(from ecx), ecx = remainder(from edx), edx = quotient(from eax) which we discard
; loop entry point if we need to handle ecx = 0
8048425: 41 inc ecx ; saves 1B vs. test/jnz in 32bit mode
8048426: e2 f8 loop 8048420 <gcd0>
08048428 <gcd0_end>:
; 8B total
; result in eax: gcd(a,0) = a
এই লুপ কাঠামো যেখানে পরীক্ষার ক্ষেত্রে ব্যর্থ হয় ecx = 0
। ( শূন্য দ্বারা বিভাজনে div
একটি #DE
হার্ডওয়্যার নির্বাহের কারণ ঘটায় ((লিনাক্সে, কার্নেল একটি SIGFPE
(ভাসমান পয়েন্ট ব্যতিক্রম) সরবরাহ করে) the লুপ এন্ট্রি পয়েন্টটি যদি ঠিক আগে থাকে তবে inc
আমরা সমস্যাটি এড়াতে চাই The x86-64 সংস্করণ এটি পরিচালনা করতে পারে বিনামূল্যে জন্য, নীচে দেখুন।
মাইকের শ্লান্টার উত্তরই এর সূচনা পয়েন্ট ছিল । আমার লুপটি তার মতো একই কাজ করে তবে স্বাক্ষরিত পূর্ণসংখ্যার জন্য কারণ এর cdq
চেয়ে এক বাইট আরও খাটো xor edx,edx
। এবং হ্যাঁ, এটি এক বা উভয় ইনপুট নেতিবাচক সাথে সঠিকভাবে কাজ করে। মাইকের সংস্করণটি দ্রুত সঞ্চালিত হবে এবং ইউওপ ক্যাশে কম স্থান নেবে ( xchg
ইন্টেল সিপিইউতে 3 টি উপ, এবং loop
বেশিরভাগ সিপিইউতে সত্যই ধীর হয় ), তবে এই সংস্করণটি মেশিন-কোড আকারে জয়ী হয়।
আমি প্রথমে লক্ষ্য করিনি যে প্রশ্নটির জন্য স্বাক্ষরবিহীন 32 বিট দরকার। xor edx,edx
পরিবর্তে ফিরে যেতে cdq
এক বাইট খরচ হবে। div
একই আকারের idiv
এবং অন্য সমস্ত কিছু একই থাকতে পারে ( xchg
ডেটা মুভমেন্ট এবং inc/loop
এখনও কাজ করার জন্য))
মজার বিষয় হল, 64 বিট অপারেন্ড-আকারের ( rax
এবং rcx
) জন্য, স্বাক্ষরযুক্ত এবং স্বাক্ষরবিহীন সংস্করণগুলি একই আকার। স্বাক্ষরিত সংস্করণটির জন্য cqo
(2 বি) জন্য একটি রেক্স প্রিফিক্স প্রয়োজন , তবে স্বাক্ষরযুক্ত সংস্করণটি এখনও 2 বি ব্যবহার করতে পারে xor edx,edx
।
Bit৪ বিট কোডে, inc ecx
2 বি: একক বাইট inc r32
এবং dec r32
অপকডগুলি REX উপসর্গ হিসাবে পুনঃপ্রেরণ করা হয়েছিল। inc/loop
64bit মোডে কোন কোড আকার সংরক্ষণ করে না, তাই হয়তো আপনি পাশাপাশি test/jnz
। Bit৪ বিট পূর্ণসংখ্যার উপর অপারেটিং আরএক্স প্রিফিক্সগুলিতে নির্দেশ ব্যতীত অন্য একটি বাইট যুক্ত করে, বাদে loop
বা ছাড়াই jnz
। বাকী অংশের পক্ষে কম gcd((2^32), (2^32 + 1))
32 বিতে (যেমন ) সমস্ত জিরো রাখা সম্ভব , তাই আমাদের পুরো আরসিএক্স পরীক্ষা করা দরকার এবং এটি দিয়ে একটি বাইট সংরক্ষণ করতে পারি না test ecx,ecx
। তবে ধীর jrcxz
ইনসনটি কেবলমাত্র 2 বি, এবং আমরা ecx=0
এন্ট্রি পরিচালনা করতে লুপের শীর্ষে রাখতে পারি :
## 64bit code, unsigned 64 integers: rax, rcx
0000000000400630 <gcd_u64>:
400630: e3 0b jrcxz 40063d <gcd_u64_end> ; handles rcx=0 on input, and smaller than test rcx,rcx/jnz
400632: 31 d2 xor edx,edx ; same length as cqo
400634: 48 f7 f1 div rcx ; REX prefixes needed on three insns
400637: 48 92 xchg rdx,rax
400639: 48 91 xchg rcx,rax
40063b: eb f3 jmp 400630 <gcd_u64>
000000000040063d <gcd_u64_end>:
## 0xD = 13 bytes of code
## result in rax: gcd(a,0) = a
32 এবং 64 বি সংস্করণের জন্য গডবোল্ট কম্পাইলার এক্সপ্লোরারে উত্স এবং asm আউটপুটmain
চালায় এমন একটি সহ পুরো চলমান টেস্ট প্রোগ্রাম । 32 বিবিট ( ), 64 বিবিট ( ) এবং এক্স 32 এবিআই ( ) এর জন্য পরীক্ষিত এবং কাজ করছে ।printf("...", gcd(atoi(argv[1]), atoi(argv[2])) );
-m32
-m64
-mx32
এছাড়াও অন্তর্ভুক্ত রয়েছে: কেবলমাত্র পুনরাবৃত্ত বিয়োগের ব্যবহার করে এমন একটি সংস্করণ , যা স্বাক্ষরবিহীন নয়, এমনকি x86-64 মোডের জন্য 9 বি, এবং যেকোন একটি ইনপুট একটি স্বেচ্ছাসেবী রেজিস্টারে নিতে পারে। তবে এটি প্রবেশের সময় 0 ইনপুট হ্যান্ডেল করতে পারে না (এটি sub
শূন্য উৎপন্ন করার সময় সনাক্ত করে, যা x - 0 কখনই করে না)।
32 বিট সংস্করণের জন্য জিএনইউ সি ইনলাইন asm উত্স (সংকলন gcc -m32 -masm=intel
)
int gcd(int a, int b) {
asm (// ".intel_syntax noprefix\n"
// "jmp .Lentry%=\n" // Uncomment to handle div-by-zero, by entering the loop in the middle. Better: `jecxz / jmp` loop structure like the 64b version
".p2align 4\n" // align to make size-counting easier
"gcd0: cdq\n\t" // sign extend eax into edx:eax. One byte shorter than xor edx,edx
" idiv ecx\n"
" xchg eax, edx\n" // there's a one-byte encoding for xchg eax,r32. So this is shorter but slower than a mov
" xchg eax, ecx\n" // eax = divisor(ecx), ecx = remainder(edx), edx = garbage that we will clear later
".Lentry%=:\n"
" inc ecx\n" // saves 1B vs. test/jnz in 32bit mode, none in 64b mode
" loop gcd0\n"
"gcd0_end:\n"
: /* outputs */ "+a" (a), "+c"(b)
: /* inputs */ // given as read-write outputs
: /* clobbers */ "edx"
);
return a;
}
সাধারণত আমি asm এ পুরো ফাংশনটি লিখতাম, তবে GNU C ইনলাইন asm একটি স্নিপেট অন্তর্ভুক্ত করার সেরা উপায় বলে মনে হয় যা আমরা বেছে নিই না কেন কিছু রেজিমে / আউটপুট থাকতে পারে। আপনি দেখতে পাচ্ছেন, জিএনইউ সি ইনলাইন asm সিনট্যাক্সটি asm কে কুশ্রী এবং গোলমাল করে। এটি asm শেখার সত্যিই একটি কঠিন উপায় ।
এটি প্রকৃতপক্ষে সংকলন এবং .att_syntax noprefix
মোডে কাজ করবে , কারণ ব্যবহৃত সমস্ত ইনসগুলি হয় একক / না অপেরাড বা xchg
। আসলেই কোনও দরকারী পর্যবেক্ষণ নয়।