2010-04-29 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
Add codegen support for unsigned right shift
https://bugs.webkit.org/show_bug.cgi?id=38375
Expose unsigned right shift in the macro assembler, and make use of it
from the jit. Currently if the result is outside the range 0..2^31-1
we simply fall back to the slow case, even in JSVALUE64 and JSVALUE32_64
where technically we could still return an immediate value.
* assembler/MacroAssemblerARM.h:
(JSC::MacroAssemblerARM::urshift32):
* assembler/MacroAssemblerARMv7.h:
(JSC::MacroAssemblerARMv7::urshift32):
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::urshift32):
* assembler/X86Assembler.h:
(JSC::X86Assembler::):
(JSC::X86Assembler::shrl_i8r):
(JSC::X86Assembler::shrl_CLr):
Add unsigned right shift to the x86 assembler
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
op_rshift no longer simply get thrown to a stub function
* jit/JIT.h:
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_urshift):
(JSC::JIT::emitSlow_op_urshift):
JSVALUE32 and JSVALUE64 implementation. Only supports
double lhs in JSVALUE64.
* jit/JITArithmetic32_64.cpp:
(JSC::JIT::emit_op_rshift):
(JSC::JIT::emitSlow_op_rshift):
(JSC::JIT::emit_op_urshift):
(JSC::JIT::emitSlow_op_urshift):
Refactor right shift code to have shared implementation between signed
and unsigned versions.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@58562 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index d19ff26..a8ee46f 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -2,6 +2,47 @@
Reviewed by Gavin Barraclough.
+ Add codegen support for unsigned right shift
+ https://bugs.webkit.org/show_bug.cgi?id=38375
+
+ Expose unsigned right shift in the macro assembler, and make use of it
+ from the jit. Currently if the result is outside the range 0..2^31-1
+ we simply fall back to the slow case, even in JSVALUE64 and JSVALUE32_64
+ where technically we could still return an immediate value.
+
+ * assembler/MacroAssemblerARM.h:
+ (JSC::MacroAssemblerARM::urshift32):
+ * assembler/MacroAssemblerARMv7.h:
+ (JSC::MacroAssemblerARMv7::urshift32):
+ * assembler/MacroAssemblerX86Common.h:
+ (JSC::MacroAssemblerX86Common::urshift32):
+ * assembler/X86Assembler.h:
+ (JSC::X86Assembler::):
+ (JSC::X86Assembler::shrl_i8r):
+ (JSC::X86Assembler::shrl_CLr):
+ Add unsigned right shift to the x86 assembler
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ (JSC::JIT::privateCompileSlowCases):
+ op_rshift no longer simply get thrown to a stub function
+ * jit/JIT.h:
+ * jit/JITArithmetic.cpp:
+ (JSC::JIT::emit_op_urshift):
+ (JSC::JIT::emitSlow_op_urshift):
+ JSVALUE32 and JSVALUE64 implementation. Only supports
+ double lhs in JSVALUE64.
+ * jit/JITArithmetic32_64.cpp:
+ (JSC::JIT::emit_op_rshift):
+ (JSC::JIT::emitSlow_op_rshift):
+ (JSC::JIT::emit_op_urshift):
+ (JSC::JIT::emitSlow_op_urshift):
+ Refactor right shift code to have shared implementation between signed
+ and unsigned versions.
+
+2010-04-29 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Gavin Barraclough.
+
Handle double on righthand side of a right shift
https://bugs.webkit.org/show_bug.cgi?id=38363
diff --git a/JavaScriptCore/assembler/MacroAssemblerARM.h b/JavaScriptCore/assembler/MacroAssemblerARM.h
index 34e60c6..c9c70d1 100644
--- a/JavaScriptCore/assembler/MacroAssemblerARM.h
+++ b/JavaScriptCore/assembler/MacroAssemblerARM.h
@@ -178,6 +178,20 @@
{
m_assembler.movs_r(dest, m_assembler.asr(dest, imm.m_value & 0x1f));
}
+
+ void urshift32(RegisterID shift_amount, RegisterID dest)
+ {
+ ARMWord w = ARMAssembler::getOp2(0x1f);
+ ASSERT(w != ARMAssembler::INVALID_IMM);
+ m_assembler.and_r(ARMRegisters::S0, shift_amount, w);
+
+ m_assembler.movs_r(dest, m_assembler.lsr_r(dest, ARMRegisters::S0));
+ }
+
+ void urshift32(Imm32 imm, RegisterID dest)
+ {
+ m_assembler.movs_r(dest, m_assembler.lsr(dest, imm.m_value & 0x1f));
+ }
void sub32(RegisterID src, RegisterID dest)
{
diff --git a/JavaScriptCore/assembler/MacroAssemblerARMv7.h b/JavaScriptCore/assembler/MacroAssemblerARMv7.h
index 3c58b93..8b422ed 100644
--- a/JavaScriptCore/assembler/MacroAssemblerARMv7.h
+++ b/JavaScriptCore/assembler/MacroAssemblerARMv7.h
@@ -257,6 +257,21 @@
{
m_assembler.asr(dest, dest, imm.m_value & 0x1f);
}
+
+ void urshift32(RegisterID shift_amount, RegisterID dest)
+ {
+ // Clamp the shift to the range 0..31
+ ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f);
+ ASSERT(armImm.isValid());
+ m_assembler.ARM_and(dataTempRegister, shift_amount, armImm);
+
+ m_assembler.lsr(dest, dest, dataTempRegister);
+ }
+
+ void urshift32(Imm32 imm, RegisterID dest)
+ {
+ m_assembler.lsr(dest, dest, imm.m_value & 0x1f);
+ }
void sub32(RegisterID src, RegisterID dest)
{
diff --git a/JavaScriptCore/assembler/MacroAssemblerX86Common.h b/JavaScriptCore/assembler/MacroAssemblerX86Common.h
index 9522511..0b32e5d 100644
--- a/JavaScriptCore/assembler/MacroAssemblerX86Common.h
+++ b/JavaScriptCore/assembler/MacroAssemblerX86Common.h
@@ -249,6 +249,33 @@
{
m_assembler.sarl_i8r(imm.m_value, dest);
}
+
+ void urshift32(RegisterID shift_amount, RegisterID dest)
+ {
+ // On x86 we can only shift by ecx; if asked to shift by another register we'll
+ // need rejig the shift amount into ecx first, and restore the registers afterwards.
+ if (shift_amount != X86Registers::ecx) {
+ swap(shift_amount, X86Registers::ecx);
+
+ // E.g. transform "shrl %eax, %eax" -> "xchgl %eax, %ecx; shrl %ecx, %ecx; xchgl %eax, %ecx"
+ if (dest == shift_amount)
+ m_assembler.shrl_CLr(X86Registers::ecx);
+ // E.g. transform "shrl %eax, %ecx" -> "xchgl %eax, %ecx; shrl %ecx, %eax; xchgl %eax, %ecx"
+ else if (dest == X86Registers::ecx)
+ m_assembler.shrl_CLr(shift_amount);
+ // E.g. transform "shrl %eax, %ebx" -> "xchgl %eax, %ecx; shrl %ecx, %ebx; xchgl %eax, %ecx"
+ else
+ m_assembler.shrl_CLr(dest);
+
+ swap(shift_amount, X86Registers::ecx);
+ } else
+ m_assembler.shrl_CLr(dest);
+ }
+
+ void urshift32(Imm32 imm, RegisterID dest)
+ {
+ m_assembler.shrl_i8r(imm.m_value, dest);
+ }
void sub32(RegisterID src, RegisterID dest)
{
diff --git a/JavaScriptCore/assembler/X86Assembler.h b/JavaScriptCore/assembler/X86Assembler.h
index c833bbc..20d72f5 100644
--- a/JavaScriptCore/assembler/X86Assembler.h
+++ b/JavaScriptCore/assembler/X86Assembler.h
@@ -201,6 +201,7 @@
GROUP1A_OP_POP = 0,
GROUP2_OP_SHL = 4,
+ GROUP2_OP_SHR = 5,
GROUP2_OP_SAR = 7,
GROUP3_OP_TEST = 0,
@@ -673,6 +674,21 @@
{
m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst);
}
+
+ void shrl_i8r(int imm, RegisterID dst)
+ {
+ if (imm == 1)
+ m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst);
+ else {
+ m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst);
+ m_formatter.immediate8(imm);
+ }
+ }
+
+ void shrl_CLr(RegisterID dst)
+ {
+ m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst);
+ }
void shll_i8r(int imm, RegisterID dst)
{
diff --git a/JavaScriptCore/jit/JIT.cpp b/JavaScriptCore/jit/JIT.cpp
index ec23d8c..a5923f5 100644
--- a/JavaScriptCore/jit/JIT.cpp
+++ b/JavaScriptCore/jit/JIT.cpp
@@ -200,7 +200,6 @@
DEFINE_BINARY_OP(op_in)
DEFINE_BINARY_OP(op_less)
DEFINE_BINARY_OP(op_lesseq)
- DEFINE_BINARY_OP(op_urshift)
DEFINE_UNARY_OP(op_is_boolean)
DEFINE_UNARY_OP(op_is_function)
DEFINE_UNARY_OP(op_is_number)
@@ -301,6 +300,7 @@
DEFINE_OP(op_resolve_with_base)
DEFINE_OP(op_ret)
DEFINE_OP(op_rshift)
+ DEFINE_OP(op_urshift)
DEFINE_OP(op_sret)
DEFINE_OP(op_strcat)
DEFINE_OP(op_stricteq)
@@ -427,6 +427,7 @@
DEFINE_SLOWCASE_OP(op_resolve_global)
#endif
DEFINE_SLOWCASE_OP(op_rshift)
+ DEFINE_SLOWCASE_OP(op_urshift)
DEFINE_SLOWCASE_OP(op_stricteq)
DEFINE_SLOWCASE_OP(op_sub)
DEFINE_SLOWCASE_OP(op_to_jsnumber)
diff --git a/JavaScriptCore/jit/JIT.h b/JavaScriptCore/jit/JIT.h
index 22e7aba..dbd311b 100644
--- a/JavaScriptCore/jit/JIT.h
+++ b/JavaScriptCore/jit/JIT.h
@@ -734,6 +734,7 @@
void emit_op_to_jsnumber(Instruction*);
void emit_op_to_primitive(Instruction*);
void emit_op_unexpected_load(Instruction*);
+ void emit_op_urshift(Instruction*);
#if ENABLE(JIT_OPTIMIZE_MOD)
void softModulo();
#endif
@@ -784,6 +785,11 @@
void emitSlow_op_sub(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_to_jsnumber(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_to_primitive(Instruction*, Vector<SlowCaseEntry>::iterator&);
+ void emitSlow_op_urshift(Instruction*, Vector<SlowCaseEntry>::iterator&);
+
+
+ void emitRightShift(Instruction*, bool isUnsigned);
+ void emitRightShiftSlowCase(Instruction*, Vector<SlowCaseEntry>::iterator&, bool isUnsigned);
/* These functions are deprecated: Please use JITStubCall instead. */
void emitPutJITStubArg(RegisterID src, unsigned argumentNumber);
diff --git a/JavaScriptCore/jit/JITArithmetic.cpp b/JavaScriptCore/jit/JITArithmetic.cpp
index 61d0e1f..d45859e 100644
--- a/JavaScriptCore/jit/JITArithmetic.cpp
+++ b/JavaScriptCore/jit/JITArithmetic.cpp
@@ -187,6 +187,118 @@
stubCall.call(result);
}
+void JIT::emit_op_urshift(Instruction* currentInstruction)
+{
+ unsigned dst = currentInstruction[1].u.operand;
+ unsigned op1 = currentInstruction[2].u.operand;
+ unsigned op2 = currentInstruction[3].u.operand;
+
+ // Slow case of urshift makes assumptions about what registers hold the
+ // shift arguments, so any changes must be updated there as well.
+ if (isOperandConstantImmediateInt(op2)) {
+ emitGetVirtualRegister(op1, regT0);
+ emitJumpSlowCaseIfNotImmediateInteger(regT0);
+ emitFastArithImmToInt(regT0);
+ int shift = getConstantOperand(op2).asInt32();
+ if (shift)
+ urshift32(Imm32(shift & 0x1f), regT0);
+ // unsigned shift < 0 or shift = k*2^32 may result in (essentially)
+ // a toUint conversion, which can result in a value we can represent
+ // as an immediate int.
+ if (shift < 0 || !(shift & 31))
+ addSlowCase(branch32(LessThan, regT0, Imm32(0)));
+#if USE(JSVALUE32)
+ addSlowCase(branchAdd32(Overflow, regT0, regT0));
+ signExtend32ToPtr(regT0, regT0);
+#endif
+ emitFastArithReTagImmediate(regT0, regT0);
+ emitPutVirtualRegister(dst, regT0);
+ return;
+ }
+ emitGetVirtualRegisters(op1, regT0, op2, regT1);
+ if (!isOperandConstantImmediateInt(op1))
+ emitJumpSlowCaseIfNotImmediateInteger(regT0);
+ emitJumpSlowCaseIfNotImmediateInteger(regT1);
+ emitFastArithImmToInt(regT0);
+ emitFastArithImmToInt(regT1);
+ urshift32(regT1, regT0);
+ addSlowCase(branch32(LessThan, regT0, Imm32(0)));
+#if USE(JSVALUE32)
+ addSlowCase(branchAdd32(Overflow, regT0, regT0));
+ signExtend32ToPtr(regT0, regT0);
+#endif
+ emitFastArithReTagImmediate(regT0, regT0);
+ emitPutVirtualRegister(dst, regT0);
+}
+
+void JIT::emitSlow_op_urshift(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ unsigned dst = currentInstruction[1].u.operand;
+ unsigned op1 = currentInstruction[2].u.operand;
+ unsigned op2 = currentInstruction[3].u.operand;
+ if (isOperandConstantImmediateInt(op2)) {
+ int shift = getConstantOperand(op2).asInt32();
+ // op1 = regT0
+ linkSlowCase(iter); // int32 check
+#if USE(JSVALUE64)
+ if (supportsFloatingPointTruncate()) {
+ JumpList failures;
+ failures.append(emitJumpIfNotImmediateNumber(regT0)); // op1 is not a double
+ addPtr(tagTypeNumberRegister, regT0);
+ movePtrToDouble(regT0, fpRegT0);
+ failures.append(branchTruncateDoubleToInt32(fpRegT0, regT0));
+ if (shift)
+ urshift32(Imm32(shift & 0x1f), regT0);
+ if (shift < 0 || !(shift & 31))
+ failures.append(branch32(LessThan, regT0, Imm32(0)));
+ emitFastArithReTagImmediate(regT0, regT0);
+ emitPutVirtualRegister(dst, regT0);
+ emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_rshift));
+ failures.link(this);
+ }
+#endif // JSVALUE64
+ if (shift < 0 || !(shift & 31))
+ linkSlowCase(iter); // failed to box in hot path
+#if USE(JSVALUE32)
+ linkSlowCase(iter); // Couldn't box result
+#endif
+ } else {
+ // op1 = regT0
+ // op2 = regT1
+ if (!isOperandConstantImmediateInt(op1)) {
+ linkSlowCase(iter); // int32 check -- op1 is not an int
+#if USE(JSVALUE64)
+ if (supportsFloatingPointTruncate()) {
+ JumpList failures;
+ failures.append(emitJumpIfNotImmediateNumber(regT0)); // op1 is not a double
+ addPtr(tagTypeNumberRegister, regT0);
+ movePtrToDouble(regT0, fpRegT0);
+ failures.append(branchTruncateDoubleToInt32(fpRegT0, regT0));
+ failures.append(emitJumpIfNotImmediateInteger(regT1)); // op2 is not an int
+ emitFastArithImmToInt(regT1);
+ urshift32(regT1, regT0);
+ failures.append(branch32(LessThan, regT0, Imm32(0)));
+ emitFastArithReTagImmediate(regT0, regT0);
+ emitPutVirtualRegister(dst, regT0);
+ emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_rshift));
+ failures.link(this);
+ }
+#endif
+ }
+
+ linkSlowCase(iter); // int32 check - op2 is not an int
+ linkSlowCase(iter); // Can't represent unsigned result as an immediate
+#if USE(JSVALUE32)
+ linkSlowCase(iter); // Couldn't box result
+#endif
+ }
+
+ JITStubCall stubCall(this, cti_op_urshift);
+ stubCall.addArgument(op1, regT0);
+ stubCall.addArgument(op2, regT1);
+ stubCall.call(dst);
+}
+
void JIT::emit_op_jnless(Instruction* currentInstruction)
{
unsigned op1 = currentInstruction[1].u.operand;
diff --git a/JavaScriptCore/jit/JITArithmetic32_64.cpp b/JavaScriptCore/jit/JITArithmetic32_64.cpp
index dab5e39..177f04c 100644
--- a/JavaScriptCore/jit/JITArithmetic32_64.cpp
+++ b/JavaScriptCore/jit/JITArithmetic32_64.cpp
@@ -314,9 +314,9 @@
stubCall.call(dst);
}
-// RightShift (>>)
+// RightShift (>>) and UnsignedRightShift (>>>) helper
-void JIT::emit_op_rshift(Instruction* currentInstruction)
+void JIT::emitRightShift(Instruction* currentInstruction, bool isUnsigned)
{
unsigned dst = currentInstruction[1].u.operand;
unsigned op1 = currentInstruction[2].u.operand;
@@ -327,7 +327,18 @@
if (isOperandConstantImmediateInt(op2)) {
emitLoad(op1, regT1, regT0);
addSlowCase(branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag)));
- rshift32(Imm32(getConstantOperand(op2).asInt32()), regT0);
+ int shift = getConstantOperand(op2).asInt32();
+ if (isUnsigned) {
+ if (shift)
+ urshift32(Imm32(shift & 0x1f), regT0);
+ // unsigned shift < 0 or shift = k*2^32 may result in (essentially)
+ // a toUint conversion, which can result in a value we can represent
+ // as an immediate int.
+ if (shift < 0 || !(shift & 31))
+ addSlowCase(branch32(LessThan, regT0, Imm32(0)));
+ } else if (shift) { // signed right shift by zero is simply toInt conversion
+ rshift32(Imm32(shift & 0x1f), regT0);
+ }
emitStoreInt32(dst, regT0, dst == op1);
return;
}
@@ -336,28 +347,41 @@
if (!isOperandConstantImmediateInt(op1))
addSlowCase(branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag)));
addSlowCase(branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag)));
- rshift32(regT2, regT0);
+ if (isUnsigned) {
+ urshift32(regT2, regT0);
+ addSlowCase(branch32(LessThan, regT0, Imm32(0)));
+ } else
+ rshift32(regT2, regT0);
emitStoreInt32(dst, regT0, dst == op1 || dst == op2);
}
-void JIT::emitSlow_op_rshift(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+void JIT::emitRightShiftSlowCase(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter, bool isUnsigned)
{
unsigned dst = currentInstruction[1].u.operand;
unsigned op1 = currentInstruction[2].u.operand;
unsigned op2 = currentInstruction[3].u.operand;
if (isOperandConstantImmediateInt(op2)) {
+ int shift = getConstantOperand(op2).asInt32();
// op1 = regT1:regT0
linkSlowCase(iter); // int32 check
if (supportsFloatingPointTruncate()) {
- Jump notDouble = branch32(AboveOrEqual, regT1, Imm32(JSValue::LowestTag));
+ JumpList failures;
+ failures.append(branch32(AboveOrEqual, regT1, Imm32(JSValue::LowestTag)));
emitLoadDouble(op1, fpRegT0);
- Jump truncationFailed = branchTruncateDoubleToInt32(fpRegT0, regT0);
- rshift32(Imm32(getConstantOperand(op2).asInt32()), regT0);
+ failures.append(branchTruncateDoubleToInt32(fpRegT0, regT0));
+ if (isUnsigned) {
+ if (shift)
+ urshift32(Imm32(shift & 0x1f), regT0);
+ if (shift < 0 || !(shift & 31))
+ failures.append(branch32(LessThan, regT0, Imm32(0)));
+ } else if (shift)
+ rshift32(Imm32(shift & 0x1f), regT0);
emitStoreInt32(dst, regT0, dst == op1 || dst == op2);
emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_rshift));
- notDouble.link(this);
- truncationFailed.link(this);
+ failures.link(this);
}
+ if (isUnsigned && (shift < 0 || !(shift & 31)))
+ linkSlowCase(iter); // failed to box in hot path
} else {
// op1 = regT1:regT0
// op2 = regT3:regT2
@@ -368,7 +392,10 @@
emitLoadDouble(op1, fpRegT0);
Jump notInt = branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag)); // op2 is not an int
Jump cantTruncate = branchTruncateDoubleToInt32(fpRegT0, regT0);
- rshift32(regT2, regT0);
+ if (isUnsigned)
+ urshift32(regT2, regT0);
+ else
+ rshift32(regT2, regT0);
emitStoreInt32(dst, regT0, dst == op1 || dst == op2);
emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_rshift));
notDouble.link(this);
@@ -378,14 +405,40 @@
}
linkSlowCase(iter); // int32 check - op2 is not an int
+ if (isUnsigned)
+ linkSlowCase(iter); // Can't represent unsigned result as an immediate
}
- JITStubCall stubCall(this, cti_op_rshift);
+ JITStubCall stubCall(this, isUnsigned ? cti_op_urshift : cti_op_rshift);
stubCall.addArgument(op1);
stubCall.addArgument(op2);
stubCall.call(dst);
}
+// RightShift (>>)
+
+void JIT::emit_op_rshift(Instruction* currentInstruction)
+{
+ emitRightShift(currentInstruction, false);
+}
+
+void JIT::emitSlow_op_rshift(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ emitRightShiftSlowCase(currentInstruction, iter, false);
+}
+
+// UnsignedRightShift (>>>)
+
+void JIT::emit_op_urshift(Instruction* currentInstruction)
+{
+ emitRightShift(currentInstruction, true);
+}
+
+void JIT::emitSlow_op_urshift(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ emitRightShiftSlowCase(currentInstruction, iter, true);
+}
+
// BitAnd (&)
void JIT::emit_op_bitand(Instruction* currentInstruction)