arch-riscv,sim: m5ops argument / return fix for 32 bit RISC-V (#900)

M5Ops C / C++ functions partially use 64 bit arguments and return value.
In general, 64 bit arguments and return values are possible for 32 bit
RISC-V systems as well, since the arguments and the return value is
split into two registers. However, at the moment, this does not work for
32 bit RISC-V systems on the simulator side, since there is a one to one
mapping between argument registers and m5op function parameters.

To solve this problem, the get() function of the RISC-V reg_abi is
updated. It now will merge two registers if there is a 64 bit argument.
For this, the function code has to be passed to the get() function. The
default value of this function code is set to 0xF00, since 0x00 is
already used for M5_ARM. The parameter list of other get() functions for
argument return is also extended by this function code parameter with
the keyword [[maybe_unused]].

To enable a return value of size 64 bit, a0 is assigned with the lower
32 bit and a1 with the higher 32 bit.

Related Issue: https://github.com/gem5/gem5/issues/881
This commit is contained in:
Ivana Mitrovic
2024-04-08 10:09:17 -07:00
committed by GitHub
12 changed files with 146 additions and 32 deletions

View File

@@ -31,6 +31,7 @@
#include <vector>
#include "base/logging.hh"
#include "sim/pseudo_inst.hh"
#include "sim/syscall_abi.hh"
namespace gem5
@@ -75,6 +76,21 @@ struct Argument<ABI, Arg,
}
};
template <>
struct Argument<ArmISA::RegABI32, pseudo_inst::GuestAddr>
{
using ABI = ArmISA::RegABI32;
using Arg = pseudo_inst::GuestAddr;
static Arg
get(ThreadContext *tc, typename ABI::State &state)
{
panic_if(state + 1 >= ABI::ArgumentRegs.size(),
"Ran out of syscall argument registers.");
return (Arg)bits(tc->getReg(ABI::ArgumentRegs[state++]), 31, 0);
}
};
} // namespace guest_abi
} // namespace gem5

View File

@@ -51,6 +51,7 @@
#include "mem/port_proxy.hh"
#include "sim/core.hh"
#include "sim/guest_abi.hh"
#include "sim/pseudo_inst.hh"
#include "sim/sim_object.hh"
namespace gem5
@@ -604,26 +605,32 @@ namespace guest_abi
template <typename Arg>
struct Argument<ArmSemihosting::Abi64, Arg,
typename std::enable_if_t<std::is_integral_v<Arg>>>
typename std::enable_if_t<
(std::is_integral_v<Arg> ||
std::is_same<Arg,pseudo_inst::GuestAddr>::value)>>
{
static Arg
get(ThreadContext *tc, ArmSemihosting::Abi64::State &state)
{
return state.get(tc);
return (Arg)state.get(tc);
}
};
template <typename Arg>
struct Argument<ArmSemihosting::Abi32, Arg,
typename std::enable_if_t<std::is_integral_v<Arg>>>
typename std::enable_if_t<
(std::is_integral_v<Arg> ||
std::is_same<Arg,pseudo_inst::GuestAddr>::value)>>
{
static Arg
get(ThreadContext *tc, ArmSemihosting::Abi32::State &state)
{
if (std::is_signed_v<Arg>)
return sext<32>(state.get(tc));
else
return state.get(tc);
if (std::is_signed_v<Arg>) {
return (Arg)sext<32>(state.get(tc));
}
else {
return (Arg)state.get(tc);
}
}
};

View File

@@ -1,5 +1,6 @@
//
// Copyright (c) 2020 Barkhausen Institut
// Copyright (c) 2024 University of Rostock
// All rights reserved
//
// The license below extends only to copyright in the software and shall
@@ -40,10 +41,12 @@ def format M5Op() {{
uint64_t result;
if (machInst.rv_type == RV32) {
pseudo_inst::pseudoInst<RegABI32>(xc->tcBase(), M5FUNC, result);
a0 = bits(result, 31, 0);
a1 = bits(result, 63, 32);
} else {
pseudo_inst::pseudoInst<RegABI64>(xc->tcBase(), M5FUNC, result);
}
a0 = rvSext(result)''',
a0 = rvSext(result);
}''',
['IsNonSpeculative', 'IsSerializeAfter'])
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)

View File

@@ -3,6 +3,7 @@
// Copyright (c) 2015 RISC-V Foundation
// Copyright (c) 2016 The University of Virginia
// Copyright (c) 2020 Barkhausen Institut
// Copyright (c) 2024 University of Rostock
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -71,6 +72,7 @@ def operands {{
'sp': IntReg('ud', 'StackPointerReg', 'IsInteger', 2),
'a0': IntReg('ud', '10', 'IsInteger', 1),
'a1': IntReg('ud', '11', 'IsInteger', 2),
'Fd': FloatRegOp('df', 'FD', 'IsFloating', 1),
'Fd_bits': FloatRegOp('ud', 'FD', 'IsFloating', 1),

View File

@@ -289,6 +289,10 @@ class RiscvLinux32 : public RiscvLinux, public OpenFlagTable<RiscvLinux32>
// Currently time_t in glibc for riscv32 is 32-bits, but will be changed.
typedef int64_t time_t;
// Linux types for RV32
typedef uint32_t size_t;
typedef int64_t off_t;
/// Limit struct for getrlimit/setrlimit.
struct rlimit
{

View File

@@ -54,8 +54,9 @@ struct RegABI32 : public GenericSyscallABI32
namespace guest_abi
{
// This method will be used if the size of argument type of function is
// greater than 4 for Riscv 32.
// greater than 4 byte for Riscv 32.
template <typename ABI, typename Arg>
struct Argument<ABI, Arg,
typename std::enable_if_t<
@@ -68,7 +69,27 @@ struct Argument<ABI, Arg,
{
panic_if(state >= ABI::ArgumentRegs.size(),
"Ran out of syscall argument registers.");
return bits(tc->getReg(ABI::ArgumentRegs[state++]), 31, 0);
auto low = ABI::ArgumentRegs[state++];
auto high = ABI::ArgumentRegs[state++];
return (Arg)ABI::mergeRegs(tc, low, high);
}
};
// This method will be used for RV32 pointers.
template <>
struct Argument<RiscvISA::RegABI32, pseudo_inst::GuestAddr>
{
using ABI = RiscvISA::RegABI32;
using Arg = pseudo_inst::GuestAddr;
static Arg
get(ThreadContext *tc, typename ABI::State &state)
{
panic_if(state >= ABI::ArgumentRegs.size(),
"Ran out of syscall argument registers.");
return (Arg)bits(tc->getReg(ABI::ArgumentRegs[state++]), 31, 0);
}
};

View File

@@ -31,6 +31,7 @@
#include "arch/sparc/regs/int.hh"
#include "cpu/thread_context.hh"
#include "sim/guest_abi.hh"
#include "sim/pseudo_inst.hh"
namespace gem5
{
@@ -67,6 +68,19 @@ struct Argument<SparcPseudoInstABI, uint64_t>
}
};
template <>
struct Argument<SparcPseudoInstABI, pseudo_inst::GuestAddr>
{
using Arg = pseudo_inst::GuestAddr;
static Arg
get(ThreadContext *tc, SparcPseudoInstABI::State &state)
{
panic_if(state >= 6, "Too many psuedo inst arguments.");
return (Arg)tc->getReg(SparcISA::int_reg::o(state++));
}
};
} // namespace guest_abi
} // namespace gem5

View File

@@ -37,6 +37,7 @@
#include "arch/x86/regs/int.hh"
#include "sim/guest_abi.hh"
#include "sim/pseudo_inst.hh"
namespace gem5
{
@@ -84,5 +85,29 @@ struct Argument<X86PseudoInstABI, uint64_t>
}
};
template <>
struct Argument<X86PseudoInstABI, pseudo_inst::GuestAddr>
{
using Arg = pseudo_inst::GuestAddr;
static Arg
get(ThreadContext *tc, X86PseudoInstABI::State &state)
{
// The first 6 integer arguments are passed in registers, the rest
// are passed on the stack.
panic_if(state >= 6, "Too many psuedo inst arguments.");
using namespace X86ISA;
constexpr RegId int_reg_map[] = {
int_reg::Rdi, int_reg::Rsi, int_reg::Rdx,
int_reg::Rcx, int_reg::R8, int_reg::R9
};
return (Arg)tc->getReg(int_reg_map[state++]);
}
};
} // namespace guest_abi
} // namespace gem5

View File

@@ -169,7 +169,7 @@ template <typename ABI, typename Arg>
static Arg
getArgument(ThreadContext *tc, typename ABI::State &state)
{
return Argument<ABI, Arg>::get(tc, state);
return (Arg)Argument<ABI, Arg>::get(tc, state);
}
} // namespace guest_abi

View File

@@ -256,27 +256,27 @@ loadsymbol(ThreadContext *tc)
}
void
addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr)
addsymbol(ThreadContext *tc, GuestAddr addr, GuestAddr symbolAddr)
{
DPRINTF(PseudoInst, "pseudo_inst::addsymbol(0x%x, 0x%x)\n",
addr, symbolAddr);
addr.addr, symbolAddr.addr);
std::string symbol;
TranslatingPortProxy fs_proxy(tc);
SETranslatingPortProxy se_proxy(tc);
PortProxy &virt_proxy = FullSystem ? fs_proxy : se_proxy;
virt_proxy.readString(symbol, symbolAddr);
virt_proxy.readString(symbol, symbolAddr.addr);
DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr);
DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr.addr);
tc->getSystemPtr()->workload->insertSymbol(
{ loader::Symbol::Binding::Global,
loader::Symbol::SymbolType::Function, symbol, addr }
loader::Symbol::SymbolType::Function, symbol, addr.addr }
);
loader::debugSymbolTable.insert(
{ loader::Symbol::Binding::Global,
loader::Symbol::SymbolType::Function, symbol, addr }
loader::Symbol::SymbolType::Function, symbol, addr.addr }
);
}
@@ -368,10 +368,10 @@ m5checkpoint(ThreadContext *tc, Tick delay, Tick period)
}
uint64_t
readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset)
readfile(ThreadContext *tc, GuestAddr vaddr, uint64_t len, uint64_t offset)
{
DPRINTF(PseudoInst, "pseudo_inst::readfile(0x%x, 0x%x, 0x%x)\n",
vaddr, len, offset);
vaddr.addr, len, offset);
const std::string &file = tc->getSystemPtr()->params().readfile;
if (file.empty()) {
@@ -404,17 +404,17 @@ readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset)
SETranslatingPortProxy se_proxy(tc);
PortProxy &virt_proxy = FullSystem ? fs_proxy : se_proxy;
virt_proxy.writeBlob(vaddr, buf, result);
virt_proxy.writeBlob(vaddr.addr, buf, result);
delete [] buf;
return result;
}
uint64_t
writefile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset,
Addr filename_addr)
writefile(ThreadContext *tc, GuestAddr vaddr, uint64_t len, uint64_t offset,
GuestAddr filename_addr)
{
DPRINTF(PseudoInst, "pseudo_inst::writefile(0x%x, 0x%x, 0x%x, 0x%x)\n",
vaddr, len, offset, filename_addr);
vaddr.addr, len, offset, filename_addr.addr);
// copy out target filename
std::string filename;
@@ -422,7 +422,7 @@ writefile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset,
SETranslatingPortProxy se_proxy(tc);
PortProxy &virt_proxy = FullSystem ? fs_proxy : se_proxy;
virt_proxy.readString(filename, filename_addr);
virt_proxy.readString(filename, filename_addr.addr);
OutputStream *out;
if (offset == 0) {
@@ -448,7 +448,7 @@ writefile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset,
// copy out data and write to file
char *buf = new char[len];
virt_proxy.readBlob(vaddr, buf, len);
virt_proxy.readBlob(vaddr.addr, buf, len);
os->write(buf, len);
if (os->fail() || os->bad())
panic("Error while doing writefile!\n");

View File

@@ -64,18 +64,37 @@ decodeAddrOffset(Addr offset, uint8_t &func)
func = bits(offset, 15, 8);
}
/**
* This struct wrapper for Addr enables m5ops for systems with 32 bit pointer,
* since it allows to distinguish between address arguments and native C++
* types. GuestAddr is only a temporary solution and will likely replaced in
* the future.
*/
struct GuestAddr
{
Addr addr;
/** Constructor is necessary to cast from uint64_t to GuestAddr. */
GuestAddr(Addr _addr) : addr(_addr) {}
};
inline std::ostream&
operator<<(std::ostream& os, const GuestAddr addr)
{
return os << addr.addr;
}
void arm(ThreadContext *tc);
void quiesce(ThreadContext *tc);
void quiesceSkip(ThreadContext *tc);
void quiesceNs(ThreadContext *tc, uint64_t ns);
void quiesceCycles(ThreadContext *tc, uint64_t cycles);
uint64_t quiesceTime(ThreadContext *tc);
uint64_t readfile(ThreadContext *tc, Addr vaddr, uint64_t len,
uint64_t readfile(ThreadContext *tc, GuestAddr vaddr, uint64_t len,
uint64_t offset);
uint64_t writefile(ThreadContext *tc, Addr vaddr, uint64_t len,
uint64_t offset, Addr filenameAddr);
uint64_t writefile(ThreadContext *tc, GuestAddr vaddr, uint64_t len,
uint64_t offset, GuestAddr filenameAddr);
void loadsymbol(ThreadContext *xc);
void addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr);
void addsymbol(ThreadContext *tc, GuestAddr addr, GuestAddr symbolAddr);
uint64_t initParam(ThreadContext *xc, uint64_t key_str1, uint64_t key_str2);
uint64_t rpns(ThreadContext *tc);
void wakeCPU(ThreadContext *tc, uint64_t cpuid);
@@ -95,6 +114,7 @@ void m5Syscall(ThreadContext *tc);
void togglesync(ThreadContext *tc);
void triggerWorkloadEvent(ThreadContext *tc);
/**
* Execute a decoded M5 pseudo instruction
*

View File

@@ -32,6 +32,7 @@
#include "base/types.hh"
#include "cpu/thread_context.hh"
#include "sim/guest_abi.hh"
#include "sim/pseudo_inst.hh"
#include "sim/syscall_return.hh"
namespace gem5
@@ -83,14 +84,15 @@ template <typename ABI, typename Arg>
struct Argument<ABI, Arg,
typename std::enable_if_t<
std::is_base_of_v<GenericSyscallABI64, ABI> &&
std::is_integral_v<Arg>>>
(std::is_integral_v<Arg> ||
std::is_same<Arg,pseudo_inst::GuestAddr>::value)>>
{
static Arg
get(ThreadContext *tc, typename ABI::State &state)
{
panic_if(state >= ABI::ArgumentRegs.size(),
"Ran out of syscall argument registers.");
return tc->getReg(ABI::ArgumentRegs[state++]);
return (Arg)tc->getReg(ABI::ArgumentRegs[state++]);
}
};