[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:
parent
eed937b04b
commit
737962f85b
1
BUILD.gn
1
BUILD.gn
@ -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
47
src/base/template-utils.h
Normal 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
|
@ -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) {}
|
||||
|
@ -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',
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user