Empty while loop hangs in iPhone release build

后端 未结 1 1705
忘了有多久
忘了有多久 2021-01-17 05:36

For some reason having an empty while loop in a release build hangs, while having it in the debug build works fine. This example works in debug but hangs in release:

<
1条回答
  •  借酒劲吻你
    2021-01-17 06:10

    The reason is of course due to compilers optimizations, as already noted in the comments.

    Remembering that Objective-C is built on top of C, I put together a simple C example with different levels of optimizations and here's the result.

    Original code

    int main(int argc, char const *argv[])  {
        char _isReadyForData = 0;
        while (!_isReadyForData);
        return 0;
    }
    

    LLVM IR with no optimizations (-O0)

    define i32 @main(i32 %argc, i8** %argv) #0 {
    entry:
      %retval = alloca i32, align 4
      %argc.addr = alloca i32, align 4
      %argv.addr = alloca i8**, align 8
      %_isReadyForData = alloca i8, align 1
      store i32 0, i32* %retval
      store i32 %argc, i32* %argc.addr, align 4
      store i8** %argv, i8*** %argv.addr, align 8
      store i8 0, i8* %_isReadyForData, align 1
      br label %while.cond
    
    while.cond:                                       ; preds = %while.body, %entry
      %0 = load i8* %_isReadyForData, align 1
      %tobool = icmp ne i8 %0, 0
      %lnot = xor i1 %tobool, true
      br i1 %lnot, label %while.body, label %while.end
    
    while.body:                                       ; preds = %while.cond
      br label %while.cond
    
    while.end:                                        ; preds = %while.cond
      ret i32 0
    }
    

    LLVM IR with level 1 optimizations (-O1)

    define i32 @main(i32 %argc, i8** nocapture %argv) #0 {
    entry:
      br label %while.cond
    
    while.cond:                                       ; preds = %while.cond, %entry
      br label %while.cond
    }
    

    As you can see, the compiler produces an infinite loop when optimizing, since the local variable _isReadyForData is useless in that context and therefore is removed.

    As suggested by @faffaffaff, using the volatile keyword on _isReadyForData may solve the issue.

    LLVM IR with level 1 optimizations (-O1) with volatile keyword

    define i32 @main(i32 %argc, i8** nocapture %argv) #0 {
    entry:
      %_isReadyForData = alloca i8, align 1
      store volatile i8 0, i8* %_isReadyForData, align 1
      br label %while.cond
    
    while.cond:                                       ; preds = %while.cond, %entry
      %_isReadyForData.0.load1 = load volatile i8* %_isReadyForData, align 1
      %lnot = icmp eq i8 %_isReadyForData.0.load1, 0
      br i1 %lnot, label %while.cond, label %while.end
    
    while.end:                                        ; preds = %while.cond
      ret i32 0
    }
    

    But I definitely agree with @rmaddy in saying that you'd better change the flow of your program and use driven logic, instead of patching what you have already.

    0 讨论(0)
提交回复
热议问题