Commit 3e0bf580 authored by Predrag Rudic's avatar Predrag Rudic Committed by Commit Bot

MIPS[64] Implementation of MSA instructions in builtin simulator

This commit is a step toward enabling test-run-wasm-simd tests for MIPS.
36 of those were failing in V8 builtin simulator because some instructions
were not implemented.  Also there are minor fixes to some of the already
implemented instructions.

This commit has only 32-bit implementation. After review I will add
64-bit version.

Bug: 
Change-Id: I25b0cac352db3efb56b922ace64ab2aaef82472d
Reviewed-on: https://chromium-review.googlesource.com/744008Reviewed-by: 's avatarIvica Bogosavljevic <ivica.bogosavljevic@mips.com>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Ivica Bogosavljevic <ivica.bogosavljevic@mips.com>
Cr-Commit-Position: refs/heads/master@{#49439}
parent dec077ae
......@@ -4668,9 +4668,12 @@ void Simulator::DecodeTypeMsaELM() {
DCHECK_EQ(rd_reg(), kMSACSRRegister);
SetResult(sa(), bit_cast<int32_t>(MSACSR_));
break;
case MOVE_V:
UNIMPLEMENTED();
break;
case MOVE_V: {
msa_reg_t ws;
get_msa_register(ws_reg(), &ws);
set_msa_register(wd_reg(), &ws);
TraceMSARegWr(&ws);
} break;
default:
opcode &= kMsaELMMask;
switch (opcode) {
......@@ -4739,7 +4742,50 @@ void Simulator::DecodeTypeMsaELM() {
UNREACHABLE();
}
} break;
case SLDI:
case SLDI: {
uint8_t v[32];
msa_reg_t ws;
msa_reg_t wd;
get_msa_register(ws_reg(), &ws);
get_msa_register(wd_reg(), &wd);
#define SLDI_DF(s, k) \
for (unsigned i = 0; i < s; i++) { \
v[i] = ws.b[s * k + i]; \
v[i + s] = wd.b[s * k + i]; \
} \
for (unsigned i = 0; i < s; i++) { \
wd.b[s * k + i] = v[i + n]; \
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
DCHECK(n < kMSALanesByte);
SLDI_DF(kMSARegSize / sizeof(int8_t) / kBitsPerByte, 0)
break;
case MSA_HALF:
DCHECK(n < kMSALanesHalf);
for (int k = 0; k < 2; ++k) {
SLDI_DF(kMSARegSize / sizeof(int16_t) / kBitsPerByte, k)
}
break;
case MSA_WORD:
DCHECK(n < kMSALanesWord);
for (int k = 0; k < 4; ++k) {
SLDI_DF(kMSARegSize / sizeof(int32_t) / kBitsPerByte, k)
}
break;
case MSA_DWORD:
DCHECK(n < kMSALanesDword);
for (int k = 0; k < 8; ++k) {
SLDI_DF(kMSARegSize / sizeof(int64_t) / kBitsPerByte, k)
}
break;
default:
UNREACHABLE();
}
set_msa_register(wd_reg(), &wd);
TraceMSARegWr(&wd);
} break;
#undef SLDI_DF
case SPLATI:
case INSVE:
UNIMPLEMENTED();
......@@ -4876,6 +4922,7 @@ void Simulator::DecodeTypeMsaBIT() {
default:
UNREACHABLE();
}
#undef MSA_BIT_DF
}
void Simulator::DecodeTypeMsaMI10() {
......@@ -5158,13 +5205,6 @@ T Simulator::Msa3RInstrHelper(uint32_t opcode, T wd, T ws, T wt) {
case DPSUB_U:
case SLD:
case SPLAT:
case PCKEV:
case PCKOD:
case ILVL:
case ILVR:
case ILVEV:
case ILVOD:
case VSHF:
UNIMPLEMENTED();
break;
case SRAR: {
......@@ -5176,16 +5216,95 @@ T Simulator::Msa3RInstrHelper(uint32_t opcode, T wd, T ws, T wt) {
int bit = wt_modulo == 0 ? 0 : (wsu >> (wt_modulo - 1)) & 1;
res = static_cast<T>((wsu >> wt_modulo) + bit);
} break;
default:
UNREACHABLE();
}
return res;
}
template <typename T_int, typename T_reg>
void Msa3RInstrHelper_shuffle(const uint32_t opcode, T_reg ws, T_reg wt,
T_reg wd, const int i, const int num_of_lanes) {
T_int *ws_p, *wt_p, *wd_p;
ws_p = reinterpret_cast<T_int*>(ws);
wt_p = reinterpret_cast<T_int*>(wt);
wd_p = reinterpret_cast<T_int*>(wd);
switch (opcode) {
case PCKEV:
wd_p[i] = wt_p[2 * i];
wd_p[i + num_of_lanes / 2] = ws_p[2 * i];
break;
case PCKOD:
wd_p[i] = wt_p[2 * i + 1];
wd_p[i + num_of_lanes / 2] = ws_p[2 * i + 1];
break;
case ILVL:
wd_p[2 * i] = wt_p[i + num_of_lanes / 2];
wd_p[2 * i + 1] = ws_p[i + num_of_lanes / 2];
break;
case ILVR:
wd_p[2 * i] = wt_p[i];
wd_p[2 * i + 1] = ws_p[i];
break;
case ILVEV:
wd_p[2 * i] = wt_p[2 * i];
wd_p[2 * i + 1] = ws_p[2 * i];
break;
case ILVOD:
wd_p[2 * i] = wt_p[2 * i + 1];
wd_p[2 * i + 1] = ws_p[2 * i + 1];
break;
case VSHF: {
const int mask_not_valid = 0xc0;
const int mask_6_bits = 0x3f;
if ((wd_p[i] & mask_not_valid)) {
wd_p[i] = 0;
} else {
int k = (wd_p[i] & mask_6_bits) % (num_of_lanes * 2);
wd_p[i] = k >= num_of_lanes ? ws_p[k - num_of_lanes] : wt_p[k];
}
} break;
default:
UNREACHABLE();
}
}
template <typename T_int, typename T_smaller_int, typename T_reg>
void Msa3RInstrHelper_horizontal(const uint32_t opcode, T_reg ws, T_reg wt,
T_reg wd, const int i,
const int num_of_lanes) {
typedef typename std::make_unsigned<T_int>::type T_uint;
typedef typename std::make_unsigned<T_smaller_int>::type T_smaller_uint;
T_int* wd_p;
T_smaller_int *ws_p, *wt_p;
ws_p = reinterpret_cast<T_smaller_int*>(ws);
wt_p = reinterpret_cast<T_smaller_int*>(wt);
wd_p = reinterpret_cast<T_int*>(wd);
T_uint* wd_pu;
T_smaller_uint *ws_pu, *wt_pu;
ws_pu = reinterpret_cast<T_smaller_uint*>(ws);
wt_pu = reinterpret_cast<T_smaller_uint*>(wt);
wd_pu = reinterpret_cast<T_uint*>(wd);
switch (opcode) {
case HADD_S:
wd_p[i] =
static_cast<T_int>(ws_p[2 * i + 1]) + static_cast<T_int>(wt_p[2 * i]);
break;
case HADD_U:
wd_pu[i] = static_cast<T_uint>(ws_pu[2 * i + 1]) +
static_cast<T_uint>(wt_pu[2 * i]);
break;
case HSUB_S:
wd_p[i] =
static_cast<T_int>(ws_p[2 * i + 1]) - static_cast<T_int>(wt_p[2 * i]);
break;
case HSUB_U:
UNIMPLEMENTED();
wd_pu[i] = static_cast<T_uint>(ws_pu[2 * i + 1]) -
static_cast<T_uint>(wt_pu[2 * i]);
break;
default:
UNREACHABLE();
}
return res;
}
void Simulator::DecodeTypeMsa3R() {
......@@ -5193,34 +5312,114 @@ void Simulator::DecodeTypeMsa3R() {
DCHECK(CpuFeatures::IsSupported(MIPS_SIMD));
uint32_t opcode = instr_.InstructionBits() & kMsa3RMask;
msa_reg_t ws, wd, wt;
get_msa_register(ws_reg(), &ws);
get_msa_register(wt_reg(), &wt);
get_msa_register(wd_reg(), &wd);
switch (opcode) {
case HADD_S:
case HADD_U:
case HSUB_S:
case HSUB_U:
#define HORIZONTAL_ARITHMETIC_DF(num_of_lanes, int_type, lesser_int_type) \
for (int i = 0; i < num_of_lanes; ++i) { \
Msa3RInstrHelper_horizontal<int_type, lesser_int_type>( \
opcode, &ws, &wt, &wd, i, num_of_lanes); \
}
switch (DecodeMsaDataFormat()) {
case MSA_HALF:
HORIZONTAL_ARITHMETIC_DF(kMSALanesHalf, int16_t, int8_t);
break;
case MSA_WORD:
HORIZONTAL_ARITHMETIC_DF(kMSALanesWord, int32_t, int16_t);
break;
case MSA_DWORD:
HORIZONTAL_ARITHMETIC_DF(kMSALanesDword, int64_t, int32_t);
break;
default:
UNREACHABLE();
}
break;
#undef HORIZONTAL_ARITHMETIC_DF
case VSHF:
#define VSHF_DF(num_of_lanes, int_type) \
for (int i = 0; i < num_of_lanes; ++i) { \
Msa3RInstrHelper_shuffle<int_type>(opcode, &ws, &wt, &wd, i, \
num_of_lanes); \
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
VSHF_DF(kMSALanesByte, int8_t);
break;
case MSA_HALF:
VSHF_DF(kMSALanesHalf, int16_t);
break;
case MSA_WORD:
VSHF_DF(kMSALanesWord, int32_t);
break;
case MSA_DWORD:
VSHF_DF(kMSALanesDword, int64_t);
break;
default:
UNREACHABLE();
}
#undef VSHF_DF
break;
case PCKEV:
case PCKOD:
case ILVL:
case ILVR:
case ILVEV:
case ILVOD:
#define INTERLEAVE_PACK_DF(num_of_lanes, int_type) \
for (int i = 0; i < num_of_lanes / 2; ++i) { \
Msa3RInstrHelper_shuffle<int_type>(opcode, &ws, &wt, &wd, i, \
num_of_lanes); \
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
INTERLEAVE_PACK_DF(kMSALanesByte, int8_t);
break;
case MSA_HALF:
INTERLEAVE_PACK_DF(kMSALanesHalf, int16_t);
break;
case MSA_WORD:
INTERLEAVE_PACK_DF(kMSALanesWord, int32_t);
break;
case MSA_DWORD:
INTERLEAVE_PACK_DF(kMSALanesDword, int64_t);
break;
default:
UNREACHABLE();
}
break;
#undef INTERLEAVE_PACK_DF
default:
#define MSA_3R_DF(elem, num_of_lanes) \
get_msa_register(instr_.WdValue(), wd.elem); \
get_msa_register(instr_.WsValue(), ws.elem); \
get_msa_register(instr_.WtValue(), wt.elem); \
for (int i = 0; i < num_of_lanes; i++) { \
wd.elem[i] = Msa3RInstrHelper(opcode, wd.elem[i], ws.elem[i], wt.elem[i]); \
} \
set_msa_register(instr_.WdValue(), wd.elem); \
TraceMSARegWr(wd.elem);
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
MSA_3R_DF(b, kMSALanesByte);
break;
case MSA_HALF:
MSA_3R_DF(h, kMSALanesHalf);
break;
case MSA_WORD:
MSA_3R_DF(w, kMSALanesWord);
break;
case MSA_DWORD:
MSA_3R_DF(d, kMSALanesDword);
break;
default:
UNREACHABLE();
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
MSA_3R_DF(b, kMSALanesByte);
break;
case MSA_HALF:
MSA_3R_DF(h, kMSALanesHalf);
break;
case MSA_WORD:
MSA_3R_DF(w, kMSALanesWord);
break;
case MSA_DWORD:
MSA_3R_DF(d, kMSALanesDword);
break;
default:
UNREACHABLE();
}
#undef MSA_3R_DF
break;
}
set_msa_register(wd_reg(), &wd);
TraceMSARegWr(&wd);
}
template <typename T_int, typename T_fp, typename T_reg>
......@@ -5318,7 +5517,7 @@ void Msa3RFInstrHelper(uint32_t opcode, T_reg ws, T_reg wt, T_reg& wd) {
break;
case FDIV: {
if (t_element == 0) {
wd = std::numeric_limits<T_fp>::quiet_NaN();
wd = bit_cast<T_int>(std::numeric_limits<T_fp>::quiet_NaN());
} else {
wd = bit_cast<T_int>(s_element / t_element);
}
......@@ -5538,6 +5737,7 @@ void Simulator::DecodeTypeMsa3RF() {
UNREACHABLE();
}
break;
#undef PACK_FLOAT16
#undef FEXDO_DF
case FTQ:
#define FTQ_DF(source, dst, fp_type, int_type) \
......@@ -5884,8 +6084,8 @@ T_int Msa2RFInstrHelper(uint32_t opcode, T_src src, T_dst& dst,
const T_int min_int = std::numeric_limits<T_int>::min();
if (std::isnan(element)) {
dst = 0;
} else if (element > max_int || element < min_int) {
dst = element > max_int ? max_int : min_int;
} else if (element >= max_int || element <= min_int) {
dst = element >= max_int ? max_int : min_int;
} else {
dst = static_cast<T_int>(std::trunc(element));
}
......@@ -5896,8 +6096,8 @@ T_int Msa2RFInstrHelper(uint32_t opcode, T_src src, T_dst& dst,
const T_uint max_int = std::numeric_limits<T_uint>::max();
if (std::isnan(element)) {
dst = 0;
} else if (element > max_int || element < 0) {
dst = element > max_int ? max_int : 0;
} else if (element >= max_int || element <= 0) {
dst = element >= max_int ? max_int : 0;
} else {
dst = static_cast<T_uint>(std::trunc(element));
}
......@@ -6006,8 +6206,8 @@ T_int Msa2RFInstrHelper(uint32_t opcode, T_src src, T_dst& dst,
return 0;
}
template <typename T_int, typename T_fp, typename T_reg, typename T_i>
T_int Msa2RFInstrHelper2(uint32_t opcode, T_reg ws, T_i i) {
template <typename T_int, typename T_fp, typename T_reg>
T_int Msa2RFInstrHelper2(uint32_t opcode, T_reg ws, int i) {
switch (opcode) {
#define EXTRACT_FLOAT16_SIGN(fp16) (fp16 >> 15)
#define EXTRACT_FLOAT16_EXP(fp16) (fp16 >> 10 & 0x1f)
......@@ -6228,6 +6428,30 @@ void Simulator::DecodeTypeImmediate() {
}
};
auto BranchHelper_MSA = [this, &next_pc, imm16,
&execute_branch_delay_instruction](bool do_branch) {
execute_branch_delay_instruction = true;
int32_t current_pc = get_pc();
const int32_t bitsIn16Int = sizeof(int16_t) * kBitsPerByte;
if (do_branch) {
if (FLAG_debug_code) {
int16_t bits = imm16 & 0xfc;
if (imm16 >= 0) {
CHECK_EQ(bits, 0);
} else {
CHECK_EQ(bits ^ 0xfc, 0);
}
}
// jump range :[pc + kInstrSize - 512 * kInstrSize,
// pc + kInstrSize + 511 * kInstrSize]
int16_t offset = static_cast<int16_t>(imm16 << (bitsIn16Int - 10)) >>
(bitsIn16Int - 12);
next_pc = current_pc + offset + Instruction::kInstrSize;
} else {
next_pc = current_pc + 2 * Instruction::kInstrSize;
}
};
auto BranchAndLinkCompactHelper = [this, &next_pc](bool do_branch, int bits) {
int32_t current_pc = get_pc();
CheckForbiddenSlot(current_pc);
......@@ -6270,18 +6494,66 @@ void Simulator::DecodeTypeImmediate() {
case BC1NEZ:
BranchHelper(get_fpu_register(ft_reg) & 0x1);
break;
case BZ_V:
case BZ_V: {
msa_reg_t wt;
get_msa_register(wt_reg(), &wt);
BranchHelper_MSA(wt.d[0] == 0 && wt.d[1] == 0);
} break;
#define BZ_DF(witdh, lanes) \
{ \
msa_reg_t wt; \
get_msa_register(wt_reg(), &wt); \
int i; \
for (i = 0; i < lanes; ++i) { \
if (wt.witdh[i] == 0) { \
break; \
} \
} \
BranchHelper_MSA(i != lanes); \
}
case BZ_B:
BZ_DF(b, kMSALanesByte)
break;
case BZ_H:
BZ_DF(h, kMSALanesHalf)
break;
case BZ_W:
BZ_DF(w, kMSALanesWord)
break;
case BZ_D:
case BNZ_V:
BZ_DF(d, kMSALanesDword)
break;
#undef BZ_DF
case BNZ_V: {
msa_reg_t wt;
get_msa_register(wt_reg(), &wt);
BranchHelper_MSA(wt.d[0] != 0 || wt.d[1] != 0);
} break;
#define BNZ_DF(witdh, lanes) \
{ \
msa_reg_t wt; \
get_msa_register(wt_reg(), &wt); \
int i; \
for (i = 0; i < lanes; ++i) { \
if (wt.witdh[i] == 0) { \
break; \
} \
} \
BranchHelper_MSA(i == lanes); \
}
case BNZ_B:
BNZ_DF(b, kMSALanesByte)
break;
case BNZ_H:
BNZ_DF(h, kMSALanesHalf)
break;
case BNZ_W:
BNZ_DF(w, kMSALanesWord)
break;
case BNZ_D:
UNIMPLEMENTED();
BNZ_DF(d, kMSALanesDword)
break;
#undef BNZ_DF
default:
UNREACHABLE();
}
......
......@@ -4880,9 +4880,12 @@ void Simulator::DecodeTypeMsaELM() {
DCHECK_EQ(rd_reg(), kMSACSRRegister);
SetResult(sa(), static_cast<int64_t>(bit_cast<int32_t>(MSACSR_)));
break;
case MOVE_V:
UNIMPLEMENTED();
break;
case MOVE_V: {
msa_reg_t ws;
get_msa_register(ws_reg(), &ws);
set_msa_register(wd_reg(), &ws);
TraceMSARegWr(&ws);
} break;
default:
opcode &= kMsaELMMask;
switch (opcode) {
......@@ -4964,7 +4967,50 @@ void Simulator::DecodeTypeMsaELM() {
UNREACHABLE();
}
} break;
case SLDI:
case SLDI: {
uint8_t v[32];
msa_reg_t ws;
msa_reg_t wd;
get_msa_register(ws_reg(), &ws);
get_msa_register(wd_reg(), &wd);
#define SLDI_DF(s, k) \
for (unsigned i = 0; i < s; i++) { \
v[i] = ws.b[s * k + i]; \
v[i + s] = wd.b[s * k + i]; \
} \
for (unsigned i = 0; i < s; i++) { \
wd.b[s * k + i] = v[i + n]; \
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
DCHECK(n < kMSALanesByte);
SLDI_DF(kMSARegSize / sizeof(int8_t) / kBitsPerByte, 0)
break;
case MSA_HALF:
DCHECK(n < kMSALanesHalf);
for (int k = 0; k < 2; ++k) {
SLDI_DF(kMSARegSize / sizeof(int16_t) / kBitsPerByte, k)
}
break;
case MSA_WORD:
DCHECK(n < kMSALanesWord);
for (int k = 0; k < 4; ++k) {
SLDI_DF(kMSARegSize / sizeof(int32_t) / kBitsPerByte, k)
}
break;
case MSA_DWORD:
DCHECK(n < kMSALanesDword);
for (int k = 0; k < 8; ++k) {
SLDI_DF(kMSARegSize / sizeof(int64_t) / kBitsPerByte, k)
}
break;
default:
UNREACHABLE();
}
set_msa_register(wd_reg(), &wd);
TraceMSARegWr(&wd);
} break;
#undef SLDI_DF
case SPLATI:
case INSVE:
UNIMPLEMENTED();
......@@ -5101,6 +5147,7 @@ void Simulator::DecodeTypeMsaBIT() {
default:
UNREACHABLE();
}
#undef MSA_BIT_DF
}
void Simulator::DecodeTypeMsaMI10() {
......@@ -5383,13 +5430,6 @@ T Simulator::Msa3RInstrHelper(uint32_t opcode, T wd, T ws, T wt) {
case DPSUB_U:
case SLD:
case SPLAT:
case PCKEV:
case PCKOD:
case ILVL:
case ILVR:
case ILVEV:
case ILVOD:
case VSHF:
UNIMPLEMENTED();
break;
case SRAR: {
......@@ -5401,16 +5441,94 @@ T Simulator::Msa3RInstrHelper(uint32_t opcode, T wd, T ws, T wt) {
int bit = wt_modulo == 0 ? 0 : (wsu >> (wt_modulo - 1)) & 1;
res = static_cast<T>((wsu >> wt_modulo) + bit);
} break;
default:
UNREACHABLE();
}
return res;
}
template <typename T_int, typename T_reg>
void Msa3RInstrHelper_shuffle(const uint32_t opcode, T_reg ws, T_reg wt,
T_reg wd, const int i, const int num_of_lanes) {
T_int *ws_p, *wt_p, *wd_p;
ws_p = reinterpret_cast<T_int*>(ws);
wt_p = reinterpret_cast<T_int*>(wt);
wd_p = reinterpret_cast<T_int*>(wd);
switch (opcode) {
case PCKEV:
wd_p[i] = wt_p[2 * i];
wd_p[i + num_of_lanes / 2] = ws_p[2 * i];
break;
case PCKOD:
wd_p[i] = wt_p[2 * i + 1];
wd_p[i + num_of_lanes / 2] = ws_p[2 * i + 1];
break;
case ILVL:
wd_p[2 * i] = wt_p[i + num_of_lanes / 2];
wd_p[2 * i + 1] = ws_p[i + num_of_lanes / 2];
break;
case ILVR:
wd_p[2 * i] = wt_p[i];
wd_p[2 * i + 1] = ws_p[i];
break;
case ILVEV:
wd_p[2 * i] = wt_p[2 * i];
wd_p[2 * i + 1] = ws_p[2 * i];
break;
case ILVOD:
wd_p[2 * i] = wt_p[2 * i + 1];
wd_p[2 * i + 1] = ws_p[2 * i + 1];
break;
case VSHF: {
const int mask_not_valid = 0xc0;
const int mask_6_bits = 0x3f;
if ((wd_p[i] & mask_not_valid)) {
wd_p[i] = 0;
} else {
int k = (wd_p[i] & mask_6_bits) % (num_of_lanes * 2);
wd_p[i] = k >= num_of_lanes ? ws_p[k - num_of_lanes] : wt_p[k];
}
} break;
default:
UNREACHABLE();
}
}
template <typename T_int, typename T_smaller_int, typename T_reg>
void Msa3RInstrHelper_horizontal(const uint32_t opcode, T_reg ws, T_reg wt,
T_reg wd, const int i,
const int num_of_lanes) {
typedef typename std::make_unsigned<T_int>::type T_uint;
typedef typename std::make_unsigned<T_smaller_int>::type T_smaller_uint;
T_int* wd_p;
T_smaller_int *ws_p, *wt_p;
ws_p = reinterpret_cast<T_smaller_int*>(ws);
wt_p = reinterpret_cast<T_smaller_int*>(wt);
wd_p = reinterpret_cast<T_int*>(wd);
T_uint* wd_pu;
T_smaller_uint *ws_pu, *wt_pu;
ws_pu = reinterpret_cast<T_smaller_uint*>(ws);
wt_pu = reinterpret_cast<T_smaller_uint*>(wt);
wd_pu = reinterpret_cast<T_uint*>(wd);
switch (opcode) {
case HADD_S:
wd_p[i] =
static_cast<T_int>(ws_p[2 * i + 1]) + static_cast<T_int>(wt_p[2 * i]);
break;
case HADD_U:
wd_pu[i] = static_cast<T_uint>(ws_pu[2 * i + 1]) +
static_cast<T_uint>(wt_pu[2 * i]);
break;
case HSUB_S:
wd_p[i] =
static_cast<T_int>(ws_p[2 * i + 1]) - static_cast<T_int>(wt_p[2 * i]);
break;
case HSUB_U:
UNIMPLEMENTED();
wd_pu[i] = static_cast<T_uint>(ws_pu[2 * i + 1]) -
static_cast<T_uint>(wt_pu[2 * i]);
break;
default:
UNREACHABLE();
}
return res;
}
void Simulator::DecodeTypeMsa3R() {
......@@ -5418,34 +5536,114 @@ void Simulator::DecodeTypeMsa3R() {
DCHECK(CpuFeatures::IsSupported(MIPS_SIMD));
uint32_t opcode = instr_.InstructionBits() & kMsa3RMask;
msa_reg_t ws, wd, wt;
#define MSA_3R_DF(elem, num_of_lanes) \
get_msa_register(instr_.WdValue(), wd.elem); \
get_msa_register(instr_.WsValue(), ws.elem); \
get_msa_register(instr_.WtValue(), wt.elem); \
for (int i = 0; i < num_of_lanes; i++) { \
wd.elem[i] = Msa3RInstrHelper(opcode, wd.elem[i], ws.elem[i], wt.elem[i]); \
} \
set_msa_register(instr_.WdValue(), wd.elem); \
TraceMSARegWr(wd.elem);
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
MSA_3R_DF(b, kMSALanesByte);
break;
case MSA_HALF:
MSA_3R_DF(h, kMSALanesHalf);
get_msa_register(ws_reg(), &ws);
get_msa_register(wt_reg(), &wt);
get_msa_register(wd_reg(), &wd);
switch (opcode) {
case HADD_S:
case HADD_U:
case HSUB_S:
case HSUB_U:
#define HORIZONTAL_ARITHMETIC_DF(num_of_lanes, int_type, lesser_int_type) \
for (int i = 0; i < num_of_lanes; ++i) { \
Msa3RInstrHelper_horizontal<int_type, lesser_int_type>( \
opcode, &ws, &wt, &wd, i, num_of_lanes); \
}
switch (DecodeMsaDataFormat()) {
case MSA_HALF:
HORIZONTAL_ARITHMETIC_DF(kMSALanesHalf, int16_t, int8_t);
break;
case MSA_WORD:
HORIZONTAL_ARITHMETIC_DF(kMSALanesWord, int32_t, int16_t);
break;
case MSA_DWORD:
HORIZONTAL_ARITHMETIC_DF(kMSALanesDword, int64_t, int32_t);
break;
default:
UNREACHABLE();
}
break;
case MSA_WORD:
MSA_3R_DF(w, kMSALanesWord);
#undef HORIZONTAL_ARITHMETIC_DF
case VSHF:
#define VSHF_DF(num_of_lanes, int_type) \
for (int i = 0; i < num_of_lanes; ++i) { \
Msa3RInstrHelper_shuffle<int_type>(opcode, &ws, &wt, &wd, i, \
num_of_lanes); \
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
VSHF_DF(kMSALanesByte, int8_t);
break;
case MSA_HALF:
VSHF_DF(kMSALanesHalf, int16_t);
break;
case MSA_WORD:
VSHF_DF(kMSALanesWord, int32_t);
break;
case MSA_DWORD:
VSHF_DF(kMSALanesDword, int64_t);
break;
default:
UNREACHABLE();
}
#undef VSHF_DF
break;
case MSA_DWORD:
MSA_3R_DF(d, kMSALanesDword);
case PCKEV:
case PCKOD:
case ILVL:
case ILVR:
case ILVEV:
case ILVOD:
#define INTERLEAVE_PACK_DF(num_of_lanes, int_type) \
for (int i = 0; i < num_of_lanes / 2; ++i) { \
Msa3RInstrHelper_shuffle<int_type>(opcode, &ws, &wt, &wd, i, \
num_of_lanes); \
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
INTERLEAVE_PACK_DF(kMSALanesByte, int8_t);
break;
case MSA_HALF:
INTERLEAVE_PACK_DF(kMSALanesHalf, int16_t);
break;
case MSA_WORD:
INTERLEAVE_PACK_DF(kMSALanesWord, int32_t);
break;
case MSA_DWORD:
INTERLEAVE_PACK_DF(kMSALanesDword, int64_t);
break;
default:
UNREACHABLE();
}
break;
#undef INTERLEAVE_PACK_DF
default:
UNREACHABLE();
#define MSA_3R_DF(elem, num_of_lanes) \
for (int i = 0; i < num_of_lanes; i++) { \
wd.elem[i] = Msa3RInstrHelper(opcode, wd.elem[i], ws.elem[i], wt.elem[i]); \
}
switch (DecodeMsaDataFormat()) {
case MSA_BYTE:
MSA_3R_DF(b, kMSALanesByte);
break;
case MSA_HALF:
MSA_3R_DF(h, kMSALanesHalf);
break;
case MSA_WORD:
MSA_3R_DF(w, kMSALanesWord);
break;
case MSA_DWORD:
MSA_3R_DF(d, kMSALanesDword);
break;
default:
UNREACHABLE();
}
#undef MSA_3R_DF
break;
}
set_msa_register(wd_reg(), &wd);
TraceMSARegWr(&wd);
}
template <typename T_int, typename T_fp, typename T_reg>
......@@ -5543,7 +5741,7 @@ void Msa3RFInstrHelper(uint32_t opcode, T_reg ws, T_reg wt, T_reg& wd) {
break;
case FDIV: {
if (t_element == 0) {
wd = std::numeric_limits<T_fp>::quiet_NaN();
wd = bit_cast<T_int>(std::numeric_limits<T_fp>::quiet_NaN());
} else {
wd = bit_cast<T_int>(s_element / t_element);
}
......@@ -5763,6 +5961,7 @@ void Simulator::DecodeTypeMsa3RF() {
UNREACHABLE();
}
break;
#undef PACK_FLOAT16
#undef FEXDO_DF
case FTQ:
#define FTQ_DF(source, dst, fp_type, int_type) \
......@@ -6116,8 +6315,8 @@ T_int Msa2RFInstrHelper(uint32_t opcode, T_src src, T_dst& dst,
const T_int min_int = std::numeric_limits<T_int>::min();
if (std::isnan(element)) {
dst = 0;
} else if (element > max_int || element < min_int) {
dst = element > max_int ? max_int : min_int;
} else if (element >= max_int || element <= min_int) {
dst = element >= max_int ? max_int : min_int;
} else {
dst = static_cast<T_int>(std::trunc(element));
}
......@@ -6128,8 +6327,8 @@ T_int Msa2RFInstrHelper(uint32_t opcode, T_src src, T_dst& dst,
const T_uint max_int = std::numeric_limits<T_uint>::max();
if (std::isnan(element)) {
dst = 0;
} else if (element > max_int || element < 0) {
dst = element > max_int ? max_int : 0;
} else if (element >= max_int || element <= 0) {
dst = element >= max_int ? max_int : 0;
} else {
dst = static_cast<T_uint>(std::trunc(element));
}
......@@ -6238,8 +6437,8 @@ T_int Msa2RFInstrHelper(uint32_t opcode, T_src src, T_dst& dst,
return 0;
}
template <typename T_int, typename T_fp, typename T_reg, typename T_i>
T_int Msa2RFInstrHelper2(uint32_t opcode, T_reg ws, T_i i) {
template <typename T_int, typename T_fp, typename T_reg>
T_int Msa2RFInstrHelper2(uint32_t opcode, T_reg ws, int i) {
switch (opcode) {
#define EXTRACT_FLOAT16_SIGN(fp16) (fp16 >> 15)
#define EXTRACT_FLOAT16_EXP(fp16) (fp16 >> 10 & 0x1f)
......@@ -6469,6 +6668,30 @@ void Simulator::DecodeTypeImmediate() {
}
};
auto BranchHelper_MSA = [this, &next_pc, imm16,
&execute_branch_delay_instruction](bool do_branch) {
execute_branch_delay_instruction = true;
int64_t current_pc = get_pc();
const int32_t bitsIn16Int = sizeof(int16_t) * kBitsPerByte;
if (do_branch) {
if (FLAG_debug_code) {
int16_t bits = imm16 & 0xfc;
if (imm16 >= 0) {
CHECK_EQ(bits, 0);
} else {
CHECK_EQ(bits ^ 0xfc, 0);
}
}
// jump range :[pc + kInstrSize - 512 * kInstrSize,
// pc + kInstrSize + 511 * kInstrSize]
int16_t offset = static_cast<int16_t>(imm16 << (bitsIn16Int - 10)) >>
(bitsIn16Int - 12);
next_pc = current_pc + offset + Instruction::kInstrSize;
} else {
next_pc = current_pc + 2 * Instruction::kInstrSize;
}
};
auto BranchAndLinkCompactHelper = [this, &next_pc](bool do_branch, int bits) {
int64_t current_pc = get_pc();
CheckForbiddenSlot(current_pc);
......@@ -6510,18 +6733,66 @@ void Simulator::DecodeTypeImmediate() {
case BC1NEZ:
BranchHelper(get_fpu_register(ft_reg) & 0x1);
break;
case BZ_V:
case BZ_V: {
msa_reg_t wt;
get_msa_register(wt_reg(), &wt);
BranchHelper_MSA(wt.d[0] == 0 && wt.d[1] == 0);
} break;
#define BZ_DF(witdh, lanes) \
{ \
msa_reg_t wt; \
get_msa_register(wt_reg(), &wt); \
int i; \
for (i = 0; i < lanes; ++i) { \
if (wt.witdh[i] == 0) { \
break; \
} \
} \
BranchHelper_MSA(i != lanes); \
}
case BZ_B:
BZ_DF(b, kMSALanesByte)
break;
case BZ_H:
BZ_DF(h, kMSALanesHalf)
break;
case BZ_W:
BZ_DF(w, kMSALanesWord)
break;
case BZ_D:
case BNZ_V:
BZ_DF(d, kMSALanesDword)
break;
#undef BZ_DF
case BNZ_V: {
msa_reg_t wt;
get_msa_register(wt_reg(), &wt);
BranchHelper_MSA(wt.d[0] != 0 || wt.d[1] != 0);
} break;
#define BNZ_DF(witdh, lanes) \
{ \
msa_reg_t wt; \
get_msa_register(wt_reg(), &wt); \
int i; \
for (i = 0; i < lanes; ++i) { \
if (wt.witdh[i] == 0) { \
break; \
} \
} \
BranchHelper_MSA(i == lanes); \
}
case BNZ_B:
BNZ_DF(b, kMSALanesByte)
break;
case BNZ_H:
BNZ_DF(h, kMSALanesHalf)
break;
case BNZ_W:
BNZ_DF(w, kMSALanesWord)
break;
case BNZ_D:
UNIMPLEMENTED();
BNZ_DF(d, kMSALanesDword)
break;
#undef BNZ_DF
default:
UNREACHABLE();
}
......
......@@ -288,7 +288,7 @@ T SaturateAdd(T a, T b) {
template <typename T>
T SaturateSub(T a, T b) {
if (std::is_signed<T>::value) {
if (a > 0 && b < 0) {
if (a >= 0 && b < 0) {
if (a > std::numeric_limits<T>::max() + b) {
return std::numeric_limits<T>::max();
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -7,6 +7,7 @@
#include <limits>
#include <set>
#include "src/utils.h"
#include "test/unittests/test-utils.h"
namespace v8 {
......@@ -211,5 +212,117 @@ TEST(FunctionalTest, BitHashDoubleDifferentForZeroAndMinusZero) {
EXPECT_NE(h(0.0), h(-0.0));
}
// src/utils.h functions
template <typename T>
class UtilsTest : public ::testing::Test {};
typedef ::testing::Types<signed char, unsigned char,
short, // NOLINT(runtime/int)
unsigned short, // NOLINT(runtime/int)
int, unsigned int, long, // NOLINT(runtime/int)
unsigned long, // NOLINT(runtime/int)
long long, // NOLINT(runtime/int)
unsigned long long, // NOLINT(runtime/int)
int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
int64_t, uint64_t>
IntegerTypes;
TYPED_TEST_CASE(UtilsTest, IntegerTypes);
TYPED_TEST(UtilsTest, SaturateSub) {
TypeParam min = std::numeric_limits<TypeParam>::min();
TypeParam max = std::numeric_limits<TypeParam>::max();
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min, 0), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max, 0), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max, min), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min, max), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min, max / 3), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min + 1, 2), min);
if (std::numeric_limits<TypeParam>::is_signed) {
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min, min),
static_cast<TypeParam>(0));
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(0, min), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(0, max), -max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max - 1, -2), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max / 3, min), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max / 5, min), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min / 3, max), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min / 9, max), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max, min / 3), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min, max / 3), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max / 3 * 2, min / 2), max);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min / 3 * 2, max / 2), min);
} else {
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(min, min), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(0, min), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(0, max), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max / 3, max), min);
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(max - 3, max), min);
}
TypeParam test_cases[] = {static_cast<TypeParam>(min / 23),
static_cast<TypeParam>(max / 3),
63,
static_cast<TypeParam>(min / 6),
static_cast<TypeParam>(max / 55),
static_cast<TypeParam>(min / 2),
static_cast<TypeParam>(max / 2),
0,
1,
2,
3,
4,
42};
TRACED_FOREACH(TypeParam, x, test_cases) {
TRACED_FOREACH(TypeParam, y, test_cases) {
if (std::numeric_limits<TypeParam>::is_signed) {
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(x, y), x - y);
} else {
EXPECT_EQ(v8::internal::SaturateSub<TypeParam>(x, y),
y > x ? min : x - y);
}
}
}
}
TYPED_TEST(UtilsTest, SaturateAdd) {
TypeParam min = std::numeric_limits<TypeParam>::min();
TypeParam max = std::numeric_limits<TypeParam>::max();
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(min, min), min);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(max, max), max);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(min, min / 3), min);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(max / 8 * 7, max / 3 * 2),
max);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(min / 3 * 2, min / 8 * 7),
min);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(max / 20 * 18, max / 25 * 18),
max);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(min / 3 * 2, min / 3 * 2),
min);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(max - 1, 2), max);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(max - 100, 101), max);
if (std::numeric_limits<TypeParam>::is_signed) {
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(min + 100, -101), min);
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(min + 1, -2), min);
}
TypeParam test_cases[] = {static_cast<TypeParam>(min / 23),
static_cast<TypeParam>(max / 3),
63,
static_cast<TypeParam>(min / 6),
static_cast<TypeParam>(max / 55),
static_cast<TypeParam>(min / 2),
static_cast<TypeParam>(max / 2),
0,
1,
2,
3,
4,
42};
TRACED_FOREACH(TypeParam, x, test_cases) {
TRACED_FOREACH(TypeParam, y, test_cases) {
EXPECT_EQ(v8::internal::SaturateAdd<TypeParam>(x, y), x + y);
}
}
}
} // namespace base
} // namespace v8
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