Commit 79b075be authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Speed up LEB decoding

Speed up LEB decoding by forcing the decoding loop to be unrolled.
Even though the compiler was free to unroll the loop before, clang did
not do so. We now manually unroll by using a template function which
calls itself recursively, passing the byte index to be decoded next.
For efficient execution, we still depend on the compiler to inline the
recursive calls (which clang does).

This optimization speeds up interpreted execution of the Jetstream
benchmarks by 15 percent.
Speedup on module decoding is negligible though.

Drive-by: Change "unsigned" to "uint32_t".

R=ahaas@chromium.org
BUG=v8:5822

Change-Id: I06d4230f92bfb2a80cdc5029d965fc3bf84ca1cc
Reviewed-on: https://chromium-review.googlesource.com/506188
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45308}
parent cc2c1144
......@@ -44,7 +44,7 @@ class Decoder {
virtual ~Decoder() {}
inline bool check(const byte* pc, unsigned length, const char* msg) {
inline bool check(const byte* pc, uint32_t length, const char* msg) {
DCHECK_LE(start_, pc);
if (V8_UNLIKELY(pc + length > end_)) {
error(pc, msg);
......@@ -82,28 +82,28 @@ class Decoder {
// Reads a variable-length unsigned integer (little endian).
template <bool checked>
uint32_t read_u32v(const byte* pc, unsigned* length,
uint32_t read_u32v(const byte* pc, uint32_t* length,
const char* name = "LEB32") {
return read_leb<uint32_t, checked, false, false>(pc, length, name);
}
// Reads a variable-length signed integer (little endian).
template <bool checked>
int32_t read_i32v(const byte* pc, unsigned* length,
int32_t read_i32v(const byte* pc, uint32_t* length,
const char* name = "signed LEB32") {
return read_leb<int32_t, checked, false, false>(pc, length, name);
}
// Reads a variable-length unsigned integer (little endian).
template <bool checked>
uint64_t read_u64v(const byte* pc, unsigned* length,
uint64_t read_u64v(const byte* pc, uint32_t* length,
const char* name = "LEB64") {
return read_leb<uint64_t, checked, false, false>(pc, length, name);
}
// Reads a variable-length signed integer (little endian).
template <bool checked>
int64_t read_i64v(const byte* pc, unsigned* length,
int64_t read_i64v(const byte* pc, uint32_t* length,
const char* name = "signed LEB64") {
return read_leb<int64_t, checked, false, false>(pc, length, name);
}
......@@ -125,13 +125,13 @@ class Decoder {
// Reads a LEB128 variable-length unsigned 32-bit integer and advances {pc_}.
uint32_t consume_u32v(const char* name = nullptr) {
unsigned length = 0;
uint32_t length = 0;
return read_leb<uint32_t, true, true, true>(pc_, &length, name);
}
// Reads a LEB128 variable-length signed 32-bit integer and advances {pc_}.
int32_t consume_i32v(const char* name = nullptr) {
unsigned length = 0;
uint32_t length = 0;
return read_leb<int32_t, true, true, true>(pc_, &length, name);
}
......@@ -265,37 +265,47 @@ class Decoder {
}
template <typename IntType, bool checked, bool advance_pc, bool trace>
inline IntType read_leb(const byte* pc, unsigned* length,
inline IntType read_leb(const byte* pc, uint32_t* length,
const char* name = "varint") {
DCHECK_IMPLIES(advance_pc, pc == pc_);
constexpr bool is_signed = std::is_signed<IntType>::value;
TRACE_IF(trace, " +%d %-20s: ", static_cast<int>(pc - start_), name);
return read_leb_tail<IntType, checked, advance_pc, trace, 0>(pc, length,
name, 0);
}
template <typename IntType, bool checked, bool advance_pc, bool trace,
int byte_index>
IntType read_leb_tail(const byte* pc, uint32_t* length, const char* name,
IntType result) {
constexpr bool is_signed = std::is_signed<IntType>::value;
constexpr int kMaxLength = (sizeof(IntType) * 8 + 6) / 7;
const byte* ptr = pc;
const byte* end = Min(end_, ptr + kMaxLength);
// The end variable is only used if checked == true. MSVC recognizes this.
USE(end);
int shift = 0;
static_assert(byte_index < kMaxLength, "invalid template instantiation");
constexpr int shift = byte_index * 7;
constexpr bool is_last_byte = byte_index == kMaxLength - 1;
const bool at_end = checked && pc >= end_;
byte b = 0;
IntType result = 0;
do {
if (checked && V8_UNLIKELY(ptr >= end)) {
TRACE_IF(trace,
ptr == pc + kMaxLength ? "<length overflow> " : "<end> ");
errorf(ptr, "expected %s", name);
result = 0;
break;
}
DCHECK_GT(end, ptr);
b = *ptr++;
if (!at_end) {
DCHECK_LT(pc_, end_);
b = *pc;
TRACE_IF(trace, "%02x ", b);
result = result | ((static_cast<IntType>(b) & 0x7F) << shift);
shift += 7;
} while (b & 0x80);
DCHECK_LE(ptr - pc, kMaxLength);
*length = static_cast<unsigned>(ptr - pc);
if (advance_pc) pc_ = ptr;
if (*length == kMaxLength) {
result = result | ((static_cast<IntType>(b) & 0x7f) << shift);
}
if (!is_last_byte && (b & 0x80)) {
// Make sure that we only instantiate the template for valid byte indexes.
// Compilers are not smart enough to figure out statically that the
// following call is unreachable if is_last_byte is false.
constexpr int next_byte_index = byte_index + (is_last_byte ? 0 : 1);
return read_leb_tail<IntType, checked, advance_pc, trace,
next_byte_index>(pc + 1, length, name, result);
}
if (advance_pc) pc_ = pc + (at_end ? 0 : 1);
*length = byte_index + (at_end ? 0 : 1);
if (checked && (at_end || (b & 0x80))) {
TRACE_IF(trace, at_end ? "<end> " : "<length overflow> ");
errorf(pc, "expected %s", name);
result = 0;
}
if (is_last_byte) {
// A signed-LEB128 must sign-extend the final byte, excluding its
// most-significant bit; e.g. for a 32-bit LEB128:
// kExtraBits = 4 (== 32 - (5-1) * 7)
......@@ -312,15 +322,14 @@ class Decoder {
if (!checked) {
DCHECK(valid_extra_bits);
} else if (!valid_extra_bits) {
error(ptr, "extra bits in varint");
error(pc, "extra bits in varint");
result = 0;
}
}
if (is_signed && *length < kMaxLength) {
int sign_ext_shift = 8 * sizeof(IntType) - shift;
// Perform sign extension.
result = (result << sign_ext_shift) >> sign_ext_shift;
}
constexpr int sign_ext_shift =
is_signed && !is_last_byte ? 8 * sizeof(IntType) - shift - 7 : 0;
// Perform sign extension.
result = (result << sign_ext_shift) >> sign_ext_shift;
if (trace && is_signed) {
TRACE("= %" PRIi64 "\n", static_cast<int64_t>(result));
} else if (trace) {
......
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