mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-06 00:28:05 +02:00
2706 lines
93 KiB
ObjectPascal
2706 lines
93 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2004 by Peter Vreman
|
|
|
|
This unit handles the assemblerfile write and assembler calls of FPC
|
|
|
|
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.
|
|
|
|
****************************************************************************
|
|
}
|
|
{# @abstract(This unit handles the assembler file write and assembler calls of FPC)
|
|
Handles the calls to the actual external assemblers, as well as the generation
|
|
of object files for smart linking. Also contains the base class for writing
|
|
the assembler statements to file.
|
|
}
|
|
unit assemble;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
|
|
uses
|
|
SysUtils,
|
|
systems,globtype,globals,aasmbase,aasmtai,aasmdata,ogbase,owbase,finput;
|
|
|
|
const
|
|
{ maximum of aasmoutput lists there will be }
|
|
maxoutputlists = ord(high(tasmlisttype))+1;
|
|
{ buffer size for writing the .s file }
|
|
AsmOutSize=32768*4;
|
|
|
|
type
|
|
TAssembler=class(TObject)
|
|
public
|
|
{assembler info}
|
|
asminfo : pasminfo;
|
|
{filenames}
|
|
path : TPathStr;
|
|
name : string;
|
|
AsmFileName, { current .s and .o file }
|
|
ObjFileName,
|
|
ppufilename : TPathStr;
|
|
asmprefix : string;
|
|
SmartAsm : boolean;
|
|
SmartFilesCount,
|
|
SmartHeaderCount : longint;
|
|
Constructor Create(info: pasminfo; smart:boolean);virtual;
|
|
Destructor Destroy;override;
|
|
procedure NextSmartName(place:tcutplace);
|
|
procedure MakeObject;virtual;abstract;
|
|
end;
|
|
|
|
TExternalAssembler = class;
|
|
|
|
IExternalAssemblerOutputFileDecorator=interface
|
|
function LinePrefix: AnsiString;
|
|
function LinePostfix: AnsiString;
|
|
function LineFilter(const s: AnsiString): AnsiString;
|
|
function LineEnding(const deflineending: ShortString): ShortString;
|
|
end;
|
|
|
|
TExternalAssemblerOutputFile=class
|
|
private
|
|
fdecorator: IExternalAssemblerOutputFileDecorator;
|
|
protected
|
|
owner: TExternalAssembler;
|
|
{outfile}
|
|
AsmSize,
|
|
AsmStartSize,
|
|
outcnt : longint;
|
|
outbuf : array[0..AsmOutSize-1] of char;
|
|
outfile : file;
|
|
fioerror : boolean;
|
|
linestart: boolean;
|
|
|
|
Procedure AsmClear;
|
|
Procedure MaybeAddLinePrefix;
|
|
Procedure MaybeAddLinePostfix;
|
|
|
|
Procedure AsmWriteAnsiStringUnfiltered(const s: ansistring);
|
|
public
|
|
Constructor Create(_owner: TExternalAssembler);
|
|
|
|
Procedure RemoveAsm;virtual;
|
|
Procedure AsmFlush;
|
|
|
|
{ mark the current output as the "empty" state (i.e., it only contains
|
|
headers/directives etc }
|
|
Procedure MarkEmpty;
|
|
{ clears the assembler output if nothing was added since it was marked
|
|
as empty, and returns whether it was empty }
|
|
function ClearIfEmpty: boolean;
|
|
{ these routines will write the filtered version of their argument
|
|
according to the current decorator }
|
|
procedure AsmWriteFiltered(const c:char);
|
|
procedure AsmWriteFiltered(const s:string);
|
|
procedure AsmWriteFiltered(const s:ansistring);
|
|
procedure AsmWriteFiltered(p:pchar; len: longint);
|
|
|
|
{# Write a string to the assembler file }
|
|
Procedure AsmWrite(const c:char);
|
|
Procedure AsmWrite(const s:string);
|
|
Procedure AsmWrite(const s:ansistring);
|
|
|
|
{# Write a string to the assembler file }
|
|
Procedure AsmWritePChar(p:pchar);
|
|
|
|
{# Write a string to the assembler file followed by a new line }
|
|
Procedure AsmWriteLn(const c:char);
|
|
Procedure AsmWriteLn(const s:string);
|
|
Procedure AsmWriteLn(const s:ansistring);
|
|
|
|
{# Write a new line to the assembler file }
|
|
Procedure AsmLn; virtual;
|
|
|
|
procedure AsmCreate(Aplace:tcutplace);
|
|
procedure AsmClose;
|
|
|
|
property ioerror: boolean read fioerror;
|
|
property decorator: IExternalAssemblerOutputFileDecorator read fdecorator write fdecorator;
|
|
end;
|
|
|
|
{# This is the base class which should be overridden for each each
|
|
assembler writer. It is used to actually assembler a file,
|
|
and write the output to the assembler file.
|
|
}
|
|
TExternalAssembler=class(TAssembler)
|
|
private
|
|
{ output writer }
|
|
fwriter: TExternalAssemblerOutputFile;
|
|
ffreewriter: boolean;
|
|
|
|
procedure CreateSmartLinkPath(const s:TPathStr);
|
|
protected
|
|
{input source info}
|
|
lastfileinfo : tfileposinfo;
|
|
infile,
|
|
lastinfile : tinputfile;
|
|
{last section type written}
|
|
lastsectype : TAsmSectionType;
|
|
procedure WriteSourceLine(hp: tailineinfo);
|
|
procedure WriteTempalloc(hp: tai_tempalloc);
|
|
procedure WriteRealConstAsBytes(hp: tai_realconst; const dbdir: string; do_line: boolean);
|
|
function WriteComments(var hp: tai): boolean;
|
|
function single2str(d : single) : string; virtual;
|
|
function double2str(d : double) : string; virtual;
|
|
function extended2str(e : extended) : string; virtual;
|
|
function sleb128tostr(a : int64) : string;
|
|
function uleb128tostr(a : qword) : string;
|
|
Function DoPipe:boolean; virtual;
|
|
|
|
function CreateNewAsmWriter: TExternalAssemblerOutputFile; virtual;
|
|
|
|
{# Return true if the external assembler should run again }
|
|
function RerunAssembler: boolean; virtual;
|
|
public
|
|
|
|
{# Returns the complete path and executable name of the assembler
|
|
program.
|
|
|
|
It first tries looking in the UTIL directory if specified,
|
|
otherwise it searches in the free pascal binary directory, in
|
|
the current working directory and then in the directories
|
|
in the $PATH environment.}
|
|
Function FindAssembler:string;
|
|
|
|
{# Actually does the call to the assembler file. Returns false
|
|
if the assembling of the file failed.}
|
|
Function CallAssembler(const command:string; const para:TCmdStr):Boolean;
|
|
|
|
Function DoAssemble:boolean;virtual;
|
|
|
|
{# This routine should be overridden for each assembler, it is used
|
|
to actually write the abstract assembler stream to file.}
|
|
procedure WriteTree(p:TAsmList);virtual;
|
|
|
|
{# This routine should be overridden for each assembler, it is used
|
|
to actually write all the different abstract assembler streams
|
|
by calling for each stream type, the @var(WriteTree) method.}
|
|
procedure WriteAsmList;virtual;
|
|
|
|
{# Constructs the command line for calling the assembler }
|
|
function MakeCmdLine: TCmdStr; virtual;
|
|
public
|
|
Constructor Create(info: pasminfo; smart: boolean); override; final;
|
|
Constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); virtual;
|
|
procedure MakeObject;override;
|
|
destructor Destroy; override;
|
|
|
|
property writer: TExternalAssemblerOutputFile read fwriter;
|
|
end;
|
|
TExternalAssemblerClass = class of TExternalAssembler;
|
|
|
|
{ TInternalAssembler }
|
|
|
|
TInternalAssembler=class(TAssembler)
|
|
private
|
|
{$ifdef ARM}
|
|
{ true, if thumb instructions are generated }
|
|
Code16 : Boolean;
|
|
{$endif ARM}
|
|
FCObjOutput : TObjOutputclass;
|
|
FCInternalAr : TObjectWriterClass;
|
|
{ the aasmoutput lists that need to be processed }
|
|
lists : byte;
|
|
list : array[1..maxoutputlists] of TAsmList;
|
|
{ current processing }
|
|
currlistidx : byte;
|
|
currlist : TAsmList;
|
|
procedure WriteStab(p:pchar);
|
|
function MaybeNextList(var hp:Tai):boolean;
|
|
function SetIndirectToSymbol(hp: Tai; const indirectname: string): Boolean;
|
|
function TreePass0(hp:Tai):Tai;
|
|
function TreePass1(hp:Tai):Tai;
|
|
function TreePass2(hp:Tai):Tai;
|
|
procedure writetree;
|
|
procedure writetreesmart;
|
|
protected
|
|
ObjData : TObjData;
|
|
ObjOutput : tObjOutput;
|
|
property CObjOutput:TObjOutputclass read FCObjOutput write FCObjOutput;
|
|
property CInternalAr : TObjectWriterClass read FCInternalAr write FCInternalAr;
|
|
public
|
|
constructor Create(info: pasminfo; smart: boolean);override;
|
|
destructor destroy;override;
|
|
procedure MakeObject;override;
|
|
end;
|
|
|
|
TAssemblerClass = class of TAssembler;
|
|
|
|
Procedure GenerateAsm(smart:boolean);
|
|
|
|
{ get an instance of an external GNU-style assembler that is compatible
|
|
with the current target, reusing an existing writer. Used by the LLVM
|
|
target to write inline assembler }
|
|
function GetExternalGnuAssemblerWithAsmInfoWriter(info: pasminfo; wr: TExternalAssemblerOutputFile): TExternalAssembler;
|
|
|
|
procedure RegisterAssembler(const r:tasminfo;c:TAssemblerClass);
|
|
|
|
|
|
Implementation
|
|
|
|
uses
|
|
{$ifdef hasunix}
|
|
unix,
|
|
{$endif}
|
|
cutils,cfileutl,
|
|
{$ifdef memdebug}
|
|
cclasses,
|
|
{$endif memdebug}
|
|
{$ifdef OMFOBJSUPPORT}
|
|
omfbase,
|
|
ogomf,
|
|
{$endif OMFOBJSUPPORT}
|
|
{$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)}
|
|
{$else}
|
|
{$ifdef FPC_SOFT_FPUX80}
|
|
sfpux80,
|
|
{$endif FPC_SOFT_FPUX80}
|
|
{$endif}
|
|
{$ifdef WASM}
|
|
ogwasm,
|
|
{$endif WASM}
|
|
cscript,fmodule,verbose,
|
|
cpubase,cpuinfo,triplet,
|
|
aasmcpu;
|
|
|
|
var
|
|
CAssembler : array[tasm] of TAssemblerClass;
|
|
|
|
function fixline(s:string):string;
|
|
{
|
|
return s with all leading and ending spaces and tabs removed
|
|
}
|
|
var
|
|
i,j,k : integer;
|
|
begin
|
|
i:=length(s);
|
|
while (i>0) and (s[i] in [#9,' ']) do
|
|
dec(i);
|
|
j:=1;
|
|
while (j<i) and (s[j] in [#9,' ']) do
|
|
inc(j);
|
|
for k:=j to i do
|
|
if s[k] in [#0..#31,#127..#255] then
|
|
s[k]:='.';
|
|
fixline:=Copy(s,j,i-j+1);
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
TAssembler
|
|
*****************************************************************************}
|
|
|
|
Constructor TAssembler.Create(info: pasminfo; smart: boolean);
|
|
begin
|
|
asminfo:=info;
|
|
{ load start values }
|
|
AsmFileName:=current_module.AsmFilename;
|
|
ObjFileName:=current_module.ObjFileName;
|
|
name:=Lower(current_module.modulename^);
|
|
path:=current_module.outputpath;
|
|
asmprefix := current_module.asmprefix^;
|
|
if current_module.outputpath = '' then
|
|
ppufilename := ''
|
|
else
|
|
ppufilename := current_module.ppufilename;
|
|
SmartAsm:=smart;
|
|
SmartFilesCount:=0;
|
|
SmartHeaderCount:=0;
|
|
SmartLinkOFiles.Clear;
|
|
end;
|
|
|
|
|
|
Destructor TAssembler.Destroy;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure TAssembler.NextSmartName(place:tcutplace);
|
|
var
|
|
s : string;
|
|
begin
|
|
inc(SmartFilesCount);
|
|
if SmartFilesCount>999999 then
|
|
Message(asmw_f_too_many_asm_files);
|
|
case place of
|
|
cut_begin :
|
|
begin
|
|
inc(SmartHeaderCount);
|
|
s:=asmprefix+tostr(SmartHeaderCount)+'h';
|
|
end;
|
|
cut_normal :
|
|
s:=asmprefix+tostr(SmartHeaderCount)+'s';
|
|
cut_end :
|
|
s:=asmprefix+tostr(SmartHeaderCount)+'t';
|
|
end;
|
|
AsmFileName:=Path+FixFileName(s+tostr(SmartFilesCount)+target_info.asmext);
|
|
ObjFileName:=Path+FixFileName(s+tostr(SmartFilesCount)+target_info.objext);
|
|
{ insert in container so it can be cleared after the linking }
|
|
SmartLinkOFiles.Insert(ObjFileName);
|
|
end;
|
|
|
|
|
|
|
|
|
|
{*****************************************************************************
|
|
TAssemblerOutputFile
|
|
*****************************************************************************}
|
|
|
|
procedure TExternalAssemblerOutputFile.RemoveAsm;
|
|
var
|
|
g : file;
|
|
begin
|
|
if cs_asm_leave in current_settings.globalswitches then
|
|
exit;
|
|
if cs_asm_extern in current_settings.globalswitches then
|
|
AsmRes.AddDeleteCommand(owner.AsmFileName)
|
|
else
|
|
begin
|
|
assign(g,owner.AsmFileName);
|
|
{$push} {$I-}
|
|
erase(g);
|
|
{$pop}
|
|
if ioresult<>0 then;
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmFlush;
|
|
begin
|
|
if outcnt>0 then
|
|
begin
|
|
{ suppress i/o error }
|
|
{$push} {$I-}
|
|
BlockWrite(outfile,outbuf,outcnt);
|
|
{$pop}
|
|
fioerror:=fioerror or (ioresult<>0);
|
|
outcnt:=0;
|
|
end;
|
|
end;
|
|
|
|
procedure TExternalAssemblerOutputFile.MarkEmpty;
|
|
begin
|
|
AsmStartSize:=AsmSize
|
|
end;
|
|
|
|
|
|
function TExternalAssemblerOutputFile.ClearIfEmpty: boolean;
|
|
begin
|
|
result:=AsmSize=AsmStartSize;
|
|
if result then
|
|
AsmClear;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmWriteFiltered(const c: char);
|
|
begin
|
|
MaybeAddLinePrefix;
|
|
AsmWriteAnsiStringUnfiltered(decorator.LineFilter(c));
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmWriteFiltered(const s: string);
|
|
begin
|
|
MaybeAddLinePrefix;
|
|
AsmWriteAnsiStringUnfiltered(decorator.LineFilter(s));
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmWriteFiltered(const s: ansistring);
|
|
begin
|
|
MaybeAddLinePrefix;
|
|
AsmWriteAnsiStringUnfiltered(decorator.LineFilter(s));
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmWriteFiltered(p: pchar; len: longint);
|
|
var
|
|
s: ansistring;
|
|
begin
|
|
MaybeAddLinePrefix;
|
|
s:='';
|
|
setlength(s,len);
|
|
move(p^,s[1],len);
|
|
AsmWriteAnsiStringUnfiltered(decorator.LineFilter(s));
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmClear;
|
|
begin
|
|
outcnt:=0;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.MaybeAddLinePrefix;
|
|
begin
|
|
if assigned(decorator) and
|
|
linestart then
|
|
begin
|
|
AsmWriteAnsiStringUnfiltered(decorator.LinePrefix);
|
|
linestart:=false;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.MaybeAddLinePostfix;
|
|
begin
|
|
if assigned(decorator) and
|
|
not linestart then
|
|
begin
|
|
AsmWriteAnsiStringUnfiltered(decorator.LinePostfix);
|
|
linestart:=true;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmWriteAnsiStringUnfiltered(const s: ansistring);
|
|
var
|
|
StartIndex, ToWrite: longint;
|
|
begin
|
|
if s='' then
|
|
exit;
|
|
if OutCnt+length(s)>=AsmOutSize then
|
|
AsmFlush;
|
|
StartIndex:=1;
|
|
ToWrite:=length(s);
|
|
while ToWrite>AsmOutSize do
|
|
begin
|
|
Move(s[StartIndex],OutBuf[OutCnt],AsmOutSize);
|
|
inc(OutCnt,AsmOutSize);
|
|
inc(AsmSize,AsmOutSize);
|
|
AsmFlush;
|
|
inc(StartIndex,AsmOutSize);
|
|
dec(ToWrite,AsmOutSize);
|
|
end;
|
|
Move(s[StartIndex],OutBuf[OutCnt],ToWrite);
|
|
inc(OutCnt,ToWrite);
|
|
inc(AsmSize,ToWrite);
|
|
end;
|
|
|
|
|
|
constructor TExternalAssemblerOutputFile.Create(_owner: TExternalAssembler);
|
|
begin
|
|
owner:=_owner;
|
|
linestart:=true;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmWrite(const c: char);
|
|
begin
|
|
if assigned(decorator) then
|
|
AsmWriteFiltered(c)
|
|
else
|
|
begin
|
|
if OutCnt+1>=AsmOutSize then
|
|
AsmFlush;
|
|
OutBuf[OutCnt]:=c;
|
|
inc(OutCnt);
|
|
inc(AsmSize);
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmWrite(const s:string);
|
|
begin
|
|
if s='' then
|
|
exit;
|
|
if assigned(decorator) then
|
|
AsmWriteFiltered(s)
|
|
else
|
|
begin
|
|
if OutCnt+length(s)>=AsmOutSize then
|
|
AsmFlush;
|
|
Move(s[1],OutBuf[OutCnt],length(s));
|
|
inc(OutCnt,length(s));
|
|
inc(AsmSize,length(s));
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmWrite(const s:ansistring);
|
|
begin
|
|
if s='' then
|
|
exit;
|
|
if assigned(decorator) then
|
|
AsmWriteFiltered(s)
|
|
else
|
|
AsmWriteAnsiStringUnfiltered(s);
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmWriteLn(const c: char);
|
|
begin
|
|
AsmWrite(c);
|
|
AsmLn;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmWriteLn(const s:string);
|
|
begin
|
|
AsmWrite(s);
|
|
AsmLn;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmWriteLn(const s: ansistring);
|
|
begin
|
|
AsmWrite(s);
|
|
AsmLn;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmWritePChar(p:pchar);
|
|
var
|
|
i,j : longint;
|
|
begin
|
|
i:=StrLen(p);
|
|
if i=0 then
|
|
exit;
|
|
if assigned(decorator) then
|
|
AsmWriteFiltered(p,i)
|
|
else
|
|
begin
|
|
j:=i;
|
|
while j>0 do
|
|
begin
|
|
i:=min(j,AsmOutSize);
|
|
if OutCnt+i>=AsmOutSize then
|
|
AsmFlush;
|
|
Move(p[0],OutBuf[OutCnt],i);
|
|
inc(OutCnt,i);
|
|
inc(AsmSize,i);
|
|
dec(j,i);
|
|
p:=pchar(@p[i]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TExternalAssemblerOutputFile.AsmLn;
|
|
var
|
|
newline: pshortstring;
|
|
newlineres: shortstring;
|
|
index: longint;
|
|
begin
|
|
MaybeAddLinePostfix;
|
|
if (cs_assemble_on_target in current_settings.globalswitches) then
|
|
newline:=@target_info.newline
|
|
else
|
|
newline:=@source_info.newline;
|
|
if assigned(decorator) then
|
|
begin
|
|
newlineres:=decorator.LineEnding(newline^);
|
|
newline:=@newlineres;
|
|
end;
|
|
if OutCnt>=AsmOutSize-length(newline^) then
|
|
AsmFlush;
|
|
index:=1;
|
|
repeat
|
|
OutBuf[OutCnt]:=newline^[index];
|
|
inc(OutCnt);
|
|
inc(AsmSize);
|
|
inc(index);
|
|
until index>length(newline^);
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmCreate(Aplace:tcutplace);
|
|
{$ifdef hasamiga}
|
|
var
|
|
tempFileName: TPathStr;
|
|
{$endif}
|
|
begin
|
|
if owner.SmartAsm then
|
|
owner.NextSmartName(Aplace);
|
|
{$ifdef hasamiga}
|
|
{ on Amiga/MorphOS try to redirect .s files to the T: assign, which is
|
|
for temp files, and usually (default setting) located in the RAM: drive.
|
|
This highly improves assembling speed for complex projects like the
|
|
compiler itself, especially on hardware with slow disk I/O.
|
|
Consider this as a poor man's pipe on Amiga, because real pipe handling
|
|
would be much more complex and error prone to implement. (KB) }
|
|
if (([cs_asm_extern,cs_asm_leave,cs_assemble_on_target] * current_settings.globalswitches) = []) then
|
|
begin
|
|
{ try to have an unique name for the .s file }
|
|
tempFileName:=HexStr(GetProcessID shr 4,7)+ExtractFileName(owner.AsmFileName);
|
|
{$ifndef morphos}
|
|
{ old Amiga RAM: handler only allows filenames up to 30 char }
|
|
if Length(tempFileName) < 30 then
|
|
{$endif}
|
|
owner.AsmFileName:='T:'+tempFileName;
|
|
end;
|
|
{$endif}
|
|
{$ifdef hasunix}
|
|
if owner.DoPipe then
|
|
begin
|
|
if owner.SmartAsm then
|
|
begin
|
|
if (owner.SmartFilesCount<=1) then
|
|
Message1(exec_i_assembling_smart,owner.name);
|
|
end
|
|
else
|
|
Message1(exec_i_assembling_pipe,owner.AsmFileName);
|
|
if checkverbosity(V_Executable) then
|
|
comment(V_Executable,'Executing "'+maybequoted(owner.FindAssembler)+'" with command line "'+
|
|
owner.MakeCmdLine+'"');
|
|
POpen(outfile,maybequoted(owner.FindAssembler)+' '+owner.MakeCmdLine,'W');
|
|
end
|
|
else
|
|
{$endif}
|
|
begin
|
|
Assign(outfile,owner.AsmFileName);
|
|
{$push} {$I-}
|
|
Rewrite(outfile,1);
|
|
{$pop}
|
|
if ioresult<>0 then
|
|
begin
|
|
fioerror:=true;
|
|
Message1(exec_d_cant_create_asmfile,owner.AsmFileName);
|
|
end;
|
|
end;
|
|
outcnt:=0;
|
|
AsmSize:=0;
|
|
AsmStartSize:=0;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssemblerOutputFile.AsmClose;
|
|
var
|
|
f : file;
|
|
FileAge : longint;
|
|
begin
|
|
AsmFlush;
|
|
{$ifdef hasunix}
|
|
if owner.DoPipe then
|
|
begin
|
|
if PClose(outfile) <> 0 then
|
|
GenerateError;
|
|
end
|
|
else
|
|
{$endif}
|
|
begin
|
|
{Touch Assembler time to ppu time is there is a ppufilename}
|
|
if owner.ppufilename<>'' then
|
|
begin
|
|
Assign(f,owner.ppufilename);
|
|
{$push} {$I-}
|
|
reset(f,1);
|
|
{$pop}
|
|
if ioresult=0 then
|
|
begin
|
|
FileAge := FileGetDate(GetFileHandle(f));
|
|
close(f);
|
|
reset(outfile,1);
|
|
FileSetDate(GetFileHandle(outFile),FileAge);
|
|
end;
|
|
end;
|
|
close(outfile);
|
|
end;
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
TExternalAssembler
|
|
*****************************************************************************}
|
|
|
|
|
|
function TExternalAssembler.single2str(d : single) : string;
|
|
var
|
|
hs : string;
|
|
begin
|
|
str(d,hs);
|
|
{ replace space with + }
|
|
if hs[1]=' ' then
|
|
hs[1]:='+';
|
|
single2str:='0d'+hs
|
|
end;
|
|
|
|
function TExternalAssembler.double2str(d : double) : string;
|
|
var
|
|
hs : string;
|
|
begin
|
|
str(d,hs);
|
|
{ replace space with + }
|
|
if hs[1]=' ' then
|
|
hs[1]:='+';
|
|
double2str:='0d'+hs
|
|
end;
|
|
|
|
function TExternalAssembler.extended2str(e : extended) : string;
|
|
var
|
|
hs : string;
|
|
begin
|
|
str(e,hs);
|
|
{ replace space with + }
|
|
if hs[1]=' ' then
|
|
hs[1]:='+';
|
|
extended2str:='0d'+hs
|
|
end;
|
|
|
|
function TExternalAssembler.sleb128tostr(a: int64): string;
|
|
var
|
|
i,len : longint;
|
|
buf : array[0..31] of byte;
|
|
begin
|
|
result:='';
|
|
len:=EncodeSleb128(a,buf,0);
|
|
for i:=0 to len-1 do
|
|
begin
|
|
if (i > 0) then
|
|
result:=result+',';
|
|
result:=result+tostr(buf[i]);
|
|
end;
|
|
end;
|
|
|
|
function TExternalAssembler.uleb128tostr(a: qword): string;
|
|
var
|
|
i,len : longint;
|
|
buf : array[0..31] of byte;
|
|
begin
|
|
result:='';
|
|
len:=EncodeUleb128(a,buf,0);
|
|
for i:=0 to len-1 do
|
|
begin
|
|
if (i > 0) then
|
|
result:=result+',';
|
|
result:=result+tostr(buf[i]);
|
|
end;
|
|
end;
|
|
|
|
|
|
Function TExternalAssembler.DoPipe:boolean;
|
|
begin
|
|
{$ifdef hasunix}
|
|
DoPipe:=(cs_asm_pipe in current_settings.globalswitches) and
|
|
(([cs_asm_extern,cs_asm_leave,cs_assemble_on_target] * current_settings.globalswitches) = []) and
|
|
((asminfo^.id in [as_gas,as_ggas,as_darwin,as_powerpc_xcoff,as_clang_gas,as_clang_llvm,as_solaris_as,as_clang_asdarwin]));
|
|
{$else hasunix}
|
|
DoPipe:=false;
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
function TExternalAssembler.CreateNewAsmWriter: TExternalAssemblerOutputFile;
|
|
begin
|
|
result:=TExternalAssemblerOutputFile.Create(self);
|
|
end;
|
|
|
|
|
|
Constructor TExternalAssembler.Create(info: pasminfo; smart: boolean);
|
|
begin
|
|
CreateWithWriter(info,CreateNewAsmWriter,true,smart);
|
|
end;
|
|
|
|
|
|
constructor TExternalAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter,smart: boolean);
|
|
begin
|
|
inherited Create(info,smart);
|
|
fwriter:=wr;
|
|
ffreewriter:=freewriter;
|
|
if SmartAsm then
|
|
begin
|
|
path:=FixPath(ChangeFileExt(AsmFileName,target_info.smartext),false);
|
|
CreateSmartLinkPath(path);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssembler.CreateSmartLinkPath(const s:TPathStr);
|
|
|
|
procedure DeleteFilesWithExt(const AExt:string);
|
|
var
|
|
dir : TRawByteSearchRec;
|
|
begin
|
|
if findfirst(FixPath(s,false)+'*'+AExt,faAnyFile,dir) = 0 then
|
|
begin
|
|
repeat
|
|
DeleteFile(s+source_info.dirsep+dir.name);
|
|
until findnext(dir) <> 0;
|
|
end;
|
|
findclose(dir);
|
|
end;
|
|
|
|
var
|
|
hs : TPathStr;
|
|
begin
|
|
if PathExists(s,false) then
|
|
begin
|
|
{ the path exists, now we clean only all the .o and .s files }
|
|
DeleteFilesWithExt(target_info.objext);
|
|
DeleteFilesWithExt(target_info.asmext);
|
|
end
|
|
else
|
|
begin
|
|
hs:=s;
|
|
if hs[length(hs)] in ['/','\'] then
|
|
delete(hs,length(hs),1);
|
|
{$push} {$I-}
|
|
mkdir(hs);
|
|
{$pop}
|
|
if ioresult<>0 then;
|
|
end;
|
|
end;
|
|
|
|
|
|
const
|
|
lastas : byte=255;
|
|
var
|
|
LastASBin : TCmdStr;
|
|
Function TExternalAssembler.FindAssembler:string;
|
|
var
|
|
asfound : boolean;
|
|
UtilExe : string;
|
|
asmbin : TCmdStr;
|
|
begin
|
|
asfound:=false;
|
|
asmbin:=asminfo^.asmbin;
|
|
if (af_llvm in asminfo^.flags) then
|
|
asmbin:=asmbin+llvmutilssuffix;
|
|
if cs_assemble_on_target in current_settings.globalswitches then
|
|
begin
|
|
{ If assembling on target, don't add any path PM }
|
|
FindAssembler:=utilsprefix+ChangeFileExt(asmbin,target_info.exeext);
|
|
exit;
|
|
end
|
|
else
|
|
UtilExe:=utilsprefix+ChangeFileExt(asmbin,source_info.exeext);
|
|
if lastas<>ord(asminfo^.id) then
|
|
begin
|
|
lastas:=ord(asminfo^.id);
|
|
{ is an assembler passed ? }
|
|
if utilsdirectory<>'' then
|
|
asfound:=FindFile(UtilExe,utilsdirectory,false,LastASBin);
|
|
if not AsFound then
|
|
asfound:=FindExe(UtilExe,false,LastASBin);
|
|
if (not asfound) and not(cs_asm_extern in current_settings.globalswitches) then
|
|
begin
|
|
Message1(exec_e_assembler_not_found,LastASBin);
|
|
current_settings.globalswitches:=current_settings.globalswitches+[cs_asm_extern];
|
|
end;
|
|
if asfound then
|
|
Message1(exec_t_using_assembler,LastASBin);
|
|
end;
|
|
FindAssembler:=LastASBin;
|
|
end;
|
|
|
|
|
|
Function TExternalAssembler.CallAssembler(const command:string; const para:TCmdStr):Boolean;
|
|
var
|
|
DosExitCode : Integer;
|
|
begin
|
|
result:=true;
|
|
if (cs_asm_extern in current_settings.globalswitches) then
|
|
begin
|
|
if SmartAsm then
|
|
AsmRes.AddAsmCommand(command,para,Name+'('+TosTr(SmartFilesCount)+')')
|
|
else
|
|
AsmRes.AddAsmCommand(command,para,name);
|
|
exit;
|
|
end;
|
|
try
|
|
FlushOutput;
|
|
DosExitCode:=RequotedExecuteProcess(command,para);
|
|
if DosExitCode<>0
|
|
then begin
|
|
Message1(exec_e_error_while_assembling,tostr(dosexitcode));
|
|
result:=false;
|
|
end;
|
|
except on E:EOSError do
|
|
begin
|
|
Message1(exec_e_cant_call_assembler,tostr(E.ErrorCode));
|
|
current_settings.globalswitches:=current_settings.globalswitches+[cs_asm_extern];
|
|
result:=false;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function TExternalAssembler.DoAssemble:boolean;
|
|
begin
|
|
result:=true;
|
|
if DoPipe then
|
|
exit;
|
|
if not(cs_asm_extern in current_settings.globalswitches) then
|
|
begin
|
|
if SmartAsm then
|
|
begin
|
|
if (SmartFilesCount<=1) then
|
|
Message1(exec_i_assembling_smart,name);
|
|
end
|
|
else
|
|
Message1(exec_i_assembling,name);
|
|
end;
|
|
|
|
repeat
|
|
result:=CallAssembler(FindAssembler,MakeCmdLine)
|
|
until not(result) or not RerunAssembler;
|
|
if result then
|
|
writer.RemoveAsm
|
|
else
|
|
GenerateError;
|
|
end;
|
|
|
|
|
|
function TExternalAssembler.MakeCmdLine: TCmdStr;
|
|
|
|
function section_high_bound:longint;
|
|
var
|
|
alt : tasmlisttype;
|
|
begin
|
|
result:=0;
|
|
for alt:=low(tasmlisttype) to high(tasmlisttype) do
|
|
result:=result+current_asmdata.asmlists[alt].section_count;
|
|
end;
|
|
|
|
const
|
|
min_big_obj_section_count = $7fff;
|
|
|
|
begin
|
|
result:=asminfo^.asmcmd;
|
|
if af_llvm in target_asm.flags then
|
|
Replace(result,'$TRIPLET',targettriplet(triplet_llvm))
|
|
{$ifdef arm}
|
|
else if (target_info.system=system_arm_ios) then
|
|
Replace(result,'$ARCH',lower(cputypestr[current_settings.cputype]))
|
|
{$endif arm}
|
|
;
|
|
if (cs_assemble_on_target in current_settings.globalswitches) then
|
|
begin
|
|
Replace(result,'$ASM',maybequoted(ScriptFixFileName(AsmFileName)));
|
|
Replace(result,'$OBJ',maybequoted(ScriptFixFileName(ObjFileName)));
|
|
end
|
|
else
|
|
begin
|
|
{$ifdef hasunix}
|
|
if DoPipe then
|
|
if not(asminfo^.id in [as_clang_gas,as_clang_asdarwin,as_clang_llvm]) then
|
|
Replace(result,'$ASM','')
|
|
else
|
|
Replace(result,'$ASM','-')
|
|
else
|
|
{$endif}
|
|
Replace(result,'$ASM',maybequoted(AsmFileName));
|
|
Replace(result,'$OBJ',maybequoted(ObjFileName));
|
|
end;
|
|
|
|
if (cs_create_pic in current_settings.moduleswitches) then
|
|
Replace(result,'$PIC','-KPIC')
|
|
else
|
|
Replace(result,'$PIC','');
|
|
|
|
if (cs_asm_source in current_settings.globalswitches) then
|
|
Replace(result,'$NOWARN','')
|
|
else
|
|
Replace(result,'$NOWARN','-W');
|
|
|
|
if target_info.endian=endian_little then
|
|
Replace(result,'$ENDIAN','-mlittle')
|
|
else
|
|
Replace(result,'$ENDIAN','-mbig');
|
|
|
|
{ as we don't keep track of the amount of sections we created we simply
|
|
enable Big Obj COFF files always for targets that need them }
|
|
if (cs_asm_pre_binutils_2_25 in current_settings.globalswitches) or
|
|
not (target_info.system in systems_all_windows+systems_nativent-[system_i8086_win16]) or
|
|
(section_high_bound<min_big_obj_section_count) then
|
|
Replace(result,'$BIGOBJ','')
|
|
else
|
|
Replace(result,'$BIGOBJ','-mbig-obj');
|
|
|
|
Replace(result,'$EXTRAOPT',asmextraopt);
|
|
end;
|
|
|
|
|
|
function TExternalAssembler.RerunAssembler: boolean;
|
|
begin
|
|
result:=false;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssembler.WriteSourceLine(hp: tailineinfo);
|
|
var
|
|
module : tmodule;
|
|
begin
|
|
{ load infile }
|
|
if (lastfileinfo.moduleindex<>hp.fileinfo.moduleindex) or
|
|
(lastfileinfo.fileindex<>hp.fileinfo.fileindex) then
|
|
begin
|
|
{ in case of a generic the module can be different }
|
|
if current_module.unit_index=hp.fileinfo.moduleindex then
|
|
module:=current_module
|
|
else
|
|
module:=get_module(hp.fileinfo.moduleindex);
|
|
{ during the compilation of the system unit there are cases when
|
|
the fileinfo contains just zeros => invalid }
|
|
if assigned(module) then
|
|
infile:=module.sourcefiles.get_file(hp.fileinfo.fileindex)
|
|
else
|
|
infile:=nil;
|
|
if assigned(infile) then
|
|
begin
|
|
{ open only if needed !! }
|
|
if (cs_asm_source in current_settings.globalswitches) then
|
|
infile.open;
|
|
end;
|
|
{ avoid unnecessary reopens of the same file !! }
|
|
lastfileinfo.fileindex:=hp.fileinfo.fileindex;
|
|
lastfileinfo.moduleindex:=hp.fileinfo.moduleindex;
|
|
{ be sure to change line !! }
|
|
lastfileinfo.line:=-1;
|
|
end;
|
|
{ write source }
|
|
if (cs_asm_source in current_settings.globalswitches) and
|
|
assigned(infile) then
|
|
begin
|
|
if (infile<>lastinfile) then
|
|
begin
|
|
writer.AsmWriteLn(asminfo^.comment+'['+infile.name+']');
|
|
if assigned(lastinfile) then
|
|
lastinfile.close;
|
|
end;
|
|
if (hp.fileinfo.line<>lastfileinfo.line) and
|
|
(hp.fileinfo.line<infile.maxlinebuf) then
|
|
begin
|
|
if (hp.fileinfo.line<>0) and
|
|
(infile.linebuf^[hp.fileinfo.line]>=0) then
|
|
writer.AsmWriteLn(asminfo^.comment+'['+tostr(hp.fileinfo.line)+'] '+
|
|
fixline(infile.GetLineStr(hp.fileinfo.line)));
|
|
{ set it to a negative value !
|
|
to make that is has been read already !! PM }
|
|
if (infile.linebuf^[hp.fileinfo.line]>=0) then
|
|
infile.linebuf^[hp.fileinfo.line]:=-infile.linebuf^[hp.fileinfo.line]-1;
|
|
end;
|
|
end;
|
|
lastfileinfo:=hp.fileinfo;
|
|
lastinfile:=infile;
|
|
end;
|
|
|
|
procedure TExternalAssembler.WriteTempalloc(hp: tai_tempalloc);
|
|
begin
|
|
{$ifdef EXTDEBUG}
|
|
if assigned(hp.problem) then
|
|
writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(hp.temppos)+','+
|
|
tostr(hp.tempsize)+' '+hp.problem^)
|
|
else
|
|
{$endif EXTDEBUG}
|
|
writer.AsmWriteLn(asminfo^.comment+'Temp '+tostr(hp.temppos)+','+
|
|
tostr(hp.tempsize)+' '+tempallocstr[hp.allocation]);
|
|
end;
|
|
|
|
|
|
procedure TExternalAssembler.WriteRealConstAsBytes(hp: tai_realconst; const dbdir: string; do_line: boolean);
|
|
var
|
|
pdata: pbyte;
|
|
index, step, swapmask, count: longint;
|
|
ssingle: single;
|
|
ddouble: double;
|
|
ccomp: comp;
|
|
{$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)}
|
|
eextended: extended;
|
|
{$else}
|
|
{$ifdef FPC_SOFT_FPUX80}
|
|
eextended: floatx80;
|
|
{$endif}
|
|
{$endif cpuextended}
|
|
begin
|
|
if do_line then
|
|
begin
|
|
case tai_realconst(hp).realtyp of
|
|
aitrealconst_s32bit:
|
|
writer.AsmWriteLn(asminfo^.comment+'value: '+single2str(tai_realconst(hp).value.s32val));
|
|
aitrealconst_s64bit:
|
|
writer.AsmWriteLn(asminfo^.comment+'value: '+double2str(tai_realconst(hp).value.s64val));
|
|
{$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)}
|
|
{ can't write full 80 bit floating point constants yet on non-x86 }
|
|
aitrealconst_s80bit:
|
|
writer.AsmWriteLn(asminfo^.comment+'value: '+extended2str(tai_realconst(hp).value.s80val));
|
|
{$else}
|
|
{$ifdef FPC_SOFT_FPUX80}
|
|
{$push}{$warn 6018 off} { Unreachable code due to compile time evaluation }
|
|
aitrealconst_s80bit:
|
|
begin
|
|
if sizeof(tai_realconst(hp).value.s80val) = sizeof(double) then
|
|
writer.AsmWriteLn(asminfo^.comment+'value: '+double2str(tai_realconst(hp).value.s80val))
|
|
else if sizeof(tai_realconst(hp).value.s80val) = sizeof(single) then
|
|
writer.AsmWriteLn(asminfo^.comment+'value: '+single2str(tai_realconst(hp).value.s80val))
|
|
else
|
|
internalerror(2017091901);
|
|
end;
|
|
{$pop}
|
|
{$endif}
|
|
{$endif cpuextended}
|
|
aitrealconst_s64comp:
|
|
writer.AsmWriteLn(asminfo^.comment+'value: '+extended2str(tai_realconst(hp).value.s64compval));
|
|
else
|
|
internalerror(2014050604);
|
|
end;
|
|
end;
|
|
writer.AsmWrite(dbdir);
|
|
{ generic float writing code: get start address of value, then write
|
|
byte by byte. Can't use fields directly, because e.g ts64comp is
|
|
defined as extended on x86 }
|
|
case tai_realconst(hp).realtyp of
|
|
aitrealconst_s32bit:
|
|
begin
|
|
ssingle:=single(tai_realconst(hp).value.s32val);
|
|
pdata:=@ssingle;
|
|
end;
|
|
aitrealconst_s64bit:
|
|
begin
|
|
ddouble:=double(tai_realconst(hp).value.s64val);
|
|
pdata:=@ddouble;
|
|
end;
|
|
{$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)}
|
|
{ can't write full 80 bit floating point constants yet on non-x86 }
|
|
aitrealconst_s80bit:
|
|
begin
|
|
eextended:=extended(tai_realconst(hp).value.s80val);
|
|
pdata:=@eextended;
|
|
end;
|
|
{$else}
|
|
{$ifdef FPC_SOFT_FPUX80}
|
|
{$push}{$warn 6018 off} { Unreachable code due to compile time evaluation }
|
|
aitrealconst_s80bit:
|
|
begin
|
|
if sizeof(tai_realconst(hp).value.s80val) = sizeof(double) then
|
|
eextended:=float64_to_floatx80(float64(double(tai_realconst(hp).value.s80val)))
|
|
else if sizeof(tai_realconst(hp).value.s80val) = sizeof(single) then
|
|
eextended:=float32_to_floatx80(float32(single(tai_realconst(hp).value.s80val)))
|
|
else
|
|
internalerror(2017091902);
|
|
pdata:=@eextended;
|
|
end;
|
|
{$pop}
|
|
{$endif}
|
|
{$endif cpuextended}
|
|
aitrealconst_s64comp:
|
|
begin
|
|
ccomp:=comp(tai_realconst(hp).value.s64compval);
|
|
pdata:=@ccomp;
|
|
end;
|
|
else
|
|
internalerror(2014051001);
|
|
end;
|
|
count:=tai_realconst(hp).datasize;
|
|
{ write bytes in inverse order if source and target endianess don't
|
|
match }
|
|
if source_info.endian<>target_info.endian then
|
|
begin
|
|
{ go from back to front }
|
|
index:=count-1;
|
|
step:=-1;
|
|
end
|
|
else
|
|
begin
|
|
index:=0;
|
|
step:=1;
|
|
end;
|
|
{$ifdef ARM}
|
|
{ ARM-specific: low and high dwords of a double may be swapped }
|
|
if tai_realconst(hp).formatoptions=fo_hiloswapped then
|
|
begin
|
|
{ only supported for double }
|
|
if tai_realconst(hp).datasize<>8 then
|
|
internalerror(2014050605);
|
|
{ switch bit of the index so that the words are written in
|
|
the opposite order }
|
|
swapmask:=4;
|
|
end
|
|
else
|
|
{$endif ARM}
|
|
swapmask:=0;
|
|
repeat
|
|
writer.AsmWrite(tostr(pdata[index xor swapmask]));
|
|
inc(index,step);
|
|
dec(count);
|
|
if count<>0 then
|
|
writer.AsmWrite(',');
|
|
until count=0;
|
|
{ padding }
|
|
for count:=tai_realconst(hp).datasize+1 to tai_realconst(hp).savesize do
|
|
writer.AsmWrite(',0');
|
|
writer.AsmLn;
|
|
end;
|
|
|
|
|
|
function TExternalAssembler.WriteComments(var hp: tai): boolean;
|
|
begin
|
|
result:=true;
|
|
case hp.typ of
|
|
ait_comment :
|
|
Begin
|
|
writer.AsmWrite(asminfo^.comment);
|
|
writer.AsmWritePChar(tai_comment(hp).str);
|
|
writer.AsmLn;
|
|
End;
|
|
|
|
ait_regalloc :
|
|
begin
|
|
if (cs_asm_regalloc in current_settings.globalswitches) then
|
|
begin
|
|
writer.AsmWrite(#9+asminfo^.comment+'Register ');
|
|
repeat
|
|
writer.AsmWrite(std_regname(Tai_regalloc(hp).reg));
|
|
if (hp.next=nil) or
|
|
(tai(hp.next).typ<>ait_regalloc) or
|
|
(tai_regalloc(hp.next).ratype<>tai_regalloc(hp).ratype) then
|
|
break;
|
|
hp:=tai(hp.next);
|
|
writer.AsmWrite(',');
|
|
until false;
|
|
writer.AsmWrite(' ');
|
|
writer.AsmWriteLn(regallocstr[tai_regalloc(hp).ratype]);
|
|
end;
|
|
end;
|
|
|
|
ait_tempalloc :
|
|
begin
|
|
if (cs_asm_tempalloc in current_settings.globalswitches) then
|
|
WriteTempalloc(tai_tempalloc(hp));
|
|
end;
|
|
|
|
ait_varloc:
|
|
begin
|
|
{ ait_varloc is present here only when register allocation is not done ( -sr option ) }
|
|
if tai_varloc(hp).newlocationhi<>NR_NO then
|
|
writer.AsmWriteLn(asminfo^.comment+'Var '+tai_varloc(hp).varsym.realname+' located in register '+
|
|
std_regname(tai_varloc(hp).newlocationhi)+':'+std_regname(tai_varloc(hp).newlocation))
|
|
else
|
|
writer.AsmWriteLn(asminfo^.comment+'Var '+tai_varloc(hp).varsym.realname+' located in register '+
|
|
std_regname(tai_varloc(hp).newlocation));
|
|
end;
|
|
else
|
|
result:=false;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TExternalAssembler.WriteTree(p:TAsmList);
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure TExternalAssembler.WriteAsmList;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure TExternalAssembler.MakeObject;
|
|
begin
|
|
writer.AsmCreate(cut_normal);
|
|
FillChar(lastfileinfo, sizeof(lastfileinfo), 0);
|
|
lastfileinfo.line := -1;
|
|
lastinfile := nil;
|
|
lastsectype := sec_none;
|
|
WriteAsmList;
|
|
writer.AsmClose;
|
|
if not(writer.ioerror) then
|
|
DoAssemble;
|
|
end;
|
|
|
|
|
|
destructor TExternalAssembler.Destroy;
|
|
begin
|
|
if ffreewriter then
|
|
writer.Free;
|
|
inherited;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
TInternalAssembler
|
|
*****************************************************************************}
|
|
|
|
constructor TInternalAssembler.Create(info: pasminfo; smart: boolean);
|
|
begin
|
|
inherited;
|
|
ObjOutput:=nil;
|
|
ObjData:=nil;
|
|
SmartAsm:=smart;
|
|
{$ifdef ARM}
|
|
Code16:=current_settings.instructionset=is_thumb;
|
|
{$endif ARM}
|
|
end;
|
|
|
|
|
|
destructor TInternalAssembler.destroy;
|
|
begin
|
|
if assigned(ObjData) then
|
|
ObjData.free;
|
|
if assigned(ObjOutput) then
|
|
ObjOutput.free;
|
|
end;
|
|
|
|
|
|
procedure TInternalAssembler.WriteStab(p:pchar);
|
|
|
|
function consumecomma(var p:pchar):boolean;
|
|
begin
|
|
while (p^=' ') do
|
|
inc(p);
|
|
result:=(p^=',');
|
|
inc(p);
|
|
end;
|
|
|
|
function consumenumber(var p:pchar;out value:longint):boolean;
|
|
var
|
|
hs : string;
|
|
len,
|
|
code : integer;
|
|
begin
|
|
value:=0;
|
|
while (p^=' ') do
|
|
inc(p);
|
|
len:=0;
|
|
while (p^ in ['0'..'9']) do
|
|
begin
|
|
inc(len);
|
|
hs[len]:=p^;
|
|
inc(p);
|
|
end;
|
|
if len>0 then
|
|
begin
|
|
hs[0]:=chr(len);
|
|
val(hs,value,code);
|
|
end
|
|
else
|
|
code:=-1;
|
|
result:=(code=0);
|
|
end;
|
|
|
|
function consumeoffset(var p:pchar;out relocsym:tobjsymbol;out value:longint):boolean;
|
|
var
|
|
hs : string;
|
|
len,
|
|
code : integer;
|
|
pstart : pchar;
|
|
sym : tobjsymbol;
|
|
exprvalue : longint;
|
|
gotmin,
|
|
have_first_symbol,
|
|
have_second_symbol,
|
|
dosub : boolean;
|
|
begin
|
|
result:=false;
|
|
value:=0;
|
|
relocsym:=nil;
|
|
gotmin:=false;
|
|
have_first_symbol:=false;
|
|
have_second_symbol:=false;
|
|
repeat
|
|
dosub:=false;
|
|
exprvalue:=0;
|
|
if gotmin then
|
|
begin
|
|
dosub:=true;
|
|
gotmin:=false;
|
|
end;
|
|
while (p^=' ') do
|
|
inc(p);
|
|
case p^ of
|
|
#0 :
|
|
break;
|
|
' ' :
|
|
inc(p);
|
|
'0'..'9' :
|
|
begin
|
|
len:=0;
|
|
while (p^ in ['0'..'9']) do
|
|
begin
|
|
inc(len);
|
|
hs[len]:=p^;
|
|
inc(p);
|
|
end;
|
|
hs[0]:=chr(len);
|
|
val(hs,exprvalue,code);
|
|
if code<>0 then
|
|
internalerror(200702251);
|
|
end;
|
|
'.','_',
|
|
'A'..'Z',
|
|
'a'..'z' :
|
|
begin
|
|
pstart:=p;
|
|
while not(p^ in [#0,' ','-','+']) do
|
|
inc(p);
|
|
len:=p-pstart;
|
|
if len>255 then
|
|
internalerror(200509187);
|
|
hs[0]:=chr(len);
|
|
move(pstart^,hs[1],len);
|
|
sym:=objdata.symbolref(hs);
|
|
{ Second symbol? }
|
|
if assigned(relocsym) then
|
|
begin
|
|
if have_second_symbol then
|
|
internalerror(2007032201);
|
|
have_second_symbol:=true;
|
|
if not have_first_symbol then
|
|
internalerror(2007032202);
|
|
{ second symbol should substracted to first }
|
|
if not dosub then
|
|
internalerror(2007032203);
|
|
if (relocsym.objsection<>sym.objsection) then
|
|
internalerror(2005091810);
|
|
exprvalue:=relocsym.address-sym.address;
|
|
relocsym:=nil;
|
|
dosub:=false;
|
|
end
|
|
else
|
|
begin
|
|
relocsym:=sym;
|
|
if assigned(sym.objsection) then
|
|
begin
|
|
{ first symbol should be + }
|
|
if not have_first_symbol and dosub then
|
|
internalerror(2007032204);
|
|
have_first_symbol:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
'+' :
|
|
begin
|
|
{ nothing, by default addition is done }
|
|
inc(p);
|
|
end;
|
|
'-' :
|
|
begin
|
|
gotmin:=true;
|
|
inc(p);
|
|
end;
|
|
else
|
|
internalerror(200509189);
|
|
end;
|
|
if dosub then
|
|
dec(value,exprvalue)
|
|
else
|
|
inc(value,exprvalue);
|
|
until false;
|
|
result:=true;
|
|
end;
|
|
|
|
var
|
|
stabstrlen,
|
|
ofs,
|
|
nline,
|
|
nidx,
|
|
nother,
|
|
i : longint;
|
|
stab : TObjStabEntry;
|
|
relocsym : TObjSymbol;
|
|
pstr,
|
|
pcurr,
|
|
pendquote : pchar;
|
|
oldsec : TObjSection;
|
|
begin
|
|
pcurr:=nil;
|
|
pstr:=nil;
|
|
pendquote:=nil;
|
|
relocsym:=nil;
|
|
ofs:=0;
|
|
|
|
{ Parse string part }
|
|
if (p[0]='"') then
|
|
begin
|
|
pstr:=@p[1];
|
|
{ Ignore \" inside the string }
|
|
i:=1;
|
|
while not((p[i]='"') and (p[i-1]<>'\')) and
|
|
(p[i]<>#0) do
|
|
inc(i);
|
|
pendquote:=@p[i];
|
|
pendquote^:=#0;
|
|
pcurr:=@p[i+1];
|
|
if not consumecomma(pcurr) then
|
|
internalerror(200509181);
|
|
end
|
|
else
|
|
pcurr:=p;
|
|
|
|
{ When in pass 1 then only alloc and leave }
|
|
if ObjData.currpass=1 then
|
|
begin
|
|
ObjData.StabsSec.Alloc(sizeof(TObjStabEntry));
|
|
if assigned(pstr) and (pstr[0]<>#0) then
|
|
ObjData.StabStrSec.Alloc(strlen(pstr)+1);
|
|
end
|
|
else
|
|
begin
|
|
{ Stabs format: nidx,nother,nline[,offset] }
|
|
if not consumenumber(pcurr,nidx) then
|
|
internalerror(200509182);
|
|
if not consumecomma(pcurr) then
|
|
internalerror(200509183);
|
|
if not consumenumber(pcurr,nother) then
|
|
internalerror(200509184);
|
|
if not consumecomma(pcurr) then
|
|
internalerror(200509185);
|
|
if not consumenumber(pcurr,nline) then
|
|
internalerror(200509186);
|
|
if consumecomma(pcurr) then
|
|
consumeoffset(pcurr,relocsym,ofs);
|
|
|
|
{ Generate stab entry }
|
|
if assigned(pstr) and (pstr[0]<>#0) then
|
|
begin
|
|
stabstrlen:=strlen(pstr);
|
|
{$ifdef optimizestabs}
|
|
StabStrEntry:=nil;
|
|
if (nidx=N_SourceFile) or (nidx=N_IncludeFile) then
|
|
begin
|
|
hs:=strpas(pstr);
|
|
StabstrEntry:=StabStrDict.Find(hs);
|
|
if not assigned(StabstrEntry) then
|
|
begin
|
|
StabstrEntry:=TStabStrEntry.Create(hs);
|
|
StabstrEntry:=StabStrSec.Size;
|
|
StabStrDict.Insert(StabstrEntry);
|
|
{ generate new stab }
|
|
StabstrEntry:=nil;
|
|
end;
|
|
end;
|
|
if assigned(StabstrEntry) then
|
|
stab.strpos:=StabstrEntry.strpos
|
|
else
|
|
{$endif optimizestabs}
|
|
begin
|
|
stab.strpos:=ObjData.StabStrSec.Size;
|
|
ObjData.StabStrSec.write(pstr^,stabstrlen+1);
|
|
end;
|
|
end
|
|
else
|
|
stab.strpos:=0;
|
|
stab.ntype:=byte(nidx);
|
|
stab.ndesc:=word(nline);
|
|
stab.nother:=byte(nother);
|
|
stab.nvalue:=ofs;
|
|
|
|
{ Write the stab first without the value field. Then
|
|
write a the value field with relocation }
|
|
oldsec:=ObjData.CurrObjSec;
|
|
ObjData.SetSection(ObjData.StabsSec);
|
|
ObjData.Writebytes(stab,sizeof(TObjStabEntry)-4);
|
|
ObjData.Writereloc(stab.nvalue,4,relocsym,RELOC_ABSOLUTE32);
|
|
ObjData.setsection(oldsec);
|
|
end;
|
|
if assigned(pendquote) then
|
|
pendquote^:='"';
|
|
end;
|
|
|
|
|
|
function TInternalAssembler.MaybeNextList(var hp:Tai):boolean;
|
|
begin
|
|
{ maybe end of list }
|
|
while not assigned(hp) do
|
|
begin
|
|
if currlistidx<lists then
|
|
begin
|
|
inc(currlistidx);
|
|
currlist:=list[currlistidx];
|
|
hp:=Tai(currList.first);
|
|
end
|
|
else
|
|
begin
|
|
MaybeNextList:=false;
|
|
exit;
|
|
end;
|
|
end;
|
|
MaybeNextList:=true;
|
|
end;
|
|
|
|
|
|
function TInternalAssembler.SetIndirectToSymbol(hp: Tai; const indirectname: string): Boolean;
|
|
var
|
|
objsym : TObjSymbol;
|
|
indsym : TObjSymbol;
|
|
begin
|
|
Result:=
|
|
Assigned(hp) and
|
|
(hp.typ=ait_symbol);
|
|
if not Result then
|
|
Exit;
|
|
objsym:=Objdata.SymbolRef(tai_symbol(hp).sym);
|
|
objsym.size:=0;
|
|
|
|
indsym := TObjSymbol(ObjData.ObjSymbolList.Find(indirectname));
|
|
if not Assigned(indsym) then
|
|
begin
|
|
{ it's possible that indirect symbol is not present in the list,
|
|
so we must create it as undefined }
|
|
indsym:=ObjData.CObjSymbol.Create(ObjData.ObjSymbolList, indirectname);
|
|
indsym.typ:=AT_NONE;
|
|
indsym.bind:=AB_NONE;
|
|
end;
|
|
objsym.indsymbol:=indsym;
|
|
Result:=true;
|
|
end;
|
|
|
|
|
|
function TInternalAssembler.TreePass0(hp:Tai):Tai;
|
|
var
|
|
objsym,
|
|
objsymend : TObjSymbol;
|
|
cpu: tcputype;
|
|
eabi_section, TmpSection: TObjSection;
|
|
begin
|
|
while assigned(hp) do
|
|
begin
|
|
case hp.typ of
|
|
ait_align :
|
|
begin
|
|
if tai_align_abstract(hp).aligntype>1 then
|
|
begin
|
|
{ always use the maximum fillsize in this pass to avoid possible
|
|
short jumps to become out of range }
|
|
Tai_align_abstract(hp).fillsize:=Tai_align_abstract(hp).aligntype;
|
|
ObjData.alloc(Tai_align_abstract(hp).fillsize);
|
|
{ may need to increase alignment of section }
|
|
if tai_align_abstract(hp).aligntype>ObjData.CurrObjSec.secalign then
|
|
ObjData.CurrObjSec.secalign:=tai_align_abstract(hp).aligntype;
|
|
end
|
|
else
|
|
Tai_align_abstract(hp).fillsize:=0;
|
|
end;
|
|
ait_datablock :
|
|
begin
|
|
{$ifdef USE_COMM_IN_BSS}
|
|
if writingpackages and
|
|
Tai_datablock(hp).is_global then
|
|
ObjData.SymbolDefine(Tai_datablock(hp).sym)
|
|
else
|
|
{$endif USE_COMM_IN_BSS}
|
|
begin
|
|
ObjData.allocalign(used_align(size_2_align(Tai_datablock(hp).size),0,ObjData.CurrObjSec.secalign));
|
|
ObjData.SymbolDefine(Tai_datablock(hp).sym);
|
|
ObjData.alloc(Tai_datablock(hp).size);
|
|
end;
|
|
end;
|
|
ait_realconst:
|
|
ObjData.alloc(tai_realconst(hp).savesize);
|
|
ait_const:
|
|
begin
|
|
{ if symbols are provided we can calculate the value for relative symbols.
|
|
This is required for length calculation of leb128 constants }
|
|
if assigned(tai_const(hp).sym) then
|
|
begin
|
|
objsym:=Objdata.SymbolRef(tai_const(hp).sym);
|
|
{ objsym already defined and there is endsym? }
|
|
if assigned(objsym.objsection) and assigned(tai_const(hp).endsym) then
|
|
begin
|
|
objsymend:=Objdata.SymbolRef(tai_const(hp).endsym);
|
|
{ objsymend already defined? }
|
|
if assigned(objsymend.objsection) then
|
|
begin
|
|
if objsymend.objsection<>objsym.objsection then
|
|
begin
|
|
{ leb128 relative constants are not relocatable, but other types are,
|
|
given that objsym belongs to the current section. }
|
|
if (Tai_const(hp).consttype in [aitconst_uleb128bit,aitconst_sleb128bit]) or
|
|
(objsym.objsection<>ObjData.CurrObjSec) then
|
|
InternalError(200404124);
|
|
end
|
|
{$push} {$R-}{$Q-}
|
|
else
|
|
Tai_const(hp).value:=objsymend.address-objsym.address+Tai_const(hp).symofs;
|
|
end;
|
|
{$pop}
|
|
end;
|
|
end;
|
|
ObjData.alloc(tai_const(hp).size);
|
|
end;
|
|
ait_directive:
|
|
begin
|
|
case tai_directive(hp).directive of
|
|
asd_indirect_symbol:
|
|
{ handled in TreePass1 }
|
|
;
|
|
asd_lazy_reference:
|
|
begin
|
|
if tai_directive(hp).name='' then
|
|
Internalerror(2009112101);
|
|
objsym:=ObjData.symbolref(tai_directive(hp).name);
|
|
objsym.bind:=AB_LAZY;
|
|
end;
|
|
asd_reference:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
asd_cpu:
|
|
begin
|
|
ObjData.CPUType:=cpu_none;
|
|
for cpu:=low(tcputype) to high(tcputype) do
|
|
if cputypestr[cpu]=tai_directive(hp).name then
|
|
begin
|
|
ObjData.CPUType:=cpu;
|
|
break;
|
|
end;
|
|
end;
|
|
{$ifdef OMFOBJSUPPORT}
|
|
asd_omf_linnum_line:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
{$endif OMFOBJSUPPORT}
|
|
{$ifdef ARM}
|
|
asd_thumb_func:
|
|
ObjData.ThumbFunc:=true;
|
|
asd_force_thumb:
|
|
begin
|
|
ObjData.ThumbFunc:=true;
|
|
Code16:=true;
|
|
end;
|
|
asd_code:
|
|
begin
|
|
{ ai_directive(hp).name can be only 16 or 32, this is checked by the reader }
|
|
ObjData.ThumbFunc:=tai_directive(hp).name='16';
|
|
Code16:=tai_directive(hp).name='16';
|
|
end
|
|
{$endif ARM}
|
|
{$ifdef RISCV}
|
|
asd_option:
|
|
internalerror(2019031701);
|
|
{$endif RISCV}
|
|
else
|
|
internalerror(2010011101);
|
|
end;
|
|
end;
|
|
ait_section:
|
|
begin
|
|
if Tai_section(hp).sectype=sec_user then
|
|
ObjData.CreateSection(Tai_section(hp).sectype,Tai_section(hp).secflags,Tai_section(hp).secprogbits,Tai_section(hp).name^,Tai_section(hp).secorder)
|
|
else
|
|
ObjData.CreateSection(Tai_section(hp).sectype,Tai_section(hp).name^,Tai_section(hp).secorder);
|
|
Tai_section(hp).sec:=ObjData.CurrObjSec;
|
|
end;
|
|
ait_symbol :
|
|
begin
|
|
{ needs extra support in the internal assembler }
|
|
{ the value is just ignored }
|
|
{if tai_symbol(hp).has_value then
|
|
internalerror(2009090804); ;}
|
|
ObjData.SymbolDefine(Tai_symbol(hp).sym);
|
|
end;
|
|
ait_symbolpair :
|
|
with tai_symbolpair(hp) do
|
|
ObjData.SymbolPairDefine(kind,sym^,value^);
|
|
ait_label :
|
|
ObjData.SymbolDefine(Tai_label(hp).labsym);
|
|
ait_string :
|
|
ObjData.alloc(Tai_string(hp).len);
|
|
ait_instruction :
|
|
begin
|
|
{$ifdef arm}
|
|
if code16 then
|
|
include(taicpu(hp).flags,cf_thumb)
|
|
else
|
|
exclude(taicpu(hp).flags,cf_thumb);
|
|
{$endif arm}
|
|
{ reset instructions which could change in pass 2 }
|
|
Taicpu(hp).resetpass2;
|
|
ObjData.alloc(Taicpu(hp).Pass1(ObjData));
|
|
end;
|
|
ait_cutobject :
|
|
if SmartAsm then
|
|
break;
|
|
ait_eabi_attribute :
|
|
begin
|
|
eabi_section:=ObjData.findsection('.ARM.attributes');
|
|
if not(assigned(eabi_section)) then
|
|
begin
|
|
TmpSection:=ObjData.CurrObjSec;
|
|
ObjData.CreateSection(sec_arm_attribute,[],SPB_ARM_ATTRIBUTES,'',secorder_default);
|
|
eabi_section:=ObjData.CurrObjSec;
|
|
ObjData.setsection(TmpSection);
|
|
end;
|
|
if eabi_section.Size=0 then
|
|
eabi_section.alloc(16);
|
|
eabi_section.alloc(LengthUleb128(tai_eabi_attribute(hp).tag));
|
|
case tai_eabi_attribute(hp).eattr_typ of
|
|
eattrtype_dword:
|
|
eabi_section.alloc(LengthUleb128(tai_eabi_attribute(hp).value));
|
|
eattrtype_ntbs:
|
|
if assigned(tai_eabi_attribute(hp).valuestr) then
|
|
eabi_section.alloc(Length(tai_eabi_attribute(hp).valuestr^)+1)
|
|
else
|
|
eabi_section.alloc(1);
|
|
else
|
|
Internalerror(2019100701);
|
|
end;
|
|
end;
|
|
{$ifdef WASM}
|
|
ait_globaltype:
|
|
TWasmObjData(ObjData).DeclareGlobalType(tai_globaltype(hp));
|
|
ait_functype:
|
|
TWasmObjData(ObjData).DeclareFuncType(tai_functype(hp));
|
|
ait_tagtype:
|
|
TWasmObjData(ObjData).DeclareTagType(tai_tagtype(hp));
|
|
ait_export_name:
|
|
TWasmObjData(ObjData).DeclareExportName(tai_export_name(hp));
|
|
ait_import_module:
|
|
TWasmObjData(ObjData).DeclareImportModule(tai_import_module(hp));
|
|
ait_import_name:
|
|
TWasmObjData(ObjData).DeclareImportName(tai_import_name(hp));
|
|
ait_local:
|
|
TWasmObjData(ObjData).DeclareLocal(tai_local(hp));
|
|
{$endif WASM}
|
|
else
|
|
;
|
|
end;
|
|
hp:=Tai(hp.next);
|
|
end;
|
|
TreePass0:=hp;
|
|
end;
|
|
|
|
|
|
function TInternalAssembler.TreePass1(hp:Tai):Tai;
|
|
var
|
|
objsym,
|
|
objsymend : TObjSymbol;
|
|
cpu: tcputype;
|
|
eabi_section: TObjSection;
|
|
begin
|
|
while assigned(hp) do
|
|
begin
|
|
case hp.typ of
|
|
ait_align :
|
|
begin
|
|
if tai_align_abstract(hp).aligntype>1 then
|
|
begin
|
|
{ here we must determine the fillsize which is used in pass2 }
|
|
Tai_align_abstract(hp).fillsize:=align(ObjData.CurrObjSec.Size,Tai_align_abstract(hp).aligntype)-
|
|
ObjData.CurrObjSec.Size;
|
|
|
|
{ maximum number of bytes for alignment exeeded? }
|
|
if (Tai_align_abstract(hp).aligntype<>Tai_align_abstract(hp).maxbytes) and
|
|
(Tai_align_abstract(hp).fillsize>Tai_align_abstract(hp).maxbytes) then
|
|
Tai_align_abstract(hp).fillsize:=align(ObjData.CurrObjSec.Size,Byte(Tai_align_abstract(hp).aligntype div 2))-
|
|
ObjData.CurrObjSec.Size;
|
|
|
|
ObjData.alloc(Tai_align_abstract(hp).fillsize);
|
|
end;
|
|
end;
|
|
ait_datablock :
|
|
begin
|
|
if (oso_data in ObjData.CurrObjSec.secoptions) and
|
|
not (oso_sparse_data in ObjData.CurrObjSec.secoptions) then
|
|
Message(asmw_e_alloc_data_only_in_bss);
|
|
{$ifdef USE_COMM_IN_BSS}
|
|
if writingpackages and
|
|
Tai_datablock(hp).is_global then
|
|
begin
|
|
objsym:=ObjData.SymbolDefine(Tai_datablock(hp).sym);
|
|
objsym.size:=Tai_datablock(hp).size;
|
|
objsym.bind:=AB_COMMON;
|
|
objsym.alignment:=needtowritealignmentalsoforELF;
|
|
end
|
|
else
|
|
{$endif USE_COMM_IN_BSS}
|
|
begin
|
|
ObjData.allocalign(used_align(size_2_align(Tai_datablock(hp).size),0,ObjData.CurrObjSec.secalign));
|
|
objsym:=ObjData.SymbolDefine(Tai_datablock(hp).sym);
|
|
objsym.size:=Tai_datablock(hp).size;
|
|
ObjData.alloc(Tai_datablock(hp).size);
|
|
end;
|
|
end;
|
|
ait_realconst:
|
|
ObjData.alloc(tai_realconst(hp).savesize);
|
|
ait_const:
|
|
begin
|
|
{ Recalculate relative symbols }
|
|
if assigned(tai_const(hp).sym) and
|
|
assigned(tai_const(hp).endsym) then
|
|
begin
|
|
objsym:=Objdata.SymbolRef(tai_const(hp).sym);
|
|
objsymend:=Objdata.SymbolRef(tai_const(hp).endsym);
|
|
if Tai_const(hp).consttype in [aitconst_gottpoff,aitconst_tlsgd,aitconst_tlsdesc] then
|
|
begin
|
|
if objsymend.objsection<>ObjData.CurrObjSec then
|
|
Internalerror(2019092801);
|
|
Tai_const(hp).value:=objsymend.address-ObjData.CurrObjSec.Size+Tai_const(hp).symofs;
|
|
end
|
|
else if objsymend.objsection<>objsym.objsection then
|
|
begin
|
|
if (Tai_const(hp).consttype in [aitconst_uleb128bit,aitconst_sleb128bit]) or
|
|
(objsym.objsection<>ObjData.CurrObjSec) then
|
|
internalerror(200905042);
|
|
end
|
|
{$push} {$R-}{$Q-}
|
|
else
|
|
Tai_const(hp).value:=objsymend.address-objsym.address+Tai_const(hp).symofs;
|
|
end;
|
|
{$pop}
|
|
if (Tai_const(hp).consttype in [aitconst_uleb128bit,aitconst_sleb128bit]) then
|
|
Tai_const(hp).fixsize;
|
|
ObjData.alloc(tai_const(hp).size);
|
|
end;
|
|
ait_section:
|
|
begin
|
|
{ use cached value }
|
|
ObjData.setsection(Tai_section(hp).sec);
|
|
end;
|
|
ait_stab :
|
|
begin
|
|
if assigned(Tai_stab(hp).str) then
|
|
WriteStab(Tai_stab(hp).str);
|
|
end;
|
|
ait_symbol :
|
|
ObjData.SymbolDefine(Tai_symbol(hp).sym);
|
|
ait_symbol_end :
|
|
begin
|
|
objsym:=ObjData.SymbolRef(Tai_symbol_end(hp).sym);
|
|
objsym.size:=ObjData.CurrObjSec.Size-objsym.offset;
|
|
end;
|
|
ait_symbolpair:
|
|
with tai_symbolpair(hp) do
|
|
ObjData.SymbolPairDefine(kind,sym^,value^);
|
|
ait_label :
|
|
ObjData.SymbolDefine(Tai_label(hp).labsym);
|
|
ait_string :
|
|
ObjData.alloc(Tai_string(hp).len);
|
|
ait_instruction :
|
|
ObjData.alloc(Taicpu(hp).Pass1(ObjData));
|
|
ait_cutobject :
|
|
if SmartAsm then
|
|
break;
|
|
ait_directive :
|
|
begin
|
|
case tai_directive(hp).directive of
|
|
asd_indirect_symbol:
|
|
if tai_directive(hp).name='' then
|
|
Internalerror(2009101103)
|
|
else if not SetIndirectToSymbol(Tai(hp.Previous), tai_directive(hp).name) then
|
|
Internalerror(2009101102);
|
|
asd_lazy_reference:
|
|
{ handled in TreePass0 }
|
|
;
|
|
asd_reference:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
asd_thumb_func:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
asd_force_thumb:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
asd_code:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
asd_option:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
{$ifdef OMFOBJSUPPORT}
|
|
asd_omf_linnum_line:
|
|
{ ignore for now, but should be added}
|
|
;
|
|
{$endif OMFOBJSUPPORT}
|
|
asd_cpu:
|
|
begin
|
|
ObjData.CPUType:=cpu_none;
|
|
for cpu:=low(tcputype) to high(tcputype) do
|
|
if cputypestr[cpu]=tai_directive(hp).name then
|
|
begin
|
|
ObjData.CPUType:=cpu;
|
|
break;
|
|
end;
|
|
end;
|
|
else
|
|
internalerror(2010011102);
|
|
end;
|
|
end;
|
|
ait_eabi_attribute :
|
|
begin
|
|
eabi_section:=ObjData.findsection('.ARM.attributes');
|
|
if not(assigned(eabi_section)) then
|
|
Internalerror(2019100702);
|
|
if eabi_section.Size=0 then
|
|
eabi_section.alloc(16);
|
|
eabi_section.alloc(LengthUleb128(tai_eabi_attribute(hp).tag));
|
|
case tai_eabi_attribute(hp).eattr_typ of
|
|
eattrtype_dword:
|
|
eabi_section.alloc(LengthUleb128(tai_eabi_attribute(hp).value));
|
|
eattrtype_ntbs:
|
|
if assigned(tai_eabi_attribute(hp).valuestr) then
|
|
eabi_section.alloc(Length(tai_eabi_attribute(hp).valuestr^)+1)
|
|
else
|
|
eabi_section.alloc(1);
|
|
else
|
|
Internalerror(2019100703);
|
|
end;
|
|
end;
|
|
else
|
|
;
|
|
end;
|
|
hp:=Tai(hp.next);
|
|
end;
|
|
TreePass1:=hp;
|
|
end;
|
|
|
|
|
|
function TInternalAssembler.TreePass2(hp:Tai):Tai;
|
|
var
|
|
fillbuffer : tfillbuffer;
|
|
leblen : byte;
|
|
lebbuf : array[0..63] of byte;
|
|
objsym,
|
|
ref,
|
|
objsymend : TObjSymbol;
|
|
zerobuf : array[0..63] of byte;
|
|
relative_reloc: boolean;
|
|
pdata : pointer;
|
|
ssingle : single;
|
|
ddouble : double;
|
|
{$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)}
|
|
eextended : extended;
|
|
{$else}
|
|
{$ifdef FPC_SOFT_FPUX80}
|
|
eextended : floatx80;
|
|
{$endif}
|
|
{$endif}
|
|
ccomp : comp;
|
|
tmp : word;
|
|
cpu: tcputype;
|
|
ddword : dword;
|
|
eabi_section: TObjSection;
|
|
s: String;
|
|
TmpDataPos: TObjSectionOfs;
|
|
begin
|
|
fillchar(zerobuf,sizeof(zerobuf),0);
|
|
fillchar(objsym,sizeof(objsym),0);
|
|
fillchar(objsymend,sizeof(objsymend),0);
|
|
{ main loop }
|
|
while assigned(hp) do
|
|
begin
|
|
case hp.typ of
|
|
ait_align :
|
|
begin
|
|
if tai_align_abstract(hp).aligntype>ObjData.CurrObjSec.secalign then
|
|
InternalError(2012072301);
|
|
if oso_data in ObjData.CurrObjSec.secoptions then
|
|
ObjData.writebytes(Tai_align_abstract(hp).calculatefillbuf(fillbuffer,oso_executable in ObjData.CurrObjSec.secoptions)^,
|
|
Tai_align_abstract(hp).fillsize)
|
|
else
|
|
ObjData.alloc(Tai_align_abstract(hp).fillsize);
|
|
end;
|
|
ait_section :
|
|
begin
|
|
{ use cached value }
|
|
ObjData.setsection(Tai_section(hp).sec);
|
|
end;
|
|
ait_symbol :
|
|
begin
|
|
ObjOutput.exportsymbol(ObjData.SymbolRef(Tai_symbol(hp).sym));
|
|
end;
|
|
ait_symbol_end :
|
|
begin
|
|
{ recalculate size, as some preceding instructions
|
|
could have been changed to smaller size }
|
|
objsym:=ObjData.SymbolRef(Tai_symbol_end(hp).sym);
|
|
objsym.size:=ObjData.CurrObjSec.Size-objsym.offset;
|
|
end;
|
|
ait_datablock :
|
|
begin
|
|
ObjOutput.exportsymbol(ObjData.SymbolRef(Tai_datablock(hp).sym));
|
|
{$ifdef USE_COMM_IN_BSS}
|
|
if not(writingpackages and
|
|
Tai_datablock(hp).is_global) then
|
|
{$endif USE_COMM_IN_BSS}
|
|
begin
|
|
ObjData.allocalign(used_align(size_2_align(Tai_datablock(hp).size),0,ObjData.CurrObjSec.secalign));
|
|
ObjData.alloc(Tai_datablock(hp).size);
|
|
end;
|
|
end;
|
|
ait_realconst:
|
|
begin
|
|
case tai_realconst(hp).realtyp of
|
|
aitrealconst_s32bit:
|
|
begin
|
|
ssingle:=single(tai_realconst(hp).value.s32val);
|
|
pdata:=@ssingle;
|
|
end;
|
|
aitrealconst_s64bit:
|
|
begin
|
|
ddouble:=double(tai_realconst(hp).value.s64val);
|
|
pdata:=@ddouble;
|
|
end;
|
|
{$if defined(cpuextended) and defined(FPC_HAS_TYPE_EXTENDED)}
|
|
{ can't write full 80 bit floating point constants yet on non-x86 }
|
|
aitrealconst_s80bit:
|
|
begin
|
|
eextended:=extended(tai_realconst(hp).value.s80val);
|
|
pdata:=@eextended;
|
|
end;
|
|
{$else}
|
|
{$ifdef FPC_SOFT_FPUX80}
|
|
{$push}{$warn 6018 off} { Unreachable code due to compile time evaluation }
|
|
aitrealconst_s80bit:
|
|
begin
|
|
if sizeof(tai_realconst(hp).value.s80val) = sizeof(double) then
|
|
eextended:=float64_to_floatx80(float64(double(tai_realconst(hp).value.s80val)))
|
|
else if sizeof(tai_realconst(hp).value.s80val) = sizeof(single) then
|
|
eextended:=float32_to_floatx80(float32(single(tai_realconst(hp).value.s80val)))
|
|
else
|
|
internalerror(2017091903);
|
|
pdata:=@eextended;
|
|
end;
|
|
{$pop}
|
|
{$endif}
|
|
{$endif cpuextended}
|
|
aitrealconst_s64comp:
|
|
begin
|
|
ccomp:=comp(tai_realconst(hp).value.s64compval);
|
|
pdata:=@ccomp;
|
|
end;
|
|
else
|
|
internalerror(2015030501);
|
|
end;
|
|
ObjData.writebytes(pdata^,tai_realconst(hp).datasize);
|
|
ObjData.writebytes(zerobuf,tai_realconst(hp).savesize-tai_realconst(hp).datasize);
|
|
end;
|
|
ait_string :
|
|
ObjData.writebytes(Tai_string(hp).str^,Tai_string(hp).len);
|
|
ait_const :
|
|
begin
|
|
{ Recalculate relative symbols, addresses of forward references
|
|
can be changed in treepass1 }
|
|
relative_reloc:=false;
|
|
if assigned(tai_const(hp).sym) and
|
|
assigned(tai_const(hp).endsym) then
|
|
begin
|
|
objsym:=Objdata.SymbolRef(tai_const(hp).sym);
|
|
objsymend:=Objdata.SymbolRef(tai_const(hp).endsym);
|
|
relative_reloc:=(objsym.objsection<>objsymend.objsection);
|
|
if Tai_const(hp).consttype in [aitconst_gottpoff] then
|
|
begin
|
|
if objsymend.objsection<>ObjData.CurrObjSec then
|
|
Internalerror(2019092802);
|
|
Tai_const(hp).value:=objsymend.address-ObjData.CurrObjSec.Size+Tai_const(hp).symofs;
|
|
end
|
|
else if Tai_const(hp).consttype in [aitconst_tlsgd,aitconst_tlsdesc] then
|
|
begin
|
|
if objsymend.objsection<>ObjData.CurrObjSec then
|
|
Internalerror(2019092803);
|
|
Tai_const(hp).value:=ObjData.CurrObjSec.Size-objsymend.address+Tai_const(hp).symofs;
|
|
end
|
|
else if objsymend.objsection<>objsym.objsection then
|
|
begin
|
|
if (Tai_const(hp).consttype in [aitconst_uleb128bit,aitconst_sleb128bit]) or
|
|
(objsym.objsection<>ObjData.CurrObjSec) then
|
|
internalerror(2019010301);
|
|
end
|
|
else
|
|
{$push} {$R-}{$Q-}
|
|
Tai_const(hp).value:=objsymend.address-objsym.address+Tai_const(hp).symofs;
|
|
end;
|
|
{$pop}
|
|
case tai_const(hp).consttype of
|
|
aitconst_64bit,
|
|
aitconst_32bit,
|
|
aitconst_16bit,
|
|
aitconst_64bit_unaligned,
|
|
aitconst_32bit_unaligned,
|
|
aitconst_16bit_unaligned,
|
|
aitconst_8bit :
|
|
begin
|
|
if assigned(tai_const(hp).sym) and
|
|
not assigned(tai_const(hp).endsym) then
|
|
ObjData.writereloc(Tai_const(hp).symofs,tai_const(hp).size,Objdata.SymbolRef(tai_const(hp).sym),RELOC_ABSOLUTE)
|
|
else if relative_reloc then
|
|
ObjData.writereloc(ObjData.CurrObjSec.size+tai_const(hp).size-objsym.address+tai_const(hp).symofs,tai_const(hp).size,objsymend,RELOC_RELATIVE)
|
|
else
|
|
ObjData.writebytes(Tai_const(hp).value,tai_const(hp).size);
|
|
end;
|
|
aitconst_rva_symbol :
|
|
begin
|
|
{ PE32+? }
|
|
if target_info.system in systems_peoptplus then
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_RVA)
|
|
else
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(pint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_RVA);
|
|
end;
|
|
aitconst_secrel32_symbol :
|
|
begin
|
|
{ Required for DWARF2 support under Windows }
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_SECREL32);
|
|
end;
|
|
{$ifdef i8086}
|
|
aitconst_farptr :
|
|
if assigned(tai_const(hp).sym) and
|
|
not assigned(tai_const(hp).endsym) then
|
|
ObjData.writereloc(Tai_const(hp).symofs,tai_const(hp).size,Objdata.SymbolRef(tai_const(hp).sym),RELOC_FARPTR)
|
|
else if relative_reloc then
|
|
internalerror(2015040601)
|
|
else
|
|
ObjData.writebytes(Tai_const(hp).value,tai_const(hp).size);
|
|
aitconst_seg:
|
|
if assigned(tai_const(hp).sym) and (tai_const(hp).size=2) then
|
|
ObjData.writereloc(0,2,Objdata.SymbolRef(tai_const(hp).sym),RELOC_SEG)
|
|
else
|
|
internalerror(2015110502);
|
|
aitconst_dgroup:
|
|
ObjData.writereloc(0,2,nil,RELOC_DGROUP);
|
|
aitconst_fardataseg:
|
|
ObjData.writereloc(0,2,nil,RELOC_FARDATASEG);
|
|
{$endif i8086}
|
|
{$ifdef arm}
|
|
aitconst_got:
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_GOT32);
|
|
{ aitconst_gottpoff:
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_TPOFF); }
|
|
aitconst_tpoff:
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_TPOFF);
|
|
aitconst_tlsgd:
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_TLSGD);
|
|
aitconst_tlsdesc:
|
|
begin
|
|
{ must be a relative symbol, thus value being valid }
|
|
if not(assigned(tai_const(hp).sym)) or not(assigned(tai_const(hp).endsym)) then
|
|
Internalerror(2019092904);
|
|
ObjData.writereloc(Tai_const(hp).value,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_TLSDESC);
|
|
end;
|
|
{$endif arm}
|
|
aitconst_dtpoff:
|
|
{ so far, the size of dtpoff is fixed to 4 bytes }
|
|
ObjData.writereloc(Tai_const(hp).symofs,4,Objdata.SymbolRef(tai_const(hp).sym),RELOC_DTPOFF);
|
|
aitconst_gotoff_symbol:
|
|
ObjData.writereloc(Tai_const(hp).symofs,sizeof(longint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_GOTOFF);
|
|
aitconst_uleb128bit,
|
|
aitconst_sleb128bit :
|
|
begin
|
|
if Tai_const(hp).fixed_size=0 then
|
|
Internalerror(2019030302);
|
|
if tai_const(hp).consttype=aitconst_uleb128bit then
|
|
leblen:=EncodeUleb128(qword(Tai_const(hp).value),lebbuf,Tai_const(hp).fixed_size)
|
|
else
|
|
leblen:=EncodeSleb128(Tai_const(hp).value,lebbuf,Tai_const(hp).fixed_size);
|
|
if leblen<>tai_const(hp).fixed_size then
|
|
internalerror(200709271);
|
|
ObjData.writebytes(lebbuf,leblen);
|
|
end;
|
|
aitconst_darwin_dwarf_delta32,
|
|
aitconst_darwin_dwarf_delta64:
|
|
ObjData.writebytes(Tai_const(hp).value,tai_const(hp).size);
|
|
aitconst_half16bit,
|
|
aitconst_gs:
|
|
begin
|
|
tmp:=Tai_const(hp).value div 2;
|
|
ObjData.writebytes(tmp,2);
|
|
end;
|
|
else
|
|
internalerror(200603254);
|
|
end;
|
|
end;
|
|
ait_label :
|
|
begin
|
|
{ exporting shouldn't be necessary as labels are local,
|
|
but it's better to be on the safe side (PFV) }
|
|
ObjOutput.exportsymbol(ObjData.SymbolRef(Tai_label(hp).labsym));
|
|
end;
|
|
ait_instruction :
|
|
Taicpu(hp).Pass2(ObjData);
|
|
ait_stab :
|
|
WriteStab(Tai_stab(hp).str);
|
|
ait_function_name,
|
|
ait_force_line : ;
|
|
ait_cutobject :
|
|
if SmartAsm then
|
|
break;
|
|
ait_directive :
|
|
begin
|
|
case tai_directive(hp).directive of
|
|
asd_weak_definition,
|
|
asd_weak_reference:
|
|
begin
|
|
objsym:=ObjData.symbolref(tai_directive(hp).name);
|
|
if objsym.bind in [AB_EXTERNAL,AB_WEAK_EXTERNAL] then
|
|
objsym.bind:=AB_WEAK_EXTERNAL
|
|
else
|
|
{ TODO: should become a weak definition; for now, do
|
|
the same as what was done for ait_weak }
|
|
objsym.bind:=AB_WEAK_EXTERNAL;
|
|
end;
|
|
asd_cpu:
|
|
begin
|
|
ObjData.CPUType:=cpu_none;
|
|
for cpu:=low(tcputype) to high(tcputype) do
|
|
if cputypestr[cpu]=tai_directive(hp).name then
|
|
begin
|
|
ObjData.CPUType:=cpu;
|
|
break;
|
|
end;
|
|
end;
|
|
{$ifdef OMFOBJSUPPORT}
|
|
asd_omf_linnum_line:
|
|
begin
|
|
TOmfObjSection(ObjData.CurrObjSec).LinNumEntries.Add(
|
|
TOmfSubRecord_LINNUM_MsLink_Entry.Create(
|
|
strtoint(tai_directive(hp).name),
|
|
ObjData.CurrObjSec.Size
|
|
));
|
|
end;
|
|
{$endif OMFOBJSUPPORT}
|
|
else
|
|
;
|
|
end
|
|
end;
|
|
ait_symbolpair:
|
|
begin
|
|
if tai_symbolpair(hp).kind=spk_set then
|
|
begin
|
|
objsym:=ObjData.symbolref(tai_symbolpair(hp).sym^);
|
|
ref:=objdata.symbolref(tai_symbolpair(hp).value^);
|
|
|
|
objsym.offset:=ref.offset;
|
|
objsym.objsection:=ref.objsection;
|
|
{$ifdef arm}
|
|
objsym.ThumbFunc:=ref.ThumbFunc;
|
|
{$endif arm}
|
|
end;
|
|
end;
|
|
{$ifndef DISABLE_WIN64_SEH}
|
|
ait_seh_directive :
|
|
tai_seh_directive(hp).generate_code(objdata);
|
|
{$endif DISABLE_WIN64_SEH}
|
|
ait_eabi_attribute :
|
|
begin
|
|
eabi_section:=ObjData.findsection('.ARM.attributes');
|
|
if not(assigned(eabi_section)) then
|
|
Internalerror(2019100704);
|
|
if eabi_section.Size=0 then
|
|
begin
|
|
s:='A';
|
|
eabi_section.write(s[1],1);
|
|
ddword:=eabi_section.Size-1;
|
|
eabi_section.write(ddword,4);
|
|
s:='aeabi'#0;
|
|
eabi_section.write(s[1],6);
|
|
s:=#1;
|
|
eabi_section.write(s[1],1);
|
|
ddword:=eabi_section.Size-1-4-6-1;
|
|
eabi_section.write(ddword,4);
|
|
end;
|
|
leblen:=EncodeUleb128(tai_eabi_attribute(hp).tag,lebbuf,0);
|
|
eabi_section.write(lebbuf,leblen);
|
|
|
|
case tai_eabi_attribute(hp).eattr_typ of
|
|
eattrtype_dword:
|
|
begin
|
|
leblen:=EncodeUleb128(tai_eabi_attribute(hp).value,lebbuf,0);
|
|
eabi_section.write(lebbuf,leblen);
|
|
end;
|
|
eattrtype_ntbs:
|
|
begin
|
|
if assigned(tai_eabi_attribute(hp).valuestr) then
|
|
s:=tai_eabi_attribute(hp).valuestr^+#0
|
|
else
|
|
s:=#0;
|
|
eabi_section.write(s[1],Length(s));
|
|
end
|
|
else
|
|
Internalerror(2019100705);
|
|
end;
|
|
{ update size of attributes section, write directly to the dyn. arrays as
|
|
we do not increase the size of section }
|
|
TmpDataPos:=eabi_section.Data.Pos;
|
|
eabi_section.Data.seek(1);
|
|
ddword:=eabi_section.Size-1;
|
|
eabi_section.Data.write(ddword,4);
|
|
eabi_section.Data.seek(12);
|
|
ddword:=eabi_section.Size-1-4-6;
|
|
eabi_section.Data.write(ddword,4);
|
|
eabi_section.Data.Seek(TmpDataPos);
|
|
end;
|
|
else
|
|
;
|
|
end;
|
|
hp:=Tai(hp.next);
|
|
end;
|
|
TreePass2:=hp;
|
|
end;
|
|
|
|
|
|
procedure TInternalAssembler.writetree;
|
|
label
|
|
doexit;
|
|
var
|
|
hp : Tai;
|
|
ObjWriter : TObjectWriter;
|
|
begin
|
|
ObjWriter:=TObjectwriter.create;
|
|
ObjOutput:=CObjOutput.Create(ObjWriter);
|
|
ObjData:=ObjOutput.newObjData(ObjFileName);
|
|
|
|
{ Pass 0 }
|
|
ObjData.currpass:=0;
|
|
ObjData.createsection(sec_code);
|
|
ObjData.beforealloc;
|
|
{ start with list 1 }
|
|
currlistidx:=1;
|
|
currlist:=list[currlistidx];
|
|
hp:=Tai(currList.first);
|
|
while assigned(hp) do
|
|
begin
|
|
hp:=TreePass0(hp);
|
|
MaybeNextList(hp);
|
|
end;
|
|
ObjData.afteralloc;
|
|
{ leave if errors have occurred }
|
|
if errorcount>0 then
|
|
goto doexit;
|
|
|
|
{ Pass 1 }
|
|
ObjData.currpass:=1;
|
|
ObjData.resetsections;
|
|
ObjData.beforealloc;
|
|
ObjData.createsection(sec_code);
|
|
{ start with list 1 }
|
|
currlistidx:=1;
|
|
currlist:=list[currlistidx];
|
|
hp:=Tai(currList.first);
|
|
while assigned(hp) do
|
|
begin
|
|
hp:=TreePass1(hp);
|
|
MaybeNextList(hp);
|
|
end;
|
|
ObjData.createsection(sec_code);
|
|
ObjData.afteralloc;
|
|
|
|
{ leave if errors have occurred }
|
|
if errorcount>0 then
|
|
goto doexit;
|
|
|
|
{ Pass 2 }
|
|
ObjData.currpass:=2;
|
|
ObjData.resetsections;
|
|
ObjData.beforewrite;
|
|
ObjData.createsection(sec_code);
|
|
{ start with list 1 }
|
|
currlistidx:=1;
|
|
currlist:=list[currlistidx];
|
|
hp:=Tai(currList.first);
|
|
while assigned(hp) do
|
|
begin
|
|
hp:=TreePass2(hp);
|
|
MaybeNextList(hp);
|
|
end;
|
|
ObjData.createsection(sec_code);
|
|
ObjData.afterwrite;
|
|
|
|
{ don't write the .o file if errors have occurred }
|
|
if errorcount=0 then
|
|
begin
|
|
{ write objectfile }
|
|
ObjOutput.startobjectfile(ObjFileName);
|
|
ObjOutput.writeobjectfile(ObjData);
|
|
end;
|
|
|
|
doexit:
|
|
{ Cleanup }
|
|
ObjData.free;
|
|
ObjData:=nil;
|
|
ObjWriter.free;
|
|
end;
|
|
|
|
|
|
procedure TInternalAssembler.writetreesmart;
|
|
var
|
|
hp : Tai;
|
|
startsectype : TAsmSectiontype;
|
|
place: tcutplace;
|
|
ObjWriter : TObjectWriter;
|
|
startsecname: String;
|
|
startsecorder: TAsmSectionOrder;
|
|
begin
|
|
if not(cs_asm_leave in current_settings.globalswitches) and
|
|
not(af_needar in asminfo^.flags) then
|
|
ObjWriter:=CInternalAr.CreateAr(current_module.staticlibfilename)
|
|
else
|
|
ObjWriter:=TObjectwriter.create;
|
|
|
|
NextSmartName(cut_normal);
|
|
ObjOutput:=CObjOutput.Create(ObjWriter);
|
|
startsectype:=sec_none;
|
|
startsecname:='';
|
|
startsecorder:=secorder_default;
|
|
|
|
{ start with list 1 }
|
|
currlistidx:=1;
|
|
currlist:=list[currlistidx];
|
|
hp:=Tai(currList.first);
|
|
while assigned(hp) do
|
|
begin
|
|
ObjData:=ObjOutput.newObjData(ObjFileName);
|
|
|
|
{ Pass 0 }
|
|
ObjData.currpass:=0;
|
|
ObjData.resetsections;
|
|
ObjData.beforealloc;
|
|
if startsectype<>sec_none then
|
|
ObjData.CreateSection(startsectype,startsecname,startsecorder);
|
|
TreePass0(hp);
|
|
ObjData.afteralloc;
|
|
{ leave if errors have occurred }
|
|
if errorcount>0 then
|
|
break;
|
|
|
|
{ Pass 1 }
|
|
ObjData.currpass:=1;
|
|
ObjData.resetsections;
|
|
ObjData.beforealloc;
|
|
if startsectype<>sec_none then
|
|
ObjData.CreateSection(startsectype,startsecname,startsecorder);
|
|
TreePass1(hp);
|
|
ObjData.afteralloc;
|
|
|
|
{ leave if errors have occurred }
|
|
if errorcount>0 then
|
|
break;
|
|
|
|
{ Pass 2 }
|
|
ObjData.currpass:=2;
|
|
ObjOutput.startobjectfile(ObjFileName);
|
|
ObjData.resetsections;
|
|
ObjData.beforewrite;
|
|
if startsectype<>sec_none then
|
|
ObjData.CreateSection(startsectype,startsecname,startsecorder);
|
|
hp:=TreePass2(hp);
|
|
ObjData.afterwrite;
|
|
|
|
{ leave if errors have occurred }
|
|
if errorcount>0 then
|
|
break;
|
|
|
|
{ write the current objectfile }
|
|
ObjOutput.writeobjectfile(ObjData);
|
|
ObjData.free;
|
|
ObjData:=nil;
|
|
|
|
{ end of lists? }
|
|
if not MaybeNextList(hp) then
|
|
break;
|
|
|
|
{ we will start a new objectfile so reset everything }
|
|
{ The place can still change in the next while loop, so don't init }
|
|
{ the writer yet (JM) }
|
|
if (hp.typ=ait_cutobject) then
|
|
place := Tai_cutobject(hp).place
|
|
else
|
|
place := cut_normal;
|
|
|
|
{ avoid empty files }
|
|
startsectype:=sec_none;
|
|
startsecname:='';
|
|
startsecorder:=secorder_default;
|
|
while assigned(hp) and
|
|
(Tai(hp).typ in [ait_marker,ait_comment,ait_section,ait_cutobject]) do
|
|
begin
|
|
if Tai(hp).typ=ait_section then
|
|
begin
|
|
startsectype:=Tai_section(hp).sectype;
|
|
startsecname:=Tai_section(hp).name^;
|
|
startsecorder:=Tai_section(hp).secorder;
|
|
end;
|
|
if (Tai(hp).typ=ait_cutobject) then
|
|
place:=Tai_cutobject(hp).place;
|
|
hp:=Tai(hp.next);
|
|
end;
|
|
|
|
if not MaybeNextList(hp) then
|
|
break;
|
|
|
|
{ start next objectfile }
|
|
NextSmartName(place);
|
|
end;
|
|
ObjData.free;
|
|
ObjData:=nil;
|
|
ObjWriter.free;
|
|
end;
|
|
|
|
|
|
procedure TInternalAssembler.MakeObject;
|
|
|
|
var to_do:set of TasmlistType;
|
|
i:TasmlistType;
|
|
|
|
procedure addlist(p:TAsmList);
|
|
begin
|
|
inc(lists);
|
|
list[lists]:=p;
|
|
end;
|
|
|
|
begin
|
|
to_do:=[low(Tasmlisttype)..high(Tasmlisttype)];
|
|
if usedeffileforexports then
|
|
exclude(to_do,al_exports);
|
|
if not(tf_section_threadvars in target_info.flags) then
|
|
exclude(to_do,al_threadvars);
|
|
for i:=low(TasmlistType) to high(TasmlistType) do
|
|
if (i in to_do) and (current_asmdata.asmlists[i]<>nil) and
|
|
(not current_asmdata.asmlists[i].empty) then
|
|
addlist(current_asmdata.asmlists[i]);
|
|
|
|
if SmartAsm then
|
|
writetreesmart
|
|
else
|
|
writetree;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
Generate Assembler Files Main Procedure
|
|
*****************************************************************************}
|
|
|
|
Procedure GenerateAsm(smart:boolean);
|
|
var
|
|
a : TAssembler;
|
|
begin
|
|
if not assigned(CAssembler[target_asm.id]) then
|
|
Message(asmw_f_assembler_output_not_supported);
|
|
a:=CAssembler[target_asm.id].Create(@target_asm,smart);
|
|
a.MakeObject;
|
|
a.Free;
|
|
end;
|
|
|
|
|
|
function GetExternalGnuAssemblerWithAsmInfoWriter(info: pasminfo; wr: TExternalAssemblerOutputFile): TExternalAssembler;
|
|
var
|
|
asmkind: tasm;
|
|
begin
|
|
for asmkind in [as_gas,as_ggas,as_darwin,as_clang_gas,as_clang_asdarwin] do
|
|
if assigned(asminfos[asmkind]) and
|
|
(target_info.system in asminfos[asmkind]^.supported_targets) then
|
|
begin
|
|
result:=TExternalAssemblerClass(CAssembler[asmkind]).CreateWithWriter(asminfos[asmkind],wr,false,false);
|
|
exit;
|
|
end;
|
|
Internalerror(2015090604);
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
Init/Done
|
|
*****************************************************************************}
|
|
|
|
procedure RegisterAssembler(const r:tasminfo;c:TAssemblerClass);
|
|
var
|
|
t : tasm;
|
|
begin
|
|
t:=r.id;
|
|
if assigned(asminfos[t]) then
|
|
writeln('Warning: Assembler is already registered!')
|
|
else
|
|
Getmem(asminfos[t],sizeof(tasminfo));
|
|
asminfos[t]^:=r;
|
|
CAssembler[t]:=c;
|
|
end;
|
|
|
|
end.
|