blob: b2ed5a61b447752d4c980399e119c1884c021cc2 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// IrNodes are kept in a separate library to have precise control over their
// dependencies on other parts of the system.
library dart2js.ir_nodes;
import '../dart2jslib.dart' as dart2js show Constant;
import '../elements/elements.dart' show FunctionElement, LibraryElement;
import 'ir_pickler.dart' show Pickler, IrConstantPool;
import '../universe/universe.dart' show Selector, SelectorKind;
import '../util/util.dart' show Spannable;
abstract class Node {
static int hashCount = 0;
final int hashCode = hashCount = (hashCount + 1) & 0x3fffffff;
accept(Visitor visitor);
}
abstract class Expression extends Node {
Expression plug(Expression expr) => throw 'impossible';
}
// Trivial is the base class of things that variables can refer to: primitives,
// continuations, function and continuation parameters, etc.
abstract class Trivial extends Node {
// The head of a linked-list of occurrences, in no particular order.
Variable firstUse = null;
}
// Operands to invocations and primitives are always variables. They point to
// their definition and are linked into a list of occurrences.
class Variable {
Trivial definition;
Variable nextUse = null;
Variable(this.definition) {
nextUse = definition.firstUse;
definition.firstUse = this;
}
}
// Binding a value (primitive or constant): 'let val x = V in E'. The bound
// value is in scope in the body.
// During one-pass construction a LetVal with an empty body is used to
// represent one-level context 'let val x = V in []'.
class LetVal extends Expression {
final Trivial value;
Expression body = null;
LetVal(this.value);
Expression plug(Expression expr) {
assert(body == null);
return body = expr;
}
accept(Visitor visitor) => visitor.visitLetVal(this);
}
// Binding a continuation: 'let cont k(v) = E in E'. The bound continuation is
// in scope in the body and the continuation parameter is in scope in the
// continuation body.
// During one-pass construction a LetCont with an empty continuation body is
// used to represent the one-level context 'let cont k(v) = [] in E'.
class LetCont extends Expression {
final Continuation continuation;
final Expression body;
LetCont(this.continuation, this.body);
Expression plug(Expression expr) {
assert(continuation.body == null);
return continuation.body = expr;
}
accept(Visitor visitor) => visitor.visitLetCont(this);
}
// Invoke a static function in tail position.
class InvokeStatic extends Expression {
final FunctionElement target;
/**
* The selector encodes how the function is invoked: number of positional
* arguments, names used in named arguments. This information is required
* to build the [StaticCallSiteTypeInformation] for the inference graph.
*/
final Selector selector;
final Variable continuation;
final List<Variable> arguments;
InvokeStatic(this.target, this.selector, Continuation cont,
List<Trivial> args)
: continuation = new Variable(cont),
arguments = args.map((t) => new Variable(t)).toList(growable: false) {
assert(selector.kind == SelectorKind.CALL);
assert(selector.name == target.name);
}
accept(Visitor visitor) => visitor.visitInvokeStatic(this);
}
// Invoke a continuation in tail position.
class InvokeContinuation extends Expression {
final Variable continuation;
final Variable argument;
InvokeContinuation(Continuation cont, Trivial arg)
: continuation = new Variable(cont),
argument = new Variable(arg);
accept(Visitor visitor) => visitor.visitInvokeContinuation(this);
}
// Constants are values, they are always bound by 'let val'.
class Constant extends Trivial {
final dart2js.Constant value;
Constant(this.value);
accept(Visitor visitor) => visitor.visitConstant(this);
}
// Function and continuation parameters are trivial.
class Parameter extends Trivial {
Parameter();
accept(Visitor visitor) => visitor.visitParameter(this);
}
// Continuations are trivial. They are normally bound by 'let cont'. A
// continuation with no parameter (or body) is used to represent a function's
// return continuation.
class Continuation extends Trivial {
final Parameter parameter;
Expression body = null;
Continuation(this.parameter);
Continuation.retrn() : parameter = null;
accept(Visitor visitor) => visitor.visitContinuation(this);
}
// A function definition, consisting of parameters and a body. The parameters
// include a distinguished continuation parameter.
class Function extends Expression {
final int endOffset;
final int namePosition;
final Continuation returnContinuation;
final Expression body;
Function(this.endOffset, this.namePosition, this.returnContinuation,
this.body);
List<int> pickle(IrConstantPool constantPool) {
return new Pickler(constantPool).pickle(this);
}
accept(Visitor visitor) => visitor.visitFunction(this);
}
abstract class Visitor<T> {
T visitNode(Node node) => node.accept(this);
T visitFunction(Function node) => visitNode(node);
T visitExpression(Expression node) => visitNode(node);
T visitTrivial(Trivial node) => visitNode(node);
T visitLetVal(LetVal expr) => visitExpression(expr);
T visitLetCont(LetCont expr) => visitExpression(expr);
T visitInvokeStatic(InvokeStatic expr) => visitExpression(expr);
T visitInvokeContinuation(InvokeContinuation expr) => visitExpression(expr);
T visitConstant(Constant triv) => visitTrivial(triv);
T visitParameter(Parameter triv) => visitTrivial(triv);
T visitContinuation(Continuation triv) => visitTrivial(triv);
}
// Generate a Lisp-like S-expression representation of an IR node as a string.
// The representation is not pretty-printed, but it can easily be quoted and
// dropped into the REPL of one's favorite Lisp or Scheme implementation to be
// pretty-printed.
class SExpressionStringifier extends Visitor<String> {
final Map<Trivial, String> names = <Trivial, String>{};
int _valueCounter = 0;
int _continuationCounter = 0;
String newValueName() => 'v${_valueCounter++}';
String newContinuationName() => 'k${_continuationCounter++}';
String visitFunction(Function node) {
names[node.returnContinuation] = 'return';
return '(Function ${node.body.accept(this)})';
}
String visitLetVal(LetVal expr) {
String name = newValueName();
names[expr.value] = name;
String value = expr.value.accept(this);
String body = expr.body.accept(this);
return '(LetVal $name $value) $body';
}
String visitLetCont(LetCont expr) {
String cont = newContinuationName();
String param = newValueName();
names[expr.continuation] = cont;
names[expr.continuation.parameter] = param;
String contBody = expr.continuation.body.accept(this);
String body = expr.body == null ? 'null' : expr.body.accept(this);
return '(LetCont ($cont $param) $contBody) $body';
}
String visitInvokeStatic(InvokeStatic expr) {
String name = expr.target.name;
String cont = names[expr.continuation.definition];
List<String> args =
expr.arguments.map((v) => names[v.definition]).toList(growable: false);
return '(InvokeStatic $name $cont ${args.join(' ')})';
}
String visitInvokeContinuation(InvokeContinuation expr) {
String cont = names[expr.continuation.definition];
String arg = names[expr.argument.definition];
return '(InvokeContinuation $cont $arg)';
}
String visitConstant(Constant triv) {
return '(Constant ${triv.value})';
}
String visitParameter(Parameter triv) {
return '(Unexpected Parameter)';
}
String visitContinuation(Continuation triv) {
return '(Unexpected Continuation)';
}
}