Commit 7954b3f8 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce TruncateTaggedToBit operator for ToBoolean truncation.

Add a dedicated simplified operator to inline the general case for the
ToBoolean conversion. In a follow up CL we will also use the ToBoolean
hints gathered by the baseline compiler.

CQ_INCLUDE_TRYBOTS=master.tryserver.v8:v8_linux_arm64_gc_stress_dbg
R=jarin@chromium.org
BUG=v8:5267

Committed: https://crrev.com/8c50b51ab3d21efcd2f6900d83962159f21e1590
Review-Url: https://codereview.chromium.org/2167593002
Cr-Original-Commit-Position: refs/heads/master@{#37882}
Cr-Commit-Position: refs/heads/master@{#39420}
parent 6d89f8a7
......@@ -265,7 +265,6 @@ void TryCloneBranch(Node* node, BasicBlock* block, Graph* graph,
Node* phi_false = graph->NewNode(phi->op(), input_count + 1, inputs);
if (phi->UseCount() == 0) {
DCHECK_EQ(phi->opcode(), IrOpcode::kEffectPhi);
DCHECK_EQ(input_count, block->SuccessorCount());
} else {
for (Edge edge : phi->use_edges()) {
Node* control = NodeProperties::GetControlInput(edge.from());
......@@ -616,6 +615,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kChangeTaggedToFloat64:
state = LowerChangeTaggedToFloat64(node, *effect, *control);
break;
case IrOpcode::kTruncateTaggedToBit:
state = LowerTruncateTaggedToBit(node, *effect, *control);
break;
case IrOpcode::kTruncateTaggedToFloat64:
state = LowerTruncateTaggedToFloat64(node, *effect, *control);
break;
......@@ -941,6 +943,157 @@ EffectControlLinearizer::LowerChangeTaggedToBit(Node* node, Node* effect,
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerTruncateTaggedToBit(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* one = jsgraph()->Int32Constant(1);
Node* zero = jsgraph()->Int32Constant(0);
Node* fzero = jsgraph()->Float64Constant(0.0);
// Collect effect/control/value triples.
int count = 0;
Node* values[7];
Node* effects[7];
Node* controls[6];
// Check if {value} is a Smi.
Node* check_smi = ObjectIsSmi(value);
Node* branch_smi = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check_smi, control);
// If {value} is a Smi, then we only need to check that it's not zero.
Node* if_smi = graph()->NewNode(common()->IfTrue(), branch_smi);
Node* esmi = effect;
{
controls[count] = if_smi;
effects[count] = esmi;
values[count] =
graph()->NewNode(machine()->Word32Equal(),
graph()->NewNode(machine()->WordEqual(), value,
jsgraph()->ZeroConstant()),
zero);
count++;
}
control = graph()->NewNode(common()->IfFalse(), branch_smi);
// Load the map instance type of {value}.
Node* value_map = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
Node* value_instance_type = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
effect, control);
// Check if {value} is an Oddball.
Node* check_oddball =
graph()->NewNode(machine()->Word32Equal(), value_instance_type,
jsgraph()->Int32Constant(ODDBALL_TYPE));
Node* branch_oddball = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check_oddball, control);
// The only Oddball {value} that is trueish is true itself.
Node* if_oddball = graph()->NewNode(common()->IfTrue(), branch_oddball);
Node* eoddball = effect;
{
controls[count] = if_oddball;
effects[count] = eoddball;
values[count] = graph()->NewNode(machine()->WordEqual(), value,
jsgraph()->TrueConstant());
count++;
}
control = graph()->NewNode(common()->IfFalse(), branch_oddball);
// Check if {value} is a String.
Node* check_string =
graph()->NewNode(machine()->Int32LessThan(), value_instance_type,
jsgraph()->Int32Constant(FIRST_NONSTRING_TYPE));
Node* branch_string =
graph()->NewNode(common()->Branch(), check_string, control);
// For String {value}, we need to check that the length is not zero.
Node* if_string = graph()->NewNode(common()->IfTrue(), branch_string);
Node* estring = effect;
{
// Load the {value} length.
Node* value_length = estring = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), value,
estring, if_string);
controls[count] = if_string;
effects[count] = estring;
values[count] =
graph()->NewNode(machine()->Word32Equal(),
graph()->NewNode(machine()->WordEqual(), value_length,
jsgraph()->ZeroConstant()),
zero);
count++;
}
control = graph()->NewNode(common()->IfFalse(), branch_string);
// Check if {value} is a HeapNumber.
Node* check_heapnumber =
graph()->NewNode(machine()->Word32Equal(), value_instance_type,
jsgraph()->Int32Constant(HEAP_NUMBER_TYPE));
Node* branch_heapnumber =
graph()->NewNode(common()->Branch(), check_heapnumber, control);
// For HeapNumber {value}, just check that its value is not 0.0, -0.0 or NaN.
Node* if_heapnumber = graph()->NewNode(common()->IfTrue(), branch_heapnumber);
Node* eheapnumber = effect;
{
// Load the raw value of {value}.
Node* value_value = eheapnumber = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), value,
eheapnumber, if_heapnumber);
// Check if {value} is either less than 0.0 or greater than 0.0.
Node* check =
graph()->NewNode(machine()->Float64LessThan(), fzero, value_value);
Node* branch = graph()->NewNode(common()->Branch(), check, if_heapnumber);
controls[count] = graph()->NewNode(common()->IfTrue(), branch);
effects[count] = eheapnumber;
values[count] = one;
count++;
controls[count] = graph()->NewNode(common()->IfFalse(), branch);
effects[count] = eheapnumber;
values[count] =
graph()->NewNode(machine()->Float64LessThan(), value_value, fzero);
count++;
}
control = graph()->NewNode(common()->IfFalse(), branch_heapnumber);
// The {value} is either a JSReceiver, a Symbol or some Simd128Value. In
// those cases we can just the undetectable bit on the map, which will only
// be set for certain JSReceivers, i.e. document.all.
{
// Load the {value} map bit field.
Node* value_map_bitfield = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapBitField()), value_map,
effect, control);
controls[count] = control;
effects[count] = effect;
values[count] = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(machine()->Word32And(), value_map_bitfield,
jsgraph()->Int32Constant(1 << Map::kIsUndetectable)),
zero);
count++;
}
// Merge the different controls.
control = graph()->NewNode(common()->Merge(count), count, controls);
effects[count] = control;
effect = graph()->NewNode(common()->EffectPhi(count), count + 1, effects);
values[count] = control;
value = graph()->NewNode(common()->Phi(MachineRepresentation::kBit, count),
count + 1, values);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerChangeTaggedToInt32(Node* node, Node* effect,
Node* control) {
......
......@@ -103,6 +103,8 @@ class EffectControlLinearizer {
Node* effect, Node* control);
ValueEffectControl LowerChangeTaggedToFloat64(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerTruncateTaggedToBit(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerTruncateTaggedToFloat64(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerTruncateTaggedToWord32(Node* node, Node* effect,
......
......@@ -179,7 +179,8 @@
V(ChangeTaggedToBit) \
V(ChangeBitToTagged) \
V(TruncateTaggedToWord32) \
V(TruncateTaggedToFloat64)
V(TruncateTaggedToFloat64) \
V(TruncateTaggedToBit)
#define SIMPLIFIED_CHECKED_OP_LIST(V) \
V(CheckedInt32Add) \
......
......@@ -650,10 +650,7 @@ Node* RepresentationChanger::GetBitRepresentationFor(
switch (node->opcode()) {
case IrOpcode::kHeapConstant: {
Handle<HeapObject> value = OpParameter<Handle<HeapObject>>(node);
DCHECK(value.is_identical_to(factory()->true_value()) ||
value.is_identical_to(factory()->false_value()));
return jsgraph()->Int32Constant(
value.is_identical_to(factory()->true_value()) ? 1 : 0);
return jsgraph()->Int32Constant(value->BooleanValue() ? 1 : 0);
}
default:
break;
......@@ -665,9 +662,31 @@ Node* RepresentationChanger::GetBitRepresentationFor(
// We just provide a dummy value here.
return jsgraph()->Int32Constant(0);
} else if (output_rep == MachineRepresentation::kTagged ||
output_rep == MachineRepresentation::kTaggedSigned ||
output_rep == MachineRepresentation::kTaggedPointer) {
op = simplified()->ChangeTaggedToBit();
if (output_type->Is(Type::BooleanOrNullOrUndefined())) {
// true is the only trueish Oddball.
op = simplified()->ChangeTaggedToBit();
} else {
op = simplified()->TruncateTaggedToBit();
}
} else if (output_rep == MachineRepresentation::kTaggedSigned) {
node = jsgraph()->graph()->NewNode(machine()->WordEqual(), node,
jsgraph()->ZeroConstant());
return jsgraph()->graph()->NewNode(machine()->Word32Equal(), node,
jsgraph()->Int32Constant(0));
} else if (IsWord(output_rep)) {
node = jsgraph()->graph()->NewNode(machine()->Word32Equal(), node,
jsgraph()->Int32Constant(0));
return jsgraph()->graph()->NewNode(machine()->Word32Equal(), node,
jsgraph()->Int32Constant(0));
} else if (output_rep == MachineRepresentation::kFloat32) {
node = jsgraph()->graph()->NewNode(machine()->Float32Abs(), node);
return jsgraph()->graph()->NewNode(machine()->Float32LessThan(),
jsgraph()->Float32Constant(0.0), node);
} else if (output_rep == MachineRepresentation::kFloat64) {
node = jsgraph()->graph()->NewNode(machine()->Float64Abs(), node);
return jsgraph()->graph()->NewNode(machine()->Float64LessThan(),
jsgraph()->Float64Constant(0.0), node);
} else {
return TypeError(node, output_rep, output_type,
MachineRepresentation::kBit);
......
......@@ -29,6 +29,9 @@ class Truncation final {
// Queries.
bool IsUnused() const { return kind_ == TruncationKind::kNone; }
bool IsUsedAsBool() const {
return LessGeneral(kind_, TruncationKind::kBool);
}
bool IsUsedAsWord32() const {
return LessGeneral(kind_, TruncationKind::kWord32);
}
......
......@@ -1343,6 +1343,18 @@ class RepresentationSelector {
//------------------------------------------------------------------
// JavaScript operators.
//------------------------------------------------------------------
case IrOpcode::kJSToBoolean: {
if (truncation.IsUsedAsBool()) {
ProcessInput(node, 0, UseInfo::Bool());
ProcessInput(node, 1, UseInfo::None());
SetOutput(node, MachineRepresentation::kBit);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else {
VisitInputs(node);
SetOutput(node, MachineRepresentation::kTagged);
}
return;
}
case IrOpcode::kJSToNumber: {
VisitInputs(node);
// TODO(bmeurer): Optimize somewhat based on input type?
......@@ -2465,7 +2477,6 @@ class RepresentationSelector {
JS_CONTEXT_OP_LIST(OPCODE_CASE)
JS_OTHER_OP_LIST(OPCODE_CASE)
#undef OPCODE_CASE
case IrOpcode::kJSToBoolean:
case IrOpcode::kJSToInteger:
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
......
......@@ -399,6 +399,7 @@ NumberOperationHint NumberOperationHintOf(const Operator* op) {
V(ChangeUint32ToTagged, Operator::kNoProperties, 1, 0) \
V(ChangeTaggedToBit, Operator::kNoProperties, 1, 0) \
V(ChangeBitToTagged, Operator::kNoProperties, 1, 0) \
V(TruncateTaggedToBit, Operator::kNoProperties, 1, 0) \
V(TruncateTaggedToWord32, Operator::kNoProperties, 1, 0) \
V(TruncateTaggedToFloat64, Operator::kNoProperties, 1, 0) \
V(ObjectIsCallable, Operator::kNoProperties, 1, 0) \
......
......@@ -304,6 +304,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* ChangeBitToTagged();
const Operator* TruncateTaggedToWord32();
const Operator* TruncateTaggedToFloat64();
const Operator* TruncateTaggedToBit();
const Operator* CheckIf();
const Operator* CheckBounds();
......
......@@ -993,6 +993,9 @@ void Verifier::Visitor::Check(Node* node) {
// CheckTypeIs(node, to));
break;
}
case IrOpcode::kTruncateTaggedToBit:
break;
case IrOpcode::kCheckBounds:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Unsigned31());
......
......@@ -651,23 +651,7 @@ TEST(Nops) {
TEST(TypeErrors) {
RepresentationChangerTester r;
// Wordish cannot be implicitly converted to/from comparison conditions.
r.CheckTypeError(MachineRepresentation::kWord8, Type::Number(),
MachineRepresentation::kBit);
r.CheckTypeError(MachineRepresentation::kWord16, Type::Number(),
MachineRepresentation::kBit);
r.CheckTypeError(MachineRepresentation::kWord32, Type::Number(),
MachineRepresentation::kBit);
r.CheckTypeError(MachineRepresentation::kWord64, Type::Number(),
MachineRepresentation::kBit);
// Floats cannot be implicitly converted to/from comparison conditions.
r.CheckTypeError(MachineRepresentation::kFloat64, Type::Number(),
MachineRepresentation::kBit);
// Floats cannot be implicitly converted to/from comparison conditions.
r.CheckTypeError(MachineRepresentation::kFloat32, Type::Number(),
MachineRepresentation::kBit);
r.CheckTypeError(MachineRepresentation::kBit, Type::Number(),
MachineRepresentation::kFloat32);
r.CheckTypeError(MachineRepresentation::kBit, Type::Boolean(),
......
......@@ -63,6 +63,8 @@ const PureOperator kPureOperators[] = {
PURE(ChangeTaggedToBit, Operator::kNoProperties, 1),
PURE(ChangeBitToTagged, Operator::kNoProperties, 1),
PURE(TruncateTaggedToWord32, Operator::kNoProperties, 1),
PURE(TruncateTaggedToFloat64, Operator::kNoProperties, 1),
PURE(TruncateTaggedToBit, Operator::kNoProperties, 1),
PURE(ObjectIsNumber, Operator::kNoProperties, 1),
PURE(ObjectIsReceiver, Operator::kNoProperties, 1),
PURE(ObjectIsSmi, Operator::kNoProperties, 1)
......
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