Essa semana eu achei um bug em três compiladores diferentes!
Eu estava de boa otimizando o rgzip quando notei ele gerando um assembly bizarro. A função era equivalente a essa:
fn vecsum(v : &[u32]) -> u32 {
return v[0] + v[1] + v[2] + v[3] + v[4] + v[5];
}
O Rust tem como princípio sempre fazer bounds checking para evitar buffer overflow, isso é cool. Mas olha só o assembly que ele gera:
movq %rsi, %rax
testq %rax, %rax
je .LBB0_7
cmpq $1, %rax
je .LBB0_8
cmpq $3, %rax
jb .LBB0_9
je .LBB0_10
cmpq $5, %rax
jb .LBB0_11
je .LBB0_12
Um bound check para cada acesso! Claramente não precisava. Se você fizer só o bound check no v[5], então todos os outros garantidamente passam, porque o vetor é contínuo.
Reportei isso na comunidade de Rust e aí ficou mais bizarro ainda. Primeiro, o bug não aparece se você inverter a função:
return v[5] + v[4] + v[3] + v[2] + v[1] + v[0];
Segundo, não é um bug do Rust, é na camada mais embaixo do LLVM. A gente sabe disso porque um código equivalente em C++ no clang gera o mesmo bug:
struct Vec {
int len;
int* data;
};
inline int get(struct Vec* vec, int i) {
if (i < vec->len) {
return vec->data[i];
} else {
abort();
}
}
int sum5(struct Vec* vec) {
return get(vec, 0) + get(vec, 1) + get(vec, 2) + get(vec, 3) + get(vec, 4) + get(vec, 5);
}
Por fim, o bug não é só no LLVM, ele aparece também no gcc e o no icc! Confiram:
Já foi tudo reportado, agora é só esperar alguém consertar. Idealmente eu mesmo consertaria, mas infelizmente a vida é curta :(
Nenhum comentário:
Postar um comentário