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