Commit 16f8061c by Eric Holk

Initial Subzero WASM prototype.

BUG= R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1837663002 .
parent 263ac526
...@@ -179,6 +179,9 @@ endif ...@@ -179,6 +179,9 @@ endif
SB_OBJDIR := $(OBJDIR)+Sandboxed SB_OBJDIR := $(OBJDIR)+Sandboxed
SBB_OBJDIR := $(OBJDIR)+SandboxedBrowser SBB_OBJDIR := $(OBJDIR)+SandboxedBrowser
V8_DIR = $(NACL_ROOT)/../v8
V8_CXXFLAGS := -I$(V8_DIR)
$(info -----------------------------------------------) $(info -----------------------------------------------)
$(info Using LLVM_SRC_PATH = $(LLVM_SRC_PATH)) $(info Using LLVM_SRC_PATH = $(LLVM_SRC_PATH))
$(info Using SB_LLVM_PATH = $(SB_LLVM_PATH)) $(info Using SB_LLVM_PATH = $(SB_LLVM_PATH))
...@@ -251,10 +254,19 @@ ifdef GPLUSPLUS ...@@ -251,10 +254,19 @@ ifdef GPLUSPLUS
OBJDIR := $(OBJDIR)+Gplusplus OBJDIR := $(OBJDIR)+Gplusplus
endif endif
BASE_CXXFLAGS := -std=gnu++11 -Wall -Wextra -Werror -fno-rtti \ BASE_CXXFLAGS := -std=gnu++11 -Wall -Wextra -fno-rtti \
-fno-exceptions $(OPTLEVEL) $(ASSERTIONS) -g -pedantic \ -fno-exceptions $(OPTLEVEL) $(ASSERTIONS) -g -pedantic \
$(LLVM_EXTRA_WARNINGS) $(CXX_EXTRA) -MP -MD $(LLVM_EXTRA_WARNINGS) $(CXX_EXTRA) -MP -MD
ifdef WASM
# The WASM code inherits a lot of V8 code, which does not compile with
# -Werror.
BASE_CXXFLAGS := $(BASE_CXXFLAGS) $(V8_CXXFLAGS) -DALLOW_WASM=1
OBJDIR := $(OBJDIR)+Wasm
else
BASE_CXXFLAGS := $(BASE_CXXFLAGS) -Werror -DALLOW_WASM=0
endif
CXXFLAGS := $(LLVM_CXXFLAGS) $(BASE_CXXFLAGS) $(CXX_DEFINES) $(HOST_FLAGS) \ CXXFLAGS := $(LLVM_CXXFLAGS) $(BASE_CXXFLAGS) $(CXX_DEFINES) $(HOST_FLAGS) \
$(STDLIB_FLAGS) $(STDLIB_FLAGS)
SB_CXXFLAGS := $(SB_LLVM_CXXFLAGS) $(BASE_CXXFLAGS) $(BASE_CXX_DEFINES) \ SB_CXXFLAGS := $(SB_LLVM_CXXFLAGS) $(BASE_CXXFLAGS) $(BASE_CXX_DEFINES) \
...@@ -315,6 +327,11 @@ ifndef MINIMAL ...@@ -315,6 +327,11 @@ ifndef MINIMAL
IceTypeConverter.cpp IceTypeConverter.cpp
endif endif
ifdef WASM
SRCS += \
WasmTranslator.cpp
endif
OBJS=$(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS)) OBJS=$(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS))
SB_OBJS=$(patsubst %.cpp, $(SB_OBJDIR)/%.o, $(SRCS)) SB_OBJS=$(patsubst %.cpp, $(SB_OBJDIR)/%.o, $(SRCS))
SBB_OBJS=$(patsubst %.cpp, $(SBB_OBJDIR)/%.o, $(SRCS)) SBB_OBJS=$(patsubst %.cpp, $(SBB_OBJDIR)/%.o, $(SRCS))
...@@ -392,9 +409,18 @@ sb_make_symlink: $(NEXES) ...@@ -392,9 +409,18 @@ sb_make_symlink: $(NEXES)
compile_only: $(OBJS) compile_only: $(OBJS)
V8_LIBDIR=$(V8_DIR)/out/native/lib.target
ifdef WASM
V8_LIBS := \
$(V8_LIBDIR)/libv8.so \
$(V8_LIBDIR)/libicuuc.so \
$(V8_LIBDIR)/libicui18n.so
endif
$(OBJDIR)/pnacl-sz: $(OBJS) $(OBJDIR)/pnacl-sz: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^ $(LLVM_LDFLAGS) \ $(CXX) $(LDFLAGS) -o $@ $^ $(LLVM_LDFLAGS) \
-Wl,-rpath=$(abspath $(LIBCXX_INSTALL_PATH)/lib) -Wl,-rpath=$(abspath $(LIBCXX_INSTALL_PATH)/lib) $(V8_LIBS)
$(SB_OBJDIR)/pnacl-sz.nonfinal.pexe: $(SB_OBJS) $(SB_OBJDIR)/pnacl-sz.nonfinal.pexe: $(SB_OBJS)
$(SB_CXX) $(SB_LDFLAGS) -o $@ $^ $(SB_LLVM_LDFLAGS) $(SB_CXX) $(SB_LDFLAGS) -o $@ $^ $(SB_LLVM_LDFLAGS)
...@@ -713,6 +739,7 @@ FORMAT_BLACKLIST += ! -name IceParseInstsTest.cpp ...@@ -713,6 +739,7 @@ FORMAT_BLACKLIST += ! -name IceParseInstsTest.cpp
FORMAT_BLACKLIST += ! -name IceParseTypesTest.cpp FORMAT_BLACKLIST += ! -name IceParseTypesTest.cpp
FORMAT_BLACKLIST += ! -name assembler_arm.h FORMAT_BLACKLIST += ! -name assembler_arm.h
FORMAT_BLACKLIST += ! -name assembler_arm.cc FORMAT_BLACKLIST += ! -name assembler_arm.cc
FORMAT_BLACKLIST += ! -path "./wasm-install/*"
format: format:
$(CLANG_FORMAT_PATH)/clang-format -style=LLVM -i \ $(CLANG_FORMAT_PATH)/clang-format -style=LLVM -i \
`find . -regex '.*\.\(c\|h\|cpp\)' $(FORMAT_BLACKLIST)` `find . -regex '.*\.\(c\|h\|cpp\)' $(FORMAT_BLACKLIST)`
......
# Wasm Prototype Experiment Notes
Here's the command I use to test:
```
LD_LIBRARY_PATH=~/nacl/v8/out/native/lib.target make -j48 \
-f Makefile.standalone WASM=1 && \
LD_LIBRARY_PATH=~/nacl/v8/out/native/lib.target ./pnacl-sz -O2 -filetype=asm \
-target=arm32 ./torture-s2wasm-sexpr-wasm/20000112-1.c.s.wast.wasm
```
You'll probably need to adjust your `LD_LIBRARY_PATH` to point to where your v8
libraries are.
You'll need to build v8 as a shared library. Build it like this:
```
make -j48 native component=shared_library
```
`wasm-run-torture-tests.py` can be used to run all the tests, or some subset.
Running a subset will enable verbose output. You can download the torture tests
from the [WebAssembly waterfall](https://wasm-stat.us/console).
#!/usr/bin/env python2
#===- subzero/wasm-run-torture-tests.py - Subzero WASM Torture Test Driver ===//
#
# The Subzero Code Generator
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===-----------------------------------------------------------------------===//
import glob
import os
import sys
success_count = 0
fail_count = 0
failures = []
def run_test(test_file, verbose=False):
global success_count
global fail_count
cmd = """LD_LIBRARY_PATH=../../../../v8/out/native/lib.target ./pnacl-sz \
-filetype=asm -target=arm32 {} -threads=0 -O2 \
-verbose=wasm""".format(test_file)
if not verbose:
cmd += " &> /dev/null"
sys.stdout.write(test_file + "...");
status = os.system(cmd);
if status != 0:
fail_count += 1
print('\033[1;31m[fail]\033[1;m')
failures.append(test_file)
else:
success_count += 1
print('\033[1;32m[ok]\033[1;m')
verbose = False
if len(sys.argv) > 1:
test_files = sys.argv[1:]
verbose = True
else:
test_files = glob.glob("./torture-s2wasm-sexpr-wasm.old/*.wasm")
for test_file in test_files:
run_test(test_file, verbose)
if len(failures) > 0:
print("Failures:")
for f in failures:
print(" \033[1;31m" + f + "\033[1;m")
print("{} / {} tests passed".format(success_count, success_count + fail_count))
...@@ -90,6 +90,8 @@ constexpr bool llvmIr() { return ALLOW_LLVM_IR; } ...@@ -90,6 +90,8 @@ constexpr bool llvmIr() { return ALLOW_LLVM_IR; }
constexpr bool llvmIrAsInput() { return ALLOW_LLVM_IR_AS_INPUT; } constexpr bool llvmIrAsInput() { return ALLOW_LLVM_IR_AS_INPUT; }
/// Return true if ALLOW_MINIMAL_BUILD is defined as a non-zero value /// Return true if ALLOW_MINIMAL_BUILD is defined as a non-zero value
constexpr bool minimal() { return ALLOW_MINIMAL_BUILD; } constexpr bool minimal() { return ALLOW_MINIMAL_BUILD; }
/// Return true if ALLOW_WASM is defined as a non-zero value
constexpr bool wasm() { return ALLOW_WASM; }
/// Return true if NDEBUG is defined /// Return true if NDEBUG is defined
constexpr bool asserts() { constexpr bool asserts() {
......
...@@ -245,6 +245,22 @@ void Cfg::translate() { ...@@ -245,6 +245,22 @@ void Cfg::translate() {
} }
} }
void Cfg::fixPhiNodes() {
for (auto *Node : Nodes) {
// Fix all the phi edges since WASM can't tell how to make them correctly at
// the beginning.
assert(Node);
const auto &InEdges = Node->getInEdges();
for (auto &Instr : Node->getPhis()) {
auto *Phi = llvm::cast<InstPhi>(&Instr);
assert(Phi);
for (SizeT i = 0; i < InEdges.size(); ++i) {
Phi->setLabel(i, InEdges[i]);
}
}
}
}
void Cfg::computeInOutEdges() { void Cfg::computeInOutEdges() {
// Compute the out-edges. // Compute the out-edges.
for (CfgNode *Node : Nodes) for (CfgNode *Node : Nodes)
...@@ -402,7 +418,7 @@ void Cfg::reorderNodes() { ...@@ -402,7 +418,7 @@ void Cfg::reorderNodes() {
// in-edge if the predecessor node was contracted). If this changes in // in-edge if the predecessor node was contracted). If this changes in
// the future, rethink the strategy. // the future, rethink the strategy.
assert(Node->getInEdges().size() >= 1); assert(Node->getInEdges().size() >= 1);
assert(Node->getOutEdges().size() == 1); assert(Node->hasSingleOutEdge());
// If it's a (non-critical) edge where the successor has a single // If it's a (non-critical) edge where the successor has a single
// in-edge, then place it before the successor. // in-edge, then place it before the successor.
......
...@@ -256,6 +256,13 @@ public: ...@@ -256,6 +256,13 @@ public:
Allocator->Deallocate(Array); Allocator->Deallocate(Array);
} }
/// Update Phi labels with InEdges.
///
/// The WASM translator cannot always determine the right incoming edge for a
/// value due to the CFG being built incrementally. The fixPhiNodes pass fills
/// in the correct information once everything is known.
void fixPhiNodes();
private: private:
friend class CfgAllocatorTraits; // for Allocator access. friend class CfgAllocatorTraits; // for Allocator access.
......
...@@ -37,10 +37,20 @@ CfgNode::CfgNode(Cfg *Func, SizeT Number) : Func(Func), Number(Number) { ...@@ -37,10 +37,20 @@ CfgNode::CfgNode(Cfg *Func, SizeT Number) : Func(Func), Number(Number) {
// Adds an instruction to either the Phi list or the regular instruction list. // Adds an instruction to either the Phi list or the regular instruction list.
// Validates that all Phis are added before all regular instructions. // Validates that all Phis are added before all regular instructions.
void CfgNode::appendInst(Inst *Instr) { void CfgNode::appendInst(Inst *Instr, bool AllowPhisAnywhere) {
++InstCountEstimate; ++InstCountEstimate;
if (BuildDefs::wasm()) {
if (llvm::isa<InstSwitch>(Instr) || llvm::isa<InstBr>(Instr)) {
for (auto *N : Instr->getTerminatorEdges()) {
N->addInEdge(this);
addOutEdge(N);
}
}
}
if (auto *Phi = llvm::dyn_cast<InstPhi>(Instr)) { if (auto *Phi = llvm::dyn_cast<InstPhi>(Instr)) {
if (!Insts.empty()) { if (!AllowPhisAnywhere && !Insts.empty()) {
Func->setError("Phi instruction added to the middle of a block"); Func->setError("Phi instruction added to the middle of a block");
return; return;
} }
...@@ -81,6 +91,8 @@ void CfgNode::computePredecessors() { ...@@ -81,6 +91,8 @@ void CfgNode::computePredecessors() {
} }
void CfgNode::computeSuccessors() { void CfgNode::computeSuccessors() {
OutEdges.clear();
InEdges.clear();
OutEdges = Insts.rbegin()->getTerminatorEdges(); OutEdges = Insts.rbegin()->getTerminatorEdges();
} }
...@@ -889,7 +901,7 @@ void CfgNode::contractIfEmpty() { ...@@ -889,7 +901,7 @@ void CfgNode::contractIfEmpty() {
// Make sure there is actually a successor to repoint in-edges to. // Make sure there is actually a successor to repoint in-edges to.
if (OutEdges.empty()) if (OutEdges.empty())
return; return;
assert(OutEdges.size() == 1); assert(hasSingleOutEdge());
// Don't try to delete a self-loop. // Don't try to delete a self-loop.
if (OutEdges[0] == this) if (OutEdges[0] == this)
return; return;
......
...@@ -72,7 +72,7 @@ public: ...@@ -72,7 +72,7 @@ public:
/// @{ /// @{
InstList &getInsts() { return Insts; } InstList &getInsts() { return Insts; }
PhiList &getPhis() { return Phis; } PhiList &getPhis() { return Phis; }
void appendInst(Inst *Instr); void appendInst(Inst *Instr, bool AllowPhisAnywhere = false);
void renumberInstructions(); void renumberInstructions();
/// Rough and generally conservative estimate of the number of instructions in /// Rough and generally conservative estimate of the number of instructions in
/// the block. It is updated when an instruction is added, but not when /// the block. It is updated when an instruction is added, but not when
...@@ -110,6 +110,13 @@ public: ...@@ -110,6 +110,13 @@ public:
void profileExecutionCount(VariableDeclaration *Var); void profileExecutionCount(VariableDeclaration *Var);
void addOutEdge(CfgNode *Out) { OutEdges.push_back(Out); }
void addInEdge(CfgNode *In) { InEdges.push_back(In); }
bool hasSingleOutEdge() const {
return (getOutEdges().size() == 1 || getOutEdges()[0] == getOutEdges()[1]);
}
private: private:
CfgNode(Cfg *Func, SizeT Number); CfgNode(Cfg *Func, SizeT Number);
bool livenessValidateIntervals(Liveness *Liveness) const; bool livenessValidateIntervals(Liveness *Liveness) const;
......
...@@ -329,6 +329,7 @@ struct dev_list_flag {}; ...@@ -329,6 +329,7 @@ struct dev_list_flag {};
"Global initializers"), \ "Global initializers"), \
clEnumValN(Ice::IceV_ConstPoolStats, "cpool", \ clEnumValN(Ice::IceV_ConstPoolStats, "cpool", \
"Constant pool counters"), \ "Constant pool counters"), \
clEnumValN(Ice::IceV_Wasm, "wasm", "WebAssembly builder"), \
clEnumValN(Ice::IceV_All, "all", "Use all verbose options"), \ clEnumValN(Ice::IceV_All, "all", "Use all verbose options"), \
clEnumValN(Ice::IceV_Most, "most", \ clEnumValN(Ice::IceV_Most, "most", \
"Use all verbose options except 'regalloc,global_init'"), \ "Use all verbose options except 'regalloc,global_init'"), \
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "IceConverter.h" #include "IceConverter.h"
#include "IceELFObjectWriter.h" #include "IceELFObjectWriter.h"
#include "PNaClTranslator.h" #include "PNaClTranslator.h"
#include "WasmTranslator.h"
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma clang diagnostic push
...@@ -54,6 +55,11 @@ bool llvmIRInput(const std::string &Filename) { ...@@ -54,6 +55,11 @@ bool llvmIRInput(const std::string &Filename) {
std::regex_match(Filename, std::regex(".*\\.ll")); std::regex_match(Filename, std::regex(".*\\.ll"));
} }
bool wasmInput(const std::string &Filename) {
return BuildDefs::llvmIrAsInput() &&
std::regex_match(Filename, std::regex(".*\\.wasm"));
}
} // end of anonymous namespace } // end of anonymous namespace
void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
...@@ -75,13 +81,27 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, ...@@ -75,13 +81,27 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
std::unique_ptr<Translator> Translator; std::unique_ptr<Translator> Translator;
const std::string IRFilename = Flags.getIRFilename(); const std::string IRFilename = Flags.getIRFilename();
const bool BuildOnRead = Flags.getBuildOnRead() && !llvmIRInput(IRFilename); const bool BuildOnRead = Flags.getBuildOnRead() && !llvmIRInput(IRFilename) &&
!wasmInput(IRFilename);
const bool WasmBuildOnRead = Flags.getBuildOnRead() && wasmInput(IRFilename);
if (BuildOnRead) { if (BuildOnRead) {
std::unique_ptr<PNaClTranslator> PTranslator(new PNaClTranslator(&Ctx)); std::unique_ptr<PNaClTranslator> PTranslator(new PNaClTranslator(&Ctx));
std::unique_ptr<llvm::StreamingMemoryObject> MemObj( std::unique_ptr<llvm::StreamingMemoryObject> MemObj(
new llvm::StreamingMemoryObjectImpl(InputStream.release())); new llvm::StreamingMemoryObjectImpl(InputStream.release()));
PTranslator->translate(IRFilename, std::move(MemObj)); PTranslator->translate(IRFilename, std::move(MemObj));
Translator.reset(PTranslator.release()); Translator.reset(PTranslator.release());
} else if (WasmBuildOnRead) {
if (BuildDefs::wasm()) {
std::unique_ptr<WasmTranslator> WTranslator(new WasmTranslator(&Ctx));
WTranslator->translate(IRFilename, std::move(InputStream));
Translator.reset(WTranslator.release());
} else {
Ctx.getStrError() << "WASM support not enabled\n";
Ctx.getErrorStatus()->assign(EC_Args);
return;
}
} else if (BuildDefs::llvmIr()) { } else if (BuildDefs::llvmIr()) {
if (BuildDefs::browser()) { if (BuildDefs::browser()) {
Ctx.getStrError() Ctx.getStrError()
......
...@@ -341,6 +341,7 @@ enum VerboseItem { ...@@ -341,6 +341,7 @@ enum VerboseItem {
IceV_AvailableRegs = 1 << 21, IceV_AvailableRegs = 1 << 21,
IceV_GlobalInit = 1 << 22, IceV_GlobalInit = 1 << 22,
IceV_ConstPoolStats = 1 << 23, IceV_ConstPoolStats = 1 << 23,
IceV_Wasm = 1 << 24,
IceV_All = ~IceV_None, IceV_All = ~IceV_None,
IceV_Most = IceV_Most =
IceV_All & ~IceV_LinearScan & ~IceV_GlobalInit & ~IceV_ConstPoolStats IceV_All & ~IceV_LinearScan & ~IceV_GlobalInit & ~IceV_ConstPoolStats
......
...@@ -624,6 +624,7 @@ public: ...@@ -624,6 +624,7 @@ public:
Operand *getOperandForTarget(CfgNode *Target) const; Operand *getOperandForTarget(CfgNode *Target) const;
void clearOperandForTarget(CfgNode *Target); void clearOperandForTarget(CfgNode *Target);
CfgNode *getLabel(SizeT Index) const { return Labels[Index]; } CfgNode *getLabel(SizeT Index) const { return Labels[Index]; }
void setLabel(SizeT Index, CfgNode *Label) { Labels[Index] = Label; }
void livenessPhiOperand(LivenessBV &Live, CfgNode *Target, void livenessPhiOperand(LivenessBV &Live, CfgNode *Target,
Liveness *Liveness); Liveness *Liveness);
Inst *lower(Cfg *Func); Inst *lower(Cfg *Func);
......
...@@ -24,20 +24,6 @@ ...@@ -24,20 +24,6 @@
namespace Ice { namespace Ice {
class CfgOptWorkItem final : public OptWorkItem {
CfgOptWorkItem() = delete;
CfgOptWorkItem(const CfgOptWorkItem &) = delete;
CfgOptWorkItem &operator=(const CfgOptWorkItem &) = delete;
public:
CfgOptWorkItem(std::unique_ptr<Cfg> Func) : Func(std::move(Func)) {}
std::unique_ptr<Cfg> getParsedCfg() override { return std::move(Func); }
~CfgOptWorkItem() override = default;
private:
std::unique_ptr<Ice::Cfg> Func;
};
Translator::Translator(GlobalContext *Ctx) Translator::Translator(GlobalContext *Ctx)
: Ctx(Ctx), NextSequenceNumber(GlobalContext::getFirstSequenceNumber()), : Ctx(Ctx), NextSequenceNumber(GlobalContext::getFirstSequenceNumber()),
ErrorStatus() {} ErrorStatus() {}
......
...@@ -74,6 +74,20 @@ protected: ...@@ -74,6 +74,20 @@ protected:
ErrorCode ErrorStatus; ErrorCode ErrorStatus;
}; };
class CfgOptWorkItem final : public OptWorkItem {
CfgOptWorkItem() = delete;
CfgOptWorkItem(const CfgOptWorkItem &) = delete;
CfgOptWorkItem &operator=(const CfgOptWorkItem &) = delete;
public:
CfgOptWorkItem(std::unique_ptr<Cfg> Func) : Func(std::move(Func)) {}
std::unique_ptr<Cfg> getParsedCfg() override { return std::move(Func); }
~CfgOptWorkItem() override = default;
private:
std::unique_ptr<Ice::Cfg> Func;
};
} // end of namespace Ice } // end of namespace Ice
#endif // SUBZERO_SRC_ICETRANSLATOR_H #endif // SUBZERO_SRC_ICETRANSLATOR_H
//===- subzero/src/WasmTranslator.cpp - WASM to Subzero Translation -------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Defines a driver for translating Wasm bitcode into PNaCl bitcode.
///
/// The translator uses V8's WebAssembly decoder to handle the binary Wasm
/// format but replaces the usual TurboFan builder with a new PNaCl builder.
///
//===----------------------------------------------------------------------===//
#include "llvm/Support/StreamingMemoryObject.h"
#include "WasmTranslator.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/zone.h"
#include "IceCfgNode.h"
#include "IceGlobalInits.h"
using namespace std;
using namespace Ice;
using namespace v8;
using namespace v8::internal;
using namespace v8::internal::wasm;
using v8::internal::wasm::DecodeWasmModule;
#include "src/wasm/ast-decoder-impl.h"
#define LOG(Expr) log([&](Ostream & out) { Expr; })
namespace {
Ice::Type toIceType(v8::internal::MachineType) {
// TODO(eholk): actually convert this.
return IceType_i32;
}
Ice::Type toIceType(wasm::LocalType Type) {
switch (Type) {
default:
llvm::report_fatal_error("unexpected enum value");
case MachineRepresentation::kNone:
llvm::report_fatal_error("kNone type not supported");
case MachineRepresentation::kBit:
return IceType_i1;
case MachineRepresentation::kWord8:
return IceType_i8;
case MachineRepresentation::kWord16:
return IceType_i16;
case MachineRepresentation::kWord32:
return IceType_i32;
case MachineRepresentation::kWord64:
return IceType_i64;
case MachineRepresentation::kFloat32:
return IceType_f32;
case MachineRepresentation::kFloat64:
return IceType_f64;
case MachineRepresentation::kSimd128:
llvm::report_fatal_error("ambiguous SIMD type");
case MachineRepresentation::kTagged:
llvm::report_fatal_error("kTagged type not supported");
}
}
} // end of anonymous namespace
/// This class wraps either an Operand or a CfgNode.
///
/// Turbofan's sea of nodes representation only has nodes for values, control
/// flow, etc. In Subzero these concepts are all separate. This class lets V8's
/// Wasm decoder treat Subzero objects as though they are all the same.
class OperandNode {
static constexpr uintptr_t NODE_FLAG = 1;
static constexpr uintptr_t UNDEF_PTR = (uintptr_t)-1;
uintptr_t Data = UNDEF_PTR;
public:
OperandNode() = default;
explicit OperandNode(Operand *Operand)
: Data(reinterpret_cast<uintptr_t>(Operand)) {}
explicit OperandNode(CfgNode *Node)
: Data(reinterpret_cast<uintptr_t>(Node) | NODE_FLAG) {}
explicit OperandNode(nullptr_t) : Data(UNDEF_PTR) {}
operator Operand *() const {
if (UNDEF_PTR == Data) {
return nullptr;
}
if (!isOperand()) {
llvm::report_fatal_error("This OperandNode is not an Operand");
}
return reinterpret_cast<Operand *>(Data);
}
operator CfgNode *() const {
if (UNDEF_PTR == Data) {
return nullptr;
}
if (!isCfgNode()) {
llvm::report_fatal_error("This OperandNode is not a CfgNode");
}
return reinterpret_cast<CfgNode *>(Data & ~NODE_FLAG);
}
explicit operator bool() const { return (Data != UNDEF_PTR) && Data; }
bool operator==(const OperandNode &Rhs) const {
return (Data == Rhs.Data) ||
(UNDEF_PTR == Data && (Rhs.Data == 0 || Rhs.Data == NODE_FLAG)) ||
(UNDEF_PTR == Rhs.Data && (Data == 0 || Data == NODE_FLAG));
}
bool operator!=(const OperandNode &Rhs) const { return !(*this == Rhs); }
bool isOperand() const { return (Data != UNDEF_PTR) && !(Data & NODE_FLAG); }
bool isCfgNode() const { return (Data != UNDEF_PTR) && (Data & NODE_FLAG); }
Operand *toOperand() const { return static_cast<Operand *>(*this); }
CfgNode *toCfgNode() const { return static_cast<CfgNode *>(*this); }
};
Ostream &operator<<(Ostream &Out, const OperandNode &Op) {
if (Op.isOperand()) {
Out << "(Operand*)" << Op.toOperand();
} else if (Op.isCfgNode()) {
Out << "(CfgNode*)" << Op.toCfgNode();
} else {
Out << "nullptr";
}
return Out;
}
constexpr bool isComparison(wasm::WasmOpcode Opcode) {
switch (Opcode) {
case kExprI32Ne:
case kExprI64Ne:
case kExprI32Eq:
case kExprI64Eq:
case kExprI32LtS:
case kExprI64LtS:
case kExprI32LtU:
case kExprI64LtU:
case kExprI32GeS:
case kExprI64GeS:
case kExprI32GtS:
case kExprI64GtS:
case kExprI32GtU:
case kExprI64GtU:
return true;
default:
return false;
}
}
class IceBuilder {
using Node = OperandNode;
IceBuilder() = delete;
IceBuilder(const IceBuilder &) = delete;
IceBuilder &operator=(const IceBuilder &) = delete;
public:
explicit IceBuilder(class Cfg *Func)
: Func(Func), Ctx(Func->getContext()), ControlPtr(nullptr) {}
/// Allocates a buffer of Nodes for use by V8.
Node *Buffer(size_t Count) {
LOG(out << "Buffer(" << Count << ")\n");
return Func->allocateArrayOf<Node>(Count);
}
Node Error() { llvm::report_fatal_error("Error"); }
Node Start(unsigned Params) {
LOG(out << "Start(" << Params << ") = ");
auto *Entry = Func->makeNode();
Func->setEntryNode(Entry);
LOG(out << Node(Entry) << "\n");
return OperandNode(Entry);
}
Node Param(unsigned Index, wasm::LocalType Type) {
LOG(out << "Param(" << Index << ") = ");
auto *Arg = makeVariable(toIceType(Type));
assert(Index == NextArg);
Func->addArg(Arg);
++NextArg;
LOG(out << Node(Arg) << "\n");
return OperandNode(Arg);
}
Node Loop(CfgNode *Entry) {
auto *Loop = Func->makeNode();
LOG(out << "Loop(" << Entry << ") = " << Loop << "\n");
Entry->appendInst(InstBr::create(Func, Loop));
return OperandNode(Loop);
}
void Terminate(Node Effect, Node Control) {
// TODO(eholk): this is almost certainly wrong
LOG(out << "Terminate(" << Effect << ", " << Control << ")"
<< "\n");
}
Node Merge(unsigned Count, Node *Controls) {
LOG(out << "Merge(" << Count);
for (unsigned i = 0; i < Count; ++i) {
LOG(out << ", " << Controls[i]);
}
LOG(out << ") = ");
auto *MergedNode = Func->makeNode();
for (unsigned i = 0; i < Count; ++i) {
CfgNode *Control = Controls[i];
Control->appendInst(InstBr::create(Func, MergedNode));
}
LOG(out << (OperandNode)MergedNode << "\n");
return OperandNode(MergedNode);
}
Node Phi(wasm::LocalType Type, unsigned Count, Node *Vals, Node Control) {
LOG(out << "Phi(" << Count << ", " << Control);
for (int i = 0; i < Count; ++i) {
LOG(out << ", " << Vals[i]);
}
LOG(out << ") = ");
const auto &InEdges = Control.toCfgNode()->getInEdges();
assert(Count == InEdges.size());
assert(Count > 0);
auto *Dest = makeVariable(Vals[0].toOperand()->getType(), Control);
// Multiply by 10 in case more things get added later.
// TODO(eholk): find a better way besides multiplying by some arbitrary
// constant.
auto *Phi = InstPhi::create(Func, Count * 10, Dest);
for (int i = 0; i < Count; ++i) {
auto *Op = Vals[i].toOperand();
assert(Op);
Phi->addArgument(Op, InEdges[i]);
}
setDefiningInst(Dest, Phi);
Control.toCfgNode()->appendInst(Phi);
LOG(out << Node(Dest) << "\n");
return OperandNode(Dest);
}
Node EffectPhi(unsigned Count, Node *Effects, Node Control) {
// TODO(eholk): this function is almost certainly wrong.
LOG(out << "EffectPhi(" << Count << ", " << Control << "):\n");
for (unsigned i = 0; i < Count; ++i) {
LOG(out << " " << Effects[i] << "\n");
}
return OperandNode(nullptr);
}
Node Int32Constant(int32_t Value) {
LOG(out << "Int32Constant(" << Value << ") = ");
auto *Const = Ctx->getConstantInt32(Value);
assert(Const);
assert(Control());
LOG(out << Node(Const) << "\n");
return OperandNode(Const);
}
Node Int64Constant(int64_t Value) {
LOG(out << "Int64Constant(" << Value << ") = ");
auto *Const = Ctx->getConstantInt64(Value);
assert(Const);
LOG(out << Node(Const) << "\n");
return OperandNode(Const);
}
Node Float32Constant(float Value) {
LOG(out << "Float32Constant(" << Value << ") = ");
auto *Const = Ctx->getConstantFloat(Value);
assert(Const);
LOG(out << Node(Const) << "\n");
return OperandNode(Const);
}
Node Float64Constant(double Value) {
LOG(out << "Float64Constant(" << Value << ") = ");
auto *Const = Ctx->getConstantDouble(Value);
assert(Const);
LOG(out << Node(Const) << "\n");
return OperandNode(Const);
}
Node Binop(wasm::WasmOpcode Opcode, Node Left, Node Right) {
LOG(out << "Binop(" << WasmOpcodes::OpcodeName(Opcode) << ", " << Left
<< ", " << Right << ") = ");
auto *Dest = makeVariable(
isComparison(Opcode) ? IceType_i1 : Left.toOperand()->getType());
switch (Opcode) {
case kExprI32Add:
case kExprI64Add:
Control()->appendInst(
InstArithmetic::create(Func, InstArithmetic::Add, Dest, Left, Right));
break;
case kExprI32Sub:
case kExprI64Sub:
Control()->appendInst(
InstArithmetic::create(Func, InstArithmetic::Sub, Dest, Left, Right));
break;
case kExprI32Mul:
case kExprI64Mul:
Control()->appendInst(
InstArithmetic::create(Func, InstArithmetic::Mul, Dest, Left, Right));
break;
case kExprI32DivU:
case kExprI64DivU:
Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Udiv,
Dest, Left, Right));
break;
case kExprI32RemU:
case kExprI64RemU:
Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Urem,
Dest, Left, Right));
break;
case kExprI32Ior:
case kExprI64Ior:
Control()->appendInst(
InstArithmetic::create(Func, InstArithmetic::Or, Dest, Left, Right));
break;
case kExprI32Xor:
case kExprI64Xor:
Control()->appendInst(
InstArithmetic::create(Func, InstArithmetic::Xor, Dest, Left, Right));
break;
case kExprI32Shl:
case kExprI64Shl:
Control()->appendInst(
InstArithmetic::create(Func, InstArithmetic::Shl, Dest, Left, Right));
break;
case kExprI32ShrU:
case kExprI64ShrU:
case kExprI32ShrS:
case kExprI64ShrS:
Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Ashr,
Dest, Left, Right));
break;
case kExprI32And:
case kExprI64And:
Control()->appendInst(
InstArithmetic::create(Func, InstArithmetic::And, Dest, Left, Right));
break;
case kExprI32Ne:
case kExprI64Ne:
Control()->appendInst(
InstIcmp::create(Func, InstIcmp::Ne, Dest, Left, Right));
break;
case kExprI32Eq:
case kExprI64Eq:
Control()->appendInst(
InstIcmp::create(Func, InstIcmp::Eq, Dest, Left, Right));
break;
case kExprI32LtS:
case kExprI64LtS:
Control()->appendInst(
InstIcmp::create(Func, InstIcmp::Slt, Dest, Left, Right));
break;
case kExprI32LtU:
case kExprI64LtU:
Control()->appendInst(
InstIcmp::create(Func, InstIcmp::Ult, Dest, Left, Right));
break;
case kExprI32GeS:
case kExprI64GeS:
Control()->appendInst(
InstIcmp::create(Func, InstIcmp::Sge, Dest, Left, Right));
case kExprI32GtS:
case kExprI64GtS:
Control()->appendInst(
InstIcmp::create(Func, InstIcmp::Sgt, Dest, Left, Right));
break;
case kExprI32GtU:
case kExprI64GtU:
Control()->appendInst(
InstIcmp::create(Func, InstIcmp::Ugt, Dest, Left, Right));
break;
default:
LOG(out << "Unknown binop: " << WasmOpcodes::OpcodeName(Opcode) << "\n");
llvm::report_fatal_error("Uncovered or invalid binop.");
return OperandNode(nullptr);
}
LOG(out << Dest << "\n");
return OperandNode(Dest);
}
Node Unop(wasm::WasmOpcode Opcode, Node Input) {
LOG(out << "Unop(" << WasmOpcodes::OpcodeName(Opcode) << ", " << Input
<< ") = ");
Ice::Variable *Dest = nullptr;
switch (Opcode) {
case kExprF32Neg: {
Dest = makeVariable(IceType_f32);
Control()->appendInst(InstArithmetic::create(
Func, InstArithmetic::Fsub, Dest, Ctx->getConstantFloat(0), Input));
break;
}
case kExprF64Neg: {
Dest = makeVariable(IceType_f64);
Control()->appendInst(InstArithmetic::create(
Func, InstArithmetic::Fsub, Dest, Ctx->getConstantDouble(0), Input));
break;
}
case kExprI64UConvertI32:
Dest = makeVariable(IceType_i64);
Control()->appendInst(
InstCast::create(Func, InstCast::Zext, Dest, Input));
break;
default:
LOG(out << "Unknown unop: " << WasmOpcodes::OpcodeName(Opcode) << "\n");
llvm::report_fatal_error("Uncovered or invalid unop.");
return OperandNode(nullptr);
}
LOG(out << Dest << "\n");
return OperandNode(Dest);
}
unsigned InputCount(CfgNode *Node) const { return Node->getInEdges().size(); }
bool IsPhiWithMerge(Node Phi, Node Merge) const {
LOG(out << "IsPhiWithMerge(" << Phi << ", " << Merge << ")"
<< "\n");
if (Phi && Phi.isOperand()) {
LOG(out << " ...is operand"
<< "\n");
if (auto *Inst = getDefiningInst(Phi)) {
LOG(out << " ...has defining instruction"
<< "\n");
LOG(out << getDefNode(Phi) << "\n");
LOG(out << " ..." << (getDefNode(Phi) == Merge) << "\n");
return getDefNode(Phi) == Merge;
}
}
return false;
}
void AppendToMerge(CfgNode *Merge, CfgNode *From) const {
From->appendInst(InstBr::create(Func, Merge));
}
void AppendToPhi(Node Merge, Node Phi, Node From) {
LOG(out << "AppendToPhi(" << Merge << ", " << Phi << ", " << From << ")"
<< "\n");
auto *Inst = getDefiningInst(Phi);
Inst->addArgument(From, getDefNode(From));
}
//-----------------------------------------------------------------------
// Operations that read and/or write {control} and {effect}.
//-----------------------------------------------------------------------
Node Branch(Node Cond, Node *TrueNode, Node *FalseNode) {
// true_node and false_node appear to be out parameters.
LOG(out << "Branch(" << Cond << ", ");
// save control here because true_node appears to alias control.
auto *Ctrl = Control();
*TrueNode = OperandNode(Func->makeNode());
*FalseNode = OperandNode(Func->makeNode());
LOG(out << *TrueNode << ", " << *FalseNode << ")"
<< "\n");
Ctrl->appendInst(InstBr::create(Func, Cond, *TrueNode, *FalseNode));
return OperandNode(nullptr);
}
Node Switch(unsigned Count, Node Key) { llvm::report_fatal_error("Switch"); }
Node IfValue(int32_t Value, Node Sw) { llvm::report_fatal_error("IfValue"); }
Node IfDefault(Node Sw) { llvm::report_fatal_error("IfDefault"); }
Node Return(unsigned Count, Node *Vals) {
assert(1 >= Count);
LOG(out << "Return(");
if (Count > 0)
LOG(out << Vals[0]);
LOG(out << ")"
<< "\n");
auto *Instr =
1 == Count ? InstRet::create(Func, Vals[0]) : InstRet::create(Func);
Control()->appendInst(Instr);
Control()->setHasReturn();
LOG(out << Node(nullptr) << "\n");
return OperandNode(nullptr);
}
Node ReturnVoid() {
LOG(out << "ReturnVoid() = ");
auto *Instr = InstRet::create(Func);
Control()->appendInst(Instr);
Control()->setHasReturn();
LOG(out << Node(nullptr) << "\n");
return OperandNode(nullptr);
}
Node Unreachable() {
LOG(out << "Unreachable() = ");
auto *Instr = InstUnreachable::create(Func);
Control()->appendInst(Instr);
LOG(out << Node(nullptr) << "\n");
return OperandNode(nullptr);
}
Node CallDirect(uint32_t Index, Node *Args) {
LOG(out << "CallDirect(" << Index << ")"
<< "\n");
assert(Module->IsValidFunction(Index));
const auto *Module = this->Module->module;
assert(Module);
const auto &Target = Module->functions[Index];
const auto *Sig = Target.sig;
assert(Sig);
const auto NumArgs = Sig->parameter_count();
LOG(out << " number of args: " << NumArgs << "\n");
const auto TargetName =
Ctx->getGlobalString(Module->GetName(Target.name_offset));
LOG(out << " target name: " << TargetName << "\n");
assert(Sig->return_count() <= 1);
auto *TargetOperand = Ctx->getConstantSym(0, TargetName);
auto *Dest = Sig->return_count() > 0
? makeVariable(toIceType(Sig->GetReturn()))
: nullptr;
auto *Call = InstCall::create(Func, NumArgs, Dest, TargetOperand,
false /* HasTailCall */);
for (int i = 0; i < NumArgs; ++i) {
// The builder reserves the first argument for the code object.
LOG(out << " args[" << i << "] = " << Args[i + 1] << "\n");
Call->addArg(Args[i + 1]);
}
Control()->appendInst(Call);
LOG(out << "Call Result = " << Node(Dest) << "\n");
return OperandNode(Dest);
}
Node CallImport(uint32_t Index, Node *Args) {
LOG(out << "CallImport(" << Index << ")"
<< "\n");
const auto *Module = this->Module->module;
assert(Module);
const auto *Sig = this->Module->GetImportSignature(Index);
assert(Sig);
const auto NumArgs = Sig->parameter_count();
LOG(out << " number of args: " << NumArgs << "\n");
const auto &Target = Module->import_table[Index];
const auto TargetName =
Ctx->getGlobalString(Module->GetName(Target.function_name_offset));
LOG(out << " target name: " << TargetName << "\n");
assert(Sig->return_count() <= 1);
auto *TargetOperand = Ctx->getConstantSym(0, TargetName);
auto *Dest = Sig->return_count() > 0
? makeVariable(toIceType(Sig->GetReturn()))
: nullptr;
constexpr bool NoTailCall = false;
auto *Call =
InstCall::create(Func, NumArgs, Dest, TargetOperand, NoTailCall);
for (int i = 0; i < NumArgs; ++i) {
// The builder reserves the first argument for the code object.
LOG(out << " args[" << i << "] = " << Args[i + 1] << "\n");
Call->addArg(Args[i + 1]);
}
Control()->appendInst(Call);
LOG(out << "Call Result = " << Node(Dest) << "\n");
return OperandNode(Dest);
}
Node CallIndirect(uint32_t Index, Node *Args) {
llvm::report_fatal_error("CallIndirect");
}
Node Invert(Node Node) { llvm::report_fatal_error("Invert"); }
Node FunctionTable() { llvm::report_fatal_error("FunctionTable"); }
//-----------------------------------------------------------------------
// Operations that concern the linear memory.
//-----------------------------------------------------------------------
Node MemSize(uint32_t Offset) { llvm::report_fatal_error("MemSize"); }
Node LoadGlobal(uint32_t Index) { llvm::report_fatal_error("LoadGlobal"); }
Node StoreGlobal(uint32_t Index, Node Val) {
llvm::report_fatal_error("StoreGlobal");
}
Node LoadMem(wasm::LocalType Type, MachineType MemType, Node Index,
uint32_t Offset) {
LOG(out << "LoadMem(" << Index << "[" << Offset << "]) = ");
// first, add the index and the offset together.
auto *OffsetConstant = Ctx->getConstantInt32(Offset);
auto *Addr = makeVariable(IceType_i32);
Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add,
Addr, Index, OffsetConstant));
// then load the memory
auto *LoadResult = makeVariable(toIceType(MemType));
Control()->appendInst(InstLoad::create(Func, LoadResult, Addr));
// and cast, if needed
Ice::Variable *Result = nullptr;
if (toIceType(Type) != toIceType(MemType)) {
Result = makeVariable(toIceType(Type));
// TODO(eholk): handle signs correctly.
Control()->appendInst(
InstCast::create(Func, InstCast::Sext, Result, LoadResult));
} else {
Result = LoadResult;
}
LOG(out << Result << "\n");
return OperandNode(Result);
}
void StoreMem(MachineType Type, Node Index, uint32_t Offset, Node Val) {
LOG(out << "StoreMem(" << Index << "[" << Offset << "] = " << Val << ")"
<< "\n");
// TODO(eholk): surely there is a better way to do this.
// first, add the index and the offset together.
auto *OffsetConstant = Ctx->getConstantInt32(Offset);
auto *Addr = makeVariable(IceType_i32);
Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add,
Addr, Index, OffsetConstant));
// cast the value to the right type, if needed
Operand *StoreVal = nullptr;
if (toIceType(Type) != Val.toOperand()->getType()) {
auto *LocalStoreVal = makeVariable(toIceType(Type));
Control()->appendInst(
InstCast::create(Func, InstCast::Trunc, LocalStoreVal, Val));
StoreVal = LocalStoreVal;
} else {
StoreVal = Val;
}
// then store the memory
Control()->appendInst(InstStore::create(Func, StoreVal, Addr));
}
static void PrintDebugName(Node node) {
llvm::report_fatal_error("PrintDebugName");
}
CfgNode *Control() {
return ControlPtr ? ControlPtr->toCfgNode() : Func->getEntryNode();
}
Node Effect() { return *EffectPtr; }
void set_module(wasm::ModuleEnv *Module) { this->Module = Module; }
void set_control_ptr(Node *Control) { this->ControlPtr = Control; }
void set_effect_ptr(Node *Effect) { this->EffectPtr = Effect; }
private:
wasm::ModuleEnv *Module;
Node *ControlPtr;
Node *EffectPtr;
class Cfg *Func;
GlobalContext *Ctx;
SizeT NextArg = 0;
CfgUnorderedMap<Operand *, InstPhi *> PhiMap;
CfgUnorderedMap<Operand *, CfgNode *> DefNodeMap;
InstPhi *getDefiningInst(Operand *Op) const {
const auto &Iter = PhiMap.find(Op);
if (Iter == PhiMap.end()) {
return nullptr;
}
return Iter->second;
}
void setDefiningInst(Operand *Op, InstPhi *Phi) {
LOG(out << "\n== setDefiningInst(" << Op << ", " << Phi << ") ==\n");
PhiMap.emplace(Op, Phi);
}
Ice::Variable *makeVariable(Ice::Type Type) {
return makeVariable(Type, Control());
}
Ice::Variable *makeVariable(Ice::Type Type, CfgNode *DefNode) {
auto *Var = Func->makeVariable(Type);
DefNodeMap.emplace(Var, DefNode);
return Var;
}
CfgNode *getDefNode(Operand *Op) const {
const auto &Iter = DefNodeMap.find(Op);
if (Iter == DefNodeMap.end()) {
return nullptr;
}
return Iter->second;
}
template <typename F = std::function<void(Ostream &)>> void log(F Fn) const {
if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_Wasm)) {
Fn(Ctx->getStrDump());
Ctx->getStrDump().flush();
}
}
};
std::string fnNameFromId(uint32_t Id) {
return std::string("fn") + to_string(Id);
}
std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone,
FunctionEnv *Env,
const byte *Base,
const byte *Start,
const byte *End) {
OstreamLocker L1(Ctx);
auto Func = Cfg::create(Ctx, getNextSequenceNumber());
Ice::CfgLocalAllocatorScope L2(Func.get());
// TODO: parse the function signature...
IceBuilder Builder(Func.get());
LR_WasmDecoder<OperandNode, IceBuilder> Decoder(Zone, &Builder);
LOG(out << getFlags().getDefaultGlobalPrefix() << "\n");
Decoder.Decode(Env, Base, Start, End);
// We don't always know where the incoming branches are in phi nodes, so this
// function finds them.
Func->fixPhiNodes();
return Func;
}
WasmTranslator::WasmTranslator(GlobalContext *Ctx)
: Translator(Ctx), BufferSize(24 << 10), Buffer(new uint8_t[24 << 10]) {
// TODO(eholk): compute the correct buffer size. This uses 24k by default,
// which has been big enough for testing but is not a general solution.
}
void WasmTranslator::translate(
const std::string &IRFilename,
std::unique_ptr<llvm::DataStreamer> InputStream) {
LOG(out << "Initializing v8/wasm stuff..."
<< "\n");
Zone Zone;
ZoneScope _(&Zone);
SizeT BytesRead = InputStream->GetBytes(Buffer.get(), BufferSize);
LOG(out << "Read " << BytesRead << " bytes"
<< "\n");
LOG(out << "Decoding module " << IRFilename << "\n");
constexpr v8::internal::Isolate *NoIsolate = nullptr;
auto Result = DecodeWasmModule(NoIsolate, &Zone, Buffer.get(),
Buffer.get() + BytesRead, false, kWasmOrigin);
auto Module = Result.val;
LOG(out << "Module info:"
<< "\n");
LOG(out << " number of globals: " << Module->globals.size() << "\n");
LOG(out << " number of signatures: " << Module->signatures.size()
<< "\n");
LOG(out << " number of functions: " << Module->functions.size() << "\n");
LOG(out << " number of data_segments: " << Module->data_segments.size()
<< "\n");
LOG(out << " function table size: " << Module->function_table.size()
<< "\n");
ModuleEnv ModuleEnv;
ModuleEnv.module = Module;
LOG(out << "\n"
<< "Function information:"
<< "\n");
for (const auto F : Module->functions) {
LOG(out << " " << F.name_offset << ": " << Module->GetName(F.name_offset));
if (F.exported)
LOG(out << " export");
if (F.external)
LOG(out << " extern");
LOG(out << "\n");
}
FunctionEnv Fenv;
Fenv.module = &ModuleEnv;
LOG(out << "Translating " << IRFilename << "\n");
// Translate each function.
uint32_t Id = 0;
for (const auto Fn : Module->functions) {
std::string NewName = fnNameFromId(Id++);
LOG(out << " " << Fn.name_offset << ": " << Module->GetName(Fn.name_offset)
<< " -> " << NewName << "...");
Fenv.sig = Fn.sig;
Fenv.local_i32_count = Fn.local_i32_count;
Fenv.local_i64_count = Fn.local_i64_count;
Fenv.local_f32_count = Fn.local_f32_count;
Fenv.local_f64_count = Fn.local_f64_count;
Fenv.SumLocals();
auto Func = translateFunction(&Zone, &Fenv, Buffer.get(),
Buffer.get() + Fn.code_start_offset,
Buffer.get() + Fn.code_end_offset);
Func->setFunctionName(Ctx->getGlobalString(NewName));
Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func)));
LOG(out << "done.\n");
}
return;
}
//===- subzero/src/WasmTranslator.h - WASM to Subzero Translation ---------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Declares a driver for translating Wasm bitcode into PNaCl bitcode.
///
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_WASMTRANSLATOR_H
#define SUBZERO_SRC_WASMTRANSLATOR_H
#include "IceGlobalContext.h"
#include "IceTranslator.h"
namespace v8 {
namespace internal {
class Zone;
namespace wasm {
class FunctionEnv;
} // end of namespace wasm
} // end of namespace internal
} // end of namespace v8
namespace Ice {
class WasmTranslator : public Translator {
WasmTranslator() = delete;
WasmTranslator(const WasmTranslator &) = delete;
WasmTranslator &operator=(const WasmTranslator &) = delete;
template <typename F = std::function<void(Ostream &)>> void log(F Fn) {
if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_Wasm)) {
Fn(Ctx->getStrDump());
Ctx->getStrDump().flush();
}
}
public:
explicit WasmTranslator(GlobalContext *Ctx);
void translate(const std::string &IRFilename,
std::unique_ptr<llvm::DataStreamer> InputStream);
/// Translates a single Wasm function.
///
/// Parameters:
/// Zone - an arena for the V8 code to allocate from.
/// Env - information about the function (signature, variable count, etc.).
/// Base - a pointer to the start of the Wasm module.
/// Start - a pointer to the start of the function within the module.
/// End - a pointer to the end of the function.
std::unique_ptr<Cfg> translateFunction(v8::internal::Zone *Zone,
v8::internal::wasm::FunctionEnv *Env,
const uint8_t *Base,
const uint8_t *Start,
const uint8_t *End);
private:
std::unique_ptr<uint8_t[]> Buffer;
SizeT BufferSize;
};
}
#endif // SUBZERO_SRC_WASMTRANSLATOR_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment