Skip to content

Commit

Permalink
Make LD more generic
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFTKP committed Nov 9, 2024
1 parent acb5809 commit 4c5c57f
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 65 deletions.
33 changes: 28 additions & 5 deletions examples/literal/literal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,43 @@

using namespace biscuit;

struct BigData {
uint64_t data_low;
uint64_t data_high;
};

constexpr uint64_t literal1_value = 0x1234567890ABCDEF;
constexpr uint64_t literal2_value = 0x1122334455667788;
constexpr uint64_t literal3_value = 0xFEDCBA0987654321;
constexpr uint64_t literal4_value = 0xAABBCCDDEEFF0011;
constexpr std::array<uint64_t, 4> big_array = {0xDEADBEEFDEADBEEF, 0x1111111111111111, 0x2222222222222222, 0x3333333333333333};
constexpr BigData big_data = {0xCAFECAFECAFECAFE, 0x1111111111111111};

void print_literals(uint64_t literal1, uint64_t literal2, uint64_t literal3, uint64_t literal4) {
void print_literals(uint64_t literal1, uint64_t literal2, uint64_t literal3, uint64_t literal4, uint64_t literal5, uint64_t literal6) {
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;
std::cout << "Literal 5: " << std::hex << literal5 << std::endl;
std::cout << "Literal 6: " << std::hex << literal6 << std::endl;

BISCUIT_ASSERT(literal1 == literal1_value);
BISCUIT_ASSERT(literal2 == literal2_value);
BISCUIT_ASSERT(literal3 == literal3_value);
BISCUIT_ASSERT(literal4 == literal4_value);
BISCUIT_ASSERT(literal5 == big_array[0]);
BISCUIT_ASSERT(literal6 == big_data.data_low);
}

int main() {
Assembler as(0x5000);

Literal64 literal1(literal1_value);
Literal64 literal2(literal2_value);
Literal64 literal3(literal3_value);
Literal64 literal4(literal4_value);
Literal literal1(literal1_value);
Literal literal2(literal2_value);
Literal literal3(literal3_value);
Literal literal4(literal4_value);
Literal literal5(big_array);
Literal literal6(big_data);

// Literal placed before the code, more than 0x1000 bytes away
as.Place(&literal1);
Expand All @@ -38,6 +51,12 @@ int main() {
// Literal placed before the code, less than 0x1000 bytes away
as.Place(&literal2);

// Place a Literal that is an array of 4 uint64_t values
as.Place(&literal5);

// Place a Literal that is a custom POD type
as.Place(&literal6);

void (*code)() = reinterpret_cast<void(*)()>(as.GetCursorPointer());
as.ADDI(sp, sp, -8);
as.SD(ra, 0, sp);
Expand All @@ -50,6 +69,10 @@ int main() {

as.LD(a3, &literal4);

as.LD(a4, &literal5);

as.LD(a5, &literal6);

as.LI(t0, (uint64_t)print_literals);
as.JALR(t0);

Expand Down
48 changes: 42 additions & 6 deletions include/biscuit/assembler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ class Assembler {
*
* @param literal A non-null valid literal to place.
*/
void Place(Literal64* literal);
template <typename T>
void Place(Literal<T>* literal) {
PlaceAtOffset(literal, m_buffer.GetCursorOffset());
}

// RV32I Instructions

Expand Down Expand Up @@ -277,7 +280,15 @@ class Assembler {
void ADDIW(GPR rd, GPR rs, int32_t imm) noexcept;
void ADDW(GPR rd, GPR lhs, GPR rhs) noexcept;
void LD(GPR rd, int32_t imm, GPR rs) noexcept;
void LD(GPR rd, Literal64* literal) noexcept;
template <typename T>
void LD(GPR rd, Literal<T>* literal) noexcept {
static_assert(sizeof(T) >= 8);
const auto offset = LinkAndGetOffset(literal);
const auto hi20 = static_cast<int32_t>((static_cast<uint32_t>(offset) + 0x800) >> 12 & 0xFFFFF);
const auto lo12 = static_cast<int32_t>(offset << 20) >> 20;
AUIPC(rd, hi20);
LD(rd, lo12, rd);
}
void LWU(GPR rd, int32_t imm, GPR rs) noexcept;
void SD(GPR rs2, int32_t imm, GPR rs1) noexcept;

Expand Down Expand Up @@ -1534,16 +1545,41 @@ class Assembler {

// Places a literal at the given offset.
template<typename T>
void PlaceAtOffset(Literal<T>* literal, Literal<T>::LocationOffset offset);
void PlaceAtOffset(Literal<T>* literal, Literal<T>::LocationOffset offset) {
BISCUIT_ASSERT(literal != nullptr);
BISCUIT_ASSERT(offset >= 0 && offset <= m_buffer.GetCursorOffset());

const T& value = literal->Place(offset);
ResolveLiteralOffsetsRaw(literal->m_location.value(), literal->m_offsets);
literal->ClearOffsets();

m_buffer.Emit(value);
}

// Links the given literal and returns the offset to it.
template<typename T>
ptrdiff_t LinkAndGetOffset(Literal<T>* literal);
ptrdiff_t LinkAndGetOffset(Literal<T>* literal) {
BISCUIT_ASSERT(literal != nullptr);

// If we have a placed literal, then it's straightforward to calculate
// the offsets.
if (literal->IsPlaced()) {
const auto cursor_address = m_buffer.GetCursorAddress();
const auto literal_offset = m_buffer.GetOffsetAddress(*literal->GetLocation());
return static_cast<ptrdiff_t>(literal_offset - cursor_address);
}

// If we don't have a placed literal, we return an offset of zero.
// While the emitter will emit a bogus load instruction initially,
// the offset will be patched over once the literal has been properly
// placed at a location.
literal->AddOffset(m_buffer.GetCursorOffset());
return 0;
}

// Resolves all literal offsets and patches any necessary
// offsets into the load instructions that require them.
template<typename T>
void ResolveLiteralOffsets(Literal<T>* literal);
void ResolveLiteralOffsetsRaw(ptrdiff_t location, const std::set<ptrdiff_t>& offsets);

CodeBuffer m_buffer;
ArchFeature m_features = ArchFeature::RV64;
Expand Down
5 changes: 2 additions & 3 deletions include/biscuit/literal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <biscuit/assert.hpp>

#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
Expand Down Expand Up @@ -29,7 +30,7 @@ namespace biscuit {
* An example of placing a literal:
* @code{.cpp}
* Assembler as{...};
* Literal64 literal(0x1234567890ABCDEF);
* Literal literal(0x1234567890ABCDEF);
*
* as.LD(x2, &literal); // Load the literal (emits a AUIPC+LD sequence)
* as.JR(x2); // Execution continues elsewhere
Expand Down Expand Up @@ -173,6 +174,4 @@ class Literal {
static_assert(std::is_trivially_copyable_v<T>, "Literal type must be trivially copyable.");
};

using Literal64 = Literal<uint64_t>;

} // namespace biscuit
54 changes: 3 additions & 51 deletions src/assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ void Assembler::Bind(Label* label) {
BindToOffset(label, m_buffer.GetCursorOffset());
}

void Assembler::Place(Literal64* literal) {
PlaceAtOffset(literal, m_buffer.GetCursorOffset());
}

void Assembler::ADD(GPR rd, GPR lhs, GPR rhs) noexcept {
EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b000, rd, 0b0110011);
}
Expand Down Expand Up @@ -536,15 +532,6 @@ void Assembler::LD(GPR rd, int32_t imm, GPR rs) noexcept {
EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b011, rd, 0b0000011);
}

void Assembler::LD(GPR rd, Literal64* literal) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
const auto offset = LinkAndGetOffset(literal);
const auto hi20 = static_cast<int32_t>((static_cast<uint32_t>(offset) + 0x800) >> 12 & 0xFFFFF);
const auto lo12 = static_cast<int32_t>(offset << 20) >> 20;
AUIPC(rd, hi20);
LD(rd, lo12, rd);
}

void Assembler::LWU(GPR rd, int32_t imm, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
BISCUIT_ASSERT(IsValidSigned12BitImm(imm));
Expand Down Expand Up @@ -1522,40 +1509,7 @@ void Assembler::ResolveLabelOffsets(Label* label) {
}
}

template <typename T>
void Assembler::PlaceAtOffset(Literal<T>* literal, Literal<T>::LocationOffset offset) {
BISCUIT_ASSERT(literal != nullptr);
BISCUIT_ASSERT(offset >= 0 && offset <= m_buffer.GetCursorOffset());

const T& value = literal->Place(offset);
ResolveLiteralOffsets(literal);
literal->ClearOffsets();

m_buffer.Emit(value);
}

template <typename T>
ptrdiff_t Assembler::LinkAndGetOffset(Literal<T>* literal) {
BISCUIT_ASSERT(literal != nullptr);

// If we have a placed literal, then it's straightforward to calculate
// the offsets.
if (literal->IsPlaced()) {
const auto cursor_address = m_buffer.GetCursorAddress();
const auto literal_offset = m_buffer.GetOffsetAddress(*literal->GetLocation());
return static_cast<ptrdiff_t>(literal_offset - cursor_address);
}

// If we don't have a placed literal, we return an offset of zero.
// While the emitter will emit a bogus load instruction initially,
// the offset will be patched over once the literal has been properly
// placed at a location.
literal->AddOffset(m_buffer.GetCursorOffset());
return 0;
}

template <typename T>
void Assembler::ResolveLiteralOffsets(Literal<T>* literal) {
void Assembler::ResolveLiteralOffsetsRaw(ptrdiff_t location, const std::set<ptrdiff_t>& offsets) {
const auto is_auipc_type = [](uint32_t instruction) {
return (instruction & 0x7F) == 0b0010111;
};
Expand All @@ -1564,9 +1518,7 @@ void Assembler::ResolveLiteralOffsets(Literal<T>* literal) {
return (instruction & 0x7F) == 0b0000011;
};

const auto literal_location = *literal->GetLocation();

for (const auto offset : literal->m_offsets) {
for (const auto offset : offsets) {
const auto address = m_buffer.GetOffsetAddress(offset);
auto* const ptr = reinterpret_cast<uint8_t*>(address);

Expand All @@ -1580,7 +1532,7 @@ void Assembler::ResolveLiteralOffsets(Literal<T>* literal) {
// It's enough to verify that the immediate is going to be valid
// and then OR it into the instruction.

const auto encoded_offset = literal_location - offset;
const auto encoded_offset = location - offset;

BISCUIT_ASSERT(is_auipc_type(instructions[0]));

Expand Down

0 comments on commit 4c5c57f

Please sign in to comment.