blob: 9a3e8b88d6b27ed9269e9a5c689ab6c29965196d [file] [log] [blame] [edit]
/*
* 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 std::string Deref(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);
static std::string ExportName(string_view mangled_name);
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 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;
enum class WriteExportsKind {
Declarations,
Definitions,
Initializers,
};
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 InitExpr&);
std::string GenerateHeaderGuard() const;
void WriteSourceTop();
void WriteMultivalueTypes();
void WriteFuncTypes();
void WriteImports();
void WriteFuncDeclarations();
void WriteFuncDeclaration(const FuncDeclaration&, const std::string&);
void WriteGlobals();
void WriteGlobal(const Global&, const std::string&);
void WriteMemories();
void WriteMemory(const std::string&);
void WriteTables();
void WriteTable(const std::string&);
void WriteDataInitializers();
void WriteElemInitializers();
void WriteInitExports();
void WriteExports(WriteExportsKind);
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_;
StackVarSymbolMap stack_var_sym_map_;
SymbolSet global_syms_;
SymbolSet local_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
std::string CWriter::Deref(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::ExportName(string_view mangled_name) {
return "WASM_RT_ADD_PREFIX(" + mangled_name.to_string() + ")";
}
// 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::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;
}
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) {
bool is_import = import_syms_.count(name.name) != 0;
if (is_import) {
Write(Deref(GetGlobalName(name.name)));
} else {
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(ExternalRef(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 InitExpr& init_expr) {
const ExprList& expr_list = init_expr.exprs;
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::WriteFuncTypes() {
Write(Newline());
Writef("static u32 func_types[%" PRIzd "];", module_->types.size());
Write(Newline(), Newline());
Write("static void init_func_types(void) {", Newline());
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(" func_types[", func_type_index, "] = wasm_rt_register_func_type(",
num_params, ", ", num_results);
for (Index i = 0; i < num_params; ++i) {
Write(", ", TypeEnum(func_type->GetParamType(i)));
}
for (Index i = 0; i < num_results; ++i) {
Write(", ", TypeEnum(func_type->GetResultType(i)));
}
Write(");", Newline());
++func_type_index;
}
Write("}", 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;
WriteFuncDeclaration(
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() {
if (module_->funcs.size() == module_->num_func_imports)
return;
Write(Newline());
Index func_index = 0;
for (const Func* func : module_->funcs) {
bool is_import = func_index < module_->num_func_imports;
if (!is_import) {
Write("static ");
WriteFuncDeclaration(func->decl, DefineGlobalScopeName(func->name));
Write(";", Newline());
}
++func_index;
}
}
void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl,
const std::string& name) {
Write(ResultType(decl.sig.result_types), " ", name, "(");
if (decl.GetNumParams() == 0) {
Write("void");
} else {
for (Index i = 0; i < decl.GetNumParams(); ++i) {
if (i != 0)
Write(", ");
Write(decl.GetParamType(i));
}
}
Write(")");
}
void CWriter::WriteGlobals() {
Index global_index = 0;
if (module_->globals.size() != module_->num_global_imports) {
Write(Newline());
for (const Global* global : module_->globals) {
bool is_import = global_index < module_->num_global_imports;
if (!is_import) {
Write("static ");
WriteGlobal(*global, DefineGlobalScopeName(global->name));
Write(";", Newline());
}
++global_index;
}
}
Write(Newline(), "static void init_globals(void) ", OpenBrace());
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.exprs.empty());
Write(GlobalName(global->name), " = ");
WriteInitExpr(global->init_expr);
Write(";", 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;
Write(Newline());
Index memory_index = 0;
for (const Memory* memory : module_->memories) {
bool is_import = memory_index < module_->num_memory_imports;
if (!is_import) {
Write("static ");
WriteMemory(DefineGlobalScopeName(memory->name));
Write(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;
Write(Newline());
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) {
Write("static ");
WriteTable(DefineGlobalScopeName(table->name));
Write(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 void init_memory(void) ", OpenBrace());
if (module_->memories.size() > module_->num_memory_imports) {
Index memory_idx = module_->num_memory_imports;
for (Index i = memory_idx; i < module_->memories.size(); i++) {
memory = module_->memories[i];
uint32_t max =
memory->page_limits.has_max ? memory->page_limits.max : 65536;
Write("wasm_rt_allocate_memory(", ExternalPtr(memory->name), ", ",
memory->page_limits.initial, ", ", max, ");", Newline());
}
}
data_segment_index = 0;
for (const DataSegment* data_segment : module_->data_segments) {
Write("LOAD_DATA(", ExternalRef(memory->name), ", ");
WriteInitExpr(data_segment->offset);
Write(", data_segment_data_", data_segment_index, ", ",
data_segment->data.size(), ");", Newline());
++data_segment_index;
}
Write(CloseBrace(), Newline());
}
void CWriter::WriteElemInitializers() {
const Table* table = module_->tables.empty() ? nullptr : module_->tables[0];
Write(Newline(), "static void init_table(void) ", OpenBrace());
Write("uint32_t offset;", 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(", ExternalPtr(table->name), ", ",
table->elem_limits.initial, ", ", max, ");", Newline());
}
Index elem_segment_index = 0;
for (const ElemSegment* elem_segment : module_->elem_segments) {
if (elem_segment->kind == SegmentKind::Passive) {
continue;
}
Write("offset = ");
WriteInitExpr(elem_segment->offset);
Write(";", Newline());
size_t i = 0;
for (const InitExpr& 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.exprs.size() == 1);
const Expr* expr = &elem_expr.exprs.front();
assert(expr->type() == ExprType::RefFunc);
const Func* func = module_->GetFunc(cast<RefFuncExpr>(expr)->var);
Index func_type_index = module_->GetFuncTypeIndex(func->decl.type_var);
Write(ExternalRef(table->name), ".data[offset + ", i,
"] = (wasm_rt_elem_t){func_types[", func_type_index,
"], (wasm_rt_anyfunc_t)", ExternalPtr(func->name), "};", Newline());
++i;
}
++elem_segment_index;
}
Write(CloseBrace(), Newline());
}
void CWriter::WriteInitExports() {
Write(Newline(), "static void init_exports(void) ", OpenBrace());
WriteExports(WriteExportsKind::Initializers);
Write(CloseBrace(), Newline());
}
void CWriter::WriteExports(WriteExportsKind kind) {
if (module_->exports.empty())
return;
if (kind != WriteExportsKind::Initializers) {
Write(Newline());
}
for (const Export* export_ : module_->exports) {
Write("/* export: '", export_->name, "' */", Newline());
if (kind == WriteExportsKind::Declarations) {
Write("extern ");
}
std::string mangled_name;
std::string internal_name;
switch (export_->kind) {
case ExternalKind::Func: {
const Func* func = module_->GetFunc(export_->var);
mangled_name =
ExportName(MangleFuncName(export_->name, func->decl.sig.param_types,
func->decl.sig.result_types));
internal_name = func->name;
if (kind != WriteExportsKind::Initializers) {
WriteFuncDeclaration(func->decl, Deref(mangled_name));
Write(";");
}
break;
}
case ExternalKind::Global: {
const Global* global = module_->GetGlobal(export_->var);
mangled_name =
ExportName(MangleGlobalName(export_->name, global->type));
internal_name = global->name;
if (kind != WriteExportsKind::Initializers) {
WriteGlobal(*global, Deref(mangled_name));
Write(";");
}
break;
}
case ExternalKind::Memory: {
const Memory* memory = module_->GetMemory(export_->var);
mangled_name = ExportName(MangleName(export_->name));
internal_name = memory->name;
if (kind != WriteExportsKind::Initializers) {
WriteMemory(Deref(mangled_name));
}
break;
}
case ExternalKind::Table: {
const Table* table = module_->GetTable(export_->var);
mangled_name = ExportName(MangleName(export_->name));
internal_name = table->name;
if (kind != WriteExportsKind::Initializers) {
WriteTable(Deref(mangled_name));
}
break;
}
default:
WABT_UNREACHABLE;
}
if (kind == WriteExportsKind::Initializers) {
Write(mangled_name, " = ", ExternalPtr(internal_name), ";");
}
Write(Newline());
}
}
void CWriter::WriteInit() {
Write(Newline(), "void WASM_RT_ADD_PREFIX(init)(void) ", OpenBrace());
Write("init_func_types();", Newline());
Write("init_globals();", Newline());
Write("init_memory();", Newline());
Write("init_table();", Newline());
Write("init_exports();", Newline());
for (Var* var : module_->starts) {
Write(ExternalRef(module_->GetFunc(*var)->name), "();", Newline());
}
Write(CloseBrace(), Newline());
}
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();
Write("static ", ResultType(func.decl.sig.result_types), " ",
GlobalName(func.name), "(");
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());
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) {
if (func_->GetNumParams() == 0) {
Write("void");
} else {
Indent(4);
for (Index i = 0; i < func_->GetNumParams(); ++i) {
if (i != 0) {
Write(", ");
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(GlobalVar(var), "(");
for (Index i = 0; i < num_params; ++i) {
if (i != 0) {
Write(", ");
}
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));
Write(" tmp = ");
} else if (num_results == 1) {
Write(StackVar(num_params, decl.GetResultType(0)), " = ");
}
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);
Write("CALL_INDIRECT(", ExternalRef(table->name), ", ");
WriteFuncDeclaration(decl, "(*)");
Write(", ", func_type_index, ", ", StackVar(0));
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), " = ", GlobalVar(var), ";", Newline());
break;
}
case ExprType::GlobalSet: {
const Var& var = cast<GlobalSetExpr>(&expr)->var;
Write(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: {
Memory* memory = module_->memories[module_->GetMemoryIndex(
cast<MemoryGrowExpr>(&expr)->memidx)];
Write(StackVar(0), " = wasm_rt_grow_memory(", ExternalPtr(memory->name),
", ", StackVar(0), ");", Newline());
break;
}
case ExprType::MemorySize: {
Memory* memory = module_->memories[module_->GetMemoryIndex(
cast<MemorySizeExpr>(&expr)->memidx)];
PushType(Type::I32);
Write(StackVar(0), " = ", 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;
}
Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
Type result_type = expr.opcode.GetResultType();
Write(StackVar(0, result_type), " = ", func, "(", ExternalPtr(memory->name),
", (u64)(", StackVar(0), ")");
if (expr.offset != 0)
Write(" + ", expr.offset, "u");
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;
}
Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
Write(func, "(", ExternalPtr(memory->name), ", (u64)(", StackVar(1), ")");
if (expr.offset != 0)
Write(" + ", expr.offset);
Write(", ", StackVar(0), ");", 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());
Write(s_header_top);
WriteMultivalueTypes();
WriteImports();
WriteExports(WriteExportsKind::Declarations);
Write(s_header_bottom);
Write(Newline(), "#endif /* ", guard, " */", Newline());
}
void CWriter::WriteCSource() {
stream_ = c_stream_;
WriteSourceTop();
WriteFuncTypes();
WriteFuncDeclarations();
WriteGlobals();
WriteMemories();
WriteTables();
WriteFuncs();
WriteDataInitializers();
WriteElemInitializers();
WriteExports(WriteExportsKind::Definitions);
WriteInitExports();
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