Até onde eu sei, o aliasing de referência / ponteiro pode prejudicar a capacidade do compilador de gerar código otimizado, pois eles devem garantir que o binário gerado se comporte corretamente no caso em que as duas referências / ponteiros realmente sejam alias. Por exemplo, no código C a seguir,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
quando compilado clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
com a -O3
bandeira, emite
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi) # The first time
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi) # The second time
a: c3 retq
Aqui, o código é armazenado novamente (%rdi)
duas vezes em case int *a
e int *b
alias.
Quando dizemos explicitamente ao compilador que esses dois ponteiros não podem usar o alias da restrict
palavra-chave:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Então o Clang emitirá uma versão mais otimizada do código binário:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Como o Rust garante (exceto no código não seguro) que duas referências mutáveis não possam usar o alias, eu pensaria que o compilador deveria poder emitir a versão mais otimizada do código.
Quando eu testo com o código abaixo e o compilo rustc 1.35.0
com -C opt-level=3 --emit obj
,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
gera:
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi)
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi)
a: c3 retq
Isso não tira proveito da garantia que a
e b
não pode ser um alias.
Isso ocorre porque o atual compilador Rust ainda está em desenvolvimento e ainda não incorporou a análise de alias para fazer a otimização?
Isso ocorre porque ainda existe uma chance a
e b
poderia, mesmo em Rust seguro?
unsafe
código, referências mutáveis no alias não são permitidas e resultam em comportamento indefinido. Você pode ter ponteiros brutos com alias, mas ounsafe
código na verdade não permite que você ignore as regras padrão do Rust. É apenas um equívoco comum e, portanto, vale a pena apontar.+=
operações no corpo deadds
podem ser reinterpretadas como*a = *a + *b + *b
. Se os ponteiros não aliás, eles podem, você pode até ver o que equivale ab* + *b
no segundo asm lista:2: 01 c0 add %eax,%eax
. Mas se eles criarem um alias, não poderão, porque na hora em que você adicionar*b
pela segunda vez, ele conterá um valor diferente do que na primeira vez (o que você armazena na linha4:
da primeira listagem de asm).Respostas:
Rust originalmente fez habilitar do LLVM
noalias
atributo, mas este código miscompiled causado . Quando todas as versões suportadas do LLVM não compactarem mais o código, ele será reativado .Se você adicionar
-Zmutable-noalias=yes
às opções do compilador, obterá o assembly esperado:Simplificando, Rust colocou o equivalente à
restrict
palavra-chave de C em todos os lugares , muito mais prevalente do que qualquer programa C usual. Isso exercitou casos de canto do LLVM mais do que era capaz de lidar corretamente. Acontece que os programadores de C e C ++ simplesmente não usamrestrict
com tanta frequência quanto&mut
é usado no Rust.Isso aconteceu várias vezes .
noalias
ativadonoalias
desativadonoalias
ativadonoalias
desativadoProblemas de ferrugem relacionados
Caso atual
Caso anterior
De outros
fonte
restrict
e compilam incorretamente no Clang e no GCC. Não se limita a idiomas que não são "C ++ suficientes", a menos que você conte o C ++ nesse grupo .noalias
em conta os ponteiros durante a execução. Ele criou novos ponteiros com base nos ponteiros de entrada, copiando incorretamente onoalias
atributo, mesmo que os novos ponteiros fizessem um alias.