Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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 | |
| 6 #include "src/compiler/access-builder.h" | |
| 7 #include "src/compiler/change-lowering.h" | |
| 8 #include "src/compiler/common-operator.h" | |
| 9 #include "src/compiler/diamond.h" | |
| 10 #include "src/compiler/graph.h" | |
| 11 #include "src/compiler/graph-visualizer.h" | |
| 12 #include "src/compiler/instruction-selector.h" | |
| 13 #include "src/compiler/js-generic-lowering.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/machine-operator.h" | |
| 18 #include "src/compiler/node-matchers.h" | |
| 19 #include "src/compiler/pipeline.h" | |
| 20 #include "src/compiler/simplified-lowering.h" | |
| 21 #include "src/compiler/simplified-operator.h" | |
| 22 #include "src/compiler/source-position.h" | |
| 23 #include "src/compiler/typer.h" | |
| 24 #include "src/compiler/wasm-compiler.h" | |
| 25 | |
| 26 #include "src/code-factory.h" | |
| 27 #include "src/code-stubs.h" | |
| 28 | |
| 29 #include "src/wasm/ast-decoder.h" | |
| 30 #include "src/wasm/wasm-module.h" | |
| 31 #include "src/wasm/wasm-opcodes.h" | |
| 32 | |
| 33 // TODO(titzer): pull WASM_64 up to a common header. | |
| 34 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 | |
| 35 #define WASM_64 1 | |
| 36 #else | |
| 37 #define WASM_64 0 | |
| 38 #endif | |
| 39 | |
| 40 namespace v8 { | |
| 41 namespace internal { | |
| 42 namespace compiler { | |
| 43 | |
| 44 namespace { | |
| 45 const Operator* UnsupportedOpcode(wasm::WasmOpcode opcode) { | |
| 46 if (wasm::WasmOpcodes::IsSupported(opcode)) { | |
| 47 V8_Fatal(__FILE__, __LINE__, | |
| 48 "Unsupported opcode #%d:%s reported as supported", opcode, | |
| 49 wasm::WasmOpcodes::OpcodeName(opcode)); | |
| 50 } | |
| 51 V8_Fatal(__FILE__, __LINE__, "Unsupported opcode #%d:%s", opcode, | |
| 52 wasm::WasmOpcodes::OpcodeName(opcode)); | |
| 53 return nullptr; | |
| 54 } | |
| 55 | |
| 56 | |
| 57 void MergeControlToEnd(JSGraph* graph, Node* node) { | |
| 58 Graph* g = graph->graph(); | |
| 59 if (g->end()) { | |
| 60 NodeProperties::MergeControlToEnd(g, graph->common(), node); | |
| 61 } else { | |
| 62 g->SetEnd(g->NewNode(graph->common()->End(1), node)); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 | |
| 67 enum TrapReason { | |
| 68 kTrapUnreachable, | |
| 69 kTrapMemOutOfBounds, | |
| 70 kTrapDivByZero, | |
| 71 kTrapDivUnrepresentable, | |
| 72 kTrapRemByZero, | |
| 73 kTrapFloatUnrepresentable, | |
| 74 kTrapFuncInvalid, | |
| 75 kTrapFuncSigMismatch, | |
| 76 kTrapCount | |
| 77 }; | |
| 78 | |
| 79 | |
| 80 static const char* kTrapMessages[] = { | |
| 81 "unreachable", "memory access out of bounds", | |
| 82 "divide by zero", "divide result unrepresentable", | |
| 83 "remainder by zero", "integer result unrepresentable", | |
| 84 "invalid function", "function signature mismatch"}; | |
| 85 } // namespace | |
| 86 | |
| 87 | |
| 88 // A helper that handles building graph fragments for trapping. | |
| 89 // To avoid generating a ton of redundant code that just calls the runtime | |
| 90 // to trap, we generate a per-trap-reason block of code that all trap sites | |
| 91 // in this function will branch to. | |
| 92 class WasmTrapHelper : public ZoneObject { | |
| 93 public: | |
| 94 explicit WasmTrapHelper(WasmGraphBuilder* b) | |
| 95 : builder(b), graph(b->graph), g(b->graph ? b->graph->graph() : nullptr) { | |
| 96 for (int i = 0; i < kTrapCount; i++) traps[i] = nullptr; | |
| 97 } | |
| 98 | |
| 99 // Make the current control path trap to unreachable. | |
| 100 void Unreachable() { ConnectTrap(kTrapUnreachable); } | |
| 101 // Add a check that traps if {node} is equal to {val}. | |
| 102 Node* TrapIfEq32(TrapReason reason, Node* node, int32_t val) { | |
| 103 Int32Matcher m(node); | |
| 104 if (m.HasValue() && !m.Is(val)) return g->start(); | |
| 105 if (val == 0) { | |
| 106 AddTrapIfFalse(reason, node); | |
| 107 } else { | |
| 108 AddTrapIfTrue(reason, g->NewNode(graph->machine()->Word32Equal(), node, | |
| 109 graph->Int32Constant(val))); | |
| 110 } | |
| 111 return *(builder->control); | |
| 112 } | |
| 113 // Add a check that traps if {node} is zero. | |
| 114 Node* ZeroCheck32(TrapReason reason, Node* node) { | |
| 115 return TrapIfEq32(reason, node, 0); | |
| 116 } | |
| 117 // Add a check that traps if {node} is equal to {val}. | |
| 118 Node* TrapIfEq64(TrapReason reason, Node* node, int64_t val) { | |
| 119 Int64Matcher m(node); | |
| 120 if (m.HasValue() && !m.Is(val)) return g->start(); | |
| 121 AddTrapIfTrue(reason, g->NewNode(graph->machine()->Word64Equal(), node, | |
| 122 graph->Int64Constant(val))); | |
| 123 return *(builder->control); | |
| 124 } | |
| 125 // Add a check that traps if {node} is zero. | |
| 126 Node* ZeroCheck64(TrapReason reason, Node* node) { | |
| 127 return TrapIfEq64(reason, node, 0); | |
| 128 } | |
| 129 // Add a trap if {cond} is true. | |
| 130 void AddTrapIfTrue(TrapReason reason, Node* cond) { | |
| 131 AddTrapIf(reason, cond, true); | |
| 132 } | |
| 133 // Add a trap if {cond} is false. | |
| 134 void AddTrapIfFalse(TrapReason reason, Node* cond) { | |
| 135 AddTrapIf(reason, cond, false); | |
| 136 } | |
| 137 // Add a trap if {cond} is true or false according to {iftrue}. | |
| 138 void AddTrapIf(TrapReason reason, Node* cond, bool iftrue) { | |
| 139 DCHECK_NOT_NULL(graph); | |
| 140 Node** effect = builder->effect; | |
| 141 Node** control = builder->control; | |
| 142 Node* before = *effect; | |
| 143 BranchHint hint = iftrue ? BranchHint::kFalse : BranchHint::kTrue; | |
| 144 Node* branch = | |
| 145 g->NewNode(graph->common()->Branch(hint), cond, *(builder->control)); | |
| 146 Node* if_true = g->NewNode(graph->common()->IfTrue(), branch); | |
| 147 Node* if_false = g->NewNode(graph->common()->IfFalse(), branch); | |
| 148 | |
| 149 *control = iftrue ? if_true : if_false; | |
| 150 ConnectTrap(reason); | |
| 151 *control = iftrue ? if_false : if_true; | |
| 152 *effect = before; | |
| 153 } | |
| 154 | |
| 155 private: | |
| 156 WasmGraphBuilder* builder; | |
|
Michael Starzinger
2015/12/11 09:15:14
nit: Fields don't follow the style guide, missing
titzer
2015/12/11 10:16:20
Done.
| |
| 157 JSGraph* graph; | |
| 158 Graph* g; | |
| 159 Node* traps[kTrapCount]; | |
| 160 Node* effects[kTrapCount]; | |
| 161 | |
| 162 void ConnectTrap(TrapReason reason) { | |
| 163 if (traps[reason] == nullptr) { | |
| 164 // Create trap code for the first time this trap is used. | |
| 165 return BuildTrapCode(reason); | |
| 166 } | |
| 167 // Connect the current control and effect to the existing trap code. | |
| 168 builder->AppendToMerge(traps[reason], *(builder->control)); | |
| 169 builder->AppendToPhi(traps[reason], effects[reason], *(builder->effect)); | |
| 170 } | |
| 171 | |
| 172 void BuildTrapCode(TrapReason reason) { | |
| 173 Node* exception = builder->String(kTrapMessages[reason]); | |
| 174 Node* end; | |
| 175 Node** control = builder->control; | |
| 176 Node** effect = builder->effect; | |
| 177 wasm::ModuleEnv* module = builder->module; | |
| 178 *control = traps[reason] = g->NewNode(graph->common()->Merge(1), *control); | |
| 179 *effect = effects[reason] = | |
| 180 g->NewNode(graph->common()->EffectPhi(1), *effect, *control); | |
| 181 | |
| 182 if (module && !module->context.is_null()) { | |
| 183 // Use the module context to call the runtime to throw an exception. | |
| 184 Runtime::FunctionId f = Runtime::kThrow; | |
| 185 const Runtime::Function* fun = Runtime::FunctionForId(f); | |
| 186 CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor( | |
| 187 graph->zone(), f, fun->nargs, Operator::kNoProperties, | |
| 188 CallDescriptor::kNoFlags); | |
| 189 Node* inputs[] = {graph->CEntryStubConstant(fun->result_size), // C entry | |
| 190 exception, // exception | |
| 191 graph->ExternalConstant( | |
| 192 ExternalReference(f, graph->isolate())), // ref | |
| 193 graph->Int32Constant(fun->nargs), // arity | |
| 194 graph->Constant(module->context), // context | |
| 195 *effect, | |
| 196 *control}; | |
| 197 | |
| 198 Node* node = g->NewNode(graph->common()->Call(desc), | |
| 199 static_cast<int>(arraysize(inputs)), inputs); | |
| 200 *control = node; | |
| 201 *effect = node; | |
| 202 } | |
| 203 if (false) { | |
| 204 // End the control flow with a throw | |
| 205 Node* thrw = g->NewNode(graph->common()->Throw(), graph->ZeroConstant(), | |
| 206 *effect, *control); | |
| 207 end = thrw; | |
| 208 } else { | |
| 209 // End the control flow with returning 0xdeadbeef | |
| 210 Node* ret_dead = | |
| 211 g->NewNode(graph->common()->Return(), | |
| 212 graph->Int32Constant(0xdeadbeef), *effect, *control); | |
| 213 end = ret_dead; | |
| 214 } | |
| 215 | |
| 216 MergeControlToEnd(graph, end); | |
| 217 } | |
| 218 }; | |
| 219 | |
| 220 | |
| 221 WasmGraphBuilder::WasmGraphBuilder(Zone* z, JSGraph* g) | |
| 222 : zone(z), | |
| 223 graph(g), | |
| 224 module(nullptr), | |
| 225 mem_buffer(nullptr), | |
| 226 mem_size(nullptr), | |
| 227 function_table(nullptr), | |
| 228 control(nullptr), | |
| 229 effect(nullptr), | |
| 230 cur_buffer(def_buffer), | |
| 231 cur_bufsize(kDefaultBufferSize), | |
| 232 trap(new (z) WasmTrapHelper(this)) {} | |
| 233 | |
| 234 Node* WasmGraphBuilder::Error() { | |
| 235 DCHECK_NOT_NULL(graph); | |
| 236 return graph->Dead(); | |
| 237 } | |
| 238 | |
| 239 | |
| 240 Node* WasmGraphBuilder::Start(unsigned params) { | |
| 241 DCHECK_NOT_NULL(graph); | |
| 242 Graph* g = graph->graph(); | |
| 243 Node* start = g->NewNode(graph->common()->Start(params)); | |
| 244 g->SetStart(start); | |
| 245 return start; | |
| 246 } | |
| 247 | |
| 248 | |
| 249 Node* WasmGraphBuilder::Param(unsigned index, wasm::LocalType type) { | |
| 250 DCHECK_NOT_NULL(graph); | |
| 251 Graph* g = graph->graph(); | |
| 252 return g->NewNode(graph->common()->Parameter(index), g->start()); | |
| 253 } | |
| 254 | |
| 255 | |
| 256 Node* WasmGraphBuilder::Loop(Node* entry) { | |
| 257 DCHECK_NOT_NULL(graph); | |
| 258 return graph->graph()->NewNode(graph->common()->Loop(1), entry); | |
| 259 } | |
| 260 | |
| 261 | |
| 262 Node* WasmGraphBuilder::Terminate(Node* effect, Node* control) { | |
| 263 DCHECK_NOT_NULL(graph); | |
| 264 Node* terminate = | |
| 265 graph->graph()->NewNode(graph->common()->Terminate(), effect, control); | |
| 266 MergeControlToEnd(graph, terminate); | |
| 267 return terminate; | |
| 268 } | |
| 269 | |
| 270 | |
| 271 unsigned WasmGraphBuilder::InputCount(Node* node) { | |
| 272 return static_cast<unsigned>(node->InputCount()); | |
| 273 } | |
| 274 | |
| 275 | |
| 276 bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) { | |
| 277 return phi && IrOpcode::IsPhiOpcode(phi->opcode()) && | |
| 278 NodeProperties::GetControlInput(phi) == merge; | |
| 279 } | |
| 280 | |
| 281 | |
| 282 void WasmGraphBuilder::AppendToMerge(Node* merge, Node* from) { | |
| 283 DCHECK_NOT_NULL(graph); | |
| 284 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode())); | |
| 285 merge->AppendInput(graph->zone(), from); | |
| 286 int new_size = merge->InputCount(); | |
| 287 NodeProperties::ChangeOp( | |
| 288 merge, graph->common()->ResizeMergeOrPhi(merge->op(), new_size)); | |
| 289 } | |
| 290 | |
| 291 | |
| 292 void WasmGraphBuilder::AppendToPhi(Node* merge, Node* phi, Node* from) { | |
| 293 DCHECK_NOT_NULL(graph); | |
| 294 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode())); | |
| 295 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode())); | |
| 296 int new_size = phi->InputCount(); | |
| 297 phi->InsertInput(graph->zone(), phi->InputCount() - 1, from); | |
| 298 NodeProperties::ChangeOp( | |
| 299 phi, graph->common()->ResizeMergeOrPhi(phi->op(), new_size)); | |
| 300 } | |
| 301 | |
| 302 | |
| 303 Node* WasmGraphBuilder::Merge(unsigned count, Node** controls) { | |
| 304 DCHECK_NOT_NULL(graph); | |
| 305 return graph->graph()->NewNode(graph->common()->Merge(count), count, | |
| 306 controls); | |
| 307 } | |
| 308 | |
| 309 | |
| 310 Node* WasmGraphBuilder::Phi(wasm::LocalType type, unsigned count, Node** vals, | |
| 311 Node* control) { | |
| 312 DCHECK(IrOpcode::IsMergeOpcode(control->opcode())); | |
| 313 Node** buf = Realloc(vals, count + 1); | |
| 314 buf[count] = control; | |
| 315 return graph->graph()->NewNode(graph->common()->Phi(type, count), count + 1, | |
| 316 buf); | |
| 317 } | |
| 318 | |
| 319 | |
| 320 Node* WasmGraphBuilder::EffectPhi(unsigned count, Node** effects, | |
| 321 Node* control) { | |
| 322 DCHECK_NOT_NULL(graph); | |
| 323 DCHECK(IrOpcode::IsMergeOpcode(control->opcode())); | |
| 324 Node** buf = Realloc(effects, count + 1); | |
| 325 buf[count] = control; | |
| 326 return graph->graph()->NewNode(graph->common()->EffectPhi(count), count + 1, | |
| 327 buf); | |
| 328 } | |
| 329 | |
| 330 | |
| 331 Node* WasmGraphBuilder::Int32Constant(int32_t value) { | |
| 332 DCHECK_NOT_NULL(graph); | |
| 333 return graph->Int32Constant(value); | |
| 334 } | |
| 335 | |
| 336 | |
| 337 Node* WasmGraphBuilder::Int64Constant(int64_t value) { | |
| 338 DCHECK_NOT_NULL(graph); | |
| 339 return graph->Int64Constant(value); | |
| 340 } | |
| 341 | |
| 342 | |
| 343 Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, | |
| 344 Node* right) { | |
| 345 DCHECK_NOT_NULL(graph); | |
| 346 const Operator* op; | |
| 347 MachineOperatorBuilder* m = graph->machine(); | |
| 348 switch (opcode) { | |
| 349 case wasm::kExprI32Add: | |
| 350 op = m->Int32Add(); | |
| 351 break; | |
| 352 case wasm::kExprI32Sub: | |
| 353 op = m->Int32Sub(); | |
| 354 break; | |
| 355 case wasm::kExprI32Mul: | |
| 356 op = m->Int32Mul(); | |
| 357 break; | |
| 358 case wasm::kExprI32DivS: { | |
| 359 trap->ZeroCheck32(kTrapDivByZero, right); | |
| 360 Node* before = *control; | |
| 361 Node* denom_is_m1; | |
| 362 Node* denom_is_not_m1; | |
| 363 Branch(graph->graph()->NewNode(graph->machine()->Word32Equal(), right, | |
| 364 graph->Int32Constant(-1)), | |
| 365 &denom_is_m1, &denom_is_not_m1); | |
| 366 *control = denom_is_m1; | |
| 367 trap->TrapIfEq32(kTrapDivUnrepresentable, left, kMinInt); | |
| 368 if (*control != denom_is_m1) { | |
| 369 *control = graph->graph()->NewNode(graph->common()->Merge(2), | |
| 370 denom_is_not_m1, *control); | |
| 371 } else { | |
| 372 *control = before; | |
| 373 } | |
| 374 return graph->graph()->NewNode(m->Int32Div(), left, right, *control); | |
| 375 } | |
| 376 case wasm::kExprI32DivU: | |
| 377 op = m->Uint32Div(); | |
| 378 return graph->graph()->NewNode(op, left, right, | |
| 379 trap->ZeroCheck32(kTrapDivByZero, right)); | |
| 380 case wasm::kExprI32RemS: { | |
| 381 trap->ZeroCheck32(kTrapRemByZero, right); | |
| 382 Diamond d(graph->graph(), graph->common(), | |
| 383 graph->graph()->NewNode(graph->machine()->Word32Equal(), right, | |
| 384 graph->Int32Constant(-1))); | |
| 385 | |
| 386 Node* rem = | |
| 387 graph->graph()->NewNode(m->Int32Mod(), left, right, d.if_false); | |
| 388 | |
| 389 return d.Phi(MachineRepresentation::kWord32, graph->Int32Constant(0), | |
| 390 rem); | |
| 391 } | |
| 392 case wasm::kExprI32RemU: | |
| 393 op = m->Uint32Mod(); | |
| 394 return graph->graph()->NewNode(op, left, right, | |
| 395 trap->ZeroCheck32(kTrapRemByZero, right)); | |
| 396 case wasm::kExprI32And: | |
| 397 op = m->Word32And(); | |
| 398 break; | |
| 399 case wasm::kExprI32Ior: | |
| 400 op = m->Word32Or(); | |
| 401 break; | |
| 402 case wasm::kExprI32Xor: | |
| 403 op = m->Word32Xor(); | |
| 404 break; | |
| 405 case wasm::kExprI32Shl: | |
| 406 op = m->Word32Shl(); | |
| 407 break; | |
| 408 case wasm::kExprI32ShrU: | |
| 409 op = m->Word32Shr(); | |
| 410 break; | |
| 411 case wasm::kExprI32ShrS: | |
| 412 op = m->Word32Sar(); | |
| 413 break; | |
| 414 case wasm::kExprI32Eq: | |
| 415 op = m->Word32Equal(); | |
| 416 break; | |
| 417 case wasm::kExprI32Ne: | |
| 418 return Invert(Binop(wasm::kExprI32Eq, left, right)); | |
| 419 case wasm::kExprI32LtS: | |
| 420 op = m->Int32LessThan(); | |
| 421 break; | |
| 422 case wasm::kExprI32LeS: | |
| 423 op = m->Int32LessThanOrEqual(); | |
| 424 break; | |
| 425 case wasm::kExprI32LtU: | |
| 426 op = m->Uint32LessThan(); | |
| 427 break; | |
| 428 case wasm::kExprI32LeU: | |
| 429 op = m->Uint32LessThanOrEqual(); | |
| 430 break; | |
| 431 case wasm::kExprI32GtS: | |
| 432 op = m->Int32LessThan(); | |
| 433 std::swap(left, right); | |
| 434 break; | |
| 435 case wasm::kExprI32GeS: | |
| 436 op = m->Int32LessThanOrEqual(); | |
| 437 std::swap(left, right); | |
| 438 break; | |
| 439 case wasm::kExprI32GtU: | |
| 440 op = m->Uint32LessThan(); | |
| 441 std::swap(left, right); | |
| 442 break; | |
| 443 case wasm::kExprI32GeU: | |
| 444 op = m->Uint32LessThanOrEqual(); | |
| 445 std::swap(left, right); | |
| 446 break; | |
| 447 #if WASM_64 | |
| 448 // Opcodes only supported on 64-bit platforms. | |
| 449 // TODO(titzer): query the machine operator builder here instead of #ifdef. | |
| 450 case wasm::kExprI64Add: | |
| 451 op = m->Int64Add(); | |
| 452 break; | |
| 453 case wasm::kExprI64Sub: | |
| 454 op = m->Int64Sub(); | |
| 455 break; | |
| 456 case wasm::kExprI64Mul: | |
| 457 op = m->Int64Mul(); | |
| 458 break; | |
| 459 case wasm::kExprI64DivS: { | |
| 460 trap->ZeroCheck64(kTrapDivByZero, right); | |
| 461 Node* before = *control; | |
| 462 Node* denom_is_m1; | |
| 463 Node* denom_is_not_m1; | |
| 464 Branch(graph->graph()->NewNode(graph->machine()->Word64Equal(), right, | |
| 465 graph->Int64Constant(-1)), | |
| 466 &denom_is_m1, &denom_is_not_m1); | |
| 467 *control = denom_is_m1; | |
| 468 trap->TrapIfEq64(kTrapDivUnrepresentable, left, | |
| 469 std::numeric_limits<int64_t>::min()); | |
| 470 if (*control != denom_is_m1) { | |
| 471 *control = graph->graph()->NewNode(graph->common()->Merge(2), | |
| 472 denom_is_not_m1, *control); | |
| 473 } else { | |
| 474 *control = before; | |
| 475 } | |
| 476 return graph->graph()->NewNode(m->Int64Div(), left, right, *control); | |
| 477 } | |
| 478 case wasm::kExprI64DivU: | |
| 479 op = m->Uint64Div(); | |
| 480 return graph->graph()->NewNode(op, left, right, | |
| 481 trap->ZeroCheck64(kTrapDivByZero, right)); | |
| 482 case wasm::kExprI64RemS: { | |
| 483 trap->ZeroCheck64(kTrapRemByZero, right); | |
| 484 Diamond d(graph->graph(), graph->common(), | |
| 485 graph->graph()->NewNode(graph->machine()->Word64Equal(), right, | |
| 486 graph->Int64Constant(-1))); | |
| 487 | |
| 488 Node* rem = | |
| 489 graph->graph()->NewNode(m->Int64Mod(), left, right, d.if_false); | |
| 490 | |
| 491 return d.Phi(MachineRepresentation::kWord64, graph->Int64Constant(0), | |
| 492 rem); | |
| 493 } | |
| 494 case wasm::kExprI64RemU: | |
| 495 op = m->Uint64Mod(); | |
| 496 return graph->graph()->NewNode(op, left, right, | |
| 497 trap->ZeroCheck64(kTrapRemByZero, right)); | |
| 498 case wasm::kExprI64And: | |
| 499 op = m->Word64And(); | |
| 500 break; | |
| 501 case wasm::kExprI64Ior: | |
| 502 op = m->Word64Or(); | |
| 503 break; | |
| 504 case wasm::kExprI64Xor: | |
| 505 op = m->Word64Xor(); | |
| 506 break; | |
| 507 case wasm::kExprI64Shl: | |
| 508 op = m->Word64Shl(); | |
| 509 break; | |
| 510 case wasm::kExprI64ShrU: | |
| 511 op = m->Word64Shr(); | |
| 512 break; | |
| 513 case wasm::kExprI64ShrS: | |
| 514 op = m->Word64Sar(); | |
| 515 break; | |
| 516 case wasm::kExprI64Eq: | |
| 517 op = m->Word64Equal(); | |
| 518 break; | |
| 519 case wasm::kExprI64Ne: | |
| 520 return Invert(Binop(wasm::kExprI64Eq, left, right)); | |
| 521 case wasm::kExprI64LtS: | |
| 522 op = m->Int64LessThan(); | |
| 523 break; | |
| 524 case wasm::kExprI64LeS: | |
| 525 op = m->Int64LessThanOrEqual(); | |
| 526 break; | |
| 527 case wasm::kExprI64LtU: | |
| 528 op = m->Uint64LessThan(); | |
| 529 break; | |
| 530 case wasm::kExprI64LeU: | |
| 531 op = m->Uint64LessThanOrEqual(); | |
| 532 break; | |
| 533 case wasm::kExprI64GtS: | |
| 534 op = m->Int64LessThan(); | |
| 535 std::swap(left, right); | |
| 536 break; | |
| 537 case wasm::kExprI64GeS: | |
| 538 op = m->Int64LessThanOrEqual(); | |
| 539 std::swap(left, right); | |
| 540 break; | |
| 541 case wasm::kExprI64GtU: | |
| 542 op = m->Uint64LessThan(); | |
| 543 std::swap(left, right); | |
| 544 break; | |
| 545 case wasm::kExprI64GeU: | |
| 546 op = m->Uint64LessThanOrEqual(); | |
| 547 std::swap(left, right); | |
| 548 break; | |
| 549 #endif | |
| 550 | |
| 551 case wasm::kExprF32CopySign: | |
| 552 return BuildF32CopySign(left, right); | |
| 553 case wasm::kExprF64CopySign: | |
| 554 return BuildF64CopySign(left, right); | |
| 555 case wasm::kExprF32Add: | |
| 556 op = m->Float32Add(); | |
| 557 break; | |
| 558 case wasm::kExprF32Sub: | |
| 559 op = m->Float32Sub(); | |
| 560 break; | |
| 561 case wasm::kExprF32Mul: | |
| 562 op = m->Float32Mul(); | |
| 563 break; | |
| 564 case wasm::kExprF32Div: | |
| 565 op = m->Float32Div(); | |
| 566 break; | |
| 567 case wasm::kExprF32Eq: | |
| 568 op = m->Float32Equal(); | |
| 569 break; | |
| 570 case wasm::kExprF32Ne: | |
| 571 return Invert(Binop(wasm::kExprF32Eq, left, right)); | |
| 572 case wasm::kExprF32Lt: | |
| 573 op = m->Float32LessThan(); | |
| 574 break; | |
| 575 case wasm::kExprF32Ge: | |
| 576 op = m->Float32LessThanOrEqual(); | |
| 577 std::swap(left, right); | |
| 578 break; | |
| 579 case wasm::kExprF32Gt: | |
| 580 op = m->Float32LessThan(); | |
| 581 std::swap(left, right); | |
| 582 break; | |
| 583 case wasm::kExprF32Le: | |
| 584 op = m->Float32LessThanOrEqual(); | |
| 585 break; | |
| 586 case wasm::kExprF64Add: | |
| 587 op = m->Float64Add(); | |
| 588 break; | |
| 589 case wasm::kExprF64Sub: | |
| 590 op = m->Float64Sub(); | |
| 591 break; | |
| 592 case wasm::kExprF64Mul: | |
| 593 op = m->Float64Mul(); | |
| 594 break; | |
| 595 case wasm::kExprF64Div: | |
| 596 op = m->Float64Div(); | |
| 597 break; | |
| 598 case wasm::kExprF64Eq: | |
| 599 op = m->Float64Equal(); | |
| 600 break; | |
| 601 case wasm::kExprF64Ne: | |
| 602 return Invert(Binop(wasm::kExprF64Eq, left, right)); | |
| 603 case wasm::kExprF64Lt: | |
| 604 op = m->Float64LessThan(); | |
| 605 break; | |
| 606 case wasm::kExprF64Le: | |
| 607 op = m->Float64LessThanOrEqual(); | |
| 608 break; | |
| 609 case wasm::kExprF64Gt: | |
| 610 op = m->Float64LessThan(); | |
| 611 std::swap(left, right); | |
| 612 break; | |
| 613 case wasm::kExprF64Ge: | |
| 614 op = m->Float64LessThanOrEqual(); | |
| 615 std::swap(left, right); | |
| 616 break; | |
| 617 case wasm::kExprF32Min: { | |
| 618 if (m->Float32Min().IsSupported()) { | |
| 619 op = m->Float32Min().op(); | |
| 620 break; | |
| 621 } else { | |
| 622 op = UnsupportedOpcode(opcode); | |
| 623 break; | |
| 624 } | |
| 625 } | |
| 626 case wasm::kExprF64Min: { | |
| 627 if (m->Float64Min().IsSupported()) { | |
| 628 op = m->Float64Min().op(); | |
| 629 break; | |
| 630 } else { | |
| 631 op = UnsupportedOpcode(opcode); | |
| 632 break; | |
| 633 } | |
| 634 } | |
| 635 case wasm::kExprF32Max: { | |
| 636 if (m->Float32Max().IsSupported()) { | |
| 637 op = m->Float32Max().op(); | |
| 638 break; | |
| 639 } else { | |
| 640 op = UnsupportedOpcode(opcode); | |
| 641 break; | |
| 642 } | |
| 643 } | |
| 644 case wasm::kExprF64Max: { | |
| 645 if (m->Float64Max().IsSupported()) { | |
| 646 op = m->Float64Max().op(); | |
| 647 break; | |
| 648 } else { | |
| 649 op = UnsupportedOpcode(opcode); | |
| 650 break; | |
| 651 } | |
| 652 } | |
| 653 default: | |
| 654 op = UnsupportedOpcode(opcode); | |
| 655 } | |
| 656 return graph->graph()->NewNode(op, left, right); | |
| 657 } | |
| 658 | |
| 659 | |
| 660 Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input) { | |
| 661 DCHECK_NOT_NULL(graph); | |
| 662 const Operator* op; | |
| 663 MachineOperatorBuilder* m = graph->machine(); | |
| 664 switch (opcode) { | |
| 665 case wasm::kExprBoolNot: | |
| 666 op = m->Word32Equal(); | |
| 667 return graph->graph()->NewNode(op, input, graph->Int32Constant(0)); | |
| 668 case wasm::kExprF32Abs: | |
| 669 op = m->Float32Abs(); | |
| 670 break; | |
| 671 case wasm::kExprF32Neg: | |
| 672 op = m->Float32Sub(); | |
| 673 return graph->graph()->NewNode(op, graph->Float32Constant(0), input); | |
| 674 case wasm::kExprF32Sqrt: | |
| 675 op = m->Float32Sqrt(); | |
| 676 break; | |
| 677 case wasm::kExprF64Abs: | |
| 678 op = m->Float64Abs(); | |
| 679 break; | |
| 680 case wasm::kExprF64Neg: | |
| 681 op = m->Float64Sub(); | |
| 682 return graph->graph()->NewNode(op, graph->Float64Constant(0), input); | |
| 683 case wasm::kExprF64Sqrt: | |
| 684 op = m->Float64Sqrt(); | |
| 685 break; | |
| 686 case wasm::kExprI32SConvertF64: | |
| 687 op = m->ChangeFloat64ToInt32(); | |
| 688 break; | |
| 689 case wasm::kExprI32UConvertF64: | |
| 690 op = m->ChangeFloat64ToUint32(); | |
| 691 break; | |
| 692 case wasm::kExprF32ConvertF64: | |
| 693 op = m->TruncateFloat64ToFloat32(); | |
| 694 break; | |
| 695 case wasm::kExprF64SConvertI32: | |
| 696 op = m->ChangeInt32ToFloat64(); | |
| 697 break; | |
| 698 case wasm::kExprF64UConvertI32: | |
| 699 op = m->ChangeUint32ToFloat64(); | |
| 700 break; | |
| 701 case wasm::kExprF32SConvertI32: | |
| 702 op = m->ChangeInt32ToFloat64(); // TODO(titzer): two conversions | |
| 703 input = graph->graph()->NewNode(op, input); | |
| 704 op = m->TruncateFloat64ToFloat32(); | |
| 705 break; | |
| 706 case wasm::kExprF32UConvertI32: | |
| 707 op = m->ChangeUint32ToFloat64(); // TODO(titzer): two conversions | |
| 708 input = graph->graph()->NewNode(op, input); | |
| 709 op = m->TruncateFloat64ToFloat32(); | |
| 710 break; | |
| 711 case wasm::kExprI32SConvertF32: | |
| 712 op = m->ChangeFloat32ToFloat64(); // TODO(titzer): two conversions | |
| 713 input = graph->graph()->NewNode(op, input); | |
| 714 op = m->ChangeFloat64ToInt32(); | |
| 715 break; | |
| 716 case wasm::kExprI32UConvertF32: | |
| 717 op = m->ChangeFloat32ToFloat64(); // TODO(titzer): two conversions | |
| 718 input = graph->graph()->NewNode(op, input); | |
| 719 op = m->ChangeFloat64ToUint32(); | |
| 720 break; | |
| 721 case wasm::kExprF64ConvertF32: | |
| 722 op = m->ChangeFloat32ToFloat64(); | |
| 723 break; | |
| 724 case wasm::kExprF32ReinterpretI32: | |
| 725 op = m->BitcastInt32ToFloat32(); | |
| 726 break; | |
| 727 case wasm::kExprI32ReinterpretF32: | |
| 728 op = m->BitcastFloat32ToInt32(); | |
| 729 break; | |
| 730 case wasm::kExprI32Clz: | |
| 731 op = m->Word32Clz(); | |
| 732 break; | |
| 733 case wasm::kExprI32Ctz: { | |
| 734 if (m->Word32Ctz().IsSupported()) { | |
| 735 op = m->Word32Ctz().op(); | |
| 736 break; | |
| 737 } else { | |
| 738 return BuildI32Ctz(input); | |
| 739 } | |
| 740 } | |
| 741 case wasm::kExprI32Popcnt: { | |
| 742 if (m->Word32Popcnt().IsSupported()) { | |
| 743 op = m->Word32Popcnt().op(); | |
| 744 break; | |
| 745 } else { | |
| 746 return BuildI32Popcnt(input); | |
| 747 } | |
| 748 } | |
| 749 case wasm::kExprF32Floor: { | |
| 750 if (m->Float32RoundDown().IsSupported()) { | |
| 751 op = m->Float32RoundDown().op(); | |
| 752 break; | |
| 753 } else { | |
| 754 op = UnsupportedOpcode(opcode); | |
| 755 break; | |
| 756 } | |
| 757 } | |
| 758 case wasm::kExprF32Ceil: { | |
| 759 if (m->Float32RoundUp().IsSupported()) { | |
| 760 op = m->Float32RoundUp().op(); | |
| 761 break; | |
| 762 } else { | |
| 763 op = UnsupportedOpcode(opcode); | |
| 764 break; | |
| 765 } | |
| 766 } | |
| 767 case wasm::kExprF32Trunc: { | |
| 768 if (m->Float32RoundTruncate().IsSupported()) { | |
| 769 op = m->Float32RoundTruncate().op(); | |
| 770 break; | |
| 771 } else { | |
| 772 op = UnsupportedOpcode(opcode); | |
| 773 break; | |
| 774 } | |
| 775 } | |
| 776 case wasm::kExprF32NearestInt: { | |
| 777 if (m->Float32RoundTiesEven().IsSupported()) { | |
| 778 op = m->Float32RoundTiesEven().op(); | |
| 779 break; | |
| 780 } else { | |
| 781 op = UnsupportedOpcode(opcode); | |
| 782 break; | |
| 783 } | |
| 784 } | |
| 785 case wasm::kExprF64Floor: { | |
| 786 if (m->Float64RoundDown().IsSupported()) { | |
| 787 op = m->Float64RoundDown().op(); | |
| 788 break; | |
| 789 } else { | |
| 790 op = UnsupportedOpcode(opcode); | |
| 791 break; | |
| 792 } | |
| 793 } | |
| 794 case wasm::kExprF64Ceil: { | |
| 795 if (m->Float64RoundUp().IsSupported()) { | |
| 796 op = m->Float64RoundUp().op(); | |
| 797 break; | |
| 798 } else { | |
| 799 op = UnsupportedOpcode(opcode); | |
| 800 break; | |
| 801 } | |
| 802 } | |
| 803 case wasm::kExprF64Trunc: { | |
| 804 if (m->Float64RoundTruncate().IsSupported()) { | |
| 805 op = m->Float64RoundTruncate().op(); | |
| 806 break; | |
| 807 } else { | |
| 808 op = UnsupportedOpcode(opcode); | |
| 809 break; | |
| 810 } | |
| 811 } | |
| 812 case wasm::kExprF64NearestInt: { | |
| 813 if (m->Float64RoundTiesEven().IsSupported()) { | |
| 814 op = m->Float64RoundTiesEven().op(); | |
| 815 break; | |
| 816 } else { | |
| 817 op = UnsupportedOpcode(opcode); | |
| 818 break; | |
| 819 } | |
| 820 } | |
| 821 | |
| 822 #if WASM_64 | |
| 823 // Opcodes only supported on 64-bit platforms. | |
| 824 // TODO(titzer): query the machine operator builder here instead of #ifdef. | |
| 825 case wasm::kExprI32ConvertI64: | |
| 826 op = m->TruncateInt64ToInt32(); | |
| 827 break; | |
| 828 case wasm::kExprI64SConvertI32: | |
| 829 op = m->ChangeInt32ToInt64(); | |
| 830 break; | |
| 831 case wasm::kExprI64UConvertI32: | |
| 832 op = m->ChangeUint32ToUint64(); | |
| 833 break; | |
| 834 case wasm::kExprF32SConvertI64: | |
| 835 op = m->RoundInt64ToFloat32(); | |
| 836 break; | |
| 837 case wasm::kExprF32UConvertI64: | |
| 838 op = m->RoundUint64ToFloat32(); | |
| 839 break; | |
| 840 case wasm::kExprF64SConvertI64: | |
| 841 op = m->RoundInt64ToFloat64(); | |
| 842 break; | |
| 843 case wasm::kExprF64UConvertI64: | |
| 844 op = m->RoundUint64ToFloat64(); | |
| 845 break; | |
| 846 case wasm::kExprF64ReinterpretI64: | |
| 847 op = m->BitcastInt64ToFloat64(); | |
| 848 break; | |
| 849 case wasm::kExprI64ReinterpretF64: | |
| 850 op = m->BitcastFloat64ToInt64(); | |
| 851 break; | |
| 852 case wasm::kExprI64Clz: | |
| 853 op = m->Word64Clz(); | |
| 854 break; | |
| 855 case wasm::kExprI64Ctz: { | |
| 856 if (m->Word64Ctz().IsSupported()) { | |
| 857 op = m->Word64Ctz().op(); | |
| 858 break; | |
| 859 } else { | |
| 860 return BuildI64Ctz(input); | |
| 861 } | |
| 862 } | |
| 863 case wasm::kExprI64Popcnt: { | |
| 864 if (m->Word64Popcnt().IsSupported()) { | |
| 865 op = m->Word64Popcnt().op(); | |
| 866 break; | |
| 867 } else { | |
| 868 return BuildI64Popcnt(input); | |
| 869 } | |
| 870 } | |
| 871 #endif | |
| 872 default: | |
| 873 op = UnsupportedOpcode(opcode); | |
| 874 } | |
| 875 return graph->graph()->NewNode(op, input); | |
| 876 } | |
| 877 | |
| 878 | |
| 879 Node* WasmGraphBuilder::Float32Constant(float value) { | |
| 880 DCHECK_NOT_NULL(graph); | |
| 881 return graph->Float32Constant(value); | |
| 882 } | |
| 883 | |
| 884 | |
| 885 Node* WasmGraphBuilder::Float64Constant(double value) { | |
| 886 DCHECK_NOT_NULL(graph); | |
| 887 return graph->Float64Constant(value); | |
| 888 } | |
| 889 | |
| 890 | |
| 891 Node* WasmGraphBuilder::Constant(Handle<Object> value) { | |
| 892 DCHECK_NOT_NULL(graph); | |
| 893 return graph->Constant(value); | |
| 894 } | |
| 895 | |
| 896 | |
| 897 Node* WasmGraphBuilder::Branch(Node* cond, Node** true_node, | |
| 898 Node** false_node) { | |
| 899 DCHECK_NOT_NULL(graph); | |
| 900 DCHECK_NOT_NULL(cond); | |
| 901 DCHECK_NOT_NULL(*control); | |
| 902 Node* branch = | |
| 903 graph->graph()->NewNode(graph->common()->Branch(), cond, *control); | |
| 904 *true_node = graph->graph()->NewNode(graph->common()->IfTrue(), branch); | |
| 905 *false_node = graph->graph()->NewNode(graph->common()->IfFalse(), branch); | |
| 906 return branch; | |
| 907 } | |
| 908 | |
| 909 | |
| 910 Node* WasmGraphBuilder::Switch(unsigned count, Node* key) { | |
| 911 DCHECK_NOT_NULL(graph); | |
| 912 return graph->graph()->NewNode(graph->common()->Switch(count), key, *control); | |
| 913 } | |
| 914 | |
| 915 | |
| 916 Node* WasmGraphBuilder::IfValue(int32_t value, Node* sw) { | |
| 917 DCHECK_NOT_NULL(graph); | |
| 918 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode()); | |
| 919 return graph->graph()->NewNode(graph->common()->IfValue(value), sw); | |
| 920 } | |
| 921 | |
| 922 | |
| 923 Node* WasmGraphBuilder::IfDefault(Node* sw) { | |
| 924 DCHECK_NOT_NULL(graph); | |
| 925 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode()); | |
| 926 return graph->graph()->NewNode(graph->common()->IfDefault(), sw); | |
| 927 } | |
| 928 | |
| 929 | |
| 930 Node* WasmGraphBuilder::Return(unsigned count, Node** vals) { | |
| 931 DCHECK_NOT_NULL(graph); | |
| 932 DCHECK_NOT_NULL(*control); | |
| 933 DCHECK_NOT_NULL(*effect); | |
| 934 | |
| 935 if (count == 0) { | |
| 936 // Handle a return of void. | |
| 937 vals[0] = graph->Int32Constant(0); | |
| 938 count = 1; | |
| 939 } | |
| 940 | |
| 941 Graph* g = graph->graph(); | |
| 942 Node** buf = Realloc(vals, count + 2); | |
| 943 buf[count] = *effect; | |
| 944 buf[count + 1] = *control; | |
| 945 Node* ret = g->NewNode(graph->common()->Return(), count + 2, vals); | |
| 946 | |
| 947 MergeControlToEnd(graph, ret); | |
| 948 return ret; | |
| 949 } | |
| 950 | |
| 951 | |
| 952 Node* WasmGraphBuilder::ReturnVoid() { return Return(0, Buffer(0)); } | |
| 953 | |
| 954 | |
| 955 Node* WasmGraphBuilder::Unreachable() { | |
| 956 DCHECK_NOT_NULL(graph); | |
| 957 trap->Unreachable(); | |
| 958 return nullptr; | |
| 959 } | |
| 960 | |
| 961 | |
| 962 Node* WasmGraphBuilder::BuildF32CopySign(Node* left, Node* right) { | |
| 963 Node* result = Unop( | |
| 964 wasm::kExprF32ReinterpretI32, | |
| 965 Binop(wasm::kExprI32Ior, | |
| 966 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, left), | |
| 967 graph->Int32Constant(0x7fffffff)), | |
| 968 Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, right), | |
| 969 graph->Int32Constant(0x80000000)))); | |
| 970 | |
| 971 return result; | |
| 972 } | |
| 973 | |
| 974 | |
| 975 Node* WasmGraphBuilder::BuildF64CopySign(Node* left, Node* right) { | |
| 976 #if WASM_64 | |
| 977 Node* result = Unop( | |
| 978 wasm::kExprF64ReinterpretI64, | |
| 979 Binop(wasm::kExprI64Ior, | |
| 980 Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, left), | |
| 981 graph->Int64Constant(0x7fffffffffffffff)), | |
| 982 Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, right), | |
| 983 graph->Int64Constant(0x8000000000000000)))); | |
| 984 | |
| 985 return result; | |
| 986 #else | |
| 987 MachineOperatorBuilder* m = graph->machine(); | |
| 988 | |
| 989 Node* high_word_left = | |
| 990 graph->graph()->NewNode(m->Float64ExtractHighWord32(), left); | |
| 991 Node* high_word_right = | |
| 992 graph->graph()->NewNode(m->Float64ExtractHighWord32(), right); | |
| 993 | |
| 994 Node* new_high_word = | |
| 995 Binop(wasm::kExprI32Ior, Binop(wasm::kExprI32And, high_word_left, | |
| 996 graph->Int32Constant(0x7fffffff)), | |
| 997 Binop(wasm::kExprI32And, high_word_right, | |
| 998 graph->Int32Constant(0x80000000))); | |
| 999 | |
| 1000 return graph->graph()->NewNode(m->Float64InsertHighWord32(), left, | |
| 1001 new_high_word); | |
| 1002 #endif | |
| 1003 } | |
| 1004 | |
| 1005 | |
| 1006 Node* WasmGraphBuilder::BuildI32Ctz(Node* input) { | |
| 1007 DCHECK_NOT_NULL(graph); | |
| 1008 //// Implement the following code as TF graph. | |
| 1009 // value = value | (value << 1); | |
| 1010 // value = value | (value << 2); | |
| 1011 // value = value | (value << 4); | |
| 1012 // value = value | (value << 8); | |
| 1013 // value = value | (value << 16); | |
| 1014 // return CountPopulation32(0xffffffff XOR value); | |
| 1015 | |
| 1016 Node* result = | |
| 1017 Binop(wasm::kExprI32Ior, input, | |
| 1018 Binop(wasm::kExprI32Shl, input, graph->Int32Constant(1))); | |
| 1019 | |
| 1020 result = Binop(wasm::kExprI32Ior, result, | |
| 1021 Binop(wasm::kExprI32Shl, result, graph->Int32Constant(2))); | |
| 1022 | |
| 1023 result = Binop(wasm::kExprI32Ior, result, | |
| 1024 Binop(wasm::kExprI32Shl, result, graph->Int32Constant(4))); | |
| 1025 | |
| 1026 result = Binop(wasm::kExprI32Ior, result, | |
| 1027 Binop(wasm::kExprI32Shl, result, graph->Int32Constant(8))); | |
| 1028 | |
| 1029 result = Binop(wasm::kExprI32Ior, result, | |
| 1030 Binop(wasm::kExprI32Shl, result, graph->Int32Constant(16))); | |
| 1031 | |
| 1032 result = BuildI32Popcnt( | |
| 1033 Binop(wasm::kExprI32Xor, graph->Int32Constant(0xffffffff), result)); | |
| 1034 | |
| 1035 return result; | |
| 1036 } | |
| 1037 | |
| 1038 | |
| 1039 Node* WasmGraphBuilder::BuildI64Ctz(Node* input) { | |
| 1040 //// Implement the following code as TF graph. | |
| 1041 // value = value | (value << 1); | |
| 1042 // value = value | (value << 2); | |
| 1043 // value = value | (value << 4); | |
| 1044 // value = value | (value << 8); | |
| 1045 // value = value | (value << 16); | |
| 1046 // value = value | (value << 32); | |
| 1047 // return CountPopulation64(0xffffffffffffffff XOR value); | |
| 1048 | |
| 1049 Node* result = | |
| 1050 Binop(wasm::kExprI64Ior, input, | |
| 1051 Binop(wasm::kExprI64Shl, input, graph->Int64Constant(1))); | |
| 1052 | |
| 1053 result = Binop(wasm::kExprI64Ior, result, | |
| 1054 Binop(wasm::kExprI64Shl, result, graph->Int64Constant(2))); | |
| 1055 | |
| 1056 result = Binop(wasm::kExprI64Ior, result, | |
| 1057 Binop(wasm::kExprI64Shl, result, graph->Int64Constant(4))); | |
| 1058 | |
| 1059 result = Binop(wasm::kExprI64Ior, result, | |
| 1060 Binop(wasm::kExprI64Shl, result, graph->Int64Constant(8))); | |
| 1061 | |
| 1062 result = Binop(wasm::kExprI64Ior, result, | |
| 1063 Binop(wasm::kExprI64Shl, result, graph->Int64Constant(16))); | |
| 1064 | |
| 1065 result = Binop(wasm::kExprI64Ior, result, | |
| 1066 Binop(wasm::kExprI64Shl, result, graph->Int64Constant(32))); | |
| 1067 | |
| 1068 result = BuildI64Popcnt(Binop( | |
| 1069 wasm::kExprI64Xor, graph->Int64Constant(0xffffffffffffffff), result)); | |
| 1070 | |
| 1071 return result; | |
| 1072 } | |
| 1073 | |
| 1074 | |
| 1075 Node* WasmGraphBuilder::BuildI32Popcnt(Node* input) { | |
| 1076 DCHECK_NOT_NULL(graph); | |
| 1077 //// Implement the following code as a TF graph. | |
| 1078 // value = ((value >> 1) & 0x55555555) + (value & 0x55555555); | |
| 1079 // value = ((value >> 2) & 0x33333333) + (value & 0x33333333); | |
| 1080 // value = ((value >> 4) & 0x0f0f0f0f) + (value & 0x0f0f0f0f); | |
| 1081 // value = ((value >> 8) & 0x00ff00ff) + (value & 0x00ff00ff); | |
| 1082 // value = ((value >> 16) & 0x0000ffff) + (value & 0x0000ffff); | |
| 1083 | |
| 1084 Node* result = | |
| 1085 Binop(wasm::kExprI32Add, | |
| 1086 Binop(wasm::kExprI32And, | |
| 1087 Binop(wasm::kExprI32ShrU, input, graph->Int32Constant(1)), | |
| 1088 graph->Int32Constant(0x55555555)), | |
| 1089 Binop(wasm::kExprI32And, input, graph->Int32Constant(0x55555555))); | |
| 1090 | |
| 1091 result = | |
| 1092 Binop(wasm::kExprI32Add, | |
| 1093 Binop(wasm::kExprI32And, | |
| 1094 Binop(wasm::kExprI32ShrU, result, graph->Int32Constant(2)), | |
| 1095 graph->Int32Constant(0x33333333)), | |
| 1096 Binop(wasm::kExprI32And, result, graph->Int32Constant(0x33333333))); | |
| 1097 | |
| 1098 result = | |
| 1099 Binop(wasm::kExprI32Add, | |
| 1100 Binop(wasm::kExprI32And, | |
| 1101 Binop(wasm::kExprI32ShrU, result, graph->Int32Constant(4)), | |
| 1102 graph->Int32Constant(0x0f0f0f0f)), | |
| 1103 Binop(wasm::kExprI32And, result, graph->Int32Constant(0x0f0f0f0f))); | |
| 1104 | |
| 1105 result = | |
| 1106 Binop(wasm::kExprI32Add, | |
| 1107 Binop(wasm::kExprI32And, | |
| 1108 Binop(wasm::kExprI32ShrU, result, graph->Int32Constant(8)), | |
| 1109 graph->Int32Constant(0x00ff00ff)), | |
| 1110 Binop(wasm::kExprI32And, result, graph->Int32Constant(0x00ff00ff))); | |
| 1111 | |
| 1112 result = | |
| 1113 Binop(wasm::kExprI32Add, | |
| 1114 Binop(wasm::kExprI32And, | |
| 1115 Binop(wasm::kExprI32ShrU, result, graph->Int32Constant(16)), | |
| 1116 graph->Int32Constant(0x0000ffff)), | |
| 1117 Binop(wasm::kExprI32And, result, graph->Int32Constant(0x0000ffff))); | |
| 1118 | |
| 1119 return result; | |
| 1120 } | |
| 1121 | |
| 1122 | |
| 1123 Node* WasmGraphBuilder::BuildI64Popcnt(Node* input) { | |
| 1124 DCHECK_NOT_NULL(graph); | |
| 1125 //// Implement the following code as a TF graph. | |
| 1126 // value = ((value >> 1) & 0x5555555555555555) + (value & 0x5555555555555555); | |
| 1127 // value = ((value >> 2) & 0x3333333333333333) + (value & 0x3333333333333333); | |
| 1128 // value = ((value >> 4) & 0x0f0f0f0f0f0f0f0f) + (value & 0x0f0f0f0f0f0f0f0f); | |
| 1129 // value = ((value >> 8) & 0x00ff00ff00ff00ff) + (value & 0x00ff00ff00ff00ff); | |
| 1130 // value = ((value >> 16) & 0x0000ffff0000ffff) + (value & | |
| 1131 // 0x0000ffff0000ffff); | |
| 1132 // value = ((value >> 32) & 0x00000000ffffffff) + (value & | |
| 1133 // 0x00000000ffffffff); | |
| 1134 | |
| 1135 Node* result = Binop(wasm::kExprI64Add, | |
| 1136 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, input, | |
| 1137 graph->Int64Constant(1)), | |
| 1138 graph->Int64Constant(0x5555555555555555)), | |
| 1139 Binop(wasm::kExprI64And, input, | |
| 1140 graph->Int64Constant(0x5555555555555555))); | |
| 1141 | |
| 1142 result = Binop(wasm::kExprI64Add, | |
| 1143 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result, | |
| 1144 graph->Int64Constant(2)), | |
| 1145 graph->Int64Constant(0x3333333333333333)), | |
| 1146 Binop(wasm::kExprI64And, result, | |
| 1147 graph->Int64Constant(0x3333333333333333))); | |
| 1148 | |
| 1149 result = Binop(wasm::kExprI64Add, | |
| 1150 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result, | |
| 1151 graph->Int64Constant(4)), | |
| 1152 graph->Int64Constant(0x0f0f0f0f0f0f0f0f)), | |
| 1153 Binop(wasm::kExprI64And, result, | |
| 1154 graph->Int64Constant(0x0f0f0f0f0f0f0f0f))); | |
| 1155 | |
| 1156 result = Binop(wasm::kExprI64Add, | |
| 1157 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result, | |
| 1158 graph->Int64Constant(8)), | |
| 1159 graph->Int64Constant(0x00ff00ff00ff00ff)), | |
| 1160 Binop(wasm::kExprI64And, result, | |
| 1161 graph->Int64Constant(0x00ff00ff00ff00ff))); | |
| 1162 | |
| 1163 result = Binop(wasm::kExprI64Add, | |
| 1164 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result, | |
| 1165 graph->Int64Constant(16)), | |
| 1166 graph->Int64Constant(0x0000ffff0000ffff)), | |
| 1167 Binop(wasm::kExprI64And, result, | |
| 1168 graph->Int64Constant(0x0000ffff0000ffff))); | |
| 1169 | |
| 1170 result = Binop(wasm::kExprI64Add, | |
| 1171 Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result, | |
| 1172 graph->Int64Constant(32)), | |
| 1173 graph->Int64Constant(0x00000000ffffffff)), | |
| 1174 Binop(wasm::kExprI64And, result, | |
| 1175 graph->Int64Constant(0x00000000ffffffff))); | |
| 1176 | |
| 1177 return result; | |
| 1178 } | |
| 1179 | |
| 1180 | |
| 1181 Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args) { | |
| 1182 const size_t params = sig->parameter_count(); | |
| 1183 const size_t extra = 2; // effect and control inputs. | |
| 1184 const size_t count = 1 + params + extra; | |
| 1185 | |
| 1186 // Reallocate the buffer to make space for extra inputs. | |
| 1187 args = Realloc(args, count); | |
| 1188 | |
| 1189 // Add effect and control inputs. | |
| 1190 args[params + 1] = *effect; | |
| 1191 args[params + 2] = *control; | |
| 1192 | |
| 1193 const Operator* op = | |
| 1194 graph->common()->Call(module->GetWasmCallDescriptor(graph->zone(), sig)); | |
| 1195 Node* call = graph->graph()->NewNode(op, static_cast<int>(count), args); | |
| 1196 | |
| 1197 *effect = call; | |
| 1198 return call; | |
| 1199 } | |
| 1200 | |
| 1201 | |
| 1202 Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args) { | |
| 1203 DCHECK_NOT_NULL(graph); | |
| 1204 DCHECK_NULL(args[0]); | |
| 1205 | |
| 1206 // Add code object as constant. | |
| 1207 args[0] = Constant(module->GetFunctionCode(index)); | |
| 1208 wasm::FunctionSig* sig = module->GetFunctionSignature(index); | |
| 1209 | |
| 1210 return BuildWasmCall(sig, args); | |
| 1211 } | |
| 1212 | |
| 1213 | |
| 1214 Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args) { | |
| 1215 DCHECK_NOT_NULL(graph); | |
| 1216 DCHECK_NOT_NULL(args[0]); | |
| 1217 | |
| 1218 Graph* g = graph->graph(); | |
| 1219 MachineOperatorBuilder* machine = graph->machine(); | |
| 1220 | |
| 1221 // Compute the code object by loading it from the function table. | |
| 1222 Node* key = args[0]; | |
| 1223 Node* table = FunctionTable(); | |
| 1224 | |
| 1225 // Bounds check the index. | |
| 1226 int table_size = static_cast<int>(module->FunctionTableSize()); | |
| 1227 { | |
| 1228 Node* size = Int32Constant(static_cast<int>(table_size)); | |
| 1229 Node* in_bounds = g->NewNode(machine->Uint32LessThan(), key, size); | |
| 1230 trap->AddTrapIfFalse(kTrapFuncInvalid, in_bounds); | |
| 1231 } | |
| 1232 | |
| 1233 // Load signature from the table and check. | |
| 1234 // The table is a FixedArray; signatures are encoded as SMIs. | |
| 1235 // [sig1, sig2, sig3, ...., code1, code2, code3 ...] | |
| 1236 ElementAccess access = AccessBuilder::ForFixedArrayElement(); | |
| 1237 const int fixed_offset = access.header_size - access.tag(); | |
| 1238 { | |
| 1239 Node* load_sig = | |
| 1240 g->NewNode(machine->Load(MachineType::AnyTagged()), table, | |
| 1241 g->NewNode(machine->Int32Add(), | |
| 1242 g->NewNode(machine->Word32Shl(), key, | |
| 1243 Int32Constant(kPointerSizeLog2)), | |
| 1244 Int32Constant(fixed_offset)), | |
| 1245 *effect, *control); | |
| 1246 Node* sig_match = | |
| 1247 g->NewNode(machine->WordEqual(), load_sig, graph->SmiConstant(index)); | |
| 1248 trap->AddTrapIfFalse(kTrapFuncSigMismatch, sig_match); | |
| 1249 } | |
| 1250 | |
| 1251 // Load code object from the table. | |
| 1252 int offset = fixed_offset + kPointerSize * table_size; | |
| 1253 Node* load_code = | |
| 1254 g->NewNode(machine->Load(MachineType::AnyTagged()), table, | |
| 1255 g->NewNode(machine->Int32Add(), | |
| 1256 g->NewNode(machine->Word32Shl(), key, | |
| 1257 Int32Constant(kPointerSizeLog2)), | |
| 1258 Int32Constant(offset)), | |
| 1259 *effect, *control); | |
| 1260 | |
| 1261 args[0] = load_code; | |
| 1262 wasm::FunctionSig* sig = module->GetSignature(index); | |
| 1263 return BuildWasmCall(sig, args); | |
| 1264 } | |
| 1265 | |
| 1266 | |
| 1267 Node* WasmGraphBuilder::ToJS(Node* node, Node* context, wasm::LocalType type) { | |
| 1268 DCHECK_NOT_NULL(graph); | |
| 1269 Graph* g = graph->graph(); | |
| 1270 SimplifiedOperatorBuilder simplified(graph->zone()); | |
| 1271 switch (type) { | |
| 1272 case wasm::kAstI32: | |
| 1273 return g->NewNode(simplified.ChangeInt32ToTagged(), node); | |
| 1274 case wasm::kAstI64: | |
| 1275 // TODO(titzer): i64->JS has no good solution right now. Using lower 32 | |
| 1276 // bits. | |
| 1277 node = g->NewNode(graph->machine()->TruncateInt64ToInt32(), node); | |
| 1278 return g->NewNode(simplified.ChangeInt32ToTagged(), node); | |
| 1279 case wasm::kAstF32: | |
| 1280 node = g->NewNode(graph->machine()->ChangeFloat32ToFloat64(), node); | |
| 1281 return g->NewNode(simplified.ChangeFloat64ToTagged(), node); | |
| 1282 case wasm::kAstF64: | |
| 1283 return g->NewNode(simplified.ChangeFloat64ToTagged(), node); | |
| 1284 case wasm::kAstStmt: | |
| 1285 return graph->UndefinedConstant(); | |
| 1286 default: | |
| 1287 UNREACHABLE(); | |
| 1288 return nullptr; | |
| 1289 } | |
| 1290 } | |
| 1291 | |
| 1292 | |
| 1293 Node* WasmGraphBuilder::FromJS(Node* node, Node* context, | |
| 1294 wasm::LocalType type) { | |
| 1295 DCHECK_NOT_NULL(graph); | |
| 1296 Graph* g = graph->graph(); | |
| 1297 // Do a JavaScript ToNumber. | |
| 1298 Node* num = g->NewNode(graph->javascript()->ToNumber(), node, context, | |
| 1299 graph->EmptyFrameState(), *effect, *control); | |
| 1300 *control = num; | |
| 1301 *effect = num; | |
| 1302 | |
| 1303 // Change representation. | |
| 1304 SimplifiedOperatorBuilder simplified(graph->zone()); | |
| 1305 num = g->NewNode(simplified.ChangeTaggedToFloat64(), num); | |
| 1306 | |
| 1307 switch (type) { | |
| 1308 case wasm::kAstI32: { | |
| 1309 num = g->NewNode( | |
| 1310 graph->machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript), | |
| 1311 num); | |
| 1312 break; | |
| 1313 } | |
| 1314 case wasm::kAstI64: | |
| 1315 // TODO(titzer): JS->i64 has no good solution right now. Using 32 bits. | |
| 1316 num = g->NewNode( | |
| 1317 graph->machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript), | |
| 1318 num); | |
| 1319 num = g->NewNode(graph->machine()->ChangeInt32ToInt64(), num); | |
| 1320 break; | |
| 1321 case wasm::kAstF32: | |
| 1322 num = g->NewNode(graph->machine()->TruncateFloat64ToFloat32(), num); | |
| 1323 break; | |
| 1324 case wasm::kAstF64: | |
| 1325 break; | |
| 1326 case wasm::kAstStmt: | |
| 1327 num = graph->Int32Constant(0); | |
| 1328 break; | |
| 1329 default: | |
| 1330 UNREACHABLE(); | |
| 1331 return nullptr; | |
| 1332 } | |
| 1333 return num; | |
| 1334 } | |
| 1335 | |
| 1336 | |
| 1337 Node* WasmGraphBuilder::Invert(Node* node) { | |
| 1338 DCHECK_NOT_NULL(graph); | |
| 1339 return Unop(wasm::kExprBoolNot, node); | |
| 1340 } | |
| 1341 | |
| 1342 | |
| 1343 void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code, | |
| 1344 wasm::FunctionSig* sig) { | |
| 1345 DCHECK_NOT_NULL(graph); | |
| 1346 | |
| 1347 int params = static_cast<int>(sig->parameter_count()); | |
| 1348 Graph* g = graph->graph(); | |
| 1349 int count = params + 3; | |
| 1350 Node** args = Buffer(count); | |
| 1351 | |
| 1352 // Build the start and the JS parameter nodes. | |
| 1353 Node* start = Start(params + 3); | |
| 1354 *control = start; | |
| 1355 *effect = start; | |
| 1356 // JS context is the last parameter. | |
| 1357 Node* context = | |
| 1358 g->NewNode(graph->common()->Parameter(params + 1, "context"), start); | |
| 1359 | |
| 1360 int pos = 0; | |
| 1361 args[pos++] = Constant(wasm_code); | |
| 1362 | |
| 1363 // Convert JS parameters to WASM numbers. | |
| 1364 for (int i = 0; i < params; i++) { | |
| 1365 Node* param = g->NewNode(graph->common()->Parameter(i), start); | |
| 1366 args[pos++] = FromJS(param, context, sig->GetParam(i)); | |
| 1367 } | |
| 1368 | |
| 1369 args[pos++] = *effect; | |
| 1370 args[pos++] = *control; | |
| 1371 | |
| 1372 // Call the WASM code. | |
| 1373 CallDescriptor* desc = module->GetWasmCallDescriptor(graph->zone(), sig); | |
| 1374 Node* call = g->NewNode(graph->common()->Call(desc), count, args); | |
| 1375 Node* jsval = | |
| 1376 ToJS(call, context, | |
| 1377 sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn()); | |
| 1378 Node* ret = g->NewNode(graph->common()->Return(), jsval, call, start); | |
| 1379 | |
| 1380 MergeControlToEnd(graph, ret); | |
| 1381 } | |
| 1382 | |
| 1383 | |
| 1384 void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function, | |
| 1385 wasm::FunctionSig* sig) { | |
| 1386 DCHECK_NOT_NULL(graph); | |
| 1387 CHECK_NOT_NULL(graph); | |
| 1388 int js_count = function->shared()->internal_formal_parameter_count(); | |
| 1389 int wasm_count = static_cast<int>(sig->parameter_count()); | |
| 1390 | |
| 1391 // Build the start and the parameter nodes. | |
| 1392 Isolate* isolate = graph->isolate(); | |
| 1393 Graph* g = graph->graph(); | |
| 1394 CallDescriptor* desc; | |
| 1395 Node* start = Start(wasm_count + 3); | |
| 1396 *effect = start; | |
| 1397 *control = start; | |
| 1398 // JS context is the last parameter. | |
| 1399 Node* context = Constant(Handle<Context>(function->context(), isolate)); | |
| 1400 Node** args = Buffer(wasm_count + 7); | |
| 1401 | |
| 1402 bool arg_count_before_args = false; | |
| 1403 bool add_new_target_undefined = false; | |
| 1404 | |
| 1405 int pos = 0; | |
| 1406 if (js_count == wasm_count) { | |
| 1407 // exact arity match, just call the function directly. | |
| 1408 desc = Linkage::GetJSCallDescriptor(g->zone(), false, wasm_count + 1, | |
| 1409 CallDescriptor::kNoFlags); | |
| 1410 arg_count_before_args = false; | |
| 1411 add_new_target_undefined = true; | |
| 1412 } else { | |
| 1413 // Use the Call builtin. | |
| 1414 Callable callable = CodeFactory::Call(isolate); | |
| 1415 args[pos++] = graph->HeapConstant(callable.code()); | |
| 1416 desc = Linkage::GetStubCallDescriptor(isolate, g->zone(), | |
| 1417 callable.descriptor(), wasm_count + 1, | |
| 1418 CallDescriptor::kNoFlags); | |
| 1419 arg_count_before_args = true; | |
| 1420 } | |
| 1421 | |
| 1422 args[pos++] = graph->Constant(function); // JS function. | |
| 1423 if (arg_count_before_args) { | |
| 1424 args[pos++] = graph->Int32Constant(wasm_count); // argument count | |
| 1425 } | |
| 1426 args[pos++] = graph->UndefinedConstant(); // JS receiver. | |
| 1427 | |
| 1428 // Convert WASM numbers to JS values. | |
| 1429 for (int i = 0; i < wasm_count; i++) { | |
| 1430 Node* param = g->NewNode(graph->common()->Parameter(i), start); | |
| 1431 args[pos++] = ToJS(param, context, sig->GetParam(i)); | |
| 1432 } | |
| 1433 | |
| 1434 if (add_new_target_undefined) { | |
| 1435 args[pos++] = graph->UndefinedConstant(); // new target | |
| 1436 } | |
| 1437 | |
| 1438 if (!arg_count_before_args) { | |
| 1439 args[pos++] = graph->Int32Constant(wasm_count); // argument count | |
| 1440 } | |
| 1441 args[pos++] = context; | |
| 1442 args[pos++] = *effect; | |
| 1443 args[pos++] = *control; | |
| 1444 | |
| 1445 Node* call = g->NewNode(graph->common()->Call(desc), pos, args); | |
| 1446 | |
| 1447 // Convert the return value back. | |
| 1448 Node* val = | |
| 1449 FromJS(call, context, | |
| 1450 sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn()); | |
| 1451 Node* ret = g->NewNode(graph->common()->Return(), val, call, start); | |
| 1452 | |
| 1453 MergeControlToEnd(graph, ret); | |
| 1454 } | |
| 1455 | |
| 1456 | |
| 1457 Node* WasmGraphBuilder::MemBuffer(uint32_t offset) { | |
| 1458 if (!graph) return nullptr; | |
| 1459 if (offset == 0) { | |
| 1460 if (!mem_buffer) mem_buffer = graph->IntPtrConstant(module->mem_start); | |
| 1461 return mem_buffer; | |
| 1462 } else { | |
| 1463 return graph->IntPtrConstant(module->mem_start + offset); | |
| 1464 } | |
| 1465 } | |
| 1466 | |
| 1467 | |
| 1468 Node* WasmGraphBuilder::MemSize(uint32_t offset) { | |
| 1469 if (!graph) return nullptr; | |
| 1470 int32_t size = static_cast<int>(module->mem_end - module->mem_start); | |
| 1471 if (offset == 0) { | |
| 1472 if (!mem_size) mem_size = graph->Int32Constant(size); | |
| 1473 return mem_size; | |
| 1474 } else { | |
| 1475 return graph->Int32Constant(size + offset); | |
| 1476 } | |
| 1477 } | |
| 1478 | |
| 1479 | |
| 1480 Node* WasmGraphBuilder::FunctionTable() { | |
| 1481 if (!graph) return nullptr; | |
| 1482 if (!function_table) { | |
| 1483 DCHECK(!module->function_table.is_null()); | |
| 1484 function_table = graph->Constant(module->function_table); | |
| 1485 } | |
| 1486 return function_table; | |
| 1487 } | |
| 1488 | |
| 1489 | |
| 1490 Node* WasmGraphBuilder::LoadGlobal(uint32_t index) { | |
| 1491 DCHECK_NOT_NULL(graph); | |
| 1492 MachineType mem_type = module->GetGlobalType(index); | |
| 1493 Node* addr = graph->IntPtrConstant(module->globals_area + | |
| 1494 module->module->globals->at(index).offset); | |
| 1495 const Operator* op = graph->machine()->Load(mem_type); | |
| 1496 Node* node = graph->graph()->NewNode(op, addr, graph->Int32Constant(0), | |
| 1497 *effect, *control); | |
| 1498 *effect = node; | |
| 1499 return node; | |
| 1500 } | |
| 1501 | |
| 1502 | |
| 1503 Node* WasmGraphBuilder::StoreGlobal(uint32_t index, Node* val) { | |
| 1504 DCHECK_NOT_NULL(graph); | |
| 1505 MachineType mem_type = module->GetGlobalType(index); | |
| 1506 Node* addr = graph->IntPtrConstant(module->globals_area + | |
| 1507 module->module->globals->at(index).offset); | |
| 1508 const Operator* op = | |
| 1509 graph->machine()->Store(StoreRepresentation(mem_type, kNoWriteBarrier)); | |
| 1510 Node* node = graph->graph()->NewNode(op, addr, graph->Int32Constant(0), val, | |
| 1511 *effect, *control); | |
| 1512 *effect = node; | |
| 1513 return node; | |
| 1514 } | |
| 1515 | |
| 1516 | |
| 1517 void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index, | |
| 1518 uint32_t offset) { | |
| 1519 // TODO(turbofan): fold bounds checks for constant indexes. | |
| 1520 Graph* g = graph->graph(); | |
| 1521 CHECK_GE(module->mem_end, module->mem_start); | |
| 1522 ptrdiff_t size = module->mem_end - module->mem_start; | |
| 1523 byte memsize = wasm::WasmOpcodes::MemSize(memtype); | |
| 1524 Node* cond; | |
| 1525 if (offset >= size || (offset + memsize) > size) { | |
| 1526 // The access will always throw. | |
| 1527 cond = graph->Int32Constant(0); | |
| 1528 } else { | |
| 1529 // Check against the limit. | |
| 1530 size_t limit = size - offset - memsize; | |
| 1531 CHECK(limit <= kMaxUInt32); | |
| 1532 cond = g->NewNode(graph->machine()->Uint32LessThanOrEqual(), index, | |
| 1533 graph->Int32Constant(static_cast<uint32_t>(limit))); | |
| 1534 } | |
| 1535 | |
| 1536 trap->AddTrapIfFalse(kTrapMemOutOfBounds, cond); | |
| 1537 } | |
| 1538 | |
| 1539 | |
| 1540 Node* WasmGraphBuilder::LoadMem(wasm::LocalType type, MachineType memtype, | |
| 1541 Node* index, uint32_t offset) { | |
| 1542 if (!graph) return nullptr; | |
| 1543 | |
| 1544 Graph* g = graph->graph(); | |
| 1545 Node* load; | |
| 1546 | |
| 1547 if (module && module->asm_js) { | |
| 1548 // asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish). | |
| 1549 DCHECK_EQ(0, offset); | |
| 1550 const Operator* op = graph->machine()->CheckedLoad(memtype); | |
| 1551 load = g->NewNode(op, MemBuffer(0), index, MemSize(0), *effect, *control); | |
| 1552 } else { | |
| 1553 // WASM semantics throw on OOB. Introduce explicit bounds check. | |
| 1554 BoundsCheckMem(memtype, index, offset); | |
| 1555 load = g->NewNode(graph->machine()->Load(memtype), MemBuffer(offset), index, | |
| 1556 *effect, *control); | |
| 1557 } | |
| 1558 | |
| 1559 *effect = load; | |
| 1560 | |
| 1561 if (type == wasm::kAstI64 && | |
| 1562 ElementSizeLog2Of(memtype.representation()) < 3) { | |
| 1563 // TODO(titzer): TF zeroes the upper bits of 64-bit loads for subword sizes. | |
| 1564 if (memtype.IsSigned()) { | |
| 1565 // sign extend | |
| 1566 load = g->NewNode(graph->machine()->ChangeInt32ToInt64(), load); | |
| 1567 } else { | |
| 1568 // zero extend | |
| 1569 load = g->NewNode(graph->machine()->ChangeUint32ToUint64(), load); | |
| 1570 } | |
| 1571 } | |
| 1572 | |
| 1573 return load; | |
| 1574 } | |
| 1575 | |
| 1576 | |
| 1577 Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index, | |
| 1578 uint32_t offset, Node* val) { | |
| 1579 if (!graph) return nullptr; | |
| 1580 | |
| 1581 Node* store; | |
| 1582 if (module && module->asm_js) { | |
| 1583 // asm.js semantics use CheckedStore (i.e. ignore OOB writes). | |
| 1584 DCHECK_EQ(0, offset); | |
| 1585 const Operator* op = graph->machine()->CheckedStore(memtype); | |
| 1586 store = graph->graph()->NewNode(op, MemBuffer(0), index, MemSize(0), val, | |
| 1587 *effect, *control); | |
| 1588 } else { | |
| 1589 // WASM semantics throw on OOB. Introduce explicit bounds check. | |
| 1590 BoundsCheckMem(memtype, index, offset); | |
| 1591 StoreRepresentation rep(memtype, kNoWriteBarrier); | |
| 1592 store = | |
| 1593 graph->graph()->NewNode(graph->machine()->Store(rep), MemBuffer(offset), | |
| 1594 index, val, *effect, *control); | |
| 1595 } | |
| 1596 *effect = store; | |
| 1597 return store; | |
| 1598 } | |
| 1599 | |
| 1600 | |
| 1601 void WasmGraphBuilder::PrintDebugName(Node* node) { | |
| 1602 PrintF("#%d:%s", node->id(), node->op()->mnemonic()); | |
| 1603 } | |
| 1604 | |
| 1605 | |
| 1606 Node* WasmGraphBuilder::String(const char* string) { | |
| 1607 DCHECK_NOT_NULL(graph); | |
| 1608 return graph->Constant( | |
| 1609 graph->isolate()->factory()->NewStringFromAsciiChecked(string)); | |
| 1610 } | |
| 1611 | |
| 1612 | |
| 1613 Handle<JSFunction> CompileJSToWasmWrapper(Isolate* isolate, | |
| 1614 wasm::ModuleEnv* module, | |
| 1615 Handle<String> name, | |
| 1616 Handle<Code> wasm_code, | |
| 1617 uint32_t index) { | |
| 1618 wasm::WasmFunction* func = &module->module->functions->at(index); | |
| 1619 | |
| 1620 //---------------------------------------------------------------------------- | |
| 1621 // Create the JSFunction object. | |
| 1622 //---------------------------------------------------------------------------- | |
| 1623 Handle<SharedFunctionInfo> shared = | |
| 1624 isolate->factory()->NewSharedFunctionInfo(name, wasm_code, false); | |
| 1625 int params = static_cast<int>(func->sig->parameter_count()); | |
| 1626 shared->set_length(params); | |
| 1627 shared->set_internal_formal_parameter_count(1 + params); | |
| 1628 Handle<JSFunction> function = isolate->factory()->NewFunction(name); | |
| 1629 function->set_shared(*shared); | |
| 1630 | |
| 1631 //---------------------------------------------------------------------------- | |
| 1632 // Create the Graph | |
| 1633 //---------------------------------------------------------------------------- | |
| 1634 Zone zone; | |
| 1635 Graph graph(&zone); | |
| 1636 CommonOperatorBuilder common(&zone); | |
| 1637 JSOperatorBuilder javascript(&zone); | |
| 1638 MachineOperatorBuilder machine(&zone); | |
| 1639 JSGraph jsgraph(isolate, &graph, &common, &javascript, nullptr, &machine); | |
| 1640 | |
| 1641 Node* control = nullptr; | |
| 1642 Node* effect = nullptr; | |
| 1643 | |
| 1644 WasmGraphBuilder builder(&zone, &jsgraph); | |
| 1645 builder.set_control_ptr(&control); | |
| 1646 builder.set_effect_ptr(&effect); | |
| 1647 builder.set_module(module); | |
| 1648 builder.BuildJSToWasmWrapper(wasm_code, func->sig); | |
| 1649 | |
| 1650 //---------------------------------------------------------------------------- | |
| 1651 // Run the compilation pipeline. | |
| 1652 //---------------------------------------------------------------------------- | |
| 1653 { | |
| 1654 // Changes lowering requires types. | |
| 1655 Typer typer(isolate, &graph); | |
| 1656 NodeVector roots(&zone); | |
| 1657 jsgraph.GetCachedNodes(&roots); | |
| 1658 typer.Run(roots); | |
| 1659 | |
| 1660 // Run generic and change lowering. | |
| 1661 JSGenericLowering generic(true, &jsgraph); | |
| 1662 ChangeLowering changes(&jsgraph); | |
| 1663 GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead()); | |
| 1664 graph_reducer.AddReducer(&changes); | |
| 1665 graph_reducer.AddReducer(&generic); | |
| 1666 graph_reducer.ReduceGraph(); | |
| 1667 | |
| 1668 if (FLAG_trace_turbo_graph) { // Simple textual RPO. | |
| 1669 OFStream os(stdout); | |
| 1670 os << "-- Graph after change lowering -- " << std::endl; | |
| 1671 os << AsRPO(graph); | |
| 1672 } | |
| 1673 | |
| 1674 // Schedule and compile to machine code. | |
| 1675 int params = static_cast<int>( | |
| 1676 module->GetFunctionSignature(index)->parameter_count()); | |
| 1677 CallDescriptor* incoming = Linkage::GetJSCallDescriptor( | |
| 1678 &zone, false, params + 1, CallDescriptor::kNoFlags); | |
| 1679 CompilationInfo info("js-to-wasm", isolate, &zone); | |
| 1680 // TODO(titzer): this is technically a WASM wrapper, not a wasm function. | |
| 1681 info.set_output_code_kind(Code::WASM_FUNCTION); | |
| 1682 Handle<Code> code = | |
| 1683 Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr); | |
| 1684 | |
| 1685 #ifdef ENABLE_DISASSEMBLER | |
| 1686 // Disassemble the wrapper code for debugging. | |
| 1687 if (!code.is_null() && FLAG_print_opt_code) { | |
| 1688 static const int kBufferSize = 128; | |
| 1689 char buffer[kBufferSize]; | |
| 1690 const char* name = ""; | |
| 1691 if (func->name_offset > 0) { | |
| 1692 const byte* ptr = module->module->module_start + func->name_offset; | |
| 1693 name = reinterpret_cast<const char*>(ptr); | |
| 1694 } | |
| 1695 snprintf(buffer, kBufferSize, "JS->WASM function wrapper #%d:%s", index, | |
| 1696 name); | |
| 1697 OFStream os(stdout); | |
| 1698 code->Disassemble(buffer, os); | |
| 1699 } | |
| 1700 #endif | |
| 1701 // Set the JSFunction's machine code. | |
| 1702 function->set_code(*code); | |
| 1703 } | |
| 1704 return function; | |
| 1705 } | |
| 1706 | |
| 1707 | |
| 1708 Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module, | |
| 1709 Handle<JSFunction> function, | |
| 1710 uint32_t index) { | |
| 1711 wasm::WasmFunction* func = &module->module->functions->at(index); | |
| 1712 | |
| 1713 //---------------------------------------------------------------------------- | |
| 1714 // Create the Graph | |
| 1715 //---------------------------------------------------------------------------- | |
| 1716 Zone zone; | |
| 1717 Graph graph(&zone); | |
| 1718 CommonOperatorBuilder common(&zone); | |
| 1719 JSOperatorBuilder javascript(&zone); | |
| 1720 MachineOperatorBuilder machine(&zone); | |
| 1721 JSGraph jsgraph(isolate, &graph, &common, &javascript, nullptr, &machine); | |
| 1722 | |
| 1723 Node* control = nullptr; | |
| 1724 Node* effect = nullptr; | |
| 1725 | |
| 1726 WasmGraphBuilder builder(&zone, &jsgraph); | |
| 1727 builder.set_control_ptr(&control); | |
| 1728 builder.set_effect_ptr(&effect); | |
| 1729 builder.set_module(module); | |
| 1730 builder.BuildWasmToJSWrapper(function, func->sig); | |
| 1731 | |
| 1732 Handle<Code> code = Handle<Code>::null(); | |
| 1733 { | |
| 1734 // Changes lowering requires types. | |
| 1735 Typer typer(isolate, &graph); | |
| 1736 NodeVector roots(&zone); | |
| 1737 jsgraph.GetCachedNodes(&roots); | |
| 1738 typer.Run(roots); | |
| 1739 | |
| 1740 // Run generic and change lowering. | |
| 1741 JSGenericLowering generic(true, &jsgraph); | |
| 1742 ChangeLowering changes(&jsgraph); | |
| 1743 GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead()); | |
| 1744 graph_reducer.AddReducer(&changes); | |
| 1745 graph_reducer.AddReducer(&generic); | |
| 1746 graph_reducer.ReduceGraph(); | |
| 1747 | |
| 1748 if (FLAG_trace_turbo_graph) { // Simple textual RPO. | |
| 1749 OFStream os(stdout); | |
| 1750 os << "-- Graph after change lowering -- " << std::endl; | |
| 1751 os << AsRPO(graph); | |
| 1752 } | |
| 1753 | |
| 1754 // Schedule and compile to machine code. | |
| 1755 CallDescriptor* incoming = module->GetWasmCallDescriptor(&zone, func->sig); | |
| 1756 CompilationInfo info("wasm-to-js", isolate, &zone); | |
| 1757 // TODO(titzer): this is technically a WASM wrapper, not a wasm function. | |
| 1758 info.set_output_code_kind(Code::WASM_FUNCTION); | |
| 1759 code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr); | |
| 1760 | |
| 1761 #ifdef ENABLE_DISASSEMBLER | |
| 1762 // Disassemble the wrapper code for debugging. | |
| 1763 if (!code.is_null() && FLAG_print_opt_code) { | |
| 1764 static const int kBufferSize = 128; | |
| 1765 char buffer[kBufferSize]; | |
| 1766 const char* name = ""; | |
| 1767 if (func->name_offset > 0) { | |
| 1768 const byte* ptr = module->module->module_start + func->name_offset; | |
| 1769 name = reinterpret_cast<const char*>(ptr); | |
| 1770 } | |
| 1771 snprintf(buffer, kBufferSize, "WASM->JS function wrapper #%d:%s", index, | |
| 1772 name); | |
| 1773 OFStream os(stdout); | |
| 1774 code->Disassemble(buffer, os); | |
| 1775 } | |
| 1776 #endif | |
| 1777 } | |
| 1778 return code; | |
| 1779 } | |
| 1780 | |
| 1781 | |
| 1782 // Helper function to compile a single function. | |
| 1783 Handle<Code> CompileWasmFunction(wasm::ErrorThrower& thrower, Isolate* isolate, | |
| 1784 wasm::ModuleEnv* module_env, | |
| 1785 const wasm::WasmFunction& function, | |
| 1786 int index) { | |
| 1787 if (FLAG_trace_wasm_compiler || FLAG_trace_wasm_decode_time) { | |
| 1788 // TODO(titzer): clean me up a bit. | |
| 1789 OFStream os(stdout); | |
| 1790 os << "Compiling WASM function #" << index << ":"; | |
| 1791 if (function.name_offset > 0) { | |
| 1792 os << module_env->module->GetName(function.name_offset); | |
| 1793 } | |
| 1794 os << std::endl; | |
| 1795 } | |
| 1796 // Initialize the function environment for decoding. | |
| 1797 wasm::FunctionEnv env; | |
| 1798 env.module = module_env; | |
| 1799 env.sig = function.sig; | |
| 1800 env.local_int32_count = function.local_int32_count; | |
| 1801 env.local_int64_count = function.local_int64_count; | |
| 1802 env.local_float32_count = function.local_float32_count; | |
| 1803 env.local_float64_count = function.local_float64_count; | |
| 1804 env.SumLocals(); | |
| 1805 | |
| 1806 // Create a TF graph during decoding. | |
| 1807 Zone zone; | |
| 1808 Graph graph(&zone); | |
| 1809 CommonOperatorBuilder common(&zone); | |
| 1810 MachineOperatorBuilder machine( | |
| 1811 &zone, MachineType::PointerRepresentation(), | |
| 1812 InstructionSelector::SupportedMachineOperatorFlags()); | |
| 1813 JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine); | |
| 1814 WasmGraphBuilder builder(&zone, &jsgraph); | |
| 1815 wasm::TreeResult result = wasm::BuildTFGraph( | |
| 1816 &builder, &env, // -- | |
| 1817 module_env->module->module_start, // -- | |
| 1818 module_env->module->module_start + function.code_start_offset, // -- | |
| 1819 module_env->module->module_start + function.code_end_offset); // -- | |
| 1820 | |
| 1821 if (result.failed()) { | |
| 1822 if (FLAG_trace_wasm_compiler) { | |
| 1823 OFStream os(stdout); | |
| 1824 os << "Compilation failed: " << result << std::endl; | |
| 1825 } | |
| 1826 // Add the function as another context for the exception | |
| 1827 const int kBufSize = 256; | |
| 1828 char buffer[kBufSize]; | |
| 1829 snprintf(buffer, kBufSize, "Compiling WASM function #%d:%s failed:", index, | |
| 1830 module_env->module->GetName(function.name_offset)); | |
| 1831 thrower.Failed(buffer, result); | |
| 1832 return Handle<Code>::null(); | |
| 1833 } | |
| 1834 | |
| 1835 // Run the compiler pipeline to generate machine code. | |
| 1836 CallDescriptor* descriptor = const_cast<CallDescriptor*>( | |
| 1837 module_env->GetWasmCallDescriptor(&zone, function.sig)); | |
| 1838 CompilationInfo info("wasm", isolate, &zone); | |
| 1839 info.set_output_code_kind(Code::WASM_FUNCTION); | |
| 1840 Handle<Code> code = | |
| 1841 Pipeline::GenerateCodeForTesting(&info, descriptor, &graph); | |
| 1842 | |
| 1843 #ifdef ENABLE_DISASSEMBLER | |
| 1844 // Disassemble the code for debugging. | |
| 1845 if (!code.is_null() && FLAG_print_opt_code) { | |
| 1846 static const int kBufferSize = 128; | |
| 1847 char buffer[kBufferSize]; | |
| 1848 const char* name = ""; | |
| 1849 if (function.name_offset > 0) { | |
| 1850 const byte* ptr = module_env->module->module_start + function.name_offset; | |
| 1851 name = reinterpret_cast<const char*>(ptr); | |
| 1852 } | |
| 1853 snprintf(buffer, kBufferSize, "WASM function #%d:%s", index, name); | |
| 1854 OFStream os(stdout); | |
| 1855 code->Disassemble(buffer, os); | |
| 1856 } | |
| 1857 #endif | |
| 1858 return code; | |
| 1859 } | |
| 1860 } // namespace compiler | |
| 1861 } // namespace internal | |
| 1862 } // namespace v8 | |
| OLD | NEW |