fpc/compiler/optdead.pas
florian b1dff29cbf * removed unused units
git-svn-id: trunk@36165 -
2017-05-09 19:53:14 +00:00

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.