2
0
Эх сурвалжийг харах

* correctly handle loads/stores of aggregate types if the source and
destination types are different: the pointer types of the load/store must
be converted in that case, rather than the loaded/to be stored value
(you can't bitcast aggregates in LLVM)

git-svn-id: branches/hlcgllvm@26987 -

Jonas Maebe 11 жил өмнө
parent
commit
98be5b0825

+ 13 - 0
compiler/llvm/aasmllvm.pas

@@ -70,6 +70,8 @@ interface
         constructor op_reg_size_ref_size(op:tllvmop;dst:tregister;fromsize:tdef;const src:treference;tosize:tdef);
         { e.g. store fromsize src, ptrsize toref}
         constructor op_size_reg_size_ref(op:tllvmop;fromsize:tdef;src:tregister;ptrsize:tdef;const toref:treference);
+        { e.g. store fromsize srcref, ptrsize toref (with srcref.refaddr=full) }
+        constructor op_size_ref_size_ref(op:tllvmop;fromsize:tdef;const src:treference;ptrsize:tdef;const toref:treference);
         { e.g. store fromsize const, ptrsize toref}
         constructor op_size_const_size_ref(op:tllvmop;fromsize:tdef;src:int64;ptrsize:tdef;const toref:treference);
         { e.g. dst = load fromsize fromref }
@@ -552,6 +554,17 @@ uses
       end;
 
 
+    constructor taillvm.op_size_ref_size_ref(op: tllvmop; fromsize: tdef; const src: treference; ptrsize: tdef; const toref: treference);
+      begin
+        create_llvm(op);
+        ops:=4;
+        loaddef(0,fromsize);
+        loadref(1,src);
+        loaddef(2,ptrsize);
+        loadref(3,toref);
+      end;
+
+
     constructor taillvm.op_size_const_size_ref(op: tllvmop; fromsize: tdef; src: int64; ptrsize: tdef; const toref: treference);
       begin
         create_llvm(op);

+ 133 - 4
compiler/llvm/hlcgllvm.pas

@@ -167,19 +167,83 @@ implementation
     end;
 
 
+  function def2intdef(fromsize, tosize: tdef): tdef;
+    begin
+      { we cannot zero-extend from/to anything but ordinal/enum
+        types }
+      if not(tosize.typ in [orddef,enumdef]) then
+        internalerror(2014012305);
+      { will give an internalerror if def_cgsize() returns OS_NO, which is
+        what we want }
+      result:=cgsize_orddef(def_cgsize(fromsize));
+    end;
+
+
   procedure thlcgllvm.a_load_reg_ref(list: TAsmList; fromsize, tosize: tdef; register: tregister; const ref: treference);
     var
+      tmpref,
       sref: treference;
-      hreg: tregister;
+      hreg,
+      hreg2: tregister;
+      tmpsize: tdef;
     begin
       sref:=make_simple_ref(list,ref,tosize);
       hreg:=register;
-      if fromsize.size<>tosize.size then
+      (* typecast the pointer to the value instead of the value itself if
+        they have the same size but are of different kinds, because we can't
+        e.g. typecast a loaded <{i32, i32}> to an i64 *)
+      if (llvmaggregatetype(fromsize) or
+          llvmaggregatetype(tosize)) and
+         (fromsize<>tosize) then
+        begin
+          if fromsize.size>tosize.size then
+            begin
+              { if source size is larger than the target size, we have to
+                truncate it before storing. Unfortunately, we cannot truncate
+                records (nor bitcast them to integers), so we first have to
+                store them to memory and then bitcast the pointer to them
+              }
+              if fromsize.typ in [arraydef,recorddef] then
+                begin
+                  { store struct/array-in-register to memory }
+                  tmpsize:=def2intdef(fromsize,tosize);
+                  tg.gethltemp(list,fromsize,fromsize.size,tt_normal,tmpref);
+                  a_load_reg_ref(list,fromsize,fromsize,register,tmpref);
+                  { typecast pointer to memory into pointer to integer type }
+                  hreg:=getaddressregister(list,getpointerdef(tmpsize));
+                  a_loadaddr_ref_reg(list,fromsize,getpointerdef(tmpsize),tmpref,hreg);
+                  reference_reset_base(sref,hreg,0,tmpref.alignment);
+                  { load the integer from the temp into the destination }
+                  a_load_ref_ref(list,tmpsize,tosize,tmpref,sref);
+                  tg.ungettemp(list,tmpref);
+                end
+              else
+                begin
+                  tmpsize:=def2intdef(tosize,fromsize);
+                  hreg:=getintregister(list,tmpsize);
+                  { truncate the integer }
+                  a_load_reg_reg(list,fromsize,tmpsize,register,hreg);
+                  { store it to memory (it will now be of the same size as the
+                    struct, and hence another path will be followed in this
+                    method) }
+                  a_load_reg_ref(list,tmpsize,tosize,hreg,sref);
+                end;
+                exit;
+            end
+          else
+            begin
+              hreg2:=getaddressregister(list,getpointerdef(fromsize));
+              a_loadaddr_ref_reg(list,tosize,getpointerdef(fromsize),sref,hreg2);
+              reference_reset_base(sref,hreg2,0,sref.alignment);
+              tosize:=fromsize;
+            end;
+        end
+      else if fromsize<>tosize then
         begin
           hreg:=getregisterfordef(list,tosize);
           a_load_reg_reg(list,fromsize,tosize,register,hreg);
         end;
-      list.concat(taillvm.op_size_reg_size_ref(la_store,tosize,hreg,getpointerdef(tosize),sref))
+      list.concat(taillvm.op_size_reg_size_ref(la_store,tosize,hreg,getpointerdef(tosize),sref));
     end;
 
 
@@ -218,15 +282,80 @@ implementation
 
   procedure thlcgllvm.a_load_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; register: tregister);
     var
+      tmpref,
       sref: treference;
       hreg: tregister;
+      tmpsize: tdef;
     begin
       sref:=make_simple_ref(list,ref,fromsize);
       { "named register"? }
       if sref.refaddr=addr_full then
-        list.concat(taillvm.op_reg_size_ref_size(la_bitcast,register,fromsize,sref,tosize))
+        begin
+          { can't bitcast records/arrays }
+          if (llvmaggregatetype(fromsize) or
+              llvmaggregatetype(tosize)) and
+             (fromsize<>tosize) then
+            begin
+              tg.gethltemp(list,fromsize,fromsize.size,tt_normal,tmpref);
+              list.concat(taillvm.op_size_ref_size_ref(la_store,fromsize,sref,getpointerdef(fromsize),tmpref));
+              a_load_ref_reg(list,fromsize,tosize,tmpref,register);
+              tg.ungettemp(list,tmpref);
+            end
+          else
+            list.concat(taillvm.op_reg_size_ref_size(la_bitcast,register,fromsize,sref,tosize))
+        end
       else
         begin
+          if ((fromsize.typ in [arraydef,recorddef]) or
+              (tosize.typ in [arraydef,recorddef])) and
+             (fromsize<>tosize) then
+            begin
+              if fromsize.size<tosize.size then
+                begin
+                  { if the target size is larger than the source size, we
+                    have to perform the zero-extension using an integer type
+                    (can't zero-extend a record/array) }
+                  if fromsize.typ in [arraydef,recorddef] then
+                    begin
+                      { typecast the pointer to the struct into a pointer to an
+                        integer of equal size }
+                      tmpsize:=def2intdef(fromsize,tosize);
+                      hreg:=getaddressregister(list,getpointerdef(tmpsize));
+                      a_loadaddr_ref_reg(list,fromsize,getpointerdef(tmpsize),sref,hreg);
+                      reference_reset_base(sref,hreg,0,sref.alignment);
+                      { load that integer }
+                      a_load_ref_reg(list,tmpsize,tosize,sref,register);
+                    end
+                  else
+                    begin
+                      { load the integer into an integer memory location with
+                        the same size as the struct (the integer should be
+                        unsigned, we don't want sign extensions here) }
+                      if is_signed(fromsize) then
+                        internalerror(2014012309);
+                      tmpsize:=def2intdef(tosize,fromsize);
+                      tg.gethltemp(list,tmpsize,tmpsize.size,tt_normal,tmpref);
+                      { typecast the struct-sized integer location into the
+                        struct type }
+                      a_load_ref_ref(list,fromsize,tmpsize,sref,tmpref);
+                      { load the struct in the register }
+                      a_load_ref_reg(list,tmpsize,tosize,tmpref,register);
+                      tg.ungettemp(list,tmpref);
+                    end;
+                  exit;
+                end
+              else
+                begin
+                  (* typecast the pointer to the value instead of the value
+                     itself if they have the same size but are of different
+                     kinds, because we can't e.g. typecast a loaded <{i32, i32}>
+                     to an i64 *)
+                  hreg:=getaddressregister(list,getpointerdef(tosize));
+                  a_loadaddr_ref_reg(list,fromsize,getpointerdef(tosize),sref,hreg);
+                  reference_reset_base(sref,hreg,0,sref.alignment);
+                  fromsize:=tosize;
+                end;
+            end;
           hreg:=register;
           if fromsize<>tosize then
             hreg:=getregisterfordef(list,fromsize);