11cb0ef41Sopenharmony_ci// Copyright 2018 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#if !V8_ENABLE_WEBASSEMBLY
61cb0ef41Sopenharmony_ci#error This header should only be included if WebAssembly is enabled.
71cb0ef41Sopenharmony_ci#endif  // !V8_ENABLE_WEBASSEMBLY
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci#ifndef V8_WASM_JUMP_TABLE_ASSEMBLER_H_
101cb0ef41Sopenharmony_ci#define V8_WASM_JUMP_TABLE_ASSEMBLER_H_
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ci#include "src/codegen/macro-assembler.h"
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_cinamespace v8 {
151cb0ef41Sopenharmony_cinamespace internal {
161cb0ef41Sopenharmony_cinamespace wasm {
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ci// The jump table is the central dispatch point for all (direct and indirect)
191cb0ef41Sopenharmony_ci// invocations in WebAssembly. It holds one slot per function in a module, with
201cb0ef41Sopenharmony_ci// each slot containing a dispatch to the currently published {WasmCode} that
211cb0ef41Sopenharmony_ci// corresponds to the function.
221cb0ef41Sopenharmony_ci//
231cb0ef41Sopenharmony_ci// Additionally to this main jump table, there exist special jump tables for
241cb0ef41Sopenharmony_ci// other purposes:
251cb0ef41Sopenharmony_ci// - the far stub table contains one entry per wasm runtime stub (see
261cb0ef41Sopenharmony_ci//   {WasmCode::RuntimeStubId}, which jumps to the corresponding embedded
271cb0ef41Sopenharmony_ci//   builtin, plus (if not the full address space can be reached via the jump
281cb0ef41Sopenharmony_ci//   table) one entry per wasm function.
291cb0ef41Sopenharmony_ci// - the lazy compile table contains one entry per wasm function which jumps to
301cb0ef41Sopenharmony_ci//   the common {WasmCompileLazy} builtin and passes the function index that was
311cb0ef41Sopenharmony_ci//   invoked.
321cb0ef41Sopenharmony_ci//
331cb0ef41Sopenharmony_ci// The main jump table is split into lines of fixed size, with lines laid out
341cb0ef41Sopenharmony_ci// consecutively within the executable memory of the {NativeModule}. The slots
351cb0ef41Sopenharmony_ci// in turn are consecutive within a line, but do not cross line boundaries.
361cb0ef41Sopenharmony_ci//
371cb0ef41Sopenharmony_ci//   +- L1 -------------------+ +- L2 -------------------+ +- L3 ...
381cb0ef41Sopenharmony_ci//   | S1 | S2 | ... | Sn | x | | S1 | S2 | ... | Sn | x | | S1  ...
391cb0ef41Sopenharmony_ci//   +------------------------+ +------------------------+ +---- ...
401cb0ef41Sopenharmony_ci//
411cb0ef41Sopenharmony_ci// The above illustrates jump table lines {Li} containing slots {Si} with each
421cb0ef41Sopenharmony_ci// line containing {n} slots and some padding {x} for alignment purposes.
431cb0ef41Sopenharmony_ci// Other jump tables are just consecutive.
441cb0ef41Sopenharmony_ci//
451cb0ef41Sopenharmony_ci// The main jump table will be patched concurrently while other threads execute
461cb0ef41Sopenharmony_ci// it. The code at the new target might also have been emitted concurrently, so
471cb0ef41Sopenharmony_ci// we need to ensure that there is proper synchronization between code emission,
481cb0ef41Sopenharmony_ci// jump table patching and code execution.
491cb0ef41Sopenharmony_ci// On Intel platforms, this all works out of the box because there is cache
501cb0ef41Sopenharmony_ci// coherency between i-cache and d-cache.
511cb0ef41Sopenharmony_ci// On ARM, it is safe because the i-cache flush after code emission executes an
521cb0ef41Sopenharmony_ci// "ic ivau" (Instruction Cache line Invalidate by Virtual Address to Point of
531cb0ef41Sopenharmony_ci// Unification), which broadcasts to all cores. A core which sees the jump table
541cb0ef41Sopenharmony_ci// update thus also sees the new code. Since the other core does not explicitly
551cb0ef41Sopenharmony_ci// execute an "isb" (Instruction Synchronization Barrier), it might still
561cb0ef41Sopenharmony_ci// execute the old code afterwards, which is no problem, since that code remains
571cb0ef41Sopenharmony_ci// available until it is garbage collected. Garbage collection itself is a
581cb0ef41Sopenharmony_ci// synchronization barrier though.
591cb0ef41Sopenharmony_ciclass V8_EXPORT_PRIVATE JumpTableAssembler : public MacroAssembler {
601cb0ef41Sopenharmony_ci public:
611cb0ef41Sopenharmony_ci  // Translate an offset into the continuous jump table to a jump table index.
621cb0ef41Sopenharmony_ci  static uint32_t SlotOffsetToIndex(uint32_t slot_offset) {
631cb0ef41Sopenharmony_ci    uint32_t line_index = slot_offset / kJumpTableLineSize;
641cb0ef41Sopenharmony_ci    uint32_t line_offset = slot_offset % kJumpTableLineSize;
651cb0ef41Sopenharmony_ci    DCHECK_EQ(0, line_offset % kJumpTableSlotSize);
661cb0ef41Sopenharmony_ci    return line_index * kJumpTableSlotsPerLine +
671cb0ef41Sopenharmony_ci           line_offset / kJumpTableSlotSize;
681cb0ef41Sopenharmony_ci  }
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci  // Translate a jump table index to an offset into the continuous jump table.
711cb0ef41Sopenharmony_ci  static uint32_t JumpSlotIndexToOffset(uint32_t slot_index) {
721cb0ef41Sopenharmony_ci    uint32_t line_index = slot_index / kJumpTableSlotsPerLine;
731cb0ef41Sopenharmony_ci    uint32_t line_offset =
741cb0ef41Sopenharmony_ci        (slot_index % kJumpTableSlotsPerLine) * kJumpTableSlotSize;
751cb0ef41Sopenharmony_ci    return line_index * kJumpTableLineSize + line_offset;
761cb0ef41Sopenharmony_ci  }
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci  // Determine the size of a jump table containing the given number of slots.
791cb0ef41Sopenharmony_ci  static constexpr uint32_t SizeForNumberOfSlots(uint32_t slot_count) {
801cb0ef41Sopenharmony_ci    return ((slot_count + kJumpTableSlotsPerLine - 1) /
811cb0ef41Sopenharmony_ci            kJumpTableSlotsPerLine) *
821cb0ef41Sopenharmony_ci           kJumpTableLineSize;
831cb0ef41Sopenharmony_ci  }
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci  // Translate a far jump table index to an offset into the table.
861cb0ef41Sopenharmony_ci  static uint32_t FarJumpSlotIndexToOffset(uint32_t slot_index) {
871cb0ef41Sopenharmony_ci    return slot_index * kFarJumpTableSlotSize;
881cb0ef41Sopenharmony_ci  }
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci  // Translate a far jump table offset to the index into the table.
911cb0ef41Sopenharmony_ci  static uint32_t FarJumpSlotOffsetToIndex(uint32_t offset) {
921cb0ef41Sopenharmony_ci    DCHECK_EQ(0, offset % kFarJumpTableSlotSize);
931cb0ef41Sopenharmony_ci    return offset / kFarJumpTableSlotSize;
941cb0ef41Sopenharmony_ci  }
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  // Determine the size of a far jump table containing the given number of
971cb0ef41Sopenharmony_ci  // slots.
981cb0ef41Sopenharmony_ci  static constexpr uint32_t SizeForNumberOfFarJumpSlots(
991cb0ef41Sopenharmony_ci      int num_runtime_slots, int num_function_slots) {
1001cb0ef41Sopenharmony_ci    int num_entries = num_runtime_slots + num_function_slots;
1011cb0ef41Sopenharmony_ci    return num_entries * kFarJumpTableSlotSize;
1021cb0ef41Sopenharmony_ci  }
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci  // Translate a slot index to an offset into the lazy compile table.
1051cb0ef41Sopenharmony_ci  static uint32_t LazyCompileSlotIndexToOffset(uint32_t slot_index) {
1061cb0ef41Sopenharmony_ci    return slot_index * kLazyCompileTableSlotSize;
1071cb0ef41Sopenharmony_ci  }
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci  // Determine the size of a lazy compile table.
1101cb0ef41Sopenharmony_ci  static constexpr uint32_t SizeForNumberOfLazyFunctions(uint32_t slot_count) {
1111cb0ef41Sopenharmony_ci    return slot_count * kLazyCompileTableSlotSize;
1121cb0ef41Sopenharmony_ci  }
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci  static void GenerateLazyCompileTable(Address base, uint32_t num_slots,
1151cb0ef41Sopenharmony_ci                                       uint32_t num_imported_functions,
1161cb0ef41Sopenharmony_ci                                       Address wasm_compile_lazy_target) {
1171cb0ef41Sopenharmony_ci    uint32_t lazy_compile_table_size = num_slots * kLazyCompileTableSlotSize;
1181cb0ef41Sopenharmony_ci    // Assume enough space, so the Assembler does not try to grow the buffer.
1191cb0ef41Sopenharmony_ci    JumpTableAssembler jtasm(base, lazy_compile_table_size + 256);
1201cb0ef41Sopenharmony_ci    for (uint32_t slot_index = 0; slot_index < num_slots; ++slot_index) {
1211cb0ef41Sopenharmony_ci      DCHECK_EQ(slot_index * kLazyCompileTableSlotSize, jtasm.pc_offset());
1221cb0ef41Sopenharmony_ci      jtasm.EmitLazyCompileJumpSlot(slot_index + num_imported_functions,
1231cb0ef41Sopenharmony_ci                                    wasm_compile_lazy_target);
1241cb0ef41Sopenharmony_ci    }
1251cb0ef41Sopenharmony_ci    DCHECK_EQ(lazy_compile_table_size, jtasm.pc_offset());
1261cb0ef41Sopenharmony_ci    FlushInstructionCache(base, lazy_compile_table_size);
1271cb0ef41Sopenharmony_ci  }
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  static void GenerateFarJumpTable(Address base, Address* stub_targets,
1301cb0ef41Sopenharmony_ci                                   int num_runtime_slots,
1311cb0ef41Sopenharmony_ci                                   int num_function_slots) {
1321cb0ef41Sopenharmony_ci    uint32_t table_size =
1331cb0ef41Sopenharmony_ci        SizeForNumberOfFarJumpSlots(num_runtime_slots, num_function_slots);
1341cb0ef41Sopenharmony_ci    // Assume enough space, so the Assembler does not try to grow the buffer.
1351cb0ef41Sopenharmony_ci    JumpTableAssembler jtasm(base, table_size + 256);
1361cb0ef41Sopenharmony_ci    int offset = 0;
1371cb0ef41Sopenharmony_ci    for (int index = 0; index < num_runtime_slots + num_function_slots;
1381cb0ef41Sopenharmony_ci         ++index) {
1391cb0ef41Sopenharmony_ci      DCHECK_EQ(offset, FarJumpSlotIndexToOffset(index));
1401cb0ef41Sopenharmony_ci      // Functions slots initially jump to themselves. They are patched before
1411cb0ef41Sopenharmony_ci      // being used.
1421cb0ef41Sopenharmony_ci      Address target =
1431cb0ef41Sopenharmony_ci          index < num_runtime_slots ? stub_targets[index] : base + offset;
1441cb0ef41Sopenharmony_ci      jtasm.EmitFarJumpSlot(target);
1451cb0ef41Sopenharmony_ci      offset += kFarJumpTableSlotSize;
1461cb0ef41Sopenharmony_ci      DCHECK_EQ(offset, jtasm.pc_offset());
1471cb0ef41Sopenharmony_ci    }
1481cb0ef41Sopenharmony_ci    FlushInstructionCache(base, table_size);
1491cb0ef41Sopenharmony_ci  }
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci  static void PatchJumpTableSlot(Address jump_table_slot,
1521cb0ef41Sopenharmony_ci                                 Address far_jump_table_slot, Address target) {
1531cb0ef41Sopenharmony_ci    // First, try to patch the jump table slot.
1541cb0ef41Sopenharmony_ci    JumpTableAssembler jtasm(jump_table_slot);
1551cb0ef41Sopenharmony_ci    if (!jtasm.EmitJumpSlot(target)) {
1561cb0ef41Sopenharmony_ci      // If that fails, we need to patch the far jump table slot, and then
1571cb0ef41Sopenharmony_ci      // update the jump table slot to jump to this far jump table slot.
1581cb0ef41Sopenharmony_ci      DCHECK_NE(kNullAddress, far_jump_table_slot);
1591cb0ef41Sopenharmony_ci      JumpTableAssembler::PatchFarJumpSlot(far_jump_table_slot, target);
1601cb0ef41Sopenharmony_ci      CHECK(jtasm.EmitJumpSlot(far_jump_table_slot));
1611cb0ef41Sopenharmony_ci    }
1621cb0ef41Sopenharmony_ci    jtasm.NopBytes(kJumpTableSlotSize - jtasm.pc_offset());
1631cb0ef41Sopenharmony_ci    FlushInstructionCache(jump_table_slot, kJumpTableSlotSize);
1641cb0ef41Sopenharmony_ci  }
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci private:
1671cb0ef41Sopenharmony_ci  // Instantiate a {JumpTableAssembler} for patching.
1681cb0ef41Sopenharmony_ci  explicit JumpTableAssembler(Address slot_addr, int size = 256)
1691cb0ef41Sopenharmony_ci      : MacroAssembler(nullptr, JumpTableAssemblerOptions(),
1701cb0ef41Sopenharmony_ci                       CodeObjectRequired::kNo,
1711cb0ef41Sopenharmony_ci                       ExternalAssemblerBuffer(
1721cb0ef41Sopenharmony_ci                           reinterpret_cast<uint8_t*>(slot_addr), size)) {}
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci// To allow concurrent patching of the jump table entries, we need to ensure
1751cb0ef41Sopenharmony_ci// that the instruction containing the call target does not cross cache-line
1761cb0ef41Sopenharmony_ci// boundaries. The jump table line size has been chosen to satisfy this.
1771cb0ef41Sopenharmony_ci#if V8_TARGET_ARCH_X64
1781cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 64;
1791cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 5;
1801cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 16;
1811cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 10;
1821cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_IA32
1831cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 64;
1841cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 5;
1851cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 5;
1861cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 10;
1871cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_ARM
1881cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 3 * kInstrSize;
1891cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 3 * kInstrSize;
1901cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 2 * kInstrSize;
1911cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 5 * kInstrSize;
1921cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_ARM64 && V8_ENABLE_CONTROL_FLOW_INTEGRITY
1931cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 2 * kInstrSize;
1941cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 2 * kInstrSize;
1951cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 6 * kInstrSize;
1961cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 4 * kInstrSize;
1971cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_ARM64 && !V8_ENABLE_CONTROL_FLOW_INTEGRITY
1981cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 1 * kInstrSize;
1991cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 1 * kInstrSize;
2001cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 4 * kInstrSize;
2011cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 3 * kInstrSize;
2021cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_S390X
2031cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 128;
2041cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 8;
2051cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 16;
2061cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 20;
2071cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_PPC64
2081cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 64;
2091cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 1 * kInstrSize;
2101cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 12 * kInstrSize;
2111cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 12 * kInstrSize;
2121cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_MIPS
2131cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 8 * kInstrSize;
2141cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 8 * kInstrSize;
2151cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 4 * kInstrSize;
2161cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 6 * kInstrSize;
2171cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_MIPS64
2181cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 8 * kInstrSize;
2191cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 8 * kInstrSize;
2201cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 6 * kInstrSize;
2211cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 8 * kInstrSize;
2221cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_RISCV64
2231cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 6 * kInstrSize;
2241cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 6 * kInstrSize;
2251cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 6 * kInstrSize;
2261cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 10 * kInstrSize;
2271cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_LOONG64
2281cb0ef41Sopenharmony_ci  static constexpr int kJumpTableLineSize = 8 * kInstrSize;
2291cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotSize = 8 * kInstrSize;
2301cb0ef41Sopenharmony_ci  static constexpr int kFarJumpTableSlotSize = 4 * kInstrSize;
2311cb0ef41Sopenharmony_ci  static constexpr int kLazyCompileTableSlotSize = 8 * kInstrSize;
2321cb0ef41Sopenharmony_ci#else
2331cb0ef41Sopenharmony_ci#error Unknown architecture.
2341cb0ef41Sopenharmony_ci#endif
2351cb0ef41Sopenharmony_ci
2361cb0ef41Sopenharmony_ci  static constexpr int kJumpTableSlotsPerLine =
2371cb0ef41Sopenharmony_ci      kJumpTableLineSize / kJumpTableSlotSize;
2381cb0ef41Sopenharmony_ci  STATIC_ASSERT(kJumpTableSlotsPerLine >= 1);
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci  // {JumpTableAssembler} is never used during snapshot generation, and its code
2411cb0ef41Sopenharmony_ci  // must be independent of the code range of any isolate anyway. Just ensure
2421cb0ef41Sopenharmony_ci  // that no relocation information is recorded, there is no buffer to store it
2431cb0ef41Sopenharmony_ci  // since it is instantiated in patching mode in existing code directly.
2441cb0ef41Sopenharmony_ci  static AssemblerOptions JumpTableAssemblerOptions() {
2451cb0ef41Sopenharmony_ci    AssemblerOptions options;
2461cb0ef41Sopenharmony_ci    options.disable_reloc_info_for_patching = true;
2471cb0ef41Sopenharmony_ci    return options;
2481cb0ef41Sopenharmony_ci  }
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci  void EmitLazyCompileJumpSlot(uint32_t func_index,
2511cb0ef41Sopenharmony_ci                               Address lazy_compile_target);
2521cb0ef41Sopenharmony_ci
2531cb0ef41Sopenharmony_ci  // Returns {true} if the jump fits in the jump table slot, {false} otherwise.
2541cb0ef41Sopenharmony_ci  bool EmitJumpSlot(Address target);
2551cb0ef41Sopenharmony_ci
2561cb0ef41Sopenharmony_ci  // Initially emit a far jump slot.
2571cb0ef41Sopenharmony_ci  void EmitFarJumpSlot(Address target);
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci  // Patch an existing far jump slot, and make sure that this updated eventually
2601cb0ef41Sopenharmony_ci  // becomes available to all execution units that might execute this code.
2611cb0ef41Sopenharmony_ci  static void PatchFarJumpSlot(Address slot, Address target);
2621cb0ef41Sopenharmony_ci
2631cb0ef41Sopenharmony_ci  void NopBytes(int bytes);
2641cb0ef41Sopenharmony_ci};
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci}  // namespace wasm
2671cb0ef41Sopenharmony_ci}  // namespace internal
2681cb0ef41Sopenharmony_ci}  // namespace v8
2691cb0ef41Sopenharmony_ci
2701cb0ef41Sopenharmony_ci#endif  // V8_WASM_JUMP_TABLE_ASSEMBLER_H_
271