* fixed llvm handling of routines that are normally declared in the

interface of a unit, or forward declared, and then the implementation turns
    out to be external. We now properly generate a wrapper routine at the
    Pascal level for the original declaration that calls through to the
    external routine

git-svn-id: trunk@31285 -
This commit is contained in:
Jonas Maebe 2015-08-05 21:05:55 +00:00
parent 1cdaf8e332
commit a58504990a
4 changed files with 68 additions and 55 deletions

View File

@ -1060,13 +1060,13 @@ implementation
procedure thlcgllvm.handle_external_proc(list: TAsmList; pd: tprocdef; const importname: TSymStr);
var
asmsym: tasmsymbol;
begin
{ Windows-style import names are not yet supported -> ignore
importname for now }
asmsym:=current_asmdata.RefAsmSymbol(tllvmprocdef(pd).external_mangledname,AT_FUNCTION);
list.concat(taillvmalias.create(asmsym,pd.mangledname,pd,llv_default,lll_internal));
{ don't do anything, because at this point we can't know yet for certain
whether the aliased routine is internal to the current routine or not.
If it's internal, we would have to generate an llvm alias, while if it's
external, we would have to generate a declaration. Additionally, aliases
cannot refer to declarations, so always creating aliases doesn't work
either -> handle in llvmtype }
end;

View File

@ -76,16 +76,7 @@ type
tllvmprocvardef = class(tcpuprocvardef)
end;
{ tllvmprocdef }
tllvmprocdef = class(tcpuprocdef)
protected
external_decl_mangled_name: TSymStr;
public
{ overried the mangled name of external functions }
function mangledname: TSymStr; override;
{ provide access to the original mangled name of external functions }
function external_mangledname: TSymStr;
end;
tllvmstringdef = class(tcpustringdef)
@ -149,38 +140,6 @@ implementation
uses
symconst,symdef,symsym;
{ tllvmprocdef }
function tllvmprocdef.mangledname: TSymStr;
begin
{ External declarations are handled separately for the LLVM target and need
a different mangled name than on other targets, because we have to
create a declaration based on the declared name that redirects to the
external name in order to deal with potential different signatures between
the external declaration the symbol that's referred.
There's no need to store the external_decl_mangled_name in the ppu file,
since it can always be regenerated on the fly. }
if not(po_external in procoptions) then
result:=inherited
else
begin
if external_decl_mangled_name='' then
begin
result:=defaultmangledname;
external_decl_mangled_name:=result;
end
else
result:=external_decl_mangled_name;
end;
end;
function tllvmprocdef.external_mangledname: TSymStr;
begin
result:=inherited mangledname;
end;
begin
{ used tdef classes }
cfiledef:=tllvmfiledef;

View File

@ -2158,13 +2158,28 @@ implementation
if tf_has_dllscanner in target_info.flags then
current_module.dllscannerinputlist.Add(proc_get_importname(pd),pd);
end;
create_hlcodegen;
hlcg.handle_external_proc(
current_asmdata.asmlists[al_procedures],
pd,
proc_get_importname(pd));
destroy_hlcodegen;
{$ifdef cpuhighleveltarget}
{ it's hard to factor this out in a virtual method, because the
generic version (the one inside this ifdef) doesn't fit in
hlcgobj but in symcreat or here, while the other version
doesn't fit in symcreat (since it uses the code generator).
Maybe we need another class for this kind of code that could
either be symcreat- or hlcgobj-based
}
if (not pd.forwarddef) and
(pd.hasforward) and
(proc_get_importname(pd)<>'') then
call_through_new_name(pd,proc_get_importname(pd))
else
{$endif cpuhighleveltarget}
begin
create_hlcodegen;
hlcg.handle_external_proc(
current_asmdata.asmlists[al_procedures],
pd,
proc_get_importname(pd));
destroy_hlcodegen;
end
end;
end;

View File

@ -114,13 +114,18 @@ interface
created staticvarsym that is responsible for allocating the global storage }
function make_field_static(recst: tsymtable; fieldvs: tfieldvarsym): tstaticvarsym;
{ create a new procdef with the signature of orgpd and (mangled) name
newname, and change the implementation of orgpd so that it calls through
to this new procedure }
procedure call_through_new_name(orgpd: tprocdef; const newname: TSymStr);
implementation
uses
cutils,cclasses,globals,verbose,systems,comphook,fmodule,
symtable,defutil,
pbase,pdecobj,pdecsub,psub,ptconst,
pbase,pdecobj,pdecsub,psub,ptconst,pparautl,
{$ifdef jvm}
pjvm,jvmdef,
{$endif jvm}
@ -1324,5 +1329,39 @@ implementation
result:=hstaticvs;
end;
procedure call_through_new_name(orgpd: tprocdef; const newname: TSymStr);
var
newpd: tprocdef;
begin
{ we have a forward declaration like
procedure test; (in the unit interface or "forward")
and then an implementation like
procedure test; external name 'something';
To solve this, we create a new external procdef for the
implementation, and then generate a procedure body for the original
one that calls through to the external procdef. This is necessary
because there may already be references to the mangled name for the
non-external "test".
}
newpd:=tprocdef(orgpd.getcopyas(procdef,pc_bareproc));
insert_funcret_para(newpd);
newpd.procoptions:=newpd.procoptions+orgpd.procoptions*[po_external,po_has_importname,po_has_importdll];
newpd.import_name:=orgpd.import_name;
orgpd.import_name:=nil;
newpd.import_dll:=orgpd.import_dll;
newpd.import_dll:=nil;
newpd.import_nr:=orgpd.import_nr;
orgpd.import_nr:=0;
newpd.setmangledname(newname);
finish_copied_procdef(newpd,'__FPC_IMPL_EXTERNAL_REDIRECT_'+newname,current_module.localsymtable,nil);
newpd.forwarddef:=false;
orgpd.skpara:=newpd;
orgpd.synthetickind:=tsk_callthrough;
orgpd.procoptions:=orgpd.procoptions-[po_external,po_has_importname,po_has_importdll];
orgpd.forwarddef:=true;
end;
end.