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
51namespace v8 {
52namespace internal {
53
54AssemblerOptions 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  options.code_range_base = isolate->heap()->code_range_base();
76#endif
77  options.short_builtin_calls =
78      isolate->is_short_builtin_calls_enabled() &&
79      !generating_embedded_builtin &&
80      (options.code_range_base != kNullAddress) &&
81      // Serialization of RUNTIME_ENTRY reloc infos is not supported yet.
82      !serializer;
83  return options;
84}
85
86AssemblerOptions AssemblerOptions::DefaultForOffHeapTrampoline(
87    Isolate* isolate) {
88  AssemblerOptions options = AssemblerOptions::Default(isolate);
89  // Off-heap trampolines may not contain any metadata since their metadata
90  // offsets refer to the off-heap metadata area.
91  options.emit_code_comments = false;
92  return options;
93}
94
95namespace {
96
97class DefaultAssemblerBuffer : public AssemblerBuffer {
98 public:
99  explicit DefaultAssemblerBuffer(int size)
100      : buffer_(base::OwnedVector<uint8_t>::NewForOverwrite(
101            std::max(AssemblerBase::kMinimalBufferSize, size))) {
102#ifdef DEBUG
103    ZapCode(reinterpret_cast<Address>(buffer_.start()), buffer_.size());
104#endif
105  }
106
107  byte* start() const override { return buffer_.start(); }
108
109  int size() const override { return static_cast<int>(buffer_.size()); }
110
111  std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
112    DCHECK_LT(size(), new_size);
113    return std::make_unique<DefaultAssemblerBuffer>(new_size);
114  }
115
116 private:
117  base::OwnedVector<uint8_t> buffer_;
118};
119
120class ExternalAssemblerBufferImpl : public AssemblerBuffer {
121 public:
122  ExternalAssemblerBufferImpl(byte* start, int size)
123      : start_(start), size_(size) {}
124
125  byte* start() const override { return start_; }
126
127  int size() const override { return size_; }
128
129  std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
130    FATAL("Cannot grow external assembler buffer");
131  }
132
133  void* operator new(std::size_t count);
134  void operator delete(void* ptr) noexcept;
135
136 private:
137  byte* const start_;
138  const int size_;
139};
140
141static thread_local std::aligned_storage_t<sizeof(ExternalAssemblerBufferImpl),
142                                           alignof(ExternalAssemblerBufferImpl)>
143    tls_singleton_storage;
144
145static thread_local bool tls_singleton_taken{false};
146
147void* ExternalAssemblerBufferImpl::operator new(std::size_t count) {
148  DCHECK_EQ(count, sizeof(ExternalAssemblerBufferImpl));
149  if (V8_LIKELY(!tls_singleton_taken)) {
150    tls_singleton_taken = true;
151    return &tls_singleton_storage;
152  }
153  return ::operator new(count);
154}
155
156void ExternalAssemblerBufferImpl::operator delete(void* ptr) noexcept {
157  if (V8_LIKELY(ptr == &tls_singleton_storage)) {
158    DCHECK(tls_singleton_taken);
159    tls_singleton_taken = false;
160    return;
161  }
162  ::operator delete(ptr);
163}
164
165}  // namespace
166
167std::unique_ptr<AssemblerBuffer> ExternalAssemblerBuffer(void* start,
168                                                         int size) {
169  return std::make_unique<ExternalAssemblerBufferImpl>(
170      reinterpret_cast<byte*>(start), size);
171}
172
173std::unique_ptr<AssemblerBuffer> NewAssemblerBuffer(int size) {
174  return std::make_unique<DefaultAssemblerBuffer>(size);
175}
176
177// -----------------------------------------------------------------------------
178// Implementation of AssemblerBase
179
180// static
181constexpr int AssemblerBase::kMinimalBufferSize;
182
183// static
184constexpr int AssemblerBase::kDefaultBufferSize;
185
186AssemblerBase::AssemblerBase(const AssemblerOptions& options,
187                             std::unique_ptr<AssemblerBuffer> buffer)
188    : buffer_(std::move(buffer)),
189      options_(options),
190      enabled_cpu_features_(0),
191      predictable_code_size_(false),
192      constant_pool_available_(false),
193      jump_optimization_info_(nullptr) {
194  if (!buffer_) buffer_ = NewAssemblerBuffer(kDefaultBufferSize);
195  buffer_start_ = buffer_->start();
196  pc_ = buffer_start_;
197}
198
199AssemblerBase::~AssemblerBase() = default;
200
201void AssemblerBase::Print(Isolate* isolate) {
202  StdoutStream os;
203  v8::internal::Disassembler::Decode(isolate, os, buffer_start_, pc_);
204}
205
206// -----------------------------------------------------------------------------
207// Implementation of CpuFeatureScope
208
209#ifdef DEBUG
210CpuFeatureScope::CpuFeatureScope(AssemblerBase* assembler, CpuFeature f,
211                                 CheckPolicy check)
212    : assembler_(assembler) {
213  DCHECK_IMPLIES(check == kCheckSupported, CpuFeatures::IsSupported(f));
214  old_enabled_ = assembler_->enabled_cpu_features();
215  assembler_->EnableCpuFeature(f);
216}
217
218CpuFeatureScope::~CpuFeatureScope() {
219  assembler_->set_enabled_cpu_features(old_enabled_);
220}
221#endif
222
223bool CpuFeatures::initialized_ = false;
224bool CpuFeatures::supports_wasm_simd_128_ = false;
225bool CpuFeatures::supports_cetss_ = false;
226unsigned CpuFeatures::supported_ = 0;
227unsigned CpuFeatures::icache_line_size_ = 0;
228unsigned CpuFeatures::dcache_line_size_ = 0;
229
230HeapObjectRequest::HeapObjectRequest(double heap_number, int offset)
231    : kind_(kHeapNumber), offset_(offset) {
232  value_.heap_number = heap_number;
233  DCHECK(!IsSmiDouble(value_.heap_number));
234}
235
236HeapObjectRequest::HeapObjectRequest(const StringConstantBase* string,
237                                     int offset)
238    : kind_(kStringConstant), offset_(offset) {
239  value_.string = string;
240  DCHECK_NOT_NULL(value_.string);
241}
242
243// Platform specific but identical code for all the platforms.
244
245void Assembler::RecordDeoptReason(DeoptimizeReason reason, uint32_t node_id,
246                                  SourcePosition position, int id) {
247  EnsureSpace ensure_space(this);
248  RecordRelocInfo(RelocInfo::DEOPT_SCRIPT_OFFSET, position.ScriptOffset());
249  RecordRelocInfo(RelocInfo::DEOPT_INLINING_ID, position.InliningId());
250  RecordRelocInfo(RelocInfo::DEOPT_REASON, static_cast<int>(reason));
251  RecordRelocInfo(RelocInfo::DEOPT_ID, id);
252#ifdef DEBUG
253  RecordRelocInfo(RelocInfo::DEOPT_NODE_ID, node_id);
254#endif  // DEBUG
255}
256
257void Assembler::DataAlign(int m) {
258  DCHECK(m >= 2 && base::bits::IsPowerOfTwo(m));
259  while ((pc_offset() & (m - 1)) != 0) {
260    // Pad with 0xcc (= int3 on ia32 and x64); the primary motivation is that
261    // the disassembler expects to find valid instructions, but this is also
262    // nice from a security point of view.
263    db(0xcc);
264  }
265}
266
267void AssemblerBase::RequestHeapObject(HeapObjectRequest request) {
268  request.set_offset(pc_offset());
269  heap_object_requests_.push_front(request);
270}
271
272int AssemblerBase::AddCodeTarget(Handle<CodeT> target) {
273  int current = static_cast<int>(code_targets_.size());
274  if (current > 0 && !target.is_null() &&
275      code_targets_.back().address() == target.address()) {
276    // Optimization if we keep jumping to the same code target.
277    return current - 1;
278  } else {
279    code_targets_.push_back(target);
280    return current;
281  }
282}
283
284Handle<CodeT> AssemblerBase::GetCodeTarget(intptr_t code_target_index) const {
285  DCHECK_LT(static_cast<size_t>(code_target_index), code_targets_.size());
286  return code_targets_[code_target_index];
287}
288
289AssemblerBase::EmbeddedObjectIndex AssemblerBase::AddEmbeddedObject(
290    Handle<HeapObject> object) {
291  EmbeddedObjectIndex current = embedded_objects_.size();
292  // Do not deduplicate invalid handles, they are to heap object requests.
293  if (!object.is_null()) {
294    auto entry = embedded_objects_map_.find(object);
295    if (entry != embedded_objects_map_.end()) {
296      return entry->second;
297    }
298    embedded_objects_map_[object] = current;
299  }
300  embedded_objects_.push_back(object);
301  return current;
302}
303
304Handle<HeapObject> AssemblerBase::GetEmbeddedObject(
305    EmbeddedObjectIndex index) const {
306  DCHECK_LT(index, embedded_objects_.size());
307  return embedded_objects_[index];
308}
309
310
311int Assembler::WriteCodeComments() {
312  if (!FLAG_code_comments) return 0;
313  CHECK_IMPLIES(code_comments_writer_.entry_count() > 0,
314                options().emit_code_comments);
315  if (code_comments_writer_.entry_count() == 0) return 0;
316  int offset = pc_offset();
317  code_comments_writer_.Emit(this);
318  int size = pc_offset() - offset;
319  DCHECK_EQ(size, code_comments_writer_.section_size());
320  return size;
321}
322
323#ifdef V8_CODE_COMMENTS
324int Assembler::CodeComment::depth() const { return assembler_->comment_depth_; }
325void Assembler::CodeComment::Open(const std::string& comment) {
326  std::stringstream sstream;
327  sstream << std::setfill(' ') << std::setw(depth() * kIndentWidth + 2);
328  sstream << "[ " << comment;
329  assembler_->comment_depth_++;
330  assembler_->RecordComment(sstream.str());
331}
332
333void Assembler::CodeComment::Close() {
334  assembler_->comment_depth_--;
335  std::string comment = "]";
336  comment.insert(0, depth() * kIndentWidth, ' ');
337  DCHECK_LE(0, depth());
338  assembler_->RecordComment(comment);
339}
340#endif
341
342}  // namespace internal
343}  // namespace v8
344