Commit 737962f8 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[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: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45589}
parent eed937b0
...@@ -2510,6 +2510,7 @@ v8_component("v8_libbase") { ...@@ -2510,6 +2510,7 @@ v8_component("v8_libbase") {
"src/base/safe_math_impl.h", "src/base/safe_math_impl.h",
"src/base/sys-info.cc", "src/base/sys-info.cc",
"src/base/sys-info.h", "src/base/sys-info.h",
"src/base/template-utils.h",
"src/base/timezone-cache.h", "src/base/timezone-cache.h",
"src/base/utils/random-number-generator.cc", "src/base/utils/random-number-generator.cc",
"src/base/utils/random-number-generator.h", "src/base/utils/random-number-generator.h",
......
// 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 { ...@@ -15,7 +15,8 @@ namespace internal {
template <typename T> template <typename T>
class Signature : public ZoneObject { class Signature : public ZoneObject {
public: 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), : return_count_(return_count),
parameter_count_(parameter_count), parameter_count_(parameter_count),
reps_(reps) {} reps_(reps) {}
......
...@@ -2021,6 +2021,7 @@ ...@@ -2021,6 +2021,7 @@
'base/safe_math_impl.h', 'base/safe_math_impl.h',
'base/sys-info.cc', 'base/sys-info.cc',
'base/sys-info.h', 'base/sys-info.h',
'base/template-utils.h',
'base/timezone-cache.h', 'base/timezone-cache.h',
'base/utils/random-number-generator.cc', 'base/utils/random-number-generator.cc',
'base/utils/random-number-generator.h', 'base/utils/random-number-generator.h',
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/wasm/wasm-opcodes.h" #include "src/wasm/wasm-opcodes.h"
#include <array>
#include "src/base/template-utils.h"
#include "src/messages.h" #include "src/messages.h"
#include "src/runtime/runtime.h" #include "src/runtime/runtime.h"
#include "src/signature.h" #include "src/signature.h"
...@@ -309,97 +313,101 @@ bool IsJSCompatibleSignature(const FunctionSig* sig) { ...@@ -309,97 +313,101 @@ bool IsJSCompatibleSignature(const FunctionSig* sig) {
return true; return true;
} }
namespace {
#define DECLARE_SIG_ENUM(name, ...) kSigEnum_##name, #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, ...) \
#define DECLARE_SIG(name, ...) \ constexpr ValueType kTypes_##name[] = {__VA_ARGS__}; \
static ValueType kTypes_##name[] = {__VA_ARGS__}; \ constexpr FunctionSig kSig_##name( \
static const FunctionSig kSig_##name( \
1, static_cast<int>(arraysize(kTypes_##name)) - 1, kTypes_##name); 1, static_cast<int>(arraysize(kTypes_##name)) - 1, kTypes_##name);
FOREACH_SIGNATURE(DECLARE_SIG) FOREACH_SIGNATURE(DECLARE_SIG)
#define DECLARE_SIG_ENTRY(name, ...) &kSig_##name, #define DECLARE_SIG_ENTRY(name, ...) &kSig_##name,
static const FunctionSig* kSimpleExprSigs[] = { constexpr const FunctionSig* kSimpleExprSigs[] = {
nullptr, FOREACH_SIGNATURE(DECLARE_SIG_ENTRY)}; nullptr, FOREACH_SIGNATURE(DECLARE_SIG_ENTRY)};
#define DECLARE_SIMD_SIG_ENTRY(name, ...) &kSig_##name, // The following constexpr functions are used to initialize the constant arrays
// defined below. They must have exactly one return statement, and no switch.
static const FunctionSig* kSimdExprSigs[] = { constexpr WasmOpcodeSig GetOpcodeSigIndex(byte opcode) {
nullptr, FOREACH_SIMD_SIGNATURE(DECLARE_SIMD_SIG_ENTRY)}; return
#define CASE(name, opc, sig) opcode == opc ? kSigEnum_##sig:
static byte kSimpleExprSigTable[256]; FOREACH_SIMPLE_OPCODE(CASE)
static byte kSimpleAsmjsExprSigTable[256]; #undef CASE
static byte kSimdExprSigTable[256]; kSigEnum_None;
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
} }
class SigTable { constexpr WasmOpcodeSig GetAsmJsOpcodeSigIndex(byte opcode) {
public: return
SigTable() { #define CASE(name, opc, sig) opcode == opc ? kSigEnum_##sig:
// TODO(ahaas): Move {InitSigTable} into the class. FOREACH_ASMJS_COMPAT_OPCODE(CASE)
InitSigTables(); #undef CASE
} kSigEnum_None;
FunctionSig* Signature(WasmOpcode opcode) const { }
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleExprSigTable[static_cast<byte>(opcode)]]); constexpr WasmOpcodeSig GetSimdOpcodeSigIndex(byte opcode) {
} return
FunctionSig* AsmjsSignature(WasmOpcode opcode) const { #define CASE(name, opc, sig) opcode == (opc & 0xff) ? kSigEnum_##sig:
return const_cast<FunctionSig*>( FOREACH_SIMD_0_OPERAND_OPCODE(CASE)
kSimpleExprSigs[kSimpleAsmjsExprSigTable[static_cast<byte>(opcode)]]); #undef CASE
} kSigEnum_None;
FunctionSig* SimdSignature(WasmOpcode opcode) const { }
return const_cast<FunctionSig*>(
kSimdExprSigs[kSimdExprSigTable[static_cast<byte>(opcode & 0xff)]]); constexpr WasmOpcodeSig GetAtomicOpcodeSigIndex(byte opcode) {
} return
FunctionSig* AtomicSignature(WasmOpcode opcode) const { #define CASE(name, opc, sig) opcode == (opc & 0xff) ? kSigEnum_##sig:
return const_cast<FunctionSig*>( FOREACH_ATOMIC_OPCODE(CASE)
kSimpleExprSigs[kAtomicExprSigTable[static_cast<byte>(opcode & 0xff)]]); #undef CASE
} kSigEnum_None;
}; }
static base::LazyInstance<SigTable>::type sig_table = LAZY_INSTANCE_INITIALIZER; // 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) { FunctionSig* WasmOpcodes::Signature(WasmOpcode opcode) {
if (opcode >> 8 == kSimdPrefix) { if (opcode >> 8 == kSimdPrefix) {
return sig_table.Get().SimdSignature(opcode); return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimdExprSigTable[opcode & 0xff]]);
} else { } 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) { 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) { 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. // TODO(titzer): pull WASM_64 up to a common header.
......
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