* fixed llvm shadow symtable construction in case fields are reordered

(-Ooorderfields) and hence their offsets are no longer monotonically rising
  * also fixed padding for variant bitpacked records in llvm shadow symtable

git-svn-id: trunk@47853 -
This commit is contained in:
Jonas Maebe 2020-12-27 13:18:44 +00:00
parent 98d3473a1a
commit c0c0acbcb9

View File

@ -197,10 +197,10 @@ interface
procedure generate; procedure generate;
// helpers // helpers
procedure appenddefoffset(vardef:tdef; fieldoffset: aint; derefclass: boolean); procedure appenddefoffset(vardef:tdef; fieldoffset: aint; derefclass: boolean);
procedure findvariantstarts(variantstarts: tfplist); procedure preprocess(out tempsymlist, variantstarts: tfplist);
procedure addalignmentpadding(finalsize: aint); procedure addalignmentpadding(finalsize: aint);
procedure buildmapping(variantstarts: tfplist); procedure buildmapping(tempsymlist, variantstarts: tfplist);
procedure buildtable(variantstarts: tfplist); procedure buildtable(tempsymlist, variantstarts: tfplist);
end; end;
{$endif llvm} {$endif llvm}
@ -1425,12 +1425,18 @@ implementation
changed: boolean; changed: boolean;
begin begin
if maybereorder and if maybereorder and
(cs_opt_reorder_fields in current_settings.optimizerswitches) then (cs_opt_reorder_fields in current_settings.optimizerswitches) and
(list.count>1) then
begin begin
{ assign dummy field offsets so we can know their order in the { assign dummy field offsets so we can know their order in the
sorting routine } sorting routine }
for i:=0 to list.count-1 do for i:=0 to list.count-1 do
tfieldvarsym(list[i]).fieldoffset:=i; begin
fieldvs:=tfieldvarsym(list[i]);
if sp_static in fieldvs.symoptions then
continue;
fieldvs.fieldoffset:=i;
end;
{ sort the non-class fields to minimise losses due to alignment } { sort the non-class fields to minimise losses due to alignment }
list.sort(@field_alignment_compare); list.sort(@field_alignment_compare);
{ now fill up gaps caused by alignment skips with smaller fields { now fill up gaps caused by alignment skips with smaller fields
@ -1526,7 +1532,12 @@ implementation
end; end;
{ reset the dummy field offsets } { reset the dummy field offsets }
for i:=0 to list.count-1 do for i:=0 to list.count-1 do
tfieldvarsym(list[i]).fieldoffset:=-1; begin
fieldvs:=tfieldvarsym(list[i]);
if sp_static in fieldvs.symoptions then
continue;
fieldvs.fieldoffset:=-1;
end;
{ finally, set the actual field offsets } { finally, set the actual field offsets }
for i:=0 to list.count-1 do for i:=0 to list.count-1 do
begin begin
@ -2118,31 +2129,42 @@ implementation
procedure tllvmshadowsymtable.addalignmentpadding(finalsize: aint); procedure tllvmshadowsymtable.addalignmentpadding(finalsize: aint);
begin begin
case equivst.usefieldalignment of if not(df_llvm_no_struct_packing in tdef(equivst.defowner).defoptions) then
{ already correct in this case } begin
bit_alignment: if equivst.usefieldalignment=bit_alignment then
; curroffset:=align(curroffset,8) div 8;
else if not(df_llvm_no_struct_packing in tdef(equivst.defowner).defoptions) then { add padding fields }
begin while (finalsize>curroffset) do
{ add padding fields } begin
while (finalsize>curroffset) do symdeflist.add(tllvmshadowsymtableentry.create(u8inttype,curroffset));
begin inc(curroffset);
symdeflist.add(tllvmshadowsymtableentry.create(u8inttype,curroffset)); end;
inc(curroffset); end;
end;
end;
end;
end; end;
procedure tllvmshadowsymtable.findvariantstarts(variantstarts: tfplist); function field_offset_compare(item1, item2: pointer): integer;
var var
sym: tfieldvarsym; field1: tfieldvarsym absolute item1;
lastoffset: aint; field2: tfieldvarsym absolute item2;
begin
result:=field1.fieldoffset-field2.fieldoffset;
end;
procedure tllvmshadowsymtable.preprocess(out tempsymlist, variantstarts: tfplist);
var
fieldvs: tfieldvarsym;
lastvariantstartoffset, prevfieldoffset: aint;
newalignment: aint; newalignment: aint;
i, j: longint; i, j: longint;
sorttempsymlist: boolean;
begin begin
i:=0; i:=0;
variantstarts:=nil;
tempsymlist:=tfplist.create;
sorttempsymlist:=false;
prevfieldoffset:=-1;
while (i<equivst.symlist.count) do while (i<equivst.symlist.count) do
begin begin
if not is_normal_fieldvarsym(tsym(equivst.symlist[i])) then if not is_normal_fieldvarsym(tsym(equivst.symlist[i])) then
@ -2150,38 +2172,42 @@ implementation
inc(i); inc(i);
continue; continue;
end; end;
sym:=tfieldvarsym(equivst.symlist[i]); fieldvs:=tfieldvarsym(equivst.symlist[i]);
tempsymlist.Add(fieldvs);
{ a "better" algorithm might be to use the largest } { a "better" algorithm might be to use the largest }
{ variant in case of (bit)packing, since then } { variant in case of (bit)packing, since then }
{ alignment doesn't matter } { alignment doesn't matter }
if (vo_is_first_field in sym.varoptions) then if (vo_is_first_field in fieldvs.varoptions) then
begin begin
{ we assume that all fields are processed in order. } { we assume that all fields are processed in order. }
if (variantstarts.count<>0) then if assigned(variantstarts) then
lastoffset:=tfieldvarsym(variantstarts[variantstarts.count-1]).fieldoffset lastvariantstartoffset:=tfieldvarsym(variantstarts[variantstarts.count-1]).fieldoffset
else else
lastoffset:=-1; begin
lastvariantstartoffset:=-1;
variantstarts:=tfplist.create;
end;
{ new variant at same level as last one: use if higher alignment } { new variant at same level as last one: use if higher alignment }
if (lastoffset=sym.fieldoffset) then if (lastvariantstartoffset=fieldvs.fieldoffset) then
begin begin
if (equivst.fieldalignment<>bit_alignment) then if (equivst.usefieldalignment<>bit_alignment) then
newalignment:=used_align(sym.vardef.alignment,equivst.recordalignmin,equivst.fieldalignment) newalignment:=used_align(fieldvs.vardef.alignment,equivst.recordalignmin,equivst.fieldalignment)
else else
newalignment:=1; newalignment:=1;
if (newalignment>tfieldvarsym(variantstarts[variantstarts.count-1]).vardef.alignment) then if (newalignment>tfieldvarsym(variantstarts[variantstarts.count-1]).vardef.alignment) then
variantstarts[variantstarts.count-1]:=sym; variantstarts[variantstarts.count-1]:=fieldvs;
end end
{ variant at deeper level than last one -> add } { variant at deeper level than last one -> add }
else if (lastoffset<sym.fieldoffset) then else if (lastvariantstartoffset<fieldvs.fieldoffset) then
variantstarts.add(sym) variantstarts.add(fieldvs)
else else
begin begin
{ a variant at a less deep level, so backtrack } { a variant at a less deep level, so backtrack }
j:=variantstarts.count-2; j:=variantstarts.count-2;
while (j>=0) do while (j>=0) do
begin begin
if (tfieldvarsym(variantstarts[j]).fieldoffset=sym.fieldoffset) then if (tfieldvarsym(variantstarts[j]).fieldoffset=fieldvs.fieldoffset) then
break; break;
dec(j); dec(j);
end; end;
@ -2189,13 +2215,13 @@ implementation
internalerror(2008051003); internalerror(2008051003);
{ new variant has higher alignment? } { new variant has higher alignment? }
if (equivst.fieldalignment<>bit_alignment) then if (equivst.fieldalignment<>bit_alignment) then
newalignment:=used_align(sym.vardef.alignment,equivst.recordalignmin,equivst.fieldalignment) newalignment:=used_align(fieldvs.vardef.alignment,equivst.recordalignmin,equivst.fieldalignment)
else else
newalignment:=1; newalignment:=1;
{ yes, replace and remove previous nested variants } { yes, replace and remove previous nested variants }
if (newalignment>tfieldvarsym(variantstarts[j]).vardef.alignment) then if (newalignment>tfieldvarsym(variantstarts[j]).vardef.alignment) then
begin begin
variantstarts[j]:=sym; variantstarts[j]:=fieldvs;
variantstarts.count:=j+1; variantstarts.count:=j+1;
end end
{ no, skip this variant } { no, skip this variant }
@ -2204,91 +2230,95 @@ implementation
inc(i); inc(i);
while (i<equivst.symlist.count) and while (i<equivst.symlist.count) and
(not is_normal_fieldvarsym(tsym(equivst.symlist[i])) or (not is_normal_fieldvarsym(tsym(equivst.symlist[i])) or
(tfieldvarsym(equivst.symlist[i]).fieldoffset>sym.fieldoffset)) do (tfieldvarsym(equivst.symlist[i]).fieldoffset>fieldvs.fieldoffset)) do
inc(i); begin
if is_normal_fieldvarsym(tsym(equivst.symlist[i])) then
tempsymlist.Add(equivst.symlist[i]);
inc(i);
end;
continue; continue;
end; end;
end; end;
end; end;
if not assigned(variantstarts) and
(fieldvs.fieldoffset<prevfieldoffset) then
sorttempsymlist:=true;
prevfieldoffset:=fieldvs.fieldoffset;
inc(i); inc(i);
end; end;
if sorttempsymlist then
tempsymlist.Sort(@field_offset_compare);
end; end;
procedure tllvmshadowsymtable.buildtable(variantstarts: tfplist); procedure tllvmshadowsymtable.buildtable(tempsymlist, variantstarts: tfplist);
var var
lastvaroffsetprocessed: aint; lastvaroffsetprocessed: aint;
i, equivcount, varcount: longint; i, symcount, varcount: longint;
fieldvs: tfieldvarsym;
begin begin
{ if it's an object/class, the first entry is the parent (if there is one) } { if it's an object/class, the first entry is the parent (if there is one) }
if (equivst.symtabletype=objectsymtable) and if (equivst.symtabletype=objectsymtable) and
assigned(tobjectdef(equivst.defowner).childof) then assigned(tobjectdef(equivst.defowner).childof) then
appenddefoffset(tobjectdef(equivst.defowner).childof,0,is_class_or_interface_or_dispinterface(tobjectdef(equivst.defowner).childof)); appenddefoffset(tobjectdef(equivst.defowner).childof,0,is_class_or_interface_or_dispinterface(tobjectdef(equivst.defowner).childof));
equivcount:=equivst.symlist.count; symcount:=tempsymlist.count;
varcount:=0; varcount:=0;
i:=0; i:=0;
lastvaroffsetprocessed:=-1; lastvaroffsetprocessed:=-1;
while (i<equivcount) do while (i<symcount) do
begin begin
if not is_normal_fieldvarsym(tsym(equivst.symlist[i])) then fieldvs:=tfieldvarsym(tempsymlist[i]);
begin
inc(i);
continue;
end;
{ start of a new variant? } { start of a new variant? }
if (vo_is_first_field in tfieldvarsym(equivst.symlist[i]).varoptions) then if (vo_is_first_field in fieldvs.varoptions) then
begin begin
{ if we want to process the same variant offset twice, it means that we } { if we want to process the same variant offset twice, it means that we }
{ got to the end and are trying to process the next variant part -> stop } { got to the end and are trying to process the next variant part -> stop }
if (tfieldvarsym(equivst.symlist[i]).fieldoffset<=lastvaroffsetprocessed) then if (fieldvs.fieldoffset<=lastvaroffsetprocessed) then
break; break;
if (varcount>=variantstarts.count) then if (varcount>=variantstarts.count) then
internalerror(2008051005); internalerror(2008051005);
{ new variant part -> use the one with the biggest alignment } { new variant part -> use the one with the biggest alignment }
i:=equivst.symlist.indexof(tobject(variantstarts[varcount])); i:=tempsymlist.indexof(tobject(variantstarts[varcount]));
lastvaroffsetprocessed:=tfieldvarsym(equivst.symlist[i]).fieldoffset; lastvaroffsetprocessed:=fieldvs.fieldoffset;
inc(varcount); inc(varcount);
if (i<0) then if (i<0) then
internalerror(2008051004); internalerror(2008051004);
end; end;
appenddefoffset(tfieldvarsym(equivst.symlist[i]).vardef,tfieldvarsym(equivst.symlist[i]).fieldoffset,false); appenddefoffset(fieldvs.vardef,fieldvs.fieldoffset,false);
inc(i); inc(i);
end; end;
addalignmentpadding(equivst.datasize); addalignmentpadding(equivst.datasize);
end; end;
procedure tllvmshadowsymtable.buildmapping(variantstarts: tfplist); procedure tllvmshadowsymtable.buildmapping(tempsymlist, variantstarts: tfplist);
var var
fieldvs: tfieldvarsym;
i, varcount: longint; i, varcount: longint;
shadowindex: longint; shadowindex: longint;
equivcount : longint; symcount : longint;
begin begin
varcount:=0; varcount:=0;
shadowindex:=0; shadowindex:=0;
equivcount:=equivst.symlist.count; symcount:=tempsymlist.count;
i:=0; i:=0;
while (i < equivcount) do while (i<symcount) do
begin begin
if not is_normal_fieldvarsym(tsym(equivst.symlist[i])) then fieldvs:=tfieldvarsym(tempsymlist[i]);
begin
inc(i);
continue;
end;
{ start of a new variant? } { start of a new variant? }
if (vo_is_first_field in tfieldvarsym(equivst.symlist[i]).varoptions) then if (vo_is_first_field in fieldvs.varoptions) then
begin begin
{ back up to a less deeply nested variant level? } { back up to a less deeply nested variant level? }
while (tfieldvarsym(equivst.symlist[i]).fieldoffset<tfieldvarsym(variantstarts[varcount]).fieldoffset) do while fieldvs.fieldoffset<tfieldvarsym(variantstarts[varcount]).fieldoffset do
dec(varcount); dec(varcount);
{ it's possible that some variants are more deeply nested than the { it's possible that some variants are more deeply nested than the
one we recorded in the shadowsymtable (since we recorded the one one we recorded in the shadowsymtable (since we recorded the one
with the biggest alignment, not necessarily the biggest one in size with the biggest alignment, not necessarily the biggest one in size
} }
if (tfieldvarsym(equivst.symlist[i]).fieldoffset>tfieldvarsym(variantstarts[varcount]).fieldoffset) then if fieldvs.fieldoffset>tfieldvarsym(variantstarts[varcount]).fieldoffset then
varcount:=variantstarts.count-1 varcount:=variantstarts.count-1
else if (tfieldvarsym(equivst.symlist[i]).fieldoffset<>tfieldvarsym(variantstarts[varcount]).fieldoffset) then else if fieldvs.fieldoffset<>tfieldvarsym(variantstarts[varcount]).fieldoffset then
internalerror(2008051006); internalerror(2008051006);
{ reset the shadowindex to the start of this variant. } { reset the shadowindex to the start of this variant. }
{ in case the llvmfieldnr is not (yet) set for this } { in case the llvmfieldnr is not (yet) set for this }
@ -2300,15 +2330,15 @@ implementation
end; end;
{ find the last shadowfield whose offset <= the current field's offset } { find the last shadowfield whose offset <= the current field's offset }
while (tllvmshadowsymtableentry(symdeflist[shadowindex]).fieldoffset<tfieldvarsym(equivst.symlist[i]).fieldoffset) and while (tllvmshadowsymtableentry(symdeflist[shadowindex]).fieldoffset<fieldvs.fieldoffset) and
(shadowindex<symdeflist.count-1) and (shadowindex<symdeflist.count-1) and
(tllvmshadowsymtableentry(symdeflist[shadowindex+1]).fieldoffset<=tfieldvarsym(equivst.symlist[i]).fieldoffset) do (tllvmshadowsymtableentry(symdeflist[shadowindex+1]).fieldoffset<=fieldvs.fieldoffset) do
inc(shadowindex); inc(shadowindex);
{ set the field number and potential offset from that field (in case } { set the field number and potential offset from that field (in case }
{ of overlapping variants) } { of overlapping variants) }
tfieldvarsym(equivst.symlist[i]).llvmfieldnr:=shadowindex; fieldvs.llvmfieldnr:=shadowindex;
tfieldvarsym(equivst.symlist[i]).offsetfromllvmfield:= fieldvs.offsetfromllvmfield:=
tfieldvarsym(equivst.symlist[i]).fieldoffset-tllvmshadowsymtableentry(symdeflist[shadowindex]).fieldoffset; fieldvs.fieldoffset-tllvmshadowsymtableentry(symdeflist[shadowindex]).fieldoffset;
inc(i); inc(i);
end; end;
end; end;
@ -2316,23 +2346,22 @@ implementation
procedure tllvmshadowsymtable.generate; procedure tllvmshadowsymtable.generate;
var var
variantstarts: tfplist; variantstarts, tempsymlist: tfplist;
begin begin
variantstarts:=tfplist.create;
{ first go through the entire record and } { first go through the entire record and }
{ store the fieldvarsyms of the variants } { store the fieldvarsyms of the variants }
{ with the highest alignment } { with the highest alignment }
findvariantstarts(variantstarts); preprocess(tempsymlist, variantstarts);
{ now go through the regular fields and the selected variants, } { now go through the regular fields and the selected variants, }
{ and add them to the llvm shadow record symtable } { and add them to the llvm shadow record symtable }
buildtable(variantstarts); buildtable(tempsymlist, variantstarts);
{ finally map all original fields to the llvm definition } { finally map all original fields to the llvm definition }
buildmapping(variantstarts); buildmapping(tempsymlist, variantstarts);
variantstarts.free; variantstarts.free;
tempsymlist.free;
end; end;
{$endif llvm} {$endif llvm}