mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-12 15:10:03 +02:00
* 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 -
This commit is contained in:
parent
43e0eb3cfd
commit
98be5b0825
@ -70,6 +70,8 @@ interface
|
|||||||
constructor op_reg_size_ref_size(op:tllvmop;dst:tregister;fromsize:tdef;const src:treference;tosize:tdef);
|
constructor op_reg_size_ref_size(op:tllvmop;dst:tregister;fromsize:tdef;const src:treference;tosize:tdef);
|
||||||
{ e.g. store fromsize src, ptrsize toref}
|
{ e.g. store fromsize src, ptrsize toref}
|
||||||
constructor op_size_reg_size_ref(op:tllvmop;fromsize:tdef;src:tregister;ptrsize:tdef;const toref:treference);
|
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}
|
{ e.g. store fromsize const, ptrsize toref}
|
||||||
constructor op_size_const_size_ref(op:tllvmop;fromsize:tdef;src:int64;ptrsize:tdef;const toref:treference);
|
constructor op_size_const_size_ref(op:tllvmop;fromsize:tdef;src:int64;ptrsize:tdef;const toref:treference);
|
||||||
{ e.g. dst = load fromsize fromref }
|
{ e.g. dst = load fromsize fromref }
|
||||||
@ -552,6 +554,17 @@ uses
|
|||||||
end;
|
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);
|
constructor taillvm.op_size_const_size_ref(op: tllvmop; fromsize: tdef; src: int64; ptrsize: tdef; const toref: treference);
|
||||||
begin
|
begin
|
||||||
create_llvm(op);
|
create_llvm(op);
|
||||||
|
@ -167,19 +167,83 @@ implementation
|
|||||||
end;
|
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);
|
procedure thlcgllvm.a_load_reg_ref(list: TAsmList; fromsize, tosize: tdef; register: tregister; const ref: treference);
|
||||||
var
|
var
|
||||||
|
tmpref,
|
||||||
sref: treference;
|
sref: treference;
|
||||||
hreg: tregister;
|
hreg,
|
||||||
|
hreg2: tregister;
|
||||||
|
tmpsize: tdef;
|
||||||
begin
|
begin
|
||||||
sref:=make_simple_ref(list,ref,tosize);
|
sref:=make_simple_ref(list,ref,tosize);
|
||||||
hreg:=register;
|
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
|
begin
|
||||||
hreg:=getregisterfordef(list,tosize);
|
hreg:=getregisterfordef(list,tosize);
|
||||||
a_load_reg_reg(list,fromsize,tosize,register,hreg);
|
a_load_reg_reg(list,fromsize,tosize,register,hreg);
|
||||||
end;
|
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;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -218,15 +282,80 @@ implementation
|
|||||||
|
|
||||||
procedure thlcgllvm.a_load_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; register: tregister);
|
procedure thlcgllvm.a_load_ref_reg(list: TAsmList; fromsize, tosize: tdef; const ref: treference; register: tregister);
|
||||||
var
|
var
|
||||||
|
tmpref,
|
||||||
sref: treference;
|
sref: treference;
|
||||||
hreg: tregister;
|
hreg: tregister;
|
||||||
|
tmpsize: tdef;
|
||||||
begin
|
begin
|
||||||
sref:=make_simple_ref(list,ref,fromsize);
|
sref:=make_simple_ref(list,ref,fromsize);
|
||||||
{ "named register"? }
|
{ "named register"? }
|
||||||
if sref.refaddr=addr_full then
|
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
|
else
|
||||||
begin
|
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;
|
hreg:=register;
|
||||||
if fromsize<>tosize then
|
if fromsize<>tosize then
|
||||||
hreg:=getregisterfordef(list,fromsize);
|
hreg:=getregisterfordef(list,fromsize);
|
||||||
|
Loading…
Reference in New Issue
Block a user