blob: 6d11313046aeb12e24b208bf2c60e1e4f9b751d8 [file] [log] [blame]
// Copyright (c) 2017, 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.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "include/dart_api.h"
#include "include/dart_native_api.h"
#include "platform/allocation.h"
#include "vm/branch_optimizer.h"
#include "vm/constant_propagator.h"
#include "vm/disassembler.h"
#include "vm/flow_graph.h"
#include "vm/flow_graph_allocator.h"
#include "vm/flow_graph_compiler.h"
#include "vm/flow_graph_inliner.h"
#include "vm/il_printer.h"
#include "vm/intermediate_language.h"
#include "vm/redundancy_elimination.h"
#include "vm/scopes.h"
#include "vm/thread.h"
namespace vipunen_dart {
Dart_Handle HandleError(Dart_Handle handle) {
if (Dart_IsError(handle)) {
Dart_PropagateError(handle);
}
return handle;
}
static Dart_Handle BuildInstructions(Assembler* assembler) {
Dart_Handle code =
Dart_NewTypedData(Dart_TypedData_kUint8, assembler->CodeSize());
Dart_TypedData_Type type;
void* data = NULL;
intptr_t code_size = 0;
HandleError(Dart_TypedDataAcquireData(code, &type, &data, &code_size));
{
int8_t* code_buffer = reinterpret_cast<int8_t*>(data);
assembler->FinalizeInstructions(
vipunen_dart::MemoryRegion(code_buffer, code_size));
if (false) {
vipunen_dart::Disassembler::Disassemble(
(vipunen_dart::uword)code_buffer,
(vipunen_dart::uword)(code_buffer + code_size),
vipunen_dart::Code(assembler->GetCodeComments()));
}
}
HandleError(Dart_TypedDataReleaseData(code));
return code;
}
static Dart_Handle BuildExternalFixups(Assembler* assembler) {
const GrowableArray<Assembler::ExternalFixup>& fixups =
assembler->GetExternalFixups();
Dart_Handle fixups_list = Dart_NewList(fixups.length());
for (intptr_t i = 0; i < fixups.length(); i++) {
Dart_Handle fixup = Dart_NewList(3);
Dart_ListSetAt(fixup, 0, Dart_NewInteger(fixups[i].offset));
switch (fixups[i].label.tag()) {
case ExternalLabel::kString:
Dart_ListSetAt(fixup, 1, Dart_NewStringFromCString("string"));
Dart_ListSetAt(fixup, 2, Dart_NewStringFromCString(
fixups[i].label.payload<char>()));
break;
case ExternalLabel::kTaggedConstant:
Dart_ListSetAt(fixup, 1, Dart_NewStringFromCString("tagged-constant"));
Dart_ListSetAt(fixup, 2, Dart_NewStringFromCString(
fixups[i].label.payload<char>()));
break;
case ExternalLabel::kDouble:
Dart_ListSetAt(fixup, 1, Dart_NewStringFromCString("double"));
Dart_ListSetAt(fixup, 2, Dart_NewDouble(fixups[i].label.f64()));
break;
case ExternalLabel::kRuntimeEntryPoint:
Dart_ListSetAt(fixup, 1, Dart_NewStringFromCString("entry-point"));
Dart_ListSetAt(fixup, 2, Dart_NewStringFromCString(
fixups[i].label.payload<char>()));
break;
case ExternalLabel::kEntryPoint:
Dart_ListSetAt(fixup, 1, Dart_NewStringFromCString("entry-point"));
Dart_ListSetAt(fixup, 2,
Dart_NewStringFromCString(
fixups[i].label.payload<CallTarget>()->name()));
break;
}
Dart_ListSetAt(fixups_list, i, fixup);
}
return fixups_list;
}
template <typename T>
class TypedDataAllocation : public ValueObject {
public:
TypedDataAllocation(Dart_TypedData_Type type, intptr_t length) {
handle_ = Dart_NewTypedData(type, length);
intptr_t code_size = 0;
HandleError(Dart_TypedDataAcquireData(handle_, &type, &data_, &code_size));
}
~TypedDataAllocation() { HandleError(Dart_TypedDataReleaseData(handle_)); }
T* data() { return reinterpret_cast<T*>(data_); }
T& operator[](intptr_t i) { return data()[i]; }
Dart_Handle handle() { return handle_; }
private:
void* data_;
Dart_Handle handle_;
};
static Dart_Handle BuildStackMaps(FlowGraphCompiler* flow_graph_compiler) {
if (flow_graph_compiler == NULL) return Dart_Null();
StackMapTableBuilder* builder = flow_graph_compiler->stackmap_table_builder();
const intptr_t length = builder->Length();
Dart_Handle result = Dart_NewList(4);
{
TypedDataAllocation<uint16_t> bitmap_lengths(Dart_TypedData_kUint16,
length);
for (intptr_t i = 0; i < length; i++) {
bitmap_lengths[i] = builder->BitMapBuilderAt(i)->Length();
}
Dart_ListSetAt(result, 0, bitmap_lengths.handle());
}
{
TypedDataAllocation<uint8_t> slowpath_bit_counts(Dart_TypedData_kUint8,
length);
for (intptr_t i = 0; i < length; i++) {
slowpath_bit_counts[i] = builder->SlowPathBitCountAt(i);
}
Dart_ListSetAt(result, 1, slowpath_bit_counts.handle());
}
{
TypedDataAllocation<uint32_t> pc_offsets(Dart_TypedData_kUint32, length);
for (intptr_t i = 0; i < length; i++) {
pc_offsets[i] = builder->PcOffsetAt(i);
}
Dart_ListSetAt(result, 2, pc_offsets.handle());
}
Dart_Handle bitmaps = Dart_NewList(length);
for (intptr_t i = 0; i < length; i++) {
BitmapBuilder* bitmap_builder = builder->BitMapBuilderAt(i);
intptr_t bitmap_length = (bitmap_builder->Length() + 7) / 8;
TypedDataAllocation<uint8_t> bitmap(Dart_TypedData_kUint8, bitmap_length);
bitmap_builder->CopyInto(bitmap.data(), bitmap_length);
Dart_ListSetAt(bitmaps, i, bitmap.handle());
}
Dart_ListSetAt(result, 3, bitmaps);
return result;
}
static Dart_Handle BuildComments(Assembler* assembler) {
const GrowableArray<Assembler::CodeComment*>& comments =
assembler->GetCodeComments();
Dart_Handle result = Dart_NewList(comments.length() * 2);
for (intptr_t i = 0; i < comments.length(); i++) {
Dart_ListSetAt(result, 2 * i, Dart_NewInteger(comments[i]->pc_offset()));
Dart_ListSetAt(result, 2 * i + 1,
Dart_NewStringFromCString(comments[i]->comment()));
}
return result;
}
static FlowGraph* DeserializeIr(Zone* Z,
Dart_Handle call_target,
Dart_Handle vil,
Dart_Handle inline_builder) {
const Function& function = Function::CreateOutermost(call_target);
ParsedFunction* parsed_function =
new (Z) ParsedFunction(Thread::Current(), function, vil, inline_builder);
return parsed_function->BuildGraph();
}
struct FilterItem {
enum When {
kBefore = 1 << 0,
kAfter = 1 << 1,
kDisable = 1 << 2,
kBeforeOrAfter = kBefore | kAfter,
};
intptr_t when;
const char* name;
};
// Parse a sequence of phase filters with the following grammar:
//
// Filters ::= Filter (',' Filter)*
// Filter ::= Modifier* PhaseName
// Modifier ::= '-' # Disable the phase
// | '[' # Dump graph before the phase
// | ']' # Dump graph after the phase
//
// If no modifiers are supplied then graph is dumped only after
// the phase.
//
// Example:
//
// [A,]B,-C Dump graph before phase A, after B and disable phase C
// []A Dump graph before and after phase A
// C Dump graph after phase C
//
// Returns an array of filters terminated with an empty filter
// {0, NULL}.
static FilterItem* ParseFilters(const char* filter) {
if (filter == NULL || *filter == 0) {
return NULL;
}
Zone* zone = Thread::Current()->zone();
intptr_t count = 0;
for (const char* p = filter; *p != 0; p++) {
if (*p == ',') count++;
}
count++;
FilterItem* items = zone->Alloc<FilterItem>(count + 1);
memset(items, 0, sizeof(FilterItem) * (count + 1));
intptr_t idx = 0;
for (const char *p = filter, *q = filter; *q != 0; p = (q + 1)) {
q = p;
while (*q != ',' && *q != 0) {
q++;
}
uint8_t when = 0;
for (;; p++) {
if (*p == ']') {
when |= FilterItem::kAfter;
} else if (*p == '[') {
when |= FilterItem::kBefore;
} else if (*p == '-') {
when |= FilterItem::kDisable;
} else {
break;
}
}
if (when == 0) {
when |= FilterItem::kAfter;
}
if (p != q) {
items[idx].when = when;
items[idx].name = zone->MakeCopyOfStringN(p, q - p);
idx++;
}
}
return items;
}
class Phase : public ZoneAllocated {
public:
virtual const char* name() const = 0;
virtual void Run(FlowGraph* flow_graph) = 0;
bool MatchesAny(FilterItem* items, FilterItem::When when) const {
if (items == NULL) {
return false;
}
for (intptr_t i = 0; items[i].name != NULL; i++) {
if ((items[i].when & when) != 0 && strcmp(name(), items[i].name) == 0) {
return true;
}
}
return false;
}
};
// TODO(vipunen) pass arguments from the Dart side.
static const char* phases_print_filter = "]AllocateRegisters";
static void RunPhases(FlowGraph* graph, ...) {
Zone* zone = graph->zone();
FilterItem* filter = ParseFilters(phases_print_filter);
va_list va;
va_start(va, graph);
bool first_phase = true;
for (Phase* phase = va_arg(va, Phase*); phase != NULL;
phase = va_arg(va, Phase*)) {
if (!phase->MatchesAny(filter, FilterItem::kDisable)) {
if (!first_phase) {
graph->Canonicalize();
}
if (phase->MatchesAny(filter, FilterItem::kBefore)) {
FlowGraphPrinter::PrintGraph(
zone->PrintToString("Before %s", phase->name()), graph);
}
phase->Run(graph);
if (phase->MatchesAny(filter, FilterItem::kAfter)) {
FlowGraphPrinter::PrintGraph(
zone->PrintToString("After %s", phase->name()), graph);
}
first_phase = false;
} else {
if (phase->MatchesAny(filter, FilterItem::kBeforeOrAfter)) {
printf("Skipping phase %s\n", phase->name());
}
}
}
va_end(va);
}
#define DEFINE_PHASE(Name, action) \
class Phase##Name : public Phase { \
public: \
virtual const char* name() const { return #Name; } \
virtual void Run(FlowGraph* flow_graph) { action; } \
};
DEFINE_PHASE(SSA, flow_graph->ComputeSSA(0, NULL));
DEFINE_PHASE(Inlining, {
FlowGraphInliner inliner(flow_graph, NULL, NULL, NULL, false,
/*inlining_black_list=*/NULL,
/*precompiler=*/NULL);
inliner.Inline();
});
DEFINE_PHASE(CSE, {
flow_graph->ComputeBlockEffects();
for (intptr_t i = 0; i < 2; i++) {
// We repeat CSE after CSE because next CSE can find
// some of the dependent redundancies.
if (!DominatorBasedCSE::Optimize(flow_graph)) {
break;
}
flow_graph->Canonicalize();
}
});
DEFINE_PHASE(BranchSimplifier, BranchSimplifier::Simplify(flow_graph));
DEFINE_PHASE(CP, ConstantPropagator::Optimize(flow_graph));
DEFINE_PHASE(SelectRepresentations, flow_graph->SelectRepresentations());
DEFINE_PHASE(EliminateEnvironments, flow_graph->EliminateEnvironments());
DEFINE_PHASE(AllocateRegisters, {
FlowGraphAllocator allocator(*flow_graph);
allocator.AllocateRegisters();
});
#define PHASE(Name) new Phase##Name(),
#define END_OF_PHASES NULL
#define PHASES \
PHASE(SSA) \
PHASE(CP) \
PHASE(CSE) \
PHASE(Inlining) \
PHASE(CP) \
PHASE(BranchSimplifier) \
PHASE(SelectRepresentations) \
PHASE(EliminateEnvironments) \
PHASE(AllocateRegisters) \
END_OF_PHASES
static Dart_Handle CompileGraph(FlowGraph* flow_graph) {
RunPhases(flow_graph, PHASES);
const bool use_far_branches = false;
const bool is_optimized = true;
Assembler assembler(use_far_branches);
FlowGraphCompiler graph_compiler(&assembler, flow_graph,
flow_graph->parsed_function(), is_optimized);
graph_compiler.CompileGraph();
Dart_Handle result = Dart_NewList(4);
Dart_ListSetAt(result, 0, BuildInstructions(&assembler));
Dart_ListSetAt(result, 1, BuildExternalFixups(&assembler));
Dart_ListSetAt(result, 2, BuildStackMaps(&graph_compiler));
Dart_ListSetAt(result, 3, BuildComments(&assembler));
return result;
}
/// The following stubs constist of machine instructions to directly invoke a
/// C function. It is assumed that all arguments to the C functions are in
/// C calling-convention registers and no arguments are passed on the stack.
/// The stubs will not modify the arguments align the frame and call out to C.
///
/// It is assumed that these C functions perform no memory allocation, never
/// call back into Dart and do little work in general.
///
/// The runtime calls are not considered a safepoint.
static void GenStubPrint(Assembler* assembler) {
AssemblerHelper h(assembler);
h.GenRuntimeCallUnsafe("print");
}
#define __ assembler->
template <Representation rep>
void GenInitStaticFieldHelper(Assembler* assembler) {
Address arg_statics(RBP, 4 * kWordSize);
Address arg_init_offset(RBP, 3 * kWordSize);
Address arg_field_offset(RBP, 2 * kWordSize);
Label cycle;
__ EnterFrame(0);
__ movq(RDI, arg_statics);
__ movq(RSI, arg_init_offset);
__ movq(RDX, Address(RDI, RSI, TIMES_1, -kHeapObjectTag)); // initializer
__ cmpq(RDX, Immediate(1)); // Cycle?
__ j(EQUAL, &cycle);
__ movq(Address(RDI, RSI, TIMES_1, -kHeapObjectTag), Immediate(1));
__ call(RDX);
// TODO(vipunen) We need a safepoint here.
__ movq(RDI, arg_statics);
__ movq(RSI, arg_init_offset);
__ movq(RDX, arg_field_offset);
__ movq(Address(RDI, RSI, TIMES_1, -kHeapObjectTag), Immediate(0));
if (rep == kUnboxedDouble) {
__ movsd(Address(RDI, RDX, TIMES_1, -kHeapObjectTag), XMM0);
} else if (rep == kTagged) {
__ movq(RCX, RAX); // StoreIntoObject will destroy value register.
__ StoreIntoObject(RDI, Address(RDI, RDX, TIMES_1, -kHeapObjectTag), RCX);
} else if (rep == kUntagged) {
__ movq(Address(RDI, RDX, TIMES_1, -kHeapObjectTag), RAX);
} else {
UNREACHABLE();
}
__ LeaveFrame();
__ ret();
__ Bind(&cycle);
__ int3();
}
#undef __
} // namespace vipunen_dart
template <typename T>
struct TableEntry {
const char* name;
T value;
};
template <typename T>
static T LookupTable(TableEntry<T>* table, const char* name) {
for (int i = 0; table[i].name != NULL; ++i) {
if (strcmp(table[i].name, name) == 0) {
return table[i].value;
}
}
return T();
}
Dart_NativeFunction ResolveName(Dart_Handle name,
int argc,
bool* auto_setup_scope);
DART_EXPORT Dart_Handle vipunen_Init(Dart_Handle parent_library) {
if (Dart_IsError(parent_library)) {
return parent_library;
}
Dart_Handle result_code =
Dart_SetNativeResolver(parent_library, ResolveName, NULL);
if (Dart_IsError(result_code)) {
return result_code;
}
return Dart_Null();
}
static void ExpectUint8List(Dart_Handle arg) {
if (!Dart_IsTypedData(arg)) {
Dart_ThrowException(Dart_NewStringFromCString("expected typed data"));
}
if (Dart_GetTypeOfTypedData(arg) != Dart_TypedData_kUint8) {
Dart_ThrowException(Dart_NewStringFromCString("expected uint8 typed data"));
}
}
void ToNativeCode(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_Handle self_target = Dart_GetNativeArgument(arguments, 0);
Dart_Handle vil = Dart_GetNativeArgument(arguments, 1);
Dart_Handle inline_builder = Dart_GetNativeArgument(arguments, 2);
ExpectUint8List(self_target);
ExpectUint8List(vil);
if (vipunen_dart::Thread::Current() == NULL)
vipunen_dart::Thread::EnterThread();
{
vipunen_dart::StackZone zone(vipunen_dart::Thread::Current());
vipunen_dart::Zone* Z = vipunen_dart::Thread::Current()->zone();
vipunen_dart::FlowGraph* graph =
vipunen_dart::DeserializeIr(Z, self_target, vil, inline_builder);
Dart_SetReturnValue(arguments, CompileGraph(graph));
}
Dart_ExitScope();
}
typedef void (*StubGenerator)(vipunen_dart::Assembler*);
TableEntry<StubGenerator> stub_list[] = {
{"internal_InitStaticFieldHelperTagged",
vipunen_dart::GenInitStaticFieldHelper<vipunen_dart::kTagged>},
{"internal_InitStaticFieldHelperUntagged",
vipunen_dart::GenInitStaticFieldHelper<vipunen_dart::kUntagged>},
{"internal_InitStaticFieldHelperDouble",
vipunen_dart::GenInitStaticFieldHelper<vipunen_dart::kUnboxedDouble>},
{"internal_Print", vipunen_dart::GenStubPrint},
{NULL, NULL},
};
void GenerateStub(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_Handle arg0 = Dart_GetNativeArgument(arguments, 0);
if (!Dart_IsString(arg0)) {
Dart_ThrowException(
Dart_NewStringFromCString("expected stub name, a string"));
}
const char* cname;
vipunen_dart::HandleError(Dart_StringToCString(arg0, &cname));
if (vipunen_dart::Thread::Current() == NULL)
vipunen_dart::Thread::EnterThread();
{
vipunen_dart::StackZone zone(vipunen_dart::Thread::Current());
StubGenerator generator = LookupTable(stub_list, cname);
if (generator == NULL) {
Dart_ThrowException(Dart_NewStringFromCString("Did not found stub."));
}
const bool use_far_branches = false;
vipunen_dart::Assembler assembler(use_far_branches);
generator(&assembler);
Dart_Handle result = Dart_NewList(4);
Dart_ListSetAt(result, 0, vipunen_dart::BuildInstructions(&assembler));
Dart_ListSetAt(result, 1, vipunen_dart::BuildExternalFixups(&assembler));
Dart_ListSetAt(result, 2, vipunen_dart::BuildStackMaps(NULL));
Dart_ListSetAt(result, 4, vipunen_dart::BuildComments(&assembler));
Dart_SetReturnValue(arguments, result);
}
Dart_ExitScope();
}
void InitializeConstants(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_Handle constants =
vipunen_dart::HandleError(Dart_GetNativeArgument(arguments, 0));
intptr_t i = 0;
int64_t value;
Dart_Handle value_handle;
#define INITIALIZE(type, name) \
value_handle = vipunen_dart::HandleError(Dart_ListGetAt(constants, i++)); \
vipunen_dart::HandleError(Dart_IntegerToInt64(value_handle, &value)); \
vipunen_dart::Constants::name = static_cast<type>(value);
CONSTANT_LIST(INITIALIZE)
#undef INITIALIZE
Dart_ExitScope();
}
void Vipunen_withNativeConstantPool(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_Handle closure = Dart_GetNativeArgument(arguments, 0);
if (vipunen_dart::Thread::Current() == NULL)
vipunen_dart::Thread::EnterThread();
{
vipunen_dart::StackZone zone(vipunen_dart::Thread::Current());
vipunen_dart::Zone* Z = vipunen_dart::Thread::Current()->zone();
vipunen_dart::Thread::Current()->set_vil_constants(
new (Z) vipunen_dart::VilConstants(Z));
Dart_Handle args[0];
Dart_Handle result = Dart_InvokeClosure(closure, 0, args);
vipunen_dart::Thread::Current()->set_vil_constants(NULL);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
}
Dart_ExitScope();
}
TableEntry<Dart_NativeFunction> function_list[] = {
{"Vipunen_toNativeCode", ToNativeCode},
{"Vipunen_generateStub", GenerateStub},
{"Vipunen_initializeConstants", InitializeConstants},
{"Vipunen_withNativeConstantPool", Vipunen_withNativeConstantPool},
{NULL, NULL}};
TableEntry<Dart_NativeFunction> no_scope_function_list[] = {{NULL, NULL}};
Dart_NativeFunction ResolveName(Dart_Handle name,
int argc,
bool* auto_setup_scope) {
if (!Dart_IsString(name)) {
return NULL;
}
if (auto_setup_scope == NULL) {
return NULL;
}
Dart_EnterScope();
const char* cname;
vipunen_dart::HandleError(Dart_StringToCString(name, &cname));
Dart_NativeFunction result;
result = LookupTable<Dart_NativeFunction>(function_list, cname);
if (result != NULL) {
*auto_setup_scope = true;
} else {
result = LookupTable<Dart_NativeFunction>(no_scope_function_list, cname);
if (result != NULL) {
*auto_setup_scope = false;
}
}
Dart_ExitScope();
return result;
}