2016-08-17 16:53:36 +02:00
|
|
|
/* This file is part of the dynarmic project.
|
|
|
|
* Copyright (c) 2016 MerryMage
|
|
|
|
* This software may be used and distributed according to the terms of the GNU
|
|
|
|
* General Public License version 2 or any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2016-08-25 16:35:50 +02:00
|
|
|
#include <initializer_list>
|
2016-08-17 16:53:36 +02:00
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
|
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "common/intrusive_list.h"
|
|
|
|
#include "common/memory_pool.h"
|
|
|
|
#include "frontend/arm_types.h"
|
|
|
|
#include "frontend/ir/microinstruction.h"
|
|
|
|
#include "frontend/ir/terminal.h"
|
2016-08-25 16:35:50 +02:00
|
|
|
#include "frontend/ir/value.h"
|
2016-08-17 16:53:36 +02:00
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
namespace IR {
|
|
|
|
|
2016-08-25 16:35:50 +02:00
|
|
|
enum class Opcode;
|
|
|
|
|
2016-08-17 16:53:36 +02:00
|
|
|
/**
|
|
|
|
* A basic block. It consists of zero or more instructions followed by exactly one terminal.
|
|
|
|
* Note that this is a linear IR and not a pure tree-based IR: i.e.: there is an ordering to
|
|
|
|
* the microinstructions. This only matters before chaining is done in order to correctly
|
|
|
|
* order memory accesses.
|
|
|
|
*/
|
|
|
|
class Block final {
|
|
|
|
public:
|
basic_block: Add proxy member functions for the instruction list
Currently basic block kind of acts like a 'dumb struct' which makes things
a little more verbose to write (as opposed to keeping it all in one place,
I guess). It's also a little wonky conceptually, considering a block is
composed of instructions (i.e. 'contains' them).
So providing accessors that make it act more like a container can make working
with algorithms a little nicer. It also makes the API a little more
defined.
Ideally, the list would be only available through a function, but
currently, the pool allocator is exposed, which seems somewhat odd,
considering the block itself should manage its overall allocations
(with placement new, and regular new), rather than putting that
sanitizing directly on the IR emitter (it should just care about emission,
not block state). However, recontaining that can be followed up with,
as it's very trivial to do.
2016-08-21 18:35:30 +02:00
|
|
|
using InstructionList = Common::IntrusiveList<Inst>;
|
2016-08-23 19:05:00 +02:00
|
|
|
using size_type = InstructionList::size_type;
|
basic_block: Add proxy member functions for the instruction list
Currently basic block kind of acts like a 'dumb struct' which makes things
a little more verbose to write (as opposed to keeping it all in one place,
I guess). It's also a little wonky conceptually, considering a block is
composed of instructions (i.e. 'contains' them).
So providing accessors that make it act more like a container can make working
with algorithms a little nicer. It also makes the API a little more
defined.
Ideally, the list would be only available through a function, but
currently, the pool allocator is exposed, which seems somewhat odd,
considering the block itself should manage its overall allocations
(with placement new, and regular new), rather than putting that
sanitizing directly on the IR emitter (it should just care about emission,
not block state). However, recontaining that can be followed up with,
as it's very trivial to do.
2016-08-21 18:35:30 +02:00
|
|
|
using iterator = InstructionList::iterator;
|
|
|
|
using const_iterator = InstructionList::const_iterator;
|
|
|
|
using reverse_iterator = InstructionList::reverse_iterator;
|
|
|
|
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
|
|
|
|
2016-08-17 16:53:36 +02:00
|
|
|
explicit Block(const Arm::LocationDescriptor& location) : location(location) {}
|
|
|
|
|
basic_block: Add proxy member functions for the instruction list
Currently basic block kind of acts like a 'dumb struct' which makes things
a little more verbose to write (as opposed to keeping it all in one place,
I guess). It's also a little wonky conceptually, considering a block is
composed of instructions (i.e. 'contains' them).
So providing accessors that make it act more like a container can make working
with algorithms a little nicer. It also makes the API a little more
defined.
Ideally, the list would be only available through a function, but
currently, the pool allocator is exposed, which seems somewhat odd,
considering the block itself should manage its overall allocations
(with placement new, and regular new), rather than putting that
sanitizing directly on the IR emitter (it should just care about emission,
not block state). However, recontaining that can be followed up with,
as it's very trivial to do.
2016-08-21 18:35:30 +02:00
|
|
|
bool empty() const { return instructions.empty(); }
|
2016-08-23 19:05:00 +02:00
|
|
|
size_type size() const { return instructions.size(); }
|
basic_block: Add proxy member functions for the instruction list
Currently basic block kind of acts like a 'dumb struct' which makes things
a little more verbose to write (as opposed to keeping it all in one place,
I guess). It's also a little wonky conceptually, considering a block is
composed of instructions (i.e. 'contains' them).
So providing accessors that make it act more like a container can make working
with algorithms a little nicer. It also makes the API a little more
defined.
Ideally, the list would be only available through a function, but
currently, the pool allocator is exposed, which seems somewhat odd,
considering the block itself should manage its overall allocations
(with placement new, and regular new), rather than putting that
sanitizing directly on the IR emitter (it should just care about emission,
not block state). However, recontaining that can be followed up with,
as it's very trivial to do.
2016-08-21 18:35:30 +02:00
|
|
|
|
|
|
|
Inst& front() { return instructions.front(); }
|
|
|
|
const Inst& front() const { return instructions.front(); }
|
|
|
|
|
|
|
|
Inst& back() { return instructions.back(); }
|
|
|
|
const Inst& back() const { return instructions.back(); }
|
|
|
|
|
|
|
|
iterator begin() { return instructions.begin(); }
|
|
|
|
const_iterator begin() const { return instructions.begin(); }
|
|
|
|
iterator end() { return instructions.end(); }
|
|
|
|
const_iterator end() const { return instructions.end(); }
|
|
|
|
|
|
|
|
reverse_iterator rbegin() { return instructions.rbegin(); }
|
|
|
|
const_reverse_iterator rbegin() const { return instructions.rbegin(); }
|
|
|
|
reverse_iterator rend() { return instructions.rend(); }
|
|
|
|
const_reverse_iterator rend() const { return instructions.rend(); }
|
|
|
|
|
|
|
|
const_iterator cbegin() const { return instructions.cbegin(); }
|
|
|
|
const_iterator cend() const { return instructions.cend(); }
|
|
|
|
|
|
|
|
const_reverse_iterator crbegin() const { return instructions.crbegin(); }
|
|
|
|
const_reverse_iterator crend() const { return instructions.crend(); }
|
|
|
|
|
2016-08-25 16:35:50 +02:00
|
|
|
/**
|
|
|
|
* Appends a new instruction to this basic block,
|
|
|
|
* handling any allocations necessary to do so.
|
|
|
|
*
|
|
|
|
* @param op Opcode representing the instruction to add.
|
|
|
|
* @param args A sequence of Value instances used as arguments for the instruction.
|
|
|
|
*/
|
|
|
|
void AppendNewInst(Opcode op, std::initializer_list<Value> args);
|
|
|
|
|
|
|
|
/// Gets the starting location for this basic block.
|
|
|
|
Arm::LocationDescriptor Location() const;
|
|
|
|
|
|
|
|
/// Gets the condition required to pass in order to execute this block.
|
|
|
|
Arm::Cond GetCondition() const;
|
|
|
|
/// Sets the condition required to pass in order to execute this block.
|
|
|
|
void SetCondition(Arm::Cond condition);
|
|
|
|
|
|
|
|
/// Gets the location of the block to execute if the predicated condition fails.
|
|
|
|
Arm::LocationDescriptor ConditionFailedLocation() const;
|
|
|
|
/// Sets the location of the block to execute if the predicated condition fails.
|
2016-09-04 12:30:57 +02:00
|
|
|
void SetConditionFailedLocation(Arm::LocationDescriptor fail_location);
|
2016-08-25 16:35:50 +02:00
|
|
|
/// Determines whether or not a prediated condition failure block is present.
|
|
|
|
bool HasConditionFailedLocation() const;
|
|
|
|
|
|
|
|
/// Gets a mutable reference to the condition failed cycle count.
|
|
|
|
size_t& ConditionFailedCycleCount();
|
|
|
|
/// Gets an immutable reference to the condition failed cycle count.
|
|
|
|
const size_t& ConditionFailedCycleCount() const;
|
|
|
|
|
|
|
|
/// Gets a mutable reference to the instruction list for this basic block.
|
|
|
|
InstructionList& Instructions();
|
|
|
|
/// Gets an immutable reference to the instruction list for this basic block.
|
|
|
|
const InstructionList& Instructions() const;
|
|
|
|
|
|
|
|
/// Gets the terminal instruction for this basic block.
|
|
|
|
Terminal GetTerminal() const;
|
|
|
|
/// Sets the terminal instruction for this basic block.
|
|
|
|
void SetTerminal(Terminal term);
|
|
|
|
/// Determines whether or not this basic block has a terminal instruction.
|
|
|
|
bool HasTerminal() const;
|
|
|
|
|
|
|
|
/// Gets a mutable reference to the cycle count for this basic block.
|
|
|
|
size_t& CycleCount();
|
|
|
|
/// Gets an immutable reference to the cycle count for this basic block.
|
|
|
|
const size_t& CycleCount() const;
|
|
|
|
|
|
|
|
private:
|
2016-08-17 16:53:36 +02:00
|
|
|
/// Description of the starting location of this block
|
|
|
|
Arm::LocationDescriptor location;
|
|
|
|
/// Conditional to pass in order to execute this block
|
|
|
|
Arm::Cond cond = Arm::Cond::AL;
|
|
|
|
/// Block to execute next if `cond` did not pass.
|
|
|
|
boost::optional<Arm::LocationDescriptor> cond_failed = {};
|
2016-08-25 04:18:27 +02:00
|
|
|
/// Number of cycles this block takes to execute if the conditional fails.
|
|
|
|
size_t cond_failed_cycle_count = 0;
|
2016-08-17 16:53:36 +02:00
|
|
|
|
|
|
|
/// List of instructions in this block.
|
basic_block: Add proxy member functions for the instruction list
Currently basic block kind of acts like a 'dumb struct' which makes things
a little more verbose to write (as opposed to keeping it all in one place,
I guess). It's also a little wonky conceptually, considering a block is
composed of instructions (i.e. 'contains' them).
So providing accessors that make it act more like a container can make working
with algorithms a little nicer. It also makes the API a little more
defined.
Ideally, the list would be only available through a function, but
currently, the pool allocator is exposed, which seems somewhat odd,
considering the block itself should manage its overall allocations
(with placement new, and regular new), rather than putting that
sanitizing directly on the IR emitter (it should just care about emission,
not block state). However, recontaining that can be followed up with,
as it's very trivial to do.
2016-08-21 18:35:30 +02:00
|
|
|
InstructionList instructions;
|
2016-08-17 16:53:36 +02:00
|
|
|
/// Memory pool for instruction list
|
|
|
|
std::unique_ptr<Common::Pool> instruction_alloc_pool = std::make_unique<Common::Pool>(sizeof(Inst), 4096);
|
|
|
|
/// Terminal instruction of this block.
|
|
|
|
Terminal terminal = Term::Invalid{};
|
|
|
|
|
|
|
|
/// Number of cycles this block takes to execute.
|
|
|
|
size_t cycle_count = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Returns a string representation of the contents of block. Intended for debugging.
|
|
|
|
std::string DumpBlock(const IR::Block& block);
|
|
|
|
|
|
|
|
} // namespace IR
|
|
|
|
} // namespace Dynarmic
|