2016-02-04 09:40:55 +00:00
|
|
|
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "src/compiler/int64-lowering.h"
|
2017-09-07 13:24:32 +00:00
|
|
|
|
2016-02-04 09:40:55 +00:00
|
|
|
#include "src/compiler/common-operator.h"
|
2016-03-16 11:02:28 +00:00
|
|
|
#include "src/compiler/diamond.h"
|
2016-02-04 09:40:55 +00:00
|
|
|
#include "src/compiler/graph.h"
|
2016-02-18 15:18:41 +00:00
|
|
|
#include "src/compiler/linkage.h"
|
2016-02-04 09:40:55 +00:00
|
|
|
#include "src/compiler/machine-operator.h"
|
2016-03-31 17:04:51 +00:00
|
|
|
#include "src/compiler/node-matchers.h"
|
2016-02-18 15:18:41 +00:00
|
|
|
#include "src/compiler/node-properties.h"
|
2016-02-04 09:40:55 +00:00
|
|
|
#include "src/compiler/node.h"
|
2017-06-27 15:38:52 +00:00
|
|
|
#include "src/compiler/wasm-compiler.h"
|
2016-09-20 16:07:25 +00:00
|
|
|
#include "src/zone/zone.h"
|
2016-02-04 09:40:55 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace compiler {
|
|
|
|
|
|
|
|
Int64Lowering::Int64Lowering(Graph* graph, MachineOperatorBuilder* machine,
|
2016-02-18 15:18:41 +00:00
|
|
|
CommonOperatorBuilder* common, Zone* zone,
|
|
|
|
Signature<MachineRepresentation>* signature)
|
|
|
|
: zone_(zone),
|
|
|
|
graph_(graph),
|
2016-02-04 09:40:55 +00:00
|
|
|
machine_(machine),
|
|
|
|
common_(common),
|
2016-03-15 06:26:23 +00:00
|
|
|
state_(graph, 3),
|
2016-02-04 09:40:55 +00:00
|
|
|
stack_(zone),
|
2016-03-30 08:13:50 +00:00
|
|
|
replacements_(nullptr),
|
|
|
|
signature_(signature),
|
|
|
|
placeholder_(graph->NewNode(common->Parameter(-2, "placeholder"),
|
|
|
|
graph->start())) {
|
2016-05-25 13:02:28 +00:00
|
|
|
DCHECK_NOT_NULL(graph);
|
|
|
|
DCHECK_NOT_NULL(graph->end());
|
2016-03-30 08:13:50 +00:00
|
|
|
replacements_ = zone->NewArray<Replacement>(graph->NodeCount());
|
2016-02-04 09:40:55 +00:00
|
|
|
memset(replacements_, 0, sizeof(Replacement) * graph->NodeCount());
|
|
|
|
}
|
|
|
|
|
2016-02-18 15:18:41 +00:00
|
|
|
void Int64Lowering::LowerGraph() {
|
2016-03-08 12:41:25 +00:00
|
|
|
if (!machine()->Is32()) {
|
2016-02-18 15:18:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-03-30 08:13:50 +00:00
|
|
|
stack_.push_back({graph()->end(), 0});
|
2016-02-04 09:40:55 +00:00
|
|
|
state_.Set(graph()->end(), State::kOnStack);
|
|
|
|
|
|
|
|
while (!stack_.empty()) {
|
2016-03-30 08:13:50 +00:00
|
|
|
NodeState& top = stack_.back();
|
2016-03-15 06:26:23 +00:00
|
|
|
if (top.input_index == top.node->InputCount()) {
|
|
|
|
// All inputs of top have already been lowered, now lower top.
|
2016-03-30 08:13:50 +00:00
|
|
|
stack_.pop_back();
|
2016-03-15 06:26:23 +00:00
|
|
|
state_.Set(top.node, State::kVisited);
|
|
|
|
LowerNode(top.node);
|
2016-02-04 09:40:55 +00:00
|
|
|
} else {
|
2016-03-15 06:26:23 +00:00
|
|
|
// Push the next input onto the stack.
|
|
|
|
Node* input = top.node->InputAt(top.input_index++);
|
|
|
|
if (state_.Get(input) == State::kUnvisited) {
|
2016-03-30 08:13:50 +00:00
|
|
|
if (input->opcode() == IrOpcode::kPhi) {
|
|
|
|
// To break cycles with phi nodes we push phis on a separate stack so
|
|
|
|
// that they are processed after all other nodes.
|
|
|
|
PreparePhiReplacement(input);
|
|
|
|
stack_.push_front({input, 0});
|
2016-11-17 11:50:37 +00:00
|
|
|
} else if (input->opcode() == IrOpcode::kEffectPhi ||
|
|
|
|
input->opcode() == IrOpcode::kLoop) {
|
2016-10-18 06:30:38 +00:00
|
|
|
stack_.push_front({input, 0});
|
2016-03-30 08:13:50 +00:00
|
|
|
} else {
|
|
|
|
stack_.push_back({input, 0});
|
|
|
|
}
|
2016-03-15 06:26:23 +00:00
|
|
|
state_.Set(input, State::kOnStack);
|
2016-02-04 09:40:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-07 18:03:08 +00:00
|
|
|
namespace {
|
|
|
|
|
2018-02-09 19:19:25 +00:00
|
|
|
int GetReturnIndexAfterLowering(CallDescriptor* call_descriptor,
|
|
|
|
int old_index) {
|
2017-10-27 10:39:27 +00:00
|
|
|
int result = old_index;
|
|
|
|
for (int i = 0; i < old_index; i++) {
|
2018-02-09 19:19:25 +00:00
|
|
|
if (call_descriptor->GetReturnType(i).representation() ==
|
2017-10-27 10:39:27 +00:00
|
|
|
MachineRepresentation::kWord64) {
|
|
|
|
result++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-02-09 19:19:25 +00:00
|
|
|
int GetReturnCountAfterLowering(CallDescriptor* call_descriptor) {
|
2017-10-27 10:39:27 +00:00
|
|
|
return GetReturnIndexAfterLowering(
|
2018-02-09 19:19:25 +00:00
|
|
|
call_descriptor, static_cast<int>(call_descriptor->ReturnCount()));
|
2017-10-27 10:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int GetParameterIndexAfterLowering(
|
2016-02-18 15:18:41 +00:00
|
|
|
Signature<MachineRepresentation>* signature, int old_index) {
|
|
|
|
int result = old_index;
|
|
|
|
for (int i = 0; i < old_index; i++) {
|
|
|
|
if (signature->GetParam(i) == MachineRepresentation::kWord64) {
|
|
|
|
result++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-03-07 18:03:08 +00:00
|
|
|
int GetReturnCountAfterLowering(Signature<MachineRepresentation>* signature) {
|
|
|
|
int result = static_cast<int>(signature->return_count());
|
|
|
|
for (int i = 0; i < static_cast<int>(signature->return_count()); i++) {
|
|
|
|
if (signature->GetReturn(i) == MachineRepresentation::kWord64) {
|
|
|
|
result++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// static
|
2016-04-20 14:44:15 +00:00
|
|
|
int Int64Lowering::GetParameterCountAfterLowering(
|
2016-02-18 15:18:41 +00:00
|
|
|
Signature<MachineRepresentation>* signature) {
|
2016-04-20 14:44:15 +00:00
|
|
|
// GetParameterIndexAfterLowering(parameter_count) returns the parameter count
|
|
|
|
// after lowering.
|
2016-02-18 15:18:41 +00:00
|
|
|
return GetParameterIndexAfterLowering(
|
|
|
|
signature, static_cast<int>(signature->parameter_count()));
|
|
|
|
}
|
|
|
|
|
2017-03-07 18:03:08 +00:00
|
|
|
// static
|
|
|
|
bool Int64Lowering::IsI64AsTwoParameters(MachineOperatorBuilder* machine,
|
|
|
|
MachineRepresentation type) {
|
|
|
|
return machine->Is32() && type == MachineRepresentation::kWord64;
|
2016-02-18 15:18:41 +00:00
|
|
|
}
|
|
|
|
|
2016-06-23 11:40:16 +00:00
|
|
|
void Int64Lowering::GetIndexNodes(Node* index, Node*& index_low,
|
|
|
|
Node*& index_high) {
|
2017-01-20 13:39:25 +00:00
|
|
|
if (HasReplacementLow(index)) {
|
|
|
|
index = GetReplacementLow(index);
|
|
|
|
}
|
2016-06-23 11:40:16 +00:00
|
|
|
#if defined(V8_TARGET_LITTLE_ENDIAN)
|
|
|
|
index_low = index;
|
|
|
|
index_high = graph()->NewNode(machine()->Int32Add(), index,
|
|
|
|
graph()->NewNode(common()->Int32Constant(4)));
|
|
|
|
#elif defined(V8_TARGET_BIG_ENDIAN)
|
|
|
|
index_low = graph()->NewNode(machine()->Int32Add(), index,
|
|
|
|
graph()->NewNode(common()->Int32Constant(4)));
|
|
|
|
index_high = index;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-02-18 15:18:41 +00:00
|
|
|
void Int64Lowering::LowerNode(Node* node) {
|
2016-02-04 09:40:55 +00:00
|
|
|
switch (node->opcode()) {
|
|
|
|
case IrOpcode::kInt64Constant: {
|
2018-03-05 08:41:11 +00:00
|
|
|
int64_t value = OpParameter<int64_t>(node->op());
|
2016-02-04 09:40:55 +00:00
|
|
|
Node* low_node = graph()->NewNode(
|
|
|
|
common()->Int32Constant(static_cast<int32_t>(value & 0xFFFFFFFF)));
|
|
|
|
Node* high_node = graph()->NewNode(
|
|
|
|
common()->Int32Constant(static_cast<int32_t>(value >> 32)));
|
2016-02-18 15:18:41 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-07-22 20:55:03 +00:00
|
|
|
case IrOpcode::kLoad:
|
|
|
|
case IrOpcode::kUnalignedLoad: {
|
|
|
|
MachineRepresentation rep;
|
|
|
|
if (node->opcode() == IrOpcode::kLoad) {
|
|
|
|
rep = LoadRepresentationOf(node->op()).representation();
|
|
|
|
} else {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(IrOpcode::kUnalignedLoad, node->opcode());
|
2018-03-08 09:49:00 +00:00
|
|
|
rep = LoadRepresentationOf(node->op()).representation();
|
2016-07-22 20:55:03 +00:00
|
|
|
}
|
2016-02-18 15:18:41 +00:00
|
|
|
|
2016-07-22 20:55:03 +00:00
|
|
|
if (rep == MachineRepresentation::kWord64) {
|
2016-02-18 15:18:41 +00:00
|
|
|
Node* base = node->InputAt(0);
|
|
|
|
Node* index = node->InputAt(1);
|
2016-06-23 11:40:16 +00:00
|
|
|
Node* index_low;
|
|
|
|
Node* index_high;
|
|
|
|
GetIndexNodes(index, index_low, index_high);
|
2016-07-22 20:55:03 +00:00
|
|
|
const Operator* load_op;
|
|
|
|
|
|
|
|
if (node->opcode() == IrOpcode::kLoad) {
|
|
|
|
load_op = machine()->Load(MachineType::Int32());
|
|
|
|
} else {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(IrOpcode::kUnalignedLoad, node->opcode());
|
2016-07-22 20:55:03 +00:00
|
|
|
load_op = machine()->UnalignedLoad(MachineType::Int32());
|
|
|
|
}
|
|
|
|
|
2016-02-18 15:18:41 +00:00
|
|
|
Node* high_node;
|
|
|
|
if (node->InputCount() > 2) {
|
|
|
|
Node* effect_high = node->InputAt(2);
|
|
|
|
Node* control_high = node->InputAt(3);
|
|
|
|
high_node = graph()->NewNode(load_op, base, index_high, effect_high,
|
|
|
|
control_high);
|
|
|
|
// change the effect change from old_node --> old_effect to
|
|
|
|
// old_node --> high_node --> old_effect.
|
|
|
|
node->ReplaceInput(2, high_node);
|
|
|
|
} else {
|
|
|
|
high_node = graph()->NewNode(load_op, base, index_high);
|
|
|
|
}
|
2016-06-23 11:40:16 +00:00
|
|
|
node->ReplaceInput(1, index_low);
|
2016-02-18 15:18:41 +00:00
|
|
|
NodeProperties::ChangeOp(node, load_op);
|
|
|
|
ReplaceNode(node, node, high_node);
|
2016-03-15 12:45:43 +00:00
|
|
|
} else {
|
|
|
|
DefaultLowering(node);
|
2016-02-18 15:18:41 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-07-22 20:55:03 +00:00
|
|
|
case IrOpcode::kStore:
|
|
|
|
case IrOpcode::kUnalignedStore: {
|
|
|
|
MachineRepresentation rep;
|
|
|
|
if (node->opcode() == IrOpcode::kStore) {
|
|
|
|
rep = StoreRepresentationOf(node->op()).representation();
|
|
|
|
} else {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(IrOpcode::kUnalignedStore, node->opcode());
|
2016-07-22 20:55:03 +00:00
|
|
|
rep = UnalignedStoreRepresentationOf(node->op());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rep == MachineRepresentation::kWord64) {
|
2016-02-18 15:18:41 +00:00
|
|
|
// We change the original store node to store the low word, and create
|
|
|
|
// a new store node to store the high word. The effect and control edges
|
|
|
|
// are copied from the original store to the new store node, the effect
|
|
|
|
// edge of the original store is redirected to the new store.
|
|
|
|
Node* base = node->InputAt(0);
|
|
|
|
Node* index = node->InputAt(1);
|
2016-06-23 11:40:16 +00:00
|
|
|
Node* index_low;
|
|
|
|
Node* index_high;
|
|
|
|
GetIndexNodes(index, index_low, index_high);
|
2016-02-18 15:18:41 +00:00
|
|
|
Node* value = node->InputAt(2);
|
|
|
|
DCHECK(HasReplacementLow(value));
|
|
|
|
DCHECK(HasReplacementHigh(value));
|
|
|
|
|
2016-07-22 20:55:03 +00:00
|
|
|
const Operator* store_op;
|
|
|
|
if (node->opcode() == IrOpcode::kStore) {
|
|
|
|
WriteBarrierKind write_barrier_kind =
|
|
|
|
StoreRepresentationOf(node->op()).write_barrier_kind();
|
|
|
|
store_op = machine()->Store(StoreRepresentation(
|
|
|
|
MachineRepresentation::kWord32, write_barrier_kind));
|
|
|
|
} else {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(IrOpcode::kUnalignedStore, node->opcode());
|
2016-07-22 20:55:03 +00:00
|
|
|
store_op = machine()->UnalignedStore(MachineRepresentation::kWord32);
|
|
|
|
}
|
2016-02-18 15:18:41 +00:00
|
|
|
|
|
|
|
Node* high_node;
|
|
|
|
if (node->InputCount() > 3) {
|
|
|
|
Node* effect_high = node->InputAt(3);
|
|
|
|
Node* control_high = node->InputAt(4);
|
|
|
|
high_node = graph()->NewNode(store_op, base, index_high,
|
|
|
|
GetReplacementHigh(value), effect_high,
|
|
|
|
control_high);
|
|
|
|
node->ReplaceInput(3, high_node);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
high_node = graph()->NewNode(store_op, base, index_high,
|
|
|
|
GetReplacementHigh(value));
|
|
|
|
}
|
|
|
|
|
2016-06-23 11:40:16 +00:00
|
|
|
node->ReplaceInput(1, index_low);
|
2016-02-18 15:18:41 +00:00
|
|
|
node->ReplaceInput(2, GetReplacementLow(value));
|
|
|
|
NodeProperties::ChangeOp(node, store_op);
|
|
|
|
ReplaceNode(node, node, high_node);
|
2016-03-15 12:45:43 +00:00
|
|
|
} else {
|
2017-02-01 16:27:12 +00:00
|
|
|
DefaultLowering(node, true);
|
2016-02-18 15:18:41 +00:00
|
|
|
}
|
2016-02-04 09:40:55 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-02-18 15:18:41 +00:00
|
|
|
case IrOpcode::kStart: {
|
|
|
|
int parameter_count = GetParameterCountAfterLowering(signature());
|
|
|
|
// Only exchange the node if the parameter count actually changed.
|
2016-11-10 10:02:44 +00:00
|
|
|
if (parameter_count != static_cast<int>(signature()->parameter_count())) {
|
2016-02-18 15:18:41 +00:00
|
|
|
int delta =
|
|
|
|
parameter_count - static_cast<int>(signature()->parameter_count());
|
|
|
|
int new_output_count = node->op()->ValueOutputCount() + delta;
|
|
|
|
NodeProperties::ChangeOp(node, common()->Start(new_output_count));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kParameter: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-02-18 15:18:41 +00:00
|
|
|
// Only exchange the node if the parameter count actually changed. We do
|
|
|
|
// not even have to do the default lowering because the the start node,
|
|
|
|
// the only input of a parameter node, only changes if the parameter count
|
|
|
|
// changes.
|
|
|
|
if (GetParameterCountAfterLowering(signature()) !=
|
2016-11-10 10:02:44 +00:00
|
|
|
static_cast<int>(signature()->parameter_count())) {
|
2016-02-18 15:18:41 +00:00
|
|
|
int old_index = ParameterIndexOf(node->op());
|
[wasm] Introduce the WasmContext
The WasmContext struct introduced in this CL is used to store the
mem_size and mem_start address of the wasm memory. These variables can
be accessed at C++ level at graph build time (e.g., initialized during
instance building). When the GrowMemory runtime is invoked, the context
variables can be changed in the WasmContext at C++ level so that the
generated code will load the correct values.
This requires to insert a relocatable pointer only in the
JSToWasmWrapper (and in the other wasm entry points), the value is then
passed from function to function as an automatically added additional
parameter. The WasmContext is then dropped when creating an Interpreter
Entry or when invoking a JavaScript function. This removes the need of
patching the generated code at runtime (i.e., when the memory grows)
with respect to WASM_MEMORY_REFERENCE and WASM_MEMORY_SIZE_REFERENCE.
However, we still need to patch the code at instance build time to patch
the JSToWasmWrappers; in fact the address of the WasmContext is not
known during compilation, but only when the instance is built.
The WasmContext address is passed as the first parameter. This has the
advantage of not having to move the WasmContext around if the function
does not use many registers. This CL also changes the wasm calling
convention so that the first parameter register is different from the
return value register. The WasmContext is attached to every
WasmMemoryObject, to share the same context with multiple instances
sharing the same memory. Moreover, the nodes representing the
WasmContext variables are cached in the SSA environment, similarly to
other local variables that might change during execution. The nodes are
created when initializing the SSA environment and refreshed every time a
grow_memory or a function call happens, so that we are sure that they
always represent the correct mem_size and mem_start variables.
This CL also removes the WasmMemorySize runtime (since it's now possible
to directly retrieve mem_size from the context) and simplifies the
GrowMemory runtime (since every instance now has a memory_object).
R=ahaas@chromium.org,clemensh@chromium.org
CC=gdeepti@chromium.org
Change-Id: I3f058e641284f5a1bbbfc35a64c88da6ff08e240
Reviewed-on: https://chromium-review.googlesource.com/671008
Commit-Queue: Enrico Bacis <enricobacis@google.com>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48209}
2017-09-28 14:59:37 +00:00
|
|
|
// TODO(wasm): Make this part not wasm specific.
|
2018-04-04 17:07:48 +00:00
|
|
|
// Prevent special lowering of the WasmContext parameter.
|
|
|
|
if (old_index == kWasmContextParameterIndex) {
|
[wasm] Introduce the WasmContext
The WasmContext struct introduced in this CL is used to store the
mem_size and mem_start address of the wasm memory. These variables can
be accessed at C++ level at graph build time (e.g., initialized during
instance building). When the GrowMemory runtime is invoked, the context
variables can be changed in the WasmContext at C++ level so that the
generated code will load the correct values.
This requires to insert a relocatable pointer only in the
JSToWasmWrapper (and in the other wasm entry points), the value is then
passed from function to function as an automatically added additional
parameter. The WasmContext is then dropped when creating an Interpreter
Entry or when invoking a JavaScript function. This removes the need of
patching the generated code at runtime (i.e., when the memory grows)
with respect to WASM_MEMORY_REFERENCE and WASM_MEMORY_SIZE_REFERENCE.
However, we still need to patch the code at instance build time to patch
the JSToWasmWrappers; in fact the address of the WasmContext is not
known during compilation, but only when the instance is built.
The WasmContext address is passed as the first parameter. This has the
advantage of not having to move the WasmContext around if the function
does not use many registers. This CL also changes the wasm calling
convention so that the first parameter register is different from the
return value register. The WasmContext is attached to every
WasmMemoryObject, to share the same context with multiple instances
sharing the same memory. Moreover, the nodes representing the
WasmContext variables are cached in the SSA environment, similarly to
other local variables that might change during execution. The nodes are
created when initializing the SSA environment and refreshed every time a
grow_memory or a function call happens, so that we are sure that they
always represent the correct mem_size and mem_start variables.
This CL also removes the WasmMemorySize runtime (since it's now possible
to directly retrieve mem_size from the context) and simplifies the
GrowMemory runtime (since every instance now has a memory_object).
R=ahaas@chromium.org,clemensh@chromium.org
CC=gdeepti@chromium.org
Change-Id: I3f058e641284f5a1bbbfc35a64c88da6ff08e240
Reviewed-on: https://chromium-review.googlesource.com/671008
Commit-Queue: Enrico Bacis <enricobacis@google.com>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48209}
2017-09-28 14:59:37 +00:00
|
|
|
DefaultLowering(node);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Adjust old_index to be compliant with the signature.
|
|
|
|
--old_index;
|
2016-02-18 15:18:41 +00:00
|
|
|
int new_index = GetParameterIndexAfterLowering(signature(), old_index);
|
2018-04-04 17:07:48 +00:00
|
|
|
// Adjust new_index to consider the WasmContext parameter.
|
[wasm] Introduce the WasmContext
The WasmContext struct introduced in this CL is used to store the
mem_size and mem_start address of the wasm memory. These variables can
be accessed at C++ level at graph build time (e.g., initialized during
instance building). When the GrowMemory runtime is invoked, the context
variables can be changed in the WasmContext at C++ level so that the
generated code will load the correct values.
This requires to insert a relocatable pointer only in the
JSToWasmWrapper (and in the other wasm entry points), the value is then
passed from function to function as an automatically added additional
parameter. The WasmContext is then dropped when creating an Interpreter
Entry or when invoking a JavaScript function. This removes the need of
patching the generated code at runtime (i.e., when the memory grows)
with respect to WASM_MEMORY_REFERENCE and WASM_MEMORY_SIZE_REFERENCE.
However, we still need to patch the code at instance build time to patch
the JSToWasmWrappers; in fact the address of the WasmContext is not
known during compilation, but only when the instance is built.
The WasmContext address is passed as the first parameter. This has the
advantage of not having to move the WasmContext around if the function
does not use many registers. This CL also changes the wasm calling
convention so that the first parameter register is different from the
return value register. The WasmContext is attached to every
WasmMemoryObject, to share the same context with multiple instances
sharing the same memory. Moreover, the nodes representing the
WasmContext variables are cached in the SSA environment, similarly to
other local variables that might change during execution. The nodes are
created when initializing the SSA environment and refreshed every time a
grow_memory or a function call happens, so that we are sure that they
always represent the correct mem_size and mem_start variables.
This CL also removes the WasmMemorySize runtime (since it's now possible
to directly retrieve mem_size from the context) and simplifies the
GrowMemory runtime (since every instance now has a memory_object).
R=ahaas@chromium.org,clemensh@chromium.org
CC=gdeepti@chromium.org
Change-Id: I3f058e641284f5a1bbbfc35a64c88da6ff08e240
Reviewed-on: https://chromium-review.googlesource.com/671008
Commit-Queue: Enrico Bacis <enricobacis@google.com>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48209}
2017-09-28 14:59:37 +00:00
|
|
|
++new_index;
|
2016-02-23 15:33:06 +00:00
|
|
|
NodeProperties::ChangeOp(node, common()->Parameter(new_index));
|
2016-02-18 15:18:41 +00:00
|
|
|
|
|
|
|
if (signature()->GetParam(old_index) ==
|
|
|
|
MachineRepresentation::kWord64) {
|
2017-10-27 10:39:27 +00:00
|
|
|
Node* high_node = graph()->NewNode(common()->Parameter(new_index + 1),
|
|
|
|
graph()->start());
|
|
|
|
ReplaceNode(node, node, high_node);
|
2016-02-04 09:40:55 +00:00
|
|
|
}
|
2016-02-18 15:18:41 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kReturn: {
|
2017-03-29 09:37:49 +00:00
|
|
|
int input_count = node->InputCount();
|
2016-02-18 15:18:41 +00:00
|
|
|
DefaultLowering(node);
|
2017-03-29 09:37:49 +00:00
|
|
|
if (input_count != node->InputCount()) {
|
|
|
|
int new_return_count = GetReturnCountAfterLowering(signature());
|
|
|
|
if (static_cast<int>(signature()->return_count()) != new_return_count) {
|
|
|
|
NodeProperties::ChangeOp(node, common()->Return(new_return_count));
|
|
|
|
}
|
2016-02-04 09:40:55 +00:00
|
|
|
}
|
2016-02-18 15:18:41 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-10-16 08:49:45 +00:00
|
|
|
case IrOpcode::kTailCall: {
|
2018-02-09 19:19:25 +00:00
|
|
|
auto call_descriptor =
|
2017-10-16 08:49:45 +00:00
|
|
|
const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
|
2017-12-12 12:29:10 +00:00
|
|
|
bool returns_require_lowering =
|
2018-02-09 19:19:25 +00:00
|
|
|
GetReturnCountAfterLowering(call_descriptor) !=
|
|
|
|
static_cast<int>(call_descriptor->ReturnCount());
|
2017-12-12 12:29:10 +00:00
|
|
|
if (DefaultLowering(node) || returns_require_lowering) {
|
2017-10-16 08:49:45 +00:00
|
|
|
// Tail calls do not have return values, so adjusting the call
|
|
|
|
// descriptor is enough.
|
2018-02-09 19:19:25 +00:00
|
|
|
auto new_descriptor = GetI32WasmCallDescriptor(zone(), call_descriptor);
|
2017-10-16 08:49:45 +00:00
|
|
|
NodeProperties::ChangeOp(node, common()->TailCall(new_descriptor));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-02-18 15:18:41 +00:00
|
|
|
case IrOpcode::kCall: {
|
2018-02-09 19:19:25 +00:00
|
|
|
auto call_descriptor =
|
2016-04-18 10:51:35 +00:00
|
|
|
const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
|
2017-10-27 10:39:27 +00:00
|
|
|
bool returns_require_lowering =
|
2018-02-09 19:19:25 +00:00
|
|
|
GetReturnCountAfterLowering(call_descriptor) !=
|
|
|
|
static_cast<int>(call_descriptor->ReturnCount());
|
2017-10-27 10:39:27 +00:00
|
|
|
if (DefaultLowering(node) || returns_require_lowering) {
|
2016-02-18 15:18:41 +00:00
|
|
|
// We have to adjust the call descriptor.
|
2018-02-09 19:19:25 +00:00
|
|
|
NodeProperties::ChangeOp(node, common()->Call(GetI32WasmCallDescriptor(
|
|
|
|
zone(), call_descriptor)));
|
2016-02-18 15:18:41 +00:00
|
|
|
}
|
2017-10-27 10:39:27 +00:00
|
|
|
if (returns_require_lowering) {
|
2018-02-09 19:19:25 +00:00
|
|
|
size_t return_arity = call_descriptor->ReturnCount();
|
2017-10-27 10:39:27 +00:00
|
|
|
if (return_arity == 1) {
|
|
|
|
// We access the additional return values through projections.
|
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(common()->Projection(0), node, graph()->start());
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(common()->Projection(1), node, graph()->start());
|
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
} else {
|
|
|
|
ZoneVector<Node*> projections(return_arity, zone());
|
|
|
|
NodeProperties::CollectValueProjections(node, projections.data(),
|
|
|
|
return_arity);
|
|
|
|
for (size_t old_index = 0, new_index = 0; old_index < return_arity;
|
|
|
|
++old_index, ++new_index) {
|
|
|
|
Node* use_node = projections[old_index];
|
|
|
|
DCHECK_EQ(ProjectionIndexOf(use_node->op()), old_index);
|
2018-02-09 19:19:25 +00:00
|
|
|
DCHECK_EQ(GetReturnIndexAfterLowering(call_descriptor,
|
2017-10-27 10:39:27 +00:00
|
|
|
static_cast<int>(old_index)),
|
|
|
|
static_cast<int>(new_index));
|
|
|
|
if (new_index != old_index) {
|
|
|
|
NodeProperties::ChangeOp(
|
|
|
|
use_node, common()->Projection(new_index));
|
|
|
|
}
|
2018-02-09 19:19:25 +00:00
|
|
|
if (call_descriptor->GetReturnType(old_index).representation() ==
|
2017-10-27 10:39:27 +00:00
|
|
|
MachineRepresentation::kWord64) {
|
|
|
|
Node* high_node = graph()->NewNode(
|
|
|
|
common()->Projection(new_index + 1), node,
|
|
|
|
graph()->start());
|
|
|
|
ReplaceNode(use_node, use_node, high_node);
|
|
|
|
++new_index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-18 15:18:41 +00:00
|
|
|
}
|
|
|
|
break;
|
2016-02-04 09:40:55 +00:00
|
|
|
}
|
2016-02-23 16:30:27 +00:00
|
|
|
case IrOpcode::kWord64And: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-02-23 16:30:27 +00:00
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
|
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(machine()->Word32And(), GetReplacementLow(left),
|
|
|
|
GetReplacementLow(right));
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(machine()->Word32And(), GetReplacementHigh(left),
|
|
|
|
GetReplacementHigh(right));
|
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kTruncateInt64ToInt32: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-02-23 16:30:27 +00:00
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
ReplaceNode(node, GetReplacementLow(input), nullptr);
|
|
|
|
node->NullAllInputs();
|
|
|
|
break;
|
|
|
|
}
|
2016-03-14 15:33:15 +00:00
|
|
|
case IrOpcode::kInt64Add: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-03-14 15:33:15 +00:00
|
|
|
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
node->ReplaceInput(1, GetReplacementLow(right));
|
|
|
|
node->AppendInput(zone(), GetReplacementHigh(right));
|
|
|
|
|
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
node->ReplaceInput(0, GetReplacementLow(left));
|
|
|
|
node->InsertInput(zone(), 1, GetReplacementHigh(left));
|
|
|
|
|
|
|
|
NodeProperties::ChangeOp(node, machine()->Int32PairAdd());
|
|
|
|
// We access the additional return values through projections.
|
2016-06-21 15:46:10 +00:00
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(common()->Projection(0), node, graph()->start());
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(common()->Projection(1), node, graph()->start());
|
2016-03-14 15:33:15 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-03-16 10:56:29 +00:00
|
|
|
case IrOpcode::kInt64Sub: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-03-16 10:56:29 +00:00
|
|
|
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
node->ReplaceInput(1, GetReplacementLow(right));
|
|
|
|
node->AppendInput(zone(), GetReplacementHigh(right));
|
|
|
|
|
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
node->ReplaceInput(0, GetReplacementLow(left));
|
|
|
|
node->InsertInput(zone(), 1, GetReplacementHigh(left));
|
|
|
|
|
|
|
|
NodeProperties::ChangeOp(node, machine()->Int32PairSub());
|
|
|
|
// We access the additional return values through projections.
|
2016-06-21 15:46:10 +00:00
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(common()->Projection(0), node, graph()->start());
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(common()->Projection(1), node, graph()->start());
|
2016-03-16 10:56:29 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-03-30 10:39:04 +00:00
|
|
|
case IrOpcode::kInt64Mul: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-03-30 10:39:04 +00:00
|
|
|
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
node->ReplaceInput(1, GetReplacementLow(right));
|
|
|
|
node->AppendInput(zone(), GetReplacementHigh(right));
|
|
|
|
|
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
node->ReplaceInput(0, GetReplacementLow(left));
|
|
|
|
node->InsertInput(zone(), 1, GetReplacementHigh(left));
|
|
|
|
|
|
|
|
NodeProperties::ChangeOp(node, machine()->Int32PairMul());
|
|
|
|
// We access the additional return values through projections.
|
2016-06-21 15:46:10 +00:00
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(common()->Projection(0), node, graph()->start());
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(common()->Projection(1), node, graph()->start());
|
2016-03-30 10:39:04 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-02-23 16:30:27 +00:00
|
|
|
case IrOpcode::kWord64Or: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-02-23 16:30:27 +00:00
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
|
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(machine()->Word32Or(), GetReplacementLow(left),
|
|
|
|
GetReplacementLow(right));
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(machine()->Word32Or(), GetReplacementHigh(left),
|
|
|
|
GetReplacementHigh(right));
|
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-02-24 09:51:30 +00:00
|
|
|
case IrOpcode::kWord64Xor: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-02-24 09:51:30 +00:00
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
|
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(machine()->Word32Xor(), GetReplacementLow(left),
|
|
|
|
GetReplacementLow(right));
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(machine()->Word32Xor(), GetReplacementHigh(left),
|
|
|
|
GetReplacementHigh(right));
|
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-03-07 15:17:54 +00:00
|
|
|
case IrOpcode::kWord64Shl: {
|
|
|
|
// TODO(turbofan): if the shift count >= 32, then we can set the low word
|
|
|
|
// of the output to 0 and just calculate the high word.
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-03-07 15:17:54 +00:00
|
|
|
Node* shift = node->InputAt(1);
|
|
|
|
if (HasReplacementLow(shift)) {
|
|
|
|
// We do not have to care about the high word replacement, because
|
|
|
|
// the shift can only be between 0 and 63 anyways.
|
|
|
|
node->ReplaceInput(1, GetReplacementLow(shift));
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* value = node->InputAt(0);
|
|
|
|
node->ReplaceInput(0, GetReplacementLow(value));
|
|
|
|
node->InsertInput(zone(), 1, GetReplacementHigh(value));
|
|
|
|
|
|
|
|
NodeProperties::ChangeOp(node, machine()->Word32PairShl());
|
|
|
|
// We access the additional return values through projections.
|
2016-06-21 15:46:10 +00:00
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(common()->Projection(0), node, graph()->start());
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(common()->Projection(1), node, graph()->start());
|
2016-03-07 15:17:54 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-03-09 16:37:29 +00:00
|
|
|
case IrOpcode::kWord64Shr: {
|
|
|
|
// TODO(turbofan): if the shift count >= 32, then we can set the low word
|
|
|
|
// of the output to 0 and just calculate the high word.
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-03-09 16:37:29 +00:00
|
|
|
Node* shift = node->InputAt(1);
|
|
|
|
if (HasReplacementLow(shift)) {
|
|
|
|
// We do not have to care about the high word replacement, because
|
|
|
|
// the shift can only be between 0 and 63 anyways.
|
|
|
|
node->ReplaceInput(1, GetReplacementLow(shift));
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* value = node->InputAt(0);
|
|
|
|
node->ReplaceInput(0, GetReplacementLow(value));
|
|
|
|
node->InsertInput(zone(), 1, GetReplacementHigh(value));
|
|
|
|
|
|
|
|
NodeProperties::ChangeOp(node, machine()->Word32PairShr());
|
|
|
|
// We access the additional return values through projections.
|
2016-06-21 15:46:10 +00:00
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(common()->Projection(0), node, graph()->start());
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(common()->Projection(1), node, graph()->start());
|
2016-03-09 16:37:29 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kWord64Sar: {
|
|
|
|
// TODO(turbofan): if the shift count >= 32, then we can set the low word
|
|
|
|
// of the output to 0 and just calculate the high word.
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-03-09 16:37:29 +00:00
|
|
|
Node* shift = node->InputAt(1);
|
|
|
|
if (HasReplacementLow(shift)) {
|
|
|
|
// We do not have to care about the high word replacement, because
|
|
|
|
// the shift can only be between 0 and 63 anyways.
|
|
|
|
node->ReplaceInput(1, GetReplacementLow(shift));
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* value = node->InputAt(0);
|
|
|
|
node->ReplaceInput(0, GetReplacementLow(value));
|
|
|
|
node->InsertInput(zone(), 1, GetReplacementHigh(value));
|
|
|
|
|
|
|
|
NodeProperties::ChangeOp(node, machine()->Word32PairSar());
|
|
|
|
// We access the additional return values through projections.
|
2016-06-21 15:46:10 +00:00
|
|
|
Node* low_node =
|
|
|
|
graph()->NewNode(common()->Projection(0), node, graph()->start());
|
|
|
|
Node* high_node =
|
|
|
|
graph()->NewNode(common()->Projection(1), node, graph()->start());
|
2016-03-09 16:37:29 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-02-24 12:09:19 +00:00
|
|
|
case IrOpcode::kWord64Equal: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-02-24 12:09:19 +00:00
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
|
|
|
|
// TODO(wasm): Use explicit comparisons and && here?
|
|
|
|
Node* replacement = graph()->NewNode(
|
|
|
|
machine()->Word32Equal(),
|
|
|
|
graph()->NewNode(
|
|
|
|
machine()->Word32Or(),
|
|
|
|
graph()->NewNode(machine()->Word32Xor(), GetReplacementLow(left),
|
|
|
|
GetReplacementLow(right)),
|
|
|
|
graph()->NewNode(machine()->Word32Xor(), GetReplacementHigh(left),
|
|
|
|
GetReplacementHigh(right))),
|
|
|
|
graph()->NewNode(common()->Int32Constant(0)));
|
|
|
|
|
|
|
|
ReplaceNode(node, replacement, nullptr);
|
|
|
|
break;
|
|
|
|
}
|
2016-02-25 12:14:35 +00:00
|
|
|
case IrOpcode::kInt64LessThan: {
|
|
|
|
LowerComparison(node, machine()->Int32LessThan(),
|
|
|
|
machine()->Uint32LessThan());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kInt64LessThanOrEqual: {
|
|
|
|
LowerComparison(node, machine()->Int32LessThan(),
|
|
|
|
machine()->Uint32LessThanOrEqual());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kUint64LessThan: {
|
|
|
|
LowerComparison(node, machine()->Uint32LessThan(),
|
|
|
|
machine()->Uint32LessThan());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kUint64LessThanOrEqual: {
|
|
|
|
LowerComparison(node, machine()->Uint32LessThan(),
|
|
|
|
machine()->Uint32LessThanOrEqual());
|
|
|
|
break;
|
|
|
|
}
|
2016-03-09 16:20:59 +00:00
|
|
|
case IrOpcode::kChangeInt32ToInt64: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-03-09 16:20:59 +00:00
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
if (HasReplacementLow(input)) {
|
|
|
|
input = GetReplacementLow(input);
|
|
|
|
}
|
|
|
|
// We use SAR to preserve the sign in the high word.
|
|
|
|
ReplaceNode(
|
|
|
|
node, input,
|
|
|
|
graph()->NewNode(machine()->Word32Sar(), input,
|
|
|
|
graph()->NewNode(common()->Int32Constant(31))));
|
|
|
|
node->NullAllInputs();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IrOpcode::kChangeUint32ToUint64: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-03-09 16:20:59 +00:00
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
if (HasReplacementLow(input)) {
|
|
|
|
input = GetReplacementLow(input);
|
|
|
|
}
|
|
|
|
ReplaceNode(node, input, graph()->NewNode(common()->Int32Constant(0)));
|
|
|
|
node->NullAllInputs();
|
|
|
|
break;
|
|
|
|
}
|
2016-03-15 12:45:43 +00:00
|
|
|
case IrOpcode::kBitcastInt64ToFloat64: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-03-15 12:45:43 +00:00
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
Node* stack_slot = graph()->NewNode(
|
|
|
|
machine()->StackSlot(MachineRepresentation::kWord64));
|
|
|
|
|
|
|
|
Node* store_high_word = graph()->NewNode(
|
|
|
|
machine()->Store(
|
|
|
|
StoreRepresentation(MachineRepresentation::kWord32,
|
|
|
|
WriteBarrierKind::kNoWriteBarrier)),
|
2016-06-23 11:40:16 +00:00
|
|
|
stack_slot,
|
2017-03-07 18:03:08 +00:00
|
|
|
graph()->NewNode(
|
|
|
|
common()->Int32Constant(kInt64UpperHalfMemoryOffset)),
|
2016-03-15 12:45:43 +00:00
|
|
|
GetReplacementHigh(input), graph()->start(), graph()->start());
|
|
|
|
|
|
|
|
Node* store_low_word = graph()->NewNode(
|
|
|
|
machine()->Store(
|
|
|
|
StoreRepresentation(MachineRepresentation::kWord32,
|
|
|
|
WriteBarrierKind::kNoWriteBarrier)),
|
2016-06-23 11:40:16 +00:00
|
|
|
stack_slot,
|
2017-03-07 18:03:08 +00:00
|
|
|
graph()->NewNode(
|
|
|
|
common()->Int32Constant(kInt64LowerHalfMemoryOffset)),
|
2016-03-15 12:45:43 +00:00
|
|
|
GetReplacementLow(input), store_high_word, graph()->start());
|
|
|
|
|
|
|
|
Node* load =
|
|
|
|
graph()->NewNode(machine()->Load(MachineType::Float64()), stack_slot,
|
|
|
|
graph()->NewNode(common()->Int32Constant(0)),
|
|
|
|
store_low_word, graph()->start());
|
|
|
|
|
|
|
|
ReplaceNode(node, load, nullptr);
|
|
|
|
break;
|
|
|
|
}
|
2016-03-15 12:17:09 +00:00
|
|
|
case IrOpcode::kBitcastFloat64ToInt64: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-03-15 12:17:09 +00:00
|
|
|
Node* input = node->InputAt(0);
|
2016-03-15 12:45:43 +00:00
|
|
|
if (HasReplacementLow(input)) {
|
|
|
|
input = GetReplacementLow(input);
|
|
|
|
}
|
2016-03-15 12:17:09 +00:00
|
|
|
Node* stack_slot = graph()->NewNode(
|
|
|
|
machine()->StackSlot(MachineRepresentation::kWord64));
|
|
|
|
Node* store = graph()->NewNode(
|
|
|
|
machine()->Store(
|
|
|
|
StoreRepresentation(MachineRepresentation::kFloat64,
|
|
|
|
WriteBarrierKind::kNoWriteBarrier)),
|
|
|
|
stack_slot, graph()->NewNode(common()->Int32Constant(0)), input,
|
|
|
|
graph()->start(), graph()->start());
|
|
|
|
|
2016-06-23 11:40:16 +00:00
|
|
|
Node* high_node = graph()->NewNode(
|
|
|
|
machine()->Load(MachineType::Int32()), stack_slot,
|
2017-03-07 18:03:08 +00:00
|
|
|
graph()->NewNode(
|
|
|
|
common()->Int32Constant(kInt64UpperHalfMemoryOffset)),
|
|
|
|
store, graph()->start());
|
2016-02-23 16:30:27 +00:00
|
|
|
|
2016-06-23 11:40:16 +00:00
|
|
|
Node* low_node = graph()->NewNode(
|
|
|
|
machine()->Load(MachineType::Int32()), stack_slot,
|
2017-03-07 18:03:08 +00:00
|
|
|
graph()->NewNode(
|
|
|
|
common()->Int32Constant(kInt64LowerHalfMemoryOffset)),
|
|
|
|
store, graph()->start());
|
2016-03-15 12:17:09 +00:00
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
break;
|
|
|
|
}
|
2016-03-31 17:04:51 +00:00
|
|
|
case IrOpcode::kWord64Ror: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-03-31 17:04:51 +00:00
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
Node* shift = HasReplacementLow(node->InputAt(1))
|
|
|
|
? GetReplacementLow(node->InputAt(1))
|
|
|
|
: node->InputAt(1);
|
|
|
|
Int32Matcher m(shift);
|
|
|
|
if (m.HasValue()) {
|
|
|
|
// Precondition: 0 <= shift < 64.
|
2017-12-02 00:30:37 +00:00
|
|
|
int32_t shift_value = m.Value() & 0x3F;
|
2016-03-31 17:04:51 +00:00
|
|
|
if (shift_value == 0) {
|
|
|
|
ReplaceNode(node, GetReplacementLow(input),
|
|
|
|
GetReplacementHigh(input));
|
|
|
|
} else if (shift_value == 32) {
|
|
|
|
ReplaceNode(node, GetReplacementHigh(input),
|
|
|
|
GetReplacementLow(input));
|
|
|
|
} else {
|
|
|
|
Node* low_input;
|
|
|
|
Node* high_input;
|
|
|
|
if (shift_value < 32) {
|
|
|
|
low_input = GetReplacementLow(input);
|
|
|
|
high_input = GetReplacementHigh(input);
|
|
|
|
} else {
|
|
|
|
low_input = GetReplacementHigh(input);
|
|
|
|
high_input = GetReplacementLow(input);
|
|
|
|
}
|
2017-12-02 00:30:37 +00:00
|
|
|
int32_t masked_shift_value = shift_value & 0x1F;
|
2016-03-31 17:04:51 +00:00
|
|
|
Node* masked_shift =
|
|
|
|
graph()->NewNode(common()->Int32Constant(masked_shift_value));
|
|
|
|
Node* inv_shift = graph()->NewNode(
|
|
|
|
common()->Int32Constant(32 - masked_shift_value));
|
|
|
|
|
|
|
|
Node* low_node = graph()->NewNode(
|
|
|
|
machine()->Word32Or(),
|
|
|
|
graph()->NewNode(machine()->Word32Shr(), low_input, masked_shift),
|
|
|
|
graph()->NewNode(machine()->Word32Shl(), high_input, inv_shift));
|
|
|
|
Node* high_node = graph()->NewNode(
|
|
|
|
machine()->Word32Or(), graph()->NewNode(machine()->Word32Shr(),
|
|
|
|
high_input, masked_shift),
|
|
|
|
graph()->NewNode(machine()->Word32Shl(), low_input, inv_shift));
|
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Node* safe_shift = shift;
|
|
|
|
if (!machine()->Word32ShiftIsSafe()) {
|
|
|
|
safe_shift =
|
|
|
|
graph()->NewNode(machine()->Word32And(), shift,
|
2017-12-02 00:30:37 +00:00
|
|
|
graph()->NewNode(common()->Int32Constant(0x1F)));
|
2016-03-31 17:04:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// By creating this bit-mask with SAR and SHL we do not have to deal
|
|
|
|
// with shift == 0 as a special case.
|
|
|
|
Node* inv_mask = graph()->NewNode(
|
|
|
|
machine()->Word32Shl(),
|
|
|
|
graph()->NewNode(machine()->Word32Sar(),
|
|
|
|
graph()->NewNode(common()->Int32Constant(
|
|
|
|
std::numeric_limits<int32_t>::min())),
|
|
|
|
safe_shift),
|
|
|
|
graph()->NewNode(common()->Int32Constant(1)));
|
|
|
|
|
|
|
|
Node* bit_mask =
|
|
|
|
graph()->NewNode(machine()->Word32Xor(), inv_mask,
|
|
|
|
graph()->NewNode(common()->Int32Constant(-1)));
|
|
|
|
|
|
|
|
// We have to mask the shift value for this comparison. If
|
|
|
|
// !machine()->Word32ShiftIsSafe() then the masking should already be
|
|
|
|
// part of the graph.
|
|
|
|
Node* masked_shift6 = shift;
|
|
|
|
if (machine()->Word32ShiftIsSafe()) {
|
|
|
|
masked_shift6 =
|
|
|
|
graph()->NewNode(machine()->Word32And(), shift,
|
2017-12-02 00:30:37 +00:00
|
|
|
graph()->NewNode(common()->Int32Constant(0x3F)));
|
2016-03-31 17:04:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Diamond lt32(
|
|
|
|
graph(), common(),
|
|
|
|
graph()->NewNode(machine()->Int32LessThan(), masked_shift6,
|
|
|
|
graph()->NewNode(common()->Int32Constant(32))));
|
|
|
|
|
|
|
|
// The low word and the high word can be swapped either at the input or
|
|
|
|
// at the output. We swap the inputs so that shift does not have to be
|
|
|
|
// kept for so long in a register.
|
|
|
|
Node* input_low =
|
|
|
|
lt32.Phi(MachineRepresentation::kWord32, GetReplacementLow(input),
|
|
|
|
GetReplacementHigh(input));
|
|
|
|
Node* input_high =
|
|
|
|
lt32.Phi(MachineRepresentation::kWord32, GetReplacementHigh(input),
|
|
|
|
GetReplacementLow(input));
|
|
|
|
|
|
|
|
Node* rotate_low =
|
|
|
|
graph()->NewNode(machine()->Word32Ror(), input_low, safe_shift);
|
|
|
|
Node* rotate_high =
|
|
|
|
graph()->NewNode(machine()->Word32Ror(), input_high, safe_shift);
|
|
|
|
|
|
|
|
Node* low_node = graph()->NewNode(
|
|
|
|
machine()->Word32Or(),
|
|
|
|
graph()->NewNode(machine()->Word32And(), rotate_low, bit_mask),
|
|
|
|
graph()->NewNode(machine()->Word32And(), rotate_high, inv_mask));
|
|
|
|
|
|
|
|
Node* high_node = graph()->NewNode(
|
|
|
|
machine()->Word32Or(),
|
|
|
|
graph()->NewNode(machine()->Word32And(), rotate_high, bit_mask),
|
|
|
|
graph()->NewNode(machine()->Word32And(), rotate_low, inv_mask));
|
|
|
|
|
|
|
|
ReplaceNode(node, low_node, high_node);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-03-16 11:02:28 +00:00
|
|
|
case IrOpcode::kWord64Clz: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-03-16 11:02:28 +00:00
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
Diamond d(
|
|
|
|
graph(), common(),
|
|
|
|
graph()->NewNode(machine()->Word32Equal(), GetReplacementHigh(input),
|
|
|
|
graph()->NewNode(common()->Int32Constant(0))));
|
|
|
|
|
|
|
|
Node* low_node = d.Phi(
|
|
|
|
MachineRepresentation::kWord32,
|
|
|
|
graph()->NewNode(machine()->Int32Add(),
|
|
|
|
graph()->NewNode(machine()->Word32Clz(),
|
|
|
|
GetReplacementLow(input)),
|
|
|
|
graph()->NewNode(common()->Int32Constant(32))),
|
|
|
|
graph()->NewNode(machine()->Word32Clz(), GetReplacementHigh(input)));
|
|
|
|
ReplaceNode(node, low_node, graph()->NewNode(common()->Int32Constant(0)));
|
|
|
|
break;
|
|
|
|
}
|
2016-03-16 12:14:44 +00:00
|
|
|
case IrOpcode::kWord64Ctz: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-03-16 12:14:44 +00:00
|
|
|
DCHECK(machine()->Word32Ctz().IsSupported());
|
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
Diamond d(
|
|
|
|
graph(), common(),
|
|
|
|
graph()->NewNode(machine()->Word32Equal(), GetReplacementLow(input),
|
|
|
|
graph()->NewNode(common()->Int32Constant(0))));
|
|
|
|
Node* low_node =
|
|
|
|
d.Phi(MachineRepresentation::kWord32,
|
|
|
|
graph()->NewNode(machine()->Int32Add(),
|
|
|
|
graph()->NewNode(machine()->Word32Ctz().op(),
|
|
|
|
GetReplacementHigh(input)),
|
|
|
|
graph()->NewNode(common()->Int32Constant(32))),
|
|
|
|
graph()->NewNode(machine()->Word32Ctz().op(),
|
|
|
|
GetReplacementLow(input)));
|
|
|
|
ReplaceNode(node, low_node, graph()->NewNode(common()->Int32Constant(0)));
|
|
|
|
break;
|
|
|
|
}
|
2016-03-15 10:41:55 +00:00
|
|
|
case IrOpcode::kWord64Popcnt: {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(1, node->InputCount());
|
2016-03-15 10:41:55 +00:00
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
// We assume that a Word64Popcnt node only has been created if
|
|
|
|
// Word32Popcnt is actually supported.
|
|
|
|
DCHECK(machine()->Word32Popcnt().IsSupported());
|
|
|
|
ReplaceNode(node, graph()->NewNode(
|
|
|
|
machine()->Int32Add(),
|
|
|
|
graph()->NewNode(machine()->Word32Popcnt().op(),
|
|
|
|
GetReplacementLow(input)),
|
|
|
|
graph()->NewNode(machine()->Word32Popcnt().op(),
|
|
|
|
GetReplacementHigh(input))),
|
|
|
|
graph()->NewNode(common()->Int32Constant(0)));
|
|
|
|
break;
|
|
|
|
}
|
2016-03-30 08:13:50 +00:00
|
|
|
case IrOpcode::kPhi: {
|
|
|
|
MachineRepresentation rep = PhiRepresentationOf(node->op());
|
|
|
|
if (rep == MachineRepresentation::kWord64) {
|
|
|
|
// The replacement nodes have already been created, we only have to
|
|
|
|
// replace placeholder nodes.
|
|
|
|
Node* low_node = GetReplacementLow(node);
|
|
|
|
Node* high_node = GetReplacementHigh(node);
|
|
|
|
for (int i = 0; i < node->op()->ValueInputCount(); i++) {
|
|
|
|
low_node->ReplaceInput(i, GetReplacementLow(node->InputAt(i)));
|
|
|
|
high_node->ReplaceInput(i, GetReplacementHigh(node->InputAt(i)));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DefaultLowering(node);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-07-29 19:31:54 +00:00
|
|
|
case IrOpcode::kWord64ReverseBytes: {
|
|
|
|
Node* input = node->InputAt(0);
|
|
|
|
ReplaceNode(node, graph()->NewNode(machine()->Word32ReverseBytes().op(),
|
|
|
|
GetReplacementHigh(input)),
|
|
|
|
graph()->NewNode(machine()->Word32ReverseBytes().op(),
|
|
|
|
GetReplacementLow(input)));
|
|
|
|
break;
|
|
|
|
}
|
2016-02-23 16:30:27 +00:00
|
|
|
|
2016-02-18 15:18:41 +00:00
|
|
|
default: { DefaultLowering(node); }
|
2016-02-04 09:40:55 +00:00
|
|
|
}
|
2016-03-31 17:04:51 +00:00
|
|
|
} // NOLINT(readability/fn_size)
|
2016-02-04 09:40:55 +00:00
|
|
|
|
2016-02-25 12:14:35 +00:00
|
|
|
void Int64Lowering::LowerComparison(Node* node, const Operator* high_word_op,
|
|
|
|
const Operator* low_word_op) {
|
2017-09-25 09:45:55 +00:00
|
|
|
DCHECK_EQ(2, node->InputCount());
|
2016-02-25 12:14:35 +00:00
|
|
|
Node* left = node->InputAt(0);
|
|
|
|
Node* right = node->InputAt(1);
|
|
|
|
Node* replacement = graph()->NewNode(
|
|
|
|
machine()->Word32Or(),
|
|
|
|
graph()->NewNode(high_word_op, GetReplacementHigh(left),
|
|
|
|
GetReplacementHigh(right)),
|
|
|
|
graph()->NewNode(
|
|
|
|
machine()->Word32And(),
|
|
|
|
graph()->NewNode(machine()->Word32Equal(), GetReplacementHigh(left),
|
|
|
|
GetReplacementHigh(right)),
|
|
|
|
graph()->NewNode(low_word_op, GetReplacementLow(left),
|
|
|
|
GetReplacementLow(right))));
|
|
|
|
|
|
|
|
ReplaceNode(node, replacement, nullptr);
|
|
|
|
}
|
|
|
|
|
2017-02-01 16:27:12 +00:00
|
|
|
bool Int64Lowering::DefaultLowering(Node* node, bool low_word_only) {
|
2016-02-18 15:18:41 +00:00
|
|
|
bool something_changed = false;
|
|
|
|
for (int i = NodeProperties::PastValueIndex(node) - 1; i >= 0; i--) {
|
|
|
|
Node* input = node->InputAt(i);
|
|
|
|
if (HasReplacementLow(input)) {
|
|
|
|
something_changed = true;
|
|
|
|
node->ReplaceInput(i, GetReplacementLow(input));
|
|
|
|
}
|
2017-02-01 16:27:12 +00:00
|
|
|
if (!low_word_only && HasReplacementHigh(input)) {
|
2016-02-18 15:18:41 +00:00
|
|
|
something_changed = true;
|
|
|
|
node->InsertInput(zone(), i + 1, GetReplacementHigh(input));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return something_changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Int64Lowering::ReplaceNode(Node* old, Node* new_low, Node* new_high) {
|
|
|
|
// if new_low == nullptr, then also new_high == nullptr.
|
|
|
|
DCHECK(new_low != nullptr || new_high == nullptr);
|
|
|
|
replacements_[old->id()].low = new_low;
|
|
|
|
replacements_[old->id()].high = new_high;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Int64Lowering::HasReplacementLow(Node* node) {
|
|
|
|
return replacements_[node->id()].low != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* Int64Lowering::GetReplacementLow(Node* node) {
|
|
|
|
Node* result = replacements_[node->id()].low;
|
|
|
|
DCHECK(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Int64Lowering::HasReplacementHigh(Node* node) {
|
|
|
|
return replacements_[node->id()].high != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* Int64Lowering::GetReplacementHigh(Node* node) {
|
|
|
|
Node* result = replacements_[node->id()].high;
|
|
|
|
DCHECK(result);
|
|
|
|
return result;
|
|
|
|
}
|
2016-03-30 08:13:50 +00:00
|
|
|
|
|
|
|
void Int64Lowering::PreparePhiReplacement(Node* phi) {
|
|
|
|
MachineRepresentation rep = PhiRepresentationOf(phi->op());
|
|
|
|
if (rep == MachineRepresentation::kWord64) {
|
|
|
|
// We have to create the replacements for a phi node before we actually
|
|
|
|
// lower the phi to break potential cycles in the graph. The replacements of
|
|
|
|
// input nodes do not exist yet, so we use a placeholder node to pass the
|
|
|
|
// graph verifier.
|
|
|
|
int value_count = phi->op()->ValueInputCount();
|
|
|
|
Node** inputs_low = zone()->NewArray<Node*>(value_count + 1);
|
|
|
|
Node** inputs_high = zone()->NewArray<Node*>(value_count + 1);
|
|
|
|
for (int i = 0; i < value_count; i++) {
|
|
|
|
inputs_low[i] = placeholder_;
|
|
|
|
inputs_high[i] = placeholder_;
|
|
|
|
}
|
|
|
|
inputs_low[value_count] = NodeProperties::GetControlInput(phi, 0);
|
|
|
|
inputs_high[value_count] = NodeProperties::GetControlInput(phi, 0);
|
|
|
|
ReplaceNode(phi,
|
|
|
|
graph()->NewNode(
|
|
|
|
common()->Phi(MachineRepresentation::kWord32, value_count),
|
|
|
|
value_count + 1, inputs_low, false),
|
|
|
|
graph()->NewNode(
|
|
|
|
common()->Phi(MachineRepresentation::kWord32, value_count),
|
|
|
|
value_count + 1, inputs_high, false));
|
|
|
|
}
|
|
|
|
}
|
2016-02-04 09:40:55 +00:00
|
|
|
} // namespace compiler
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|