c++ बिटवाइज़ का कंपाइलर ऑप्टिमाइज़ेशन ऑपरेशन नहीं




embedded iar (3)

यदि दो सरणियाँ एक-दूसरे से उलट हों तो मेरा एक साधारण कार्य परीक्षण है। वे समान रूप से समान हैं, सिवाय एक tmp चर के। एक काम करता है दूसरा नहीं करता है। मैं अपने जीवन के लिए यह पता नहीं लगा सकता कि संकलक इस का अनुकूलन क्यों करेगा - यदि यह वास्तव में अनुकूलन समस्या है (मेरा संकलक IAR कार्यक्षेत्र v4.30.1 है)। यहाँ मेरा कोड है:

// this works as expected
uint8 verifyInverseBuffer(uint8 *buf, uint8 *bufi, uint32 len)
{
  uint8 tmp;
  for (uint32 i = 0; i < len; i++)
  {
    tmp = ~bufi[i];
    if (buf[i] != tmp)
    {
      return 0;
    }
  }
  return 1;  
}

// this does NOT work as expected (I only removed the tmp!)
uint8 verifyInverseBuffer(uint8 *buf, uint8 *bufi, uint32 len)
{
  for (uint32 i = 0; i < len; i++)
  {
    if (buf[i] != (~bufi[i]))
    {
      return 0;
    }
  }
  return 1;  
}

कोड का पहला संस्करण काम करता है, दूसरा नहीं करता है। क्या कोई यह पता लगा सकता है कि क्यों? या जांच करने के लिए कुछ परीक्षण आते हैं कि क्या गलत है?


आप जो देखते हैं वह पूर्णांक पदोन्नति के नियमों का परिणाम है। किसी भी समय एक int से छोटा एक चर का उपयोग एक अभिव्यक्ति में किया जाता है, मान को int टाइप करने के लिए बढ़ावा दिया जाता है।

मान लीजिए कि bufi[i] में मान 255 है। इस का हेक्स प्रतिनिधित्व 0xFF । यह मान तब ~ ऑपरेटर के ऑपरेंड है। इसलिए मूल्य को पहले int बढ़ावा दिया जाएगा (यह मानते हुए कि यह 32 बिट है) का मान 0x000000FF , और इस पर आवेदन करने से आपको 0xFFFFFF00 । आप फिर इस मान की तुलना buf[i] जो कि uint8_t प्रकार का है। मान 0xFFFFFF00 इस सीमा के बाहर है, इसलिए तुलना हमेशा झूठी होगी।

यदि आप uint8_t वैरिएबल पर ~ के परिणाम को असाइन करते हैं, तो 0xFFFFFF00 का मान 0x00 बदल जाता है। यह यह परिवर्तित मान है जो तब buf[i] मुकाबले तुलना की जाती है।

इसलिए आपके द्वारा देखा जाने वाला व्यवहार एक अनुकूलन का परिणाम नहीं है, बल्कि भाषा के नियमों का है। एक अस्थायी चर का उपयोग कर के रूप में आप इस मुद्दे को संबोधित करने का एक तरीका है। आप uint8 परिणाम भी डाल सकते हैं:

if(buf[i] != (uint8)(~bufi[i]))

या सभी कम से कम आदेश बाइट बाहर मुखौटा:

if(buf[i] != (~bufi[i] & 0xff))

जैसा कि पहले से ही लुंडिन और डीबुश द्वारा उल्लेख किया गया है, दूसरे संस्करण में तुलना हमेशा विफल रहती है क्योंकि int को बढ़ावा uint8 किसी भी uint8 मूल्य के विपरीत सभी uint8 मूल्यों से अलग है। दूसरे शब्दों में, दूसरा संस्करण इसके बराबर है:

// this does NOT work as expected (I only removed the tmp!)
uint8 verifyInverseBuffer(uint8 *buf, uint8 *bufi, uint32 len) {
    if (len) return 0;
    return 1;
}

जैसा कि गॉडबोल्ट के संकलक एक्सप्लोरर पर देखा जा सकता है, दोनों gcc और clang इसका पता लगाते हैं और कोड को पूरी तरह से अनुकूलित करते हैं:

verifyInverseBuffer:
    test    edx, edx
    sete    al
    ret

gcc एक स्पष्ट हस्ताक्षरित / अहस्ताक्षरित तुलना मुद्दे की ओर संकेत करते हुए एक बल्कि गुप्त चेतावनी पैदा करता है, जो वास्तविक समस्या नहीं है ... बंद करें लेकिन कोई केला नहीं।

<source>: In function 'verifyInverseBuffer':
<source>:8:16: warning: comparison of promoted bitwise complement of an unsigned value with unsigned [-Wsign-compare]
    8 |     if (buf[i] != (~bufi[i]))
      |                ^~
Compiler returned: 0

समस्या पूर्णांक पदोन्नति है। ~ ऑपरेटर बहुत खतरनाक है!

~bufi[i] मामले में, ~bufi[i] के ऑपरेंड को पूर्णांक प्रचार के अनुसार प्रचारित किया जाता है। कोड को ~(int)bufi[i] के समतुल्य बनाना।

तो दूसरे मामले में buf[i] != (~bufi[i]) आपको 0xXX != 0xFFFFFFFFYY जैसा कुछ मिलता है 0xXX != 0xFFFFFFFFYY , जहां "XX" और "YY" वे वास्तविक मूल्य हैं जिनकी आप तुलना करना चाहते हैं और 0xFFFF अनपेक्षित बकवास है। एक int के बिटवाइज़ सप्लीमेंट लेकर। यह हमेशा true मूल्यांकन करेगा, ताकि संकलक कोड के कुछ हिस्सों का अनुकूलन कर सके, एक बहुत ही सूक्ष्म बग का निर्माण करेगा।

tmp = ~bufi[i]; मामले में tmp = ~bufi[i]; आप इस बग को "YY" में 0xFFFFFFFFYY करके उस मान को चकमा देते हैं, जिस मूल्य में आप रुचि रखते हैं।

विवरण के लिए लागू प्रकार के प्रचार नियम देखें। इसके अलावा सूक्ष्म कीड़े को चकमा देने के लिए MISRA-C को अपनाने पर विचार करें।





iar