[turbofan] Remove obsolete LoadBuffer and StoreBuffer operators.

These operators were only used by the old asm.js pipeline (with
fullcodegen and the AstGraphBuilder). When going through the new
pipeline, accesses to TypedArrays are handled by the native
context specialization during inlining.

Bug: v8:6409
Change-Id: Ib9b888c0b96f297a335580ee42dfa951bde566be
Reviewed-on: https://chromium-review.googlesource.com/612347
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47322}
This commit is contained in:
Benedikt Meurer 2017-08-11 19:21:22 +02:00 committed by Commit Bot
parent 438a845c52
commit 94830f4b1b
14 changed files with 1 additions and 899 deletions

View File

@ -411,13 +411,7 @@ JSTypedLowering::JSTypedLowering(Editor* editor,
Type::Union(Type::SymbolOrReceiver(), empty_string_type_,
graph()->zone()),
graph()->zone())),
type_cache_(TypeCache::Get()) {
for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) {
double min = kMinInt / (1 << k);
double max = kMaxInt / (1 << k);
shifted_int32_ranges_[k] = Type::Range(min, max, graph()->zone());
}
}
type_cache_(TypeCache::Get()) {}
Reduction JSTypedLowering::ReduceSpeculativeNumberAdd(Node* node) {
JSBinopReduction r(this, node);
@ -1135,131 +1129,6 @@ Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) {
return NoChange();
}
Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) {
Node* key = NodeProperties::GetValueInput(node, 1);
Node* base = NodeProperties::GetValueInput(node, 0);
Type* key_type = NodeProperties::GetType(key);
HeapObjectMatcher mbase(base);
if (mbase.HasValue() && mbase.Value()->IsJSTypedArray()) {
Handle<JSTypedArray> const array =
Handle<JSTypedArray>::cast(mbase.Value());
if (!array->GetBuffer()->was_neutered() &&
!array->GetBuffer()->is_wasm_buffer()) {
array->GetBuffer()->set_is_neuterable(false);
BufferAccess const access(array->type());
size_t const k =
ElementSizeLog2Of(access.machine_type().representation());
double const byte_length = array->byte_length()->Number();
CHECK_LT(k, arraysize(shifted_int32_ranges_));
if (key_type->Is(shifted_int32_ranges_[k]) && byte_length <= kMaxInt) {
// JSLoadProperty(typed-array, int32)
Handle<FixedTypedArrayBase> elements =
Handle<FixedTypedArrayBase>::cast(handle(array->elements()));
Node* buffer = jsgraph()->PointerConstant(elements->external_pointer());
Node* length = jsgraph()->Constant(byte_length);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Check if we can avoid the bounds check.
if (key_type->Min() >= 0 && key_type->Max() < array->length_value()) {
Node* load = graph()->NewNode(
simplified()->LoadElement(
AccessBuilder::ForTypedArrayElement(array->type(), true)),
buffer, key, effect, control);
ReplaceWithValue(node, load, load);
return Replace(load);
}
// Compute byte offset.
Node* offset =
(k == 0) ? key : graph()->NewNode(
simplified()->NumberShiftLeft(), key,
jsgraph()->Constant(static_cast<double>(k)));
Node* load = graph()->NewNode(simplified()->LoadBuffer(access), buffer,
offset, length, effect, control);
ReplaceWithValue(node, load, load);
return Replace(load);
}
}
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
Node* key = NodeProperties::GetValueInput(node, 1);
Node* base = NodeProperties::GetValueInput(node, 0);
Node* value = NodeProperties::GetValueInput(node, 2);
Type* key_type = NodeProperties::GetType(key);
Type* value_type = NodeProperties::GetType(value);
if (!value_type->Is(Type::PlainPrimitive())) return NoChange();
HeapObjectMatcher mbase(base);
if (mbase.HasValue() && mbase.Value()->IsJSTypedArray()) {
Handle<JSTypedArray> const array =
Handle<JSTypedArray>::cast(mbase.Value());
if (!array->GetBuffer()->was_neutered() &&
!array->GetBuffer()->is_wasm_buffer()) {
array->GetBuffer()->set_is_neuterable(false);
BufferAccess const access(array->type());
size_t const k =
ElementSizeLog2Of(access.machine_type().representation());
double const byte_length = array->byte_length()->Number();
CHECK_LT(k, arraysize(shifted_int32_ranges_));
if (access.external_array_type() != kExternalUint8ClampedArray &&
key_type->Is(shifted_int32_ranges_[k]) && byte_length <= kMaxInt) {
// JSLoadProperty(typed-array, int32)
Handle<FixedTypedArrayBase> elements =
Handle<FixedTypedArrayBase>::cast(handle(array->elements()));
Node* buffer = jsgraph()->PointerConstant(elements->external_pointer());
Node* length = jsgraph()->Constant(byte_length);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Convert to a number first.
if (!value_type->Is(Type::Number())) {
Reduction number_reduction = ReduceJSToNumberInput(value);
if (number_reduction.Changed()) {
value = number_reduction.replacement();
} else {
value =
graph()->NewNode(simplified()->PlainPrimitiveToNumber(), value);
}
}
// Check if we can avoid the bounds check.
if (key_type->Min() >= 0 && key_type->Max() < array->length_value()) {
RelaxControls(node);
node->ReplaceInput(0, buffer);
DCHECK_EQ(key, node->InputAt(1));
node->ReplaceInput(2, value);
node->ReplaceInput(3, effect);
node->ReplaceInput(4, control);
node->TrimInputCount(5);
NodeProperties::ChangeOp(
node,
simplified()->StoreElement(
AccessBuilder::ForTypedArrayElement(array->type(), true)));
return Changed(node);
}
// Compute byte offset.
Node* offset =
(k == 0) ? key : graph()->NewNode(
simplified()->NumberShiftLeft(), key,
jsgraph()->Constant(static_cast<double>(k)));
// Turn into a StoreBuffer operation.
RelaxControls(node);
node->ReplaceInput(0, buffer);
node->ReplaceInput(1, offset);
node->ReplaceInput(2, length);
node->ReplaceInput(3, value);
node->ReplaceInput(4, effect);
node->ReplaceInput(5, control);
node->TrimInputCount(6);
NodeProperties::ChangeOp(node, simplified()->StoreBuffer(access));
return Changed(node);
}
}
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSHasInPrototypeChain(Node* node) {
DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
Node* value = NodeProperties::GetValueInput(node, 0);
@ -2264,10 +2133,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSTypeOf(node);
case IrOpcode::kJSLoadNamed:
return ReduceJSLoadNamed(node);
case IrOpcode::kJSLoadProperty:
return ReduceJSLoadProperty(node);
case IrOpcode::kJSStoreProperty:
return ReduceJSStoreProperty(node);
case IrOpcode::kJSLoadContext:
return ReduceJSLoadContext(node);
case IrOpcode::kJSStoreContext:

View File

@ -44,8 +44,6 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceJSAdd(Node* node);
Reduction ReduceJSComparison(Node* node);
Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSLoadProperty(Node* node);
Reduction ReduceJSStoreProperty(Node* node);
Reduction ReduceJSHasInPrototypeChain(Node* node);
Reduction ReduceJSOrdinaryHasInstance(Node* node);
Reduction ReduceJSLoadContext(Node* node);
@ -102,7 +100,6 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
CompilationDependencies* dependencies_;
JSGraph* jsgraph_;
Type* empty_string_type_;
Type* shifted_int32_ranges_[4];
Type* pointer_comparable_type_;
TypeCache const& type_cache_;
};

View File

@ -1081,7 +1081,6 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
state = state->KillElement(object, index, zone());
break;
}
case IrOpcode::kStoreBuffer:
case IrOpcode::kStoreTypedElement: {
// Doesn't affect anything we track with the state currently.
break;

View File

@ -338,11 +338,9 @@
V(ConvertTaggedHoleToUndefined) \
V(Allocate) \
V(LoadField) \
V(LoadBuffer) \
V(LoadElement) \
V(LoadTypedElement) \
V(StoreField) \
V(StoreBuffer) \
V(StoreElement) \
V(StoreTypedElement) \
V(TransitionAndStoreElement) \

View File

@ -2492,53 +2492,6 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kLoadBuffer: {
if (truncation.IsUnused()) return VisitUnused(node);
BufferAccess access = BufferAccessOf(node->op());
ProcessInput(node, 0, UseInfo::PointerInt()); // buffer
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // offset
ProcessInput(node, 2, UseInfo::TruncatingWord32()); // length
ProcessRemainingInputs(node, 3);
MachineRepresentation output;
if (truncation.IdentifiesUndefinedAndNaN()) {
if (truncation.IdentifiesUndefinedAndZero()) {
// If undefined is truncated to a non-NaN number, we can use
// the load's representation.
output = access.machine_type().representation();
} else {
// If undefined is truncated to a number, but the use can
// observe NaN, we need to output at least the float32
// representation.
if (access.machine_type().representation() ==
MachineRepresentation::kFloat32) {
output = access.machine_type().representation();
} else {
output = MachineRepresentation::kFloat64;
}
}
} else {
// If undefined is not truncated away, we need to have the tagged
// representation.
output = MachineRepresentation::kTagged;
}
SetOutput(node, output);
if (lower()) lowering->DoLoadBuffer(node, output, changer_);
return;
}
case IrOpcode::kStoreBuffer: {
BufferAccess access = BufferAccessOf(node->op());
ProcessInput(node, 0, UseInfo::PointerInt()); // buffer
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // offset
ProcessInput(node, 2, UseInfo::TruncatingWord32()); // length
ProcessInput(node, 3,
TruncatingUseInfoFromRepresentation(
access.machine_type().representation())); // value
ProcessRemainingInputs(node, 4);
SetOutput(node, MachineRepresentation::kNone);
if (lower()) lowering->DoStoreBuffer(node);
return;
}
case IrOpcode::kLoadElement: {
if (truncation.IsUnused()) return VisitUnused(node);
ElementAccess access = ElementAccessOf(node->op());
@ -3220,75 +3173,6 @@ void SimplifiedLowering::DoJSToNumberTruncatesToWord32(
selector->DeferReplacement(node, value);
}
void SimplifiedLowering::DoLoadBuffer(Node* node,
MachineRepresentation output_rep,
RepresentationChanger* changer) {
DCHECK_EQ(IrOpcode::kLoadBuffer, node->opcode());
DCHECK_NE(MachineRepresentation::kNone, output_rep);
MachineType const access_type = BufferAccessOf(node->op()).machine_type();
if (output_rep != access_type.representation()) {
Node* const buffer = node->InputAt(0);
Node* const offset = node->InputAt(1);
Node* const length = node->InputAt(2);
Node* const effect = node->InputAt(3);
Node* const control = node->InputAt(4);
Node* const index =
machine()->Is64()
? graph()->NewNode(machine()->ChangeUint32ToUint64(), offset)
: offset;
Node* check = graph()->NewNode(machine()->Uint32LessThan(), offset, length);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = graph()->NewNode(machine()->Load(access_type), buffer, index,
effect, if_true);
Type* element_type =
Type::Intersect(NodeProperties::GetType(node), Type::Number(), zone());
Node* vtrue = changer->GetRepresentationFor(
etrue, access_type.representation(), element_type, node,
UseInfo(output_rep, Truncation::None()));
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
Node* vfalse;
if (output_rep == MachineRepresentation::kTagged) {
vfalse = jsgraph()->UndefinedConstant();
} else if (output_rep == MachineRepresentation::kFloat64) {
vfalse =
jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN());
} else if (output_rep == MachineRepresentation::kFloat32) {
vfalse =
jsgraph()->Float32Constant(std::numeric_limits<float>::quiet_NaN());
} else {
vfalse = jsgraph()->Int32Constant(0);
}
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
// Replace effect uses of {node} with the {ephi}.
NodeProperties::ReplaceUses(node, node, ephi);
// Turn the {node} into a Phi.
node->ReplaceInput(0, vtrue);
node->ReplaceInput(1, vfalse);
node->ReplaceInput(2, merge);
node->TrimInputCount(3);
NodeProperties::ChangeOp(node, common()->Phi(output_rep, 2));
} else {
NodeProperties::ChangeOp(node, machine()->CheckedLoad(access_type));
}
}
void SimplifiedLowering::DoStoreBuffer(Node* node) {
DCHECK_EQ(IrOpcode::kStoreBuffer, node->opcode());
MachineRepresentation const rep =
BufferAccessOf(node->op()).machine_type().representation();
NodeProperties::ChangeOp(node, machine()->CheckedStore(rep));
}
Node* SimplifiedLowering::Float64Round(Node* const node) {
Node* const one = jsgraph()->Float64Constant(1.0);
Node* const one_half = jsgraph()->Float64Constant(0.5);

View File

@ -34,11 +34,6 @@ class SimplifiedLowering final {
RepresentationSelector* selector);
void DoJSToNumberTruncatesToWord32(Node* node,
RepresentationSelector* selector);
// TODO(turbofan): The representation can be removed once the result of the
// representation analysis is stored in the node bounds.
void DoLoadBuffer(Node* node, MachineRepresentation rep,
RepresentationChanger* changer);
void DoStoreBuffer(Node* node);
void DoShift(Node* node, Operator const* op, Type* rhs_type);
void DoStringToNumber(Node* node);
void DoIntegral32ToBit(Node* node);

View File

@ -29,63 +29,6 @@ std::ostream& operator<<(std::ostream& os, BaseTaggedness base_taggedness) {
UNREACHABLE();
}
MachineType BufferAccess::machine_type() const {
switch (external_array_type_) {
case kExternalUint8Array:
case kExternalUint8ClampedArray:
return MachineType::Uint8();
case kExternalInt8Array:
return MachineType::Int8();
case kExternalUint16Array:
return MachineType::Uint16();
case kExternalInt16Array:
return MachineType::Int16();
case kExternalUint32Array:
return MachineType::Uint32();
case kExternalInt32Array:
return MachineType::Int32();
case kExternalFloat32Array:
return MachineType::Float32();
case kExternalFloat64Array:
return MachineType::Float64();
}
UNREACHABLE();
}
bool operator==(BufferAccess lhs, BufferAccess rhs) {
return lhs.external_array_type() == rhs.external_array_type();
}
bool operator!=(BufferAccess lhs, BufferAccess rhs) { return !(lhs == rhs); }
size_t hash_value(BufferAccess access) {
return base::hash<ExternalArrayType>()(access.external_array_type());
}
std::ostream& operator<<(std::ostream& os, BufferAccess access) {
switch (access.external_array_type()) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return os << #Type;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
UNREACHABLE();
}
BufferAccess const BufferAccessOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kLoadBuffer ||
op->opcode() == IrOpcode::kStoreBuffer);
return OpParameter<BufferAccess>(op);
}
bool operator==(FieldAccess const& lhs, FieldAccess const& rhs) {
// On purpose we don't include the write barrier kind here, as this method is
// really only relevant for eliminating loads and they don't care about the
@ -810,28 +753,6 @@ struct SimplifiedOperatorGlobalCache final {
kSpeculativeToNumberNumberOperator;
SpeculativeToNumberOperator<NumberOperationHint::kNumberOrOddball>
kSpeculativeToNumberNumberOrOddballOperator;
#define BUFFER_ACCESS(Type, type, TYPE, ctype, size) \
struct LoadBuffer##Type##Operator final : public Operator1<BufferAccess> { \
LoadBuffer##Type##Operator() \
: Operator1<BufferAccess>( \
IrOpcode::kLoadBuffer, \
Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite, \
"LoadBuffer", 3, 1, 1, 1, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
}; \
struct StoreBuffer##Type##Operator final : public Operator1<BufferAccess> { \
StoreBuffer##Type##Operator() \
: Operator1<BufferAccess>( \
IrOpcode::kStoreBuffer, \
Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow, \
"StoreBuffer", 4, 1, 1, 0, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
}; \
LoadBuffer##Type##Operator kLoadBuffer##Type; \
StoreBuffer##Type##Operator kStoreBuffer##Type;
TYPED_ARRAYS(BUFFER_ACCESS)
#undef BUFFER_ACCESS
};
static base::LazyInstance<SimplifiedOperatorGlobalCache>::type
@ -1033,30 +954,6 @@ const Operator* SimplifiedOperatorBuilder::Allocate(Type* type,
1, 1, 1, 1, 1, 0, AllocateParameters(type, pretenure));
}
const Operator* SimplifiedOperatorBuilder::LoadBuffer(BufferAccess access) {
switch (access.external_array_type()) {
#define LOAD_BUFFER(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return &cache_.kLoadBuffer##Type;
TYPED_ARRAYS(LOAD_BUFFER)
#undef LOAD_BUFFER
}
UNREACHABLE();
}
const Operator* SimplifiedOperatorBuilder::StoreBuffer(BufferAccess access) {
switch (access.external_array_type()) {
#define STORE_BUFFER(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return &cache_.kStoreBuffer##Type;
TYPED_ARRAYS(STORE_BUFFER)
#undef STORE_BUFFER
}
UNREACHABLE();
}
const Operator* SimplifiedOperatorBuilder::StringFromCodePoint(
UnicodeEncoding encoding) {
switch (encoding) {

View File

@ -34,30 +34,6 @@ size_t hash_value(BaseTaggedness);
std::ostream& operator<<(std::ostream&, BaseTaggedness);
// An access descriptor for loads/stores of array buffers.
class BufferAccess final {
public:
explicit BufferAccess(ExternalArrayType external_array_type)
: external_array_type_(external_array_type) {}
ExternalArrayType external_array_type() const { return external_array_type_; }
MachineType machine_type() const;
private:
ExternalArrayType const external_array_type_;
};
V8_EXPORT_PRIVATE bool operator==(BufferAccess, BufferAccess);
bool operator!=(BufferAccess, BufferAccess);
size_t hash_value(BufferAccess);
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&, BufferAccess);
V8_EXPORT_PRIVATE BufferAccess const BufferAccessOf(const Operator* op)
WARN_UNUSED_RESULT;
// An access descriptor for loads/stores of fixed structures like field
// accesses of heap objects. Accesses from either tagged or untagged base
// pointers are supported; untagging is done automatically during lowering.
@ -486,12 +462,6 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* LoadField(FieldAccess const&);
const Operator* StoreField(FieldAccess const&);
// load-buffer buffer, offset, length
const Operator* LoadBuffer(BufferAccess);
// store-buffer buffer, offset, length, value
const Operator* StoreBuffer(BufferAccess);
// load-element [base + index]
const Operator* LoadElement(ElementAccess const&);

View File

@ -1911,18 +1911,6 @@ Type* Typer::Visitor::TypeLoadField(Node* node) {
return FieldAccessOf(node->op()).type;
}
Type* Typer::Visitor::TypeLoadBuffer(Node* node) {
switch (BufferAccessOf(node->op()).external_array_type()) {
#define TYPED_ARRAY_CASE(ElemType, type, TYPE, ctype, size) \
case kExternal##ElemType##Array: \
return Type::Union(typer_->cache_.k##ElemType, Type::Undefined(), zone());
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
UNREACHABLE();
}
Type* Typer::Visitor::TypeLoadElement(Node* node) {
return ElementAccessOf(node->op()).type;
}
@ -1942,12 +1930,6 @@ Type* Typer::Visitor::TypeStoreField(Node* node) {
UNREACHABLE();
}
Type* Typer::Visitor::TypeStoreBuffer(Node* node) {
UNREACHABLE();
}
Type* Typer::Visitor::TypeStoreElement(Node* node) {
UNREACHABLE();
}

View File

@ -1252,8 +1252,6 @@ void Verifier::Visitor::Check(Node* node) {
// CheckValueInputIs(node, 0, Type::Object());
// CheckTypeIs(node, FieldAccessOf(node->op()).type));
break;
case IrOpcode::kLoadBuffer:
break;
case IrOpcode::kLoadElement:
// Object -> elementtype
// TODO(rossberg): activate once machine ops are typed.
@ -1269,8 +1267,6 @@ void Verifier::Visitor::Check(Node* node) {
// CheckValueInputIs(node, 1, FieldAccessOf(node->op()).type));
CheckNotTyped(node);
break;
case IrOpcode::kStoreBuffer:
break;
case IrOpcode::kStoreElement:
// (Object, elementtype) -> _|_
// TODO(rossberg): activate once machine ops are typed.

View File

@ -25403,46 +25403,6 @@ TEST(StringConcatOverflow) {
}
TEST(TurboAsmDisablesNeuter) {
i::FLAG_opt = true;
i::FLAG_allow_natives_syntax = true;
v8::V8::Initialize();
v8::HandleScope scope(CcTest::isolate());
LocalContext context;
const char* load =
"function Module(stdlib, foreign, heap) {"
" 'use asm';"
" var MEM32 = new stdlib.Int32Array(heap);"
" function load() { return MEM32[0]; }"
" return { load: load };"
"}"
"var buffer = new ArrayBuffer(4);"
"var module = Module(this, {}, buffer);"
"%OptimizeFunctionOnNextCall(module.load);"
"module.load();"
"buffer";
v8::Local<v8::ArrayBuffer> result = CompileRun(load).As<v8::ArrayBuffer>();
CHECK(!result->IsNeuterable());
const char* store =
"function Module(stdlib, foreign, heap) {"
" 'use asm';"
" var MEM32 = new stdlib.Int32Array(heap);"
" function store() { MEM32[0] = 0; }"
" return { store: store };"
"}"
"var buffer = new ArrayBuffer(4);"
"var module = Module(this, {}, buffer);"
"%OptimizeFunctionOnNextCall(module.store);"
"module.store();"
"buffer";
result = CompileRun(store).As<v8::ArrayBuffer>();
CHECK(!result->IsNeuterable());
}
TEST(GetPrototypeAccessControl) {
i::FLAG_allow_natives_syntax = true;
v8::Isolate* isolate = CcTest::isolate();

View File

@ -28,19 +28,11 @@ namespace compiler {
namespace {
const ExternalArrayType kExternalArrayTypes[] = {
kExternalUint8Array, kExternalInt8Array, kExternalUint16Array,
kExternalInt16Array, kExternalUint32Array, kExternalInt32Array,
kExternalFloat32Array, kExternalFloat64Array};
const size_t kIndices[] = {0, 1, 42, 100, 1024};
Type* const kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
Type::Number(), Type::String(), Type::Object()};
STATIC_ASSERT(LANGUAGE_END == 2);
const LanguageMode kLanguageModes[] = {SLOPPY, STRICT};
} // namespace
@ -449,212 +441,6 @@ TEST_F(JSTypedLoweringTest, JSStoreContext) {
}
// -----------------------------------------------------------------------------
// JSLoadProperty
TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
VectorSlotPair feedback;
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
int const element_size = static_cast<int>(array->element_size());
Node* key = Parameter(
Type::Range(kMinInt / element_size, kMaxInt / element_size, zone()));
Node* base = HeapConstant(array);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
Reduction r =
Reduce(graph()->NewNode(javascript()->LoadProperty(feedback), base, key,
context, EmptyFrameState(), effect, control));
Matcher<Node*> offset_matcher =
element_size == 1
? key
: IsNumberShiftLeft(key,
IsNumberConstant(WhichPowerOf2(element_size)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsLoadBuffer(BufferAccess(type),
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher,
IsNumberConstant(array->byte_length()->Number()), effect,
control));
}
}
TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArrayWithSafeKey) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
VectorSlotPair feedback;
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
ElementAccess access = AccessBuilder::ForTypedArrayElement(type, true);
int min = random_number_generator()->NextInt(static_cast<int>(kLength));
int max = random_number_generator()->NextInt(static_cast<int>(kLength));
if (min > max) std::swap(min, max);
Node* key = Parameter(Type::Range(min, max, zone()));
Node* base = HeapConstant(array);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
Reduction r =
Reduce(graph()->NewNode(javascript()->LoadProperty(feedback), base, key,
context, EmptyFrameState(), effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsLoadElement(access,
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
key, effect, control));
}
}
// -----------------------------------------------------------------------------
// JSStoreProperty
TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
int const element_size = static_cast<int>(array->element_size());
Node* key = Parameter(
Type::Range(kMinInt / element_size, kMaxInt / element_size, zone()));
Node* base = HeapConstant(array);
Node* value =
Parameter(AccessBuilder::ForTypedArrayElement(type, true).type);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
VectorSlotPair feedback;
const Operator* op = javascript()->StoreProperty(language_mode, feedback);
Node* node = graph()->NewNode(op, base, key, value, context,
EmptyFrameState(), effect, control);
Reduction r = Reduce(node);
Matcher<Node*> offset_matcher =
element_size == 1
? key
: IsNumberShiftLeft(
key, IsNumberConstant(WhichPowerOf2(element_size)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreBuffer(
BufferAccess(type),
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher, IsNumberConstant(array->byte_length()->Number()),
value, effect, control));
}
}
}
TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithConversion) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
int const element_size = static_cast<int>(array->element_size());
Node* key = Parameter(
Type::Range(kMinInt / element_size, kMaxInt / element_size, zone()));
Node* base = HeapConstant(array);
Node* value = Parameter(Type::PlainPrimitive());
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
VectorSlotPair feedback;
const Operator* op = javascript()->StoreProperty(language_mode, feedback);
Node* node = graph()->NewNode(op, base, key, value, context,
EmptyFrameState(), effect, control);
Reduction r = Reduce(node);
Matcher<Node*> offset_matcher =
element_size == 1
? key
: IsNumberShiftLeft(
key, IsNumberConstant(WhichPowerOf2(element_size)));
Matcher<Node*> value_matcher = IsPlainPrimitiveToNumber(value);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreBuffer(
BufferAccess(type),
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher, IsNumberConstant(array->byte_length()->Number()),
value_matcher, effect, control));
}
}
}
TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithSafeKey) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
ElementAccess access = AccessBuilder::ForTypedArrayElement(type, true);
int min = random_number_generator()->NextInt(static_cast<int>(kLength));
int max = random_number_generator()->NextInt(static_cast<int>(kLength));
if (min > max) std::swap(min, max);
Node* key = Parameter(Type::Range(min, max, zone()));
Node* base = HeapConstant(array);
Node* value = Parameter(access.type);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
VectorSlotPair feedback;
const Operator* op = javascript()->StoreProperty(language_mode, feedback);
Node* node = graph()->NewNode(op, base, key, value, context,
EmptyFrameState(), effect, control);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreElement(
access, IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
key, value, effect, control));
}
}
}
// -----------------------------------------------------------------------------
// JSLoadNamed

View File

@ -966,132 +966,6 @@ class IsStoreFieldMatcher final : public NodeMatcher {
const Matcher<Node*> control_matcher_;
};
class IsLoadBufferMatcher final : public NodeMatcher {
public:
IsLoadBufferMatcher(const Matcher<BufferAccess>& access_matcher,
const Matcher<Node*>& buffer_matcher,
const Matcher<Node*>& offset_matcher,
const Matcher<Node*>& length_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kLoadBuffer),
access_matcher_(access_matcher),
buffer_matcher_(buffer_matcher),
offset_matcher_(offset_matcher),
length_matcher_(length_matcher),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
*os << " whose access (";
access_matcher_.DescribeTo(os);
*os << "), buffer (";
buffer_matcher_.DescribeTo(os);
*os << "), offset (";
offset_matcher_.DescribeTo(os);
*os << "), length (";
length_matcher_.DescribeTo(os);
*os << "), effect (";
effect_matcher_.DescribeTo(os);
*os << ") and control (";
control_matcher_.DescribeTo(os);
*os << ")";
}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(BufferAccessOf(node->op()), "access",
access_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
"buffer", buffer_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
"offset", offset_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2),
"length", length_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
}
private:
const Matcher<BufferAccess> access_matcher_;
const Matcher<Node*> buffer_matcher_;
const Matcher<Node*> offset_matcher_;
const Matcher<Node*> length_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
class IsStoreBufferMatcher final : public NodeMatcher {
public:
IsStoreBufferMatcher(const Matcher<BufferAccess>& access_matcher,
const Matcher<Node*>& buffer_matcher,
const Matcher<Node*>& offset_matcher,
const Matcher<Node*>& length_matcher,
const Matcher<Node*>& value_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kStoreBuffer),
access_matcher_(access_matcher),
buffer_matcher_(buffer_matcher),
offset_matcher_(offset_matcher),
length_matcher_(length_matcher),
value_matcher_(value_matcher),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
*os << " whose access (";
access_matcher_.DescribeTo(os);
*os << "), buffer (";
buffer_matcher_.DescribeTo(os);
*os << "), offset (";
offset_matcher_.DescribeTo(os);
*os << "), length (";
length_matcher_.DescribeTo(os);
*os << "), value (";
value_matcher_.DescribeTo(os);
*os << "), effect (";
effect_matcher_.DescribeTo(os);
*os << ") and control (";
control_matcher_.DescribeTo(os);
*os << ")";
}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(BufferAccessOf(node->op()), "access",
access_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
"buffer", buffer_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
"offset", offset_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2),
"length", length_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 3),
"value", value_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
}
private:
const Matcher<BufferAccess> access_matcher_;
const Matcher<Node*> buffer_matcher_;
const Matcher<Node*> offset_matcher_;
const Matcher<Node*> length_matcher_;
const Matcher<Node*> value_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
class IsLoadElementMatcher final : public NodeMatcher {
public:
IsLoadElementMatcher(const Matcher<ElementAccess>& access_matcher,
@ -2088,32 +1962,6 @@ Matcher<Node*> IsStoreField(const Matcher<FieldAccess>& access_matcher,
control_matcher));
}
Matcher<Node*> IsLoadBuffer(const Matcher<BufferAccess>& access_matcher,
const Matcher<Node*>& buffer_matcher,
const Matcher<Node*>& offset_matcher,
const Matcher<Node*>& length_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsLoadBufferMatcher(access_matcher, buffer_matcher,
offset_matcher, length_matcher,
effect_matcher, control_matcher));
}
Matcher<Node*> IsStoreBuffer(const Matcher<BufferAccess>& access_matcher,
const Matcher<Node*>& buffer_matcher,
const Matcher<Node*>& offset_matcher,
const Matcher<Node*>& length_matcher,
const Matcher<Node*>& value_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsStoreBufferMatcher(
access_matcher, buffer_matcher, offset_matcher, length_matcher,
value_matcher, effect_matcher, control_matcher));
}
Matcher<Node*> IsLoadElement(const Matcher<ElementAccess>& access_matcher,
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& index_matcher,

View File

@ -122,81 +122,6 @@ INSTANTIATE_TEST_CASE_P(SimplifiedOperatorTest, SimplifiedPureOperatorTest,
::testing::ValuesIn(kPureOperators));
// -----------------------------------------------------------------------------
// Buffer access operators.
namespace {
const ExternalArrayType kExternalArrayTypes[] = {
kExternalUint8Array, kExternalInt8Array, kExternalUint16Array,
kExternalInt16Array, kExternalUint32Array, kExternalInt32Array,
kExternalFloat32Array, kExternalFloat64Array};
} // namespace
class SimplifiedBufferAccessOperatorTest
: public TestWithZone,
public ::testing::WithParamInterface<ExternalArrayType> {};
TEST_P(SimplifiedBufferAccessOperatorTest, InstancesAreGloballyShared) {
BufferAccess const access(GetParam());
SimplifiedOperatorBuilder simplified1(zone());
SimplifiedOperatorBuilder simplified2(zone());
EXPECT_EQ(simplified1.LoadBuffer(access), simplified2.LoadBuffer(access));
EXPECT_EQ(simplified1.StoreBuffer(access), simplified2.StoreBuffer(access));
}
TEST_P(SimplifiedBufferAccessOperatorTest, LoadBuffer) {
SimplifiedOperatorBuilder simplified(zone());
BufferAccess const access(GetParam());
const Operator* op = simplified.LoadBuffer(access);
EXPECT_EQ(IrOpcode::kLoadBuffer, op->opcode());
EXPECT_EQ(Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite,
op->properties());
EXPECT_EQ(access, BufferAccessOf(op));
EXPECT_EQ(3, op->ValueInputCount());
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, op->ControlInputCount());
EXPECT_EQ(5, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(1, op->ValueOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ControlOutputCount());
}
TEST_P(SimplifiedBufferAccessOperatorTest, StoreBuffer) {
SimplifiedOperatorBuilder simplified(zone());
BufferAccess const access(GetParam());
const Operator* op = simplified.StoreBuffer(access);
EXPECT_EQ(IrOpcode::kStoreBuffer, op->opcode());
EXPECT_EQ(Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow,
op->properties());
EXPECT_EQ(access, BufferAccessOf(op));
EXPECT_EQ(4, op->ValueInputCount());
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, op->ControlInputCount());
EXPECT_EQ(6, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, op->ValueOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ControlOutputCount());
}
INSTANTIATE_TEST_CASE_P(SimplifiedOperatorTest,
SimplifiedBufferAccessOperatorTest,
::testing::ValuesIn(kExternalArrayTypes));
// -----------------------------------------------------------------------------
// Element access operators.