Commit 0a9d4003 authored by ddchen's avatar ddchen Committed by Commit bot

[wasm] Add support for multiple indirect function tables

This patch updates internal data structures used by V8 to support
multiple indirect function tables (WebAssembly/design#682). But, since
this feature is post-MVP, the functionality is not directly exposed and
parsing/generation of WebAssembly is left unchanged. Nevertheless, it
is being used in an experiment to implement fine-grained control flow
integrity based on C/C++ types.

BUG=

Review-Url: https://codereview.chromium.org/2174123002
Cr-Commit-Position: refs/heads/master@{#38110}
parent ff683fed
...@@ -278,7 +278,7 @@ WasmGraphBuilder::WasmGraphBuilder( ...@@ -278,7 +278,7 @@ WasmGraphBuilder::WasmGraphBuilder(
module_(nullptr), module_(nullptr),
mem_buffer_(nullptr), mem_buffer_(nullptr),
mem_size_(nullptr), mem_size_(nullptr),
function_table_(nullptr), function_tables_(zone),
control_(nullptr), control_(nullptr),
effect_(nullptr), effect_(nullptr),
cur_buffer_(def_buffer_), cur_buffer_(def_buffer_),
...@@ -2059,11 +2059,14 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, ...@@ -2059,11 +2059,14 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
// Compute the code object by loading it from the function table. // Compute the code object by loading it from the function table.
Node* key = args[0]; Node* key = args[0];
// Assume only one table for now.
DCHECK_LE(module_->instance->function_tables.size(), 1u);
// Bounds check the index. // Bounds check the index.
int table_size = static_cast<int>(module_->FunctionTableSize()); uint32_t table_size =
module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0;
if (table_size > 0) { if (table_size > 0) {
// Bounds check against the table size. // Bounds check against the table size.
Node* size = Int32Constant(static_cast<int>(table_size)); Node* size = Uint32Constant(table_size);
Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size); Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size);
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, in_bounds, position); trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, in_bounds, position);
} else { } else {
...@@ -2071,7 +2074,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, ...@@ -2071,7 +2074,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position); trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position);
return trap_->GetTrapValue(module_->GetSignature(index)); return trap_->GetTrapValue(module_->GetSignature(index));
} }
Node* table = FunctionTable(); Node* table = FunctionTable(0);
// Load signature from the table and check. // Load signature from the table and check.
// The table is a FixedArray; signatures are encoded as SMIs. // The table is a FixedArray; signatures are encoded as SMIs.
...@@ -2093,13 +2096,13 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, ...@@ -2093,13 +2096,13 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args,
} }
// Load code object from the table. // Load code object from the table.
int offset = fixed_offset + kPointerSize * table_size; uint32_t offset = fixed_offset + kPointerSize * table_size;
Node* load_code = graph()->NewNode( Node* load_code = graph()->NewNode(
machine->Load(MachineType::AnyTagged()), table, machine->Load(MachineType::AnyTagged()), table,
graph()->NewNode(machine->Int32Add(), graph()->NewNode(machine->Int32Add(),
graph()->NewNode(machine->Word32Shl(), key, graph()->NewNode(machine->Word32Shl(), key,
Int32Constant(kPointerSizeLog2)), Int32Constant(kPointerSizeLog2)),
Int32Constant(offset)), Uint32Constant(offset)),
*effect_, *control_); *effect_, *control_);
args[0] = load_code; args[0] = load_code;
...@@ -2131,10 +2134,13 @@ Node* WasmGraphBuilder::JITSingleFunction(Node* const base, Node* const length, ...@@ -2131,10 +2134,13 @@ Node* WasmGraphBuilder::JITSingleFunction(Node* const base, Node* const length,
// Bounds check the index. // Bounds check the index.
{ {
int table_size = static_cast<int>(module_->FunctionTableSize()); // Assume only one table for now.
DCHECK_LE(module_->instance->function_tables.size(), 1u);
uint32_t table_size =
module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0;
if (table_size > 0) { if (table_size > 0) {
// Bounds check against the table size. // Bounds check against the table size.
Node* size = Int32Constant(static_cast<int>(table_size)); Node* size = Uint32Constant(table_size);
Node* in_bounds = Node* in_bounds =
graph()->NewNode(machine->Uint32LessThan(), index, size); graph()->NewNode(machine->Uint32LessThan(), index, size);
trap_->AddTrapIfFalse(wasm::kTrapInvalidIndex, in_bounds, position); trap_->AddTrapIfFalse(wasm::kTrapInvalidIndex, in_bounds, position);
...@@ -2164,7 +2170,7 @@ Node* WasmGraphBuilder::JITSingleFunction(Node* const base, Node* const length, ...@@ -2164,7 +2170,7 @@ Node* WasmGraphBuilder::JITSingleFunction(Node* const base, Node* const length,
inputs[1] = BuildChangeUint32ToSmi(base); inputs[1] = BuildChangeUint32ToSmi(base);
inputs[2] = BuildChangeUint32ToSmi(length); inputs[2] = BuildChangeUint32ToSmi(length);
inputs[3] = BuildChangeUint32ToSmi(index); inputs[3] = BuildChangeUint32ToSmi(index);
inputs[4] = FunctionTable(); inputs[4] = FunctionTable(0);
inputs[5] = Uint32Constant(sig_index); inputs[5] = Uint32Constant(sig_index);
inputs[6] = BuildChangeUint32ToSmi(Uint32Constant(return_count)); inputs[6] = BuildChangeUint32ToSmi(Uint32Constant(return_count));
...@@ -2806,13 +2812,17 @@ Node* WasmGraphBuilder::DefaultS128Value() { ...@@ -2806,13 +2812,17 @@ Node* WasmGraphBuilder::DefaultS128Value() {
zero, zero); zero, zero);
} }
Node* WasmGraphBuilder::FunctionTable() { Node* WasmGraphBuilder::FunctionTable(uint32_t index) {
DCHECK(module_ && module_->instance && DCHECK(module_ && module_->instance &&
!module_->instance->function_table.is_null()); index < module_->instance->function_tables.size());
if (!function_table_) { if (!function_tables_.size()) {
function_table_ = HeapConstant(module_->instance->function_table); for (size_t i = 0; i < module_->instance->function_tables.size(); ++i) {
DCHECK(!module_->instance->function_tables[i].is_null());
function_tables_.push_back(
HeapConstant(module_->instance->function_tables[i]));
}
} }
return function_table_; return function_tables_[index];
} }
Node* WasmGraphBuilder::ChangeToRuntimeCall(Node* node, Node* WasmGraphBuilder::ChangeToRuntimeCall(Node* node,
......
...@@ -168,7 +168,7 @@ class WasmGraphBuilder { ...@@ -168,7 +168,7 @@ class WasmGraphBuilder {
Node* ToJS(Node* node, Node* context, wasm::LocalType type); Node* ToJS(Node* node, Node* context, wasm::LocalType type);
Node* FromJS(Node* node, Node* context, wasm::LocalType type); Node* FromJS(Node* node, Node* context, wasm::LocalType type);
Node* Invert(Node* node); Node* Invert(Node* node);
Node* FunctionTable(); Node* FunctionTable(uint32_t index);
Node* ChangeToRuntimeCall(Node* node, Runtime::FunctionId function_id, Node* ChangeToRuntimeCall(Node* node, Runtime::FunctionId function_id,
Signature<Conversion>* signature); Signature<Conversion>* signature);
...@@ -215,7 +215,7 @@ class WasmGraphBuilder { ...@@ -215,7 +215,7 @@ class WasmGraphBuilder {
wasm::ModuleEnv* module_; wasm::ModuleEnv* module_;
Node* mem_buffer_; Node* mem_buffer_;
Node* mem_size_; Node* mem_size_;
Node* function_table_; NodeVector function_tables_;
Node** control_; Node** control_;
Node** effect_; Node** effect_;
Node** cur_buffer_; Node** cur_buffer_;
......
...@@ -257,38 +257,19 @@ class ModuleDecoder : public Decoder { ...@@ -257,38 +257,19 @@ class ModuleDecoder : public Decoder {
} }
break; break;
} }
case WasmSection::Code::FunctionTablePad: {
if (!FLAG_wasm_jit_prototype) {
error("FunctionTablePad section without jiting enabled");
}
// An indirect function table requires functions first.
module->indirect_table_size = consume_u32v("indirect entry count");
if (module->indirect_table_size > 0 &&
module->indirect_table_size < module->function_table.size()) {
error("more predefined indirect entries than table can hold");
}
break;
}
case WasmSection::Code::FunctionTable: { case WasmSection::Code::FunctionTable: {
// An indirect function table requires functions first. // An indirect function table requires functions first.
CheckForFunctions(module, section); CheckForFunctions(module, section);
uint32_t function_table_count = consume_u32v("function table count"); // Assume only one table for now.
module->function_table.reserve(SafeReserve(function_table_count)); static const uint32_t kSupportedTableCount = 1;
module->function_tables.reserve(SafeReserve(kSupportedTableCount));
// Decode function table. // Decode function table.
for (uint32_t i = 0; i < function_table_count; ++i) { for (uint32_t i = 0; i < kSupportedTableCount; ++i) {
if (failed()) break; if (failed()) break;
TRACE("DecodeFunctionTable[%d] module+%d\n", i, TRACE("DecodeFunctionTable[%d] module+%d\n", i,
static_cast<int>(pc_ - start_)); static_cast<int>(pc_ - start_));
uint16_t index = consume_u32v(); module->function_tables.push_back({0, 0, std::vector<uint16_t>()});
if (index >= module->functions.size()) { DecodeFunctionTableInModule(module, &module->function_tables[i]);
error(pc_ - 2, "invalid function index");
break;
}
module->function_table.push_back(index);
}
if (module->indirect_table_size > 0 &&
module->indirect_table_size < module->function_table.size()) {
error("more predefined indirect entries than table can hold");
} }
break; break;
} }
...@@ -520,6 +501,27 @@ class ModuleDecoder : public Decoder { ...@@ -520,6 +501,27 @@ class ModuleDecoder : public Decoder {
consume_bytes(segment->source_size); consume_bytes(segment->source_size);
} }
// Decodes a single function table inside a module starting at {pc_}.
void DecodeFunctionTableInModule(WasmModule* module,
WasmIndirectFunctionTable* table) {
table->size = consume_u32v("function table entry count");
table->max_size = FLAG_wasm_jit_prototype ? consume_u32v() : table->size;
if ((!FLAG_wasm_jit_prototype && table->max_size != table->size) ||
(FLAG_wasm_jit_prototype && table->max_size < table->size)) {
error("invalid table maximum size");
}
for (uint32_t i = 0; i < table->size; ++i) {
uint16_t index = consume_u32v();
if (index >= module->functions.size()) {
error(pc_ - sizeof(index), "invalid function index");
break;
}
table->values.push_back(index);
}
}
// Calculate individual global offsets and total size of globals table. // Calculate individual global offsets and total size of globals table.
void CalculateGlobalsOffsets(WasmModule* module) { void CalculateGlobalsOffsets(WasmModule* module) {
uint32_t offset = 0; uint32_t offset = 0;
......
...@@ -915,9 +915,12 @@ class CodeMap { ...@@ -915,9 +915,12 @@ class CodeMap {
return Preprocess(&interpreter_code_[function_index]); return Preprocess(&interpreter_code_[function_index]);
} }
InterpreterCode* GetIndirectCode(uint32_t indirect_index) { InterpreterCode* GetIndirectCode(uint32_t table_index, uint32_t entry_index) {
if (indirect_index >= module_->function_table.size()) return nullptr; if (table_index >= module_->function_tables.size()) return nullptr;
uint32_t index = module_->function_table[indirect_index]; const WasmIndirectFunctionTable* table =
&module_->function_tables[table_index];
if (entry_index >= table->values.size()) return nullptr;
uint32_t index = table->values[entry_index];
if (index >= interpreter_code_.size()) return nullptr; if (index >= interpreter_code_.size()) return nullptr;
return GetCode(index); return GetCode(index);
} }
...@@ -1383,14 +1386,13 @@ class ThreadImpl : public WasmInterpreter::Thread { ...@@ -1383,14 +1386,13 @@ class ThreadImpl : public WasmInterpreter::Thread {
CallIndirectOperand operand(&decoder, code->at(pc)); CallIndirectOperand operand(&decoder, code->at(pc));
size_t index = stack_.size() - operand.arity - 1; size_t index = stack_.size() - operand.arity - 1;
DCHECK_LT(index, stack_.size()); DCHECK_LT(index, stack_.size());
uint32_t table_index = stack_[index].to<uint32_t>(); uint32_t entry_index = stack_[index].to<uint32_t>();
if (table_index >= module()->function_table.size()) { // Assume only one table for now.
DCHECK_LE(module()->function_tables.size(), 1u);
InterpreterCode* target = codemap()->GetIndirectCode(0, entry_index);
if (target == nullptr) {
return DoTrap(kTrapFuncInvalid, pc); return DoTrap(kTrapFuncInvalid, pc);
} } else if (target->function->sig_index != operand.index) {
uint16_t function_index = module()->function_table[table_index];
InterpreterCode* target = codemap()->GetCode(function_index);
DCHECK(target);
if (target->function->sig_index != operand.index) {
return DoTrap(kTrapFuncSigMismatch, pc); return DoTrap(kTrapFuncSigMismatch, pc);
} }
......
...@@ -154,11 +154,11 @@ enum CompiledWasmObjectFields { ...@@ -154,11 +154,11 @@ enum CompiledWasmObjectFields {
kExports, // maybe FixedArray of FixedArray of WasmExportMetadata kExports, // maybe FixedArray of FixedArray of WasmExportMetadata
// structure // structure
kStartupFunction, // maybe FixedArray of WasmExportMetadata structure kStartupFunction, // maybe FixedArray of WasmExportMetadata structure
kIndirectFunctionTableSize, // Smi. Size of indirect function table. kTableOfIndirectFunctionTables, // maybe FixedArray of FixedArray of
kIndirectFunctionTablePrototype, // maybe FixedArray // WasmIndirectFunctionTableMetadata
kModuleBytes, // maybe String kModuleBytes, // maybe String
kFunctionNameTable, // maybe ByteArray kFunctionNameTable, // maybe ByteArray
kMinRequiredMemory, // Smi. an uint32_t kMinRequiredMemory, // Smi. an uint32_t
// The following 2 are either together present or absent: // The following 2 are either together present or absent:
kDataSegmentsInfo, // maybe FixedArray of FixedArray respecting the kDataSegmentsInfo, // maybe FixedArray of FixedArray respecting the
// WasmSegmentInfo structure // WasmSegmentInfo structure
...@@ -192,6 +192,12 @@ enum WasmSegmentInfo { ...@@ -192,6 +192,12 @@ enum WasmSegmentInfo {
kWasmSegmentInfoSize // Sentinel value. kWasmSegmentInfoSize // Sentinel value.
}; };
enum WasmIndirectFunctionTableMetadata {
kSize, // Smi. an uint32_t
kTable, // FixedArray of indirect function table
kWasmIndirectFunctionTableMetadataSize // Sentinel value.
};
uint32_t GetMinModuleMemSize(const WasmModule* module) { uint32_t GetMinModuleMemSize(const WasmModule* module) {
return WasmModule::kPageSize * module->min_mem_pages; return WasmModule::kPageSize * module->min_mem_pages;
} }
...@@ -257,39 +263,6 @@ void SaveDataSegmentInfo(Factory* factory, const WasmModule* module, ...@@ -257,39 +263,6 @@ void SaveDataSegmentInfo(Factory* factory, const WasmModule* module,
compiled_module->set(kDataSegments, *data); compiled_module->set(kDataSegments, *data);
} }
MaybeHandle<FixedArray> BuildFunctionTable(Isolate* isolate,
const WasmModule* module) {
// Compute the size of the indirect function table
uint32_t table_size = module->FunctionTableSize();
if (table_size == 0) {
return MaybeHandle<FixedArray>();
}
DCHECK_GE(table_size, module->function_table.size());
Handle<FixedArray> fixed = isolate->factory()->NewFixedArray(2 * table_size);
for (uint32_t i = 0; i < module->function_table.size(); ++i) {
const WasmFunction* function =
&module->functions[module->function_table[i]];
int fixed_array_index = static_cast<int>(i);
fixed->set(fixed_array_index, Smi::FromInt(function->sig_index));
fixed->set(fixed_array_index + table_size,
Smi::FromInt(module->function_table[fixed_array_index]));
}
// Set the remaining elements to -1 (instead of "undefined"). These
// elements are accessed directly as SMIs (without a check). On 64-bit
// platforms, it is possible to have the top bits of "undefined" take
// small integer values (or zero), which are more likely to be equal to
// the signature index we check against.
for (uint32_t i = static_cast<uint32_t>(module->function_table.size());
i < table_size;
++i) {
fixed->set(i, Smi::FromInt(-1));
}
return fixed;
}
void PatchFunctionTable(Handle<Code> code, void PatchFunctionTable(Handle<Code> code,
Handle<FixedArray> old_indirect_table, Handle<FixedArray> old_indirect_table,
Handle<FixedArray> new_indirect_table) { Handle<FixedArray> new_indirect_table) {
...@@ -377,7 +350,8 @@ Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index, ...@@ -377,7 +350,8 @@ Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index,
// the {constant_pool_offset} field of the code object. // the {constant_pool_offset} field of the code object.
// TODO(titzer): placeholder code objects are somewhat dangerous. // TODO(titzer): placeholder code objects are somewhat dangerous.
static byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions. static byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions.
static CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr, 0, nullptr}; static CodeDesc desc = {
buffer, arraysize(buffer), arraysize(buffer), 0, 0, nullptr, 0, nullptr};
Handle<Code> code = factory->NewCode(desc, Code::KindField::encode(kind), Handle<Code> code = factory->NewCode(desc, Code::KindField::encode(kind),
Handle<Object>::null()); Handle<Object>::null());
code->set_constant_pool_offset(static_cast<int>(index) + kPlaceholderMarker); code->set_constant_pool_offset(static_cast<int>(index) + kPlaceholderMarker);
...@@ -461,7 +435,6 @@ WasmModule::WasmModule(byte* module_start) ...@@ -461,7 +435,6 @@ WasmModule::WasmModule(byte* module_start)
start_function_index(-1), start_function_index(-1),
origin(kWasmOrigin), origin(kWasmOrigin),
globals_size(0), globals_size(0),
indirect_table_size(0),
pending_tasks(new base::Semaphore(0)) {} pending_tasks(new base::Semaphore(0)) {}
static MaybeHandle<JSFunction> ReportFFIError( static MaybeHandle<JSFunction> ReportFFIError(
...@@ -1027,10 +1000,19 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions( ...@@ -1027,10 +1000,19 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
temp_instance_for_compilation.mem_start = nullptr; temp_instance_for_compilation.mem_start = nullptr;
temp_instance_for_compilation.globals_start = nullptr; temp_instance_for_compilation.globals_start = nullptr;
MaybeHandle<FixedArray> indirect_table = BuildFunctionTable(isolate, this); MaybeHandle<FixedArray> indirect_table =
if (!indirect_table.is_null()) { function_tables.size()
temp_instance_for_compilation.function_table = ? factory->NewFixedArray(static_cast<int>(function_tables.size()))
indirect_table.ToHandleChecked(); : MaybeHandle<FixedArray>();
for (uint32_t i = 0; i < function_tables.size(); ++i) {
Handle<FixedArray> values = wasm::BuildFunctionTable(isolate, i, this);
temp_instance_for_compilation.function_tables[i] = values;
Handle<FixedArray> metadata = isolate->factory()->NewFixedArray(
kWasmIndirectFunctionTableMetadataSize);
metadata->set(kSize, Smi::FromInt(function_tables[i].size));
metadata->set(kTable, *values);
indirect_table.ToHandleChecked()->set(i, *metadata);
} }
HistogramTimerScope wasm_compile_module_time_scope( HistogramTimerScope wasm_compile_module_time_scope(
...@@ -1077,11 +1059,8 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions( ...@@ -1077,11 +1059,8 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
Handle<FixedArray> ret = Handle<FixedArray> ret =
factory->NewFixedArray(kCompiledWasmObjectTableSize, TENURED); factory->NewFixedArray(kCompiledWasmObjectTableSize, TENURED);
ret->set(kFunctions, *compiled_functions); ret->set(kFunctions, *compiled_functions);
ret->set(kIndirectFunctionTableSize,
Smi::FromInt(static_cast<int>(function_table.size())));
if (!indirect_table.is_null()) { if (!indirect_table.is_null()) {
ret->set(kIndirectFunctionTablePrototype, ret->set(kTableOfIndirectFunctionTables, *indirect_table.ToHandleChecked());
*indirect_table.ToHandleChecked());
} }
Handle<FixedArray> import_data = GetImportsMetadata(factory, this); Handle<FixedArray> import_data = GetImportsMetadata(factory, this);
ret->set(kImportData, *import_data); ret->set(kImportData, *import_data);
...@@ -1183,28 +1162,54 @@ Handle<FixedArray> CloneModuleForInstance(Isolate* isolate, ...@@ -1183,28 +1162,54 @@ Handle<FixedArray> CloneModuleForInstance(Isolate* isolate,
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
Handle<FixedArray> clone = factory->CopyFixedArray(original); Handle<FixedArray> clone = factory->CopyFixedArray(original);
// Copy the outer table, each WasmIndirectFunctionTableMetadata table, and the
// inner kTable.
MaybeHandle<FixedArray> maybe_indirect_tables =
original->GetValue<FixedArray>(kTableOfIndirectFunctionTables);
Handle<FixedArray> indirect_tables, clone_indirect_tables;
if (maybe_indirect_tables.ToHandle(&indirect_tables)) {
clone_indirect_tables = factory->CopyFixedArray(indirect_tables);
clone->set(kTableOfIndirectFunctionTables, *clone_indirect_tables);
for (int i = 0; i < clone_indirect_tables->length(); ++i) {
Handle<FixedArray> orig_metadata =
clone_indirect_tables->GetValueChecked<FixedArray>(i);
Handle<FixedArray> clone_metadata =
factory->CopyFixedArray(orig_metadata);
clone_indirect_tables->set(i, *clone_metadata);
Handle<FixedArray> orig_table =
clone_metadata->GetValueChecked<FixedArray>(kTable);
Handle<FixedArray> clone_table = factory->CopyFixedArray(orig_table);
clone_metadata->set(kTable, *clone_table);
}
}
// Clone each code, then if indirect tables are used, patch the cloned code to
// refer to the cloned kTable.
Handle<FixedArray> orig_wasm_functions = Handle<FixedArray> orig_wasm_functions =
original->GetValueChecked<FixedArray>(kFunctions); original->GetValueChecked<FixedArray>(kFunctions);
Handle<FixedArray> clone_wasm_functions = Handle<FixedArray> clone_wasm_functions =
factory->CopyFixedArray(orig_wasm_functions); factory->CopyFixedArray(orig_wasm_functions);
clone->set(kFunctions, *clone_wasm_functions); clone->set(kFunctions, *clone_wasm_functions);
for (int i = 0; i < clone_wasm_functions->length(); ++i) {
Handle<Code> orig_code = clone_wasm_functions->GetValueChecked<Code>(i);
Handle<Code> cloned_code = factory->CopyCode(orig_code);
clone_wasm_functions->set(i, *cloned_code);
MaybeHandle<FixedArray> maybe_indirect_table = if (!clone_indirect_tables.is_null()) {
original->GetValue<FixedArray>(kIndirectFunctionTablePrototype); for (int j = 0; j < clone_indirect_tables->length(); ++j) {
Handle<FixedArray> orig_metadata =
indirect_tables->GetValueChecked<FixedArray>(j);
Handle<FixedArray> orig_table =
orig_metadata->GetValueChecked<FixedArray>(kTable);
Handle<FixedArray> indirect_table; Handle<FixedArray> clone_metadata =
Handle<FixedArray> old_table; clone_indirect_tables->GetValueChecked<FixedArray>(j);
if (maybe_indirect_table.ToHandle(&old_table)) { Handle<FixedArray> clone_table =
indirect_table = factory->CopyFixedArray(old_table); clone_metadata->GetValueChecked<FixedArray>(kTable);
clone->set(kIndirectFunctionTablePrototype, *indirect_table);
}
for (int i = 0; i < orig_wasm_functions->length(); ++i) { PatchFunctionTable(cloned_code, orig_table, clone_table);
Handle<Code> orig_code = orig_wasm_functions->GetValueChecked<Code>(i); }
Handle<Code> cloned_code = factory->CopyCode(orig_code);
clone_wasm_functions->set(i, *cloned_code);
if (!maybe_indirect_table.is_null()) {
PatchFunctionTable(cloned_code, old_table, indirect_table);
} }
} }
...@@ -1276,14 +1281,12 @@ MaybeHandle<JSObject> WasmModule::Instantiate( ...@@ -1276,14 +1281,12 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
Handle<FixedArray> code_table = Handle<FixedArray> code_table =
compiled_module->GetValueChecked<FixedArray>(kFunctions); compiled_module->GetValueChecked<FixedArray>(kFunctions);
{ std::vector<Handle<Code>> functions(
std::vector<Handle<Code>> functions( static_cast<size_t>(code_table->length()));
static_cast<size_t>(code_table->length())); for (int i = 0; i < code_table->length(); ++i) {
for (int i = 0; i < code_table->length(); ++i) { functions[static_cast<size_t>(i)] = code_table->GetValueChecked<Code>(i);
functions[static_cast<size_t>(i)] = code_table->GetValueChecked<Code>(i);
}
LinkModuleFunctions(isolate, functions);
} }
LinkModuleFunctions(isolate, functions);
RecordStats(isolate, code_table); RecordStats(isolate, code_table);
...@@ -1307,20 +1310,18 @@ MaybeHandle<JSObject> WasmModule::Instantiate( ...@@ -1307,20 +1310,18 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
FlushAssemblyCache(isolate, code_table); FlushAssemblyCache(isolate, code_table);
MaybeHandle<FixedArray> maybe_indirect_table = MaybeHandle<FixedArray> maybe_indirect_tables =
compiled_module->GetValue<FixedArray>(kIndirectFunctionTablePrototype); compiled_module->GetValue<FixedArray>(kTableOfIndirectFunctionTables);
Handle<FixedArray> indirect_table; Handle<FixedArray> indirect_tables;
if (maybe_indirect_table.ToHandle(&indirect_table)) { if (maybe_indirect_tables.ToHandle(&indirect_tables)) {
int table_size = for (int i = 0; i < indirect_tables->length(); ++i) {
Smi::cast(compiled_module->get(kIndirectFunctionTableSize))->value(); Handle<FixedArray> metadata =
int half_point = indirect_table->length() / 2; indirect_tables->GetValueChecked<FixedArray>(i);
for (int i = half_point; i < half_point + table_size; ++i) { uint32_t size = Smi::cast(metadata->get(kSize))->value();
int index = Smi::cast(indirect_table->get(i))->value(); Handle<FixedArray> table = metadata->GetValueChecked<FixedArray>(kTable);
DCHECK_GE(index, 0); wasm::PopulateFunctionTable(table, size, &functions);
DCHECK_LT(index, code_table->length());
indirect_table->set(i, code_table->get(index));
} }
js_object->SetInternalField(kWasmModuleFunctionTable, *indirect_table); js_object->SetInternalField(kWasmModuleFunctionTable, *indirect_tables);
} }
// Run the start function if one was specified. // Run the start function if one was specified.
...@@ -1475,6 +1476,40 @@ bool UpdateWasmModuleMemory(Handle<JSObject> object, Address old_start, ...@@ -1475,6 +1476,40 @@ bool UpdateWasmModuleMemory(Handle<JSObject> object, Address old_start,
return true; return true;
} }
Handle<FixedArray> BuildFunctionTable(Isolate* isolate, uint32_t index,
const WasmModule* module) {
const WasmIndirectFunctionTable* table = &module->function_tables[index];
DCHECK_EQ(table->size, table->values.size());
DCHECK_GE(table->max_size, table->size);
Handle<FixedArray> values =
isolate->factory()->NewFixedArray(2 * table->max_size);
for (uint32_t i = 0; i < table->size; ++i) {
const WasmFunction* function = &module->functions[table->values[i]];
values->set(i, Smi::FromInt(function->sig_index));
values->set(i + table->max_size, Smi::FromInt(table->values[i]));
}
// Set the remaining elements to -1 (instead of "undefined"). These
// elements are accessed directly as SMIs (without a check). On 64-bit
// platforms, it is possible to have the top bits of "undefined" take
// small integer values (or zero), which are more likely to be equal to
// the signature index we check against.
for (uint32_t i = table->size; i < table->max_size; i++) {
values->set(i, Smi::FromInt(-1));
}
return values;
}
void PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size,
const std::vector<Handle<Code>>* code_table) {
uint32_t max_size = table->length() / 2;
for (uint32_t i = max_size; i < max_size + table_size; ++i) {
int index = Smi::cast(table->get(static_cast<int>(i)))->value();
DCHECK_GE(index, 0);
DCHECK_LT(static_cast<size_t>(index), code_table->size());
table->set(static_cast<int>(i), *(*code_table)[index]);
}
}
int GetNumberOfFunctions(JSObject* wasm) { int GetNumberOfFunctions(JSObject* wasm) {
Object* func_names_obj = wasm->GetInternalField(kWasmFunctionNamesArray); Object* func_names_obj = wasm->GetInternalField(kWasmFunctionNamesArray);
// TODO(clemensh): this looks inside an array constructed elsewhere. Refactor. // TODO(clemensh): this looks inside an array constructed elsewhere. Refactor.
......
...@@ -43,7 +43,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40; ...@@ -43,7 +43,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
F(FunctionBodies, 8, "code") \ F(FunctionBodies, 8, "code") \
F(DataSegments, 9, "data") \ F(DataSegments, 9, "data") \
F(Names, 10, "name") \ F(Names, 10, "name") \
F(FunctionTablePad, 11, "table_pad") \
F(Globals, 0, "global") \ F(Globals, 0, "global") \
F(End, 0, "end") F(End, 0, "end")
...@@ -61,8 +60,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40; ...@@ -61,8 +60,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
8, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n' 8, 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n'
#define WASM_SECTION_FUNCTION_BODIES 4, 'c', 'o', 'd', 'e' #define WASM_SECTION_FUNCTION_BODIES 4, 'c', 'o', 'd', 'e'
#define WASM_SECTION_NAMES 4, 'n', 'a', 'm', 'e' #define WASM_SECTION_NAMES 4, 'n', 'a', 'm', 'e'
#define WASM_SECTION_FUNCTION_TABLE_PAD \
9, 't', 'a', 'b', 'l', 'e', '_', 'p', 'a', 'd'
// Constants for the above section headers' size (LEB128 + characters). // Constants for the above section headers' size (LEB128 + characters).
#define WASM_SECTION_MEMORY_SIZE ((size_t)7) #define WASM_SECTION_MEMORY_SIZE ((size_t)7)
...@@ -77,7 +74,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40; ...@@ -77,7 +74,6 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
#define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)9) #define WASM_SECTION_FUNCTION_SIGNATURES_SIZE ((size_t)9)
#define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)5) #define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)5)
#define WASM_SECTION_NAMES_SIZE ((size_t)5) #define WASM_SECTION_NAMES_SIZE ((size_t)5)
#define WASM_SECTION_FUNCTION_TABLE_PAD_SIZE ((size_t)10)
class WasmDebugInfo; class WasmDebugInfo;
...@@ -153,6 +149,13 @@ struct WasmDataSegment { ...@@ -153,6 +149,13 @@ struct WasmDataSegment {
bool init; // true if loaded upon instantiation. bool init; // true if loaded upon instantiation.
}; };
// Static representation of a wasm indirect call table.
struct WasmIndirectFunctionTable {
uint32_t size; // initial table size.
uint32_t max_size; // maximum table size.
std::vector<uint16_t> values; // function table.
};
enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin }; enum ModuleOrigin { kWasmOrigin, kAsmJsOrigin };
// Static representation of a module. // Static representation of a module.
...@@ -175,12 +178,10 @@ struct WasmModule { ...@@ -175,12 +178,10 @@ struct WasmModule {
std::vector<WasmGlobal> globals; // globals in this module. std::vector<WasmGlobal> globals; // globals in this module.
uint32_t globals_size; // size of globals table. uint32_t globals_size; // size of globals table.
uint32_t indirect_table_size; // size of indirect function
// table (includes padding).
std::vector<FunctionSig*> signatures; // signatures in this module. std::vector<FunctionSig*> signatures; // signatures in this module.
std::vector<WasmFunction> functions; // functions in this module. std::vector<WasmFunction> functions; // functions in this module.
std::vector<WasmDataSegment> data_segments; // data segments in this module. std::vector<WasmDataSegment> data_segments; // data segments in this module.
std::vector<uint16_t> function_table; // function table. std::vector<WasmIndirectFunctionTable> function_tables; // function tables.
std::vector<WasmImport> import_table; // import table. std::vector<WasmImport> import_table; // import table.
std::vector<WasmExport> export_table; // export table. std::vector<WasmExport> export_table; // export table.
// We store the semaphore here to extend its lifetime. In <libc-2.21, which we // We store the semaphore here to extend its lifetime. In <libc-2.21, which we
...@@ -238,14 +239,6 @@ struct WasmModule { ...@@ -238,14 +239,6 @@ struct WasmModule {
MaybeHandle<FixedArray> CompileFunctions(Isolate* isolate, MaybeHandle<FixedArray> CompileFunctions(Isolate* isolate,
ErrorThrower* thrower) const; ErrorThrower* thrower) const;
uint32_t FunctionTableSize() const {
if (indirect_table_size > 0) {
return indirect_table_size;
}
DCHECK_LE(function_table.size(), UINT32_MAX);
return static_cast<uint32_t>(function_table.size());
}
private: private:
DISALLOW_COPY_AND_ASSIGN(WasmModule); DISALLOW_COPY_AND_ASSIGN(WasmModule);
}; };
...@@ -258,7 +251,7 @@ struct WasmModuleInstance { ...@@ -258,7 +251,7 @@ struct WasmModuleInstance {
Handle<Context> context; // JavaScript native context. Handle<Context> context; // JavaScript native context.
Handle<JSArrayBuffer> mem_buffer; // Handle to array buffer of memory. Handle<JSArrayBuffer> mem_buffer; // Handle to array buffer of memory.
Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals. Handle<JSArrayBuffer> globals_buffer; // Handle to array buffer of globals.
Handle<FixedArray> function_table; // indirect function table. std::vector<Handle<FixedArray>> function_tables; // indirect function tables.
std::vector<Handle<Code>> function_code; // code objects for each function. std::vector<Handle<Code>> function_code; // code objects for each function.
std::vector<Handle<Code>> import_code; // code objects for each import. std::vector<Handle<Code>> import_code; // code objects for each import.
// -- raw memory ------------------------------------------------------------ // -- raw memory ------------------------------------------------------------
...@@ -269,6 +262,7 @@ struct WasmModuleInstance { ...@@ -269,6 +262,7 @@ struct WasmModuleInstance {
explicit WasmModuleInstance(const WasmModule* m) explicit WasmModuleInstance(const WasmModule* m)
: module(m), : module(m),
function_tables(m->function_tables.size()),
function_code(m->functions.size()), function_code(m->functions.size()),
import_code(m->import_table.size()), import_code(m->import_table.size()),
mem_start(nullptr), mem_start(nullptr),
...@@ -286,18 +280,21 @@ struct ModuleEnv { ...@@ -286,18 +280,21 @@ struct ModuleEnv {
// reloc infos. // reloc infos.
std::vector<Handle<Code>> placeholders; std::vector<Handle<Code>> placeholders;
bool IsValidGlobal(uint32_t index) { bool IsValidGlobal(uint32_t index) const {
return module && index < module->globals.size(); return module && index < module->globals.size();
} }
bool IsValidFunction(uint32_t index) const { bool IsValidFunction(uint32_t index) const {
return module && index < module->functions.size(); return module && index < module->functions.size();
} }
bool IsValidSignature(uint32_t index) { bool IsValidSignature(uint32_t index) const {
return module && index < module->signatures.size(); return module && index < module->signatures.size();
} }
bool IsValidImport(uint32_t index) { bool IsValidImport(uint32_t index) const {
return module && index < module->import_table.size(); return module && index < module->import_table.size();
} }
bool IsValidTable(uint32_t index) const {
return module && index < module->function_tables.size();
}
LocalType GetGlobalType(uint32_t index) { LocalType GetGlobalType(uint32_t index) {
DCHECK(IsValidGlobal(index)); DCHECK(IsValidGlobal(index));
return module->globals[index].type; return module->globals[index].type;
...@@ -314,8 +311,9 @@ struct ModuleEnv { ...@@ -314,8 +311,9 @@ struct ModuleEnv {
DCHECK(IsValidSignature(index)); DCHECK(IsValidSignature(index));
return module->signatures[index]; return module->signatures[index];
} }
uint32_t FunctionTableSize() const { const WasmIndirectFunctionTable* GetTable(uint32_t index) const {
return module->FunctionTableSize(); DCHECK(IsValidTable(index));
return &module->function_tables[index];
} }
bool asm_js() { return origin == kAsmJsOrigin; } bool asm_js() { return origin == kAsmJsOrigin; }
...@@ -387,6 +385,16 @@ bool UpdateWasmModuleMemory(Handle<JSObject> object, Address old_start, ...@@ -387,6 +385,16 @@ bool UpdateWasmModuleMemory(Handle<JSObject> object, Address old_start,
Address new_start, uint32_t old_size, Address new_start, uint32_t old_size,
uint32_t new_size); uint32_t new_size);
// Constructs a single function table as a FixedArray of double size,
// populating it with function signature indices and function indices.
Handle<FixedArray> BuildFunctionTable(Isolate* isolate, uint32_t index,
const WasmModule* module);
// Populates a function table by replacing function indices with handles to
// the compiled code.
void PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size,
const std::vector<Handle<Code>>* code_table);
namespace testing { namespace testing {
// Decode, verify, and run the function labeled "main" in the // Decode, verify, and run the function labeled "main" in the
......
...@@ -2407,8 +2407,9 @@ WASM_EXEC_TEST(SimpleCallIndirect) { ...@@ -2407,8 +2407,9 @@ WASM_EXEC_TEST(SimpleCallIndirect) {
module.AddSignature(sigs.d_dd()); module.AddSignature(sigs.d_dd());
// Function table. // Function table.
int table[] = {0, 1}; uint16_t indirect_function_table[] = {0, 1};
module.AddIndirectFunctionTable(table, 2); module.AddIndirectFunctionTable(indirect_function_table,
arraysize(indirect_function_table));
module.PopulateIndirectFunctionTable(); module.PopulateIndirectFunctionTable();
// Builder the caller function. // Builder the caller function.
...@@ -2438,8 +2439,9 @@ WASM_EXEC_TEST(MultipleCallIndirect) { ...@@ -2438,8 +2439,9 @@ WASM_EXEC_TEST(MultipleCallIndirect) {
module.AddSignature(sigs.d_dd()); module.AddSignature(sigs.d_dd());
// Function table. // Function table.
int table[] = {0, 1}; uint16_t indirect_function_table[] = {0, 1};
module.AddIndirectFunctionTable(table, 2); module.AddIndirectFunctionTable(indirect_function_table,
arraysize(indirect_function_table));
module.PopulateIndirectFunctionTable(); module.PopulateIndirectFunctionTable();
// Builder the caller function. // Builder the caller function.
......
...@@ -222,25 +222,24 @@ class TestingModule : public ModuleEnv { ...@@ -222,25 +222,24 @@ class TestingModule : public ModuleEnv {
instance->function_code[index] = code; instance->function_code[index] = code;
} }
void AddIndirectFunctionTable(int* functions, int table_size) { void AddIndirectFunctionTable(uint16_t* functions, uint32_t table_size) {
Handle<FixedArray> fixed = module_.function_tables.push_back(
isolate_->factory()->NewFixedArray(2 * table_size); {table_size, table_size, std::vector<uint16_t>()});
instance->function_table = fixed; for (uint32_t i = 0; i < table_size; ++i) {
DCHECK_EQ(0u, module->function_table.size()); module_.function_tables.back().values.push_back(functions[i]);
for (int i = 0; i < table_size; i++) {
module_.function_table.push_back(functions[i]);
} }
Handle<FixedArray> values = BuildFunctionTable(
isolate_, static_cast<int>(module_.function_tables.size() - 1),
&module_);
instance->function_tables.push_back(values);
} }
void PopulateIndirectFunctionTable() { void PopulateIndirectFunctionTable() {
if (instance->function_table.is_null()) return; for (uint32_t i = 0; i < instance->function_tables.size(); i++) {
int table_size = static_cast<int>(module->function_table.size()); PopulateFunctionTable(instance->function_tables[i],
for (int i = 0; i < table_size; i++) { module_.function_tables[i].size,
int function_index = module->function_table[i]; &instance->function_code);
const WasmFunction* function = &module->functions[function_index];
instance->function_table->set(i, Smi::FromInt(function->sig_index));
instance->function_table->set(i + table_size,
*instance->function_code[function_index]);
} }
} }
......
...@@ -64,7 +64,6 @@ var kDeclExports = 0x09; ...@@ -64,7 +64,6 @@ var kDeclExports = 0x09;
var kDeclFunctions = 0x0a; var kDeclFunctions = 0x0a;
var kDeclCode = 0x0b; var kDeclCode = 0x0b;
var kDeclNames = 0x0c; var kDeclNames = 0x0c;
var kDeclFunctionTablePad = 0x0d;
var kArity0 = 0; var kArity0 = 0;
var kArity1 = 1; var kArity1 = 1;
...@@ -75,7 +74,7 @@ var kWasmFunctionTypeForm = 0x40; ...@@ -75,7 +74,7 @@ var kWasmFunctionTypeForm = 0x40;
var section_names = [ var section_names = [
"memory", "type", "old_function", "global", "data", "memory", "type", "old_function", "global", "data",
"table", "end", "start", "import", "export", "table", "end", "start", "import", "export",
"function", "code", "name", "table_pad"]; "function", "code", "name"];
// Function declaration flags // Function declaration flags
var kDeclFunctionName = 0x01; var kDeclFunctionName = 0x01;
......
...@@ -227,6 +227,10 @@ class WasmModuleBuilder { ...@@ -227,6 +227,10 @@ class WasmModuleBuilder {
if (debug) print("emitting table @ " + binary.length); if (debug) print("emitting table @ " + binary.length);
binary.emit_section(kDeclTable, section => { binary.emit_section(kDeclTable, section => {
section.emit_varint(wasm.table.length); section.emit_varint(wasm.table.length);
if (wasm.pad !== null) {
if (debug) print("emitting table padding @ " + binary.length);
section.emit_varint(wasm.pad);
}
for (let index of wasm.table) { for (let index of wasm.table) {
section.emit_varint(index); section.emit_varint(index);
} }
...@@ -338,19 +342,6 @@ class WasmModuleBuilder { ...@@ -338,19 +342,6 @@ class WasmModuleBuilder {
}); });
} }
// Add an indirect function table pad section.
if (wasm.pad !== null) {
if (debug)
print("emitting indirect function table pad @ " + binary.length);
binary.emit_section(kDeclFunctionTablePad, section => {
section.emit_varint(wasm.pad);
});
}
// End the module.
if (debug) print("emitting end @ " + binary.length);
binary.emit_section(kDeclEnd, section => {});
return binary; return binary;
} }
......
...@@ -507,8 +507,9 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) { ...@@ -507,8 +507,9 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
if (result.ok()) { if (result.ok()) {
EXPECT_EQ(1, result.val->signatures.size()); EXPECT_EQ(1, result.val->signatures.size());
EXPECT_EQ(1, result.val->functions.size()); EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(1, result.val->function_table.size()); EXPECT_EQ(1, result.val->function_tables.size());
EXPECT_EQ(0, result.val->function_table[0]); EXPECT_EQ(1, result.val->function_tables[0].values.size());
EXPECT_EQ(0, result.val->function_tables[0].values[0]);
} }
if (result.val) delete result.val; if (result.val) delete result.val;
} }
...@@ -539,9 +540,10 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) { ...@@ -539,9 +540,10 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
if (result.ok()) { if (result.ok()) {
EXPECT_EQ(2, result.val->signatures.size()); EXPECT_EQ(2, result.val->signatures.size());
EXPECT_EQ(4, result.val->functions.size()); EXPECT_EQ(4, result.val->functions.size());
EXPECT_EQ(8, result.val->function_table.size()); EXPECT_EQ(1, result.val->function_tables.size());
EXPECT_EQ(8, result.val->function_tables[0].values.size());
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
EXPECT_EQ(i & 3, result.val->function_table[i]); EXPECT_EQ(i & 3, result.val->function_tables[0].values[i]);
} }
} }
if (result.val) delete result.val; if (result.val) delete result.val;
......
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