रस्ट कंपाइलर कोड को यह मानकर ऑप्टिमाइज़ क्यों नहीं करता कि दो परस्पर संदर्भ अलग-अलग नहीं हो सकते?


301

जहां तक ​​मुझे पता है, संदर्भ / पॉइंटर एलियासिंग कंपाइलर की क्षमता को अनुकूलित कोड उत्पन्न करने में बाधा डाल सकते हैं, क्योंकि उन्हें यह सुनिश्चित करना चाहिए कि उत्पन्न बाइनरी सही तरीके से उस मामले में व्यवहार करती है जहां दो संदर्भ / पॉइंटर्स वास्तव में उपनाम हैं। उदाहरण के लिए, निम्नलिखित C कोड में,

void adds(int  *a, int *b) {
    *a += *b;
    *a += *b;
}

जब ध्वज के clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)साथ संकलित किया जाता है -O3, तो यह निकलता है

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax
   2:    03 06                    add    (%rsi),%eax
   4:    89 07                    mov    %eax,(%rdi)  # The first time
   6:    03 06                    add    (%rsi),%eax
   8:    89 07                    mov    %eax,(%rdi)  # The second time
   a:    c3                       retq

यहां कोड (%rdi)दो बार केस int *aऔर int *bउर्फ में वापस स्टोर होता है ।

जब हम संकलक को स्पष्ट रूप से बताएंगे कि ये दोनों संकेत restrictकीवर्ड के साथ नहीं जा सकते हैं :

void adds(int * restrict a, int * restrict b) {
    *a += *b;
    *a += *b;
}

फिर क्लैंग बाइनरी कोड के अधिक अनुकूलित संस्करण का उत्सर्जन करेगा:

0000000000000000 <adds>:
   0:    8b 06                    mov    (%rsi),%eax
   2:    01 c0                    add    %eax,%eax
   4:    01 07                    add    %eax,(%rdi)
   6:    c3                       retq

चूँकि Rust सुनिश्चित करता है (असुरक्षित कोड को छोड़कर) कि दो परस्पर संदर्भ अलग नहीं हो सकते, मुझे लगता है कि संकलक को कोड के अधिक अनुकूलित संस्करण का उत्सर्जन करने में सक्षम होना चाहिए।

जब मैं नीचे दिए गए कोड के साथ परीक्षण करने और साथ यह संकलन rustc 1.35.0के साथ -C opt-level=3 --emit obj,

#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
    *a += *b;
    *a += *b;
}

यह उत्पन्न करता है:

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax
   2:    03 06                    add    (%rsi),%eax
   4:    89 07                    mov    %eax,(%rdi)
   6:    03 06                    add    (%rsi),%eax
   8:    89 07                    mov    %eax,(%rdi)
   a:    c3                       retq

यह उस गारंटी का लाभ नहीं उठाता जो aऔर bअन्य नहीं हो सकती।

क्या यह इसलिए है क्योंकि वर्तमान जंग संकलक अभी भी विकास में है और अभी तक अनुकूलन करने के लिए उर्फ ​​विश्लेषण को शामिल नहीं किया है?

क्या ऐसा इसलिए है क्योंकि सुरक्षित रुस्तम में भी अभी भी एक मौका है aऔर bवह अन्य लोगों को भी दे सकता है?



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

6
मुझे यह पता लगाने में थोड़ा समय लगा कि उदाहरण क्या मिल रहा है, क्योंकि मैं एएसएम पढ़ने में कुशल नहीं हूं, इसलिए यदि यह किसी और की मदद करता है: तो यह उबलता है कि क्या +=शरीर में दो ऑपरेशनों की addsफिर से व्याख्या की जा सकती है *a = *a + *b + *b। यदि संकेत अन्य नहीं हैं, तो वे कर सकते हैं, आप यह भी देख सकते हैं कि b* + *bदूसरी एएसएम सूची में क्या मात्राएँ हैं 2: 01 c0 add %eax,%eax:। लेकिन अगर वे उर्फ ​​करते हैं, तो वे नहीं कर सकते, क्योंकि जब तक आप *bदूसरी बार जोड़ते हैं , तब तक इसमें पहली बार के आस-पास (आप 4:पहले asm लिस्टिंग की लाइन पर स्टोर करने वाले) की तुलना में एक अलग मूल्य होगा ।
dlukes

जवाबों:


364

जंग ने मूल रूप से एलएलवीएम की noaliasविशेषता को सक्षम किया , लेकिन इससे गलत कोड का कारण बना । जब सभी समर्थित LLVM संस्करण अब कोड को गलत नहीं बनाते हैं, तो इसे फिर से सक्षम किया जाएगा

यदि आप -Zmutable-noalias=yesसंकलक विकल्पों में जोड़ते हैं, तो आपको अपेक्षित असेंबली मिलती है:

adds:
        mov     eax, dword ptr [rsi]
        add     eax, eax
        add     dword ptr [rdi], eax
        ret

सीधे शब्दों में कहें, रस्ट ने हर जगह सी के restrictकीवर्ड के बराबर , किसी भी सामान्य सी प्रोग्राम की तुलना में कहीं अधिक प्रचलित किया। एलएलवीएम के इस कोने वाले व्यायाम के मामलों की तुलना में यह सही ढंग से निपटने में सक्षम था। यह पता चला है कि सी और सी ++ प्रोग्रामर बस का उपयोग नहीं करते हैं जैसा कि अक्सर जंग में किया जाता है।restrict&mut

ऐसा कई बार हुआ है

  • 1.7 के माध्यम से जंग 1.0 - noaliasसक्षम
  • 1.27 के माध्यम से जंग 1.8 - noaliasविकलांग
  • 1.29 के माध्यम से 1.28 रस्ट - noaliasसक्षम
  • 1.30 के माध्यम से जंग ??? - noaliasविकलांग

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


12
यह आश्चर्य की बात नहीं है। बहु-भाषा-मित्रता के बावजूद, व्यापक रूप से स्कोप किए गए दावों के बावजूद, LLVM को विशेष रूप से C ++ बैकएंड के रूप में डिज़ाइन किया गया था और इसमें हमेशा सी ++ की तरह न दिखने वाली चीज़ों पर ध्यान देने की एक मजबूत प्रवृत्ति होती है।
मेसन व्हीलर

47
@MasonWheeler यदि आप कुछ मुद्दों पर क्लिक करते हैं, तो आप सी कोड के उदाहरण पा सकते हैं जो restrictक्लैंग और जीसीसी दोनों पर उपयोग और गलत तरीके से करते हैं। यह उन भाषाओं तक सीमित नहीं है जो "C ++ पर्याप्त" नहीं हैं, जब तक कि आप उस समूह में C ++ की गणना स्वयं नहीं करते हैं
Shepmaster

6
@MasonWheeler: मुझे नहीं लगता कि LLVM वास्तव में C या C ++ के नियमों के आसपास डिज़ाइन किया गया था, बल्कि LLVM के नियमों के आसपास है। यह ऐसी धारणाएँ बनाता है जो आमतौर पर C या C ++ कोड के लिए सही होती हैं, लेकिन जो मैं बता सकता हूं कि यह डिज़ाइन एक स्थैतिक-डेटा-निर्भरता मॉडल पर अनुमानित है, जो मुश्किल कोने के मामलों को संभाल नहीं सकता है। यह ठीक होगा अगर यह निराशावादी रूप से डेटा निर्भरताओं को ग्रहण करता है जो कि अव्यवस्थित नहीं हो सकता है, लेकिन इसके बजाय यह नो-ऑप्स क्रियाओं के रूप में व्यवहार करता है जो उसी बिट पैटर्न के साथ भंडारण को लिखते हैं, और जिसके पास संभावित लेकिन न होने योग्य डेटा क्षमताएँ हैं। पढ़ो और लिखो।
सुपरकैट

8
@supercat मैंने आपकी टिप्पणियों को कुछ बार पढ़ा है, लेकिन मैं मानता हूं कि मैं स्तब्ध हूं - मुझे नहीं पता कि उन्हें इस प्रश्न या उत्तर के साथ क्या करना है। अपरिभाषित व्यवहार यहाँ खेलने में नहीं आता है, यह "बस" है कई अनुकूलन का मामला एक दूसरे के साथ खराब बातचीत करता है।
शीपस्टर

2
@avl_sweden को दोहराना, यह सिर्फ एक बग है । लूप अनरोलिंग ऑप्टिमाइज़ेशन स्टेप ने ( noaliasनिष्पादित ?) पूरी तरह से पॉइंटर्स को निष्पादित करते समय ध्यान में नहीं रखा। इसने नए बिंदुओं को इनपुट बिंदुओं के आधार पर बनाया, जबकि अनुचित रूप noaliasसे नए बिंदुओं ने उर्फ ​​किया, भले ही वह विशेषता की नकल कर रहा हो ।
12pm पर Shepmaster
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.