[Liftoff] Extend binop tests for div and rem
Add binop tests for div and rem of i32 and i64. The test is extended to handle traps, and to check that the value of local variables is not affected by the operation. R=titzer@chromium.org Bug: v8:6600, chromium:839800 Change-Id: I1a4cbc40bd399666d9831d021afb96e0c53a9f64 Reviewed-on: https://chromium-review.googlesource.com/1044166 Reviewed-by: Ben Titzer <titzer@chromium.org> Commit-Queue: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#52989}
This commit is contained in:
parent
d1012816d8
commit
a2dbb84ccd
@ -3176,10 +3176,13 @@ WASM_EXEC_TEST(IfInsideUnreachable) {
|
||||
// This test targets binops in Liftoff.
|
||||
// Initialize a number of local variables to force them into different
|
||||
// registers, then perform a binary operation on two of the locals.
|
||||
// Afterwards, write back all locals to memory, to check that their value was
|
||||
// not overwritten.
|
||||
template <typename ctype>
|
||||
void BinOpOnDifferentRegisters(WasmExecutionMode execution_mode, ValueType type,
|
||||
Vector<const ctype> inputs, WasmOpcode opcode,
|
||||
std::function<ctype(ctype, ctype)> expect_fn) {
|
||||
void BinOpOnDifferentRegisters(
|
||||
WasmExecutionMode execution_mode, ValueType type,
|
||||
Vector<const ctype> inputs, WasmOpcode opcode,
|
||||
std::function<ctype(ctype, ctype, bool*)> expect_fn) {
|
||||
static constexpr int kMaxNumLocals = 8;
|
||||
for (int num_locals = 1; num_locals < kMaxNumLocals; ++num_locals) {
|
||||
// {init_locals_code} is shared by all code generated in the loop below.
|
||||
@ -3191,6 +3194,15 @@ void BinOpOnDifferentRegisters(WasmExecutionMode execution_mode, ValueType type,
|
||||
WASM_SET_LOCAL(i, WASM_LOAD_MEM(ValueTypes::MachineTypeFor(type),
|
||||
WASM_I32V_2(sizeof(ctype) * i))));
|
||||
}
|
||||
// {write_locals_code} is shared by all code generated in the loop below.
|
||||
std::vector<byte> write_locals_code;
|
||||
// Write locals back into memory, shifted by one element to the right.
|
||||
for (int i = 0; i < num_locals; ++i) {
|
||||
ADD_CODE(write_locals_code,
|
||||
WASM_STORE_MEM(ValueTypes::MachineTypeFor(type),
|
||||
WASM_I32V_2(sizeof(ctype) * (i + 1)),
|
||||
WASM_GET_LOCAL(i)));
|
||||
}
|
||||
for (int lhs = 0; lhs < num_locals; ++lhs) {
|
||||
for (int rhs = 0; rhs < num_locals; ++rhs) {
|
||||
WasmRunner<int32_t> r(execution_mode);
|
||||
@ -3201,24 +3213,41 @@ void BinOpOnDifferentRegisters(WasmExecutionMode execution_mode, ValueType type,
|
||||
}
|
||||
std::vector<byte> code(init_locals_code);
|
||||
ADD_CODE(code,
|
||||
// Store the result of the binary operation at memory 0.
|
||||
// Store the result of the binary operation at memory[0].
|
||||
WASM_STORE_MEM(ValueTypes::MachineTypeFor(type), WASM_ZERO,
|
||||
WASM_BINOP(opcode, WASM_GET_LOCAL(lhs),
|
||||
WASM_GET_LOCAL(rhs))),
|
||||
// Return 0.
|
||||
WASM_ZERO);
|
||||
code.insert(code.end(), write_locals_code.begin(),
|
||||
write_locals_code.end());
|
||||
r.Build(code.data(), code.data() + code.size());
|
||||
for (ctype lhs_value : inputs) {
|
||||
for (ctype rhs_value : inputs) {
|
||||
if (lhs == rhs) lhs_value = rhs_value;
|
||||
WriteLittleEndianValue<ctype>(
|
||||
reinterpret_cast<Address>(&memory[lhs]), lhs_value);
|
||||
WriteLittleEndianValue<ctype>(
|
||||
reinterpret_cast<Address>(&memory[rhs]), rhs_value);
|
||||
int64_t expect = expect_fn(lhs_value, rhs_value);
|
||||
for (int i = 0; i < num_locals; ++i) {
|
||||
ctype value =
|
||||
i == lhs ? lhs_value
|
||||
: i == rhs ? rhs_value : static_cast<ctype>(i + 47);
|
||||
WriteLittleEndianValue<ctype>(
|
||||
reinterpret_cast<Address>(&memory[i]), value);
|
||||
}
|
||||
bool trap = false;
|
||||
int64_t expect = expect_fn(lhs_value, rhs_value, &trap);
|
||||
if (trap) {
|
||||
CHECK_TRAP(r.Call());
|
||||
continue;
|
||||
}
|
||||
CHECK_EQ(0, r.Call());
|
||||
CHECK_EQ(expect, ReadLittleEndianValue<ctype>(
|
||||
reinterpret_cast<Address>(&memory[0])));
|
||||
for (int i = 0; i < num_locals; ++i) {
|
||||
ctype value =
|
||||
i == lhs ? lhs_value
|
||||
: i == rhs ? rhs_value : static_cast<ctype>(i + 47);
|
||||
CHECK_EQ(value, ReadLittleEndianValue<ctype>(
|
||||
reinterpret_cast<Address>(&memory[i + 1])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3235,79 +3264,152 @@ static constexpr int64_t kSome64BitInputs[] = {
|
||||
WASM_EXEC_TEST(I32AddOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32Add,
|
||||
[](int32_t lhs, int32_t rhs) { return lhs + rhs; });
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) { return lhs + rhs; });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32SubOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32Sub,
|
||||
[](int32_t lhs, int32_t rhs) { return lhs - rhs; });
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) { return lhs - rhs; });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32MulOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32Mul,
|
||||
[](int32_t lhs, int32_t rhs) { return lhs * rhs; });
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) { return lhs * rhs; });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32ShlOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32Shl,
|
||||
[](int32_t lhs, int32_t rhs) { return lhs << (rhs & 31); });
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) { return lhs << (rhs & 31); });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32ShrSOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32ShrS,
|
||||
[](int32_t lhs, int32_t rhs) { return lhs >> (rhs & 31); });
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) { return lhs >> (rhs & 31); });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32ShrUOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32ShrU,
|
||||
[](int32_t lhs, int32_t rhs) {
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) {
|
||||
return static_cast<uint32_t>(lhs) >> (rhs & 31);
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32DivSOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32DivS,
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) {
|
||||
*trap = rhs == 0;
|
||||
return *trap ? 0 : lhs / rhs;
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32DivUOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32DivU,
|
||||
[](uint32_t lhs, uint32_t rhs, bool* trap) {
|
||||
*trap = rhs == 0;
|
||||
return *trap ? 0 : lhs / rhs;
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32RemSOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32RemS,
|
||||
[](int32_t lhs, int32_t rhs, bool* trap) {
|
||||
*trap = rhs == 0;
|
||||
return *trap || rhs == -1 ? 0 : lhs % rhs;
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I32RemUOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int32_t>(
|
||||
execution_mode, kWasmI32, ArrayVector(kSome32BitInputs), kExprI32RemU,
|
||||
[](uint32_t lhs, uint32_t rhs, bool* trap) {
|
||||
*trap = rhs == 0;
|
||||
return *trap ? 0 : lhs % rhs;
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64AddOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64Add,
|
||||
[](int64_t lhs, int64_t rhs) { return lhs + rhs; });
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) { return lhs + rhs; });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64SubOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64Sub,
|
||||
[](int64_t lhs, int64_t rhs) { return lhs - rhs; });
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) { return lhs - rhs; });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64MulOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64Mul,
|
||||
[](int64_t lhs, int64_t rhs) { return lhs * rhs; });
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) { return lhs * rhs; });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64ShlOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64Shl,
|
||||
[](int64_t lhs, int64_t rhs) { return lhs << (rhs & 63); });
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) { return lhs << (rhs & 63); });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64ShrSOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64ShrS,
|
||||
[](int64_t lhs, int64_t rhs) { return lhs >> (rhs & 63); });
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) { return lhs >> (rhs & 63); });
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64ShrUOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64ShrU,
|
||||
[](int64_t lhs, int64_t rhs) {
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) {
|
||||
return static_cast<uint64_t>(lhs) >> (rhs & 63);
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64DivSOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64DivS,
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) {
|
||||
*trap = rhs == 0 ||
|
||||
(rhs == -1 && lhs == std::numeric_limits<int64_t>::min());
|
||||
return *trap ? 0 : lhs / rhs;
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64DivUOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64DivU,
|
||||
[](uint64_t lhs, uint64_t rhs, bool* trap) {
|
||||
*trap = rhs == 0;
|
||||
return *trap ? 0 : lhs / rhs;
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64RemSOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64RemS,
|
||||
[](int64_t lhs, int64_t rhs, bool* trap) {
|
||||
*trap = rhs == 0;
|
||||
return *trap || rhs == -1 ? 0 : lhs % rhs;
|
||||
});
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(I64RemUOnDifferentRegisters) {
|
||||
BinOpOnDifferentRegisters<int64_t>(
|
||||
execution_mode, kWasmI64, ArrayVector(kSome64BitInputs), kExprI64RemU,
|
||||
[](uint64_t lhs, uint64_t rhs, bool* trap) {
|
||||
*trap = rhs == 0;
|
||||
return *trap ? 0 : lhs % rhs;
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Liftoff_prologue) {
|
||||
// The tested prologue is only inserted in tiering mode. The prologue
|
||||
// is responsible for jumping to the optimized, tiered up code if
|
||||
|
Loading…
Reference in New Issue
Block a user