2012-10-16 18 views
14

मैं कुछ कोड है कि मुझे स्थानांतरण त्रुटियों दे रही है जब संकलन है नीचे एक उदाहरण है जो समस्या को दिखाता है:डमीज़ के लिए gfortran: mcmodel = मध्यम वास्तव में क्या करता है?

program main 
    common/baz/a,b,c 
    real a,b,c 
    b = 0.0 
    call foo() 
    print*, b 
    end 

    subroutine foo() 
    common/baz/a,b,c 
    real a,b,c 

    integer, parameter :: nx = 450 
    integer, parameter :: ny = 144 
    integer, parameter :: nz = 144 
    integer, parameter :: nf = 23*3 
    real :: bar(nf,nx*ny*nz) 

    !real, allocatable,dimension(:,:) :: bar 
    !allocate(bar(nf,nx*ny*nz)) 

    bar = 1.0 
    b = bar(12,32*138*42) 

    return 
    end 

gfortran -O3 -g -o test test.f के साथ इस संकलन, मैं निम्न त्रुटि:

relocation truncated to fit: R_X86_64_PC32 against symbol `baz_' defined in COMMON section in /tmp/ccIkj6tt.o 

लेकिन यह काम करता है अगर मैं gfortran -O3 -mcmodel=medium -g -o test test.f का उपयोग करता हूं। यह भी ध्यान रखें कि यह काम करता है अगर मैं सरणी आवंटित करता हूं और इसे सबराउटिन के भीतर आवंटित करता हूं।

मेरा प्रश्न है वास्तव में -mcmodel=medium क्या करता है? मैं धारणा है कि कोड (allocatable सरणियों के साथ एक और बिना एक) के दो संस्करणों में कम या ज्यादा बराबर थे तहत किया गया था ...

उत्तर

27

bar के बाद से काफी बड़ी संकलक स्टैक पर बजाय स्थिर आवंटन स्वत: आवंटन उत्पन्न करता है। स्टेटिक सरणी .comm असेंबली निर्देश के साथ बनाई गई हैं जो तथाकथित COMMON अनुभाग में आवंटन करती है। उस खंड से प्रतीकों को इकट्ठा किया जाता है, समान नाम वाले प्रतीकों को विलय कर दिया जाता है (अनुरोध किए गए सबसे बड़े आकार के बराबर आकार के साथ एक प्रतीक अनुरोध में कम किया जाता है) और फिर अधिकांश निष्पादन योग्य प्रारूपों में बीएसएस (अनियंत्रित डेटा) अनुभाग में शेष को मैप किया जाता है। ईएलएफ निष्पादन योग्य के साथ .bss अनुभाग डेटा सेगमेंट में स्थित है, केवल ढेर के डेटा सेगमेंट हिस्से से पहले (अज्ञात मेमोरी मैपिंग्स द्वारा प्रबंधित एक और ढेर हिस्सा है जो डेटा सेगमेंट में नहीं रहता है)।

small मेमोरी मॉडल 32-बिट एड्रेसिंग निर्देशों के साथ x86_64 पर प्रतीकों को संबोधित करने के लिए उपयोग किया जाता है। यह कोड को छोटा और तेज़ बनाता है। कुछ विधानसभा उत्पादन जब small स्मृति मॉडल का उपयोग:

movl $bar.1535, %ebx <---- Instruction length saving 
... 
movl %eax, baz_+4(%rip) <---- Problem!! 
... 
.local bar.1535 
.comm bar.1535,2575411200,32 
... 
.comm baz_,12,16 

इस में (यह मान प्रतीक स्थान के पते के बराबर) bar.1535 प्रतीक का मूल्य डाल करने के लिए एक 32-बिट कदम अनुदेश (5 बाइट्स लंबे) का उपयोग करता है RBX रजिस्टर के निचले 32 बिट्स (ऊपरी 32 बिट्स शून्य हो जाते हैं)। bar.1535 प्रतीक स्वयं को .comm निर्देश का उपयोग करके आवंटित किया गया है। baz के लिए मेमोरी बाद में COMMON ब्लॉक आवंटित किया गया है। क्योंकि bar.1535 बहुत बड़ा है, baz_.bss सेक्शन की शुरुआत से 2 जीबी से अधिक समाप्त होता है। यह RIP से गैर-32 बिट (हस्ताक्षरित) ऑफ़सेट के बाद b वैरिएबल को संबोधित करने के लिए उपयोग किया जाना चाहिए, जहां EAX के मान को स्थानांतरित किया जाना चाहिए, दूसरे movl निर्देश में समस्या उत्पन्न हुई है। यह केवल लिंक समय के दौरान पता चला है। असेंबलर को उचित ऑफसेट नहीं पता है क्योंकि यह नहीं जानता कि निर्देश सूचक (RIP) का मूल्य क्या होगा (यह पूर्ण वर्चुअल एड्रेस पर निर्भर करता है जहां कोड लोड होता है और यह लिंकर द्वारा निर्धारित किया जाता है), इसलिए यह बस 0 का ऑफ़सेट रखता है और फिर R_X86_64_PC32 टाइप का स्थानांतरण स्थान बनाता है। यह वास्तविक ऑफसेट मान के साथ 0 के मान को पैच करने के लिए लिंकर को निर्देश देता है। लेकिन ऐसा नहीं हो सकता है क्योंकि ऑफसेट मान एक हस्ताक्षरित 32-बिट पूर्णांक के अंदर फिट नहीं होगा और इसलिए बाहर निकलता है।

जगह बातों में medium स्मृति मॉडल के साथ इस तरह दिखेगा:

movabsq $bar.1535, %r10 
... 
movl %eax, baz_+4(%rip) 
... 
.local bar.1535 
.largecomm  bar.1535,2575411200,32 
... 
.comm baz_,12,16 

पहले एक 64-बिट तत्काल कदम अनुदेश (10 बाइट्स लंबे) 64-बिट मूल्य जिनमें से पते का प्रतिनिधित्व डाल करने के लिए प्रयोग किया जाता है bar.1535 रजिस्टर R10 में। bar.1535 प्रतीक के लिए मेमोरी .largecomm निर्देश का उपयोग करके आवंटित की जाती है और इस प्रकार यह ईएलएफ निष्कर्ष के .lbss अनुभाग में समाप्त होता है। .lbss दुकान प्रतीकों छोटे बातें .bss करने के लिए जाना है, जबकि जो पहले 2 GiB में फिट हो सकता है नहीं (और इसलिए 32-बिट निर्देश या आरआईपी-रिश्तेदार को संबोधित का उपयोग कर संबोधित नहीं किया जाना चाहिए), करने के लिए प्रयोग किया जाता है (baz_ अभी भी .comm और नहीं .largecomm का उपयोग कर आवंटित किया जाता है)। चूंकि .lbss अनुभाग ईएलएफ लिंकर स्क्रिप्ट में .bss अनुभाग के बाद रखा गया है, baz_ 32-बिट आरआईपी-संबंधित पते का उपयोग करके पहुंच से बाहर नहीं होगा।

सभी संबोधित मोड System V ABI: AMD64 Architecture Processor Supplement में वर्णित हैं। यह एक भारी तकनीकी पठन है लेकिन किसी को भी पढ़ना चाहिए जो वास्तव में समझना चाहता है कि 64-बिट कोड अधिकांश x86_64 यूनिक्स पर कैसे काम करता है।

movl $2575411200, %edi 
... 
call malloc 
movq %rax, %rdi 

यह मूलतः RDI = malloc(2575411200) है:

जब एक ALLOCATABLE सरणी के बजाय प्रयोग किया जाता है, gfortran ढेर स्मृति (सबसे अधिक संभावना एक गुमनाम स्मृति नक्शा आवंटन के बड़े आकार को देखते हुए के रूप में लागू) आवंटित करता है। तो bar के तत्वों पर से RDI में संग्रहीत मूल्य से सकारात्मक ऑफसेट का उपयोग करके पहुंचा जा सकता है:

movl 51190040(%rdi), %eax 
movl %eax, baz_+4(%rip) 

स्थानों कि bar की शुरुआत से अधिक से अधिक 2 GiB हैं, उनके लिए एक अधिक विस्तृत विधि का इस्तेमाल किया जाता है। जैसे कुछ भी नहीं के बाद से पता नहीं है जहां गतिशील आवंटन किया जाएगा के बारे में माना जाता है

; Some computations that leave the offset in RAX 
movl (%rdi,%rax), %eax 
movl %eax, baz_+4(%rip) 

इस कोड को स्मृति मॉडल से प्रभावित नहीं है: b = bar(12,144*144*450)gfortran का उत्सर्जन करता है लागू करने के लिए। इसके अलावा, चूंकि सरणी पारित नहीं होती है, इसलिए कोई वर्णनकर्ता नहीं बनाया जा रहा है। यदि आप एक और फ़ंक्शन जोड़ते हैं जो मानी गई आकार वाली सरणी लेता है और bar पास करता है, तो bar के लिए एक वर्णक स्वचालित चर के रूप में बनाया गया है (यानी foo के ढेर पर)। (

movl $bar.1580, %edi 
... 
; RAX still holds the address of the allocated memory as returned by malloc 
; Computations, computations 
movl -232(%rax,%rdx,4), %eax 
movl %eax, baz_+4(%rip) 

पहला कदम एक समारोह कॉल के तर्क को तैयार करता है मेरी नमूना मामले call boo(bar) जहां boo एक अंतरफलक है कि वाणी है में: सरणी SAVE विशेषता के साथ स्थिर किया जाता है, वर्णनकर्ता .bss खंड में रखा गया है यह एक अनुमानित आकार सरणी लेने के रूप में)। यह bar के सरणी वर्णनकर्ता का पता EDI में ले जाता है। यह एक 32-बिट तत्काल कदम है ताकि वर्णनकर्ता पहले 2 जीबीबी में होने की उम्मीद है। वास्तव में, यह दोनों small और medium स्मृति मॉडल इस तरह में .bss में आवंटित किया जाता है:

.local bar.1580 
.comm bar.1580,72,32 
+1

यह एक बहुत अच्छा स्पष्टीकरण है। धन्यवाद। यह मुझे इस सामान के एक समूह में बहुत गहराई से देखने के लिए एक अच्छी शुरुआत देता है (जो मैं चाहता था)। – mgilson

+0

@mgilson, सिर्फ उत्तर की पूर्णता के लिए, मैंने यह भी बताया है कि क्या होता है जब वर्णनकर्ता द्वारा अन्य उप-सूटिन में 'बार' पास किया जाता है। –

8

नहीं है, (अपने bar के रूप में) बड़े स्थिर सरणियों सीमा से अधिक हो सकता है, तो आप -mcmodel=medium का उपयोग नहीं करते हैं। लेकिन आवंटन निश्चित रूप से बेहतर हैं। आवंटन के लिए केवल सरणी वर्णक 2 जीबी में फिट होना चाहिए, पूरे सरणी में नहीं।

जीसीसी संदर्भ से:

-mcmodel=small 
Generate code for the small code model: the program and its symbols must be linked in the lower 2 GB of the address space. Pointers are 64 bits. Programs can be statically or dynamically linked. This is the default code model. 
-mcmodel=kernel 
Generate code for the kernel code model. The kernel runs in the negative 2 GB of the address space. This model has to be used for Linux kernel code. 
-mcmodel=medium 
Generate code for the medium model: The program is linked in the lower 2 GB of the address space but symbols can be located anywhere in the address space. Programs can be statically or dynamically linked, but building of shared libraries are not supported with the medium model. 
-mcmodel=large 
Generate code for the large model: This model makes no assumptions about addresses and sizes of sections. Currently GCC does not implement this model. 
+0

मुझे लगता है, है शायद सवाल क्या एक "स्थिर सरणी" और एक "allocatable सरणी" के बीच अंतर है?मेरी धारणा यह थी कि उन्हें दोनों मामलों में ढेर से आवंटित किया जाएगा (हालांकि मुझे यह स्वीकार करना चाहिए कि मैं उन चीज़ों के बारे में बात कर रहा हूं जिनके बारे में मुझे ज्यादा ज्ञान नहीं है) – mgilson

+0

मैंने लिखा जब आपने अभी जवाब संपादित किया था। Allocatables में एक वर्णक (अतिरिक्त डेटा के साथ सूचक) है और केवल यह 2 जीबी में फिट होना चाहिए। स्टेटिक सरणी स्थिर खंड में पूरी तरह से किसी अन्य स्थिर चर के रूप में है। –

+0

(शायद स्थिर खंड में वर्णक के लिए केवल एक सूचक है, लेकिन यह अंतर नहीं बदलता है।) –