/compiler/x86/rgx86.pas
Pascal | 415 lines | 220 code | 47 blank | 148 comment | 16 complexity | 7af374c71919652e977fbf354255fbac MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, LGPL-3.0
1{ 2 Copyright (c) 1998-2002 by Florian Klaempfl 3 4 This unit implements the x86 specific class for the register 5 allocator 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 21 **************************************************************************** 22} 23 24unit rgx86; 25 26{$i fpcdefs.inc} 27 28 interface 29 30 uses 31 cclasses,globtype, 32 cpubase,cpuinfo,cgbase,cgutils, 33 aasmbase,aasmtai,aasmdata,aasmcpu, 34 rgobj; 35 36 type 37 trgx86 = class(trgobj) 38 function get_spill_subreg(r : tregister) : tsubregister;override; 39 function do_spill_replace(list:TAsmList;instr:taicpu;orgreg:tsuperregister;const spilltemp:treference):boolean;override; 40 end; 41 42 tpushedsavedloc = record 43 case byte of 44 0: (pushed: boolean); 45 1: (ofs: longint); 46 end; 47 48 tpushedsavedfpu = array[tsuperregister] of tpushedsavedloc; 49 50 trgx86fpu = class 51 { The "usableregsxxx" contain all registers of type "xxx" that } 52 { aren't currently allocated to a regvar. The "unusedregsxxx" } 53 { contain all registers of type "xxx" that aren't currently } 54 { allocated } 55 unusedregsfpu,usableregsfpu : Tsuperregisterset; 56 { these counters contain the number of elements in the } 57 { unusedregsxxx/usableregsxxx sets } 58 countunusedregsfpu : byte; 59 60 { Contains the registers which are really used by the proc itself. 61 It doesn't take care of registers used by called procedures 62 } 63 used_in_proc : tcpuregisterset; 64 65 {reg_pushes_other : regvarother_longintarray; 66 is_reg_var_other : regvarother_booleanarray; 67 regvar_loaded_other : regvarother_booleanarray;} 68 69 fpuvaroffset : byte; 70 71 constructor create; 72 73 function getregisterfpu(list: TAsmList) : tregister; 74 procedure ungetregisterfpu(list: TAsmList; r : tregister); 75 76 { pushes and restores registers } 77 procedure saveusedfpuregisters(list:TAsmList; 78 var saved:Tpushedsavedfpu; 79 const s:Tcpuregisterset); 80 procedure restoreusedfpuregisters(list:TAsmList; 81 const saved:Tpushedsavedfpu); 82 83 { corrects the fpu stack register by ofs } 84 function correct_fpuregister(r : tregister;ofs : byte) : tregister; 85 end; 86 87 88implementation 89 90 uses 91 systems, 92 verbose; 93 94 const 95 { This value is used in tsaved. If the array value is equal 96 to this, then this means that this register is not used.} 97 reg_not_saved = $7fffffff; 98 99 100{****************************************************************************** 101 Trgcpu 102******************************************************************************} 103 104 function trgx86.get_spill_subreg(r : tregister) : tsubregister; 105 begin 106 result:=getsubreg(r); 107 end; 108 109 110 function trgx86.do_spill_replace(list:TAsmList;instr:taicpu;orgreg:tsuperregister;const spilltemp:treference):boolean; 111 112 {Decide wether a "replace" spill is possible, i.e. wether we can replace a register 113 in an instruction by a memory reference. For example, in "mov ireg26d,0", the imaginary 114 register ireg26d can be replaced by a memory reference.} 115 116 var 117 n,replaceoper : longint; 118 begin 119 result:=false; 120 with instr do 121 begin 122 replaceoper:=-1; 123 case ops of 124 1 : 125 begin 126 if (oper[0]^.typ=top_reg) and 127 (getregtype(oper[0]^.reg)=regtype) then 128 begin 129 if get_alias(getsupreg(oper[0]^.reg))<>orgreg then 130 internalerror(200410101); 131 replaceoper:=0; 132 end; 133 end; 134 2,3 : 135 begin 136 { We can handle opcodes with 2 and 3 operands the same way. The opcodes 137 with 3 registers are shrd/shld, where the 3rd operand is const or CL, 138 that doesn't need spilling. 139 However, due to AT&T order inside the compiler, the 3rd operand is 140 numbered 0, so look at operand no. 1 and 2 if we have 3 operands by 141 adding a "n". } 142 n:=0; 143 if ops=3 then 144 n:=1; 145 if (oper[n+0]^.typ=top_reg) and 146 (oper[n+1]^.typ=top_reg) and 147 ((getregtype(oper[n+0]^.reg)<>regtype) or 148 (getregtype(oper[n+1]^.reg)<>regtype) or 149 (get_alias(getsupreg(oper[n+0]^.reg))<>get_alias(getsupreg(oper[n+1]^.reg)))) then 150 begin 151 if (getregtype(oper[n+0]^.reg)=regtype) and 152 (get_alias(getsupreg(oper[n+0]^.reg))=orgreg) then 153 replaceoper:=0+n 154 else if (getregtype(oper[n+1]^.reg)=regtype) and 155 (get_alias(getsupreg(oper[n+1]^.reg))=orgreg) then 156 replaceoper:=1+n; 157 end 158 else if (oper[n+0]^.typ=top_reg) and 159 (oper[n+1]^.typ=top_const) then 160 begin 161 if (getregtype(oper[0+n]^.reg)=regtype) and 162 (get_alias(getsupreg(oper[0+n]^.reg))=orgreg) then 163 replaceoper:=0+n 164 else 165 internalerror(200704282); 166 end 167 else if (oper[n+0]^.typ=top_const) and 168 (oper[n+1]^.typ=top_reg) then 169 begin 170 if (getregtype(oper[1+n]^.reg)=regtype) and 171 (get_alias(getsupreg(oper[1+n]^.reg))=orgreg) then 172 replaceoper:=1+n 173 else 174 internalerror(200704283); 175 end; 176 case replaceoper of 177 0 : 178 begin 179 { Some instructions don't allow memory references 180 for source } 181 case instr.opcode of 182 A_BT, 183 A_BTS, 184 A_BTC, 185 A_BTR, 186 187 { shufp* would require 16 byte alignment for memory locations so we force the source 188 operand into a register } 189 A_SHUFPD, 190 A_SHUFPS : 191 replaceoper:=-1; 192 end; 193 end; 194 1 : 195 begin 196 { Some instructions don't allow memory references 197 for destination } 198 case instr.opcode of 199 A_CMOVcc, 200 A_MOVZX, 201 A_MOVSX, 202 A_MOVSXD, 203 A_MULSS, 204 A_MULSD, 205 A_SUBSS, 206 A_SUBSD, 207 A_ADDSD, 208 A_ADDSS, 209 A_DIVSD, 210 A_DIVSS, 211 A_SHLD, 212 A_SHRD, 213 A_COMISD, 214 A_COMISS, 215 A_CVTDQ2PD, 216 A_CVTDQ2PS, 217 A_CVTPD2DQ, 218 A_CVTPD2PI, 219 A_CVTPD2PS, 220 A_CVTPI2PD, 221 A_CVTPS2DQ, 222 A_CVTPS2PD, 223 A_CVTSD2SI, 224 A_CVTSD2SS, 225 A_CVTSI2SD, 226 A_CVTSS2SD, 227 A_CVTTPD2PI, 228 A_CVTTPD2DQ, 229 A_CVTTPS2DQ, 230 A_CVTTSD2SI, 231 A_CVTPI2PS, 232 A_CVTPS2PI, 233 A_CVTSI2SS, 234 A_CVTSS2SI, 235 A_CVTTPS2PI, 236 A_CVTTSS2SI, 237 A_IMUL, 238 A_XORPD, 239 A_XORPS, 240 A_ORPD, 241 A_ORPS, 242 A_ANDPD, 243 A_ANDPS, 244 A_UNPCKLPS, 245 A_UNPCKHPS, 246 A_SHUFPD, 247 A_SHUFPS: 248 249 replaceoper:=-1; 250{$ifdef x86_64} 251 A_MOV: 252 { 64 bit constants can only be moved into registers } 253 if (oper[0]^.typ=top_const) and 254 (oper[1]^.typ=top_reg) and 255 ((oper[0]^.val<low(longint)) or 256 (oper[0]^.val>high(longint))) then 257 replaceoper:=-1; 258{$endif x86_64} 259 end; 260 end; 261 end; 262 end; 263 end; 264 265 {$ifdef x86_64} 266 { 32 bit operations on 32 bit registers on x86_64 can result in 267 zeroing the upper 32 bits of the register. This does not happen 268 with memory operations, so we have to perform these calculations 269 in registers. } 270 if (instr.opsize=S_L) then 271 replaceoper:=-1; 272 {$endif x86_64} 273 274 { Replace register with spill reference } 275 if replaceoper<>-1 then 276 begin 277 oper[replaceoper]^.typ:=top_ref; 278 new(oper[replaceoper]^.ref); 279 oper[replaceoper]^.ref^:=spilltemp; 280 { memory locations aren't guaranteed to be aligned } 281 case opcode of 282 A_MOVAPS: 283 opcode:=A_MOVSS; 284 A_MOVAPD: 285 opcode:=A_MOVSD; 286 end; 287 result:=true; 288 end; 289 end; 290 end; 291 292 293{****************************************************************************** 294 Trgx86fpu 295******************************************************************************} 296 297 constructor Trgx86fpu.create; 298 begin 299 used_in_proc:=[]; 300 unusedregsfpu:=usableregsfpu; 301 end; 302 303 304 function trgx86fpu.getregisterfpu(list: TAsmList) : tregister; 305 begin 306 { note: don't return R_ST0, see comments above implementation of } 307 { a_loadfpu_* methods in cgcpu (JM) } 308 result:=NR_ST; 309 end; 310 311 312 procedure trgx86fpu.ungetregisterfpu(list : TAsmList; r : tregister); 313 begin 314 { nothing to do, fpu stack management is handled by the load/ } 315 { store operations in cgcpu (JM) } 316 end; 317 318 319 320 function trgx86fpu.correct_fpuregister(r : tregister;ofs : byte) : tregister; 321 begin 322 correct_fpuregister:=r; 323 setsupreg(correct_fpuregister,ofs); 324 end; 325 326 327 procedure trgx86fpu.saveusedfpuregisters(list: TAsmList; 328 var saved : tpushedsavedfpu; 329 const s: tcpuregisterset); 330 { var 331 r : tregister; 332 hr : treference; } 333 begin 334 used_in_proc:=used_in_proc+s; 335 336{ TODO: firstsavefpureg} 337(* 338 { don't try to save the fpu registers if not desired (e.g. for } 339 { the 80x86) } 340 if firstsavefpureg <> R_NO then 341 for r.enum:=firstsavefpureg to lastsavefpureg do 342 begin 343 saved[r.enum].ofs:=reg_not_saved; 344 { if the register is used by the calling subroutine and if } 345 { it's not a regvar (those are handled separately) } 346 if not is_reg_var_other[r.enum] and 347 (r.enum in s) and 348 { and is present in use } 349 not(r.enum in unusedregsfpu) then 350 begin 351 { then save it } 352 tg.GetTemp(list,extended_size,tt_persistent,hr); 353 saved[r.enum].ofs:=hr.offset; 354 cg.a_loadfpu_reg_ref(list,OS_FLOAT,OS_FLOAT,r,hr); 355 cg.a_reg_dealloc(list,r); 356 include(unusedregsfpu,r.enum); 357 inc(countunusedregsfpu); 358 end; 359 end; 360*) 361 end; 362 363 364 procedure trgx86fpu.restoreusedfpuregisters(list : TAsmList; 365 const saved : tpushedsavedfpu); 366{ 367 var 368 r,r2 : tregister; 369 hr : treference; 370} 371 begin 372{ TODO: firstsavefpureg} 373(* 374 if firstsavefpureg <> R_NO then 375 for r.enum:=lastsavefpureg downto firstsavefpureg do 376 begin 377 if saved[r.enum].ofs <> reg_not_saved then 378 begin 379 r2.enum:=R_INTREGISTER; 380 r2.number:=NR_FRAME_POINTER_REG; 381 reference_reset_base(hr,r2,saved[r.enum].ofs); 382 cg.a_reg_alloc(list,r); 383 cg.a_loadfpu_ref_reg(list,OS_FLOAT,OS_FLOAT,hr,r); 384 if not (r.enum in unusedregsfpu) then 385 { internalerror(10) 386 in n386cal we always save/restore the reg *state* 387 using save/restoreunusedstate -> the current state 388 may not be real (JM) } 389 else 390 begin 391 dec(countunusedregsfpu); 392 exclude(unusedregsfpu,r.enum); 393 end; 394 tg.UnGetTemp(list,hr); 395 end; 396 end; 397*) 398 end; 399 400(* 401 procedure Trgx86fpu.saveotherregvars(list: TAsmList; const s: totherregisterset); 402 var 403 r: Tregister; 404 begin 405 if not(cs_opt_regvar in current_settings.optimizerswitches) then 406 exit; 407 if firstsavefpureg <> NR_NO then 408 for r.enum := firstsavefpureg to lastsavefpureg do 409 if is_reg_var_other[r.enum] and 410 (r.enum in s) then 411 store_regvar(list,r); 412 end; 413*) 414 415end.