• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/wasm/wasm-code-manager.h"
6 
7 #include <iomanip>
8 
9 #include "src/assembler-inl.h"
10 #include "src/base/macros.h"
11 #include "src/base/platform/platform.h"
12 #include "src/codegen.h"
13 #include "src/disassembler.h"
14 #include "src/globals.h"
15 #include "src/macro-assembler-inl.h"
16 #include "src/macro-assembler.h"
17 #include "src/objects-inl.h"
18 #include "src/wasm/function-compiler.h"
19 #include "src/wasm/jump-table-assembler.h"
20 #include "src/wasm/wasm-module.h"
21 #include "src/wasm/wasm-objects-inl.h"
22 #include "src/wasm/wasm-objects.h"
23 
24 #define TRACE_HEAP(...)                                   \
25   do {                                                    \
26     if (FLAG_wasm_trace_native_heap) PrintF(__VA_ARGS__); \
27   } while (false)
28 
29 namespace v8 {
30 namespace internal {
31 namespace wasm {
32 
33 namespace {
34 
35 // Binary predicate to perform lookups in {NativeModule::owned_code_} with a
36 // given address into a code object. Use with {std::upper_bound} for example.
37 struct WasmCodeUniquePtrComparator {
operator ()v8::internal::wasm::__anonbededbb10111::WasmCodeUniquePtrComparator38   bool operator()(Address pc, const std::unique_ptr<WasmCode>& code) const {
39     DCHECK_NE(kNullAddress, pc);
40     DCHECK_NOT_NULL(code);
41     return pc < code->instruction_start();
42   }
43 };
44 
45 }  // namespace
46 
Merge(AddressRange range)47 void DisjointAllocationPool::Merge(AddressRange range) {
48   auto dest_it = ranges_.begin();
49   auto dest_end = ranges_.end();
50 
51   // Skip over dest ranges strictly before {range}.
52   while (dest_it != dest_end && dest_it->end < range.start) ++dest_it;
53 
54   // After last dest range: insert and done.
55   if (dest_it == dest_end) {
56     ranges_.push_back(range);
57     return;
58   }
59 
60   // Adjacent (from below) to dest: merge and done.
61   if (dest_it->start == range.end) {
62     dest_it->start = range.start;
63     return;
64   }
65 
66   // Before dest: insert and done.
67   if (dest_it->start > range.end) {
68     ranges_.insert(dest_it, range);
69     return;
70   }
71 
72   // Src is adjacent from above. Merge and check whether the merged range is now
73   // adjacent to the next range.
74   DCHECK_EQ(dest_it->end, range.start);
75   dest_it->end = range.end;
76   auto next_dest = dest_it;
77   ++next_dest;
78   if (next_dest != dest_end && dest_it->end == next_dest->start) {
79     dest_it->end = next_dest->end;
80     ranges_.erase(next_dest);
81   }
82 }
83 
Allocate(size_t size)84 AddressRange DisjointAllocationPool::Allocate(size_t size) {
85   for (auto it = ranges_.begin(), end = ranges_.end(); it != end; ++it) {
86     size_t range_size = it->size();
87     if (size > range_size) continue;
88     AddressRange ret{it->start, it->start + size};
89     if (size == range_size) {
90       ranges_.erase(it);
91     } else {
92       it->start += size;
93       DCHECK_LT(it->start, it->end);
94     }
95     return ret;
96   }
97   return {};
98 }
99 
constant_pool() const100 Address WasmCode::constant_pool() const {
101   if (FLAG_enable_embedded_constant_pool) {
102     if (constant_pool_offset_ < instructions().size()) {
103       return instruction_start() + constant_pool_offset_;
104     }
105   }
106   return kNullAddress;
107 }
108 
trap_handler_index() const109 size_t WasmCode::trap_handler_index() const {
110   CHECK(HasTrapHandlerIndex());
111   return static_cast<size_t>(trap_handler_index_);
112 }
113 
set_trap_handler_index(size_t value)114 void WasmCode::set_trap_handler_index(size_t value) {
115   trap_handler_index_ = value;
116 }
117 
RegisterTrapHandlerData()118 void WasmCode::RegisterTrapHandlerData() {
119   DCHECK(!HasTrapHandlerIndex());
120   if (kind() != WasmCode::kFunction) return;
121 
122   Address base = instruction_start();
123 
124   size_t size = instructions().size();
125   const int index =
126       RegisterHandlerData(base, size, protected_instructions().size(),
127                           protected_instructions().start());
128 
129   // TODO(eholk): if index is negative, fail.
130   CHECK_LE(0, index);
131   set_trap_handler_index(static_cast<size_t>(index));
132 }
133 
HasTrapHandlerIndex() const134 bool WasmCode::HasTrapHandlerIndex() const { return trap_handler_index_ >= 0; }
135 
ShouldBeLogged(Isolate * isolate)136 bool WasmCode::ShouldBeLogged(Isolate* isolate) {
137   return isolate->logger()->is_listening_to_code_events() ||
138          isolate->is_profiling();
139 }
140 
LogCode(Isolate * isolate) const141 void WasmCode::LogCode(Isolate* isolate) const {
142   DCHECK(ShouldBeLogged(isolate));
143   if (IsAnonymous()) return;
144   ModuleWireBytes wire_bytes(native_module()->wire_bytes());
145   // TODO(herhut): Allow to log code without on-heap round-trip of the name.
146   ModuleEnv* module_env = GetModuleEnv(native_module()->compilation_state());
147   WireBytesRef name_ref =
148       module_env->module->LookupFunctionName(wire_bytes, index());
149   WasmName name_vec = wire_bytes.GetName(name_ref);
150   MaybeHandle<String> maybe_name =
151       isolate->factory()->NewStringFromUtf8(Vector<const char>::cast(name_vec));
152   Handle<String> name;
153   if (!maybe_name.ToHandle(&name)) {
154     name = isolate->factory()->NewStringFromAsciiChecked("<name too long>");
155   }
156   int name_length;
157   auto cname =
158       name->ToCString(AllowNullsFlag::DISALLOW_NULLS,
159                       RobustnessFlag::ROBUST_STRING_TRAVERSAL, &name_length);
160   PROFILE(isolate,
161           CodeCreateEvent(CodeEventListener::FUNCTION_TAG, this,
162                           {cname.get(), static_cast<size_t>(name_length)}));
163   if (!source_positions().is_empty()) {
164     LOG_CODE_EVENT(isolate, CodeLinePosInfoRecordEvent(instruction_start(),
165                                                        source_positions()));
166   }
167 }
168 
Validate() const169 void WasmCode::Validate() const {
170 #ifdef DEBUG
171   // We expect certain relocation info modes to never appear in {WasmCode}
172   // objects or to be restricted to a small set of valid values. Hence the
173   // iteration below does not use a mask, but visits all relocation data.
174   for (RelocIterator it(instructions(), reloc_info(), constant_pool());
175        !it.done(); it.next()) {
176     RelocInfo::Mode mode = it.rinfo()->rmode();
177     switch (mode) {
178       case RelocInfo::WASM_CALL: {
179         Address target = it.rinfo()->wasm_call_address();
180         WasmCode* code = native_module_->Lookup(target);
181         CHECK_NOT_NULL(code);
182         CHECK_EQ(WasmCode::kJumpTable, code->kind());
183         CHECK(code->contains(target));
184         break;
185       }
186       case RelocInfo::WASM_STUB_CALL: {
187         Address target = it.rinfo()->wasm_stub_call_address();
188         WasmCode* code = native_module_->Lookup(target);
189         CHECK_NOT_NULL(code);
190         CHECK_EQ(WasmCode::kRuntimeStub, code->kind());
191         CHECK_EQ(target, code->instruction_start());
192         break;
193       }
194       case RelocInfo::INTERNAL_REFERENCE:
195       case RelocInfo::INTERNAL_REFERENCE_ENCODED: {
196         Address target = it.rinfo()->target_internal_reference();
197         CHECK(contains(target));
198         break;
199       }
200       case RelocInfo::JS_TO_WASM_CALL:
201       case RelocInfo::EXTERNAL_REFERENCE:
202       case RelocInfo::COMMENT:
203       case RelocInfo::CONST_POOL:
204       case RelocInfo::VENEER_POOL:
205         // These are OK to appear.
206         break;
207       default:
208         FATAL("Unexpected mode: %d", mode);
209     }
210   }
211 #endif
212 }
213 
Print(const char * name) const214 void WasmCode::Print(const char* name) const {
215   StdoutStream os;
216   os << "--- WebAssembly code ---\n";
217   Disassemble(name, os);
218   os << "--- End code ---\n";
219 }
220 
Disassemble(const char * name,std::ostream & os,Address current_pc) const221 void WasmCode::Disassemble(const char* name, std::ostream& os,
222                            Address current_pc) const {
223   if (name) os << "name: " << name << "\n";
224   if (!IsAnonymous()) os << "index: " << index() << "\n";
225   os << "kind: " << GetWasmCodeKindAsString(kind_) << "\n";
226   os << "compiler: " << (is_liftoff() ? "Liftoff" : "TurboFan") << "\n";
227   size_t body_size = instructions().size();
228   os << "Body (size = " << body_size << ")\n";
229 
230 #ifdef ENABLE_DISASSEMBLER
231   size_t instruction_size = body_size;
232   if (constant_pool_offset_ && constant_pool_offset_ < instruction_size) {
233     instruction_size = constant_pool_offset_;
234   }
235   if (safepoint_table_offset_ && safepoint_table_offset_ < instruction_size) {
236     instruction_size = safepoint_table_offset_;
237   }
238   if (handler_table_offset_ && handler_table_offset_ < instruction_size) {
239     instruction_size = handler_table_offset_;
240   }
241   DCHECK_LT(0, instruction_size);
242   os << "Instructions (size = " << instruction_size << ")\n";
243   Disassembler::Decode(nullptr, &os, instructions().start(),
244                        instructions().start() + instruction_size,
245                        CodeReference(this), current_pc);
246   os << "\n";
247 
248   if (handler_table_offset_ > 0) {
249     HandlerTable table(instruction_start(), handler_table_offset_);
250     os << "Exception Handler Table (size = " << table.NumberOfReturnEntries()
251        << "):\n";
252     table.HandlerTableReturnPrint(os);
253     os << "\n";
254   }
255 
256   if (!protected_instructions_.is_empty()) {
257     os << "Protected instructions:\n pc offset  land pad\n";
258     for (auto& data : protected_instructions()) {
259       os << std::setw(10) << std::hex << data.instr_offset << std::setw(10)
260          << std::hex << data.landing_offset << "\n";
261     }
262     os << "\n";
263   }
264 
265   if (!source_positions().is_empty()) {
266     os << "Source positions:\n pc offset  position\n";
267     for (SourcePositionTableIterator it(source_positions()); !it.done();
268          it.Advance()) {
269       os << std::setw(10) << std::hex << it.code_offset() << std::dec
270          << std::setw(10) << it.source_position().ScriptOffset()
271          << (it.is_statement() ? "  statement" : "") << "\n";
272     }
273     os << "\n";
274   }
275 
276   os << "RelocInfo (size = " << reloc_info_.size() << ")\n";
277   for (RelocIterator it(instructions(), reloc_info(), constant_pool());
278        !it.done(); it.next()) {
279     it.rinfo()->Print(nullptr, os);
280   }
281   os << "\n";
282 #endif  // ENABLE_DISASSEMBLER
283 }
284 
GetWasmCodeKindAsString(WasmCode::Kind kind)285 const char* GetWasmCodeKindAsString(WasmCode::Kind kind) {
286   switch (kind) {
287     case WasmCode::kFunction:
288       return "wasm function";
289     case WasmCode::kWasmToJsWrapper:
290       return "wasm-to-js";
291     case WasmCode::kLazyStub:
292       return "lazy-compile";
293     case WasmCode::kRuntimeStub:
294       return "runtime-stub";
295     case WasmCode::kInterpreterEntry:
296       return "interpreter entry";
297     case WasmCode::kJumpTable:
298       return "jump table";
299   }
300   return "unknown kind";
301 }
302 
~WasmCode()303 WasmCode::~WasmCode() {
304   if (HasTrapHandlerIndex()) {
305     CHECK_LT(trap_handler_index(),
306              static_cast<size_t>(std::numeric_limits<int>::max()));
307     trap_handler::ReleaseHandlerData(static_cast<int>(trap_handler_index()));
308   }
309 }
310 
NativeModule(Isolate * isolate,const WasmFeatures & enabled,bool can_request_more,VirtualMemory * code_space,WasmCodeManager * code_manager,std::shared_ptr<const WasmModule> module,const ModuleEnv & env)311 NativeModule::NativeModule(Isolate* isolate, const WasmFeatures& enabled,
312                            bool can_request_more, VirtualMemory* code_space,
313                            WasmCodeManager* code_manager,
314                            std::shared_ptr<const WasmModule> module,
315                            const ModuleEnv& env)
316     : enabled_features_(enabled),
317       module_(std::move(module)),
318       compilation_state_(NewCompilationState(isolate, env)),
319       free_code_space_({code_space->address(), code_space->end()}),
320       wasm_code_manager_(code_manager),
321       can_request_more_memory_(can_request_more),
322       use_trap_handler_(env.use_trap_handler) {
323   DCHECK_EQ(module_.get(), env.module);
324   DCHECK_NOT_NULL(module_);
325   VirtualMemory my_mem;
326   owned_code_space_.push_back(my_mem);
327   owned_code_space_.back().TakeControl(code_space);
328   owned_code_.reserve(num_functions());
329 
330   uint32_t num_wasm_functions = module_->num_declared_functions;
331   if (num_wasm_functions > 0) {
332     code_table_.reset(new WasmCode*[num_wasm_functions]);
333     memset(code_table_.get(), 0, num_wasm_functions * sizeof(WasmCode*));
334 
335     jump_table_ = CreateEmptyJumpTable(num_wasm_functions);
336   }
337 }
338 
ReserveCodeTableForTesting(uint32_t max_functions)339 void NativeModule::ReserveCodeTableForTesting(uint32_t max_functions) {
340   DCHECK_LE(num_functions(), max_functions);
341   WasmCode** new_table = new WasmCode*[max_functions];
342   memset(new_table, 0, max_functions * sizeof(*new_table));
343   memcpy(new_table, code_table_.get(),
344          module_->num_declared_functions * sizeof(*new_table));
345   code_table_.reset(new_table);
346 
347   // Re-allocate jump table.
348   jump_table_ = CreateEmptyJumpTable(max_functions);
349 }
350 
LogWasmCodes(Isolate * isolate)351 void NativeModule::LogWasmCodes(Isolate* isolate) {
352   if (!WasmCode::ShouldBeLogged(isolate)) return;
353 
354   // TODO(titzer): we skip the logging of the import wrappers
355   // here, but they should be included somehow.
356   for (WasmCode* code : code_table()) {
357     if (code != nullptr) code->LogCode(isolate);
358   }
359 }
360 
AddOwnedCode(Maybe<uint32_t> index,Vector<const byte> instructions,uint32_t stack_slots,size_t safepoint_table_offset,size_t handler_table_offset,size_t constant_pool_offset,OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,OwnedVector<const byte> reloc_info,OwnedVector<const byte> source_position_table,WasmCode::Kind kind,WasmCode::Tier tier)361 WasmCode* NativeModule::AddOwnedCode(
362     Maybe<uint32_t> index, Vector<const byte> instructions,
363     uint32_t stack_slots, size_t safepoint_table_offset,
364     size_t handler_table_offset, size_t constant_pool_offset,
365     OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,
366     OwnedVector<const byte> reloc_info,
367     OwnedVector<const byte> source_position_table, WasmCode::Kind kind,
368     WasmCode::Tier tier) {
369   WasmCode* code;
370   {
371     // Both allocation and insertion in owned_code_ happen in the same critical
372     // section, thus ensuring owned_code_'s elements are rarely if ever moved.
373     base::LockGuard<base::Mutex> lock(&allocation_mutex_);
374     Address executable_buffer = AllocateForCode(instructions.size());
375     if (executable_buffer == kNullAddress) {
376       V8::FatalProcessOutOfMemory(nullptr, "NativeModule::AddOwnedCode");
377       UNREACHABLE();
378     }
379     // Ownership will be transferred to {owned_code_} below.
380     code = new WasmCode(
381         this, index,
382         {reinterpret_cast<byte*>(executable_buffer), instructions.size()},
383         stack_slots, safepoint_table_offset, handler_table_offset,
384         constant_pool_offset, std::move(protected_instructions),
385         std::move(reloc_info), std::move(source_position_table), kind, tier);
386 
387     if (owned_code_.empty() ||
388         code->instruction_start() > owned_code_.back()->instruction_start()) {
389       // Common case.
390       owned_code_.emplace_back(code);
391     } else {
392       // Slow but unlikely case.
393       // TODO(mtrofin): We allocate in increasing address order, and
394       // even if we end up with segmented memory, we may end up only with a few
395       // large moves - if, for example, a new segment is below the current ones.
396       auto insert_before = std::upper_bound(
397           owned_code_.begin(), owned_code_.end(), code->instruction_start(),
398           WasmCodeUniquePtrComparator{});
399       owned_code_.emplace(insert_before, code);
400     }
401   }
402   memcpy(reinterpret_cast<void*>(code->instruction_start()),
403          instructions.start(), instructions.size());
404 
405   return code;
406 }
407 
AddCodeCopy(Handle<Code> code,WasmCode::Kind kind,uint32_t index)408 WasmCode* NativeModule::AddCodeCopy(Handle<Code> code, WasmCode::Kind kind,
409                                     uint32_t index) {
410   // TODO(wasm): Adding instance-specific wasm-to-js wrappers as owned code to
411   // this NativeModule is a memory leak until the whole NativeModule dies.
412   WasmCode* ret = AddAnonymousCode(code, kind);
413   ret->index_ = Just(index);
414   if (index >= module_->num_imported_functions) set_code(index, ret);
415   return ret;
416 }
417 
AddInterpreterEntry(Handle<Code> code,uint32_t index)418 WasmCode* NativeModule::AddInterpreterEntry(Handle<Code> code, uint32_t index) {
419   WasmCode* ret = AddAnonymousCode(code, WasmCode::kInterpreterEntry);
420   ret->index_ = Just(index);
421   base::LockGuard<base::Mutex> lock(&allocation_mutex_);
422   PatchJumpTable(index, ret->instruction_start(), WasmCode::kFlushICache);
423   set_code(index, ret);
424   return ret;
425 }
426 
SetLazyBuiltin(Handle<Code> code)427 void NativeModule::SetLazyBuiltin(Handle<Code> code) {
428   uint32_t num_wasm_functions = module_->num_declared_functions;
429   if (num_wasm_functions == 0) return;
430   WasmCode* lazy_builtin = AddAnonymousCode(code, WasmCode::kLazyStub);
431   // Fill the jump table with jumps to the lazy compile stub.
432   Address lazy_compile_target = lazy_builtin->instruction_start();
433   for (uint32_t i = 0; i < num_wasm_functions; ++i) {
434     JumpTableAssembler::EmitLazyCompileJumpSlot(
435         jump_table_->instruction_start(), i,
436         i + module_->num_imported_functions, lazy_compile_target,
437         WasmCode::kNoFlushICache);
438   }
439   Assembler::FlushICache(jump_table_->instructions().start(),
440                          jump_table_->instructions().size());
441 }
442 
SetRuntimeStubs(Isolate * isolate)443 void NativeModule::SetRuntimeStubs(Isolate* isolate) {
444   DCHECK_NULL(runtime_stub_table_[0]);  // Only called once.
445 #define COPY_BUILTIN(Name)                                                     \
446   runtime_stub_table_[WasmCode::k##Name] =                                     \
447       AddAnonymousCode(isolate->builtins()->builtin_handle(Builtins::k##Name), \
448                        WasmCode::kRuntimeStub);
449 #define COPY_BUILTIN_TRAP(Name) COPY_BUILTIN(ThrowWasm##Name)
450   WASM_RUNTIME_STUB_LIST(COPY_BUILTIN, COPY_BUILTIN_TRAP);
451 #undef COPY_BUILTIN_TRAP
452 #undef COPY_BUILTIN
453 }
454 
AddAnonymousCode(Handle<Code> code,WasmCode::Kind kind)455 WasmCode* NativeModule::AddAnonymousCode(Handle<Code> code,
456                                          WasmCode::Kind kind) {
457   // For off-heap builtins, we create a copy of the off-heap instruction stream
458   // instead of the on-heap code object containing the trampoline. Ensure that
459   // we do not apply the on-heap reloc info to the off-heap instructions.
460   const size_t relocation_size =
461       code->is_off_heap_trampoline() ? 0 : code->relocation_size();
462   OwnedVector<byte> reloc_info = OwnedVector<byte>::New(relocation_size);
463   memcpy(reloc_info.start(), code->relocation_start(), relocation_size);
464   Handle<ByteArray> source_pos_table(code->SourcePositionTable(),
465                                      code->GetIsolate());
466   OwnedVector<byte> source_pos =
467       OwnedVector<byte>::New(source_pos_table->length());
468   source_pos_table->copy_out(0, source_pos.start(), source_pos_table->length());
469   Vector<const byte> instructions(
470       reinterpret_cast<byte*>(code->InstructionStart()),
471       static_cast<size_t>(code->InstructionSize()));
472   int stack_slots = code->has_safepoint_info() ? code->stack_slots() : 0;
473   int safepoint_table_offset =
474       code->has_safepoint_info() ? code->safepoint_table_offset() : 0;
475   WasmCode* ret =
476       AddOwnedCode(Nothing<uint32_t>(),           // index
477                    instructions,                  // instructions
478                    stack_slots,                   // stack_slots
479                    safepoint_table_offset,        // safepoint_table_offset
480                    code->handler_table_offset(),  // handler_table_offset
481                    code->constant_pool_offset(),  // constant_pool_offset
482                    {},                            // protected_instructions
483                    std::move(reloc_info),         // reloc_info
484                    std::move(source_pos),         // source positions
485                    kind,                          // kind
486                    WasmCode::kOther);             // tier
487 
488   // Apply the relocation delta by iterating over the RelocInfo.
489   intptr_t delta = ret->instruction_start() - code->InstructionStart();
490   int mode_mask = RelocInfo::kApplyMask |
491                   RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL);
492   RelocIterator orig_it(*code, mode_mask);
493   for (RelocIterator it(ret->instructions(), ret->reloc_info(),
494                         ret->constant_pool(), mode_mask);
495        !it.done(); it.next(), orig_it.next()) {
496     RelocInfo::Mode mode = it.rinfo()->rmode();
497     if (RelocInfo::IsWasmStubCall(mode)) {
498       uint32_t stub_call_tag = orig_it.rinfo()->wasm_call_tag();
499       DCHECK_LT(stub_call_tag, WasmCode::kRuntimeStubCount);
500       WasmCode* code =
501           runtime_stub(static_cast<WasmCode::RuntimeStubId>(stub_call_tag));
502       it.rinfo()->set_wasm_stub_call_address(code->instruction_start(),
503                                              SKIP_ICACHE_FLUSH);
504     } else {
505       it.rinfo()->apply(delta);
506     }
507   }
508 
509   // Flush the i-cache here instead of in AddOwnedCode, to include the changes
510   // made while iterating over the RelocInfo above.
511   Assembler::FlushICache(ret->instructions().start(),
512                          ret->instructions().size());
513   if (FLAG_print_code || FLAG_print_wasm_code) ret->Print();
514   ret->Validate();
515   return ret;
516 }
517 
AddCode(uint32_t index,const CodeDesc & desc,uint32_t stack_slots,size_t safepoint_table_offset,size_t handler_table_offset,OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,OwnedVector<const byte> source_pos_table,WasmCode::Tier tier)518 WasmCode* NativeModule::AddCode(
519     uint32_t index, const CodeDesc& desc, uint32_t stack_slots,
520     size_t safepoint_table_offset, size_t handler_table_offset,
521     OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,
522     OwnedVector<const byte> source_pos_table, WasmCode::Tier tier) {
523   OwnedVector<byte> reloc_info = OwnedVector<byte>::New(desc.reloc_size);
524   memcpy(reloc_info.start(), desc.buffer + desc.buffer_size - desc.reloc_size,
525          desc.reloc_size);
526   WasmCode* ret = AddOwnedCode(
527       Just(index), {desc.buffer, static_cast<size_t>(desc.instr_size)},
528       stack_slots, safepoint_table_offset, handler_table_offset,
529       desc.instr_size - desc.constant_pool_size,
530       std::move(protected_instructions), std::move(reloc_info),
531       std::move(source_pos_table), WasmCode::kFunction, tier);
532 
533   // Apply the relocation delta by iterating over the RelocInfo.
534   intptr_t delta = ret->instructions().start() - desc.buffer;
535   int mode_mask = RelocInfo::kApplyMask |
536                   RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
537                   RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL);
538   for (RelocIterator it(ret->instructions(), ret->reloc_info(),
539                         ret->constant_pool(), mode_mask);
540        !it.done(); it.next()) {
541     RelocInfo::Mode mode = it.rinfo()->rmode();
542     if (RelocInfo::IsWasmCall(mode)) {
543       uint32_t call_tag = it.rinfo()->wasm_call_tag();
544       Address target = GetCallTargetForFunction(call_tag);
545       it.rinfo()->set_wasm_call_address(target, SKIP_ICACHE_FLUSH);
546     } else if (RelocInfo::IsWasmStubCall(mode)) {
547       uint32_t stub_call_tag = it.rinfo()->wasm_call_tag();
548       DCHECK_LT(stub_call_tag, WasmCode::kRuntimeStubCount);
549       WasmCode* code =
550           runtime_stub(static_cast<WasmCode::RuntimeStubId>(stub_call_tag));
551       it.rinfo()->set_wasm_stub_call_address(code->instruction_start(),
552                                              SKIP_ICACHE_FLUSH);
553     } else {
554       it.rinfo()->apply(delta);
555     }
556   }
557 
558   // Flush the i-cache here instead of in AddOwnedCode, to include the changes
559   // made while iterating over the RelocInfo above.
560   Assembler::FlushICache(ret->instructions().start(),
561                          ret->instructions().size());
562   if (FLAG_print_code || FLAG_print_wasm_code) ret->Print();
563   ret->Validate();
564   return ret;
565 }
566 
AddDeserializedCode(uint32_t index,Vector<const byte> instructions,uint32_t stack_slots,size_t safepoint_table_offset,size_t handler_table_offset,size_t constant_pool_offset,OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,OwnedVector<const byte> reloc_info,OwnedVector<const byte> source_position_table,WasmCode::Tier tier)567 WasmCode* NativeModule::AddDeserializedCode(
568     uint32_t index, Vector<const byte> instructions, uint32_t stack_slots,
569     size_t safepoint_table_offset, size_t handler_table_offset,
570     size_t constant_pool_offset,
571     OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,
572     OwnedVector<const byte> reloc_info,
573     OwnedVector<const byte> source_position_table, WasmCode::Tier tier) {
574   WasmCode* code = AddOwnedCode(
575       Just(index), instructions, stack_slots, safepoint_table_offset,
576       handler_table_offset, constant_pool_offset,
577       std::move(protected_instructions), std::move(reloc_info),
578       std::move(source_position_table), WasmCode::kFunction, tier);
579 
580   if (!code->protected_instructions_.is_empty()) {
581     code->RegisterTrapHandlerData();
582   }
583   set_code(index, code);
584   PatchJumpTable(index, code->instruction_start(), WasmCode::kFlushICache);
585   // Note: we do not flush the i-cache here, since the code needs to be
586   // relocated anyway. The caller is responsible for flushing the i-cache later.
587   return code;
588 }
589 
PublishCode(WasmCode * code)590 void NativeModule::PublishCode(WasmCode* code) {
591   base::LockGuard<base::Mutex> lock(&allocation_mutex_);
592   // Skip publishing code if there is an active redirection to the interpreter
593   // for the given function index, in order to preserve the redirection.
594   if (has_code(code->index()) &&
595       this->code(code->index())->kind() == WasmCode::kInterpreterEntry) {
596     return;
597   }
598   if (!code->protected_instructions_.is_empty()) {
599     code->RegisterTrapHandlerData();
600   }
601   DCHECK(!code->IsAnonymous());
602   set_code(code->index(), code);
603   PatchJumpTable(code->index(), code->instruction_start(),
604                  WasmCode::kFlushICache);
605 }
606 
SnapshotCodeTable() const607 std::vector<WasmCode*> NativeModule::SnapshotCodeTable() const {
608   base::LockGuard<base::Mutex> lock(&allocation_mutex_);
609   std::vector<WasmCode*> result;
610   result.reserve(code_table().size());
611   for (WasmCode* code : code_table()) result.push_back(code);
612   return result;
613 }
614 
CreateEmptyJumpTable(uint32_t num_wasm_functions)615 WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t num_wasm_functions) {
616   // Only call this if we really need a jump table.
617   DCHECK_LT(0, num_wasm_functions);
618   OwnedVector<byte> instructions = OwnedVector<byte>::New(
619       JumpTableAssembler::SizeForNumberOfSlots(num_wasm_functions));
620   memset(instructions.start(), 0, instructions.size());
621   return AddOwnedCode(Nothing<uint32_t>(),       // index
622                       instructions.as_vector(),  // instructions
623                       0,                         // stack_slots
624                       0,                         // safepoint_table_offset
625                       0,                         // handler_table_offset
626                       0,                         // constant_pool_offset
627                       {},                        // protected_instructions
628                       {},                        // reloc_info
629                       {},                        // source_pos
630                       WasmCode::kJumpTable,      // kind
631                       WasmCode::kOther);         // tier
632 }
633 
PatchJumpTable(uint32_t func_index,Address target,WasmCode::FlushICache flush_icache)634 void NativeModule::PatchJumpTable(uint32_t func_index, Address target,
635                                   WasmCode::FlushICache flush_icache) {
636   DCHECK_LE(module_->num_imported_functions, func_index);
637   uint32_t slot_idx = func_index - module_->num_imported_functions;
638   JumpTableAssembler::PatchJumpTableSlot(jump_table_->instruction_start(),
639                                          slot_idx, target, flush_icache);
640 }
641 
AllocateForCode(size_t size)642 Address NativeModule::AllocateForCode(size_t size) {
643   // This happens under a lock assumed by the caller.
644   size = RoundUp(size, kCodeAlignment);
645   AddressRange mem = free_code_space_.Allocate(size);
646   if (mem.is_empty()) {
647     if (!can_request_more_memory_) return kNullAddress;
648 
649     Address hint = owned_code_space_.empty() ? kNullAddress
650                                              : owned_code_space_.back().end();
651     VirtualMemory empty_mem;
652     owned_code_space_.push_back(empty_mem);
653     VirtualMemory& new_mem = owned_code_space_.back();
654     wasm_code_manager_->TryAllocate(size, &new_mem,
655                                     reinterpret_cast<void*>(hint));
656     if (!new_mem.IsReserved()) return kNullAddress;
657     base::LockGuard<base::Mutex> lock(
658         &wasm_code_manager_->native_modules_mutex_);
659     wasm_code_manager_->AssignRanges(new_mem.address(), new_mem.end(), this);
660 
661     free_code_space_.Merge({new_mem.address(), new_mem.end()});
662     mem = free_code_space_.Allocate(size);
663     if (mem.is_empty()) return kNullAddress;
664   }
665   Address commit_start = RoundUp(mem.start, AllocatePageSize());
666   Address commit_end = RoundUp(mem.end, AllocatePageSize());
667   // {commit_start} will be either mem.start or the start of the next page.
668   // {commit_end} will be the start of the page after the one in which
669   // the allocation ends.
670   // We start from an aligned start, and we know we allocated vmem in
671   // page multiples.
672   // We just need to commit what's not committed. The page in which we
673   // start is already committed (or we start at the beginning of a page).
674   // The end needs to be committed all through the end of the page.
675   if (commit_start < commit_end) {
676 #if V8_OS_WIN
677     // On Windows, we cannot commit a range that straddles different
678     // reservations of virtual memory. Because we bump-allocate, and because, if
679     // we need more memory, we append that memory at the end of the
680     // owned_code_space_ list, we traverse that list in reverse order to find
681     // the reservation(s) that guide how to chunk the region to commit.
682     for (auto it = owned_code_space_.crbegin(),
683               rend = owned_code_space_.crend();
684          it != rend && commit_start < commit_end; ++it) {
685       if (commit_end > it->end() || it->address() >= commit_end) continue;
686       Address start = std::max(commit_start, it->address());
687       size_t commit_size = static_cast<size_t>(commit_end - start);
688       DCHECK(IsAligned(commit_size, AllocatePageSize()));
689       if (!wasm_code_manager_->Commit(start, commit_size)) {
690         return kNullAddress;
691       }
692       committed_code_space_.fetch_add(commit_size);
693       commit_end = start;
694     }
695 #else
696     size_t commit_size = static_cast<size_t>(commit_end - commit_start);
697     DCHECK(IsAligned(commit_size, AllocatePageSize()));
698     if (!wasm_code_manager_->Commit(commit_start, commit_size)) {
699       return kNullAddress;
700     }
701     committed_code_space_.fetch_add(commit_size);
702 #endif
703   }
704   DCHECK(IsAligned(mem.start, kCodeAlignment));
705   allocated_code_space_.Merge(std::move(mem));
706   TRACE_HEAP("Code alloc for %p: %" PRIuPTR ",+%zu\n", this, mem.start, size);
707   return mem.start;
708 }
709 
Lookup(Address pc) const710 WasmCode* NativeModule::Lookup(Address pc) const {
711   base::LockGuard<base::Mutex> lock(&allocation_mutex_);
712   if (owned_code_.empty()) return nullptr;
713   auto iter = std::upper_bound(owned_code_.begin(), owned_code_.end(), pc,
714                                WasmCodeUniquePtrComparator());
715   if (iter == owned_code_.begin()) return nullptr;
716   --iter;
717   WasmCode* candidate = iter->get();
718   DCHECK_NOT_NULL(candidate);
719   return candidate->contains(pc) ? candidate : nullptr;
720 }
721 
GetCallTargetForFunction(uint32_t func_index) const722 Address NativeModule::GetCallTargetForFunction(uint32_t func_index) const {
723   // TODO(clemensh): Measure performance win of returning instruction start
724   // directly if we have turbofan code. Downside: Redirecting functions (e.g.
725   // for debugging) gets much harder.
726 
727   // Return the jump table slot for that function index.
728   DCHECK_NOT_NULL(jump_table_);
729   uint32_t slot_idx = func_index - module_->num_imported_functions;
730   uint32_t slot_offset = JumpTableAssembler::SlotIndexToOffset(slot_idx);
731   DCHECK_LT(slot_offset, jump_table_->instructions().size());
732   return jump_table_->instruction_start() + slot_offset;
733 }
734 
GetFunctionIndexFromJumpTableSlot(Address slot_address) const735 uint32_t NativeModule::GetFunctionIndexFromJumpTableSlot(
736     Address slot_address) const {
737   DCHECK(is_jump_table_slot(slot_address));
738   uint32_t slot_offset =
739       static_cast<uint32_t>(slot_address - jump_table_->instruction_start());
740   uint32_t slot_idx = JumpTableAssembler::SlotOffsetToIndex(slot_offset);
741   DCHECK_LT(slot_idx, module_->num_declared_functions);
742   return module_->num_imported_functions + slot_idx;
743 }
744 
DisableTrapHandler()745 void NativeModule::DisableTrapHandler() {
746   // Switch {use_trap_handler_} from true to false.
747   DCHECK(use_trap_handler_);
748   use_trap_handler_ = false;
749 
750   // Clear the code table (just to increase the chances to hit an error if we
751   // forget to re-add all code).
752   uint32_t num_wasm_functions = module_->num_declared_functions;
753   memset(code_table_.get(), 0, num_wasm_functions * sizeof(WasmCode*));
754 
755   // TODO(clemensh): Actually free the owned code, such that the memory can be
756   // recycled.
757 }
758 
~NativeModule()759 NativeModule::~NativeModule() {
760   TRACE_HEAP("Deleting native module: %p\n", reinterpret_cast<void*>(this));
761   compilation_state_.reset();  // Cancels tasks, needs to be done first.
762   wasm_code_manager_->FreeNativeModule(this);
763 }
764 
WasmCodeManager(WasmMemoryTracker * memory_tracker,size_t max_committed)765 WasmCodeManager::WasmCodeManager(WasmMemoryTracker* memory_tracker,
766                                  size_t max_committed)
767     : memory_tracker_(memory_tracker),
768       remaining_uncommitted_code_space_(max_committed) {
769   DCHECK_LE(max_committed, kMaxWasmCodeMemory);
770 }
771 
Commit(Address start,size_t size)772 bool WasmCodeManager::Commit(Address start, size_t size) {
773   DCHECK(IsAligned(start, AllocatePageSize()));
774   DCHECK(IsAligned(size, AllocatePageSize()));
775   // Reserve the size. Use CAS loop to avoid underflow on
776   // {remaining_uncommitted_}. Temporary underflow would allow concurrent
777   // threads to over-commit.
778   while (true) {
779     size_t old_value = remaining_uncommitted_code_space_.load();
780     if (old_value < size) return false;
781     if (remaining_uncommitted_code_space_.compare_exchange_weak(
782             old_value, old_value - size)) {
783       break;
784     }
785   }
786   PageAllocator::Permission permission = FLAG_wasm_write_protect_code_memory
787                                              ? PageAllocator::kReadWrite
788                                              : PageAllocator::kReadWriteExecute;
789 
790   bool ret = SetPermissions(start, size, permission);
791   TRACE_HEAP("Setting rw permissions for %p:%p\n",
792              reinterpret_cast<void*>(start),
793              reinterpret_cast<void*>(start + size));
794 
795   if (!ret) {
796     // Highly unlikely.
797     remaining_uncommitted_code_space_.fetch_add(size);
798     return false;
799   }
800   return ret;
801 }
802 
AssignRanges(Address start,Address end,NativeModule * native_module)803 void WasmCodeManager::AssignRanges(Address start, Address end,
804                                    NativeModule* native_module) {
805   lookup_map_.insert(std::make_pair(start, std::make_pair(end, native_module)));
806 }
807 
TryAllocate(size_t size,VirtualMemory * ret,void * hint)808 void WasmCodeManager::TryAllocate(size_t size, VirtualMemory* ret, void* hint) {
809   DCHECK_GT(size, 0);
810   size = RoundUp(size, AllocatePageSize());
811   DCHECK(!ret->IsReserved());
812   if (!memory_tracker_->ReserveAddressSpace(size)) return;
813   if (hint == nullptr) hint = GetRandomMmapAddr();
814 
815   if (!AlignedAllocVirtualMemory(size, static_cast<size_t>(AllocatePageSize()),
816                                  hint, ret)) {
817     DCHECK(!ret->IsReserved());
818     memory_tracker_->ReleaseReservation(size);
819   }
820   TRACE_HEAP("VMem alloc: %p:%p (%zu)\n",
821              reinterpret_cast<void*>(ret->address()),
822              reinterpret_cast<void*>(ret->end()), ret->size());
823 }
824 
SampleModuleSizes(Isolate * isolate) const825 void WasmCodeManager::SampleModuleSizes(Isolate* isolate) const {
826   base::LockGuard<base::Mutex> lock(&native_modules_mutex_);
827   for (NativeModule* native_module : native_modules_) {
828     int code_size =
829         static_cast<int>(native_module->committed_code_space_.load() / MB);
830     isolate->counters()->wasm_module_code_size_mb()->AddSample(code_size);
831   }
832 }
833 
834 namespace {
835 
ModuleSamplingCallback(v8::Isolate * v8_isolate,v8::GCType type,v8::GCCallbackFlags flags,void * data)836 void ModuleSamplingCallback(v8::Isolate* v8_isolate, v8::GCType type,
837                             v8::GCCallbackFlags flags, void* data) {
838   Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
839   isolate->wasm_engine()->code_manager()->SampleModuleSizes(isolate);
840 }
841 
842 }  // namespace
843 
844 // static
InstallSamplingGCCallback(Isolate * isolate)845 void WasmCodeManager::InstallSamplingGCCallback(Isolate* isolate) {
846   isolate->heap()->AddGCEpilogueCallback(ModuleSamplingCallback,
847                                          v8::kGCTypeMarkSweepCompact, nullptr);
848 }
849 
850 // static
EstimateNativeModuleSize(const WasmModule * module)851 size_t WasmCodeManager::EstimateNativeModuleSize(const WasmModule* module) {
852   constexpr size_t kCodeSizeMultiplier = 4;
853   constexpr size_t kImportSize = 32 * kPointerSize;
854 
855   uint32_t num_wasm_functions = module->num_declared_functions;
856 
857   size_t estimate =
858       AllocatePageSize() /* TODO(titzer): 1 page spot bonus */ +
859       sizeof(NativeModule) +
860       (sizeof(WasmCode*) * num_wasm_functions /* code table size */) +
861       (sizeof(WasmCode) * num_wasm_functions /* code object size */) +
862       (kImportSize * module->num_imported_functions /* import size */) +
863       (JumpTableAssembler::SizeForNumberOfSlots(num_wasm_functions));
864 
865   for (auto& function : module->functions) {
866     estimate += kCodeSizeMultiplier * function.code.length();
867   }
868 
869   return estimate;
870 }
871 
ShouldForceCriticalMemoryPressureNotification()872 bool WasmCodeManager::ShouldForceCriticalMemoryPressureNotification() {
873   base::LockGuard<base::Mutex> lock(&native_modules_mutex_);
874   // TODO(titzer): we force a critical memory pressure notification
875   // when the code space is almost exhausted, but only upon the next module
876   // creation. This is only for one isolate, and it should really do this for
877   // all isolates, at the point of commit.
878   constexpr size_t kCriticalThreshold = 32 * 1024 * 1024;
879   return native_modules_.size() > 1 &&
880          remaining_uncommitted_code_space_.load() < kCriticalThreshold;
881 }
882 
NewNativeModule(Isolate * isolate,const WasmFeatures & enabled,size_t memory_estimate,bool can_request_more,std::shared_ptr<const WasmModule> module,const ModuleEnv & env)883 std::unique_ptr<NativeModule> WasmCodeManager::NewNativeModule(
884     Isolate* isolate, const WasmFeatures& enabled, size_t memory_estimate,
885     bool can_request_more, std::shared_ptr<const WasmModule> module,
886     const ModuleEnv& env) {
887   if (ShouldForceCriticalMemoryPressureNotification()) {
888     (reinterpret_cast<v8::Isolate*>(isolate))
889         ->MemoryPressureNotification(MemoryPressureLevel::kCritical);
890   }
891 
892   // If the code must be contiguous, reserve enough address space up front.
893   size_t vmem_size = kRequiresCodeRange ? kMaxWasmCodeMemory : memory_estimate;
894   // Try up to three times; getting rid of dead JSArrayBuffer allocations might
895   // require two GCs because the first GC maybe incremental and may have
896   // floating garbage.
897   static constexpr int kAllocationRetries = 2;
898   VirtualMemory mem;
899   for (int retries = 0;; ++retries) {
900     TryAllocate(vmem_size, &mem);
901     if (mem.IsReserved()) break;
902     if (retries == kAllocationRetries) {
903       V8::FatalProcessOutOfMemory(isolate, "WasmCodeManager::NewNativeModule");
904       UNREACHABLE();
905     }
906     // Run one GC, then try the allocation again.
907     isolate->heap()->MemoryPressureNotification(MemoryPressureLevel::kCritical,
908                                                 true);
909   }
910 
911   Address start = mem.address();
912   size_t size = mem.size();
913   Address end = mem.end();
914   std::unique_ptr<NativeModule> ret(new NativeModule(
915       isolate, enabled, can_request_more, &mem, this, std::move(module), env));
916   TRACE_HEAP("New NativeModule %p: Mem: %" PRIuPTR ",+%zu\n", this, start,
917              size);
918   base::LockGuard<base::Mutex> lock(&native_modules_mutex_);
919   AssignRanges(start, end, ret.get());
920   native_modules_.emplace(ret.get());
921   return ret;
922 }
923 
SetExecutable(bool executable)924 bool NativeModule::SetExecutable(bool executable) {
925   if (is_executable_ == executable) return true;
926   TRACE_HEAP("Setting module %p as executable: %d.\n", this, executable);
927 
928   if (FLAG_wasm_write_protect_code_memory) {
929     PageAllocator::Permission permission =
930         executable ? PageAllocator::kReadExecute : PageAllocator::kReadWrite;
931 #if V8_OS_WIN
932     // On windows, we need to switch permissions per separate virtual memory
933     // reservation. This is really just a problem when the NativeModule is
934     // growable (meaning can_request_more_memory_). That's 32-bit in production,
935     // or unittests.
936     // For now, in that case, we commit at reserved memory granularity.
937     // Technically, that may be a waste, because we may reserve more than we
938     // use. On 32-bit though, the scarce resource is the address space -
939     // committed or not.
940     if (can_request_more_memory_) {
941       for (auto& vmem : owned_code_space_) {
942         if (!SetPermissions(vmem.address(), vmem.size(), permission)) {
943           return false;
944         }
945         TRACE_HEAP("Set %p:%p to executable:%d\n", vmem.address(), vmem.end(),
946                    executable);
947       }
948       is_executable_ = executable;
949       return true;
950     }
951 #endif
952     for (auto& range : allocated_code_space_.ranges()) {
953       // allocated_code_space_ is fine-grained, so we need to
954       // page-align it.
955       size_t range_size = RoundUp(range.size(), AllocatePageSize());
956       if (!SetPermissions(range.start, range_size, permission)) {
957         return false;
958       }
959       TRACE_HEAP("Set %p:%p to executable:%d\n",
960                  reinterpret_cast<void*>(range.start),
961                  reinterpret_cast<void*>(range.end), executable);
962     }
963   }
964   is_executable_ = executable;
965   return true;
966 }
967 
FreeNativeModule(NativeModule * native_module)968 void WasmCodeManager::FreeNativeModule(NativeModule* native_module) {
969   base::LockGuard<base::Mutex> lock(&native_modules_mutex_);
970   DCHECK_EQ(1, native_modules_.count(native_module));
971   native_modules_.erase(native_module);
972   TRACE_HEAP("Freeing NativeModule %p\n", this);
973   for (auto& vmem : native_module->owned_code_space_) {
974     lookup_map_.erase(vmem.address());
975     Free(&vmem);
976     DCHECK(!vmem.IsReserved());
977   }
978   native_module->owned_code_space_.clear();
979 
980   size_t code_size = native_module->committed_code_space_.load();
981   DCHECK(IsAligned(code_size, AllocatePageSize()));
982   remaining_uncommitted_code_space_.fetch_add(code_size);
983 }
984 
985 // TODO(wasm): We can make this more efficient if needed. For
986 // example, we can preface the first instruction with a pointer to
987 // the WasmCode. In the meantime, we have a separate API so we can
988 // easily identify those places where we know we have the first
989 // instruction PC.
GetCodeFromStartAddress(Address pc) const990 WasmCode* WasmCodeManager::GetCodeFromStartAddress(Address pc) const {
991   WasmCode* code = LookupCode(pc);
992   // This method can only be called for valid instruction start addresses.
993   DCHECK_NOT_NULL(code);
994   DCHECK_EQ(pc, code->instruction_start());
995   return code;
996 }
997 
LookupNativeModule(Address pc) const998 NativeModule* WasmCodeManager::LookupNativeModule(Address pc) const {
999   base::LockGuard<base::Mutex> lock(&native_modules_mutex_);
1000   if (lookup_map_.empty()) return nullptr;
1001 
1002   auto iter = lookup_map_.upper_bound(pc);
1003   if (iter == lookup_map_.begin()) return nullptr;
1004   --iter;
1005   Address range_start = iter->first;
1006   Address range_end = iter->second.first;
1007   NativeModule* candidate = iter->second.second;
1008 
1009   DCHECK_NOT_NULL(candidate);
1010   return range_start <= pc && pc < range_end ? candidate : nullptr;
1011 }
1012 
LookupCode(Address pc) const1013 WasmCode* WasmCodeManager::LookupCode(Address pc) const {
1014   NativeModule* candidate = LookupNativeModule(pc);
1015   return candidate ? candidate->Lookup(pc) : nullptr;
1016 }
1017 
Free(VirtualMemory * mem)1018 void WasmCodeManager::Free(VirtualMemory* mem) {
1019   DCHECK(mem->IsReserved());
1020   void* start = reinterpret_cast<void*>(mem->address());
1021   void* end = reinterpret_cast<void*>(mem->end());
1022   size_t size = mem->size();
1023   mem->Free();
1024   memory_tracker_->ReleaseReservation(size);
1025   TRACE_HEAP("VMem Release: %p:%p (%zu)\n", start, end, size);
1026 }
1027 
remaining_uncommitted_code_space() const1028 size_t WasmCodeManager::remaining_uncommitted_code_space() const {
1029   return remaining_uncommitted_code_space_.load();
1030 }
1031 
1032 // TODO(v8:7424): Code protection scopes are not yet supported with shared code
1033 // enabled and need to be revisited to work with --wasm-shared-code as well.
NativeModuleModificationScope(NativeModule * native_module)1034 NativeModuleModificationScope::NativeModuleModificationScope(
1035     NativeModule* native_module)
1036     : native_module_(native_module) {
1037   if (FLAG_wasm_write_protect_code_memory && native_module_ &&
1038       (native_module_->modification_scope_depth_++) == 0) {
1039     bool success = native_module_->SetExecutable(false);
1040     CHECK(success);
1041   }
1042 }
1043 
~NativeModuleModificationScope()1044 NativeModuleModificationScope::~NativeModuleModificationScope() {
1045   if (FLAG_wasm_write_protect_code_memory && native_module_ &&
1046       (native_module_->modification_scope_depth_--) == 1) {
1047     bool success = native_module_->SetExecutable(true);
1048     CHECK(success);
1049   }
1050 }
1051 
1052 }  // namespace wasm
1053 }  // namespace internal
1054 }  // namespace v8
1055 #undef TRACE_HEAP
1056