blob: 9c91c0fcdb76605306429ce4e86130b2722e57f0 [file] [log] [blame]
// Copyright (c) 2016, 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.
import 'package:kernel/ast.dart' as ir;
import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
import '../common/tasks.dart' show CompilerTask;
import '../compiler.dart';
import '../diagnostics/spannable.dart';
import '../elements/elements.dart';
import '../io/source_information.dart';
import '../js_backend/backend.dart' show JavaScriptBackend;
import '../kernel/kernel.dart';
import '../kernel/kernel_visitor.dart';
import '../resolution/tree_elements.dart';
import '../tree/dartstring.dart';
import '../types/masks.dart';
import 'graph_builder.dart';
import 'kernel_ast_adapter.dart';
import 'locals_handler.dart';
import 'nodes.dart';
class SsaKernelBuilderTask extends CompilerTask {
final JavaScriptBackend backend;
final SourceInformationStrategy sourceInformationFactory;
String get name => 'SSA kernel builder';
SsaKernelBuilderTask(JavaScriptBackend backend, this.sourceInformationFactory)
: backend = backend,
super(backend.compiler.measurer);
HGraph build(CodegenWorkItem work) {
return measure(() {
AstElement element = work.element.implementation;
TreeElements treeElements = work.resolvedAst.elements;
Kernel kernel = new Kernel(backend.compiler);
KernelVisitor visitor = new KernelVisitor(element, treeElements, kernel);
IrFunction function;
try {
function = visitor.buildFunction();
} catch (e) {
throw "Failed to convert to Kernel IR: $e";
}
KernelSsaBuilder builder = new KernelSsaBuilder(
function,
element,
work.resolvedAst,
backend.compiler,
work.registry,
sourceInformationFactory,
visitor,
kernel);
return builder.build();
});
}
}
class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
final IrFunction function;
final FunctionElement functionElement;
final ResolvedAst resolvedAst;
final Compiler compiler;
final CodegenRegistry registry;
JavaScriptBackend get backend => compiler.backend;
LocalsHandler localsHandler;
SourceInformationBuilder sourceInformationBuilder;
KernelAstAdapter astAdapter;
KernelSsaBuilder(
this.function,
this.functionElement,
this.resolvedAst,
this.compiler,
this.registry,
SourceInformationStrategy sourceInformationFactory,
KernelVisitor visitor,
Kernel kernel) {
graph.element = functionElement;
// TODO(het): Should sourceInformationBuilder be in GraphBuilder?
this.sourceInformationBuilder =
sourceInformationFactory.createBuilderForContext(resolvedAst);
graph.sourceInformation =
sourceInformationBuilder.buildVariableDeclaration();
this.localsHandler =
new LocalsHandler(this, functionElement, null, compiler);
this.astAdapter = new KernelAstAdapter(compiler.backend, resolvedAst,
visitor.nodeToAst, visitor.nodeToElement, kernel.functions);
}
HGraph build() {
// TODO(het): no reason to do this here...
HInstruction.idCounter = 0;
if (function.kind == ir.ProcedureKind.Method) {
buildMethod(function, functionElement);
} else {
compiler.reporter.internalError(
functionElement,
"Unable to convert this kind of Kernel "
"procedure to SSA: ${function.kind}");
}
assert(graph.isValid());
return graph;
}
/// Builds a SSA graph for [method].
void buildMethod(IrFunction method, FunctionElement functionElement) {
openFunction(method, functionElement);
method.node.body.accept(this);
closeFunction();
}
void openFunction(IrFunction method, FunctionElement functionElement) {
HBasicBlock block = graph.addNewBlock();
open(graph.entry);
// TODO(het): Register parameters with a locals handler
localsHandler.startFunction(functionElement, resolvedAst.node);
close(new HGoto()).addSuccessor(block);
open(block);
}
void closeFunction() {
if (!isAborted()) closeAndGotoExit(new HGoto());
graph.finalize();
}
@override
void visitBlock(ir.Block block) {
assert(!isAborted());
for (ir.Statement statement in block.statements) {
statement.accept(this);
if (!isReachable) {
// The block has been aborted by a return or a throw.
if (stack.isNotEmpty) {
compiler.reporter.internalError(
NO_LOCATION_SPANNABLE, 'Non-empty instruction stack.');
}
return;
}
}
assert(!current.isClosed());
if (stack.isNotEmpty) {
compiler.reporter
.internalError(NO_LOCATION_SPANNABLE, 'Non-empty instruction stack');
}
}
@override
void visitReturnStatement(ir.ReturnStatement returnStatement) {
HInstruction value;
if (returnStatement.expression == null) {
value = graph.addConstantNull(compiler);
} else {
returnStatement.expression.accept(this);
value = pop();
// TODO(het): Check or trust the type of value
}
// TODO(het): Add source information
// TODO(het): Set a return value instead of closing the function when we
// support inlining.
closeAndGotoExit(new HReturn(value, null));
}
@override
void visitIntLiteral(ir.IntLiteral intLiteral) {
stack.add(graph.addConstantInt(intLiteral.value, compiler));
}
@override
visitDoubleLiteral(ir.DoubleLiteral doubleLiteral) {
stack.add(graph.addConstantDouble(doubleLiteral.value, compiler));
}
@override
visitBoolLiteral(ir.BoolLiteral boolLiteral) {
stack.add(graph.addConstantBool(boolLiteral.value, compiler));
}
@override
visitStringLiteral(ir.StringLiteral stringLiteral) {
stack.add(graph.addConstantString(
new DartString.literal(stringLiteral.value), compiler));
}
@override
visitSymbolLiteral(ir.SymbolLiteral symbolLiteral) {
stack.add(graph.addConstant(
astAdapter.getConstantForSymbol(symbolLiteral), compiler));
registry?.registerConstSymbol(symbolLiteral.value);
}
@override
visitNullLiteral(ir.NullLiteral nullLiteral) {
stack.add(graph.addConstantNull(compiler));
}
@override
visitVariableGet(ir.VariableGet variableGet) {
LocalElement local = astAdapter.getElement(variableGet.variable);
stack.add(localsHandler.readLocal(local));
}
@override
visitStaticInvocation(ir.StaticInvocation invocation) {
List<HInstruction> inputs = <HInstruction>[];
for (ir.Expression argument in invocation.arguments.positional) {
argument.accept(this);
inputs.add(pop());
}
for (ir.NamedExpression argument in invocation.arguments.named) {
argument.value.accept(this);
inputs.add(pop());
}
ir.Procedure target = invocation.target;
bool targetCanThrow = astAdapter.getCanThrow(target);
TypeMask typeMask = astAdapter.returnTypeOf(target);
HInstruction instruction = new HInvokeStatic(
astAdapter.getElement(target).declaration, inputs, typeMask,
targetCanThrow: targetCanThrow);
instruction.sideEffects = astAdapter.getSideEffects(target);
push(instruction);
}
@override
visitExpressionStatement(ir.ExpressionStatement exprStatement) {
exprStatement.expression.accept(this);
pop();
}
}