I was looking at something like this:
void example(bool a) {
bool b = (a || a) || (a || a);
}
Which if compiled with -S -emit-llvm -Xclang -disable-llvm-optzns gives us:
define dso_local void @example(bool)(i1 noundef zeroext %0) #0 !dbg !10 {
%2 = alloca i8, align 1
%3 = alloca i8, align 1
%4 = zext i1 %0 to i8
store i8 %4, ptr %2, align 1
#dbg_declare(ptr %2, !16, !DIExpression(), !17)
#dbg_declare(ptr %3, !18, !DIExpression(), !19)
%5 = load i8, ptr %2, align 1, !dbg !20
%6 = trunc i8 %5 to i1, !dbg !20
br i1 %6, label %18, label %7, !dbg !21
7:
%8 = load i8, ptr %2, align 1, !dbg !22
%9 = trunc i8 %8 to i1, !dbg !22
br i1 %9, label %18, label %10, !dbg !23
10:
%11 = load i8, ptr %2, align 1, !dbg !24
%12 = trunc i8 %11 to i1, !dbg !24
br i1 %12, label %16, label %13, !dbg !25
13:
%14 = load i8, ptr %2, align 1, !dbg !26
%15 = trunc i8 %14 to i1, !dbg !26
br label %16, !dbg !25
16:
%17 = phi i1 [ true, %10 ], [ %15, %13 ]
br label %18, !dbg !23
18:
%19 = phi i1 [ true, %7 ], [ true, %1 ], [ %17, %16 ]
%20 = zext i1 %19 to i8, !dbg !19
store i8 %20, ptr %3, align 1, !dbg !19
ret void, !dbg !27
}
attributes #0 = { mustprogress noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
What’s the advantage of doing this instead of having sometlhing like:
br i1 %in_var, label %is_true, label %is_false
is_true:
store i8 1, ptr %res_var
br label %end
is_false:
store i8 0, ptr %res_var
br label %end
end:
I’m guessing the constants in the phi nodes give us some kind of advantage on some kind of pass, but I don’t see how.