/compiler/mips/cpupara.pas
Pascal | 578 lines | 433 code | 45 blank | 100 comment | 48 complexity | 2a7d7c8cb5a48e0e112c26427d9469cc MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, LGPL-3.0
1{ 2 Copyright (c) 1998-2002 by Florian Klaempfl and David Zhang 3 4 Calling conventions for the MIPSEL 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 *****************************************************************************} 20unit cpupara; 21 22{$i fpcdefs.inc} 23 24interface 25 26 uses 27 globtype, 28 cclasses, 29 aasmtai, 30 cpubase,cpuinfo, 31 symconst,symbase,symsym,symtype,symdef,paramgr,parabase,cgbase,cgutils; 32 33 const 34 MIPS_MAX_OFFSET = 20; 35 36 { The value below is OK for O32 and N32 calling conventions } 37 MIPS_MAX_REGISTERS_USED_IN_CALL = 6; 38 39 { All ABI seem to start with $4 i.e. $a0 } 40 MIPS_FIRST_REGISTER_USED_IN_CALL = RS_R4; 41 { O32 ABI uses $a0 to $a3, i.e R4 to R7 } 42 MIPS_LAST_REGISTER_USED_IN_CALL_ABI_O32 = RS_R7; 43 { N32 ABI uses also R8 and R9 } 44 MIPS_LAST_REGISTER_USED_IN_CALL_ABI_N32 = RS_R9; 45 { The calculation below is based on the assumption 46 that all registers used for ABI calls are 47 ordered and follow each other } 48 MIPS_NB_REGISTERS_USED_IN_CALL_O32 = 49 MIPS_LAST_REGISTER_USED_IN_CALL_ABI_O32 50 - MIPS_FIRST_REGISTER_USED_IN_CALL + 1; 51 MIPS_NB_REGISTERS_USED_IN_CALL_N32 = 52 MIPS_LAST_REGISTER_USED_IN_CALL_ABI_N32 53 - MIPS_FIRST_REGISTER_USED_IN_CALL + 1; 54 55 56 { Set O32 ABI as default } 57 const 58 mips_nb_used_registers : longint = MIPS_NB_REGISTERS_USED_IN_CALL_O32; 59 60 { Might need to be changed if we support N64 ABI later } 61 mips_sizeof_register_param : longint = 4; 62 63 type 64 tparasupregs = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of tsuperregister; 65 tparasupregsused = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of boolean; 66 tparasupregsize = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of tcgsize; 67 tparasuprename = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of shortstring; 68 tparasupregsoffset = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of longint; 69 70 const 71 72 parasupregs : tparasupregs = (RS_R4, RS_R5, RS_R6, RS_R7, RS_R8, RS_R9); 73 74 type 75 TMIPSParaManager=class(TParaManager) 76 function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override; 77 function get_volatile_registers_int(calloption : tproccalloption):TCpuRegisterSet;override; 78 function get_volatile_registers_fpu(calloption : tproccalloption):TCpuRegisterSet;override; 79 {Returns a structure giving the information on the storage of the parameter 80 (which must be an integer parameter) 81 @param(nr Parameter number of routine, starting from 1)} 82 procedure getintparaloc(pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);override; 83 function create_paraloc_info(p : TAbstractProcDef; side: tcallercallee):longint;override; 84 function create_varargs_paraloc_info(p : TAbstractProcDef; varargspara:tvarargsparalist):longint;override; 85 function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override; 86 private 87 intparareg, 88 intparasize : longint; 89 can_use_float : boolean; 90 function is_abi_record(def: tdef): boolean; 91 procedure create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist); 92 end; 93 94implementation 95 96 uses 97 cutils,verbose,systems, 98 defutil, cpupi, procinfo, 99 cgobj; 100 101 102 103 function TMIPSParaManager.get_volatile_registers_int(calloption : tproccalloption):TCpuRegisterSet; 104 begin 105 { O32 ABI values } 106 result:=[RS_R1..RS_R15,RS_R24..RS_R25,RS_R31]; 107 end; 108 109 110 function TMIPSParaManager.get_volatile_registers_fpu(calloption : tproccalloption):TCpuRegisterSet; 111 begin 112 { O32 ABI values } 113 result:=[RS_F0..RS_F19]; 114 end; 115 116 117 { whether "def" must be treated as record when used as function result, 118 i.e. its address passed in a0 } 119 function TMIPSParaManager.is_abi_record(def: tdef): boolean; 120 begin 121 result:=(def.typ=recorddef) or 122 ((def.typ=procvardef) and not tprocvardef(def).is_addressonly); 123 end; 124 125 126 procedure TMIPSParaManager.GetIntParaLoc(pd : tabstractprocdef; nr : longint; var cgpara : tcgpara); 127 var 128 paraloc : pcgparalocation; 129 def : tdef; 130 begin 131 if nr<1 then 132 InternalError(2002100806); 133 def:=tparavarsym(pd.paras[nr-1]).vardef; 134 cgpara.reset; 135 cgpara.size:=def_cgsize(def); 136 cgpara.intsize:=tcgsize2size[cgpara.size]; 137 cgpara.alignment:=std_param_align; 138 cgpara.def:=def; 139 paraloc:=cgpara.add_location; 140 with paraloc^ do 141 begin 142 { MIPS: ABI dependent number of first parameters 143 are passed into registers } 144 dec(nr); 145 if nr<mips_nb_used_registers then 146 begin 147 loc:=LOC_REGISTER; 148 register:=newreg(R_INTREGISTER,parasupregs[nr],R_SUBWHOLE); 149 end 150 else 151 begin 152 { The other parameters are passed on the stack } 153 loc:=LOC_REFERENCE; 154 reference.index:=NR_STACK_POINTER_REG; 155 reference.offset:=nr*mips_sizeof_register_param; 156 end; 157 size:=OS_INT; 158 { Be sure to reserve enough stack space tp cope with 159 that parameter } 160 if assigned(current_procinfo) then 161 TMIPSProcinfo(current_procinfo).allocate_push_parasize((nr+1)*mips_sizeof_register_param); 162 end; 163 end; 164 165 { true if a parameter is too large to copy and only the address is pushed } 166 function TMIPSParaManager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean; 167 begin 168 result:=false; 169 { var,out,constref always require address } 170 if varspez in [vs_var,vs_out,vs_constref] then 171 begin 172 result:=true; 173 exit; 174 end; 175 case def.typ of 176 recorddef: 177 { According to 032 ABI we should have } 178 result:=false; 179 arraydef: 180 result:=true; {(tarraydef(def).highrange>=tarraydef(def).lowrange) or 181 is_open_array(def) or 182 is_array_of_const(def) or 183 is_array_constructor(def);} 184 variantdef, 185 formaldef : 186 result:=true; 187 objectdef : 188 result:=is_object(def); 189 stringdef : 190 result:=(tstringdef(def).stringtype in [st_shortstring,st_longstring]); 191 procvardef : 192 { If we always push records by value, we have to handle methodpointers that way too. } 193 result:=false; {not tprocvardef(def).is_addressonly;} 194 setdef : 195 result:=not(is_smallset(def)); 196 end; 197 end; 198 199 200 function TMIPSParaManager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara; 201 var 202 paraloc : pcgparalocation; 203 retcgsize : tcgsize; 204 retdef : tdef; 205 begin 206 if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then 207 begin 208 { Return is passed as var parameter, 209 in this case we use the first register R4 for it } 210 if assigned(forcetempdef) then 211 retdef:=forcetempdef 212 else 213 retdef:=p.returndef; 214 if ret_in_param(retdef,p) and 215 is_abi_record(retdef) then 216 begin 217 if intparareg=0 then 218 inc(intparareg); 219 if side=calleeside then 220 begin 221 result.reset; 222 paraloc:=result.add_location; 223 paraloc^.loc:=LOC_REFERENCE; 224 paraloc^.reference.index:=NR_STACK_POINTER_REG; 225 { return is at offset zero } 226 paraloc^.reference.offset:=0; 227 paraloc^.size:=retcgsize; 228 { Reserve first register for ret_in_param } 229 if assigned(current_procinfo) then 230 begin 231 TMIPSProcInfo(current_procinfo).register_used[0]:=true; 232 TMIPSProcInfo(current_procinfo).register_size[0]:=retcgsize; 233 TMIPSProcInfo(current_procinfo).register_name[0]:='ret_in_param_result'; 234 TMIPSProcInfo(current_procinfo).register_offset[0]:=0; 235 end; 236 end 237 else 238 begin 239 getIntParaLoc(p,1,result); 240 result.def:=retdef; 241 end; 242 // This is now done in set_common_funcretloc_info already 243 // result.def:=getpointerdef(result.def); 244 end; 245 exit; 246 end; 247 248 paraloc:=result.add_location; 249 { Return in FPU register? } 250 if result.def.typ=floatdef then 251 begin 252 paraloc^.loc:=LOC_FPUREGISTER; 253 paraloc^.register:=NR_FPU_RESULT_REG; 254 if retcgsize=OS_F64 then 255 setsubreg(paraloc^.register,R_SUBFD); 256 paraloc^.size:=retcgsize; 257 end 258 else 259 { Return in register } 260 begin 261{$ifndef cpu64bitalu} 262 if retcgsize in [OS_64,OS_S64] then 263 begin 264 { low } 265 paraloc^.loc:=LOC_REGISTER; 266 if side=callerside then 267 paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG 268 else 269 paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG; 270 paraloc^.size:=OS_32; 271 { high } 272 paraloc:=result.add_location; 273 paraloc^.loc:=LOC_REGISTER; 274 if side=callerside then 275 paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG 276 else 277 paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG; 278 paraloc^.size:=OS_32; 279 end 280 else 281{$endif cpu64bitalu} 282 begin 283 paraloc^.loc:=LOC_REGISTER; 284 paraloc^.size:=retcgsize; 285 if side=callerside then 286 paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize)) 287 else 288 paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize)); 289 end; 290 end 291 end; 292 293 294 procedure TMIPSParaManager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee;paras:tparalist); 295 var 296 paraloc : pcgparalocation; 297 i : integer; 298 hp : tparavarsym; 299 paracgsize : tcgsize; 300 paralen : longint; 301 paradef : tdef; 302 fpparareg : integer; 303 reg : tsuperregister; 304 alignment : longint; 305 tmp : longint; 306 begin 307 fpparareg := 0; 308 for i:=0 to paras.count-1 do 309 begin 310 hp:=tparavarsym(paras[i]); 311 paradef := hp.vardef; 312 313 { currently only support C-style array of const } 314 if (p.proccalloption in cstylearrayofconst) and 315 is_array_of_const(paradef) then 316 begin 317 paraloc:=hp.paraloc[side].add_location; 318 { hack: the paraloc must be valid, but is not actually used } 319 paraloc^.loc:=LOC_REGISTER; 320 paraloc^.register:=NR_R0; 321 paraloc^.size:=OS_ADDR; 322 break; 323 end; 324 325 if push_addr_param(hp.varspez,paradef,p.proccalloption) then 326 begin 327 paracgsize := OS_ADDR; 328 paralen := tcgsize2size[paracgsize]; 329 paradef := getpointerdef(paradef); 330 end 331 else 332 begin 333 paracgsize := def_cgsize(paradef); 334 { for things like formaldef } 335 if (paracgsize=OS_NO) and (paradef.typ <> recorddef) then 336 begin 337 paracgsize:=OS_ADDR; 338 paradef:=voidpointertype; 339 end; 340 if not is_special_array(paradef) then 341 paralen := paradef.size 342 else 343 paralen := tcgsize2size[paracgsize]; 344 end; 345 346 if (paracgsize in [OS_64, OS_S64, OS_F64]) or (paradef.alignment = 8) then 347 alignment := 8 348 else 349 alignment := 4; 350 //writeln('para: ',hp.Name,' typ=',hp.vardef.typ,' paracgsize=',paracgsize,' align=',hp.vardef.alignment); 351 hp.paraloc[side].reset; 352 hp.paraloc[side].Alignment:=alignment; 353 if paracgsize=OS_NO then 354 begin 355 paracgsize:=OS_32; 356 paralen:=align(paralen,4); 357 end 358 else 359 paralen:=tcgsize2size[paracgsize]; 360 hp.paraloc[side].intsize:=paralen; 361 hp.paraloc[side].size:=paracgsize; 362 hp.paraloc[side].def:=paradef; 363 { check the alignment, mips O32ABI require a nature alignment } 364 tmp := align(intparasize, alignment) - intparasize; 365 while tmp > 0 do 366 begin 367 inc(intparareg); 368 inc(intparasize,4); 369 dec(tmp,4); 370 end; 371 372 { any non-float args will disable the use the floating regs } 373 { up to two fp args } 374 if (not(paracgsize in [OS_F32, OS_F64])) or (fpparareg = 2) then 375 can_use_float := false; 376 377 while paralen>0 do 378 begin 379 paraloc:=hp.paraloc[side].add_location; 380 { We can allocate at maximum 32 bits per register } 381 if (paracgsize in [OS_64,OS_S64]) or 382 ((paracgsize in [OS_F32,OS_F64]) and 383 not(can_use_float)) then 384 paraloc^.size:=OS_32 385 else 386 paraloc^.size:=paracgsize; 387 388 { big-endian targets require that record data stored in parameter 389 registers is left-aligned } 390 if (target_info.endian=endian_big) and 391 (paradef.typ = recorddef) and 392 (tcgsize2size[paraloc^.size] <> sizeof(aint)) then 393 begin 394 paraloc^.shiftval := (sizeof(aint)-tcgsize2size[paraloc^.size])*(-8); 395 paraloc^.size := OS_INT; 396 end; 397 398 { ret in param? } 399 if (vo_is_funcret in hp.varoptions) and 400 is_abi_record(hp.vardef) then 401 begin 402 { This should be the first parameter } 403 if (side=calleeside) and assigned(current_procinfo) then 404 begin 405 TMIPSProcInfo(current_procinfo).register_used[0]:=true; 406 TMIPSProcInfo(current_procinfo).register_name[0]:='result'; 407 TMIPSProcInfo(current_procinfo).register_size[0]:=paraloc^.size; 408 TMIPSProcInfo(current_procinfo).register_offset[0]:=0; 409 end; 410 //if (intparareg<>1) then 411 // Comment(V_Warning,'intparareg should be one for funcret in TMipsParaManager.create_paraloc_info_intern'); 412 if side=callerside then 413 begin 414 paraloc^.loc:=LOC_REGISTER; 415 paraloc^.register:=newreg(R_INTREGISTER,parasupregs[0],R_SUBWHOLE); 416 end 417 else 418 begin 419 paraloc^.loc:=LOC_REFERENCE; 420 if (po_nostackframe in p.procoptions) then 421 paraloc^.reference.index := NR_STACK_POINTER_REG 422 else 423 begin 424 paraloc^.reference.index := NR_FRAME_POINTER_REG; 425 if assigned(current_procinfo) then 426 TMIPSProcinfo(current_procinfo).needs_frame_pointer := true; 427 end; 428 paraloc^.reference.offset:=0; 429 end; 430 inc(intparasize,align(tcgsize2size[paraloc^.size],sizeof(aint))); 431 end 432 { In case of po_delphi_nested_cc, the parent frame pointer 433 is always passed on the stack. } 434 else if (intparareg<mips_nb_used_registers) and 435 (not(vo_is_parentfp in hp.varoptions) or 436 not(po_delphi_nested_cc in p.procoptions)) then 437 begin 438 if (can_use_float) then 439 begin 440 paraloc^.loc:=LOC_FPUREGISTER; 441 if (fpparareg = 0) then 442 reg := RS_F12 443 else 444 reg := RS_F14; 445 if (paraloc^.size = OS_F64) then 446 begin 447 paraloc^.register:=newreg(R_FPUREGISTER, reg, R_SUBFD); 448 inc(fpparareg); 449 inc(intparareg); 450 inc(intparareg); 451 inc(intparasize,8); 452 end 453 else 454 begin 455 paraloc^.register:=newreg(R_FPUREGISTER, reg, R_SUBFS); 456 inc(fpparareg); 457 inc(intparareg); 458 inc(intparasize,sizeof(aint)); 459 end; 460 end 461 else { not can use float } 462 begin 463 if (side=calleeside) and assigned(current_procinfo) then 464 begin 465 TMIPSProcInfo(current_procinfo).register_used[intparareg]:=true; 466 TMIPSProcInfo(current_procinfo).register_name[intparareg]:=hp.prettyname; 467 TMIPSProcInfo(current_procinfo).register_size[intparareg]:=paraloc^.size; 468 TMIPSProcInfo(current_procinfo).register_offset[intparareg]:=intparareg*mips_sizeof_register_param; 469 end; 470 if side=callerside then 471 begin 472 paraloc^.loc:=LOC_REGISTER; 473 paraloc^.register:=newreg(R_INTREGISTER,parasupregs[intparareg],R_SUBWHOLE); 474 end 475 else 476 begin 477 paraloc^.loc:=LOC_REFERENCE; 478 if (po_nostackframe in p.procoptions) then 479 paraloc^.reference.index := NR_STACK_POINTER_REG 480 else 481 begin 482 paraloc^.reference.index := NR_FRAME_POINTER_REG; 483 if assigned(current_procinfo) then 484 TMIPSProcinfo(current_procinfo).needs_frame_pointer := true; 485 end; 486 paraloc^.reference.offset:=intparareg*mips_sizeof_register_param; 487 end; 488 inc(intparareg); 489 inc(intparasize,align(tcgsize2size[paraloc^.size],mips_sizeof_register_param)); 490 end; 491 end 492 else 493 begin 494 paraloc^.loc:=LOC_REFERENCE; 495 paraloc^.size:=int_cgsize(paralen); 496 497 { Force size to multiple of 4 for records passed by value, 498 to obtain correct memory layout for big endian } 499(* 500 if (paradef.typ = recorddef) and 501 (tcgsize2size[paraloc^.size] < tcgsize2size[OS_32]) then 502 begin 503 inc(paralen,tcgsize2size[OS_32]-tcgsize2size[paraloc^.size]); 504 paraloc^.size := OS_32; 505 end; 506*) 507 if side=callerside then 508 begin 509 paraloc^.reference.index := NR_STACK_POINTER_REG; 510 paraloc^.reference.offset:=intparasize; 511 end 512 else 513 begin 514 if (po_nostackframe in p.procoptions) then 515 paraloc^.reference.index := NR_STACK_POINTER_REG 516 else 517 begin 518 paraloc^.reference.index := NR_FRAME_POINTER_REG; 519 if assigned(current_procinfo) then 520 TMIPSProcinfo(current_procinfo).needs_frame_pointer := true; 521 end; 522 paraloc^.reference.offset:=intparasize; 523 end; 524 inc(intparasize,align(paralen,mips_sizeof_register_param)); 525 paralen:=0; 526 end; 527 dec(paralen,tcgsize2size[paraloc^.size]); 528 end; 529 end; 530 { O32 ABI reqires at least 16 bytes } 531 if (intparasize < 16) then 532 intparasize := 16; 533 { Increase maxpushparasize, but only if not examining itself } 534 //if assigned(current_procinfo) and (side=callerside) and 535 // (current_procinfo.procdef <> p) then 536 // begin 537 // TMIPSProcinfo(current_procinfo).allocate_push_parasize(intparasize); 538 // end; 539 end; 540 541 542 function TMIPSParaManager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint; 543 begin 544 intparareg:=0; 545 intparasize:=0; 546 can_use_float := true; 547 { Create Function result paraloc } 548 create_funcretloc_info(p,callerside); 549 { calculate the registers for the normal parameters } 550 create_paraloc_info_intern(p,callerside,p.paras); 551 { append the varargs } 552 can_use_float := false; 553 { restore correct intparasize value } 554 if intparareg < 4 then 555 intparasize:=intparareg * 4; 556 create_paraloc_info_intern(p,callerside,varargspara); 557 { We need to return the size allocated on the stack } 558 result:=intparasize; 559 end; 560 561 562 563 function TMIPSParaManager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint; 564 begin 565 intparareg:=0; 566 intparasize:=0; 567 can_use_float := true; 568 { Create Function result paraloc } 569 create_funcretloc_info(p,side); 570 create_paraloc_info_intern(p,side,p.paras); 571 { We need to return the size allocated on the stack } 572 result:=intparasize; 573 end; 574 575 576begin 577 ParaManager:=TMIPSParaManager.create; 578end.