diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/wasm2c/src/c-writer.cc | |
parent | Initial commit. (diff) | |
download | firefox-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.cc | 2595 |
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 |