mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-11-02 08:29:32 +01:00
* Moved procedures calculating "magic" numbers for division by constants from powerpc64/cgcpu.pas to cgutils.pas, so they can be reused for all targets.
* "Shift" parameter for signed calculation changed from aint to byte, since it only holds values up to 63. It is consistent with unsigned routine. git-svn-id: trunk@27883 -
This commit is contained in:
parent
af95876eba
commit
90d2009a31
@ -176,6 +176,12 @@ unit cgutils;
|
||||
{ returns r with the given alignment }
|
||||
function setalignment(const r : treference;b : byte) : treference;
|
||||
|
||||
{ Helper function which calculate "magic" values for replacement of division
|
||||
by constant operation by multiplication. See the "PowerPC compiler developer
|
||||
manual" for more information.
|
||||
N is number of bits to handle, functionality tested for values 32 and 64. }
|
||||
procedure calc_divconst_magic_signed(N: byte; d: aInt; out magic_m: aInt; out magic_s: byte);
|
||||
procedure calc_divconst_magic_unsigned(N: byte; d: aWord; out magic_m: aWord; out magic_add: boolean; out magic_shift: byte);
|
||||
|
||||
implementation
|
||||
|
||||
@ -328,5 +334,98 @@ uses
|
||||
end;
|
||||
|
||||
|
||||
{$push}
|
||||
{$r-,q-}
|
||||
procedure calc_divconst_magic_signed(N: byte; d: aInt; out magic_m: aInt; out magic_s: byte);
|
||||
var
|
||||
p: aInt;
|
||||
ad,anc,delta,q1,r1,q2,r2,t: aWord;
|
||||
two_N_minus_1: aWord;
|
||||
begin
|
||||
assert((d<-1) or (d>1));
|
||||
two_N_minus_1:=aWord(1) shl (N-1);
|
||||
|
||||
ad:=abs(d);
|
||||
t:=two_N_minus_1+(aWord(d) shr (N-1));
|
||||
anc:=t-1-t mod ad; { absolute value of nc }
|
||||
p:=(N-1); { initialize p }
|
||||
q1:=two_N_minus_1 div anc; { initialize q1 = 2**p/abs(nc) }
|
||||
r1:=two_N_minus_1-q1*anc; { initialize r1 = rem(2**p,abs(nc)) }
|
||||
q2:=two_N_minus_1 div ad; { initialize q2 = 2**p/abs(d) }
|
||||
r2:=two_N_minus_1-q2*ad; { initialize r2 = rem(2**p,abs(d)) }
|
||||
repeat
|
||||
inc(p);
|
||||
q1:=2*q1; { update q1 = 2**p/abs(nc) }
|
||||
r1:=2*r1; { update r1 = rem(2**p/abs(nc)) }
|
||||
if (r1>=anc) then { must be unsigned comparison }
|
||||
begin
|
||||
inc(q1);
|
||||
dec(r1,anc);
|
||||
end;
|
||||
q2:=2*q2; { update q2 = 2p/abs(d) }
|
||||
r2:=2*r2; { update r2 = rem(2p/abs(d)) }
|
||||
if (r2>=ad) then { must be unsigned comparison }
|
||||
begin
|
||||
inc(q2);
|
||||
dec(r2,ad);
|
||||
end;
|
||||
delta:=ad-r2;
|
||||
until not ((q1<delta) or ((q1=delta) and (r1=0)));
|
||||
magic_m:=q2+1;
|
||||
if (d<0) then
|
||||
magic_m:=-magic_m; { resulting magic number }
|
||||
magic_s:=p-N; { resulting shift }
|
||||
end;
|
||||
|
||||
|
||||
procedure calc_divconst_magic_unsigned(N: byte; d: aWord; out magic_m: aWord; out magic_add: boolean; out magic_shift: byte);
|
||||
var
|
||||
p: aInt;
|
||||
nc,delta,q1,r1,q2,r2,two_N_minus_1 : aWord;
|
||||
begin
|
||||
two_N_minus_1:=aWord(1) shl (N-1);
|
||||
magic_add:=false;
|
||||
{$push}
|
||||
{$warnings off }
|
||||
nc:=aWord(-1)-(-d) mod d;
|
||||
{$pop}
|
||||
p:=N-1; { initialize p }
|
||||
q1:=two_N_minus_1 div nc; { initialize q1 = 2**p/nc }
|
||||
r1:=two_N_minus_1-q1*nc; { initialize r1 = rem(2**p,nc) }
|
||||
q2:=(two_N_minus_1-1) div d; { initialize q2 = (2**p-1)/d }
|
||||
r2:=(two_N_minus_1-1)-q2*d; { initialize r2 = rem((2**p-1),d) }
|
||||
repeat
|
||||
inc(p);
|
||||
if (r1>=(nc-r1)) then
|
||||
begin
|
||||
q1:=2*q1+1; { update q1 }
|
||||
r1:=2*r1-nc; { update r1 }
|
||||
end
|
||||
else
|
||||
begin
|
||||
q1:=2*q1; { update q1 }
|
||||
r1:=2*r1; { update r1 }
|
||||
end;
|
||||
if ((r2+1)>=(d-r2)) then
|
||||
begin
|
||||
if (q2>=(two_N_minus_1-1)) then
|
||||
magic_add:=true;
|
||||
q2:=2*q2+1; { update q2 }
|
||||
r2:=2*r2+1-d; { update r2 }
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (q2>=two_N_minus_1) then
|
||||
magic_add:=true;
|
||||
q2:=2*q2; { update q2 }
|
||||
r2:=2*r2+1; { update r2 }
|
||||
end;
|
||||
delta:=d-1-r2;
|
||||
until not ((p<(2*N)) and ((q1<delta) or ((q1=delta) and (r1=0))));
|
||||
magic_m:=q2+1; { resulting magic number }
|
||||
magic_shift:=p-N; { resulting shift }
|
||||
end;
|
||||
{$pop}
|
||||
|
||||
end.
|
||||
|
||||
|
||||
@ -155,105 +155,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{$push}
|
||||
{$r-}
|
||||
{$q-}
|
||||
{ helper function which calculate "magic" values for replacement of unsigned
|
||||
division by constant operation by multiplication. See the PowerPC compiler
|
||||
developer manual for more information }
|
||||
procedure getmagic_unsignedN(const N : byte; const d : aWord;
|
||||
out magic_m : aWord; out magic_add : boolean; out magic_shift : byte);
|
||||
var
|
||||
p : aInt;
|
||||
nc, delta, q1, r1, q2, r2, two_N_minus_1 : aWord;
|
||||
begin
|
||||
assert(d > 0);
|
||||
|
||||
two_N_minus_1 := aWord(1) shl (N-1);
|
||||
|
||||
magic_add := false;
|
||||
{$push}
|
||||
{$warnings off }
|
||||
nc := aWord(-1) - (-d) mod d;
|
||||
{$pop}
|
||||
p := N-1; { initialize p }
|
||||
q1 := two_N_minus_1 div nc; { initialize q1 = 2p/nc }
|
||||
r1 := two_N_minus_1 - q1*nc; { initialize r1 = rem(2p,nc) }
|
||||
q2 := (two_N_minus_1-1) div d; { initialize q2 = (2p-1)/d }
|
||||
r2 := (two_N_minus_1-1) - q2*d; { initialize r2 = rem((2p-1),d) }
|
||||
repeat
|
||||
inc(p);
|
||||
if (r1 >= (nc - r1)) then begin
|
||||
q1 := 2 * q1 + 1; { update q1 }
|
||||
r1 := 2*r1 - nc; { update r1 }
|
||||
end else begin
|
||||
q1 := 2*q1; { update q1 }
|
||||
r1 := 2*r1; { update r1 }
|
||||
end;
|
||||
if ((r2 + 1) >= (d - r2)) then begin
|
||||
if (q2 >= (two_N_minus_1-1)) then
|
||||
magic_add := true;
|
||||
q2 := 2*q2 + 1; { update q2 }
|
||||
r2 := 2*r2 + 1 - d; { update r2 }
|
||||
end else begin
|
||||
if (q2 >= two_N_minus_1) then
|
||||
magic_add := true;
|
||||
q2 := 2*q2; { update q2 }
|
||||
r2 := 2*r2 + 1; { update r2 }
|
||||
end;
|
||||
delta := d - 1 - r2;
|
||||
until not ((p < (2*N)) and ((q1 < delta) or ((q1 = delta) and (r1 = 0))));
|
||||
magic_m := q2 + 1; { resulting magic number }
|
||||
magic_shift := p - N; { resulting shift }
|
||||
end;
|
||||
|
||||
{ helper function which calculate "magic" values for replacement of signed
|
||||
division by constant operation by multiplication. See the PowerPC compiler
|
||||
developer manual for more information }
|
||||
procedure getmagic_signedN(const N : byte; const d : aInt;
|
||||
out magic_m : aInt; out magic_s : aInt);
|
||||
var
|
||||
p : aInt;
|
||||
ad, anc, delta, q1, r1, q2, r2, t : aWord;
|
||||
two_N_minus_1 : aWord;
|
||||
|
||||
begin
|
||||
assert((d < -1) or (d > 1));
|
||||
|
||||
two_N_minus_1 := aWord(1) shl (N-1);
|
||||
|
||||
ad := abs(d);
|
||||
t := two_N_minus_1 + (aWord(d) shr (N-1));
|
||||
anc := t - 1 - t mod ad; { absolute value of nc }
|
||||
p := (N-1); { initialize p }
|
||||
q1 := two_N_minus_1 div anc; { initialize q1 = 2p/abs(nc) }
|
||||
r1 := two_N_minus_1 - q1*anc; { initialize r1 = rem(2p,abs(nc)) }
|
||||
q2 := two_N_minus_1 div ad; { initialize q2 = 2p/abs(d) }
|
||||
r2 := two_N_minus_1 - q2*ad; { initialize r2 = rem(2p,abs(d)) }
|
||||
repeat
|
||||
inc(p);
|
||||
q1 := 2*q1; { update q1 = 2p/abs(nc) }
|
||||
r1 := 2*r1; { update r1 = rem(2p/abs(nc)) }
|
||||
if (r1 >= anc) then begin { must be unsigned comparison }
|
||||
inc(q1);
|
||||
dec(r1, anc);
|
||||
end;
|
||||
q2 := 2*q2; { update q2 = 2p/abs(d) }
|
||||
r2 := 2*r2; { update r2 = rem(2p/abs(d)) }
|
||||
if (r2 >= ad) then begin { must be unsigned comparison }
|
||||
inc(q2);
|
||||
dec(r2, ad);
|
||||
end;
|
||||
delta := ad - r2;
|
||||
until not ((q1 < delta) or ((q1 = delta) and (r1 = 0)));
|
||||
magic_m := q2 + 1;
|
||||
if (d < 0) then begin
|
||||
magic_m := -magic_m; { resulting magic number }
|
||||
end;
|
||||
magic_s := p - N; { resulting shift }
|
||||
end;
|
||||
{$pop}
|
||||
|
||||
{ finds positive and negative powers of two of the given value, returning the
|
||||
power and whether it's a negative power or not in addition to the actual result
|
||||
of the function }
|
||||
@ -710,7 +611,7 @@ var
|
||||
const
|
||||
negops : array[boolean] of tasmop = (A_NEG, A_NEGO);
|
||||
var
|
||||
magic, shift : int64;
|
||||
magic : int64;
|
||||
u_magic : qword;
|
||||
u_shift : byte;
|
||||
u_add : boolean;
|
||||
@ -742,7 +643,7 @@ var
|
||||
{ from "The PowerPC Compiler Writer's Guide" pg. 53ff }
|
||||
divreg := getintregister(list, OS_INT);
|
||||
if (signed) then begin
|
||||
getmagic_signedN(sizeof(aInt)*8, a, magic, shift);
|
||||
calc_divconst_magic_signed(sizeof(aInt)*8, a, magic, u_shift);
|
||||
{ load magic value }
|
||||
a_load_const_reg(list, OS_INT, magic, divreg);
|
||||
{ multiply }
|
||||
@ -754,7 +655,7 @@ var
|
||||
a_op_reg_reg_reg(list, OP_SUB, OS_INT, src, dst, dst);
|
||||
end;
|
||||
{ shift shift places to the right (arithmetic) }
|
||||
a_op_const_reg_reg(list, OP_SAR, OS_INT, shift, dst, dst);
|
||||
a_op_const_reg_reg(list, OP_SAR, OS_INT, u_shift, dst, dst);
|
||||
{ extract and add sign bit }
|
||||
if (a >= 0) then begin
|
||||
a_op_const_reg_reg(list, OP_SHR, OS_INT, 63, src, divreg);
|
||||
@ -763,7 +664,7 @@ var
|
||||
end;
|
||||
a_op_reg_reg_reg(list, OP_ADD, OS_INT, dst, divreg, dst);
|
||||
end else begin
|
||||
getmagic_unsignedN(sizeof(aWord)*8, a, u_magic, u_add, u_shift);
|
||||
calc_divconst_magic_unsigned(sizeof(aWord)*8, a, u_magic, u_add, u_shift);
|
||||
{ load magic in divreg }
|
||||
a_load_const_reg(list, OS_INT, aint(u_magic), divreg);
|
||||
list.concat(taicpu.op_reg_reg_reg(A_MULHDU, dst, src, divreg));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user