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:
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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++]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user