x86_64 मशीन कोड, 4 बाइट्स
BSF (बिट स्कैन फॉरवर्ड) निर्देश बिल्कुल यही करता है !
0x0f 0xbc 0xc7 0xc3
जीसीसी शैली विधानसभा में, यह है:
.globl f
f:
bsfl %edi, %eax
ret
इनपुट EDI रजिस्टर में दिया गया है और EAX रजिस्टर में मानक 64-बिट c कॉलिंग कन्वेंशन के अनुसार दिया गया है।
दो पूरक बाइनरी एन्कोडिंग के कारण, यह -ve के साथ-साथ वी + संख्याओं के लिए भी काम करता है।
इसके अलावा, दस्तावेज़ के यह कहने के बावजूद कि "यदि स्रोत ऑपरेंड की सामग्री 0 है, तो गंतव्य ऑपरेंड की सामग्री अपरिभाषित है।" , मैं अपने उबंटू वीएम पर पाता हूं कि आउटपुट f(0)
0 है।
निर्देश:
- ऊपर के रूप में सहेजें
evenness.s
और साथ इकट्ठाgcc -c evenness.s -o evenness.o
- निम्न परीक्षण चालक को सहेजें
evenness-main.c
और इसके साथ संकलित करें gcc -c evenness-main.c -o evenness-main.o
:
#include <stdio.h>
extern int f(int n);
int main (int argc, char **argv) {
int i;
int testcases[] = { 14, 20, 94208, 7, 0, -4 };
for (i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) {
printf("%d, %d\n", testcases[i], f(testcases[i]));
}
return 0;
}
फिर:
- संपर्क:
gcc evenness-main.o evenness.o -o evenness
- Daud:
./evenness
@FarazMasroor ने इस बारे में अधिक जानकारी मांगी कि यह उत्तर कैसे प्राप्त हुआ।
मैं के साथ और अधिक परिचित हूँ सी 86 की पेचीदगियों को विधानसभा से है, तो आम तौर पर मैं एक संकलक का उपयोग मेरे लिए विधानसभा कोड उत्पन्न करने के लिए। मैं अनुभव से जानता हूं कि gcc एक्सटेंशन जैसे कि __builtin_ffs()
, __builtin_ctz()
और__builtin_popcount()
आमतौर पर x86 पर 1 या 2 निर्देशों को संकलित करना और इकट्ठा करना। इसलिए मैंने सी फ़ंक्शन के साथ शुरुआत की:
int f(int n) {
return __builtin_ctz(n);
}
ऑब्जेक्ट कोड के लिए सभी तरह से नियमित gcc संकलन का उपयोग करने के बजाय, आप -S
केवल असेंबली करने के लिए विकल्प का उपयोग कर सकते हैं - gcc -S -c evenness.c
। यह evenness.s
इस तरह एक विधानसभा फ़ाइल देता है :
.file "evenness.c"
.text
.globl f
.type f, @function
f:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
rep bsfl %eax, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size f, .-f
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section .note.GNU-stack,"",@progbits
इस का एक बहुत बाहर निकाला जा सकता है। विशेष रूप से हम जानते हैं कि हस्ताक्षर के साथ कार्यों के लिए सी कॉलिंग सम्मेलनint f(int n);
अच्छा और सरल है - इनपुट परम को EDI
रजिस्टर में पारित किया जाता है और रजिस्टर में रिटर्न वैल्यू लौटा दी जाती है EAX
। इसलिए हम अधिकांश निर्देश निकाल सकते हैं - उनमें से बहुत से रजिस्टरों को बचाने और एक नया स्टैक फ्रेम स्थापित करने से चिंतित हैं। हम यहां स्टैक का उपयोग नहीं करते हैं और केवल EAX
रजिस्टर का उपयोग करते हैं, इसलिए अन्य रजिस्टरों के बारे में चिंता करने की आवश्यकता नहीं है। यह "गोल्फ" विधानसभा कोड छोड़ देता है:
.globl f
f:
bsfl %edi, %eax
ret
@Zwol के अनुसार ध्यान दें, आप समान परिणाम प्राप्त करने के लिए अनुकूलित संकलन का भी उपयोग कर सकते हैं। विशेष रूप -Os
से उपरोक्त निर्देशों (कुछ अतिरिक्त कोडांतरक निर्देशों के साथ, जो किसी भी अतिरिक्त ऑब्जेक्ट कोड का उत्पादन नहीं करते हैं) का उत्पादन करता है।
यह अब के साथ इकट्ठा किया गया है gcc -c evenness.s -o evenness.o
, जिसे बाद में ऊपर वर्णित के रूप में एक परीक्षण चालक कार्यक्रम में जोड़ा जा सकता है।
इस विधानसभा के अनुरूप मशीन कोड निर्धारित करने के कई तरीके हैं। मेरा पसंदीदा gdb disass
disassembly कमांड का उपयोग करना है:
$ gdb ./evenness
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Reading symbols from ./evenness...(no debugging symbols found)...done.
(gdb) disass /r f
Dump of assembler code for function f:
0x00000000004005ae <+0>: 0f bc c7 bsf %edi,%eax
0x00000000004005b1 <+3>: c3 retq
0x00000000004005b2 <+4>: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:0x0(%rax,%rax,1)
0x00000000004005bc <+14>: 0f 1f 40 00 nopl 0x0(%rax)
End of assembler dump.
(gdb)
इसलिए हम देख सकते हैं कि bsf
निर्देश के लिए मशीन कोड है 0f bc c7
और ret
है c3
।