summaryrefslogtreecommitdiff
path: root/compiler/assemble.pas
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/assemble.pas')
-rw-r--r--compiler/assemble.pas1482
1 files changed, 1482 insertions, 0 deletions
diff --git a/compiler/assemble.pas b/compiler/assemble.pas
new file mode 100644
index 0000000000..92f47eca39
--- /dev/null
+++ b/compiler/assemble.pas
@@ -0,0 +1,1482 @@
+{
+ 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
+{$IFDEF USE_SYSUTILS}
+ sysutils,
+{$ELSE USE_SYSUTILS}
+ strings,
+ dos,
+{$ENDIF USE_SYSUTILS}
+ systems,globtype,globals,aasmbase,aasmtai,ogbase;
+
+ const
+ { maximum of aasmoutput lists there will be }
+ maxoutputlists = 20;
+ { buffer size for writing the .s file }
+ AsmOutSize=32768;
+
+ type
+ TAssembler=class(TAbstractAssembler)
+ public
+ {filenames}
+ path : pathstr;
+ name : namestr;
+ asmfile, { current .s and .o file }
+ objfile : string;
+ ppufilename : string;
+ asmprefix : string;
+ SmartAsm : boolean;
+ SmartFilesCount,
+ SmartHeaderCount : longint;
+ Constructor Create(smart:boolean);virtual;
+ Destructor Destroy;override;
+ procedure NextSmartName(place:tcutplace);
+ procedure MakeObject;virtual;abstract;
+ end;
+
+ {# This is the base class which should be overriden 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
+ procedure CreateSmartLinkPath(const s:string);
+ protected
+ {outfile}
+ AsmSize,
+ AsmStartSize,
+ outcnt : longint;
+ outbuf : array[0..AsmOutSize-1] of char;
+ outfile : file;
+ ioerror : boolean;
+ 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;
+ Procedure RemoveAsm;
+ Procedure AsmFlush;
+ Procedure AsmClear;
+
+ {# Write a string to the assembler file }
+ Procedure AsmWrite(const s:string);
+
+ {# 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 s:string);
+
+ {# Write a new line to the assembler file }
+ Procedure AsmLn;
+
+ procedure AsmCreate(Aplace:tcutplace);
+ procedure AsmClose;
+
+ {# This routine should be overriden for each assembler, it is used
+ to actually write the abstract assembler stream to file.}
+ procedure WriteTree(p:TAAsmoutput);virtual;
+
+ {# This routine should be overriden 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;
+ public
+ Constructor Create(smart:boolean);override;
+ procedure MakeObject;override;
+ end;
+
+ TInternalAssembler=class(TAssembler)
+ public
+ constructor create(smart:boolean);override;
+ destructor destroy;override;
+ procedure MakeObject;override;
+ protected
+ objectdata : TAsmObjectData;
+ objectoutput : tobjectoutput;
+ private
+ { the aasmoutput lists that need to be processed }
+ lists : byte;
+ list : array[1..maxoutputlists] of TAAsmoutput;
+ { current processing }
+ currlistidx : byte;
+ currlist : TAAsmoutput;
+ currpass : byte;
+ procedure convertstab(p:pchar);
+ function MaybeNextList(var hp:Tai):boolean;
+ function TreePass0(hp:Tai):Tai;
+ function TreePass1(hp:Tai):Tai;
+ function TreePass2(hp:Tai):Tai;
+ procedure writetree;
+ procedure writetreesmart;
+ end;
+
+ TAssemblerClass = class of TAssembler;
+
+ Procedure GenerateAsm(smart:boolean);
+ Procedure OnlyAsm;
+
+ procedure RegisterAssembler(const r:tasminfo;c:TAssemblerClass);
+ procedure InitAssembler;
+ procedure DoneAssembler;
+
+
+Implementation
+
+ uses
+{$ifdef hasunix}
+ {$ifdef havelinuxrtl10}
+ linux,
+ {$else}
+ unix,
+ {$endif}
+{$endif}
+ cutils,script,fmodule,verbose,
+{$ifdef memdebug}
+ cclasses,
+{$endif memdebug}
+{$ifdef m68k}
+ cpuinfo,
+{$endif m68k}
+ aasmcpu
+ ;
+
+ var
+ CAssembler : array[tasm] of TAssemblerClass;
+
+
+{*****************************************************************************
+ TAssembler
+*****************************************************************************}
+
+ Constructor TAssembler.Create(smart:boolean);
+ begin
+ { load start values }
+ asmfile:=current_module.get_asmfilename;
+ objfile:=current_module.objfilename^;
+ name:=Lower(current_module.modulename^);
+ path:=current_module.outputpath^;
+ asmprefix := current_module.asmprefix^;
+ if not assigned(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;
+ AsmFile:=Path+FixFileName(s+tostr(SmartFilesCount)+target_info.asmext);
+ ObjFile:=Path+FixFileName(s+tostr(SmartFilesCount)+target_info.objext);
+ { insert in container so it can be cleared after the linking }
+ SmartLinkOFiles.Insert(Objfile);
+ end;
+
+
+{*****************************************************************************
+ TExternalAssembler
+*****************************************************************************}
+
+ Function DoPipe:boolean;
+ begin
+ DoPipe:=(cs_asm_pipe in aktglobalswitches) and
+ not(cs_asm_leave in aktglobalswitches)
+ and ((target_asm.id in [as_gas,as_darwin]));
+ end;
+
+
+ Constructor TExternalAssembler.Create(smart:boolean);
+ begin
+ inherited Create(smart);
+ if SmartAsm then
+ begin
+ path:=FixPath(path+FixFileName(name)+target_info.smartext,false);
+ CreateSmartLinkPath(path);
+ end;
+ Outcnt:=0;
+ end;
+
+
+ procedure TExternalAssembler.CreateSmartLinkPath(const s:string);
+ var
+{$IFDEF USE_SYSUTILS}
+ dir : TSearchRec;
+{$ELSE USE_SYSUTILS}
+ dir : searchrec;
+{$ENDIF USE_SYSUTILS}
+ hs : string;
+ begin
+ if PathExists(s) then
+ begin
+ { the path exists, now we clean only all the .o and .s files }
+ { .o files }
+{$IFDEF USE_SYSUTILS}
+ if findfirst(s+source_info.dirsep+'*'+target_info.objext,faAnyFile,dir) = 0
+ then repeat
+ RemoveFile(s+source_info.dirsep+dir.name);
+ until findnext(dir) <> 0;
+{$ELSE USE_SYSUTILS}
+ findfirst(s+source_info.dirsep+'*'+target_info.objext,anyfile,dir);
+ while (doserror=0) do
+ begin
+ RemoveFile(s+source_info.dirsep+dir.name);
+ findnext(dir);
+ end;
+{$ENDIF USE_SYSUTILS}
+ findclose(dir);
+ { .s files }
+{$IFDEF USE_SYSUTILS}
+ if findfirst(s+source_info.dirsep+'*'+target_info.asmext,faAnyFile,dir) = 0
+ then repeat
+ RemoveFile(s+source_info.dirsep+dir.name);
+ until findnext(dir) <> 0;
+{$ELSE USE_SYSUTILS}
+ findfirst(s+source_info.dirsep+'*'+target_info.asmext,anyfile,dir);
+ while (doserror=0) do
+ begin
+ RemoveFile(s+source_info.dirsep+dir.name);
+ findnext(dir);
+ end;
+{$ENDIF USE_SYSUTILS}
+ findclose(dir);
+ end
+ else
+ begin
+ hs:=s;
+ if hs[length(hs)] in ['/','\'] then
+ delete(hs,length(hs),1);
+ {$I-}
+ mkdir(hs);
+ {$I+}
+ if ioresult<>0 then;
+ end;
+ end;
+
+
+ const
+ lastas : byte=255;
+ var
+ LastASBin : pathstr;
+ Function TExternalAssembler.FindAssembler:string;
+ var
+ asfound : boolean;
+ UtilExe : string;
+ begin
+ asfound:=false;
+ if cs_link_on_target in aktglobalswitches then
+ begin
+ { If linking on target, don't add any path PM }
+ FindAssembler:=utilsprefix+AddExtension(target_asm.asmbin,target_info.exeext);
+ exit;
+ end
+ else
+ UtilExe:=utilsprefix+AddExtension(target_asm.asmbin,source_info.exeext);
+ if lastas<>ord(target_asm.id) then
+ begin
+ lastas:=ord(target_asm.id);
+ { is an assembler passed ? }
+ if utilsdirectory<>'' then
+ asfound:=FindFile(UtilExe,utilsdirectory,LastASBin);
+ if not AsFound then
+ asfound:=FindExe(UtilExe,LastASBin);
+ if (not asfound) and not(cs_asm_extern in aktglobalswitches) then
+ begin
+ Message1(exec_e_assembler_not_found,LastASBin);
+ aktglobalswitches:=aktglobalswitches+[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;
+{$IFDEF USE_SYSUTILS}
+ var
+ DosExitCode:Integer;
+{$ENDIF USE_SYSUTILS}
+ begin
+ callassembler:=true;
+ if not(cs_asm_extern in aktglobalswitches) then
+{$IFDEF USE_SYSUTILS}
+ try
+ DosExitCode := ExecuteProcess(command,para);
+ if DosExitCode <>0
+ then begin
+ Message1(exec_e_error_while_assembling,tostr(dosexitcode));
+ callassembler:=false;
+ end;
+ except on E:EOSError do
+ begin
+ Message1(exec_e_cant_call_assembler,tostr(E.ErrorCode));
+ aktglobalswitches:=aktglobalswitches+[cs_asm_extern];
+ callassembler:=false;
+ end
+ end
+{$ELSE USE_SYSUTILS}
+ begin
+ swapvectors;
+ exec(maybequoted(command),para);
+ swapvectors;
+ if (doserror<>0) then
+ begin
+ Message1(exec_e_cant_call_assembler,tostr(doserror));
+ aktglobalswitches:=aktglobalswitches+[cs_asm_extern];
+ callassembler:=false;
+ end
+ else
+ if (dosexitcode<>0) then
+ begin
+ Message1(exec_e_error_while_assembling,tostr(dosexitcode));
+ callassembler:=false;
+ end;
+ end
+{$ENDIF USE_SYSUTILS}
+ else
+ AsmRes.AddAsmCommand(command,para,name);
+ end;
+
+
+ procedure TExternalAssembler.RemoveAsm;
+ var
+ g : file;
+ begin
+ if cs_asm_leave in aktglobalswitches then
+ exit;
+ if cs_asm_extern in aktglobalswitches then
+ AsmRes.AddDeleteCommand(AsmFile)
+ else
+ begin
+ assign(g,AsmFile);
+ {$I-}
+ erase(g);
+ {$I+}
+ if ioresult<>0 then;
+ end;
+ end;
+
+
+ Function TExternalAssembler.DoAssemble:boolean;
+ var
+ s : TCmdStr;
+ begin
+ DoAssemble:=true;
+ if DoPipe then
+ exit;
+ if not(cs_asm_extern in aktglobalswitches) then
+ begin
+ if SmartAsm then
+ begin
+ if (SmartFilesCount<=1) then
+ Message1(exec_i_assembling_smart,name);
+ end
+ else
+ Message1(exec_i_assembling,name);
+ end;
+ s:=target_asm.asmcmd;
+{$ifdef m68k}
+ if aktoptprocessor = MC68020 then
+ s:='-m68020 '+s
+ else
+ s:='-m68000 '+s;
+{$endif}
+ if (cs_link_on_target in aktglobalswitches) then
+ begin
+ Replace(s,'$ASM',maybequoted(ScriptFixFileName(AsmFile)));
+ Replace(s,'$OBJ',maybequoted(ScriptFixFileName(ObjFile)));
+ end
+ else
+ begin
+ Replace(s,'$ASM',maybequoted(AsmFile));
+ Replace(s,'$OBJ',maybequoted(ObjFile));
+ end;
+ if CallAssembler(FindAssembler,s) then
+ RemoveAsm
+ else
+ begin
+ DoAssemble:=false;
+ GenerateError;
+ end;
+ end;
+
+
+ Procedure TExternalAssembler.AsmFlush;
+ begin
+ if outcnt>0 then
+ begin
+ { suppress i/o error }
+ {$i-}
+ BlockWrite(outfile,outbuf,outcnt);
+ {$i+}
+ ioerror:=ioerror or (ioresult<>0);
+ outcnt:=0;
+ end;
+ end;
+
+
+ Procedure TExternalAssembler.AsmClear;
+ begin
+ outcnt:=0;
+ end;
+
+
+ Procedure TExternalAssembler.AsmWrite(const s:string);
+ begin
+ if OutCnt+length(s)>=AsmOutSize then
+ AsmFlush;
+ Move(s[1],OutBuf[OutCnt],length(s));
+ inc(OutCnt,length(s));
+ inc(AsmSize,length(s));
+ end;
+
+
+ Procedure TExternalAssembler.AsmWriteLn(const s:string);
+ begin
+ AsmWrite(s);
+ AsmLn;
+ end;
+
+
+ Procedure TExternalAssembler.AsmWritePChar(p:pchar);
+ var
+ i,j : longint;
+ begin
+ i:=StrLen(p);
+ 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;
+
+
+ Procedure TExternalAssembler.AsmLn;
+ begin
+ if OutCnt>=AsmOutSize-2 then
+ AsmFlush;
+ if (cs_link_on_target in aktglobalswitches) then
+ begin
+ OutBuf[OutCnt]:=target_info.newline[1];
+ inc(OutCnt);
+ inc(AsmSize);
+ if length(target_info.newline)>1 then
+ begin
+ OutBuf[OutCnt]:=target_info.newline[2];
+ inc(OutCnt);
+ inc(AsmSize);
+ end;
+ end
+ else
+ begin
+ OutBuf[OutCnt]:=source_info.newline[1];
+ inc(OutCnt);
+ inc(AsmSize);
+ if length(source_info.newline)>1 then
+ begin
+ OutBuf[OutCnt]:=source_info.newline[2];
+ inc(OutCnt);
+ inc(AsmSize);
+ end;
+ end;
+ end;
+
+
+ procedure TExternalAssembler.AsmCreate(Aplace:tcutplace);
+ begin
+ if SmartAsm then
+ NextSmartName(Aplace);
+{$ifdef hasunix}
+ if DoPipe then
+ begin
+ Message1(exec_i_assembling_pipe,asmfile);
+ POpen(outfile,'as -o '+objfile,'W');
+ end
+ else
+{$endif}
+ begin
+ Assign(outfile,asmfile);
+ {$I-}
+ Rewrite(outfile,1);
+ {$I+}
+ if ioresult<>0 then
+ begin
+ ioerror:=true;
+ Message1(exec_d_cant_create_asmfile,asmfile);
+ end;
+ end;
+ outcnt:=0;
+ AsmSize:=0;
+ AsmStartSize:=0;
+ end;
+
+
+ procedure TExternalAssembler.AsmClose;
+ var
+ f : file;
+ FileAge : longint;
+ begin
+ AsmFlush;
+{$ifdef hasunix}
+ if 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 ppufilename<>'' then
+ begin
+ Assign(f,ppufilename);
+ {$I-}
+ reset(f,1);
+ {$I+}
+ if ioresult=0 then
+ begin
+{$IFDEF USE_SYSUTILS}
+ FileAge := FileGetDate(GetFileHandle(f));
+{$ELSE USE_SYSUTILS}
+ GetFTime(f, FileAge);
+{$ENDIF USE_SYSUTILS}
+ close(f);
+ reset(outfile,1);
+{$IFDEF USE_SYSUTILS}
+ FileSetDate(GetFileHandle(outFile),FileAge);
+{$ELSE USE_SYSUTILS}
+ SetFTime(f, FileAge);
+{$ENDIF USE_SYSUTILS}
+ end;
+ end;
+ close(outfile);
+ end;
+ end;
+
+
+ procedure TExternalAssembler.WriteTree(p:TAAsmoutput);
+ begin
+ end;
+
+
+ procedure TExternalAssembler.WriteAsmList;
+ begin
+ end;
+
+
+ procedure TExternalAssembler.MakeObject;
+ begin
+ AsmCreate(cut_normal);
+ WriteAsmList;
+ AsmClose;
+ if not(ioerror) then
+ DoAssemble;
+ end;
+
+
+{*****************************************************************************
+ TInternalAssembler
+*****************************************************************************}
+
+ constructor TInternalAssembler.create(smart:boolean);
+ begin
+ inherited create(smart);
+ objectoutput:=nil;
+ objectdata:=nil;
+ SmartAsm:=smart;
+ currpass:=0;
+ end;
+
+
+ destructor TInternalAssembler.destroy;
+{$ifdef MEMDEBUG}
+ var
+ d : tmemdebug;
+{$endif}
+ begin
+{$ifdef MEMDEBUG}
+ d := tmemdebug.create(name+' - agbin');
+{$endif}
+ objectdata.free;
+ objectoutput.free;
+{$ifdef MEMDEBUG}
+ d.free;
+{$endif}
+ end;
+
+
+ procedure TInternalAssembler.convertstab(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:tasmsymbol;out value:longint):boolean;
+ var
+ hs : string;
+ len,
+ code : integer;
+ pstart : pchar;
+ sym : tasmsymbol;
+ exprvalue : longint;
+ gotmin,
+ dosub : boolean;
+ begin
+ result:=false;
+ value:=0;
+ relocsym:=nil;
+ gotmin:=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);
+ 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);
+ move(pstart^,hs[1],len);
+ hs[0]:=chr(len);
+ sym:=objectlibrary.newasmsymbol(hs,AB_EXTERNAL,AT_NONE);
+ if not assigned(sym) then
+ internalerror(200509188);
+ objectlibrary.UsedAsmSymbolListInsert(sym);
+ { Second symbol? }
+ if assigned(relocsym) then
+ begin
+ if (relocsym.section<>sym.section) then
+ internalerror(2005091810);
+ relocsym:=nil;
+ end
+ else
+ begin
+ relocsym:=sym;
+ end;
+ exprvalue:=sym.address;
+ 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;
+
+ const
+ N_Function = $24; { function or const }
+ var
+ ofs,
+ nline,
+ nidx,
+ nother,
+ i : longint;
+ relocsym : tasmsymbol;
+ pstr,
+ pcurr,
+ pendquote : pchar;
+ begin
+ pcurr:=nil;
+ pstr:=nil;
+ pendquote:=nil;
+
+ { 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 currpass=1 then
+ objectdata.allocstab(pstr)
+ 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)
+ else
+ begin
+ ofs:=0;
+ relocsym:=nil;
+ end;
+ if (nidx=N_Function) and
+ target_info.use_function_relative_addresses then
+ ofs:=0;
+ objectdata.writestab(ofs,relocsym,nidx,nother,nline,pstr);
+ 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.TreePass0(hp:Tai):Tai;
+ var
+ l : longint;
+ begin
+ while assigned(hp) do
+ begin
+ case hp.typ of
+ ait_align :
+ begin
+ { always use the maximum fillsize in this pass to avoid possible
+ short jumps to become out of range }
+ Tai_align(hp).fillsize:=Tai_align(hp).aligntype;
+ objectdata.alloc(Tai_align(hp).fillsize);
+ end;
+ ait_datablock :
+ begin
+ l:=used_align(size_2_align(Tai_datablock(hp).size),0,objectdata.currsec.addralign);
+ if SmartAsm or (not Tai_datablock(hp).is_global) then
+ begin
+ objectdata.allocalign(l);
+ objectdata.alloc(Tai_datablock(hp).size);
+ end;
+ end;
+ ait_real_80bit :
+ objectdata.alloc(10);
+ ait_real_64bit :
+ objectdata.alloc(8);
+ ait_real_32bit :
+ objectdata.alloc(4);
+ ait_comp_64bit :
+ objectdata.alloc(8);
+ ait_const_64bit,
+ ait_const_32bit,
+ ait_const_16bit,
+ ait_const_8bit,
+ ait_const_rva_symbol,
+ ait_const_indirect_symbol :
+ objectdata.alloc(tai_const(hp).size);
+ ait_section:
+ begin
+ objectdata.CreateSection(Tai_section(hp).sectype,Tai_section(hp).name^,Tai_section(hp).secalign,[]);
+ Tai_section(hp).sec:=objectdata.CurrSec;
+ end;
+ ait_symbol :
+ objectdata.allocsymbol(currpass,Tai_symbol(hp).sym,0);
+ ait_label :
+ objectdata.allocsymbol(currpass,Tai_label(hp).l,0);
+ ait_string :
+ objectdata.alloc(Tai_string(hp).len);
+ ait_instruction :
+ begin
+{$ifdef i386}
+{$ifndef NOAG386BIN}
+ { reset instructions which could change in pass 2 }
+ Taicpu(hp).resetpass2;
+ objectdata.alloc(Taicpu(hp).Pass1(objectdata.currsec.datasize));
+{$endif NOAG386BIN}
+{$endif i386}
+{$ifdef arm}
+ { reset instructions which could change in pass 2 }
+ Taicpu(hp).resetpass2;
+ objectdata.alloc(Taicpu(hp).Pass1(objectdata.currsec.datasize));
+{$endif arm}
+ end;
+ ait_cutobject :
+ if SmartAsm then
+ break;
+ end;
+ hp:=Tai(hp.next);
+ end;
+ TreePass0:=hp;
+ end;
+
+
+ function TInternalAssembler.TreePass1(hp:Tai):Tai;
+ var
+ InlineLevel,
+ l,
+ i : longint;
+ begin
+ inlinelevel:=0;
+ while assigned(hp) do
+ begin
+ case hp.typ of
+ ait_align :
+ begin
+ { here we must determine the fillsize which is used in pass2 }
+ Tai_align(hp).fillsize:=align(objectdata.currsec.datasize,Tai_align(hp).aligntype)-
+ objectdata.currsec.datasize;
+ objectdata.alloc(Tai_align(hp).fillsize);
+ end;
+ ait_datablock :
+ begin
+ if not (objectdata.currsec.sectype in [sec_bss,sec_threadvar]) then
+ Message(asmw_e_alloc_data_only_in_bss);
+ l:=used_align(size_2_align(Tai_datablock(hp).size),0,objectdata.currsec.addralign);
+{ if Tai_datablock(hp).is_global and
+ not SmartAsm then
+ begin}
+{ objectdata.allocsymbol(currpass,Tai_datablock(hp).sym,Tai_datablock(hp).size);}
+ { force to be common/external, must be after setaddress as that would
+ set it to AB_GLOBAL }
+{ Tai_datablock(hp).sym.currbind:=AB_COMMON;
+ end
+ else
+ begin}
+ objectdata.allocalign(l);
+ objectdata.allocsymbol(currpass,Tai_datablock(hp).sym,Tai_datablock(hp).size);
+ objectdata.alloc(Tai_datablock(hp).size);
+{ end;}
+ objectlibrary.UsedAsmSymbolListInsert(Tai_datablock(hp).sym);
+ end;
+ ait_real_80bit :
+ objectdata.alloc(10);
+ ait_real_64bit :
+ objectdata.alloc(8);
+ ait_real_32bit :
+ objectdata.alloc(4);
+ ait_comp_64bit :
+ objectdata.alloc(8);
+ ait_const_64bit,
+ ait_const_32bit,
+ ait_const_16bit,
+ ait_const_8bit,
+ ait_const_rva_symbol,
+ ait_const_indirect_symbol :
+ begin
+ objectdata.alloc(tai_const(hp).size);
+ if assigned(Tai_const(hp).sym) then
+ objectlibrary.UsedAsmSymbolListInsert(Tai_const(hp).sym);
+ if assigned(Tai_const(hp).endsym) then
+ objectlibrary.UsedAsmSymbolListInsert(Tai_const(hp).endsym);
+ end;
+ ait_section:
+ begin
+ { use cached value }
+ objectdata.setsection(Tai_section(hp).sec);
+ end;
+ ait_stab :
+ begin
+ if assigned(Tai_stab(hp).str) then
+ convertstab(Tai_stab(hp).str);
+ end;
+ ait_function_name,
+ ait_force_line : ;
+ ait_symbol :
+ begin
+ objectdata.allocsymbol(currpass,Tai_symbol(hp).sym,0);
+ objectlibrary.UsedAsmSymbolListInsert(Tai_symbol(hp).sym);
+ end;
+ ait_symbol_end :
+ begin
+ if target_info.system in [system_i386_linux,system_i386_beos] then
+ begin
+ Tai_symbol_end(hp).sym.size:=objectdata.currsec.datasize-Tai_symbol_end(hp).sym.address;
+ objectlibrary.UsedAsmSymbolListInsert(Tai_symbol_end(hp).sym);
+ end;
+ end;
+ ait_label :
+ begin
+ objectdata.allocsymbol(currpass,Tai_label(hp).l,0);
+ objectlibrary.UsedAsmSymbolListInsert(Tai_label(hp).l);
+ end;
+ ait_string :
+ objectdata.alloc(Tai_string(hp).len);
+ ait_instruction :
+ begin
+ objectdata.alloc(Taicpu(hp).Pass1(objectdata.currsec.datasize));
+ { fixup the references }
+ for i:=1 to Taicpu(hp).ops do
+ begin
+ with Taicpu(hp).oper[i-1]^ do
+ begin
+ case typ of
+ top_ref :
+ begin
+ if assigned(ref^.symbol) then
+ objectlibrary.UsedAsmSymbolListInsert(ref^.symbol);
+ if assigned(ref^.relsymbol) then
+ objectlibrary.UsedAsmSymbolListInsert(ref^.symbol);
+ end;
+ end;
+ end;
+ end;
+ end;
+ ait_cutobject :
+ if SmartAsm then
+ break;
+ ait_marker :
+ if tai_marker(hp).kind=InlineStart then
+ inc(InlineLevel)
+ else if tai_marker(hp).kind=InlineEnd then
+ dec(InlineLevel);
+ end;
+ hp:=Tai(hp.next);
+ end;
+ TreePass1:=hp;
+ end;
+
+
+ function TInternalAssembler.TreePass2(hp:Tai):Tai;
+ var
+ fillbuffer : tfillbuffer;
+ InlineLevel,
+ l : longint;
+ v : int64;
+{$ifdef x86}
+ co : comp;
+{$endif x86}
+ begin
+ inlinelevel:=0;
+ { main loop }
+ while assigned(hp) do
+ begin
+ case hp.typ of
+ ait_align :
+ begin
+ if objectdata.currsec.sectype in [sec_bss,sec_threadvar] then
+ objectdata.alloc(Tai_align(hp).fillsize)
+ else
+ objectdata.writebytes(Tai_align(hp).calculatefillbuf(fillbuffer)^,Tai_align(hp).fillsize);
+ end;
+ ait_section :
+ begin
+ { use cached value }
+ objectdata.setsection(Tai_section(hp).sec);
+ end;
+ ait_symbol :
+ begin
+ objectdata.writesymbol(Tai_symbol(hp).sym);
+ objectoutput.exportsymbol(Tai_symbol(hp).sym);
+ end;
+ ait_datablock :
+ begin
+ l:=used_align(size_2_align(Tai_datablock(hp).size),0,objectdata.currsec.addralign);
+ objectdata.writesymbol(Tai_datablock(hp).sym);
+ objectoutput.exportsymbol(Tai_datablock(hp).sym);
+{ if SmartAsm or (not Tai_datablock(hp).is_global) then
+ begin}
+ objectdata.allocalign(l);
+ objectdata.alloc(Tai_datablock(hp).size);
+{ end;}
+ end;
+ ait_real_80bit :
+ objectdata.writebytes(Tai_real_80bit(hp).value,10);
+ ait_real_64bit :
+ objectdata.writebytes(Tai_real_64bit(hp).value,8);
+ ait_real_32bit :
+ objectdata.writebytes(Tai_real_32bit(hp).value,4);
+ ait_comp_64bit :
+ begin
+{$ifdef x86}
+ co:=comp(Tai_comp_64bit(hp).value);
+ objectdata.writebytes(co,8);
+{$endif x86}
+ end;
+ ait_string :
+ objectdata.writebytes(Tai_string(hp).str^,Tai_string(hp).len);
+ ait_const_64bit,
+ ait_const_32bit,
+ ait_const_16bit,
+ ait_const_8bit :
+ begin
+ if assigned(tai_const(hp).sym) then
+ begin
+ if assigned(tai_const(hp).endsym) then
+ begin
+ if tai_const(hp).endsym.section<>tai_const(hp).sym.section then
+ internalerror(200404124);
+ v:=tai_const(hp).endsym.address-tai_const(hp).sym.address+Tai_const(hp).value;
+ objectdata.writebytes(v,tai_const(hp).size);
+ end
+ else
+ objectdata.writereloc(Tai_const(hp).value,Tai_const(hp).size,
+ Tai_const(hp).sym,RELOC_ABSOLUTE);
+ end
+ else
+ objectdata.writebytes(Tai_const(hp).value,tai_const(hp).size);
+ end;
+ ait_const_rva_symbol :
+ objectdata.writereloc(Tai_const(hp).value,sizeof(aint),Tai_const(hp).sym,RELOC_RVA);
+ ait_label :
+ begin
+ objectdata.writesymbol(Tai_label(hp).l);
+ { exporting shouldn't be necessary as labels are local,
+ but it's better to be on the safe side (PFV) }
+ objectoutput.exportsymbol(Tai_label(hp).l);
+ end;
+ ait_instruction :
+ Taicpu(hp).Pass2(objectdata);
+ ait_stab :
+ convertstab(Tai_stab(hp).str);
+ ait_function_name,
+ ait_force_line : ;
+ ait_cutobject :
+ if SmartAsm then
+ break;
+ ait_marker :
+ if tai_marker(hp).kind=InlineStart then
+ inc(InlineLevel)
+ else if tai_marker(hp).kind=InlineEnd then
+ dec(InlineLevel);
+ end;
+ hp:=Tai(hp.next);
+ end;
+ TreePass2:=hp;
+ end;
+
+
+ procedure TInternalAssembler.writetree;
+ var
+ hp : Tai;
+ label
+ doexit;
+ begin
+ objectdata:=objectoutput.newobjectdata(Objfile);
+ { reset the asmsymbol list }
+ objectlibrary.CreateUsedAsmsymbolList;
+
+ { Pass 0 }
+ currpass:=0;
+ objectdata.createsection(sec_code,'',0,[]);
+ objectdata.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;
+ objectdata.afteralloc;
+ { leave if errors have occured }
+ if errorcount>0 then
+ goto doexit;
+
+ { Pass 1 }
+ currpass:=1;
+ objectdata.resetsections;
+ objectdata.beforealloc;
+ objectdata.createsection(sec_code,'',0,[]);
+ { start with list 1 }
+ currlistidx:=1;
+ currlist:=list[currlistidx];
+ hp:=Tai(currList.first);
+ while assigned(hp) do
+ begin
+ hp:=TreePass1(hp);
+ MaybeNextList(hp);
+ end;
+ objectdata.createsection(sec_code,'',0,[]);
+ objectdata.afteralloc;
+ { check for undefined labels and reset }
+ objectlibrary.UsedAsmSymbolListCheckUndefined;
+
+ { leave if errors have occured }
+ if errorcount>0 then
+ goto doexit;
+
+ { Pass 2 }
+ currpass:=2;
+ objectdata.resetsections;
+ objectdata.beforewrite;
+ objectdata.createsection(sec_code,'',0,[]);
+ { start with list 1 }
+ currlistidx:=1;
+ currlist:=list[currlistidx];
+ hp:=Tai(currList.first);
+ while assigned(hp) do
+ begin
+ hp:=TreePass2(hp);
+ MaybeNextList(hp);
+ end;
+ objectdata.createsection(sec_code,'',0,[]);
+ objectdata.afterwrite;
+
+ { don't write the .o file if errors have occured }
+ if errorcount=0 then
+ begin
+ { write objectfile }
+ objectoutput.startobjectfile(ObjFile);
+ objectoutput.writeobjectfile(objectdata);
+ objectdata.free;
+ objectdata:=nil;
+ end;
+
+ doexit:
+ { reset the used symbols back, must be after the .o has been
+ written }
+ objectlibrary.UsedAsmsymbolListReset;
+ objectlibrary.DestroyUsedAsmsymbolList;
+ end;
+
+
+ procedure TInternalAssembler.writetreesmart;
+ var
+ hp : Tai;
+ startsectype : TAsmSectionType;
+ place: tcutplace;
+ begin
+ NextSmartName(cut_normal);
+ objectdata:=objectoutput.newobjectdata(Objfile);
+ startsectype:=sec_code;
+
+ { start with list 1 }
+ currlistidx:=1;
+ currlist:=list[currlistidx];
+ hp:=Tai(currList.first);
+ while assigned(hp) do
+ begin
+ { reset the asmsymbol list }
+ objectlibrary.CreateUsedAsmSymbolList;
+
+ { Pass 0 }
+ currpass:=0;
+ objectdata.resetsections;
+ objectdata.beforealloc;
+ objectdata.createsection(startsectype,'',0,[]);
+ TreePass0(hp);
+ objectdata.afteralloc;
+ { leave if errors have occured }
+ if errorcount>0 then
+ exit;
+
+ { Pass 1 }
+ currpass:=1;
+ objectdata.resetsections;
+ objectdata.beforealloc;
+ objectdata.createsection(startsectype,'',0,[]);
+ TreePass1(hp);
+ objectdata.afteralloc;
+ { check for undefined labels }
+ objectlibrary.UsedAsmSymbolListCheckUndefined;
+
+ { leave if errors have occured }
+ if errorcount>0 then
+ exit;
+
+ { Pass 2 }
+ currpass:=2;
+ objectoutput.startobjectfile(Objfile);
+ objectdata.resetsections;
+ objectdata.beforewrite;
+ objectdata.createsection(startsectype,'',0,[]);
+ hp:=TreePass2(hp);
+ { save section type for next loop, must be done before EndFileLineInfo
+ because that changes the section to sec_code }
+ startsectype:=objectdata.currsec.sectype;
+ objectdata.afterwrite;
+ { leave if errors have occured }
+ if errorcount>0 then
+ exit;
+
+ { write the current objectfile }
+ objectoutput.writeobjectfile(objectdata);
+ objectdata.free;
+ objectdata:=nil;
+
+ { reset the used symbols back, must be after the .o has been
+ written }
+ objectlibrary.UsedAsmsymbolListReset;
+ objectlibrary.DestroyUsedAsmsymbolList;
+
+ { 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 }
+ 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
+ startsectype:=Tai_section(hp).sectype
+ else if (Tai(hp).typ=ait_cutobject) then
+ place:=Tai_cutobject(hp).place;
+ hp:=Tai(hp.next);
+ end;
+ { there is a problem if startsectype is sec_none !! PM }
+ if startsectype=sec_none then
+ startsectype:=sec_code;
+
+ if not MaybeNextList(hp) then
+ break;
+
+ { start next objectfile }
+ NextSmartName(place);
+ objectdata:=objectoutput.newobjectdata(Objfile);
+ end;
+ end;
+
+
+ procedure TInternalAssembler.MakeObject;
+
+ var to_do:set of Tasmlist;
+ i:Tasmlist;
+
+ procedure addlist(p:TAAsmoutput);
+ begin
+ inc(lists);
+ list[lists]:=p;
+ end;
+
+ begin
+ to_do:=[low(Tasmlist)..high(Tasmlist)];
+ if usedeffileforexports then
+ exclude(to_do,al_exports);
+ {$warning TODO internal writer support for dwarf}
+ exclude(to_do,al_dwarf);
+{$ifndef segment_threadvars}
+ exclude(to_do,al_threadvars);
+{$endif}
+ for i:=low(Tasmlist) to high(Tasmlist) do
+ if (i in to_do) and (asmlist[i]<>nil) then
+ addlist(asmlist[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(smart);
+ a.MakeObject;
+ a.Free;
+ end;
+
+
+ Procedure OnlyAsm;
+ var
+ a : TExternalAssembler;
+ begin
+ a:=TExternalAssembler.Create(false);
+ a.DoAssemble;
+ a.Free;
+ 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;
+
+
+ procedure InitAssembler;
+ begin
+ end;
+
+
+ procedure DoneAssembler;
+ begin
+ end;
+
+end.