x86 32-বিট (i386) মেশিন কোড ফাংশন, 13 বাইট
কলিং কনভেনশন: আই 386 সিস্টেম ভি (স্ট্যাক আরোগুলি), সাথে-শেষের-তালিকার শেষের জন্য প্রেরণক / টার্মিনেটর হিসাবে একটি NULL পয়েন্টার রয়েছে । (ক্লোবার্স ইডিআই, অন্যথায় এসআইএসভি মেনে চলে)।
সি (এবং asm) প্রকারভেদ ফাংশনগুলিতে প্রকারের তথ্য প্রেরণ করবেন না, সুতরাং কোনও প্রকারের স্ট্রাক্ট / শ্রেণীর অবজেক্ট (বা এরূপের জন্য পয়েন্টার) কে পাশ করে এমন কোনও কনভেনশনেই কোনও স্পষ্ট প্রকারের তথ্য সহ পূর্ণসংখ্যা বা অ্যারে পাস করার ওপির বর্ণনাকে কার্যকর করা যেতে পারে so ), স্ট্যাকের উপর খালি পূর্ণসংখ্যা নয়। সুতরাং আমি ধরে নেওয়ার সিদ্ধান্ত নিয়েছি যে সমস্ত আরোগুলি নন-নুল পয়েন্টার, এবং কলার একটি নূন্যতম টার্মিনেটর পাস করে।
POSIX এরexecl(3)
মতো ক্রিয়াকলাপগুলির জন্য কার্যত সিগুলিতে একটি নাল-টার্মিনেটেড পয়েন্টার তালিকাটি ব্যবহৃত হয় : int execl(const char *path, const char *arg, ... /* (char *) NULL */);
সি int foo(...);
কোনও স্থির তর্ক ছাড়াই প্রোটোটাইপগুলিকে অনুমতি দেয় না , তবে int foo();
একই জিনিসটি বোঝায়: আরোগুলি অনির্ধারিত। (এটির অর্থ সি ++ এর থেকে পৃথক নয় int foo(void)
)। যাই হোক না কেন, এটি একটি asm উত্তর। এই ফাংশনটি সরাসরি কল করতে একটি সি সংকলক কোক্সিং করা আকর্ষণীয় তবে প্রয়োজনীয় নয়।
nasm -felf32 -l/dev/stdout arg-count.asm
কিছু মন্তব্য লাইন সরানো আছে।
24 global argcount_pointer_loop
25 argcount_pointer_loop:
26 .entry:
28 00000000 31C0 xor eax, eax ; search pattern = NULL
29 00000002 99 cdq ; counter = 0
30 00000003 89E7 mov edi, esp
31 ; scasd ; edi+=4; skip retaddr
32 .scan_args:
33 00000005 42 inc edx
34 00000006 AF scasd ; cmp eax,[edi] / edi+=4
35 00000007 75FC jne .scan_args
36 ; dec edx ; correct for overshoot: don't count terminator
37 ; xchg eax,edx
38 00000009 8D42FE lea eax, [edx-2] ; terminator + ret addr
40 0000000C C3 ret
size = 0D db $ - .entry
প্রশ্নটি দেখায় যে ফাংশনটি অবশ্যই 0টিতে ফিরে আসতে সক্ষম হবে এবং আমি সিদ্ধান্তটি আরগের গণনায় শেষ হওয়া এনএলএল পয়েন্টারকে অন্তর্ভুক্ত না করে সেই প্রয়োজনীয়তাটি অনুসরণ করার সিদ্ধান্ত নিয়েছি। যদিও এটির জন্য 1 বাইট খরচ হয়। (12-বাইট সংস্করণের জন্য, এলইএটি সরিয়ে ফেলুন এবং scasd
লুপ এবং এর বাইরের অংশটি নিরঙ্কিত করুন I xchg
তবে dec edx
আমি এলইএই ব্যবহার করেছিলাম কারণ এটি অন্য তিনটি নির্দেশাবলীর সাথে একইভাবে ব্যয় করা হলেও এটি আরও কার্যকর, সুতরাং ফাংশনটি কম uops।)
পরীক্ষার জন্য সি কলার :
এর সাথে নির্মিত:
nasm -felf32 -l /dev/stdout arg-count.asm | cut -b -28,$((28+12))- &&
gcc -Wall -O3 -g -std=gnu11 -m32 -fcall-used-edi arg-count.c arg-count.o -o ac &&
./ac
-fcall-used-edi
এমনকি- O0 এ জিসিসি বলার জন্য edi
এটি সংরক্ষণ করতে / পুনরুদ্ধার না করে ফাংশন ক্লোবারকে ধরে নিতেও বলা প্রয়োজন , কারণ আমি একটি সি স্টেটমেন্টে ( printf
কল) এমন অনেক কল ব্যবহার করেছি যা এমনকি -O0
ইডিআই ব্যবহার করে। এটি main
গিগিবির সাথে লিনাক্সে, জিসিসি'র নিজস্ব কলার (সিআরটি কোডে) থেকে ক্লোবার ইডিআইয়ের পক্ষে নিরাপদ বলে মনে হয় , তবে অন্যথায় এটি মিশ্রিত / ম্যাচ কোডের সাথে আলাদা আলাদা সংকলন করা সম্পূর্ণ জাল -fcall-used-reg
। __attribute__
এটির কোনও সংস্করণ নেই যাতে কাস্টম কলিং কনভেনশনগুলির সাথে asm ফাংশনগুলি স্বাভাবিক থেকে আলাদা ঘোষণা করি।
#include <stdio.h>
int argcount_rep_scas(); // not (...): ISO C requires at least one fixed arg
int argcount_pointer_loop(); // if you declare args at all
int argcount_loopne();
#define TEST(...) printf("count=%d = %d = %d (scasd/jne) | (rep scas) | (scas/loopne)\n", \
argcount_pointer_loop(__VA_ARGS__), argcount_rep_scas(__VA_ARGS__), \
argcount_loopne(__VA_ARGS__))
int main(void) {
TEST("abc", 0);
TEST(1, 1, 1, 1, 1, 1, 1, 0);
TEST(0);
}
অন্য দুটি সংস্করণও 13 বাইটে এলো: এটির ভিত্তিতে এটি loopne
একটি মান প্রদান করে যা 1 দ্বারা খুব বেশি।
45 global argcount_loopne
46 argcount_loopne:
47 .entry:
49 00000010 31C0 xor eax, eax ; search pattern = NULL
50 00000012 31C9 xor ecx, ecx ; counter = 0
51 00000014 89E7 mov edi, esp
52 00000016 AF scasd ; edi+=4; skip retaddr
53 .scan_args:
54 00000017 AF scasd
55 00000018 E0FD loopne .scan_args
56 0000001A 29C8 sub eax, ecx
58 0000001C C3 ret
size = 0D = 13 bytes db $ - .entry
এই সংস্করণটিতে লুপের পরিবর্তে রেপ স্ক্যাসড ব্যবহার করা হয় তবে এটি 256-তে আর্গ গণনা গ্রহণ করে Or (বা উপরের বাইটগুলি ecx
এন্ট্রিতে 0 হয় তবে 256 এ ক্যাপড !)
63 ; return int8_t maybe?
64 global argcount_rep_scas
65 argcount_rep_scas:
66 .entry:
67 00000020 31C0 xor eax, eax
68 ; lea ecx, [eax-1]
69 00000022 B1FF mov cl, -1
70 00000024 89E7 mov edi, esp
71 ; scasd ; skip retaddr
72 00000026 F2AF repne scasd ; ecx = -len - 2 (including retaddr)
73 00000028 B0FD mov al, -3
74 0000002A 28C8 sub al, cl ; eax = -3 +len + 2
75 ; dec eax
76 ; dec eax
77 0000002C C3 ret
size = 0D = 13 bytes db $ - .entry
মজাদারভাবে, inc eax
/ pop edx
/ test edx,edx
/ এর উপর ভিত্তি করে এখনও অন্য সংস্করণটি jnz
13 বাইটে এসেছিল। এটি একটি কলি-পপস কনভেনশন, যা সি বাস্তবায়নের দ্বারা বৈকল্পিক ফাংশনগুলির জন্য কখনও ব্যবহৃত হয় না। (আমি রেট সংযোজনকে এক্সেক্সে পপড করেছিলাম, এবং রেটের পরিবর্তে জেএমপি এক্সএক্স (অথবা রিটার্ন-ঠিকানা ভবিষ্যদ্বাণীকের স্ট্যাকটি না ভাঙ্গার জন্য চাপ / রেট চাপি))।