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 183 kB image not shown  

Quelle  c-writer.cc   Sprache: C

 
/*
 * Copyright 2017 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/c-writer.h"

#include <cctype>
#include <cinttypes>
#include <iterator>
#include <limits>
#include <map>
#include <set>
#include <string_view>
#include <vector>

#include "wabt/cast.h"
#include "wabt/common.h"
#include "wabt/ir.h"
#include "wabt/literal.h"
#include "wabt/sha256.h"
#include "wabt/stream.h"
#include "wabt/string-util.h"

#define INDENT_SIZE 2

#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort()

// code to be inserted into the generated output
extern const char* s_header_top;
extern const char* s_header_bottom;
extern const char* s_source_includes;
extern const char* s_source_declarations;
extern const char* s_simd_source_declarations;
extern const char* s_atomicops_source_declarations;

namespace wabt {

namespace {

struct Label {
  Label(LabelType label_type,
        const std::string& name,
        const TypeVector& sig,
        size_t type_stack_size,
        size_t try_catch_stack_size,
        bool used = false)
      : label_type(label_type),
        name(name),
        sig(sig),
        type_stack_size(type_stack_size),
        try_catch_stack_size(try_catch_stack_size),
        used(used) {}

  bool HasValue() const { return !sig.empty(); }

  LabelType label_type;
  const std::string& name;
  const TypeVector& sig;
  size_t type_stack_size;
  size_t try_catch_stack_size;
  bool used = false;
};

struct LocalName {
  explicit LocalName(const std::string& name) : name(name) {}
  const std::string& name;
};

struct ParamName : LocalName {
  using LocalName::LocalName;
  ParamName(const Var& var) : LocalName(var.name()) {}
};

struct LabelName : LocalName {
  using LocalName::LocalName;
};

struct GlobalName {
  GlobalName(ModuleFieldType type, const std::string& name)
      : type(type), name(name) {}
  ModuleFieldType type;
  const std::string& name;
};

struct TagSymbol : GlobalName {
  explicit TagSymbol(const std::string& name)
      : GlobalName(ModuleFieldType::Tag, name) {}
};

struct ExternalRef : GlobalName {
  using GlobalName::GlobalName;
};

struct WrapperRef : GlobalName {
  explicit WrapperRef(const std::string& name)
      : GlobalName(ModuleFieldType::Func, name) {}
};

struct TailCallRef : GlobalName {
  explicit TailCallRef(const std::string& name)
      : GlobalName(ModuleFieldType::Func, name) {}
};

struct ExternalInstancePtr : GlobalName {
  using GlobalName::GlobalName;
};

struct ExternalInstanceRef : GlobalName {
  using GlobalName::GlobalName;
};

struct GotoLabel {
  explicit GotoLabel(const Var& var) : var(var) {}
  const Var& var;
};

struct LabelDecl {
  explicit LabelDecl(const std::string& name) : name(name) {}
  std::string name;
};

struct GlobalInstanceVar {
  explicit GlobalInstanceVar(const Var& var) : var(var) {}
  const Var& var;
};

struct StackVar {
  explicit StackVar(Index index, Type type = Type::Any)
      : index(index), type(type) {}
  Index index;
  Type type;
};

struct TypeEnum {
  explicit TypeEnum(Type type) : type(type) {}
  Type type;
};

struct SignedType {
  explicit SignedType(Type type) : type(type) {}
  Type type;
};

struct TryCatchLabel {
  TryCatchLabel(const std::string& name, size_t try_catch_stack_size)
      : name(name), try_catch_stack_size(try_catch_stack_size), used(false) {}
  std::string name;
  size_t try_catch_stack_size;
  bool used;
};

struct FuncTypeExpr {
  const FuncType* func_type;
  FuncTypeExpr(const FuncType* f) : func_type(f) {}
};

struct Newline {};
struct OpenBrace {};
struct CloseBrace {};

int GetShiftMask(Type type) {
  // clang-format off
  switch (type) {
    case Type::I32: return 31;
    case Type::I64: return 63;
    default: WABT_UNREACHABLE; return 0;
  }
  // clang-format on
}

/*
 * This function is the default behavior for name_to_output_file_index_. For
 * single .c output, this function returns a vector filled with 0. For multiple
 * .c outputs, this function sorts all non-imported functions in the module by
 * their names, and then divides all non-imported functions into equal-sized
 * buckets (# of non-imported functions / # of .c outputs) based on the sorting.
 */

static std::vector<size_t> default_name_to_output_file_index(
    std::vector<Func*>::const_iterator func_begin,
    std::vector<Func*>::const_iterator func_end,
    size_t num_imports,
    size_t num_streams) {
  std::vector<size_t> result;
  result.resize(std::distance(func_begin, func_end));
  if (num_streams == 1) {
    return result;
  }

  std::map<std::string, Index> sorted_functions;
  size_t non_imported_funcs = result.size() - num_imports;
  size_t bucket_size = non_imported_funcs / num_streams +
                       (non_imported_funcs % num_streams ? 1 : 0);
  Index func_index = 0;
  for (auto func = func_begin; func != func_end; func++) {
    sorted_functions.insert({(*func)->name, func_index});
    ++func_index;
  }
  Index sorted_func_index = 0;
  for (const auto& [func_name, index] : sorted_functions) {
    bool is_import = index < num_imports;
    if (!is_import) {
      result.at(index) = sorted_func_index / bucket_size;
      ++sorted_func_index;
    }
  }
  return result;
}

class CWriter {
 public:
  CWriter(std::vector<Stream*>&& c_streams,
          Stream* h_stream,
          Stream* h_impl_stream,
          const char* header_name,
          const char* header_impl_name,
          const WriteCOptions& options)
      : options_(options),
        c_streams_(std::move(c_streams)),
        h_stream_(h_stream),
        h_impl_stream_(h_impl_stream),
        header_name_(header_name),
        header_impl_name_(header_impl_name) {
    module_prefix_ = MangleModuleName(options_.module_name);
    if (c_streams_.size() != 1 && options.name_to_output_file_index) {
      name_to_output_file_index_ = options.name_to_output_file_index;
    } else {
      name_to_output_file_index_ = default_name_to_output_file_index;
    }
  }

  Result WriteModule(const Module&);

 private:
  using SymbolSet = std::set<std::string>;
  using SymbolMap = std::map<std::string, std::string>;
  using StackTypePair = std::pair<Index, Type>;
  using StackVarSymbolMap = std::map<StackTypePair, std::string>;

  void WriteCHeader();
  void WriteCSource();

  size_t MarkTypeStack() const;
  void ResetTypeStack(size_t mark);
  Type StackType(Index) const;
  void PushType(Type);
  void PushTypes(const TypeVector&);
  void DropTypes(size_t count);

  void PushLabel(LabelType,
                 const std::string& name,
                 const FuncSignature&,
                 bool used = false);
  const Label* FindLabel(const Var& var, bool mark_used = true);
  bool IsTopLabelUsed() const;
  void PopLabel();

  static constexpr char MangleType(Type);
  static constexpr char MangleField(ModuleFieldType);
  static std::string MangleTypes(const TypeVector&);
  static std::string Mangle(std::string_view name, bool double_underscores);
  static std::string MangleName(std::string_view);
  static std::string MangleModuleName(std::string_view);
  static std::string ExportName(std::string_view module_name,
                                std::string_view export_name);
  std::string ExportName(std::string_view export_name) const;
  static std::string TailCallExportName(std::string_view module_name,
                                        std::string_view export_name);
  std::string TailCallExportName(std::string_view export_name) const;
  std::string ModuleInstanceTypeName() const;
  static std::string ModuleInstanceTypeName(std::string_view module_name);
  void ClaimName(SymbolSet& set,
                 SymbolMap& map,
                 char type_suffix,
                 std::string_view wasm_name,
                 const std::string& c_name);
  std::string FindUniqueName(SymbolSet& set,
                             std::string_view proposed_name) const;
  std::string ClaimUniqueName(SymbolSet& set,
                              SymbolMap& map,
                              char type_suffix,
                              std::string_view wasm_name,
                              const std::string& proposed_c_name);
  void DefineImportName(const Import* import,
                        std::string_view module_name,
                        std::string_view field_name);
  void ReserveExportNames();
  void ReserveExportName(std::string_view);
  std::string DefineImportedModuleInstanceName(std::string_view name);
  std::string DefineInstanceMemberName(ModuleFieldType, std::string_view);
  std::string DefineGlobalScopeName(ModuleFieldType, std::string_view);
  std::string DefineLocalScopeName(std::string_view name, bool is_label);
  std::string DefineParamName(std::string_view);
  std::string DefineLabelName(std::string_view);
  std::string DefineStackVarName(Index, Type, std::string_view);

  static void SerializeFuncType(const FuncType&, std::string&);

  std::string GetGlobalName(ModuleFieldType, const std::string&) const;
  std::string GetLocalName(const std::string&, bool is_label) const;
  std::string GetTailCallRef(const std::string&) const;

  void Indent(int size = INDENT_SIZE);
  void Dedent(int size = INDENT_SIZE);
  void NonIndented(std::function<void()> func);
  void WriteIndent();
  void WriteData(const char* src, size_t size);
  void Writef(const char* format, ...);

  template <typename T, typename U, typename... Args>
  void Write(T&& t, U&& u, Args&&... args) {
    Write(std::forward<T>(t));
    Write(std::forward<U>(u));
    Write(std::forward<Args>(args)...);
  }

  static const char* GetReferenceTypeName(const Type& type);
  static const char* GetReferenceNullValue(const Type& type);
  static const char* GetCTypeName(const Type& type);

  const char* InternalSymbolScope() const;

  enum class CWriterPhase {
    Declarations,
    Definitions,
  };

  void Write() {}
  void Write(Newline);
  void Write(OpenBrace);
  void Write(CloseBrace);
  void Write(uint64_t);
  void Write(std::string_view);
  void Write(const ParamName&);
  void Write(const LabelName&);
  void Write(const GlobalName&);
  void Write(const TagSymbol&);
  void Write(const ExternalRef&);
  void Write(const WrapperRef&);
  void Write(const TailCallRef&);
  void Write(const ExternalInstancePtr&);
  void Write(const ExternalInstanceRef&);
  void Write(Type);
  void Write(SignedType);
  void Write(TypeEnum);
  void Write(const GotoLabel&);
  void Write(const LabelDecl&);
  void Write(const GlobalInstanceVar&);
  void Write(const StackVar&);
  void Write(const TypeVector&);
  void Write(const Const&);
  void WriteInitExpr(const ExprList&);
  void WriteInitExprTerminal(const Expr*);
  std::string GenerateHeaderGuard() const;
  void WriteSourceTop();
  void WriteMultiCTop();
  void WriteMultiCTopEmpty();
  void DeclareStruct(const TypeVector&);
  void WriteTailcalleeParamTypes();
  void WriteMultivalueResultTypes();
  void WriteTagTypes();
  void WriteFuncTypeDecls();
  void WriteFuncTypes();
  void Write(const FuncTypeExpr&);
  void WriteTagDecls();
  void WriteTags();
  void ComputeUniqueImports();
  void BeginInstance();
  void WriteImports();
  void WriteTailCallWeakImports();
  void WriteFuncDeclarations();
  void WriteFuncDeclaration(const FuncDeclaration&, const std::string&);
  void WriteTailCallFuncDeclaration(const std::string&);
  void WriteImportFuncDeclaration(const FuncDeclaration&,
                                  const std::string& module_name,
                                  const std::string&);
  void WriteFuncRefWrapper(const Func* func);
  void WriteCallIndirectFuncDeclaration(const FuncDeclaration&,
                                        const std::string&);
  void ComputeSimdScope();
  void WriteHeaderIncludes();
  void WriteV128Decl();
  void WriteModuleInstance();
  void WriteGlobals();
  void WriteGlobal(const Global&, const std::string&);
  void WriteGlobalPtr(const Global&, const std::string&);
  void WriteMemories();
  void WriteMemory(const std::string&, const Memory& memory);
  void WriteMemoryPtr(const std::string&, const Memory& memory);
  void WriteTables();
  void WriteTable(const std::string&, const wabt::Type&);
  void WriteTablePtr(const std::string&, const Table&);
  void WriteTableType(const wabt::Type&);
  void WriteDataInstances();
  void WriteElemInstances();
  void WriteGlobalInitializers();
  void WriteDataInitializerDecls();
  void WriteDataInitializers();
  void WriteElemInitializerDecls();
  void WriteElemInitializers();
  void WriteFuncRefWrappers();
  void WriteElemTableInit(boolconst ElemSegment*, const Table*);
  bool IsSingleUnsharedMemory();
  void InstallSegueBase(Memory* memory, bool save_old_value);
  void RestoreSegueBase();
  void WriteExports(CWriterPhase);
  void WriteTailCallExports(CWriterPhase);
  void WriteInitDecl();
  void WriteFreeDecl();
  void WriteGetFuncTypeDecl();
  void WriteInit();
  void WriteFree();
  void WriteGetFuncType();
  void WriteInitInstanceImport();
  void WriteImportProperties(CWriterPhase);
  void WriteFuncs();
  void BeginFunction(const Func&);
  void FinishFunction(size_t);
  void Write(const Func&);
  void WriteTailCallee(const Func&);
  void WriteParamsAndLocals();
  void WriteParams(const std::vector<std::string>& index_to_name,
                   bool setjmp_safe = false);
  void WriteParamSymbols(const std::vector<std::string>& index_to_name);
  void WriteParamTypes(const FuncDeclaration& decl);
  void WriteLocals(const std::vector<std::string>& index_to_name);
  void WriteStackVarDeclarations();
  void Write(const ExprList&);
  void WriteTailCallAsserts(const FuncSignature&);
  void WriteTailCallStack();
  void WriteUnwindTryCatchStack(const Label*);
  void FinishReturnCall();
  void Spill(const TypeVector&);
  void Unspill(const TypeVector&);

  template <typename Vars, typename TypeOf, typename ToDo>
  void WriteVarsByType(const Vars&, const TypeOf&, const ToDo&, bool);

  template <typename sources>
  void Spill(const TypeVector&, const sources& src);
  template <typename targets>
  void Unspill(const TypeVector&, const targets& tgt);

  enum class AssignOp {
    Disallowed,
    Allowed,
  };

  void WriteSimpleUnaryExpr(Opcode, const char* op);
  void WriteInfixBinaryExpr(Opcode,
                            const char* op,
                            AssignOp = AssignOp::Allowed);
  void WritePrefixBinaryExpr(Opcode, const char* op);
  void WriteSignedBinaryExpr(Opcode, const char* op);
  void Write(const BinaryExpr&);
  void Write(const CompareExpr&);
  void Write(const ConvertExpr&);
  void Write(const LoadExpr&);
  void Write(const StoreExpr&);
  void Write(const UnaryExpr&);
  void Write(const TernaryExpr&);
  void Write(const SimdLaneOpExpr&);
  void Write(const SimdLoadLaneExpr&);
  void Write(const SimdStoreLaneExpr&);
  void Write(const SimdShuffleOpExpr&);
  void Write(const LoadSplatExpr&);
  void Write(const LoadZeroExpr&);
  void Write(const Block&);

  void Write(const AtomicLoadExpr& expr);
  void Write(const AtomicStoreExpr& expr);
  void Write(const AtomicRmwExpr& expr);
  void Write(const AtomicRmwCmpxchgExpr& expr);

  size_t BeginTry(const TryExpr& tryexpr);
  void WriteTryCatch(const TryExpr& tryexpr);
  void WriteTryDelegate(const TryExpr& tryexpr);
  void Write(const Catch& c);
  void WriteThrow();

  void PushTryCatch(const std::string& name);
  void PopTryCatch();

  void PushFuncSection(std::string_view include_condition = "");

  bool IsImport(const std::string& name) const;

  const WriteCOptions& options_;
  const Module* module_ = nullptr;
  const Func* func_ = nullptr;
  Stream* stream_ = nullptr;
  std::vector<Stream*> c_streams_;
  Stream* h_stream_ = nullptr;
  Stream* h_impl_stream_ = nullptr;
  std::string header_name_;
  std::string header_impl_name_;
  Result result_ = Result::Ok;
  int indent_ = 0;
  bool should_write_indent_next_ = false;
  int consecutive_newline_count_ = 0;

  SymbolMap global_sym_map_;
  SymbolMap local_sym_map_;
  SymbolMap import_module_sym_map_;
  StackVarSymbolMap stack_var_sym_map_;
  SymbolSet global_syms_;
  SymbolSet local_syms_;
  TypeVector type_stack_;
  std::vector<Label> label_stack_;
  std::vector<TryCatchLabel> try_catch_stack_;
  std::string module_prefix_;
  SymbolSet typevector_structs_;

  std::vector<const Import*> unique_imports_;
  SymbolSet import_module_set_;       // modules that are imported from
  SymbolSet import_func_module_set_;  // modules that funcs are imported from

  std::vector<std::pair<std::string, MemoryStream>> func_sections_;
  SymbolSet func_includes_;

  std::vector<std::string> unique_func_type_names_;

  std::function<std::vector<size_t>(std::vector<Func*>::const_iterator,
                                    std::vector<Func*>::const_iterator,
                                    size_t,
                                    size_t)>
      name_to_output_file_index_;

  bool simd_used_in_header_;

  bool in_tail_callee_;
};

// TODO: if WABT begins supporting debug names for labels,
// will need to avoid conflict between a label named "$Bfunc" and
// the implicit func label
static constexpr char kImplicitFuncLabel[] = "$Bfunc";

// These should be greater than any ModuleFieldType (used for MangleField).
static constexpr char kParamSuffix =
    'a' + static_cast<char>(ModuleFieldType::Tag) + 1;
static constexpr char kLabelSuffix = kParamSuffix + 1;

static constexpr char kGlobalSymbolPrefix[] = "w2c_";
static constexpr char kWrapperSymbolPrefix[] = "wrap_";
static constexpr char kLocalSymbolPrefix[] = "var_";
static constexpr char kAdminSymbolPrefix[] = "wasm2c_";
static constexpr char kTailCallSymbolPrefix[] = "wasm_tailcall_";
static constexpr char kTailCallFallbackPrefix[] = "wasm_fallback_";
static constexpr unsigned int kTailCallStackSize = 1024;

size_t CWriter::MarkTypeStack() const {
  return type_stack_.size();
}

void CWriter::ResetTypeStack(size_t mark) {
  assert(mark <= type_stack_.size());
  type_stack_.erase(type_stack_.begin() + mark, type_stack_.end());
}

Type CWriter::StackType(Index index) const {
  assert(index < type_stack_.size());
  return *(type_stack_.rbegin() + index);
}

void CWriter::PushType(Type type) {
  type_stack_.push_back(type);
}

void CWriter::PushTypes(const TypeVector& types) {
  type_stack_.insert(type_stack_.end(), types.begin(), types.end());
}

void CWriter::DropTypes(size_t count) {
  assert(count <= type_stack_.size());
  type_stack_.erase(type_stack_.end() - count, type_stack_.end());
}

void CWriter::PushLabel(LabelType label_type,
                        const std::string& name,
                        const FuncSignature& sig,
                        bool used) {
  if (label_type == LabelType::Loop)
    label_stack_.emplace_back(label_type, name, sig.param_types,
                              type_stack_.size(), try_catch_stack_.size(),
                              used);
  else
    label_stack_.emplace_back(label_type, name, sig.result_types,
                              type_stack_.size(), try_catch_stack_.size(),
                              used);
}

const Label* CWriter::FindLabel(const Var& var, bool mark_used) {
  Label* label = nullptr;

  if (var.is_index()) {
    // We've generated names for all labels, so we should only be using an
    // index when branching to the implicit function label, which can't be
    // named.
    assert(var.index() + 1 == label_stack_.size());
    label = &label_stack_[0];
  } else {
    assert(var.is_name());
    for (Index i = label_stack_.size(); i > 0; --i) {
      label = &label_stack_[i - 1];
      if (label->name == var.name())
        break;
    }
  }

  assert(label);
  if (mark_used) {
    label->used = true;
  }
  return label;
}

bool CWriter::IsTopLabelUsed() const {
  assert(!label_stack_.empty());
  return label_stack_.back().used;
}

void CWriter::PopLabel() {
  label_stack_.pop_back();
}

// static
constexpr char CWriter::MangleType(Type type) {
  // clang-format off
  switch (type) {
    case Type::I32: return 'i';
    case Type::I64: return 'j';
    case Type::F32: return 'f';
    case Type::F64: return 'd';
    case Type::V128: return 'o';
    case Type::FuncRef: return 'r';
    case Type::ExternRef: return 'e';
    default:
      WABT_UNREACHABLE;
  }
  // clang-format on
}

// static
constexpr char CWriter::MangleField(ModuleFieldType type) {
  assert(static_cast<std::underlying_type<ModuleFieldType>::type>(type) <
         std::numeric_limits<char>::max());
  return 'a' + static_cast<char>(type);
}

// remove risky characters for pasting into a C-style comment
static std::string SanitizeForComment(std::string_view str) {
  std::string result;

  for (const uint8_t ch : str) {
    // escape control chars, DEL, >7-bit chars, trigraphs, and end of comment
    if (ch < ' ' || ch > '~' || ch == '?' || ch == '/') {
      result += "\\" + StringPrintf("%02X", ch);
    } else {
      result += ch;
    }
  }

  return result;
}

// static
std::string CWriter::MangleTypes(const TypeVector& types) {
  assert(types.size() >= 2);
  std::string result = "wasm_multi_";
  for (auto type : types) {
    result += MangleType(type);
  }
  return result;
}

/* The C symbol for an export from this module. */
std::string CWriter::ExportName(std::string_view export_name) const {
  return kGlobalSymbolPrefix + module_prefix_ + '_' + MangleName(export_name);
}

/* The C symbol for an export from an arbitrary module. */
// static
std::string CWriter::ExportName(std::string_view module_name,
                                std::string_view export_name) {
  return kGlobalSymbolPrefix + MangleModuleName(module_name) + '_' +
         MangleName(export_name);
}

/* The C symbol for a tail-callee export from this module. */
std::string CWriter::TailCallExportName(std::string_view export_name) const {
  return kTailCallSymbolPrefix + ExportName(export_name);
}

/* The C symbol for a tail-callee export from an arbitrary module. */
// static
std::string CWriter::TailCallExportName(std::string_view module_name,
                                        std::string_view export_name) {
  return kTailCallSymbolPrefix + ExportName(module_name, export_name);
}

/* The type name of an instance of this module. */
std::string CWriter::ModuleInstanceTypeName() const {
  return kGlobalSymbolPrefix + module_prefix_;
}

/* The type name of an instance of an arbitrary module. */
// static
std::string CWriter::ModuleInstanceTypeName(std::string_view module_name) {
  return kGlobalSymbolPrefix + MangleModuleName(module_name);
}

/*
 * Hardcoded "C"-locale versions of isalpha/isdigit/isalnum/isxdigit for use
 * in CWriter::Mangle(). We don't use the standard isalpha/isdigit/isalnum
 * because the caller might have changed the current locale.
 */

static bool internal_isalpha(uint8_t ch) {
  return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
}

static bool internal_isdigit(uint8_t ch) {
  return (ch >= '0' && ch <= '9');
}

static bool internal_isalnum(uint8_t ch) {
  return internal_isalpha(ch) || internal_isdigit(ch);
}

static bool internal_ishexdigit(uint8_t ch) {
  return internal_isdigit(ch) || (ch >= 'A' && ch <= 'F');  // capitals only
}

// static
std::string CWriter::Mangle(std::string_view name, bool double_underscores) {
  /*
   * Name mangling transforms arbitrary Wasm names into "safe" C names
   * in a deterministic way. To avoid collisions, distinct Wasm names must be
   * transformed into distinct C names.
   *
   * The rules implemented here are:
   * 1) any hex digit ('A' through 'F') that follows the sequence "0x"
   *    is escaped
   * 2) any underscore at the beginning, at the end, or following another
   *    underscore, is escaped
   * 3) if double_underscores is set, underscores are replaced with
   *    two underscores.
   * 4) otherwise, any alphanumeric character is kept as-is,
   *    and any other character is escaped
   *
   * "Escaped" means the character is represented with the sequence "0xAB",
   * where A B are hex digits ('0'-'9' or 'A'-'F') representing the character's
   * numeric value.
   *
   * Module names are mangled with double_underscores=true to prevent
   * collisions between, e.g., a module "alfa" with export
   * "bravo_charlie" vs. a module "alfa_bravo" with export "charlie".
   */


  enum State { Any, Zero, ZeroX, ZeroXHexDigit } state{Any};
  bool last_was_underscore = false;

  std::string result;
  auto append_escaped = [&](const uint8_t ch) {
    result += "0x" + StringPrintf("%02X", ch);
    last_was_underscore = false;
    state = Any;
  };

  auto append_verbatim = [&](const uint8_t ch) {
    result += ch;
    last_was_underscore = (ch == '_');
  };

  for (auto it = name.begin(); it != name.end(); ++it) {
    const uint8_t ch = *it;
    switch (state) {
      case Any:
        state = (ch == '0') ? Zero : Any;
        break;
      case Zero:
        state = (ch == 'x') ? ZeroX : Any;
        break;
      case ZeroX:
        state = internal_ishexdigit(ch) ? ZeroXHexDigit : Any;
        break;
      case ZeroXHexDigit:
        WABT_UNREACHABLE;
        break;
    }

    /* rule 1 */
    if (state == ZeroXHexDigit) {
      append_escaped(ch);
      continue;
    }

    /* rule 2 */
    if ((ch == '_') && ((it == name.begin()) || (std::next(it) == name.end()) ||
                        last_was_underscore)) {
      append_escaped(ch);
      continue;
    }

    /* rule 3 */
    if (double_underscores && ch == '_') {
      append_verbatim(ch);
      append_verbatim(ch);
      continue;
    }

    /* rule 4 */
    if (internal_isalnum(ch) || (ch == '_')) {
      append_verbatim(ch);
    } else {
      append_escaped(ch);
    }
  }

  return result;
}

// static
std::string CWriter::MangleName(std::string_view name) {
  return Mangle(name, false);
}

// static
std::string CWriter::MangleModuleName(std::string_view name) {
  return Mangle(name, true);
}

/*
 * Allocate a C symbol (must be unused) in the SymbolSet,
 * and a mapping from the Wasm name (tagged with
 * the index space of the name) to that C symbol.
 */

void CWriter::ClaimName(SymbolSet& set,
                        SymbolMap& map,
                        char type_suffix,
                        std::string_view wasm_name,
                        const std::string& c_name) {
  const std::string type_tagged_wasm_name =
      std::string(wasm_name) + type_suffix;

  [[maybe_unused]] bool success;
  success = set.insert(c_name).second;
  assert(success);

  success = map.emplace(type_tagged_wasm_name, c_name).second;
  assert(success);
}

/*
 * Make a proposed C symbol unique in a given symbol set by appending
 * an integer to the symbol if necessary.
 */

std::string CWriter::FindUniqueName(SymbolSet& set,
                                    std::string_view proposed_name) const {
  std::string unique{proposed_name};
  if (set.find(unique) != set.end()) {
    std::string base = unique + "_";
    size_t count = 0;
    do {
      unique = base + std::to_string(count++);
    } while (set.find(unique) != set.end());
  }
  return unique;
}

/*
 * Find a unique C symbol in the symbol set and claim it (mapping the
 * type-tagged Wasm name to it).
 */

std::string CWriter::ClaimUniqueName(SymbolSet& set,
                                     SymbolMap& map,
                                     char type_suffix,
                                     std::string_view wasm_name,
                                     const std::string& proposed_c_name) {
  const std::string unique = FindUniqueName(set, proposed_c_name);
  ClaimName(set, map, type_suffix, wasm_name, unique);
  return unique;
}

std::string_view StripLeadingDollar(std::string_view name) {
  assert(!name.empty());
  assert(name.front() == '$');
  name.remove_prefix(1);
  return name;
}

void CWriter::DefineImportName(const Import* import,
                               std::string_view module,
                               std::string_view field_name) {
  std::string name;
  ModuleFieldType type{};

  switch (import->kind()) {
    case ExternalKind::Func:
      type = ModuleFieldType::Func;
      name = cast<FuncImport>(import)->func.name;
      break;
    case ExternalKind::Tag:
      type = ModuleFieldType::Tag;
      name = cast<TagImport>(import)->tag.name;
      break;
    case ExternalKind::Global:
      type = ModuleFieldType::Global;
      name = cast<GlobalImport>(import)->global.name;
      break;
    case ExternalKind::Memory:
      type = ModuleFieldType::Memory;
      name = cast<MemoryImport>(import)->memory.name;
      break;
    case ExternalKind::Table:
      type = ModuleFieldType::Table;
      name = cast<TableImport>(import)->table.name;
      break;
  }

  import_module_sym_map_.emplace(name, import->module_name);

  const std::string mangled = ExportName(module, field_name);
  global_syms_.erase(mangled);  // duplicate imports are allowed
  ClaimName(global_syms_, global_sym_map_, MangleField(type), name, mangled);
}

/*
 * Reserve a C symbol for the public name of a module's export.  The
 * format of these is "w2c_" + the module prefix + "_" + the mangled
 * export name. Reserving the symbol prevents internal functions and
 * other names from shadowing/overlapping the exports.
 */

void CWriter::ReserveExportName(std::string_view name) {
  ClaimName(global_syms_, global_sym_map_, MangleField(ModuleFieldType::Export),
            name, ExportName(name));
}

/*
 * Names for functions, function types, tags, and segments are globally unique
 * across modules (formatted the same as an export, as "w2c_" + module prefix +
 * "_" + the name, made unique if necessary).
 */

std::string CWriter::DefineGlobalScopeName(ModuleFieldType type,
                                           std::string_view name) {
  return ClaimUniqueName(global_syms_, global_sym_map_, MangleField(type), name,
                         ExportName(StripLeadingDollar(name)));
}

std::string CWriter::GetGlobalName(ModuleFieldType type,
                                   const std::string& name) const {
  std::string mangled = name + MangleField(type);
  assert(global_sym_map_.count(mangled) == 1);
  return global_sym_map_.at(mangled);
}

/* Names for params, locals, and stack vars are formatted as "var_" + name. */
std::string CWriter::DefineLocalScopeName(std::string_view name,
                                          bool is_label) {
  return ClaimUniqueName(
      local_syms_, local_sym_map_, is_label ? kLabelSuffix : kParamSuffix, name,
      kLocalSymbolPrefix + MangleName(StripLeadingDollar(name)));
}

std::string CWriter::GetLocalName(const std::string& name,
                                  bool is_label) const {
  std::string mangled = name + (is_label ? kLabelSuffix : kParamSuffix);
  assert(local_sym_map_.count(mangled) == 1);
  return local_sym_map_.at(mangled);
}

std::string CWriter::GetTailCallRef(const std::string& name) const {
  return kTailCallSymbolPrefix + GetGlobalName(ModuleFieldType::Func, name);
}

std::string CWriter::DefineParamName(std::string_view name) {
  return DefineLocalScopeName(name, false);
}

std::string CWriter::DefineLabelName(std::string_view name) {
  return DefineLocalScopeName(name, true);
}

std::string CWriter::DefineStackVarName(Index index,
                                        Type type,
                                        std::string_view name) {
  std::string unique =
      FindUniqueName(local_syms_, kLocalSymbolPrefix + MangleName(name));
  StackTypePair stp = {index, type};
  [[maybe_unused]] bool success =
      stack_var_sym_map_.emplace(stp, unique).second;
  assert(success);
  return unique;
}

/*
 * Members of the module instance (globals, tables, and memories) are formatted
 * as "w2c_" + the mangled name of the element (made unique if necessary).
 */

std::string CWriter::DefineInstanceMemberName(ModuleFieldType type,
                                              std::string_view name) {
  return ClaimUniqueName(
      global_syms_, global_sym_map_, MangleField(type), name,
      kGlobalSymbolPrefix + MangleName(StripLeadingDollar(name)));
}

/*
 * The name of a module-instance member that points to the originating
 * instance of an imported function is formatted as "w2c_" + originating
 * module prefix + "_instance".
 */

std::string CWriter::DefineImportedModuleInstanceName(std::string_view name) {
  return ClaimUniqueName(global_syms_, global_sym_map_,
                         MangleField(ModuleFieldType::Import), name,
                         ExportName(name, "instance"));
}

void CWriter::Indent(int size) {
  indent_ += size;
}

void CWriter::Dedent(int size) {
  indent_ -= size;
  assert(indent_ >= 0);
}

void CWriter::NonIndented(std::function<void()> func) {
  int copy = indent_;
  indent_ = 0;
  func();
  indent_ = copy;
}

void CWriter::WriteIndent() {
  static char s_indent[] =
      " "
      " ";
  static size_t s_indent_len = sizeof(s_indent) - 1;
  size_t to_write = indent_;
  while (to_write >= s_indent_len) {
    stream_->WriteData(s_indent, s_indent_len);
    to_write -= s_indent_len;
  }
  if (to_write > 0) {
    stream_->WriteData(s_indent, to_write);
  }
}

void CWriter::WriteData(const char* src, size_t size) {
  if (should_write_indent_next_) {
    WriteIndent();
    should_write_indent_next_ = false;
  }
  if (size > 0 && src[0] != '\n') {
    consecutive_newline_count_ = 0;
  }
  stream_->WriteData(src, size);
}

void WABT_PRINTF_FORMAT(2, 3) CWriter::Writef(const char* format, ...) {
  WABT_SNPRINTF_ALLOCA(buffer, length, format);
  WriteData(buffer, length);
}

void CWriter::Write(Newline) {
  // Allow maximum one blank line between sections
  if (consecutive_newline_count_ < 2) {
    Write("\n");
    consecutive_newline_count_++;
  }
  should_write_indent_next_ = true;
}

void CWriter::Write(OpenBrace) {
  Write("{");
  Indent();
  Write(Newline());
}

void CWriter::Write(CloseBrace) {
  Dedent();
  Write("}");
}

void CWriter::Write(uint64_t val) {
  Writef("%" PRIu64, val);
}

void CWriter::Write(std::string_view s) {
  WriteData(s.data(), s.size());
}

void CWriter::Write(const ParamName& name) {
  Write(GetLocalName(name.name, false));
}

void CWriter::Write(const LabelName& name) {
  Write(GetLocalName(name.name, true));
}

void CWriter::Write(const GlobalName& name) {
  Write(GetGlobalName(name.type, name.name));
}

void CWriter::Write(const TagSymbol& name) {
  if (!IsImport(name.name)) {
    Write("&");
  }
  Write(GlobalName(name));
}

void CWriter::Write(const ExternalInstancePtr& name) {
  if (!IsImport(name.name)) {
    Write("&");
  }
  Write("instance->", GlobalName(name));
}

void CWriter::Write(const ExternalRef& name) {
  if (name.type == ModuleFieldType::Func || !IsImport(name.name)) {
    Write(GlobalName(name));
  } else {
    Write("(*", GlobalName(name), ")");
  }
}

void CWriter::Write(const WrapperRef& name) {
  Write(kWrapperSymbolPrefix, GlobalName(name));
}

void CWriter::Write(const TailCallRef& name) {
  Write(GetTailCallRef(name.name));
}

void CWriter::Write(const ExternalInstanceRef& name) {
  if (IsImport(name.name)) {
    Write("(*instance->", GlobalName(name), ")");
  } else {
    Write("instance->", GlobalName(name));
  }
}

void CWriter::Write(const GotoLabel& goto_label) {
  const Label* label = FindLabel(goto_label.var);
  if (label->HasValue()) {
    size_t amount = label->sig.size();
    assert(type_stack_.size() >= label->type_stack_size);
    assert(type_stack_.size() >= amount);
    assert(type_stack_.size() - amount >= label->type_stack_size);
    Index offset = type_stack_.size() - label->type_stack_size - amount;
    if (offset != 0) {
      for (Index i = 0; i < amount; ++i) {
        Write(StackVar(amount - i - 1 + offset, label->sig[i]), " = ",
              StackVar(amount - i - 1), "; ");
      }
    }
  }

  WriteUnwindTryCatchStack(label);

  if (goto_label.var.is_name()) {
    Write("goto ", LabelName(goto_label.var.name()), ";");
  } else {
    // We've generated names for all labels, so we should only be using an
    // index when branching to the implicit function label, which can't be
    // named.
    Write("goto ", LabelName(kImplicitFuncLabel), ";");
  }
}

void CWriter::Write(const LabelDecl& label) {
  if (IsTopLabelUsed())
    Write(label.name, ":;", Newline());
}

void CWriter::Write(const GlobalInstanceVar& var) {
  assert(var.var.is_name());
  Write(ExternalInstanceRef(ModuleFieldType::Global, var.var.name()));
}

void CWriter::Write(const StackVar& sv) {
  Index index = type_stack_.size() - 1 - sv.index;
  Type type = sv.type;
  if (type == Type::Any) {
    assert(index < type_stack_.size());
    type = type_stack_[index];
  }

  StackTypePair stp = {index, type};
  auto iter = stack_var_sym_map_.find(stp);
  if (iter == stack_var_sym_map_.end()) {
    std::string name = MangleType(type) + std::to_string(index);
    Write(DefineStackVarName(index, type, name));
  } else {
    Write(iter->second);
  }
}

// static
const char* CWriter::GetCTypeName(const Type& type) {
  // clang-format off
  switch (type) {
  case Type::I32: return "u32";
  case Type::I64: return "u64";
  case Type::F32: return "f32";
  case Type::F64: return "f64";
  case Type::V128: return "v128";
  case Type::FuncRef: return "wasm_rt_funcref_t";
  case Type::ExternRef: return "wasm_rt_externref_t";
    default:
      WABT_UNREACHABLE;
  }
  // clang-format on
}

void CWriter::Write(Type type) {
  Write(GetCTypeName(type));
}

void CWriter::Write(TypeEnum type) {
  // clang-format off
  switch (type.type) {
    case Type::I32: Write("WASM_RT_I32"); break;
    case Type::I64: Write("WASM_RT_I64"); break;
    case Type::F32: Write("WASM_RT_F32"); break;
    case Type::F64: Write("WASM_RT_F64"); break;
    case Type::V128: Write("WASM_RT_V128"); break;
    case Type::FuncRef: Write("WASM_RT_FUNCREF"); break;
    case Type::ExternRef: Write("WASM_RT_EXTERNREF"); break;
    default:
      WABT_UNREACHABLE;
  }
  // clang-format on
}

void CWriter::Write(SignedType type) {
  // clang-format off
  switch (type.type) {
    case Type::I32: Write("s32"); break;
    case Type::I64: Write("s64"); break;
    default:
      WABT_UNREACHABLE;
  }
  // clang-format on
}

void CWriter::Write(const TypeVector& types) {
  if (types.empty()) {
    Write("void");
  } else if (types.size() == 1) {
    Write(types[0]);
  } else {
    Write("struct ", MangleTypes(types));
  }
}

void CWriter::Write(const Const& const_) {
  switch (const_.type()) {
    case Type::I32:
      Writef("%uu"static_cast<int32_t>(const_.u32()));
      break;

    case Type::I64:
      Writef("%" PRIu64 "ull"static_cast<int64_t>(const_.u64()));
      break;

    case Type::F32: {
      uint32_t f32_bits = const_.f32_bits();
      // TODO(binji): Share with similar float info in interp.cc and literal.cc
      if ((f32_bits & 0x7f800000u) == 0x7f800000u) {
        const char* sign = (f32_bits & 0x80000000) ? "-" : "";
        uint32_t significand = f32_bits & 0x7fffffu;
        if (significand == 0) {
          // Infinity.
          Writef("%sINFINITY", sign);
        } else {
          // Nan.
          Writef("f32_reinterpret_i32(0x%08x) /* %snan:0x%06x */", f32_bits,
                 sign, significand);
        }
      } else if (f32_bits == 0x80000000) {
        // Negative zero. Special-cased so it isn't written as -0 below.
        Writef("-0.f");
      } else {
        Writef("%.9g", Bitcast<float>(f32_bits));
      }
      break;
    }

    case Type::F64: {
      uint64_t f64_bits = const_.f64_bits();
      // TODO(binji): Share with similar float info in interp.cc and literal.cc
      if ((f64_bits & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) {
        const char* sign = (f64_bits & 0x8000000000000000ull) ? "-" : "";
        uint64_t significand = f64_bits & 0xfffffffffffffull;
        if (significand == 0) {
          // Infinity.
          Writef("%sINFINITY", sign);
        } else {
          // Nan.
          Writef("f64_reinterpret_i64(0x%016" PRIx64 ") /* %snan:0x%013" PRIx64
                 " */",
                 f64_bits, sign, significand);
        }
      } else if (f64_bits == 0x8000000000000000ull) {
        // Negative zero. Special-cased so it isn't written as -0 below.
        Writef("-0.0");
      } else {
        char buf[128];
        snprintf(buf, sizeof(buf), "%.17g", Bitcast<double>(f64_bits));
        // Append .0 if sprint didn't include a decimal point or use the
        // exponent ('e') form.  This is a workaround for an MSVC parsing
        // issue: https://github.com/WebAssembly/wabt/issues/2422
        if (!strchr(buf, '.') && !strchr(buf, 'e')) {
          strcat(buf, ".0");
        }
        Writef("%s", buf);
      }
      break;
    }
    case Type::V128: {
      Writef("v128_const(0x%02x", const_.vec128().u8(0));
      for (int i = 1; i < 16; i++) {
        Writef(", 0x%02x", const_.vec128().u8(i));
      }
      Write(")");
      break;
    }

    default:
      WABT_UNREACHABLE;
  }
}

void CWriter::WriteInitDecl() {
  Write("void ", kAdminSymbolPrefix, module_prefix_, "_instantiate(",
        ModuleInstanceTypeName(), "*");
  for (const auto& import_module_name : import_module_set_) {
    Write(", struct ", ModuleInstanceTypeName(import_module_name), "*");
  }
  Write(");", Newline());
}

void CWriter::WriteFreeDecl() {
  Write("void ", kAdminSymbolPrefix, module_prefix_, "_free(",
        ModuleInstanceTypeName(), "*);", Newline());
}

void CWriter::WriteGetFuncTypeDecl() {
  Write("wasm_rt_func_type_t ", kAdminSymbolPrefix, module_prefix_,
        "_get_func_type(uint32_t param_count, uint32_t result_count, ...);",
        Newline());
}

static std::string GetMemoryTypeString(const Memory& memory) {
  return memory.page_limits.is_shared ? "wasm_rt_shared_memory_t"
                                      : "wasm_rt_memory_t";
}

static std::string GetMemoryAPIString(const Memory& memory, std::string api) {
  return memory.page_limits.is_shared ? (api + "_shared") : api;
}

void CWriter::WriteInitExpr(const ExprList& expr_list) {
  if (expr_list.empty()) {
    WABT_UNREACHABLE;
  }

  std::vector<std::string> mini_stack;

  for (const auto& expr : expr_list) {
    if (expr.type() == ExprType::Binary) {
      // Extended const expressions include at least one binary op.
      // This builds a C expression from the operands.
      if (mini_stack.size() < 2) {
        WABT_UNREACHABLE;
      }

      const auto binexpr = cast<BinaryExpr>(&expr);
      char op;
      switch (binexpr->opcode) {
        case Opcode::I32Add:
        case Opcode::I64Add:
        case Opcode::F32Add:
        case Opcode::F64Add:
          op = '+';
          break;

        case Opcode::I32Sub:
        case Opcode::I64Sub:
        case Opcode::F32Sub:
        case Opcode::F64Sub:
          op = '-';
          break;

        case Opcode::I32Mul:
        case Opcode::I64Mul:
        case Opcode::F32Mul:
        case Opcode::F64Mul:
          op = '*';
          break;

        default:
          WABT_UNREACHABLE;
      }

      std::string combination =
          "((" + std::string(GetCTypeName(binexpr->opcode.GetParamType1())) +
          ")" + mini_stack.at(mini_stack.size() - 2) + ")" + op + "((" +
          std::string(GetCTypeName(binexpr->opcode.GetParamType2())) + ")" +
          mini_stack.at(mini_stack.size() - 1) + ")";
      mini_stack.resize(mini_stack.size() - 2);
      mini_stack.push_back(std::move(combination));
    } else {
      // Leaf node (nullary const expression)
      Stream* existing_stream = stream_;
      MemoryStream terminal_stream;
      stream_ = &terminal_stream;
      WriteInitExprTerminal(&expr);
      const auto& buf = terminal_stream.output_buffer();
      mini_stack.emplace_back(reinterpret_cast<const char*>(buf.data.data()),
                              buf.data.size());
      stream_ = existing_stream;
    }
  }

  if (mini_stack.size() != 1) {
    WABT_UNREACHABLE;
  }

  Write(mini_stack.front());
}

void CWriter::WriteInitExprTerminal(const Expr* expr) {
  switch (expr->type()) {
    case ExprType::Const:
      Write(cast<ConstExpr>(expr)->const_);
      break;

    case ExprType::GlobalGet:
      Write(GlobalInstanceVar(cast<GlobalGetExpr>(expr)->var));
      break;

    case ExprType::RefFunc: {
      const Func* func = module_->GetFunc(cast<RefFuncExpr>(expr)->var);
      const FuncDeclaration& decl = func->decl;

      assert(decl.has_func_type);
      const FuncType* func_type = module_->GetFuncType(decl.type_var);

      Write("(wasm_rt_funcref_t){", FuncTypeExpr(func_type), ", ",
            "(wasm_rt_function_ptr_t)", WrapperRef(func->name), ", {");
      if (options_.features.tail_call_enabled() &&
          (IsImport(func->name) || func->features_used.tailcall)) {
        Write(TailCallRef(func->name));
      } else {
        Write("NULL");
      }
      Write("}, ");

      if (IsImport(func->name)) {
        Write("instance->", GlobalName(ModuleFieldType::Import,
                                       import_module_sym_map_[func->name]));
      } else {
        Write("instance");
      }
      Write("}");
    } break;

    case ExprType::RefNull:
      Write(GetReferenceNullValue(cast<RefNullExpr>(expr)->type));
      break;

    default:
      WABT_UNREACHABLE;
  }
}

std::string CWriter::GenerateHeaderGuard() const {
  std::string result;
  for (char c : header_name_) {
    if (isalnum(c) || c == '_') {
      result += toupper(c);
    } else {
      result += '_';
    }
  }
  result += "_GENERATED_";
  return result;
}

void CWriter::WriteSourceTop() {
  Write(s_source_includes);
  Write(Newline(), "#include \"", header_name_, "\"", Newline());

  if (IsSingleUnsharedMemory()) {
    Write("#define IS_SINGLE_UNSHARED_MEMORY 1", Newline());
  }

  Write(s_source_declarations, Newline());

  if (module_->features_used.simd) {
    if (!simd_used_in_header_) {
      WriteV128Decl();
    }
    Write(s_simd_source_declarations);
  }

  if (module_->features_used.threads) {
    Write(s_atomicops_source_declarations);
  }
}

void CWriter::WriteMultiCTop() {
  if (c_streams_.size() > 1) {
    assert(header_impl_name_.size() > 0);
    Write("/* Automatically generated by wasm2c */", Newline());
    Write("#include \"", header_impl_name_, "\"", Newline());
  }
}

void CWriter::WriteMultiCTopEmpty() {
  for (auto& stream : c_streams_) {
    if (stream->offset() == 0) {
      stream_ = stream;
      Write("/* Empty wasm2c generated file */\n");
      Write("typedef int dummy_def;");
    }
  }
}

void CWriter::DeclareStruct(const TypeVector& types) {
  const std::string name = MangleTypes(types);
  if (!typevector_structs_.insert(name).second) {
    return;
  }

  Write(Newline(), "#ifndef ", name, Newline());
  Write("#define ", name, " ", name, Newline());
  Write("struct ", name, " ", OpenBrace());
  for (Index i = 0; i < types.size(); ++i) {
    const Type type = types[i];
    Write(type);
    Writef(" %c%d;", MangleType(type), i);
    Write(Newline());
  }
  Write(CloseBrace(), ";", Newline(), "#endif /* ", name, " */", Newline());
}

void CWriter::WriteTailcalleeParamTypes() {
  // A structure for the spilled parameters of a tail-callee is needed in
  // three cases: for a function that makes a tail-call (and therefore
  // will have a tail-callee version generated), for any imported function
  // (for which wasm2c generates a weak import that presents the tail-callee
  // interface, in case the exporting module doesn't generate one itself), and
  // for any type entry referenced in a return_call_indirect instruction.

  for (const Func* func : module_->funcs) {
    if (IsImport(func->name) || func->features_used.tailcall) {
      if (func->decl.sig.GetNumParams() > 1) {
        DeclareStruct(func->decl.sig.param_types);
      }
    }
  }

  for (TypeEntry* type : module_->types) {
    FuncType* func_type = cast<FuncType>(type);
    if (func_type->GetNumParams() > 1 && func_type->features_used.tailcall) {
      DeclareStruct(func_type->sig.param_types);
    }
  }
}

void CWriter::WriteMultivalueResultTypes() {
  for (TypeEntry* type : module_->types) {
    FuncType* func_type = cast<FuncType>(type);
    if (func_type->GetNumResults() > 1) {
      DeclareStruct(func_type->sig.result_types);
    }
  }
}

void CWriter::WriteTagTypes() {
  for (const Tag* tag : module_->tags) {
    const FuncDeclaration& tag_type = tag->decl;
    Index num_params = tag_type.GetNumParams();
    if (num_params <= 1) {
      continue;
    }
    DeclareStruct(tag_type.sig.param_types);
  }
}

void CWriter::WriteFuncTypeDecls() {
  if (module_->types.empty()) {
    return;
  }

  Write(Newline());

  std::string serialized_type;
  for (const TypeEntry* type : module_->types) {
    const std::string name =
        DefineGlobalScopeName(ModuleFieldType::Type, type->name);

    if (c_streams_.size() > 1) {
      Write("FUNC_TYPE_DECL_EXTERN_T(", name, ");", Newline());
    }
  }
}

void CWriter::WriteFuncTypes() {
  if (module_->types.empty()) {
    return;
  }

  Write(Newline());

  std::unordered_map<std::string, std::string> type_hash;

  std::string serialized_type;
  for (const TypeEntry* type : module_->types) {
    const std::string name = GetGlobalName(ModuleFieldType::Type, type->name);
    SerializeFuncType(*cast<FuncType>(type), serialized_type);

    auto prior_type = type_hash.find(serialized_type);
    if (prior_type != type_hash.end()) {
      /* duplicate function type */
      unique_func_type_names_.push_back(prior_type->second);
    } else {
      unique_func_type_names_.push_back(name);
      type_hash.emplace(serialized_type, name);
      if (c_streams_.size() > 1) {
        Write("FUNC_TYPE_EXTERN_T(");
      } else {
        Write("FUNC_TYPE_T(");
      }
      Write(name, ") = \"");
      for (uint8_t x : serialized_type) {
        Writef("\\x%02x", x);
      }
      Write("\";", Newline());
    }
  }
}

void CWriter::Write(const FuncTypeExpr& expr) {
  Index func_type_index = module_->GetFuncTypeIndex(expr.func_type->sig);
  Write(unique_func_type_names_.at(func_type_index));
}

// static
void CWriter::SerializeFuncType(const FuncType& func_type,
                                std::string& serialized_type) {
  unsigned int len = func_type.GetNumParams() + func_type.GetNumResults() + 1;

  charconst mangled_signature = static_cast<char*>(alloca(len));
  char* next_byte = mangled_signature;

  // step 1: serialize each param type
  for (Index i = 0; i < func_type.GetNumParams(); ++i) {
    *next_byte++ = MangleType(func_type.GetParamType(i));
  }

  // step 2: separate params and results with a space
  *next_byte++ = ' ';

  // step 3: serialize each result type
  for (Index i = 0; i < func_type.GetNumResults(); ++i) {
    *next_byte++ = MangleType(func_type.GetResultType(i));
  }

  assert(next_byte - mangled_signature == static_cast<ptrdiff_t>(len));

  // step 4: SHA-256 the whole string
  sha256({mangled_signature, len}, serialized_type);
}

void CWriter::WriteTagDecls() {
  Index tag_index = 0;
  for (const Tag* tag : module_->tags) {
    bool is_import = tag_index < module_->num_tag_imports;
    if (!is_import) {
      // Tags are identified and compared solely by their (unique) address.
      // The data stored in this variable is never read.
      if (tag_index == module_->num_tag_imports) {
        Write(Newline());
        Write("typedef char wasm_tag_placeholder_t;", Newline());
      }
      DefineGlobalScopeName(ModuleFieldType::Tag, tag->name);
      if (c_streams_.size() > 1) {
        Write("extern const wasm_tag_placeholder_t ",
              GlobalName(ModuleFieldType::Tag, tag->name), ";", Newline());
      }
    }
    tag_index++;
  }
}

void CWriter::WriteTags() {
  Write(Newline());
  Index tag_index = 0;
  for (const Tag* tag : module_->tags) {
    bool is_import = tag_index < module_->num_tag_imports;
    if (!is_import) {
      Write(InternalSymbolScope(), "const wasm_tag_placeholder_t ",
            GlobalName(ModuleFieldType::Tag, tag->name), ";", Newline());
    }
    tag_index++;
  }
}

void CWriter::ComputeUniqueImports() {
  using modname_name_pair = std::pair<std::string, std::string>;
  std::map<modname_name_pair, const Import*> import_map;
  for (const Import* import : module_->imports) {
    // After emplacing, the returned bool says whether the insert happened;
    // i.e., was there already an import with the same modname and name?
    // If there was, make sure it was at least the same kind of import.
    const auto iterator_and_insertion_bool = import_map.emplace(
        modname_name_pair(import->module_name, import->field_name), import);
    if (!iterator_and_insertion_bool.second) {
      if (iterator_and_insertion_bool.first->second->kind() != import->kind()) {
        UNIMPLEMENTED("contradictory import declaration");
      } else {
        fprintf(stderr, "warning: duplicate import declaration \"%s\" \"%s\"\n",
                import->module_name.c_str(), import->field_name.c_str());
      }
    }
    import_module_set_.insert(import->module_name);
    if (import->kind() == ExternalKind::Func) {
      import_func_module_set_.insert(import->module_name);
    }
  }

  for (const auto& node : import_map) {
    unique_imports_.push_back(node.second);
  }
}

void CWriter::BeginInstance() {
  if (module_->imports.empty()) {
    Write("typedef struct ", ModuleInstanceTypeName(), " ", OpenBrace());
    return;
  }

  ComputeUniqueImports();

  // define names of per-instance imports
  for (const Import* import : module_->imports) {
    DefineImportName(import, import->module_name, import->field_name);
  }

  // Forward declaring module instance types
  for (const auto& import_module : import_module_set_) {
    DefineImportedModuleInstanceName(import_module);
    Write("struct ", ModuleInstanceTypeName(import_module), ";", Newline());
  }

  // Forward declaring module imports
  for (const Import* import : unique_imports_) {
    if ((import->kind() == ExternalKind::Func) ||
        (import->kind() == ExternalKind::Tag)) {
      continue;
    }

    Write("extern ");
    switch (import->kind()) {
      case ExternalKind::Global: {
        const Global& global = cast<GlobalImport>(import)->global;
        Write(global.type);
        break;
      }

      case ExternalKind::Memory: {
        Write(GetMemoryTypeString(cast<MemoryImport>(import)->memory));
        break;
      }

      case ExternalKind::Table:
        WriteTableType(cast<TableImport>(import)->table.elem_type);
        break;

      default:
        WABT_UNREACHABLE;
    }
    Write("* ", ExportName(import->module_name, import->field_name), "(struct ",
          ModuleInstanceTypeName(import->module_name), "*);", Newline());
  }
  Write(Newline());

  // Add pointers to module instances that any func is imported from,
  // so that imported functions can be given their own module instances
  // when invoked
  Write("typedef struct ", ModuleInstanceTypeName(), " ", OpenBrace());
  for (const auto& import_module : import_func_module_set_) {
    Write("struct ", ModuleInstanceTypeName(import_module), "* ",
          GlobalName(ModuleFieldType::Import, import_module), ";", Newline());
  }

  for (const Import* import : unique_imports_) {
    if ((import->kind() == ExternalKind::Func) ||
        (import->kind() == ExternalKind::Tag)) {
      continue;
    }

    Write("/* import: '", SanitizeForComment(import->module_name), "' '",
          SanitizeForComment(import->field_name), "' */", Newline());

    switch (import->kind()) {
      case ExternalKind::Global:
        WriteGlobal(cast<GlobalImport>(import)->global,
                    std::string("*") +
                        ExportName(import->module_name, import->field_name));
        break;

      case ExternalKind::Memory:
        WriteMemory(std::string("*") +
                        ExportName(import->module_name, import->field_name),
                    cast<MemoryImport>(import)->memory);
        break;

      case ExternalKind::Table: {
        const Table& table = cast<TableImport>(import)->table;
        WriteTable(std::string("*") +
                       ExportName(import->module_name, import->field_name),
                   table.elem_type);
      } break;

      default:
        WABT_UNREACHABLE;
    }

    Write(Newline());
  }
}

// Write module-wide imports (funcs & tags), which aren't tied to an instance.
void CWriter::WriteImports() {
  if (unique_imports_.empty())
    return;

  Write(Newline());

  for (const Import* import : unique_imports_) {
    if (import->kind() == ExternalKind::Func) {
      Write(Newline(), "/* import: '", SanitizeForComment(import->module_name),
            "' '", SanitizeForComment(import->field_name), "' */", Newline());
      const Func& func = cast<FuncImport>(import)->func;
      WriteImportFuncDeclaration(
          func.decl, import->module_name,
          ExportName(import->module_name, import->field_name));
      Write(";", Newline());
      if (options_.features.tail_call_enabled()) {
        WriteTailCallFuncDeclaration(GetTailCallRef(func.name));
        Write(";", Newline());
      }
    } else if (import->kind() == ExternalKind::Tag) {
      Write(Newline(), "/* import: '", SanitizeForComment(import->module_name),
            "' '", SanitizeForComment(import->field_name), "' */", Newline());
      Write("extern const wasm_rt_tag_t ",
            ExportName(import->module_name, import->field_name), ";",
            Newline());
    }
  }
}

void CWriter::WriteTailCallWeakImports() {
  for (const Import* import : unique_imports_) {
    if (import->kind() != ExternalKind::Func) {
      continue;
    }
    const Func& func = cast<FuncImport>(import)->func;
    Write(Newline(), "/* handler for missing tail-call on import: '",
          SanitizeForComment(import->module_name), "' '",
          SanitizeForComment(import->field_name), "' */", Newline());
    Write("WEAK_FUNC_DECL(",
          TailCallExportName(import->module_name, import->field_name), ", ",
          kTailCallFallbackPrefix, module_prefix_, "_",
          ExportName(import->module_name, import->field_name), ")", Newline());
    Write(OpenBrace(), "next->fn = NULL;", Newline());

    Index num_params = func.GetNumParams();
    Index num_results = func.GetNumResults();
    if (num_params >= 1) {
      Write(func.decl.sig.param_types, " params;", Newline());
      Write("wasm_rt_memcpy(params, tail_call_stack, sizeof(params));",
            Newline());
    }

    if (num_results >= 1) {
      Write(func.decl.sig.result_types, " result = ");
    }

    Write(ExportName(import->module_name, import->field_name),
          "(*instance_ptr");

    if (num_params == 1) {
      Write(", params");
    } else {
      for (Index i = 0; i < num_params; ++i) {
        Writef(", params.%c%d", MangleType(func.GetParamType(i)), i);
      }
    }

    Write(");", Newline());

    if (num_results >= 1) {
      Write("wasm_rt_memcpy(tail_call_stack, &result, sizeof(result));",
            Newline());
    }

    Write(CloseBrace(), Newline());
  }
}

void CWriter::WriteFuncDeclarations() {
  if (module_->funcs.size() == module_->num_func_imports)
    return;

  Write(Newline());

  Index func_index = 0;
  for (const Func* func : module_->funcs) {
    bool is_import = func_index < module_->num_func_imports;
    if (!is_import) {
      Write(InternalSymbolScope());
      WriteFuncDeclaration(
          func->decl, DefineGlobalScopeName(ModuleFieldType::Func, func->name));
      Write(";", Newline());

      if (func->features_used.tailcall) {
        Write(InternalSymbolScope());
        WriteTailCallFuncDeclaration(GetTailCallRef(func->name));
        Write(";", Newline());
      }
    }
    ++func_index;
  }
}

void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl,
                                   const std::string& name) {
  Write(decl.sig.result_types, " ", name, "(");
  Write(ModuleInstanceTypeName(), "*");
  WriteParamTypes(decl);
  Write(")");
}

void CWriter::WriteTailCallFuncDeclaration(const std::string& mangled_name) {
  Write("void ", mangled_name,
        "(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t "
        "*next)");
}

void CWriter::WriteImportFuncDeclaration(const FuncDeclaration& decl,
                                         const std::string& module_name,
                                         const std::string& name) {
  Write(decl.sig.result_types, " ", name, "(");
  Write("struct ", ModuleInstanceTypeName(module_name), "*");
  WriteParamTypes(decl);
  Write(")");
}

void CWriter::WriteCallIndirectFuncDeclaration(const FuncDeclaration& decl,
                                               const std::string& name) {
  Write(decl.sig.result_types, " ", name, "(void*");
  WriteParamTypes(decl);
  Write(")");
}

static bool func_uses_simd(const FuncSignature& sig) {
  return std::any_of(sig.param_types.begin(), sig.param_types.end(),
                     [](auto x) { return x == Type::V128; }) ||
         std::any_of(sig.result_types.begin(), sig.result_types.end(),
                     [](auto x) { return x == Type::V128; });
}

void CWriter::ComputeSimdScope() {
  simd_used_in_header_ =
      module_->features_used.simd &&
      (std::any_of(module_->globals.begin(), module_->globals.end(),
                   [](const auto& x) { return x->type == Type::V128; }) ||
       std::any_of(module_->imports.begin(), module_->imports.end(),
                   [](const auto& x) {
                     return x->kind() == ExternalKind::Func &&
                            func_uses_simd(cast<FuncImport>(x)->func.decl.sig);
                   }) ||
       std::any_of(module_->exports.begin(), module_->exports.end(),
                   [&](const auto& x) {
                     return x->kind == ExternalKind::Func &&
                            func_uses_simd(module_->GetFunc(x->var)->decl.sig);
                   }));
}

void CWriter::WriteHeaderIncludes() {
  Write("#include \"wasm-rt.h\"", Newline());

  if (module_->features_used.exceptions) {
    Write("#include \"wasm-rt-exceptions.h\"", Newline(), Newline());
  }

  if (simd_used_in_header_) {
    WriteV128Decl();
  }

  Write(Newline());
}

void CWriter::WriteV128Decl() {
  Write("#include ", Newline(), Newline());
  Write("#ifndef WASM_RT_SIMD_TYPE_DEFINED", Newline(),
        "#define WASM_RT_SIMD_TYPE_DEFINED", Newline(),
        "typedef simde_v128_t v128;", Newline(), "#endif", Newline(),
        Newline());
}

void CWriter::WriteModuleInstance() {
  BeginInstance();
  WriteGlobals();
  WriteMemories();
  WriteTables();
  WriteDataInstances();
  WriteElemInstances();

  // C forbids an empty struct
  if (module_->globals.empty() && module_->memories.empty() &&
      module_->tables.empty() && import_func_module_set_.empty()) {
    Write("char dummy_member;", Newline());
  }

  Write(CloseBrace(), " ", ModuleInstanceTypeName(), ";", Newline());
  Write(Newline());
}

void CWriter::WriteGlobals() {
  Index global_index = 0;
  if (module_->globals.size() != module_->num_global_imports) {
    for (const Global* global : module_->globals) {
      bool is_import = global_index < module_->num_global_imports;
      if (!is_import) {
        WriteGlobal(*global, DefineInstanceMemberName(ModuleFieldType::Global,
                                                      global->name));
        Write(Newline());
      }
      ++global_index;
    }
  }
}

void CWriter::WriteGlobal(const Global& global, const std::string& name) {
  Write(global.type, " ", name, ";");
}

void CWriter::WriteGlobalPtr(const Global& global, const std::string& name) {
  Write(global.type, "* ", name, "(", ModuleInstanceTypeName(), "* instance)");
}

void CWriter::WriteMemories() {
  if (module_->memories.size() == module_->num_memory_imports)
    return;

  Index memory_index = 0;
  for (const Memory* memory : module_->memories) {
    bool is_import = memory_index < module_->num_memory_imports;
    if (!is_import) {
      WriteMemory(
          DefineInstanceMemberName(ModuleFieldType::Memory, memory->name),
          *memory);
      Write(Newline());
    }
    ++memory_index;
  }
}

void CWriter::WriteMemory(const std::string& name, const Memory& memory) {
  Write(GetMemoryTypeString(memory), " ", name, ";");
}

void CWriter::WriteMemoryPtr(const std::string& name, const Memory& memory) {
  Write(GetMemoryTypeString(memory), "* ", name, "(", ModuleInstanceTypeName(),
        "* instance)");
}

void CWriter::WriteTables() {
  if (module_->tables.size() == module_->num_table_imports) {
    return;
  }

  Index table_index = 0;
  for (const Table* table : module_->tables) {
    bool is_import = table_index < module_->num_table_imports;
    if (!is_import) {
      WriteTable(DefineInstanceMemberName(ModuleFieldType::Table, table->name),
                 table->elem_type);
      Write(Newline());
    }
    ++table_index;
  }
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=89 G=90

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© 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.