Commit 87def2c8 by Eric Holk

Subzero, Wasm: Dynamically reallocate read buffer. Runtime improvements.

This change fills in several more runtime functions needed by several benchmarks, as well as changing the buffer handling in the WASM decoder. Now the decoder will resize the buffer as needed to accomodate large .wasm modules. Tracing can now be enabled on runtime functions to aid with debugging. Additionally, runtime failures such as bounds check failures or invalid indirect function calls tell what kind of failure occured. BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4369 R=jpp@chromium.org, stichnot@chromium.org Review URL: https://codereview.chromium.org/1918213003 .
parent de29f120
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
./wasm-install/bin/emscripten/emcc "$1" -s BINARYEN=1 \ ./wasm-install/bin/emscripten/emcc "$1" -s BINARYEN=1 \
-s 'BINARYEN_METHOD="native-wasm"' \ -s 'BINARYEN_METHOD="native-wasm"' \
--em-config wasm-install/emscripten_config_vanilla && \ --em-config wasm-install/emscripten_config_vanilla -O2 && \
./wasm-install/bin/sexpr-wasm a.out.wast -o a.out.wasm && \ ./wasm-install/bin/sexpr-wasm a.out.wast -o a.out.wasm && \
./pnacl-sz a.out.wasm -o a.out.o -filetype=obj -O2 && \ ./pnacl-sz a.out.wasm -o a.out.o -filetype=obj -O2 && \
clang -m32 a.out.o ./runtime/szrt.c \ clang -m32 a.out.o ./runtime/szrt.c \
./runtime/wasm-runtime.cpp -lm -g ./runtime/wasm-runtime.cpp -lm -g -lstdc++
...@@ -163,7 +163,7 @@ def run_test(test_file, verbose=False): ...@@ -163,7 +163,7 @@ def run_test(test_file, verbose=False):
# Try to link and run the program. # Try to link and run the program.
cmd = "clang -g -m32 {} -o {} " + \ cmd = "clang -g -m32 {} -o {} " + \
"./runtime/szrt.c ./runtime/wasm-runtime.cpp -lm" "./runtime/szrt.c ./runtime/wasm-runtime.cpp -lm -lstdc++"
cmd = cmd.format(obj_file, exe_file) cmd = cmd.format(obj_file, exe_file)
if not run_test or os.system(cmd) == 0: if not run_test or os.system(cmd) == 0:
......
...@@ -56,7 +56,7 @@ bool llvmIRInput(const std::string &Filename) { ...@@ -56,7 +56,7 @@ bool llvmIRInput(const std::string &Filename) {
} }
bool wasmInput(const std::string &Filename) { bool wasmInput(const std::string &Filename) {
return BuildDefs::llvmIrAsInput() && return BuildDefs::wasm() &&
std::regex_match(Filename, std::regex(".*\\.wasm")); std::regex_match(Filename, std::regex(".*\\.wasm"));
} }
......
...@@ -680,6 +680,7 @@ public: ...@@ -680,6 +680,7 @@ public:
<< ") = "); << ") = ");
Ice::Variable *Dest = nullptr; Ice::Variable *Dest = nullptr;
switch (Opcode) { switch (Opcode) {
// TODO (eholk): merge these next two cases using getConstantInteger
case kExprI32Eqz: { case kExprI32Eqz: {
Dest = makeVariable(IceType_i32); Dest = makeVariable(IceType_i32);
auto *Tmp = makeVariable(IceType_i1); auto *Tmp = makeVariable(IceType_i1);
...@@ -772,6 +773,34 @@ public: ...@@ -772,6 +773,34 @@ public:
Control()->appendInst(Call); Control()->appendInst(Call);
break; break;
} }
case kExprF32Sqrt: {
Dest = makeVariable(IceType_f32);
const auto FnName = Ctx->getGlobalString("llvm.sqrt.f32");
bool BadInstrinsic = false;
const auto *Info = Ctx->getIntrinsicsInfo().find(FnName, BadInstrinsic);
assert(!BadInstrinsic);
assert(Info);
auto *Call = InstIntrinsicCall::create(
Func, 1, Dest, Ctx->getConstantExternSym(FnName), Info->Info);
Call->addArg(Input);
Control()->appendInst(Call);
break;
}
case kExprF64Sqrt: {
Dest = makeVariable(IceType_f64);
const auto FnName = Ctx->getGlobalString("llvm.sqrt.f64");
bool BadInstrinsic = false;
const auto *Info = Ctx->getIntrinsicsInfo().find(FnName, BadInstrinsic);
assert(!BadInstrinsic);
assert(Info);
auto *Call = InstIntrinsicCall::create(
Func, 1, Dest, Ctx->getConstantExternSym(FnName), Info->Info);
Call->addArg(Input);
Control()->appendInst(Call);
break;
}
case kExprI64UConvertI32: case kExprI64UConvertI32:
Dest = makeVariable(IceType_i64); Dest = makeVariable(IceType_i64);
Control()->appendInst( Control()->appendInst(
...@@ -1065,7 +1094,7 @@ public: ...@@ -1065,7 +1094,7 @@ public:
assert(Module); assert(Module);
const auto &IndirectTable = Module->function_table; const auto &IndirectTable = Module->function_table;
auto *Abort = getAbortTarget(); auto *Abort = getIndirectFailTarget();
assert(Args[0].toOperand()); assert(Args[0].toOperand());
...@@ -1173,7 +1202,7 @@ public: ...@@ -1173,7 +1202,7 @@ public:
// terrible (see https://goo.gl/Zj7DTr). Try adding a new instruction that // terrible (see https://goo.gl/Zj7DTr). Try adding a new instruction that
// encapsulates this "abort if false" pattern. // encapsulates this "abort if false" pattern.
auto *CheckPassed = Func->makeNode(); auto *CheckPassed = Func->makeNode();
auto *CheckFailed = getAbortTarget(); auto *CheckFailed = getBoundsFailTarget();
auto *Check = makeVariable(IceType_i1); auto *Check = makeVariable(IceType_i1);
Control()->appendInst(InstIcmp::create(Func, InstIcmp::Ult, Check, Base, Control()->appendInst(InstIcmp::create(Func, InstIcmp::Ult, Check, Base,
...@@ -1281,7 +1310,8 @@ private: ...@@ -1281,7 +1310,8 @@ private:
class Cfg *Func; class Cfg *Func;
GlobalContext *Ctx; GlobalContext *Ctx;
CfgNode *AbortTarget = nullptr; CfgNode *BoundsFailTarget = nullptr;
CfgNode *IndirectFailTarget = nullptr;
SizeT NextArg = 0; SizeT NextArg = 0;
...@@ -1319,16 +1349,33 @@ private: ...@@ -1319,16 +1349,33 @@ private:
return Iter->second; return Iter->second;
} }
CfgNode *getAbortTarget() { CfgNode *getBoundsFailTarget() {
if (!AbortTarget) { if (!BoundsFailTarget) {
// TODO (eholk): Move this node to the end of the CFG, or even better, // TODO (eholk): Move this node to the end of the CFG, or even better,
// have only one abort block for the whole module. // have only one abort block for the whole module.
AbortTarget = Func->makeNode(); BoundsFailTarget = Func->makeNode();
// TODO (eholk): This should probably actually call abort instead. BoundsFailTarget->appendInst(InstCall::create(
AbortTarget->appendInst(InstUnreachable::create(Func)); Func, 0, nullptr,
Ctx->getConstantExternSym(Ctx->getGlobalString("__Sz_bounds_fail")),
false));
BoundsFailTarget->appendInst(InstUnreachable::create(Func));
} }
return AbortTarget; return BoundsFailTarget;
}
CfgNode *getIndirectFailTarget() {
if (!IndirectFailTarget) {
// TODO (eholk): Move this node to the end of the CFG, or even better,
// have only one abort block for the whole module.
IndirectFailTarget = Func->makeNode();
IndirectFailTarget->appendInst(InstCall::create(
Func, 0, nullptr,
Ctx->getConstantExternSym(Ctx->getGlobalString("__Sz_indirect_fail")),
false));
IndirectFailTarget->appendInst(InstUnreachable::create(Func));
}
return IndirectFailTarget;
} }
template <typename F = std::function<void(Ostream &)>> void log(F Fn) const { template <typename F = std::function<void(Ostream &)>> void log(F Fn) const {
...@@ -1364,13 +1411,10 @@ std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone, ...@@ -1364,13 +1411,10 @@ std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone,
return Func; return Func;
} }
// TODO(eholk): compute the correct buffer size. This uses 256k by default, constexpr SizeT InitialBufferSize = 16 << 10; // 16KB
// which has been big enough for testing but is not a general solution.
constexpr SizeT BufferSize = 256 << 10;
WasmTranslator::WasmTranslator(GlobalContext *Ctx) WasmTranslator::WasmTranslator(GlobalContext *Ctx)
: Translator(Ctx), Buffer(new uint8_t[ ::BufferSize]), : Translator(Ctx), Buffer(InitialBufferSize) {}
BufferSize(::BufferSize) {}
void WasmTranslator::translate( void WasmTranslator::translate(
const std::string &IRFilename, const std::string &IRFilename,
...@@ -1380,16 +1424,22 @@ void WasmTranslator::translate( ...@@ -1380,16 +1424,22 @@ void WasmTranslator::translate(
Zone Zone; Zone Zone;
ZoneScope _(&Zone); ZoneScope _(&Zone);
SizeT BytesRead = InputStream->GetBytes(Buffer.get(), BufferSize); SizeT BytesRead = 0;
while (true) {
BytesRead +=
InputStream->GetBytes(&Buffer[BytesRead], Buffer.size() - BytesRead);
LOG(out << "Read " << BytesRead << " bytes" LOG(out << "Read " << BytesRead << " bytes"
<< "\n"); << "\n");
assert(BytesRead < BufferSize); if (BytesRead < Buffer.size())
break;
Buffer.resize(Buffer.size() * 2);
}
LOG(out << "Decoding module " << IRFilename << "\n"); LOG(out << "Decoding module " << IRFilename << "\n");
constexpr v8::internal::Isolate *NoIsolate = nullptr; constexpr v8::internal::Isolate *NoIsolate = nullptr;
auto Result = DecodeWasmModule(NoIsolate, &Zone, Buffer.get(), auto Result = DecodeWasmModule(NoIsolate, &Zone, Buffer.data(),
Buffer.get() + BytesRead, false, kWasmOrigin); Buffer.data() + BytesRead, false, kWasmOrigin);
auto Module = Result.val; auto Module = Result.val;
...@@ -1540,9 +1590,9 @@ void WasmTranslator::translate( ...@@ -1540,9 +1590,9 @@ void WasmTranslator::translate(
LOG(out << " " << Fn.func_index << ": " << FnName << "..."); LOG(out << " " << Fn.func_index << ": " << FnName << "...");
Body.sig = Fn.sig; Body.sig = Fn.sig;
Body.base = Buffer.get(); Body.base = Buffer.data();
Body.start = Buffer.get() + Fn.code_start_offset; Body.start = Buffer.data() + Fn.code_start_offset;
Body.end = Buffer.get() + Fn.code_end_offset; Body.end = Buffer.data() + Fn.code_end_offset;
auto Func = translateFunction(&Zone, Body); auto Func = translateFunction(&Zone, Body);
Func->setFunctionName(Ctx->getGlobalString(FnName)); Func->setFunctionName(Ctx->getGlobalString(FnName));
......
...@@ -70,8 +70,7 @@ public: ...@@ -70,8 +70,7 @@ public:
v8::internal::wasm::FunctionBody &Body); v8::internal::wasm::FunctionBody &Body);
private: private:
std::unique_ptr<uint8_t[]> Buffer; std::vector<uint8_t> Buffer;
SizeT BufferSize;
}; };
} }
......
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