mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-05 15:58:28 +02:00
* don't optimize funcret with assignment result if the value is also used
as a parameter and the funcret is also passed as parameter. Because in that case both are pointing to the same memory location git-svn-id: trunk@10159 -
This commit is contained in:
parent
58b3208552
commit
169516ffc0
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -7916,6 +7916,8 @@ tests/webtbs/tw10681.pp svneol=native#text/plain
|
|||||||
tests/webtbs/tw1071.pp svneol=native#text/plain
|
tests/webtbs/tw1071.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw1073.pp svneol=native#text/plain
|
tests/webtbs/tw1073.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw10736.pp svneol=native#text/plain
|
tests/webtbs/tw10736.pp svneol=native#text/plain
|
||||||
|
tests/webtbs/tw10753.pp svneol=native#text/plain
|
||||||
|
tests/webtbs/tw10753a.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw1081.pp svneol=native#text/plain
|
tests/webtbs/tw1081.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw1090.pp svneol=native#text/plain
|
tests/webtbs/tw1090.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw1092.pp svneol=native#text/plain
|
tests/webtbs/tw1092.pp svneol=native#text/plain
|
||||||
|
@ -63,6 +63,7 @@ interface
|
|||||||
function gen_self_tree:tnode;
|
function gen_self_tree:tnode;
|
||||||
function gen_vmt_tree:tnode;
|
function gen_vmt_tree:tnode;
|
||||||
procedure gen_hidden_parameters;
|
procedure gen_hidden_parameters;
|
||||||
|
function funcret_can_be_reused:boolean;
|
||||||
procedure maybe_create_funcret_node;
|
procedure maybe_create_funcret_node;
|
||||||
procedure bind_parasym;
|
procedure bind_parasym;
|
||||||
procedure add_init_statement(n:tnode);
|
procedure add_init_statement(n:tnode);
|
||||||
@ -1651,10 +1652,95 @@ implementation
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function check_funcret_used_as_para(var n: tnode; arg: pointer): foreachnoderesult;
|
||||||
|
var
|
||||||
|
destsym : tsym absolute arg;
|
||||||
|
begin
|
||||||
|
result := fen_false;
|
||||||
|
if (n.nodetype=loadn) and
|
||||||
|
(tloadnode(n).symtableentry = destsym) then
|
||||||
|
result := fen_norecurse_true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function tcallnode.funcret_can_be_reused:boolean;
|
||||||
|
var
|
||||||
|
realassignmenttarget: tnode;
|
||||||
|
begin
|
||||||
|
result:=false;
|
||||||
|
|
||||||
|
{ we are processing an assignment node? }
|
||||||
|
if not(assigned(aktassignmentnode) and
|
||||||
|
(aktassignmentnode.right=self) and
|
||||||
|
(aktassignmentnode.left.resultdef=resultdef)) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
{ destination must be able to be passed as var parameter }
|
||||||
|
if not valid_for_var(aktassignmentnode.left,false) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
{ destination must be a simple load so it doesn't need a temp when
|
||||||
|
it is evaluated }
|
||||||
|
if not is_simple_para_load(aktassignmentnode.left,false) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
{ remove possible typecasts }
|
||||||
|
realassignmenttarget:=aktassignmentnode.left.actualtargetnode;
|
||||||
|
|
||||||
|
{ when it is not passed in a parameter it will only be used after the
|
||||||
|
function call }
|
||||||
|
if not paramanager.ret_in_param(resultdef,procdefinition.proccalloption) then
|
||||||
|
begin
|
||||||
|
result:=true;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ when we substitute a function result inside an inlined function,
|
||||||
|
we may take the address of this function result. Therefore the
|
||||||
|
substituted function result may not be in a register, as we cannot
|
||||||
|
take its address in that case }
|
||||||
|
if (realassignmenttarget.nodetype=temprefn) and
|
||||||
|
not(ti_addr_taken in ttemprefnode(realassignmenttarget).tempinfo^.flags) and
|
||||||
|
not(ti_may_be_in_reg in ttemprefnode(realassignmenttarget).tempinfo^.flags) then
|
||||||
|
begin
|
||||||
|
result:=true;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if (realassignmenttarget.nodetype=loadn) and
|
||||||
|
{ nested procedures may access the current procedure's locals }
|
||||||
|
(procdefinition.parast.symtablelevel=normal_function_level) and
|
||||||
|
{ must be a local variable, a value para or a hidden function result }
|
||||||
|
{ parameter (which can be passed by address, but in that case it got }
|
||||||
|
{ through these same checks at the caller side and is thus safe }
|
||||||
|
(
|
||||||
|
(tloadnode(realassignmenttarget).symtableentry.typ=localvarsym) or
|
||||||
|
(
|
||||||
|
(tloadnode(realassignmenttarget).symtableentry.typ=paravarsym) and
|
||||||
|
((tparavarsym(tloadnode(realassignmenttarget).symtableentry).varspez = vs_value) or
|
||||||
|
(vo_is_funcret in tparavarsym(tloadnode(realassignmenttarget).symtableentry).varoptions))
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
{ the address may not have been taken of the variable/parameter, because }
|
||||||
|
{ otherwise it's possible that the called function can access it via a }
|
||||||
|
{ global variable or other stored state }
|
||||||
|
(
|
||||||
|
not(tabstractvarsym(tloadnode(realassignmenttarget).symtableentry).addr_taken) and
|
||||||
|
(tabstractvarsym(tloadnode(realassignmenttarget).symtableentry).varregable in [vr_none,vr_addr])
|
||||||
|
) then
|
||||||
|
begin
|
||||||
|
{ If the funcret is also used as a parameter we can't optimize becuase the funcret
|
||||||
|
and the parameter will point to the same address. That means that a change of the result variable
|
||||||
|
will result also in a change of the parameter value }
|
||||||
|
result:=not foreachnodestatic(left,@check_funcret_used_as_para,tloadnode(realassignmenttarget).symtableentry);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure tcallnode.maybe_create_funcret_node;
|
procedure tcallnode.maybe_create_funcret_node;
|
||||||
var
|
var
|
||||||
temp : ttempcreatenode;
|
temp : ttempcreatenode;
|
||||||
realassignmenttarget: tnode;
|
|
||||||
begin
|
begin
|
||||||
{ For the function result we need to create a temp node for:
|
{ For the function result we need to create a temp node for:
|
||||||
- Inlined functions
|
- Inlined functions
|
||||||
@ -1681,51 +1767,7 @@ implementation
|
|||||||
of the refcount before being assigned. This is all done after the call so there
|
of the refcount before being assigned. This is all done after the call so there
|
||||||
is no issue with exceptions and possible use of the old value in the called
|
is no issue with exceptions and possible use of the old value in the called
|
||||||
function }
|
function }
|
||||||
if assigned(aktassignmentnode) then
|
if funcret_can_be_reused then
|
||||||
realassignmenttarget:=aktassignmentnode.left.actualtargetnode;
|
|
||||||
if assigned(aktassignmentnode) and
|
|
||||||
(aktassignmentnode.right=self) and
|
|
||||||
(aktassignmentnode.left.resultdef=resultdef) and
|
|
||||||
valid_for_var(aktassignmentnode.left,false) and
|
|
||||||
(
|
|
||||||
{ when it is not passed in a parameter it will only be used after the
|
|
||||||
function call, but only do it when it will be a simple parameter node
|
|
||||||
and doesn't need to be in a temp }
|
|
||||||
(
|
|
||||||
not paramanager.ret_in_param(resultdef,procdefinition.proccalloption) and
|
|
||||||
{ when we substitute a function result inside an inlined function,
|
|
||||||
we may take the address of this function result. Therefore the
|
|
||||||
substituted function result may not be in a register, as we cannot
|
|
||||||
take its address in that case }
|
|
||||||
is_simple_para_load(aktassignmentnode.left,false)
|
|
||||||
) or
|
|
||||||
(
|
|
||||||
(realassignmenttarget.nodetype=temprefn) and
|
|
||||||
not(ti_addr_taken in ttemprefnode(realassignmenttarget).tempinfo^.flags) and
|
|
||||||
not(ti_may_be_in_reg in ttemprefnode(realassignmenttarget).tempinfo^.flags)
|
|
||||||
) or
|
|
||||||
(
|
|
||||||
(realassignmenttarget.nodetype=loadn) and
|
|
||||||
{ nested procedures may access the current procedure's locals }
|
|
||||||
(procdefinition.parast.symtablelevel=normal_function_level) and
|
|
||||||
{ must be a local variable, a value para or a hidden function result }
|
|
||||||
{ parameter (which can be passed by address, but in that case it got }
|
|
||||||
{ through these same checks at the caller side and is thus safe }
|
|
||||||
(
|
|
||||||
(tloadnode(realassignmenttarget).symtableentry.typ=localvarsym) or
|
|
||||||
(
|
|
||||||
(tloadnode(realassignmenttarget).symtableentry.typ=paravarsym) and
|
|
||||||
((tparavarsym(tloadnode(realassignmenttarget).symtableentry).varspez = vs_value) or
|
|
||||||
(vo_is_funcret in tparavarsym(tloadnode(realassignmenttarget).symtableentry).varoptions))
|
|
||||||
)
|
|
||||||
) and
|
|
||||||
{ the address may not have been taken of the variable/parameter, because }
|
|
||||||
{ otherwise it's possible that the called function can access it via a }
|
|
||||||
{ global variable or other stored state }
|
|
||||||
not(tabstractvarsym(tloadnode(realassignmenttarget).symtableentry).addr_taken) and
|
|
||||||
(tabstractvarsym(tloadnode(realassignmenttarget).symtableentry).varregable in [vr_none,vr_addr])
|
|
||||||
)
|
|
||||||
) then
|
|
||||||
begin
|
begin
|
||||||
funcretnode:=aktassignmentnode.left.getcopy;
|
funcretnode:=aktassignmentnode.left.getcopy;
|
||||||
include(funcretnode.flags,nf_is_funcret);
|
include(funcretnode.flags,nf_is_funcret);
|
||||||
|
33
tests/webtbs/tw10753.pp
Normal file
33
tests/webtbs/tw10753.pp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{$mode objfpc}{$H+}
|
||||||
|
uses
|
||||||
|
SysUtils;
|
||||||
|
|
||||||
|
var
|
||||||
|
err : boolean;
|
||||||
|
|
||||||
|
procedure p;
|
||||||
|
var
|
||||||
|
AStr,AText: string;
|
||||||
|
AValue: int64;
|
||||||
|
begin
|
||||||
|
//This goes wrong, notice the AStr input and output
|
||||||
|
AValue:=1234567890;
|
||||||
|
AStr := Format('%0.n',[1.0*AValue]); //1.234.567.890
|
||||||
|
AStr := Format('<font color="#ff0000">%s</font>',[AStr]);
|
||||||
|
Writeln('Wrong:' +AStr); //Wrong: <font color="#ff0000"></font>????
|
||||||
|
if AStr<>'<font color="#ff0000">1,234,567,890</font>' then
|
||||||
|
err:=true;
|
||||||
|
//This is Ok, notice the changed output AText
|
||||||
|
AValue:=2134567890;
|
||||||
|
AStr := Format('%0.n',[1.0*AValue]); //2.134.567.890
|
||||||
|
AText := Format('<font color="#ff0000">%s</font>',[AStr]);
|
||||||
|
Writeln('Ok:' +AText); //Ok 2.134.567.890
|
||||||
|
if Atext<>'<font color="#ff0000">2,134,567,890</font>' then
|
||||||
|
err:=true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
p;
|
||||||
|
if err then
|
||||||
|
halt(1);
|
||||||
|
end.
|
37
tests/webtbs/tw10753a.pp
Normal file
37
tests/webtbs/tw10753a.pp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{$ifdef fpc}{$mode objfpc}{$H+}{$endif}
|
||||||
|
|
||||||
|
const buf: array[0..5] of char = 'abcdef';
|
||||||
|
|
||||||
|
function foo(const a: string): string;
|
||||||
|
begin
|
||||||
|
SetLength(result, 6);
|
||||||
|
Move(buf, result[1], sizeof(buf));
|
||||||
|
if a <> '1234567890' then
|
||||||
|
begin
|
||||||
|
writeln('Failed: ', a);
|
||||||
|
Halt(1);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
writeln('ok');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure test_proc(var a: string);
|
||||||
|
var
|
||||||
|
s: string;
|
||||||
|
begin
|
||||||
|
{ Don't call UniqueString(s) here because it makes the compiler assume
|
||||||
|
that address of s is taken, and assignment s := foo(s) is not optimized }
|
||||||
|
s := a; // refcount=2
|
||||||
|
a := 'whatever'; // modify source -> s.refcount becomes 1
|
||||||
|
writeln('before: ', s);
|
||||||
|
s := foo(s);
|
||||||
|
writeln(s);
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
s: string;
|
||||||
|
begin
|
||||||
|
s := '1234567890';
|
||||||
|
UniqueString(s);
|
||||||
|
test_proc(s);
|
||||||
|
end.
|
Loading…
Reference in New Issue
Block a user