Delphi etiqueta e asm estranheza?
Pergunta
Escrevi um asm função em Delphi 7, mas transforma o meu código para outra coisa:
function f(x: Cardinal): Cardinal; register;
label err;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
end;
// compiled version
f:
push ebx // !!!
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz +$0e
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
mov eax, ebx // !!!
pop ebx // !!!
ret
// the almost equivalent without asm
function f(x: Cardinal): Cardinal;
var
c: Cardinal;
begin
x := not x;
x := x and x shr 1;
if x <> 0 then
begin
c := bsf(x); // bitscanforward
x := 1 shl c;
Result := x or (x shl 1)
end
else
Result := 0;
end;
Por que não gerar push ebx
e pop ebx
?E por que ele faz mov eax, ebx
?
Parece que ele gera parcial do quadro de pilha devido a mov eax, ebx
.
Este teste simples gera mov eax, edx
mas não gerar o quadro de pilha:
function asmtest(x: Cardinal): Cardinal; register;
label err;
asm
not eax
and eax, 1
jz err
ret
err:
xor eax, eax
end;
// compiled
asmtest:
not eax
and eax, $01
jz +$01
ret
xor eax, eax
mov eax, edx // !!!
ret
Parece que ele tem algo a ver com o label err
.Se eu remover o que não tenho mov eax, *
a parte.
Por que isso acontece?
Fez um relatório de bug no Qualidade Central.
Solução
O conselho prático é: não use a palavra-chave do rótulo no código ASM, use @@-etiquetas prefixadas:
function f(x: Cardinal): Cardinal; register;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz @@err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
@@err:
xor eax, eax
end;
Atualizada:
Eu não encontrei o relatório de bug em Área BASM. Parece um bug, mas eu uso o BASM há muitos anos e nunca pensei em usar a palavra -chave do rótulo dessa maneira. Na verdade, eu nunca usei a palavra -chave do rótulo em Delphi. :)
Outras dicas
Bem ...de volta, em seguida, em Delphi-Manual, usado para dizer algo sobre o Compilador de Otimização e thealike-loucura:
O Compilador gera Stackframes apenas para outras Rotinas, por Rotinas de ter Variáveis locais e Rotinas com Pilha-Parâmetros
O auto-gerado de Inicialização e Finalizationcode para Rotinas inclui:
PUSH EBP ; If Locals <> 0 or Params <> 0
MOV EBP,ESP ; If Locals <> 0 or Params <> 0
SUB ESP,Locals ; If Locals <> 0
...
MOV ESP,EBP ; If Locals <> 0
POP EBP ; If Locals <> 0 or Params <> 0
RET Params ; Always
Se as Variáveis locais conter Variantes, Cadeias longas ou Interfaces que são inicializados com um valor Nulo, mas não são registrados posteriormente.
Moradores é o Tamanho de Variáveis locais, Parâmetros o Tamanho de Parâmetros.Se ambos os Locais, bem como de Parâmetros são Nulos não Init-Código será gerado e o Finalizationcode contém apenas um RET-Intruction.
Talvez que tem algo a ver com tudo isso...