summaryrefslogtreecommitdiffstats
path: root/third_party/wasm2c/src/c-writer.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/wasm2c/src/c-writer.cc
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/wasm2c/src/c-writer.cc')
-rw-r--r--third_party/wasm2c/src/c-writer.cc2595
1 files changed, 2595 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/c-writer.cc b/third_party/wasm2c/src/c-writer.cc
new file mode 100644
index 0000000000..c008474aeb
--- /dev/null
+++ b/third_party/wasm2c/src/c-writer.cc
@@ -0,0 +1,2595 @@
+/*
+ * 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 "src/c-writer.h"
+
+#include <cctype>
+#include <cinttypes>
+#include <map>
+#include <set>
+
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/ir.h"
+#include "src/literal.h"
+#include "src/stream.h"
+#include "src/string-view.h"
+
+#define INDENT_SIZE 2
+
+#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort()
+
+namespace wabt {
+
+namespace {
+
+struct Label {
+ Label(LabelType label_type,
+ const std::string& name,
+ const TypeVector& sig,
+ size_t type_stack_size,
+ bool used = false)
+ : label_type(label_type),
+ name(name),
+ sig(sig),
+ type_stack_size(type_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;
+ bool used = false;
+};
+
+template <int>
+struct Name {
+ explicit Name(const std::string& name) : name(name) {}
+ const std::string& name;
+};
+
+typedef Name<0> LocalName;
+typedef Name<1> GlobalName;
+typedef Name<2> ExternalPtr;
+typedef Name<3> ExternalRef;
+
+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 GlobalVar {
+ explicit GlobalVar(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 ResultType {
+ explicit ResultType(const TypeVector& types) : types(types) {}
+ const TypeVector& types;
+};
+
+struct Newline {};
+struct OpenBrace {};
+struct CloseBrace {};
+
+int GetShiftMask(Type type) {
+ switch (type) {
+ case Type::I32: return 31;
+ case Type::I64: return 63;
+ default: WABT_UNREACHABLE; return 0;
+ }
+}
+
+class CWriter {
+ public:
+ CWriter(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const WriteCOptions& options)
+ : options_(options),
+ c_stream_(c_stream),
+ h_stream_(h_stream),
+ header_name_(header_name) {}
+
+ Result WriteModule(const Module&);
+
+ private:
+ typedef std::set<std::string> SymbolSet;
+ typedef std::map<std::string, std::string> SymbolMap;
+ typedef std::pair<Index, Type> StackTypePair;
+ typedef std::map<StackTypePair, std::string> StackVarSymbolMap;
+
+ void UseStream(Stream*);
+
+ 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 IsTopLabelUsed() const;
+ void PopLabel();
+
+ static std::string AddressOf(const std::string&);
+
+ static char MangleType(Type);
+ static std::string MangleTypes(const TypeVector&);
+ static std::string MangleMultivalueTypes(const TypeVector&);
+ static std::string MangleName(string_view);
+ static std::string MangleFuncName(string_view,
+ const TypeVector& param_types,
+ const TypeVector& result_types);
+ static std::string MangleGlobalName(string_view, Type);
+ static std::string LegalizeName(string_view);
+ std::string DefineName(SymbolSet*, string_view);
+ std::string DefineImportName(const std::string& name,
+ string_view module_name,
+ string_view mangled_field_name);
+ std::string DefineGlobalScopeName(const std::string&);
+ std::string DefineLocalScopeName(const std::string&);
+ std::string DefineGlobalVarName(const std::string& name);
+ std::string DefineStackVarName(Index, Type, string_view);
+
+ void Indent(int size = INDENT_SIZE);
+ void Dedent(int size = INDENT_SIZE);
+ void WriteIndent();
+ void WriteData(const void* 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)...);
+ }
+
+ std::string GetGlobalName(const std::string&) const;
+ std::string GetGlobalVarName(const std::string& name) const;
+
+ void Write() {}
+ void Write(Newline);
+ void Write(OpenBrace);
+ void Write(CloseBrace);
+ void Write(Index);
+ void Write(string_view);
+ void Write(const LocalName&);
+ void Write(const GlobalName&);
+ void Write(const ExternalPtr&);
+ void Write(const ExternalRef&);
+ void Write(Type);
+ void Write(SignedType);
+ void Write(TypeEnum);
+ void Write(const Var&);
+ void Write(const GotoLabel&);
+ void Write(const LabelDecl&);
+ void Write(const GlobalVar&);
+ void Write(const StackVar&);
+ void Write(const ResultType&);
+ void Write(const Const&);
+ void WriteInitExpr(const ExprList&);
+ std::string GenerateHeaderGuard() const;
+ void WriteSourceTop();
+ void WriteMultivalueTypes();
+ void WriteSandboxStruct();
+ void WriteFuncTypes();
+ void WriteImports();
+ bool IsFuncStatic(std::string name);
+ std::string GetFuncStaticOrExport(std::string);
+ void WriteFuncDeclarations(bool for_header);
+ void WriteFuncDeclaration(const FuncDeclaration&, const std::string&, bool add_storage_class);
+ void WriteEntryFuncs();
+ void WriteEntryFunc(const FuncDeclaration&, const std::string&, bool add_storage_class);
+ void WriteImportFuncDeclaration(const FuncDeclaration&, const std::string&);
+ std::string GetMainMemoryName();
+ void WriteGlobalInitializers();
+ void WriteGlobals();
+ void WriteGlobalsExport();
+ void WriteGlobal(const Global&, const std::string&);
+ void WriteMemories();
+ void WriteMemoriesExport();
+ void WriteMemory(const std::string&);
+ void WriteTables();
+ void WriteTablesExport();
+ void WriteTable(const std::string&);
+ void WriteDataInitializers();
+ void WriteElemInitializers();
+ void WriteExportLookup();
+ void WriteCallbackAddRemove();
+ void WriteInit();
+ void WriteFuncs();
+ void Write(const Func&);
+ void WriteParamsAndLocals();
+ void WriteParams(const std::vector<std::string>& index_to_name);
+ void WriteLocals(const std::vector<std::string>& index_to_name);
+ void WriteStackVarDeclarations();
+ void Write(const ExprList&);
+
+ 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&);
+
+ const WriteCOptions& options_;
+ const Module* module_ = nullptr;
+ const Func* func_ = nullptr;
+ Stream* stream_ = nullptr;
+ MemoryStream func_stream_;
+ Stream* c_stream_ = nullptr;
+ Stream* h_stream_ = nullptr;
+ std::string header_name_;
+ Result result_ = Result::Ok;
+ int indent_ = 0;
+ bool should_write_indent_next_ = false;
+
+ SymbolMap global_sym_map_;
+ SymbolMap local_sym_map_;
+ SymbolMap globalvars_sym_map_;
+ StackVarSymbolMap stack_var_sym_map_;
+ SymbolSet global_syms_;
+ SymbolSet local_syms_;
+ SymbolSet globalvars_syms_;
+ SymbolSet import_syms_;
+ TypeVector type_stack_;
+ std::vector<Label> label_stack_;
+};
+
+static const char kImplicitFuncLabel[] = "$Bfunc";
+
+#define SECTION_NAME(x) s_header_##x
+#include "src/prebuilt/wasm2c.include.h"
+#undef SECTION_NAME
+
+#define SECTION_NAME(x) s_source_##x
+#include "src/prebuilt/wasm2c.include.c"
+#undef SECTION_NAME
+
+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(), used);
+ else
+ label_stack_.emplace_back(label_type, name, sig.result_types,
+ type_stack_.size(), used);
+}
+
+const Label* CWriter::FindLabel(const Var& var) {
+ 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);
+ 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
+std::string CWriter::AddressOf(const std::string& s) {
+ return "(&" + s + ")";
+}
+
+// static
+char CWriter::MangleType(Type type) {
+ switch (type) {
+ case Type::I32: return 'i';
+ case Type::I64: return 'j';
+ case Type::F32: return 'f';
+ case Type::F64: return 'd';
+ default: WABT_UNREACHABLE;
+ }
+}
+
+// static
+std::string CWriter::MangleTypes(const TypeVector& types) {
+ if (types.empty())
+ return std::string("v");
+
+ std::string result;
+ for (auto type : types) {
+ result += MangleType(type);
+ }
+ return result;
+}
+
+// static
+std::string CWriter::MangleMultivalueTypes(const TypeVector& types) {
+ assert(types.size() >= 2);
+ std::string result = "wasm_multi_";
+ for (auto type : types) {
+ result += MangleType(type);
+ }
+ return result;
+}
+
+// static
+std::string CWriter::MangleName(string_view name) {
+ const char kPrefix = 'Z';
+ std::string result = "Z_";
+
+ if (!name.empty()) {
+ for (char c : name) {
+ if ((isalnum(c) && c != kPrefix) || c == '_') {
+ result += c;
+ } else {
+ result += kPrefix;
+ result += StringPrintf("%02X", static_cast<uint8_t>(c));
+ }
+ }
+ }
+
+ return result;
+}
+
+// static
+std::string CWriter::MangleFuncName(string_view name,
+ const TypeVector& param_types,
+ const TypeVector& result_types) {
+ std::string sig = MangleTypes(result_types) + MangleTypes(param_types);
+ return MangleName(name) + MangleName(sig);
+}
+
+// static
+std::string CWriter::MangleGlobalName(string_view name, Type type) {
+ std::string sig(1, MangleType(type));
+ return MangleName(name) + MangleName(sig);
+}
+
+// static
+std::string CWriter::LegalizeName(string_view name) {
+ if (name.empty())
+ return "_";
+
+ std::string result;
+ result = isalpha(name[0]) ? name[0] : '_';
+ for (size_t i = 1; i < name.size(); ++i)
+ result += isalnum(name[i]) ? name[i] : '_';
+
+ // In addition to containing valid characters for C, we must also avoid
+ // colliding with things C cares about, such as reserved words (e.g. "void")
+ // or a function name like main() (which a compiler will complain about if we
+ // define it with another type). To avoid such problems, prefix.
+ result = "w2c_" + result;
+
+ return result;
+}
+
+std::string CWriter::DefineName(SymbolSet* set, string_view name) {
+ std::string legal = LegalizeName(name);
+ if (set->find(legal) != set->end()) {
+ std::string base = legal + "_";
+ size_t count = 0;
+ do {
+ legal = base + std::to_string(count++);
+ } while (set->find(legal) != set->end());
+ }
+ set->insert(legal);
+ return legal;
+}
+
+string_view StripLeadingDollar(string_view name) {
+ if (!name.empty() && name[0] == '$') {
+ name.remove_prefix(1);
+ }
+ return name;
+}
+
+std::string CWriter::DefineImportName(const std::string& name,
+ string_view module,
+ string_view mangled_field_name) {
+ std::string mangled = MangleName(module) + mangled_field_name.to_string();
+ import_syms_.insert(name);
+ global_syms_.insert(mangled);
+ global_sym_map_.insert(SymbolMap::value_type(name, mangled));
+ return mangled;
+}
+
+std::string CWriter::DefineGlobalScopeName(const std::string& name) {
+ std::string unique = DefineName(&global_syms_, StripLeadingDollar(name));
+ global_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineLocalScopeName(const std::string& name) {
+ std::string unique = DefineName(&local_syms_, StripLeadingDollar(name));
+ local_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineGlobalVarName(const std::string& name) {
+ std::string unique = DefineName(&globalvars_syms_, StripLeadingDollar(name));
+ globalvars_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineStackVarName(Index index,
+ Type type,
+ string_view name) {
+ std::string unique = DefineName(&local_syms_, name);
+ StackTypePair stp = {index, type};
+ stack_var_sym_map_.insert(StackVarSymbolMap::value_type(stp, unique));
+ return unique;
+}
+
+void CWriter::Indent(int size) {
+ indent_ += size;
+}
+
+void CWriter::Dedent(int size) {
+ indent_ -= size;
+ assert(indent_ >= 0);
+}
+
+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 void* src, size_t size) {
+ if (should_write_indent_next_) {
+ WriteIndent();
+ should_write_indent_next_ = false;
+ }
+ 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) {
+ Write("\n");
+ should_write_indent_next_ = true;
+}
+
+void CWriter::Write(OpenBrace) {
+ Write("{");
+ Indent();
+ Write(Newline());
+}
+
+void CWriter::Write(CloseBrace) {
+ Dedent();
+ Write("}");
+}
+
+void CWriter::Write(Index index) {
+ Writef("%" PRIindex, index);
+}
+
+void CWriter::Write(string_view s) {
+ WriteData(s.data(), s.size());
+}
+
+void CWriter::Write(const LocalName& name) {
+ assert(local_sym_map_.count(name.name) == 1);
+ Write(local_sym_map_[name.name]);
+}
+
+std::string CWriter::GetGlobalName(const std::string& name) const {
+ assert(global_sym_map_.count(name) == 1);
+ auto iter = global_sym_map_.find(name);
+ assert(iter != global_sym_map_.end());
+ return iter->second;
+}
+
+std::string CWriter::GetGlobalVarName(const std::string& name) const {
+ assert(globalvars_sym_map_.count(name) == 1);
+ auto iter = globalvars_sym_map_.find(name);
+ assert(iter != globalvars_sym_map_.end());
+ return iter->second;
+}
+
+void CWriter::Write(const GlobalName& name) {
+ Write(GetGlobalName(name.name));
+}
+
+void CWriter::Write(const ExternalPtr& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (is_import) {
+ Write(GetGlobalName(name.name));
+ } else {
+ Write(AddressOf(GetGlobalName(name.name)));
+ }
+}
+
+void CWriter::Write(const ExternalRef& name) {
+ Write(GetGlobalName(name.name));
+}
+
+void CWriter::Write(const Var& var) {
+ assert(var.is_name());
+ Write(LocalName(var.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), "; ");
+ }
+ }
+ }
+
+ if (goto_label.var.is_name()) {
+ Write("goto ", goto_label.var, ";");
+ } 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 ", Var(kImplicitFuncLabel), ";");
+ }
+}
+
+void CWriter::Write(const LabelDecl& label) {
+ if (IsTopLabelUsed())
+ Write(label.name, ":;", Newline());
+}
+
+void CWriter::Write(const GlobalVar& var) {
+ assert(var.var.is_name());
+ Write(GetGlobalVarName(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);
+ }
+}
+
+void CWriter::Write(Type type) {
+ switch (type) {
+ case Type::I32: Write("u32"); break;
+ case Type::I64: Write("u64"); break;
+ case Type::F32: Write("f32"); break;
+ case Type::F64: Write("f64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(TypeEnum type) {
+ 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;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(SignedType type) {
+ switch (type.type) {
+ case Type::I32: Write("s32"); break;
+ case Type::I64: Write("s64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const ResultType& rt) {
+ if (rt.types.empty()) {
+ Write("void");
+ } else if (rt.types.size() == 1) {
+ Write(rt.types[0]);
+ } else {
+ Write("struct ", MangleMultivalueTypes(rt.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 {
+ Writef("%.17g", Bitcast<double>(f64_bits));
+ }
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::WriteInitExpr(const ExprList& expr_list) {
+ if (expr_list.empty())
+ return;
+
+ assert(expr_list.size() == 1);
+ const Expr* expr = &expr_list.front();
+ switch (expr_list.front().type()) {
+ case ExprType::Const:
+ Write(cast<ConstExpr>(expr)->const_);
+ break;
+
+ case ExprType::GlobalGet:
+ Write(GlobalVar(cast<GlobalGetExpr>(expr)->var));
+ 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());
+ Write(s_source_declarations);
+}
+
+void CWriter::WriteMultivalueTypes() {
+ for (TypeEntry* type : module_->types) {
+ FuncType* func_type = cast<FuncType>(type);
+ Index num_results = func_type->GetNumResults();
+ if (num_results <= 1) {
+ continue;
+ }
+ std::string name = MangleMultivalueTypes(func_type->sig.result_types);
+ // these ifndefs are actually to support importing multiple modules
+ // incidentally they also mean we don't have to bother with deduplication
+ Write("#ifndef ", name, Newline());
+ Write("#define ", name, " ", name, Newline());
+ Write("struct ", name, " {", Newline());
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func_type->GetResultType(i);
+ Write(" ", type);
+ Writef(" %c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write("};", Newline(), "#endif /* ", name, " */", Newline());
+ }
+}
+
+void CWriter::WriteSandboxStruct() {
+ Write("struct wasm2c_sandbox_t {", Newline());
+ Indent(2);
+
+ Write("wasm_sandbox_wasi_data wasi_data;", Newline());
+
+ WriteMemories();
+ WriteTables();
+
+ {
+ Write("wasm_func_type_t* func_type_structs;");
+ Write(Newline());
+ Write("u32 func_type_count;");
+ Write(Newline());
+ Writef("u32 func_types[%" PRIzd "];", module_->types.size());
+ Write(Newline());
+ }
+
+ WriteGlobals();
+
+ Dedent(2);
+ Write("};", Newline(), Newline());
+}
+
+void CWriter::WriteFuncTypes() {
+ Write(Newline());
+ Write("static void init_func_types(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ Index func_type_index = 0;
+ for (TypeEntry* type : module_->types) {
+ FuncType* func_type = cast<FuncType>(type);
+ Index num_params = func_type->GetNumParams();
+ Index num_results = func_type->GetNumResults();
+ Write(OpenBrace());
+ Write("wasm_rt_type_t param_ret_types[] = { ");
+ bool first = true;
+ if (num_results == 0 && num_params == 0) {
+ // Make sure array has at least one element
+ Write("/* void(*)(void) */ WASM_RT_I32 ");
+ } else {
+ for (Index i = 0; i < num_params; ++i) {
+ if (!first) {
+ Write(", ");
+ }
+ Write(TypeEnum(func_type->GetParamType(i)));
+ first = false;
+ }
+ for (Index i = 0; i < num_results; ++i) {
+ if (!first) {
+ Write(", ");
+ }
+ Write(TypeEnum(func_type->GetResultType(i)));
+ first = false;
+ }
+ }
+ Write(" };", Newline());
+ Write("sbx->func_types[", func_type_index, "] = wasm_rt_register_func_type(&sbx->func_type_structs, &sbx->func_type_count, ",
+ num_params, ", ", num_results, ", param_ret_types");
+ Write(");", Newline());
+ Write(CloseBrace(), Newline());
+ ++func_type_index;
+ }
+ Write(CloseBrace(), Newline());
+
+ Write("static void cleanup_func_types(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ {
+ Write("wasm_rt_cleanup_func_types(&sbx->func_type_structs, &sbx->func_type_count);", Newline());
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteImports() {
+ if (module_->imports.empty())
+ return;
+
+ Write(Newline());
+
+ // TODO(binji): Write imports ordered by type.
+ for (const Import* import : module_->imports) {
+ Write("/* import: '", import->module_name, "' '", import->field_name,
+ "' */", Newline());
+ Write("extern ");
+ switch (import->kind()) {
+ case ExternalKind::Func: {
+ const Func& func = cast<FuncImport>(import)->func;
+ WriteImportFuncDeclaration(
+ func.decl,
+ DefineImportName(
+ func.name, import->module_name,
+ MangleFuncName(import->field_name, func.decl.sig.param_types,
+ func.decl.sig.result_types)));
+ Write(";");
+ break;
+ }
+
+ case ExternalKind::Global: {
+ const Global& global = cast<GlobalImport>(import)->global;
+ WriteGlobal(global,
+ DefineImportName(
+ global.name, import->module_name,
+ MangleGlobalName(import->field_name, global.type)));
+ Write(";");
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ const Memory& memory = cast<MemoryImport>(import)->memory;
+ WriteMemory(DefineImportName(memory.name, import->module_name,
+ MangleName(import->field_name)));
+ break;
+ }
+
+ case ExternalKind::Table: {
+ const Table& table = cast<TableImport>(import)->table;
+ WriteTable(DefineImportName(table.name, import->module_name,
+ MangleName(import->field_name)));
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ Write(Newline());
+ }
+}
+
+void CWriter::WriteFuncDeclarations(bool for_header) {
+ if (module_->funcs.size() == module_->num_func_imports)
+ return;
+
+ Write(Newline());
+
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ std::string global_func_name;
+ if (for_header) {
+ global_func_name = DefineGlobalScopeName(func->name);
+ } else {
+ global_func_name = GetGlobalName(func->name);
+ }
+ bool is_import = func_index < module_->num_func_imports;
+ bool static_exclude = for_header && IsFuncStatic(global_func_name);
+ if (!is_import && !static_exclude) {
+ WriteFuncDeclaration(func->decl, global_func_name, true /* add_storage_class */);
+ Write(";", Newline());
+ }
+ ++func_index;
+ }
+}
+
+
+void CWriter::WriteEntryFuncs() {
+ if (module_->funcs.size() == module_->num_func_imports)
+ return;
+
+ Write(Newline());
+ Write("#if defined(ENTRY_PROLOGUE) || defined(ENTRY_EPILOGUE)", Newline());
+
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import) {
+ WriteEntryFunc(func->decl, GetGlobalName(func->name), true /* add_storage_class */);
+ Write(Newline());
+ }
+ ++func_index;
+ }
+
+ Write("#endif", Newline());
+}
+
+
+bool CWriter::IsFuncStatic(std::string name) {
+ // static functions starts with prefix __
+ return name.rfind("w2c___", 0) == 0 || name == "w2c_main";
+}
+
+std::string CWriter::GetFuncStaticOrExport(std::string name) {
+ std::string static_export_string = IsFuncStatic(name)? "static " : "FUNC_EXPORT ";
+ return static_export_string;
+}
+
+void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& name,
+ bool add_storage_class) {
+ // LLVM adds some extra function calls to all wasm objects prefixed with "__".
+ // Keep this static (private), else we cause symbol collisions when linking multiple wasm modules
+ // Additionally windows dlls have to export functions explicitly
+ if (add_storage_class) {
+ Write(GetFuncStaticOrExport(name));
+ }
+ Write(ResultType(decl.sig.result_types), " ", name, "(wasm2c_sandbox_t* const");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", ", decl.GetParamType(i));
+ }
+ Write(")");
+}
+
+void CWriter::WriteEntryFunc(const FuncDeclaration& decl,
+ const std::string& name,
+ bool add_storage_class) {
+ // LLVM adds some extra function calls to all wasm objects prefixed with "__".
+ // Keep this static (private), else we cause symbol collisions when linking multiple wasm modules
+ // Additionally windows dlls have to export functions explicitly
+ if (add_storage_class) {
+ Write(GetFuncStaticOrExport(name));
+ }
+ Write(ResultType(decl.sig.result_types), " w2centry_", name, "(wasm2c_sandbox_t* const sbx");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", ", decl.GetParamType(i), " p", std::to_string(i));
+ }
+ Write(") ", OpenBrace());
+ {
+ Write("ENTRY_PROLOGUE;", Newline());
+ if (!decl.sig.result_types.empty()) {
+ Write(ResultType(decl.sig.result_types), " ret = ");
+ }
+ Write(name, "(sbx");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", p", std::to_string(i));
+ }
+ Write(");", Newline());
+ Write("ENTRY_EPILOGUE;", Newline());
+ if (!decl.sig.result_types.empty()) {
+ Write("return ret;", Newline());
+ }
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteImportFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& name) {
+ Write(ResultType(decl.sig.result_types), " ", name, "(void*");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", ", decl.GetParamType(i));
+ }
+ Write(")");
+}
+
+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, DefineGlobalVarName(global->name));
+ Write(";", Newline());
+ }
+ ++global_index;
+ }
+ }
+}
+
+void CWriter::WriteGlobalsExport() {
+ 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) {
+ std::string curr_global_name = GetGlobalVarName(global->name);
+ Writef("if (strcmp(\"%s\", name) == 0)", curr_global_name.c_str());
+ Write(OpenBrace());
+ Write("return &(sbx->", curr_global_name, ");", Newline());
+ Write(CloseBrace(), Newline());
+ }
+ ++global_index;
+ }
+ }
+}
+
+std::string CWriter::GetMainMemoryName() {
+ assert (!(module_->memories.size() == module_->num_memory_imports));
+ assert(module_->memories.size() <= 1);
+
+ std::string ret = GetGlobalName(module_->memories[0]->name);
+ return ret;
+}
+
+void CWriter::WriteGlobalInitializers() {
+
+ Write(Newline(), "static void init_globals(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+
+ {
+ Index global_index = 0;
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ assert(!global->init_expr.empty());
+ Write("sbx->", GetGlobalVarName(global->name), " = ");
+ WriteInitExpr(global->init_expr);
+ Write(";", Newline());
+ }
+ ++global_index;
+ }
+ }
+
+ {
+ Index global_index = 0;
+ std::string memory_name = GetMainMemoryName();
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ std::string global_name = GetGlobalVarName(global->name);
+ std::string global_name_expr = "sbx->" + global_name;
+ Write("WASM2C_SHADOW_MEMORY_RESERVE(&(sbx->", memory_name ,"), ", global_name_expr, ", sizeof(", global_name_expr, "));", Newline());
+ if (global_name == "w2c___heap_base") {
+ Write("WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(&(sbx->", memory_name, "), ", global_name_expr, ");", Newline());
+ }
+ }
+
+ ++global_index;
+ }
+ }
+
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteGlobal(const Global& global, const std::string& name) {
+ Write(global.type, " ", name);
+}
+
+void CWriter::WriteMemories() {
+ if (module_->memories.size() == module_->num_memory_imports)
+ return;
+
+ assert(module_->memories.size() <= 1);
+ Index memory_index = 0;
+ for (const Memory* memory : module_->memories) {
+ bool is_import = memory_index < module_->num_memory_imports;
+ if (!is_import) {
+ WriteMemory(DefineGlobalScopeName(memory->name));
+ Write(Newline());
+ }
+ ++memory_index;
+ }
+}
+
+void CWriter::WriteMemoriesExport() {
+ if (module_->memories.size() == module_->num_memory_imports)
+ return;
+
+ assert(module_->memories.size() <= 1);
+ Index memory_index = 0;
+ for (const Memory* memory : module_->memories) {
+ bool is_import = memory_index < module_->num_memory_imports;
+ if (!is_import) {
+ std::string curr_memory_name = GetGlobalName(memory->name);
+ Writef("if (strcmp(\"%s\", name) == 0)", curr_memory_name.c_str());
+ Write(OpenBrace());
+ Write("return &(sbx->", curr_memory_name, ");", Newline());
+ Write(CloseBrace(), Newline());
+ }
+ ++memory_index;
+ }
+}
+
+void CWriter::WriteMemory(const std::string& name) {
+ Write("wasm_rt_memory_t ", name, ";");
+}
+
+void CWriter::WriteTables() {
+ if (module_->tables.size() == module_->num_table_imports)
+ return;
+
+ assert(module_->tables.size() <= 1);
+ Index table_index = 0;
+ for (const Table* table : module_->tables) {
+ bool is_import = table_index < module_->num_table_imports;
+ if (!is_import) {
+ std::string curr_table_name = DefineGlobalScopeName(table->name);
+ Writef("u32 %s_current_index;", curr_table_name.c_str());
+ Write(Newline());
+ WriteTable(curr_table_name);
+ Write(Newline());
+ }
+ ++table_index;
+ }
+}
+
+void CWriter::WriteTablesExport() {
+ if (module_->tables.size() == module_->num_table_imports)
+ return;
+
+ assert(module_->tables.size() <= 1);
+ Index table_index = 0;
+ for (const Table* table : module_->tables) {
+ bool is_import = table_index < module_->num_table_imports;
+ if (!is_import) {
+ std::string curr_table_name = GetGlobalName(table->name);
+ Writef("if (strcmp(\"%s\", name) == 0)", curr_table_name.c_str());
+ Write(OpenBrace());
+ Write("return &(sbx->", curr_table_name, ");", Newline());
+ Write(CloseBrace(), Newline());
+ }
+ ++table_index;
+ }
+}
+
+void CWriter::WriteTable(const std::string& name) {
+ Write("wasm_rt_table_t ", name, ";");
+}
+
+void CWriter::WriteDataInitializers() {
+ const Memory* memory = nullptr;
+ Index data_segment_index = 0;
+
+ if (!module_->memories.empty()) {
+ if (module_->data_segments.empty()) {
+ Write(Newline());
+ } else {
+ for (const DataSegment* data_segment : module_->data_segments) {
+ Write(Newline(), "static const u8 data_segment_data_",
+ data_segment_index, "[] = ", OpenBrace());
+ size_t i = 0;
+ for (uint8_t x : data_segment->data) {
+ Writef("0x%02x, ", x);
+ if ((++i % 12) == 0)
+ Write(Newline());
+ }
+ if (i > 0)
+ Write(Newline());
+ Write(CloseBrace(), ";", Newline());
+ ++data_segment_index;
+ }
+ }
+
+ memory = module_->memories[0];
+ }
+
+ Write(Newline(), "static bool init_memory(wasm2c_sandbox_t* const sbx, uint32_t max_wasm_pages_from_rt) ", OpenBrace());
+ if (memory && module_->num_memory_imports == 0) {
+ Write("const uint32_t max_pages_specified_in_module = ", memory->page_limits.has_max ? memory->page_limits.max : 0, ";", Newline());
+ Write("const uint32_t max_pages = max_wasm_pages_from_rt == 0? max_pages_specified_in_module : max_wasm_pages_from_rt;", Newline());
+ Write("const bool success = wasm_rt_allocate_memory(&(sbx->", ExternalRef(memory->name), "), ",
+ memory->page_limits.initial, ", max_pages);", Newline());
+ Write("if (!success) { return false; }", Newline(), Newline());
+ }
+ data_segment_index = 0;
+ for (const DataSegment* data_segment : module_->data_segments) {
+ Write("LOAD_DATA(sbx->", ExternalRef(memory->name), ", ");
+ WriteInitExpr(data_segment->offset);
+ Write(", data_segment_data_", data_segment_index, ", ",
+ data_segment->data.size(), ");", Newline());
+ ++data_segment_index;
+ }
+
+ Write("sbx->wasi_data.heap_memory = &(sbx->", ExternalRef(memory->name), ");", Newline());
+ Write("return true;", Newline());
+ Write(CloseBrace(), Newline());
+
+ Write(Newline(), "static void cleanup_memory(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ Write("wasm_rt_deallocate_memory(&(sbx->", ExternalRef(memory->name), "));", Newline());
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteElemInitializers() {
+ const Table* table = module_->tables.empty() ? nullptr : module_->tables[0];
+
+ Write(Newline(), "static void init_table(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ Write("uint32_t offset = 0;", Newline());
+ if (table && module_->num_table_imports == 0) {
+ uint32_t max =
+ table->elem_limits.has_max ? table->elem_limits.max : UINT32_MAX;
+ Write("wasm_rt_allocate_table(&(sbx->", ExternalRef(table->name), "), ",
+ table->elem_limits.initial, ", ", max, ");", Newline());
+ }
+ Index elem_segment_index = 0;
+ size_t first_unused_elem = 0;
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ Write("offset = ");
+ WriteInitExpr(elem_segment->offset);
+ Write(";", Newline());
+
+ size_t i = 0;
+ for (const ElemExpr& elem_expr : elem_segment->elem_exprs) {
+ // We don't support the bulk-memory proposal here, so we know that we
+ // don't have any passive segments (where ref.null can be used).
+ assert(elem_expr.kind == ElemExprKind::RefFunc);
+ const Func* func = module_->GetFunc(elem_expr.var);
+ Index func_type_index = module_->GetFuncTypeIndex(func->decl.type_var);
+
+ Write("sbx->",ExternalRef(table->name), ".data[offset + ", i,
+ "] = (wasm_rt_elem_t){ WASM_RT_INTERNAL_FUNCTION, sbx->func_types[", func_type_index,
+ "], (wasm_rt_anyfunc_t)", ExternalPtr(func->name), " };", Newline());
+ if (i >= first_unused_elem) {
+ first_unused_elem = i+1;
+ }
+ ++i;
+ }
+ ++elem_segment_index;
+ }
+ Write("sbx->", ExternalRef(table->name), "_current_index = offset + ", std::to_string(first_unused_elem), ";", Newline());
+ Write(CloseBrace(), Newline());
+
+ Write(Newline(), "static void cleanup_table(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ if (table && module_->num_table_imports == 0) {
+ Write("wasm_rt_deallocate_table(&(sbx->", ExternalRef(table->name), "));", Newline());
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteExportLookup() {
+ Write(Newline(), "static void* lookup_wasm2c_nonfunc_export(void* sbx_ptr, const char* name) ", OpenBrace());
+ Write("wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;", Newline());
+
+ WriteMemoriesExport();
+ WriteTablesExport();
+ WriteGlobalsExport();
+
+ Write("return 0;", Newline());
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteCallbackAddRemove() {
+ const Table* table = module_->tables.empty() ? nullptr : module_->tables[0];
+
+ Write(Newline(), "static wasm_rt_table_t* get_wasm2c_callback_table(void* sbx_ptr)", OpenBrace());
+ Write("wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;", Newline());
+ Write("return &(sbx->", ExternalRef(table->name), ");", Newline());
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteInit() {
+ Write(Newline(), "static void init_module_starts(void) ", OpenBrace());
+ for (Var* var : module_->starts) {
+ Write(ExternalRef(module_->GetFunc(*var)->name), "();", Newline());
+ }
+ Write(CloseBrace(), Newline());
+
+ Write(s_source_sandboxapis);
+}
+
+void CWriter::WriteFuncs() {
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import)
+ Write(Newline(), *func, Newline());
+ ++func_index;
+ }
+}
+
+void CWriter::Write(const Func& func) {
+ func_ = &func;
+ // Copy symbols from global symbol table so we don't shadow them.
+ local_syms_ = global_syms_;
+ local_sym_map_.clear();
+ stack_var_sym_map_.clear();
+
+ std::string func_name_suffix;
+ auto out_func_name = GetGlobalName(func.name);
+
+ if (out_func_name == "w2c_dlmalloc" || out_func_name == "w2c_dlfree")
+ {
+ func_name_suffix = "_wrapped";
+ }
+
+ Write(GetFuncStaticOrExport(out_func_name), ResultType(func.decl.sig.result_types), " ",
+ out_func_name + func_name_suffix, "(");
+ WriteParamsAndLocals();
+ Write("FUNC_PROLOGUE;", Newline());
+
+ stream_ = &func_stream_;
+ stream_->ClearOffset();
+
+ std::string label = DefineLocalScopeName(kImplicitFuncLabel);
+ ResetTypeStack(0);
+ std::string empty; // Must not be temporary, since address is taken by Label.
+ PushLabel(LabelType::Func, empty, func.decl.sig);
+ Write(func.exprs, LabelDecl(label));
+ PopLabel();
+ ResetTypeStack(0);
+ PushTypes(func.decl.sig.result_types);
+ Write("FUNC_EPILOGUE;", Newline());
+
+ // Return the top of the stack implicitly.
+ Index num_results = func.GetNumResults();
+ if (num_results == 1) {
+ Write("return ", StackVar(0), ";", Newline());
+ } else if (num_results >= 2) {
+ Write(OpenBrace());
+ Write(ResultType(func.decl.sig.result_types), " tmp;", Newline());
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func.GetResultType(i);
+ Writef("tmp.%c%d = ", MangleType(type), i);
+ Write(StackVar(num_results - i - 1), ";", Newline());
+ }
+ Write("return tmp;", Newline());
+ Write(CloseBrace(), Newline());
+ }
+
+ stream_ = c_stream_;
+
+ WriteStackVarDeclarations();
+
+ std::unique_ptr<OutputBuffer> buf = func_stream_.ReleaseOutputBuffer();
+ stream_->WriteData(buf->data.data(), buf->data.size());
+
+ Write(CloseBrace());
+
+ std::string memory_name = GetMainMemoryName();
+ if (out_func_name == "w2c_dlmalloc") {
+ Write(Newline(), Newline());
+ Write(GetFuncStaticOrExport(out_func_name), "u32 w2c_dlmalloc(wasm2c_sandbox_t* const sbx, u32 ptr_size) ", OpenBrace());
+ Write("u32 ret = w2c_dlmalloc_wrapped(sbx, ptr_size);", Newline());
+ Write("WASM2C_SHADOW_MEMORY_DLMALLOC(&(sbx->", memory_name, "), ret, ptr_size);", Newline());
+ Write("WASM2C_MALLOC_FAIL_CHECK(ret, ptr_size);", Newline());
+ Write("return ret;", Newline());
+ Write(CloseBrace());
+ } else if (out_func_name == "w2c_dlfree") {
+ Write(Newline(), Newline());
+ Write(GetFuncStaticOrExport(out_func_name), "void w2c_dlfree(wasm2c_sandbox_t* const sbx, u32 ptr) ", OpenBrace());
+ Write("WASM2C_SHADOW_MEMORY_DLFREE(&(sbx->", memory_name, "), ptr);", Newline());
+ Write("w2c_dlfree_wrapped(sbx, ptr);", Newline());
+ Write(CloseBrace());
+ }
+
+ func_stream_.Clear();
+ func_ = nullptr;
+}
+
+void CWriter::WriteParamsAndLocals() {
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func_->GetNumParamsAndLocals(), func_->bindings,
+ &index_to_name);
+ WriteParams(index_to_name);
+ WriteLocals(index_to_name);
+}
+
+void CWriter::WriteParams(const std::vector<std::string>& index_to_name) {
+ Indent(4);
+ Write("wasm2c_sandbox_t* const sbx");
+ for (Index i = 0; i < func_->GetNumParams(); ++i) {
+ Write(", ");
+ if (i != 0) {
+ if ((i % 8) == 0)
+ Write(Newline());
+ }
+ Write(func_->GetParamType(i), " ",
+ DefineLocalScopeName(index_to_name[i]));
+ }
+ Dedent(4);
+ Write(") ", OpenBrace());
+}
+
+void CWriter::WriteLocals(const std::vector<std::string>& index_to_name) {
+ Index num_params = func_->GetNumParams();
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64}) {
+ Index local_index = 0;
+ size_t count = 0;
+ for (Type local_type : func_->local_types) {
+ if (local_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(DefineLocalScopeName(index_to_name[num_params + local_index]),
+ " = 0");
+ ++count;
+ }
+ ++local_index;
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::WriteStackVarDeclarations() {
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64}) {
+ size_t count = 0;
+ for (const auto& pair : stack_var_sym_map_) {
+ Type stp_type = pair.first.second;
+ const std::string& name = pair.second;
+
+ if (stp_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(name);
+ ++count;
+ }
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::Write(const ExprList& exprs) {
+ for (const Expr& expr : exprs) {
+ switch (expr.type()) {
+ case ExprType::Binary:
+ Write(*cast<BinaryExpr>(&expr));
+ break;
+
+ case ExprType::Block: {
+ const Block& block = cast<BlockExpr>(&expr)->block;
+ std::string label = DefineLocalScopeName(block.label);
+ DropTypes(block.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Block, block.label, block.decl.sig);
+ PushTypes(block.decl.sig.param_types);
+ Write(block.exprs, LabelDecl(label));
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.decl.sig.result_types);
+ break;
+ }
+
+ case ExprType::Br:
+ Write(GotoLabel(cast<BrExpr>(&expr)->var), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::BrIf:
+ Write("if (", StackVar(0), ") {");
+ DropTypes(1);
+ Write(GotoLabel(cast<BrIfExpr>(&expr)->var), "}", Newline());
+ break;
+
+ case ExprType::BrTable: {
+ const auto* bt_expr = cast<BrTableExpr>(&expr);
+ Write("switch (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ Index i = 0;
+ for (const Var& var : bt_expr->targets) {
+ Write("case ", i++, ": ", GotoLabel(var), Newline());
+ }
+ Write("default: ");
+ Write(GotoLabel(bt_expr->default_target), Newline(), CloseBrace(),
+ Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+ }
+
+ case ExprType::Call: {
+ const Var& var = cast<CallExpr>(&expr)->var;
+ const Func& func = *module_->GetFunc(var);
+ Index num_params = func.GetNumParams();
+ Index num_results = func.GetNumResults();
+ assert(type_stack_.size() >= num_params);
+ if (num_results > 1) {
+ Write(OpenBrace());
+ Write("struct ", MangleMultivalueTypes(func.decl.sig.result_types));
+ Write(" tmp = ");
+ } else if (num_results == 1) {
+ Write(StackVar(num_params - 1, func.GetResultType(0)), " = ");
+ }
+
+ Write(GlobalName(var.name()), "(sbx");
+ for (Index i = 0; i < num_params; ++i) {
+ Write(", ", StackVar(num_params - i - 1));
+ }
+ Write(");", Newline());
+ DropTypes(num_params);
+ if (num_results > 1) {
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func.GetResultType(i);
+ PushType(type);
+ Write(StackVar(0));
+ Writef(" = tmp.%c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), Newline());
+ } else {
+ PushTypes(func.decl.sig.result_types);
+ }
+ break;
+ }
+
+ case ExprType::CallIndirect: {
+ const FuncDeclaration& decl = cast<CallIndirectExpr>(&expr)->decl;
+ Index num_params = decl.GetNumParams();
+ Index num_results = decl.GetNumResults();
+ assert(type_stack_.size() > num_params);
+ if (num_results > 1) {
+ Write(OpenBrace());
+ Write("struct ", MangleMultivalueTypes(decl.sig.result_types)," tmp;");
+ }
+
+ assert(module_->tables.size() == 1);
+ const Table* table = module_->tables[0];
+
+ assert(decl.has_func_type);
+ Index func_type_index = module_->GetFuncTypeIndex(decl.type_var);
+
+ if (num_results > 0) {
+ assert(num_results == 1);
+ Write("CALL_INDIRECT_RES(");
+ if (num_results > 1) {
+ Write("tmp, ");
+ } else {
+ Write(StackVar(num_params, decl.GetResultType(0)), ", ");
+ }
+ } else {
+ Write("CALL_INDIRECT_VOID(");
+ }
+
+ Write("sbx->", ExternalRef(table->name), ", ");
+ WriteFuncDeclaration(decl, "(*)", false /* add_storage_class*/);
+ Write(", ", func_type_index, ", ", StackVar(0));
+ Write(", sbx->func_types, sbx");
+ for (Index i = 0; i < num_params; ++i) {
+ Write(", ", StackVar(num_params - i));
+ }
+ Write(");", Newline());
+ DropTypes(num_params + 1);
+ if (num_results > 1) {
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = decl.GetResultType(i);
+ PushType(type);
+ Write(StackVar(0));
+ Writef(" = tmp.%c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), Newline());
+ } else {
+ PushTypes(decl.sig.result_types);
+ }
+ break;
+ }
+
+ case ExprType::Compare:
+ Write(*cast<CompareExpr>(&expr));
+ break;
+
+ case ExprType::Const: {
+ const Const& const_ = cast<ConstExpr>(&expr)->const_;
+ PushType(const_.type());
+ Write(StackVar(0), " = ", const_, ";", Newline());
+ break;
+ }
+
+ case ExprType::Convert:
+ Write(*cast<ConvertExpr>(&expr));
+ break;
+
+ case ExprType::Drop:
+ DropTypes(1);
+ break;
+
+ case ExprType::GlobalGet: {
+ const Var& var = cast<GlobalGetExpr>(&expr)->var;
+ PushType(module_->GetGlobal(var)->type);
+ Write(StackVar(0), " = ", "sbx->", GlobalVar(var), ";", Newline());
+ break;
+ }
+
+ case ExprType::GlobalSet: {
+ const Var& var = cast<GlobalSetExpr>(&expr)->var;
+ Write("sbx->", GlobalVar(var), " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::If: {
+ const IfExpr& if_ = *cast<IfExpr>(&expr);
+ Write("if (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ std::string label = DefineLocalScopeName(if_.true_.label);
+ DropTypes(if_.true_.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::If, if_.true_.label, if_.true_.decl.sig);
+ PushTypes(if_.true_.decl.sig.param_types);
+ Write(if_.true_.exprs, CloseBrace());
+ if (!if_.false_.empty()) {
+ ResetTypeStack(mark);
+ PushTypes(if_.true_.decl.sig.param_types);
+ Write(" else ", OpenBrace(), if_.false_, CloseBrace());
+ }
+ ResetTypeStack(mark);
+ Write(Newline(), LabelDecl(label));
+ PopLabel();
+ PushTypes(if_.true_.decl.sig.result_types);
+ break;
+ }
+
+ case ExprType::Load:
+ Write(*cast<LoadExpr>(&expr));
+ break;
+
+ case ExprType::LocalGet: {
+ const Var& var = cast<LocalGetExpr>(&expr)->var;
+ PushType(func_->GetLocalType(var));
+ Write(StackVar(0), " = ", var, ";", Newline());
+ break;
+ }
+
+ case ExprType::LocalSet: {
+ const Var& var = cast<LocalSetExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::LocalTee: {
+ const Var& var = cast<LocalTeeExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ break;
+ }
+
+ case ExprType::Loop: {
+ const Block& block = cast<LoopExpr>(&expr)->block;
+ if (!block.exprs.empty()) {
+ Write(DefineLocalScopeName(block.label), ": ");
+ Indent();
+ DropTypes(block.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Loop, block.label, block.decl.sig);
+ PushTypes(block.decl.sig.param_types);
+ Write(Newline(), block.exprs);
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.decl.sig.result_types);
+ Dedent();
+ }
+ break;
+ }
+
+ case ExprType::MemoryCopy:
+ case ExprType::DataDrop:
+ case ExprType::MemoryInit:
+ case ExprType::MemoryFill:
+ case ExprType::TableCopy:
+ case ExprType::ElemDrop:
+ case ExprType::TableInit:
+ case ExprType::TableGet:
+ case ExprType::TableSet:
+ case ExprType::TableGrow:
+ case ExprType::TableSize:
+ case ExprType::TableFill:
+ case ExprType::RefFunc:
+ case ExprType::RefNull:
+ case ExprType::RefIsNull:
+ UNIMPLEMENTED("...");
+ break;
+
+ case ExprType::MemoryGrow: {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Write(StackVar(0), " = wasm_rt_grow_memory((&sbx->", ExternalRef(memory->name),
+ "), ", StackVar(0), ");", Newline());
+ break;
+ }
+
+ case ExprType::MemorySize: {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ PushType(Type::I32);
+ Write(StackVar(0), " = sbx->", ExternalRef(memory->name), ".pages;",
+ Newline());
+ break;
+ }
+
+ case ExprType::Nop:
+ break;
+
+ case ExprType::Return:
+ // Goto the function label instead; this way we can do shared function
+ // cleanup code in one place.
+ Write(GotoLabel(Var(label_stack_.size() - 1)), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::Select: {
+ Type type = StackType(1);
+ Write(StackVar(2), " = ", StackVar(0), " ? ", StackVar(2), " : ",
+ StackVar(1), ";", Newline());
+ DropTypes(3);
+ PushType(type);
+ break;
+ }
+
+ case ExprType::Store:
+ Write(*cast<StoreExpr>(&expr));
+ break;
+
+ case ExprType::Unary:
+ Write(*cast<UnaryExpr>(&expr));
+ break;
+
+ case ExprType::Ternary:
+ Write(*cast<TernaryExpr>(&expr));
+ break;
+
+ case ExprType::SimdLaneOp: {
+ Write(*cast<SimdLaneOpExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdLoadLane: {
+ Write(*cast<SimdLoadLaneExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdStoreLane: {
+ Write(*cast<SimdStoreLaneExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdShuffleOp: {
+ Write(*cast<SimdShuffleOpExpr>(&expr));
+ break;
+ }
+
+ case ExprType::LoadSplat:
+ Write(*cast<LoadSplatExpr>(&expr));
+ break;
+
+ case ExprType::LoadZero:
+ Write(*cast<LoadZeroExpr>(&expr));
+ break;
+
+ case ExprType::Unreachable:
+ Write("UNREACHABLE;", Newline());
+ return;
+
+ case ExprType::AtomicLoad:
+ case ExprType::AtomicRmw:
+ case ExprType::AtomicRmwCmpxchg:
+ case ExprType::AtomicStore:
+ case ExprType::AtomicWait:
+ case ExprType::AtomicFence:
+ case ExprType::AtomicNotify:
+ case ExprType::Rethrow:
+ case ExprType::ReturnCall:
+ case ExprType::ReturnCallIndirect:
+ case ExprType::Throw:
+ case ExprType::Try:
+ case ExprType::CallRef:
+ UNIMPLEMENTED("...");
+ break;
+ }
+ }
+}
+
+void CWriter::WriteSimpleUnaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", op, "(", StackVar(0), ");", Newline());
+ DropTypes(1);
+ PushType(opcode.GetResultType());
+}
+
+void CWriter::WriteInfixBinaryExpr(Opcode opcode,
+ const char* op,
+ AssignOp assign_op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type));
+ if (assign_op == AssignOp::Allowed) {
+ Write(" ", op, "= ", StackVar(0));
+ } else {
+ Write(" = ", StackVar(1), " ", op, " ", StackVar(0));
+ }
+ Write(";", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WritePrefixBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type), " = ", op, "(", StackVar(1), ", ",
+ StackVar(0), ");", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WriteSignedBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Type type = opcode.GetParamType1();
+ assert(opcode.GetParamType2() == type);
+ Write(StackVar(1, result_type), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " ", op, " (", SignedType(type), ")", StackVar(0), ");",
+ Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::Write(const BinaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Add:
+ case Opcode::I64Add:
+ case Opcode::F32Add:
+ case Opcode::F64Add:
+ WriteInfixBinaryExpr(expr.opcode, "+");
+ break;
+
+ case Opcode::I32Sub:
+ case Opcode::I64Sub:
+ case Opcode::F32Sub:
+ case Opcode::F64Sub:
+ WriteInfixBinaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::I32Mul:
+ case Opcode::I64Mul:
+ case Opcode::F32Mul:
+ case Opcode::F64Mul:
+ WriteInfixBinaryExpr(expr.opcode, "*");
+ break;
+
+ case Opcode::I32DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_DIV_S");
+ break;
+
+ case Opcode::I64DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_DIV_S");
+ break;
+
+ case Opcode::I32DivU:
+ case Opcode::I64DivU:
+ WritePrefixBinaryExpr(expr.opcode, "DIV_U");
+ break;
+
+ case Opcode::F32Div:
+ case Opcode::F64Div:
+ WriteInfixBinaryExpr(expr.opcode, "/");
+ break;
+
+ case Opcode::I32RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_REM_S");
+ break;
+
+ case Opcode::I64RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_REM_S");
+ break;
+
+ case Opcode::I32RemU:
+ case Opcode::I64RemU:
+ WritePrefixBinaryExpr(expr.opcode, "REM_U");
+ break;
+
+ case Opcode::I32And:
+ case Opcode::I64And:
+ WriteInfixBinaryExpr(expr.opcode, "&");
+ break;
+
+ case Opcode::I32Or:
+ case Opcode::I64Or:
+ WriteInfixBinaryExpr(expr.opcode, "|");
+ break;
+
+ case Opcode::I32Xor:
+ case Opcode::I64Xor:
+ WriteInfixBinaryExpr(expr.opcode, "^");
+ break;
+
+ case Opcode::I32Shl:
+ case Opcode::I64Shl:
+ Write(StackVar(1), " <<= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32ShrS:
+ case Opcode::I64ShrS: {
+ Type type = expr.opcode.GetResultType();
+ Write(StackVar(1), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " >> (", StackVar(0), " & ", GetShiftMask(type), "));",
+ Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case Opcode::I32ShrU:
+ case Opcode::I64ShrU:
+ Write(StackVar(1), " >>= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTL");
+ break;
+
+ case Opcode::I64Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTL");
+ break;
+
+ case Opcode::I32Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTR");
+ break;
+
+ case Opcode::I64Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTR");
+ break;
+
+ case Opcode::F32Min:
+ case Opcode::F64Min:
+ WritePrefixBinaryExpr(expr.opcode, "FMIN");
+ break;
+
+ case Opcode::F32Max:
+ case Opcode::F64Max:
+ WritePrefixBinaryExpr(expr.opcode, "FMAX");
+ break;
+
+ case Opcode::F32Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysignf");
+ break;
+
+ case Opcode::F64Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysign");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const CompareExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eq:
+ case Opcode::I64Eq:
+ case Opcode::F32Eq:
+ case Opcode::F64Eq:
+ WriteInfixBinaryExpr(expr.opcode, "==", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32Ne:
+ case Opcode::I64Ne:
+ case Opcode::F32Ne:
+ case Opcode::F64Ne:
+ WriteInfixBinaryExpr(expr.opcode, "!=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LtS:
+ case Opcode::I64LtS:
+ WriteSignedBinaryExpr(expr.opcode, "<");
+ break;
+
+ case Opcode::I32LtU:
+ case Opcode::I64LtU:
+ case Opcode::F32Lt:
+ case Opcode::F64Lt:
+ WriteInfixBinaryExpr(expr.opcode, "<", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LeS:
+ case Opcode::I64LeS:
+ WriteSignedBinaryExpr(expr.opcode, "<=");
+ break;
+
+ case Opcode::I32LeU:
+ case Opcode::I64LeU:
+ case Opcode::F32Le:
+ case Opcode::F64Le:
+ WriteInfixBinaryExpr(expr.opcode, "<=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GtS:
+ case Opcode::I64GtS:
+ WriteSignedBinaryExpr(expr.opcode, ">");
+ break;
+
+ case Opcode::I32GtU:
+ case Opcode::I64GtU:
+ case Opcode::F32Gt:
+ case Opcode::F64Gt:
+ WriteInfixBinaryExpr(expr.opcode, ">", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GeS:
+ case Opcode::I64GeS:
+ WriteSignedBinaryExpr(expr.opcode, ">=");
+ break;
+
+ case Opcode::I32GeU:
+ case Opcode::I64GeU:
+ case Opcode::F32Ge:
+ case Opcode::F64Ge:
+ WriteInfixBinaryExpr(expr.opcode, ">=", AssignOp::Disallowed);
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const ConvertExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eqz:
+ case Opcode::I64Eqz:
+ WriteSimpleUnaryExpr(expr.opcode, "!");
+ break;
+
+ case Opcode::I64ExtendI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)");
+ break;
+
+ case Opcode::I64ExtendI32U:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)");
+ break;
+
+ case Opcode::I32WrapI64:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)");
+ break;
+
+ case Opcode::I32TruncF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F32");
+ break;
+
+ case Opcode::I64TruncF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F32");
+ break;
+
+ case Opcode::I32TruncF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F64");
+ break;
+
+ case Opcode::I64TruncF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F64");
+ break;
+
+ case Opcode::I32TruncF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F32");
+ break;
+
+ case Opcode::I64TruncF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F32");
+ break;
+
+ case Opcode::I32TruncF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F64");
+ break;
+
+ case Opcode::I64TruncF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F64");
+ break;
+
+ case Opcode::I32TruncSatF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F32");
+ break;
+
+ case Opcode::I64TruncSatF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F32");
+ break;
+
+ case Opcode::I32TruncSatF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F64");
+ break;
+
+ case Opcode::I64TruncSatF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F64");
+ break;
+
+ case Opcode::I32TruncSatF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F32");
+ break;
+
+ case Opcode::I64TruncSatF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F32");
+ break;
+
+ case Opcode::I32TruncSatF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F64");
+ break;
+
+ case Opcode::I64TruncSatF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F64");
+ break;
+
+ case Opcode::F32ConvertI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s32)");
+ break;
+
+ case Opcode::F32ConvertI64S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s64)");
+ break;
+
+ case Opcode::F32ConvertI32U:
+ case Opcode::F32DemoteF64:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F32ConvertI64U:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_float).
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F64ConvertI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s32)");
+ break;
+
+ case Opcode::F64ConvertI64S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s64)");
+ break;
+
+ case Opcode::F64ConvertI32U:
+ case Opcode::F64PromoteF32:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F64ConvertI64U:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_double).
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F32ReinterpretI32:
+ WriteSimpleUnaryExpr(expr.opcode, "f32_reinterpret_i32");
+ break;
+
+ case Opcode::I32ReinterpretF32:
+ WriteSimpleUnaryExpr(expr.opcode, "i32_reinterpret_f32");
+ break;
+
+ case Opcode::F64ReinterpretI64:
+ WriteSimpleUnaryExpr(expr.opcode, "f64_reinterpret_i64");
+ break;
+
+ case Opcode::I64ReinterpretF64:
+ WriteSimpleUnaryExpr(expr.opcode, "i64_reinterpret_f64");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const LoadExpr& expr) {
+ const char* func = nullptr;
+ switch (expr.opcode) {
+ case Opcode::I32Load: func = "i32_load"; break;
+ case Opcode::I64Load: func = "i64_load"; break;
+ case Opcode::F32Load: func = "f32_load"; break;
+ case Opcode::F64Load: func = "f64_load"; break;
+ case Opcode::I32Load8S: func = "i32_load8_s"; break;
+ case Opcode::I64Load8S: func = "i64_load8_s"; break;
+ case Opcode::I32Load8U: func = "i32_load8_u"; break;
+ case Opcode::I64Load8U: func = "i64_load8_u"; break;
+ case Opcode::I32Load16S: func = "i32_load16_s"; break;
+ case Opcode::I64Load16S: func = "i64_load16_s"; break;
+ case Opcode::I32Load16U: func = "i32_load16_u"; break;
+ case Opcode::I64Load16U: func = "i64_load16_u"; break;
+ case Opcode::I64Load32S: func = "i64_load32_s"; break;
+ case Opcode::I64Load32U: func = "i64_load32_u"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", func, "(&(sbx->", ExternalRef(memory->name),
+ "), (u64)(", StackVar(0), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset, "u");
+ Write(", \"", GetGlobalName(func_->name), "\"");
+ Write(");", Newline());
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::Write(const StoreExpr& expr) {
+ const char* func = nullptr;
+ switch (expr.opcode) {
+ case Opcode::I32Store: func = "i32_store"; break;
+ case Opcode::I64Store: func = "i64_store"; break;
+ case Opcode::F32Store: func = "f32_store"; break;
+ case Opcode::F64Store: func = "f64_store"; break;
+ case Opcode::I32Store8: func = "i32_store8"; break;
+ case Opcode::I64Store8: func = "i64_store8"; break;
+ case Opcode::I32Store16: func = "i32_store16"; break;
+ case Opcode::I64Store16: func = "i64_store16"; break;
+ case Opcode::I64Store32: func = "i64_store32"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Write(func, "(&(sbx->", ExternalRef(memory->name), "), (u64)(", StackVar(1), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write(", ", StackVar(0));
+ Write(", \"", GetGlobalName(func_->name), "\"");
+ Write(");", Newline());
+ DropTypes(2);
+}
+
+void CWriter::Write(const UnaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CLZ");
+ break;
+
+ case Opcode::I64Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CLZ");
+ break;
+
+ case Opcode::I32Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CTZ");
+ break;
+
+ case Opcode::I64Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CTZ");
+ break;
+
+ case Opcode::I32Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_POPCNT");
+ break;
+
+ case Opcode::I64Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_POPCNT");
+ break;
+
+ case Opcode::F32Neg:
+ case Opcode::F64Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::F32Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "fabsf");
+ break;
+
+ case Opcode::F64Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "fabs");
+ break;
+
+ case Opcode::F32Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "sqrtf");
+ break;
+
+ case Opcode::F64Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "sqrt");
+ break;
+
+ case Opcode::F32Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "ceilf");
+ break;
+
+ case Opcode::F64Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "ceil");
+ break;
+
+ case Opcode::F32Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "floorf");
+ break;
+
+ case Opcode::F64Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "floor");
+ break;
+
+ case Opcode::F32Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "truncf");
+ break;
+
+ case Opcode::F64Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "trunc");
+ break;
+
+ case Opcode::F32Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "nearbyintf");
+ break;
+
+ case Opcode::F64Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "nearbyint");
+ break;
+
+ case Opcode::I32Extend8S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s8)(u8)");
+ break;
+
+ case Opcode::I32Extend16S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s16)(u16)");
+ break;
+
+ case Opcode::I64Extend8S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s8)(u8)");
+ break;
+
+ case Opcode::I64Extend16S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s16)(u16)");
+ break;
+
+ case Opcode::I64Extend32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)(u32)");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const TernaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::V128BitSelect: {
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(2, result_type), " = ", "v128.bitselect", "(", StackVar(0),
+ ", ", StackVar(1), ", ", StackVar(2), ");", Newline());
+ DropTypes(3);
+ PushType(result_type);
+ break;
+ }
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const SimdLaneOpExpr& expr) {
+ Type result_type = expr.opcode.GetResultType();
+
+ switch (expr.opcode) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F64X2ExtractLane: {
+ Write(StackVar(0, result_type), " = ", expr.opcode.GetName(), "(",
+ StackVar(0), ", lane Imm: ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::F64X2ReplaceLane: {
+ Write(StackVar(1, result_type), " = ", expr.opcode.GetName(), "(",
+ StackVar(0), ", ", StackVar(1), ", lane Imm: ", expr.val, ");",
+ Newline());
+ DropTypes(2);
+ break;
+ }
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ PushType(result_type);
+}
+
+void CWriter::Write(const SimdLoadLaneExpr& expr) {
+ UNIMPLEMENTED("SIMD support");
+}
+
+void CWriter::Write(const SimdStoreLaneExpr& expr) {
+ UNIMPLEMENTED("SIMD support");
+}
+
+void CWriter::Write(const SimdShuffleOpExpr& expr) {
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(1, result_type), " = ", expr.opcode.GetName(), "(",
+ StackVar(1), " ", StackVar(0), ", lane Imm: $0x%08x %08x %08x %08x",
+ expr.val.u32(0), expr.val.u32(1), expr.val.u32(2), expr.val.u32(3), ")",
+ Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::Write(const LoadSplatExpr& expr) {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", expr.opcode.GetName(), "(",
+ ExternalPtr(memory->name), ", (u64)(", StackVar(0));
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write("));", Newline());
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::Write(const LoadZeroExpr& expr) {
+ UNIMPLEMENTED("SIMD support");
+}
+
+void CWriter::WriteCHeader() {
+ stream_ = h_stream_;
+ std::string guard = GenerateHeaderGuard();
+ Write("#ifndef ", guard, Newline());
+ Write("#define ", guard, Newline(), Newline());
+ Write("#define WASM_CURR_MODULE_PREFIX ", options_.mod_name, Newline());
+ Write(s_header_top);
+ WriteMultivalueTypes();
+ WriteImports();
+ WriteFuncDeclarations(true /* for_header */);
+ Write(s_header_bottom, Newline());
+ Write("#endif /* ", guard, " */", Newline());
+}
+
+void CWriter::WriteCSource() {
+ stream_ = c_stream_;
+ WriteSourceTop();
+ WriteSandboxStruct();
+ WriteFuncTypes();
+ WriteFuncDeclarations(false /* for_header */);
+ WriteEntryFuncs();
+ WriteGlobalInitializers();
+ WriteFuncs();
+ WriteDataInitializers();
+ WriteElemInitializers();
+ WriteExportLookup();
+ WriteCallbackAddRemove();
+ WriteInit();
+}
+
+Result CWriter::WriteModule(const Module& module) {
+ WABT_USE(options_);
+ module_ = &module;
+ WriteCHeader();
+ WriteCSource();
+ return result_;
+}
+
+} // end anonymous namespace
+
+Result WriteC(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const Module* module,
+ const WriteCOptions& options) {
+ CWriter c_writer(c_stream, h_stream, header_name, options);
+ return c_writer.WriteModule(*module);
+}
+
+} // namespace wabt