Commit 3412af0b authored by titzer's avatar titzer Committed by Commit bot

[wasm] Refactor encoder.h to use a proper buffer and remove OldFunctions section.

This removes the last use of the old_functions section, which greatly
simplifies encoding.

R=bradnelson@chromium.org,aseemgarg@chromium.org,mtrofin@chromium.org

BUG=

Review-Url: https://codereview.chromium.org/2014533003
Cr-Commit-Position: refs/heads/master@{#36523}
parent 4a4f2537
......@@ -1759,12 +1759,14 @@ AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
// TODO(aseemgarg): probably should take zone (to write wasm to) as input so
// that zone in constructor may be thrown away once wasm module is written.
WasmModuleIndex* AsmWasmBuilder::Run(i::Handle<i::FixedArray>* foreign_args) {
ZoneBuffer* AsmWasmBuilder::Run(i::Handle<i::FixedArray>* foreign_args) {
AsmWasmBuilderImpl impl(isolate_, zone_, literal_, typer_);
impl.Compile();
*foreign_args = impl.GetForeignArgs();
ZoneBuffer* buffer = new (zone_) ZoneBuffer(zone_);
WasmModuleWriter* writer = impl.builder_->Build(zone_);
return writer->WriteTo(zone_);
writer->WriteTo(*buffer);
return buffer;
}
} // namespace wasm
} // namespace internal
......
......@@ -22,7 +22,7 @@ class AsmWasmBuilder {
public:
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
AsmTyper* typer);
WasmModuleIndex* Run(Handle<FixedArray>* foreign_args);
ZoneBuffer* Run(Handle<FixedArray>* foreign_args);
private:
Isolate* isolate_;
......
This diff is collapsed.
......@@ -10,6 +10,7 @@
#include "src/base/smart-pointers.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
......@@ -19,14 +20,104 @@ namespace v8 {
namespace internal {
namespace wasm {
class ZoneBuffer : public ZoneObject {
public:
static const uint32_t kInitialSize = 4096;
explicit ZoneBuffer(Zone* zone, size_t initial = kInitialSize)
: zone_(zone), buffer_(reinterpret_cast<byte*>(zone->New(initial))) {
pos_ = buffer_;
end_ = buffer_ + initial;
}
void write_u8(uint8_t x) {
EnsureSpace(1);
*(pos_++) = x;
}
void write_u16(uint16_t x) {
EnsureSpace(2);
WriteUnalignedUInt16(pos_, x);
pos_ += 2;
}
void write_u32(uint32_t x) {
EnsureSpace(4);
WriteUnalignedUInt32(pos_, x);
pos_ += 4;
}
void write_u32v(uint32_t val) {
EnsureSpace(kMaxVarInt32Size);
LEBHelper::write_u32v(&pos_, val);
}
void write_size(size_t val) {
EnsureSpace(kMaxVarInt32Size);
DCHECK_EQ(val, static_cast<uint32_t>(val));
LEBHelper::write_u32v(&pos_, static_cast<uint32_t>(val));
}
void write(const byte* data, size_t size) {
EnsureSpace(size);
memcpy(pos_, data, size);
pos_ += size;
}
size_t reserve_u32v() {
size_t off = offset();
EnsureSpace(kMaxVarInt32Size);
pos_ += kMaxVarInt32Size;
return off;
}
// Patch a (padded) u32v at the given offset to be the given value.
void patch_u32v(size_t offset, uint32_t val) {
byte* ptr = buffer_ + offset;
for (size_t pos = 0; pos != kPaddedVarInt32Size; ++pos) {
uint32_t next = val >> 7;
byte out = static_cast<byte>(val & 0x7f);
if (pos != kPaddedVarInt32Size - 1) {
*(ptr++) = 0x80 | out;
val = next;
} else {
*(ptr++) = out;
}
}
}
size_t offset() { return static_cast<size_t>(pos_ - buffer_); }
size_t size() { return static_cast<size_t>(pos_ - buffer_); }
const byte* begin() { return buffer_; }
const byte* end() { return pos_; }
void EnsureSpace(size_t size) {
if ((pos_ + size) > end_) {
size_t new_size = 4096 + (end_ - buffer_) * 3;
byte* new_buffer = reinterpret_cast<byte*>(zone_->New(new_size));
memcpy(new_buffer, buffer_, (pos_ - buffer_));
pos_ = new_buffer + (pos_ - buffer_);
buffer_ = new_buffer;
end_ = new_buffer + new_size;
}
}
byte** pos_ptr() { return &pos_; }
private:
Zone* zone_;
byte* buffer_;
byte* pos_;
byte* end_;
};
class WasmModuleBuilder;
class WasmFunctionEncoder : public ZoneObject {
public:
uint32_t HeaderSize() const;
uint32_t BodySize() const;
uint32_t NameSize() const;
void Serialize(byte* buffer, byte** header, byte** body) const;
void WriteSignature(ZoneBuffer& buffer) const;
void WriteExport(ZoneBuffer& buffer, uint32_t func_index) const;
void WriteBody(ZoneBuffer& buffer) const;
bool exported() const { return exported_; }
private:
WasmFunctionEncoder(Zone* zone, LocalDeclEncoder locals, bool exported);
......@@ -71,28 +162,13 @@ class WasmDataSegmentEncoder : public ZoneObject {
public:
WasmDataSegmentEncoder(Zone* zone, const byte* data, uint32_t size,
uint32_t dest);
uint32_t HeaderSize() const;
uint32_t BodySize() const;
void Serialize(byte* buffer, byte** header, byte** body) const;
void Write(ZoneBuffer& buffer) const;
private:
ZoneVector<byte> data_;
uint32_t dest_;
};
class WasmModuleIndex : public ZoneObject {
public:
const byte* Begin() const { return begin_; }
const byte* End() const { return end_; }
private:
friend class WasmModuleWriter;
WasmModuleIndex(const byte* begin, const byte* end)
: begin_(begin), end_(end) {}
const byte* begin_;
const byte* end_;
};
struct WasmFunctionImport {
uint32_t sig_index;
const char* name;
......@@ -101,7 +177,7 @@ struct WasmFunctionImport {
class WasmModuleWriter : public ZoneObject {
public:
WasmModuleIndex* WriteTo(Zone* zone) const;
void WriteTo(ZoneBuffer& buffer) const;
private:
friend class WasmModuleBuilder;
......
......@@ -9,6 +9,9 @@ namespace v8 {
namespace internal {
namespace wasm {
static const size_t kPaddedVarInt32Size = 5;
static const size_t kMaxVarInt32Size = 5;
class LEBHelper {
public:
// Write a 32-bit unsigned LEB to {dest}, updating {dest} to point after
......
......@@ -118,6 +118,10 @@ class ModuleDecoder : public Decoder {
break;
}
TRACE(" +%d section name : \"%.*s\"\n",
static_cast<int>(section_name_start - start_),
string_length < 20 ? string_length : 20, section_name_start);
WasmSection::Code section =
WasmSection::lookup(section_name_start, string_length);
......@@ -171,8 +175,7 @@ class ModuleDecoder : public Decoder {
0, // name_offset
0, // name_length
0, // code_start_offset
0, // code_end_offset
false}); // exported
0}); // code_end_offset
WasmFunction* function = &module->functions.back();
function->sig_index = consume_sig_index(module, &function->sig);
}
......@@ -204,44 +207,6 @@ class ModuleDecoder : public Decoder {
}
break;
}
case WasmSection::Code::OldFunctions: {
int length;
uint32_t functions_count = consume_u32v(&length, "functions count");
module->functions.reserve(SafeReserve(functions_count));
// Set up module environment for verification.
ModuleEnv menv;
menv.module = module;
menv.instance = nullptr;
menv.origin = origin_;
// Decode functions.
for (uint32_t i = 0; i < functions_count; i++) {
if (failed()) break;
TRACE("DecodeFunction[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
module->functions.push_back({nullptr, // sig
i, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0, // code_end_offset
false}); // exported
WasmFunction* function = &module->functions.back();
DecodeFunctionInModule(module, function, false);
}
if (ok() && verify_functions) {
for (uint32_t i = 0; i < functions_count; i++) {
if (failed()) break;
WasmFunction* function = &module->functions[i];
VerifyFunctionBody(i, &menv, function);
if (result_.failed()) {
error(result_.error_pc, result_.error_msg.get());
}
}
}
break;
}
case WasmSection::Code::Names: {
int length;
const byte* pos = pc_;
......@@ -464,7 +429,6 @@ class ModuleDecoder : public Decoder {
function->name_length = 0; // ---- name length
function->code_start_offset = off(pc_); // ---- code start
function->code_end_offset = off(limit_); // ---- code end
function->exported = false; // ---- exported
if (ok()) VerifyFunctionBody(0, module_env, function);
......@@ -498,46 +462,6 @@ class ModuleDecoder : public Decoder {
global->exported = consume_u8("exported") != 0;
}
// Decodes a single function entry inside a module starting at {pc_}.
// TODO(titzer): legacy function body; remove
void DecodeFunctionInModule(WasmModule* module, WasmFunction* function,
bool verify_body = true) {
byte decl_bits = consume_u8("function decl");
const byte* sigpos = pc_;
function->sig_index = consume_u16("signature index");
if (function->sig_index >= module->signatures.size()) {
return error(sigpos, "invalid signature index");
} else {
function->sig = module->signatures[function->sig_index];
}
TRACE(" +%d <function attributes:%s%s>\n", static_cast<int>(pc_ - start_),
decl_bits & kDeclFunctionName ? " name" : "",
decl_bits & kDeclFunctionExport ? " exported" : "");
function->exported = decl_bits & kDeclFunctionExport;
if (decl_bits & kDeclFunctionName) {
function->name_offset =
consume_string(&function->name_length, function->exported);
}
uint16_t size = consume_u16("body size");
if (ok()) {
if ((pc_ + size) > limit_) {
return error(pc_, limit_,
"expected %d bytes for function body, fell off end", size);
}
function->code_start_offset = static_cast<uint32_t>(pc_ - start_);
function->code_end_offset = function->code_start_offset + size;
TRACE(" +%d %-20s: (%d bytes)\n", static_cast<int>(pc_ - start_),
"function body", size);
pc_ += size;
}
}
bool IsWithinLimit(uint32_t limit, uint32_t offset, uint32_t size) {
if (offset > limit) return false;
if ((offset + size) < offset) return false; // overflow
......
......@@ -121,7 +121,7 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (result.val) delete result.val;
}
v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
v8::internal::wasm::ZoneBuffer* TranslateAsmModule(
i::ParseInfo* info, ErrorThrower* thrower,
i::Handle<i::FixedArray>* foreign_args) {
info->set_global();
......@@ -154,9 +154,7 @@ v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(),
info->literal(), &typer);
auto module = builder.Run(foreign_args);
return module;
return builder.Run(foreign_args);
}
i::MaybeHandle<i::JSObject> InstantiateModuleCommon(
......@@ -231,7 +229,7 @@ void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
i::MaybeHandle<i::Object> maybe_module_object =
InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower,
InstantiateModuleCommon(args, module->begin(), module->end(), &thrower,
internal::wasm::kAsmJsOrigin);
if (maybe_module_object.is_null()) {
return;
......
......@@ -577,8 +577,6 @@ bool FinishCompilation(Isolate* isolate, const WasmModule* module,
DCHECK_EQ(i, func.func_index);
WasmName str = module->GetName(func.name_offset, func.name_length);
Handle<Code> code = Handle<Code>::null();
Handle<JSFunction> function = Handle<JSFunction>::null();
Handle<String> function_name = Handle<String>::null();
if (FLAG_wasm_num_compilation_tasks != 0) {
code = results[i];
} else {
......@@ -591,28 +589,12 @@ bool FinishCompilation(Isolate* isolate, const WasmModule* module,
str.start());
return false;
}
if (func.exported) {
function_name = factory->InternalizeUtf8String(str);
function = compiler::CompileJSToWasmWrapper(
isolate, &module_env, function_name, code, instance.js_object, i);
code_stats.Record(function->code());
}
if (!code.is_null()) {
// Install the code into the linker table.
module_env.linker->Finish(i, code);
code_table->set(i, *code);
code_stats.Record(*code);
}
if (func.exported) {
// Exported functions are installed as read-only properties on the
// module.
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, instance.js_object, function_name, &desc,
Object::THROW_ON_ERROR);
if (!status.IsJust())
thrower.Error("export of %.*s failed.", str.length(), str.start());
}
}
return true;
}
......@@ -786,14 +768,19 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
// Create and populate the exports object.
//-------------------------------------------------------------------------
if (export_table.size() > 0 || mem_export) {
// Create the "exports" object.
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate->native_context()->object_function(), isolate);
Handle<JSObject> exports_object =
factory->NewJSObject(object_function, TENURED);
Handle<String> exports_name = factory->InternalizeUtf8String("exports");
JSObject::AddProperty(instance.js_object, exports_name, exports_object,
READ_ONLY);
Handle<JSObject> exports_object;
if (origin == kWasmOrigin) {
// Create the "exports" object.
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate->native_context()->object_function(), isolate);
exports_object = factory->NewJSObject(object_function, TENURED);
Handle<String> exports_name = factory->InternalizeUtf8String("exports");
JSObject::AddProperty(instance.js_object, exports_name, exports_object,
READ_ONLY);
} else {
// Just export the functions directly on the object returned.
exports_object = instance.js_object;
}
// Compile wrappers and add them to the exports object.
for (const WasmExport& exp : export_table) {
......@@ -808,8 +795,10 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);
if (!status.IsJust())
if (!status.IsJust()) {
thrower.Error("export of %.*s failed.", str.length(), str.start());
break;
}
}
if (mem_export) {
......@@ -879,8 +868,9 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
Zone zone(isolate->allocator());
// Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway.
ModuleResult result = DecodeWasmModule(isolate, &zone, module_start,
module_end, false, kWasmOrigin);
ModuleResult result =
DecodeWasmModule(isolate, &zone, module_start, module_end, false,
asm_js ? kAsmJsOrigin : kWasmOrigin);
if (result.failed()) {
if (result.val) {
delete result.val;
......@@ -924,34 +914,25 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const WasmModule* module) {
module_env.linker = &linker;
module_env.origin = module->origin;
if (module->export_table.size() == 0) {
thrower.Error("WASM.compileRun() failed: no exported functions");
return -2;
}
// Compile all functions.
Handle<Code> main_code = Handle<Code>::null(); // record last code.
uint32_t index = 0;
int main_index = 0;
for (const WasmFunction& func : module->functions) {
DCHECK_EQ(index, func.func_index);
// Compile the function and install it in the code table.
// Compile the function and install it in the linker.
Handle<Code> code = compiler::WasmCompilationUnit::CompileWasmFunction(
&thrower, isolate, &module_env, &func);
if (!code.is_null()) {
if (func.exported) {
main_code = code;
main_index = index;
}
linker.Finish(index, code);
}
if (!code.is_null()) linker.Finish(func.func_index, code);
if (thrower.error()) return -1;
index++;
}
if (main_code.is_null()) {
thrower.Error("WASM.compileRun() failed: no main code found");
return -1;
}
linker.Link(instance.function_table, instance.module->function_table);
// Wrap the main code so it can be called as a JS function.
uint32_t main_index = module->export_table.back().func_index;
Handle<Code> main_code = linker.GetFunctionCode(main_index);
Handle<String> name = isolate->factory()->NewStringFromStaticChars("main");
Handle<JSObject> module_object = Handle<JSObject>(0, isolate);
Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper(
......
......@@ -42,15 +42,12 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
F(FunctionBodies, 8, "code") \
F(DataSegments, 9, "data") \
F(Names, 10, "name") \
F(OldFunctions, 0, "old_function") \
F(Globals, 0, "global") \
F(End, 0, "end")
// Contants for the above section types: {LEB128 length, characters...}.
#define WASM_SECTION_MEMORY 6, 'm', 'e', 'm', 'o', 'r', 'y'
#define WASM_SECTION_SIGNATURES 4, 't', 'y', 'p', 'e'
#define WASM_SECTION_OLD_FUNCTIONS \
12, 'o', 'l', 'd', '_', 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n'
#define WASM_SECTION_GLOBALS 6, 'g', 'l', 'o', 'b', 'a', 'l'
#define WASM_SECTION_DATA_SEGMENTS 4, 'd', 'a', 't', 'a'
#define WASM_SECTION_FUNCTION_TABLE 5, 't', 'a', 'b', 'l', 'e'
......@@ -66,7 +63,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
// Constants for the above section headers' size (LEB128 + characters).
#define WASM_SECTION_MEMORY_SIZE ((size_t)7)
#define WASM_SECTION_SIGNATURES_SIZE ((size_t)5)
#define WASM_SECTION_OLD_FUNCTIONS_SIZE ((size_t)13)
#define WASM_SECTION_GLOBALS_SIZE ((size_t)7)
#define WASM_SECTION_DATA_SEGMENTS_SIZE ((size_t)5)
#define WASM_SECTION_FUNCTION_TABLE_SIZE ((size_t)6)
......@@ -114,7 +110,6 @@ struct WasmFunction {
uint32_t name_length; // length in bytes of the name.
uint32_t code_start_offset; // offset in the module bytes of code start.
uint32_t code_end_offset; // offset in the module bytes of code end.
bool exported; // true if this function is exported.
};
// Static representation of an imported WASM function.
......
......@@ -29,7 +29,8 @@ std::ostream& operator<<(std::ostream& os, const ErrorCode& error_code) {
}
void ErrorThrower::Error(const char* format, ...) {
if (error_) return; // only report the first error.
// only report the first error.
if (error_ || isolate_->has_pending_exception()) return;
error_ = true;
char buffer[256];
......
......@@ -20,12 +20,17 @@ using namespace v8::internal::compiler;
using namespace v8::internal::wasm;
namespace {
void TestModule(WasmModuleIndex* module, int32_t expected_result) {
void TestModule(Zone* zone, WasmModuleBuilder* builder,
int32_t expected_result) {
ZoneBuffer buffer(zone);
WasmModuleWriter* writer = builder->Build(zone);
writer->WriteTo(buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
WasmJs::InstallWasmFunctionMap(isolate, isolate->native_context());
int32_t result =
CompileAndRunWasmModule(isolate, module->Begin(), module->End());
CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
CHECK_EQ(expected_result, result);
}
} // namespace
......@@ -43,8 +48,7 @@ TEST(Run_WasmModule_Return114) {
f->Exported(1);
byte code[] = {WASM_I8(kReturnValue)};
f->EmitCode(code, sizeof(code));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), kReturnValue);
TestModule(&zone, builder, kReturnValue);
}
TEST(Run_WasmModule_CallAdd) {
......@@ -69,8 +73,7 @@ TEST(Run_WasmModule_CallAdd) {
f->Exported(1);
byte code2[] = {WASM_CALL_FUNCTION2(f1_index, WASM_I8(77), WASM_I8(22))};
f->EmitCode(code2, sizeof(code2));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 99);
TestModule(&zone, builder, 99);
}
TEST(Run_WasmModule_ReadLoadedDataSegment) {
......@@ -91,8 +94,7 @@ TEST(Run_WasmModule_ReadLoadedDataSegment) {
byte data[] = {0xaa, 0xbb, 0xcc, 0xdd};
builder->AddDataSegment(new (&zone) WasmDataSegmentEncoder(
&zone, data, sizeof(data), kDataSegmentDest0));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 0xddccbbaa);
TestModule(&zone, builder, 0xddccbbaa);
}
TEST(Run_WasmModule_CheckMemoryIsZero) {
......@@ -117,8 +119,7 @@ TEST(Run_WasmModule_CheckMemoryIsZero) {
WASM_BRV(2, WASM_I8(-1)), WASM_INC_LOCAL_BY(localIndex, 4))),
WASM_I8(11))};
f->EmitCode(code, sizeof(code));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 11);
TestModule(&zone, builder, 11);
}
TEST(Run_WasmModule_CallMain_recursive) {
......@@ -142,8 +143,7 @@ TEST(Run_WasmModule_CallMain_recursive) {
WASM_BRV(1, WASM_CALL_FUNCTION0(0))),
WASM_BRV(0, WASM_I8(55))))};
f->EmitCode(code, sizeof(code));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 55);
TestModule(&zone, builder, 55);
}
TEST(Run_WasmModule_Global) {
......@@ -168,6 +168,5 @@ TEST(Run_WasmModule_Global) {
WASM_STORE_GLOBAL(global2, WASM_I32V_1(41)),
WASM_RETURN1(WASM_CALL_FUNCTION0(f1_index))};
f->EmitCode(code2, sizeof(code2));
WasmModuleWriter* writer = builder->Build(&zone);
TestModule(writer->WriteTo(&zone), 97);
TestModule(&zone, builder, 97);
}
......@@ -41,9 +41,9 @@ void testFunctionNameTable(Vector<Vector<const char>> names) {
name.start() + name.length());
// Make every second function name null-terminated.
if (func_index % 2) all_names.push_back('\0');
module.functions.push_back(
{nullptr, 0, 0, static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0, false});
module.functions.push_back({nullptr, 0, 0,
static_cast<uint32_t>(name_offset),
static_cast<uint32_t>(name.length()), 0, 0});
++func_index;
}
......
......@@ -179,7 +179,7 @@ class TestingModule : public ModuleEnv {
module_.functions.reserve(kMaxFunctions);
}
uint32_t index = static_cast<uint32_t>(module->functions.size());
module_.functions.push_back({sig, index, 0, 0, 0, 0, 0, false});
module_.functions.push_back({sig, index, 0, 0, 0, 0, 0});
instance->function_code.push_back(code);
if (interpreter_) {
const WasmFunction* function = &module->functions.back();
......
......@@ -1169,14 +1169,13 @@ class TestModuleEnv : public ModuleEnv {
return static_cast<byte>(mod.signatures.size() - 1);
}
byte AddFunction(FunctionSig* sig) {
mod.functions.push_back({sig, // sig
0, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0, // code_end_offset
false}); // exported
mod.functions.push_back({sig, // sig
0, // func_index
0, // sig_index
0, // name_offset
0, // name_length
0, // code_start_offset
0}); // code_end_offset
CHECK(mod.functions.size() <= 127);
return static_cast<byte>(mod.functions.size() - 1);
}
......
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