Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "src/compiler/ffi-compiler.h" | |
| 6 | |
| 7 #include "src/isolate-inl.h" | |
| 8 | |
| 9 #include "src/code-factory.h" | |
| 10 #include "src/compiler/access-builder.h" | |
| 11 #include "src/compiler/common-operator.h" | |
| 12 #include "src/compiler/graph-visualizer.h" | |
| 13 #include "src/compiler/graph.h" | |
| 14 #include "src/compiler/js-graph.h" | |
| 15 #include "src/compiler/js-operator.h" | |
| 16 #include "src/compiler/linkage.h" | |
| 17 #include "src/compiler/pipeline.h" | |
| 18 #include "src/factory.h" | |
| 19 #include "src/profiler/cpu-profiler.h" | |
| 20 | |
| 21 namespace v8 { | |
| 22 namespace internal { | |
| 23 namespace compiler { | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 // TODO(ofrobots): duplicated from wasm-compiler.cc | |
| 28 static void MergeControlToEnd(JSGraph* jsgraph, Node* node) { | |
| 29 Graph* g = jsgraph->graph(); | |
| 30 if (g->end()) { | |
| 31 NodeProperties::MergeControlToEnd(g, jsgraph->common(), node); | |
| 32 } else { | |
| 33 g->SetEnd(g->NewNode(jsgraph->common()->End(1), node)); | |
| 34 } | |
| 35 } | |
| 36 | |
| 37 } // namespace | |
| 38 | |
| 39 FFIGraphBuilder::FFIGraphBuilder(Zone* zone, JSGraph* jsgraph) | |
| 40 : zone_(zone), | |
| 41 jsgraph_(jsgraph), | |
| 42 control_(nullptr), | |
| 43 effect_(nullptr), | |
| 44 cur_buffer_(def_buffer_), | |
| 45 cur_bufsize_(kDefaultBufferSize) { | |
| 46 DCHECK_NOT_NULL(jsgraph_); | |
| 47 } | |
| 48 | |
| 49 Node* FFIGraphBuilder::Start(unsigned params) { | |
| 50 Node* start = graph()->NewNode(jsgraph()->common()->Start(params)); | |
| 51 graph()->SetStart(start); | |
| 52 return start; | |
| 53 } | |
| 54 | |
| 55 Node* FFIGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) { | |
| 56 const size_t params = sig->parameter_count(); | |
| 57 const size_t extra = 2; // effect and control inputs. | |
| 58 const size_t count = 1 + params + extra; | |
| 59 | |
| 60 // Reallocate the buffer to make space for extra inputs. | |
| 61 args = Realloc(args, 1 + params, count); | |
| 62 | |
| 63 // Add effect and control inputs. | |
| 64 args[params + 1] = *effect_; | |
| 65 args[params + 2] = *control_; | |
| 66 | |
| 67 CallDescriptor* desc = | |
| 68 Linkage::GetSimplifiedCDescriptor(jsgraph()->zone(), sig); | |
| 69 | |
| 70 const Operator* op = jsgraph()->common()->Call(desc); | |
| 71 Node* call = graph()->NewNode(op, static_cast<int>(count), args); | |
| 72 *effect_ = call; | |
| 73 return call; | |
| 74 } | |
| 75 | |
| 76 Node* FFIGraphBuilder::BuildChangeInt32ToTagged(Node* value) { | |
| 77 MachineOperatorBuilder* machine = jsgraph()->machine(); | |
| 78 CommonOperatorBuilder* common = jsgraph()->common(); | |
| 79 | |
| 80 if (machine->Is64()) { | |
| 81 return BuildChangeInt32ToSmi(value); | |
| 82 } | |
| 83 | |
| 84 Node* add = graph()->NewNode(machine->Int32AddWithOverflow(), value, value); | |
| 85 | |
| 86 Node* ovf = graph()->NewNode(common->Projection(1), add); | |
| 87 Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), ovf, | |
| 88 graph()->start()); | |
| 89 | |
| 90 Node* if_true = graph()->NewNode(common->IfTrue(), branch); | |
| 91 Node* vtrue = BuildAllocateHeapNumberWithValue( | |
| 92 graph()->NewNode(machine->ChangeInt32ToFloat64(), value), if_true); | |
| 93 | |
| 94 Node* if_false = graph()->NewNode(common->IfFalse(), branch); | |
| 95 Node* vfalse = graph()->NewNode(common->Projection(0), add); | |
| 96 | |
| 97 Node* merge = graph()->NewNode(common->Merge(2), if_true, if_false); | |
| 98 Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2), | |
| 99 vtrue, vfalse, merge); | |
| 100 return phi; | |
| 101 } | |
| 102 | |
| 103 Node* FFIGraphBuilder::BuildChangeFloat64ToTagged(Node* value) { | |
| 104 MachineOperatorBuilder* machine = jsgraph()->machine(); | |
| 105 CommonOperatorBuilder* common = jsgraph()->common(); | |
| 106 | |
| 107 Node* value32 = graph()->NewNode(machine->RoundFloat64ToInt32(), value); | |
| 108 Node* check_same = graph()->NewNode( | |
| 109 machine->Float64Equal(), value, | |
| 110 graph()->NewNode(machine->ChangeInt32ToFloat64(), value32)); | |
| 111 Node* branch_same = | |
| 112 graph()->NewNode(common->Branch(), check_same, graph()->start()); | |
| 113 | |
| 114 Node* if_smi = graph()->NewNode(common->IfTrue(), branch_same); | |
| 115 Node* vsmi; | |
| 116 Node* if_box = graph()->NewNode(common->IfFalse(), branch_same); | |
| 117 Node* vbox; | |
| 118 | |
| 119 // We only need to check for -0 if the {value} can potentially contain -0. | |
| 120 Node* check_zero = graph()->NewNode(machine->Word32Equal(), value32, | |
| 121 jsgraph()->Int32Constant(0)); | |
| 122 Node* branch_zero = | |
| 123 graph()->NewNode(common->Branch(BranchHint::kFalse), check_zero, if_smi); | |
| 124 | |
| 125 Node* if_zero = graph()->NewNode(common->IfTrue(), branch_zero); | |
| 126 Node* if_notzero = graph()->NewNode(common->IfFalse(), branch_zero); | |
| 127 | |
| 128 // In case of 0, we need to check the high bits for the IEEE -0 pattern. | |
| 129 Node* check_negative = graph()->NewNode( | |
| 130 machine->Int32LessThan(), | |
| 131 graph()->NewNode(machine->Float64ExtractHighWord32(), value), | |
| 132 jsgraph()->Int32Constant(0)); | |
| 133 Node* branch_negative = graph()->NewNode(common->Branch(BranchHint::kFalse), | |
| 134 check_negative, if_zero); | |
| 135 | |
| 136 Node* if_negative = graph()->NewNode(common->IfTrue(), branch_negative); | |
| 137 Node* if_notnegative = graph()->NewNode(common->IfFalse(), branch_negative); | |
| 138 | |
| 139 // We need to create a box for negative 0. | |
| 140 if_smi = graph()->NewNode(common->Merge(2), if_notzero, if_notnegative); | |
| 141 if_box = graph()->NewNode(common->Merge(2), if_box, if_negative); | |
| 142 | |
| 143 // On 64-bit machines we can just wrap the 32-bit integer in a smi, for 32-bit | |
| 144 // machines we need to deal with potential overflow and fallback to boxing. | |
| 145 if (machine->Is64()) { | |
| 146 vsmi = BuildChangeInt32ToSmi(value32); | |
| 147 } else { | |
| 148 Node* smi_tag = | |
| 149 graph()->NewNode(machine->Int32AddWithOverflow(), value32, value32); | |
| 150 | |
| 151 Node* check_ovf = graph()->NewNode(common->Projection(1), smi_tag); | |
| 152 Node* branch_ovf = | |
| 153 graph()->NewNode(common->Branch(BranchHint::kFalse), check_ovf, if_smi); | |
| 154 | |
| 155 Node* if_ovf = graph()->NewNode(common->IfTrue(), branch_ovf); | |
| 156 if_box = graph()->NewNode(common->Merge(2), if_ovf, if_box); | |
| 157 | |
| 158 if_smi = graph()->NewNode(common->IfFalse(), branch_ovf); | |
| 159 vsmi = graph()->NewNode(common->Projection(0), smi_tag); | |
| 160 } | |
| 161 | |
| 162 // Allocate the box for the {value}. | |
| 163 vbox = BuildAllocateHeapNumberWithValue(value, if_box); | |
| 164 | |
| 165 Node* control = graph()->NewNode(common->Merge(2), if_smi, if_box); | |
| 166 value = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2), vsmi, | |
| 167 vbox, control); | |
| 168 return value; | |
| 169 } | |
| 170 | |
| 171 Node* FFIGraphBuilder::ToJS(Node* node, Node* context, MachineType type) { | |
| 172 switch (type.representation()) { | |
| 173 case MachineRepresentation::kWord32: | |
| 174 return BuildChangeInt32ToTagged(node); | |
| 175 case MachineRepresentation::kWord64: | |
| 176 // TODO(titzer): i64->JS has no good solution right now. Using lower 32 | |
| 177 // bits. | |
| 178 if (jsgraph()->machine()->Is64()) { | |
| 179 // On 32 bit platforms we do not have to do the truncation because the | |
| 180 // node we get in as a parameter only contains the low word anyways. | |
| 181 node = graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), | |
| 182 node); | |
| 183 } | |
| 184 return BuildChangeInt32ToTagged(node); | |
| 185 case MachineRepresentation::kFloat32: | |
| 186 node = graph()->NewNode(jsgraph()->machine()->ChangeFloat32ToFloat64(), | |
| 187 node); | |
| 188 return BuildChangeFloat64ToTagged(node); | |
| 189 case MachineRepresentation::kFloat64: | |
| 190 return BuildChangeFloat64ToTagged(node); | |
| 191 default: | |
| 192 UNREACHABLE(); | |
| 193 return nullptr; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 Node* FFIGraphBuilder::BuildJavaScriptToNumber(Node* node, Node* context, | |
| 198 Node* effect, Node* control) { | |
| 199 Callable callable = CodeFactory::ToNumber(jsgraph()->isolate()); | |
| 200 CallDescriptor* desc = Linkage::GetStubCallDescriptor( | |
| 201 jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0, | |
| 202 CallDescriptor::kNoFlags, Operator::kNoProperties); | |
| 203 Node* stub_code = jsgraph()->HeapConstant(callable.code()); | |
| 204 | |
| 205 Node* result = graph()->NewNode(jsgraph()->common()->Call(desc), stub_code, | |
| 206 node, context, effect, control); | |
| 207 | |
| 208 *control_ = result; | |
| 209 *effect_ = result; | |
| 210 | |
| 211 return result; | |
| 212 } | |
| 213 | |
| 214 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.
| |
| 215 if (value->opcode() != opcode) return false; | |
| 216 bool first = true; | |
| 217 for (Edge const edge : value->use_edges()) { | |
| 218 if (NodeProperties::IsControlEdge(edge)) continue; | |
| 219 if (NodeProperties::IsEffectEdge(edge)) continue; | |
| 220 DCHECK(NodeProperties::IsValueEdge(edge)); | |
| 221 if (!first) return false; | |
| 222 first = false; | |
| 223 } | |
| 224 return true; | |
| 225 } | |
| 226 | |
| 227 Node* FFIGraphBuilder::BuildChangeTaggedToInt32(Node* value) { | |
| 228 CommonOperatorBuilder* common = jsgraph()->common(); | |
| 229 | |
| 230 Node* check = BuildTestNotSmi(value); | |
| 231 Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check, | |
| 232 graph()->start()); | |
| 233 | |
| 234 Node* if_smi = graph()->NewNode(common->IfFalse(), branch); | |
| 235 Node* vfrom_smi = BuildChangeSmiToInt32(value); | |
| 236 | |
| 237 // TODO(ofrobots): the not_smi path is too ugly and slow at the moment | |
| 238 Node* if_not_smi = graph()->NewNode(common->IfTrue(), branch); | |
| 239 Node* vfloat = BuildChangeTaggedToFloat64(value); | |
| 240 Node* vnot_smi = | |
| 241 graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToWord32(), vfloat); | |
| 242 | |
| 243 Node* merge = graph()->NewNode(common->Merge(2), if_not_smi, if_smi); | |
| 244 Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kWord32, 2), | |
| 245 vnot_smi, vfrom_smi, merge); | |
| 246 return phi; | |
| 247 } | |
| 248 | |
| 249 Node* FFIGraphBuilder::BuildChangeTaggedToFloat64(Node* value) { | |
| 250 MachineOperatorBuilder* machine = jsgraph()->machine(); | |
| 251 CommonOperatorBuilder* common = jsgraph()->common(); | |
| 252 | |
| 253 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.
| |
| 254 // ChangeTaggedToFloat64(JSToNumber(x)) => | |
| 255 // if IsSmi(x) then ChangeSmiToFloat64(x) | |
| 256 // else let y = JSToNumber(x) in | |
| 257 // if IsSmi(y) then ChangeSmiToFloat64(y) | |
| 258 // else BuildLoadHeapNumberValue(y) | |
| 259 Node* object = NodeProperties::GetValueInput(value, 0); | |
| 260 Node* context = NodeProperties::GetContextInput(value); | |
| 261 Node* frame_state = NodeProperties::GetFrameStateInput(value, 0); | |
| 262 Node* effect = NodeProperties::GetEffectInput(value); | |
| 263 Node* control = NodeProperties::GetControlInput(value); | |
| 264 | |
| 265 const Operator* merge_op = common->Merge(2); | |
| 266 const Operator* ephi_op = common->EffectPhi(2); | |
| 267 const Operator* phi_op = common->Phi(MachineRepresentation::kFloat64, 2); | |
| 268 | |
| 269 Node* check1 = BuildTestNotSmi(object); | |
| 270 Node* branch1 = | |
| 271 graph()->NewNode(common->Branch(BranchHint::kFalse), check1, control); | |
| 272 | |
| 273 Node* if_true1 = graph()->NewNode(common->IfTrue(), branch1); | |
| 274 Node* vtrue1 = graph()->NewNode(value->op(), object, context, frame_state, | |
| 275 effect, if_true1); | |
| 276 Node* etrue1 = vtrue1; | |
| 277 | |
| 278 Node* check2 = BuildTestNotSmi(vtrue1); | |
| 279 Node* branch2 = graph()->NewNode(common->Branch(), check2, if_true1); | |
| 280 | |
| 281 Node* if_true2 = graph()->NewNode(common->IfTrue(), branch2); | |
| 282 Node* vtrue2 = BuildLoadHeapNumberValue(vtrue1, if_true2); | |
| 283 | |
| 284 Node* if_false2 = graph()->NewNode(common->IfFalse(), branch2); | |
| 285 Node* vfalse2 = BuildChangeSmiToFloat64(vtrue1); | |
| 286 | |
| 287 if_true1 = graph()->NewNode(merge_op, if_true2, if_false2); | |
| 288 vtrue1 = graph()->NewNode(phi_op, vtrue2, vfalse2, if_true1); | |
| 289 | |
| 290 Node* if_false1 = graph()->NewNode(common->IfFalse(), branch1); | |
| 291 Node* vfalse1 = BuildChangeSmiToFloat64(object); | |
| 292 Node* efalse1 = effect; | |
| 293 | |
| 294 Node* merge1 = graph()->NewNode(merge_op, if_true1, if_false1); | |
| 295 Node* ephi1 = graph()->NewNode(ephi_op, etrue1, efalse1, merge1); | |
| 296 Node* phi1 = graph()->NewNode(phi_op, vtrue1, vfalse1, merge1); | |
| 297 | |
| 298 // Wire the new diamond into the graph, {JSToNumber} can still throw. | |
| 299 NodeProperties::ReplaceUses(value, phi1, ephi1, etrue1, etrue1); | |
| 300 | |
| 301 // TODO(mstarzinger): This iteration cuts out the IfSuccess projection from | |
| 302 // the node and places it inside the diamond. Come up with a helper method! | |
| 303 for (Node* use : etrue1->uses()) { | |
| 304 if (use->opcode() == IrOpcode::kIfSuccess) { | |
| 305 use->ReplaceUses(merge1); | |
| 306 NodeProperties::ReplaceControlInput(branch2, use); | |
| 307 } | |
| 308 } | |
| 309 return phi1; | |
| 310 } | |
| 311 | |
| 312 Node* check = BuildTestNotSmi(value); | |
| 313 Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check, | |
| 314 graph()->start()); | |
| 315 | |
| 316 Node* if_not_smi = graph()->NewNode(common->IfTrue(), branch); | |
| 317 | |
| 318 Node* vnot_smi; | |
| 319 Node* check_undefined = graph()->NewNode(machine->WordEqual(), value, | |
| 320 jsgraph()->UndefinedConstant()); | |
| 321 Node* branch_undefined = graph()->NewNode(common->Branch(BranchHint::kFalse), | |
| 322 check_undefined, if_not_smi); | |
| 323 | |
| 324 Node* if_undefined = graph()->NewNode(common->IfTrue(), branch_undefined); | |
| 325 Node* vundefined = | |
| 326 jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN()); | |
| 327 | |
| 328 Node* if_not_undefined = | |
| 329 graph()->NewNode(common->IfFalse(), branch_undefined); | |
| 330 Node* vheap_number = BuildLoadHeapNumberValue(value, if_not_undefined); | |
| 331 | |
| 332 if_not_smi = | |
| 333 graph()->NewNode(common->Merge(2), if_undefined, if_not_undefined); | |
| 334 vnot_smi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2), | |
| 335 vundefined, vheap_number, if_not_smi); | |
| 336 | |
| 337 Node* if_smi = graph()->NewNode(common->IfFalse(), branch); | |
| 338 Node* vfrom_smi = BuildChangeSmiToFloat64(value); | |
| 339 | |
| 340 Node* merge = graph()->NewNode(common->Merge(2), if_not_smi, if_smi); | |
| 341 Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2), | |
| 342 vnot_smi, vfrom_smi, merge); | |
| 343 | |
| 344 return phi; | |
| 345 } | |
| 346 | |
| 347 Node* FFIGraphBuilder::FromJS(Node* node, Node* context, MachineType type) { | |
| 348 // Do a JavaScript ToNumber. | |
| 349 Node* num = BuildJavaScriptToNumber(node, context, *effect_, *control_); | |
| 350 | |
| 351 switch (type.representation()) { | |
| 352 case MachineRepresentation::kWord32: { | |
| 353 // num = BuildChangeTaggedToFloat64(num); | |
| 354 // num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToWord32(), | |
| 355 // num); | |
| 356 num = BuildChangeTaggedToInt32(num); | |
| 357 break; | |
| 358 } | |
| 359 // TODO(ofrobots): handle other types. | |
| 360 default: | |
| 361 UNREACHABLE(); | |
| 362 return nullptr; | |
| 363 } | |
| 364 return num; | |
| 365 } | |
| 366 | |
| 367 Node* FFIGraphBuilder::BuildChangeInt32ToSmi(Node* value) { | |
| 368 if (jsgraph()->machine()->Is64()) { | |
| 369 value = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), value); | |
| 370 } | |
| 371 return graph()->NewNode(jsgraph()->machine()->WordShl(), value, | |
| 372 BuildSmiShiftBitsConstant()); | |
| 373 } | |
| 374 | |
| 375 Node* FFIGraphBuilder::BuildChangeSmiToInt32(Node* value) { | |
| 376 value = graph()->NewNode(jsgraph()->machine()->WordSar(), value, | |
| 377 BuildSmiShiftBitsConstant()); | |
| 378 if (jsgraph()->machine()->Is64()) { | |
| 379 value = | |
| 380 graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), value); | |
| 381 } | |
| 382 return value; | |
| 383 } | |
| 384 | |
| 385 Node* FFIGraphBuilder::BuildChangeSmiToFloat64(Node* value) { | |
| 386 return graph()->NewNode(jsgraph()->machine()->ChangeInt32ToFloat64(), | |
| 387 BuildChangeSmiToInt32(value)); | |
| 388 } | |
| 389 | |
| 390 Node* FFIGraphBuilder::BuildTestNotSmi(Node* value) { | |
| 391 STATIC_ASSERT(kSmiTag == 0); | |
| 392 STATIC_ASSERT(kSmiTagMask == 1); | |
| 393 return graph()->NewNode(jsgraph()->machine()->WordAnd(), value, | |
| 394 jsgraph()->IntPtrConstant(kSmiTagMask)); | |
| 395 } | |
| 396 | |
| 397 Node* FFIGraphBuilder::BuildSmiShiftBitsConstant() { | |
| 398 return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize); | |
| 399 } | |
| 400 | |
| 401 Node* FFIGraphBuilder::BuildAllocateHeapNumberWithValue(Node* value, | |
| 402 Node* control) { | |
| 403 MachineOperatorBuilder* machine = jsgraph()->machine(); | |
| 404 CommonOperatorBuilder* common = jsgraph()->common(); | |
| 405 // The AllocateHeapNumberStub does not use the context, so we can safely pass | |
| 406 // in Smi zero here. | |
| 407 Callable callable = CodeFactory::AllocateHeapNumber(jsgraph()->isolate()); | |
| 408 Node* target = jsgraph()->HeapConstant(callable.code()); | |
| 409 Node* context = jsgraph()->NoContextConstant(); | |
| 410 Node* effect = | |
| 411 graph()->NewNode(common->BeginRegion(RegionObservability::kNotObservable), | |
| 412 graph()->start()); | |
| 413 if (!allocate_heap_number_operator_.is_set()) { | |
| 414 CallDescriptor* descriptor = Linkage::GetStubCallDescriptor( | |
| 415 jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0, | |
| 416 CallDescriptor::kNoFlags, Operator::kNoThrow); | |
| 417 allocate_heap_number_operator_.set(common->Call(descriptor)); | |
| 418 } | |
| 419 Node* heap_number = graph()->NewNode(allocate_heap_number_operator_.get(), | |
| 420 target, context, effect, control); | |
| 421 Node* store = | |
| 422 graph()->NewNode(machine->Store(StoreRepresentation( | |
| 423 MachineRepresentation::kFloat64, kNoWriteBarrier)), | |
| 424 heap_number, BuildHeapNumberValueIndexConstant(), value, | |
| 425 heap_number, control); | |
| 426 return graph()->NewNode(common->FinishRegion(), heap_number, store); | |
| 427 } | |
| 428 | |
| 429 Node* FFIGraphBuilder::BuildLoadHeapNumberValue(Node* value, Node* control) { | |
| 430 return graph()->NewNode(jsgraph()->machine()->Load(MachineType::Float64()), | |
| 431 value, BuildHeapNumberValueIndexConstant(), | |
| 432 graph()->start(), control); | |
| 433 } | |
| 434 | |
| 435 Node* FFIGraphBuilder::BuildHeapNumberValueIndexConstant() { | |
| 436 return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag); | |
| 437 } | |
| 438 | |
| 439 void FFIGraphBuilder::BuildJSToNativeWrapper(ffi::NativeFunction func) { | |
| 440 MachineSignature* sig = func.sig; | |
| 441 int param_count; | |
| 442 // if (jsgraph()->machine()->Is64()) { | |
| 443 param_count = static_cast<int>(sig->parameter_count()); | |
| 444 // } else { | |
| 445 // param_count = Int64Lowering::GetParameterCountAfterLowering(sig); | |
| 446 // } | |
| 447 int count = param_count + 1; // the function is the first arg | |
| 448 Node** args = Buffer(count); | |
| 449 | |
| 450 // Build the start and the JS parameter nodes. | |
| 451 Node* start = Start(param_count + 5); // FIXME: why 5? | |
| 452 *control_ = start; | |
| 453 *effect_ = start; | |
| 454 | |
| 455 // Create the context parameter. | |
| 456 Node* context = graph()->NewNode( | |
| 457 jsgraph()->common()->Parameter( | |
| 458 Linkage::GetJSCallContextParamIndex(param_count + 1), "%context"), | |
| 459 graph()->start()); | |
| 460 | |
| 461 int pos = 0; | |
| 462 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
| |
| 463 ExternalReference ref(&api_func, ExternalReference::DIRECT_API_CALL, | |
| 464 jsgraph()->isolate()); | |
| 465 Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref)); | |
| 466 args[pos++] = function; | |
| 467 | |
| 468 // Convert JS parameters to native numbers. | |
| 469 for (int i = 0; i < param_count; i++) { | |
| 470 Node* param = | |
| 471 graph()->NewNode(jsgraph()->common()->Parameter(i + 1), start); | |
| 472 Node* native_param = FromJS(param, context, sig->GetParam(i)); | |
| 473 args[pos++] = native_param; | |
| 474 } | |
| 475 | |
| 476 CHECK_EQ(pos, count); | |
| 477 | |
| 478 // Call the Native code. | |
| 479 Node* call = BuildCCall(sig, args); | |
| 480 | |
| 481 Node* retval = call; | |
| 482 Node* jsval = ToJS(retval, context, sig->GetReturn()); | |
| 483 Node* ret = | |
| 484 graph()->NewNode(jsgraph()->common()->Return(), jsval, call, start); | |
| 485 | |
| 486 MergeControlToEnd(jsgraph(), ret); | |
| 487 } | |
| 488 | |
| 489 void FFIGraphBuilder::PrintDebugName(Node* node) { | |
| 490 PrintF("#%d:%s", node->id(), node->op()->mnemonic()); | |
| 491 } | |
| 492 | |
| 493 Graph* FFIGraphBuilder::graph() { return jsgraph()->graph(); } | |
| 494 | |
| 495 static void RecordFunctionCompilation(Logger::LogEventsAndTags tag, | |
| 496 CompilationInfo* info, | |
| 497 const char* message, | |
| 498 Vector<const char> func_name) { | |
| 499 Isolate* isolate = info->isolate(); | |
| 500 if (isolate->logger()->is_logging_code_events() || | |
| 501 isolate->cpu_profiler()->is_profiling()) { | |
| 502 ScopedVector<char> buffer(128); | |
| 503 SNPrintF(buffer, "%s:%.*s", message, func_name.length(), func_name.start()); | |
| 504 Handle<String> name_str = | |
| 505 isolate->factory()->NewStringFromAsciiChecked(buffer.start()); | |
| 506 Handle<String> script_str = | |
| 507 isolate->factory()->NewStringFromAsciiChecked("(FFI)"); | |
| 508 Handle<Code> code = info->code(); | |
| 509 Handle<SharedFunctionInfo> shared = | |
| 510 isolate->factory()->NewSharedFunctionInfo(name_str, code, false); | |
| 511 PROFILE(isolate, CodeCreateEvent(tag, AbstractCode::cast(*code), *shared, | |
| 512 *script_str, 0, 0)); | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 // TODO(ofrobots): find a better home for this. | |
| 517 static void Setup(Isolate* isolate, Handle<Context> context) { | |
| 518 if (!context->get(Context::NATIVE_FUNCTION_MAP_INDEX)->IsMap()) { | |
| 519 // TODO(ofrobots): move this to boostrapper.cc?? | |
| 520 Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate); | |
| 521 | |
| 522 InstanceType instance_type = prev_map->instance_type(); | |
| 523 int internal_fields = JSObject::GetInternalFieldCount(*prev_map); | |
| 524 CHECK_EQ(0, internal_fields); | |
| 525 int pre_allocated = | |
| 526 prev_map->GetInObjectProperties() - prev_map->unused_property_fields(); | |
| 527 int instance_size; | |
| 528 int in_object_properties; | |
| 529 JSFunction::CalculateInstanceSizeHelper(instance_type, internal_fields, 0, | |
| 530 &instance_size, | |
| 531 &in_object_properties); | |
| 532 int unused_property_fields = in_object_properties - pre_allocated; | |
| 533 Handle<Map> map = Map::CopyInitialMap( | |
| 534 prev_map, instance_size, in_object_properties, unused_property_fields); | |
| 535 context->set_native_function_map(*map); | |
| 536 } | |
| 537 } | |
| 538 | |
| 539 Handle<JSFunction> CompileJSToNativeWrapper(Isolate* isolate, | |
| 540 Handle<String> name, | |
| 541 ffi::NativeFunction func) { | |
| 542 Handle<Context> context(isolate->context()); | |
| 543 Setup(isolate, context); | |
| 544 | |
| 545 //---------------------------------------------------------------------------- | |
| 546 // Create the JSFunction object. | |
| 547 //---------------------------------------------------------------------------- | |
| 548 Handle<SharedFunctionInfo> shared = isolate->factory()->NewSharedFunctionInfo( | |
| 549 name, MaybeHandle<Code>(), false); | |
| 550 int params = static_cast<int>(func.sig->parameter_count()); | |
| 551 shared->set_length(params); | |
| 552 shared->set_internal_formal_parameter_count(params); | |
| 553 Handle<JSFunction> function = isolate->factory()->NewFunction( | |
| 554 isolate->native_function_map(), name, MaybeHandle<Code>()); | |
| 555 // function->SetInternalField(0, *module_object); | |
| 556 function->set_shared(*shared); | |
| 557 | |
| 558 //---------------------------------------------------------------------------- | |
| 559 // Create the Graph | |
| 560 //---------------------------------------------------------------------------- | |
| 561 Zone zone(isolate->allocator()); | |
| 562 Graph graph(&zone); | |
| 563 CommonOperatorBuilder common(&zone); | |
| 564 MachineOperatorBuilder machine(&zone); | |
| 565 JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine); | |
| 566 | |
| 567 // TODO(ofrobots): we only support 64-bit at the moment | |
| 568 CHECK(machine.Is64()); | |
| 569 | |
| 570 Node* control = nullptr; | |
| 571 Node* effect = nullptr; | |
| 572 | |
| 573 FFIGraphBuilder builder(&zone, &jsgraph); | |
| 574 builder.set_control_ptr(&control); | |
| 575 builder.set_effect_ptr(&effect); | |
| 576 builder.BuildJSToNativeWrapper(func); | |
| 577 | |
| 578 //---------------------------------------------------------------------------- | |
| 579 // Run the compilation pipeline. | |
| 580 //---------------------------------------------------------------------------- | |
| 581 { | |
| 582 if (FLAG_trace_turbo_graph) { // Simple textual RPO. | |
| 583 OFStream os(stdout); | |
| 584 os << "-- Graph after change lowering -- " << std::endl; | |
| 585 os << AsRPO(graph); | |
| 586 } | |
| 587 | |
| 588 // Schedule and compile to machine code. | |
| 589 int params = static_cast<int>(func.sig->parameter_count()); | |
| 590 CallDescriptor* incoming = Linkage::GetJSCallDescriptor( | |
| 591 &zone, false, params + 1, CallDescriptor::kNoFlags); | |
| 592 Code::Flags flags = Code::ComputeFlags(Code::JS_TO_NATIVE_FUNCTION); | |
| 593 | |
| 594 int length = 0; | |
| 595 base::SmartArrayPointer<char> c_str = | |
| 596 name->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &length); | |
| 597 ScopedVector<char> buffer(32 + length); | |
| 598 int chars = SNPrintF(buffer, "js-to-native#%s", c_str.get()); | |
| 599 Vector<const char> func_name = | |
| 600 Vector<const char>::cast(buffer.SubVector(0, chars)); | |
| 601 | |
| 602 CompilationInfo info(func_name, isolate, &zone, flags); | |
| 603 Handle<Code> code = | |
| 604 Pipeline::GenerateCodeForTesting(&info, incoming, &graph); | |
| 605 #ifdef ENABLE_DISASSEMBLER | |
| 606 if (FLAG_print_opt_code && !code.is_null()) { | |
| 607 OFStream os(stdout); | |
| 608 code->Disassemble(buffer.start(), os); | |
| 609 } | |
| 610 #endif | |
| 611 | |
| 612 RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, "js-to-native", | |
| 613 func_name); | |
| 614 // Set the JSFunction's machine code. | |
| 615 function->set_code(*code); | |
| 616 } | |
| 617 return function; | |
| 618 } | |
| 619 | |
| 620 } // namespace compiler | |
| 621 } // namespace internal | |
| 622 } // namespace v8 | |
| OLD | NEW |