Skip to content

LibDisassembly: RISC-V disassembly 4-2/4-5: Disassemble extensions FD (floating-point), A (atomic) and Zicsr (CSRs) #25836

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Userland/Libraries/LibDisassembly/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
set(SOURCES
x86/Instruction.cpp
riscv64/A.cpp
riscv64/Encoding.cpp
riscv64/FD.cpp
riscv64/Formatting.cpp
riscv64/Instruction.cpp
riscv64/IM.cpp
riscv64/Zicsr.cpp
)

serenity_lib(LibDisassembly disassembly)
41 changes: 41 additions & 0 deletions Userland/Libraries/LibDisassembly/riscv64/A.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2023, kleines Filmröllchen <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include "A.h"

namespace Disassembly::RISCV64 {

NonnullOwnPtr<InstructionImpl> parse_amo(u32 instruction)
{
auto raw_parts = RawRType::parse(instruction);
auto is_acquire = (raw_parts.funct7 & 0b10) > 0;
auto is_release = (raw_parts.funct7 & 1) > 0;
auto width = MemoryAccessMode::from_funct3(raw_parts.funct3).width;

auto numeric_operation = raw_parts.funct7 >> 2;
auto operation = static_cast<AtomicMemoryOperation::Operation>(numeric_operation);
switch (numeric_operation) {
case 0b00010:
case 0b00011: {
auto is_sc = (raw_parts.funct7 & 0b100) > 0;
return adopt_own(*new (nothrow) LoadReservedStoreConditional(is_sc ? LoadReservedStoreConditional::Operation::StoreConditional : LoadReservedStoreConditional::Operation::LoadReserved, is_acquire, is_release, width, raw_parts.rs1, raw_parts.rs2, raw_parts.rd));
}
case 0b00001:
case 0b00000:
case 0b00100:
case 0b01000:
case 0b01100:
case 0b10000:
case 0b10100:
case 0b11000:
case 0b11100:
return adopt_own(*new (nothrow) AtomicMemoryOperation(operation, is_acquire, is_release, width, raw_parts.rs1, raw_parts.rs2, raw_parts.rd));
default:
return adopt_own(*new (nothrow) UnknownInstruction);
}
}

}
93 changes: 93 additions & 0 deletions Userland/Libraries/LibDisassembly/riscv64/A.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2023, kleines Filmröllchen <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <LibDisassembly/riscv64/Instruction.h>

// A extension.
namespace Disassembly::RISCV64 {

class AtomicOperation : public RTypeInstruction {
public:
virtual ~AtomicOperation() = default;
bool operator==(AtomicOperation const&) const = default;

AtomicOperation(DataWidth width, bool is_acquire, bool is_release, Register rs1, Register rs2, Register rd)
: RTypeInstruction(rs1, rs2, rd)
, m_width(width)
, m_is_acquire(is_acquire)
, m_is_release(is_release)
{
}

DataWidth width() const { return m_width; }
bool is_acquire() const { return m_is_acquire; }
bool is_release() const { return m_is_release; }
bool is_acquire_release() const { return m_is_acquire && m_is_release; }

private:
DataWidth m_width;
bool m_is_acquire;
bool m_is_release;
};

class LoadReservedStoreConditional : public AtomicOperation {
public:
enum class Operation : bool {
LoadReserved,
StoreConditional,
};

virtual ~LoadReservedStoreConditional() = default;
virtual String to_string(DisplayStyle display_style, u32 origin, Optional<SymbolProvider const&> symbol_provider) const override;
virtual String mnemonic() const override;
virtual bool instruction_equals(InstructionImpl const&) const override;
bool operator==(LoadReservedStoreConditional const&) const = default;

LoadReservedStoreConditional(Operation operation, bool is_acquire, bool is_release, DataWidth width, Register rs1, Register rs2, Register rd)
: AtomicOperation(width, is_acquire, is_release, rs1, rs2, rd)
, m_operation(operation)
{
}

private:
Operation m_operation;
};

class AtomicMemoryOperation : public AtomicOperation {
public:
enum class Operation : u8 {
Swap = 0b00001,
Add = 0b00000,
Xor = 0b00100,
And = 0b01100,
Or = 0b01000,
Min = 0b10000,
Max = 0b10100,
MinUnsigned = 0b11000,
MaxUnsigned = 0b11100,
};

virtual ~AtomicMemoryOperation() = default;
virtual String to_string(DisplayStyle display_style, u32 origin, Optional<SymbolProvider const&> symbol_provider) const override;
virtual String mnemonic() const override;
virtual bool instruction_equals(InstructionImpl const&) const override;
bool operator==(AtomicMemoryOperation const&) const = default;

AtomicMemoryOperation(Operation operation, bool is_acquire, bool is_release, DataWidth width, Register rs1, Register rs2, Register rd)
: AtomicOperation(width, is_acquire, is_release, rs1, rs2, rd)
, m_operation(operation)
{
}

private:
Operation m_operation;
};

NonnullOwnPtr<InstructionImpl> parse_amo(u32 instruction);

}
171 changes: 171 additions & 0 deletions Userland/Libraries/LibDisassembly/riscv64/FD.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2023, kleines Filmröllchen <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include "FD.h"
#include <AK/Assertions.h>
#include <LibDisassembly/riscv64/Instruction.h>

namespace Disassembly::RISCV64 {

NonnullOwnPtr<InstructionImpl> parse_op_fp(u32 instruction)
{
auto raw_parts = RawRType::parse(instruction);
auto width = static_cast<FloatWidth>(raw_parts.funct7 & 0b11);
auto rounding_mode = static_cast<RoundingMode>(raw_parts.funct3);

auto operation = FloatArithmeticInstruction::Operation::Add;
switch (raw_parts.funct7 & ~0b11) {
case 0b0000000:
operation = FloatArithmeticInstruction::Operation::Add;
break;
case 0b0000100:
operation = FloatArithmeticInstruction::Operation::Subtract;
break;
case 0b0001000:
operation = FloatArithmeticInstruction::Operation::Multiply;
break;
case 0b0001100:
operation = FloatArithmeticInstruction::Operation::Divide;
break;
case 0b0010000:
switch (raw_parts.funct3) {
case 0b000:
operation = FloatArithmeticInstruction::Operation::SignInject;
break;
case 0b001:
operation = FloatArithmeticInstruction::Operation::SignInjectNegate;
break;
case 0b010:
operation = FloatArithmeticInstruction::Operation::SignInjectXor;
break;
default:
return adopt_own(*new (nothrow) UnknownInstruction);
}
rounding_mode = RoundingMode::DYN;
break;
case 0b0010100:
switch (raw_parts.funct3) {
case 0b000:
operation = FloatArithmeticInstruction::Operation::Min;
break;
case 0b001:
operation = FloatArithmeticInstruction::Operation::Max;
break;
default:
return adopt_own(*new (nothrow) UnknownInstruction);
}
rounding_mode = RoundingMode::DYN;
break;
case 0b0101100:
if (raw_parts.rs2 != 0)
return adopt_own(*new (nothrow) UnknownInstruction);
return adopt_own(*new FloatSquareRoot(rounding_mode, width, as_float_register(raw_parts.rs1), as_float_register(raw_parts.rd)));
case 0b1010000: {
auto compare_operation = FloatCompare::Operation::Equals;
switch (raw_parts.funct3) {
case 0b010:
compare_operation = FloatCompare::Operation::Equals;
break;
case 0b001:
compare_operation = FloatCompare::Operation::LessThan;
break;
case 0b000:
compare_operation = FloatCompare::Operation::LessThanEquals;
break;
default:
return adopt_own(*new (nothrow) UnknownInstruction);
}
return adopt_own(*new FloatCompare(compare_operation, width, as_float_register(raw_parts.rs1), as_float_register(raw_parts.rs2), raw_parts.rd));
}
case 0b0100000: {
if (raw_parts.funct7 == 0b0100000 && raw_parts.rs2 == 1)
return adopt_own(*new ConvertFloat(ConvertFloat::Operation::SingleToDouble, rounding_mode, as_float_register(raw_parts.rs1), as_float_register(raw_parts.rd)));
if (raw_parts.funct7 == 0b0100001 && raw_parts.rs2 == 0)
return adopt_own(*new ConvertFloat(ConvertFloat::Operation::DoubleToSingle, rounding_mode, as_float_register(raw_parts.rs1), as_float_register(raw_parts.rd)));
return adopt_own(*new (nothrow) UnknownInstruction);
}
case 0b1110000: {
if (raw_parts.rs2 != 0 || raw_parts.funct3 > 0b001)
return adopt_own(*new (nothrow) UnknownInstruction);
if (raw_parts.funct3 == 0b001)
return adopt_own(*new FloatClassify(width, as_float_register(raw_parts.rs1), raw_parts.rd));
if (raw_parts.funct3 == 0b000)
return adopt_own(*new MoveFloatToInteger(width, as_float_register(raw_parts.rs1), raw_parts.rd));
VERIFY_NOT_REACHED(); // Logically impossible
}
case 0b1100000:
case 0b1101000: {
if (raw_parts.rs2.value() > 0b11)
return adopt_own(*new (nothrow) UnknownInstruction);
// Not mentioned in the spec, but bit 3 of funct7 distinguishes between float-to-integer (0) and integer-to-float (1) conversions.
auto is_int_to_float = (raw_parts.funct7 & (1 << 3)) > 0;
// Not mentioned in the spec either, but all "normal" float-integer conversion functions can be distinguished by 4 specific bits:
// - Bit 0 of "rs2" distinguishes between signed (0) and unsigned (1) integer conversions
auto signedness = (raw_parts.rs2.value() & 1) > 0 ? Signedness::Unsigned : Signedness::Signed;
// - Bit 1 of "rs2" distinguishes between word (0) and doubleword (1) integer conversions
auto integer_word_width = (raw_parts.rs2.value() & 0b10) > 0 ? DataWidth::DoubleWord : DataWidth::Word;
MemoryAccessMode integer_width { .width = integer_word_width, .signedness = signedness };
if (is_int_to_float)
return adopt_own(*new ConvertIntegerToFloat(rounding_mode, integer_width, width, raw_parts.rs1, as_float_register(raw_parts.rd)));
return adopt_own(*new ConvertFloatToInteger(rounding_mode, integer_width, width, as_float_register(raw_parts.rs1), raw_parts.rd));
}
case 0b1111000:
if (raw_parts.rs2 != 0)
return adopt_own(*new (nothrow) UnknownInstruction);
return adopt_own(*new MoveIntegerToFloat(width, raw_parts.rs1, as_float_register(raw_parts.rd)));
default:
return adopt_own(*new (nothrow) UnknownInstruction);
}
return adopt_own(*new FloatArithmeticInstruction(operation, rounding_mode, width, as_float_register(raw_parts.rs1), as_float_register(raw_parts.rs2), as_float_register(raw_parts.rd)));
}

NonnullOwnPtr<InstructionImpl> parse_fma(u32 instruction)
{
auto raw_parts = RawR4Type::parse(instruction);
auto width = static_cast<FloatWidth>(raw_parts.fmt);
auto rounding_mode = static_cast<RoundingMode>(raw_parts.funct3);

auto operation = FloatFusedMultiplyAdd::Operation::MultiplyAdd;
switch (static_cast<MajorOpcode>(raw_parts.opcode)) {
case MajorOpcode::MADD:
operation = FloatFusedMultiplyAdd::Operation::MultiplyAdd;
break;
case MajorOpcode::MSUB:
operation = FloatFusedMultiplyAdd::Operation::MultiplySubtract;
break;
case MajorOpcode::NMADD:
operation = FloatFusedMultiplyAdd::Operation::NegatedMultiplyAdd;
break;
case MajorOpcode::NMSUB:
operation = FloatFusedMultiplyAdd::Operation::NegatedMultiplySubtract;
break;
default:
return adopt_own(*new (nothrow) UnknownInstruction);
}
return adopt_own(*new FloatFusedMultiplyAdd(operation, rounding_mode, width, as_float_register(raw_parts.rs1), as_float_register(raw_parts.rs2), as_float_register(raw_parts.rs3), as_float_register(raw_parts.rd)));
}

NonnullOwnPtr<InstructionImpl> parse_load_fp(u32 instruction)
{
auto raw_parts = RawIType::parse(instruction);
if (raw_parts.funct3 > 0b11)
return adopt_own(*new (nothrow) UnknownInstruction);

auto width = data_width_to_float_width(static_cast<DataWidth>(raw_parts.funct3 & 0b11));
return adopt_own(*new (nothrow) FloatMemoryLoad(raw_parts.imm, raw_parts.rs1, width, as_float_register(raw_parts.rd)));
}

NonnullOwnPtr<InstructionImpl> parse_store_fp(u32 instruction)
{
auto raw_parts = RawSType::parse(instruction);
if (raw_parts.funct3 > 0b11)
return adopt_own(*new (nothrow) UnknownInstruction);

auto width = data_width_to_float_width(static_cast<DataWidth>(raw_parts.funct3 & 0b11));
return adopt_own(*new (nothrow) FloatMemoryStore(raw_parts.imm, as_float_register(raw_parts.rs2), raw_parts.rs1, width));
}

}
Loading
Loading