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