x86-64 মেশিন কোড ফাংশন, 30 বাইট।
@ লেভেল রিভার সেন্ট দ্বারা সি উত্তর হিসাবে একই পুনরাবৃত্তি যুক্তি ব্যবহার করে । (সর্বোচ্চ পুনরাবৃত্তির গভীরতা = 100)
puts(3)
Libc থেকে ফাংশনটি ব্যবহার করে , যা সাধারণ এক্সিকিউটেবলের সাথে যে কোনওভাবে লিঙ্কযুক্ত। এটি x86-64 সিস্টেম ভি এবিআই ব্যবহার করে কল করা যায়, লিনাক্স বা ওএস এক্স-এর সি থেকে, এবং এটি মনে করা হয় না এমন কোনও রেজিস্টারগুলি ক্লোবার করে না।
objdump -drwC -Mintel
আউটপুট, ব্যাখ্যা সহ মন্তব্য
0000000000400340 <g>: ## wrapper function
400340: 6a 64 push 0x64
400342: 5f pop rdi ; mov edi, 100 in 3 bytes instead of 5
; tailcall f by falling into it.
0000000000400343 <f>: ## the recursive function
400343: ff cf dec edi
400345: 97 xchg edi,eax
400346: 6a 0a push 0xa
400348: 5f pop rdi ; mov edi, 10
400349: 0f 8c d1 ff ff ff jl 400320 <putchar> # conditional tailcall
; if we don't tailcall, then eax=--n = arg for next recursion depth, and edi = 10 = '\n'
40034f: 89 f9 mov ecx,edi ; loop count = the ASCII code for newline; saves us one byte
0000000000400351 <f.loop>:
400351: 50 push rax ; save local state
400352: 51 push rcx
400353: 97 xchg edi,eax ; arg goes in rdi
400354: e8 ea ff ff ff call 400343 <f>
400359: 59 pop rcx ; and restore it after recursing
40035a: 58 pop rax
40035b: e2 f4 loop 400351 <f.loop>
40035d: c3 ret
# the function ends here
000000000040035e <_start>:
0x040035e - 0x0400340 = 30 bytes
# not counted: a caller that passes argc-1 to f() instead of calling g
000000000040035e <_start>:
40035e: 8b 3c 24 mov edi,DWORD PTR [rsp]
400361: ff cf dec edi
400363: e8 db ff ff ff call 400343 <f>
400368: e8 c3 ff ff ff call 400330 <exit@plt> # flush I/O buffers, which the _exit system call (eax=60) doesn't do.
দিয়ে নির্মিত yasm -felf64 -Worphan-labels -gdwarf2 golf-googol.asm &&
gcc -nostartfiles -o golf-googol golf-googol.o
। আমি আসল এনএএসএম উত্সটি পোস্ট করতে পারি, তবে এএসএম নির্দেশাবলী যেহেতু বিচ্ছিন্নতার মধ্যে রয়েছে ঠিক তেমন বিশৃঙ্খলার মতো বলে মনে হয়েছিল।
putchar@plt
এর থেকে 128 বাইটেরও কম দূরে jl
, তাই আমি লাফের কাছে 6-বাইটের পরিবর্তে 2 বাইট শর্ট জাম্প ব্যবহার করতে পারতাম, তবে এটি কেবলমাত্র একটি ছোট্ট এক্সিকিউটেবলের ক্ষেত্রেই সত্য, বড় প্রোগ্রামের অংশ হিসাবে নয়। সুতরাং আমি মনে করি না যে আমি লিবিসির পুটগুলি বাস্তবায়নের আকারটি গণনা না করে ন্যায্যতা প্রমাণ করতে পারি যদি আমি এটিতে পৌঁছানোর জন্য একটি সংক্ষিপ্ত জেসিসি এনকোডিংয়েরও সুবিধা গ্রহণ করি।
পুনরাবৃত্তির প্রতিটি স্তরে 24 বি স্ট্যাক স্পেস ব্যবহার করা হয় (2 টি পুশ করে এবং ফিরে আসার ঠিকানা সিএলএল দ্বারা চালিত)। অন্য সমস্ত গভীরতা putchar
স্ট্যাকের সাথে কল করবে কেবল 16 দ্বারা নয়, 8 দ্বারা প্রান্তিক, তাই এটি এবিআই লঙ্ঘন করে না। স্ট্যাকের এক্সএমজি রেজিস্টারগুলিকে ছড়িয়ে দেওয়ার জন্য সারিবদ্ধ স্টোর ব্যবহার করা একটি স্টিডিও বাস্তবায়ন ত্রুটিযুক্ত হবে। তবে গ্লিবিসি এটি putchar
করে না, সম্পূর্ণ বাফারিং সহ একটি পাইপে লিখন বা লাইন বাফারিং সহ একটি টার্মিনালে লিখিত। উবুন্টু 15.10 এ পরীক্ষা করা হয়েছে। .loop
পুনরাবৃত্তির কল করার আগে স্ট্যাকটিকে আরও 8 দ্বারা অফসেট করতে, এটি একটি ডামি পুশ / পপ দিয়ে ঠিক করা যেতে পারে ।
প্রমাণ যে এটি নতুন লাইনের সঠিক সংখ্যাটি মুদ্রণ করে:
# with a version that uses argc-1 (i.e. the shell's $i) instead of a fixed 100
$ for i in {0..8}; do echo -n "$i: "; ./golf-googol $(seq $i) |wc -c; done
0: 1
1: 10
2: 100
3: 1000
4: 10000
5: 100000
6: 1000000
7: 10000000
8: 100000000
... output = 10^n newlines every time.
আমার এটির প্রথম সংস্করণটি 43 বি ছিল এবং puts()
এটি 9 টি নতুন লাইন (এবং একটি সমাপ্তি 0 বাইট) এর বাফারে ব্যবহৃত হয়েছিল, সুতরাং পুটগুলি 10 তম সংযোজন করবে। এই পুনরাবৃত্তি বেস-কেস সি অনুপ্রেরণার আরও কাছাকাছি ছিল।
10 ^ 100 থেকে আলাদাভাবে ফ্যাক্টরিং করা বাফারটি সংক্ষিপ্ত করে দিতে পারে, সম্ভবত 4 টি নতুন লাইন, 5 বাইট সাশ্রয় করতে পারে, তবে পুটচার ব্যবহার এতদূর ভাল। এটির জন্য কেবল একটি পূর্ণসংখ্যার অর্গ প্রয়োজন, পয়েন্টার নয় এবং কোনও বাফার নেই। সি স্ট্যান্ডার্ড বাস্তবায়নের অনুমতি দেয় যেখানে এটি ম্যাক্রোর জন্য putc(val, stdout)
, তবে গ্লিবিসি-তে এটি একটি আসল ফাংশন হিসাবে উপস্থিত থাকে যা আপনি এসএমএস থেকে কল করতে পারেন।
10 এর পরিবর্তে কলটিতে কেবলমাত্র একটি নিউলাইন মুদ্রণ করার অর্থ হ'ল 10 নতুন লাইনের আরও একটি ফ্যাক্টর পেতে আমাদের পুনরাবৃত্তির সর্বোচ্চ গভীরতা 1 দ্বারা বাড়িয়ে নেওয়া দরকার। যেহেতু 99 এবং 100 উভয়ই সাইন-প্রসারিত 8-বিট তাত্ক্ষণিকভাবে প্রতিনিধিত্ব করতে পারে, push 100
এখনও মাত্র 2 বাইট।
আরও ভাল, 10
একটি রেজিস্টার থাকা একটি বাইট সংরক্ষণ করে একটি নতুন লাইন এবং লুপ কাউন্টার হিসাবে কাজ করে।
বাইটস সংরক্ষণের জন্য ধারণা
একটি 32-বিট সংস্করণটি একটি বাইট সংরক্ষণ করতে পারে dec edi
, তবে স্ট্যাক-আরগস কলিং কনভেনশন (পুটচারের মতো লাইব্রেরি ফাংশনগুলির জন্য) লেজ-কলকে সহজেই কম করে তোলে এবং সম্ভবত আরও জায়গায় আরও বাইট লাগবে। আমি প্রাইভেটের জন্য একটি রেজিস্টার-আরগ কনভেনশন ব্যবহার করতে পারতাম f()
, কেবলমাত্র তাকে ডেকে আনা হয়েছিল g()
তবে আমি পুটচারকে টেল-কল করতে পারি না (কারণ চ () এবং পুতচর () আলাদা স্ট্যাক-আরগ নিতে পারে)।
কল করা অবস্থায় সংরক্ষণ / পুনরুদ্ধার না করে কল (চ) কলারের অবস্থা সংরক্ষণ করা সম্ভব হবে be এটি সম্ভবত সফল হয়, কারণ এটি সম্ভবত শাখার প্রতিটি দিকে পৃথকভাবে পেতে প্রয়োজন হবে, এবং টেলকেলিংয়ের সাথে সামঞ্জস্যপূর্ণ নয়। আমি চেষ্টা করেছিলাম কিন্তু কোনও সঞ্চয় পাইনি।
স্ট্যাকের উপর একটি লুপ কাউন্টার রাখা (লুপে আরসিএলকে ধাক্কা / পপ করার পরিবর্তে) কোনও উপকারে আসেনি। এটি যে সংস্করণটি রাখে তার সাথে এটি 1 বি আরও খারাপ ছিল এবং সম্ভবত এই সংস্করণটির সাথে আরও বেশি ক্ষতি হয়েছে যা আরসিএক্সকে আরও সস্তায় সেট করে।