From 82b315ce75c0404f287a3214824ff262d7a44142 Mon Sep 17 00:00:00 2001 From: bmeurer Date: Wed, 9 Nov 2016 05:33:50 -0800 Subject: [PATCH] [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} --- src/compiler/access-info.cc | 2 - src/compiler/effect-control-linearizer.cc | 264 ++++++++++++------ src/compiler/effect-control-linearizer.h | 4 + .../js-native-context-specialization.cc | 8 + src/compiler/opcodes.h | 1 + src/compiler/operation-typer.cc | 7 + src/compiler/simplified-lowering.cc | 90 ++++++ src/compiler/simplified-lowering.h | 4 + src/compiler/simplified-operator.cc | 1 + src/compiler/simplified-operator.h | 1 + src/compiler/type-cache.h | 2 + src/compiler/typed-optimization.cc | 11 + src/compiler/typed-optimization.h | 1 + src/compiler/verifier.cc | 1 + test/mjsunit/compiler/uint8-clamped-array.js | 73 +++++ 15 files changed, 388 insertions(+), 82 deletions(-) create mode 100644 test/mjsunit/compiler/uint8-clamped-array.js diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc index 50ccaaa571..635641f9f6 100644 --- a/src/compiler/access-info.cc +++ b/src/compiler/access-info.cc @@ -25,8 +25,6 @@ bool CanInlineElementAccess(Handle map) { if (map->has_indexed_interceptor()) return false; ElementsKind const elements_kind = map->elements_kind(); 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; return false; } diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc index 351382ebf4..d17195c984 100644 --- a/src/compiler/effect-control-linearizer.cc +++ b/src/compiler/effect-control-linearizer.cc @@ -780,6 +780,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, case IrOpcode::kFloat64RoundTruncate: state = LowerFloat64RoundTruncate(node, *effect, *control); break; + case IrOpcode::kFloat64RoundTiesEven: + state = LowerFloat64RoundTiesEven(node, *effect, *control); + break; default: return false; } @@ -3374,6 +3377,137 @@ EffectControlLinearizer::LowerFloat64RoundUp(Node* node, Node* effect, return ValueEffectControl(value, effect, merge0); } +EffectControlLinearizer::ValueEffectControl +EffectControlLinearizer::BuildFloat64RoundDown(Node* value, Node* effect, + Node* control) { + if (machine()->Float64RoundDown().IsSupported()) { + value = graph()->NewNode(machine()->Float64RoundDown().op(), value); + } else { + Node* const one = jsgraph()->Float64Constant(1.0); + Node* const zero = jsgraph()->Float64Constant(0.0); + Node* const minus_one = jsgraph()->Float64Constant(-1.0); + Node* const minus_zero = jsgraph()->Float64Constant(-0.0); + Node* const two_52 = jsgraph()->Float64Constant(4503599627370496.0E0); + Node* const minus_two_52 = + jsgraph()->Float64Constant(-4503599627370496.0E0); + Node* const input = value; + + // General case for floor. + // + // if 0.0 < input then + // if 2^52 <= input then + // input + // else + // let temp1 = (2^52 + input) - 2^52 in + // if input < temp1 then + // temp1 - 1 + // else + // temp1 + // else + // if input == 0 then + // input + // else + // if input <= -2^52 then + // input + // else + // let temp1 = -0 - input in + // let temp2 = (2^52 + temp1) - 2^52 in + // if temp2 < temp1 then + // -1 - temp2 + // else + // -0 - temp2 + // + // Note: We do not use the Diamond helper class here, because it really + // hurts + // readability with nested diamonds. + + Node* check0 = graph()->NewNode(machine()->Float64LessThan(), zero, input); + Node* branch0 = + graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); + + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); + Node* vtrue0; + { + Node* check1 = + graph()->NewNode(machine()->Float64LessThanOrEqual(), two_52, input); + Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0); + + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); + Node* vtrue1 = input; + + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); + Node* vfalse1; + { + Node* temp1 = graph()->NewNode( + machine()->Float64Sub(), + graph()->NewNode(machine()->Float64Add(), two_52, input), two_52); + vfalse1 = graph()->NewNode( + common()->Select(MachineRepresentation::kFloat64), + graph()->NewNode(machine()->Float64LessThan(), input, temp1), + graph()->NewNode(machine()->Float64Sub(), temp1, one), temp1); + } + + if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); + vtrue0 = + graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), + vtrue1, vfalse1, if_true0); + } + + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); + Node* vfalse0; + { + Node* check1 = graph()->NewNode(machine()->Float64Equal(), input, zero); + Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), + check1, if_false0); + + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); + Node* vtrue1 = input; + + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); + Node* vfalse1; + { + Node* check2 = graph()->NewNode(machine()->Float64LessThanOrEqual(), + input, minus_two_52); + Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse), + check2, if_false1); + + Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); + Node* vtrue2 = input; + + Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); + Node* vfalse2; + { + Node* temp1 = + graph()->NewNode(machine()->Float64Sub(), minus_zero, input); + Node* temp2 = graph()->NewNode( + machine()->Float64Sub(), + graph()->NewNode(machine()->Float64Add(), two_52, temp1), two_52); + vfalse2 = graph()->NewNode( + common()->Select(MachineRepresentation::kFloat64), + graph()->NewNode(machine()->Float64LessThan(), temp2, temp1), + graph()->NewNode(machine()->Float64Sub(), minus_one, temp2), + graph()->NewNode(machine()->Float64Sub(), minus_zero, temp2)); + } + + if_false1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2); + vfalse1 = + graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), + vtrue2, vfalse2, if_false1); + } + + if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); + vfalse0 = + graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), + vtrue1, vfalse1, if_false0); + } + + control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); + value = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), + vtrue0, vfalse0, control); + } + return ValueEffectControl(value, effect, control); +} + EffectControlLinearizer::ValueEffectControl EffectControlLinearizer::LowerFloat64RoundDown(Node* node, Node* effect, Node* control) { @@ -3382,108 +3516,78 @@ EffectControlLinearizer::LowerFloat64RoundDown(Node* node, Node* effect, return ValueEffectControl(node, effect, control); } + Node* const input = node->InputAt(0); + return BuildFloat64RoundDown(input, effect, control); +} + +EffectControlLinearizer::ValueEffectControl +EffectControlLinearizer::LowerFloat64RoundTiesEven(Node* node, Node* effect, + Node* control) { + // Nothing to be done if a fast hardware instruction is available. + if (machine()->Float64RoundTiesEven().IsSupported()) { + return ValueEffectControl(node, effect, control); + } + Node* const one = jsgraph()->Float64Constant(1.0); + Node* const two = jsgraph()->Float64Constant(2.0); + Node* const half = jsgraph()->Float64Constant(0.5); Node* const zero = jsgraph()->Float64Constant(0.0); - Node* const minus_one = jsgraph()->Float64Constant(-1.0); - Node* const minus_zero = jsgraph()->Float64Constant(-0.0); - Node* const two_52 = jsgraph()->Float64Constant(4503599627370496.0E0); - Node* const minus_two_52 = jsgraph()->Float64Constant(-4503599627370496.0E0); Node* const input = node->InputAt(0); - // General case for floor. + // Generate case for round ties to even: // - // if 0.0 < input then - // if 2^52 <= input then - // input - // else - // let temp1 = (2^52 + input) - 2^52 in - // if input < temp1 then - // temp1 - 1 - // else - // temp1 + // let value = floor(input) in + // let temp1 = input - value in + // if temp1 < 0.5 then + // value + // else if 0.5 < temp1 then + // value + 1.0 // else - // if input == 0 then - // input + // let temp2 = value % 2.0 in + // if temp2 == 0.0 then + // value // else - // if input <= -2^52 then - // input - // else - // let temp1 = -0 - input in - // let temp2 = (2^52 + temp1) - 2^52 in - // if temp2 < temp1 then - // -1 - temp2 - // else - // -0 - temp2 + // value + 1.0 // // Note: We do not use the Diamond helper class here, because it really hurts // readability with nested diamonds. - Node* check0 = graph()->NewNode(machine()->Float64LessThan(), zero, input); - Node* branch0 = - graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); + ValueEffectControl continuation = + BuildFloat64RoundDown(input, effect, control); + Node* value = continuation.value; + effect = continuation.effect; + control = continuation.control; + + Node* temp1 = graph()->NewNode(machine()->Float64Sub(), input, value); + + Node* check0 = graph()->NewNode(machine()->Float64LessThan(), temp1, half); + Node* branch0 = graph()->NewNode(common()->Branch(), check0, control); Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); - Node* vtrue0; - { - Node* check1 = - graph()->NewNode(machine()->Float64LessThanOrEqual(), two_52, input); - Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0); - - Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); - Node* vtrue1 = input; - - Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); - Node* vfalse1; - { - Node* temp1 = graph()->NewNode( - machine()->Float64Sub(), - graph()->NewNode(machine()->Float64Add(), two_52, input), two_52); - vfalse1 = graph()->NewNode( - common()->Select(MachineRepresentation::kFloat64), - graph()->NewNode(machine()->Float64LessThan(), input, temp1), - graph()->NewNode(machine()->Float64Sub(), temp1, one), temp1); - } - - if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); - vtrue0 = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), - vtrue1, vfalse1, if_true0); - } + Node* vtrue0 = value; Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); Node* vfalse0; { - Node* check1 = graph()->NewNode(machine()->Float64Equal(), input, zero); - Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), - check1, if_false0); + Node* check1 = graph()->NewNode(machine()->Float64LessThan(), half, temp1); + Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); - Node* vtrue1 = input; + Node* vtrue1 = graph()->NewNode(machine()->Float64Add(), value, one); Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); Node* vfalse1; { - Node* check2 = graph()->NewNode(machine()->Float64LessThanOrEqual(), - input, minus_two_52); - Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse), - check2, if_false1); + Node* temp2 = graph()->NewNode(machine()->Float64Mod(), value, two); + + Node* check2 = graph()->NewNode(machine()->Float64Equal(), temp2, zero); + Node* branch2 = graph()->NewNode(common()->Branch(), check2, if_false1); Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); - Node* vtrue2 = input; + Node* vtrue2 = value; Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); - Node* vfalse2; - { - Node* temp1 = - graph()->NewNode(machine()->Float64Sub(), minus_zero, input); - Node* temp2 = graph()->NewNode( - machine()->Float64Sub(), - graph()->NewNode(machine()->Float64Add(), two_52, temp1), two_52); - vfalse2 = graph()->NewNode( - common()->Select(MachineRepresentation::kFloat64), - graph()->NewNode(machine()->Float64LessThan(), temp2, temp1), - graph()->NewNode(machine()->Float64Sub(), minus_one, temp2), - graph()->NewNode(machine()->Float64Sub(), minus_zero, temp2)); - } + Node* vfalse2 = graph()->NewNode(machine()->Float64Add(), value, one); if_false1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2); vfalse1 = @@ -3497,11 +3601,11 @@ EffectControlLinearizer::LowerFloat64RoundDown(Node* node, Node* effect, vtrue1, vfalse1, if_false0); } - Node* merge0 = graph()->NewNode(common()->Merge(2), if_true0, if_false0); - Node* value = - graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), - vtrue0, vfalse0, merge0); - return ValueEffectControl(value, effect, merge0); + control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); + value = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), + vtrue0, vfalse0, control); + + return ValueEffectControl(value, effect, control); } EffectControlLinearizer::ValueEffectControl diff --git a/src/compiler/effect-control-linearizer.h b/src/compiler/effect-control-linearizer.h index b9344131e1..bc0366ae20 100644 --- a/src/compiler/effect-control-linearizer.h +++ b/src/compiler/effect-control-linearizer.h @@ -182,6 +182,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { Node* control); ValueEffectControl LowerFloat64RoundDown(Node* node, Node* effect, Node* control); + ValueEffectControl LowerFloat64RoundTiesEven(Node* node, Node* effect, + Node* control); ValueEffectControl LowerFloat64RoundTruncate(Node* node, Node* effect, Node* control); @@ -193,6 +195,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ValueEffectControl BuildCheckedHeapNumberOrOddballToFloat64( CheckTaggedInputMode mode, Node* value, Node* frame_state, Node* effect, Node* control); + ValueEffectControl BuildFloat64RoundDown(Node* value, Node* effect, + Node* control); ValueEffectControl LowerStringComparison(Callable const& callable, Node* node, Node* effect, Node* control); diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index d95b210178..7ade44c6c1 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -1200,6 +1200,14 @@ JSNativeContextSpecialization::BuildElementAccess( value = effect = graph()->NewNode(simplified()->CheckNumber(), value, 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. if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { Node* check = diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index 967bfb9d01..5d9811cf44 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -281,6 +281,7 @@ V(NumberToBoolean) \ V(NumberToInt32) \ V(NumberToUint32) \ + V(NumberToUint8Clamped) \ V(NumberSilenceNaN) #define SIMPLIFIED_OTHER_OP_LIST(V) \ diff --git a/src/compiler/operation-typer.cc b/src/compiler/operation-typer.cc index 0cdb6d1031..9198f4b9a9 100644 --- a/src/compiler/operation-typer.cc +++ b/src/compiler/operation-typer.cc @@ -490,6 +490,13 @@ Type* OperationTyper::NumberToUint32(Type* type) { 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) { DCHECK(type->Is(Type::Number())); // TODO(jarin): This is a terrible hack; we definitely need a dedicated type diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 9555a3fe00..ca3ed1f759 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -2111,6 +2111,31 @@ class RepresentationSelector { if (lower()) DeferReplacement(node, node->InputAt(0)); 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: { VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); if (lower()) { @@ -3294,6 +3319,71 @@ void SimplifiedLowering::DoNumberToBit(Node* node) { 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() { if (!to_number_code_.is_set()) { Callable callable = CodeFactory::ToNumber(isolate()); diff --git a/src/compiler/simplified-lowering.h b/src/compiler/simplified-lowering.h index 9e2a499bc6..09e58ff18a 100644 --- a/src/compiler/simplified-lowering.h +++ b/src/compiler/simplified-lowering.h @@ -44,6 +44,10 @@ class SimplifiedLowering final { void DoIntegral32ToBit(Node* node); void DoOrderedNumberToBit(Node* node); void DoNumberToBit(Node* node); + void DoIntegerToUint8Clamped(Node* node); + void DoNumberToUint8Clamped(Node* node); + void DoSigned32ToUint8Clamped(Node* node); + void DoUnsigned32ToUint8Clamped(Node* node); private: JSGraph* const jsgraph_; diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index be66d2fe05..345a2c5f88 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -393,6 +393,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) { V(NumberToBoolean, Operator::kNoProperties, 1, 0) \ V(NumberToInt32, Operator::kNoProperties, 1, 0) \ V(NumberToUint32, Operator::kNoProperties, 1, 0) \ + V(NumberToUint8Clamped, Operator::kNoProperties, 1, 0) \ V(NumberSilenceNaN, Operator::kNoProperties, 1, 0) \ V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \ V(StringFromCharCode, Operator::kNoProperties, 1, 0) \ diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index 4ba6d58984..833a0554f5 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -269,6 +269,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final const Operator* NumberToBoolean(); const Operator* NumberToInt32(); const Operator* NumberToUint32(); + const Operator* NumberToUint8Clamped(); const Operator* NumberSilenceNaN(); diff --git a/src/compiler/type-cache.h b/src/compiler/type-cache.h index 5579ddbf8b..4814959eb2 100644 --- a/src/compiler/type-cache.h +++ b/src/compiler/type-cache.h @@ -26,6 +26,8 @@ class TypeCache final { Type* const kInt8 = CreateRange(); Type* const kUint8 = CreateRange(); Type* const kUint8Clamped = kUint8; + Type* const kUint8OrMinusZeroOrNaN = + Type::Union(kUint8, Type::MinusZeroOrNaN(), zone()); Type* const kInt16 = CreateRange(); Type* const kUint16 = CreateRange(); Type* const kInt32 = Type::Signed32(); diff --git a/src/compiler/typed-optimization.cc b/src/compiler/typed-optimization.cc index 42d5232d58..5ebc390c8b 100644 --- a/src/compiler/typed-optimization.cc +++ b/src/compiler/typed-optimization.cc @@ -87,6 +87,8 @@ Reduction TypedOptimization::Reduce(Node* node) { case IrOpcode::kNumberRound: case IrOpcode::kNumberTrunc: return ReduceNumberRoundop(node); + case IrOpcode::kNumberToUint8Clamped: + return ReduceNumberToUint8Clamped(node); case IrOpcode::kPhi: return ReducePhi(node); case IrOpcode::kSelect: @@ -192,6 +194,15 @@ Reduction TypedOptimization::ReduceNumberRoundop(Node* node) { 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) { // 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 diff --git a/src/compiler/typed-optimization.h b/src/compiler/typed-optimization.h index 8a7093a77d..fb2db7249d 100644 --- a/src/compiler/typed-optimization.h +++ b/src/compiler/typed-optimization.h @@ -47,6 +47,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final Reduction ReduceCheckString(Node* node); Reduction ReduceLoadField(Node* node); Reduction ReduceNumberRoundop(Node* node); + Reduction ReduceNumberToUint8Clamped(Node* node); Reduction ReducePhi(Node* node); Reduction ReduceSelect(Node* node); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 6552654cf3..6f5d4d0623 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -826,6 +826,7 @@ void Verifier::Visitor::Check(Node* node) { CheckTypeIs(node, Type::Signed32()); break; case IrOpcode::kNumberToUint32: + case IrOpcode::kNumberToUint8Clamped: // Number -> Unsigned32 CheckValueInputIs(node, 0, Type::Number()); CheckTypeIs(node, Type::Unsigned32()); diff --git a/test/mjsunit/compiler/uint8-clamped-array.js b/test/mjsunit/compiler/uint8-clamped-array.js new file mode 100644 index 0000000000..66274d54d1 --- /dev/null +++ b/test/mjsunit/compiler/uint8-clamped-array.js @@ -0,0 +1,73 @@ +// 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]); +})();