It looks like it happens somewhere in a MIR transformation. These optimizations happen before the code is passed on to the compiler backend (LLVM in this case). Compiling with nightly, compare the difference in output between -Zmir-opt-level=0
and -Zmir-opt-level=1
: Compiler Explorer
Output (since nightly is always being updated, the results on Godbolt are subject to change)
-C opt-level=3 -Zmir-opt-level=0
example::some_top_secret_checker::h221a3e4ba06d2223:
mov al, 1
cmp edi, 42
je .LBB0_4
cmp edi, 322
je .LBB0_4
cmp edi, 1337
je .LBB0_4
xor eax, eax
.LBB0_4:
ret
define noundef zeroext i1 @example::some_top_secret_checker::h221a3e4ba06d2223(i32 noundef %var) unnamed_addr {
start:
switch i32 %var, label %bb6 [
i32 42, label %bb7
i32 322, label %bb7
i32 1337, label %bb7
]
bb6:
br label %bb7
bb7:
%_0.sroa.0.0 = phi i1 [ false, %bb6 ], [ true, %start ], [ true, %start ], [ true, %start ]
ret i1 %_0.sroa.0.0
}
-C opt-level=3 -Zmir-opt-level=1
example::some_top_secret_checker::h221a3e4ba06d2223:
mov al, 1
cmp edi, 42
je .LBB0_4
cmp edi, 1337
je .LBB0_4
cmp edi, 322
je .LBB0_4
xor eax, eax
.LBB0_4:
ret
define noundef zeroext i1 @example::some_top_secret_checker::h221a3e4ba06d2223(i32 noundef %var) unnamed_addr {
start:
switch i32 %var, label %bb6 [
i32 42, label %bb7
i32 322, label %bb7
i32 1337, label %bb7
]
bb7:
%_0.sroa.0.0 = phi i1 [ false, %bb6 ], [ true, %start ], [ true, %start ], [ true, %start ]
ret i1 %_0.sroa.0.0
bb6:
br label %bb7
}
Slightly altering the code (for example adding more branches and making it take a reference) had the correct branching order with -Zmir-opt-level=1
, but not with -Zmir-opt-level=2
.
I agree that maintaining the order of conditional branches is valuable because it allows people to have their expressions return early the majority of the time if they have knowledge of the values it'll see. I think this should be addressed in LLVM, but the frontend is generating weird LLVM IR that causes this behavior, and it shouldn't be too hard to track down which specific MIR transformation is causing it. After that, I have no clue how hard it'd be to fix, but I've looked at a couple of MIR transforms and I can't say I personally understood the logic of anything in there, so...
Edit: I suppose it doesn't necessarily have to be caused by one of the MIR transformations directly; it might also be that this is a bug in the part responsible for lowering MIR into LLVM IR, and it just so happens that the optimized version of the MIR triggers it while the non-optimized does not, meaning it could happen to any code regardless of whether it's been subject MIR transforms.