1 // Copyright (c) 1994-2006 Sun Microsystems Inc.
2 // All Rights Reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // - Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 //
11 // - Redistribution in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // - Neither the name of Sun Microsystems or the names of contributors may
16 // be used to endorse or promote products derived from this software without
17 // specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 // The original source code covered by the above license above has been
32 // modified significantly by Google Inc.
33 // Copyright 2012 the V8 project authors. All rights reserved.
34 
35 #include "src/codegen/assembler.h"
36 
37 #ifdef V8_CODE_COMMENTS
38 #include <iomanip>
39 #endif
40 #include "src/base/vector.h"
41 #include "src/codegen/assembler-inl.h"
42 #include "src/codegen/string-constants.h"
43 #include "src/deoptimizer/deoptimizer.h"
44 #include "src/diagnostics/disassembler.h"
45 #include "src/execution/isolate.h"
46 #include "src/heap/heap-inl.h"  // For MemoryAllocator. TODO(jkummerow): Drop.
47 #include "src/snapshot/embedded/embedded-data.h"
48 #include "src/snapshot/snapshot.h"
49 #include "src/utils/ostreams.h"
50 
51 namespace v8 {
52 namespace internal {
53 
Default(Isolate * isolate)54 AssemblerOptions AssemblerOptions::Default(Isolate* isolate) {
55   AssemblerOptions options;
56   const bool serializer = isolate->serializer_enabled();
57   const bool generating_embedded_builtin =
58       isolate->IsGeneratingEmbeddedBuiltins();
59   options.record_reloc_info_for_serialization = serializer;
60   options.enable_root_relative_access =
61       !serializer && !generating_embedded_builtin;
62 #ifdef USE_SIMULATOR
63   // Even though the simulator is enabled, we may still need to generate code
64   // that may need to run on both the simulator and real hardware. For example,
65   // if we are cross-compiling and embedding a script into the snapshot, the
66   // script will need to run on the host causing the embedded builtins to run in
67   // the simulator. While the final cross-compiled V8 will not have a simulator.
68 
69   // So here we enable simulator specific code if not generating the snapshot or
70   // if we are but we are targetting the simulator *only*.
71   options.enable_simulator_code = !serializer || FLAG_target_is_simulator;
72 #endif
73   options.inline_offheap_trampolines &= !generating_embedded_builtin;
74 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64
75   const base::AddressRegion& code_range = isolate->heap()->code_region();
76   DCHECK_IMPLIES(code_range.begin() != kNullAddress, !code_range.is_empty());
77   options.code_range_start = code_range.begin();
78 #endif
79   options.short_builtin_calls =
80       isolate->is_short_builtin_calls_enabled() &&
81       !generating_embedded_builtin &&
82       (options.code_range_start != kNullAddress) &&
83       // Serialization of RUNTIME_ENTRY reloc infos is not supported yet.
84       !serializer;
85   return options;
86 }
87 
DefaultForOffHeapTrampoline(Isolate * isolate)88 AssemblerOptions AssemblerOptions::DefaultForOffHeapTrampoline(
89     Isolate* isolate) {
90   AssemblerOptions options = AssemblerOptions::Default(isolate);
91   // Off-heap trampolines may not contain any metadata since their metadata
92   // offsets refer to the off-heap metadata area.
93   options.emit_code_comments = false;
94   return options;
95 }
96 
97 namespace {
98 
99 class DefaultAssemblerBuffer : public AssemblerBuffer {
100  public:
DefaultAssemblerBuffer(int size)101   explicit DefaultAssemblerBuffer(int size)
102       : buffer_(base::OwnedVector<uint8_t>::NewForOverwrite(
103             std::max(AssemblerBase::kMinimalBufferSize, size))) {
104 #ifdef DEBUG
105     ZapCode(reinterpret_cast<Address>(buffer_.start()), buffer_.size());
106 #endif
107   }
108 
start() const109   byte* start() const override { return buffer_.start(); }
110 
size() const111   int size() const override { return static_cast<int>(buffer_.size()); }
112 
Grow(int new_size)113   std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
114     DCHECK_LT(size(), new_size);
115     return std::make_unique<DefaultAssemblerBuffer>(new_size);
116   }
117 
118  private:
119   base::OwnedVector<uint8_t> buffer_;
120 };
121 
122 class ExternalAssemblerBufferImpl : public AssemblerBuffer {
123  public:
ExternalAssemblerBufferImpl(byte * start,int size)124   ExternalAssemblerBufferImpl(byte* start, int size)
125       : start_(start), size_(size) {}
126 
start() const127   byte* start() const override { return start_; }
128 
size() const129   int size() const override { return size_; }
130 
Grow(int new_size)131   std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
132     FATAL("Cannot grow external assembler buffer");
133   }
134 
135   void* operator new(std::size_t count);
136   void operator delete(void* ptr) noexcept;
137 
138  private:
139   byte* const start_;
140   const int size_;
141 };
142 
143 class OnHeapAssemblerBuffer : public AssemblerBuffer {
144  public:
OnHeapAssemblerBuffer(Isolate * isolate,Handle<Code> code,int size,int gc_count)145   OnHeapAssemblerBuffer(Isolate* isolate, Handle<Code> code, int size,
146                         int gc_count)
147       : isolate_(isolate), code_(code), size_(size), gc_count_(gc_count) {}
148 
start() const149   byte* start() const override {
150     return reinterpret_cast<byte*>(code_->raw_instruction_start());
151   }
152 
size() const153   int size() const override { return size_; }
154 
Grow(int new_size)155   std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
156     DCHECK_LT(size(), new_size);
157     Heap* heap = isolate_->heap();
158     if (Code::SizeFor(new_size) <
159         heap->MaxRegularHeapObjectSize(AllocationType::kCode)) {
160       MaybeHandle<Code> code =
161           isolate_->factory()->NewEmptyCode(CodeKind::BASELINE, new_size);
162       if (!code.is_null()) {
163         return std::make_unique<OnHeapAssemblerBuffer>(
164             isolate_, code.ToHandleChecked(), new_size, heap->gc_count());
165       }
166     }
167     // We fall back to the slow path using the default assembler buffer and
168     // compile the code off the GC heap.
169     return std::make_unique<DefaultAssemblerBuffer>(new_size);
170   }
171 
IsOnHeap() const172   bool IsOnHeap() const override { return true; }
173 
OnHeapGCCount() const174   int OnHeapGCCount() const override { return gc_count_; }
175 
code() const176   MaybeHandle<Code> code() const override { return code_; }
177 
178  private:
179   Isolate* isolate_;
180   Handle<Code> code_;
181   const int size_;
182   const int gc_count_;
183 };
184 
185 static thread_local std::aligned_storage_t<sizeof(ExternalAssemblerBufferImpl),
186                                            alignof(ExternalAssemblerBufferImpl)>
187     tls_singleton_storage;
188 
189 static thread_local bool tls_singleton_taken{false};
190 
operator new(std::size_t count)191 void* ExternalAssemblerBufferImpl::operator new(std::size_t count) {
192   DCHECK_EQ(count, sizeof(ExternalAssemblerBufferImpl));
193   if (V8_LIKELY(!tls_singleton_taken)) {
194     tls_singleton_taken = true;
195     return &tls_singleton_storage;
196   }
197   return ::operator new(count);
198 }
199 
operator delete(void * ptr)200 void ExternalAssemblerBufferImpl::operator delete(void* ptr) noexcept {
201   if (V8_LIKELY(ptr == &tls_singleton_storage)) {
202     DCHECK(tls_singleton_taken);
203     tls_singleton_taken = false;
204     return;
205   }
206   ::operator delete(ptr);
207 }
208 
209 }  // namespace
210 
ExternalAssemblerBuffer(void * start,int size)211 std::unique_ptr<AssemblerBuffer> ExternalAssemblerBuffer(void* start,
212                                                          int size) {
213   return std::make_unique<ExternalAssemblerBufferImpl>(
214       reinterpret_cast<byte*>(start), size);
215 }
216 
NewAssemblerBuffer(int size)217 std::unique_ptr<AssemblerBuffer> NewAssemblerBuffer(int size) {
218   return std::make_unique<DefaultAssemblerBuffer>(size);
219 }
220 
NewOnHeapAssemblerBuffer(Isolate * isolate,int estimated)221 std::unique_ptr<AssemblerBuffer> NewOnHeapAssemblerBuffer(Isolate* isolate,
222                                                           int estimated) {
223   int size = std::max(AssemblerBase::kMinimalBufferSize, estimated);
224   MaybeHandle<Code> code =
225       isolate->factory()->NewEmptyCode(CodeKind::BASELINE, size);
226   if (code.is_null()) return {};
227   return std::make_unique<OnHeapAssemblerBuffer>(
228       isolate, code.ToHandleChecked(), size, isolate->heap()->gc_count());
229 }
230 
231 // -----------------------------------------------------------------------------
232 // Implementation of AssemblerBase
233 
234 // static
235 constexpr int AssemblerBase::kMinimalBufferSize;
236 
237 // static
238 constexpr int AssemblerBase::kDefaultBufferSize;
239 
AssemblerBase(const AssemblerOptions & options,std::unique_ptr<AssemblerBuffer> buffer)240 AssemblerBase::AssemblerBase(const AssemblerOptions& options,
241                              std::unique_ptr<AssemblerBuffer> buffer)
242     : buffer_(std::move(buffer)),
243       options_(options),
244       enabled_cpu_features_(0),
245       predictable_code_size_(false),
246       constant_pool_available_(false),
247       jump_optimization_info_(nullptr) {
248   if (!buffer_) buffer_ = NewAssemblerBuffer(kDefaultBufferSize);
249   buffer_start_ = buffer_->start();
250   pc_ = buffer_start_;
251   if (IsOnHeap()) {
252     saved_handles_for_raw_object_ptr_.reserve(
253         kSavedHandleForRawObjectsInitialSize);
254     saved_offsets_for_runtime_entries_.reserve(
255         kSavedOffsetForRuntimeEntriesInitialSize);
256   }
257 }
258 
259 AssemblerBase::~AssemblerBase() = default;
260 
Print(Isolate * isolate)261 void AssemblerBase::Print(Isolate* isolate) {
262   StdoutStream os;
263   v8::internal::Disassembler::Decode(isolate, os, buffer_start_, pc_);
264 }
265 
266 // -----------------------------------------------------------------------------
267 // Implementation of CpuFeatureScope
268 
269 #ifdef DEBUG
CpuFeatureScope(AssemblerBase * assembler,CpuFeature f,CheckPolicy check)270 CpuFeatureScope::CpuFeatureScope(AssemblerBase* assembler, CpuFeature f,
271                                  CheckPolicy check)
272     : assembler_(assembler) {
273   DCHECK_IMPLIES(check == kCheckSupported, CpuFeatures::IsSupported(f));
274   old_enabled_ = assembler_->enabled_cpu_features();
275   assembler_->EnableCpuFeature(f);
276 }
277 
~CpuFeatureScope()278 CpuFeatureScope::~CpuFeatureScope() {
279   assembler_->set_enabled_cpu_features(old_enabled_);
280 }
281 #endif
282 
283 bool CpuFeatures::initialized_ = false;
284 bool CpuFeatures::supports_wasm_simd_128_ = false;
285 unsigned CpuFeatures::supported_ = 0;
286 unsigned CpuFeatures::icache_line_size_ = 0;
287 unsigned CpuFeatures::dcache_line_size_ = 0;
288 
HeapObjectRequest(double heap_number,int offset)289 HeapObjectRequest::HeapObjectRequest(double heap_number, int offset)
290     : kind_(kHeapNumber), offset_(offset) {
291   value_.heap_number = heap_number;
292   DCHECK(!IsSmiDouble(value_.heap_number));
293 }
294 
HeapObjectRequest(const StringConstantBase * string,int offset)295 HeapObjectRequest::HeapObjectRequest(const StringConstantBase* string,
296                                      int offset)
297     : kind_(kStringConstant), offset_(offset) {
298   value_.string = string;
299   DCHECK_NOT_NULL(value_.string);
300 }
301 
302 // Platform specific but identical code for all the platforms.
303 
RecordDeoptReason(DeoptimizeReason reason,uint32_t node_id,SourcePosition position,int id)304 void Assembler::RecordDeoptReason(DeoptimizeReason reason, uint32_t node_id,
305                                   SourcePosition position, int id) {
306   EnsureSpace ensure_space(this);
307   RecordRelocInfo(RelocInfo::DEOPT_SCRIPT_OFFSET, position.ScriptOffset());
308   RecordRelocInfo(RelocInfo::DEOPT_INLINING_ID, position.InliningId());
309   RecordRelocInfo(RelocInfo::DEOPT_REASON, static_cast<int>(reason));
310   RecordRelocInfo(RelocInfo::DEOPT_ID, id);
311 #ifdef DEBUG
312   RecordRelocInfo(RelocInfo::DEOPT_NODE_ID, node_id);
313 #endif  // DEBUG
314 }
315 
DataAlign(int m)316 void Assembler::DataAlign(int m) {
317   DCHECK(m >= 2 && base::bits::IsPowerOfTwo(m));
318   while ((pc_offset() & (m - 1)) != 0) {
319     // Pad with 0xcc (= int3 on ia32 and x64); the primary motivation is that
320     // the disassembler expects to find valid instructions, but this is also
321     // nice from a security point of view.
322     db(0xcc);
323   }
324 }
325 
RequestHeapObject(HeapObjectRequest request)326 void AssemblerBase::RequestHeapObject(HeapObjectRequest request) {
327   request.set_offset(pc_offset());
328   heap_object_requests_.push_front(request);
329 }
330 
AddCodeTarget(Handle<Code> target)331 int AssemblerBase::AddCodeTarget(Handle<Code> target) {
332   int current = static_cast<int>(code_targets_.size());
333   if (current > 0 && !target.is_null() &&
334       code_targets_.back().address() == target.address()) {
335     // Optimization if we keep jumping to the same code target.
336     return current - 1;
337   } else {
338     code_targets_.push_back(target);
339     return current;
340   }
341 }
342 
GetCodeTarget(intptr_t code_target_index) const343 Handle<Code> AssemblerBase::GetCodeTarget(intptr_t code_target_index) const {
344   DCHECK_LT(static_cast<size_t>(code_target_index), code_targets_.size());
345   return code_targets_[code_target_index];
346 }
347 
AddEmbeddedObject(Handle<HeapObject> object)348 AssemblerBase::EmbeddedObjectIndex AssemblerBase::AddEmbeddedObject(
349     Handle<HeapObject> object) {
350   EmbeddedObjectIndex current = embedded_objects_.size();
351   // Do not deduplicate invalid handles, they are to heap object requests.
352   if (!object.is_null()) {
353     auto entry = embedded_objects_map_.find(object);
354     if (entry != embedded_objects_map_.end()) {
355       return entry->second;
356     }
357     embedded_objects_map_[object] = current;
358   }
359   embedded_objects_.push_back(object);
360   return current;
361 }
362 
GetEmbeddedObject(EmbeddedObjectIndex index) const363 Handle<HeapObject> AssemblerBase::GetEmbeddedObject(
364     EmbeddedObjectIndex index) const {
365   DCHECK_LT(index, embedded_objects_.size());
366   return embedded_objects_[index];
367 }
368 
369 
WriteCodeComments()370 int Assembler::WriteCodeComments() {
371   if (!FLAG_code_comments) return 0;
372   CHECK_IMPLIES(code_comments_writer_.entry_count() > 0,
373                 options().emit_code_comments);
374   if (code_comments_writer_.entry_count() == 0) return 0;
375   int offset = pc_offset();
376   code_comments_writer_.Emit(this);
377   int size = pc_offset() - offset;
378   DCHECK_EQ(size, code_comments_writer_.section_size());
379   return size;
380 }
381 
382 #ifdef V8_CODE_COMMENTS
depth() const383 int Assembler::CodeComment::depth() const { return assembler_->comment_depth_; }
Open(const std::string & comment)384 void Assembler::CodeComment::Open(const std::string& comment) {
385   std::stringstream sstream;
386   sstream << std::setfill(' ') << std::setw(depth() * kIndentWidth + 2);
387   sstream << "[ " << comment;
388   assembler_->comment_depth_++;
389   assembler_->RecordComment(sstream.str());
390 }
391 
Close()392 void Assembler::CodeComment::Close() {
393   assembler_->comment_depth_--;
394   std::string comment = "]";
395   comment.insert(0, depth() * kIndentWidth, ' ');
396   DCHECK_LE(0, depth());
397   assembler_->RecordComment(comment);
398 }
399 #endif
400 
401 }  // namespace internal
402 }  // namespace v8
403