fpc/compiler/powerpc64/nppccnv.pas
Jonas Maebe a25ebbba3e + added volatility information to all memory references
o separate information for reading and writing, because e.g. in a
     try-block, only the writes to local variables and parameters are
     volatile (they have to be committed immediately in case the next
     instruction causes an exception)
   o for now, only references to absolute memory addresses are marked
     as volatile
   o the volatily information is (should be) properly maintained throughout
     all code generators for all archictures with this patch
   o no optimizers or other compiler infrastructure uses the volatility
     information yet
   o this functionality is not (yet) exposed at the language level, it
     is only for internal code generator use right now

git-svn-id: trunk@34996 -
2016-11-27 18:17:37 +00:00

237 lines
8.9 KiB
ObjectPascal

{
Copyright (c) 1998-2002 by Florian Klaempfl
Generate PowerPC assembler for type converting nodes
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit nppccnv;
{$I fpcdefs.inc}
interface
uses
node, ncnv, ncgcnv, ngppccnv;
type
tppctypeconvnode = class(tgenppctypeconvnode)
protected
{ procedure second_int_to_int;override; }
{ procedure second_string_to_string;override; }
{ procedure second_cstring_to_pchar;override; }
{ procedure second_string_to_chararray;override; }
{ procedure second_array_to_pointer;override; }
function first_int_to_real: tnode; override;
{ procedure second_pointer_to_array;override; }
{ procedure second_chararray_to_string;override; }
{ procedure second_char_to_string;override; }
procedure second_int_to_real; override;
{ procedure second_real_to_real; override;}
{ procedure second_cord_to_pointer;override; }
{ procedure second_proc_to_procvar;override; }
{ procedure second_bool_to_int;override; }
{ procedure second_int_to_bool; override; }
{ procedure second_load_smallset;override; }
{ procedure second_ansistring_to_pchar;override; }
{ procedure second_pchar_to_string;override; }
{ procedure second_class_to_intf;override; }
{ procedure second_char_to_char;override; }
end;
implementation
uses
verbose, globtype, globals, systems,
symconst, symdef, aasmbase, aasmtai,aasmdata,
defutil, symcpu,
cgbase, cgutils, pass_1, pass_2,
ncon, ncal,procinfo,
ncgutil,
cpubase, aasmcpu,
rgobj, tgobj, cgobj, hlcgobj;
{*****************************************************************************
FirstTypeConv
*****************************************************************************}
function tppctypeconvnode.first_int_to_real: tnode;
begin
if (is_currency(left.resultdef)) then begin
// hack to avoid double division by 10000, as it's
// already done by typecheckpass.resultdef_int_to_real
left.resultdef := s64inttype;
end else begin
// everything that is less than 64 bits is converted to a 64 bit signed
// integer - because the int_to_real conversion is faster for 64 bit
// signed ints compared to 64 bit unsigned ints.
if (not (torddef(left.resultdef).ordtype in [s64bit, u64bit, scurrency])) then begin
inserttypeconv(left, s64inttype);
end;
end;
firstpass(left);
result := nil;
expectloc := LOC_FPUREGISTER;
end;
{*****************************************************************************
SecondTypeConv
*****************************************************************************}
procedure tppctypeconvnode.second_int_to_real;
const
convconst : double = $100000000;
var
tempconst : tnode;
disp, disp2: treference;
// temp registers for converting signed ints
valuereg, leftreg,
// additional temp registers for converting unsigned 64 bit ints
tmpintreg1, tmpintreg2, tmpfpureg, tmpfpuconst : tregister;
size: tcgsize;
signed: boolean;
begin
location_reset(location, LOC_FPUREGISTER, def_cgsize(resultdef));
reference_reset(disp2,0,[]);
tempconst:=nil;
{ the code here comes from the PowerPC Compiler Writer's Guide }
{ * longint to double (works for all rounding modes) }
{ std R3,disp(R1) # store doubleword }
{ lfd FR1,disp(R1) # load float double }
{ fcfid FR1,FR1 # convert to floating-point integer }
{ * unsigned 64 bit int to fp value (works for all rounding modes) }
{ rldicl rT1,rS,32,32 # isolate high half }
{ rldicl rT2,rS,0,32 # isolate low half }
{ std rT1,disp(R1) # store high half }
{ std rT2,disp+8(R1) # store low half }
{ lfd frT1,disp(R1) # load high half }
{ lfd frD,disp+8(R1) # load low half }
{ fcfid frT1,frT1 # convert each half to floating }
{ fcfid frD,frD # point integer (no round) }
{ fmadd frD,frC,frT1,frD # (2^32)*high + low }
{ # (only add can round) }
tg.Gettemp(current_asmdata.CurrAsmList, 8, 8, tt_normal, disp);
{ do the signed case for everything but 64 bit unsigned integers }
signed := (left.location.size <> OS_64);
{ we need a certain constant for the conversion of unsigned 64 bit integers,
so create them here. Additonally another temporary location is neeted }
if (not signed) then begin
// allocate temp for constant value used for unsigned 64 bit ints
tempconst :=
crealconstnode.create(convconst, pbestrealtype^);
typecheckpass(tempconst);
firstpass(tempconst);
secondpass(tempconst);
if (tempconst.location.loc <> LOC_CREFERENCE) then
internalerror(200110011);
// allocate second temp memory
tg.Gettemp(current_asmdata.CurrAsmList, 8, 8, tt_normal, disp2);
end;
if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER,LOC_REFERENCE,LOC_CREFERENCE]) then
hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
case left.location.loc of
// the conversion algorithm does not modify the input register, so it can
// be used for both LOC_REGISTER and LOC_CREGISTER
LOC_REGISTER, LOC_CREGISTER:
begin
leftreg := left.location.register;
valuereg := leftreg;
end;
LOC_REFERENCE, LOC_CREFERENCE:
begin
leftreg := cg.getintregister(current_asmdata.CurrAsmList, OS_INT);
valuereg := leftreg;
if signed then
size := OS_S64
else
size := OS_64;
cg.a_load_ref_reg(current_asmdata.CurrAsmList, def_cgsize(left.resultdef),
size, left.location.reference, leftreg);
end
else
internalerror(200110012);
end;
if (signed) then begin
// std rS, disp(r1)
cg.a_load_reg_ref(current_asmdata.CurrAsmList, OS_S64, OS_S64, valuereg, disp);
// lfd frD, disp(r1)
location.register := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64, OS_F64, disp, location.register);
// fcfid frD, frD
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCFID, location.register,
location.register));
end else begin
{ ts:todo use TOC for this constant or at least schedule better }
// lfd frC, const
tmpfpuconst := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64,OS_F64,tempconst.location.reference,
tmpfpuconst);
tempconst.free;
tmpintreg1 := cg.getintregister(current_asmdata.CurrAsmList, OS_64);
// rldicl rT1, rS, 32, 32
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_const_const(A_RLDICL, tmpintreg1, valuereg, 32, 32));
// rldicl rT2, rS, 0, 32
tmpintreg2 := cg.getintregister(current_asmdata.CurrAsmList, OS_64);
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_const_const(A_RLDICL, tmpintreg2, valuereg, 0, 32));
// std rT1, disp(r1)
cg.a_load_reg_ref(current_asmdata.CurrAsmList, OS_S64, OS_S64, tmpintreg1, disp);
// std rT2, disp2(r1)
cg.a_load_reg_ref(current_asmdata.CurrAsmList, OS_S64, OS_S64, tmpintreg2, disp2);
// lfd frT1, disp(R1)
tmpfpureg := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64, OS_F64, disp, tmpfpureg);
// lfd frD, disp+8(R1)
location.register := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64, OS_F64, disp2, location.register);
// fcfid frT1, frT1
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCFID, tmpfpureg,
tmpfpureg));
// fcfid frD, frD
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCFID, location.register,
location.register));
// fmadd frD,frC,frT1,frD # (2^32)*high + low }
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg_reg(A_FMADD, location.register, tmpfpuconst,
tmpfpureg, location.register));
// free used temps
tg.ungetiftemp(current_asmdata.CurrAsmList, disp2);
end;
// free reference
tg.ungetiftemp(current_asmdata.CurrAsmList, disp);
// make sure the precision is correct
if (tfloatdef(resultdef).floattype = s32real) then
current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FRSP,location.register,
location.register));
end;
begin
ctypeconvnode := tppctypeconvnode;
end.