Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/wasm2c/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 63 kB image not shown  

Quelle  binary-writer.cc   Sprache: C

 
/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "wabt/binary-writer.h"

#include <cassert>
#include <cmath>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <set>
#include <string_view>
#include <vector>

#include "wabt/config.h"

#include "wabt/binary.h"
#include "wabt/cast.h"
#include "wabt/expr-visitor.h"
#include "wabt/ir.h"
#include "wabt/leb128.h"
#include "wabt/stream.h"

#define PRINT_HEADER_NO_INDEX -1
#define MAX_U32_LEB128_BYTES 5

namespace wabt {

void WriteStr(Stream* stream,
              std::string_view s,
              const char* desc,
              PrintChars print_chars) {
  WriteU32Leb128(stream, s.length(), "string length");
  stream->WriteData(s.data(), s.length(), desc, print_chars);
}

void WriteOpcode(Stream* stream, Opcode opcode) {
  if (opcode.HasPrefix()) {
    stream->WriteU8(opcode.GetPrefix(), "prefix");
    WriteU32Leb128(stream, opcode.GetCode(), opcode.GetName());
  } else {
    stream->WriteU8(opcode.GetCode(), opcode.GetName());
  }
}

void WriteType(Stream* stream, Type type, const char* desc) {
  WriteS32Leb128(stream, type, desc ? desc : type.GetName().c_str());
  if (type.IsReferenceWithIndex()) {
    WriteS32Leb128(stream, type.GetReferenceIndex(),
                   desc ? desc : type.GetName().c_str());
  }
}

void WriteLimitsFlags(Stream* stream, uint32_t flags) {
  WriteU32Leb128(stream, flags, "limits: flags");
}

uint32_t ComputeLimitsFlags(const Limits* limits) {
  uint32_t flags = limits->has_max ? WABT_BINARY_LIMITS_HAS_MAX_FLAG : 0;
  flags |= limits->is_shared ? WABT_BINARY_LIMITS_IS_SHARED_FLAG : 0;
  flags |= limits->is_64 ? WABT_BINARY_LIMITS_IS_64_FLAG : 0;
  return flags;
}

void WriteLimitsData(Stream* stream, const Limits* limits) {
  if (limits->is_64) {
    WriteU64Leb128(stream, limits->initial, "limits: initial");
    if (limits->has_max) {
      WriteU64Leb128(stream, limits->max, "limits: max");
    }
  } else {
    WriteU32Leb128(stream, limits->initial, "limits: initial");
    if (limits->has_max) {
      WriteU32Leb128(stream, limits->max, "limits: max");
    }
  }
}

void WriteDebugName(Stream* stream, std::string_view name, const char* desc) {
  std::string_view stripped_name = name;
  if (!stripped_name.empty()) {
    // Strip leading $ from name
    assert(stripped_name.front() == '$');
    stripped_name.remove_prefix(1);
  }
  WriteStr(stream, stripped_name, desc, PrintChars::Yes);
}

namespace {

/* TODO(binji): better leb size guess. Some sections we know will only be 1
 byte, but others we can be fairly certain will be larger. */

constexpr size_t LEB_SECTION_SIZE_GUESS = 1;

#define ALLOC_FAILURE \
  fprintf(stderr, "%s:%d: allocation failed\n", __FILE__, __LINE__)

struct RelocSection {
  RelocSection(const char* name, Index index)
      : name(name), section_index(index) {}

  const char* name;
  Index section_index;
  std::vector<Reloc> relocations;
};

class Symbol {
 public:
  struct Function {
    static const SymbolType type = SymbolType::Function;
    Index index;
  };
  struct Data {
    static const SymbolType type = SymbolType::Data;
    Index index;
    Offset offset;
    Address size;
  };
  struct Global {
    static const SymbolType type = SymbolType::Global;
    Index index;
  };
  struct Section {
    static const SymbolType type = SymbolType::Section;
    Index section;
  };
  struct Tag {
    static const SymbolType type = SymbolType::Tag;
    Index index;
  };
  struct Table {
    static const SymbolType type = SymbolType::Table;
    Index index;
  };

 private:
  SymbolType type_;
  std::string_view name_;
  uint8_t flags_;
  union {
    Function function_;
    Data data_;
    Global global_;
    Section section_;
    Tag tag_;
    Table table_;
  };

 public:
  Symbol(const std::string_view& name, uint8_t flags, const Function& f)
      : type_(Function::type), name_(name), flags_(flags), function_(f) {}
  Symbol(const std::string_view& name, uint8_t flags, const Data& d)
      : type_(Data::type), name_(name), flags_(flags), data_(d) {}
  Symbol(const std::string_view& name, uint8_t flags, const Global& g)
      : type_(Global::type), name_(name), flags_(flags), global_(g) {}
  Symbol(const std::string_view& name, uint8_t flags, const Section& s)
      : type_(Section::type), name_(name), flags_(flags), section_(s) {}
  Symbol(const std::string_view& name, uint8_t flags, const Tag& e)
      : type_(Tag::type), name_(name), flags_(flags), tag_(e) {}
  Symbol(const std::string_view& name, uint8_t flags, const Table& t)
      : type_(Table::type), name_(name), flags_(flags), table_(t) {}

  SymbolType type() const { return type_; }
  const std::string_view& name() const { return name_; }
  uint8_t flags() const { return flags_; }

  SymbolVisibility visibility() const {
    return static_cast<SymbolVisibility>(flags() & WABT_SYMBOL_MASK_VISIBILITY);
  }
  SymbolBinding binding() const {
    return static_cast<SymbolBinding>(flags() & WABT_SYMBOL_MASK_BINDING);
  }
  bool undefined() const { return flags() & WABT_SYMBOL_FLAG_UNDEFINED; }
  bool defined() const { return !undefined(); }
  bool exported() const { return flags() & WABT_SYMBOL_FLAG_EXPORTED; }
  bool explicit_name() const {
    return flags() & WABT_SYMBOL_FLAG_EXPLICIT_NAME;
  }
  bool no_strip() const { return flags() & WABT_SYMBOL_FLAG_NO_STRIP; }

  bool IsFunction() const { return type() == Function::type; }
  bool IsData() const { return type() == Data::type; }
  bool IsGlobal() const { return type() == Global::type; }
  bool IsSection() const { return type() == Section::type; }
  bool IsTag() const { return type() == Tag::type; }
  bool IsTable() const { return type() == Table::type; }

  const Function& AsFunction() const {
    assert(IsFunction());
    return function_;
  }
  const Data& AsData() const {
    assert(IsData());
    return data_;
  }
  const Global& AsGlobal() const {
    assert(IsGlobal());
    return global_;
  }
  const Section& AsSection() const {
    assert(IsSection());
    return section_;
  }
  const Tag& AsTag() const {
    assert(IsTag());
    return tag_;
  }
  const Table& AsTable() const {
    assert(IsTable());
    return table_;
  }
};

class SymbolTable {
  WABT_DISALLOW_COPY_AND_ASSIGN(SymbolTable);

  std::vector<Symbol> symbols_;

  std::vector<Index> functions_;
  std::vector<Index> tables_;
  std::vector<Index> globals_;

  std::set<std::string_view> seen_names_;

  Result EnsureUnique(const std::string_view& name) {
    if (seen_names_.count(name)) {
      fprintf(stderr,
              "error: duplicate symbol when writing relocatable "
              "binary: %s\n",
              &name[0]);
      return Result::Error;
    }
    seen_names_.insert(name);
    return Result::Ok;
  };

  template <typename T>
  Result AddSymbol(std::vector<Index>* map,
                   std::string_view name,
                   bool imported,
                   bool exported,
                   T&& sym) {
    uint8_t flags = 0;
    if (imported) {
      flags |= WABT_SYMBOL_FLAG_UNDEFINED;
      // Wabt currently has no way for a user to explicitly specify the name of
      // an import, so never set the EXPLICIT_NAME flag, and ignore any display
      // name fabricated by wabt.
      name = std::string_view();
    } else {
      if (name.empty()) {
        // Definitions without a name are local.
        flags |= uint8_t(SymbolBinding::Local);
        flags |= uint8_t(SymbolVisibility::Hidden);
      } else {
        // Otherwise, strip the dollar off the name; a definition $foo is
        // available for linking as "foo".
        assert(name[0] == '$');
        name.remove_prefix(1);
      }

      if (exported) {
        CHECK_RESULT(EnsureUnique(name));
        flags |= uint8_t(SymbolVisibility::Hidden);
        flags |= WABT_SYMBOL_FLAG_NO_STRIP;
      }
    }
    if (exported) {
      flags |= WABT_SYMBOL_FLAG_EXPORTED;
    }

    map->push_back(symbols_.size());
    symbols_.emplace_back(name, flags, sym);
    return Result::Ok;
  };

  Index SymbolIndex(const std::vector<Index>& table, Index index) const {
    // For well-formed modules, an index into (e.g.) functions_ will always be
    // within bounds; the out-of-bounds case here is just to allow --relocatable
    // to write known-invalid modules.
    return index < table.size() ? table[index] : kInvalidIndex;
  }

 public:
  SymbolTable() {}

  Result Populate(const Module* module) {
    std::set<Index> exported_funcs;
    std::set<Index> exported_globals;
    std::set<Index> exported_tags;
    std::set<Index> exported_tables;

    for (const Export* export_ : module->exports) {
      switch (export_->kind) {
        case ExternalKind::Func:
          exported_funcs.insert(module->GetFuncIndex(export_->var));
          break;
        case ExternalKind::Table:
          exported_tables.insert(module->GetTableIndex(export_->var));
          break;
        case ExternalKind::Memory:
          break;
        case ExternalKind::Global:
          exported_globals.insert(module->GetGlobalIndex(export_->var));
          break;
        case ExternalKind::Tag:
          exported_tags.insert(module->GetTagIndex(export_->var));
          break;
      }
    }

    // We currently only create symbol table entries for function, table, and
    // global symbols.
    for (size_t i = 0; i < module->funcs.size(); ++i) {
      const Func* func = module->funcs[i];
      bool imported = i < module->num_func_imports;
      bool exported = exported_funcs.count(i);
      CHECK_RESULT(AddSymbol(&functions_, func->name, imported, exported,
                             Symbol::Function{Index(i)}));
    }

    for (size_t i = 0; i < module->tables.size(); ++i) {
      const Table* table = module->tables[i];
      bool imported = i < module->num_table_imports;
      bool exported = exported_tables.count(i);
      CHECK_RESULT(AddSymbol(&tables_, table->name, imported, exported,
                             Symbol::Table{Index(i)}));
    }

    for (size_t i = 0; i < module->globals.size(); ++i) {
      const Global* global = module->globals[i];
      bool imported = i < module->num_global_imports;
      bool exported = exported_globals.count(i);
      CHECK_RESULT(AddSymbol(&globals_, global->name, imported, exported,
                             Symbol::Global{Index(i)}));
    }

    return Result::Ok;
  }

  const std::vector<Symbol>& symbols() const { return symbols_; }
  Index FunctionSymbolIndex(Index index) const {
    return SymbolIndex(functions_, index);
  }
  Index TableSymbolIndex(Index index) const {
    return SymbolIndex(tables_, index);
  }
  Index GlobalSymbolIndex(Index index) const {
    return SymbolIndex(globals_, index);
  }
};

struct CodeMetadata {
  Offset offset;
  std::vector<uint8_t> data;
  CodeMetadata(Offset offset, std::vector<uint8_t> data)
      : offset(offset), data(std::move(data)) {}
};
struct FuncCodeMetadata {
  Index func_idx;
  std::vector<CodeMetadata> entries;
  FuncCodeMetadata(Index func_idx) : func_idx(func_idx) {}
};
struct CodeMetadataSection {
  std::vector<FuncCodeMetadata> entries;
};
using CodeMetadataSections =
    std::unordered_map<std::string_view, CodeMetadataSection>;

class BinaryWriter {
  WABT_DISALLOW_COPY_AND_ASSIGN(BinaryWriter);

 public:
  BinaryWriter(Stream*,
               const WriteBinaryOptions& options,
               const Module* module);

  Result WriteModule();

 private:
  void WriteHeader(const char* name, int index);
  Offset WriteU32Leb128Space(Offset leb_size_guess, const char* desc);
  Offset WriteFixupU32Leb128Size(Offset offset,
                                 Offset leb_size_guess,
                                 const char* desc);
  void BeginKnownSection(BinarySection section_code);
  void BeginCustomSection(const char* name);
  void WriteSectionHeader(const char* desc, BinarySection section_code);
  void EndSection();
  void BeginSubsection(const char* name);
  void EndSubsection();
  Index GetLabelVarDepth(const Var* var);
  Index GetTagVarDepth(const Var* var);
  Index GetLocalIndex(const Func* func, const Var& var);
  Index GetSymbolIndex(RelocType reloc_type, Index index);
  void AddReloc(RelocType reloc_type, Index index);
  void WriteBlockDecl(const BlockDeclaration& decl);
  void WriteU32Leb128WithReloc(Index index,
                               const char* desc,
                               RelocType reloc_type);
  void WriteS32Leb128WithReloc(int32_t value,
                               const char* desc,
                               RelocType reloc_type);
  void WriteTableNumberWithReloc(Index table_number, const char* desc);
  template <typename T>
  void WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc);
  template <typename T>
  void WriteSimdLoadStoreLaneExpr(const Func* func,
                                  const Expr* expr,
                                  const char* desc);
  void WriteExpr(const Func* func, const Expr* expr);
  void WriteExprList(const Func* func, const ExprList& exprs);
  void WriteInitExpr(const ExprList& expr);
  void WriteFuncLocals(const Func* func, const LocalTypes& local_types);
  void WriteFunc(const Func* func);
  void WriteTable(const Table* table);
  void WriteMemory(const Memory* memory);
  void WriteGlobalHeader(const Global* global);
  void WriteTagType(const Tag* tag);
  void WriteRelocSection(const RelocSection* reloc_section);
  void WriteLinkingSection();
  template <typename T>
  void WriteNames(const std::vector<T*>& elems, NameSectionSubsection type);
  void WriteCodeMetadataSections();

  Stream* stream_;
  const WriteBinaryOptions& options_;
  const Module* module_;

  SymbolTable symtab_;
  std::vector<RelocSection> reloc_sections_;
  RelocSection* current_reloc_section_ = nullptr;

  Index section_count_ = 0;
  size_t last_section_offset_ = 0;
  size_t last_section_leb_size_guess_ = 0;
  BinarySection last_section_type_ = BinarySection::Invalid;
  size_t last_section_payload_offset_ = 0;

  size_t last_subsection_offset_ = 0;
  size_t last_subsection_leb_size_guess_ = 0;
  size_t last_subsection_payload_offset_ = 0;

  // Information about the data count section, so it can be removed if it is
  // not needed, and relocs relative to the code section patched up.
  size_t code_start_ = 0;
  size_t data_count_start_ = 0;
  size_t data_count_end_ = 0;
  bool has_data_segment_instruction_ = false;

  CodeMetadataSections code_metadata_sections_;
  Offset cur_func_start_offset_;
  Index cur_func_index_;
};

static uint8_t log2_u32(uint32_t x) {
  uint8_t result = 0;
  while (x > 1) {
    x >>= 1;
    result++;
  }
  return result;
}

BinaryWriter::BinaryWriter(Stream* stream,
                           const WriteBinaryOptions& options,
                           const Module* module)
    : stream_(stream), options_(options), module_(module) {}

void BinaryWriter::WriteHeader(const char* name, int index) {
  if (stream_->has_log_stream()) {
    if (index == PRINT_HEADER_NO_INDEX) {
      stream_->log_stream().Writef("; %s\n", name);
    } else {
      stream_->log_stream().Writef("; %s %d\n", name, index);
    }
  }
}

/* returns offset of leb128 */
Offset BinaryWriter::WriteU32Leb128Space(Offset leb_size_guess,
                                         const char* desc) {
  assert(leb_size_guess <= MAX_U32_LEB128_BYTES);
  uint8_t data[MAX_U32_LEB128_BYTES] = {0};
  Offset result = stream_->offset();
  Offset bytes_to_write =
      options_.canonicalize_lebs ? leb_size_guess : MAX_U32_LEB128_BYTES;
  stream_->WriteData(data, bytes_to_write, desc);
  return result;
}

Offset BinaryWriter::WriteFixupU32Leb128Size(Offset offset,
                                             Offset leb_size_guess,
                                             const char* desc) {
  if (options_.canonicalize_lebs) {
    Offset size = stream_->offset() - offset - leb_size_guess;
    Offset leb_size = U32Leb128Length(size);
    Offset delta = leb_size - leb_size_guess;
    if (delta != 0) {
      Offset src_offset = offset + leb_size_guess;
      Offset dst_offset = offset + leb_size;
      stream_->MoveData(dst_offset, src_offset, size);
    }
    WriteU32Leb128At(stream_, offset, size, desc);
    stream_->AddOffset(delta);
    return delta;
  } else {
    Offset size = stream_->offset() - offset - MAX_U32_LEB128_BYTES;
    WriteFixedU32Leb128At(stream_, offset, size, desc);
    return 0;
  }
}

void BinaryWriter::WriteBlockDecl(const BlockDeclaration& decl) {
  if (decl.sig.GetNumParams() == 0 && decl.sig.GetNumResults() <= 1) {
    if (decl.sig.GetNumResults() == 0) {
      WriteType(stream_, Type::Void);
    } else if (decl.sig.GetNumResults() == 1) {
      WriteType(stream_, decl.sig.GetResultType(0));
    }
    return;
  }

  Index index = decl.has_func_type ? module_->GetFuncTypeIndex(decl.type_var)
                                   : module_->GetFuncTypeIndex(decl.sig);
  assert(index != kInvalidIndex);
  WriteS32Leb128WithReloc(index, "block type function index",
                          RelocType::TypeIndexLEB);
}

void BinaryWriter::WriteSectionHeader(const char* desc,
                                      BinarySection section_code) {
  assert(last_section_leb_size_guess_ == 0);
  WriteHeader(desc, PRINT_HEADER_NO_INDEX);
  stream_->WriteU8Enum(section_code, "section code");
  last_section_type_ = section_code;
  last_section_leb_size_guess_ = LEB_SECTION_SIZE_GUESS;
  last_section_offset_ =
      WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "section size (guess)");
  last_section_payload_offset_ = stream_->offset();
}

void BinaryWriter::BeginKnownSection(BinarySection section_code) {
  char desc[100];
  wabt_snprintf(desc, sizeof(desc), "section \"%s\" (%u)",
                GetSectionName(section_code),
                static_cast<unsigned>(section_code));
  WriteSectionHeader(desc, section_code);
}

void BinaryWriter::BeginCustomSection(const char* name) {
  char desc[100];
  wabt_snprintf(desc, sizeof(desc), "section \"%s\"", name);
  WriteSectionHeader(desc, BinarySection::Custom);
  WriteStr(stream_, name, "custom section name", PrintChars::Yes);
}

void BinaryWriter::EndSection() {
  assert(last_section_leb_size_guess_ != 0);
  WriteFixupU32Leb128Size(last_section_offset_, last_section_leb_size_guess_,
                          "FIXUP section size");
  last_section_leb_size_guess_ = 0;
  section_count_++;
}

void BinaryWriter::BeginSubsection(const char* name) {
  assert(last_subsection_leb_size_guess_ == 0);
  last_subsection_leb_size_guess_ = LEB_SECTION_SIZE_GUESS;
  last_subsection_offset_ =
      WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "subsection size (guess)");
  last_subsection_payload_offset_ = stream_->offset();
}

void BinaryWriter::EndSubsection() {
  assert(last_subsection_leb_size_guess_ != 0);
  WriteFixupU32Leb128Size(last_subsection_offset_,
                          last_subsection_leb_size_guess_,
                          "FIXUP subsection size");
  last_subsection_leb_size_guess_ = 0;
}

Index BinaryWriter::GetLabelVarDepth(const Var* var) {
  return var->index();
}

Index BinaryWriter::GetTagVarDepth(const Var* var) {
  return var->index();
}

Index BinaryWriter::GetSymbolIndex(RelocType reloc_type, Index index) {
  switch (reloc_type) {
    case RelocType::FuncIndexLEB:
      return symtab_.FunctionSymbolIndex(index);
    case RelocType::TableNumberLEB:
      return symtab_.TableSymbolIndex(index);
    case RelocType::GlobalIndexLEB:
      return symtab_.GlobalSymbolIndex(index);
    case RelocType::TypeIndexLEB:
      // Type indexes don't create entries in the symbol table; instead their
      // index is used directly.
      return index;
    default:
      fprintf(stderr, "warning: unsupported relocation type: %s\n",
              GetRelocTypeName(reloc_type));
      return kInvalidIndex;
  }
}

void BinaryWriter::AddReloc(RelocType reloc_type, Index index) {
  // Add a new reloc section if needed
  if (!current_reloc_section_ ||
      current_reloc_section_->section_index != section_count_) {
    reloc_sections_.emplace_back(GetSectionName(last_section_type_),
                                 section_count_);
    current_reloc_section_ = &reloc_sections_.back();
  }

  // Add a new relocation to the curent reloc section
  size_t offset = stream_->offset() - last_section_payload_offset_;
  Index symbol_index = GetSymbolIndex(reloc_type, index);
  if (symbol_index == kInvalidIndex) {
    // The file is invalid, for example a reference to function 42 where only 10
    // functions are defined.  The user must have already passed --no-check, so
    // no extra warning here is needed.
    return;
  }
  current_reloc_section_->relocations.emplace_back(reloc_type, offset,
                                                   symbol_index);
}

void BinaryWriter::WriteU32Leb128WithReloc(Index index,
                                           const char* desc,
                                           RelocType reloc_type) {
  if (options_.relocatable) {
    AddReloc(reloc_type, index);
    WriteFixedU32Leb128(stream_, index, desc);
  } else {
    WriteU32Leb128(stream_, index, desc);
  }
}

void BinaryWriter::WriteS32Leb128WithReloc(int32_t value,
                                           const char* desc,
                                           RelocType reloc_type) {
  if (options_.relocatable) {
    AddReloc(reloc_type, value);
    WriteFixedS32Leb128(stream_, value, desc);
  } else {
    WriteS32Leb128(stream_, value, desc);
  }
}

void BinaryWriter::WriteTableNumberWithReloc(Index value, const char* desc) {
  // Unless reference types are enabled, all references to tables refer to table
  // 0, so no relocs need be emitted when making relocatable binaries.
  if (options_.relocatable && options_.features.reference_types_enabled()) {
    AddReloc(RelocType::TableNumberLEB, value);
    WriteFixedS32Leb128(stream_, value, desc);
  } else {
    WriteS32Leb128(stream_, value, desc);
  }
}

Index BinaryWriter::GetLocalIndex(const Func* func, const Var& var) {
  // func can be nullptr when using local.get/local.set/local.tee in an
  // init_expr.
  if (func) {
    return func->GetLocalIndex(var);
  } else if (var.is_index()) {
    return var.index();
  } else {
    return kInvalidIndex;
  }
}

// TODO(binji): Rename this, it is used for more than loads/stores now.
template <typename T>
void BinaryWriter::WriteLoadStoreExpr(const Func* func,
                                      const Expr* expr,
                                      const char* desc) {
  auto* typed_expr = cast<T>(expr);
  WriteOpcode(stream_, typed_expr->opcode);
  Address align = typed_expr->opcode.GetAlignment(typed_expr->align);
  Index memidx = module_->GetMemoryIndex(typed_expr->memidx);
  if (memidx != 0) {
    stream_->WriteU8(log2_u32(align) | (1 << 6), "alignment");
    WriteU32Leb128(stream_, memidx, "memidx");
  } else {
    stream_->WriteU8(log2_u32(align), "alignment");
  }
  WriteU64Leb128(stream_, typed_expr->offset, desc);
}

template <typename T>
void BinaryWriter::WriteSimdLoadStoreLaneExpr(const Func* func,
                                              const Expr* expr,
                                              const char* desc) {
  WriteLoadStoreExpr<T>(func, expr, desc);
  auto* typed_expr = cast<T>(expr);
  stream_->WriteU8(static_cast<uint8_t>(typed_expr->val), "Simd Lane literal");
}

void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) {
  switch (expr->type()) {
    case ExprType::AtomicLoad:
      WriteLoadStoreExpr<AtomicLoadExpr>(func, expr, "memory offset");
      break;
    case ExprType::AtomicRmw:
      WriteLoadStoreExpr<AtomicRmwExpr>(func, expr, "memory offset");
      break;
    case ExprType::AtomicRmwCmpxchg:
      WriteLoadStoreExpr<AtomicRmwCmpxchgExpr>(func, expr, "memory offset");
      break;
    case ExprType::AtomicStore:
      WriteLoadStoreExpr<AtomicStoreExpr>(func, expr, "memory offset");
      break;
    case ExprType::AtomicWait:
      WriteLoadStoreExpr<AtomicWaitExpr>(func, expr, "memory offset");
      break;
    case ExprType::AtomicFence: {
      auto* fence_expr = cast<AtomicFenceExpr>(expr);
      WriteOpcode(stream_, Opcode::AtomicFence);
      WriteU32Leb128(stream_, fence_expr->consistency_model,
                     "consistency model");
      break;
    }
    case ExprType::AtomicNotify:
      WriteLoadStoreExpr<AtomicNotifyExpr>(func, expr, "memory offset");
      break;
    case ExprType::Binary:
      WriteOpcode(stream_, cast<BinaryExpr>(expr)->opcode);
      break;
    case ExprType::Block:
      WriteOpcode(stream_, Opcode::Block);
      WriteBlockDecl(cast<BlockExpr>(expr)->block.decl);
      WriteExprList(func, cast<BlockExpr>(expr)->block.exprs);
      WriteOpcode(stream_, Opcode::End);
      break;
    case ExprType::Br:
      WriteOpcode(stream_, Opcode::Br);
      WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrExpr>(expr)->var),
                     "break depth");
      break;
    case ExprType::BrIf:
      WriteOpcode(stream_, Opcode::BrIf);
      WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrIfExpr>(expr)->var),
                     "break depth");
      break;
    case ExprType::BrTable: {
      auto* br_table_expr = cast<BrTableExpr>(expr);
      WriteOpcode(stream_, Opcode::BrTable);
      WriteU32Leb128(stream_, br_table_expr->targets.size(), "num targets");
      Index depth;
      for (const Var& var : br_table_expr->targets) {
        depth = GetLabelVarDepth(&var);
        WriteU32Leb128(stream_, depth, "break depth");
      }
      depth = GetLabelVarDepth(&br_table_expr->default_target);
      WriteU32Leb128(stream_, depth, "break depth for default");
      break;
    }
    case ExprType::Call: {
      Index index = module_->GetFuncIndex(cast<CallExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::Call);
      WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
      break;
    }
    case ExprType::ReturnCall: {
      Index index = module_->GetFuncIndex(cast<ReturnCallExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::ReturnCall);
      WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
      break;
    }
    case ExprType::CallIndirect: {
      Index sig_index =
          module_->GetFuncTypeIndex(cast<CallIndirectExpr>(expr)->decl);
      Index table_index =
          module_->GetTableIndex(cast<CallIndirectExpr>(expr)->table);
      WriteOpcode(stream_, Opcode::CallIndirect);
      WriteU32Leb128WithReloc(sig_index, "signature index",
                              RelocType::TypeIndexLEB);
      WriteTableNumberWithReloc(table_index, "table index");
      break;
    }
    case ExprType::CallRef: {
      WriteOpcode(stream_, Opcode::CallRef);
      break;
    }
    case ExprType::ReturnCallIndirect: {
      Index sig_index =
          module_->GetFuncTypeIndex(cast<ReturnCallIndirectExpr>(expr)->decl);
      Index table_index =
          module_->GetTableIndex(cast<ReturnCallIndirectExpr>(expr)->table);
      WriteOpcode(stream_, Opcode::ReturnCallIndirect);
      WriteU32Leb128WithReloc(sig_index, "signature index",
                              RelocType::TypeIndexLEB);
      WriteTableNumberWithReloc(table_index, "table index");
      break;
    }
    case ExprType::Compare:
      WriteOpcode(stream_, cast<CompareExpr>(expr)->opcode);
      break;
    case ExprType::Const: {
      const Const& const_ = cast<ConstExpr>(expr)->const_;
      switch (const_.type()) {
        case Type::I32: {
          WriteOpcode(stream_, Opcode::I32Const);
          WriteS32Leb128(stream_, const_.u32(), "i32 literal");
          break;
        }
        case Type::I64:
          WriteOpcode(stream_, Opcode::I64Const);
          WriteS64Leb128(stream_, const_.u64(), "i64 literal");
          break;
        case Type::F32:
          WriteOpcode(stream_, Opcode::F32Const);
          stream_->WriteU32(const_.f32_bits(), "f32 literal");
          break;
        case Type::F64:
          WriteOpcode(stream_, Opcode::F64Const);
          stream_->WriteU64(const_.f64_bits(), "f64 literal");
          break;
        case Type::V128:
          WriteOpcode(stream_, Opcode::V128Const);
          stream_->WriteU128(const_.vec128(), "v128 literal");
          break;
        default:
          assert(0);
      }
      break;
    }
    case ExprType::Convert:
      WriteOpcode(stream_, cast<ConvertExpr>(expr)->opcode);
      break;
    case ExprType::Drop:
      WriteOpcode(stream_, Opcode::Drop);
      break;
    case ExprType::GlobalGet: {
      Index index = module_->GetGlobalIndex(cast<GlobalGetExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::GlobalGet);
      WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
      break;
    }
    case ExprType::GlobalSet: {
      Index index = module_->GetGlobalIndex(cast<GlobalSetExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::GlobalSet);
      WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
      break;
    }
    case ExprType::If: {
      auto* if_expr = cast<IfExpr>(expr);
      WriteOpcode(stream_, Opcode::If);
      WriteBlockDecl(if_expr->true_.decl);
      WriteExprList(func, if_expr->true_.exprs);
      if (!if_expr->false_.empty()) {
        WriteOpcode(stream_, Opcode::Else);
        WriteExprList(func, if_expr->false_);
      }
      WriteOpcode(stream_, Opcode::End);
      break;
    }
    case ExprType::Load:
      WriteLoadStoreExpr<LoadExpr>(func, expr, "load offset");
      break;
    case ExprType::LocalGet: {
      Index index = GetLocalIndex(func, cast<LocalGetExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::LocalGet);
      WriteU32Leb128(stream_, index, "local index");
      break;
    }
    case ExprType::LocalSet: {
      Index index = GetLocalIndex(func, cast<LocalSetExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::LocalSet);
      WriteU32Leb128(stream_, index, "local index");
      break;
    }
    case ExprType::LocalTee: {
      Index index = GetLocalIndex(func, cast<LocalTeeExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::LocalTee);
      WriteU32Leb128(stream_, index, "local index");
      break;
    }
    case ExprType::Loop:
      WriteOpcode(stream_, Opcode::Loop);
      WriteBlockDecl(cast<LoopExpr>(expr)->block.decl);
      WriteExprList(func, cast<LoopExpr>(expr)->block.exprs);
      WriteOpcode(stream_, Opcode::End);
      break;
    case ExprType::MemoryCopy: {
      Index destmemidx =
          module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->destmemidx);
      Index srcmemidx =
          module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->srcmemidx);
      WriteOpcode(stream_, Opcode::MemoryCopy);
      WriteU32Leb128(stream_, destmemidx, "memory.copy destmemidx");
      WriteU32Leb128(stream_, srcmemidx, "memory.copy srcmemidx");
      break;
    }
    case ExprType::DataDrop: {
      Index index = module_->GetDataSegmentIndex(cast<DataDropExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::DataDrop);
      WriteU32Leb128(stream_, index, "data.drop segment");
      has_data_segment_instruction_ = true;
      break;
    }
    case ExprType::MemoryFill: {
      Index memidx =
          module_->GetMemoryIndex(cast<MemoryFillExpr>(expr)->memidx);
      WriteOpcode(stream_, Opcode::MemoryFill);
      WriteU32Leb128(stream_, memidx, "memory.fill memidx");
      break;
    }
    case ExprType::MemoryGrow: {
      Index memidx =
          module_->GetMemoryIndex(cast<MemoryGrowExpr>(expr)->memidx);
      WriteOpcode(stream_, Opcode::MemoryGrow);
      WriteU32Leb128(stream_, memidx, "memory.grow memidx");
      break;
    }
    case ExprType::MemoryInit: {
      Index index =
          module_->GetDataSegmentIndex(cast<MemoryInitExpr>(expr)->var);
      Index memidx =
          module_->GetMemoryIndex(cast<MemoryInitExpr>(expr)->memidx);
      WriteOpcode(stream_, Opcode::MemoryInit);
      WriteU32Leb128(stream_, index, "memory.init segment");
      WriteU32Leb128(stream_, memidx, "memory.init memidx");
      has_data_segment_instruction_ = true;
      break;
    }
    case ExprType::MemorySize: {
      Index memidx =
          module_->GetMemoryIndex(cast<MemorySizeExpr>(expr)->memidx);
      WriteOpcode(stream_, Opcode::MemorySize);
      WriteU32Leb128(stream_, memidx, "memory.size memidx");
      break;
    }
    case ExprType::TableCopy: {
      auto* copy_expr = cast<TableCopyExpr>(expr);
      Index dst = module_->GetTableIndex(copy_expr->dst_table);
      Index src = module_->GetTableIndex(copy_expr->src_table);
      WriteOpcode(stream_, Opcode::TableCopy);
      WriteTableNumberWithReloc(dst, "table.copy dst_table");
      WriteTableNumberWithReloc(src, "table.copy src_table");
      break;
    }
    case ExprType::ElemDrop: {
      Index index = module_->GetElemSegmentIndex(cast<ElemDropExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::ElemDrop);
      WriteU32Leb128(stream_, index, "elem.drop segment");
      break;
    }
    case ExprType::TableInit: {
      auto* init_expr = cast<TableInitExpr>(expr);
      Index table_index = module_->GetTableIndex(init_expr->table_index);
      Index segment_index =
          module_->GetElemSegmentIndex(init_expr->segment_index);
      WriteOpcode(stream_, Opcode::TableInit);
      WriteU32Leb128(stream_, segment_index, "table.init segment");
      WriteTableNumberWithReloc(table_index, "table.init table");
      break;
    }
    case ExprType::TableGet: {
      Index index = module_->GetTableIndex(cast<TableGetExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::TableGet);
      WriteTableNumberWithReloc(index, "table.get table index");
      break;
    }
    case ExprType::TableSet: {
      Index index = module_->GetTableIndex(cast<TableSetExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::TableSet);
      WriteTableNumberWithReloc(index, "table.set table index");
      break;
    }
    case ExprType::TableGrow: {
      Index index = module_->GetTableIndex(cast<TableGrowExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::TableGrow);
      WriteTableNumberWithReloc(index, "table.grow table index");
      break;
    }
    case ExprType::TableSize: {
      Index index = module_->GetTableIndex(cast<TableSizeExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::TableSize);
      WriteTableNumberWithReloc(index, "table.size table index");
      break;
    }
    case ExprType::TableFill: {
      Index index = module_->GetTableIndex(cast<TableFillExpr>(expr)->var);
      WriteOpcode(stream_, Opcode::TableFill);
      WriteTableNumberWithReloc(index, "table.fill table index");
      break;
    }
    case ExprType::RefFunc: {
      WriteOpcode(stream_, Opcode::RefFunc);
      Index index = module_->GetFuncIndex(cast<RefFuncExpr>(expr)->var);
      WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
      break;
    }
    case ExprType::RefNull: {
      WriteOpcode(stream_, Opcode::RefNull);
      WriteType(stream_, cast<RefNullExpr>(expr)->type, "ref.null type");
      break;
    }
    case ExprType::RefIsNull:
      WriteOpcode(stream_, Opcode::RefIsNull);
      break;
    case ExprType::Nop:
      WriteOpcode(stream_, Opcode::Nop);
      break;
    case ExprType::Rethrow:
      WriteOpcode(stream_, Opcode::Rethrow);
      WriteU32Leb128(stream_, GetLabelVarDepth(&cast<RethrowExpr>(expr)->var),
                     "rethrow depth");
      break;
    case ExprType::Return:
      WriteOpcode(stream_, Opcode::Return);
      break;
    case ExprType::Select: {
      auto* select_expr = cast<SelectExpr>(expr);
      if (select_expr->result_type.empty()) {
        WriteOpcode(stream_, Opcode::Select);
      } else {
        WriteOpcode(stream_, Opcode::SelectT);
        WriteU32Leb128(stream_, select_expr->result_type.size(),
                       "num result types");
        for (Type t : select_expr->result_type) {
          WriteType(stream_, t, "result type");
        }
      }
      break;
    }
    case ExprType::Store:
      WriteLoadStoreExpr<StoreExpr>(func, expr, "store offset");
      break;
    case ExprType::Throw:
      WriteOpcode(stream_, Opcode::Throw);
      WriteU32Leb128(stream_, GetTagVarDepth(&cast<ThrowExpr>(expr)->var),
                     "throw tag");
      break;
    case ExprType::Try: {
      auto* try_expr = cast<TryExpr>(expr);
      WriteOpcode(stream_, Opcode::Try);
      WriteBlockDecl(try_expr->block.decl);
      WriteExprList(func, try_expr->block.exprs);
      switch (try_expr->kind) {
        case TryKind::Catch:
          for (const Catch& catch_ : try_expr->catches) {
            if (catch_.IsCatchAll()) {
              WriteOpcode(stream_, Opcode::CatchAll);
            } else {
              WriteOpcode(stream_, Opcode::Catch);
              WriteU32Leb128(stream_, GetTagVarDepth(&catch_.var), "catch tag");
            }
            WriteExprList(func, catch_.exprs);
          }
          WriteOpcode(stream_, Opcode::End);
          break;
        case TryKind::Delegate:
          WriteOpcode(stream_, Opcode::Delegate);
          WriteU32Leb128(stream_, GetLabelVarDepth(&try_expr->delegate_target),
                         "delegate depth");
          break;
        case TryKind::Plain:
          WriteOpcode(stream_, Opcode::End);
          break;
      }
      break;
    }
    case ExprType::Unary:
      WriteOpcode(stream_, cast<UnaryExpr>(expr)->opcode);
      break;
    case ExprType::Ternary:
      WriteOpcode(stream_, cast<TernaryExpr>(expr)->opcode);
      break;
    case ExprType::SimdLaneOp: {
      const Opcode opcode = cast<SimdLaneOpExpr>(expr)->opcode;
      WriteOpcode(stream_, opcode);
      stream_->WriteU8(static_cast<uint8_t>(cast<SimdLaneOpExpr>(expr)->val),
                       "Simd Lane literal");
      break;
    }
    case ExprType::SimdLoadLane: {
      WriteSimdLoadStoreLaneExpr<SimdLoadLaneExpr>(func, expr, "load offset");
      break;
    }
    case ExprType::SimdStoreLane: {
      WriteSimdLoadStoreLaneExpr<SimdStoreLaneExpr>(func, expr, "store offset");
      break;
    }
    case ExprType::SimdShuffleOp: {
      const Opcode opcode = cast<SimdShuffleOpExpr>(expr)->opcode;
      WriteOpcode(stream_, opcode);
      stream_->WriteU128(cast<SimdShuffleOpExpr>(expr)->val,
                         "Simd Lane[16] literal");
      break;
    }
    case ExprType::LoadSplat:
      WriteLoadStoreExpr<LoadSplatExpr>(func, expr, "load offset");
      break;
    case ExprType::LoadZero:
      WriteLoadStoreExpr<LoadZeroExpr>(func, expr, "load offset");
      break;
    case ExprType::Unreachable:
      WriteOpcode(stream_, Opcode::Unreachable);
      break;
    case ExprType::CodeMetadata: {
      auto* meta_expr = cast<CodeMetadataExpr>(expr);
      auto& s = code_metadata_sections_[meta_expr->name];
      if (s.entries.empty() || s.entries.back().func_idx != cur_func_index_) {
        s.entries.emplace_back(cur_func_index_);
      }
      auto& a = s.entries.back();
      Offset code_offset = stream_->offset() - cur_func_start_offset_;
      a.entries.emplace_back(code_offset, meta_expr->data);
      break;
    }
  }
}

void BinaryWriter::WriteExprList(const Func* func, const ExprList& exprs) {
  for (const Expr& expr : exprs) {
    WriteExpr(func, &expr);
  }
}

void BinaryWriter::WriteInitExpr(const ExprList& expr) {
  WriteExprList(nullptr, expr);
  WriteOpcode(stream_, Opcode::End);
}

void BinaryWriter::WriteFuncLocals(const Func* func,
                                   const LocalTypes& local_types) {
  if (local_types.size() == 0) {
    WriteU32Leb128(stream_, 0, "local decl count");
    return;
  }

  Index local_decl_count = local_types.decls().size();
  WriteU32Leb128(stream_, local_decl_count, "local decl count");
  for (auto decl : local_types.decls()) {
    WriteU32Leb128(stream_, decl.second, "local type count");
    WriteType(stream_, decl.first);
  }
}

void BinaryWriter::WriteFunc(const Func* func) {
  WriteFuncLocals(func, func->local_types);
  WriteExprList(func, func->exprs);
  WriteOpcode(stream_, Opcode::End);
}

void BinaryWriter::WriteTable(const Table* table) {
  WriteType(stream_, table->elem_type);
  WriteLimitsFlags(stream_, ComputeLimitsFlags(&table->elem_limits));
  WriteLimitsData(stream_, &table->elem_limits);
}

void BinaryWriter::WriteMemory(const Memory* memory) {
  uint32_t flags = ComputeLimitsFlags(&memory->page_limits);
  const bool custom_page_size = memory->page_size != WABT_DEFAULT_PAGE_SIZE;
  flags |= custom_page_size ? WABT_BINARY_LIMITS_HAS_CUSTOM_PAGE_SIZE_FLAG : 0;
  WriteLimitsFlags(stream_, flags);
  WriteLimitsData(stream_, &memory->page_limits);
  if (custom_page_size) {
    WriteU32Leb128(stream_, log2_u32(memory->page_size), "memory page size");
  }
}

void BinaryWriter::WriteGlobalHeader(const Global* global) {
  WriteType(stream_, global->type);
  stream_->WriteU8(global->mutable_, "global mutability");
}

void BinaryWriter::WriteTagType(const Tag* tag) {
  stream_->WriteU8(0, "tag attribute");
  WriteU32Leb128(stream_, module_->GetFuncTypeIndex(tag->decl),
                 "tag signature index");
}

void BinaryWriter::WriteRelocSection(const RelocSection* reloc_section) {
  char section_name[128];
  wabt_snprintf(section_name, sizeof(section_name), "%s.%s",
                WABT_BINARY_SECTION_RELOC, reloc_section->name);
  BeginCustomSection(section_name);
  WriteU32Leb128(stream_, reloc_section->section_index, "reloc section index");
  const std::vector<Reloc>& relocs = reloc_section->relocations;
  WriteU32Leb128(stream_, relocs.size(), "num relocs");

  for (const Reloc& reloc : relocs) {
    WriteU32Leb128(stream_, reloc.type, "reloc type");
    WriteU32Leb128(stream_, reloc.offset, "reloc offset");
    WriteU32Leb128(stream_, reloc.index, "reloc index");
    switch (reloc.type) {
      case RelocType::MemoryAddressLEB:
      case RelocType::MemoryAddressLEB64:
      case RelocType::MemoryAddressSLEB:
      case RelocType::MemoryAddressSLEB64:
      case RelocType::MemoryAddressRelSLEB:
      case RelocType::MemoryAddressRelSLEB64:
      case RelocType::MemoryAddressI32:
      case RelocType::MemoryAddressI64:
      case RelocType::MemoryAddressLocRelI32:
      case RelocType::FunctionOffsetI32:
      case RelocType::FunctionOffsetI64:
      case RelocType::SectionOffsetI32:
      case RelocType::MemoryAddressTLSSLEB:
      case RelocType::MemoryAddressTLSSLEB64:
        WriteU32Leb128(stream_, reloc.addend, "reloc addend");
        break;
      case RelocType::FuncIndexLEB:
      case RelocType::FuncIndexI32:
      case RelocType::TableIndexSLEB:
      case RelocType::TableIndexSLEB64:
      case RelocType::TableIndexI32:
      case RelocType::TableIndexI64:
      case RelocType::TypeIndexLEB:
      case RelocType::GlobalIndexLEB:
      case RelocType::TagIndexLEB:
      case RelocType::TableIndexRelSLEB:
      case RelocType::TableIndexRelSLEB64:
      case RelocType::TableNumberLEB:
        break;
      default:
        fprintf(stderr, "warning: unsupported relocation type: %s\n",
                GetRelocTypeName(reloc.type));
    }
  }

  EndSection();
}

void BinaryWriter::WriteLinkingSection() {
  BeginCustomSection(WABT_BINARY_SECTION_LINKING);
  WriteU32Leb128(stream_, 2, "metadata version");
  const std::vector<Symbol>& symbols = symtab_.symbols();
  if (symbols.size()) {
    stream_->WriteU8Enum(LinkingEntryType::SymbolTable, "symbol table");
    BeginSubsection("symbol table");
    WriteU32Leb128(stream_, symbols.size(), "num symbols");

    for (const Symbol& sym : symbols) {
      stream_->WriteU8Enum(sym.type(), "symbol type");
      WriteU32Leb128(stream_, sym.flags(), "symbol flags");
      switch (sym.type()) {
        case SymbolType::Function:
          WriteU32Leb128(stream_, sym.AsFunction().index, "function index");
          if (sym.defined() || sym.explicit_name()) {
            WriteStr(stream_, sym.name(), "function name", PrintChars::Yes);
          }
          break;
        case SymbolType::Data:
          WriteStr(stream_, sym.name(), "data name", PrintChars::Yes);
          if (sym.defined()) {
            WriteU32Leb128(stream_, sym.AsData().index, "data index");
            WriteU32Leb128(stream_, sym.AsData().offset, "data offset");
            WriteU32Leb128(stream_, sym.AsData().size, "data size");
          }
          break;
        case SymbolType::Global:
          WriteU32Leb128(stream_, sym.AsGlobal().index, "global index");
          if (sym.defined() || sym.explicit_name()) {
            WriteStr(stream_, sym.name(), "global name", PrintChars::Yes);
          }
          break;
        case SymbolType::Section:
          WriteU32Leb128(stream_, sym.AsSection().section, "section index");
          break;
        case SymbolType::Tag:
          WriteU32Leb128(stream_, sym.AsTag().index, "tag index");
          if (sym.defined() || sym.explicit_name()) {
            WriteStr(stream_, sym.name(), "tag name", PrintChars::Yes);
          }
          break;
        case SymbolType::Table:
          WriteU32Leb128(stream_, sym.AsTable().index, "table index");
          if (sym.defined() || sym.explicit_name()) {
            WriteStr(stream_, sym.name(), "table name", PrintChars::Yes);
          }
          break;
      }
    }
    EndSubsection();
  }
  EndSection();
}

template <typename T>
void BinaryWriter::WriteNames(const std::vector<T*>& elems,
                              NameSectionSubsection type) {
  size_t num_named_elems = 0;
  for (const T* elem : elems) {
    if (!elem->name.empty()) {
      num_named_elems++;
    }
  }

  if (!num_named_elems) {
    return;
  }

  WriteU32Leb128(stream_, type, "name subsection type");
  BeginSubsection("name subsection");

  char desc[100];
  WriteU32Leb128(stream_, num_named_elems, "num names");
  for (size_t i = 0; i < elems.size(); ++i) {
    const T* elem = elems[i];
    if (elem->name.empty()) {
      continue;
    }
    WriteU32Leb128(stream_, i, "elem index");
    wabt_snprintf(desc, sizeof(desc), "elem name %" PRIzd, i);
    WriteDebugName(stream_, elem->name, desc);
  }
  EndSubsection();
}

Result BinaryWriter::WriteModule() {
  stream_->WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
  stream_->WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION");

  if (options_.relocatable) {
    CHECK_RESULT(symtab_.Populate(module_));
  }

  if (module_->types.size()) {
    BeginKnownSection(BinarySection::Type);
    WriteU32Leb128(stream_, module_->types.size(), "num types");
    for (size_t i = 0; i < module_->types.size(); ++i) {
      const TypeEntry* type = module_->types[i];
      switch (type->kind()) {
        case TypeEntryKind::Func: {
          const FuncType* func_type = cast<FuncType>(type);
          const FuncSignature* sig = &func_type->sig;
          WriteHeader("func type", i);
          WriteType(stream_, Type::Func);

          Index num_params = sig->param_types.size();
          Index num_results = sig->result_types.size();
          WriteU32Leb128(stream_, num_params, "num params");
          for (size_t j = 0; j < num_params; ++j) {
            WriteType(stream_, sig->param_types[j]);
          }

          WriteU32Leb128(stream_, num_results, "num results");
          for (size_t j = 0; j < num_results; ++j) {
            WriteType(stream_, sig->result_types[j]);
          }
          break;
        }

        case TypeEntryKind::Struct: {
          const StructType* struct_type = cast<StructType>(type);
          WriteHeader("struct type", i);
          WriteType(stream_, Type::Struct);
          Index num_fields = struct_type->fields.size();
          WriteU32Leb128(stream_, num_fields, "num fields");
          for (size_t j = 0; j < num_fields; ++j) {
            const Field& field = struct_type->fields[j];
            WriteType(stream_, field.type);
            stream_->WriteU8(field.mutable_, "field mutability");
          }
          break;
        }

        case TypeEntryKind::Array: {
          const ArrayType* array_type = cast<ArrayType>(type);
          WriteHeader("array type", i);
          WriteType(stream_, Type::Array);
          WriteType(stream_, array_type->field.type);
          stream_->WriteU8(array_type->field.mutable_, "field mutability");
          break;
        }
      }
    }
    EndSection();
  }

  if (module_->imports.size()) {
    BeginKnownSection(BinarySection::Import);
    WriteU32Leb128(stream_, module_->imports.size(), "num imports");

    for (size_t i = 0; i < module_->imports.size(); ++i) {
      const Import* import = module_->imports[i];
      WriteHeader("import header", i);
      WriteStr(stream_, import->module_name, "import module name",
               PrintChars::Yes);
      WriteStr(stream_, import->field_name, "import field name",
               PrintChars::Yes);
      stream_->WriteU8Enum(import->kind(), "import kind");
      switch (import->kind()) {
        case ExternalKind::Func:
          WriteU32Leb128(
              stream_,
              module_->GetFuncTypeIndex(cast<FuncImport>(import)->func.decl),
              "import signature index");
          break;

        case ExternalKind::Table:
          WriteTable(&cast<TableImport>(import)->table);
          break;

        case ExternalKind::Memory:
          WriteMemory(&cast<MemoryImport>(import)->memory);
          break;

        case ExternalKind::Global:
          WriteGlobalHeader(&cast<GlobalImport>(import)->global);
          break;

        case ExternalKind::Tag:
          WriteTagType(&cast<TagImport>(import)->tag);
          break;
      }
    }
    EndSection();
  }

  assert(module_->funcs.size() >= module_->num_func_imports);
  Index num_funcs = module_->funcs.size() - module_->num_func_imports;
  if (num_funcs) {
    BeginKnownSection(BinarySection::Function);
    WriteU32Leb128(stream_, num_funcs, "num functions");

    for (size_t i = 0; i < num_funcs; ++i) {
      const Func* func = module_->funcs[i + module_->num_func_imports];
      char desc[100];
      wabt_snprintf(desc, sizeof(desc), "function %" PRIzd " signature index",
                    i);
      WriteU32Leb128(stream_, module_->GetFuncTypeIndex(func->decl), desc);
    }
    EndSection();
  }

  assert(module_->tables.size() >= module_->num_table_imports);
  Index num_tables = module_->tables.size() - module_->num_table_imports;
  if (num_tables) {
    BeginKnownSection(BinarySection::Table);
    WriteU32Leb128(stream_, num_tables, "num tables");
    for (size_t i = 0; i < num_tables; ++i) {
      const Table* table = module_->tables[i + module_->num_table_imports];
      WriteHeader("table", i);
      WriteTable(table);
    }
    EndSection();
  }

  assert(module_->memories.size() >= module_->num_memory_imports);
  Index num_memories = module_->memories.size() - module_->num_memory_imports;
  if (num_memories) {
    BeginKnownSection(BinarySection::Memory);
    WriteU32Leb128(stream_, num_memories, "num memories");
    for (size_t i = 0; i < num_memories; ++i) {
      const Memory* memory = module_->memories[i + module_->num_memory_imports];
      WriteHeader("memory", i);
      WriteMemory(memory);
    }
    EndSection();
  }

  assert(module_->tags.size() >= module_->num_tag_imports);
  Index num_tags = module_->tags.size() - module_->num_tag_imports;
  if (num_tags) {
    BeginKnownSection(BinarySection::Tag);
    WriteU32Leb128(stream_, num_tags, "tag count");
    for (size_t i = 0; i < num_tags; ++i) {
      WriteHeader("tag", i);
      const Tag* tag = module_->tags[i + module_->num_tag_imports];
      WriteTagType(tag);
    }
    EndSection();
  }

  assert(module_->globals.size() >= module_->num_global_imports);
  Index num_globals = module_->globals.size() - module_->num_global_imports;
  if (num_globals) {
    BeginKnownSection(BinarySection::Global);
    WriteU32Leb128(stream_, num_globals, "num globals");

    for (size_t i = 0; i < num_globals; ++i) {
      const Global* global = module_->globals[i + module_->num_global_imports];
      WriteGlobalHeader(global);
      WriteInitExpr(global->init_expr);
    }
    EndSection();
  }

  if (module_->exports.size()) {
    BeginKnownSection(BinarySection::Export);
    WriteU32Leb128(stream_, module_->exports.size(), "num exports");

    for (const Export* export_ : module_->exports) {
      WriteStr(stream_, export_->name, "export name", PrintChars::Yes);
      stream_->WriteU8Enum(export_->kind, "export kind");
      switch (export_->kind) {
        case ExternalKind::Func: {
          Index index = module_->GetFuncIndex(export_->var);
          WriteU32Leb128(stream_, index, "export func index");
          break;
        }
        case ExternalKind::Table: {
          Index index = module_->GetTableIndex(export_->var);
          WriteU32Leb128(stream_, index, "export table index");
          break;
        }
        case ExternalKind::Memory: {
          Index index = module_->GetMemoryIndex(export_->var);
          WriteU32Leb128(stream_, index, "export memory index");
          break;
        }
        case ExternalKind::Global: {
          Index index = module_->GetGlobalIndex(export_->var);
          WriteU32Leb128(stream_, index, "export global index");
          break;
        }
        case ExternalKind::Tag: {
          Index index = module_->GetTagIndex(export_->var);
          WriteU32Leb128(stream_, index, "export tag index");
          break;
        }
      }
    }
    EndSection();
  }

  if (module_->starts.size()) {
    Index start_func_index = module_->GetFuncIndex(*module_->starts[0]);
    if (start_func_index != kInvalidIndex) {
      BeginKnownSection(BinarySection::Start);
      WriteU32Leb128(stream_, start_func_index, "start func index");
      EndSection();
    }
  }

  if (module_->elem_segments.size()) {
    BeginKnownSection(BinarySection::Elem);
    WriteU32Leb128(stream_, module_->elem_segments.size(), "num elem segments");
    for (size_t i = 0; i < module_->elem_segments.size(); ++i) {
      ElemSegment* segment = module_->elem_segments[i];
      WriteHeader("elem segment header", i);
      // 1. flags
      uint8_t flags = segment->GetFlags(module_);
      stream_->WriteU8(flags, "segment flags");
      // 2. optional target table
      if (flags & SegExplicitIndex && segment->kind != SegmentKind::Declared) {
        WriteU32Leb128(stream_, module_->GetTableIndex(segment->table_var),
                       "table index");
      }
      // 3. optional target location within the table (active segments only)
      if (!(flags & SegPassive)) {
        WriteInitExpr(segment->offset);
      }
      // 4. type of item in the following list (omitted for "legacy" segments)
      if (flags & (SegPassive | SegExplicitIndex)) {
        if (flags & SegUseElemExprs) {
          WriteType(stream_, segment->elem_type, "elem expr list type");
        } else {
          stream_->WriteU8Enum(ExternalKind::Func, "elem list type");
        }
      }
      // 5. actual list of elements (with extern indexes or elem expr's)
      // preceeded by length
      WriteU32Leb128(stream_, segment->elem_exprs.size(), "num elems");
      if (flags & SegUseElemExprs) {
        for (const ExprList& elem_expr : segment->elem_exprs) {
          WriteInitExpr(elem_expr);
        }
      } else {
        for (const ExprList& elem_expr : segment->elem_exprs) {
          assert(elem_expr.size() == 1);
          const Expr* expr = &elem_expr.front();
          assert(expr->type() == ExprType::RefFunc);
          WriteU32Leb128(stream_,
                         module_->GetFuncIndex(cast<RefFuncExpr>(expr)->var),
                         "elem function index");
        }
      }
    }
    EndSection();
  }

  if (options_.features.bulk_memory_enabled() &&
      module_->data_segments.size()) {
    // Keep track of the data count section offset so it can be removed if
    // it isn't needed.
    data_count_start_ = stream_->offset();
    BeginKnownSection(BinarySection::DataCount);
    WriteU32Leb128(stream_, module_->data_segments.size(), "data count");
    EndSection();
    data_count_end_ = stream_->offset();
  }

  if (num_funcs) {
    code_start_ = stream_->offset();
    BeginKnownSection(BinarySection::Code);
    WriteU32Leb128(stream_, num_funcs, "num functions");

    for (size_t i = 0; i < num_funcs; ++i) {
      cur_func_index_ = i + module_->num_func_imports;
      WriteHeader("function body", i);
      const Func* func = module_->funcs[cur_func_index_];

      /* TODO(binji): better guess of the size of the function body section */
      const Offset leb_size_guess = 1;
      Offset body_size_offset =
          WriteU32Leb128Space(leb_size_guess, "func body size (guess)");
      cur_func_start_offset_ = stream_->offset();
      WriteFunc(func);
      auto func_start_offset = body_size_offset - last_section_payload_offset_;
      auto func_end_offset = stream_->offset() - last_section_payload_offset_;
      auto delta = WriteFixupU32Leb128Size(body_size_offset, leb_size_guess,
                                           "FIXUP func body size");
      if (current_reloc_section_ && delta != 0) {
        for (Reloc& reloc : current_reloc_section_->relocations) {
          if (reloc.offset >= func_start_offset &&
              reloc.offset <= func_end_offset) {
            reloc.offset += delta;
          }
        }
      }
    }
    EndSection();
  }

  // Remove the DataCount section if there are no instructions that require it.
  if (options_.features.bulk_memory_enabled() &&
      module_->data_segments.size() && !has_data_segment_instruction_) {
    Offset size = stream_->offset() - data_count_end_;
    if (size) {
      // If the DataCount section was followed by anything, assert that it's
      // only the Code section.  This limits the amount of fixing-up that we
      // need to do.
      assert(data_count_end_ == code_start_);
      assert(last_section_type_ == BinarySection::Code);
      stream_->MoveData(data_count_start_, data_count_end_, size);
      code_start_ = data_count_start_;
    }
    stream_->Truncate(data_count_start_ + size);

    --section_count_;

    // We just effectively decremented the code section's index; adjust anything
    // that might have captured it.
    for (RelocSection& section : reloc_sections_) {
      if (section.section_index == section_count_) {
        assert(last_section_type_ == BinarySection::Code);
        --section.section_index;
      }
    }
  }

  WriteCodeMetadataSections();

  if (module_->data_segments.size()) {
    BeginKnownSection(BinarySection::Data);
    WriteU32Leb128(stream_, module_->data_segments.size(), "num data segments");
    for (size_t i = 0; i < module_->data_segments.size(); ++i) {
      const DataSegment* segment = module_->data_segments[i];
      WriteHeader("data segment header", i);
      uint8_t flags = segment->GetFlags(module_);
      stream_->WriteU8(flags, "segment flags");
      if (!(flags & SegPassive)) {
        if (options_.features.multi_memory_enabled() &&
            (flags & SegExplicitIndex)) {
          WriteU32Leb128(stream_, module_->GetMemoryIndex(segment->memory_var),
                         "memidx");
        } else {
          assert(module_->GetMemoryIndex(segment->memory_var) == 0);
        }
        WriteInitExpr(segment->offset);
      }
      WriteU32Leb128(stream_, segment->data.size(), "data segment size");
      WriteHeader("data segment data", i);
      stream_->WriteData(segment->data, "data segment data");
    }
    EndSection();
  }

  for (const Custom& custom : module_->customs) {
    // These custom sections are already specially handled by BinaryWriter, so
    // we don't want to double-write.
    if ((custom.name == WABT_BINARY_SECTION_NAME &&
         options_.write_debug_names) ||
        (custom.name.rfind(WABT_BINARY_SECTION_RELOC) == 0 &&
         options_.relocatable) ||
        (custom.name == WABT_BINARY_SECTION_LINKING && options_.relocatable) ||
        (custom.name.find(WABT_BINARY_SECTION_CODE_METADATA) == 0 &&
         options_.features.code_metadata_enabled())) {
      continue;
    }
    BeginCustomSection(custom.name.data());
    stream_->WriteData(custom.data, "custom data");
    EndSection();
  }

  if (options_.write_debug_names) {
    std::vector<std::string> index_to_name;

    char desc[100];
    BeginCustomSection(WABT_BINARY_SECTION_NAME);

    if (!module_->name.empty()) {
      WriteU32Leb128(stream_, NameSectionSubsection::Module,
                     "module name type");
      BeginSubsection("module name subsection");
      WriteDebugName(stream_, module_->name, "module name");
      EndSubsection();
    }

    WriteNames<Func>(module_->funcs, NameSectionSubsection::Function);

    WriteU32Leb128(stream_, 2, "local name type");

    BeginSubsection("local name subsection");
    WriteU32Leb128(stream_, module_->funcs.size(), "num functions");
    for (size_t i = 0; i < module_->funcs.size(); ++i) {
      const Func* func = module_->funcs[i];
      Index num_params_and_locals = func->GetNumParamsAndLocals();
      MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings,
                                    &index_to_name);
      Index num_named = 0;
      for (auto s : index_to_name) {
        if (!s.empty()) {
          num_named++;
        }
      }

      WriteU32Leb128(stream_, i, "function index");
      WriteU32Leb128(stream_, num_named, "num locals");

      for (size_t j = 0; j < num_params_and_locals; ++j) {
        const std::string& name = index_to_name[j];
        if (!name.empty()) {
          wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j);
          WriteU32Leb128(stream_, j, "local index");
          WriteDebugName(stream_, name, desc);
        }
      }
    }
    EndSubsection();

    WriteNames<TypeEntry>(module_->types, NameSectionSubsection::Type);
    WriteNames<Table>(module_->tables, NameSectionSubsection::Table);
    WriteNames<Memory>(module_->memories, NameSectionSubsection::Memory);
    WriteNames<Global>(module_->globals, NameSectionSubsection::Global);
    WriteNames<ElemSegment>(module_->elem_segments,
                            NameSectionSubsection::ElemSegment);
    WriteNames<DataSegment>(module_->data_segments,
                            NameSectionSubsection::DataSegment);
    WriteNames<Tag>(module_->tags, NameSectionSubsection::Tag);

    EndSection();
  }

  if (options_.relocatable) {
    WriteLinkingSection();
    for (RelocSection& section : reloc_sections_) {
      WriteRelocSection(§ion);
    }
  }

  return stream_->result();
}

void BinaryWriter::WriteCodeMetadataSections() {
  if (code_metadata_sections_.empty())
    return;

  section_count_ -= 1;
  // We have to increment the code section's index; adjust anything
  // that might have captured it.
  for (RelocSection& section : reloc_sections_) {
    if (section.section_index == section_count_) {
      assert(last_section_type_ == BinarySection::Code);
      section.section_index += code_metadata_sections_.size();
    }
  }

  MemoryStream tmp_stream;
  Stream* main_stream = stream_;
  stream_ = &tmp_stream;
  for (auto& s : code_metadata_sections_) {
    std::string name = "metadata.code.";
    name.append(s.first);
    auto& section = s.second;
    BeginCustomSection(name.c_str());
    WriteU32Leb128(stream_, section.entries.size(), "function count");
    for (auto& f : section.entries) {
      WriteU32Leb128WithReloc(f.func_idx, "function index",
                              RelocType::FuncIndexLEB);
      WriteU32Leb128(stream_, f.entries.size(), "instances count");
      for (auto& a : f.entries) {
        WriteU32Leb128(stream_, a.offset, "code offset");
        WriteU32Leb128(stream_, a.data.size(), "data length");
        stream_->WriteData(a.data.data(), a.data.size(), "data",
                           PrintChars::Yes);
      }
    }
    EndSection();
  }
  stream_ = main_stream;
  auto buf = tmp_stream.ReleaseOutputBuffer();
  stream_->MoveData(code_start_ + buf->data.size(), code_start_,
                    stream_->offset() - code_start_);
  stream_->WriteDataAt(code_start_, buf->data.data(), buf->data.size());
  stream_->AddOffset(buf->data.size());
  code_start_ += buf->data.size();
  section_count_ += 1;
  last_section_type_ = BinarySection::Code;
}

}  // end anonymous namespace

Result WriteBinaryModule(Stream* stream,
                         const Module* module,
                         const WriteBinaryOptions& options) {
  BinaryWriter binary_writer(stream, options, module);
  return binary_writer.WriteModule();
}

}  // namespace wabt

Messung V0.5
C=97 H=96 G=96

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.