Browse Source

* Properly fill treference.alignment when variable is loaded by tcgloadnode. It allows code generator to insert unaligned handling if needed.
* Improved generic a_load_ref_reg_unaligned if ref alignment is 2.
* Improved unaligned load/store of register for ARM.
* It fixes passing records by value on ARM.
+ New test.

git-svn-id: trunk@10681 -

yury 17 years ago
parent
commit
ec943198fd
6 changed files with 168 additions and 89 deletions
  1. 1 0
      .gitattributes
  2. 69 43
      compiler/arm/cgcpu.pas
  3. 53 35
      compiler/cgobj.pas
  4. 2 1
      compiler/ncgld.pas
  5. 1 10
      compiler/tgobj.pas
  6. 42 0
      tests/test/cg/tpara3.pp

+ 1 - 0
.gitattributes

@@ -7069,6 +7069,7 @@ tests/test/cg/tobjsiz2.pp svneol=native#text/plain
 tests/test/cg/tobjsize.pp svneol=native#text/plain
 tests/test/cg/tpara1.pp svneol=native#text/plain
 tests/test/cg/tpara2.pp svneol=native#text/plain
+tests/test/cg/tpara3.pp svneol=native#text/plain
 tests/test/cg/tprintf.pp svneol=native#text/plain
 tests/test/cg/tprintf2.pp svneol=native#text/plain
 tests/test/cg/tprintf3.pp svneol=native#text/plain

+ 69 - 43
compiler/arm/cgcpu.pas

@@ -791,7 +791,7 @@ unit cgcpu;
            else
              InternalError(200308295);
          end;
-         if ref.alignment<>0 then
+         if (ref.alignment in [1,2]) and (ref.alignment<tcgsize2size[tosize]) then
            begin
              if target_info.endian=endian_big then
                dir:=-1
@@ -812,21 +812,35 @@ unit cgcpu;
                  end;
                OS_32,OS_S32:
                  begin
-                   shifterop_reset(so);so.shiftmode:=SM_LSR;so.shiftimm:=8;
                    tmpreg:=getintregister(list,OS_INT);
                    usedtmpref:=ref;
-                   if target_info.endian=endian_big then
-                     inc(usedtmpref.offset,3);
-                   usedtmpref:=a_internal_load_reg_ref(list,OS_8,OS_8,reg,usedtmpref);
-                   list.concat(taicpu.op_reg_reg_shifterop(A_MOV,tmpreg,reg,so));
-                   inc(usedtmpref.offset,dir);
-                   a_internal_load_reg_ref(list,OS_8,OS_8,tmpreg,usedtmpref);
-                   list.concat(taicpu.op_reg_reg_shifterop(A_MOV,tmpreg,tmpreg,so));
-                   inc(usedtmpref.offset,dir);
-                   a_internal_load_reg_ref(list,OS_8,OS_8,tmpreg,usedtmpref);
-                   list.concat(taicpu.op_reg_reg_shifterop(A_MOV,tmpreg,tmpreg,so));
-                   inc(usedtmpref.offset,dir);
-                   a_internal_load_reg_ref(list,OS_8,OS_8,tmpreg,usedtmpref);
+                   shifterop_reset(so);so.shiftmode:=SM_LSR;
+                   if ref.alignment=2 then
+                     begin
+                       so.shiftimm:=16;
+                       if target_info.endian=endian_big then
+                         inc(usedtmpref.offset,2);
+                       usedtmpref:=a_internal_load_reg_ref(list,OS_16,OS_16,reg,usedtmpref);
+                       list.concat(taicpu.op_reg_reg_shifterop(A_MOV,tmpreg,reg,so));
+                       inc(usedtmpref.offset,dir*2);
+                       a_internal_load_reg_ref(list,OS_16,OS_16,tmpreg,usedtmpref);
+                     end
+                   else
+                     begin
+                       so.shiftimm:=8;
+                       if target_info.endian=endian_big then
+                         inc(usedtmpref.offset,3);
+                       usedtmpref:=a_internal_load_reg_ref(list,OS_8,OS_8,reg,usedtmpref);
+                       list.concat(taicpu.op_reg_reg_shifterop(A_MOV,tmpreg,reg,so));
+                       inc(usedtmpref.offset,dir);
+                       a_internal_load_reg_ref(list,OS_8,OS_8,tmpreg,usedtmpref);
+                       list.concat(taicpu.op_reg_reg_shifterop(A_MOV,tmpreg,tmpreg,so));
+                       inc(usedtmpref.offset,dir);
+                       a_internal_load_reg_ref(list,OS_8,OS_8,tmpreg,usedtmpref);
+                       list.concat(taicpu.op_reg_reg_shifterop(A_MOV,tmpreg,tmpreg,so));
+                       inc(usedtmpref.offset,dir);
+                       a_internal_load_reg_ref(list,OS_8,OS_8,tmpreg,usedtmpref);
+                     end;
                  end
                else
                  handle_load_store(list,A_STR,oppostfix,reg,ref);
@@ -841,7 +855,7 @@ unit cgcpu;
        var
          oppostfix:toppostfix;
          usedtmpref: treference;
-         tmpreg,tmpreg2,tmpreg3 : tregister;
+         tmpreg,tmpreg2 : tregister;
          so : tshifterop;
          dir : integer;
        begin
@@ -863,7 +877,7 @@ unit cgcpu;
            else
              InternalError(200308297);
          end;
-         if Ref.alignment<>0 then
+         if (ref.alignment in [1,2]) and (ref.alignment<tcgsize2size[tosize]) then
            begin
              if target_info.endian=endian_big then
                dir:=-1
@@ -881,9 +895,9 @@ unit cgcpu;
                      (reg=ref.index) or
                      (reg=ref.base) then
                      begin
-                       tmpreg3:=getintregister(list,OS_INT);
-                       a_loadaddr_ref_reg(list,ref,tmpreg3);
-                       reference_reset_base(usedtmpref,tmpreg3,0);
+                       tmpreg2:=getintregister(list,OS_INT);
+                       a_loadaddr_ref_reg(list,ref,tmpreg2);
+                       reference_reset_base(usedtmpref,tmpreg2,0);
                      end
                    else
                      usedtmpref:=ref;
@@ -892,19 +906,17 @@ unit cgcpu;
                      inc(usedtmpref.offset,1);
                    shifterop_reset(so);so.shiftmode:=SM_LSL;so.shiftimm:=8;
                    tmpreg:=getintregister(list,OS_INT);
-                   a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg);
+                   a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,reg);
                    inc(usedtmpref.offset,dir);
-                   tmpreg2:=getintregister(list,OS_INT);
                    if FromSize=OS_16 then
-                     a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg2)
+                     a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg)
                    else
-                     a_internal_load_ref_reg(list,OS_S8,OS_S8,usedtmpref,tmpreg2);
-                   list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,reg,tmpreg,tmpreg2,so));
+                     a_internal_load_ref_reg(list,OS_S8,OS_S8,usedtmpref,tmpreg);
+                   list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,reg,reg,tmpreg,so));
                  end;
                OS_32,OS_S32:
                  begin
                    tmpreg:=getintregister(list,OS_INT);
-                   tmpreg2:=getintregister(list,OS_INT);
 
                    { only complicated references need an extra loadaddr }
                    if assigned(ref.symbol) or
@@ -915,28 +927,42 @@ unit cgcpu;
                      (reg=ref.index) or
                      (reg=ref.base) then
                      begin
-                       tmpreg3:=getintregister(list,OS_INT);
-                       a_loadaddr_ref_reg(list,ref,tmpreg3);
-                       reference_reset_base(usedtmpref,tmpreg3,0);
+                       tmpreg2:=getintregister(list,OS_INT);
+                       a_loadaddr_ref_reg(list,ref,tmpreg2);
+                       reference_reset_base(usedtmpref,tmpreg2,0);
                      end
                    else
                      usedtmpref:=ref;
 
-                   if target_info.endian=endian_big then
-                     inc(usedtmpref.offset,3);
-                   shifterop_reset(so);so.shiftmode:=SM_LSL;so.shiftimm:=8;
-                   a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,reg);
-                   inc(usedtmpref.offset,dir);
-                   a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg);
-                   list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,tmpreg2,reg,tmpreg,so));
-                   inc(usedtmpref.offset,dir);
-                   a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,reg);
-                   so.shiftimm:=16;
-                   list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,tmpreg,tmpreg2,reg,so));
-                   inc(usedtmpref.offset,dir);
-                   a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg2);
-                   so.shiftimm:=24;
-                   list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,reg,tmpreg,tmpreg2,so));
+                   shifterop_reset(so);so.shiftmode:=SM_LSL;
+                   if ref.alignment=2 then
+                     begin
+                       if target_info.endian=endian_big then
+                         inc(usedtmpref.offset,2);
+                       a_internal_load_ref_reg(list,OS_16,OS_16,usedtmpref,reg);
+                       inc(usedtmpref.offset,dir*2);
+                       a_internal_load_ref_reg(list,OS_16,OS_16,usedtmpref,tmpreg);
+                       so.shiftimm:=16;
+                       list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,reg,reg,tmpreg,so));
+                     end
+                   else
+                     begin
+                       if target_info.endian=endian_big then
+                         inc(usedtmpref.offset,3);
+                       a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,reg);
+                       inc(usedtmpref.offset,dir);
+                       a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg);
+                       so.shiftimm:=8;
+                       list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,reg,reg,tmpreg,so));
+                       inc(usedtmpref.offset,dir);
+                       a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg);
+                       so.shiftimm:=16;
+                       list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,reg,reg,tmpreg,so));
+                       inc(usedtmpref.offset,dir);
+                       a_internal_load_ref_reg(list,OS_8,OS_8,usedtmpref,tmpreg);
+                       so.shiftimm:=24;
+                       list.concat(taicpu.op_reg_reg_reg_shifterop(A_ORR,reg,reg,tmpreg,so));
+                     end;
                  end
                else
                  handle_load_store(list,A_LDR,oppostfix,reg,ref);

+ 53 - 35
compiler/cgobj.pas

@@ -2194,48 +2194,66 @@ implementation
         tmpreg2 : tregister;
         i : longint;
       begin
-        if ref.alignment<>0 then
+        if ref.alignment in [1,2] then
           begin
             tmpref:=ref;
             { we take care of the alignment now }
             tmpref.alignment:=0;
             case FromSize of
               OS_16,OS_S16:
-                begin
-                  { first load in tmpreg, because the target register }
-                  { may be used in ref as well                        }
-                  if target_info.endian=endian_little then
-                    inc(tmpref.offset);
-                  tmpreg:=getintregister(list,OS_8);
-                  a_load_ref_reg(list,OS_8,OS_8,tmpref,tmpreg);
-                  tmpreg:=makeregsize(list,tmpreg,OS_16);
-                  a_op_const_reg(list,OP_SHL,OS_16,8,tmpreg);
-                  if target_info.endian=endian_little then
-                    dec(tmpref.offset)
-                  else
-                    inc(tmpref.offset);
-                  a_load_ref_reg(list,OS_8,OS_16,tmpref,register);
-                  a_op_reg_reg(list,OP_OR,OS_16,tmpreg,register);
-                end;
+                if ref.alignment=2 then
+                  a_load_ref_reg(list,fromsize,tosize,tmpref,register)
+                else
+                  begin
+                    { first load in tmpreg, because the target register }
+                    { may be used in ref as well                        }
+                    if target_info.endian=endian_little then
+                      inc(tmpref.offset);
+                    tmpreg:=getintregister(list,OS_8);
+                    a_load_ref_reg(list,OS_8,OS_8,tmpref,tmpreg);
+                    tmpreg:=makeregsize(list,tmpreg,OS_16);
+                    a_op_const_reg(list,OP_SHL,OS_16,8,tmpreg);
+                    if target_info.endian=endian_little then
+                      dec(tmpref.offset)
+                    else
+                      inc(tmpref.offset);
+                    a_load_ref_reg(list,OS_8,OS_16,tmpref,register);
+                    a_op_reg_reg(list,OP_OR,OS_16,tmpreg,register);
+                  end;
               OS_32,OS_S32:
-                begin
-                  if target_info.endian=endian_little then
-                    inc(tmpref.offset,3);
-                  tmpreg:=getintregister(list,OS_32);
-                  a_load_ref_reg(list,OS_8,OS_32,tmpref,tmpreg);
-                  tmpreg2:=getintregister(list,OS_32);
-                  for i:=1 to 3 do
-                    begin
-                      a_op_const_reg(list,OP_SHL,OS_32,8,tmpreg);
-                      if target_info.endian=endian_little then
-                        dec(tmpref.offset)
-                      else
-                        inc(tmpref.offset);
-                      a_load_ref_reg(list,OS_8,OS_32,tmpref,tmpreg2);
-                      a_op_reg_reg(list,OP_OR,OS_32,tmpreg2,tmpreg);
-                    end;
-                  a_load_reg_reg(list,OS_32,OS_32,tmpreg,register);
-                end
+                if ref.alignment=2 then
+                  begin
+                    if target_info.endian=endian_little then
+                      inc(tmpref.offset,2);
+                    tmpreg:=getintregister(list,OS_32);
+                    a_load_ref_reg(list,OS_16,OS_32,tmpref,tmpreg);
+                    a_op_const_reg(list,OP_SHL,OS_32,16,tmpreg);
+                    if target_info.endian=endian_little then
+                      dec(tmpref.offset,2)
+                    else
+                      inc(tmpref.offset,2);
+                    a_load_ref_reg(list,OS_16,OS_32,tmpref,register);
+                    a_op_reg_reg(list,OP_OR,OS_32,tmpreg,register);
+                  end
+                else
+                  begin
+                    if target_info.endian=endian_little then
+                      inc(tmpref.offset,3);
+                    tmpreg:=getintregister(list,OS_32);
+                    a_load_ref_reg(list,OS_8,OS_32,tmpref,tmpreg);
+                    tmpreg2:=getintregister(list,OS_32);
+                    for i:=1 to 3 do
+                      begin
+                        a_op_const_reg(list,OP_SHL,OS_32,8,tmpreg);
+                        if target_info.endian=endian_little then
+                          dec(tmpref.offset)
+                        else
+                          inc(tmpref.offset);
+                        a_load_ref_reg(list,OS_8,OS_32,tmpref,tmpreg2);
+                        a_op_reg_reg(list,OP_OR,OS_32,tmpreg2,tmpreg);
+                      end;
+                    a_load_reg_reg(list,OS_32,OS_32,tmpreg,register);
+                  end
               else
                 a_load_ref_reg(list,fromsize,tosize,tmpref,register);
             end;

+ 2 - 1
compiler/ncgld.pas

@@ -361,6 +361,7 @@ implementation
                  if (gvs.varspez=vs_const) and
                     (location.loc=LOC_REFERENCE) then
                    location.loc:=LOC_CREFERENCE;
+                 location.reference.alignment:=gvs.vardef.alignment;
                end;
              paravarsym,
              localvarsym :
@@ -401,7 +402,7 @@ implementation
                  if (vs.varspez=vs_const) and
                     (location.loc=LOC_REFERENCE) then
                    location.loc:=LOC_CREFERENCE;
-               end;
+              end;
             procsym:
                begin
                   if not assigned(procdef) then

+ 1 - 10
compiler/tgobj.pas

@@ -643,22 +643,13 @@ implementation
 
     procedure ttgobj.getlocal(list: TAsmList; size : longint; alignment : shortint; def:tdef;var ref : treference);
       begin
-{$ifdef arm}
-        { for ARM CPU records must be aligned in stack depending of record size           }
-        { to prevent misaligned error when the record is passed as parameter in registers }
-        if def.typ=recorddef then
-          if size>2 then
-            alignment:=current_settings.alignment.localalignmax
-          else
-            alignment:=size;
-{$else}
         alignment:=used_align(alignment,current_settings.alignment.localalignmin,current_settings.alignment.localalignmax);
-{$endif arm}
         { can't use reference_reset_base, because that will let tgobj depend
           on cgobj (PFV) }
         fillchar(ref,sizeof(ref),0);
         ref.base:=current_procinfo.framepointer;
         ref.offset:=alloctemp(list,size,alignment,tt_persistent,nil);
+        ref.alignment:=alignment;
       end;
 
 

+ 42 - 0
tests/test/cg/tpara3.pp

@@ -0,0 +1,42 @@
+type
+  TRec = record
+    bbb: array[1..8] of byte;
+    w: word;
+  end;
+
+  TRec2 = packed record
+    a: array[1..9] of char;
+  end;
+
+procedure dotest(p: TRec);
+var
+  i: longint;
+begin
+  for i:=1 to 8 do
+    write(p.bbb[i], ' ');
+  writeln;
+  if qword(p.bbb)<>$0102030405060708 then begin
+    writeln('Test FAILED.');
+    Halt(1);
+  end;
+end;
+
+procedure dotest2(p: TRec);
+var
+  rr: TRec2;
+  pp: TRec;
+begin
+  pp:=p;
+  dotest(pp);
+end;
+
+var
+  b: byte;
+  p: TRec;
+  i: longint;
+
+begin
+  qword(p.bbb):=$0102030405060708;
+  dotest2(p);
+  writeln('Test OK.');
+end.