fpc/compiler/i8086/n8086ld.pas
2020-10-13 19:59:01 +00:00

267 lines
11 KiB
ObjectPascal

{
Copyright (c) 2002-2014 by Florian Klaempfl
Generate i8086 assembler for load nodes
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit n8086ld;
{$i fpcdefs.inc}
interface
uses
globtype,
symsym,symtype,
node,ncgld,nx86ld,aasmbase;
type
{ ti8086loadnode }
ti8086loadnode = class(tx86loadnode)
protected
procedure generate_nested_access(vs: tsym); override;
procedure generate_absaddr_access(vs: tabsolutevarsym); override;
procedure generate_threadvar_access(gvs: tstaticvarsym); override;
public
procedure pass_generate_code;override;
end;
implementation
uses
globals,verbose,aasmdata,defutil,
symconst,symdef,symtable,symcpu,
nld,
cgbase,cgobj,cgutils,
hlcgobj,
cpubase,cpuinfo,
parabase,paramgr;
{*****************************************************************************
TI8086LOADNODE
*****************************************************************************}
procedure ti8086loadnode.generate_nested_access(vs: tsym);
begin
inherited;
{ the parentfp pointer is always a near pointer (this is turbo pascal
compatible) regardless of memory model, so we need to set the segment
manually.
todo: once the far data memory models are fully implemented, the
parentfp type should be changed to a near 'ss' pointer in all memory
models and then this code can be removed. But this can only happen
after:
1) all calls to a_loadaddr_ref_reg go through the high level code
generator
2) a_loadaddr_ref_reg in the low level code generator stops using
the presence of a segment in the source reference to determine the
destination reg size
3) make_simple_ref is updated to remove unnecessary segment prefixes
4) hlcg.reference_reset_base is updated to set the segment on near_ss
pointers }
if (left.nodetype=loadparentfpn) and
(current_settings.x86memorymodel in x86_far_data_models) then
location.reference.segment:=NR_SS;
end;
procedure ti8086loadnode.generate_absaddr_access(vs: tabsolutevarsym);
begin
if tcpuabsolutevarsym(symtableentry).absseg then
begin
location.reference.segment:=cg.getintregister(current_asmdata.CurrAsmList,OS_16);
cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_16,aint(tcpuabsolutevarsym(symtableentry).addrsegment),location.reference.segment);
end;
inherited;
end;
procedure ti8086loadnode.generate_threadvar_access(gvs: tstaticvarsym);
var
segref: treference;
segreg: TRegister;
newsize: TCgSize;
norelocatelab: TAsmLabel;
endrelocatelab: TAsmLabel;
pvd: tdef;
paraloc1 : tcgpara;
hregister: TRegister;
href: treference;
begin
if current_settings.x86memorymodel=mm_huge then
begin
if (cs_compilesystem in current_settings.moduleswitches) then
begin
inherited generate_threadvar_access(gvs);
exit;
end;
{ we don't know the size of all arrays }
newsize:=def_cgsize(resultdef);
{ alignment is overridden per case below }
location_reset_ref(location,LOC_REFERENCE,newsize,resultdef.alignment,[]);
{
Thread var loading is optimized to first check if
a relocate function is available. When the function
is available it is called to retrieve the address.
Otherwise the address is loaded with the symbol
The code needs to be in the order to first handle the
call and then the address load to be sure that the
register that is used for returning is the same (PFV)
}
current_asmdata.getjumplabel(norelocatelab);
current_asmdata.getjumplabel(endrelocatelab);
{ make sure hregister can't allocate the register necessary for the parameter }
pvd:=search_system_type('TRELOCATETHREADVARHANDLER').typedef;
if pvd.typ<>procvardef then
internalerror(2012120902);
paraloc1.init;
paramanager.getcgtempparaloc(current_asmdata.CurrAsmList,tprocvardef(pvd),1,paraloc1);
hregister:=hlcg.getaddressregister(current_asmdata.CurrAsmList,pvd);
segreg:=cg.getintregister(current_asmdata.CurrAsmList,OS_16);
reference_reset_symbol(segref,current_asmdata.RefAsmSymbol('FPC_THREADVAR_RELOCATE',AT_DATA),0,pvd.alignment,[]);
segref.refaddr:=addr_seg;
cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_16,OS_16,segref,segreg);
reference_reset_symbol(href,current_asmdata.RefAsmSymbol('FPC_THREADVAR_RELOCATE',AT_DATA),0,pvd.alignment,[]);
href.segment:=segreg;
hlcg.a_load_ref_reg(current_asmdata.CurrAsmList,pvd,pvd,href,hregister);
hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,pvd,OC_EQ,0,hregister,norelocatelab);
{ don't save the allocated register else the result will be destroyed later }
if not(vo_is_weak_external in gvs.varoptions) then
reference_reset_symbol(href,current_asmdata.RefAsmSymbol(gvs.mangledname,AT_DATA),0,2,[])
else
reference_reset_symbol(href,current_asmdata.WeakRefAsmSymbol(gvs.mangledname,AT_DATA),0,2,[]);
cg.a_load_ref_cgpara(current_asmdata.CurrAsmList,OS_16,href,paraloc1);
paramanager.freecgpara(current_asmdata.CurrAsmList,paraloc1);
paraloc1.done;
cg.allocallcpuregisters(current_asmdata.CurrAsmList);
cg.a_call_reg(current_asmdata.CurrAsmList,hregister);
cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
cg.getcpuregister(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
cg.ungetcpuregister(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
hregister:=hlcg.getaddressregister(current_asmdata.CurrAsmList,voidpointertype);
cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_INT,OS_ADDR,NR_FUNCTION_RESULT_REG,hregister);
cg.a_jmp_always(current_asmdata.CurrAsmList,endrelocatelab);
cg.a_label(current_asmdata.CurrAsmList,norelocatelab);
{ no relocation needed, load the address of the variable only, the
layout of a threadvar is (4 bytes pointer):
0 - Threadvar index
4 - Threadvar value in single threading }
if not(vo_is_weak_external in gvs.varoptions) then
reference_reset_symbol(href,current_asmdata.RefAsmSymbol(gvs.mangledname,AT_DATA),sizeof(pint),2,[])
else
reference_reset_symbol(href,current_asmdata.WeakRefAsmSymbol(gvs.mangledname,AT_DATA),sizeof(pint),2,[]);
hlcg.a_loadaddr_ref_reg(current_asmdata.CurrAsmList,resultdef,voidpointertype,href,hregister);
cg.a_label(current_asmdata.CurrAsmList,endrelocatelab);
hlcg.reference_reset_base(location.reference,voidpointertype,hregister,0,ctempposinvalid,location.reference.alignment,[]);
end
else
inherited generate_threadvar_access(gvs);
end;
procedure ti8086loadnode.pass_generate_code;
var
gvs: tstaticvarsym;
segref: treference;
refsym: TAsmSymbol;
segreg: TRegister;
newsize: TCgSize;
begin
case symtableentry.typ of
staticvarsym:
begin
gvs:=tstaticvarsym(symtableentry);
if (vo_is_dll_var in gvs.varoptions) then
{ DLL variable }
begin
inherited pass_generate_code;
exit;
end
{ Thread variable }
else if (vo_is_thread_var in gvs.varoptions) then
begin
{ this will be handled in ti8086loadnode.generate_threadvar_access }
inherited pass_generate_code;
exit;
end
{ Normal (or external) variable }
else
begin
if ((current_settings.x86memorymodel<>mm_huge) and not (vo_is_far in gvs.varoptions)) or
(not (vo_is_external in gvs.varoptions) and gvs.Owner.iscurrentunit) then
begin
inherited pass_generate_code;
if location.loc in [LOC_REFERENCE,LOC_CREFERENCE] then
location.reference.segment:=NR_DS;
exit;
end;
{ we don't know the size of all arrays }
newsize:=def_cgsize(resultdef);
{ alignment is overridden per case below }
location_reset_ref(location,LOC_REFERENCE,newsize,resultdef.alignment,[]);
if gvs.localloc.loc=LOC_INVALID then
begin
if not(vo_is_weak_external in gvs.varoptions) then
refsym:=current_asmdata.RefAsmSymbol(gvs.mangledname,AT_DATA)
else
refsym:=current_asmdata.WeakRefAsmSymbol(gvs.mangledname,AT_DATA);
segreg:=cg.getintregister(current_asmdata.CurrAsmList,OS_16);
reference_reset_symbol(segref,refsym,0,0,[]);
segref.refaddr:=addr_seg;
cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_16,OS_16,segref,segreg);
reference_reset_symbol(location.reference,refsym,0,location.reference.alignment,[]);
location.reference.segment:=segreg;
end
else
location:=gvs.localloc;
end;
{ make const a LOC_CREFERENCE }
if (gvs.varspez=vs_const) and
(location.loc=LOC_REFERENCE) then
location.loc:=LOC_CREFERENCE;
end;
procsym:
begin
inherited pass_generate_code;
if current_settings.x86memorymodel in x86_near_code_models then
begin
if (location.loc=LOC_REFERENCE) or (location.loc=LOC_CREFERENCE) then
location.reference.segment:=NR_CS;
end;
end;
else
inherited pass_generate_code;
end;
end;
begin
cloadnode:=ti8086loadnode;
end.