2016-12-22 8 views
34

के रूप में संकलित करते समय जीसीसी कोड पीढ़ी में बड़े अंतर मैं x86-64 असेंबली के साथ कुछ अलग-अलग सिम एक्सटेंशन (एमएमएक्स, एसएसई, एवीएक्स) के बारे में अधिक जानने की कोशिश कर रहा हूं।सी ++ बनाम सी

यह देखने के लिए कि सीसीसी या सी ++ संरचनाओं का जीसीसी द्वारा मशीन कोड में कितना अलग अनुवाद किया गया है, मैं Compiler Explorer का उपयोग कर रहा हूं जो एक शानदार उपकरण है।

मेरे 'प्ले सत्र' में से एक के दौरान मैं देखना चाहता था कि जीसीसी एक पूर्णांक सरणी के सरल रन-टाइम प्रारंभिकरण को कैसे अनुकूलित कर सकता है। इस मामले में मैंने संख्या 20 से 2047 को 2048 हस्ताक्षरित पूर्णांकों की सरणी में लिखने की कोशिश की।

कोड इस प्रकार दिखता है:

unsigned int buffer[2048]; 

void setup() 
{ 
    for (unsigned int i = 0; i < 2048; ++i) 
    { 
    buffer[i] = i; 
    } 
} 

अगर मैं अनुकूलन और AVX-512 निर्देश सक्षम -O3 -mavx512f -mtune=intel जीसीसी 6.3 वास्तव में कुछ चालाक कोड :)

setup(): 
     mov  eax, OFFSET FLAT:buffer 
     mov  edx, OFFSET FLAT:buffer+8192 
     vmovdqa64  zmm0, ZMMWORD PTR .LC0[rip] 
     vmovdqa64  zmm1, ZMMWORD PTR .LC1[rip] 
.L2: 
     vmovdqa64  ZMMWORD PTR [rax], zmm0 
     add  rax, 64 
     cmp  rdx, rax 
     vpaddd zmm0, zmm0, zmm1 
     jne  .L2 
     ret 
buffer: 
     .zero 8192 
.LC0: 
     .long 0 
     .long 1 
     .long 2 
     .long 3 
     .long 4 
     .long 5 
     .long 6 
     .long 7 
     .long 8 
     .long 9 
     .long 10 
     .long 11 
     .long 12 
     .long 13 
     .long 14 
     .long 15 
.LC1: 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 
     .long 16 

, जब मैं परीक्षण किया उत्पन्न करता है फिर भी क्या होगा अगर 0xझंडे जोड़कर जीसीसी सी-कंपाइलर का उपयोग करके उसी कोड को संकलित किया गया था तो मैं उत्पन्न हुआ था। मैं वास्तव में आश्चर्यचकित था।

मुझे समान उम्मीद है, अगर समान नहीं है, तो परिणामस्वरूप सी-कंपाइलर उत्पन्न करता है अधिक जटिल और संभवतः धीमे मशीन कोड भी। परिणामी असेंबली पूरी तरह से पेस्ट करने के लिए बहुत बड़ी है, लेकिन इसे this लिंक का पालन करके godbolt.org पर देखा जा सकता है।

उत्पन्न कोड का एक टुकड़ा, लाइनों 83 करने के लिए 58, नीचे देखा जा सकता है:

.L2: 
     vpbroadcastd zmm0, r8d 
     lea  rsi, buffer[0+rcx*4] 
     vmovdqa64  zmm1, ZMMWORD PTR .LC1[rip] 
     vpaddd zmm0, zmm0, ZMMWORD PTR .LC0[rip] 
     xor  ecx, ecx 
.L4: 
     add  ecx, 1 
     add  rsi, 64 
     vmovdqa64  ZMMWORD PTR [rsi-64], zmm0 
     cmp  ecx, edi 
     vpaddd zmm0, zmm0, zmm1 
     jb  .L4 
     sub  edx, r10d 
     cmp  r9d, r10d 
     lea  eax, [r8+r10] 
     je  .L1 
     mov  ecx, eax 
     cmp  edx, 1 
     mov  DWORD PTR buffer[0+rcx*4], eax 
     lea  ecx, [rax+1] 
     je  .L1 
     mov  esi, ecx 
     cmp  edx, 2 
     mov  DWORD PTR buffer[0+rsi*4], ecx 
     lea  ecx, [rax+2] 

आप देख सकते हैं, इस कोड को जटिल चाल का एक बहुत कुछ है और कूदता है और एक बहुत ही तरह सामान्य महसूस करता में एक सरल सरणी प्रारंभ करने का जटिल तरीका।

जेनरेट कोड में इतना बड़ा अंतर क्यों है?

क्या जीसीसी सी ++ - सी-कंपाइलर की तुलना में सी और सी ++ दोनों में मान्य कोड को अनुकूलित करने में सामान्य रूप से बेहतर है?

+2

अतिरिक्त डेटा बिंदु: 'स्थिर हस्ताक्षरित int बफर [2048] का उपयोग करके; 'सी कोड को भी समान बनाता है। आपको वास्तव में 'बफर' का उपयोग करना होगा ताकि यह पूरी तरह से समाप्त नहीं हो पाये।ऐसा लगता है कि यह एक संरेखण मुद्दा है, गलत कोड संभालने के लिए अतिरिक्त कोड है। – Jester

+1

समान वाक्यविन्यास/व्याकरण समान अर्थशास्त्र का अर्थ नहीं है। आप ** अलग ** भाषाओं सी और सी ++ को एक ही कोड उत्पन्न करने की अपेक्षा क्यों करते हैं? यह सवाल एक जीसीसी मंच में बेहतर पूछा जाता है। – Olaf

+9

@ ओलाफ शायद आप कोड के इस टुकड़े के लिए सी और सी ++ में सेमेन्टिक्स के बीच अंतर पर भर सकते हैं –

उत्तर

39

अतिरिक्त कोड गलत संरेखण को संभालने के लिए है क्योंकि vmovdqa64 का उपयोग किया गया निर्देश 64 बाइट संरेखण की आवश्यकता है।

मेरा परीक्षण दिखाता है कि मानक मानक होने के बावजूद, जीसीसी किसी अन्य मॉड्यूल में परिभाषा को सी मोड में यहां ओवरराइड करने की अनुमति देता है। यह परिभाषा केवल मूल संरेखण आवश्यकताओं (4 बाइट्स) का अनुपालन कर सकती है, इस प्रकार संकलक बड़े संरेखण पर भरोसा नहीं कर सकता है। तकनीकी रूप से, जीसीसी इस टिकाऊ परिभाषा के लिए .comm असेंबली निर्देश उत्सर्जित करता है, जबकि बाहरी परिभाषा .data अनुभाग में सामान्य प्रतीक का उपयोग करती है। इस प्रतीक को जोड़ने के दौरान .comm पर प्राथमिकता लेती है।

नोट करें कि यदि आप extern unsigned int buffer[2048]; का उपयोग करने के लिए प्रोग्राम को बदलते हैं तो भी C++ संस्करण में अतिरिक्त कोड होगा। इसके विपरीत, इसे static unsigned int buffer[2048]; बनाना सी संस्करण को अनुकूलित में बदल देगा।

+0

इस कोड में जोड़ने के संबंध में सी और सी ++ के बीच कोई अंतर नहीं है। दोनों भाषाओं में: (ए) 'बफर 'में बाहरी संबंध है, (बी) कोई अन्य इकाई * * बफर' परिभाषित नहीं कर सकती है, (सी) अन्य इकाइयां * * बफर 'घोषित कर सकती हैं। –

+0

"सी मामले में, सरणी में किसी अन्य मॉड्यूल में परिभाषा हो सकती है" - यह मानक सी में अपरिभाषित व्यवहार होगा। लिंकेज के बारे में चर्चा लाल हेरिंग की तरह लगती है, यह देखते हुए कि '= {0}' भी " अनुकूलित "संस्करण –

+2

नोट, जीसीसी के साथ सी कोड संस्करण को संकलित करने के लिए, आप '-फनो-आम' कंपाइलर ध्वज जोड़ सकते हैं, या' __attribute __ ((गठबंधन (64)) 'के साथ' बफर 'चर को एनोटेट कर सकते हैं, और यह सी ++ संस्करण के लिए समान कोड उत्पन्न करेगा। – nos

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