x86-64 मशीन कोड, 12 बाइट्स के लिए int64_t
इनपुट के
के लिए 6 बाइट्स double
इनपुट के
आवश्यक popcnt
ईसा विस्तार (CPUID.01H:ECX.POPCNT [Bit 23] = 1
)।
(या 13 बाइट्स अगर arg में जगह को संशोधित करने के लिए ऊपरी 32 में कचरा छोड़ने के बजाय सभी 64-बिट लिखने की आवश्यकता होती है। मुझे लगता है कि यह तर्क देना उचित है कि कॉलर शायद कम 32b को वैसे भी लोड करना चाहेगा, और xx 280 प्रत्येक 32-बिट ऑपरेशन के साथ 32 से 64 तक अनुमानित है। फिर भी, यह कॉलर को करने से रोकता है add rbx, [rdi]
कुछ या कुछ है।)
x87 निर्देश अधिक स्पष्ट SSE2 cvtsi2sd
/ movq
( @ सीलिंगकैट के उत्तर में प्रयुक्त ) से कम होते हैं, और एक [reg]
एड्रेसिंग मोड एक ही आकार का होता है reg
: बस एक मॉड / आरएम बाइट।
ट्रिक को मेमोरी को पास करने के तरीके के साथ आना था, बिना मोड्स को संबोधित करने के लिए बहुत सारे बाइट्स की आवश्यकता के बिना। (जैसे स्टैक पर गुजरना इतना अच्छा नहीं है।) सौभाग्य से, नियम पढ़ने / लिखने की अनुमति देता है, या अलग आउटपुट आर्ग देता है , इसलिए मुझे बस कॉल करने वाला मिल सकता है मुझे एक पॉइंटर पास करने के लिए जो मुझे लिखने की अनुमति है।
हस्ताक्षर के साथ C से कॉल करने योग्य: void popc_double(int64_t *in_out);
परिणाम का केवल कम 32b मान्य है, जो शायद C के लिए अजीब है, लेकिन asm के लिए स्वाभाविक है। (इसे ठीक करने के लिए अंतिम स्टोर पर एक आरईएक्स उपसर्ग की आवश्यकता होती है ( mov [rdi], rax
), इसलिए एक और बाइट।) विंडोज पर, को बदल rdi
देंrdx
, के बाद से विंडोज x86-64 सिस्टम वी ABI का उपयोग नहीं करता है।
एनएएसएम लिस्टिंग। TIO लिंक में डिस्सैड के बिना स्रोत कोड है।
1 addr machine global popcnt_double_outarg
2 code popcnt_double_outarg:
3 ;; normal x86-64 ABI, or x32: void pcd(int64_t *in_out)
4 00000000 DF2F fild qword [rdi] ; int64_t -> st0
5 00000002 DD1F fstp qword [rdi] ; store binary64, using retval as scratch space.
6 00000004 F3480FB807 popcnt rax, [rdi]
7 00000009 8907 mov [rdi], eax ; update only the low 32b of the in/out arg
8 0000000B C3 ret
# ends at 0x0C = 12 bytes
इसे ऑनलाइन आज़माएं! इसमें एक _start
परीक्षण कार्यक्रम शामिल है जो इसे एक मान देता है और बाहर निकलने की स्थिति के साथ बाहर निकलता है = popcnt वापसी मूल्य। (इसे देखने के लिए "डीबग" टैब खोलें।)
अलग इनपुट / आउटपुट पॉइंटर्स पास करना भी काम करेगा (x86-64 SystemV ABI में rdi और rsi), लेकिन तब हम 64-बिट इनपुट को यथोचित रूप से नष्ट नहीं कर सकते हैं या केवल लिखते समय 64-बिट आउटपुट बफर की आवश्यकता को आसानी से सही ठहरा सकते हैं कम 32 ब।
यदि हम यह तर्क देना चाहते हैं कि हम इनपुट पूर्णांक के लिए एक सूचक ले सकते हैं और इसे नष्ट कर सकते हैं, जबकि आउटपुट वापस कर सकते हैं rax
, तो बस से चूक कर सकते mov [rdi], eax
हैं popcnt_double_outarg
, इसे 10 बाइट्स तक ला सकते हैं।
मूर्खतापूर्ण कॉलिंग-कन्वेंशन ट्रिक्स के बिना वैकल्पिक, 14 बाइट्स
स्टैक को स्क्रैच स्पेस के रूप में उपयोग करें, push
इसे वहां लाने के लिए। 3 के बजाय 2 बाइट्स में रजिस्टरों को कॉपी करने के लिए push
/ pop
का उपयोग करें mov rdi, rsp
। ( [rsp]
हमेशा एक एसआईबी बाइट की आवश्यकता होती है, इसलिए इसका rsp
उपयोग करने वाले तीन निर्देशों से पहले कॉपी करने के लिए 2 बाइट खर्च करने लायक है।)
इस हस्ताक्षर के साथ C से कॉल करें: int popcnt_double_push(int64_t);
11 global popcnt_double_push
12 popcnt_double_push:
13 00000040 57 push rdi ; put the input arg on the stack (still in binary integer format)
14 00000041 54 push rsp ; pushes the old value (rsp updates after the store).
15 00000042 5A pop rdx ; mov rdx, rsp
16 00000043 DF2A fild qword [rdx]
17 00000045 DD1A fstp qword [rdx]
18 00000047 F3480FB802 popcnt rax, [rdx]
19 0000004C 5F pop rdi ; rebalance the stack
20 0000004D C3 ret
next byte is 0x4E, so size = 14 bytes.
double
प्रारूप में इनपुट स्वीकार करना
सवाल सिर्फ यह कहता है कि यह एक निश्चित सीमा में पूर्णांक है, ऐसा नहीं है कि इसे बेस 2 बाइनरी पूर्णांक प्रतिनिधित्व में होना चाहिए। double
इनपुट स्वीकार करने का मतलब है कि अब x87 का उपयोग करने का कोई मतलब नहीं है। (जब तक आप एक कस्टम कॉलिंग कन्वेंशन का उपयोग नहीं करते हैं जहां double
s x87 रजिस्टरों में पारित हो जाते हैं। तब स्टैक के नीचे रेड-ज़ोन में स्टोर करें, और वहां से पॉपकॉइन करें।)
11 बाइट्स:
57 00000110 66480F7EC0 movq rax, xmm0
58 00000115 F3480FB8C0 popcnt rax, rax
59 0000011A C3 ret
लेकिन हम 6-बाइट संस्करण बनाने के लिए उसी पास-दर-संदर्भ ट्रिक का उपयोग कर सकते हैं: int pcd(const double&d);
58 00000110 F3480FB807 popcnt rax, [rdi]
59 00000115 C3 ret
6 बाइट्स ।
binary64
फॉर्मेट में पहले से ही स्वीकार कर सकें, अगर वे चाहें तो? कुछ लोग (स्वयं सहित, शुरू में) इस प्रश्न की व्याख्या कर रहे थे कि कार्यों को C के जैसे पूर्णांक प्रकार के रूप में इनपुट स्वीकार करना आवश्यक हैlong
। सी में, आप तर्क दे सकते हैं कि भाषा आपके लिए परिवर्तित हो जाएगी, जैसे आप कॉल करते हैंsqrt((int)foo)
। लेकिन कुछ x86 मशीन-कोड asm उत्तर हैं (जैसे codegolf.stackexchange.com/a/136360/30206 और मेरा) जो दोनों मान रहे थे कि हमें 64-बिट पूर्णांक इनपुट स्वीकार करना था।binary64
मान स्वीकार करने से 5 बाइट बचती हैं।