xref: /third_party/node/deps/v8/src/codegen/assembler.h (revision 1cb0ef41)
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#ifndef V8_CODEGEN_ASSEMBLER_H_
36#define V8_CODEGEN_ASSEMBLER_H_
37
38#include <forward_list>
39#include <memory>
40#include <unordered_map>
41
42#include "src/base/macros.h"
43#include "src/base/memory.h"
44#include "src/codegen/code-comments.h"
45#include "src/codegen/cpu-features.h"
46#include "src/codegen/external-reference.h"
47#include "src/codegen/reglist.h"
48#include "src/codegen/reloc-info.h"
49#include "src/common/globals.h"
50#include "src/deoptimizer/deoptimize-reason.h"
51#include "src/flags/flags.h"
52#include "src/handles/handles.h"
53#include "src/objects/objects.h"
54
55namespace v8 {
56
57// Forward declarations.
58class ApiFunction;
59
60namespace internal {
61
62using base::Memory;
63using base::ReadUnalignedValue;
64using base::WriteUnalignedValue;
65
66// Forward declarations.
67class EmbeddedData;
68class OffHeapInstructionStream;
69class Isolate;
70class SCTableReference;
71class SourcePosition;
72class StatsCounter;
73class StringConstantBase;
74
75// -----------------------------------------------------------------------------
76// Optimization for far-jmp like instructions that can be replaced by shorter.
77
78class JumpOptimizationInfo {
79 public:
80  bool is_collecting() const { return stage_ == kCollection; }
81  bool is_optimizing() const { return stage_ == kOptimization; }
82  void set_optimizing() {
83    DCHECK(is_optimizable());
84    stage_ = kOptimization;
85  }
86
87  bool is_optimizable() const { return optimizable_; }
88  void set_optimizable() {
89    DCHECK(is_collecting());
90    optimizable_ = true;
91  }
92
93  // Used to verify the instruction sequence is always the same in two stages.
94  size_t hash_code() const { return hash_code_; }
95  void set_hash_code(size_t hash_code) { hash_code_ = hash_code; }
96
97  std::vector<uint32_t>& farjmp_bitmap() { return farjmp_bitmap_; }
98
99 private:
100  enum { kCollection, kOptimization } stage_ = kCollection;
101  bool optimizable_ = false;
102  std::vector<uint32_t> farjmp_bitmap_;
103  size_t hash_code_ = 0u;
104};
105
106class HeapObjectRequest {
107 public:
108  explicit HeapObjectRequest(double heap_number, int offset = -1);
109  explicit HeapObjectRequest(const StringConstantBase* string, int offset = -1);
110
111  enum Kind { kHeapNumber, kStringConstant };
112  Kind kind() const { return kind_; }
113
114  double heap_number() const {
115    DCHECK_EQ(kind(), kHeapNumber);
116    return value_.heap_number;
117  }
118
119  const StringConstantBase* string() const {
120    DCHECK_EQ(kind(), kStringConstant);
121    return value_.string;
122  }
123
124  // The code buffer offset at the time of the request.
125  int offset() const {
126    DCHECK_GE(offset_, 0);
127    return offset_;
128  }
129  void set_offset(int offset) {
130    DCHECK_LT(offset_, 0);
131    offset_ = offset;
132    DCHECK_GE(offset_, 0);
133  }
134
135 private:
136  Kind kind_;
137
138  union {
139    double heap_number;
140    const StringConstantBase* string;
141  } value_;
142
143  int offset_;
144};
145
146// -----------------------------------------------------------------------------
147// Platform independent assembler base class.
148
149enum class CodeObjectRequired { kNo, kYes };
150
151struct V8_EXPORT_PRIVATE AssemblerOptions {
152  // Recording reloc info for external references and off-heap targets is
153  // needed whenever code is serialized, e.g. into the snapshot or as a Wasm
154  // module. This flag allows this reloc info to be disabled for code that
155  // will not survive process destruction.
156  bool record_reloc_info_for_serialization = true;
157  // Recording reloc info can be disabled wholesale. This is needed when the
158  // assembler is used on existing code directly (e.g. JumpTableAssembler)
159  // without any buffer to hold reloc information.
160  bool disable_reloc_info_for_patching = false;
161  // Enables root-relative access to arbitrary untagged addresses (usually
162  // external references). Only valid if code will not survive the process.
163  bool enable_root_relative_access = false;
164  // Enables specific assembler sequences only used for the simulator.
165  bool enable_simulator_code = false;
166  // Enables use of isolate-independent constants, indirected through the
167  // root array.
168  // (macro assembler feature).
169  bool isolate_independent_code = false;
170  // Enables the use of isolate-independent builtins through an off-heap
171  // trampoline. (macro assembler feature).
172  bool inline_offheap_trampolines = true;
173  // Enables generation of pc-relative calls to builtins if the off-heap
174  // builtins are guaranteed to be within the reach of pc-relative call or jump
175  // instructions. For example, when the bultins code is re-embedded into the
176  // code range.
177  bool short_builtin_calls = false;
178  // On some platforms, all code is created within a certain address range in
179  // the process, and the base of this code range is configured here.
180  Address code_range_base = 0;
181  // Enable pc-relative calls/jumps on platforms that support it. When setting
182  // this flag, the code range must be small enough to fit all offsets into
183  // the instruction immediates.
184  bool use_pc_relative_calls_and_jumps = false;
185  // Enables the collection of information useful for the generation of unwind
186  // info. This is useful in some platform (Win64) where the unwind info depends
187  // on a function prologue/epilogue.
188  bool collect_win64_unwind_info = false;
189  // Whether to emit code comments.
190  bool emit_code_comments = FLAG_code_comments;
191
192  static AssemblerOptions Default(Isolate* isolate);
193  static AssemblerOptions DefaultForOffHeapTrampoline(Isolate* isolate);
194};
195
196class AssemblerBuffer {
197 public:
198  virtual ~AssemblerBuffer() = default;
199  virtual byte* start() const = 0;
200  virtual int size() const = 0;
201  // Return a grown copy of this buffer. The contained data is uninitialized.
202  // The data in {this} will still be read afterwards (until {this} is
203  // destructed), but not written.
204  virtual std::unique_ptr<AssemblerBuffer> Grow(int new_size)
205      V8_WARN_UNUSED_RESULT = 0;
206};
207
208// Allocate an AssemblerBuffer which uses an existing buffer. This buffer cannot
209// grow, so it must be large enough for all code emitted by the Assembler.
210V8_EXPORT_PRIVATE
211std::unique_ptr<AssemblerBuffer> ExternalAssemblerBuffer(void* buffer,
212                                                         int size);
213
214// Allocate a new growable AssemblerBuffer with a given initial size.
215V8_EXPORT_PRIVATE
216std::unique_ptr<AssemblerBuffer> NewAssemblerBuffer(int size);
217
218class V8_EXPORT_PRIVATE AssemblerBase : public Malloced {
219 public:
220  AssemblerBase(const AssemblerOptions& options,
221                std::unique_ptr<AssemblerBuffer>);
222  virtual ~AssemblerBase();
223
224  const AssemblerOptions& options() const { return options_; }
225
226  bool predictable_code_size() const { return predictable_code_size_; }
227  void set_predictable_code_size(bool value) { predictable_code_size_ = value; }
228
229  uint64_t enabled_cpu_features() const { return enabled_cpu_features_; }
230  void set_enabled_cpu_features(uint64_t features) {
231    enabled_cpu_features_ = features;
232  }
233  // Features are usually enabled by CpuFeatureScope, which also asserts that
234  // the features are supported before they are enabled.
235  // IMPORTANT:  IsEnabled() should only be used by DCHECKs. For real feature
236  // detection, use IsSupported().
237  bool IsEnabled(CpuFeature f) {
238    return (enabled_cpu_features_ & (static_cast<uint64_t>(1) << f)) != 0;
239  }
240  void EnableCpuFeature(CpuFeature f) {
241    enabled_cpu_features_ |= (static_cast<uint64_t>(1) << f);
242  }
243
244  bool is_constant_pool_available() const {
245    if (FLAG_enable_embedded_constant_pool) {
246      // We need to disable constant pool here for embeded builtins
247      // because the metadata section is not adjacent to instructions
248      return constant_pool_available_ && !options().isolate_independent_code;
249    } else {
250      // Embedded constant pool not supported on this architecture.
251      UNREACHABLE();
252    }
253  }
254
255  JumpOptimizationInfo* jump_optimization_info() {
256    return jump_optimization_info_;
257  }
258  void set_jump_optimization_info(JumpOptimizationInfo* jump_opt) {
259    jump_optimization_info_ = jump_opt;
260  }
261
262  void FinalizeJumpOptimizationInfo() {}
263
264  // Overwrite a host NaN with a quiet target NaN.  Used by mksnapshot for
265  // cross-snapshotting.
266  static void QuietNaN(HeapObject nan) {}
267
268  int pc_offset() const { return static_cast<int>(pc_ - buffer_start_); }
269
270  int pc_offset_for_safepoint() {
271#if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_MIPS64) || \
272    defined(V8_TARGET_ARCH_LOONG64)
273    // MIPS and LOONG need to use their own implementation to avoid trampoline's
274    // influence.
275    UNREACHABLE();
276#else
277    return pc_offset();
278#endif
279  }
280
281  byte* buffer_start() const { return buffer_->start(); }
282  int buffer_size() const { return buffer_->size(); }
283  int instruction_size() const { return pc_offset(); }
284
285  std::unique_ptr<AssemblerBuffer> ReleaseBuffer() {
286    std::unique_ptr<AssemblerBuffer> buffer = std::move(buffer_);
287    DCHECK_NULL(buffer_);
288    // Reset fields to prevent accidental further modifications of the buffer.
289    buffer_start_ = nullptr;
290    pc_ = nullptr;
291    return buffer;
292  }
293
294  // This function is called when code generation is aborted, so that
295  // the assembler could clean up internal data structures.
296  virtual void AbortedCodeGeneration() {}
297
298  // Debugging
299  void Print(Isolate* isolate);
300
301  // Record an inline code comment that can be used by a disassembler.
302  // Use --code-comments to enable.
303  V8_INLINE void RecordComment(const char* comment) {
304    // Set explicit dependency on --code-comments for dead-code elimination in
305    // release builds.
306    if (!FLAG_code_comments) return;
307    if (options().emit_code_comments) {
308      code_comments_writer_.Add(pc_offset(), std::string(comment));
309    }
310  }
311
312  V8_INLINE void RecordComment(std::string comment) {
313    // Set explicit dependency on --code-comments for dead-code elimination in
314    // release builds.
315    if (!FLAG_code_comments) return;
316    if (options().emit_code_comments) {
317      code_comments_writer_.Add(pc_offset(), std::move(comment));
318    }
319  }
320
321#ifdef V8_CODE_COMMENTS
322  class CodeComment {
323   public:
324    explicit CodeComment(Assembler* assembler, const std::string& comment)
325        : assembler_(assembler) {
326      if (FLAG_code_comments) Open(comment);
327    }
328    ~CodeComment() {
329      if (FLAG_code_comments) Close();
330    }
331    static const int kIndentWidth = 2;
332
333   private:
334    int depth() const;
335    void Open(const std::string& comment);
336    void Close();
337    Assembler* assembler_;
338  };
339#else  // V8_CODE_COMMENTS
340  class CodeComment {
341    explicit CodeComment(Assembler* assembler, std::string comment) {}
342  };
343#endif
344
345  // The minimum buffer size. Should be at least two times the platform-specific
346  // {Assembler::kGap}.
347  static constexpr int kMinimalBufferSize = 128;
348
349  // The default buffer size used if we do not know the final size of the
350  // generated code.
351  static constexpr int kDefaultBufferSize = 4 * KB;
352
353 protected:
354  // Add 'target' to the {code_targets_} vector, if necessary, and return the
355  // offset at which it is stored.
356  int AddCodeTarget(Handle<CodeT> target);
357  Handle<CodeT> GetCodeTarget(intptr_t code_target_index) const;
358
359  // Add 'object' to the {embedded_objects_} vector and return the index at
360  // which it is stored.
361  using EmbeddedObjectIndex = size_t;
362  EmbeddedObjectIndex AddEmbeddedObject(Handle<HeapObject> object);
363  Handle<HeapObject> GetEmbeddedObject(EmbeddedObjectIndex index) const;
364
365  // The buffer into which code and relocation info are generated.
366  std::unique_ptr<AssemblerBuffer> buffer_;
367  // Cached from {buffer_->start()}, for faster access.
368  byte* buffer_start_;
369  std::forward_list<HeapObjectRequest> heap_object_requests_;
370  // The program counter, which points into the buffer above and moves forward.
371  // TODO(jkummerow): This should probably have type {Address}.
372  byte* pc_;
373
374  void set_constant_pool_available(bool available) {
375    if (FLAG_enable_embedded_constant_pool) {
376      constant_pool_available_ = available;
377    } else {
378      // Embedded constant pool not supported on this architecture.
379      UNREACHABLE();
380    }
381  }
382
383  // {RequestHeapObject} records the need for a future heap number allocation,
384  // code stub generation or string allocation. After code assembly, each
385  // platform's {Assembler::AllocateAndInstallRequestedHeapObjects} will
386  // allocate these objects and place them where they are expected (determined
387  // by the pc offset associated with each request).
388  void RequestHeapObject(HeapObjectRequest request);
389
390  bool ShouldRecordRelocInfo(RelocInfo::Mode rmode) const {
391    DCHECK(!RelocInfo::IsNoInfo(rmode));
392    if (options().disable_reloc_info_for_patching) return false;
393    if (RelocInfo::IsOnlyForSerializer(rmode) &&
394        !options().record_reloc_info_for_serialization && !FLAG_debug_code) {
395      return false;
396    }
397#ifndef ENABLE_DISASSEMBLER
398    if (RelocInfo::IsLiteralConstant(rmode)) return false;
399#endif
400    return true;
401  }
402
403  CodeCommentsWriter code_comments_writer_;
404
405 private:
406  // Before we copy code into the code space, we sometimes cannot encode
407  // call/jump code targets as we normally would, as the difference between the
408  // instruction's location in the temporary buffer and the call target is not
409  // guaranteed to fit in the instruction's offset field. We keep track of the
410  // code handles we encounter in calls in this vector, and encode the index of
411  // the code handle in the vector instead.
412  std::vector<Handle<CodeT>> code_targets_;
413
414  // If an assembler needs a small number to refer to a heap object handle
415  // (for example, because there are only 32bit available on a 64bit arch), the
416  // assembler adds the object into this vector using AddEmbeddedObject, and
417  // may then refer to the heap object using the handle's index in this vector.
418  std::vector<Handle<HeapObject>> embedded_objects_;
419
420  // Embedded objects are deduplicated based on handle location. This is a
421  // compromise that is almost as effective as deduplication based on actual
422  // heap object addresses maintains GC safety.
423  std::unordered_map<Handle<HeapObject>, EmbeddedObjectIndex,
424                     Handle<HeapObject>::hash, Handle<HeapObject>::equal_to>
425      embedded_objects_map_;
426
427  const AssemblerOptions options_;
428  uint64_t enabled_cpu_features_;
429  bool predictable_code_size_;
430
431  // Indicates whether the constant pool can be accessed, which is only possible
432  // if the pp register points to the current code object's constant pool.
433  bool constant_pool_available_;
434
435  JumpOptimizationInfo* jump_optimization_info_;
436
437#ifdef V8_CODE_COMMENTS
438  int comment_depth_ = 0;
439#endif
440
441  // Constant pool.
442  friend class FrameAndConstantPoolScope;
443  friend class ConstantPoolUnavailableScope;
444};
445
446// Enable a specified feature within a scope.
447class V8_EXPORT_PRIVATE V8_NODISCARD CpuFeatureScope {
448 public:
449  enum CheckPolicy {
450    kCheckSupported,
451    kDontCheckSupported,
452  };
453
454#ifdef DEBUG
455  CpuFeatureScope(AssemblerBase* assembler, CpuFeature f,
456                  CheckPolicy check = kCheckSupported);
457  ~CpuFeatureScope();
458
459 private:
460  AssemblerBase* assembler_;
461  uint64_t old_enabled_;
462#else
463  CpuFeatureScope(AssemblerBase* assembler, CpuFeature f,
464                  CheckPolicy check = kCheckSupported) {}
465  ~CpuFeatureScope() {
466    // Define a destructor to avoid unused variable warnings.
467  }
468#endif
469};
470
471#ifdef V8_CODE_COMMENTS
472#define ASM_CODE_COMMENT(asm) ASM_CODE_COMMENT_STRING(asm, __func__)
473#define ASM_CODE_COMMENT_STRING(asm, comment) \
474  AssemblerBase::CodeComment UNIQUE_IDENTIFIER(asm_code_comment)(asm, comment)
475#else
476#define ASM_CODE_COMMENT(asm)
477#define ASM_CODE_COMMENT_STRING(asm, ...)
478#endif
479
480}  // namespace internal
481}  // namespace v8
482#endif  // V8_CODEGEN_ASSEMBLER_H_
483