-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
assembler: Add PC-relative literal loads
- Loading branch information
Showing
6 changed files
with
359 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
add_subdirectory(cpuinfo) | ||
add_subdirectory(literal) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
add_executable(literal literal.cpp) | ||
target_link_libraries(literal biscuit) | ||
set_property(TARGET literal PROPERTY CXX_STANDARD 20) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#include <biscuit/assert.hpp> | ||
#include <biscuit/assembler.hpp> | ||
|
||
#include <iostream> | ||
|
||
using namespace biscuit; | ||
|
||
constexpr const static uint64_t literal1_value = 0x1234567890ABCDEF; | ||
constexpr const static uint64_t literal2_value = 0x1122334455667788; | ||
constexpr const static uint64_t literal3_value = 0xFEDCBA0987654321; | ||
constexpr const static uint64_t literal4_value = 0xAABBCCDDEEFF0011; | ||
|
||
void print_literals(uint64_t literal1, uint64_t literal2, uint64_t literal3, uint64_t literal4) { | ||
std::cout << "Literal 1: " << std::hex << literal1 << std::endl; | ||
std::cout << "Literal 2: " << std::hex << literal2 << std::endl; | ||
std::cout << "Literal 3: " << std::hex << literal3 << std::endl; | ||
std::cout << "Literal 4: " << std::hex << literal4 << std::endl; | ||
|
||
BISCUIT_ASSERT(literal1 == literal1_value); | ||
BISCUIT_ASSERT(literal2 == literal2_value); | ||
BISCUIT_ASSERT(literal3 == literal3_value); | ||
BISCUIT_ASSERT(literal4 == literal4_value); | ||
} | ||
|
||
int main() { | ||
Assembler as(0x5000); | ||
|
||
Literal64 literal1(literal1_value); | ||
Literal64 literal2(literal2_value); | ||
Literal64 literal3(literal3_value); | ||
Literal64 literal4(literal4_value); | ||
|
||
// Literal placed before the code, more than 0x1000 bytes away | ||
as.Place(&literal1); | ||
|
||
as.AdvanceBuffer(as.GetCodeBuffer().GetCursorOffset() + 0x1000); | ||
|
||
// Literal placed before the code, less than 0x1000 bytes away | ||
as.Place(&literal2); | ||
|
||
void (*code)() = reinterpret_cast<void(*)()>(as.GetCursorPointer()); | ||
as.ADDI(sp, sp, -8); | ||
as.SD(ra, 0, sp); | ||
|
||
as.LD(a0, &literal1); | ||
|
||
as.LD(a1, &literal2); | ||
|
||
as.LD(a2, &literal3); | ||
|
||
as.LD(a3, &literal4); | ||
|
||
as.LI(t0, (uint64_t)print_literals); | ||
as.JALR(t0); | ||
|
||
as.LD(ra, 0, sp); | ||
as.ADDI(sp, sp, 8); | ||
as.RET(); | ||
|
||
// Literal placed after the code, less than 0x1000 bytes away | ||
as.Place(&literal3); | ||
|
||
as.AdvanceBuffer(as.GetCodeBuffer().GetCursorOffset() + 0x1000); | ||
|
||
// Literal placed after the code, more than 0x1000 bytes away | ||
as.Place(&literal4); | ||
|
||
as.GetCodeBuffer().SetExecutable(); | ||
|
||
code(); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
#pragma once | ||
|
||
#include <cstddef> | ||
#include <cstdint> | ||
#include <optional> | ||
#include <set> | ||
#include <biscuit/assert.hpp> | ||
|
||
namespace biscuit { | ||
|
||
/** | ||
* A Literal is a representation of a constant value that can be loaded into a register. | ||
* This is useful for avoiding multiple instructions for loading big constants. | ||
* | ||
* Literals, like Labels, don't need to be placed immediately. They can be created | ||
* and used with loads that require a Literal, and placed in the buffer at a later point. | ||
* | ||
* @note Any literal that is created, is used with a load instruction, | ||
* but is *not* placed to a location (via Place() in the assembler) | ||
* will result in an assertion being invoked when the literal instance's | ||
* destructor is executed. | ||
* | ||
* @note A literal may only be placed to one location. Any attempt to place | ||
* a literal that is already placed will result in an assertion being | ||
* invoked. | ||
* | ||
* @par | ||
* An example of placing a literal: | ||
* @code{.cpp} | ||
* Assembler as{...}; | ||
* Literal64 literal(0x1234567890ABCDEF); | ||
* | ||
* as.LD(x2, &literal); // Load the literal (emits a AUIPC+LD sequence) | ||
* as.JR(x2); // Execution continues elsewhere | ||
* as.Place(&literal); // Place the literal at this location in the buffer | ||
* @endcode | ||
*/ | ||
template<class T> | ||
class Literal { | ||
public: | ||
using Location = std::optional<ptrdiff_t>; | ||
using LocationOffset = Location::value_type; | ||
|
||
/** | ||
* This constructor results in a literal being constructed that is not | ||
* placed at a particular location yet. | ||
* | ||
* @param value The value that this literal represents. | ||
*/ | ||
explicit Literal(T value) : m_value{value} {} | ||
|
||
/// Destructor | ||
~Literal() noexcept { | ||
// It's a logic bug if something references a literal and hasn't been handled. | ||
// | ||
// This is usually indicative of a scenario where a literal is referenced but | ||
// hasn't been placed at a location. | ||
// | ||
BISCUIT_ASSERT(IsResolved()); | ||
} | ||
|
||
// Copying disabled for the same reasons as Labels. | ||
Literal(const Literal&) = delete; | ||
Literal& operator=(const Literal&) = delete; | ||
|
||
Literal(Literal&&) noexcept = default; | ||
Literal& operator=(Literal&&) noexcept = default; | ||
|
||
/** | ||
* Determines whether or not this literal instance has a location assigned to it. | ||
* | ||
* A literal is considered placed if it has an assigned location. | ||
*/ | ||
[[nodiscard]] bool IsPlaced() const noexcept { | ||
return m_location.has_value(); | ||
} | ||
|
||
/** | ||
* Determines whether or not this literal is resolved. | ||
* | ||
* A literal is considered resolved when all referencing offsets have been handled. | ||
*/ | ||
[[nodiscard]] bool IsResolved() const noexcept { | ||
return m_offsets.empty(); | ||
} | ||
|
||
/** | ||
* Determines whether or not this literal is unresolved. | ||
* | ||
* A literal is considered unresolved if it still has any unhandled referencing offsets. | ||
*/ | ||
[[nodiscard]] bool IsUnresolved() const noexcept { | ||
return !IsResolved(); | ||
} | ||
|
||
/** | ||
* Retrieves the location for this literal. | ||
* | ||
* @note If the returned location is empty, then this literal has not been assigned | ||
* a location yet. | ||
*/ | ||
[[nodiscard]] Location GetLocation() const noexcept { | ||
return m_location; | ||
} | ||
|
||
private: | ||
// A literal instance is inherently bound to the assembler it's | ||
// used with, as the offsets within the literal set depend on | ||
// said assemblers code buffer. | ||
friend class Assembler; | ||
|
||
/** | ||
* Places a literal to the given location. | ||
* | ||
* @param offset The offset to place this literal at. | ||
* | ||
* @returns The literal value so it can be copied to memory by the assembler. | ||
* | ||
* @pre The literal must not have already been placed at a previous location. | ||
* Attempting to place a literal multiple times is typically, in almost all scenarios, | ||
* the source of bugs. | ||
* Attempting to place an already placed literal will result in an assertion | ||
* being triggered. | ||
*/ | ||
[[nodiscard]] const T& Place(LocationOffset offset) noexcept { | ||
BISCUIT_ASSERT(!IsPlaced()); | ||
m_location = offset; | ||
return m_value; | ||
} | ||
|
||
/** | ||
* Marks the given address as dependent on this literal. | ||
* | ||
* This is used in scenarios where a literal exists, but has not yet been | ||
* placed at a location yet. It's important to track these addresses, | ||
* as we'll need to patch the dependent load instructions with the | ||
* proper offset once the literal is finally placed by the assembler. | ||
* | ||
* During literal placement, the offset will be calculated and inserted | ||
* into dependent instructions. | ||
*/ | ||
void AddOffset(LocationOffset offset) { | ||
// If a literal is already placed at a location, then offset tracking | ||
// isn't necessary. Tripping this assert means we have a bug somewhere. | ||
BISCUIT_ASSERT(!IsPlaced()); | ||
BISCUIT_ASSERT(IsNewOffset(offset)); | ||
|
||
m_offsets.insert(offset); | ||
} | ||
|
||
// Clears all the underlying offsets for this literal. | ||
void ClearOffsets() noexcept { | ||
m_offsets.clear(); | ||
} | ||
|
||
// Determines whether or not this address has already been added before. | ||
[[nodiscard]] bool IsNewOffset(LocationOffset offset) const noexcept { | ||
return m_offsets.find(offset) == m_offsets.cend(); | ||
} | ||
|
||
std::set<LocationOffset> m_offsets; | ||
Location m_location; | ||
const T m_value; | ||
}; | ||
|
||
using Literal64 = Literal<uint64_t>; | ||
|
||
} // namespace biscuit |
Oops, something went wrong.