x86 32-bit (i386) machine code function, 13 bytes
कॉलिंग कन्वेंशन: i386 सिस्टम V (स्टैक args), NULL पॉइंटर के साथ एक सेंटिनल / टर्मिनेटर के रूप में एंड-ऑफ-आर्ग-लिस्ट के लिए. (Clobbers EDI, otherwise complies with SysV).
सी (और एएसएम) प्रकार की जानकारी को चर कार्यों के लिए पारित नहीं करते हैं, इसलिए बिना किसी स्पष्ट प्रकार की जानकारी के पूर्णांक या सरणियों को पास करने का ओपी विवरण केवल एक सम्मेलन में लागू किया जा सकता है जो किसी प्रकार की संरचना / वर्ग वस्तु (या बिंदुओं को पारित करता है) ), ढेर पर नंगे पूर्णांक नहीं। इसलिए मैंने यह मानने का फैसला किया कि सभी आर्ग गैर-पूर्ण बिंदु थे, और कॉल करने वाला एक पूर्ण टर्मिनेटर से गुजरता है।
POSIX जैसे कार्यों के लिए C की वास्तव में एक पूर्ण-समाप्ति सूचक सूची C में उपयोग की जाती हैexecl(3)
: int execl(const char *path, const char *arg, ... /* (char *) NULL */);
C int foo(...);
कोई निश्चित arg वाले प्रोटोटाइप की अनुमति नहीं देता है , लेकिन int foo();
इसका मतलब समान है: अनिर्दिष्ट args। (C ++ के विपरीत जहां इसका मतलब हैint foo(void)
)। किसी भी मामले में, यह एक जवाब है। इस फ़ंक्शन को सीधे कॉल करने के लिए एक सी कंपाइलर कोअक्सिंग करना दिलचस्प है लेकिन आवश्यक नहीं है।
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 को वापस करने में सक्षम होना चाहिए, और मैंने उस आवश्यकता का पालन करने का फैसला किया जिसमें Arg गिनती में NULL पॉइंटर को समाप्त नहीं किया गया था। हालांकि यह 1 बाइट खर्च करता है। (12-बाइट संस्करण के लिए, एलईए को हटा दें और scasd
लूप और ए के बाहर को अनकम्प्लिमेंट करें 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
को बचाने / बहाल करने के बिना उस फ़ंक्शन क्लोबर को gcc बताने के लिए , क्योंकि मैंने एक सी स्टेटमेंट ( printf
कॉल) में इतने कॉल -O0
का उपयोग किया था कि यहां तक कि ईडीआई का उपयोग कर रहा था। यह main
glibc के साथ लिनक्स पर अपने स्वयं के कॉलर (CRT कोड में) से cccbber EDI के लिए gcc के लिए सुरक्षित प्रतीत होता है , लेकिन अन्यथा यह विभिन्न के साथ संकलित / मैच कोड को मिलाने के लिए पूरी तरह से फर्जी है -fcall-used-reg
। __attribute__
इसका कोई संस्करण नहीं है कि हम सामान्य कॉलिंग रिवाज़ों के साथ ऐश कार्यों को सामान्य से अलग घोषित करें।
#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 लेता है। (या 256 पर छाया हुआ यदि ऊपरी बाइट्स ecx
प्रवेश पर 0 हैं!)
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
जाहिर है, अभी तक 13 / बाइट्स पर आधारित inc eax
/ pop edx
/ पर एक और संस्करण आया था। यह एक कैली-पोप सम्मेलन है, जिसका उपयोग चर कार्यों के लिए सी कार्यान्वयन द्वारा कभी नहीं किया जाता है। (मैंने रिट एडियर को एक्सएक्सएक्स, और जेएमपी एक्सएक्सएक्स में रेट के बजाय पॉप किया। (या रिटर्न-एड्रेस प्रेडिक्टर स्टैक को नहीं तोड़ने के लिए पुश / रिट)।test edx,edx
jnz