123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- {
- 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.
|