2015-12-11 12:26:16 +00:00
|
|
|
// Copyright 2015 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.
|
|
|
|
|
2015-12-17 14:52:27 +00:00
|
|
|
#include "src/compiler/wasm-compiler.h"
|
|
|
|
|
|
|
|
#include "src/isolate-inl.h"
|
|
|
|
|
|
|
|
#include "src/base/platform/platform.h"
|
2015-12-11 12:26:16 +00:00
|
|
|
|
|
|
|
#include "src/compiler/access-builder.h"
|
|
|
|
#include "src/compiler/change-lowering.h"
|
|
|
|
#include "src/compiler/common-operator.h"
|
|
|
|
#include "src/compiler/diamond.h"
|
|
|
|
#include "src/compiler/graph.h"
|
|
|
|
#include "src/compiler/graph-visualizer.h"
|
|
|
|
#include "src/compiler/instruction-selector.h"
|
2016-02-04 09:40:55 +00:00
|
|
|
#include "src/compiler/int64-lowering.h"
|
2015-12-11 12:26:16 +00:00
|
|
|
#include "src/compiler/js-generic-lowering.h"
|
|
|
|
#include "src/compiler/js-graph.h"
|
|
|
|
#include "src/compiler/js-operator.h"
|
|
|
|
#include "src/compiler/linkage.h"
|
|
|
|
#include "src/compiler/machine-operator.h"
|
|
|
|
#include "src/compiler/node-matchers.h"
|
|
|
|
#include "src/compiler/pipeline.h"
|
|
|
|
#include "src/compiler/simplified-lowering.h"
|
|
|
|
#include "src/compiler/simplified-operator.h"
|
|
|
|
#include "src/compiler/source-position.h"
|
|
|
|
#include "src/compiler/typer.h"
|
|
|
|
|
|
|
|
#include "src/code-factory.h"
|
|
|
|
#include "src/code-stubs.h"
|
2016-01-29 11:21:34 +00:00
|
|
|
#include "src/factory.h"
|
|
|
|
#include "src/log-inl.h"
|
|
|
|
#include "src/profiler/cpu-profiler.h"
|
2015-12-11 12:26:16 +00:00
|
|
|
|
|
|
|
#include "src/wasm/ast-decoder.h"
|
|
|
|
#include "src/wasm/wasm-module.h"
|
|
|
|
#include "src/wasm/wasm-opcodes.h"
|
|
|
|
|
|
|
|
// TODO(titzer): pull WASM_64 up to a common header.
|
|
|
|
#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
|
|
|
|
#define WASM_64 1
|
|
|
|
#else
|
|
|
|
#define WASM_64 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace compiler {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
const Operator* UnsupportedOpcode(wasm::WasmOpcode opcode) {
|
|
|
|
if (wasm::WasmOpcodes::IsSupported(opcode)) {
|
|
|
|
V8_Fatal(__FILE__, __LINE__,
|
|
|
|
"Unsupported opcode #%d:%s reported as supported", opcode,
|
|
|
|
wasm::WasmOpcodes::OpcodeName(opcode));
|
|
|
|
}
|
|
|
|
V8_Fatal(__FILE__, __LINE__, "Unsupported opcode #%d:%s", opcode,
|
|
|
|
wasm::WasmOpcodes::OpcodeName(opcode));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MergeControlToEnd(JSGraph* jsgraph, Node* node) {
|
|
|
|
Graph* g = jsgraph->graph();
|
|
|
|
if (g->end()) {
|
|
|
|
NodeProperties::MergeControlToEnd(g, jsgraph->common(), node);
|
|
|
|
} else {
|
|
|
|
g->SetEnd(g->NewNode(jsgraph->common()->End(1), node));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum TrapReason {
|
|
|
|
kTrapUnreachable,
|
|
|
|
kTrapMemOutOfBounds,
|
|
|
|
kTrapDivByZero,
|
|
|
|
kTrapDivUnrepresentable,
|
|
|
|
kTrapRemByZero,
|
|
|
|
kTrapFloatUnrepresentable,
|
|
|
|
kTrapFuncInvalid,
|
|
|
|
kTrapFuncSigMismatch,
|
|
|
|
kTrapCount
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const char* kTrapMessages[] = {
|
|
|
|
"unreachable", "memory access out of bounds",
|
|
|
|
"divide by zero", "divide result unrepresentable",
|
|
|
|
"remainder by zero", "integer result unrepresentable",
|
|
|
|
"invalid function", "function signature mismatch"};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
// A helper that handles building graph fragments for trapping.
|
|
|
|
// To avoid generating a ton of redundant code that just calls the runtime
|
|
|
|
// to trap, we generate a per-trap-reason block of code that all trap sites
|
|
|
|
// in this function will branch to.
|
|
|
|
class WasmTrapHelper : public ZoneObject {
|
|
|
|
public:
|
|
|
|
explicit WasmTrapHelper(WasmGraphBuilder* builder)
|
|
|
|
: builder_(builder),
|
|
|
|
jsgraph_(builder->jsgraph()),
|
|
|
|
graph_(builder->jsgraph() ? builder->jsgraph()->graph() : nullptr) {
|
|
|
|
for (int i = 0; i < kTrapCount; i++) traps_[i] = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the current control path trap to unreachable.
|
|
|
|
void Unreachable() { ConnectTrap(kTrapUnreachable); }
|
|
|
|
|
2016-01-28 16:56:54 +00:00
|
|
|
// Always trap with the given reason.
|
|
|
|
void TrapAlways(TrapReason reason) { ConnectTrap(reason); }
|
|
|
|
|
2015-12-11 12:26:16 +00:00
|
|
|
// Add a check that traps if {node} is equal to {val}.
|
|
|
|
Node* TrapIfEq32(TrapReason reason, Node* node, int32_t val) {
|
|
|
|
Int32Matcher m(node);
|
|
|
|
if (m.HasValue() && !m.Is(val)) return graph()->start();
|
|
|
|
if (val == 0) {
|
|
|
|
AddTrapIfFalse(reason, node);
|
|
|
|
} else {
|
|
|
|
AddTrapIfTrue(reason,
|
|
|
|
graph()->NewNode(jsgraph()->machine()->Word32Equal(), node,
|
|
|
|
jsgraph()->Int32Constant(val)));
|
|
|
|
}
|
|
|
|
return builder_->Control();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a check that traps if {node} is zero.
|
|
|
|
Node* ZeroCheck32(TrapReason reason, Node* node) {
|
|
|
|
return TrapIfEq32(reason, node, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a check that traps if {node} is equal to {val}.
|
|
|
|
Node* TrapIfEq64(TrapReason reason, Node* node, int64_t val) {
|
|
|
|
Int64Matcher m(node);
|
|
|
|
if (m.HasValue() && !m.Is(val)) return graph()->start();
|
|
|
|
AddTrapIfTrue(reason,
|
|
|
|
graph()->NewNode(jsgraph()->machine()->Word64Equal(), node,
|
|
|
|
jsgraph()->Int64Constant(val)));
|
|
|
|
return builder_->Control();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a check that traps if {node} is zero.
|
|
|
|
Node* ZeroCheck64(TrapReason reason, Node* node) {
|
|
|
|
return TrapIfEq64(reason, node, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a trap if {cond} is true.
|
|
|
|
void AddTrapIfTrue(TrapReason reason, Node* cond) {
|
|
|
|
AddTrapIf(reason, cond, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a trap if {cond} is false.
|
|
|
|
void AddTrapIfFalse(TrapReason reason, Node* cond) {
|
|
|
|
AddTrapIf(reason, cond, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a trap if {cond} is true or false according to {iftrue}.
|
|
|
|
void AddTrapIf(TrapReason reason, Node* cond, bool iftrue) {
|
|
|
|
Node** effect_ptr = builder_->effect_;
|
|
|
|
Node** control_ptr = builder_->control_;
|
|
|
|
Node* before = *effect_ptr;
|
|
|
|
BranchHint hint = iftrue ? BranchHint::kFalse : BranchHint::kTrue;
|
|
|
|
Node* branch = graph()->NewNode(common()->Branch(hint), cond, *control_ptr);
|
|
|
|
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
|
|
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
|
|
|
|
|
|
*control_ptr = iftrue ? if_true : if_false;
|
|
|
|
ConnectTrap(reason);
|
|
|
|
*control_ptr = iftrue ? if_false : if_true;
|
|
|
|
*effect_ptr = before;
|
|
|
|
}
|
|
|
|
|
2016-01-28 16:56:54 +00:00
|
|
|
Node* GetTrapValue(wasm::FunctionSig* sig) {
|
|
|
|
if (sig->return_count() > 0) {
|
|
|
|
switch (sig->GetReturn()) {
|
|
|
|
case wasm::kAstI32:
|
|
|
|
return jsgraph()->Int32Constant(0xdeadbeef);
|
|
|
|
case wasm::kAstI64:
|
|
|
|
return jsgraph()->Int64Constant(0xdeadbeefdeadbeef);
|
|
|
|
case wasm::kAstF32:
|
|
|
|
return jsgraph()->Float32Constant(bit_cast<float>(0xdeadbeef));
|
|
|
|
case wasm::kAstF64:
|
|
|
|
return jsgraph()->Float64Constant(
|
|
|
|
bit_cast<double>(0xdeadbeefdeadbeef));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return jsgraph()->Int32Constant(0xdeadbeef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-11 12:26:16 +00:00
|
|
|
private:
|
|
|
|
WasmGraphBuilder* builder_;
|
|
|
|
JSGraph* jsgraph_;
|
|
|
|
Graph* graph_;
|
|
|
|
Node* traps_[kTrapCount];
|
|
|
|
Node* effects_[kTrapCount];
|
|
|
|
|
|
|
|
JSGraph* jsgraph() { return jsgraph_; }
|
|
|
|
Graph* graph() { return jsgraph_->graph(); }
|
|
|
|
CommonOperatorBuilder* common() { return jsgraph()->common(); }
|
|
|
|
|
|
|
|
void ConnectTrap(TrapReason reason) {
|
|
|
|
if (traps_[reason] == nullptr) {
|
|
|
|
// Create trap code for the first time this trap is used.
|
|
|
|
return BuildTrapCode(reason);
|
|
|
|
}
|
|
|
|
// Connect the current control and effect to the existing trap code.
|
|
|
|
builder_->AppendToMerge(traps_[reason], builder_->Control());
|
|
|
|
builder_->AppendToPhi(traps_[reason], effects_[reason], builder_->Effect());
|
|
|
|
}
|
|
|
|
|
|
|
|
void BuildTrapCode(TrapReason reason) {
|
|
|
|
Node* exception = builder_->String(kTrapMessages[reason]);
|
|
|
|
Node* end;
|
|
|
|
Node** control_ptr = builder_->control_;
|
|
|
|
Node** effect_ptr = builder_->effect_;
|
|
|
|
wasm::ModuleEnv* module = builder_->module_;
|
|
|
|
*control_ptr = traps_[reason] =
|
|
|
|
graph()->NewNode(common()->Merge(1), *control_ptr);
|
|
|
|
*effect_ptr = effects_[reason] =
|
|
|
|
graph()->NewNode(common()->EffectPhi(1), *effect_ptr, *control_ptr);
|
|
|
|
|
2016-01-27 11:04:02 +00:00
|
|
|
if (module && !module->instance->context.is_null()) {
|
2015-12-11 12:26:16 +00:00
|
|
|
// Use the module context to call the runtime to throw an exception.
|
|
|
|
Runtime::FunctionId f = Runtime::kThrow;
|
|
|
|
const Runtime::Function* fun = Runtime::FunctionForId(f);
|
|
|
|
CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
|
|
|
|
jsgraph()->zone(), f, fun->nargs, Operator::kNoProperties,
|
|
|
|
CallDescriptor::kNoFlags);
|
|
|
|
Node* inputs[] = {
|
|
|
|
jsgraph()->CEntryStubConstant(fun->result_size), // C entry
|
|
|
|
exception, // exception
|
|
|
|
jsgraph()->ExternalConstant(
|
|
|
|
ExternalReference(f, jsgraph()->isolate())), // ref
|
|
|
|
jsgraph()->Int32Constant(fun->nargs), // arity
|
2016-01-27 11:04:02 +00:00
|
|
|
jsgraph()->Constant(module->instance->context), // context
|
2015-12-11 12:26:16 +00:00
|
|
|
*effect_ptr,
|
|
|
|
*control_ptr};
|
|
|
|
|
|
|
|
Node* node = graph()->NewNode(
|
|
|
|
common()->Call(desc), static_cast<int>(arraysize(inputs)), inputs);
|
|
|
|
*control_ptr = node;
|
|
|
|
*effect_ptr = node;
|
|
|
|
}
|
|
|
|
if (false) {
|
|
|
|
// End the control flow with a throw
|
|
|
|
Node* thrw =
|
|
|
|
graph()->NewNode(common()->Throw(), jsgraph()->ZeroConstant(),
|
|
|
|
*effect_ptr, *control_ptr);
|
|
|
|
end = thrw;
|
|
|
|
} else {
|
|
|
|
// End the control flow with returning 0xdeadbeef
|
2016-01-28 16:56:54 +00:00
|
|
|
Node* ret_value = GetTrapValue(builder_->GetFunctionSignature());
|
2015-12-11 14:54:07 +00:00
|
|
|
end = graph()->NewNode(jsgraph()->common()->Return(), ret_value,
|
|
|
|
*effect_ptr, *control_ptr);
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MergeControlToEnd(jsgraph(), end);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-12-11 14:54:07 +00:00
|
|
|
WasmGraphBuilder::WasmGraphBuilder(Zone* zone, JSGraph* jsgraph,
|
|
|
|
wasm::FunctionSig* function_signature)
|
2015-12-11 12:26:16 +00:00
|
|
|
: zone_(zone),
|
|
|
|
jsgraph_(jsgraph),
|
|
|
|
module_(nullptr),
|
|
|
|
mem_buffer_(nullptr),
|
|
|
|
mem_size_(nullptr),
|
|
|
|
function_table_(nullptr),
|
|
|
|
control_(nullptr),
|
|
|
|
effect_(nullptr),
|
|
|
|
cur_buffer_(def_buffer_),
|
|
|
|
cur_bufsize_(kDefaultBufferSize),
|
2015-12-11 14:54:07 +00:00
|
|
|
trap_(new (zone) WasmTrapHelper(this)),
|
|
|
|
function_signature_(function_signature) {
|
2015-12-11 12:26:16 +00:00
|
|
|
DCHECK_NOT_NULL(jsgraph_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Error() { return jsgraph()->Dead(); }
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Start(unsigned params) {
|
|
|
|
Node* start = graph()->NewNode(jsgraph()->common()->Start(params));
|
|
|
|
graph()->SetStart(start);
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Param(unsigned index, wasm::LocalType type) {
|
|
|
|
return graph()->NewNode(jsgraph()->common()->Parameter(index),
|
|
|
|
graph()->start());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Loop(Node* entry) {
|
|
|
|
return graph()->NewNode(jsgraph()->common()->Loop(1), entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Terminate(Node* effect, Node* control) {
|
|
|
|
Node* terminate =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Terminate(), effect, control);
|
|
|
|
MergeControlToEnd(jsgraph(), terminate);
|
|
|
|
return terminate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned WasmGraphBuilder::InputCount(Node* node) {
|
|
|
|
return static_cast<unsigned>(node->InputCount());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
|
|
|
|
return phi && IrOpcode::IsPhiOpcode(phi->opcode()) &&
|
|
|
|
NodeProperties::GetControlInput(phi) == merge;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WasmGraphBuilder::AppendToMerge(Node* merge, Node* from) {
|
|
|
|
DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
|
|
|
|
merge->AppendInput(jsgraph()->zone(), from);
|
|
|
|
int new_size = merge->InputCount();
|
|
|
|
NodeProperties::ChangeOp(
|
|
|
|
merge, jsgraph()->common()->ResizeMergeOrPhi(merge->op(), new_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WasmGraphBuilder::AppendToPhi(Node* merge, Node* phi, Node* from) {
|
|
|
|
DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
|
|
|
|
DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
|
|
|
|
int new_size = phi->InputCount();
|
|
|
|
phi->InsertInput(jsgraph()->zone(), phi->InputCount() - 1, from);
|
|
|
|
NodeProperties::ChangeOp(
|
|
|
|
phi, jsgraph()->common()->ResizeMergeOrPhi(phi->op(), new_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Merge(unsigned count, Node** controls) {
|
|
|
|
return graph()->NewNode(jsgraph()->common()->Merge(count), count, controls);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Phi(wasm::LocalType type, unsigned count, Node** vals,
|
|
|
|
Node* control) {
|
|
|
|
DCHECK(IrOpcode::IsMergeOpcode(control->opcode()));
|
2015-12-17 14:52:27 +00:00
|
|
|
Node** buf = Realloc(vals, count);
|
|
|
|
buf = Realloc(buf, count + 1);
|
2015-12-11 12:26:16 +00:00
|
|
|
buf[count] = control;
|
|
|
|
return graph()->NewNode(jsgraph()->common()->Phi(type, count), count + 1,
|
|
|
|
buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::EffectPhi(unsigned count, Node** effects,
|
|
|
|
Node* control) {
|
|
|
|
DCHECK(IrOpcode::IsMergeOpcode(control->opcode()));
|
2015-12-17 14:52:27 +00:00
|
|
|
Node** buf = Realloc(effects, count);
|
|
|
|
buf = Realloc(buf, count + 1);
|
2015-12-11 12:26:16 +00:00
|
|
|
buf[count] = control;
|
|
|
|
return graph()->NewNode(jsgraph()->common()->EffectPhi(count), count + 1,
|
|
|
|
buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Int32Constant(int32_t value) {
|
|
|
|
return jsgraph()->Int32Constant(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Int64Constant(int64_t value) {
|
|
|
|
return jsgraph()->Int64Constant(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left,
|
|
|
|
Node* right) {
|
|
|
|
const Operator* op;
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
|
|
|
switch (opcode) {
|
|
|
|
case wasm::kExprI32Add:
|
|
|
|
op = m->Int32Add();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Sub:
|
|
|
|
op = m->Int32Sub();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Mul:
|
|
|
|
op = m->Int32Mul();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32DivS: {
|
|
|
|
trap_->ZeroCheck32(kTrapDivByZero, right);
|
|
|
|
Node* before = *control_;
|
|
|
|
Node* denom_is_m1;
|
|
|
|
Node* denom_is_not_m1;
|
|
|
|
Branch(graph()->NewNode(jsgraph()->machine()->Word32Equal(), right,
|
|
|
|
jsgraph()->Int32Constant(-1)),
|
|
|
|
&denom_is_m1, &denom_is_not_m1);
|
|
|
|
*control_ = denom_is_m1;
|
|
|
|
trap_->TrapIfEq32(kTrapDivUnrepresentable, left, kMinInt);
|
|
|
|
if (*control_ != denom_is_m1) {
|
|
|
|
*control_ = graph()->NewNode(jsgraph()->common()->Merge(2),
|
|
|
|
denom_is_not_m1, *control_);
|
|
|
|
} else {
|
|
|
|
*control_ = before;
|
|
|
|
}
|
|
|
|
return graph()->NewNode(m->Int32Div(), left, right, *control_);
|
|
|
|
}
|
|
|
|
case wasm::kExprI32DivU:
|
|
|
|
op = m->Uint32Div();
|
|
|
|
return graph()->NewNode(op, left, right,
|
|
|
|
trap_->ZeroCheck32(kTrapDivByZero, right));
|
|
|
|
case wasm::kExprI32RemS: {
|
|
|
|
trap_->ZeroCheck32(kTrapRemByZero, right);
|
|
|
|
Diamond d(graph(), jsgraph()->common(),
|
|
|
|
graph()->NewNode(jsgraph()->machine()->Word32Equal(), right,
|
|
|
|
jsgraph()->Int32Constant(-1)));
|
|
|
|
|
|
|
|
Node* rem = graph()->NewNode(m->Int32Mod(), left, right, d.if_false);
|
|
|
|
|
|
|
|
return d.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
|
|
|
|
rem);
|
|
|
|
}
|
|
|
|
case wasm::kExprI32RemU:
|
|
|
|
op = m->Uint32Mod();
|
|
|
|
return graph()->NewNode(op, left, right,
|
|
|
|
trap_->ZeroCheck32(kTrapRemByZero, right));
|
|
|
|
case wasm::kExprI32And:
|
|
|
|
op = m->Word32And();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Ior:
|
|
|
|
op = m->Word32Or();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Xor:
|
|
|
|
op = m->Word32Xor();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Shl:
|
|
|
|
op = m->Word32Shl();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32ShrU:
|
|
|
|
op = m->Word32Shr();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32ShrS:
|
|
|
|
op = m->Word32Sar();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Eq:
|
|
|
|
op = m->Word32Equal();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Ne:
|
|
|
|
return Invert(Binop(wasm::kExprI32Eq, left, right));
|
|
|
|
case wasm::kExprI32LtS:
|
|
|
|
op = m->Int32LessThan();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32LeS:
|
|
|
|
op = m->Int32LessThanOrEqual();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32LtU:
|
|
|
|
op = m->Uint32LessThan();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32LeU:
|
|
|
|
op = m->Uint32LessThanOrEqual();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32GtS:
|
|
|
|
op = m->Int32LessThan();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32GeS:
|
|
|
|
op = m->Int32LessThanOrEqual();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32GtU:
|
|
|
|
op = m->Uint32LessThan();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32GeU:
|
|
|
|
op = m->Uint32LessThanOrEqual();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
2016-02-04 09:40:55 +00:00
|
|
|
case wasm::kExprI64And:
|
|
|
|
op = m->Word64And();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
#if WASM_64
|
|
|
|
// Opcodes only supported on 64-bit platforms.
|
|
|
|
// TODO(titzer): query the machine operator builder here instead of #ifdef.
|
|
|
|
case wasm::kExprI64Add:
|
|
|
|
op = m->Int64Add();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Sub:
|
|
|
|
op = m->Int64Sub();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Mul:
|
|
|
|
op = m->Int64Mul();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64DivS: {
|
|
|
|
trap_->ZeroCheck64(kTrapDivByZero, right);
|
|
|
|
Node* before = *control_;
|
|
|
|
Node* denom_is_m1;
|
|
|
|
Node* denom_is_not_m1;
|
|
|
|
Branch(graph()->NewNode(jsgraph()->machine()->Word64Equal(), right,
|
|
|
|
jsgraph()->Int64Constant(-1)),
|
|
|
|
&denom_is_m1, &denom_is_not_m1);
|
|
|
|
*control_ = denom_is_m1;
|
|
|
|
trap_->TrapIfEq64(kTrapDivUnrepresentable, left,
|
|
|
|
std::numeric_limits<int64_t>::min());
|
|
|
|
if (*control_ != denom_is_m1) {
|
|
|
|
*control_ = graph()->NewNode(jsgraph()->common()->Merge(2),
|
|
|
|
denom_is_not_m1, *control_);
|
|
|
|
} else {
|
|
|
|
*control_ = before;
|
|
|
|
}
|
|
|
|
return graph()->NewNode(m->Int64Div(), left, right, *control_);
|
|
|
|
}
|
|
|
|
case wasm::kExprI64DivU:
|
|
|
|
op = m->Uint64Div();
|
|
|
|
return graph()->NewNode(op, left, right,
|
|
|
|
trap_->ZeroCheck64(kTrapDivByZero, right));
|
|
|
|
case wasm::kExprI64RemS: {
|
|
|
|
trap_->ZeroCheck64(kTrapRemByZero, right);
|
|
|
|
Diamond d(jsgraph()->graph(), jsgraph()->common(),
|
|
|
|
graph()->NewNode(jsgraph()->machine()->Word64Equal(), right,
|
|
|
|
jsgraph()->Int64Constant(-1)));
|
|
|
|
|
|
|
|
Node* rem = graph()->NewNode(m->Int64Mod(), left, right, d.if_false);
|
|
|
|
|
|
|
|
return d.Phi(MachineRepresentation::kWord64, jsgraph()->Int64Constant(0),
|
|
|
|
rem);
|
|
|
|
}
|
|
|
|
case wasm::kExprI64RemU:
|
|
|
|
op = m->Uint64Mod();
|
|
|
|
return graph()->NewNode(op, left, right,
|
|
|
|
trap_->ZeroCheck64(kTrapRemByZero, right));
|
|
|
|
case wasm::kExprI64Ior:
|
|
|
|
op = m->Word64Or();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Xor:
|
|
|
|
op = m->Word64Xor();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Shl:
|
|
|
|
op = m->Word64Shl();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64ShrU:
|
|
|
|
op = m->Word64Shr();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64ShrS:
|
|
|
|
op = m->Word64Sar();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Eq:
|
|
|
|
op = m->Word64Equal();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Ne:
|
|
|
|
return Invert(Binop(wasm::kExprI64Eq, left, right));
|
|
|
|
case wasm::kExprI64LtS:
|
|
|
|
op = m->Int64LessThan();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64LeS:
|
|
|
|
op = m->Int64LessThanOrEqual();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64LtU:
|
|
|
|
op = m->Uint64LessThan();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64LeU:
|
|
|
|
op = m->Uint64LessThanOrEqual();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64GtS:
|
|
|
|
op = m->Int64LessThan();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64GeS:
|
|
|
|
op = m->Int64LessThanOrEqual();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64GtU:
|
|
|
|
op = m->Uint64LessThan();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64GeU:
|
|
|
|
op = m->Uint64LessThanOrEqual();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
case wasm::kExprF32CopySign:
|
|
|
|
return BuildF32CopySign(left, right);
|
|
|
|
case wasm::kExprF64CopySign:
|
|
|
|
return BuildF64CopySign(left, right);
|
|
|
|
case wasm::kExprF32Add:
|
|
|
|
op = m->Float32Add();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Sub:
|
|
|
|
op = m->Float32Sub();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Mul:
|
|
|
|
op = m->Float32Mul();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Div:
|
|
|
|
op = m->Float32Div();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Eq:
|
|
|
|
op = m->Float32Equal();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Ne:
|
|
|
|
return Invert(Binop(wasm::kExprF32Eq, left, right));
|
|
|
|
case wasm::kExprF32Lt:
|
|
|
|
op = m->Float32LessThan();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Ge:
|
|
|
|
op = m->Float32LessThanOrEqual();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Gt:
|
|
|
|
op = m->Float32LessThan();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Le:
|
|
|
|
op = m->Float32LessThanOrEqual();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Add:
|
|
|
|
op = m->Float64Add();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Sub:
|
|
|
|
op = m->Float64Sub();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Mul:
|
|
|
|
op = m->Float64Mul();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Div:
|
|
|
|
op = m->Float64Div();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Eq:
|
|
|
|
op = m->Float64Equal();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Ne:
|
|
|
|
return Invert(Binop(wasm::kExprF64Eq, left, right));
|
|
|
|
case wasm::kExprF64Lt:
|
|
|
|
op = m->Float64LessThan();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Le:
|
|
|
|
op = m->Float64LessThanOrEqual();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Gt:
|
|
|
|
op = m->Float64LessThan();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Ge:
|
|
|
|
op = m->Float64LessThanOrEqual();
|
|
|
|
std::swap(left, right);
|
|
|
|
break;
|
2015-12-11 16:39:54 +00:00
|
|
|
case wasm::kExprF32Min:
|
|
|
|
return BuildF32Min(left, right);
|
|
|
|
case wasm::kExprF64Min:
|
|
|
|
return BuildF64Min(left, right);
|
|
|
|
case wasm::kExprF32Max:
|
|
|
|
return BuildF32Max(left, right);
|
|
|
|
case wasm::kExprF64Max:
|
|
|
|
return BuildF64Max(left, right);
|
2015-12-11 12:26:16 +00:00
|
|
|
default:
|
|
|
|
op = UnsupportedOpcode(opcode);
|
|
|
|
}
|
|
|
|
return graph()->NewNode(op, left, right);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input) {
|
|
|
|
const Operator* op;
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
|
|
|
switch (opcode) {
|
|
|
|
case wasm::kExprBoolNot:
|
|
|
|
op = m->Word32Equal();
|
|
|
|
return graph()->NewNode(op, input, jsgraph()->Int32Constant(0));
|
|
|
|
case wasm::kExprF32Abs:
|
|
|
|
op = m->Float32Abs();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32Neg:
|
2015-12-16 11:44:46 +00:00
|
|
|
return BuildF32Neg(input);
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprF32Sqrt:
|
|
|
|
op = m->Float32Sqrt();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Abs:
|
|
|
|
op = m->Float64Abs();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64Neg:
|
2015-12-16 11:44:46 +00:00
|
|
|
return BuildF64Neg(input);
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprF64Sqrt:
|
|
|
|
op = m->Float64Sqrt();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32SConvertF64:
|
2015-12-21 10:52:48 +00:00
|
|
|
return BuildI32SConvertF64(input);
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprI32UConvertF64:
|
2015-12-21 10:52:48 +00:00
|
|
|
return BuildI32UConvertF64(input);
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprF32ConvertF64:
|
|
|
|
op = m->TruncateFloat64ToFloat32();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64SConvertI32:
|
|
|
|
op = m->ChangeInt32ToFloat64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64UConvertI32:
|
|
|
|
op = m->ChangeUint32ToFloat64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32SConvertI32:
|
[turbofan] Add the RoundInt32ToFloat32 operator to turbofan.
The new operator converts an int32 input to float32. If the input cannot
be represented exactly in float32, the value is rounded using the
round-ties-even rounding mode (the default rounding mode).
I provide implementations of the new operator for x64, ia32, arm, arm64,
mips, mips64, ppc, and ppc64.
R=titzer@chromium.org, v8-arm-ports@googlegroups.com, v8-mips-ports@googlegroups.com, v8-ppc-ports@googlegroups.com
Review URL: https://codereview.chromium.org/1589363002
Cr-Commit-Position: refs/heads/master@{#33347}
2016-01-16 13:11:40 +00:00
|
|
|
op = m->RoundInt32ToFloat32();
|
2015-12-11 12:26:16 +00:00
|
|
|
break;
|
|
|
|
case wasm::kExprF32UConvertI32:
|
2016-02-06 18:07:39 +00:00
|
|
|
op = m->RoundUint32ToFloat32();
|
2015-12-11 12:26:16 +00:00
|
|
|
break;
|
|
|
|
case wasm::kExprI32SConvertF32:
|
2015-12-21 10:52:48 +00:00
|
|
|
return BuildI32SConvertF32(input);
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprI32UConvertF32:
|
2015-12-21 10:52:48 +00:00
|
|
|
return BuildI32UConvertF32(input);
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprF64ConvertF32:
|
|
|
|
op = m->ChangeFloat32ToFloat64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32ReinterpretI32:
|
|
|
|
op = m->BitcastInt32ToFloat32();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32ReinterpretF32:
|
|
|
|
op = m->BitcastFloat32ToInt32();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Clz:
|
|
|
|
op = m->Word32Clz();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI32Ctz: {
|
|
|
|
if (m->Word32Ctz().IsSupported()) {
|
|
|
|
op = m->Word32Ctz().op();
|
|
|
|
break;
|
2016-02-16 11:14:01 +00:00
|
|
|
} else if (m->Word32ReverseBits().IsSupported()) {
|
|
|
|
Node* reversed = graph()->NewNode(m->Word32ReverseBits().op(), input);
|
|
|
|
Node* result = graph()->NewNode(m->Word32Clz(), reversed);
|
|
|
|
return result;
|
2015-12-11 12:26:16 +00:00
|
|
|
} else {
|
|
|
|
return BuildI32Ctz(input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case wasm::kExprI32Popcnt: {
|
|
|
|
if (m->Word32Popcnt().IsSupported()) {
|
|
|
|
op = m->Word32Popcnt().op();
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
return BuildI32Popcnt(input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case wasm::kExprF32Floor: {
|
2016-02-02 12:26:38 +00:00
|
|
|
if (!m->Float32RoundDown().IsSupported()) return BuildF32Floor(input);
|
|
|
|
op = m->Float32RoundDown().op();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprF32Ceil: {
|
2016-02-02 12:26:38 +00:00
|
|
|
if (!m->Float32RoundUp().IsSupported()) return BuildF32Ceil(input);
|
|
|
|
op = m->Float32RoundUp().op();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprF32Trunc: {
|
2016-02-02 12:26:38 +00:00
|
|
|
if (!m->Float32RoundTruncate().IsSupported()) return BuildF32Trunc(input);
|
|
|
|
op = m->Float32RoundTruncate().op();
|
2016-01-20 15:43:03 +00:00
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprF32NearestInt: {
|
2016-02-02 12:26:38 +00:00
|
|
|
if (!m->Float32RoundTiesEven().IsSupported())
|
|
|
|
return BuildF32NearestInt(input);
|
|
|
|
op = m->Float32RoundTiesEven().op();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprF64Floor: {
|
2016-02-02 12:26:38 +00:00
|
|
|
if (!m->Float64RoundDown().IsSupported()) return BuildF64Floor(input);
|
|
|
|
op = m->Float64RoundDown().op();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprF64Ceil: {
|
2016-02-02 12:26:38 +00:00
|
|
|
if (!m->Float64RoundUp().IsSupported()) return BuildF64Ceil(input);
|
|
|
|
op = m->Float64RoundUp().op();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprF64Trunc: {
|
2016-02-02 10:57:58 +00:00
|
|
|
if (!m->Float64RoundTruncate().IsSupported()) return BuildF64Trunc(input);
|
|
|
|
op = m->Float64RoundTruncate().op();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprF64NearestInt: {
|
2016-02-02 12:26:38 +00:00
|
|
|
if (!m->Float64RoundTiesEven().IsSupported())
|
|
|
|
return BuildF64NearestInt(input);
|
|
|
|
op = m->Float64RoundTiesEven().op();
|
|
|
|
break;
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
case wasm::kExprI32ConvertI64:
|
|
|
|
op = m->TruncateInt64ToInt32();
|
|
|
|
break;
|
2016-02-04 09:40:55 +00:00
|
|
|
#if WASM_64
|
|
|
|
// Opcodes only supported on 64-bit platforms.
|
|
|
|
// TODO(titzer): query the machine operator builder here instead of #ifdef.
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprI64SConvertI32:
|
|
|
|
op = m->ChangeInt32ToInt64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64UConvertI32:
|
|
|
|
op = m->ChangeUint32ToUint64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32SConvertI64:
|
|
|
|
op = m->RoundInt64ToFloat32();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF32UConvertI64:
|
|
|
|
op = m->RoundUint64ToFloat32();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64SConvertI64:
|
|
|
|
op = m->RoundInt64ToFloat64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprF64UConvertI64:
|
|
|
|
op = m->RoundUint64ToFloat64();
|
|
|
|
break;
|
2015-12-11 14:54:07 +00:00
|
|
|
case wasm::kExprI64SConvertF32: {
|
|
|
|
Node* trunc = graph()->NewNode(m->TryTruncateFloat32ToInt64(), input);
|
|
|
|
Node* result =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
|
|
|
|
Node* overflow =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
|
|
|
|
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case wasm::kExprI64SConvertF64: {
|
|
|
|
Node* trunc = graph()->NewNode(m->TryTruncateFloat64ToInt64(), input);
|
|
|
|
Node* result =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
|
|
|
|
Node* overflow =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
|
|
|
|
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case wasm::kExprI64UConvertF32: {
|
|
|
|
Node* trunc = graph()->NewNode(m->TryTruncateFloat32ToUint64(), input);
|
|
|
|
Node* result =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
|
|
|
|
Node* overflow =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
|
|
|
|
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case wasm::kExprI64UConvertF64: {
|
|
|
|
Node* trunc = graph()->NewNode(m->TryTruncateFloat64ToUint64(), input);
|
|
|
|
Node* result =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
|
|
|
|
Node* overflow =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
|
|
|
|
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
|
|
|
|
return result;
|
|
|
|
}
|
2015-12-11 12:26:16 +00:00
|
|
|
case wasm::kExprF64ReinterpretI64:
|
|
|
|
op = m->BitcastInt64ToFloat64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64ReinterpretF64:
|
|
|
|
op = m->BitcastFloat64ToInt64();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Clz:
|
|
|
|
op = m->Word64Clz();
|
|
|
|
break;
|
|
|
|
case wasm::kExprI64Ctz: {
|
|
|
|
if (m->Word64Ctz().IsSupported()) {
|
|
|
|
op = m->Word64Ctz().op();
|
|
|
|
break;
|
2016-02-16 11:14:01 +00:00
|
|
|
} else if (m->Word64ReverseBits().IsSupported()) {
|
|
|
|
Node* reversed = graph()->NewNode(m->Word64ReverseBits().op(), input);
|
|
|
|
Node* result = graph()->NewNode(m->Word64Clz(), reversed);
|
|
|
|
return result;
|
2015-12-11 12:26:16 +00:00
|
|
|
} else {
|
|
|
|
return BuildI64Ctz(input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case wasm::kExprI64Popcnt: {
|
|
|
|
if (m->Word64Popcnt().IsSupported()) {
|
|
|
|
op = m->Word64Popcnt().op();
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
return BuildI64Popcnt(input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
op = UnsupportedOpcode(opcode);
|
|
|
|
}
|
|
|
|
return graph()->NewNode(op, input);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Float32Constant(float value) {
|
|
|
|
return jsgraph()->Float32Constant(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Float64Constant(double value) {
|
|
|
|
return jsgraph()->Float64Constant(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Constant(Handle<Object> value) {
|
|
|
|
return jsgraph()->Constant(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Branch(Node* cond, Node** true_node,
|
|
|
|
Node** false_node) {
|
|
|
|
DCHECK_NOT_NULL(cond);
|
|
|
|
DCHECK_NOT_NULL(*control_);
|
|
|
|
Node* branch =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Branch(), cond, *control_);
|
|
|
|
*true_node = graph()->NewNode(jsgraph()->common()->IfTrue(), branch);
|
|
|
|
*false_node = graph()->NewNode(jsgraph()->common()->IfFalse(), branch);
|
|
|
|
return branch;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Switch(unsigned count, Node* key) {
|
|
|
|
return graph()->NewNode(jsgraph()->common()->Switch(count), key, *control_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::IfValue(int32_t value, Node* sw) {
|
|
|
|
DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
|
|
|
|
return graph()->NewNode(jsgraph()->common()->IfValue(value), sw);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::IfDefault(Node* sw) {
|
|
|
|
DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
|
|
|
|
return graph()->NewNode(jsgraph()->common()->IfDefault(), sw);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Return(unsigned count, Node** vals) {
|
|
|
|
DCHECK_NOT_NULL(*control_);
|
|
|
|
DCHECK_NOT_NULL(*effect_);
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
|
|
// Handle a return of void.
|
|
|
|
vals[0] = jsgraph()->Int32Constant(0);
|
|
|
|
count = 1;
|
|
|
|
}
|
|
|
|
|
2015-12-17 14:52:27 +00:00
|
|
|
Node** buf = Realloc(vals, count);
|
|
|
|
buf = Realloc(buf, count + 2);
|
2015-12-11 12:26:16 +00:00
|
|
|
buf[count] = *effect_;
|
|
|
|
buf[count + 1] = *control_;
|
|
|
|
Node* ret = graph()->NewNode(jsgraph()->common()->Return(), count + 2, vals);
|
|
|
|
|
|
|
|
MergeControlToEnd(jsgraph(), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::ReturnVoid() { return Return(0, Buffer(0)); }
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Unreachable() {
|
|
|
|
trap_->Unreachable();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-16 11:44:46 +00:00
|
|
|
Node* WasmGraphBuilder::BuildF32Neg(Node* input) {
|
|
|
|
Node* result =
|
|
|
|
Unop(wasm::kExprF32ReinterpretI32,
|
|
|
|
Binop(wasm::kExprI32Xor, Unop(wasm::kExprI32ReinterpretF32, input),
|
|
|
|
jsgraph()->Int32Constant(0x80000000)));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF64Neg(Node* input) {
|
|
|
|
#if WASM_64
|
|
|
|
Node* result =
|
|
|
|
Unop(wasm::kExprF64ReinterpretI64,
|
|
|
|
Binop(wasm::kExprI64Xor, Unop(wasm::kExprI64ReinterpretF64, input),
|
|
|
|
jsgraph()->Int64Constant(0x8000000000000000)));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
#else
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
|
|
|
|
|
|
|
Node* old_high_word = graph()->NewNode(m->Float64ExtractHighWord32(), input);
|
|
|
|
Node* new_high_word = Binop(wasm::kExprI32Xor, old_high_word,
|
|
|
|
jsgraph()->Int32Constant(0x80000000));
|
|
|
|
|
|
|
|
return graph()->NewNode(m->Float64InsertHighWord32(), input, new_high_word);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-11 12:26:16 +00:00
|
|
|
Node* WasmGraphBuilder::BuildF32CopySign(Node* left, Node* right) {
|
|
|
|
Node* result = Unop(
|
|
|
|
wasm::kExprF32ReinterpretI32,
|
|
|
|
Binop(wasm::kExprI32Ior,
|
|
|
|
Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, left),
|
|
|
|
jsgraph()->Int32Constant(0x7fffffff)),
|
|
|
|
Binop(wasm::kExprI32And, Unop(wasm::kExprI32ReinterpretF32, right),
|
|
|
|
jsgraph()->Int32Constant(0x80000000))));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF64CopySign(Node* left, Node* right) {
|
|
|
|
#if WASM_64
|
|
|
|
Node* result = Unop(
|
|
|
|
wasm::kExprF64ReinterpretI64,
|
|
|
|
Binop(wasm::kExprI64Ior,
|
|
|
|
Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, left),
|
|
|
|
jsgraph()->Int64Constant(0x7fffffffffffffff)),
|
|
|
|
Binop(wasm::kExprI64And, Unop(wasm::kExprI64ReinterpretF64, right),
|
|
|
|
jsgraph()->Int64Constant(0x8000000000000000))));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
#else
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
|
|
|
|
|
|
|
Node* high_word_left = graph()->NewNode(m->Float64ExtractHighWord32(), left);
|
|
|
|
Node* high_word_right =
|
|
|
|
graph()->NewNode(m->Float64ExtractHighWord32(), right);
|
|
|
|
|
|
|
|
Node* new_high_word =
|
|
|
|
Binop(wasm::kExprI32Ior, Binop(wasm::kExprI32And, high_word_left,
|
|
|
|
jsgraph()->Int32Constant(0x7fffffff)),
|
|
|
|
Binop(wasm::kExprI32And, high_word_right,
|
|
|
|
jsgraph()->Int32Constant(0x80000000)));
|
|
|
|
|
|
|
|
return graph()->NewNode(m->Float64InsertHighWord32(), left, new_high_word);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-11 16:39:54 +00:00
|
|
|
Node* WasmGraphBuilder::BuildF32Min(Node* left, Node* right) {
|
|
|
|
Diamond left_le_right(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF32Le, left, right));
|
|
|
|
|
|
|
|
Diamond right_lt_left(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF32Lt, right, left));
|
|
|
|
|
|
|
|
Diamond left_is_not_nan(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF32Eq, left, left));
|
|
|
|
|
|
|
|
return left_le_right.Phi(
|
|
|
|
wasm::kAstF32, left,
|
2016-02-05 14:59:40 +00:00
|
|
|
right_lt_left.Phi(
|
|
|
|
wasm::kAstF32, right,
|
|
|
|
left_is_not_nan.Phi(
|
|
|
|
wasm::kAstF32,
|
|
|
|
Binop(wasm::kExprF32Mul, right, Float32Constant(1.0)),
|
|
|
|
Binop(wasm::kExprF32Mul, left, Float32Constant(1.0)))));
|
2015-12-11 16:39:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF32Max(Node* left, Node* right) {
|
|
|
|
Diamond left_ge_right(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF32Ge, left, right));
|
|
|
|
|
|
|
|
Diamond right_gt_left(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF32Gt, right, left));
|
|
|
|
|
|
|
|
Diamond left_is_not_nan(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF32Eq, left, left));
|
|
|
|
|
|
|
|
return left_ge_right.Phi(
|
|
|
|
wasm::kAstF32, left,
|
2016-02-05 14:59:40 +00:00
|
|
|
right_gt_left.Phi(
|
|
|
|
wasm::kAstF32, right,
|
|
|
|
left_is_not_nan.Phi(
|
|
|
|
wasm::kAstF32,
|
|
|
|
Binop(wasm::kExprF32Mul, right, Float32Constant(1.0)),
|
|
|
|
Binop(wasm::kExprF32Mul, left, Float32Constant(1.0)))));
|
2015-12-11 16:39:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF64Min(Node* left, Node* right) {
|
|
|
|
Diamond left_le_right(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF64Le, left, right));
|
|
|
|
|
|
|
|
Diamond right_lt_left(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF64Lt, right, left));
|
|
|
|
|
|
|
|
Diamond left_is_not_nan(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF64Eq, left, left));
|
|
|
|
|
|
|
|
return left_le_right.Phi(
|
|
|
|
wasm::kAstF64, left,
|
2016-02-05 14:59:40 +00:00
|
|
|
right_lt_left.Phi(
|
|
|
|
wasm::kAstF64, right,
|
|
|
|
left_is_not_nan.Phi(
|
|
|
|
wasm::kAstF64,
|
|
|
|
Binop(wasm::kExprF64Mul, right, Float64Constant(1.0)),
|
|
|
|
Binop(wasm::kExprF64Mul, left, Float64Constant(1.0)))));
|
2015-12-11 16:39:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF64Max(Node* left, Node* right) {
|
|
|
|
Diamond left_ge_right(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF64Ge, left, right));
|
|
|
|
|
|
|
|
Diamond right_gt_left(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF64Lt, right, left));
|
|
|
|
|
|
|
|
Diamond left_is_not_nan(graph(), jsgraph()->common(),
|
|
|
|
Binop(wasm::kExprF64Eq, left, left));
|
|
|
|
|
|
|
|
return left_ge_right.Phi(
|
|
|
|
wasm::kAstF64, left,
|
2016-02-05 14:59:40 +00:00
|
|
|
right_gt_left.Phi(
|
|
|
|
wasm::kAstF64, right,
|
|
|
|
left_is_not_nan.Phi(
|
|
|
|
wasm::kAstF64,
|
|
|
|
Binop(wasm::kExprF64Mul, right, Float64Constant(1.0)),
|
|
|
|
Binop(wasm::kExprF64Mul, left, Float64Constant(1.0)))));
|
2015-12-11 16:39:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-21 10:52:48 +00:00
|
|
|
Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input) {
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
|
|
|
// Truncation of the input value is needed for the overflow check later.
|
|
|
|
Node* trunc = Unop(wasm::kExprF32Trunc, input);
|
[turbofan] Add the TruncateFloat32ToInt32 operator to turbofan.
The new operator converts a float32 input to int32 through truncation.
I provide implementations of the new operator for x64, ia32, arm,
arm64, mips, mips64, and x87. @v8-ppc-ports, can you please take care
of the ppc implementation?
R=titzer@chromium.org, v8-arm-ports@googlegroups.com, v8-mips-ports@googlegroups.com, weiliang.lin@intel.com
Review URL: https://codereview.chromium.org/1583323004
Cr-Commit-Position: refs/heads/master@{#33346}
2016-01-16 11:40:47 +00:00
|
|
|
Node* result = graph()->NewNode(m->TruncateFloat32ToInt32(), trunc);
|
2015-12-21 10:52:48 +00:00
|
|
|
|
|
|
|
// Convert the result back to f64. If we end up at a different value than the
|
|
|
|
// truncated input value, then there has been an overflow and we trap.
|
[turbofan] Add the TruncateFloat32ToInt32 operator to turbofan.
The new operator converts a float32 input to int32 through truncation.
I provide implementations of the new operator for x64, ia32, arm,
arm64, mips, mips64, and x87. @v8-ppc-ports, can you please take care
of the ppc implementation?
R=titzer@chromium.org, v8-arm-ports@googlegroups.com, v8-mips-ports@googlegroups.com, weiliang.lin@intel.com
Review URL: https://codereview.chromium.org/1583323004
Cr-Commit-Position: refs/heads/master@{#33346}
2016-01-16 11:40:47 +00:00
|
|
|
Node* check = Unop(wasm::kExprF32SConvertI32, result);
|
|
|
|
Node* overflow = Binop(wasm::kExprF32Ne, trunc, check);
|
2015-12-21 10:52:48 +00:00
|
|
|
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildI32SConvertF64(Node* input) {
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
2016-01-19 08:28:46 +00:00
|
|
|
if (module_ && module_->asm_js) {
|
|
|
|
return graph()->NewNode(
|
|
|
|
m->TruncateFloat64ToInt32(TruncationMode::kJavaScript), input);
|
|
|
|
}
|
2015-12-21 10:52:48 +00:00
|
|
|
// Truncation of the input value is needed for the overflow check later.
|
|
|
|
Node* trunc = Unop(wasm::kExprF64Trunc, input);
|
|
|
|
Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), trunc);
|
|
|
|
|
|
|
|
// Convert the result back to f64. If we end up at a different value than the
|
|
|
|
// truncated input value, then there has been an overflow and we trap.
|
|
|
|
Node* check = Unop(wasm::kExprF64SConvertI32, result);
|
|
|
|
Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
|
|
|
|
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildI32UConvertF32(Node* input) {
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
|
|
|
// Truncation of the input value is needed for the overflow check later.
|
|
|
|
Node* trunc = Unop(wasm::kExprF32Trunc, input);
|
2016-02-06 18:08:54 +00:00
|
|
|
Node* result = graph()->NewNode(m->TruncateFloat32ToUint32(), trunc);
|
2015-12-21 10:52:48 +00:00
|
|
|
|
2016-02-06 18:08:54 +00:00
|
|
|
// Convert the result back to f32. If we end up at a different value than the
|
2015-12-21 10:52:48 +00:00
|
|
|
// truncated input value, then there has been an overflow and we trap.
|
2016-02-06 18:08:54 +00:00
|
|
|
Node* check = Unop(wasm::kExprF32UConvertI32, result);
|
|
|
|
Node* overflow = Binop(wasm::kExprF32Ne, trunc, check);
|
2015-12-21 10:52:48 +00:00
|
|
|
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildI32UConvertF64(Node* input) {
|
|
|
|
MachineOperatorBuilder* m = jsgraph()->machine();
|
2016-01-19 08:28:46 +00:00
|
|
|
if (module_ && module_->asm_js) {
|
|
|
|
return graph()->NewNode(
|
|
|
|
m->TruncateFloat64ToInt32(TruncationMode::kJavaScript), input);
|
|
|
|
}
|
2015-12-21 10:52:48 +00:00
|
|
|
// Truncation of the input value is needed for the overflow check later.
|
|
|
|
Node* trunc = Unop(wasm::kExprF64Trunc, input);
|
|
|
|
Node* result = graph()->NewNode(m->ChangeFloat64ToUint32(), trunc);
|
|
|
|
|
|
|
|
// Convert the result back to f64. If we end up at a different value than the
|
|
|
|
// truncated input value, then there has been an overflow and we trap.
|
|
|
|
Node* check = Unop(wasm::kExprF64UConvertI32, result);
|
|
|
|
Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
|
|
|
|
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-11 12:26:16 +00:00
|
|
|
Node* WasmGraphBuilder::BuildI32Ctz(Node* input) {
|
|
|
|
//// Implement the following code as TF graph.
|
|
|
|
// value = value | (value << 1);
|
|
|
|
// value = value | (value << 2);
|
|
|
|
// value = value | (value << 4);
|
|
|
|
// value = value | (value << 8);
|
|
|
|
// value = value | (value << 16);
|
|
|
|
// return CountPopulation32(0xffffffff XOR value);
|
|
|
|
|
|
|
|
Node* result =
|
|
|
|
Binop(wasm::kExprI32Ior, input,
|
|
|
|
Binop(wasm::kExprI32Shl, input, jsgraph()->Int32Constant(1)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI32Ior, result,
|
|
|
|
Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(2)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI32Ior, result,
|
|
|
|
Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(4)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI32Ior, result,
|
|
|
|
Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(8)));
|
|
|
|
|
|
|
|
result =
|
|
|
|
Binop(wasm::kExprI32Ior, result,
|
|
|
|
Binop(wasm::kExprI32Shl, result, jsgraph()->Int32Constant(16)));
|
|
|
|
|
|
|
|
result = BuildI32Popcnt(
|
|
|
|
Binop(wasm::kExprI32Xor, jsgraph()->Int32Constant(0xffffffff), result));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildI64Ctz(Node* input) {
|
|
|
|
//// Implement the following code as TF graph.
|
|
|
|
// value = value | (value << 1);
|
|
|
|
// value = value | (value << 2);
|
|
|
|
// value = value | (value << 4);
|
|
|
|
// value = value | (value << 8);
|
|
|
|
// value = value | (value << 16);
|
|
|
|
// value = value | (value << 32);
|
|
|
|
// return CountPopulation64(0xffffffffffffffff XOR value);
|
|
|
|
|
|
|
|
Node* result =
|
|
|
|
Binop(wasm::kExprI64Ior, input,
|
|
|
|
Binop(wasm::kExprI64Shl, input, jsgraph()->Int64Constant(1)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Ior, result,
|
|
|
|
Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(2)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Ior, result,
|
|
|
|
Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(4)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Ior, result,
|
|
|
|
Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(8)));
|
|
|
|
|
|
|
|
result =
|
|
|
|
Binop(wasm::kExprI64Ior, result,
|
|
|
|
Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(16)));
|
|
|
|
|
|
|
|
result =
|
|
|
|
Binop(wasm::kExprI64Ior, result,
|
|
|
|
Binop(wasm::kExprI64Shl, result, jsgraph()->Int64Constant(32)));
|
|
|
|
|
|
|
|
result = BuildI64Popcnt(Binop(
|
|
|
|
wasm::kExprI64Xor, jsgraph()->Int64Constant(0xffffffffffffffff), result));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildI32Popcnt(Node* input) {
|
|
|
|
//// Implement the following code as a TF graph.
|
|
|
|
// value = ((value >> 1) & 0x55555555) + (value & 0x55555555);
|
|
|
|
// value = ((value >> 2) & 0x33333333) + (value & 0x33333333);
|
|
|
|
// value = ((value >> 4) & 0x0f0f0f0f) + (value & 0x0f0f0f0f);
|
|
|
|
// value = ((value >> 8) & 0x00ff00ff) + (value & 0x00ff00ff);
|
|
|
|
// value = ((value >> 16) & 0x0000ffff) + (value & 0x0000ffff);
|
|
|
|
|
|
|
|
Node* result = Binop(
|
|
|
|
wasm::kExprI32Add,
|
|
|
|
Binop(wasm::kExprI32And,
|
|
|
|
Binop(wasm::kExprI32ShrU, input, jsgraph()->Int32Constant(1)),
|
|
|
|
jsgraph()->Int32Constant(0x55555555)),
|
|
|
|
Binop(wasm::kExprI32And, input, jsgraph()->Int32Constant(0x55555555)));
|
|
|
|
|
|
|
|
result = Binop(
|
|
|
|
wasm::kExprI32Add,
|
|
|
|
Binop(wasm::kExprI32And,
|
|
|
|
Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(2)),
|
|
|
|
jsgraph()->Int32Constant(0x33333333)),
|
|
|
|
Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x33333333)));
|
|
|
|
|
|
|
|
result = Binop(
|
|
|
|
wasm::kExprI32Add,
|
|
|
|
Binop(wasm::kExprI32And,
|
|
|
|
Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(4)),
|
|
|
|
jsgraph()->Int32Constant(0x0f0f0f0f)),
|
|
|
|
Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x0f0f0f0f)));
|
|
|
|
|
|
|
|
result = Binop(
|
|
|
|
wasm::kExprI32Add,
|
|
|
|
Binop(wasm::kExprI32And,
|
|
|
|
Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(8)),
|
|
|
|
jsgraph()->Int32Constant(0x00ff00ff)),
|
|
|
|
Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x00ff00ff)));
|
|
|
|
|
|
|
|
result = Binop(
|
|
|
|
wasm::kExprI32Add,
|
|
|
|
Binop(wasm::kExprI32And,
|
|
|
|
Binop(wasm::kExprI32ShrU, result, jsgraph()->Int32Constant(16)),
|
|
|
|
jsgraph()->Int32Constant(0x0000ffff)),
|
|
|
|
Binop(wasm::kExprI32And, result, jsgraph()->Int32Constant(0x0000ffff)));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildI64Popcnt(Node* input) {
|
|
|
|
//// Implement the following code as a TF graph.
|
|
|
|
// value = ((value >> 1) & 0x5555555555555555) + (value & 0x5555555555555555);
|
|
|
|
// value = ((value >> 2) & 0x3333333333333333) + (value & 0x3333333333333333);
|
|
|
|
// value = ((value >> 4) & 0x0f0f0f0f0f0f0f0f) + (value & 0x0f0f0f0f0f0f0f0f);
|
|
|
|
// value = ((value >> 8) & 0x00ff00ff00ff00ff) + (value & 0x00ff00ff00ff00ff);
|
|
|
|
// value = ((value >> 16) & 0x0000ffff0000ffff) + (value &
|
|
|
|
// 0x0000ffff0000ffff);
|
|
|
|
// value = ((value >> 32) & 0x00000000ffffffff) + (value &
|
|
|
|
// 0x00000000ffffffff);
|
|
|
|
|
|
|
|
Node* result =
|
|
|
|
Binop(wasm::kExprI64Add,
|
|
|
|
Binop(wasm::kExprI64And,
|
|
|
|
Binop(wasm::kExprI64ShrU, input, jsgraph()->Int64Constant(1)),
|
|
|
|
jsgraph()->Int64Constant(0x5555555555555555)),
|
|
|
|
Binop(wasm::kExprI64And, input,
|
|
|
|
jsgraph()->Int64Constant(0x5555555555555555)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Add,
|
|
|
|
Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
|
|
|
|
jsgraph()->Int64Constant(2)),
|
|
|
|
jsgraph()->Int64Constant(0x3333333333333333)),
|
|
|
|
Binop(wasm::kExprI64And, result,
|
|
|
|
jsgraph()->Int64Constant(0x3333333333333333)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Add,
|
|
|
|
Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
|
|
|
|
jsgraph()->Int64Constant(4)),
|
|
|
|
jsgraph()->Int64Constant(0x0f0f0f0f0f0f0f0f)),
|
|
|
|
Binop(wasm::kExprI64And, result,
|
|
|
|
jsgraph()->Int64Constant(0x0f0f0f0f0f0f0f0f)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Add,
|
|
|
|
Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
|
|
|
|
jsgraph()->Int64Constant(8)),
|
|
|
|
jsgraph()->Int64Constant(0x00ff00ff00ff00ff)),
|
|
|
|
Binop(wasm::kExprI64And, result,
|
|
|
|
jsgraph()->Int64Constant(0x00ff00ff00ff00ff)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Add,
|
|
|
|
Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
|
|
|
|
jsgraph()->Int64Constant(16)),
|
|
|
|
jsgraph()->Int64Constant(0x0000ffff0000ffff)),
|
|
|
|
Binop(wasm::kExprI64And, result,
|
|
|
|
jsgraph()->Int64Constant(0x0000ffff0000ffff)));
|
|
|
|
|
|
|
|
result = Binop(wasm::kExprI64Add,
|
|
|
|
Binop(wasm::kExprI64And, Binop(wasm::kExprI64ShrU, result,
|
|
|
|
jsgraph()->Int64Constant(32)),
|
|
|
|
jsgraph()->Int64Constant(0x00000000ffffffff)),
|
|
|
|
Binop(wasm::kExprI64And, result,
|
|
|
|
jsgraph()->Int64Constant(0x00000000ffffffff)));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-01-20 15:43:03 +00:00
|
|
|
Node* WasmGraphBuilder::BuildF32Trunc(Node* input) {
|
2016-02-02 12:26:38 +00:00
|
|
|
MachineType type = MachineType::Float32();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f32_trunc_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF32Floor(Node* input) {
|
|
|
|
MachineType type = MachineType::Float32();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f32_floor_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF32Ceil(Node* input) {
|
|
|
|
MachineType type = MachineType::Float32();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f32_ceil_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF32NearestInt(Node* input) {
|
|
|
|
MachineType type = MachineType::Float32();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f32_nearest_int_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
2016-01-20 15:43:03 +00:00
|
|
|
}
|
|
|
|
|
2016-02-02 10:57:58 +00:00
|
|
|
Node* WasmGraphBuilder::BuildF64Trunc(Node* input) {
|
2016-02-02 12:26:38 +00:00
|
|
|
MachineType type = MachineType::Float64();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f64_trunc_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF64Floor(Node* input) {
|
|
|
|
MachineType type = MachineType::Float64();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f64_floor_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF64Ceil(Node* input) {
|
|
|
|
MachineType type = MachineType::Float64();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f64_ceil_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildF64NearestInt(Node* input) {
|
|
|
|
MachineType type = MachineType::Float64();
|
|
|
|
ExternalReference ref =
|
|
|
|
ExternalReference::f64_nearest_int_wrapper_function(jsgraph()->isolate());
|
|
|
|
return BuildRoundingInstruction(input, ref, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildRoundingInstruction(Node* input,
|
|
|
|
ExternalReference ref,
|
|
|
|
MachineType type) {
|
2016-02-02 10:57:58 +00:00
|
|
|
// We do truncation by calling a C function which calculates the truncation
|
|
|
|
// for us. The input is passed to the C function as a double* to avoid double
|
|
|
|
// parameters. For this we reserve a slot on the stack, store the parameter in
|
|
|
|
// that slot, pass a pointer to the slot to the C function, and after calling
|
|
|
|
// the C function we collect the return value from the stack slot.
|
|
|
|
|
2016-02-02 12:26:38 +00:00
|
|
|
Node* stack_slot_param =
|
|
|
|
graph()->NewNode(jsgraph()->machine()->StackSlot(type.representation()));
|
2016-02-02 10:57:58 +00:00
|
|
|
|
|
|
|
const Operator* store_op = jsgraph()->machine()->Store(
|
2016-02-02 12:26:38 +00:00
|
|
|
StoreRepresentation(type.representation(), kNoWriteBarrier));
|
2016-02-02 10:57:58 +00:00
|
|
|
*effect_ =
|
|
|
|
graph()->NewNode(store_op, stack_slot_param, jsgraph()->Int32Constant(0),
|
|
|
|
input, *effect_, *control_);
|
|
|
|
|
|
|
|
Signature<MachineType>::Builder sig_builder(jsgraph()->zone(), 0, 1);
|
|
|
|
sig_builder.AddParam(MachineType::Pointer());
|
2016-02-02 12:26:38 +00:00
|
|
|
Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref));
|
2016-02-02 10:57:58 +00:00
|
|
|
|
|
|
|
Node* args[] = {function, stack_slot_param};
|
|
|
|
|
|
|
|
BuildCCall(sig_builder.Build(), args);
|
|
|
|
|
2016-02-02 12:26:38 +00:00
|
|
|
const Operator* load_op = jsgraph()->machine()->Load(type);
|
2016-02-02 10:57:58 +00:00
|
|
|
|
|
|
|
Node* load =
|
|
|
|
graph()->NewNode(load_op, stack_slot_param, jsgraph()->Int32Constant(0),
|
|
|
|
*effect_, *control_);
|
|
|
|
*effect_ = load;
|
|
|
|
return load;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) {
|
|
|
|
const size_t params = sig->parameter_count();
|
|
|
|
const size_t extra = 2; // effect and control inputs.
|
|
|
|
const size_t count = 1 + params + extra;
|
|
|
|
|
|
|
|
// Reallocate the buffer to make space for extra inputs.
|
|
|
|
args = Realloc(args, count);
|
|
|
|
|
|
|
|
// Add effect and control inputs.
|
|
|
|
args[params + 1] = *effect_;
|
|
|
|
args[params + 2] = *control_;
|
|
|
|
|
|
|
|
CallDescriptor* desc =
|
|
|
|
Linkage::GetSimplifiedCDescriptor(jsgraph()->zone(), sig);
|
|
|
|
|
|
|
|
const Operator* op = jsgraph()->common()->Call(desc);
|
|
|
|
Node* call = graph()->NewNode(op, static_cast<int>(count), args);
|
|
|
|
*effect_ = call;
|
|
|
|
return call;
|
|
|
|
}
|
2016-01-20 15:43:03 +00:00
|
|
|
|
2015-12-11 12:26:16 +00:00
|
|
|
Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args) {
|
|
|
|
const size_t params = sig->parameter_count();
|
|
|
|
const size_t extra = 2; // effect and control inputs.
|
|
|
|
const size_t count = 1 + params + extra;
|
|
|
|
|
|
|
|
// Reallocate the buffer to make space for extra inputs.
|
|
|
|
args = Realloc(args, count);
|
|
|
|
|
|
|
|
// Add effect and control inputs.
|
|
|
|
args[params + 1] = *effect_;
|
|
|
|
args[params + 2] = *control_;
|
|
|
|
|
2016-02-18 15:18:41 +00:00
|
|
|
CallDescriptor* descriptor =
|
2016-02-23 15:33:06 +00:00
|
|
|
wasm::ModuleEnv::GetWasmCallDescriptor(jsgraph()->zone(), sig);
|
2016-02-18 15:18:41 +00:00
|
|
|
const Operator* op = jsgraph()->common()->Call(descriptor);
|
2015-12-11 12:26:16 +00:00
|
|
|
Node* call = graph()->NewNode(op, static_cast<int>(count), args);
|
|
|
|
|
|
|
|
*effect_ = call;
|
|
|
|
return call;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args) {
|
|
|
|
DCHECK_NULL(args[0]);
|
|
|
|
|
|
|
|
// Add code object as constant.
|
|
|
|
args[0] = Constant(module_->GetFunctionCode(index));
|
|
|
|
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
|
|
|
|
|
|
|
|
return BuildWasmCall(sig, args);
|
|
|
|
}
|
|
|
|
|
2016-02-19 14:58:25 +00:00
|
|
|
Node* WasmGraphBuilder::CallImport(uint32_t index, Node** args) {
|
|
|
|
DCHECK_NULL(args[0]);
|
|
|
|
|
|
|
|
// Add code object as constant.
|
|
|
|
args[0] = Constant(module_->GetImportCode(index));
|
2016-02-22 00:46:52 +00:00
|
|
|
wasm::FunctionSig* sig = module_->GetImportSignature(index);
|
2016-02-19 14:58:25 +00:00
|
|
|
|
|
|
|
return BuildWasmCall(sig, args);
|
|
|
|
}
|
2015-12-11 12:26:16 +00:00
|
|
|
|
|
|
|
Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args) {
|
|
|
|
DCHECK_NOT_NULL(args[0]);
|
2016-01-28 16:56:54 +00:00
|
|
|
DCHECK(module_ && module_->instance);
|
2015-12-11 12:26:16 +00:00
|
|
|
|
|
|
|
MachineOperatorBuilder* machine = jsgraph()->machine();
|
|
|
|
|
|
|
|
// Compute the code object by loading it from the function table.
|
|
|
|
Node* key = args[0];
|
|
|
|
|
|
|
|
// Bounds check the index.
|
|
|
|
int table_size = static_cast<int>(module_->FunctionTableSize());
|
2016-01-28 16:56:54 +00:00
|
|
|
if (table_size > 0) {
|
|
|
|
// Bounds check against the table size.
|
2015-12-11 12:26:16 +00:00
|
|
|
Node* size = Int32Constant(static_cast<int>(table_size));
|
|
|
|
Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size);
|
|
|
|
trap_->AddTrapIfFalse(kTrapFuncInvalid, in_bounds);
|
2016-01-28 16:56:54 +00:00
|
|
|
} else {
|
|
|
|
// No function table. Generate a trap and return a constant.
|
|
|
|
trap_->AddTrapIfFalse(kTrapFuncInvalid, Int32Constant(0));
|
|
|
|
return trap_->GetTrapValue(module_->GetSignature(index));
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
2016-01-28 16:56:54 +00:00
|
|
|
Node* table = FunctionTable();
|
2015-12-11 12:26:16 +00:00
|
|
|
|
|
|
|
// Load signature from the table and check.
|
|
|
|
// The table is a FixedArray; signatures are encoded as SMIs.
|
|
|
|
// [sig1, sig2, sig3, ...., code1, code2, code3 ...]
|
|
|
|
ElementAccess access = AccessBuilder::ForFixedArrayElement();
|
|
|
|
const int fixed_offset = access.header_size - access.tag();
|
|
|
|
{
|
|
|
|
Node* load_sig = graph()->NewNode(
|
|
|
|
machine->Load(MachineType::AnyTagged()), table,
|
|
|
|
graph()->NewNode(machine->Int32Add(),
|
|
|
|
graph()->NewNode(machine->Word32Shl(), key,
|
|
|
|
Int32Constant(kPointerSizeLog2)),
|
|
|
|
Int32Constant(fixed_offset)),
|
|
|
|
*effect_, *control_);
|
|
|
|
Node* sig_match = graph()->NewNode(machine->WordEqual(), load_sig,
|
|
|
|
jsgraph()->SmiConstant(index));
|
|
|
|
trap_->AddTrapIfFalse(kTrapFuncSigMismatch, sig_match);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load code object from the table.
|
|
|
|
int offset = fixed_offset + kPointerSize * table_size;
|
|
|
|
Node* load_code = graph()->NewNode(
|
|
|
|
machine->Load(MachineType::AnyTagged()), table,
|
|
|
|
graph()->NewNode(machine->Int32Add(),
|
|
|
|
graph()->NewNode(machine->Word32Shl(), key,
|
|
|
|
Int32Constant(kPointerSizeLog2)),
|
|
|
|
Int32Constant(offset)),
|
|
|
|
*effect_, *control_);
|
|
|
|
|
|
|
|
args[0] = load_code;
|
|
|
|
wasm::FunctionSig* sig = module_->GetSignature(index);
|
|
|
|
return BuildWasmCall(sig, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::ToJS(Node* node, Node* context, wasm::LocalType type) {
|
|
|
|
SimplifiedOperatorBuilder simplified(jsgraph()->zone());
|
|
|
|
switch (type) {
|
|
|
|
case wasm::kAstI32:
|
|
|
|
return graph()->NewNode(simplified.ChangeInt32ToTagged(), node);
|
|
|
|
case wasm::kAstI64:
|
|
|
|
// TODO(titzer): i64->JS has no good solution right now. Using lower 32
|
|
|
|
// bits.
|
|
|
|
node =
|
|
|
|
graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), node);
|
|
|
|
return graph()->NewNode(simplified.ChangeInt32ToTagged(), node);
|
|
|
|
case wasm::kAstF32:
|
|
|
|
node = graph()->NewNode(jsgraph()->machine()->ChangeFloat32ToFloat64(),
|
|
|
|
node);
|
|
|
|
return graph()->NewNode(simplified.ChangeFloat64ToTagged(), node);
|
|
|
|
case wasm::kAstF64:
|
|
|
|
return graph()->NewNode(simplified.ChangeFloat64ToTagged(), node);
|
|
|
|
case wasm::kAstStmt:
|
|
|
|
return jsgraph()->UndefinedConstant();
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::FromJS(Node* node, Node* context,
|
|
|
|
wasm::LocalType type) {
|
|
|
|
// Do a JavaScript ToNumber.
|
|
|
|
Node* num =
|
|
|
|
graph()->NewNode(jsgraph()->javascript()->ToNumber(), node, context,
|
|
|
|
jsgraph()->EmptyFrameState(), *effect_, *control_);
|
|
|
|
*control_ = num;
|
|
|
|
*effect_ = num;
|
|
|
|
|
|
|
|
// Change representation.
|
|
|
|
SimplifiedOperatorBuilder simplified(jsgraph()->zone());
|
|
|
|
num = graph()->NewNode(simplified.ChangeTaggedToFloat64(), num);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case wasm::kAstI32: {
|
|
|
|
num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToInt32(
|
|
|
|
TruncationMode::kJavaScript),
|
|
|
|
num);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case wasm::kAstI64:
|
|
|
|
// TODO(titzer): JS->i64 has no good solution right now. Using 32 bits.
|
|
|
|
num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToInt32(
|
|
|
|
TruncationMode::kJavaScript),
|
|
|
|
num);
|
|
|
|
num = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), num);
|
|
|
|
break;
|
|
|
|
case wasm::kAstF32:
|
|
|
|
num = graph()->NewNode(jsgraph()->machine()->TruncateFloat64ToFloat32(),
|
|
|
|
num);
|
|
|
|
break;
|
|
|
|
case wasm::kAstF64:
|
|
|
|
break;
|
|
|
|
case wasm::kAstStmt:
|
|
|
|
num = jsgraph()->Int32Constant(0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::Invert(Node* node) {
|
|
|
|
return Unop(wasm::kExprBoolNot, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
|
|
|
wasm::FunctionSig* sig) {
|
|
|
|
int params = static_cast<int>(sig->parameter_count());
|
|
|
|
int count = params + 3;
|
|
|
|
Node** args = Buffer(count);
|
|
|
|
|
|
|
|
// Build the start and the JS parameter nodes.
|
|
|
|
Node* start = Start(params + 3);
|
|
|
|
*control_ = start;
|
|
|
|
*effect_ = start;
|
|
|
|
// JS context is the last parameter.
|
|
|
|
Node* context = graph()->NewNode(
|
|
|
|
jsgraph()->common()->Parameter(params + 1, "context"), start);
|
|
|
|
|
|
|
|
int pos = 0;
|
|
|
|
args[pos++] = Constant(wasm_code);
|
|
|
|
|
|
|
|
// Convert JS parameters to WASM numbers.
|
|
|
|
for (int i = 0; i < params; i++) {
|
|
|
|
Node* param = graph()->NewNode(jsgraph()->common()->Parameter(i), start);
|
|
|
|
args[pos++] = FromJS(param, context, sig->GetParam(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
args[pos++] = *effect_;
|
|
|
|
args[pos++] = *control_;
|
|
|
|
|
|
|
|
// Call the WASM code.
|
2016-02-23 15:33:06 +00:00
|
|
|
CallDescriptor* desc =
|
|
|
|
wasm::ModuleEnv::GetWasmCallDescriptor(jsgraph()->zone(), sig);
|
2015-12-11 12:26:16 +00:00
|
|
|
Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), count, args);
|
|
|
|
Node* jsval =
|
|
|
|
ToJS(call, context,
|
|
|
|
sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn());
|
|
|
|
Node* ret =
|
|
|
|
graph()->NewNode(jsgraph()->common()->Return(), jsval, call, start);
|
|
|
|
|
|
|
|
MergeControlToEnd(jsgraph(), ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function,
|
|
|
|
wasm::FunctionSig* sig) {
|
|
|
|
int js_count = function->shared()->internal_formal_parameter_count();
|
|
|
|
int wasm_count = static_cast<int>(sig->parameter_count());
|
|
|
|
|
|
|
|
// Build the start and the parameter nodes.
|
|
|
|
Isolate* isolate = jsgraph()->isolate();
|
|
|
|
CallDescriptor* desc;
|
|
|
|
Node* start = Start(wasm_count + 3);
|
|
|
|
*effect_ = start;
|
|
|
|
*control_ = start;
|
|
|
|
// JS context is the last parameter.
|
|
|
|
Node* context = Constant(Handle<Context>(function->context(), isolate));
|
|
|
|
Node** args = Buffer(wasm_count + 7);
|
|
|
|
|
|
|
|
bool arg_count_before_args = false;
|
|
|
|
bool add_new_target_undefined = false;
|
|
|
|
|
|
|
|
int pos = 0;
|
|
|
|
if (js_count == wasm_count) {
|
|
|
|
// exact arity match, just call the function directly.
|
|
|
|
desc = Linkage::GetJSCallDescriptor(graph()->zone(), false, wasm_count + 1,
|
|
|
|
CallDescriptor::kNoFlags);
|
|
|
|
arg_count_before_args = false;
|
|
|
|
add_new_target_undefined = true;
|
|
|
|
} else {
|
|
|
|
// Use the Call builtin.
|
|
|
|
Callable callable = CodeFactory::Call(isolate);
|
|
|
|
args[pos++] = jsgraph()->HeapConstant(callable.code());
|
|
|
|
desc = Linkage::GetStubCallDescriptor(isolate, graph()->zone(),
|
|
|
|
callable.descriptor(), wasm_count + 1,
|
|
|
|
CallDescriptor::kNoFlags);
|
|
|
|
arg_count_before_args = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
args[pos++] = jsgraph()->Constant(function); // JS function.
|
|
|
|
if (arg_count_before_args) {
|
|
|
|
args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count
|
|
|
|
}
|
2016-01-14 09:48:45 +00:00
|
|
|
// JS receiver.
|
|
|
|
Handle<Object> global(function->context()->global_object(), isolate);
|
|
|
|
args[pos++] = jsgraph()->Constant(global);
|
2015-12-11 12:26:16 +00:00
|
|
|
|
|
|
|
// Convert WASM numbers to JS values.
|
|
|
|
for (int i = 0; i < wasm_count; i++) {
|
|
|
|
Node* param = graph()->NewNode(jsgraph()->common()->Parameter(i), start);
|
|
|
|
args[pos++] = ToJS(param, context, sig->GetParam(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (add_new_target_undefined) {
|
|
|
|
args[pos++] = jsgraph()->UndefinedConstant(); // new target
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!arg_count_before_args) {
|
|
|
|
args[pos++] = jsgraph()->Int32Constant(wasm_count); // argument count
|
|
|
|
}
|
|
|
|
args[pos++] = context;
|
|
|
|
args[pos++] = *effect_;
|
|
|
|
args[pos++] = *control_;
|
|
|
|
|
|
|
|
Node* call = graph()->NewNode(jsgraph()->common()->Call(desc), pos, args);
|
|
|
|
|
|
|
|
// Convert the return value back.
|
|
|
|
Node* val =
|
|
|
|
FromJS(call, context,
|
|
|
|
sig->return_count() == 0 ? wasm::kAstStmt : sig->GetReturn());
|
|
|
|
Node* ret = graph()->NewNode(jsgraph()->common()->Return(), val, call, start);
|
|
|
|
|
|
|
|
MergeControlToEnd(jsgraph(), ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(module_ && module_->instance);
|
2015-12-11 12:26:16 +00:00
|
|
|
if (offset == 0) {
|
2016-01-27 11:04:02 +00:00
|
|
|
if (!mem_buffer_) {
|
|
|
|
mem_buffer_ = jsgraph()->IntPtrConstant(
|
|
|
|
reinterpret_cast<uintptr_t>(module_->instance->mem_start));
|
|
|
|
}
|
2015-12-11 12:26:16 +00:00
|
|
|
return mem_buffer_;
|
|
|
|
} else {
|
2016-01-27 11:04:02 +00:00
|
|
|
return jsgraph()->IntPtrConstant(
|
|
|
|
reinterpret_cast<uintptr_t>(module_->instance->mem_start + offset));
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::MemSize(uint32_t offset) {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(module_ && module_->instance);
|
|
|
|
uint32_t size = static_cast<uint32_t>(module_->instance->mem_size);
|
2015-12-11 12:26:16 +00:00
|
|
|
if (offset == 0) {
|
|
|
|
if (!mem_size_) mem_size_ = jsgraph()->Int32Constant(size);
|
|
|
|
return mem_size_;
|
|
|
|
} else {
|
|
|
|
return jsgraph()->Int32Constant(size + offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::FunctionTable() {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(module_ && module_->instance &&
|
|
|
|
!module_->instance->function_table.is_null());
|
2015-12-11 12:26:16 +00:00
|
|
|
if (!function_table_) {
|
2016-01-27 11:04:02 +00:00
|
|
|
function_table_ = jsgraph()->Constant(module_->instance->function_table);
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
return function_table_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::LoadGlobal(uint32_t index) {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(module_ && module_->instance && module_->instance->globals_start);
|
2015-12-11 12:26:16 +00:00
|
|
|
MachineType mem_type = module_->GetGlobalType(index);
|
|
|
|
Node* addr = jsgraph()->IntPtrConstant(
|
2016-01-27 11:04:02 +00:00
|
|
|
reinterpret_cast<uintptr_t>(module_->instance->globals_start +
|
|
|
|
module_->module->globals->at(index).offset));
|
2015-12-11 12:26:16 +00:00
|
|
|
const Operator* op = jsgraph()->machine()->Load(mem_type);
|
|
|
|
Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), *effect_,
|
|
|
|
*control_);
|
|
|
|
*effect_ = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::StoreGlobal(uint32_t index, Node* val) {
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(module_ && module_->instance && module_->instance->globals_start);
|
2015-12-11 12:26:16 +00:00
|
|
|
MachineType mem_type = module_->GetGlobalType(index);
|
|
|
|
Node* addr = jsgraph()->IntPtrConstant(
|
2016-01-27 11:04:02 +00:00
|
|
|
reinterpret_cast<uintptr_t>(module_->instance->globals_start +
|
|
|
|
module_->module->globals->at(index).offset));
|
2015-12-11 12:26:16 +00:00
|
|
|
const Operator* op = jsgraph()->machine()->Store(
|
2015-12-11 15:34:00 +00:00
|
|
|
StoreRepresentation(mem_type.representation(), kNoWriteBarrier));
|
2015-12-11 12:26:16 +00:00
|
|
|
Node* node = graph()->NewNode(op, addr, jsgraph()->Int32Constant(0), val,
|
|
|
|
*effect_, *control_);
|
|
|
|
*effect_ = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
|
|
|
|
uint32_t offset) {
|
|
|
|
// TODO(turbofan): fold bounds checks for constant indexes.
|
2016-01-27 11:04:02 +00:00
|
|
|
DCHECK(module_ && module_->instance);
|
|
|
|
size_t size = module_->instance->mem_size;
|
2015-12-11 12:26:16 +00:00
|
|
|
byte memsize = wasm::WasmOpcodes::MemSize(memtype);
|
|
|
|
Node* cond;
|
2016-01-27 11:04:02 +00:00
|
|
|
if (offset >= size || (static_cast<uint64_t>(offset) + memsize) > size) {
|
2015-12-11 12:26:16 +00:00
|
|
|
// The access will always throw.
|
|
|
|
cond = jsgraph()->Int32Constant(0);
|
|
|
|
} else {
|
|
|
|
// Check against the limit.
|
|
|
|
size_t limit = size - offset - memsize;
|
|
|
|
CHECK(limit <= kMaxUInt32);
|
|
|
|
cond = graph()->NewNode(
|
|
|
|
jsgraph()->machine()->Uint32LessThanOrEqual(), index,
|
|
|
|
jsgraph()->Int32Constant(static_cast<uint32_t>(limit)));
|
|
|
|
}
|
|
|
|
|
|
|
|
trap_->AddTrapIfFalse(kTrapMemOutOfBounds, cond);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::LoadMem(wasm::LocalType type, MachineType memtype,
|
|
|
|
Node* index, uint32_t offset) {
|
|
|
|
Node* load;
|
|
|
|
|
|
|
|
if (module_ && module_->asm_js) {
|
|
|
|
// asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish).
|
|
|
|
DCHECK_EQ(0, offset);
|
|
|
|
const Operator* op = jsgraph()->machine()->CheckedLoad(memtype);
|
|
|
|
load = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), *effect_,
|
|
|
|
*control_);
|
|
|
|
} else {
|
|
|
|
// WASM semantics throw on OOB. Introduce explicit bounds check.
|
|
|
|
BoundsCheckMem(memtype, index, offset);
|
|
|
|
load = graph()->NewNode(jsgraph()->machine()->Load(memtype),
|
|
|
|
MemBuffer(offset), index, *effect_, *control_);
|
|
|
|
}
|
|
|
|
|
|
|
|
*effect_ = load;
|
|
|
|
|
|
|
|
if (type == wasm::kAstI64 &&
|
|
|
|
ElementSizeLog2Of(memtype.representation()) < 3) {
|
|
|
|
// TODO(titzer): TF zeroes the upper bits of 64-bit loads for subword sizes.
|
|
|
|
if (memtype.IsSigned()) {
|
|
|
|
// sign extend
|
|
|
|
load = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), load);
|
|
|
|
} else {
|
|
|
|
// zero extend
|
|
|
|
load =
|
|
|
|
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), load);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return load;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
|
|
|
|
uint32_t offset, Node* val) {
|
|
|
|
Node* store;
|
|
|
|
if (module_ && module_->asm_js) {
|
|
|
|
// asm.js semantics use CheckedStore (i.e. ignore OOB writes).
|
|
|
|
DCHECK_EQ(0, offset);
|
2015-12-11 15:34:00 +00:00
|
|
|
const Operator* op =
|
|
|
|
jsgraph()->machine()->CheckedStore(memtype.representation());
|
2015-12-11 12:26:16 +00:00
|
|
|
store = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), val, *effect_,
|
|
|
|
*control_);
|
|
|
|
} else {
|
|
|
|
// WASM semantics throw on OOB. Introduce explicit bounds check.
|
|
|
|
BoundsCheckMem(memtype, index, offset);
|
2015-12-11 15:34:00 +00:00
|
|
|
StoreRepresentation rep(memtype.representation(), kNoWriteBarrier);
|
2015-12-11 12:26:16 +00:00
|
|
|
store =
|
|
|
|
graph()->NewNode(jsgraph()->machine()->Store(rep), MemBuffer(offset),
|
|
|
|
index, val, *effect_, *control_);
|
|
|
|
}
|
|
|
|
*effect_ = store;
|
|
|
|
return store;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void WasmGraphBuilder::PrintDebugName(Node* node) {
|
|
|
|
PrintF("#%d:%s", node->id(), node->op()->mnemonic());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node* WasmGraphBuilder::String(const char* string) {
|
|
|
|
return jsgraph()->Constant(
|
|
|
|
jsgraph()->isolate()->factory()->NewStringFromAsciiChecked(string));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Graph* WasmGraphBuilder::graph() { return jsgraph()->graph(); }
|
|
|
|
|
2016-02-04 09:40:55 +00:00
|
|
|
void WasmGraphBuilder::Int64LoweringForTesting() {
|
2016-02-18 15:18:41 +00:00
|
|
|
if (kPointerSize == 4) {
|
|
|
|
Int64Lowering r(jsgraph()->graph(), jsgraph()->machine(),
|
|
|
|
jsgraph()->common(), jsgraph()->zone(),
|
|
|
|
function_signature_);
|
|
|
|
r.LowerGraph();
|
|
|
|
}
|
2016-02-04 09:40:55 +00:00
|
|
|
}
|
2015-12-11 12:26:16 +00:00
|
|
|
|
2016-01-29 11:21:34 +00:00
|
|
|
static void RecordFunctionCompilation(Logger::LogEventsAndTags tag,
|
|
|
|
CompilationInfo* info,
|
|
|
|
const char* message, uint32_t index,
|
|
|
|
const char* func_name) {
|
|
|
|
Isolate* isolate = info->isolate();
|
|
|
|
if (isolate->logger()->is_logging_code_events() ||
|
|
|
|
isolate->cpu_profiler()->is_profiling()) {
|
|
|
|
ScopedVector<char> buffer(128);
|
|
|
|
SNPrintF(buffer, "%s#%d:%s", message, index, func_name);
|
|
|
|
Handle<String> name_str =
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked(buffer.start());
|
|
|
|
Handle<String> script_str =
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked("(WASM)");
|
|
|
|
Handle<Code> code = info->code();
|
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
isolate->factory()->NewSharedFunctionInfo(name_str, code, false);
|
|
|
|
PROFILE(isolate,
|
|
|
|
CodeCreateEvent(tag, *code, *shared, info, *script_str, 0, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-17 14:52:27 +00:00
|
|
|
Handle<JSFunction> CompileJSToWasmWrapper(
|
|
|
|
Isolate* isolate, wasm::ModuleEnv* module, Handle<String> name,
|
|
|
|
Handle<Code> wasm_code, Handle<JSObject> module_object, uint32_t index) {
|
2015-12-11 12:26:16 +00:00
|
|
|
wasm::WasmFunction* func = &module->module->functions->at(index);
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Create the JSFunction object.
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
Handle<SharedFunctionInfo> shared =
|
|
|
|
isolate->factory()->NewSharedFunctionInfo(name, wasm_code, false);
|
|
|
|
int params = static_cast<int>(func->sig->parameter_count());
|
|
|
|
shared->set_length(params);
|
|
|
|
shared->set_internal_formal_parameter_count(1 + params);
|
2015-12-17 14:52:27 +00:00
|
|
|
Handle<JSFunction> function = isolate->factory()->NewFunction(
|
|
|
|
isolate->wasm_function_map(), name, MaybeHandle<Code>());
|
|
|
|
function->SetInternalField(0, *module_object);
|
2015-12-11 12:26:16 +00:00
|
|
|
function->set_shared(*shared);
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Create the Graph
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
Zone zone;
|
|
|
|
Graph graph(&zone);
|
|
|
|
CommonOperatorBuilder common(&zone);
|
|
|
|
JSOperatorBuilder javascript(&zone);
|
|
|
|
MachineOperatorBuilder machine(&zone);
|
|
|
|
JSGraph jsgraph(isolate, &graph, &common, &javascript, nullptr, &machine);
|
|
|
|
|
|
|
|
Node* control = nullptr;
|
|
|
|
Node* effect = nullptr;
|
|
|
|
|
2015-12-11 14:54:07 +00:00
|
|
|
WasmGraphBuilder builder(&zone, &jsgraph, func->sig);
|
2015-12-11 12:26:16 +00:00
|
|
|
builder.set_control_ptr(&control);
|
|
|
|
builder.set_effect_ptr(&effect);
|
|
|
|
builder.set_module(module);
|
|
|
|
builder.BuildJSToWasmWrapper(wasm_code, func->sig);
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Run the compilation pipeline.
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
{
|
|
|
|
// Changes lowering requires types.
|
|
|
|
Typer typer(isolate, &graph);
|
|
|
|
NodeVector roots(&zone);
|
|
|
|
jsgraph.GetCachedNodes(&roots);
|
|
|
|
typer.Run(roots);
|
|
|
|
|
|
|
|
// Run generic and change lowering.
|
2016-02-19 08:03:07 +00:00
|
|
|
JSGenericLowering generic(true, &jsgraph);
|
2016-02-19 10:13:37 +00:00
|
|
|
ChangeLowering changes(&jsgraph);
|
|
|
|
GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead());
|
2015-12-11 12:26:16 +00:00
|
|
|
graph_reducer.AddReducer(&changes);
|
|
|
|
graph_reducer.AddReducer(&generic);
|
|
|
|
graph_reducer.ReduceGraph();
|
|
|
|
|
|
|
|
if (FLAG_trace_turbo_graph) { // Simple textual RPO.
|
|
|
|
OFStream os(stdout);
|
|
|
|
os << "-- Graph after change lowering -- " << std::endl;
|
|
|
|
os << AsRPO(graph);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Schedule and compile to machine code.
|
|
|
|
int params = static_cast<int>(
|
|
|
|
module->GetFunctionSignature(index)->parameter_count());
|
|
|
|
CallDescriptor* incoming = Linkage::GetJSCallDescriptor(
|
|
|
|
&zone, false, params + 1, CallDescriptor::kNoFlags);
|
|
|
|
// TODO(titzer): this is technically a WASM wrapper, not a wasm function.
|
2016-01-20 15:17:39 +00:00
|
|
|
Code::Flags flags = Code::ComputeFlags(Code::WASM_FUNCTION);
|
2016-02-18 16:51:43 +00:00
|
|
|
bool debugging =
|
|
|
|
#if DEBUG
|
|
|
|
true;
|
|
|
|
#else
|
|
|
|
FLAG_print_opt_code || FLAG_trace_turbo || FLAG_trace_turbo_graph;
|
|
|
|
#endif
|
|
|
|
const char* func_name = "js-to-wasm";
|
|
|
|
|
|
|
|
static unsigned id = 0;
|
|
|
|
Vector<char> buffer;
|
|
|
|
if (debugging) {
|
|
|
|
buffer = Vector<char>::New(128);
|
|
|
|
SNPrintF(buffer, "js-to-wasm#%d", id);
|
|
|
|
func_name = buffer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
CompilationInfo info(func_name, isolate, &zone, flags);
|
2015-12-11 12:26:16 +00:00
|
|
|
Handle<Code> code =
|
|
|
|
Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr);
|
2016-02-18 16:51:43 +00:00
|
|
|
if (debugging) {
|
|
|
|
buffer.Dispose();
|
|
|
|
}
|
|
|
|
|
2016-01-29 11:21:34 +00:00
|
|
|
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, "js-to-wasm", index,
|
|
|
|
module->module->GetName(func->name_offset));
|
2015-12-11 12:26:16 +00:00
|
|
|
// Set the JSFunction's machine code.
|
|
|
|
function->set_code(*code);
|
|
|
|
}
|
|
|
|
return function;
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
|
|
|
|
Handle<JSFunction> function,
|
2016-02-19 14:58:25 +00:00
|
|
|
wasm::FunctionSig* sig, const char* name) {
|
2015-12-11 12:26:16 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Create the Graph
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
Zone zone;
|
|
|
|
Graph graph(&zone);
|
|
|
|
CommonOperatorBuilder common(&zone);
|
|
|
|
JSOperatorBuilder javascript(&zone);
|
|
|
|
MachineOperatorBuilder machine(&zone);
|
|
|
|
JSGraph jsgraph(isolate, &graph, &common, &javascript, nullptr, &machine);
|
|
|
|
|
|
|
|
Node* control = nullptr;
|
|
|
|
Node* effect = nullptr;
|
|
|
|
|
2016-02-19 14:58:25 +00:00
|
|
|
WasmGraphBuilder builder(&zone, &jsgraph, sig);
|
2015-12-11 12:26:16 +00:00
|
|
|
builder.set_control_ptr(&control);
|
|
|
|
builder.set_effect_ptr(&effect);
|
|
|
|
builder.set_module(module);
|
2016-02-19 14:58:25 +00:00
|
|
|
builder.BuildWasmToJSWrapper(function, sig);
|
2015-12-11 12:26:16 +00:00
|
|
|
|
|
|
|
Handle<Code> code = Handle<Code>::null();
|
|
|
|
{
|
|
|
|
// Changes lowering requires types.
|
|
|
|
Typer typer(isolate, &graph);
|
|
|
|
NodeVector roots(&zone);
|
|
|
|
jsgraph.GetCachedNodes(&roots);
|
|
|
|
typer.Run(roots);
|
|
|
|
|
|
|
|
// Run generic and change lowering.
|
2016-02-19 08:03:07 +00:00
|
|
|
JSGenericLowering generic(true, &jsgraph);
|
2016-02-19 10:13:37 +00:00
|
|
|
ChangeLowering changes(&jsgraph);
|
|
|
|
GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead());
|
2015-12-11 12:26:16 +00:00
|
|
|
graph_reducer.AddReducer(&changes);
|
|
|
|
graph_reducer.AddReducer(&generic);
|
|
|
|
graph_reducer.ReduceGraph();
|
|
|
|
|
|
|
|
if (FLAG_trace_turbo_graph) { // Simple textual RPO.
|
|
|
|
OFStream os(stdout);
|
|
|
|
os << "-- Graph after change lowering -- " << std::endl;
|
|
|
|
os << AsRPO(graph);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Schedule and compile to machine code.
|
2016-02-23 15:33:06 +00:00
|
|
|
CallDescriptor* incoming =
|
|
|
|
wasm::ModuleEnv::GetWasmCallDescriptor(&zone, sig);
|
2015-12-11 12:26:16 +00:00
|
|
|
// TODO(titzer): this is technically a WASM wrapper, not a wasm function.
|
2016-01-20 15:17:39 +00:00
|
|
|
Code::Flags flags = Code::ComputeFlags(Code::WASM_FUNCTION);
|
2016-02-18 16:51:43 +00:00
|
|
|
bool debugging =
|
|
|
|
#if DEBUG
|
|
|
|
true;
|
|
|
|
#else
|
|
|
|
FLAG_print_opt_code || FLAG_trace_turbo || FLAG_trace_turbo_graph;
|
|
|
|
#endif
|
|
|
|
const char* func_name = "wasm-to-js";
|
|
|
|
static unsigned id = 0;
|
|
|
|
Vector<char> buffer;
|
|
|
|
if (debugging) {
|
|
|
|
buffer = Vector<char>::New(128);
|
|
|
|
SNPrintF(buffer, "wasm-to-js#%d", id);
|
|
|
|
func_name = buffer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
CompilationInfo info(func_name, isolate, &zone, flags);
|
2015-12-11 12:26:16 +00:00
|
|
|
code = Pipeline::GenerateCodeForTesting(&info, incoming, &graph, nullptr);
|
2016-02-18 16:51:43 +00:00
|
|
|
if (debugging) {
|
|
|
|
buffer.Dispose();
|
|
|
|
}
|
2015-12-11 12:26:16 +00:00
|
|
|
|
2016-02-19 14:58:25 +00:00
|
|
|
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, "wasm-to-js", 0,
|
|
|
|
name);
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function to compile a single function.
|
|
|
|
Handle<Code> CompileWasmFunction(wasm::ErrorThrower& thrower, Isolate* isolate,
|
|
|
|
wasm::ModuleEnv* module_env,
|
2016-02-15 15:59:57 +00:00
|
|
|
const wasm::WasmFunction& function) {
|
2015-12-11 12:26:16 +00:00
|
|
|
if (FLAG_trace_wasm_compiler || FLAG_trace_wasm_decode_time) {
|
|
|
|
OFStream os(stdout);
|
2016-02-15 15:59:57 +00:00
|
|
|
os << "Compiling WASM function "
|
|
|
|
<< wasm::WasmFunctionName(&function, module_env) << std::endl;
|
2015-12-11 12:26:16 +00:00
|
|
|
os << std::endl;
|
|
|
|
}
|
|
|
|
// Initialize the function environment for decoding.
|
|
|
|
wasm::FunctionEnv env;
|
|
|
|
env.module = module_env;
|
|
|
|
env.sig = function.sig;
|
2016-02-03 11:05:57 +00:00
|
|
|
env.local_i32_count = function.local_i32_count;
|
|
|
|
env.local_i64_count = function.local_i64_count;
|
|
|
|
env.local_f32_count = function.local_f32_count;
|
|
|
|
env.local_f64_count = function.local_f64_count;
|
2015-12-11 12:26:16 +00:00
|
|
|
env.SumLocals();
|
|
|
|
|
|
|
|
// Create a TF graph during decoding.
|
|
|
|
Zone zone;
|
|
|
|
Graph graph(&zone);
|
|
|
|
CommonOperatorBuilder common(&zone);
|
|
|
|
MachineOperatorBuilder machine(
|
|
|
|
&zone, MachineType::PointerRepresentation(),
|
|
|
|
InstructionSelector::SupportedMachineOperatorFlags());
|
|
|
|
JSGraph jsgraph(isolate, &graph, &common, nullptr, nullptr, &machine);
|
2015-12-11 14:54:07 +00:00
|
|
|
WasmGraphBuilder builder(&zone, &jsgraph, function.sig);
|
2015-12-11 12:26:16 +00:00
|
|
|
wasm::TreeResult result = wasm::BuildTFGraph(
|
|
|
|
&builder, &env, // --
|
|
|
|
module_env->module->module_start, // --
|
|
|
|
module_env->module->module_start + function.code_start_offset, // --
|
|
|
|
module_env->module->module_start + function.code_end_offset); // --
|
|
|
|
|
|
|
|
if (result.failed()) {
|
|
|
|
if (FLAG_trace_wasm_compiler) {
|
|
|
|
OFStream os(stdout);
|
|
|
|
os << "Compilation failed: " << result << std::endl;
|
|
|
|
}
|
|
|
|
// Add the function as another context for the exception
|
2016-01-29 11:21:34 +00:00
|
|
|
ScopedVector<char> buffer(128);
|
2016-02-15 15:59:57 +00:00
|
|
|
SNPrintF(buffer, "Compiling WASM function #%d:%s failed:",
|
|
|
|
function.func_index,
|
2015-12-11 12:26:16 +00:00
|
|
|
module_env->module->GetName(function.name_offset));
|
2015-12-17 14:52:27 +00:00
|
|
|
thrower.Failed(buffer.start(), result);
|
2015-12-11 12:26:16 +00:00
|
|
|
return Handle<Code>::null();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the compiler pipeline to generate machine code.
|
2016-02-18 15:18:41 +00:00
|
|
|
CallDescriptor* descriptor =
|
2016-02-23 15:33:06 +00:00
|
|
|
wasm::ModuleEnv::GetWasmCallDescriptor(&zone, function.sig);
|
2016-02-18 15:18:41 +00:00
|
|
|
if (kPointerSize == 4) {
|
|
|
|
descriptor = module_env->GetI32WasmCallDescriptor(&zone, descriptor);
|
|
|
|
}
|
2016-01-20 15:17:39 +00:00
|
|
|
Code::Flags flags = Code::ComputeFlags(Code::WASM_FUNCTION);
|
2016-01-29 11:21:34 +00:00
|
|
|
// add flags here if a meaningful name is helpful for debugging.
|
|
|
|
bool debugging =
|
2016-02-17 03:51:22 +00:00
|
|
|
#if DEBUG
|
|
|
|
true;
|
|
|
|
#else
|
2016-01-29 11:21:34 +00:00
|
|
|
FLAG_print_opt_code || FLAG_trace_turbo || FLAG_trace_turbo_graph;
|
2016-02-17 03:51:22 +00:00
|
|
|
#endif
|
2016-01-29 11:21:34 +00:00
|
|
|
const char* func_name = "wasm";
|
|
|
|
Vector<char> buffer;
|
|
|
|
if (debugging) {
|
|
|
|
buffer = Vector<char>::New(128);
|
2016-02-15 15:59:57 +00:00
|
|
|
SNPrintF(buffer, "WASM_function_#%d:%s", function.func_index,
|
2016-01-29 11:21:34 +00:00
|
|
|
module_env->module->GetName(function.name_offset));
|
|
|
|
func_name = buffer.start();
|
|
|
|
}
|
|
|
|
CompilationInfo info(func_name, isolate, &zone, flags);
|
2016-02-04 09:40:55 +00:00
|
|
|
|
2015-12-11 12:26:16 +00:00
|
|
|
Handle<Code> code =
|
|
|
|
Pipeline::GenerateCodeForTesting(&info, descriptor, &graph);
|
2016-01-29 11:21:34 +00:00
|
|
|
if (debugging) {
|
|
|
|
buffer.Dispose();
|
2015-12-11 12:26:16 +00:00
|
|
|
}
|
2016-01-29 11:21:34 +00:00
|
|
|
if (!code.is_null()) {
|
|
|
|
RecordFunctionCompilation(
|
2016-02-15 15:59:57 +00:00
|
|
|
Logger::FUNCTION_TAG, &info, "WASM_function", function.func_index,
|
2016-01-29 11:21:34 +00:00
|
|
|
module_env->module->GetName(function.name_offset));
|
|
|
|
}
|
|
|
|
|
2015-12-11 12:26:16 +00:00
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace compiler
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|