sourcecode

컴파일러 최적화 bitwise not operation

copyscript 2023. 10. 6. 21:56
반응형

컴파일러 최적화 bitwise not operation

저는 두 배열이 서로 반대인지 간단한 기능 테스트를 하고 있습니다.그들은 겉보기엔 동일하지만, 다만 a를 제외하고는.tmp변수.한 명은 일을 하고 다른 한 명은 그렇지 않습니다.컴파일러가 이 문제를 최적화하는 이유를 평생 이해할 수 없습니다. 만약 정말로 최적화 문제라면(제 컴파일러는 IAR Workbench 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 값을 포함합니다.이것의 16진수 표현은0xFF. 그러면 이 값은 다음의 피연산자가 됩니다.~교환입니다.그래서 그 가치는 먼저 다음으로 승격될 것입니다.int(assuming 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))

문제는 정수승진입니다. 그.~오퍼레이터는 매우 위험합니다!

의 경우~bufi[i], 의 피연산자~정수 승급에 따라 승급됩니다.코드를 다음과 동일하게 만들기~(int)bufi[i].

그래서 두번째 경우에는buf[i] != (~bufi[i])당신은 뭔가를 얻습니다.0xXX != 0xFFFFFFFFYY, 여기서 "XX"와 "YY"는 비교하고자 하는 실제 값이고 0xFFFF는 의도하지 않은 쓰레기입니다.int. 이는 항상 다음과 같이 평가할 것입니다.true그래서 컴파일러는 코드의 일부를 최적화하여 매우 미묘한 버그를 생성할 수 있습니다.

의 경우tmp = ~bufi[i];당신은 이 버그를 잘라냄으로써 이 버그를 피합니다.0xFFFFFFFFYY관심 있는 값인 "YY"로 이동합니다.

자세한 내용은 암시적 유형 승격 규칙을 참조하십시오.또한 MISRA-C를 채택하여 이와 같은 미묘한 버그를 피하는 것도 고려해보세요.

, 두 에서의 비교는 하는데, 그 는 Lundin dbush 에 합니다와는 반대이기 때문입니다. 왜냐하면 어떤 것의 반대이기 때문입니다.uint8int다와 .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;
}

Godbolt의 컴파일러 탐색기에서 볼 수 있듯이, 둘 다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

언급URL : https://stackoverflow.com/questions/57823023/compiler-optimization-of-bitwise-not-operation

반응형