I tested two different switch initialization syntaxes, and they produce different assembly code. One variant of A uses more cmp instructions, which doesn’t seem to be optimally optimized. Is the compiler capable of optimizing variant A into variant B?
Here is the URL I used for testing: Compiler Explorer
const std = @import("std");
export fn initA(cond: i32) void {
const num1: i32 = switch(cond) {
1 => num1_with_cond_1,
2 => num1_with_cond_2,
else => num1_with_cond_else,
};
const num2: i32 = switch(cond) {
1 => num2_with_cond_1,
2 => num2_with_cond_2,
100 => num2_with_cond_3,
else => num2_with_cond_else,
};
testPrintI32(num1);
testPrintI32(num2);
}
export fn initB(cond: i32) void {
var num1: i32 = undefined;
var num2: i32 = undefined;
switch(cond) {
1 => {
num1 = num1_with_cond_1;
num2 = num2_with_cond_1;
},
2 => {
num1 = num1_with_cond_2;
num2 = num2_with_cond_2;
},
100 => {
num1 = num1_with_cond_else;
num2 = num2_with_cond_3;
},
else => {
num1 = num1_with_cond_else;
num2 = num2_with_cond_else;
}
}
testPrintI32(num1);
testPrintI32(num2);
}
export fn testPrintI32(num: i32) void {
std.debug.print("{d}", .{num});
}
const num1_with_cond_1 = 453;
const num1_with_cond_2 = 879;
const num1_with_cond_else = 134;
const num2_with_cond_1 = 1334;
const num2_with_cond_2 = 3345;
const num2_with_cond_3 = 5456;
const num2_with_cond_else = 9789;
In the assembly code for initA, there are five cmp instructions, and both the numbers 2 and 1 are compared twice.
initA:
push rbp
mov rbp, rsp
push rbx
push rax
cmp edi, 100
je .LBB0_5
cmp edi, 2
je .LBB0_4
mov ebx, 1334
cmp edi, 1
je .LBB0_3
mov ebx, 9789
jmp .LBB0_3
.LBB0_4:
mov ebx, 3345
jmp .LBB0_3
.LBB0_5:
mov ebx, 5456
.LBB0_3:
cmp edi, 2
mov eax, 879
mov ecx, 134
cmove ecx, eax
cmp edi, 1
mov edi, 453
cmovne edi, ecx
call testPrintI32
mov edi, ebx
add rsp, 8
pop rbx
pop rbp
jmp testPrintI32
In the assembly code for initB, there are only three cmp instructions, which aligns with expectations.
initB:
push rbp
mov rbp, rsp
push rbx
push rax
cmp edi, 100
je .LBB9_6
cmp edi, 2
je .LBB9_5
cmp edi, 1
jne .LBB9_7
mov edi, 453
mov ebx, 1334
jmp .LBB9_4
.LBB9_5:
mov edi, 879
mov ebx, 3345
jmp .LBB9_4
.LBB9_6:
mov edi, 134
mov ebx, 5456
jmp .LBB9_4
.LBB9_7:
mov edi, 134
mov ebx, 9789
.LBB9_4:
call testPrintI32
mov edi, ebx
add rsp, 8
pop rbx
pop rbp
jmp testPrintI32
Additionally, I’d like to know which initialization style is better: the Zig style or the C style?