+ support for simplifying simple inline functions down to a single

constant node (rather than to just a blocknode with a statement
    assigning a constant to a temp)

git-svn-id: trunk@6832 -
This commit is contained in:
Jonas Maebe 2007-03-13 22:27:55 +00:00
parent 0f546da041
commit 168e652d42
5 changed files with 175 additions and 4 deletions

1
.gitattributes vendored
View File

@ -6777,6 +6777,7 @@ tests/test/tinline3.pp svneol=native#text/plain
tests/test/tinline4.pp svneol=native#text/plain
tests/test/tinline5.pp -text
tests/test/tinline6.pp svneol=native#text/plain
tests/test/tinline7.pp svneol=native#text/plain
tests/test/tint2str1.pp svneol=native#text/plain
tests/test/tint2str2.pp svneol=native#text/plain
tests/test/tint641.pp svneol=native#text/plain

View File

@ -70,6 +70,7 @@ interface
tstatementnode = class(tbinarynode)
constructor create(l,r : tnode);virtual;
function simplify : tnode; override;
function pass_1 : tnode;override;
function pass_typecheck:tnode;override;
procedure printnodetree(var t:text);override;
@ -81,6 +82,7 @@ interface
tblocknode = class(tunarynode)
constructor create(l : tnode);virtual;
destructor destroy; override;
function simplify : tnode; override;
function pass_1 : tnode;override;
function pass_typecheck:tnode;override;
{$ifdef state_tracking}
@ -307,6 +309,58 @@ implementation
inherited create(statementn,l,r);
end;
function tstatementnode.simplify : tnode;
begin
result:=nil;
{ these "optimizations" are only to make it more easy to recognise }
{ blocknodes which at the end of inlining only contain one single }
{ statement. Simplifying inside blocknode.simplify could be dangerous }
{ because if the main blocknode which makes up a procedure/function }
{ body were replaced with a statementn/nothingn, this could cause }
{ problems elsewhere in the compiler which expects a blocknode }
{ remove next statement if it's a nothing-statement (since if it's }
{ the last, it won't remove itself -- see next simplification) }
while assigned(right) and
(tstatementnode(right).left.nodetype = nothingn) do
begin
result:=tstatementnode(right).right;
tstatementnode(right).right:=nil;
right.free;
right:=result;
result:=nil;
end;
{ Remove initial nothingn if there are other statements. If there }
{ are no other statements, returning nil doesn't help (will be }
{ interpreted as "can't be simplified") and replacing the }
{ statementnode with a nothingnode cannot be done (because it's }
{ possible this statementnode is a child of a blocknode, and }
{ blocknodes are expected to only contain statementnodes) }
if (left.nodetype = nothingn) and
assigned(right) then
begin
result:=right;
right:=nil;
exit;
end;
{ if the current statement contains a block with one statement, }
{ replace the current statement with that block's statement }
if (left.nodetype = blockn) and
assigned(tblocknode(left).left) and
not assigned(tstatementnode(tblocknode(left).left).right) then
begin
result:=tblocknode(left).left;
tstatementnode(result).right:=right;
right:=nil;
tblocknode(left).left:=nil;
exit;
end;
end;
function tstatementnode.pass_typecheck:tnode;
begin
result:=nil;
@ -387,6 +441,31 @@ implementation
inherited destroy;
end;
function tblocknode.simplify: tnode;
var
hp, next: tstatementnode;
begin
result := nil;
{ Warning: never replace a blocknode with another node type, }
{ since the block may be the main block of a procedure/function/ }
{ main program body, and those nodes should always be blocknodes }
{ since that's what the compiler expects elsewhere. }
{ if the current block contains only one statement, and }
{ this one statement only contains another block, replace }
{ this block with that other block. }
if assigned(left) and
not assigned(tstatementnode(left).right) and
(tstatementnode(left).left.nodetype = blockn) then
begin
result:=tstatementnode(left).left;
tstatementnode(left).left:=nil;
exit;
end;
end;
function tblocknode.pass_typecheck:tnode;
var
hp : tstatementnode;

View File

@ -71,6 +71,7 @@ interface
function replaceparaload(var n: tnode; arg: pointer): foreachnoderesult;
procedure createlocaltemps(p:TObject;arg:pointer);
function pass1_inline:tnode;
function getfuncretassignment(inlineblock: tblocknode): tnode;
protected
pushedparasize : longint;
public
@ -2628,6 +2629,62 @@ implementation
end;
function tcallnode.getfuncretassignment(inlineblock: tblocknode): tnode;
var
hp: tstatementnode;
resassign: tnode;
begin
result:=nil;
if not assigned(funcretnode) or
not(cnf_return_value_used in callnodeflags) then
exit;
{ tempcreatenode for the function result }
hp:=tstatementnode(inlineblock.left);
if not(assigned(hp)) or
(hp.left.nodetype <> tempcreaten) then
exit;
{ assignment to the result }
hp:=tstatementnode(hp.right);
if not(assigned(hp)) or
(hp.left.nodetype<>assignn) or
{ left must be function result }
(not(tassignmentnode(hp.left).left.isequal(funcretnode)) and
{ can have extra type conversion due to absolute mapping }
{ of <fucntionname> on function result var }
not((tassignmentnode(hp.left).left.nodetype = typeconvn) and
(ttypeconvnode(tassignmentnode(hp.left).left).convtype = tc_equal) and
(ttypeconvnode(tassignmentnode(hp.left).left).left.isequal(funcretnode)))) or
{ right must be a constant (mainly to avoid trying to reuse }
{ local temps which may already be freed afterwards once these }
{ checks are made looser) }
not is_constnode(tassignmentnode(hp.left).right) then
exit
else
resassign:=hp.left;
{ tempdelete to normal of the function result }
hp:=tstatementnode(hp.right);
if not(assigned(hp)) or
(hp.left.nodetype <> tempdeleten) then
exit;
{ the function result once more }
hp:=tstatementnode(hp.right);
if not(assigned(hp)) or
not(hp.left.isequal(funcretnode)) then
exit;
{ should be the end }
if assigned(hp.right) then
exit;
{ we made it! }
result:=tassignmentnode(resassign).right.getcopy;
firstpass(result);
end;
function tcallnode.pass1_inline:tnode;
var
@ -2676,11 +2733,22 @@ implementation
exclude(procdefinition.procoptions,po_inline);
dosimplify(createblock);
firstpass(createblock);
include(procdefinition.procoptions,po_inline);
{ return inlined block }
result := createblock;
{ if all that's left of the inlined function is an constant }
{ assignment to the result, replace the whole block with what's }
{ assigned to the result. There will also be a tempcreatenode for }
{ the function result itself though, so ignore it. The statement/ }
{ blocknode simplification code will have removed all nothingn- }
{ statements empty nested blocks, so we don't have to care about }
{ those }
result := getfuncretassignment(createblock);
if assigned(result) then
createblock.free
else
{ return inlined block }
result := createblock;
{$ifdef DEBUGINLINE}
writeln;

View File

@ -170,7 +170,19 @@ implementation
firstpass(hp);
{ switch to new node }
p:=hp;
end;
end
else
begin
{ inlining happens in pass_1 and can cause new }
{ simplify opportunities }
hp:=p.simplify;
if assigned(hp) then
begin
p.free;
firstpass(hp);
p:=hp;
end;
end;
if codegenerror then
include(p.flags,nf_error)
else

11
tests/test/tinline7.pp Normal file
View File

@ -0,0 +1,11 @@
{$inline on}
function f(const a,b: longint): longint; inline;
begin
f:=a*b;
end;
begin
if (f(f(f(2,5),3),4) <> 120) then
halt(1);
end.