/*
* Copyright 2017 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wabt/c-writer.h"
#include <cctype>
#include <cinttypes>
#include <iterator>
#include <limits>
#include <map>
#include <set>
#include <string_view>
#include <vector>
#include "wabt/cast.h"
#include "wabt/common.h"
#include "wabt/ir.h"
#include "wabt/literal.h"
#include "wabt/sha256.h"
#include "wabt/stream.h"
#include "wabt/string-util.h"
#define INDENT_SIZE 2
#define UNIMPLEMENTED(x) printf(
"unimplemented: %s\n", (x)), abort()
// code to be inserted into the generated output
extern const char* s_header_top;
extern const char* s_header_bottom;
extern const char* s_source_includes;
extern const char* s_source_declarations;
extern const char* s_simd_source_declarations;
extern const char* s_atomicops_source_declarations;
namespace wabt {
namespace {
struct Label {
Label(LabelType label_type,
const std::string& name,
const TypeVector& sig,
size_t type_stack_size,
size_t try_catch_stack_size,
bool used =
false)
: label_type(label_type),
name(name),
sig(sig),
type_stack_size(type_stack_size),
try_catch_stack_size(try_catch_stack_size),
used(used) {}
bool HasValue()
const {
return !sig.empty(); }
LabelType label_type;
const std::string& name;
const TypeVector& sig;
size_t type_stack_size;
size_t try_catch_stack_size;
bool used =
false;
};
struct LocalName {
explicit LocalName(
const std::string& name) : name(name) {}
const std::string& name;
};
struct ParamName : LocalName {
using LocalName::LocalName;
ParamName(
const Var& var) : LocalName(var.name()) {}
};
struct LabelName : LocalName {
using LocalName::LocalName;
};
struct GlobalName {
GlobalName(ModuleFieldType type,
const std::string& name)
: type(type), name(name) {}
ModuleFieldType type;
const std::string& name;
};
struct TagSymbol : GlobalName {
explicit TagSymbol(
const std::string& name)
: GlobalName(ModuleFieldType::Tag, name) {}
};
struct ExternalRef : GlobalName {
using GlobalName::GlobalName;
};
struct WrapperRef : GlobalName {
explicit WrapperRef(
const std::string& name)
: GlobalName(ModuleFieldType::Func, name) {}
};
struct TailCallRef : GlobalName {
explicit TailCallRef(
const std::string& name)
: GlobalName(ModuleFieldType::Func, name) {}
};
struct ExternalInstancePtr : GlobalName {
using GlobalName::GlobalName;
};
struct ExternalInstanceRef : GlobalName {
using GlobalName::GlobalName;
};
struct GotoLabel {
explicit GotoLabel(
const Var& var) : var(var) {}
const Var& var;
};
struct LabelDecl {
explicit LabelDecl(
const std::string& name) : name(name) {}
std::string name;
};
struct GlobalInstanceVar {
explicit GlobalInstanceVar(
const Var& var) : var(var) {}
const Var& var;
};
struct StackVar {
explicit StackVar(Index index, Type type = Type::Any)
: index(index), type(type) {}
Index index;
Type type;
};
struct TypeEnum {
explicit TypeEnum(Type type) : type(type) {}
Type type;
};
struct SignedType {
explicit SignedType(Type type) : type(type) {}
Type type;
};
struct TryCatchLabel {
TryCatchLabel(
const std::string& name, size_t try_catch_stack_size)
: name(name), try_catch_stack_size(try_catch_stack_size), used(
false) {}
std::string name;
size_t try_catch_stack_size;
bool used;
};
struct FuncTypeExpr {
const FuncType* func_type;
FuncTypeExpr(
const FuncType* f) : func_type(f) {}
};
struct Newline {};
struct OpenBrace {};
struct CloseBrace {};
int GetShiftMask(Type type) {
// clang-format off
switch (type) {
case Type::I32:
return 31;
case Type::I64:
return 63;
default: WABT_UNREACHABLE;
return 0;
}
// clang-format on
}
/*
* This function is the default behavior for name_to_output_file_index_. For
* single .c output, this function returns a vector filled with 0. For multiple
* .c outputs, this function sorts all non-imported functions in the module by
* their names, and then divides all non-imported functions into equal-sized
* buckets (# of non-imported functions / # of .c outputs) based on the sorting.
*/
static std::vector<size_t> default_name_to_output_file_index(
std::vector<Func*>::const_iterator func_begin,
std::vector<Func*>::const_iterator func_end,
size_t num_imports,
size_t num_streams) {
std::vector<size_t> result;
result.resize(std::distance(func_begin, func_end));
if (num_streams == 1) {
return result;
}
std::map<std::string, Index> sorted_functions;
size_t non_imported_funcs = result.size() - num_imports;
size_t bucket_size = non_imported_funcs / num_streams +
(non_imported_funcs % num_streams ? 1 : 0);
Index func_index = 0;
for (
auto func = func_begin; func != func_end; func++) {
sorted_functions.insert({(*func)->name, func_index});
++func_index;
}
Index sorted_func_index = 0;
for (
const auto& [func_name, index] : sorted_functions) {
bool is_import = index < num_imports;
if (!is_import) {
result.at(index) = sorted_func_index / bucket_size;
++sorted_func_index;
}
}
return result;
}
class CWriter {
public:
CWriter(std::vector<Stream*>&& c_streams,
Stream* h_stream,
Stream* h_impl_stream,
const char* header_name,
const char* header_impl_name,
const WriteCOptions& options)
: options_(options),
c_streams_(std::move(c_streams)),
h_stream_(h_stream),
h_impl_stream_(h_impl_stream),
header_name_(header_name),
header_impl_name_(header_impl_name) {
module_prefix_ = MangleModuleName(options_.module_name);
if (c_streams_.size() != 1 && options.name_to_output_file_index) {
name_to_output_file_index_ = options.name_to_output_file_index;
}
else {
name_to_output_file_index_ = default_name_to_output_file_index;
}
}
Result WriteModule(
const Module&);
private:
using SymbolSet = std::set<std::string>;
using SymbolMap = std::map<std::string, std::string>;
using StackTypePair = std::pair<Index, Type>;
using StackVarSymbolMap = std::map<StackTypePair, std::string>;
void WriteCHeader();
void WriteCSource();
size_t MarkTypeStack()
const;
void ResetTypeStack(size_t mark);
Type StackType(Index)
const;
void PushType(Type);
void PushTypes(
const TypeVector&);
void DropTypes(size_t count);
void PushLabel(LabelType,
const std::string& name,
const FuncSignature&,
bool used =
false);
const Label* FindLabel(
const Var& var,
bool mark_used =
true);
bool IsTopLabelUsed()
const;
void PopLabel();
static constexpr
char MangleType(Type);
static constexpr
char MangleField(ModuleFieldType);
static std::string MangleTypes(
const TypeVector&);
static std::string Mangle(std::string_view name,
bool double_underscores);
static std::string MangleName(std::string_view);
static std::string MangleModuleName(std::string_view);
static std::string ExportName(std::string_view module_name,
std::string_view export_name);
std::string ExportName(std::string_view export_name)
const;
static std::string TailCallExportName(std::string_view module_name,
std::string_view export_name);
std::string TailCallExportName(std::string_view export_name)
const;
std::string ModuleInstanceTypeName()
const;
static std::string ModuleInstanceTypeName(std::string_view module_name);
void ClaimName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& c_name);
std::string FindUniqueName(SymbolSet& set,
std::string_view proposed_name)
const;
std::string ClaimUniqueName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& proposed_c_name);
void DefineImportName(
const Import* import,
std::string_view module_name,
std::string_view field_name);
void ReserveExportNames();
void ReserveExportName(std::string_view);
std::string DefineImportedModuleInstanceName(std::string_view name);
std::string DefineInstanceMemberName(ModuleFieldType, std::string_view);
std::string DefineGlobalScopeName(ModuleFieldType, std::string_view);
std::string DefineLocalScopeName(std::string_view name,
bool is_label);
std::string DefineParamName(std::string_view);
std::string DefineLabelName(std::string_view);
std::string DefineStackVarName(Index, Type, std::string_view);
static void SerializeFuncType(
const FuncType&, std::string&);
std::string GetGlobalName(ModuleFieldType,
const std::string&)
const;
std::string GetLocalName(
const std::string&,
bool is_label)
const;
std::string GetTailCallRef(
const std::string&)
const;
void Indent(
int size = INDENT_SIZE);
void Dedent(
int size = INDENT_SIZE);
void NonIndented(std::function<
void()> func);
void WriteIndent();
void WriteData(
const char* src, size_t size);
void Writef(
const char* format, ...);
template <
typename T,
typename U,
typename... Args>
void Write(T&& t, U&& u, Args&&... args) {
Write(std::forward<T>(t));
Write(std::forward<U>(u));
Write(std::forward<Args>(args)...);
}
static const char* GetReferenceTypeName(
const Type& type);
static const char* GetReferenceNullValue(
const Type& type);
static const char* GetCTypeName(
const Type& type);
const char* InternalSymbolScope()
const;
enum class CWriterPhase {
Declarations,
Definitions,
};
void Write() {}
void Write(Newline);
void Write(OpenBrace);
void Write(CloseBrace);
void Write(uint64_t);
void Write(std::string_view);
void Write(
const ParamName&);
void Write(
const LabelName&);
void Write(
const GlobalName&);
void Write(
const TagSymbol&);
void Write(
const ExternalRef&);
void Write(
const WrapperRef&);
void Write(
const TailCallRef&);
void Write(
const ExternalInstancePtr&);
void Write(
const ExternalInstanceRef&);
void Write(Type);
void Write(SignedType);
void Write(TypeEnum);
void Write(
const GotoLabel&);
void Write(
const LabelDecl&);
void Write(
const GlobalInstanceVar&);
void Write(
const StackVar&);
void Write(
const TypeVector&);
void Write(
const Const&);
void WriteInitExpr(
const ExprList&);
void WriteInitExprTerminal(
const Expr*);
std::string GenerateHeaderGuard()
const;
void WriteSourceTop();
void WriteMultiCTop();
void WriteMultiCTopEmpty();
void DeclareStruct(
const TypeVector&);
void WriteTailcalleeParamTypes();
void WriteMultivalueResultTypes();
void WriteTagTypes();
void WriteFuncTypeDecls();
void WriteFuncTypes();
void Write(
const FuncTypeExpr&);
void WriteTagDecls();
void WriteTags();
void ComputeUniqueImports();
void BeginInstance();
void WriteImports();
void WriteTailCallWeakImports();
void WriteFuncDeclarations();
void WriteFuncDeclaration(
const FuncDeclaration&,
const std::string&);
void WriteTailCallFuncDeclaration(
const std::string&);
void WriteImportFuncDeclaration(
const FuncDeclaration&,
const std::string& module_name,
const std::string&);
void WriteFuncRefWrapper(
const Func* func);
void WriteCallIndirectFuncDeclaration(
const FuncDeclaration&,
const std::string&);
void ComputeSimdScope();
void WriteHeaderIncludes();
void WriteV128Decl();
void WriteModuleInstance();
void WriteGlobals();
void WriteGlobal(
const Global&,
const std::string&);
void WriteGlobalPtr(
const Global&,
const std::string&);
void WriteMemories();
void WriteMemory(
const std::string&,
const Memory& memory);
void WriteMemoryPtr(
const std::string&,
const Memory& memory);
void WriteTables();
void WriteTable(
const std::string&,
const wabt::Type&);
void WriteTablePtr(
const std::string&,
const Table&);
void WriteTableType(
const wabt::Type&);
void WriteDataInstances();
void WriteElemInstances();
void WriteGlobalInitializers();
void WriteDataInitializerDecls();
void WriteDataInitializers();
void WriteElemInitializerDecls();
void WriteElemInitializers();
void WriteFuncRefWrappers();
void WriteElemTableInit(
bool,
const ElemSegment*,
const Table*);
bool IsSingleUnsharedMemory();
void InstallSegueBase(Memory* memory,
bool save_old_value);
void RestoreSegueBase();
void WriteExports(CWriterPhase);
void WriteTailCallExports(CWriterPhase);
void WriteInitDecl();
void WriteFreeDecl();
void WriteGetFuncTypeDecl();
void WriteInit();
void WriteFree();
void WriteGetFuncType();
void WriteInitInstanceImport();
void WriteImportProperties(CWriterPhase);
void WriteFuncs();
void BeginFunction(
const Func&);
void FinishFunction(size_t);
void Write(
const Func&);
void WriteTailCallee(
const Func&);
void WriteParamsAndLocals();
void WriteParams(
const std::vector<std::string>& index_to_name,
bool setjmp_safe =
false);
void WriteParamSymbols(
const std::vector<std::string>& index_to_name);
void WriteParamTypes(
const FuncDeclaration& decl);
void WriteLocals(
const std::vector<std::string>& index_to_name);
void WriteStackVarDeclarations();
void Write(
const ExprList&);
void WriteTailCallAsserts(
const FuncSignature&);
void WriteTailCallStack();
void WriteUnwindTryCatchStack(
const Label*);
void FinishReturnCall();
void Spill(
const TypeVector&);
void Unspill(
const TypeVector&);
template <
typename Vars,
typename TypeOf,
typename ToDo>
void WriteVarsByType(
const Vars&,
const TypeOf&,
const ToDo&,
bool);
template <
typename sources>
void Spill(
const TypeVector&,
const sources& src);
template <
typename targets>
void Unspill(
const TypeVector&,
const targets& tgt);
enum class AssignOp {
Disallowed,
Allowed,
};
void WriteSimpleUnaryExpr(Opcode,
const char* op);
void WriteInfixBinaryExpr(Opcode,
const char* op,
AssignOp = AssignOp::Allowed);
void WritePrefixBinaryExpr(Opcode,
const char* op);
void WriteSignedBinaryExpr(Opcode,
const char* op);
void Write(
const BinaryExpr&);
void Write(
const CompareExpr&);
void Write(
const ConvertExpr&);
void Write(
const LoadExpr&);
void Write(
const StoreExpr&);
void Write(
const UnaryExpr&);
void Write(
const TernaryExpr&);
void Write(
const SimdLaneOpExpr&);
void Write(
const SimdLoadLaneExpr&);
void Write(
const SimdStoreLaneExpr&);
void Write(
const SimdShuffleOpExpr&);
void Write(
const LoadSplatExpr&);
void Write(
const LoadZeroExpr&);
void Write(
const Block&);
void Write(
const AtomicLoadExpr& expr);
void Write(
const AtomicStoreExpr& expr);
void Write(
const AtomicRmwExpr& expr);
void Write(
const AtomicRmwCmpxchgExpr& expr);
size_t BeginTry(
const TryExpr& tryexpr);
void WriteTryCatch(
const TryExpr& tryexpr);
void WriteTryDelegate(
const TryExpr& tryexpr);
void Write(
const Catch& c);
void WriteThrow();
void PushTryCatch(
const std::string& name);
void PopTryCatch();
void PushFuncSection(std::string_view include_condition =
"");
bool IsImport(
const std::string& name)
const;
const WriteCOptions& options_;
const Module* module_ = nullptr;
const Func* func_ = nullptr;
Stream* stream_ = nullptr;
std::vector<Stream*> c_streams_;
Stream* h_stream_ = nullptr;
Stream* h_impl_stream_ = nullptr;
std::string header_name_;
std::string header_impl_name_;
Result result_ = Result::Ok;
int indent_ = 0;
bool should_write_indent_next_ =
false;
int consecutive_newline_count_ = 0;
SymbolMap global_sym_map_;
SymbolMap local_sym_map_;
SymbolMap import_module_sym_map_;
StackVarSymbolMap stack_var_sym_map_;
SymbolSet global_syms_;
SymbolSet local_syms_;
TypeVector type_stack_;
std::vector<Label> label_stack_;
std::vector<TryCatchLabel> try_catch_stack_;
std::string module_prefix_;
SymbolSet typevector_structs_;
std::vector<
const Import*> unique_imports_;
SymbolSet import_module_set_;
// modules that are imported from
SymbolSet import_func_module_set_;
// modules that funcs are imported from
std::vector<std::pair<std::string, MemoryStream>> func_sections_;
SymbolSet func_includes_;
std::vector<std::string> unique_func_type_names_;
std::function<std::vector<size_t>(std::vector<Func*>::const_iterator,
std::vector<Func*>::const_iterator,
size_t,
size_t)>
name_to_output_file_index_;
bool simd_used_in_header_;
bool in_tail_callee_;
};
// TODO: if WABT begins supporting debug names for labels,
// will need to avoid conflict between a label named "$Bfunc" and
// the implicit func label
static constexpr
char kImplicitFuncLabel[] =
"$Bfunc";
// These should be greater than any ModuleFieldType (used for MangleField).
static constexpr
char kParamSuffix =
'a' +
static_cast<
char>(ModuleFieldType::Tag) + 1;
static constexpr
char kLabelSuffix = kParamSuffix + 1;
static constexpr
char kGlobalSymbolPrefix[] =
"w2c_";
static constexpr
char kWrapperSymbolPrefix[] =
"wrap_";
static constexpr
char kLocalSymbolPrefix[] =
"var_";
static constexpr
char kAdminSymbolPrefix[] =
"wasm2c_";
static constexpr
char kTailCallSymbolPrefix[] =
"wasm_tailcall_";
static constexpr
char kTailCallFallbackPrefix[] =
"wasm_fallback_";
static constexpr
unsigned int kTailCallStackSize = 1024;
size_t CWriter::MarkTypeStack()
const {
return type_stack_.size();
}
void CWriter::ResetTypeStack(size_t mark) {
assert(mark <= type_stack_.size());
type_stack_.erase(type_stack_.begin() + mark, type_stack_.end());
}
Type CWriter::StackType(Index index)
const {
assert(index < type_stack_.size());
return *(type_stack_.rbegin() + index);
}
void CWriter::PushType(Type type) {
type_stack_.push_back(type);
}
void CWriter::PushTypes(
const TypeVector& types) {
type_stack_.insert(type_stack_.end(), types.begin(), types.end());
}
void CWriter::DropTypes(size_t count) {
assert(count <= type_stack_.size());
type_stack_.erase(type_stack_.end() - count, type_stack_.end());
}
void CWriter::PushLabel(LabelType label_type,
const std::string& name,
const FuncSignature& sig,
bool used) {
if (label_type == LabelType::Loop)
label_stack_.emplace_back(label_type, name, sig.param_types,
type_stack_.size(), try_catch_stack_.size(),
used);
else
label_stack_.emplace_back(label_type, name, sig.result_types,
type_stack_.size(), try_catch_stack_.size(),
used);
}
const Label* CWriter::FindLabel(
const Var& var,
bool mark_used) {
Label* label = nullptr;
if (var.is_index()) {
// We've generated names for all labels, so we should only be using an
// index when branching to the implicit function label, which can't be
// named.
assert(var.index() + 1 == label_stack_.size());
label = &label_stack_[0];
}
else {
assert(var.is_name());
for (Index i = label_stack_.size(); i > 0; --i) {
label = &label_stack_[i - 1];
if (label->name == var.name())
break;
}
}
assert(label);
if (mark_used) {
label->used =
true;
}
return label;
}
bool CWriter::IsTopLabelUsed()
const {
assert(!label_stack_.empty());
return label_stack_.back().used;
}
void CWriter::PopLabel() {
label_stack_.pop_back();
}
// static
constexpr
char CWriter::MangleType(Type type) {
// clang-format off
switch (type) {
case Type::I32:
return 'i';
case Type::I64:
return 'j';
case Type::F32:
return 'f';
case Type::F64:
return 'd';
case Type::V128:
return 'o';
case Type::FuncRef:
return 'r';
case Type::ExternRef:
return 'e';
default:
WABT_UNREACHABLE;
}
// clang-format on
}
// static
constexpr
char CWriter::MangleField(ModuleFieldType type) {
assert(
static_cast<std::underlying_type<ModuleFieldType>::type>(type) <
std::numeric_limits<
char>::max());
return 'a' +
static_cast<
char>(type);
}
// remove risky characters for pasting into a C-style comment
static std::string SanitizeForComment(std::string_view str) {
std::string result;
for (
const uint8_t ch : str) {
// escape control chars, DEL, >7-bit chars, trigraphs, and end of comment
if (ch <
' ' || ch >
'~' || ch ==
'?' || ch ==
'/') {
result +=
"\\" + StringPrintf(
"%02X", ch);
}
else {
result += ch;
}
}
return result;
}
// static
std::string CWriter::MangleTypes(
const TypeVector& types) {
assert(types.size() >= 2);
std::string result =
"wasm_multi_";
for (
auto type : types) {
result += MangleType(type);
}
return result;
}
/* The C symbol for an export from this module. */
std::string CWriter::ExportName(std::string_view export_name)
const {
return kGlobalSymbolPrefix + module_prefix_ +
'_' + MangleName(export_name);
}
/* The C symbol for an export from an arbitrary module. */
// static
std::string CWriter::ExportName(std::string_view module_name,
std::string_view export_name) {
return kGlobalSymbolPrefix + MangleModuleName(module_name) +
'_' +
MangleName(export_name);
}
/* The C symbol for a tail-callee export from this module. */
std::string CWriter::TailCallExportName(std::string_view export_name)
const {
return kTailCallSymbolPrefix + ExportName(export_name);
}
/* The C symbol for a tail-callee export from an arbitrary module. */
// static
std::string CWriter::TailCallExportName(std::string_view module_name,
std::string_view export_name) {
return kTailCallSymbolPrefix + ExportName(module_name, export_name);
}
/* The type name of an instance of this module. */
std::string CWriter::ModuleInstanceTypeName()
const {
return kGlobalSymbolPrefix + module_prefix_;
}
/* The type name of an instance of an arbitrary module. */
// static
std::string CWriter::ModuleInstanceTypeName(std::string_view module_name) {
return kGlobalSymbolPrefix + MangleModuleName(module_name);
}
/*
* Hardcoded "C"-locale versions of isalpha/isdigit/isalnum/isxdigit for use
* in CWriter::Mangle(). We don't use the standard isalpha/isdigit/isalnum
* because the caller might have changed the current locale.
*/
static bool internal_isalpha(uint8_t ch) {
return (ch >=
'A' && ch <=
'Z') || (ch >=
'a' && ch <=
'z');
}
static bool internal_isdigit(uint8_t ch) {
return (ch >=
'0' && ch <=
'9');
}
static bool internal_isalnum(uint8_t ch) {
return internal_isalpha(ch) || internal_isdigit(ch);
}
static bool internal_ishexdigit(uint8_t ch) {
return internal_isdigit(ch) || (ch >=
'A' && ch <=
'F');
// capitals only
}
// static
std::string CWriter::Mangle(std::string_view name,
bool double_underscores) {
/*
* Name mangling transforms arbitrary Wasm names into "safe" C names
* in a deterministic way. To avoid collisions, distinct Wasm names must be
* transformed into distinct C names.
*
* The rules implemented here are:
* 1) any hex digit ('A' through 'F') that follows the sequence "0x"
* is escaped
* 2) any underscore at the beginning, at the end, or following another
* underscore, is escaped
* 3) if double_underscores is set, underscores are replaced with
* two underscores.
* 4) otherwise, any alphanumeric character is kept as-is,
* and any other character is escaped
*
* "Escaped" means the character is represented with the sequence "0xAB",
* where A B are hex digits ('0'-'9' or 'A'-'F') representing the character's
* numeric value.
*
* Module names are mangled with double_underscores=true to prevent
* collisions between, e.g., a module "alfa" with export
* "bravo_charlie" vs. a module "alfa_bravo" with export "charlie".
*/
enum State { Any, Zero, ZeroX, ZeroXHexDigit } state{Any};
bool last_was_underscore =
false;
std::string result;
auto append_escaped = [&](
const uint8_t ch) {
result +=
"0x" + StringPrintf(
"%02X", ch);
last_was_underscore =
false;
state = Any;
};
auto append_verbatim = [&](
const uint8_t ch) {
result += ch;
last_was_underscore = (ch ==
'_');
};
for (
auto it = name.begin(); it != name.end(); ++it) {
const uint8_t ch = *it;
switch (state) {
case Any:
state = (ch ==
'0') ? Zero : Any;
break;
case Zero:
state = (ch ==
'x') ? ZeroX : Any;
break;
case ZeroX:
state = internal_ishexdigit(ch) ? ZeroXHexDigit : Any;
break;
case ZeroXHexDigit:
WABT_UNREACHABLE;
break;
}
/* rule 1 */
if (state == ZeroXHexDigit) {
append_escaped(ch);
continue;
}
/* rule 2 */
if ((ch ==
'_') && ((it == name.begin()) || (std::next(it) == name.end()) ||
last_was_underscore)) {
append_escaped(ch);
continue;
}
/* rule 3 */
if (double_underscores && ch ==
'_') {
append_verbatim(ch);
append_verbatim(ch);
continue;
}
/* rule 4 */
if (internal_isalnum(ch) || (ch ==
'_')) {
append_verbatim(ch);
}
else {
append_escaped(ch);
}
}
return result;
}
// static
std::string CWriter::MangleName(std::string_view name) {
return Mangle(name,
false);
}
// static
std::string CWriter::MangleModuleName(std::string_view name) {
return Mangle(name,
true);
}
/*
* Allocate a C symbol (must be unused) in the SymbolSet,
* and a mapping from the Wasm name (tagged with
* the index space of the name) to that C symbol.
*/
void CWriter::ClaimName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& c_name) {
const std::string type_tagged_wasm_name =
std::string(wasm_name) + type_suffix;
[[maybe_unused]]
bool success;
success = set.insert(c_name).second;
assert(success);
success = map.emplace(type_tagged_wasm_name, c_name).second;
assert(success);
}
/*
* Make a proposed C symbol unique in a given symbol set by appending
* an integer to the symbol if necessary.
*/
std::string CWriter::FindUniqueName(SymbolSet& set,
std::string_view proposed_name)
const {
std::string unique{proposed_name};
if (set.find(unique) != set.end()) {
std::string base = unique +
"_";
size_t count = 0;
do {
unique = base + std::to_string(count++);
}
while (set.find(unique) != set.end());
}
return unique;
}
/*
* Find a unique C symbol in the symbol set and claim it (mapping the
* type-tagged Wasm name to it).
*/
std::string CWriter::ClaimUniqueName(SymbolSet& set,
SymbolMap& map,
char type_suffix,
std::string_view wasm_name,
const std::string& proposed_c_name) {
const std::string unique = FindUniqueName(set, proposed_c_name);
ClaimName(set, map, type_suffix, wasm_name, unique);
return unique;
}
std::string_view StripLeadingDollar(std::string_view name) {
assert(!name.empty());
assert(name.front() ==
'$');
name.remove_prefix(1);
return name;
}
void CWriter::DefineImportName(
const Import* import,
std::string_view module,
std::string_view field_name) {
std::string name;
ModuleFieldType type{};
switch (import->kind()) {
case ExternalKind::Func:
type = ModuleFieldType::Func;
name = cast<FuncImport>(import)->func.name;
break;
case ExternalKind::Tag:
type = ModuleFieldType::Tag;
name = cast<TagImport>(import)->tag.name;
break;
case ExternalKind::Global:
type = ModuleFieldType::Global;
name = cast<GlobalImport>(import)->global.name;
break;
case ExternalKind::Memory:
type = ModuleFieldType::Memory;
name = cast<MemoryImport>(import)->memory.name;
break;
case ExternalKind::Table:
type = ModuleFieldType::Table;
name = cast<TableImport>(import)->table.name;
break;
}
import_module_sym_map_.emplace(name, import->module_name);
const std::string mangled = ExportName(module, field_name);
global_syms_.erase(mangled);
// duplicate imports are allowed
ClaimName(global_syms_, global_sym_map_, MangleField(type), name, mangled);
}
/*
* Reserve a C symbol for the public name of a module's export. The
* format of these is "w2c_" + the module prefix + "_" + the mangled
* export name. Reserving the symbol prevents internal functions and
* other names from shadowing/overlapping the exports.
*/
void CWriter::ReserveExportName(std::string_view name) {
ClaimName(global_syms_, global_sym_map_, MangleField(ModuleFieldType::Export),
name, ExportName(name));
}
/*
* Names for functions, function types, tags, and segments are globally unique
* across modules (formatted the same as an export, as "w2c_" + module prefix +
* "_" + the name, made unique if necessary).
*/
std::string CWriter::DefineGlobalScopeName(ModuleFieldType type,
std::string_view name) {
return ClaimUniqueName(global_syms_, global_sym_map_, MangleField(type), name,
ExportName(StripLeadingDollar(name)));
}
std::string CWriter::GetGlobalName(ModuleFieldType type,
const std::string& name)
const {
std::string mangled = name + MangleField(type);
assert(global_sym_map_.count(mangled) == 1);
return global_sym_map_.at(mangled);
}
/* Names for params, locals, and stack vars are formatted as "var_" + name. */
std::string CWriter::DefineLocalScopeName(std::string_view name,
bool is_label) {
return ClaimUniqueName(
local_syms_, local_sym_map_, is_label ? kLabelSuffix : kParamSuffix, name,
kLocalSymbolPrefix + MangleName(StripLeadingDollar(name)));
}
std::string CWriter::GetLocalName(
const std::string& name,
bool is_label)
const {
std::string mangled = name + (is_label ? kLabelSuffix : kParamSuffix);
assert(local_sym_map_.count(mangled) == 1);
return local_sym_map_.at(mangled);
}
std::string CWriter::GetTailCallRef(
const std::string& name)
const {
return kTailCallSymbolPrefix + GetGlobalName(ModuleFieldType::Func, name);
}
std::string CWriter::DefineParamName(std::string_view name) {
return DefineLocalScopeName(name,
false);
}
std::string CWriter::DefineLabelName(std::string_view name) {
return DefineLocalScopeName(name,
true);
}
std::string CWriter::DefineStackVarName(Index index,
Type type,
std::string_view name) {
std::string unique =
FindUniqueName(local_syms_, kLocalSymbolPrefix + MangleName(name));
StackTypePair stp = {index, type};
[[maybe_unused]]
bool success =
stack_var_sym_map_.emplace(stp, unique).second;
assert(success);
return unique;
}
/*
* Members of the module instance (globals, tables, and memories) are formatted
* as "w2c_" + the mangled name of the element (made unique if necessary).
*/
std::string CWriter::DefineInstanceMemberName(ModuleFieldType type,
std::string_view name) {
return ClaimUniqueName(
global_syms_, global_sym_map_, MangleField(type), name,
kGlobalSymbolPrefix + MangleName(StripLeadingDollar(name)));
}
/*
* The name of a module-instance member that points to the originating
* instance of an imported function is formatted as "w2c_" + originating
* module prefix + "_instance".
*/
std::string CWriter::DefineImportedModuleInstanceName(std::string_view name) {
return ClaimUniqueName(global_syms_, global_sym_map_,
MangleField(ModuleFieldType::Import), name,
ExportName(name,
"instance"));
}
void CWriter::Indent(
int size) {
indent_ += size;
}
void CWriter::Dedent(
int size) {
indent_ -= size;
assert(indent_ >= 0);
}
void CWriter::NonIndented(std::function<
void()> func) {
int copy = indent_;
indent_ = 0;
func();
indent_ = copy;
}
void CWriter::WriteIndent() {
static char s_indent[] =
" "
" ";
static size_t s_indent_len =
sizeof(s_indent) - 1;
size_t to_write = indent_;
while (to_write >= s_indent_len) {
stream_->WriteData(s_indent, s_indent_len);
to_write -= s_indent_len;
}
if (to_write > 0) {
stream_->WriteData(s_indent, to_write);
}
}
void CWriter::WriteData(
const char* src, size_t size) {
if (should_write_indent_next_) {
WriteIndent();
should_write_indent_next_ =
false;
}
if (size > 0 && src[0] !=
'\n') {
consecutive_newline_count_ = 0;
}
stream_->WriteData(src, size);
}
void WABT_PRINTF_FORMAT(2, 3) CWriter::Writef(
const char* format, ...) {
WABT_SNPRINTF_ALLOCA(buffer, length, format);
WriteData(buffer, length);
}
void CWriter::Write(Newline) {
// Allow maximum one blank line between sections
if (consecutive_newline_count_ < 2) {
Write(
"\n");
consecutive_newline_count_++;
}
should_write_indent_next_ =
true;
}
void CWriter::Write(OpenBrace) {
Write(
"{");
Indent();
Write(Newline());
}
void CWriter::Write(CloseBrace) {
Dedent();
Write(
"}");
}
void CWriter::Write(uint64_t val) {
Writef(
"%" PRIu64, val);
}
void CWriter::Write(std::string_view s) {
WriteData(s.data(), s.size());
}
void CWriter::Write(
const ParamName& name) {
Write(GetLocalName(name.name,
false));
}
void CWriter::Write(
const LabelName& name) {
Write(GetLocalName(name.name,
true));
}
void CWriter::Write(
const GlobalName& name) {
Write(GetGlobalName(name.type, name.name));
}
void CWriter::Write(
const TagSymbol& name) {
if (!IsImport(name.name)) {
Write(
"&");
}
Write(GlobalName(name));
}
void CWriter::Write(
const ExternalInstancePtr& name) {
if (!IsImport(name.name)) {
Write(
"&");
}
Write(
"instance->", GlobalName(name));
}
void CWriter::Write(
const ExternalRef& name) {
if (name.type == ModuleFieldType::Func || !IsImport(name.name)) {
Write(GlobalName(name));
}
else {
Write(
"(*", GlobalName(name),
")");
}
}
void CWriter::Write(
const WrapperRef& name) {
Write(kWrapperSymbolPrefix, GlobalName(name));
}
void CWriter::Write(
const TailCallRef& name) {
Write(GetTailCallRef(name.name));
}
void CWriter::Write(
const ExternalInstanceRef& name) {
if (IsImport(name.name)) {
Write(
"(*instance->", GlobalName(name),
")");
}
else {
Write(
"instance->", GlobalName(name));
}
}
void CWriter::Write(
const GotoLabel& goto_label) {
const Label* label = FindLabel(goto_label.var);
if (label->HasValue()) {
size_t amount = label->sig.size();
assert(type_stack_.size() >= label->type_stack_size);
assert(type_stack_.size() >= amount);
assert(type_stack_.size() - amount >= label->type_stack_size);
Index offset = type_stack_.size() - label->type_stack_size - amount;
if (offset != 0) {
for (Index i = 0; i < amount; ++i) {
Write(StackVar(amount - i - 1 + offset, label->sig[i]),
" = ",
StackVar(amount - i - 1),
"; ");
}
}
}
WriteUnwindTryCatchStack(label);
if (goto_label.var.is_name()) {
Write(
"goto ", LabelName(goto_label.var.name()),
";");
}
else {
// We've generated names for all labels, so we should only be using an
// index when branching to the implicit function label, which can't be
// named.
Write(
"goto ", LabelName(kImplicitFuncLabel),
";");
}
}
void CWriter::Write(
const LabelDecl& label) {
if (IsTopLabelUsed())
Write(label.name,
":;", Newline());
}
void CWriter::Write(
const GlobalInstanceVar& var) {
assert(var.var.is_name());
Write(ExternalInstanceRef(ModuleFieldType::Global, var.var.name()));
}
void CWriter::Write(
const StackVar& sv) {
Index index = type_stack_.size() - 1 - sv.index;
Type type = sv.type;
if (type == Type::Any) {
assert(index < type_stack_.size());
type = type_stack_[index];
}
StackTypePair stp = {index, type};
auto iter = stack_var_sym_map_.find(stp);
if (iter == stack_var_sym_map_.end()) {
std::string name = MangleType(type) + std::to_string(index);
Write(DefineStackVarName(index, type, name));
}
else {
Write(iter->second);
}
}
// static
const char* CWriter::GetCTypeName(
const Type& type) {
// clang-format off
switch (type) {
case Type::I32:
return "u32";
case Type::I64:
return "u64";
case Type::F32:
return "f32";
case Type::F64:
return "f64";
case Type::V128:
return "v128";
case Type::FuncRef:
return "wasm_rt_funcref_t";
case Type::ExternRef:
return "wasm_rt_externref_t";
default:
WABT_UNREACHABLE;
}
// clang-format on
}
void CWriter::Write(Type type) {
Write(GetCTypeName(type));
}
void CWriter::Write(TypeEnum type) {
// clang-format off
switch (type.type) {
case Type::I32: Write(
"WASM_RT_I32");
break;
case Type::I64: Write(
"WASM_RT_I64");
break;
case Type::F32: Write(
"WASM_RT_F32");
break;
case Type::F64: Write(
"WASM_RT_F64");
break;
case Type::V128: Write(
"WASM_RT_V128");
break;
case Type::FuncRef: Write(
"WASM_RT_FUNCREF");
break;
case Type::ExternRef: Write(
"WASM_RT_EXTERNREF");
break;
default:
WABT_UNREACHABLE;
}
// clang-format on
}
void CWriter::Write(SignedType type) {
// clang-format off
switch (type.type) {
case Type::I32: Write(
"s32");
break;
case Type::I64: Write(
"s64");
break;
default:
WABT_UNREACHABLE;
}
// clang-format on
}
void CWriter::Write(
const TypeVector& types) {
if (types.empty()) {
Write(
"void");
}
else if (types.size() == 1) {
Write(types[0]);
}
else {
Write(
"struct ", MangleTypes(types));
}
}
void CWriter::Write(
const Const& const_) {
switch (const_.type()) {
case Type::I32:
Writef(
"%uu",
static_cast<int32_t>(const_.u32()));
break;
case Type::I64:
Writef(
"%" PRIu64
"ull",
static_cast<int64_t>(const_.u64()));
break;
case Type::F32: {
uint32_t f32_bits = const_.f32_bits();
// TODO(binji): Share with similar float info in interp.cc and literal.cc
if ((f32_bits & 0x7f800000u) == 0x7f800000u) {
const char* sign = (f32_bits & 0x80000000) ?
"-" :
"";
uint32_t significand = f32_bits & 0x7fffffu;
if (significand == 0) {
// Infinity.
Writef(
"%sINFINITY", sign);
}
else {
// Nan.
Writef(
"f32_reinterpret_i32(0x%08x) /* %snan:0x%06x */", f32_bits,
sign, significand);
}
}
else if (f32_bits == 0x80000000) {
// Negative zero. Special-cased so it isn't written as -0 below.
Writef(
"-0.f");
}
else {
Writef(
"%.9g", Bitcast<
float>(f32_bits));
}
break;
}
case Type::F64: {
uint64_t f64_bits = const_.f64_bits();
// TODO(binji): Share with similar float info in interp.cc and literal.cc
if ((f64_bits & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) {
const char* sign = (f64_bits & 0x8000000000000000ull) ?
"-" :
"";
uint64_t significand = f64_bits & 0xfffffffffffffull;
if (significand == 0) {
// Infinity.
Writef(
"%sINFINITY", sign);
}
else {
// Nan.
Writef(
"f64_reinterpret_i64(0x%016" PRIx64
") /* %snan:0x%013" PRIx64
" */",
f64_bits, sign, significand);
}
}
else if (f64_bits == 0x8000000000000000ull) {
// Negative zero. Special-cased so it isn't written as -0 below.
Writef(
"-0.0");
}
else {
char buf[128];
snprintf(buf,
sizeof(buf),
"%.17g", Bitcast<
double>(f64_bits));
// Append .0 if sprint didn't include a decimal point or use the
// exponent ('e') form. This is a workaround for an MSVC parsing
// issue: https://github.com/WebAssembly/wabt/issues/2422
if (!strchr(buf,
'.') && !strchr(buf,
'e')) {
strcat(buf,
".0");
}
Writef(
"%s", buf);
}
break;
}
case Type::V128: {
Writef(
"v128_const(0x%02x", const_.vec128().u8(0));
for (
int i = 1; i < 16; i++) {
Writef(
", 0x%02x", const_.vec128().u8(i));
}
Write(
")");
break;
}
default:
WABT_UNREACHABLE;
}
}
void CWriter::WriteInitDecl() {
Write(
"void ", kAdminSymbolPrefix, module_prefix_,
"_instantiate(",
ModuleInstanceTypeName(),
"*");
for (
const auto& import_module_name : import_module_set_) {
Write(
", struct ", ModuleInstanceTypeName(import_module_name),
"*");
}
Write(
");", Newline());
}
void CWriter::WriteFreeDecl() {
Write(
"void ", kAdminSymbolPrefix, module_prefix_,
"_free(",
ModuleInstanceTypeName(),
"*);", Newline());
}
void CWriter::WriteGetFuncTypeDecl() {
Write(
"wasm_rt_func_type_t ", kAdminSymbolPrefix, module_prefix_,
"_get_func_type(uint32_t param_count, uint32_t result_count, ...);",
Newline());
}
static std::string GetMemoryTypeString(
const Memory& memory) {
return memory.page_limits.is_shared ?
"wasm_rt_shared_memory_t"
:
"wasm_rt_memory_t";
}
static std::string GetMemoryAPIString(
const Memory& memory, std::string api) {
return memory.page_limits.is_shared ? (api +
"_shared") : api;
}
void CWriter::WriteInitExpr(
const ExprList& expr_list) {
if (expr_list.empty()) {
WABT_UNREACHABLE;
}
std::vector<std::string> mini_stack;
for (
const auto& expr : expr_list) {
if (expr.type() == ExprType::Binary) {
// Extended const expressions include at least one binary op.
// This builds a C expression from the operands.
if (mini_stack.size() < 2) {
WABT_UNREACHABLE;
}
const auto binexpr = cast<BinaryExpr>(&expr);
char op;
switch (binexpr->opcode) {
case Opcode::I32Add:
case Opcode::I64Add:
case Opcode::F32Add:
case Opcode::F64Add:
op =
'+';
break;
case Opcode::I32Sub:
case Opcode::I64Sub:
case Opcode::F32Sub:
case Opcode::F64Sub:
op =
'-';
break;
case Opcode::I32Mul:
case Opcode::I64Mul:
case Opcode::F32Mul:
case Opcode::F64Mul:
op =
'*';
break;
default:
WABT_UNREACHABLE;
}
std::string combination =
"((" + std::string(GetCTypeName(binexpr->opcode.GetParamType1())) +
")" + mini_stack.at(mini_stack.size() - 2) +
")" + op +
"((" +
std::string(GetCTypeName(binexpr->opcode.GetParamType2())) +
")" +
mini_stack.at(mini_stack.size() - 1) +
")";
mini_stack.resize(mini_stack.size() - 2);
mini_stack.push_back(std::move(combination));
}
else {
// Leaf node (nullary const expression)
Stream* existing_stream = stream_;
MemoryStream terminal_stream;
stream_ = &terminal_stream;
WriteInitExprTerminal(&expr);
const auto& buf = terminal_stream.output_buffer();
mini_stack.emplace_back(
reinterpret_cast<
const char*>(buf.data.data()),
buf.data.size());
stream_ = existing_stream;
}
}
if (mini_stack.size() != 1) {
WABT_UNREACHABLE;
}
Write(mini_stack.front());
}
void CWriter::WriteInitExprTerminal(
const Expr* expr) {
switch (expr->type()) {
case ExprType::
Const:
Write(cast<ConstExpr>(expr)->const_);
break;
case ExprType::GlobalGet:
Write(GlobalInstanceVar(cast<GlobalGetExpr>(expr)->var));
break;
case ExprType::RefFunc: {
const Func* func = module_->GetFunc(cast<RefFuncExpr>(expr)->var);
const FuncDeclaration& decl = func->decl;
assert(decl.has_func_type);
const FuncType* func_type = module_->GetFuncType(decl.type_var);
Write(
"(wasm_rt_funcref_t){", FuncTypeExpr(func_type),
", ",
"(wasm_rt_function_ptr_t)", WrapperRef(func->name),
", {");
if (options_.features.tail_call_enabled() &&
(IsImport(func->name) || func->features_used.tailcall)) {
Write(TailCallRef(func->name));
}
else {
Write(
"NULL");
}
Write(
"}, ");
if (IsImport(func->name)) {
Write(
"instance->", GlobalName(ModuleFieldType::Import,
import_module_sym_map_[func->name]));
}
else {
Write(
"instance");
}
Write(
"}");
}
break;
case ExprType::RefNull:
Write(GetReferenceNullValue(cast<RefNullExpr>(expr)->type));
break;
default:
WABT_UNREACHABLE;
}
}
std::string CWriter::GenerateHeaderGuard()
const {
std::string result;
for (
char c : header_name_) {
if (isalnum(c) || c ==
'_') {
result += toupper(c);
}
else {
result +=
'_';
}
}
result +=
"_GENERATED_";
return result;
}
void CWriter::WriteSourceTop() {
Write(s_source_includes);
Write(Newline(),
"#include \"", header_name_, "\
"", Newline());
if (IsSingleUnsharedMemory()) {
Write(
"#define IS_SINGLE_UNSHARED_MEMORY 1", Newline());
}
Write(s_source_declarations, Newline());
if (module_->features_used.simd) {
if (!simd_used_in_header_) {
WriteV128Decl();
}
Write(s_simd_source_declarations);
}
if (module_->features_used.threads) {
Write(s_atomicops_source_declarations);
}
}
void CWriter::WriteMultiCTop() {
if (c_streams_.size() > 1) {
assert(header_impl_name_.size() > 0);
Write(
"/* Automatically generated by wasm2c */", Newline());
Write(
"#include \"", header_impl_name_, "\
"", Newline());
}
}
void CWriter::WriteMultiCTopEmpty() {
for (
auto& stream : c_streams_) {
if (stream->offset() == 0) {
stream_ = stream;
Write(
"/* Empty wasm2c generated file */\n");
Write(
"typedef int dummy_def;");
}
}
}
void CWriter::DeclareStruct(
const TypeVector& types) {
const std::string name = MangleTypes(types);
if (!typevector_structs_.insert(name).second) {
return;
}
Write(Newline(),
"#ifndef ", name, Newline());
Write(
"#define ", name,
" ", name, Newline());
Write(
"struct ", name,
" ", OpenBrace());
for (Index i = 0; i < types.size(); ++i) {
const Type type = types[i];
Write(type);
Writef(
" %c%d;", MangleType(type), i);
Write(Newline());
}
Write(CloseBrace(),
";", Newline(),
"#endif /* ", name, " */", Newline());
}
void CWriter::WriteTailcalleeParamTypes() {
// A structure for the spilled parameters of a tail-callee is needed in
// three cases: for a function that makes a tail-call (and therefore
// will have a tail-callee version generated), for any imported function
// (for which wasm2c generates a weak import that presents the tail-callee
// interface, in case the exporting module doesn't generate one itself), and
// for any type entry referenced in a return_call_indirect instruction.
for (
const Func* func : module_->funcs) {
if (IsImport(func->name) || func->features_used.tailcall) {
if (func->decl.sig.GetNumParams() > 1) {
DeclareStruct(func->decl.sig.param_types);
}
}
}
for (TypeEntry* type : module_->types) {
FuncType* func_type = cast<FuncType>(type);
if (func_type->GetNumParams() > 1 && func_type->features_used.tailcall) {
DeclareStruct(func_type->sig.param_types);
}
}
}
void CWriter::WriteMultivalueResultTypes() {
for (TypeEntry* type : module_->types) {
FuncType* func_type = cast<FuncType>(type);
if (func_type->GetNumResults() > 1) {
DeclareStruct(func_type->sig.result_types);
}
}
}
void CWriter::WriteTagTypes() {
for (
const Tag* tag : module_->tags) {
const FuncDeclaration& tag_type = tag->decl;
Index num_params = tag_type.GetNumParams();
if (num_params <= 1) {
continue;
}
DeclareStruct(tag_type.sig.param_types);
}
}
void CWriter::WriteFuncTypeDecls() {
if (module_->types.empty()) {
return;
}
Write(Newline());
std::string serialized_type;
for (
const TypeEntry* type : module_->types) {
const std::string name =
DefineGlobalScopeName(ModuleFieldType::Type, type->name);
if (c_streams_.size() > 1) {
Write(
"FUNC_TYPE_DECL_EXTERN_T(", name,
");", Newline());
}
}
}
void CWriter::WriteFuncTypes() {
if (module_->types.empty()) {
return;
}
Write(Newline());
std::unordered_map<std::string, std::string> type_hash;
std::string serialized_type;
for (
const TypeEntry* type : module_->types) {
const std::string name = GetGlobalName(ModuleFieldType::Type, type->name);
SerializeFuncType(*cast<FuncType>(type), serialized_type);
auto prior_type = type_hash.find(serialized_type);
if (prior_type != type_hash.end()) {
/* duplicate function type */
unique_func_type_names_.push_back(prior_type->second);
}
else {
unique_func_type_names_.push_back(name);
type_hash.emplace(serialized_type, name);
if (c_streams_.size() > 1) {
Write(
"FUNC_TYPE_EXTERN_T(");
}
else {
Write(
"FUNC_TYPE_T(");
}
Write(name,
") = \"");
for (uint8_t x : serialized_type) {
Writef(
"\\x%02x", x);
}
Write(
"\";
", Newline());
}
}
}
void CWriter::Write(
const FuncTypeExpr& expr) {
Index func_type_index = module_->GetFuncTypeIndex(expr.func_type->sig);
Write(unique_func_type_names_.at(func_type_index));
}
// static
void CWriter::SerializeFuncType(
const FuncType& func_type,
std::string& serialized_type) {
unsigned int len = func_type.GetNumParams() + func_type.GetNumResults() + 1;
char*
const mangled_signature =
static_cast<
char*>(alloca(len));
char* next_byte = mangled_signature;
// step 1: serialize each param type
for (Index i = 0; i < func_type.GetNumParams(); ++i) {
*next_byte++ = MangleType(func_type.GetParamType(i));
}
// step 2: separate params and results with a space
*next_byte++ =
' ';
// step 3: serialize each result type
for (Index i = 0; i < func_type.GetNumResults(); ++i) {
*next_byte++ = MangleType(func_type.GetResultType(i));
}
assert(next_byte - mangled_signature ==
static_cast<ptrdiff_t>(len));
// step 4: SHA-256 the whole string
sha256({mangled_signature, len}, serialized_type);
}
void CWriter::WriteTagDecls() {
Index tag_index = 0;
for (
const Tag* tag : module_->tags) {
bool is_import = tag_index < module_->num_tag_imports;
if (!is_import) {
// Tags are identified and compared solely by their (unique) address.
// The data stored in this variable is never read.
if (tag_index == module_->num_tag_imports) {
Write(Newline());
Write(
"typedef char wasm_tag_placeholder_t;", Newline());
}
DefineGlobalScopeName(ModuleFieldType::Tag, tag->name);
if (c_streams_.size() > 1) {
Write(
"extern const wasm_tag_placeholder_t ",
GlobalName(ModuleFieldType::Tag, tag->name),
";", Newline());
}
}
tag_index++;
}
}
void CWriter::WriteTags() {
Write(Newline());
Index tag_index = 0;
for (
const Tag* tag : module_->tags) {
bool is_import = tag_index < module_->num_tag_imports;
if (!is_import) {
Write(InternalSymbolScope(),
"const wasm_tag_placeholder_t ",
GlobalName(ModuleFieldType::Tag, tag->name),
";", Newline());
}
tag_index++;
}
}
void CWriter::ComputeUniqueImports() {
using modname_name_pair = std::pair<std::string, std::string>;
std::map<modname_name_pair,
const Import*> import_map;
for (
const Import* import : module_->imports) {
// After emplacing, the returned bool says whether the insert happened;
// i.e., was there already an import with the same modname and name?
// If there was, make sure it was at least the same kind of import.
const auto iterator_and_insertion_bool = import_map.emplace(
modname_name_pair(import->module_name, import->field_name), import);
if (!iterator_and_insertion_bool.second) {
if (iterator_and_insertion_bool.first->second->kind() != import->kind()) {
UNIMPLEMENTED(
"contradictory import declaration");
}
else {
fprintf(stderr,
"warning: duplicate import declaration \"%s\
" \"%s\
"\n",
import->module_name.c_str(), import->field_name.c_str());
}
}
import_module_set_.insert(import->module_name);
if (import->kind() == ExternalKind::Func) {
import_func_module_set_.insert(import->module_name);
}
}
for (
const auto& node : import_map) {
unique_imports_.push_back(node.second);
}
}
void CWriter::BeginInstance() {
if (module_->imports.empty()) {
Write(
"typedef struct ", ModuleInstanceTypeName(),
" ", OpenBrace());
return;
}
ComputeUniqueImports();
// define names of per-instance imports
for (
const Import* import : module_->imports) {
DefineImportName(import, import->module_name, import->field_name);
}
// Forward declaring module instance types
for (
const auto& import_module : import_module_set_) {
DefineImportedModuleInstanceName(import_module);
Write(
"struct ", ModuleInstanceTypeName(import_module),
";", Newline());
}
// Forward declaring module imports
for (
const Import* import : unique_imports_) {
if ((import->kind() == ExternalKind::Func) ||
(import->kind() == ExternalKind::Tag)) {
continue;
}
Write(
"extern ");
switch (import->kind()) {
case ExternalKind::Global: {
const Global& global = cast<GlobalImport>(import)->global;
Write(global.type);
break;
}
case ExternalKind::Memory: {
Write(GetMemoryTypeString(cast<MemoryImport>(import)->memory));
break;
}
case ExternalKind::Table:
WriteTableType(cast<TableImport>(import)->table.elem_type);
break;
default:
WABT_UNREACHABLE;
}
Write(
"* ", ExportName(import->module_name, import->field_name),
"(struct ",
ModuleInstanceTypeName(import->module_name),
"*);", Newline());
}
Write(Newline());
// Add pointers to module instances that any func is imported from,
// so that imported functions can be given their own module instances
// when invoked
Write(
"typedef struct ", ModuleInstanceTypeName(),
" ", OpenBrace());
for (
const auto& import_module : import_func_module_set_) {
Write(
"struct ", ModuleInstanceTypeName(import_module),
"* ",
GlobalName(ModuleFieldType::Import, import_module),
";", Newline());
}
for (
const Import* import : unique_imports_) {
if ((import->kind() == ExternalKind::Func) ||
(import->kind() == ExternalKind::Tag)) {
continue;
}
Write(
"/* import: '", SanitizeForComment(import->module_name), "' '",
SanitizeForComment(import->field_name),
"' */", Newline());
switch (import->kind()) {
case ExternalKind::Global:
WriteGlobal(cast<GlobalImport>(import)->global,
std::string(
"*") +
ExportName(import->module_name, import->field_name));
break;
case ExternalKind::Memory:
WriteMemory(std::string(
"*") +
ExportName(import->module_name, import->field_name),
cast<MemoryImport>(import)->memory);
break;
case ExternalKind::Table: {
const Table& table = cast<TableImport>(import)->table;
WriteTable(std::string(
"*") +
ExportName(import->module_name, import->field_name),
table.elem_type);
}
break;
default:
WABT_UNREACHABLE;
}
Write(Newline());
}
}
// Write module-wide imports (funcs & tags), which aren't tied to an instance.
void CWriter::WriteImports() {
if (unique_imports_.empty())
return;
Write(Newline());
for (
const Import* import : unique_imports_) {
if (import->kind() == ExternalKind::Func) {
Write(Newline(),
"/* import: '", SanitizeForComment(import->module_name),
"' '", SanitizeForComment(import->field_name),
"' */", Newline());
const Func& func = cast<FuncImport>(import)->func;
WriteImportFuncDeclaration(
func.decl, import->module_name,
ExportName(import->module_name, import->field_name));
Write(
";", Newline());
if (options_.features.tail_call_enabled()) {
WriteTailCallFuncDeclaration(GetTailCallRef(func.name));
Write(
";", Newline());
}
}
else if (import->kind() == ExternalKind::Tag) {
Write(Newline(),
"/* import: '", SanitizeForComment(import->module_name),
"' '", SanitizeForComment(import->field_name),
"' */", Newline());
Write(
"extern const wasm_rt_tag_t ",
ExportName(import->module_name, import->field_name),
";",
Newline());
}
}
}
void CWriter::WriteTailCallWeakImports() {
for (
const Import* import : unique_imports_) {
if (import->kind() != ExternalKind::Func) {
continue;
}
const Func& func = cast<FuncImport>(import)->func;
Write(Newline(),
"/* handler for missing tail-call on import: '",
SanitizeForComment(import->module_name),
"' '",
SanitizeForComment(import->field_name),
"' */", Newline());
Write(
"WEAK_FUNC_DECL(",
TailCallExportName(import->module_name, import->field_name),
", ",
kTailCallFallbackPrefix, module_prefix_,
"_",
ExportName(import->module_name, import->field_name),
")", Newline());
Write(OpenBrace(),
"next->fn = NULL;", Newline());
Index num_params = func.GetNumParams();
Index num_results = func.GetNumResults();
if (num_params >= 1) {
Write(func.decl.sig.param_types,
" params;", Newline());
Write(
"wasm_rt_memcpy(params, tail_call_stack, sizeof(params));",
Newline());
}
if (num_results >= 1) {
Write(func.decl.sig.result_types,
" result = ");
}
Write(ExportName(import->module_name, import->field_name),
"(*instance_ptr");
if (num_params == 1) {
Write(
", params");
}
else {
for (Index i = 0; i < num_params; ++i) {
Writef(
", params.%c%d", MangleType(func.GetParamType(i)), i);
}
}
Write(
");", Newline());
if (num_results >= 1) {
Write(
"wasm_rt_memcpy(tail_call_stack, &result, sizeof(result));",
Newline());
}
Write(CloseBrace(), Newline());
}
}
void CWriter::WriteFuncDeclarations() {
if (module_->funcs.size() == module_->num_func_imports)
return;
Write(Newline());
Index func_index = 0;
for (
const Func* func : module_->funcs) {
bool is_import = func_index < module_->num_func_imports;
if (!is_import) {
Write(InternalSymbolScope());
WriteFuncDeclaration(
func->decl, DefineGlobalScopeName(ModuleFieldType::Func, func->name));
Write(
";", Newline());
if (func->features_used.tailcall) {
Write(InternalSymbolScope());
WriteTailCallFuncDeclaration(GetTailCallRef(func->name));
Write(
";", Newline());
}
}
++func_index;
}
}
void CWriter::WriteFuncDeclaration(
const FuncDeclaration& decl,
const std::string& name) {
Write(decl.sig.result_types,
" ", name,
"(");
Write(ModuleInstanceTypeName(),
"*");
WriteParamTypes(decl);
Write(
")");
}
void CWriter::WriteTailCallFuncDeclaration(
const std::string& mangled_name) {
Write(
"void ", mangled_name,
"(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t "
"*next)");
}
void CWriter::WriteImportFuncDeclaration(
const FuncDeclaration& decl,
const std::string& module_name,
const std::string& name) {
Write(decl.sig.result_types,
" ", name,
"(");
Write(
"struct ", ModuleInstanceTypeName(module_name),
"*");
WriteParamTypes(decl);
Write(
")");
}
void CWriter::WriteCallIndirectFuncDeclaration(
const FuncDeclaration& decl,
const std::string& name) {
Write(decl.sig.result_types,
" ", name,
"(void*");
WriteParamTypes(decl);
Write(
")");
}
static bool func_uses_simd(
const FuncSignature& sig) {
return std::any_of(sig.param_types.begin(), sig.param_types.end(),
[](
auto x) {
return x == Type::V128; }) ||
std::any_of(sig.result_types.begin(), sig.result_types.end(),
[](
auto x) {
return x == Type::V128; });
}
void CWriter::ComputeSimdScope() {
simd_used_in_header_ =
module_->features_used.simd &&
(std::any_of(module_->globals.begin(), module_->globals.end(),
[](
const auto& x) {
return x->type == Type::V128; }) ||
std::any_of(module_->imports.begin(), module_->imports.end(),
[](
const auto& x) {
return x->kind() == ExternalKind::Func &&
func_uses_simd(cast<FuncImport>(x)->func.decl.sig);
}) ||
std::any_of(module_->exports.begin(), module_->exports.end(),
[&](
const auto& x) {
return x->kind == ExternalKind::Func &&
func_uses_simd(module_->GetFunc(x->var)->decl.sig);
}));
}
void CWriter::WriteHeaderIncludes() {
Write(
"#include \"wasm-rt.h\
"", Newline());
if (module_->features_used.exceptions) {
Write(
"#include \"wasm-rt-exceptions.h\
"", Newline(), Newline());
}
if (simd_used_in_header_) {
WriteV128Decl();
}
Write(Newline());
}
void CWriter::WriteV128Decl() {
Write(
"#include ", Newline(), Newline());
Write(
"#ifndef WASM_RT_SIMD_TYPE_DEFINED", Newline(),
"#define WASM_RT_SIMD_TYPE_DEFINED", Newline(),
"typedef simde_v128_t v128;", Newline(),
"#endif", Newline(),
Newline());
}
void CWriter::WriteModuleInstance() {
BeginInstance();
WriteGlobals();
WriteMemories();
WriteTables();
WriteDataInstances();
WriteElemInstances();
// C forbids an empty struct
if (module_->globals.empty() && module_->memories.empty() &&
module_->tables.empty() && import_func_module_set_.empty()) {
Write(
"char dummy_member;", Newline());
}
Write(CloseBrace(),
" ", ModuleInstanceTypeName(),
";", Newline());
Write(Newline());
}
void CWriter::WriteGlobals() {
Index global_index = 0;
if (module_->globals.size() != module_->num_global_imports) {
for (
const Global* global : module_->globals) {
bool is_import = global_index < module_->num_global_imports;
if (!is_import) {
WriteGlobal(*global, DefineInstanceMemberName(ModuleFieldType::Global,
global->name));
Write(Newline());
}
++global_index;
}
}
}
void CWriter::WriteGlobal(
const Global& global,
const std::string& name) {
Write(global.type,
" ", name,
";");
}
void CWriter::WriteGlobalPtr(
const Global& global,
const std::string& name) {
Write(global.type,
"* ", name,
"(", ModuleInstanceTypeName(),
"* instance)");
}
void CWriter::WriteMemories() {
if (module_->memories.size() == module_->num_memory_imports)
return;
Index memory_index = 0;
for (
const Memory* memory : module_->memories) {
bool is_import = memory_index < module_->num_memory_imports;
if (!is_import) {
WriteMemory(
DefineInstanceMemberName(ModuleFieldType::Memory, memory->name),
*memory);
Write(Newline());
}
++memory_index;
}
}
void CWriter::WriteMemory(
const std::string& name,
const Memory& memory) {
Write(GetMemoryTypeString(memory),
" ", name,
";");
}
void CWriter::WriteMemoryPtr(
const std::string& name,
const Memory& memory) {
Write(GetMemoryTypeString(memory),
"* ", name,
"(", ModuleInstanceTypeName(),
"* instance)");
}
void CWriter::WriteTables() {
if (module_->tables.size() == module_->num_table_imports) {
return;
}
Index table_index = 0;
for (
const Table* table : module_->tables) {
bool is_import = table_index < module_->num_table_imports;
if (!is_import) {
WriteTable(DefineInstanceMemberName(ModuleFieldType::Table, table->name),
table->elem_type);
Write(Newline());
}
++table_index;
}
}
--> --------------------
--> maximum size reached
--> --------------------