ARM64: Use the shifter operand to merge in previous shift instructions.

When possible, we transform sequences of code of the form
    lsl x8, x9, #imm
    add x0, x1, x8
into
    add x0, x1, x9 LSL #imm

R=ulan@chromium.org

Review URL: https://codereview.chromium.org/257203002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21161 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 55c077f9
......@@ -1748,6 +1748,13 @@ class Assembler : public AssemblerBase {
inline static Instr ImmCondCmp(unsigned imm);
inline static Instr Nzcv(StatusFlags nzcv);
static bool IsImmAddSub(int64_t immediate);
static bool IsImmLogical(uint64_t value,
unsigned width,
unsigned* n,
unsigned* imm_s,
unsigned* imm_r);
// MemOperand offset encoding.
inline static Instr ImmLSUnsigned(int imm12);
inline static Instr ImmLS(int imm9);
......@@ -1861,11 +1868,6 @@ class Assembler : public AssemblerBase {
unsigned imm_s,
unsigned imm_r,
LogicalOp op);
static bool IsImmLogical(uint64_t value,
unsigned width,
unsigned* n,
unsigned* imm_s,
unsigned* imm_r);
void ConditionalCompare(const Register& rn,
const Operand& operand,
......@@ -1896,7 +1898,6 @@ class Assembler : public AssemblerBase {
const Operand& operand,
FlagsUpdate S,
AddSubOp op);
static bool IsImmAddSub(int64_t immediate);
static bool IsImmFP32(float imm);
static bool IsImmFP64(double imm);
......
......@@ -89,6 +89,8 @@ const unsigned kZeroRegCode = 31;
const unsigned kJSSPCode = 28;
const unsigned kSPRegInternalCode = 63;
const unsigned kRegCodeMask = 0x1f;
const unsigned kShiftAmountWRegMask = 0x1f;
const unsigned kShiftAmountXRegMask = 0x3f;
// Standard machine types defined by AAPCS64.
const unsigned kByteSize = 8;
const unsigned kByteSizeInBytes = kByteSize >> 3;
......
......@@ -826,6 +826,12 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
if (instr->representation().IsSmiOrInteger32()) {
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LInstruction* shifted_operation = TryDoOpWithShiftedRightOperand(instr);
if (shifted_operation != NULL) {
return shifted_operation;
}
LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
LOperand* right =
UseRegisterOrConstantAtStart(instr->BetterRightOperand());
......@@ -906,6 +912,11 @@ LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) {
ASSERT(instr->right()->representation().Equals(instr->representation()));
ASSERT(instr->CheckFlag(HValue::kTruncatingToInt32));
LInstruction* shifted_operation = TryDoOpWithShiftedRightOperand(instr);
if (shifted_operation != NULL) {
return shifted_operation;
}
LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
LOperand* right =
UseRegisterOrConstantAtStart(instr->BetterRightOperand());
......@@ -2027,6 +2038,117 @@ LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
}
HBitwiseBinaryOperation* LChunkBuilder::CanTransformToShiftedOp(HValue* val,
HValue** left) {
if (!val->representation().IsInteger32()) return NULL;
if (!(val->IsBitwise() || val->IsAdd() || val->IsSub())) return NULL;
HBinaryOperation* hinstr = HBinaryOperation::cast(val);
HValue* hleft = hinstr->left();
HValue* hright = hinstr->right();
ASSERT(hleft->representation().Equals(hinstr->representation()));
ASSERT(hright->representation().Equals(hinstr->representation()));
if ((hright->IsConstant() &&
LikelyFitsImmField(hinstr, HConstant::cast(hright)->Integer32Value())) ||
(hinstr->IsCommutative() && hleft->IsConstant() &&
LikelyFitsImmField(hinstr, HConstant::cast(hleft)->Integer32Value()))) {
// The constant operand will likely fit in the immediate field. We are
// better off with
// lsl x8, x9, #imm
// add x0, x8, #imm2
// than with
// mov x16, #imm2
// add x0, x16, x9 LSL #imm
return NULL;
}
HBitwiseBinaryOperation* shift = NULL;
// TODO(aleram): We will miss situations where a shift operation is used by
// different instructions both as a left and right operands.
if (hright->IsBitwiseBinaryShift() &&
HBitwiseBinaryOperation::cast(hright)->right()->IsConstant()) {
shift = HBitwiseBinaryOperation::cast(hright);
if (left != NULL) {
*left = hleft;
}
} else if (hinstr->IsCommutative() &&
hleft->IsBitwiseBinaryShift() &&
HBitwiseBinaryOperation::cast(hleft)->right()->IsConstant()) {
shift = HBitwiseBinaryOperation::cast(hleft);
if (left != NULL) {
*left = hright;
}
} else {
return NULL;
}
if ((JSShiftAmountFromHConstant(shift->right()) == 0) && shift->IsShr()) {
// Shifts right by zero can deoptimize.
return NULL;
}
return shift;
}
bool LChunkBuilder::ShiftCanBeOptimizedAway(HBitwiseBinaryOperation* shift) {
if (!shift->representation().IsInteger32()) {
return false;
}
for (HUseIterator it(shift->uses()); !it.Done(); it.Advance()) {
if (shift != CanTransformToShiftedOp(it.value())) {
return false;
}
}
return true;
}
LInstruction* LChunkBuilder::TryDoOpWithShiftedRightOperand(
HBinaryOperation* instr) {
HValue* left;
HBitwiseBinaryOperation* shift = CanTransformToShiftedOp(instr, &left);
if ((shift != NULL) && ShiftCanBeOptimizedAway(shift)) {
return DoShiftedBinaryOp(instr, left, shift);
}
return NULL;
}
LInstruction* LChunkBuilder::DoShiftedBinaryOp(
HBinaryOperation* hinstr, HValue* hleft, HBitwiseBinaryOperation* hshift) {
ASSERT(hshift->IsBitwiseBinaryShift());
ASSERT(!hshift->IsShr() || (JSShiftAmountFromHConstant(hshift->right()) > 0));
LTemplateResultInstruction<1>* res;
LOperand* left = UseRegisterAtStart(hleft);
LOperand* right = UseRegisterAtStart(hshift->left());
LOperand* shift_amount = UseConstant(hshift->right());
Shift shift_op;
switch (hshift->opcode()) {
case HValue::kShl: shift_op = LSL; break;
case HValue::kShr: shift_op = LSR; break;
case HValue::kSar: shift_op = ASR; break;
default: UNREACHABLE(); shift_op = NO_SHIFT;
}
if (hinstr->IsBitwise()) {
res = new(zone()) LBitI(left, right, shift_op, shift_amount);
} else if (hinstr->IsAdd()) {
res = new(zone()) LAddI(left, right, shift_op, shift_amount);
} else {
ASSERT(hinstr->IsSub());
res = new(zone()) LSubI(left, right, shift_op, shift_amount);
}
if (hinstr->CheckFlag(HValue::kCanOverflow)) {
AssignEnvironment(res);
}
return DefineAsRegister(res);
}
LInstruction* LChunkBuilder::DoShift(Token::Value op,
HBitwiseBinaryOperation* instr) {
if (instr->representation().IsTagged()) {
......@@ -2038,6 +2160,10 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
if (ShiftCanBeOptimizedAway(instr)) {
return NULL;
}
LOperand* left = instr->representation().IsSmi()
? UseRegister(instr->left())
: UseRegisterAtStart(instr->left());
......@@ -2048,8 +2174,7 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
int constant_value = 0;
if (right_value->IsConstant()) {
right = UseConstant(right_value);
HConstant* constant = HConstant::cast(right_value);
constant_value = constant->Integer32Value() & 0x1f;
constant_value = JSShiftAmountFromHConstant(right_value);
} else {
right = UseRegisterAtStart(right_value);
if (op == Token::ROR) {
......@@ -2311,6 +2436,12 @@ LInstruction* LChunkBuilder::DoSub(HSub* instr) {
if (instr->representation().IsSmiOrInteger32()) {
ASSERT(instr->left()->representation().Equals(instr->representation()));
ASSERT(instr->right()->representation().Equals(instr->representation()));
LInstruction* shifted_operation = TryDoOpWithShiftedRightOperand(instr);
if (shifted_operation != NULL) {
return shifted_operation;
}
LOperand *left;
if (instr->left()->IsConstant() &&
(HConstant::cast(instr->left())->Integer32Value() == 0)) {
......
......@@ -565,7 +565,14 @@ class LAddE V8_FINAL : public LTemplateInstruction<1, 2, 0> {
class LAddI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
public:
LAddI(LOperand* left, LOperand* right) {
LAddI(LOperand* left, LOperand* right)
: shift_(NO_SHIFT), shift_amount_(0) {
inputs_[0] = left;
inputs_[1] = right;
}
LAddI(LOperand* left, LOperand* right, Shift shift, LOperand* shift_amount)
: shift_(shift), shift_amount_(shift_amount) {
inputs_[0] = left;
inputs_[1] = right;
}
......@@ -573,8 +580,15 @@ class LAddI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
Shift shift() const { return shift_; }
LOperand* shift_amount() const { return shift_amount_; }
DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
DECLARE_HYDROGEN_ACCESSOR(Add)
protected:
Shift shift_;
LOperand* shift_amount_;
};
......@@ -734,7 +748,14 @@ class LBoundsCheck V8_FINAL : public LTemplateInstruction<0, 2, 0> {
class LBitI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
public:
LBitI(LOperand* left, LOperand* right) {
LBitI(LOperand* left, LOperand* right)
: shift_(NO_SHIFT), shift_amount_(0) {
inputs_[0] = left;
inputs_[1] = right;
}
LBitI(LOperand* left, LOperand* right, Shift shift, LOperand* shift_amount)
: shift_(shift), shift_amount_(shift_amount) {
inputs_[0] = left;
inputs_[1] = right;
}
......@@ -742,10 +763,17 @@ class LBitI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
Shift shift() const { return shift_; }
LOperand* shift_amount() const { return shift_amount_; }
Token::Value op() const { return hydrogen()->op(); }
DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i")
DECLARE_HYDROGEN_ACCESSOR(Bitwise)
protected:
Shift shift_;
LOperand* shift_amount_;
};
......@@ -2730,7 +2758,14 @@ class LStoreGlobalCell V8_FINAL : public LTemplateInstruction<0, 1, 2> {
class LSubI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
public:
LSubI(LOperand* left, LOperand* right) {
LSubI(LOperand* left, LOperand* right)
: shift_(NO_SHIFT), shift_amount_(0) {
inputs_[0] = left;
inputs_[1] = right;
}
LSubI(LOperand* left, LOperand* right, Shift shift, LOperand* shift_amount)
: shift_(shift), shift_amount_(shift_amount) {
inputs_[0] = left;
inputs_[1] = right;
}
......@@ -2738,8 +2773,15 @@ class LSubI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
Shift shift() const { return shift_; }
LOperand* shift_amount() const { return shift_amount_; }
DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i")
DECLARE_HYDROGEN_ACCESSOR(Sub)
protected:
Shift shift_;
LOperand* shift_amount_;
};
......@@ -3080,6 +3122,39 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
void VisitInstruction(HInstruction* current);
void DoBasicBlock(HBasicBlock* block);
int JSShiftAmountFromHConstant(HValue* constant) {
return HConstant::cast(constant)->Integer32Value() & 0x1f;
}
bool LikelyFitsImmField(HInstruction* instr, int imm) {
if (instr->IsAdd() || instr->IsSub()) {
return Assembler::IsImmAddSub(imm) || Assembler::IsImmAddSub(-imm);
} else {
ASSERT(instr->IsBitwise());
unsigned unused_n, unused_imm_s, unused_imm_r;
return Assembler::IsImmLogical(imm, kWRegSizeInBits,
&unused_n, &unused_imm_s, &unused_imm_r);
}
}
// Indicates if a sequence of the form
// lsl x8, x9, #imm
// add x0, x1, x8
// can be replaced with:
// add x0, x1, x9 LSL #imm
// If this is not possible, the function returns NULL. Otherwise it returns a
// pointer to the shift instruction that would be optimized away.
HBitwiseBinaryOperation* CanTransformToShiftedOp(HValue* val,
HValue** left = NULL);
// Checks if all uses of the shift operation can optimize it away.
bool ShiftCanBeOptimizedAway(HBitwiseBinaryOperation* shift);
// Attempts to merge the binary operation and an eventual previous shift
// operation into a single operation. Returns the merged instruction on
// success, and NULL otherwise.
LInstruction* TryDoOpWithShiftedRightOperand(HBinaryOperation* op);
LInstruction* DoShiftedBinaryOp(HBinaryOperation* instr,
HValue* left,
HBitwiseBinaryOperation* shift);
LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr);
LInstruction* DoArithmeticD(Token::Value op,
HArithmeticBinaryOperation* instr);
......
......@@ -1276,6 +1276,21 @@ Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const {
}
template<class LI>
Operand LCodeGen::ToShiftedRightOperand32(LOperand* right, LI* shift_info,
IntegerSignedness signedness) {
if (shift_info->shift() == NO_SHIFT) {
return (signedness == SIGNED_INT32) ? ToOperand32I(right)
: ToOperand32U(right);
} else {
return Operand(
ToRegister32(right),
shift_info->shift(),
JSShiftAmountFromLConstant(shift_info->shift_amount()));
}
}
bool LCodeGen::IsSmi(LConstantOperand* op) const {
return chunk_->LookupLiteralRepresentation(op).IsSmi();
}
......@@ -1472,7 +1487,8 @@ void LCodeGen::DoAddI(LAddI* instr) {
bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
Register result = ToRegister32(instr->result());
Register left = ToRegister32(instr->left());
Operand right = ToOperand32I(instr->right());
Operand right = ToShiftedRightOperand32I(instr->right(), instr);
if (can_overflow) {
__ Adds(result, left, right);
DeoptimizeIf(vs, instr->environment());
......@@ -1750,7 +1766,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
void LCodeGen::DoBitI(LBitI* instr) {
Register result = ToRegister32(instr->result());
Register left = ToRegister32(instr->left());
Operand right = ToOperand32U(instr->right());
Operand right = ToShiftedRightOperand32U(instr->right(), instr);
switch (instr->op()) {
case Token::BIT_AND: __ And(result, left, right); break;
......@@ -4827,7 +4843,7 @@ void LCodeGen::DoShiftI(LShiftI* instr) {
}
} else {
ASSERT(right_op->IsConstantOperand());
int shift_count = ToInteger32(LConstantOperand::cast(right_op)) & 0x1f;
int shift_count = JSShiftAmountFromLConstant(right_op);
if (shift_count == 0) {
if ((instr->op() == Token::SHR) && instr->can_deopt()) {
DeoptimizeIfNegative(left, instr->environment());
......@@ -4890,7 +4906,7 @@ void LCodeGen::DoShiftS(LShiftS* instr) {
}
} else {
ASSERT(right_op->IsConstantOperand());
int shift_count = ToInteger32(LConstantOperand::cast(right_op)) & 0x1f;
int shift_count = JSShiftAmountFromLConstant(right_op);
if (shift_count == 0) {
if ((instr->op() == Token::SHR) && instr->can_deopt()) {
DeoptimizeIfNegative(left, instr->environment());
......@@ -5483,7 +5499,8 @@ void LCodeGen::DoSubI(LSubI* instr) {
bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
Register result = ToRegister32(instr->result());
Register left = ToRegister32(instr->left());
Operand right = ToOperand32I(instr->right());
Operand right = ToShiftedRightOperand32I(instr->right(), instr);
if (can_overflow) {
__ Subs(result, left, right);
DeoptimizeIf(vs, instr->environment());
......
......@@ -81,6 +81,7 @@ class LCodeGen: public LCodeGenBase {
// information on it.
void FinishCode(Handle<Code> code);
enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
// Support for converting LOperands to assembler types.
// LOperand must be a register.
Register ToRegister(LOperand* op) const;
......@@ -93,6 +94,25 @@ class LCodeGen: public LCodeGenBase {
StackMode stack_mode = kCanUseStackPointer) const;
Handle<Object> ToHandle(LConstantOperand* op) const;
template<class LI>
Operand ToShiftedRightOperand32I(LOperand* right,
LI* shift_info) {
return ToShiftedRightOperand32(right, shift_info, SIGNED_INT32);
}
template<class LI>
Operand ToShiftedRightOperand32U(LOperand* right,
LI* shift_info) {
return ToShiftedRightOperand32(right, shift_info, UNSIGNED_INT32);
}
template<class LI>
Operand ToShiftedRightOperand32(LOperand* right,
LI* shift_info,
IntegerSignedness signedness);
int JSShiftAmountFromLConstant(LOperand* constant) {
return ToInteger32(LConstantOperand::cast(constant)) & 0x1f;
}
// TODO(jbramley): Examine these helpers and check that they make sense.
// IsInteger32Constant returns true for smi constants, for example.
bool IsInteger32Constant(LConstantOperand* op) const;
......@@ -122,7 +142,6 @@ class LCodeGen: public LCodeGenBase {
Label* exit,
Label* allocation_entry);
enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
void DoDeferredNumberTagU(LInstruction* instr,
LOperand* value,
LOperand* temp1,
......
......@@ -1979,7 +1979,8 @@ void Simulator::VisitDataProcessing2Source(Instruction* instr) {
if (shift_op != NO_SHIFT) {
// Shift distance encoded in the least-significant five/six bits of the
// register.
int mask = (instr->SixtyFourBits() == 1) ? 0x3f : 0x1f;
int mask = (instr->SixtyFourBits() == 1) ? kShiftAmountXRegMask
: kShiftAmountWRegMask;
unsigned shift = wreg(instr->Rm()) & mask;
result = ShiftOperand(reg_size, reg(reg_size, instr->Rn()), shift_op,
shift);
......
......@@ -655,6 +655,10 @@ class HValue : public ZoneObject {
HYDROGEN_ABSTRACT_INSTRUCTION_LIST(DECLARE_PREDICATE)
#undef DECLARE_PREDICATE
bool IsBitwiseBinaryShift() {
return IsShl() || IsShr() || IsSar();
}
HValue(HType type = HType::Tagged())
: block_(NULL),
id_(kNoNumber),
......
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