mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-01 02:42:05 +00:00
965 lines
27 KiB
ObjectPascal
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.
|