Chromium Code Reviews| 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 ef0bb6800e4933ecf905a65f3ca03558760db289..16712addffbd26a94a0b612eb9bad6767455af2e 100644 |
| --- a/src/compiler/arm/instruction-selector-arm.cc |
| +++ b/src/compiler/arm/instruction-selector-arm.cc |
| @@ -304,6 +304,78 @@ 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); |
| +} |
| + |
| +// Simplify or elide mask on 8-bit and 16-bit stores. Returns the input node of |
| +// the mask operation if it can be elided, otherwise the value node. |
| +Node* SimplifyAndForMaskedStore(InstructionSelector* selector, Node* node, |
|
Benedikt Meurer
2016/04/14 18:39:58
This looks like something that should be done in t
martyn.capewell
2016/04/15 14:05:49
You're right, I should update MachineOperatorReduc
|
| + Node* value, ArchOpcode opcode) { |
| + ArmOperandGenerator g(selector); |
| + NodeMatcher nm(value); |
| + if (nm.IsWord32And() && selector->CanCover(node, value)) { |
| + Uint32BinopMatcher m(value); |
| + if (m.right().HasValue()) { |
| + uint32_t mask = m.right().Value(); |
| + |
| + // Rewrite the opcode to make the later if statement simpler. |
| + opcode = (opcode == kCheckedStoreWord8) ? kArmStrb : opcode; |
| + opcode = (opcode == kCheckedStoreWord16) ? kArmStrh : opcode; |
| + |
| + if (((opcode == kArmStrb) && ((mask & 0xff) == 0xff)) || |
| + ((opcode == kArmStrh) && ((mask & 0xffff) == 0xffff))) { |
| + // The masking operation can be elided, so return the left input to the |
| + // mask. |
| + return m.left().node(); |
| + } else if (opcode == kArmStrh) { |
| + // Strh effectively applies 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. |
| + } |
| + } |
| + return value; |
| +} |
| + |
| } // namespace |
| @@ -339,13 +411,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 +489,8 @@ void InstructionSelector::VisitStore(Node* node) { |
| return; |
| } |
| + value = 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 +533,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); |
| } |
| @@ -481,7 +543,7 @@ void InstructionSelector::VisitCheckedStore(Node* node) { |
| Node* const buffer = node->InputAt(0); |
| Node* const offset = node->InputAt(1); |
| Node* const length = node->InputAt(2); |
| - Node* const value = node->InputAt(3); |
| + Node* value = node->InputAt(3); |
| ArchOpcode opcode = kArchNop; |
| switch (rep) { |
| case MachineRepresentation::kWord8: |
| @@ -507,6 +569,9 @@ void InstructionSelector::VisitCheckedStore(Node* node) { |
| UNREACHABLE(); |
| return; |
| } |
| + |
| + value = SimplifyAndForMaskedStore(this, node, value, opcode); |
| + |
| InstructionOperand offset_operand = g.UseRegister(offset); |
| InstructionOperand length_operand = g.CanBeImmediate(length, kArmCmp) |
| ? g.UseImmediate(length) |
| @@ -545,6 +610,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 +671,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()); |