étiquette Delphi et asm bizarrerie?
Question
J'écrit une fonction asm en Delphi 7, mais il transforme mon code à autre chose:
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;
Pourquoi faut-il générer push ebx
et pop ebx
? Et pourquoi fait-il mov eax, ebx
?
Il semble qu'il génère la trame de pile partielle en raison de la mov eax, ebx
.
Ce test simple génère mov eax, edx
mais ne génère pas que cadre de pile:
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
Il semble qu'il a quelque chose à voir avec le label err
. Si je retire ce que je ne reçois pas la partie mov eax, *
.
Pourquoi cela?
Fait un rapport de bogue sur Qualité Central.
La solution
Les conseils pratiques: ne pas utiliser mot-clé étiquette dans le code asm, utilisez @@ - étiquettes préfixés:
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;
Mise à jour :
Je n'ai pas trouvé le rapport de bogue dans zone Basm . Il ressemble à un bug, mais je l'ai utilisé BASM depuis de nombreuses années et n'a jamais pensé à utiliser mot-clé étiquette d'une telle manière. En fait, je jamais utilisé mot-clé étiquette dans Delphi du tout. :)
Autres conseils
Eh bien ... à l'époque, dans le Delphi-Manuel, il l'habitude de dire quelque chose à propos du compilateur-optimisation et thealike-crazyness:
Le compilateur génère Stackframes seulement pour les routines imbriquées, pour Routines ayant des variables locales et des routines avec Stack-Paramètres
Le Initialization- généré automatiquement et Finalizationcode pour Routines comprend:
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
Si les variables locales contiennent des variantes, des chaînes longues ou interfaces, ils sont initialisés avec nul, mais ne sont pas finalisés par la suite.
Les sections locales est la taille des variables locales, Params la taille des paramètres. Si les deux sections locales ainsi que Params sont nulles pas Init-code sera généré et le Finalizationcode ne contient qu'une RET-intruction.
Peut-être a quelque chose à voir avec tout cela ...