Commit 82b315ce authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for accessing Uint8ClampedArrays.

This adds a new NumberToUint8Clamped simplified operator that does the
round ties to even + clamping necessary to store to Uint8ClampedArrays.

BUG=v8:4470,v8:5267,v8:5615
R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2489563004
Cr-Commit-Position: refs/heads/master@{#40861}
parent ed35983a
...@@ -25,8 +25,6 @@ bool CanInlineElementAccess(Handle<Map> map) { ...@@ -25,8 +25,6 @@ bool CanInlineElementAccess(Handle<Map> map) {
if (map->has_indexed_interceptor()) return false; if (map->has_indexed_interceptor()) return false;
ElementsKind const elements_kind = map->elements_kind(); ElementsKind const elements_kind = map->elements_kind();
if (IsFastElementsKind(elements_kind)) return true; if (IsFastElementsKind(elements_kind)) return true;
// TODO(bmeurer): Add support for other elements kind.
if (elements_kind == UINT8_CLAMPED_ELEMENTS) return false;
if (IsFixedTypedArrayElementsKind(elements_kind)) return true; if (IsFixedTypedArrayElementsKind(elements_kind)) return true;
return false; return false;
} }
......
This diff is collapsed.
...@@ -182,6 +182,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -182,6 +182,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* control); Node* control);
ValueEffectControl LowerFloat64RoundDown(Node* node, Node* effect, ValueEffectControl LowerFloat64RoundDown(Node* node, Node* effect,
Node* control); Node* control);
ValueEffectControl LowerFloat64RoundTiesEven(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerFloat64RoundTruncate(Node* node, Node* effect, ValueEffectControl LowerFloat64RoundTruncate(Node* node, Node* effect,
Node* control); Node* control);
...@@ -193,6 +195,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -193,6 +195,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
ValueEffectControl BuildCheckedHeapNumberOrOddballToFloat64( ValueEffectControl BuildCheckedHeapNumberOrOddballToFloat64(
CheckTaggedInputMode mode, Node* value, Node* frame_state, Node* effect, CheckTaggedInputMode mode, Node* value, Node* frame_state, Node* effect,
Node* control); Node* control);
ValueEffectControl BuildFloat64RoundDown(Node* value, Node* effect,
Node* control);
ValueEffectControl LowerStringComparison(Callable const& callable, Node* node, ValueEffectControl LowerStringComparison(Callable const& callable, Node* node,
Node* effect, Node* control); Node* effect, Node* control);
......
...@@ -1200,6 +1200,14 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -1200,6 +1200,14 @@ JSNativeContextSpecialization::BuildElementAccess(
value = effect = graph()->NewNode(simplified()->CheckNumber(), value, value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
effect, control); effect, control);
// Introduce the appropriate truncation for {value}. Currently we
// only need to do this for ClamedUint8Array {receiver}s, as the
// other truncations are implicit in the StoreTypedElement, but we
// might want to change that at some point.
if (external_array_type == kExternalUint8ClampedArray) {
value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value);
}
// Check if we can skip the out-of-bounds store. // Check if we can skip the out-of-bounds store.
if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
Node* check = Node* check =
......
...@@ -281,6 +281,7 @@ ...@@ -281,6 +281,7 @@
V(NumberToBoolean) \ V(NumberToBoolean) \
V(NumberToInt32) \ V(NumberToInt32) \
V(NumberToUint32) \ V(NumberToUint32) \
V(NumberToUint8Clamped) \
V(NumberSilenceNaN) V(NumberSilenceNaN)
#define SIMPLIFIED_OTHER_OP_LIST(V) \ #define SIMPLIFIED_OTHER_OP_LIST(V) \
......
...@@ -490,6 +490,13 @@ Type* OperationTyper::NumberToUint32(Type* type) { ...@@ -490,6 +490,13 @@ Type* OperationTyper::NumberToUint32(Type* type) {
return Type::Unsigned32(); return Type::Unsigned32();
} }
Type* OperationTyper::NumberToUint8Clamped(Type* type) {
DCHECK(type->Is(Type::Number()));
if (type->Is(cache_.kUint8)) return type;
return cache_.kUint8;
}
Type* OperationTyper::NumberSilenceNaN(Type* type) { Type* OperationTyper::NumberSilenceNaN(Type* type) {
DCHECK(type->Is(Type::Number())); DCHECK(type->Is(Type::Number()));
// TODO(jarin): This is a terrible hack; we definitely need a dedicated type // TODO(jarin): This is a terrible hack; we definitely need a dedicated type
......
...@@ -2111,6 +2111,31 @@ class RepresentationSelector { ...@@ -2111,6 +2111,31 @@ class RepresentationSelector {
if (lower()) DeferReplacement(node, node->InputAt(0)); if (lower()) DeferReplacement(node, node->InputAt(0));
return; return;
} }
case IrOpcode::kNumberToUint8Clamped: {
Type* const input_type = TypeOf(node->InputAt(0));
if (input_type->Is(type_cache_.kUint8OrMinusZeroOrNaN)) {
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else if (input_type->Is(Type::Unsigned32OrMinusZeroOrNaN())) {
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) lowering->DoUnsigned32ToUint8Clamped(node);
} else if (input_type->Is(Type::Signed32OrMinusZeroOrNaN())) {
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) lowering->DoSigned32ToUint8Clamped(node);
} else if (input_type->Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) lowering->DoIntegerToUint8Clamped(node);
} else {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) lowering->DoNumberToUint8Clamped(node);
}
return;
}
case IrOpcode::kReferenceEqual: { case IrOpcode::kReferenceEqual: {
VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
if (lower()) { if (lower()) {
...@@ -3294,6 +3319,71 @@ void SimplifiedLowering::DoNumberToBit(Node* node) { ...@@ -3294,6 +3319,71 @@ void SimplifiedLowering::DoNumberToBit(Node* node) {
NodeProperties::ChangeOp(node, machine()->Float64LessThan()); NodeProperties::ChangeOp(node, machine()->Float64LessThan());
} }
void SimplifiedLowering::DoIntegerToUint8Clamped(Node* node) {
Node* const input = node->InputAt(0);
Node* const min = jsgraph()->Float64Constant(0.0);
Node* const max = jsgraph()->Float64Constant(255.0);
node->ReplaceInput(
0, graph()->NewNode(machine()->Float64LessThan(), min, input));
node->AppendInput(
graph()->zone(),
graph()->NewNode(
common()->Select(MachineRepresentation::kFloat64),
graph()->NewNode(machine()->Float64LessThan(), input, max), input,
max));
node->AppendInput(graph()->zone(), min);
NodeProperties::ChangeOp(node,
common()->Select(MachineRepresentation::kFloat64));
}
void SimplifiedLowering::DoNumberToUint8Clamped(Node* node) {
Node* const input = node->InputAt(0);
Node* const min = jsgraph()->Float64Constant(0.0);
Node* const max = jsgraph()->Float64Constant(255.0);
node->ReplaceInput(
0, graph()->NewNode(
common()->Select(MachineRepresentation::kFloat64),
graph()->NewNode(machine()->Float64LessThan(), min, input),
graph()->NewNode(
common()->Select(MachineRepresentation::kFloat64),
graph()->NewNode(machine()->Float64LessThan(), input, max),
input, max),
min));
NodeProperties::ChangeOp(node,
machine()->Float64RoundTiesEven().placeholder());
}
void SimplifiedLowering::DoSigned32ToUint8Clamped(Node* node) {
Node* const input = node->InputAt(0);
Node* const min = jsgraph()->Int32Constant(0);
Node* const max = jsgraph()->Int32Constant(255);
node->ReplaceInput(
0, graph()->NewNode(machine()->Int32LessThanOrEqual(), input, max));
node->AppendInput(
graph()->zone(),
graph()->NewNode(common()->Select(MachineRepresentation::kWord32),
graph()->NewNode(machine()->Int32LessThan(), input, min),
min, input));
node->AppendInput(graph()->zone(), max);
NodeProperties::ChangeOp(node,
common()->Select(MachineRepresentation::kWord32));
}
void SimplifiedLowering::DoUnsigned32ToUint8Clamped(Node* node) {
Node* const input = node->InputAt(0);
Node* const max = jsgraph()->Uint32Constant(255u);
node->ReplaceInput(
0, graph()->NewNode(machine()->Uint32LessThanOrEqual(), input, max));
node->AppendInput(graph()->zone(), input);
node->AppendInput(graph()->zone(), max);
NodeProperties::ChangeOp(node,
common()->Select(MachineRepresentation::kWord32));
}
Node* SimplifiedLowering::ToNumberCode() { Node* SimplifiedLowering::ToNumberCode() {
if (!to_number_code_.is_set()) { if (!to_number_code_.is_set()) {
Callable callable = CodeFactory::ToNumber(isolate()); Callable callable = CodeFactory::ToNumber(isolate());
......
...@@ -44,6 +44,10 @@ class SimplifiedLowering final { ...@@ -44,6 +44,10 @@ class SimplifiedLowering final {
void DoIntegral32ToBit(Node* node); void DoIntegral32ToBit(Node* node);
void DoOrderedNumberToBit(Node* node); void DoOrderedNumberToBit(Node* node);
void DoNumberToBit(Node* node); void DoNumberToBit(Node* node);
void DoIntegerToUint8Clamped(Node* node);
void DoNumberToUint8Clamped(Node* node);
void DoSigned32ToUint8Clamped(Node* node);
void DoUnsigned32ToUint8Clamped(Node* node);
private: private:
JSGraph* const jsgraph_; JSGraph* const jsgraph_;
......
...@@ -393,6 +393,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) { ...@@ -393,6 +393,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(NumberToBoolean, Operator::kNoProperties, 1, 0) \ V(NumberToBoolean, Operator::kNoProperties, 1, 0) \
V(NumberToInt32, Operator::kNoProperties, 1, 0) \ V(NumberToInt32, Operator::kNoProperties, 1, 0) \
V(NumberToUint32, Operator::kNoProperties, 1, 0) \ V(NumberToUint32, Operator::kNoProperties, 1, 0) \
V(NumberToUint8Clamped, Operator::kNoProperties, 1, 0) \
V(NumberSilenceNaN, Operator::kNoProperties, 1, 0) \ V(NumberSilenceNaN, Operator::kNoProperties, 1, 0) \
V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \ V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \
V(StringFromCharCode, Operator::kNoProperties, 1, 0) \ V(StringFromCharCode, Operator::kNoProperties, 1, 0) \
......
...@@ -269,6 +269,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -269,6 +269,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* NumberToBoolean(); const Operator* NumberToBoolean();
const Operator* NumberToInt32(); const Operator* NumberToInt32();
const Operator* NumberToUint32(); const Operator* NumberToUint32();
const Operator* NumberToUint8Clamped();
const Operator* NumberSilenceNaN(); const Operator* NumberSilenceNaN();
......
...@@ -26,6 +26,8 @@ class TypeCache final { ...@@ -26,6 +26,8 @@ class TypeCache final {
Type* const kInt8 = CreateRange<int8_t>(); Type* const kInt8 = CreateRange<int8_t>();
Type* const kUint8 = CreateRange<uint8_t>(); Type* const kUint8 = CreateRange<uint8_t>();
Type* const kUint8Clamped = kUint8; Type* const kUint8Clamped = kUint8;
Type* const kUint8OrMinusZeroOrNaN =
Type::Union(kUint8, Type::MinusZeroOrNaN(), zone());
Type* const kInt16 = CreateRange<int16_t>(); Type* const kInt16 = CreateRange<int16_t>();
Type* const kUint16 = CreateRange<uint16_t>(); Type* const kUint16 = CreateRange<uint16_t>();
Type* const kInt32 = Type::Signed32(); Type* const kInt32 = Type::Signed32();
......
...@@ -87,6 +87,8 @@ Reduction TypedOptimization::Reduce(Node* node) { ...@@ -87,6 +87,8 @@ Reduction TypedOptimization::Reduce(Node* node) {
case IrOpcode::kNumberRound: case IrOpcode::kNumberRound:
case IrOpcode::kNumberTrunc: case IrOpcode::kNumberTrunc:
return ReduceNumberRoundop(node); return ReduceNumberRoundop(node);
case IrOpcode::kNumberToUint8Clamped:
return ReduceNumberToUint8Clamped(node);
case IrOpcode::kPhi: case IrOpcode::kPhi:
return ReducePhi(node); return ReducePhi(node);
case IrOpcode::kSelect: case IrOpcode::kSelect:
...@@ -192,6 +194,15 @@ Reduction TypedOptimization::ReduceNumberRoundop(Node* node) { ...@@ -192,6 +194,15 @@ Reduction TypedOptimization::ReduceNumberRoundop(Node* node) {
return NoChange(); return NoChange();
} }
Reduction TypedOptimization::ReduceNumberToUint8Clamped(Node* node) {
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetType(input);
if (input_type->Is(type_cache_.kUint8)) {
return Replace(input);
}
return NoChange();
}
Reduction TypedOptimization::ReducePhi(Node* node) { Reduction TypedOptimization::ReducePhi(Node* node) {
// Try to narrow the type of the Phi {node}, which might be more precise now // Try to narrow the type of the Phi {node}, which might be more precise now
// after lowering based on types, i.e. a SpeculativeNumberAdd has a more // after lowering based on types, i.e. a SpeculativeNumberAdd has a more
......
...@@ -47,6 +47,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final ...@@ -47,6 +47,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
Reduction ReduceCheckString(Node* node); Reduction ReduceCheckString(Node* node);
Reduction ReduceLoadField(Node* node); Reduction ReduceLoadField(Node* node);
Reduction ReduceNumberRoundop(Node* node); Reduction ReduceNumberRoundop(Node* node);
Reduction ReduceNumberToUint8Clamped(Node* node);
Reduction ReducePhi(Node* node); Reduction ReducePhi(Node* node);
Reduction ReduceSelect(Node* node); Reduction ReduceSelect(Node* node);
......
...@@ -826,6 +826,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -826,6 +826,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Signed32()); CheckTypeIs(node, Type::Signed32());
break; break;
case IrOpcode::kNumberToUint32: case IrOpcode::kNumberToUint32:
case IrOpcode::kNumberToUint8Clamped:
// Number -> Unsigned32 // Number -> Unsigned32
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Unsigned32()); CheckTypeIs(node, Type::Unsigned32());
......
// Copyright 2016 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.
// Flags: --allow-natives-syntax
(function() {
function foo(a, v) {
a[0] = v & 0xff;
}
var a = new Uint8ClampedArray(4);
foo(a, 1);
foo(a, 2);
%OptimizeFunctionOnNextCall(foo);
foo(a, 256);
assertOptimized(foo);
assertEquals(0, a[0]);
})();
(function() {
function foo(a, v) {
a[0] = v >>> 0;
}
var a = new Uint8ClampedArray(4);
foo(a, 1);
foo(a, 2);
%OptimizeFunctionOnNextCall(foo);
foo(a, 256);
assertOptimized(foo);
assertEquals(255, a[0]);
})();
(function() {
function foo(a, v) {
a[0] = v | 0;
}
var a = new Uint8ClampedArray(4);
foo(a, 1);
foo(a, 2);
%OptimizeFunctionOnNextCall(foo);
foo(a, 256);
assertOptimized(foo);
assertEquals(255, a[0]);
foo(a, -1);
assertOptimized(foo);
assertEquals(0, a[0]);
})();
(function() {
function foo(a, v) {
a[0] = v;
}
var a = new Uint8ClampedArray(4);
foo(a, 1);
foo(a, 2);
%OptimizeFunctionOnNextCall(foo);
foo(a, Infinity);
assertOptimized(foo);
assertEquals(255, a[0]);
foo(a, -Infinity);
assertOptimized(foo);
assertEquals(0, a[0]);
foo(a, 0.5);
assertOptimized(foo);
assertEquals(0, a[0]);
foo(a, 1.5);
assertOptimized(foo);
assertEquals(2, a[0]);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment