mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 20:27:56 +02:00
470 lines
13 KiB
ObjectPascal
470 lines
13 KiB
ObjectPascal
{
|
|
Copyright (c) 2008 by Jonas Maebe
|
|
|
|
Optimization information related to dead code removal
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
****************************************************************************
|
|
}
|
|
|
|
unit optdead;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
globtype,
|
|
cclasses,
|
|
wpobase;
|
|
|
|
type
|
|
|
|
{ twpodeadcodeinfo }
|
|
|
|
twpodeadcodeinfo = class(twpodeadcodehandler)
|
|
private
|
|
{ hashtable of symbols which are live }
|
|
fsymbols : tfphashlist;
|
|
|
|
procedure documentformat(writer: twposectionwriterintf);
|
|
public
|
|
constructor create; override;
|
|
destructor destroy; override;
|
|
|
|
class function getwpotype: twpotype; override;
|
|
class function generatesinfoforwposwitches: twpoptimizerswitches; override;
|
|
class function performswpoforswitches: twpoptimizerswitches; override;
|
|
class function sectionname: shortstring; override;
|
|
|
|
class procedure checkoptions; override;
|
|
|
|
{ information collection }
|
|
procedure storewpofilesection(writer: twposectionwriterintf); override;
|
|
|
|
{ information providing }
|
|
procedure loadfromwpofilesection(reader: twposectionreaderintf); override;
|
|
function symbolinfinalbinary(const s: shortstring): boolean;override;
|
|
|
|
end;
|
|
|
|
{ tdeadcodeinfofromexternallinker }
|
|
|
|
twpodeadcodeinfofromexternallinker = class(twpodeadcodeinfo)
|
|
private
|
|
|
|
fsymtypepos,
|
|
fsymnamepos : longint;
|
|
fsymfile : text;
|
|
fsymfilename : tcmdstr;
|
|
aixstrings : TDynStringArray;
|
|
fuseaixextractstrings : boolean;
|
|
function parselinenm(const line: ansistring): boolean;
|
|
function parselineobjdump(const line: ansistring): boolean;
|
|
public
|
|
class procedure checkoptions; override;
|
|
|
|
{ information collection }
|
|
procedure constructfromcompilerstate; override;
|
|
destructor destroy; override;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
cutils,cfileutl,
|
|
sysutils,
|
|
globals,systems,fmodule,
|
|
verbose;
|
|
|
|
|
|
const
|
|
SYMBOL_SECTION_NAME = 'live_symbols';
|
|
|
|
{ twpodeadcodeinfo }
|
|
|
|
constructor twpodeadcodeinfo.create;
|
|
begin
|
|
inherited create;
|
|
fsymbols:=tfphashlist.create;
|
|
end;
|
|
|
|
|
|
destructor twpodeadcodeinfo.destroy;
|
|
begin
|
|
fsymbols.free;
|
|
fsymbols:=nil;
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
class function twpodeadcodeinfo.getwpotype: twpotype;
|
|
begin
|
|
result:=wpo_live_symbol_information;
|
|
end;
|
|
|
|
|
|
class function twpodeadcodeinfo.generatesinfoforwposwitches: twpoptimizerswitches;
|
|
begin
|
|
result:=[cs_wpo_symbol_liveness];
|
|
end;
|
|
|
|
|
|
class function twpodeadcodeinfo.performswpoforswitches: twpoptimizerswitches;
|
|
begin
|
|
result:=[cs_wpo_symbol_liveness];
|
|
end;
|
|
|
|
|
|
class function twpodeadcodeinfo.sectionname: shortstring;
|
|
begin
|
|
result:=SYMBOL_SECTION_NAME;
|
|
end;
|
|
|
|
|
|
class procedure twpodeadcodeinfo.checkoptions;
|
|
begin
|
|
{ we don't have access to the symbol info if the linking
|
|
hasn't happend
|
|
}
|
|
if (([cs_link_on_target,cs_link_nolink] * init_settings.globalswitches) <> []) then
|
|
begin
|
|
cgmessage(wpo_cannot_extract_live_symbol_info_no_link);
|
|
exit;
|
|
end;
|
|
|
|
{ without dead code stripping/smart linking, this doesn't make sense }
|
|
if not(cs_link_smart in init_settings.globalswitches) then
|
|
begin
|
|
cgmessage(wpo_symbol_live_info_needs_smart_linking);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure twpodeadcodeinfo.documentformat(writer: twposectionwriterintf);
|
|
begin
|
|
writer.sectionputline('# section format:');
|
|
writer.sectionputline('# symbol1_that_is_live');
|
|
writer.sectionputline('# symbol2_that_is_live');
|
|
writer.sectionputline('# ...');
|
|
writer.sectionputline('#');
|
|
end;
|
|
|
|
|
|
procedure twpodeadcodeinfo.storewpofilesection(writer: twposectionwriterintf);
|
|
var
|
|
i: longint;
|
|
begin
|
|
writer.startsection(SYMBOL_SECTION_NAME);
|
|
documentformat(writer);
|
|
for i:=0 to fsymbols.count-1 do
|
|
writer.sectionputline(fsymbols.nameofindex(i));
|
|
end;
|
|
|
|
|
|
procedure twpodeadcodeinfo.loadfromwpofilesection(reader: twposectionreaderintf);
|
|
var
|
|
symname: shortstring;
|
|
begin
|
|
while reader.sectiongetnextline(symname) do
|
|
fsymbols.add(symname,pointer(1));
|
|
end;
|
|
|
|
|
|
function twpodeadcodeinfo.symbolinfinalbinary(const s: shortstring): boolean;
|
|
begin
|
|
result:=fsymbols.find(s)<>nil;
|
|
end;
|
|
|
|
|
|
{ twpodeadcodeinfofromexternallinker }
|
|
|
|
{$ifdef relaxed_objdump_parsing}
|
|
const
|
|
objdumpcheckstr='.text';
|
|
{$else}
|
|
const
|
|
objdumpcheckstr='F .text';
|
|
{$endif}
|
|
objdumpsearchstr=' '+objdumpcheckstr;
|
|
|
|
class procedure twpodeadcodeinfofromexternallinker.checkoptions;
|
|
begin
|
|
inherited checkoptions;
|
|
|
|
{ we need symbol information }
|
|
if (cs_link_strip in init_settings.globalswitches) then
|
|
begin
|
|
cgmessage(wpo_cannot_extract_live_symbol_info_strip);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
function twpodeadcodeinfofromexternallinker.parselinenm(const line: ansistring): boolean;
|
|
begin
|
|
if fuseaixextractstrings then
|
|
begin
|
|
result:=true;
|
|
if ExtractStrings([' ',#9],[],pchar(line),aixstrings)>=2 then
|
|
begin
|
|
if (length(aixstrings[1])=1) and
|
|
(aixstrings[1][1] in ['t','T']) and
|
|
(aixstrings[0][1]='.') then
|
|
fsymbols.add(copy(aixstrings[0],2,length(aixstrings[0])),pointer(1));
|
|
end;
|
|
setlength(aixstrings,0);
|
|
end
|
|
else
|
|
begin
|
|
if (length(line) < fsymnamepos) then
|
|
begin
|
|
cgmessage1(wpo_error_reading_symbol_file,'nm');
|
|
close(fsymfile);
|
|
deletefile(fsymfilename);
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
if (line[fsymtypepos] in ['T','t']) and
|
|
(not use_dotted_functions or
|
|
(line[fsymnamepos-1]='.')) then
|
|
fsymbols.add(copy(line,fsymnamepos,length(line)),pointer(1));
|
|
end;
|
|
result:=true;
|
|
end;
|
|
|
|
|
|
function twpodeadcodeinfofromexternallinker.parselineobjdump(const line: ansistring): boolean;
|
|
begin
|
|
{ there are a couple of empty lines at the end }
|
|
if (line='') then
|
|
begin
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
if (length(line) < fsymtypepos) then
|
|
begin
|
|
cgmessage1(wpo_error_reading_symbol_file,'objdump');
|
|
close(fsymfile);
|
|
deletefile(fsymfilename);
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
if (copy(line,fsymtypepos,length(objdumpcheckstr))=objdumpcheckstr) then
|
|
fsymbols.add(copy(line,fsymnamepos,length(line)),pointer(1));
|
|
result:=true;
|
|
end;
|
|
|
|
|
|
procedure twpodeadcodeinfofromexternallinker.constructfromcompilerstate;
|
|
|
|
type
|
|
tparselineproc = function(const line: ansistring): boolean of object;
|
|
|
|
var
|
|
nmfullname,
|
|
objdumpfullname,
|
|
symbolprogfullpath : tcmdstr;
|
|
line : ansistring;
|
|
parseline : tparselineproc;
|
|
exitcode : longint;
|
|
symbolprogfound : boolean;
|
|
symbolprogisnm : boolean;
|
|
|
|
|
|
function findutil(const utilname: string; out fullutilname, fullutilpath: tcmdstr): boolean;
|
|
begin
|
|
result:=false;
|
|
fullutilname:=utilsprefix+changefileext(utilname,source_info.exeext);
|
|
if utilsdirectory<>'' then
|
|
result:=findfile(fullutilname,utilsdirectory,false,fullutilpath);
|
|
if not result then
|
|
result:=findexe(fullutilname,false,fullutilpath);
|
|
end;
|
|
|
|
|
|
function failiferror(error: boolean): boolean;
|
|
begin
|
|
result:=error;
|
|
if not result then
|
|
exit;
|
|
cgmessage1(wpo_error_reading_symbol_file,symbolprogfullpath);
|
|
{$push}{$i-}
|
|
close(fsymfile);
|
|
{$pop}
|
|
if fileexists(fsymfilename) then
|
|
deletefile(fsymfilename);
|
|
end;
|
|
|
|
|
|
function setnminfo: boolean;
|
|
begin
|
|
{ expected format:
|
|
0000bce0 T FPC_ABSTRACTERROR
|
|
...
|
|
}
|
|
result:=false;
|
|
if (source_info.system in systems_aix) and
|
|
(target_info.system in systems_aix) then
|
|
begin
|
|
{ check for native aix nm:
|
|
.__start t 268435792 213
|
|
.__start T 268435792
|
|
}
|
|
if not(line[1] in ['0'..'9','a'..'f','A'..'F']) then
|
|
begin
|
|
fuseaixextractstrings:=true;
|
|
setlength(aixstrings,0);
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
fsymtypepos:=pos(' ',line)+1;
|
|
fsymnamepos:=fsymtypepos+2;
|
|
{ on Linux/ppc64, there is an extra '.' at the start
|
|
of public function names
|
|
}
|
|
if use_dotted_functions then
|
|
inc(fsymnamepos);
|
|
if failiferror(fsymtypepos<=0) then
|
|
exit;
|
|
{ make sure there's room for the name }
|
|
if failiferror(fsymnamepos>length(line)) then
|
|
exit;
|
|
result:=true;
|
|
end;
|
|
|
|
|
|
function setobjdumpinfo: boolean;
|
|
begin
|
|
{ expected format:
|
|
prog: file format elf32-i386
|
|
|
|
SYMBOL TABLE:
|
|
08048080 l d .text 00000000 .text
|
|
00000000 l d .stabstr 00000000 .stabstr
|
|
00000000 l df *ABS* 00000000 nest.pp
|
|
08048160 l F .text 00000068 SYSTEM_INITSYSCALLINTF
|
|
...
|
|
}
|
|
result:=false;
|
|
while (pos(objdumpsearchstr,line)<=0) do
|
|
begin
|
|
if failiferror(eof(fsymfile)) then
|
|
exit;
|
|
readln(fsymfile,line)
|
|
end;
|
|
fsymtypepos:=pos(objdumpsearchstr,line)+1;
|
|
{ find begin of symbol name }
|
|
fsymnamepos:=(pointer(strrscan(pchar(line),' '))-pointer(@line[1]))+2;
|
|
{ sanity check }
|
|
if (fsymnamepos <= fsymtypepos+length(objdumpcheckstr)) then
|
|
exit;
|
|
result:=true;
|
|
end;
|
|
|
|
|
|
begin { twpodeadcodeinfofromexternallinker }
|
|
objdumpfullname:='';
|
|
fuseaixextractstrings:=false;
|
|
{ gnu-nm (e.g., on solaris) }
|
|
symbolprogfound:=findutil('gnm',nmfullname,symbolprogfullpath);
|
|
{ regular nm }
|
|
if not symbolprogfound then
|
|
symbolprogfound:=findutil('nm',nmfullname,symbolprogfullpath);
|
|
if not symbolprogfound and
|
|
(target_info.system in systems_linux) then
|
|
begin
|
|
{ try objdump }
|
|
symbolprogfound:=findutil('objdump',objdumpfullname,symbolprogfullpath);
|
|
symbolprogfullpath:=symbolprogfullpath+' -t ';
|
|
symbolprogisnm:=false;
|
|
end
|
|
else
|
|
begin
|
|
symbolprogfullpath:=symbolprogfullpath+' -p ';
|
|
{ GNU nm shows 64 bit addresses when processing 32 bit binaries on
|
|
a 64 bit platform, but only skips 8 spaces for the address in case
|
|
of undefined symbols -> skip undefined symbols }
|
|
if target_info.system in (systems_linux+systems_windows) then
|
|
symbolprogfullpath:=symbolprogfullpath+'--defined-only ';
|
|
symbolprogisnm:=true;
|
|
end;
|
|
if not symbolprogfound then
|
|
begin
|
|
cgmessage2(wpo_cannot_find_symbol_progs,nmfullname,objdumpfullname);
|
|
exit;
|
|
end;
|
|
|
|
{ upper case to have the least chance of tripping some long file name
|
|
conversion stuff
|
|
}
|
|
fsymfilename:=outputexedir+'FPCWPO.SYM';
|
|
{ -p gives the same kind of output with Solaris nm as
|
|
with GNU nm, and for GNU nm it simply means "unsorted"
|
|
}
|
|
exitcode:=shell(symbolprogfullpath+maybequoted(current_module.exefilename)+' > '+fsymfilename);
|
|
if (exitcode<>0) then
|
|
begin
|
|
cgmessage2(wpo_error_executing_symbol_prog,symbolprogfullpath,tostr(exitcode));
|
|
if fileexists(fsymfilename) then
|
|
deletefile(fsymfilename);
|
|
exit;
|
|
end;
|
|
|
|
assign(fsymfile,fsymfilename);
|
|
{$push}{$i-}
|
|
reset(fsymfile);
|
|
{$pop}
|
|
if failiferror((ioresult<>0) or eof(fsymfile)) then
|
|
exit;
|
|
readln(fsymfile, line);
|
|
if (symbolprogisnm) then
|
|
begin
|
|
if not setnminfo then
|
|
exit;
|
|
parseline:=@parselinenm
|
|
end
|
|
else
|
|
begin
|
|
if not setobjdumpinfo then
|
|
exit;
|
|
parseline:=@parselineobjdump;
|
|
end;
|
|
if not parseline(line) then
|
|
exit;
|
|
while not eof(fsymfile) do
|
|
begin
|
|
readln(fsymfile,line);
|
|
if not parseline(line) then
|
|
exit;
|
|
end;
|
|
close(fsymfile);
|
|
deletefile(fsymfilename);
|
|
end;
|
|
|
|
|
|
destructor twpodeadcodeinfofromexternallinker.destroy;
|
|
begin
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
end.
|
|
|