2011-07-20 12 views
9

मैं आईआई 32 के लिए सीएमपीएक्सएचजी 8 बी के लिए जीसीसी इनलाइन एएसएम लिखने की कोशिश कर रहा हूं। नहीं, मैं __sync_bool_compare_and_swap का उपयोग नहीं कर सकता। इसे -एफपीआईसी के साथ और बिना काम करना है।जीसीसी इनलाइन असेंबली में सीएमपीएक्सएचजी 8 बी को लपेटने का सही तरीका, 32 बिट

अब तक सबसे अच्छा मैं कर दिया है (संपादित: सब के बाद काम नहीं करता है, जानकारी के लिए नीचे अपने ही जवाब देखने)

register int32 ebx_val asm("ebx")= set & 0xFFFFFFFF; 
asm ("lock; cmpxchg8b %0;" 
    "setz %1;" 
    : "+m" (*a), "=q" (ret), "+A" (*cmp) 
    : "r" (ebx_val), "c" ((int32)(set >> 32)) 
    : "flags") 

है हालांकि मुझे यकीन है कि नहीं कर रहा हूँ अगर यह तथ्य सही है ।

मैं पीआईसी के कारण ebx_val के लिए "b" ((int32)(set & 0xFFFFFFFF)) नहीं कर सकता, लेकिन स्पष्ट रूप से register asm("ebx") चर संकलक द्वारा स्वीकार किया जाता है।

बोनस: सेवानिवृत्त चर शाखाओं के लिए प्रयोग किया जाता है, तो कोड इस तरह की तलाश में समाप्त होता है:

cmpxchg8b [edi]; 
setz cl; 
cmp cl, 0; 
je foo; 

किसी भी विचार इतना है कि यह हो जाता है कि कैसे उत्पादन ऑपरेंड वर्णन करने के लिए:

cmpxchg8b [edi] 
jz foo 

?

धन्यवाद।

+3

तथ्य यह है कि संकलक आंतरिक -fPIC साथ काम नहीं करता सिर्फ एक खुले दिल से संकलक बग है: http: //gcc.gnu.org/bugzilla/show_bug.cgi?id=37651 जब आप टूटे हुए कंपाइलर के आसपास काम करना चाहते हैं तो यह बेकार हो जाता है, ताकि आप स्वयं को उस बग के लिए सीसी सूची में रखना चाहें। – Crashworks

+0

आप आईए 32 पर '-fPIC' का उपयोग करते हैं? मैं उत्सुक हूँ क्यों। – Gabe

+4

@Gabe - साझा लाइब्रेरी ऑब्जेक्ट्स लिखते समय यह सबसे महत्वपूर्ण है। Ulrich Drepper इस विषय पर एक अच्छा पेपर है: http://www.akkadia.org/drepper/dsohowto.pdf – Crashworks

उत्तर

2

कैसे निम्नलिखित है, जो एक छोटे से परीक्षण में मेरे लिए काम करने लगता है के बारे में:

int sbcas(uint64_t* ptr, uint64_t oldval, uint64_t newval) 
{ 
    int changed = 0; 
    __asm__ (
     "push %%ebx\n\t" // -fPIC uses ebx, so save it 
     "mov %5, %%ebx\n\t" // load ebx with needed value 
     "lock\n\t" 
     "cmpxchg8b %0\n\t" // perform CAS operation 
     "setz %%al\n\t" // eax potentially modified anyway 
     "movzx %%al, %1\n\t" // store result of comparison in 'changed' 
     "pop %%ebx\n\t" // restore ebx 
     : "+m" (*ptr), "=r" (changed) 
     : "d" ((uint32_t)(oldval >> 32)), "a" ((uint32_t)(oldval & 0xffffffff)), "c" ((uint32_t)(newval >> 32)), "r" ((uint32_t)(newval & 0xffffffff)) 
     : "flags", "memory" 
     ); 
    return changed; 
} 

यह भी miscompiled हो जाता है आप एक छोटा स्निपेट है कि इस व्यवहार से चलाता शामिल करें सकता है?

बोनस प्रश्न के संबंध में मुझे नहीं लगता कि cmpxchg8b निर्देश से स्थिति कोड का उपयोग करके असेंबलर ब्लॉक के बाद शाखा करना संभव है (जब तक आप asm goto या इसी तरह की कार्यक्षमता का उपयोग नहीं करते)।GNU C Language Extensions से:

असेंबलर निर्देश द्वारा छोड़े गए शर्त कोड तक पहुंच प्रदान करने का एक तरीका ढूंढना एक प्राकृतिक विचार है। हालांकि, जब हमने इसे लागू करने का प्रयास किया, तो हमें इसे विश्वसनीय तरीके से काम करने का कोई रास्ता नहीं मिला। समस्या यह है कि आउटपुट ऑपरेटरों को पुनः लोड करने की आवश्यकता हो सकती है, जिसके परिणामस्वरूप अतिरिक्त "स्टोर" निर्देशों का परिणाम होगा। अधिकांश मशीनों पर, इन निर्देशों का परीक्षण करने के लिए समय होने से पहले हालत कोड बदल जाएगा। यह समस्या साधारण "परीक्षण" और "तुलना" निर्देशों के लिए उत्पन्न नहीं होती है क्योंकि उनके पास कोई आउटपुट ऑपरेंड नहीं है।

संपादित करें: मैं किसी भी स्रोत है कि एक ही रास्ता या अन्य चाहे वह करते हुए भी %N इनपुट मानों का उपयोग करके स्टैक को संशोधित करने के लिए ठीक है निर्दिष्ट करता नहीं खोजा जा सका (This प्राचीन लिंक कहते हैं, "तुम भी पर अपने रजिस्टरों धक्का कर सकते हैं ढेर, उनका उपयोग करें, और उन्हें वापस रखो। "लेकिन उदाहरण में इनपुट नहीं है)।

लेकिन यह अन्य रजिस्टरों को मान फिक्सिंग से बिना ऐसा करना संभव होना चाहिए:

int sbcas(uint64_t* ptr, uint64_t oldval, uint64_t newval) 
{ 
    int changed = 0; 
    __asm__ (
     "push %%ebx\n\t" // -fPIC uses ebx 
     "mov %%edi, %%ebx\n\t" // load ebx with needed value 
     "lock\n\t" 
     "cmpxchg8b (%%esi)\n\t" 
     "setz %%al\n\t" // eax potentially modified anyway 
     "movzx %%al, %1\n\t" 
     "pop %%ebx\n\t" 
     : "+S" (ptr), "=a" (changed) 
     : "0" (ptr), "d" ((uint32_t)(oldval >> 32)), "a" ((uint32_t)(oldval & 0xffffffff)), "c" ((uint32_t)(newval >> 32)), "D" ((uint32_t)(newval & 0xffffffff)) 
     : "flags", "memory" 
     ); 
    return changed; 
} 
+1

धन्यवाद!एक मुद्दा जो मैं देख सकता हूं वह मेरे स्निपेट के समान है: संकलक ईएसपी के माध्यम से% 0 को संबोधित कर सकता है और यह नहीं बता सकता कि ईएसपी पुश/पॉप द्वारा बदल गया है। इसके अलावा, जानकारी के लिए धन्यवाद। आउटपुट में हालत कोड, इसने पुष्टि की है कि मुझे क्या संदेह था। –

+0

मुझे नहीं लगता कि मैंने कभी जीसीसी को ऐसा देखा है। यह (हमेशा?) इसे 'ebp' के माध्यम से करता है जहां तक ​​मैंने देखा है। मैं देखूंगा कि क्या मैं हमेशा इस पर एक संदर्भ खोद सकता हूं कि क्या यह हमेशा गारंटीकृत है (या यदि इसे बनाया जा सकता है)। – user786653

+0

मैंने संदर्भ देखा है कि आईसीसी ऐसा करता है। –

2

यह है कि मैं क्या है:

bool 
spin_lock(int64_t* lock, int64_t thread_id, int tries) 
{ 
    register int32_t pic_hack asm("ebx") = thread_id & 0xffffffff; 
retry: 
    if (tries-- > 0) { 
     asm goto ("lock cmpxchg8b %0; jnz %l[retry]" 
        : 
        : "m" (*lock), "A" ((int64_t) 0), 
        "c" ((int32_t) (thread_id >> 32)), "r" (pic_hack) 
        : 
        : retry); 
     return true; 
    } 
    return false; 
} 

यह asm goto सुविधा, जीसीसी 4.5, कि सी लेबल में इनलाइन विधानसभा से छलांग की अनुमति देता है के साथ नए प्रयोग करता है। (ओह, मैं जीसीसी के पुराने संस्करणों का समर्थन करने के बारे में आपकी टिप्पणी देखता हूं। ओह ठीक है। मैंने कोशिश की। :-P)

+0

धन्यवाद! मुझे लगता है कि मेरा कोड पहले से ही आपके जैसा ही है, हालांकि निश्चित रूप से मैं एएसएम गोटो का उपयोग नहीं कर सकता। प्रश्नों के युगल हालांकि: 1) क्यों * लॉक ऑपरेंड केवल इनपुट है और इनपुट/आउटपुट नहीं है? 2) क्यों EFLAGS क्लॉबर्ड रजिस्टर सूची में नहीं है? –

+0

@ लॉरिनस: 1. 'asm goto' में कोई आउटपुट बाधा नहीं हो सकती है (वर्तमान सीमा जिसे बाद में जीसीसी संस्करणों में हटाया जा सकता है); चूंकि मैं लॉक के वर्तमान मूल्य के बारे में "परवाह नहीं करता" (हम रिकर्सिव लॉकिंग करने की कोशिश नहीं कर रहे हैं ;-)), यह स्वीकार्य था। 2. क्योंकि 'एएसएम गोटो' के उदाहरणों में यह नहीं है (और हाँ, यह सशर्त कूद भी करता है), इसलिए मुझे लगता है कि 'asm goto' डिफ़ॉल्ट रूप से एक क्लॉब्ड झंडे को मानता है। –

+0

भविष्य के पाठकों के लिए: x86 और x86-64 के लिए gcc की मशीन-परिभाषाएं प्रत्येक इनलाइन एएसएम कथन को स्पष्ट रूप से '" सीसी "क्लॉबर शामिल करती हैं। आपको x86 asm के लिए स्पष्ट रूप से एक लिखने की आवश्यकता नहीं है। –

1

आश्चर्यजनक रूप से पर्याप्त है, प्रश्न में कोड खंड अभी भी कुछ परिस्थितियों में गलत हो गया है: यदि शून्य- ईएसएक्स रजिस्टर register asm के साथ स्थापित होने से पहले एएसएक्स ऑपरेंड अप्रत्यक्ष रूप से ईबीएक्स (पीआईसी) के माध्यम से संबोधित करने योग्य है, तो जीसीसी set & 0xFFFFFFFF को सौंपा जाने के बाद ईबीएक्स के माध्यम से ऑपरेंड लोड करने के लिए आगे बढ़ता है!

इस कोड को मैं अब काम करने के लिए कोशिश कर रहा हूँ है: (संपादित करें: धक्का से बचने/पॉप)

asm ("movl %%edi, -4(%%esp);" 
    "leal %0, %%edi;" 
    "xchgl %%ebx, %%esi;" 
    "lock; cmpxchg8b (%%edi);" // Sets ZF 
    "movl %%esi, %%ebx;"  // Preserves ZF 
    "movl -4(%%esp), %%edi;" // Preserves ZF 
    "setz %1;"     // Reads ZF 
    : "+m" (*a), "=q" (ret), "+A" (*cmp) 
    : "S" ((int32)(set & 0xFFFFFFFF)), "c" ((int32)(set >> 32)) 
    : "flags") 

विचार यहाँ EBX clobbering से पहले ऑपरेंड लोड करने के लिए है, यह भी किसी भी अप्रत्यक्ष को संबोधित करते हुए बचने सीएमपीएक्सएचजी 8 बी के लिए ईबीएक्स मूल्य निर्धारित करना। मैं ऑपरेंड के निचले हिस्से के लिए हार्ड रजिस्टर ईएसआई को ठीक करता हूं, क्योंकि यदि मैंने नहीं किया, तो जीसीसी किसी भी पहले से लिया गया रजिस्टर फिर से उपयोग करने के लिए स्वतंत्र महसूस करेगा अगर यह साबित कर सकता है कि मूल्य बराबर था। ईडीआई रजिस्टर मैन्युअल रूप से सहेजा जाता है, क्योंकि इसे केवल क्लॉबर्ड रजिस्टर सूची में जोड़कर जीसीसी को "असंभव रीलोड" के साथ चॉक करता है, शायद उच्च पंजीकरण दबाव के कारण। ईएसआई को बचाने में पुश/पीओपी से बचा जाता है, क्योंकि अन्य ऑपरेशंस ईएसपी-संबोधित हो सकते हैं।

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