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 |