2015-12-14 8 views
5

को पुनः लोड करते समय कक्षा 'GENERAL_REGS' में कोई रजिस्टर नहीं मिल रहा है, मैं एआरएम कॉर्टेक्स-ए 8 पर एआरएम असेंबली में 256-बिट ऑपरेंड के साथ 32-बिट ऑपरेंड को गुणा करने की कोशिश कर रहा हूं। समस्या यह है कि मैं रजिस्टरों से बाहर निकल रहा हूं और मुझे नहीं पता कि मैं यहां इस्तेमाल किए गए रजिस्टरों की संख्या को कैसे कम कर सकता हूं। यहाँ मेरी समारोह है:एआरएम असेंबली: 'asm'

typedef struct UN_256fe{ 

uint32_t uint32[8]; 

}UN_256fe; 

typedef struct UN_288bite{ 

uint32_t uint32[9]; 

}UN_288bite; 
void multiply32x256(uint32_t A, UN_256fe* B, UN_288bite* res){ 

asm (

     "umull   r3, r4, %9, %10;\n\t" 
     "mov   %0, r3;   \n\t"/*res->uint32[0] = r3*/ 
     "umull   r3, r5, %9, %11;\n\t" 
     "adds   r6, r3, r4;  \n\t"/*res->uint32[1] = r3 + r4*/ 
     "mov   %1, r6;   \n\t" 
     "umull   r3, r4, %9, %12;\n\t" 
     "adcs   r6, r5, r3;  \n\t" 
     "mov   %2, r6;   \n\t"/*res->uint32[2] = r6*/ 
     "umull   r3, r5, %9, %13;\n\t" 
     "adcs   r6, r3, r4;  \n\t" 
     "mov   %3, r6;   \n\t"/*res->uint32[3] = r6*/ 
     "umull   r3, r4, %9, %14;\n\t" 
     "adcs   r6, r3, r5;  \n\t" 
     "mov   %4, r6;   \n\t"/*res->uint32[4] = r6*/ 
     "umull   r3, r5, %9, %15;\n\t" 
     "adcs   r6, r3, r4;  \n\t" 
     "mov   %5, r6;   \n\t"/*res->uint32[5] = r6*/ 
     "umull   r3, r4, %9, %16;\n\t" 
     "adcs   r6, r3, r5;  \n\t" 
     "mov   %6, r6;   \n\t"/*res->uint32[6] = r6*/ 
     "umull   r3, r5, %9, %17;\n\t" 
     "adcs   r6, r3, r4;  \n\t" 
     "mov   %7, r6;   \n\t"/*res->uint32[7] = r6*/ 
     "adc   r6, r5, #0 ; \n\t" 
     "mov   %8, r6;   \n\t"/*res->uint32[8] = r6*/ 

     : "=r"(res->uint32[8]), "=r"(res->uint32[7]), "=r"(res->uint32[6]), "=r"(res->uint32[5]), "=r"(res->uint32[4]), 
      "=r"(res->uint32[3]), "=r"(res->uint32[2]), "=r"(res->uint32[1]), "=r"(res->uint32[0]) 
     : "r"(A), "r"(B->uint32[7]), "r"(B->uint32[6]), "r"(B->uint32[5]), 
      "r"(B->uint32[4]), "r"(B->uint32[3]), "r"(B->uint32[2]), "r"(B->uint32[1]), "r"(B->uint32[0]), "r"(temp) 
     : "r3", "r4", "r5", "r6", "cc", "memory"); 

} 

संपादित-1: मैं अपने पीटना पहली टिप्पणी के आधार पर सूची को अपडेट, लेकिन मैं अब भी वही त्रुटि

+0

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

+0

@RossRidge क्या कोई तरीका है कि मैं अपने इनपुट से पहले 'आर' 'के बजाय एक और नोटेशन का उपयोग कर सकता हूं और सही परिणाम प्राप्त कर सकता हूं? मेरा मतलब कुछ है "जी" या '" एम "'? – A23149577

+0

आपको वास्तव में एक लूप की आवश्यकता है [पुनरावृत्ति गणना 8 के साथ] जो आप कर रहे हैं उससे। पुनर्विचार: यदि आपके इनपुट वेक्टर में 20,000 तत्व थे तो आप यह कैसे करेंगे? आपको स्केलर 'ए' वैल्यू,' बी 'पीआरटी के लिए reg,' res' ptr के लिए reg, पुनरावृत्ति गणना के लिए reg, और जो भी अन्य regs आपको umull et करने की आवश्यकता है, के लिए reg की आवश्यकता होगी। अल [शायद एक और 4-6] प्रत्येक पाश पुनरावृत्ति पर, तो कुल ~ 10 है। जैसा कि है, आप 2-3 के वेक्टर आकार के साथ regs से बाहर निकलते हैं, अकेले रहने दें 8. अपने वेक्टर एल्गोरिदम को सीधे प्राप्त करने के लिए, सी एफएनसी कोडिंग करने के बारे में यह कैसे करता है [यह आपके लिए एएसएम एफएनसी के संदर्भ में भी कार्य करता है]। –

उत्तर

0

एक सरल समाधान इस अप और डॉन को तोड़ने के लिए है मिल 'क्लोबबर' का उपयोग नहीं करें। चर 'tmp1' के रूप में घोषित करें, आदि mov कथन का उपयोग न करने का प्रयास करें; यदि इसे करना है तो संकलक इसे ऐसा करने दें। संकलक जानकारी के सर्वोत्तम 'प्रवाह' को जानने के लिए एल्गोरिदम का उपयोग करेगा। यदि आप 'क्लॉबर' का उपयोग करते हैं, तो यह रजिस्टरों का पुन: उपयोग नहीं कर सकता है। वे जिस तरह से हैं, आप इसे असेंबलर निष्पादित करने से पहले सभी स्मृति को लोड करते हैं। यह खराब है क्योंकि आप मेमोरी/सीपीयू एएलयू पाइपलाइन चाहते हैं।

void multiply32x256(uint32_t A, UN_256fe* B, UN_288bite* res) 
{ 

    uint32_t mulhi1, mullo1; 
    uint32_t mulhi2, mullo2; 
    uint32_t tmp; 

    asm("umull   %0, %1, %2, %3;\n\t" 
     : "=r" (mullo1), "=r" (mulhi1) 
     : "r"(A), "r"(B->uint32[7]) 
); 
    res->uint32[8] = mullo1; /* was 'mov %0, r3; */ 
    volatile asm("umull   %0, %1, %3, %4;\n\t" 
     "adds   %2, %5, %6;  \n\t"/*res->uint32[1] = r3 + r4*/ 
    : "=r" (mullo2), "=r" (mulhi2), "=r" (tmp) 
    : "r"(A), "r"(B->uint32[6]), "r" (mullo1), "r"(mulhi1) 
    : "cc" 
    ); 
    res->uint32[7] = tmp; /* was 'mov %1, r6; */ 
    /* ... etc */ 
} 

'जीसीसी इनलाइन असेंबलर' का पूरा उद्देश्य सीधे 'सी' फ़ाइल में असेंबलर को कोड नहीं करना है। यह संकलक और के रजिस्टर आवंटन तर्क का उपयोग करना है जो 'सी' में आसानी से नहीं किया जा सकता है। आपके मामले में लेयर तर्क का उपयोग।

इसे एक बड़ा 'एएसएम' खंड नहीं बनाकर, संकलक लोड से शेड्यूल को शेड्यूल कर सकता है क्योंकि इसे नए रजिस्टरों की आवश्यकता होती है। यह लोड/स्टोर इकाई के साथ आपकी 'उमूल' एएलयू गतिविधि को भी पाइपलाइन करेगा।

यदि आपको निर्देश एक विशिष्ट रजिस्टर को स्पष्ट रूप से क्लॉबर्स करता है तो आपको केवल क्लॉबर का उपयोग करना चाहिए। आप कुछ भी उपयोग कर सकते हैं,

register int *p1 asm ("r0"); 

और आउटपुट के रूप में इसका उपयोग करें। हालांकि, मुझे इस तरह के किसी भी एआरएम निर्देशों के बारे में पता नहीं है जो ढेर को बदल सकते हैं और आपका कोड इनका उपयोग नहीं करता है और पाठ्यक्रम का पालन नहीं करता है।

जीसीसी जानता है कि अगर इनपुट/आउटपुट के रूप में सूचीबद्ध किया गया है तो मेमोरी बदलती है, इसलिए आपको मेमोरी क्लॉबर की आवश्यकता नहीं है। वास्तव में यह मेमोरी क्लोबबर compiler memory barrier है और इससे स्मृति को लिखा जा सकता है जब संकलक बाद में शेड्यूल कर सकता है।


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

आप the GMP library पर देखना चाहते हैं, जिसमें आपके कोड की तरह दिखने वाले कुछ मुद्दों को कुशलता से निपटने के कई तरीके हैं।

+0

सबसे पहले आपके संक्षिप्त उत्तर के लिए धन्यवाद। मेरे पास एक सवाल है, अगर मैं इस समारोह से सर्वश्रेष्ठ प्रदर्शन प्राप्त करना चाहता हूं, तो क्या इनलाइन असेंबली में सभी कोड को लागू करना बेहतर नहीं है? मुझे पता है कि मैं यहां रजिस्टरों से बाहर हो सकता हूं, लेकिन मैं दो रजिस्टरों के अंदर अपने पॉइंटर्स ('* बी' और' * res') डालने की सोच रहा था और 'ldr' निर्देश का उपयोग करके प्रत्येक' uint32_t' सरणी तक पहुंच प्राप्त कर रहा था। मुझे यकीन नहीं है कि यह संभव है, लेकिन प्रदर्शन मेरे लिए वास्तव में महत्वपूर्ण है – A23149577

+0

हां, आप अपने कोड में 'ldr' का उपयोग कर सकते हैं; और मैं इसके बारे में एक बेंच मार्क के रूप में सोच रहा था। कंपाइलर में एएलयू और लोड/स्टोर ('ldr/str') निर्देशों को अंतःस्थापित करने के लिए ह्यूरिस्टिक्स और स्मारक हैं। यह मेमोरी ऑर्डर के आधार पर 'ldm/stm' और/या' ldrd/strd' भी कर सकता है और कौन से रजिस्ट्रार उपलब्ध हैं और सीपीयू की श्रेणी (आप इसे ए 8 के लिए कड़ी मेहनत करेंगे जहां लोड/स्टोर नहीं कर रहा है) चुनने के लिए संकलक)। हर तरह से आप एक बड़ी दिनचर्या कर सकते हैं और 'ldr' का उपयोग कर सकते हैं क्योंकि आपने एआरएम रजिस्टरों से बाहर भाग लिया है। हालांकि, मुझे लगता है कि यदि आप विभाजन करते हैं और आउटपुट देखते हैं तो आपको बहुत कुछ पता चल जाएगा। –

+1

आप आश्चर्यचकित हो सकते हैं कि आपका एल्गोरिदम स्मृति बाध्य/प्रभावशाली है और सीपीयू बाध्य/प्रभावशाली नहीं है। निश्चित रूप से आपके वर्तमान बड़े दिनचर्या इन ऑपरेशन * पाइपलाइन * को नहीं दे रहे हैं या समानांतर में होते हैं (लोग मेमोरी स्टॉल भी कहते हैं)। मुझे विश्वास है कि आप इसे आउटपुट देखने के बाद कंपाइलर को हरा सकते हैं। अगर मैं नहीं देखता कि हाथ से पहले यह किस तरह की चीजें कर सकता है तो मुझे इसे मारना दुर्लभ है (बड़े आकार के दिनचर्या पर)। वर्तमान में आपके पास 9 /25 ~ = 36% निर्देश 'mov' कथन के रूप में हैं और कंपेलर चीजों को लोड/स्टोर करने के लिए जिम्मेदार नहीं है। –

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