2011-06-02 13 views
21

मैं थोड़ा x86 लेने की कोशिश कर रहा हूं। मैं gcc -S -O0 के साथ 64 बिट मैक पर संकलन कर रहा हूं। सी मेंprintf पर कॉल करने से पहले% eax शून्य क्यों है?

कोड:

printf("%d", 1); 

आउटपुट:

movl $1, %esi 
leaq LC0(%rip), %rdi 
movl $0, %eax  ; WHY? 
call _printf 

मुझे समझ नहीं आता से पहले 'printf' कहा जाता है क्यों% eax 0 को मंजूरी दे दी है। चूंकि printf%eax पर मुद्रित वर्णों की संख्या लौटाता है, मेरा सबसे अच्छा अनुमान है कि इसे printf के लिए तैयार करने के लिए शून्य आउट किया गया है, लेकिन मुझे लगता है कि printf इसे तैयार करने के लिए ज़िम्मेदार होना होगा। इसके अलावा, इसके विपरीत, अगर मैं अपना खुद का फ़ंक्शन int testproc(int p1) कहता हूं, gcc%eax तैयार करने की आवश्यकता नहीं देखता है। तो मुझे आश्चर्य है कि क्यों gccprintf और testproc अलग-अलग व्यवहार करता है।

उत्तर

21

x86_64 System V ABI से:

Register Usage 
%rax  temporary register; with variable arguments 
      passes information about the number of vector 
      registers used; 1st return register 
... 

printf चर तर्क के साथ एक समारोह है, और उपयोग वेक्टर रजिस्टरों की संख्या शून्य है।

ध्यान दें कि printf को केवल %al की जांच करनी चाहिए, क्योंकि कॉलर को %rax के उच्च बाइट्स में कचरा छोड़ने की अनुमति है। (फिर भी, xor %eax,%eax शून्य %al का सबसे कारगर तरीका है)

अधिक जानकारी के लिए this Q&A और टैग विकि देखें, या अप-टू-डेट ABI लिंक के लिए ऊपर दिए गए लिंक बासी है यदि।

+0

'puts' भी बाहर के शून्यीकरण है'% कॉल से ठीक पहले eax 'हालांकि यह केवल एक सूचक लेता है। ऐसा क्यों है? – sh54

+0

यह मेरे स्वयं के 'शून्य प्रो()' फ़ंक्शन (यहां तक ​​कि -O2 सेट के साथ) के किसी भी कॉल से पहले भी होता है, लेकिन 'शून्य proc2 (int param)' फ़ंक्शन को कॉल करते समय इसे शून्य नहीं किया जाता है। – sh54

+2

रिकॉर्ड के लिए, यह 'void proc()' पर कॉल करने से पहले होता है क्योंकि सी में यह हस्ताक्षर वास्तव में प्रो की धैर्य के बारे में कुछ भी नहीं कहता है, और यह एक भिन्न कार्य भी हो सकता है, इसलिए शून्य रैक्स आवश्यक है। 'शून्य प्रो()' 'शून्य प्रो (शून्य) 'से अलग है। एबीआई से http://stackoverflow.com/questions/693788/c-void-arguments – frangio

32

x86_64 एबीआई में, यदि किसी फ़ंक्शन में परिवर्तनीय तर्क हैं तो AL (जो EAX का हिस्सा है) से उस कार्य में तर्क रखने के लिए प्रयुक्त वेक्टर रजिस्टरों की संख्या को पकड़ने की उम्मीद है।

अपने उदाहरण में: तो एक वेक्टर रजिस्टर के लिए कोई आवश्यकता नहीं है, इसलिए AL 0 पर सेट है

printf("%d", 1); 

एक पूर्णांक तर्क है।

दूसरी ओर, यदि आप करने के लिए अपने उदाहरण बदलने के लिए:

printf("%f", 1.0f); 

तो फ्लोटिंग प्वाइंट शाब्दिक एक वेक्टर रजिस्टर में संग्रहीत किया जाता है और, तदनुसार, AL1 पर सेट है:

movsd LC1(%rip), %xmm0 
leaq LC0(%rip), %rdi 
movl $1, %eax 
call _printf 

जैसी उम्मीद थी:

printf("%f %f", 1.0f, 2.0f); 

कारण होगा संकलक 2 को AL स्थापित करने के लिए के बाद से वहाँ दो फ्लोटिंग प्वाइंट तर्क हैं:

movsd LC0(%rip), %xmm0 
movapd %xmm0, %xmm1 
movsd LC2(%rip), %xmm0 
leaq LC1(%rip), %rdi 
movl $2, %eax 
call _printf 

अपने अन्य प्रश्न के लिए के रूप में:

puts भी सही कॉल हालांकि यह पहले %eax बाहर के शून्यीकरण है केवल एक सूचक लेता है। ऐसा क्यों है?

यह नहीं होना चाहिए। उदाहरण के लिए:

#include <stdio.h> 

void test(void) { 
    puts("foo"); 
} 

gcc -c -O0 -S, आउटपुट के साथ संकलित जब:

pushq %rbp 
movq %rsp, %rbp 
leaq LC0(%rip), %rdi 
call _puts 
leave 
ret 

और %eax बाहर ध्यान केंद्रित किया नहीं है। हालांकि, अगर आप #include <stdio.h> को दूर फिर परिणामी विधानसभा बाहर शून्य सही puts() कॉल करने से पहले %eax करता है:

यह भी अपने खुद के लिए किसी भी कॉल करने से पहले होता है:

pushq %rbp 
movq %rsp, %rbp 
leaq LC0(%rip), %rdi 
movl $0, %eax 
call _puts 
leave 
ret 

कारण आपके दूसरे प्रश्न से संबंधित है शून्य प्रो() फ़ंक्शन (यहां तक ​​कि -O2 सेट के साथ), लेकिन शून्य शून्य proc (int param) फ़ंक्शन को कॉल करते समय शून्य नहीं किया जाता है।

यदि कंपाइलर किसी फ़ंक्शन की घोषणा नहीं देखता है तो यह इसके पैरामीटर के बारे में कोई धारणा नहीं करता है, और फ़ंक्शन वेरिएबल तर्कों को अच्छी तरह से स्वीकार कर सकता है। वही लागू होता है यदि आप एक खाली पैरामीटर सूची निर्दिष्ट करते हैं (जिसे आपको नहीं करना चाहिए, और इसे आईएसओ/आईईसी द्वारा एक अश्लील सी सुविधा के रूप में चिह्नित किया गया है)। चूंकि कंपाइलर में फ़ंक्शन पैरामीटर के बारे में पर्याप्त जानकारी नहीं है, इसलिए यह फ़ंक्शन को कॉल करने से पहले %eax निकाल देता है क्योंकि यह ऐसा हो सकता है कि फ़ंक्शन को वेरिएबल तर्क के रूप में परिभाषित किया गया हो।

उदाहरण के लिए:

#include <stdio.h> 

void function() { 
    puts("foo"); 
} 

void test(void) { 
    function(); 
} 

जहां function() एक खाली पैरामीटर सूची है, में परिणाम:

pushq %rbp 
movq %rsp, %rbp 
movl $0, %eax 
call _function 
leave 
ret 

हालांकि, अगर आप void निर्दिष्ट करने के अभ्यास की सिफारिश जब समारोह कोई पैरामीटर स्वीकार का पालन करें, जैसे:

#include <stdio.h> 

void function(void) { 
    puts("foo"); 
} 

void test(void) { 
    function(); 
} 

n संकलक जानता है कि function() तर्क को स्वीकार नहीं करता - विशेष रूप से, यह चर तर्क को स्वीकार नहीं करता - और इसलिए है कि समारोह कॉल करने से पहले स्पष्ट %eax नहीं करता है:

pushq %rbp 
movq %rsp, %rbp 
call _function 
leave 
ret 
+1

नोट देखें: "हम एसएसई या एवीएक्स रजिस्टर को संदर्भित करने के लिए वेक्टर रजिस्टर का उपयोग करते हैं।" –

+1

'% रैक्स' में वेक्टर गिनती गुजरने का क्या फायदा है? क्या यह केवल प्रदर्शन है, "रजिस्टर सहेजें क्षेत्र" पर बेकार रजिस्टरों को बचाने से बचने के लिए? –

संबंधित मुद्दे