}
-void Assembler::mul(Register dst, Register src1, Register src2,
- SBit s, Condition cond) {
+void Assembler::mul(Register dst, Register src1, Register src2, SBit s,
+ Condition cond) {
DCHECK(!dst.is(pc) && !src1.is(pc) && !src2.is(pc));
// dst goes in bits 16-19 for this instruction!
- emit(cond | s | dst.code()*B16 | src2.code()*B8 | B7 | B4 | src1.code());
+ emit(cond | s | dst.code() * B16 | src2.code() * B8 | B7 | B4 | src1.code());
+}
+
+
+void Assembler::smmla(Register dst, Register src1, Register src2, Register srcA,
+ Condition cond) {
+ DCHECK(!dst.is(pc) && !src1.is(pc) && !src2.is(pc) && !srcA.is(pc));
+ emit(cond | B26 | B25 | B24 | B22 | B20 | dst.code() * B16 |
+ srcA.code() * B12 | src2.code() * B8 | B4 | src1.code());
+}
+
+
+void Assembler::smmls(Register dst, Register src1, Register src2, Register srcA,
+ Condition cond) {
+ DCHECK(!dst.is(pc) && !src1.is(pc) && !src2.is(pc) && !srcA.is(pc));
+ emit(cond | B26 | B25 | B24 | B22 | B20 | dst.code() * B16 |
+ srcA.code() * B12 | src2.code() * B8 | B7 | B6 | B4 | src1.code());
+}
+
+
+void Assembler::smmul(Register dst, Register src1, Register src2,
+ Condition cond) {
+ DCHECK(!dst.is(pc) && !src1.is(pc) && !src2.is(pc));
+ emit(cond | B26 | B25 | B24 | B22 | B20 | dst.code() * B16 | 0xf * B12 |
+ src2.code() * B8 | B4 | src1.code());
}
void mul(Register dst, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al);
+ void smmla(Register dst, Register src1, Register src2, Register srcA,
+ Condition cond = al);
+
+ void smmls(Register dst, Register src1, Register src2, Register srcA,
+ Condition cond = al);
+
+ void smmul(Register dst, Register src1, Register src2, Condition cond = al);
+
void smlal(Register dstL, Register dstH, Register src1, Register src2,
SBit s = LeaveCC, Condition cond = al);
break;
}
case db_x: {
+ if (instr->Bits(22, 20) == 0x5) {
+ if (instr->Bits(7, 4) == 0xd) {
+ // SMMLS (in V8 notation matching ARM ISA format)
+ Format(instr, "smmls'cond 'rn, 'rm, 'rs, 'rd");
+ break;
+ }
+ if (instr->Bits(7, 4) == 0x1) {
+ if (instr->Bits(15, 12) == 0xF) {
+ Format(instr, "smmul'cond 'rn, 'rm, 'rs");
+ } else {
+ // SMMLA (in V8 notation matching ARM ISA format)
+ Format(instr, "smmla'cond 'rn, 'rm, 'rs, 'rd");
+ }
+ break;
+ }
+ }
if (FLAG_enable_sudiv) {
if (instr->Bits(5, 4) == 0x1) {
if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) {
DCHECK(!dividend.is(ip));
DCHECK(!result.is(ip));
base::MagicNumbersForDivision<uint32_t> mag =
- base::SignedDivisionByConstant(static_cast<uint32_t>(divisor));
+ base::SignedDivisionByConstant(bit_cast<uint32_t>(divisor));
mov(ip, Operand(mag.multiplier));
- smull(ip, result, dividend, ip);
- bool neg = (mag.multiplier & (static_cast<uint32_t>(1) << 31)) != 0;
+ bool neg = (mag.multiplier & (1U << 31)) != 0;
if (divisor > 0 && neg) {
- add(result, result, Operand(dividend));
- }
- if (divisor < 0 && !neg && mag.multiplier > 0) {
- sub(result, result, Operand(dividend));
+ smmla(result, dividend, ip, dividend);
+ } else {
+ smmul(result, dividend, ip);
+ if (divisor < 0 && !neg && mag.multiplier > 0) {
+ sub(result, result, Operand(dividend));
+ }
}
if (mag.shift > 0) mov(result, Operand(result, ASR, mag.shift));
add(result, result, Operand(dividend, LSR, 31));
}
-
-} } // namespace v8::internal
+} // namespace internal
+} // namespace v8
#endif // V8_TARGET_ARCH_ARM
break;
}
case db_x: {
+ if (instr->Bits(22, 20) == 0x5) {
+ if (instr->Bits(7, 4) == 0xd) {
+ // SMMLS (in V8 notation matching ARM ISA format)
+ // Format(instr, "smmls'cond 'rn, 'rm, 'rs, 'rd");
+ int rm = instr->RmValue();
+ int32_t rm_val = get_register(rm);
+ int rs = instr->RsValue();
+ int32_t rs_val = get_register(rs);
+ int rd = instr->RdValue();
+ int32_t rd_val = get_register(rd);
+ rn_val = base::bits::SignedMulHighAndSub32(rm_val, rs_val, rd_val);
+ set_register(rn, rn_val);
+ return;
+ }
+ if (instr->Bits(7, 4) == 0x1) {
+ int rm = instr->RmValue();
+ int32_t rm_val = get_register(rm);
+ int rs = instr->RsValue();
+ int32_t rs_val = get_register(rs);
+ if (instr->Bits(15, 12) == 0xF) {
+ // SMMUL (in V8 notation matching ARM ISA format)
+ // Format(instr, "smmul'cond 'rn, 'rm, 'rs");
+ rn_val = base::bits::SignedMulHigh32(rm_val, rs_val);
+ } else {
+ // SMMLA (in V8 notation matching ARM ISA format)
+ // Format(instr, "smmla'cond 'rn, 'rm, 'rs, 'rd");
+ int rd = instr->RdValue();
+ int32_t rd_val = get_register(rd);
+ rn_val = base::bits::SignedMulHighAndAdd32(rm_val, rs_val, rd_val);
+ }
+ set_register(rn, rn_val);
+ return;
+ }
+ }
if (FLAG_enable_sudiv) {
if (instr->Bits(5, 4) == 0x1) {
if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) {
return value + 1;
}
+
+int32_t SignedMulHigh32(int32_t lhs, int32_t rhs) {
+ int64_t const value = static_cast<int64_t>(lhs) * static_cast<int64_t>(rhs);
+ return bit_cast<int32_t, uint32_t>(bit_cast<uint64_t>(value) >> 32u);
+}
+
+
+int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs, int32_t acc) {
+ return bit_cast<int32_t>(bit_cast<uint32_t>(acc) +
+ bit_cast<uint32_t>(SignedMulHigh32(lhs, rhs)));
+}
+
+
+int32_t SignedMulHighAndSub32(int32_t lhs, int32_t rhs, int32_t acc) {
+ return bit_cast<int32_t>(bit_cast<uint32_t>(acc) -
+ bit_cast<uint32_t>(SignedMulHigh32(lhs, rhs)));
+}
+
+
} // namespace bits
} // namespace base
} // namespace v8
#endif
}
+
+// SignedMulHigh32(lhs, rhs) multiplies two signed 32-bit values |lhs| and
+// |rhs|, extracts the most significant 32 bits of the result, and returns
+// those.
+int32_t SignedMulHigh32(int32_t lhs, int32_t rhs);
+
+
+// SignedMulHighAndAdd32(lhs, rhs, acc) multiplies two signed 32-bit values
+// |lhs| and |rhs|, extracts the most significant 32 bits of the result, and
+// adds the accumulate value |acc|.
+int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs, int32_t acc);
+
+
+// SignedMulHighAndAdd32(lhs, rhs, acc) multiplies two signed 32-bit values
+// |lhs| and |rhs|, extracts the most significant 32 bits of the result, and
+// subtracts it from the accumulate value |acc|.
+int32_t SignedMulHighAndSub32(int32_t lhs, int32_t rhs, int32_t acc);
+
} // namespace bits
} // namespace base
} // namespace v8
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <iostream> // NOLINT(readability/streams)
+
#include "src/v8.h"
#include "test/cctest/cctest.h"
#include "src/factory.h"
#include "src/ostreams.h"
+using namespace v8::base;
using namespace v8::internal;
#undef TEST_SDIV
+TEST(smmla) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ smmla(r1, r1, r2, r3);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt(), z = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, z, 0);
+ CHECK_EQ(bits::SignedMulHighAndAdd32(x, y, z), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(smmls) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ smmls(r1, r1, r2, r3);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt(), z = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, z, 0);
+ CHECK_EQ(bits::SignedMulHighAndSub32(x, y, z), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(smmul) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ smmul(r1, r1, r2);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0);
+ CHECK_EQ(bits::SignedMulHigh32(x, y), r);
+ USE(dummy);
+ }
+}
+
+
TEST(code_relative_offset) {
// Test extracting the offset of a label from the beginning of the code
// in a register.
"e6cf3474 uxtb16 r3, r4, ror #8");
}
+ COMPARE(smmls(r0, r1, r2, r3), "e75032d1 smmls r0, r1, r2, r3");
+ COMPARE(smmls(r10, r9, r8, r7), "e75a78d9 smmls r10, r9, r8, r7");
+
+ COMPARE(smmla(r0, r1, r2, r3), "e7503211 smmla r0, r1, r2, r3");
+ COMPARE(smmla(r10, r9, r8, r7), "e75a7819 smmla r10, r9, r8, r7");
+
+ COMPARE(smmul(r0, r1, r2), "e750f211 smmul r0, r1, r2");
+ COMPARE(smmul(r8, r9, r10), "e758fa19 smmul r8, r9, r10");
+
VERIFY_RUN();
}
}
}
+
+TEST(Bits, SignedMulHigh32) {
+ EXPECT_EQ(0, SignedMulHigh32(0, 0));
+ TRACED_FORRANGE(int32_t, i, 1, 50) {
+ TRACED_FORRANGE(int32_t, j, 1, i) { EXPECT_EQ(0, SignedMulHigh32(i, j)); }
+ }
+ EXPECT_EQ(-1073741824, SignedMulHigh32(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::min()));
+ EXPECT_EQ(-1073741824, SignedMulHigh32(std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max()));
+ EXPECT_EQ(1, SignedMulHigh32(1024 * 1024 * 1024, 4));
+ EXPECT_EQ(2, SignedMulHigh32(8 * 1024, 1024 * 1024));
+}
+
+
+TEST(Bits, SignedMulHighAndAdd32) {
+ TRACED_FORRANGE(int32_t, i, 1, 50) {
+ EXPECT_EQ(i, SignedMulHighAndAdd32(0, 0, i));
+ TRACED_FORRANGE(int32_t, j, 1, i) {
+ EXPECT_EQ(i, SignedMulHighAndAdd32(j, j, i));
+ }
+ EXPECT_EQ(i + 1, SignedMulHighAndAdd32(1024 * 1024 * 1024, 4, i));
+ }
+}
+
+
+TEST(Bits, SignedMulHighAndSub32) {
+ TRACED_FORRANGE(int32_t, i, 1, 50) {
+ EXPECT_EQ(i, SignedMulHighAndSub32(0, 0, i));
+ TRACED_FORRANGE(int32_t, j, 1, i) {
+ EXPECT_EQ(i, SignedMulHighAndSub32(j, j, i));
+ }
+ EXPECT_EQ(i - 1, SignedMulHighAndSub32(1024 * 1024 * 1024, 4, i));
+ }
+}
+
} // namespace bits
} // namespace base
} // namespace v8