Chromium Code Reviews| 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..2148c52e687a21052f6aa2d7b47d9e4da4374886 |
| --- /dev/null |
| +++ b/src/compiler/ffi-compiler.cc |
| @@ -0,0 +1,622 @@ |
| +// 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 "src/isolate-inl.h" |
| + |
| +#include "src/code-factory.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 internal { |
| +namespace compiler { |
| + |
| +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 |
| + |
| +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; |
| +} |
| + |
| +Node* FFIGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) { |
| + const size_t params = sig->parameter_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_; |
| + |
| + CallDescriptor* desc = |
| + Linkage::GetSimplifiedCDescriptor(jsgraph()->zone(), sig); |
| + |
| + const Operator* op = jsgraph()->common()->Call(desc); |
| + Node* call = graph()->NewNode(op, static_cast<int>(count), args); |
| + *effect_ = call; |
| + 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, MachineType type) { |
| + switch (type.representation()) { |
| + case MachineRepresentation::kWord32: |
| + return BuildChangeInt32ToTagged(node); |
| + case MachineRepresentation::kWord64: |
| + // 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 MachineRepresentation::kFloat32: |
| + node = graph()->NewNode(jsgraph()->machine()->ChangeFloat32ToFloat64(), |
| + node); |
| + return BuildChangeFloat64ToTagged(node); |
| + case MachineRepresentation::kFloat64: |
| + return BuildChangeFloat64ToTagged(node); |
| + default: |
| + UNREACHABLE(); |
| + return nullptr; |
| + } |
| +} |
| + |
| +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_ = result; |
| + *effect_ = result; |
| + |
| + return result; |
| +} |
| + |
| +static bool CanCover(Node* value, IrOpcode::Value opcode) { |
|
Benedikt Meurer
2016/06/21 03:26:01
You don't need this helper (see below).
ofrobots
2016/06/23 01:02:51
Acknowledged.
|
| + 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)) { |
|
Benedikt Meurer
2016/06/21 03:26:01
You don't need this magic. It will never trigger a
ofrobots
2016/06/23 01:02:51
Acknowledged.
|
| + // 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, 0); |
| + 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, MachineType type) { |
| + // Do a JavaScript ToNumber. |
| + Node* num = BuildJavaScriptToNumber(node, context, *effect_, *control_); |
| + |
| + switch (type.representation()) { |
| + case MachineRepresentation::kWord32: { |
| + // num = BuildChangeTaggedToFloat64(num); |
| + // num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToWord32(), |
| + // num); |
| + num = BuildChangeTaggedToInt32(num); |
| + break; |
| + } |
| + // TODO(ofrobots): handle other types. |
| + default: |
| + UNREACHABLE(); |
| + return nullptr; |
| + } |
| + return num; |
| +} |
| + |
| +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) { |
| + MachineSignature* 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); |
|
ofrobots
2016/06/23 01:02:51
Any thoughts on the abuse of 'ApiFunction' here?
Benedikt Meurer
2016/06/23 03:34:06
I think it's OK for prototyping. Long-term you'll
|
| + 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* call = BuildCCall(sig, args); |
| + |
| + Node* retval = call; |
| + Node* jsval = ToJS(retval, context, sig->GetReturn()); |
| + Node* ret = |
| + graph()->NewNode(jsgraph()->common()->Return(), jsval, 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->cpu_profiler()->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()); |
| + 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; |
| + base::SmartArrayPointer<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; |
| +} |
| + |
| +} // namespace compiler |
| +} // namespace internal |
| +} // namespace v8 |