[wasm] [cleanup] Avoid lazy initialization of arrays

Both lazy initialization and static initialization (via static
initializer) are bad. Fortunately, the arrays we are constructing are
constant anyway, so we can just compute them at compile time. This is
enforced by making them constexpr.
This also saves all code needed for the initialization, and makes
accesses to the tables faster, as they don't need any atomic operations
(via LazyInstance).

R=ahaas@chromium.org

Change-Id: I7d3ba9b0f2602f596a6c71c8c567e0d1bc306268
Reviewed-on: https://chromium-review.googlesource.com/517083
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45589}
This commit is contained in:
Clemens Hammacher 2017-05-30 10:47:37 +02:00 committed by Commit Bot
parent eed937b04b
commit 737962f85b
5 changed files with 125 additions and 67 deletions

View File

@ -2510,6 +2510,7 @@ v8_component("v8_libbase") {
"src/base/safe_math_impl.h",
"src/base/sys-info.cc",
"src/base/sys-info.h",
"src/base/template-utils.h",
"src/base/timezone-cache.h",
"src/base/utils/random-number-generator.cc",
"src/base/utils/random-number-generator.h",

47
src/base/template-utils.h Normal file
View File

@ -0,0 +1,47 @@
// Copyright 2017 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 <array>
namespace v8 {
namespace base {
namespace detail {
// make_array_helper statically iteratively creates the index list 0 .. Size-1.
// A specialization for the base case (first index is 0) finally constructs the
// array.
// TODO(clemensh): Use std::index_sequence once we have C++14 support.
template <class Function, std::size_t... Indexes>
struct make_array_helper;
template <class Function, std::size_t... Indexes>
struct make_array_helper<Function, 0, Indexes...> {
constexpr static auto make_array(Function f)
-> std::array<decltype(f(std::size_t{0})), sizeof...(Indexes) + 1> {
return {{f(0), f(Indexes)...}};
}
};
template <class Function, std::size_t FirstIndex, std::size_t... Indexes>
struct make_array_helper<Function, FirstIndex, Indexes...>
: make_array_helper<Function, FirstIndex - 1, FirstIndex, Indexes...> {};
} // namespace detail
// base::make_array: Create an array of fixed length, initialized by a function.
// The content of the array is created by calling the function with 0 .. Size-1.
// Example usage to create the array {0, 2, 4}:
// std::array<int, 3> arr = base::make_array<3>(
// [](std::size_t i) { return static_cast<int>(2 * i); });
// The resulting array will be constexpr if the passed function is constexpr.
template <std::size_t Size, class Function>
constexpr auto make_array(Function f)
-> std::array<decltype(f(std::size_t{0})), Size> {
static_assert(Size > 0, "Can only create non-empty arrays");
return detail::make_array_helper<Function, Size - 1>::make_array(f);
}
} // namespace base
} // namespace v8

View File

@ -15,7 +15,8 @@ namespace internal {
template <typename T>
class Signature : public ZoneObject {
public:
Signature(size_t return_count, size_t parameter_count, const T* reps)
constexpr Signature(size_t return_count, size_t parameter_count,
const T* reps)
: return_count_(return_count),
parameter_count_(parameter_count),
reps_(reps) {}

View File

@ -2021,6 +2021,7 @@
'base/safe_math_impl.h',
'base/sys-info.cc',
'base/sys-info.h',
'base/template-utils.h',
'base/timezone-cache.h',
'base/utils/random-number-generator.cc',
'base/utils/random-number-generator.h',

View File

@ -3,6 +3,10 @@
// found in the LICENSE file.
#include "src/wasm/wasm-opcodes.h"
#include <array>
#include "src/base/template-utils.h"
#include "src/messages.h"
#include "src/runtime/runtime.h"
#include "src/signature.h"
@ -309,97 +313,101 @@ bool IsJSCompatibleSignature(const FunctionSig* sig) {
return true;
}
namespace {
#define DECLARE_SIG_ENUM(name, ...) kSigEnum_##name,
enum WasmOpcodeSig { FOREACH_SIGNATURE(DECLARE_SIG_ENUM) };
enum WasmOpcodeSig : byte {
kSigEnum_None,
FOREACH_SIGNATURE(DECLARE_SIG_ENUM)
};
// TODO(titzer): not static-initializer safe. Wrap in LazyInstance.
#define DECLARE_SIG(name, ...) \
static ValueType kTypes_##name[] = {__VA_ARGS__}; \
static const FunctionSig kSig_##name( \
#define DECLARE_SIG(name, ...) \
constexpr ValueType kTypes_##name[] = {__VA_ARGS__}; \
constexpr FunctionSig kSig_##name( \
1, static_cast<int>(arraysize(kTypes_##name)) - 1, kTypes_##name);
FOREACH_SIGNATURE(DECLARE_SIG)
#define DECLARE_SIG_ENTRY(name, ...) &kSig_##name,
static const FunctionSig* kSimpleExprSigs[] = {
constexpr const FunctionSig* kSimpleExprSigs[] = {
nullptr, FOREACH_SIGNATURE(DECLARE_SIG_ENTRY)};
#define DECLARE_SIMD_SIG_ENTRY(name, ...) &kSig_##name,
static const FunctionSig* kSimdExprSigs[] = {
nullptr, FOREACH_SIMD_SIGNATURE(DECLARE_SIMD_SIG_ENTRY)};
static byte kSimpleExprSigTable[256];
static byte kSimpleAsmjsExprSigTable[256];
static byte kSimdExprSigTable[256];
static byte kAtomicExprSigTable[256];
// Initialize the signature table.
static void InitSigTables() {
#define SET_SIG_TABLE(name, opcode, sig) \
kSimpleExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1;
FOREACH_SIMPLE_OPCODE(SET_SIG_TABLE);
#undef SET_SIG_TABLE
#define SET_ASMJS_SIG_TABLE(name, opcode, sig) \
kSimpleAsmjsExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1;
FOREACH_ASMJS_COMPAT_OPCODE(SET_ASMJS_SIG_TABLE);
#undef SET_ASMJS_SIG_TABLE
byte simd_index;
#define SET_SIG_TABLE(name, opcode, sig) \
simd_index = opcode & 0xff; \
kSimdExprSigTable[simd_index] = static_cast<int>(kSigEnum_##sig) + 1;
FOREACH_SIMD_0_OPERAND_OPCODE(SET_SIG_TABLE)
#undef SET_SIG_TABLE
byte atomic_index;
#define SET_ATOMIC_SIG_TABLE(name, opcode, sig) \
atomic_index = opcode & 0xff; \
kAtomicExprSigTable[atomic_index] = static_cast<int>(kSigEnum_##sig) + 1;
FOREACH_ATOMIC_OPCODE(SET_ATOMIC_SIG_TABLE)
#undef SET_ATOMIC_SIG_TABLE
// The following constexpr functions are used to initialize the constant arrays
// defined below. They must have exactly one return statement, and no switch.
constexpr WasmOpcodeSig GetOpcodeSigIndex(byte opcode) {
return
#define CASE(name, opc, sig) opcode == opc ? kSigEnum_##sig:
FOREACH_SIMPLE_OPCODE(CASE)
#undef CASE
kSigEnum_None;
}
class SigTable {
public:
SigTable() {
// TODO(ahaas): Move {InitSigTable} into the class.
InitSigTables();
}
FunctionSig* Signature(WasmOpcode opcode) const {
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleExprSigTable[static_cast<byte>(opcode)]]);
}
FunctionSig* AsmjsSignature(WasmOpcode opcode) const {
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleAsmjsExprSigTable[static_cast<byte>(opcode)]]);
}
FunctionSig* SimdSignature(WasmOpcode opcode) const {
return const_cast<FunctionSig*>(
kSimdExprSigs[kSimdExprSigTable[static_cast<byte>(opcode & 0xff)]]);
}
FunctionSig* AtomicSignature(WasmOpcode opcode) const {
return const_cast<FunctionSig*>(
kSimpleExprSigs[kAtomicExprSigTable[static_cast<byte>(opcode & 0xff)]]);
}
};
constexpr WasmOpcodeSig GetAsmJsOpcodeSigIndex(byte opcode) {
return
#define CASE(name, opc, sig) opcode == opc ? kSigEnum_##sig:
FOREACH_ASMJS_COMPAT_OPCODE(CASE)
#undef CASE
kSigEnum_None;
}
static base::LazyInstance<SigTable>::type sig_table = LAZY_INSTANCE_INITIALIZER;
constexpr WasmOpcodeSig GetSimdOpcodeSigIndex(byte opcode) {
return
#define CASE(name, opc, sig) opcode == (opc & 0xff) ? kSigEnum_##sig:
FOREACH_SIMD_0_OPERAND_OPCODE(CASE)
#undef CASE
kSigEnum_None;
}
constexpr WasmOpcodeSig GetAtomicOpcodeSigIndex(byte opcode) {
return
#define CASE(name, opc, sig) opcode == (opc & 0xff) ? kSigEnum_##sig:
FOREACH_ATOMIC_OPCODE(CASE)
#undef CASE
kSigEnum_None;
}
// gcc 4.7 - 4.9 have a bug which prohibits marking the array constexpr
// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52892).
// TODO(clemensh): Remove this once we require gcc >= 5.0.
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 4
#define CONSTEXPR_IF_NOT_GCC_4
#else
#define CONSTEXPR_IF_NOT_GCC_4 constexpr
#endif
CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kSimpleExprSigTable =
base::make_array<256>(GetOpcodeSigIndex);
CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kSimpleAsmjsExprSigTable =
base::make_array<256>(GetAsmJsOpcodeSigIndex);
CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kSimdExprSigTable =
base::make_array<256>(GetSimdOpcodeSigIndex);
CONSTEXPR_IF_NOT_GCC_4 std::array<WasmOpcodeSig, 256> kAtomicExprSigTable =
base::make_array<256>(GetAtomicOpcodeSigIndex);
} // namespace
FunctionSig* WasmOpcodes::Signature(WasmOpcode opcode) {
if (opcode >> 8 == kSimdPrefix) {
return sig_table.Get().SimdSignature(opcode);
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimdExprSigTable[opcode & 0xff]]);
} else {
return sig_table.Get().Signature(opcode);
DCHECK_GT(kSimpleExprSigTable.size(), static_cast<size_t>(opcode));
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleExprSigTable[opcode]]);
}
}
FunctionSig* WasmOpcodes::AsmjsSignature(WasmOpcode opcode) {
return sig_table.Get().AsmjsSignature(opcode);
DCHECK_GT(kSimpleAsmjsExprSigTable.size(), static_cast<size_t>(opcode));
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleAsmjsExprSigTable[opcode]]);
}
FunctionSig* WasmOpcodes::AtomicSignature(WasmOpcode opcode) {
return sig_table.Get().AtomicSignature(opcode);
return const_cast<FunctionSig*>(
kSimpleExprSigs[kAtomicExprSigTable[opcode & 0xff]]);
}
// TODO(titzer): pull WASM_64 up to a common header.