2011-06-17 21 views
19

मैं क्रिप्टोग्राफी प्रोग्राम लिख रहा हूं, और मूल (विस्तृत मल्टीप्ली रूटीन) x86-64 असेंबली में गति के लिए लिखा गया है और क्योंकि यह adc जैसे निर्देशों का व्यापक रूप से उपयोग करता है जो आसानी से नहीं हैं सी से सुलभ। मैं इस फ़ंक्शन को इनलाइन नहीं करना चाहता, क्योंकि यह बड़ा है और इसे आंतरिक लूप में कई बार कहा जाता है।इनलाइन असेंबली जो रेड जोन

आदर्श रूप से मैं इस फ़ंक्शन के लिए एक कस्टम कॉलिंग सम्मेलन को भी परिभाषित करना चाहता हूं, क्योंकि आंतरिक रूप से यह सभी रजिस्टरों का उपयोग करता है (rsp को छोड़कर), इसके तर्कों को रोकता नहीं है, और रजिस्टरों में लौटाता है। अभी, यह सी कॉलिंग सम्मेलन के अनुकूल है, लेकिन निश्चित रूप से यह धीमा हो जाता है (लगभग 10% तक)।

इससे बचने के लिए, मैं इसे asm("call %Pn" : ... : my_function... : "cc", all the registers); से कॉल कर सकता हूं लेकिन क्या जीसीसी को यह बताने का कोई तरीका है कि कॉल निर्देश स्टैक के साथ गड़बड़ करता है? अन्यथा जीसीसी सिर्फ उन सभी रजिस्ट्रारों को लाल क्षेत्र में रखेगा, और शीर्ष पर कब्जा कर लिया जाएगा। मैं पूरे मॉड्यूल को -नो-रेड-जोन के साथ संकलित कर सकता हूं, लेकिन मैं जीसीसी को यह बताने का एक तरीका पसंद करूंगा कि, लाल क्षेत्र के शीर्ष 8 बाइटों को गिरफ्तार किया जाएगा ताकि वह वहां कुछ भी न डाले।

+0

बस एक अनचाहे हालांकि, लेकिन क्या आप सिर्फ एक अतिरिक्त डमी इनपुट निर्दिष्ट नहीं कर सकते हैं, जैसे कि जीसीसी इसे लाल क्षेत्र में रखता है और यह (हानिरहित रूप से) हो जाता है? –

+3

एचएम। शायद भरोसेमंद नहीं। मैंने पाया है कि यह नियंत्रित करना बहुत कठिन है कि जीसीसी किस स्टैक पर, कब और कहां फैलता है। यह मैंने लिखा है कि अन्य क्रिप्टो सामान, मैंने जीसीसी की लिखने की प्रवृत्ति को दबाने के लिए मिश्रित सफलता के साथ प्रयास किया है, उदाहरण के लिए, छोटे कारणों से ढेर के लिए पूरी कुंजी टेबल। –

+0

एक क्लॉबर के रूप में 'sp' जोड़ें? एक मेमोरी क्लॉबर जोड़ें? –

उत्तर

0

सुनिश्चित नहीं है लेकिन GCC documentation for function attributes पर देखकर, मुझे stdcall फ़ंक्शन विशेषता मिली जो कि ब्याज की हो सकती है।

मैं अभी भी सोच रहा हूं कि आपको अपने एएसएम कॉल संस्करण के साथ समस्याग्रस्त क्या लगता है। यदि यह सिर्फ सौंदर्यशास्त्र है, तो आप इसे एक मैक्रो, या एक इनलाइन फ़ंक्शन में बदल सकते हैं।

+2

'कॉल' निर्देश स्टैक पर वर्तमान निर्देश सूचक को धक्का देता है। यह ठीक है अगर स्टैक के नीचे कुछ भी नहीं है ("लाल क्षेत्र" में), लेकिन x86-64 पर, एबीआई संकलक को पत्ते के कार्यों में सामान रखने की इजाजत देता है, यानी वे जो कुछ भी नहीं कहते हैं। हालांकि, जीसीसी को इस कॉल को कॉल के रूप में नहीं देखा जाता है, क्योंकि यह इनलाइन एएसएम में छिपा हुआ है। तो यह लाल क्षेत्र में कुछ डाल सकता है, और यह कॉल द्वारा clobbered हो जाएगा। यह केवल सैद्धांतिक संभावना नहीं है, यह वास्तव में होता है और वास्तव में मेरे कोड में एक बग का कारण बनता है। इसके अलावा, stdcall ऐसा नहीं करता है। –

+0

विशिष्ट रूप से stdcall के साथ समस्या यह है कि यह केवल वास्तविक, गैर-इनलाइन कार्यों पर काम करता है। लेकिन मेरे फ़ंक्शन के लिए एक कस्टम कॉलिंग सम्मेलन को परिभाषित करने के लिए, मैं इसे इनलाइन एएसएम के माध्यम से कॉल करने की कोशिश कर रहा हूं। तो जीसीसी को यह एहसास नहीं है कि यह एक फ़ंक्शन कॉल है (जो पहली जगह में समस्या है), और इस प्रकार मैं इसमें विशेषताओं को संलग्न नहीं कर सकता। –

2

क्या आप अपने कार्य में प्रवेश पर 128 बाइट्स द्वारा स्टैक पॉइंटर को स्थानांतरित करके x86-64 एबीआई में सिग्नल की आवश्यकताओं को पूरा करने के लिए अपने असेंबली फ़ंक्शन को संशोधित नहीं कर सकते?

या यदि आप वापसी सूचक ही की बात कर रहे हैं तो आपके कॉल मैक्रो (ताकि sub %rsp; call...)

+3

मैं इसे फ़ंक्शन के भीतर से नहीं कर सकता, क्योंकि 'कॉल' स्टैक का उपयोग करता है और इसलिए चीजों को स्वयं ही तोड़ देता है। 'सब $ 128,% आरएसपी; फोन ...; $ 128,% आरएसपी 'काम जोड़ें, लेकिन यह आदर्श से कम है। मुझे लगता है कि संतुलन पर यह अनुमान लगाया जा सकता है कि यह संभवतः मेरे काम को एबीआई से पूरा करने के लिए सबसे अच्छा है। –

4

में बदलाव कर दिया अपने मूल प्रश्न मैं पत्ती कार्यों के लिए जीसीसी सीमित लाल क्षेत्र उपयोग नहीं पता था से। मुझे नहीं लगता कि x86_64 एबीआई द्वारा इसकी आवश्यकता है, लेकिन यह एक कंपाइलर के लिए एक उचित सरलीकृत धारणा है। यदि global सच हो जाएगा

int global; 

was_leaf() 
{ 
    if (global) other(); 
} 

जीसीसी तो यह कॉल दूर अनुकूलित नहीं कर सकते नहीं बता सकता,: उस मामले में आप केवल समारोह अपने विधानसभा दिनचर्या बुला संकलन के प्रयोजनों के लिए एक गैर-पत्ती बनाने की जरूरत है other() पर was_leaf() अब एक पत्ता समारोह नहीं है। मैंने इसे संकलित किया (स्टैक उपयोग को ट्रिगर करने वाले अधिक कोड के साथ) और देखा कि एक पत्ते के रूप में यह %rsp नहीं चला और संशोधित संशोधन के साथ यह किया गया।

मैं भी एक पत्ती में 128 से अधिक बाइट (सिर्फ char buf[150]) का आवंटन बस की कोशिश की लेकिन मैं इसे केवल एक आंशिक घटाव किया देखने के लिए झटका लगा: यदि मैं में वापस पत्ती को हराने कोड डाल

pushq %rbp 
    movq %rsp, %rbp 
    subq $40, %rsp 
    movb $7, -155(%rbp) 

यह subq $160, %rsp

+3

'__attribute __ (पत्ता)' है लेकिन दुर्भाग्य से '__attribute __ (nonleaf)' –

+0

जैसे कुछ भी नहीं है मुझे यह चौंकाने वाला नहीं लगता है कि जीसीसी लाल क्षेत्र पर छोड़ नहीं देता है जब इसे कुछ स्टैक स्पेस आरक्षित करना होता है: इनमें से एक रेड-जोन के लाभ disp8 विस्थापन के साथ अधिक मेमोरी तक पहुंचने में सक्षम हैं, इसलिए स्थानीय लोगों के बीच में 'आरएसपी' होने का मतलब है कि यह उन सभी तक पहुंच सकता है' [rsp-128 .. + 127] 'संबोधित मोड । यह एक अच्छा अनुकूलन है। (या अगर यह '-153 (% आरबीपी)' –

0

सी में लिखा गया डमी फ़ंक्शन बनाने के बारे में क्या है और इनलाइन असेंबली को कॉल करने के अलावा कुछ भी नहीं करता है?

+0

के बजाय आरएसपी-रिश्तेदार एड्रेसिंग मोड प्राप्त करने के लिए '-O3' +' अस्थिर चार buf [150] 'का उपयोग किया गया था, तो यह होगा कि उस कार्य को' __attribute __ ((noinline)) '? '-O0' के साथ, कंपाइलर अभी भी लाल-क्षेत्र में फ़ंक्शन तर्क फैला सकता है। –

0

अधिकतम प्रदर्शन तरीका एएसएम में पूरे आंतरिक लूप को लिखने के लिए हो सकता है (call निर्देशों सहित, अगर यह अनलॉक करने के लिए वास्तव में लायक है लेकिन इनलाइन नहीं है। निश्चित रूप से व्यवहार्य है अगर पूरी तरह से इनलाइनिंग बहुत अधिक यूओपी-कैश को कहीं और याद करती है)।

वैसे भी, सी ने आपके अनुकूलित लूप युक्त एएसएम फ़ंक्शन को कॉल किया है।तो आप अच्छी तरह से पूरे पाश खुद के अनुकूलन से आगे बाहर आ सकता है

Btw, सभी रजिस्टरों clobbering, यह मुश्किल जीसीसी एक बहुत अच्छा पाश बनाने के लिए बनाता है। (उदाहरण के लिए शायद एक रजिस्टर में एक पॉइंटर रखें, और स्मृति में एक अंत-सूचक, क्योंकि cmp mem,reg अभी भी काफी कुशल है)।

एक asm बयान है कि एक सरणी तत्व (Godbolt पर) को संशोधित करता है चारों ओर कोड जीसीसी/बजना चादर पर एक नज़र डालें:

void testloop(long *p, long count) { 
    for (long i = 0 ; i < count ; i++) { 
    asm(" # XXX asm operand in %0" 
    : "+r" (p[i]) 
    : 
    : // "rax", 
    "rbx", "rcx", "rdx", "rdi", "rsi", "rbp", 
     "r8", "r9", "r10", "r11", "r12","r13","r14","r15" 
    ); 
    } 
} 

#gcc7.2 -O3 -march=haswell 

    push registers and other function-intro stuff 
    lea  rcx, [rdi+rsi*8]  ; end-pointer 
    mov  rax, rdi 

    mov  QWORD PTR [rsp-8], rcx ; store the end-pointer 
    mov  QWORD PTR [rsp-16], rdi ; and the start-pointer 

.L6: 
    # rax holds the current-position pointer on loop entry 
    # also stored in [rsp-16] 
    mov  rdx, QWORD PTR [rax] 
    mov  rax, rdx     # looks like a missed optimization vs. mov rax, [rax], because the asm clobbers rdx 

     XXX asm operand in rax 

    mov  rbx, QWORD PTR [rsp-16] # reload the pointer 
    mov  QWORD PTR [rbx], rax 
    mov  rax, rbx   # another weird missed-optimization (lea rax, [rbx+8]) 
    add  rax, 8 
    mov  QWORD PTR [rsp-16], rax 
    cmp  QWORD PTR [rsp-8], rax 
    jne  .L6 

    # cleanup omitted. 

बजना शून्य की ओर नीचे एक अलग काउंटर में गिना जाता है। लेकिन यह स्मृति-गंतव्य add [mem], -1/jnz के बजाय लोड/एड -1/स्टोर का उपयोग करता है।

यदि आप अपने हॉट लूप के उस भाग को संकलक में छोड़ने के बजाय स्वयं को लूप स्वयं लिखते हैं तो आप शायद इससे बेहतर कर सकते हैं।

पूर्णांक रजिस्ट्रारों पर रजिस्टर दबाव को कम करने के लिए पूर्णांक अंकगणितीय के लिए कुछ एक्सएमएम रजिस्टरों का उपयोग करने पर विचार करें, यदि संभव हो तो। इंटेल सीपीयू पर, जीपी और एक्सएमएम रजिस्टरों के बीच चलने से केवल 1 एएलयू यूओपी 1 सी विलंबता के साथ होता है। (यह अभी भी एएमडी पर 1 यूओपी है, लेकिन विशेष रूप से बुलडोजर-परिवार पर उच्च विलंबता)। एक्सएमएम रजिस्टरों में स्केलर पूर्णांक सामग्री करना बहुत खराब नहीं है, और यदि कुल यूओपी थ्रूपुट आपकी बाधा है, तो यह इसके लायक हो सकता है, या यह लागत से अधिक स्पिल/रीलोड बचाता है।

लेकिन निश्चित XMM के पाश काउंटर (paddd/pcmpeq/pmovmskb/cmp/jcc या psubd/ptest/jccsub [mem], 1/जेसीसी की तुलना में महान नहीं कर रहे हैं), या संकेत के लिए, या विस्तारित परिशुद्धता के लिए के लिए बहुत व्यवहार्य नहीं है अंकगणित (एक तुलना के साथ मैन्युअल रूप से कैर-आउट कर रहा है और paddq के साथ कैर-इन 32-बिट मोड में भी बेकार है जहां 64-बिट पूर्णांक regs उपलब्ध नहीं हैं)। यदि आप लोड/स्टोर यूओएस पर बाधा नहीं डालते हैं, तो एक्सएमएम रजिस्टरों की बजाय स्मृति में स्पिल/पुनः लोड करना बेहतर होता है।


आप भी लूप (सफाई या कुछ और) बाहर से कार्य करने के लिए कॉल की जरूरत है, एक आवरण लिखने या add $-128, %rsp ; call ; sub $-128, %rsp का उपयोग उन संस्करणों में लाल क्षेत्र को बचाने के लिये। (ध्यान दें कि -128 encodeable है के रूप में एक imm8 लेकिन +128 नहीं है।)

अपने सी समारोह में एक वास्तविक समारोह कॉल जरूरी यह लाल क्षेत्र ग्रहण करने के लिए सुरक्षित नहीं है सहित अप्रयुक्त है, हालांकि। (कंपाइलर-दृश्यमान) फ़ंक्शन कॉल के बीच कोई भी स्पिल/रीलोड लाल-ज़ोन का उपयोग कर सकता है, इसलिए asm कथन में सभी रजिस्टरों को क्लॉबर करना उस व्यवहार को ट्रिगर करने की काफी संभावना है।

// a non-leaf function that still uses the red-zone with gcc 
void bar(void) { 
    //cryptofunc(1); // gcc/clang don't use the redzone after this (not future-proof) 

    volatile int tmp = 1; 
    (void)tmp; 
    cryptofunc(1); // but gcc will use the redzone before a tailcall 
} 

# gcc7.2 -O3 output 
    mov  edi, 1 
    mov  DWORD PTR [rsp-12], 1 
    mov  eax, DWORD PTR [rsp-12] 
    jmp  cryptofunc(long) 

आप संकलक विशिष्ट व्यवहार पर निर्भर करना चाहते हैं, तो आप गर्म पाश से पहले एक गैर इनलाइन समारोह (नियमित रूप से सी के साथ) कह सकते हैं। मौजूदा जीसीसी/क्लैंग के साथ, इससे उन्हें पर्याप्त स्टैक स्पेस आरक्षित कर दिया जाएगा क्योंकि उन्हें किसी भी तरह का स्टैक समायोजित करना होगा (से पहले rsp को संरेखित करने के लिए)। यह भविष्य में सबूत नहीं है, लेकिन काम के साथ होना चाहिए।


GNU सी एक __attribute__((target("options"))) x86 function attribute है, लेकिन यह मनमाना विकल्प के लिए प्रयोग करने योग्य नहीं है, और -mno-redzone हैं जिन्हें आप एक प्रति-समारोह के आधार पर बदल सकते हैं, या #pragma GCC target ("options") एक संकलन इकाई के भीतर के साथ में से एक नहीं है।

आप

__attribute__((target("sse4.1,arch=core2"))) 
void penryn_version(void) { 
    ... 
} 

नहीं बल्कि __attribute__((target("-mno-redzone"))) की तरह सामान का उपयोग कर सकते हैं।

#pragma GCC optimize और optimize फ़ंक्शन-एट्रिब्यूट (दोनों जिनमें से उत्पादन कोड के लिए इरादा नहीं है), लेकिन #pragma GCC optimize ("-mno-redzone") वैसे भी काम नहीं करता है। मुझे लगता है कि विचार कुछ महत्वपूर्ण कार्यों को डीबग बिल्ड में -O2 के साथ अनुकूलित करने देना है। आप -f विकल्प या -O सेट कर सकते हैं।

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