Files
Nim/nim/transf.pas
2010-02-14 00:29:35 +01:00

965 lines
27 KiB
ObjectPascal

//
//
// The Nimrod Compiler
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit transf;
// This module implements the transformator. It transforms the syntax tree
// to ease the work of the code generators. Does some transformations:
//
// * inlines iterators
// * inlines constants
// * performes contant folding
// * introduces nkHiddenDeref, nkHiddenSubConv, etc.
// * introduces method dispatchers
interface
{$include 'config.inc'}
uses
sysutils, nsystem, charsets, strutils,
lists, options, ast, astalgo, trees, treetab, evals,
msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys, cgmeth;
const
genPrefix = ':tmp'; // prefix for generated names
function transfPass(): TPass;
implementation
type
PTransCon = ^TTransCon;
TTransCon = record // part of TContext; stackable
mapping: TIdNodeTable; // mapping from symbols to nodes
owner: PSym; // current owner
forStmt: PNode; // current for stmt
next: PTransCon; // for stacking
end;
TTransfContext = object(passes.TPassContext)
module: PSym;
transCon: PTransCon; // top of a TransCon stack
end;
PTransf = ^TTransfContext;
function newTransCon(): PTransCon;
begin
new(result);
{@ignore}
fillChar(result^, sizeof(result^), 0);
{@emit}
initIdNodeTable(result.mapping);
end;
procedure pushTransCon(c: PTransf; t: PTransCon);
begin
t.next := c.transCon;
c.transCon := t;
end;
procedure popTransCon(c: PTransf);
begin
if (c.transCon = nil) then InternalError('popTransCon');
c.transCon := c.transCon.next;
end;
// ------------ helpers -----------------------------------------------------
function getCurrOwner(c: PTransf): PSym;
begin
if c.transCon <> nil then result := c.transCon.owner
else result := c.module;
end;
function newTemp(c: PTransf; typ: PType; const info: TLineInfo): PSym;
begin
result := newSym(skTemp, getIdent(genPrefix), getCurrOwner(c));
result.info := info;
result.typ := skipTypes(typ, {@set}[tyGenericInst]);
include(result.flags, sfFromGeneric);
end;
// --------------------------------------------------------------------------
function transform(c: PTransf; n: PNode): PNode; forward;
(*
Transforming iterators into non-inlined versions is pretty hard, but
unavoidable for not bloating the code too much. If we had direct access to
the program counter, things'd be much easier.
::
iterator items(a: string): char =
var i = 0
while i < length(a):
yield a[i]
inc(i)
for ch in items("hello world"): # `ch` is an iteration variable
echo(ch)
Should be transformed into::
type
TItemsClosure = record
i: int
state: int
proc items(a: string, c: var TItemsClosure): char =
case c.state
of 0: goto L0 # very difficult without goto!
of 1: goto L1 # can be implemented by GCC's computed gotos
block L0:
c.i = 0
while c.i < length(a):
c.state = 1
return a[i]
block L1: inc(c.i)
More efficient, but not implementable::
type
TItemsClosure = record
i: int
pc: pointer
proc items(a: string, c: var TItemsClosure): char =
goto c.pc
c.i = 0
while c.i < length(a):
c.pc = label1
return a[i]
label1: inc(c.i)
*)
function newAsgnStmt(c: PTransf; le, ri: PNode): PNode;
begin
result := newNodeI(nkFastAsgn, ri.info);
addSon(result, le);
addSon(result, ri);
end;
function transformSym(c: PTransf; n: PNode): PNode;
var
tc: PTransCon;
b: PNode;
begin
if (n.kind <> nkSym) then internalError(n.info, 'transformSym');
tc := c.transCon;
if sfBorrow in n.sym.flags then begin
// simply exchange the symbol:
b := n.sym.ast.sons[codePos];
if b.kind <> nkSym then
internalError(n.info, 'wrong AST for borrowed symbol');
b := newSymNode(b.sym);
b.info := n.info;
end
else
b := n;
//writeln('transformSym', n.sym.id : 5);
while tc <> nil do begin
result := IdNodeTableGet(tc.mapping, b.sym);
if result <> nil then exit;
//write('not found in: ');
//writeIdNodeTable(tc.mapping);
tc := tc.next
end;
result := b;
case b.sym.kind of
skConst, skEnumField: begin // BUGFIX: skEnumField was missing
if not (skipTypes(b.sym.typ, abstractInst).kind in ConstantDataTypes) then begin
result := getConstExpr(c.module, b);
if result = nil then InternalError(b.info, 'transformSym: const');
end
end
else begin end
end
end;
procedure transformContinueAux(c: PTransf; n: PNode; labl: PSym;
var counter: int);
var
i: int;
begin
if n = nil then exit;
case n.kind of
nkEmpty..nkNilLit, nkForStmt, nkWhileStmt: begin end;
nkContinueStmt: begin
n.kind := nkBreakStmt;
addSon(n, newSymNode(labl));
inc(counter);
end;
else begin
for i := 0 to sonsLen(n)-1 do
transformContinueAux(c, n.sons[i], labl, counter);
end
end
end;
function transformContinue(c: PTransf; n: PNode): PNode;
// we transform the continue statement into a block statement
var
i, counter: int;
x: PNode;
labl: PSym;
begin
result := n;
for i := 0 to sonsLen(n)-1 do
result.sons[i] := transform(c, n.sons[i]);
counter := 0;
labl := newSym(skLabel, nil, getCurrOwner(c));
labl.name := getIdent(genPrefix +{&} ToString(labl.id));
labl.info := result.info;
transformContinueAux(c, result, labl, counter);
if counter > 0 then begin
x := newNodeI(nkBlockStmt, result.info);
addSon(x, newSymNode(labl));
addSon(x, result);
result := x
end
end;
function skipConv(n: PNode): PNode;
begin
case n.kind of
nkObjUpConv, nkObjDownConv, nkPassAsOpenArray, nkChckRange,
nkChckRangeF, nkChckRange64:
result := n.sons[0];
nkHiddenStdConv, nkHiddenSubConv, nkConv: result := n.sons[1];
else result := n
end
end;
function newTupleAccess(tup: PNode; i: int): PNode;
var
lit: PNode;
begin
result := newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i]);
addSon(result, copyTree(tup));
lit := newNodeIT(nkIntLit, tup.info, getSysType(tyInt));
lit.intVal := i;
addSon(result, lit);
end;
procedure unpackTuple(c: PTransf; n, father: PNode);
var
i: int;
begin
// XXX: BUG: what if `n` is an expression with side-effects?
for i := 0 to sonsLen(n)-1 do begin
addSon(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i))));
end
end;
function transformYield(c: PTransf; n: PNode): PNode;
var
e: PNode;
i: int;
begin
result := newNodeI(nkStmtList, n.info);
e := n.sons[0];
if skipTypes(e.typ, {@set}[tyGenericInst]).kind = tyTuple then begin
e := skipConv(e);
if e.kind = nkPar then begin
for i := 0 to sonsLen(e)-1 do begin
addSon(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, copyTree(e.sons[i]))));
end
end
else
unpackTuple(c, e, result);
end
else begin
e := transform(c, copyTree(e));
addSon(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], e));
end;
// add body of the for loop:
addSon(result, transform(c, lastSon(c.transCon.forStmt)));
end;
function inlineIter(c: PTransf; n: PNode): PNode;
var
i, j, L: int;
it: PNode;
newVar: PSym;
begin
result := n;
if n = nil then exit;
case n.kind of
nkEmpty..nkNilLit: begin
result := transform(c, copyTree(n));
end;
nkYieldStmt: result := transformYield(c, n);
nkVarSection: begin
result := copyTree(n);
for i := 0 to sonsLen(result)-1 do begin
it := result.sons[i];
if it.kind = nkCommentStmt then continue;
if it.kind = nkIdentDefs then begin
if (it.sons[0].kind <> nkSym) then
InternalError(it.info, 'inlineIter');
newVar := copySym(it.sons[0].sym);
include(newVar.flags, sfFromGeneric);
// fixes a strange bug for rodgen:
//include(it.sons[0].sym.flags, sfFromGeneric);
newVar.owner := getCurrOwner(c);
IdNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar));
it.sons[0] := newSymNode(newVar);
it.sons[2] := transform(c, it.sons[2]);
end
else begin
if it.kind <> nkVarTuple then
InternalError(it.info, 'inlineIter: not nkVarTuple');
L := sonsLen(it);
for j := 0 to L-3 do begin
newVar := copySym(it.sons[j].sym);
include(newVar.flags, sfFromGeneric);
newVar.owner := getCurrOwner(c);
IdNodeTablePut(c.transCon.mapping, it.sons[j].sym,
newSymNode(newVar));
it.sons[j] := newSymNode(newVar);
end;
assert(it.sons[L-2] = nil);
it.sons[L-1] := transform(c, it.sons[L-1]);
end
end
end
else begin
result := copyNode(n);
for i := 0 to sonsLen(n)-1 do addSon(result, inlineIter(c, n.sons[i]));
result := transform(c, result);
end
end
end;
procedure addVar(father, v: PNode);
var
vpart: PNode;
begin
vpart := newNodeI(nkIdentDefs, v.info);
addSon(vpart, v);
addSon(vpart, nil);
addSon(vpart, nil);
addSon(father, vpart);
end;
function transformAddrDeref(c: PTransf; n: PNode; a, b: TNodeKind): PNode;
var
m: PNode;
begin
case n.sons[0].kind of
nkObjUpConv, nkObjDownConv, nkPassAsOpenArray, nkChckRange,
nkChckRangeF, nkChckRange64: begin
m := n.sons[0].sons[0];
if (m.kind = a) or (m.kind = b) then begin
// addr ( nkPassAsOpenArray ( deref ( x ) ) ) --> nkPassAsOpenArray(x)
n.sons[0].sons[0] := m.sons[0];
result := transform(c, n.sons[0]);
exit
end
end;
nkHiddenStdConv, nkHiddenSubConv, nkConv: begin
m := n.sons[0].sons[1];
if (m.kind = a) or (m.kind = b) then begin
// addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
n.sons[0].sons[1] := m.sons[0];
result := transform(c, n.sons[0]);
exit
end
end;
else begin
if (n.sons[0].kind = a) or (n.sons[0].kind = b) then begin
// addr ( deref ( x )) --> x
result := transform(c, n.sons[0].sons[0]);
exit
end
end
end;
n.sons[0] := transform(c, n.sons[0]);
result := n;
end;
function transformConv(c: PTransf; n: PNode): PNode;
var
source, dest: PType;
diff: int;
begin
n.sons[1] := transform(c, n.sons[1]);
result := n;
// numeric types need range checks:
dest := skipTypes(n.typ, abstractVarRange);
source := skipTypes(n.sons[1].typ, abstractVarRange);
case dest.kind of
tyInt..tyInt64, tyEnum, tyChar, tyBool: begin
if (firstOrd(dest) <= firstOrd(source)) and
(lastOrd(source) <= lastOrd(dest)) then begin
// BUGFIX: simply leave n as it is; we need a nkConv node,
// but no range check:
result := n;
end
else begin // generate a range check:
if (dest.kind = tyInt64) or (source.kind = tyInt64) then
result := newNodeIT(nkChckRange64, n.info, n.typ)
else
result := newNodeIT(nkChckRange, n.info, n.typ);
dest := skipTypes(n.typ, abstractVar);
addSon(result, n.sons[1]);
addSon(result, newIntTypeNode(nkIntLit, firstOrd(dest), source));
addSon(result, newIntTypeNode(nkIntLit, lastOrd(dest), source));
end
end;
tyFloat..tyFloat128: begin
if skipTypes(n.typ, abstractVar).kind = tyRange then begin
result := newNodeIT(nkChckRangeF, n.info, n.typ);
dest := skipTypes(n.typ, abstractVar);
addSon(result, n.sons[1]);
addSon(result, copyTree(dest.n.sons[0]));
addSon(result, copyTree(dest.n.sons[1]));
end
end;
tyOpenArray: begin
result := newNodeIT(nkPassAsOpenArray, n.info, n.typ);
addSon(result, n.sons[1]);
end;
tyCString: begin
if source.kind = tyString then begin
result := newNodeIT(nkStringToCString, n.info, n.typ);
addSon(result, n.sons[1]);
end;
end;
tyString: begin
if source.kind = tyCString then begin
result := newNodeIT(nkCStringToString, n.info, n.typ);
addSon(result, n.sons[1]);
end;
end;
tyRef, tyPtr: begin
dest := skipTypes(dest, abstractPtrs);
source := skipTypes(source, abstractPtrs);
if source.kind = tyObject then begin
diff := inheritanceDiff(dest, source);
if diff < 0 then begin
result := newNodeIT(nkObjUpConv, n.info, n.typ);
addSon(result, n.sons[1]);
end
else if diff > 0 then begin
result := newNodeIT(nkObjDownConv, n.info, n.typ);
addSon(result, n.sons[1]);
end
else result := n.sons[1];
end
end;
// conversions between different object types:
tyObject: begin
diff := inheritanceDiff(dest, source);
if diff < 0 then begin
result := newNodeIT(nkObjUpConv, n.info, n.typ);
addSon(result, n.sons[1]);
end
else if diff > 0 then begin
result := newNodeIT(nkObjDownConv, n.info, n.typ);
addSon(result, n.sons[1]);
end
else result := n.sons[1];
end; (*
tyArray, tySeq: begin
if skipGeneric(dest
end; *)
tyGenericParam, tyOrdinal: result := n.sons[1];
// happens sometimes for generated assignments, etc.
else begin end
end;
end;
function skipPassAsOpenArray(n: PNode): PNode;
begin
result := n;
while result.kind = nkPassAsOpenArray do result := result.sons[0]
end;
type
TPutArgInto = (paDirectMapping, paFastAsgn, paVarAsgn);
function putArgInto(arg: PNode; formal: PType): TPutArgInto;
// This analyses how to treat the mapping "formal <-> arg" in an
// inline context.
var
i: int;
begin
if skipTypes(formal, abstractInst).kind = tyOpenArray then begin
result := paDirectMapping; // XXX really correct?
// what if ``arg`` has side-effects?
exit
end;
case arg.kind of
nkEmpty..nkNilLit: result := paDirectMapping;
nkPar, nkCurly, nkBracket: begin
result := paFastAsgn;
for i := 0 to sonsLen(arg)-1 do
if putArgInto(arg.sons[i], formal) <> paDirectMapping then
exit;
result := paDirectMapping;
end;
else begin
if skipTypes(formal, abstractInst).kind = tyVar then
result := paVarAsgn
else
result := paFastAsgn
end
end
end;
function transformFor(c: PTransf; n: PNode): PNode;
// generate access statements for the parameters (unless they are constant)
// put mapping from formal parameters to actual parameters
var
i, len: int;
call, v, body, arg: PNode;
newC: PTransCon;
temp, formal: PSym;
begin
if (n.kind <> nkForStmt) then InternalError(n.info, 'transformFor');
result := newNodeI(nkStmtList, n.info);
len := sonsLen(n);
n.sons[len-1] := transformContinue(c, n.sons[len-1]);
v := newNodeI(nkVarSection, n.info);
for i := 0 to len-3 do addVar(v, copyTree(n.sons[i])); // declare new vars
addSon(result, v);
newC := newTransCon();
call := n.sons[len-2];
if (call.kind <> nkCall) or (call.sons[0].kind <> nkSym) then
InternalError(call.info, 'transformFor');
newC.owner := call.sons[0].sym;
newC.forStmt := n;
if (newC.owner.kind <> skIterator) then
InternalError(call.info, 'transformFor');
// generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC);
for i := 1 to sonsLen(call)-1 do begin
arg := skipPassAsOpenArray(transform(c, call.sons[i]));
formal := skipTypes(newC.owner.typ, abstractInst).n.sons[i].sym;
//if IdentEq(newc.Owner.name, 'items') then
// liMessage(arg.info, warnUser, 'items: ' + nodeKindToStr[arg.kind]);
case putArgInto(arg, formal.typ) of
paDirectMapping: IdNodeTablePut(newC.mapping, formal, arg);
paFastAsgn: begin
// generate a temporary and produce an assignment statement:
temp := newTemp(c, formal.typ, formal.info);
addVar(v, newSymNode(temp));
addSon(result, newAsgnStmt(c, newSymNode(temp), arg));
IdNodeTablePut(newC.mapping, formal, newSymNode(temp));
end;
paVarAsgn: begin
assert(skipTypes(formal.typ, abstractInst).kind = tyVar);
InternalError(arg.info, 'not implemented: pass to var parameter');
end;
end;
end;
body := newC.owner.ast.sons[codePos];
pushInfoContext(n.info);
addSon(result, inlineIter(c, body));
popInfoContext();
popTransCon(c);
end;
function getMagicOp(call: PNode): TMagic;
begin
if (call.sons[0].kind = nkSym)
and (call.sons[0].sym.kind in [skProc, skMethod, skConverter]) then
result := call.sons[0].sym.magic
else
result := mNone
end;
procedure gatherVars(c: PTransf; n: PNode; var marked: TIntSet;
owner: PSym; container: PNode);
// gather used vars for closure generation
var
i: int;
s: PSym;
found: bool;
begin
if n = nil then exit;
case n.kind of
nkSym: begin
s := n.sym;
found := false;
case s.kind of
skVar: found := not (sfGlobal in s.flags);
skTemp, skForVar, skParam: found := true;
else begin end;
end;
if found and (owner.id <> s.owner.id)
and not IntSetContainsOrIncl(marked, s.id) then begin
include(s.flags, sfInClosure);
addSon(container, copyNode(n)); // DON'T make a copy of the symbol!
end
end;
nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: begin end;
else begin
for i := 0 to sonsLen(n)-1 do
gatherVars(c, n.sons[i], marked, owner, container);
end
end
end;
(*
# example:
proc map(f: proc (x: int): int {.closure}, a: seq[int]): seq[int] =
result = @[]
for elem in a:
add result, f(a)
proc addList(a: seq[int], y: int): seq[int] =
result = map(lambda (x: int): int = return x + y, a)
should generate -->
proc map(f: proc(x: int): int, closure: pointer,
a: seq[int]): seq[int] =
result = @[]
for elem in a:
add result, f(a, closure)
type
PMyClosure = ref object
y: var int
proc myLambda(x: int, closure: pointer) =
var cl = cast[PMyClosure](closure)
return x + cl.y
proc addList(a: seq[int], y: int): seq[int] =
var
cl: PMyClosure
new(cl)
cl.y = y
result = map(myLambda, cast[pointer](cl), a)
or (but this is not easier and not binary compatible with C!) -->
type
PClosure = ref object of TObject
f: proc (x: int, c: PClosure): int
proc map(f: PClosure, a: seq[int]): seq[int] =
result = @[]
for elem in a:
add result, f.f(a, f)
type
PMyClosure = ref object of PClosure
y: var int
proc myLambda(x: int, cl: PMyClosure) =
return x + cl.y
proc addList(a: seq[int], y: int): seq[int] =
var
cl: PMyClosure
new(cl)
cl.y = y
cl.f = myLambda
result = map(cl, a)
*)
procedure addFormalParam(routine: PSym; param: PSym);
begin
addSon(routine.typ, param.typ);
addSon(routine.ast.sons[paramsPos], newSymNode(param));
end;
function indirectAccess(a, b: PSym): PNode;
// returns a^ .b as a node
var
x, y, deref: PNode;
begin
x := newSymNode(a);
y := newSymNode(b);
deref := newNodeI(nkDerefExpr, x.info);
deref.typ := x.typ.sons[0];
addSon(deref, x);
result := newNodeI(nkDotExpr, x.info);
addSon(result, deref);
addSon(result, y);
result.typ := y.typ;
end;
function transformLambda(c: PTransf; n: PNode): PNode;
var
marked: TIntSet;
closure: PNode;
s, param: PSym;
cl, p: PType;
i: int;
newC: PTransCon;
begin
result := n;
IntSetInit(marked);
if (n.sons[namePos].kind <> nkSym) then
InternalError(n.info, 'transformLambda');
s := n.sons[namePos].sym;
closure := newNodeI(nkRecList, n.sons[codePos].info);
gatherVars(c, n.sons[codePos], marked, s, closure);
// add closure type to the param list (even if closure is empty!):
cl := newType(tyObject, s);
cl.n := closure;
addSon(cl, nil); // no super class
p := newType(tyRef, s);
addSon(p, cl);
param := newSym(skParam, getIdent(genPrefix + 'Cl'), s);
param.typ := p;
addFormalParam(s, param);
// all variables that are accessed should be accessed by the new closure
// parameter:
if sonsLen(closure) > 0 then begin
newC := newTransCon();
for i := 0 to sonsLen(closure)-1 do begin
IdNodeTablePut(newC.mapping, closure.sons[i].sym,
indirectAccess(param, closure.sons[i].sym))
end;
pushTransCon(c, newC);
n.sons[codePos] := transform(c, n.sons[codePos]);
popTransCon(c);
end;
// Generate code to allocate and fill the closure. This has to be done in
// the outer routine!
end;
function transformCase(c: PTransf; n: PNode): PNode;
// removes `elif` branches of a case stmt
// adds ``else: nil`` if needed for the code generator
var
len, i, j: int;
ifs, elsen: PNode;
begin
len := sonsLen(n);
i := len-1;
if n.sons[i].kind = nkElse then dec(i);
if n.sons[i].kind = nkElifBranch then begin
while n.sons[i].kind = nkElifBranch do dec(i);
if (n.sons[i].kind <> nkOfBranch) then
InternalError(n.sons[i].info, 'transformCase');
ifs := newNodeI(nkIfStmt, n.sons[i+1].info);
elsen := newNodeI(nkElse, ifs.info);
for j := i+1 to len-1 do addSon(ifs, n.sons[j]);
setLength(n.sons, i+2);
addSon(elsen, ifs);
n.sons[i+1] := elsen;
end
else if (n.sons[len-1].kind <> nkElse) and
not (skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
[tyInt..tyInt64, tyChar, tyEnum]) then begin
//MessageOut(renderTree(n));
elsen := newNodeI(nkElse, n.info);
addSon(elsen, newNodeI(nkNilLit, n.info));
addSon(n, elsen)
end;
result := n;
for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]);
end;
function transformArrayAccess(c: PTransf; n: PNode): PNode;
var
i: int;
begin
result := copyTree(n);
result.sons[0] := skipConv(result.sons[0]);
result.sons[1] := skipConv(result.sons[1]);
for i := 0 to sonsLen(result)-1 do
result.sons[i] := transform(c, result.sons[i]);
end;
function getMergeOp(n: PNode): PSym;
begin
result := nil;
case n.kind of
nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit: begin
if (n.sons[0].Kind = nkSym) and (n.sons[0].sym.kind = skProc)
and (sfMerge in n.sons[0].sym.flags) then
result := n.sons[0].sym;
end
else begin end
end
end;
procedure flattenTreeAux(d, a: PNode; op: PSym);
var
i: int;
op2: PSym;
begin
op2 := getMergeOp(a);
if (op2 <> nil) and ((op2.id = op.id)
or (op.magic <> mNone) and (op2.magic = op.magic)) then
for i := 1 to sonsLen(a)-1 do
flattenTreeAux(d, a.sons[i], op)
else
// a is a "leaf", so add it:
addSon(d, copyTree(a))
end;
function flattenTree(root: PNode): PNode;
var
op: PSym;
begin
op := getMergeOp(root);
if op <> nil then begin
result := copyNode(root);
addSon(result, copyTree(root.sons[0]));
flattenTreeAux(result, root, op)
end
else
result := root
end;
function transformCall(c: PTransf; n: PNode): PNode;
var
i, j: int;
m, a: PNode;
op: PSym;
begin
result := flattenTree(n);
for i := 0 to sonsLen(result)-1 do
result.sons[i] := transform(c, result.sons[i]);
op := getMergeOp(result);
if (op <> nil) and (op.magic <> mNone) and (sonsLen(result) >= 3) then begin
m := result;
result := newNodeIT(nkCall, m.info, m.typ);
addSon(result, copyTree(m.sons[0]));
j := 1;
while j < sonsLen(m) do begin
a := m.sons[j];
inc(j);
if isConstExpr(a) then
while (j < sonsLen(m)) and isConstExpr(m.sons[j]) do begin
a := evalOp(op.magic, m, a, m.sons[j], nil);
inc(j)
end;
addSon(result, a);
end;
if sonsLen(result) = 2 then
result := result.sons[1];
end
else if (result.sons[0].kind = nkSym)
and (result.sons[0].sym.kind = skMethod) then begin
// use the dispatcher for the call:
result := methodCall(result);
end
(*
else if result.sons[0].kind = nkSym then begin
// optimization still too aggressive
op := result.sons[0].sym;
if (op.magic = mNone) and (op.kind = skProc)
and ([sfSideEffect, sfForward, sfNoReturn, sfImportc] * op.flags = [])
then begin
for i := 1 to sonsLen(result)-1 do
if not isConstExpr(result.sons[i]) then exit;
// compile-time evaluation:
a := evalConstExpr(c.module, result);
if (a <> nil) and (a.kind <> nkEmpty) then begin
messageout('evaluated at compile time: ' + rendertree(result));
result := a
end
end
end *)
end;
function transform(c: PTransf; n: PNode): PNode;
var
i: int;
cnst: PNode;
begin
result := n;
if n = nil then exit;
//if ToLinenumber(n.info) = 32 then
// MessageOut(RenderTree(n));
case n.kind of
nkSym: begin
result := transformSym(c, n);
exit
end;
nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: begin
// nothing to be done for leaves
end;
nkBracketExpr: result := transformArrayAccess(c, n);
nkLambda: result := transformLambda(c, n);
nkForStmt: result := transformFor(c, n);
nkCaseStmt: result := transformCase(c, n);
nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef: begin
if n.sons[genericParamsPos] = nil then begin
n.sons[codePos] := transform(c, n.sons[codePos]);
if n.kind = nkMethodDef then
methodDef(n.sons[namePos].sym);
end
end;
nkWhileStmt: begin
if (sonsLen(n) <> 2) then InternalError(n.info, 'transform');
n.sons[0] := transform(c, n.sons[0]);
n.sons[1] := transformContinue(c, n.sons[1]);
end;
nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
result := transformCall(c, result);
nkAddr, nkHiddenAddr:
result := transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref);
nkDerefExpr, nkHiddenDeref:
result := transformAddrDeref(c, n, nkAddr, nkHiddenAddr);
nkHiddenStdConv, nkHiddenSubConv, nkConv:
result := transformConv(c, n);
nkDiscardStmt: begin
for i := 0 to sonsLen(n)-1 do
result.sons[i] := transform(c, n.sons[i]);
if isConstExpr(result.sons[0]) then
result := newNode(nkCommentStmt)
end;
nkCommentStmt, nkTemplateDef: exit;
nkConstSection: exit; // do not replace ``const c = 3`` with ``const 3 = 3``
else begin
for i := 0 to sonsLen(n)-1 do
result.sons[i] := transform(c, n.sons[i]);
end
end;
cnst := getConstExpr(c.module, result);
if cnst <> nil then result := cnst; // do not miss an optimization
end;
function processTransf(context: PPassContext; n: PNode): PNode;
var
c: PTransf;
begin
c := PTransf(context);
result := transform(c, n);
end;
function openTransf(module: PSym; const filename: string): PPassContext;
var
n: PTransf;
begin
new(n);
{@ignore}
fillChar(n^, sizeof(n^), 0);
{@emit}
n.module := module;
result := n;
end;
function transfPass(): TPass;
begin
initPass(result);
result.open := openTransf;
result.process := processTransf;
result.close := processTransf; // we need to process generics too!
end;
end.