| Index: src/compiler/ffi-compiler.cc
|
| diff --git a/src/compiler/ffi-compiler.cc b/src/compiler/ffi-compiler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a8857ea4665a3f2858d3476016bcbfc73bb54f89
|
| --- /dev/null
|
| +++ b/src/compiler/ffi-compiler.cc
|
| @@ -0,0 +1,1237 @@
|
| +// Copyright 2016 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "src/compiler/ffi-compiler.h"
|
| +
|
| +#include "include/v8-ffi.h"
|
| +#include "include/v8.h"
|
| +
|
| +#include "src/isolate-inl.h"
|
| +
|
| +#include "src/api.h"
|
| +#include "src/code-factory.h"
|
| +#include "src/compilation-info.h"
|
| +#include "src/compiler/access-builder.h"
|
| +#include "src/compiler/common-operator.h"
|
| +#include "src/compiler/graph-visualizer.h"
|
| +#include "src/compiler/graph.h"
|
| +#include "src/compiler/js-graph.h"
|
| +#include "src/compiler/js-operator.h"
|
| +#include "src/compiler/linkage.h"
|
| +#include "src/compiler/pipeline.h"
|
| +#include "src/factory.h"
|
| +#include "src/profiler/cpu-profiler.h"
|
| +
|
| +namespace v8 {
|
| +
|
| +namespace ffi {
|
| +
|
| +Local<Function> CompileJSToNativeWrapper(Isolate* isolate, Local<String> name,
|
| + NativeFunction func) {
|
| + EscapableHandleScope handle_scope(isolate);
|
| + i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
| + i::Handle<i::String> internal_name = Utils::OpenHandle(*name);
|
| + internal::ffi::FFISignature internal_sig(
|
| + func.sig->return_count, func.sig->param_count, func.sig->representations);
|
| + internal::ffi::NativeFunction internal_func = {&internal_sig, func.start};
|
| + i::Handle<i::JSFunction> internal_function =
|
| + internal::compiler::CompileJSToNativeWrapper(
|
| + internal_isolate, internal_name, internal_func);
|
| + Local<Function> result;
|
| + if (!ToLocal<Function>(internal_function, &result)) {
|
| + return Local<Function>();
|
| + }
|
| + return handle_scope.Escape(result);
|
| +}
|
| +
|
| +void* FFIFunctionBind(Isolate* isolate, NativeFunction func, Object* args) {
|
| + HandleScope handle_scope(isolate);
|
| + i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
| + internal::ffi::FFISignature internal_sig(
|
| + func.sig->return_count, func.sig->param_count, func.sig->representations);
|
| + internal::ffi::NativeFunction internal_func = {&internal_sig, func.start};
|
| + return internal::compiler::FFIFunctionBind(
|
| + internal_isolate, internal_func,
|
| + i::Handle<i::JSReceiver>::New(reinterpret_cast<i::JSReceiver*>(args),
|
| + internal_isolate));
|
| +}
|
| +
|
| +void* BuildFFIArgumentSerializer(Isolate* isolate, NativeFunction func) {
|
| + HandleScope handle_scope(isolate);
|
| + i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
| + internal::ffi::FFISignature internal_sig(
|
| + func.sig->return_count, func.sig->param_count, func.sig->representations);
|
| + internal::ffi::NativeFunction internal_func = {&internal_sig, func.start};
|
| + return internal::compiler::BuildFFISerializer(internal_isolate,
|
| + internal_func);
|
| +}
|
| +
|
| +void* BuildFFIArgumentDeserializer(Isolate* isolate, NativeFunction func) {
|
| + HandleScope handle_scope(isolate);
|
| + i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
| + internal::ffi::FFISignature internal_sig(
|
| + func.sig->return_count, func.sig->param_count, func.sig->representations);
|
| + internal::ffi::NativeFunction internal_func = {&internal_sig, func.start};
|
| + return internal::compiler::BuildFFIDeserializedExecutor(internal_isolate,
|
| + internal_func);
|
| +}
|
| +
|
| +} // namespace ffi
|
| +
|
| +namespace internal {
|
| +namespace compiler {
|
| +
|
| +int TypeSize(ffi::FFIType type) {
|
| + switch (type) {
|
| + case ffi::FFIType::kInt32:
|
| + case ffi::FFIType::kFloat32:
|
| + return 4;
|
| + case ffi::FFIType::kInt64:
|
| + case ffi::FFIType::kFloat64:
|
| + case ffi::FFIType::kCharPtr:
|
| + case ffi::FFIType::kTypedArray:
|
| + case ffi::FFIType::kBufferNoCopy:
|
| + case ffi::FFIType::kFunction:
|
| + case ffi::FFIType::kStructPtr:
|
| + case ffi::FFIType::kForeign:
|
| + return 8;
|
| + case ffi::FFIType::kRawJS:
|
| + UNREACHABLE();
|
| + return sizeof(Handle<JSObject>);
|
| + }
|
| +}
|
| +
|
| +void WriteObject(Handle<Object> value, ffi::FFIType type, char* dest) {
|
| + int size = TypeSize(type);
|
| + // TODO(mattloring): finish supporting all types
|
| + // TODO(mattloring): deduplicate translation with those in runtime-ffi.cc
|
| + switch (type) {
|
| + case ffi::FFIType::kInt32: {
|
| + int int32_value = static_cast<int>(value->Number());
|
| + memcpy(dest, &int32_value, size);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kFloat32: {
|
| + float float_value = static_cast<float>(value->Number());
|
| + memcpy(dest, &float_value, size);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kInt64: {
|
| + int64_t int64_value = static_cast<int64_t>(value->Number());
|
| + memcpy(dest, &int64_value, size);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kFloat64: {
|
| + double double_value = value->Number();
|
| + memcpy(dest, &double_value, size);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kCharPtr:
|
| + break;
|
| + case ffi::FFIType::kTypedArray:
|
| + break;
|
| + case ffi::FFIType::kBufferNoCopy: {
|
| + Handle<JSTypedArray> array(JSTypedArray::cast(*value));
|
| + uint8_t* contents =
|
| + reinterpret_cast<uint8_t*>(array->GetBuffer()->backing_store());
|
| + size_t offset = NumberToSize(array->byte_offset());
|
| + uint8_t* base = contents + offset;
|
| + memcpy(dest, &base, size);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kFunction:
|
| + break;
|
| + case ffi::FFIType::kStructPtr:
|
| + break;
|
| + case ffi::FFIType::kForeign:
|
| + break;
|
| + case ffi::FFIType::kRawJS:
|
| + break;
|
| + }
|
| +}
|
| +
|
| +MachineType FFITypeToMachineType(ffi::FFITypeElement elem) {
|
| + switch (elem.type) {
|
| + case ffi::FFIType::kInt32:
|
| + return MachineType::Int32();
|
| + case ffi::FFIType::kInt64:
|
| + return MachineType::Int64();
|
| + case ffi::FFIType::kFloat32:
|
| + return MachineType::Float32();
|
| + case ffi::FFIType::kFloat64:
|
| + return MachineType::Float64();
|
| + case ffi::FFIType::kCharPtr:
|
| + case ffi::FFIType::kTypedArray:
|
| + case ffi::FFIType::kBufferNoCopy:
|
| + case ffi::FFIType::kFunction:
|
| + case ffi::FFIType::kStructPtr:
|
| + case ffi::FFIType::kForeign:
|
| + return MachineType::Pointer();
|
| + case ffi::FFIType::kRawJS:
|
| + return MachineType::TaggedPointer();
|
| + }
|
| +}
|
| +
|
| +MachineType* TranslateSignature(ffi::FFISignature* sig) {
|
| + const size_t params = sig->parameter_count();
|
| + const size_t returns = sig->return_count();
|
| + MachineType* reps = new MachineType[params + returns];
|
| + for (size_t i = 0; i < returns; i++) {
|
| + reps[i] = FFITypeToMachineType(sig->GetReturn(i));
|
| + }
|
| + for (size_t i = 0; i < params; i++) {
|
| + reps[returns + i] = FFITypeToMachineType(sig->GetParam(i));
|
| + }
|
| + return reps;
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +// TODO(ofrobots): duplicated from wasm-compiler.cc
|
| +static void MergeControlToEnd(JSGraph* jsgraph, Node* node) {
|
| + Graph* g = jsgraph->graph();
|
| + if (g->end()) {
|
| + NodeProperties::MergeControlToEnd(g, jsgraph->common(), node);
|
| + } else {
|
| + g->SetEnd(g->NewNode(jsgraph->common()->End(1), node));
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +static Node* BuildCallToRuntime(Runtime::FunctionId f, JSGraph* jsgraph,
|
| + Node* context, Node** parameters,
|
| + int parameter_count, Node** effect_ptr,
|
| + Node** control_ptr) {
|
| + // At the moment we only allow 2 parameters. If more parameters are needed,
|
| + // then the size of {inputs} below has to be increased accordingly.
|
| + DCHECK(parameter_count <= 2);
|
| + const Runtime::Function* fun = Runtime::FunctionForId(f);
|
| + // fun->nargs may be negative for Runtime::kCall (variable arg count)
|
| + CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
|
| + jsgraph->zone(), f, fun->nargs < 0 ? parameter_count : fun->nargs,
|
| + Operator::kNoProperties, CallDescriptor::kNoFlags);
|
| + DCHECK_EQ(1, fun->result_size);
|
| + Node* inputs[8];
|
| + int count = 0;
|
| + inputs[count++] = jsgraph->CEntryStubConstant(fun->result_size);
|
| + for (int i = 0; i < parameter_count; i++) {
|
| + inputs[count++] = parameters[i];
|
| + }
|
| + inputs[count++] = jsgraph->ExternalConstant(
|
| + ExternalReference(f, jsgraph->isolate())); // ref
|
| + inputs[count++] = jsgraph->Int32Constant(fun->nargs); // arity
|
| + inputs[count++] = context; // context
|
| + inputs[count++] = *effect_ptr;
|
| + inputs[count++] = *control_ptr;
|
| +
|
| + Node* node =
|
| + jsgraph->graph()->NewNode(jsgraph->common()->Call(desc), count, inputs);
|
| + *effect_ptr = node;
|
| + *control_ptr =
|
| + jsgraph->graph()->NewNode(jsgraph->common()->IfSuccess(), node);
|
| + return node;
|
| +}
|
| +
|
| +FFIGraphBuilder::FFIGraphBuilder(Zone* zone, JSGraph* jsgraph)
|
| + : zone_(zone),
|
| + jsgraph_(jsgraph),
|
| + control_(nullptr),
|
| + effect_(nullptr),
|
| + cur_buffer_(def_buffer_),
|
| + cur_bufsize_(kDefaultBufferSize) {
|
| + DCHECK_NOT_NULL(jsgraph_);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::Start(unsigned params) {
|
| + Node* start = graph()->NewNode(jsgraph()->common()->Start(params));
|
| + graph()->SetStart(start);
|
| + return start;
|
| +}
|
| +
|
| +MachineType* FFIGraphBuilder::GetMachineTypes(ffi::FFISignature* sig) {
|
| + return TranslateSignature(sig);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildCCall(ffi::FFISignature* sig, Node** args) {
|
| + const size_t params = sig->parameter_count();
|
| + const size_t returns = sig->return_count();
|
| + const size_t extra = 2; // effect and control inputs.
|
| + const size_t count = 1 + params + extra;
|
| +
|
| + // Reallocate the buffer to make space for extra inputs.
|
| + args = Realloc(args, 1 + params, count);
|
| +
|
| + // Add effect and control inputs.
|
| + args[params + 1] = *effect_;
|
| + args[params + 2] = *control_;
|
| +
|
| + MachineType* reps = TranslateSignature(sig);
|
| + MachineSignature mach_sig(returns, params, reps);
|
| +
|
| + CallDescriptor* desc =
|
| + Linkage::GetSimplifiedCDescriptor(jsgraph()->zone(), &mach_sig);
|
| +
|
| + const Operator* op = jsgraph()->common()->Call(desc);
|
| + Node* call = graph()->NewNode(op, static_cast<int>(count), args);
|
| + *effect_ = call;
|
| + *control_ = graph()->NewNode(jsgraph()->common()->IfSuccess(), call);
|
| + delete[] reps;
|
| + return call;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildChangeInt32ToTagged(Node* value) {
|
| + MachineOperatorBuilder* machine = jsgraph()->machine();
|
| + CommonOperatorBuilder* common = jsgraph()->common();
|
| +
|
| + if (machine->Is64()) {
|
| + return BuildChangeInt32ToSmi(value);
|
| + }
|
| +
|
| + Node* add = graph()->NewNode(machine->Int32AddWithOverflow(), value, value);
|
| +
|
| + Node* ovf = graph()->NewNode(common->Projection(1), add);
|
| + Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), ovf,
|
| + graph()->start());
|
| +
|
| + Node* if_true = graph()->NewNode(common->IfTrue(), branch);
|
| + Node* vtrue = BuildAllocateHeapNumberWithValue(
|
| + graph()->NewNode(machine->ChangeInt32ToFloat64(), value), if_true);
|
| +
|
| + Node* if_false = graph()->NewNode(common->IfFalse(), branch);
|
| + Node* vfalse = graph()->NewNode(common->Projection(0), add);
|
| +
|
| + Node* merge = graph()->NewNode(common->Merge(2), if_true, if_false);
|
| + Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2),
|
| + vtrue, vfalse, merge);
|
| + return phi;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildChangeFloat64ToTagged(Node* value) {
|
| + MachineOperatorBuilder* machine = jsgraph()->machine();
|
| + CommonOperatorBuilder* common = jsgraph()->common();
|
| +
|
| + Node* value32 = graph()->NewNode(machine->RoundFloat64ToInt32(), value);
|
| + Node* check_same = graph()->NewNode(
|
| + machine->Float64Equal(), value,
|
| + graph()->NewNode(machine->ChangeInt32ToFloat64(), value32));
|
| + Node* branch_same =
|
| + graph()->NewNode(common->Branch(), check_same, graph()->start());
|
| +
|
| + Node* if_smi = graph()->NewNode(common->IfTrue(), branch_same);
|
| + Node* vsmi;
|
| + Node* if_box = graph()->NewNode(common->IfFalse(), branch_same);
|
| + Node* vbox;
|
| +
|
| + // We only need to check for -0 if the {value} can potentially contain -0.
|
| + Node* check_zero = graph()->NewNode(machine->Word32Equal(), value32,
|
| + jsgraph()->Int32Constant(0));
|
| + Node* branch_zero =
|
| + graph()->NewNode(common->Branch(BranchHint::kFalse), check_zero, if_smi);
|
| +
|
| + Node* if_zero = graph()->NewNode(common->IfTrue(), branch_zero);
|
| + Node* if_notzero = graph()->NewNode(common->IfFalse(), branch_zero);
|
| +
|
| + // In case of 0, we need to check the high bits for the IEEE -0 pattern.
|
| + Node* check_negative = graph()->NewNode(
|
| + machine->Int32LessThan(),
|
| + graph()->NewNode(machine->Float64ExtractHighWord32(), value),
|
| + jsgraph()->Int32Constant(0));
|
| + Node* branch_negative = graph()->NewNode(common->Branch(BranchHint::kFalse),
|
| + check_negative, if_zero);
|
| +
|
| + Node* if_negative = graph()->NewNode(common->IfTrue(), branch_negative);
|
| + Node* if_notnegative = graph()->NewNode(common->IfFalse(), branch_negative);
|
| +
|
| + // We need to create a box for negative 0.
|
| + if_smi = graph()->NewNode(common->Merge(2), if_notzero, if_notnegative);
|
| + if_box = graph()->NewNode(common->Merge(2), if_box, if_negative);
|
| +
|
| + // On 64-bit machines we can just wrap the 32-bit integer in a smi, for 32-bit
|
| + // machines we need to deal with potential overflow and fallback to boxing.
|
| + if (machine->Is64()) {
|
| + vsmi = BuildChangeInt32ToSmi(value32);
|
| + } else {
|
| + Node* smi_tag =
|
| + graph()->NewNode(machine->Int32AddWithOverflow(), value32, value32);
|
| +
|
| + Node* check_ovf = graph()->NewNode(common->Projection(1), smi_tag);
|
| + Node* branch_ovf =
|
| + graph()->NewNode(common->Branch(BranchHint::kFalse), check_ovf, if_smi);
|
| +
|
| + Node* if_ovf = graph()->NewNode(common->IfTrue(), branch_ovf);
|
| + if_box = graph()->NewNode(common->Merge(2), if_ovf, if_box);
|
| +
|
| + if_smi = graph()->NewNode(common->IfFalse(), branch_ovf);
|
| + vsmi = graph()->NewNode(common->Projection(0), smi_tag);
|
| + }
|
| +
|
| + // Allocate the box for the {value}.
|
| + vbox = BuildAllocateHeapNumberWithValue(value, if_box);
|
| +
|
| + Node* control = graph()->NewNode(common->Merge(2), if_smi, if_box);
|
| + value = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2), vsmi,
|
| + vbox, control);
|
| + return value;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::ToJS(Node* node, Node* context,
|
| + ffi::FFITypeElement elem) {
|
| + switch (elem.type) {
|
| + case ffi::FFIType::kInt32:
|
| + return BuildChangeInt32ToTagged(node);
|
| + case ffi::FFIType::kInt64:
|
| + // TODO(titzer): i64->JS has no good solution right now. Using lower 32
|
| + // bits.
|
| + if (jsgraph()->machine()->Is64()) {
|
| + // On 32 bit platforms we do not have to do the truncation because the
|
| + // node we get in as a parameter only contains the low word anyways.
|
| + node = graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(),
|
| + node);
|
| + }
|
| + return BuildChangeInt32ToTagged(node);
|
| + case ffi::FFIType::kFloat32:
|
| + node = graph()->NewNode(jsgraph()->machine()->ChangeFloat32ToFloat64(),
|
| + node);
|
| + return BuildChangeFloat64ToTagged(node);
|
| + case ffi::FFIType::kFloat64:
|
| + return BuildChangeFloat64ToTagged(node);
|
| + case ffi::FFIType::kCharPtr:
|
| + return CharPtrToJSString(node, context, *effect_, *control_);
|
| + case ffi::FFIType::kTypedArray: {
|
| + UNREACHABLE();
|
| + return nullptr;
|
| + }
|
| + case ffi::FFIType::kBufferNoCopy: {
|
| + UNREACHABLE();
|
| + return nullptr;
|
| + }
|
| + case ffi::FFIType::kFunction: {
|
| + UNREACHABLE();
|
| + return nullptr;
|
| + }
|
| + case ffi::FFIType::kStructPtr: {
|
| + UNREACHABLE();
|
| + return nullptr;
|
| + }
|
| + case ffi::FFIType::kForeign: {
|
| + return PointerToForeign(node, context, *effect_, *control_);
|
| + }
|
| + case ffi::FFIType::kRawJS: {
|
| + return node;
|
| + }
|
| + }
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildJavaScriptToNumber(Node* node, Node* context,
|
| + Node* effect, Node* control) {
|
| + Callable callable = CodeFactory::ToNumber(jsgraph()->isolate());
|
| + CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
| + jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0,
|
| + CallDescriptor::kNoFlags, Operator::kNoProperties);
|
| + Node* stub_code = jsgraph()->HeapConstant(callable.code());
|
| +
|
| + Node* result = graph()->NewNode(jsgraph()->common()->Call(desc), stub_code,
|
| + node, context, effect, control);
|
| +
|
| + *control_ = graph()->NewNode(jsgraph()->common()->IfSuccess(), result);
|
| + *effect_ = result;
|
| +
|
| + return result;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::JSStringToCharPtr(Node* node, Node* context,
|
| + Node* effect, Node* control) {
|
| + Node* parameters[] = {node};
|
| + return BuildCallToRuntime(Runtime::kJSStringToCharPtr, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::TypedArrayToUint8Ptr(Node* node, Node* context,
|
| + Node* effect, Node* control) {
|
| + Node* parameters[] = {node};
|
| + return BuildCallToRuntime(Runtime::kTypedArrayToUint8Ptr, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BufferToPtrNoCopy(Node* node, Node* context,
|
| + Node* effect, Node* control) {
|
| + Node* parameters[] = {node};
|
| + return BuildCallToRuntime(Runtime::kBufferToPtrNoCopy, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +v8::ffi::FFISignature* CopySignature(ffi::FFISignature* sig) {
|
| + size_t param_count = sig->parameter_count();
|
| + size_t return_count = sig->return_count();
|
| + ffi::FFITypeElement* reps =
|
| + new ffi::FFITypeElement[param_count + return_count];
|
| + for (size_t i = 0; i < return_count; i++) {
|
| + ffi::FFITypeElement elem = sig->GetReturn(i);
|
| + // TODO(mattloring): handle structs
|
| + if (elem.type == ffi::FFIType::kFunction) {
|
| + internal::ffi::FFISignature internal_sig(
|
| + elem.info->function_signature->return_count,
|
| + elem.info->function_signature->param_count,
|
| + elem.info->function_signature->representations);
|
| + v8::ffi::FFISupplementalInfo* info = new v8::ffi::FFISupplementalInfo{
|
| + .function_signature = CopySignature(&internal_sig)};
|
| + reps[i] = {elem.type, info};
|
| + } else {
|
| + reps[i] = elem;
|
| + }
|
| + }
|
| + for (size_t i = 0; i < param_count; i++) {
|
| + ffi::FFITypeElement elem = sig->GetParam(i);
|
| + // TODO(mattloring): handle structs
|
| + if (elem.type == ffi::FFIType::kFunction) {
|
| + internal::ffi::FFISignature internal_sig(
|
| + elem.info->function_signature->return_count,
|
| + elem.info->function_signature->param_count,
|
| + elem.info->function_signature->representations);
|
| + v8::ffi::FFISupplementalInfo* info = new v8::ffi::FFISupplementalInfo{
|
| + .function_signature = CopySignature(&internal_sig)};
|
| + reps[i] = {elem.type, info};
|
| + } else {
|
| + reps[i] = elem;
|
| + }
|
| + }
|
| + return new v8::ffi::FFISignature{return_count, param_count, reps};
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::JSFunctionToFnPtr(Node* node, Node* context,
|
| + ffi::FFISignature* sig, Node* effect,
|
| + Node* control) {
|
| + // TODO(mattloring): free ffi signature
|
| + v8::ffi::FFISignature* allocated_sig = CopySignature(sig);
|
| + ExternalReference ref(reinterpret_cast<Address>(allocated_sig),
|
| + jsgraph()->isolate());
|
| + Node* sig_reference =
|
| + graph()->NewNode(jsgraph()->common()->ExternalConstant(ref));
|
| + Node* parameters[] = {node, sig_reference};
|
| + return BuildCallToRuntime(Runtime::kJSFunctionToFnPtr, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::JSObjectToStructPtr(Node* node, Node* context,
|
| + ffi::FFIStructSignature* sig,
|
| + Node* effect, Node* control) {
|
| + v8::ffi::FFIStructElement* reps =
|
| + new v8::ffi::FFIStructElement[sig->elem_count];
|
| + // TODO(mattloring): if there are nested signatures they must be allocated as
|
| + // well
|
| + for (size_t i = 0; i < sig->elem_count; i++) {
|
| + reps[i] = sig->elems[i];
|
| + }
|
| + v8::ffi::FFIStructSignature* allocated_sig =
|
| + new v8::ffi::FFIStructSignature{sig->elem_count, reps};
|
| + ExternalReference ref(reinterpret_cast<Address>(allocated_sig),
|
| + jsgraph()->isolate());
|
| + Node* sig_reference =
|
| + graph()->NewNode(jsgraph()->common()->ExternalConstant(ref));
|
| + Node* parameters[] = {node, sig_reference};
|
| + return BuildCallToRuntime(Runtime::kJSObjectToStructPtr, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::ForeignToPointer(Node* node, Node* context, Node* effect,
|
| + Node* control) {
|
| + Node* parameters[] = {node};
|
| + return BuildCallToRuntime(Runtime::kForeignToPointer, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::CharPtrToJSString(Node* node, Node* context,
|
| + Node* effect, Node* control) {
|
| + Node* parameters[] = {node};
|
| + return BuildCallToRuntime(Runtime::kCharPtrToJSString, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::PointerToForeign(Node* node, Node* context, Node* effect,
|
| + Node* control) {
|
| + Node* parameters[] = {node};
|
| + return BuildCallToRuntime(Runtime::kPointerToForeign, jsgraph(), context,
|
| + parameters, arraysize(parameters), &effect,
|
| + &control);
|
| +}
|
| +
|
| +static bool CanCover(Node* value, IrOpcode::Value opcode) {
|
| + if (value->opcode() != opcode) return false;
|
| + bool first = true;
|
| + for (Edge const edge : value->use_edges()) {
|
| + if (NodeProperties::IsControlEdge(edge)) continue;
|
| + if (NodeProperties::IsEffectEdge(edge)) continue;
|
| + DCHECK(NodeProperties::IsValueEdge(edge));
|
| + if (!first) return false;
|
| + first = false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildChangeTaggedToInt32(Node* value) {
|
| + CommonOperatorBuilder* common = jsgraph()->common();
|
| +
|
| + Node* check = BuildTestNotSmi(value);
|
| + Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check,
|
| + graph()->start());
|
| +
|
| + Node* if_smi = graph()->NewNode(common->IfFalse(), branch);
|
| + Node* vfrom_smi = BuildChangeSmiToInt32(value);
|
| +
|
| + // TODO(ofrobots): the not_smi path is too ugly and slow at the moment
|
| + Node* if_not_smi = graph()->NewNode(common->IfTrue(), branch);
|
| + Node* vfloat = BuildChangeTaggedToFloat64(value);
|
| + Node* vnot_smi =
|
| + graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToWord32(), vfloat);
|
| +
|
| + Node* merge = graph()->NewNode(common->Merge(2), if_not_smi, if_smi);
|
| + Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kWord32, 2),
|
| + vnot_smi, vfrom_smi, merge);
|
| + return phi;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildChangeTaggedToFloat64(Node* value) {
|
| + MachineOperatorBuilder* machine = jsgraph()->machine();
|
| + CommonOperatorBuilder* common = jsgraph()->common();
|
| +
|
| + if (CanCover(value, IrOpcode::kJSToNumber)) {
|
| + // ChangeTaggedToFloat64(JSToNumber(x)) =>
|
| + // if IsSmi(x) then ChangeSmiToFloat64(x)
|
| + // else let y = JSToNumber(x) in
|
| + // if IsSmi(y) then ChangeSmiToFloat64(y)
|
| + // else BuildLoadHeapNumberValue(y)
|
| + Node* object = NodeProperties::GetValueInput(value, 0);
|
| + Node* context = NodeProperties::GetContextInput(value);
|
| + Node* frame_state = NodeProperties::GetFrameStateInput(value);
|
| + Node* effect = NodeProperties::GetEffectInput(value);
|
| + Node* control = NodeProperties::GetControlInput(value);
|
| +
|
| + const Operator* merge_op = common->Merge(2);
|
| + const Operator* ephi_op = common->EffectPhi(2);
|
| + const Operator* phi_op = common->Phi(MachineRepresentation::kFloat64, 2);
|
| +
|
| + Node* check1 = BuildTestNotSmi(object);
|
| + Node* branch1 =
|
| + graph()->NewNode(common->Branch(BranchHint::kFalse), check1, control);
|
| +
|
| + Node* if_true1 = graph()->NewNode(common->IfTrue(), branch1);
|
| + Node* vtrue1 = graph()->NewNode(value->op(), object, context, frame_state,
|
| + effect, if_true1);
|
| + Node* etrue1 = vtrue1;
|
| +
|
| + Node* check2 = BuildTestNotSmi(vtrue1);
|
| + Node* branch2 = graph()->NewNode(common->Branch(), check2, if_true1);
|
| +
|
| + Node* if_true2 = graph()->NewNode(common->IfTrue(), branch2);
|
| + Node* vtrue2 = BuildLoadHeapNumberValue(vtrue1, if_true2);
|
| +
|
| + Node* if_false2 = graph()->NewNode(common->IfFalse(), branch2);
|
| + Node* vfalse2 = BuildChangeSmiToFloat64(vtrue1);
|
| +
|
| + if_true1 = graph()->NewNode(merge_op, if_true2, if_false2);
|
| + vtrue1 = graph()->NewNode(phi_op, vtrue2, vfalse2, if_true1);
|
| +
|
| + Node* if_false1 = graph()->NewNode(common->IfFalse(), branch1);
|
| + Node* vfalse1 = BuildChangeSmiToFloat64(object);
|
| + Node* efalse1 = effect;
|
| +
|
| + Node* merge1 = graph()->NewNode(merge_op, if_true1, if_false1);
|
| + Node* ephi1 = graph()->NewNode(ephi_op, etrue1, efalse1, merge1);
|
| + Node* phi1 = graph()->NewNode(phi_op, vtrue1, vfalse1, merge1);
|
| +
|
| + // Wire the new diamond into the graph, {JSToNumber} can still throw.
|
| + NodeProperties::ReplaceUses(value, phi1, ephi1, etrue1, etrue1);
|
| +
|
| + // TODO(mstarzinger): This iteration cuts out the IfSuccess projection from
|
| + // the node and places it inside the diamond. Come up with a helper method!
|
| + for (Node* use : etrue1->uses()) {
|
| + if (use->opcode() == IrOpcode::kIfSuccess) {
|
| + use->ReplaceUses(merge1);
|
| + NodeProperties::ReplaceControlInput(branch2, use);
|
| + }
|
| + }
|
| + return phi1;
|
| + }
|
| +
|
| + Node* check = BuildTestNotSmi(value);
|
| + Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check,
|
| + graph()->start());
|
| +
|
| + Node* if_not_smi = graph()->NewNode(common->IfTrue(), branch);
|
| +
|
| + Node* vnot_smi;
|
| + Node* check_undefined = graph()->NewNode(machine->WordEqual(), value,
|
| + jsgraph()->UndefinedConstant());
|
| + Node* branch_undefined = graph()->NewNode(common->Branch(BranchHint::kFalse),
|
| + check_undefined, if_not_smi);
|
| +
|
| + Node* if_undefined = graph()->NewNode(common->IfTrue(), branch_undefined);
|
| + Node* vundefined =
|
| + jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN());
|
| +
|
| + Node* if_not_undefined =
|
| + graph()->NewNode(common->IfFalse(), branch_undefined);
|
| + Node* vheap_number = BuildLoadHeapNumberValue(value, if_not_undefined);
|
| +
|
| + if_not_smi =
|
| + graph()->NewNode(common->Merge(2), if_undefined, if_not_undefined);
|
| + vnot_smi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2),
|
| + vundefined, vheap_number, if_not_smi);
|
| +
|
| + Node* if_smi = graph()->NewNode(common->IfFalse(), branch);
|
| + Node* vfrom_smi = BuildChangeSmiToFloat64(value);
|
| +
|
| + Node* merge = graph()->NewNode(common->Merge(2), if_not_smi, if_smi);
|
| + Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2),
|
| + vnot_smi, vfrom_smi, merge);
|
| +
|
| + return phi;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::FromJS(Node* node, Node* context,
|
| + ffi::FFITypeElement elem) {
|
| + Node* result = nullptr;
|
| +
|
| + switch (elem.type) {
|
| + case ffi::FFIType::kInt32: {
|
| + // num = BuildChangeTaggedToFloat64(num);
|
| + // num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToWord32(),
|
| + // num);
|
| + // Do a JavaScript ToNumber.
|
| + Node* num = BuildJavaScriptToNumber(node, context, *effect_, *control_);
|
| + result = BuildChangeTaggedToInt32(num);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kInt64: {
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| + case ffi::FFIType::kFloat32: {
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| + case ffi::FFIType::kFloat64: {
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| + case ffi::FFIType::kCharPtr: {
|
| + result = JSStringToCharPtr(node, context, *effect_, *control_);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kTypedArray: {
|
| + result = TypedArrayToUint8Ptr(node, context, *effect_, *control_);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kBufferNoCopy: {
|
| + result = BufferToPtrNoCopy(node, context, *effect_, *control_);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kFunction: {
|
| + result = JSFunctionToFnPtr(
|
| + node, context, (ffi::FFISignature*)elem.info->function_signature,
|
| + *effect_, *control_);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kStructPtr: {
|
| + result = JSObjectToStructPtr(
|
| + node, context, (ffi::FFIStructSignature*)elem.info->struct_elements,
|
| + *effect_, *control_);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kForeign: {
|
| + result = ForeignToPointer(node, context, *effect_, *control_);
|
| + break;
|
| + }
|
| + case ffi::FFIType::kRawJS: {
|
| + result = node;
|
| + break;
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildChangeInt32ToSmi(Node* value) {
|
| + if (jsgraph()->machine()->Is64()) {
|
| + value = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), value);
|
| + }
|
| + return graph()->NewNode(jsgraph()->machine()->WordShl(), value,
|
| + BuildSmiShiftBitsConstant());
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildChangeSmiToInt32(Node* value) {
|
| + value = graph()->NewNode(jsgraph()->machine()->WordSar(), value,
|
| + BuildSmiShiftBitsConstant());
|
| + if (jsgraph()->machine()->Is64()) {
|
| + value =
|
| + graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), value);
|
| + }
|
| + return value;
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildChangeSmiToFloat64(Node* value) {
|
| + return graph()->NewNode(jsgraph()->machine()->ChangeInt32ToFloat64(),
|
| + BuildChangeSmiToInt32(value));
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildTestNotSmi(Node* value) {
|
| + STATIC_ASSERT(kSmiTag == 0);
|
| + STATIC_ASSERT(kSmiTagMask == 1);
|
| + return graph()->NewNode(jsgraph()->machine()->WordAnd(), value,
|
| + jsgraph()->IntPtrConstant(kSmiTagMask));
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildSmiShiftBitsConstant() {
|
| + return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildAllocateHeapNumberWithValue(Node* value,
|
| + Node* control) {
|
| + MachineOperatorBuilder* machine = jsgraph()->machine();
|
| + CommonOperatorBuilder* common = jsgraph()->common();
|
| + // The AllocateHeapNumberStub does not use the context, so we can safely pass
|
| + // in Smi zero here.
|
| + Callable callable = CodeFactory::AllocateHeapNumber(jsgraph()->isolate());
|
| + Node* target = jsgraph()->HeapConstant(callable.code());
|
| + Node* context = jsgraph()->NoContextConstant();
|
| + Node* effect =
|
| + graph()->NewNode(common->BeginRegion(RegionObservability::kNotObservable),
|
| + graph()->start());
|
| + if (!allocate_heap_number_operator_.is_set()) {
|
| + CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
|
| + jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0,
|
| + CallDescriptor::kNoFlags, Operator::kNoThrow);
|
| + allocate_heap_number_operator_.set(common->Call(descriptor));
|
| + }
|
| + Node* heap_number = graph()->NewNode(allocate_heap_number_operator_.get(),
|
| + target, context, effect, control);
|
| + Node* store =
|
| + graph()->NewNode(machine->Store(StoreRepresentation(
|
| + MachineRepresentation::kFloat64, kNoWriteBarrier)),
|
| + heap_number, BuildHeapNumberValueIndexConstant(), value,
|
| + heap_number, control);
|
| + return graph()->NewNode(common->FinishRegion(), heap_number, store);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildLoadHeapNumberValue(Node* value, Node* control) {
|
| + return graph()->NewNode(jsgraph()->machine()->Load(MachineType::Float64()),
|
| + value, BuildHeapNumberValueIndexConstant(),
|
| + graph()->start(), control);
|
| +}
|
| +
|
| +Node* FFIGraphBuilder::BuildHeapNumberValueIndexConstant() {
|
| + return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag);
|
| +}
|
| +
|
| +void FFIGraphBuilder::BuildJSToNativeWrapper(ffi::NativeFunction func) {
|
| + ffi::FFISignature* sig = func.sig;
|
| + int param_count;
|
| + // if (jsgraph()->machine()->Is64()) {
|
| + param_count = static_cast<int>(sig->parameter_count());
|
| + // } else {
|
| + // param_count = Int64Lowering::GetParameterCountAfterLowering(sig);
|
| + // }
|
| + int count = param_count + 1; // the function is the first arg
|
| + Node** args = Buffer(count);
|
| +
|
| + // Build the start and the JS parameter nodes.
|
| + Node* start = Start(param_count + 5); // FIXME: why 5?
|
| + *control_ = start;
|
| + *effect_ = start;
|
| +
|
| + // Create the context parameter.
|
| + Node* context = graph()->NewNode(
|
| + jsgraph()->common()->Parameter(
|
| + Linkage::GetJSCallContextParamIndex(param_count + 1), "%context"),
|
| + graph()->start());
|
| +
|
| + int pos = 0;
|
| + ApiFunction api_func(func.start);
|
| + ExternalReference ref(&api_func, ExternalReference::DIRECT_API_CALL,
|
| + jsgraph()->isolate());
|
| + Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref));
|
| + args[pos++] = function;
|
| +
|
| + // Convert JS parameters to native numbers.
|
| + for (int i = 0; i < param_count; i++) {
|
| + Node* param =
|
| + graph()->NewNode(jsgraph()->common()->Parameter(i + 1), start);
|
| + Node* native_param = FromJS(param, context, sig->GetParam(i));
|
| + args[pos++] = native_param;
|
| + }
|
| +
|
| + CHECK_EQ(pos, count);
|
| +
|
| + // Call the Native code.
|
| + Node* native_call = BuildCCall(sig, args);
|
| + Node* cleaned_call =
|
| + BuildCallToRuntime(Runtime::kReleaseFFIMemPool, jsgraph(), context, NULL,
|
| + 0, effect_, control_);
|
| +
|
| + Node* retval;
|
| + if (sig->return_count() != 0) {
|
| + retval = ToJS(native_call, context, sig->GetReturn());
|
| + } else {
|
| + retval = jsgraph()->UndefinedConstant();
|
| + }
|
| + Node* ret = graph()->NewNode(jsgraph()->common()->Return(),
|
| + jsgraph()->Int32Constant(0), retval,
|
| + cleaned_call, start);
|
| +
|
| + MergeControlToEnd(jsgraph(), ret);
|
| +}
|
| +
|
| +void FFIGraphBuilder::PrintDebugName(Node* node) {
|
| + PrintF("#%d:%s", node->id(), node->op()->mnemonic());
|
| +}
|
| +
|
| +Graph* FFIGraphBuilder::graph() { return jsgraph()->graph(); }
|
| +
|
| +static void RecordFunctionCompilation(Logger::LogEventsAndTags tag,
|
| + CompilationInfo* info,
|
| + const char* message,
|
| + Vector<const char> func_name) {
|
| + Isolate* isolate = info->isolate();
|
| + if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
|
| + ScopedVector<char> buffer(128);
|
| + SNPrintF(buffer, "%s:%.*s", message, func_name.length(), func_name.start());
|
| + Handle<String> name_str =
|
| + isolate->factory()->NewStringFromAsciiChecked(buffer.start());
|
| + Handle<String> script_str =
|
| + isolate->factory()->NewStringFromAsciiChecked("(FFI)");
|
| + Handle<Code> code = info->code();
|
| + Handle<SharedFunctionInfo> shared =
|
| + isolate->factory()->NewSharedFunctionInfo(name_str, code, false);
|
| + PROFILE(isolate, CodeCreateEvent(tag, AbstractCode::cast(*code), *shared,
|
| + *script_str, 0, 0));
|
| + }
|
| +}
|
| +
|
| +// TODO(ofrobots): find a better home for this.
|
| +static void Setup(Isolate* isolate, Handle<Context> context) {
|
| + if (!context->get(Context::NATIVE_FUNCTION_MAP_INDEX)->IsMap()) {
|
| + // TODO(ofrobots): move this to boostrapper.cc??
|
| + Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
|
| +
|
| + InstanceType instance_type = prev_map->instance_type();
|
| + int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
|
| + CHECK_EQ(0, internal_fields);
|
| + int pre_allocated =
|
| + prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
|
| + int instance_size;
|
| + int in_object_properties;
|
| + JSFunction::CalculateInstanceSizeHelper(instance_type, internal_fields, 0,
|
| + &instance_size,
|
| + &in_object_properties);
|
| + int unused_property_fields = in_object_properties - pre_allocated;
|
| + Handle<Map> map = Map::CopyInitialMap(
|
| + prev_map, instance_size, in_object_properties, unused_property_fields);
|
| + context->set_native_function_map(*map);
|
| + }
|
| +}
|
| +
|
| +Handle<JSFunction> CompileJSToNativeWrapper(Isolate* isolate,
|
| + Handle<String> name,
|
| + ffi::NativeFunction func) {
|
| + Handle<Context> context(isolate->context());
|
| + Setup(isolate, context);
|
| +
|
| + //----------------------------------------------------------------------------
|
| + // Create the JSFunction object.
|
| + //----------------------------------------------------------------------------
|
| + Handle<SharedFunctionInfo> shared = isolate->factory()->NewSharedFunctionInfo(
|
| + name, MaybeHandle<Code>(), false);
|
| + int params = static_cast<int>(func.sig->parameter_count());
|
| + shared->set_length(params);
|
| + shared->set_internal_formal_parameter_count(params);
|
| + Handle<JSFunction> function = isolate->factory()->NewFunction(
|
| + isolate->native_function_map(), name, MaybeHandle<Code>());
|
| + // function->SetInternalField(0, *module_object);
|
| + function->set_shared(*shared);
|
| +
|
| + //----------------------------------------------------------------------------
|
| + // Create the Graph
|
| + //----------------------------------------------------------------------------
|
| + Zone zone(isolate->allocator(), ZONE_NAME);
|
| + Graph graph(&zone);
|
| + CommonOperatorBuilder common(&zone);
|
| + MachineOperatorBuilder machine(&zone);
|
| + JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
|
| +
|
| + // TODO(ofrobots): we only support 64-bit at the moment
|
| + CHECK(machine.Is64());
|
| +
|
| + Node* control = nullptr;
|
| + Node* effect = nullptr;
|
| +
|
| + FFIGraphBuilder builder(&zone, &jsgraph);
|
| + builder.set_control_ptr(&control);
|
| + builder.set_effect_ptr(&effect);
|
| + builder.BuildJSToNativeWrapper(func);
|
| +
|
| + //----------------------------------------------------------------------------
|
| + // Run the compilation pipeline.
|
| + //----------------------------------------------------------------------------
|
| + {
|
| + if (FLAG_trace_turbo_graph) { // Simple textual RPO.
|
| + OFStream os(stdout);
|
| + os << "-- Graph after change lowering -- " << std::endl;
|
| + os << AsRPO(graph);
|
| + }
|
| +
|
| + // Schedule and compile to machine code.
|
| + int params = static_cast<int>(func.sig->parameter_count());
|
| + CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
|
| + &zone, false, params + 1, CallDescriptor::kNoFlags);
|
| + Code::Flags flags = Code::ComputeFlags(Code::JS_TO_NATIVE_FUNCTION);
|
| +
|
| + int length = 0;
|
| + std::unique_ptr<char[]> c_str =
|
| + name->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &length);
|
| + ScopedVector<char> buffer(32 + length);
|
| + int chars = SNPrintF(buffer, "js-to-native#%s", c_str.get());
|
| + Vector<const char> func_name =
|
| + Vector<const char>::cast(buffer.SubVector(0, chars));
|
| +
|
| + CompilationInfo info(func_name, isolate, &zone, flags);
|
| + Handle<Code> code =
|
| + Pipeline::GenerateCodeForTesting(&info, incoming, &graph);
|
| +#ifdef ENABLE_DISASSEMBLER
|
| + if (FLAG_print_opt_code && !code.is_null()) {
|
| + OFStream os(stdout);
|
| + code->Disassemble(buffer.start(), os);
|
| + }
|
| +#endif
|
| +
|
| + RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, "js-to-native",
|
| + func_name);
|
| + // Set the JSFunction's machine code.
|
| + function->set_code(*code);
|
| + }
|
| + return function;
|
| +}
|
| +
|
| +void WriteArgs(Isolate* isolate, ffi::FFISignature* sig,
|
| + Handle<JSReceiver> packed_args, char* arg_dest) {
|
| + const int params = static_cast<int>(sig->parameter_count());
|
| + char* write_pos = arg_dest;
|
| + for (int i = 0; i < params; i++) {
|
| + ffi::FFITypeElement elem = sig->GetParam(i);
|
| + LookupIterator it(isolate, packed_args, i, packed_args);
|
| + Handle<Object> value = Object::GetProperty(&it).ToHandleChecked();
|
| + WriteObject(value, elem.type, write_pos);
|
| + write_pos += TypeSize(elem.type);
|
| + }
|
| +}
|
| +
|
| +void* FFIFunctionBind(Isolate* isolate, ffi::NativeFunction func,
|
| + Handle<JSReceiver> packed_args) {
|
| + // TODO(mattloring): free this
|
| + // TODO(mattloring): compute precise size
|
| + char* arguments = reinterpret_cast<char*>(malloc(64));
|
| +
|
| + WriteArgs(isolate, func.sig, packed_args, arguments);
|
| +
|
| + const int params = static_cast<int>(func.sig->parameter_count());
|
| + const int returns = static_cast<int>(func.sig->return_count());
|
| +
|
| + Zone zone(isolate->allocator(), ZONE_NAME);
|
| + Graph graph(&zone);
|
| + CommonOperatorBuilder common(&zone);
|
| + MachineOperatorBuilder machine(&zone);
|
| +
|
| + MachineType* ffi_reps = TranslateSignature(func.sig);
|
| + MachineSignature ffi_mach_sig(returns, params, ffi_reps);
|
| +
|
| + CallDescriptor* ffi_desc =
|
| + Linkage::GetSimplifiedCDescriptor(&zone, &ffi_mach_sig);
|
| +
|
| + Node* start = graph.NewNode(common.Start(4)); // 4 + param count
|
| + graph.SetStart(start);
|
| + Node* function = graph.NewNode(
|
| + common.Int64Constant(reinterpret_cast<int64_t>(func.start)));
|
| + Node** args =
|
| + reinterpret_cast<Node**>(zone.New((3 + params) * sizeof(Node*)));
|
| + int index = 0;
|
| + args[index++] = function;
|
| + char* write_pos = arguments;
|
| + Node* load_effect = start;
|
| + for (int i = 0; i < params; i++) {
|
| + ffi::FFITypeElement elem = func.sig->GetParam(i);
|
| + MachineType mach_type = FFITypeToMachineType(elem);
|
| + args[index] = graph.NewNode(
|
| + machine.Load(mach_type), graph.NewNode(common.Int64Constant(
|
| + reinterpret_cast<int64_t>(write_pos))),
|
| + graph.NewNode(common.Int64Constant(0)), load_effect, start);
|
| + load_effect = args[index++];
|
| + write_pos += TypeSize(elem.type);
|
| + }
|
| + args[index++] = load_effect;
|
| + args[index++] = start;
|
| + Node* call = graph.NewNode(common.Call(ffi_desc), (3 + params), args);
|
| + Node* pop_size = graph.NewNode(common.Int32Constant(0));
|
| + Node* ret = graph.NewNode(common.Return(), pop_size, call, call, start);
|
| + graph.SetEnd(graph.NewNode(common.End(1),
|
| + ret)); // No clue what this number should be.
|
| +
|
| + MachineSignature mach_sig(1, 0, ffi_reps);
|
| +
|
| + CallDescriptor* desc = Linkage::GetSimplifiedCDescriptor(&zone, &mach_sig);
|
| +
|
| + Code::Flags flags = Code::ComputeFlags(Code::STUB);
|
| +
|
| + CompilationInfo info(ArrayVector("FFIFunctionBind"), isolate, &zone, flags);
|
| + Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, desc, &graph);
|
| + return reinterpret_cast<void*>(code->entry());
|
| +}
|
| +
|
| +void* BuildFFISerializer(Isolate* isolate, ffi::NativeFunction func) {
|
| + const int params = static_cast<int>(func.sig->parameter_count());
|
| +
|
| + Zone zone(isolate->allocator(), ZONE_NAME);
|
| + Graph graph(&zone);
|
| + CommonOperatorBuilder common(&zone);
|
| + MachineOperatorBuilder machine(&zone);
|
| +
|
| + Node* start = graph.NewNode(common.Start(4 + params)); // 4 + param count
|
| + graph.SetStart(start);
|
| +
|
| + // Begin Malloc Call
|
| + Node** args = reinterpret_cast<Node**>(zone.New((4) * sizeof(Node*)));
|
| + ExternalReference malloc_ptr(reinterpret_cast<Address>(malloc), isolate);
|
| + args[0] = graph.NewNode(common.ExternalConstant(malloc_ptr));
|
| + // TODO(mattloring): compute precise size
|
| + args[1] = graph.NewNode(common.Int32Constant(64));
|
| + args[2] = start;
|
| + args[3] = start;
|
| + MachineType malloc_reps[] = {MachineType::Pointer(), MachineType::Int32()};
|
| + MachineSignature malloc_sig(1, 1, malloc_reps);
|
| + CallDescriptor* malloc_desc =
|
| + Linkage::GetSimplifiedCDescriptor(&zone, &malloc_sig);
|
| +
|
| + Node* malloc_call = graph.NewNode(common.Call(malloc_desc), 4, args);
|
| + Node* malloc_control = graph.NewNode(common.IfSuccess(), malloc_call);
|
| + // End Malloc Call
|
| +
|
| + Node* store_effect = malloc_call;
|
| + int write_offset = 0;
|
| + for (int i = 0; i < params; i++) {
|
| + ffi::FFITypeElement elem = func.sig->GetParam(i);
|
| + MachineType mach_type = FFITypeToMachineType(elem);
|
| + Node* param = graph.NewNode(common.Parameter(i, "%param"), start);
|
| + store_effect = graph.NewNode(
|
| + machine.Store(
|
| + StoreRepresentation(mach_type.representation(), kNoWriteBarrier)),
|
| + malloc_call, graph.NewNode(common.Int64Constant(write_offset)), param,
|
| + store_effect, malloc_control);
|
| + write_offset += TypeSize(elem.type);
|
| + }
|
| + Node* ret =
|
| + graph.NewNode(common.Return(), graph.NewNode(common.Int32Constant(0)),
|
| + malloc_call, store_effect, malloc_control);
|
| + graph.SetEnd(graph.NewNode(common.End(1),
|
| + ret)); // No clue what this number should be.
|
| +
|
| + MachineSignature::Builder builder(&zone, 1, params);
|
| + builder.AddReturn(MachineType::Pointer()); // Pointer to the argument storage
|
| + for (int i = 0; i < params; i++) {
|
| + builder.AddParam(FFITypeToMachineType(func.sig->GetParam(i)));
|
| + }
|
| + MachineSignature* mach_sig = builder.Build();
|
| + CallDescriptor* desc = Linkage::GetSimplifiedCDescriptor(&zone, mach_sig);
|
| +
|
| + Code::Flags flags = Code::ComputeFlags(Code::STUB);
|
| +
|
| + CompilationInfo info(ArrayVector("FFISerialize"), isolate, &zone, flags);
|
| + Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, desc, &graph);
|
| + return reinterpret_cast<void*>(code->entry());
|
| +}
|
| +
|
| +void* BuildFFIDeserializedExecutor(Isolate* isolate, ffi::NativeFunction func) {
|
| + const int params = static_cast<int>(func.sig->parameter_count());
|
| + const int returns = static_cast<int>(func.sig->return_count());
|
| +
|
| + Zone zone(isolate->allocator(), ZONE_NAME);
|
| + Graph graph(&zone);
|
| + CommonOperatorBuilder common(&zone);
|
| + MachineOperatorBuilder machine(&zone);
|
| +
|
| + MachineType* ffi_reps = TranslateSignature(func.sig);
|
| + MachineSignature ffi_mach_sig(returns, params, ffi_reps);
|
| +
|
| + CallDescriptor* ffi_desc =
|
| + Linkage::GetSimplifiedCDescriptor(&zone, &ffi_mach_sig);
|
| +
|
| + Node* start = graph.NewNode(common.Start(4 + 1)); // 4 + param count
|
| + graph.SetStart(start);
|
| + Node* function = graph.NewNode(
|
| + common.Int64Constant(reinterpret_cast<int64_t>(func.start)));
|
| + Node** args =
|
| + reinterpret_cast<Node**>(zone.New((3 + params) * sizeof(Node*)));
|
| + int index = 0;
|
| + args[index++] = function;
|
| + int load_offset = 0;
|
| + Node* load_effect = start;
|
| + for (int i = 0; i < params; i++) {
|
| + ffi::FFITypeElement elem = func.sig->GetParam(i);
|
| + MachineType mach_type = FFITypeToMachineType(elem);
|
| + Node* base = graph.NewNode(common.Parameter(0, "%param"), start);
|
| + args[index] = graph.NewNode(
|
| + machine.Load(mach_type), base,
|
| + graph.NewNode(common.Int64Constant(load_offset)), load_effect, start);
|
| + load_effect = args[index++];
|
| + load_offset += TypeSize(elem.type);
|
| + }
|
| + args[index++] = load_effect;
|
| + args[index++] = start;
|
| + Node* call = graph.NewNode(common.Call(ffi_desc), (3 + params), args);
|
| + Node* call_control = graph.NewNode(common.IfSuccess(), call);
|
| +
|
| + // Begin Free Call
|
| + Node** free_args = reinterpret_cast<Node**>(zone.New((4) * sizeof(Node*)));
|
| + ExternalReference free_ptr(reinterpret_cast<Address>(free), isolate);
|
| + free_args[0] = graph.NewNode(common.ExternalConstant(free_ptr));
|
| + free_args[1] = graph.NewNode(common.Parameter(0, "%param"), start);
|
| + free_args[2] = call;
|
| + free_args[3] = call_control;
|
| + MachineType free_reps[] = {MachineType::Pointer()};
|
| + MachineSignature free_sig(0, 1, free_reps);
|
| + CallDescriptor* free_desc =
|
| + Linkage::GetSimplifiedCDescriptor(&zone, &free_sig);
|
| +
|
| + Node* free_call = graph.NewNode(common.Call(free_desc), 4, free_args);
|
| + Node* free_control = graph.NewNode(common.IfSuccess(), free_call);
|
| + // End Free Call
|
| +
|
| + Node* pop_size = graph.NewNode(common.Int32Constant(0));
|
| + Node* ret =
|
| + graph.NewNode(common.Return(), pop_size, call, free_call, free_control);
|
| + graph.SetEnd(graph.NewNode(common.End(1),
|
| + ret)); // No clue what this number should be.
|
| +
|
| + MachineSignature::Builder builder(&zone, 1, 1);
|
| + builder.AddParam(MachineType::Pointer());
|
| + builder.AddReturn(ffi_mach_sig.GetReturn(0));
|
| + MachineSignature* mach_sig = builder.Build();
|
| +
|
| + CallDescriptor* desc = Linkage::GetSimplifiedCDescriptor(&zone, mach_sig);
|
| +
|
| + Code::Flags flags = Code::ComputeFlags(Code::STUB);
|
| +
|
| + CompilationInfo info(ArrayVector("FFIFunctionDeserialize"), isolate, &zone,
|
| + flags);
|
| + Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, desc, &graph);
|
| + return reinterpret_cast<void*>(code->entry());
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|