Reland "MIPS[64] Implementation of MSA instructions on builtin simulator"

This is reland of 3e0bf580e8
Original change's description:
> 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/744008
> Reviewed-by: Ivica Bogosavljevic <ivica.bogosavljevic@mips.com>
> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
> Commit-Queue: Ivica Bogosavljevic <ivica.bogosavljevic@mips.com>
> Cr-Commit-Position: refs/heads/master@{#49439}

Bug: 
Change-Id: I3a904caf675d314186c02c1c843d1e6a91a21a14
Reviewed-on: https://chromium-review.googlesource.com/776813
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Ivica Bogosavljevic <ivica.bogosavljevic@mips.com>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Ivica Bogosavljevic <ivica.bogosavljevic@mips.com>
Cr-Commit-Position: refs/heads/master@{#49666}
This commit is contained in:
Predrag Rudic 2017-11-27 16:11:08 +01:00 committed by Commit Bot
parent 42bc9e8c64
commit 25d4f93740
8 changed files with 2972 additions and 1544 deletions

View File

@ -4671,9 +4671,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) {
@ -4742,7 +4745,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();
@ -4879,6 +4925,7 @@ void Simulator::DecodeTypeMsaBIT() {
default:
UNREACHABLE();
}
#undef MSA_BIT_DF
}
void Simulator::DecodeTypeMsaMI10() {
@ -5161,13 +5208,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: {
@ -5179,51 +5219,210 @@ 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;
case HADD_S:
case HADD_U:
case HSUB_S:
case HSUB_U:
UNIMPLEMENTED();
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:
wd_pu[i] = static_cast<T_uint>(ws_pu[2 * i + 1]) -
static_cast<T_uint>(wt_pu[2 * i]);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeMsa3R() {
DCHECK(IsMipsArchVariant(kMips32r6));
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>
@ -5321,7 +5520,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);
}
@ -5541,6 +5740,7 @@ void Simulator::DecodeTypeMsa3RF() {
UNREACHABLE();
}
break;
#undef PACK_FLOAT16
#undef FEXDO_DF
case FTQ:
#define FTQ_DF(source, dst, fp_type, int_type) \
@ -5887,8 +6087,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));
}
@ -5899,8 +6099,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));
}
@ -6009,8 +6209,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)
@ -6231,6 +6431,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);
@ -6273,18 +6497,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:
case BZ_H:
case BZ_W:
case BZ_D:
case BNZ_V:
case BNZ_B:
case BNZ_H:
case BNZ_W:
case BNZ_D:
UNIMPLEMENTED();
BZ_DF(b, kMSALanesByte)
break;
case BZ_H:
BZ_DF(h, kMSALanesHalf)
break;
case BZ_W:
BZ_DF(w, kMSALanesWord)
break;
case BZ_D:
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:
BNZ_DF(d, kMSALanesDword)
break;
#undef BNZ_DF
default:
UNREACHABLE();
}

View File

@ -4883,9 +4883,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) {
@ -4967,7 +4970,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();
@ -5104,6 +5150,7 @@ void Simulator::DecodeTypeMsaBIT() {
default:
UNREACHABLE();
}
#undef MSA_BIT_DF
}
void Simulator::DecodeTypeMsaMI10() {
@ -5386,13 +5433,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: {
@ -5404,51 +5444,209 @@ 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;
case HADD_S:
case HADD_U:
case HSUB_S:
case HSUB_U:
UNIMPLEMENTED();
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:
wd_pu[i] = static_cast<T_uint>(ws_pu[2 * i + 1]) -
static_cast<T_uint>(wt_pu[2 * i]);
break;
default:
UNREACHABLE();
}
}
void Simulator::DecodeTypeMsa3R() {
DCHECK_EQ(kArchVariant, kMips64r6);
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>
@ -5546,7 +5744,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);
}
@ -5766,6 +5964,7 @@ void Simulator::DecodeTypeMsa3RF() {
UNREACHABLE();
}
break;
#undef PACK_FLOAT16
#undef FEXDO_DF
case FTQ:
#define FTQ_DF(source, dst, fp_type, int_type) \
@ -6119,8 +6318,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));
}
@ -6131,8 +6330,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));
}
@ -6241,8 +6440,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)
@ -6472,6 +6671,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);
@ -6513,18 +6736,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:
case BZ_H:
case BZ_W:
case BZ_D:
case BNZ_V:
case BNZ_B:
case BNZ_H:
case BNZ_W:
case BNZ_D:
UNIMPLEMENTED();
BZ_DF(b, kMSALanesByte)
break;
case BZ_H:
BZ_DF(h, kMSALanesHalf)
break;
case BZ_W:
BZ_DF(w, kMSALanesWord)
break;
case BZ_D:
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:
BNZ_DF(d, kMSALanesDword)
break;
#undef BNZ_DF
default:
UNREACHABLE();
}

View File

@ -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();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -176,6 +176,7 @@ v8_source_set("unittests_sources") {
"test-utils.cc",
"test-utils.h",
"unicode-unittest.cc",
"utils-unittest.cc",
"value-serializer-unittest.cc",
"wasm/control-transfer-unittest.cc",
"wasm/decoder-unittest.cc",

View File

@ -149,6 +149,7 @@
'test-utils.h',
'test-utils.cc',
'unicode-unittest.cc',
'utils-unittest.cc',
'value-serializer-unittest.cc',
'zone/segmentpool-unittest.cc',
'zone/zone-allocator-unittest.cc',

View File

@ -0,0 +1,113 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits>
#include "src/utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
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(SaturateSub<TypeParam>(min, 0), min);
EXPECT_EQ(SaturateSub<TypeParam>(max, 0), max);
EXPECT_EQ(SaturateSub<TypeParam>(max, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(min, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(min, max / 3), min);
EXPECT_EQ(SaturateSub<TypeParam>(min + 1, 2), min);
if (std::numeric_limits<TypeParam>::is_signed) {
EXPECT_EQ(SaturateSub<TypeParam>(min, min), static_cast<TypeParam>(0));
EXPECT_EQ(SaturateSub<TypeParam>(0, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(max / 3, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(max / 5, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(min / 3, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(min / 9, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(max, min / 3), max);
EXPECT_EQ(SaturateSub<TypeParam>(min, max / 3), min);
EXPECT_EQ(SaturateSub<TypeParam>(max / 3 * 2, min / 2), max);
EXPECT_EQ(SaturateSub<TypeParam>(min / 3 * 2, max / 2), min);
} else {
EXPECT_EQ(SaturateSub<TypeParam>(min, min), min);
EXPECT_EQ(SaturateSub<TypeParam>(0, min), min);
EXPECT_EQ(SaturateSub<TypeParam>(0, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(max / 3, max), min);
EXPECT_EQ(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(SaturateSub<TypeParam>(x, y), x - y);
} else {
EXPECT_EQ(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(SaturateAdd<TypeParam>(min, min), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max, max), max);
EXPECT_EQ(SaturateAdd<TypeParam>(min, min / 3), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max / 8 * 7, max / 3 * 2), max);
EXPECT_EQ(SaturateAdd<TypeParam>(min / 3 * 2, min / 8 * 7), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max / 20 * 18, max / 25 * 18), max);
EXPECT_EQ(SaturateAdd<TypeParam>(min / 3 * 2, min / 3 * 2), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max - 1, 2), max);
EXPECT_EQ(SaturateAdd<TypeParam>(max - 100, 101), max);
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(SaturateAdd<TypeParam>(x, y), x + y);
}
}
}
} // namespace internal
} // namespace v8