/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_Label_h #define jit_Label_h #include "mozilla/Assertions.h" #include namespace js { namespace jit { struct LabelBase { private: // We use uint32_t instead of bool to ensure MSVC packs these fields // correctly. uint32_t bound_ : 1; // offset_ < INVALID_OFFSET means that the label is either bound or has // incoming uses and needs to be bound. uint32_t offset_ : 31; void operator=(const LabelBase& label) = delete; #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) public: #endif static const uint32_t INVALID_OFFSET = 0x7fffffff; // UINT31_MAX. public: LabelBase() : bound_(false), offset_(INVALID_OFFSET) {} // If the label is bound, all incoming edges have been patched and any // future incoming edges will be immediately patched. bool bound() const { return bound_; } int32_t offset() const { MOZ_ASSERT(bound() || used()); return offset_; } // Returns whether the label is not bound, but has incoming uses. bool used() const { return !bound() && offset_ < INVALID_OFFSET; } // Binds the label, fixing its final position in the code stream. void bind(int32_t offset) { MOZ_ASSERT(!bound()); MOZ_ASSERT(offset >= 0); MOZ_ASSERT(uint32_t(offset) < INVALID_OFFSET); offset_ = offset; bound_ = true; MOZ_ASSERT(offset_ == offset, "offset fits in 31 bits"); } // Marks the label as neither bound nor used. void reset() { offset_ = INVALID_OFFSET; bound_ = false; } // Sets the label's latest used position. void use(int32_t offset) { MOZ_ASSERT(!bound()); MOZ_ASSERT(offset >= 0); MOZ_ASSERT(uint32_t(offset) < INVALID_OFFSET); offset_ = offset; MOZ_ASSERT(offset_ == offset, "offset fits in 31 bits"); } }; // A label represents a position in an assembly buffer that may or may not have // already been generated. Labels can either be "bound" or "unbound", the // former meaning that its position is known and the latter that its position // is not yet known. // // A jump to an unbound label adds that jump to the label's incoming queue. A // jump to a bound label automatically computes the jump distance. The process // of binding a label automatically corrects all incoming jumps. class Label : public LabelBase { public: #ifdef DEBUG ~Label(); #endif }; static_assert(sizeof(Label) == sizeof(uint32_t), "Label should have same size as uint32_t"); // Label's destructor asserts that if it has been used it has also been bound. // In the case long-lived labels, however, failed compilation (e.g. OOM) will // trigger this failure innocuously. This Label silences the assertion. class NonAssertingLabel : public Label { public: #ifdef DEBUG ~NonAssertingLabel() { if (used()) { bind(0); } } #endif }; } // namespace jit } // namespace js #endif // jit_Label_h