PageRenderTime 22ms CodeModel.GetById 17ms app.highlight 2ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler/mips/cpupara.pas

https://github.com/slibre/freepascal
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.