• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
55 namespace v8 {
56 
57 // Forward declarations.
58 class ApiFunction;
59 
60 namespace internal {
61 
62 using base::Memory;
63 using base::ReadUnalignedValue;
64 using base::WriteUnalignedValue;
65 
66 // Forward declarations.
67 class EmbeddedData;
68 class OffHeapInstructionStream;
69 class Isolate;
70 class SCTableReference;
71 class SourcePosition;
72 class StatsCounter;
73 class StringConstantBase;
74 
75 // -----------------------------------------------------------------------------
76 // Optimization for far-jmp like instructions that can be replaced by shorter.
77 
78 class JumpOptimizationInfo {
79  public:
is_collecting()80   bool is_collecting() const { return stage_ == kCollection; }
is_optimizing()81   bool is_optimizing() const { return stage_ == kOptimization; }
set_optimizing()82   void set_optimizing() {
83     DCHECK(is_optimizable());
84     stage_ = kOptimization;
85   }
86 
is_optimizable()87   bool is_optimizable() const { return optimizable_; }
set_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.
hash_code()94   size_t hash_code() const { return hash_code_; }
set_hash_code(size_t hash_code)95   void set_hash_code(size_t hash_code) { hash_code_ = hash_code; }
96 
farjmp_bitmap()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 
106 class 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 };
kind()112   Kind kind() const { return kind_; }
113 
heap_number()114   double heap_number() const {
115     DCHECK_EQ(kind(), kHeapNumber);
116     return value_.heap_number;
117   }
118 
string()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.
offset()125   int offset() const {
126     DCHECK_GE(offset_, 0);
127     return offset_;
128   }
set_offset(int offset)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 
149 enum class CodeObjectRequired { kNo, kYes };
150 
151 struct 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 
196 class 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.
210 V8_EXPORT_PRIVATE
211 std::unique_ptr<AssemblerBuffer> ExternalAssemblerBuffer(void* buffer,
212                                                          int size);
213 
214 // Allocate a new growable AssemblerBuffer with a given initial size.
215 V8_EXPORT_PRIVATE
216 std::unique_ptr<AssemblerBuffer> NewAssemblerBuffer(int size);
217 
218 class V8_EXPORT_PRIVATE AssemblerBase : public Malloced {
219  public:
220   AssemblerBase(const AssemblerOptions& options,
221                 std::unique_ptr<AssemblerBuffer>);
222   virtual ~AssemblerBase();
223 
options()224   const AssemblerOptions& options() const { return options_; }
225 
predictable_code_size()226   bool predictable_code_size() const { return predictable_code_size_; }
set_predictable_code_size(bool value)227   void set_predictable_code_size(bool value) { predictable_code_size_ = value; }
228 
enabled_cpu_features()229   uint64_t enabled_cpu_features() const { return enabled_cpu_features_; }
set_enabled_cpu_features(uint64_t 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().
IsEnabled(CpuFeature f)237   bool IsEnabled(CpuFeature f) {
238     return (enabled_cpu_features_ & (static_cast<uint64_t>(1) << f)) != 0;
239   }
EnableCpuFeature(CpuFeature f)240   void EnableCpuFeature(CpuFeature f) {
241     enabled_cpu_features_ |= (static_cast<uint64_t>(1) << f);
242   }
243 
is_constant_pool_available()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 
jump_optimization_info()255   JumpOptimizationInfo* jump_optimization_info() {
256     return jump_optimization_info_;
257   }
set_jump_optimization_info(JumpOptimizationInfo * jump_opt)258   void set_jump_optimization_info(JumpOptimizationInfo* jump_opt) {
259     jump_optimization_info_ = jump_opt;
260   }
261 
FinalizeJumpOptimizationInfo()262   void FinalizeJumpOptimizationInfo() {}
263 
264   // Overwrite a host NaN with a quiet target NaN.  Used by mksnapshot for
265   // cross-snapshotting.
QuietNaN(HeapObject nan)266   static void QuietNaN(HeapObject nan) {}
267 
pc_offset()268   int pc_offset() const { return static_cast<int>(pc_ - buffer_start_); }
269 
pc_offset_for_safepoint()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 
buffer_start()281   byte* buffer_start() const { return buffer_->start(); }
buffer_size()282   int buffer_size() const { return buffer_->size(); }
instruction_size()283   int instruction_size() const { return pc_offset(); }
284 
ReleaseBuffer()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.
AbortedCodeGeneration()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.
RecordComment(const char * comment)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 
RecordComment(std::string comment)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:
CodeComment(Assembler * assembler,const std::string & comment)324     explicit CodeComment(Assembler* assembler, const std::string& comment)
325         : assembler_(assembler) {
326       if (FLAG_code_comments) Open(comment);
327     }
~CodeComment()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 {
CodeComment(Assembler * assembler,std::string comment)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 
set_constant_pool_available(bool available)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 
ShouldRecordRelocInfo(RelocInfo::Mode rmode)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.
447 class 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) {}
~CpuFeatureScope()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