Duckstation/dep/vixl/include/vixl/code-generation-scopes-vixl.h
2019-12-04 20:32:38 +10:00

323 lines
11 KiB
C++

// Copyright 2016, VIXL authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of ARM Limited nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef VIXL_CODE_GENERATION_SCOPES_H_
#define VIXL_CODE_GENERATION_SCOPES_H_
#include "assembler-base-vixl.h"
#include "macro-assembler-interface.h"
namespace vixl {
// This scope will:
// - Allow code emission from the specified `Assembler`.
// - Optionally reserve space in the `CodeBuffer` (if it is managed by VIXL).
// - Optionally, on destruction, check the size of the generated code.
// (The size can be either exact or a maximum size.)
class CodeBufferCheckScope {
public:
// Tell whether or not the scope needs to ensure the associated CodeBuffer
// has enough space for the requested size.
enum BufferSpacePolicy {
kReserveBufferSpace,
kDontReserveBufferSpace,
// Deprecated, but kept for backward compatibility.
kCheck = kReserveBufferSpace,
kNoCheck = kDontReserveBufferSpace
};
// Tell whether or not the scope should assert the amount of code emitted
// within the scope is consistent with the requested amount.
enum SizePolicy {
kNoAssert, // Do not check the size of the code emitted.
kExactSize, // The code emitted must be exactly size bytes.
kMaximumSize // The code emitted must be at most size bytes.
};
// This constructor implicitly calls `Open` to initialise the scope
// (`assembler` must not be `NULL`), so it is ready to use immediately after
// it has been constructed.
CodeBufferCheckScope(internal::AssemblerBase* assembler,
size_t size,
BufferSpacePolicy check_policy = kReserveBufferSpace,
SizePolicy size_policy = kMaximumSize)
: assembler_(NULL), initialised_(false) {
Open(assembler, size, check_policy, size_policy);
}
// This constructor does not implicitly initialise the scope. Instead, the
// user is required to explicitly call the `Open` function before using the
// scope.
CodeBufferCheckScope() : assembler_(NULL), initialised_(false) {
// Nothing to do.
}
virtual ~CodeBufferCheckScope() { Close(); }
// This function performs the actual initialisation work.
void Open(internal::AssemblerBase* assembler,
size_t size,
BufferSpacePolicy check_policy = kReserveBufferSpace,
SizePolicy size_policy = kMaximumSize) {
VIXL_ASSERT(!initialised_);
VIXL_ASSERT(assembler != NULL);
assembler_ = assembler;
if (check_policy == kReserveBufferSpace) {
assembler->GetBuffer()->EnsureSpaceFor(size);
}
#ifdef VIXL_DEBUG
limit_ = assembler_->GetSizeOfCodeGenerated() + size;
assert_policy_ = size_policy;
previous_allow_assembler_ = assembler_->AllowAssembler();
assembler_->SetAllowAssembler(true);
#else
USE(size_policy);
#endif
initialised_ = true;
}
// This function performs the cleaning-up work. It must succeed even if the
// scope has not been opened. It is safe to call multiple times.
void Close() {
#ifdef VIXL_DEBUG
if (!initialised_) {
return;
}
assembler_->SetAllowAssembler(previous_allow_assembler_);
switch (assert_policy_) {
case kNoAssert:
break;
case kExactSize:
VIXL_ASSERT(assembler_->GetSizeOfCodeGenerated() == limit_);
break;
case kMaximumSize:
VIXL_ASSERT(assembler_->GetSizeOfCodeGenerated() <= limit_);
break;
default:
VIXL_UNREACHABLE();
}
#endif
initialised_ = false;
}
protected:
internal::AssemblerBase* assembler_;
SizePolicy assert_policy_;
size_t limit_;
bool previous_allow_assembler_;
bool initialised_;
};
// This scope will:
// - Do the same as `CodeBufferCheckSCope`, but:
// - If managed by VIXL, always reserve space in the `CodeBuffer`.
// - Always check the size (exact or maximum) of the generated code on
// destruction.
// - Emit pools if the specified size would push them out of range.
// - Block pools emission for the duration of the scope.
// This scope allows the `Assembler` and `MacroAssembler` to be freely and
// safely mixed for its duration.
class EmissionCheckScope : public CodeBufferCheckScope {
public:
// This constructor implicitly calls `Open` (when `masm` is not `NULL`) to
// initialise the scope, so it is ready to use immediately after it has been
// constructed.
EmissionCheckScope(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy = kMaximumSize) {
Open(masm, size, size_policy);
}
// This constructor does not implicitly initialise the scope. Instead, the
// user is required to explicitly call the `Open` function before using the
// scope.
EmissionCheckScope() {}
virtual ~EmissionCheckScope() { Close(); }
enum PoolPolicy {
// Do not forbid pool emission inside the scope. Pools will not be emitted
// on `Open` either.
kIgnorePools,
// Force pools to be generated on `Open` if necessary and block their
// emission inside the scope.
kBlockPools,
// Deprecated, but kept for backward compatibility.
kCheckPools = kBlockPools
};
void Open(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy = kMaximumSize) {
Open(masm, size, size_policy, kBlockPools);
}
void Close() {
if (!initialised_) {
return;
}
if (masm_ == NULL) {
// Nothing to do.
return;
}
// Perform the opposite of `Open`, which is:
// - Check the code generation limit was not exceeded.
// - Release the pools.
CodeBufferCheckScope::Close();
if (pool_policy_ == kBlockPools) {
masm_->ReleasePools();
}
VIXL_ASSERT(!initialised_);
}
protected:
void Open(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy,
PoolPolicy pool_policy) {
if (masm == NULL) {
// Nothing to do.
// We may reach this point in a context of conditional code generation.
// See `aarch64::MacroAssembler::MoveImmediateHelper()` for an example.
return;
}
masm_ = masm;
pool_policy_ = pool_policy;
if (pool_policy_ == kBlockPools) {
// To avoid duplicating the work to check that enough space is available
// in the buffer, do not use the more generic `EnsureEmitFor()`. It is
// done below when opening `CodeBufferCheckScope`.
masm->EnsureEmitPoolsFor(size);
masm->BlockPools();
}
// The buffer should be checked *after* we emit the pools.
CodeBufferCheckScope::Open(masm->AsAssemblerBase(),
size,
kReserveBufferSpace,
size_policy);
VIXL_ASSERT(initialised_);
}
// This constructor should only be used from code that is *currently
// generating* the pools, to avoid an infinite loop.
EmissionCheckScope(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy,
PoolPolicy pool_policy) {
Open(masm, size, size_policy, pool_policy);
}
MacroAssemblerInterface* masm_;
PoolPolicy pool_policy_;
};
// Use this scope when you need a one-to-one mapping between methods and
// instructions. This scope will:
// - Do the same as `EmissionCheckScope`.
// - Block access to the MacroAssemblerInterface (using run-time assertions).
class ExactAssemblyScope : public EmissionCheckScope {
public:
// This constructor implicitly calls `Open` (when `masm` is not `NULL`) to
// initialise the scope, so it is ready to use immediately after it has been
// constructed.
ExactAssemblyScope(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy = kExactSize) {
Open(masm, size, size_policy);
}
// This constructor does not implicitly initialise the scope. Instead, the
// user is required to explicitly call the `Open` function before using the
// scope.
ExactAssemblyScope() {}
virtual ~ExactAssemblyScope() { Close(); }
void Open(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy = kExactSize) {
Open(masm, size, size_policy, kBlockPools);
}
void Close() {
if (!initialised_) {
return;
}
if (masm_ == NULL) {
// Nothing to do.
return;
}
#ifdef VIXL_DEBUG
masm_->SetAllowMacroInstructions(previous_allow_macro_assembler_);
#else
USE(previous_allow_macro_assembler_);
#endif
EmissionCheckScope::Close();
}
protected:
// This protected constructor allows overriding the pool policy. It is
// available to allow this scope to be used in code that handles generation
// of pools.
ExactAssemblyScope(MacroAssemblerInterface* masm,
size_t size,
SizePolicy assert_policy,
PoolPolicy pool_policy) {
Open(masm, size, assert_policy, pool_policy);
}
void Open(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy,
PoolPolicy pool_policy) {
VIXL_ASSERT(size_policy != kNoAssert);
if (masm == NULL) {
// Nothing to do.
return;
}
// Rely on EmissionCheckScope::Open to initialise `masm_` and
// `pool_policy_`.
EmissionCheckScope::Open(masm, size, size_policy, pool_policy);
#ifdef VIXL_DEBUG
previous_allow_macro_assembler_ = masm->AllowMacroInstructions();
masm->SetAllowMacroInstructions(false);
#endif
}
private:
bool previous_allow_macro_assembler_;
};
} // namespace vixl
#endif // VIXL_CODE_GENERATION_SCOPES_H_