Index: src/compiler/arm/instruction-selector-arm.cc |
diff --git a/src/compiler/arm/instruction-selector-arm.cc b/src/compiler/arm/instruction-selector-arm.cc |
index b8d2d905e45681b9933250f3a27e78e35593f82c..536b0fcd18ec49ef3f927ac332adeef59c7191b2 100644 |
--- a/src/compiler/arm/instruction-selector-arm.cc |
+++ b/src/compiler/arm/instruction-selector-arm.cc |
@@ -304,6 +304,64 @@ void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode div_opcode, |
} |
} |
+void EmitLoad(InstructionSelector* selector, ArchOpcode opcode, Node* node, |
+ Node* const base, Node* const index) { |
+ ArmOperandGenerator g(selector); |
+ InstructionCode icode; |
+ InstructionOperand index_operand; |
+ if (g.CanBeImmediate(index, opcode)) { |
+ icode = opcode | AddressingModeField::encode(kMode_Offset_RI); |
+ index_operand = g.UseImmediate(index); |
+ } else { |
+ icode = opcode | AddressingModeField::encode(kMode_Offset_RR); |
+ index_operand = g.UseRegister(index); |
+ } |
+ selector->Emit(icode, g.DefineAsRegister(node), g.UseRegister(base), |
+ index_operand); |
+} |
+ |
+void EmitCheckedLoad(InstructionSelector* selector, ArchOpcode opcode, |
+ Node* node, Node* const buffer, Node* const offset, |
+ Node* const length) { |
+ ArmOperandGenerator g(selector); |
+ InstructionOperand offset_operand = g.UseRegister(offset); |
+ InstructionOperand length_operand = g.CanBeImmediate(length, kArmCmp) |
+ ? g.UseImmediate(length) |
+ : g.UseRegister(length); |
+ selector->Emit(opcode | AddressingModeField::encode(kMode_Offset_RR), |
+ g.DefineAsRegister(node), offset_operand, length_operand, |
+ g.UseRegister(buffer), offset_operand); |
+} |
+ |
+void SimplifyAndForMaskedStore(InstructionSelector* selector, Node* node, |
+ Node* value, ArchOpcode opcode) { |
+ ArmOperandGenerator g(selector); |
+ NodeMatcher nm(value); |
+ if (nm.IsWord32And() && selector->CanCover(node, value)) { |
+ Uint32BinopMatcher m(value); |
+ if (m.right().HasValue() && |
+ ((opcode == kCheckedStoreWord16) || (opcode == kArmStrh))) { |
+ uint32_t mask = m.right().Value(); |
+ |
+ // Halfword stores effectively apply a mask of 0xffff, so simplify the |
+ // AND's mask using this, with the aim of making the new immediate |
+ // encodable in the instruction. |
+ mask &= 0xffff; |
+ if (g.CanBeImmediate(mask)) { |
+ selector->Emit(kArmAnd | AddressingModeField::encode(kMode_Operand2_I), |
+ g.DefineAsRegister(value), |
+ g.UseRegister(m.left().node()), g.TempImmediate(mask)); |
+ } else if (g.CanBeImmediate(mask ^ 0xffff)) { |
+ selector->Emit(kArmBic | AddressingModeField::encode(kMode_Operand2_I), |
+ g.DefineAsRegister(value), |
+ g.UseRegister(m.left().node()), |
+ g.TempImmediate(mask ^ 0xffff)); |
+ } |
+ // TODO(turbofan): Consider similar optimisation for strb/and 0xff. |
+ } |
+ } |
+} |
+ |
} // namespace |
@@ -339,13 +397,7 @@ void InstructionSelector::VisitLoad(Node* node) { |
return; |
} |
- if (g.CanBeImmediate(index, opcode)) { |
- Emit(opcode | AddressingModeField::encode(kMode_Offset_RI), |
- g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index)); |
- } else { |
- Emit(opcode | AddressingModeField::encode(kMode_Offset_RR), |
- g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(index)); |
- } |
+ EmitLoad(this, opcode, node, base, index); |
} |
@@ -423,6 +475,8 @@ void InstructionSelector::VisitStore(Node* node) { |
return; |
} |
+ SimplifyAndForMaskedStore(this, node, value, opcode); |
+ |
if (g.CanBeImmediate(index, opcode)) { |
Emit(opcode | AddressingModeField::encode(kMode_Offset_RI), g.NoOutput(), |
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); |
@@ -465,13 +519,7 @@ void InstructionSelector::VisitCheckedLoad(Node* node) { |
UNREACHABLE(); |
return; |
} |
- InstructionOperand offset_operand = g.UseRegister(offset); |
- InstructionOperand length_operand = g.CanBeImmediate(length, kArmCmp) |
- ? g.UseImmediate(length) |
- : g.UseRegister(length); |
- Emit(opcode | AddressingModeField::encode(kMode_Offset_RR), |
- g.DefineAsRegister(node), offset_operand, length_operand, |
- g.UseRegister(buffer), offset_operand); |
+ EmitCheckedLoad(this, opcode, node, buffer, offset, length); |
} |
@@ -507,6 +555,9 @@ void InstructionSelector::VisitCheckedStore(Node* node) { |
UNREACHABLE(); |
return; |
} |
+ |
+ SimplifyAndForMaskedStore(this, node, value, opcode); |
+ |
InstructionOperand offset_operand = g.UseRegister(offset); |
InstructionOperand length_operand = g.CanBeImmediate(length, kArmCmp) |
? g.UseImmediate(length) |
@@ -545,6 +596,42 @@ void EmitUbfx(InstructionSelector* selector, Node* node, Node* left, |
g.TempImmediate(lsb), g.TempImmediate(width)); |
} |
+// Try to emit an AND or BIC instruction when the left input is an 8/16-bit |
+// load. Returns true if an instruction was emitted, false otherwise. |
+bool TryEmitAndForMaskedLoad(InstructionSelector* selector, Node* node, |
+ MachineType type, uint32_t value) { |
+ ArmOperandGenerator g(selector); |
+ Int32BinopMatcher m(node); |
+ if (type == MachineType::Uint16()) { |
+ // The input load has already applied a 0xffff mask to the input, so |
+ // simplify the value and test if it's an immediate that can be encoded in |
+ // the instruction. |
+ value &= 0xffff; |
+ if (g.CanBeImmediate(value)) { |
+ selector->Emit(kArmAnd | AddressingModeField::encode(kMode_Operand2_I), |
+ g.DefineAsRegister(node), g.UseRegister(m.left().node()), |
+ g.TempImmediate(value)); |
+ return true; |
+ } |
+ // Test if an inverted value can be used with BIC. |
+ if (g.CanBeImmediate(value ^ 0xffff)) { |
+ selector->Emit(kArmBic | AddressingModeField::encode(kMode_Operand2_I), |
+ g.DefineAsRegister(node), g.UseRegister(m.left().node()), |
+ g.TempImmediate(value ^ 0xffff)); |
+ return true; |
+ } |
+ } else if (type == MachineType::Uint8()) { |
+ // The input load has already applied a 0xff mask to the input. |
+ value &= 0xff; |
+ DCHECK(g.CanBeImmediate(value)); // All 8-bit values are encodable. |
+ selector->Emit(kArmAnd | AddressingModeField::encode(kMode_Operand2_I), |
+ g.DefineAsRegister(node), g.UseRegister(m.left().node()), |
+ g.TempImmediate(value)); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
} // namespace |
@@ -570,6 +657,46 @@ void InstructionSelector::VisitWord32And(Node* node) { |
uint32_t width = base::bits::CountPopulation32(value); |
uint32_t leading_zeros = base::bits::CountLeadingZeros32(value); |
+ if (m.left().IsLoad()) { |
+ LoadRepresentation load_rep = LoadRepresentationOf(m.left().node()->op()); |
+ if (CanCover(node, m.left().node()) && |
+ ((value == 0xff) || (value == 0xffff))) { |
+ // Elide mask operation on load inputs by modifying the size of the |
+ // load. |
+ ArchOpcode opcode = kArmLdrb; |
+ if ((value == 0xffff) && |
+ (load_rep.representation() != MachineRepresentation::kWord8)) { |
+ opcode = kArmLdrh; |
+ } |
+ EmitLoad(this, opcode, node, m.left().InputAt(0), m.left().InputAt(1)); |
+ return; |
+ } |
+ if (!g.CanBeImmediate(value) && |
+ TryEmitAndForMaskedLoad(this, node, load_rep, value)) { |
+ return; |
+ } |
+ } else if (m.left().IsCheckedLoad()) { |
+ CheckedLoadRepresentation load_rep = |
+ CheckedLoadRepresentationOf(m.left().node()->op()); |
+ if (CanCover(node, m.left().node()) && |
+ ((value == 0xff) || (value == 0xffff))) { |
+ // Elide mask operation on load inputs by modifying the size of the |
+ // load. |
+ ArchOpcode opcode = kCheckedLoadUint8; |
+ if ((value == 0xffff) && |
+ (load_rep.representation() != MachineRepresentation::kWord8)) { |
+ opcode = kCheckedLoadUint16; |
+ } |
+ EmitCheckedLoad(this, opcode, node, m.left().InputAt(0), |
+ m.left().InputAt(1), m.left().InputAt(2)); |
+ return; |
+ } |
+ if (!g.CanBeImmediate(value) && |
+ TryEmitAndForMaskedLoad(this, node, load_rep, value)) { |
+ return; |
+ } |
+ } |
+ |
// Try to merge SHR operations on the left hand input into this AND. |
if (m.left().IsWord32Shr()) { |
Int32BinopMatcher mshr(m.left().node()); |