* 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:
sergei 2014-06-07 00:00:53 +00:00
parent af95876eba
commit 90d2009a31
2 changed files with 103 additions and 103 deletions

View File

@ -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.

View File

@ -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));