mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-23 14:25:37 +00:00
323 lines
11 KiB
C++
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_
|