[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}
This commit is contained in:
parent
ed35983ab7
commit
82b315ce75
@ -25,8 +25,6 @@ bool CanInlineElementAccess(Handle<Map> 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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -281,6 +281,7 @@
|
||||
V(NumberToBoolean) \
|
||||
V(NumberToInt32) \
|
||||
V(NumberToUint32) \
|
||||
V(NumberToUint8Clamped) \
|
||||
V(NumberSilenceNaN)
|
||||
|
||||
#define SIMPLIFIED_OTHER_OP_LIST(V) \
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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_;
|
||||
|
@ -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) \
|
||||
|
@ -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();
|
||||
|
||||
|
@ -26,6 +26,8 @@ class TypeCache final {
|
||||
Type* const kInt8 = CreateRange<int8_t>();
|
||||
Type* const kUint8 = CreateRange<uint8_t>();
|
||||
Type* const kUint8Clamped = kUint8;
|
||||
Type* const kUint8OrMinusZeroOrNaN =
|
||||
Type::Union(kUint8, Type::MinusZeroOrNaN(), zone());
|
||||
Type* const kInt16 = CreateRange<int16_t>();
|
||||
Type* const kUint16 = CreateRange<uint16_t>();
|
||||
Type* const kInt32 = Type::Signed32();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
73
test/mjsunit/compiler/uint8-clamped-array.js
Normal file
73
test/mjsunit/compiler/uint8-clamped-array.js
Normal file
@ -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]);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user