1 {
2     Copyright (c) 1998-2002 by Florian Klaempfl
3 
4     Generate PowerPC assembler for type converting nodes
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  ****************************************************************************
21 }
22 unit nppccnv;
23 
24 {$I fpcdefs.inc}
25 
26 interface
27 
28 uses
29   node, ncnv, ncgcnv, ngppccnv;
30 
31 type
32   tppctypeconvnode = class(tgenppctypeconvnode)
33   protected
34     { procedure second_int_to_int;override; }
35     { procedure second_string_to_string;override; }
36     { procedure second_cstring_to_pchar;override; }
37     { procedure second_string_to_chararray;override; }
38     { procedure second_array_to_pointer;override; }
first_int_to_realnull39     function first_int_to_real: tnode; override;
40     { procedure second_pointer_to_array;override; }
41     { procedure second_chararray_to_string;override; }
42     { procedure second_char_to_string;override; }
43     procedure second_int_to_real; override;
44     { procedure second_real_to_real; override;}
45     { procedure second_cord_to_pointer;override; }
46     { procedure second_proc_to_procvar;override; }
47     { procedure second_bool_to_int;override; }
48     { procedure second_int_to_bool; override; }
49     { procedure second_load_smallset;override;  }
50     { procedure second_ansistring_to_pchar;override; }
51     { procedure second_pchar_to_string;override; }
52     { procedure second_class_to_intf;override; }
53     { procedure second_char_to_char;override; }
54   end;
55 
56 implementation
57 
58 uses
59   verbose, globtype, globals, systems,
60   symconst, symdef, aasmbase, aasmtai,aasmdata,
61   defutil, symcpu,
62   cgbase, cgutils, pass_1, pass_2,
63   ncon, ncal,procinfo,
64   ncgutil,
65   cpubase, aasmcpu,
66   rgobj, tgobj, cgobj, hlcgobj;
67 
68 {*****************************************************************************
69                              FirstTypeConv
70 *****************************************************************************}
71 
tppctypeconvnode.first_int_to_realnull72 function tppctypeconvnode.first_int_to_real: tnode;
73 begin
74   if (is_currency(left.resultdef)) then begin
75     // hack to avoid double division by 10000, as it's
76     // already done by typecheckpass.resultdef_int_to_real
77     left.resultdef := s64inttype;
78   end else begin
79     // everything that is less than 64 bits is converted to a 64 bit signed
80     // integer - because the int_to_real conversion is faster for 64 bit
81     // signed ints compared to 64 bit unsigned ints.
82     if (not (torddef(left.resultdef).ordtype in [s64bit, u64bit, scurrency])) then begin
83       inserttypeconv(left, s64inttype);
84     end;
85   end;
86   firstpass(left);
87   result := nil;
88   expectloc := LOC_FPUREGISTER;
89 end;
90 
91 {*****************************************************************************
92                              SecondTypeConv
93 *****************************************************************************}
94 
95 procedure tppctypeconvnode.second_int_to_real;
96 const
97   convconst : double = $100000000;
98 var
99   tempconst : tnode;
100   disp, disp2: treference;
101   // temp registers for converting signed ints
102   valuereg, leftreg,
103   // additional temp registers for converting unsigned 64 bit ints
104   tmpintreg1, tmpintreg2, tmpfpureg, tmpfpuconst : tregister;
105   size: tcgsize;
106   signed: boolean;
107 begin
108   location_reset(location, LOC_FPUREGISTER, def_cgsize(resultdef));
109   reference_reset(disp2,0,[]);
110   tempconst:=nil;
111 
112   { the code here comes from the PowerPC Compiler Writer's Guide }
113   { * longint to double (works for all rounding modes) }
114   { std   R3,disp(R1) # store doubleword }
115   { lfd   FR1,disp(R1) # load float double }
116   { fcfid FR1,FR1 # convert to floating-point integer  }
117 
118   { * unsigned 64 bit int to fp value (works for all rounding modes) }
119   { rldicl rT1,rS,32,32 # isolate high half }
120   { rldicl rT2,rS,0,32 # isolate low half }
121   { std rT1,disp(R1) # store high half }
122   { std rT2,disp+8(R1) # store low half }
123   { lfd frT1,disp(R1) # load high half }
124   { lfd frD,disp+8(R1) # load low half }
125   { fcfid frT1,frT1 # convert each half to floating }
126   { fcfid frD,frD # point integer (no round) }
127   { fmadd frD,frC,frT1,frD # (2^32)*high + low }
128   { # (only add can round) }
129   tg.Gettemp(current_asmdata.CurrAsmList, 8, 8, tt_normal, disp);
130 
131   { do the signed case for everything but 64 bit unsigned integers }
132   signed := (left.location.size <> OS_64);
133 
134   { we need a certain constant for the conversion of unsigned 64 bit integers,
135     so create them here. Additonally another temporary location is neeted }
136   if (not signed) then begin
137     // allocate temp for constant value used for unsigned 64 bit ints
138     tempconst :=
139       crealconstnode.create(convconst, pbestrealtype^);
140     typecheckpass(tempconst);
141     firstpass(tempconst);
142     secondpass(tempconst);
143     if (tempconst.location.loc <> LOC_CREFERENCE) then
144       internalerror(200110011);
145 
146     // allocate second temp memory
147     tg.Gettemp(current_asmdata.CurrAsmList, 8, 8, tt_normal, disp2);
148   end;
149 
150   if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER,LOC_REFERENCE,LOC_CREFERENCE]) then
151     hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
152   case left.location.loc of
153     // the conversion algorithm does not modify the input register, so it can
154     // be used for both LOC_REGISTER and LOC_CREGISTER
155     LOC_REGISTER, LOC_CREGISTER:
156       begin
157         leftreg := left.location.register;
158         valuereg := leftreg;
159       end;
160     LOC_REFERENCE, LOC_CREFERENCE:
161       begin
162         leftreg := cg.getintregister(current_asmdata.CurrAsmList, OS_INT);
163         valuereg := leftreg;
164         if signed then
165           size := OS_S64
166         else
167           size := OS_64;
168         cg.a_load_ref_reg(current_asmdata.CurrAsmList, def_cgsize(left.resultdef),
169           size, left.location.reference, leftreg);
170       end
171   else
172     internalerror(200110012);
173   end;
174 
175   if (signed) then begin
176     // std rS, disp(r1)
177     cg.a_load_reg_ref(current_asmdata.CurrAsmList, OS_S64, OS_S64, valuereg, disp);
178     // lfd frD, disp(r1)
179     location.register := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
180     cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64, OS_F64, disp, location.register);
181     // fcfid frD, frD
182     current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCFID, location.register,
183       location.register));
184   end else begin
185     { ts:todo use TOC for this constant or at least schedule better }
186     // lfd frC, const
187     tmpfpuconst := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
188     cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64,OS_F64,tempconst.location.reference,
189       tmpfpuconst);
190     tempconst.free;
191 
192     tmpintreg1 := cg.getintregister(current_asmdata.CurrAsmList, OS_64);
193     // rldicl rT1, rS, 32, 32
194     current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_const_const(A_RLDICL, tmpintreg1, valuereg, 32, 32));
195     // rldicl rT2, rS, 0, 32
196     tmpintreg2 := cg.getintregister(current_asmdata.CurrAsmList, OS_64);
197     current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_const_const(A_RLDICL, tmpintreg2, valuereg, 0, 32));
198 
199     // std rT1, disp(r1)
200     cg.a_load_reg_ref(current_asmdata.CurrAsmList, OS_S64, OS_S64, tmpintreg1, disp);
201     // std rT2, disp2(r1)
202     cg.a_load_reg_ref(current_asmdata.CurrAsmList, OS_S64, OS_S64, tmpintreg2, disp2);
203 
204     // lfd frT1, disp(R1)
205     tmpfpureg := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
206     cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64, OS_F64, disp, tmpfpureg);
207     // lfd frD, disp+8(R1)
208     location.register := cg.getfpuregister(current_asmdata.CurrAsmList,OS_F64);
209     cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,OS_F64, OS_F64, disp2, location.register);
210 
211     // fcfid frT1, frT1
212     current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCFID, tmpfpureg,
213       tmpfpureg));
214     // fcfid frD, frD
215     current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FCFID, location.register,
216       location.register));
217     // fmadd frD,frC,frT1,frD # (2^32)*high + low }
218     current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg_reg_reg(A_FMADD, location.register, tmpfpuconst,
219       tmpfpureg, location.register));
220 
221     // free used temps
222     tg.ungetiftemp(current_asmdata.CurrAsmList, disp2);
223   end;
224   // free reference
225   tg.ungetiftemp(current_asmdata.CurrAsmList, disp);
226 
227   // make sure the precision is correct
228   if (tfloatdef(resultdef).floattype = s32real) then
229     current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FRSP,location.register,
230       location.register));
231 end;
232 
233 begin
234   ctypeconvnode := tppctypeconvnode;
235 end.
236 
237