Commit 5c0f7219 authored by Seth Brenith's avatar Seth Brenith Committed by Commit Bot

Check for zero-extended phi inputs during instruction selection

The 64-bit instruction selectors check whether the input value to
a ChangeUint32ToUint64 node was produced by a node that sets the upper
32 bits to zero, and if so, they avoid emitting an extra instruction to
clear the upper bits. This change:

1. Extends that existing mechanism to also include phi values: if all of
   the inputs to a phi value guarantee that the upper 32 bits are zero,
   then the phi value does too.
2. Updates x64 to include non-negative int32 constants in the list of
   nodes that always clear the upper bits. I didn't add this to any
   other architecture because I'm less certain of how they resolve moves
   from constants.

This change improves the speed of the Mono interpreter on x64 by about
5%.

Bug: v8:10606
Change-Id: Ife8ce9c7330524e0b2fad836209a81180b4870e8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2382509Reviewed-by: 's avatarBill Budge <bbudge@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#69706}
parent 1f7cb7e1
......@@ -1775,10 +1775,9 @@ void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
VisitRR(this, kArm64Sxtw, node);
}
void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
Arm64OperandGenerator g(this);
Node* value = node->InputAt(0);
switch (value->opcode()) {
bool InstructionSelector::ZeroExtendsWord32ToWord64NoPhis(Node* node) {
DCHECK_NE(node->opcode(), IrOpcode::kPhi);
switch (node->opcode()) {
case IrOpcode::kWord32And:
case IrOpcode::kWord32Or:
case IrOpcode::kWord32Xor:
......@@ -1805,26 +1804,31 @@ void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
// 32-bit operations will write their result in a W register (implicitly
// clearing the top 32-bit of the corresponding X register) so the
// zero-extension is a no-op.
EmitIdentity(node);
return;
return true;
}
case IrOpcode::kLoad: {
// As for the operations above, a 32-bit load will implicitly clear the
// top 32 bits of the destination register.
LoadRepresentation load_rep = LoadRepresentationOf(value->op());
LoadRepresentation load_rep = LoadRepresentationOf(node->op());
switch (load_rep.representation()) {
case MachineRepresentation::kWord8:
case MachineRepresentation::kWord16:
case MachineRepresentation::kWord32:
EmitIdentity(node);
return;
return true;
default:
break;
return false;
}
break;
}
default:
break;
return false;
}
}
void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
Arm64OperandGenerator g(this);
Node* value = node->InputAt(0);
if (ZeroExtendsWord32ToWord64(value)) {
return EmitIdentity(node);
}
Emit(kArm64Mov32, g.DefineAsRegister(node), g.UseRegister(value));
}
......
......@@ -62,7 +62,12 @@ InstructionSelector::InstructionSelector(
trace_turbo_(trace_turbo),
tick_counter_(tick_counter),
max_unoptimized_frame_height_(max_unoptimized_frame_height),
max_pushed_argument_count_(max_pushed_argument_count) {
max_pushed_argument_count_(max_pushed_argument_count)
#if V8_TARGET_ARCH_64_BIT
,
phi_states_(node_count, Upper32BitsState::kNotYetChecked, zone)
#endif
{
DCHECK_EQ(*max_unoptimized_frame_height, 0); // Caller-initialized.
instructions_.reserve(node_count);
......@@ -3126,6 +3131,54 @@ bool InstructionSelector::CanProduceSignalingNaN(Node* node) {
return true;
}
#if V8_TARGET_ARCH_64_BIT
bool InstructionSelector::ZeroExtendsWord32ToWord64(Node* node,
int recursion_depth) {
// To compute whether a Node sets its upper 32 bits to zero, there are three
// cases.
// 1. Phi node, with a computed result already available in phi_states_:
// Read the value from phi_states_.
// 2. Phi node, with no result available in phi_states_ yet:
// Recursively check its inputs, and store the result in phi_states_.
// 3. Anything else:
// Call the architecture-specific ZeroExtendsWord32ToWord64NoPhis.
// Limit recursion depth to avoid the possibility of stack overflow on very
// large functions.
const int kMaxRecursionDepth = 100;
if (node->opcode() == IrOpcode::kPhi) {
Upper32BitsState current = phi_states_[node->id()];
if (current != Upper32BitsState::kNotYetChecked) {
return current == Upper32BitsState::kUpperBitsGuaranteedZero;
}
// If further recursion is prevented, we can't make any assumptions about
// the output of this phi node.
if (recursion_depth >= kMaxRecursionDepth) {
return false;
}
// Mark the current node so that we skip it if we recursively visit it
// again. Or, said differently, we compute a largest fixed-point so we can
// be optimistic when we hit cycles.
phi_states_[node->id()] = Upper32BitsState::kUpperBitsGuaranteedZero;
int input_count = node->op()->ValueInputCount();
for (int i = 0; i < input_count; ++i) {
Node* input = NodeProperties::GetValueInput(node, i);
if (!ZeroExtendsWord32ToWord64(input, recursion_depth + 1)) {
phi_states_[node->id()] = Upper32BitsState::kNoGuarantee;
return false;
}
}
return true;
}
return ZeroExtendsWord32ToWord64NoPhis(node);
}
#endif // V8_TARGET_ARCH_64_BIT
namespace {
FrameStateDescriptor* GetFrameStateDescriptorInternal(Zone* zone, Node* state) {
......
......@@ -667,6 +667,17 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
void VisitWord64AtomicNarrowBinop(Node* node, ArchOpcode uint8_op,
ArchOpcode uint16_op, ArchOpcode uint32_op);
#if V8_TARGET_ARCH_64_BIT
bool ZeroExtendsWord32ToWord64(Node* node, int recursion_depth = 0);
bool ZeroExtendsWord32ToWord64NoPhis(Node* node);
enum Upper32BitsState : uint8_t {
kNotYetChecked,
kUpperBitsGuaranteedZero,
kNoGuarantee,
};
#endif // V8_TARGET_ARCH_64_BIT
// ===========================================================================
Zone* const zone_;
......@@ -702,6 +713,13 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
// arguments (for calls). Later used to apply an offset to stack checks.
size_t* max_unoptimized_frame_height_;
size_t* max_pushed_argument_count_;
#if V8_TARGET_ARCH_64_BIT
// Holds lazily-computed results for whether phi nodes guarantee their upper
// 32 bits to be zero. Indexed by node ID; nobody reads or writes the values
// for non-phi nodes.
ZoneVector<Upper32BitsState> phi_states_;
#endif
};
} // namespace compiler
......
......@@ -1399,35 +1399,40 @@ void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
}
}
void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
Mips64OperandGenerator g(this);
Node* value = node->InputAt(0);
switch (value->opcode()) {
bool InstructionSelector::ZeroExtendsWord32ToWord64NoPhis(Node* node) {
DCHECK_NE(node->opcode(), IrOpcode::kPhi);
switch (node->opcode()) {
// 32-bit operations will write their result in a 64 bit register,
// clearing the top 32 bits of the destination register.
case IrOpcode::kUint32Div:
case IrOpcode::kUint32Mod:
case IrOpcode::kUint32MulHigh: {
Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
return;
}
case IrOpcode::kUint32MulHigh:
return true;
case IrOpcode::kLoad: {
LoadRepresentation load_rep = LoadRepresentationOf(value->op());
LoadRepresentation load_rep = LoadRepresentationOf(node->op());
if (load_rep.IsUnsigned()) {
switch (load_rep.representation()) {
case MachineRepresentation::kWord8:
case MachineRepresentation::kWord16:
case MachineRepresentation::kWord32:
Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
return;
return true;
default:
break;
return false;
}
}
break;
return false;
}
default:
break;
return false;
}
}
void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
Mips64OperandGenerator g(this);
Node* value = node->InputAt(0);
if (ZeroExtendsWord32ToWord64(value)) {
Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
return;
}
Emit(kMips64Dext, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)),
g.TempImmediate(0), g.TempImmediate(32));
......
......@@ -1200,6 +1200,10 @@ void InstructionSelector::VisitSignExtendWord32ToInt64(Node* node) {
VisitRR(this, kPPC_ExtendSignWord32, node);
}
bool InstructionSelector::ZeroExtendsWord32ToWord64NoPhis(Node* node) {
UNIMPLEMENTED();
}
void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
// TODO(mbrandy): inspect input to see if nop is appropriate.
VisitRR(this, kPPC_Uint32ToUint64, node);
......
......@@ -1297,9 +1297,9 @@ void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
}
}
namespace {
bool ZeroExtendsWord32ToWord64(Node* node) {
bool InstructionSelector::ZeroExtendsWord32ToWord64NoPhis(Node* node) {
X64OperandGenerator g(this);
DCHECK_NE(node->opcode(), IrOpcode::kPhi);
switch (node->opcode()) {
case IrOpcode::kWord32And:
case IrOpcode::kWord32Or:
......@@ -1353,13 +1353,20 @@ bool ZeroExtendsWord32ToWord64(Node* node) {
return false;
}
}
case IrOpcode::kInt32Constant:
case IrOpcode::kInt64Constant:
// Constants are loaded with movl or movq, or xorl for zero; see
// CodeGenerator::AssembleMove. So any non-negative constant that fits
// in a 32-bit signed integer is zero-extended to 64 bits.
if (g.CanBeImmediate(node)) {
return g.GetImmediateIntegerValue(node) >= 0;
}
return false;
default:
return false;
}
}
} // namespace
void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
X64OperandGenerator g(this);
Node* value = node->InputAt(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