blob: b829234bac70afe4d73d83eb610898f40abfe6fd [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "jit/Ion.h"
namespace js {
namespace jit {
struct LabelBase
{
protected:
// offset_ >= 0 means that the label is either bound or has incoming
// uses and needs to be bound.
int32_t offset_ : 31;
bool bound_ : 1;
// Disallow assignment.
void operator =(const LabelBase& label);
public:
static const int32_t INVALID_OFFSET = -1;
LabelBase() : offset_(INVALID_OFFSET), bound_(false)
{ }
// 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_;
}
void offsetBy(int32_t delta) {
MOZ_ASSERT(bound() || used());
MOZ_ASSERT(offset() + delta >= offset(), "no overflow");
mozilla::DebugOnly<int32_t> oldOffset(offset());
offset_ += delta;
MOZ_ASSERT(offset_ == delta + oldOffset, "new offset fits in 31 bits");
}
// 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());
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, returning the old use position in
// the process.
int32_t use(int32_t offset) {
MOZ_ASSERT(!bound());
int32_t old = offset_;
offset_ = offset;
MOZ_ASSERT(offset_ == offset, "offset fits in 31 bits");
return old;
}
};
// 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:
~Label()
{
#ifdef DEBUG
// The assertion below doesn't hold if an error occurred.
JitContext* context = MaybeGetJitContext();
bool hadError = OOM_counter >= OOM_maxAllocations ||
(context && context->runtime->hadOutOfMemory());
MOZ_ASSERT_IF(!hadError, !used());
#endif
}
};
// 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:
~NonAssertingLabel()
{
#ifdef DEBUG
if (used())
bind(0);
#endif
}
};
} // namespace jit
} // namespace js
#endif // jit_Label_h