Skip to content

Commit 7da36db

Browse files
committed
workaround to support custom extensions that use standard prefixes
RISC-V ISA states (21.1): "A standard-compatible global encoding can also use standard prefixes for non-standard extensions if the associated standard extensions are not included in the global encoding." Currently all the instructions (either from standard or custom extensions) are all being inserted into a single std::vector which is then being sorted. An instruction matching process performs linear search on that vector. The problem is that when a custom extension uses the same opcode as standard one (i.e. match and mask are equal to the standard counterparts) it is undefined which instruction will be picked. That is because in std::sort "The order of equal elements is not guaranteed to be preserved". That being said it is impossible to define custom extension (via customext) that would use the prefix of a disabled standard extension. In this change I separate custom and standard extensions in two separate std::vector's. By default we report an error if they have common elements (There're an additional processor_t constructor's argument that skips this check). If this error is disabled during instruction matching we first trying to find it among custom instructions. If it has been found the search is stopped and custom instruction is executed, otherwise we look for it among standard instructions. Overall this change does not completely fix the problem but at least makes it possible to use the feature of RISC-V ISA.
1 parent 8d239ff commit 7da36db

File tree

7 files changed

+165
-32
lines changed

7 files changed

+165
-32
lines changed

ci-tests/create-ci-binary-tarball

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ mkdir -p build/hello && cd "$_"
1212
riscv64-unknown-elf-gcc -O2 -o hello `git rev-parse --show-toplevel`/ci-tests/hello.c
1313
cd -
1414

15+
mkdir -p build/dummy-slliuw && cd "$_"
16+
riscv64-unknown-elf-gcc -O2 -o dummy-slliuw `git rev-parse --show-toplevel`/ci-tests/dummy-slliuw.c
17+
cd -
18+
1519
mv build/pk/pk .
1620
mv build/hello/hello .
21+
mv build/dummy-slliuw/dummy-slliuw .
22+
tar -cf spike-ci.tar pk hello dummy-slliuw
1723

18-
tar -cf spike-ci.tar pk hello
19-
20-
rm pk hello
24+
rm pk hello dummy-slliuw

ci-tests/dummy-slliuw.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <stdio.h>
2+
3+
int main() {
4+
// as if slli.uw zero, t1, 3
5+
asm(".4byte 0x0833101b");
6+
printf("Executed successfully\n");
7+
return 0;
8+
}

ci-tests/test-customext.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#include <riscv/extension.h>
2+
#include <riscv/sim.h>
3+
4+
struct : public arg_t {
5+
std::string to_string(insn_t insn) const { return xpr_name[insn.rd()]; }
6+
} xrd;
7+
8+
struct : public arg_t {
9+
std::string to_string(insn_t insn) const { return xpr_name[insn.rs1()]; }
10+
} xrs1;
11+
12+
struct : public arg_t {
13+
std::string to_string(insn_t insn) const { return xpr_name[insn.rs2()]; }
14+
} xrs2;
15+
16+
struct : public arg_t {
17+
std::string to_string(insn_t insn) const {
18+
return std::to_string((int)insn.shamt());
19+
}
20+
} shamt;
21+
22+
static reg_t do_nop4([[maybe_unused]] processor_t *p,
23+
[[maybe_unused]] insn_t insn, reg_t pc) {
24+
return pc + 4;
25+
}
26+
27+
// dummy extension that uses the same prefix as standard zba extension
28+
struct xslliuw_dummy_t : public extension_t {
29+
const char *name() { return "dummyslliuw"; }
30+
31+
xslliuw_dummy_t() {}
32+
33+
std::vector<insn_desc_t> get_instructions() {
34+
std::vector<insn_desc_t> insns;
35+
insns.push_back(insn_desc_t{MATCH_SLLI_UW, MASK_SLLI_UW, do_nop4, do_nop4,
36+
do_nop4, do_nop4, do_nop4, do_nop4, do_nop4,
37+
do_nop4});
38+
return insns;
39+
}
40+
41+
std::vector<disasm_insn_t *> get_disasms() {
42+
std::vector<disasm_insn_t *> insns;
43+
insns.push_back(new disasm_insn_t("dummy_slliuw", MATCH_SLLI_UW,
44+
MASK_SLLI_UW, {&xrd, &xrs1, &shamt}));
45+
return insns;
46+
}
47+
};
48+
49+
REGISTER_EXTENSION(dummyslliuw, []() { return new xslliuw_dummy_t; })
50+
51+
// Copied from spike main.
52+
// TODO: This should really be provided in libriscv
53+
static std::vector<std::pair<reg_t, abstract_mem_t *>>
54+
make_mems(const std::vector<mem_cfg_t> &layout) {
55+
std::vector<std::pair<reg_t, abstract_mem_t *>> mems;
56+
mems.reserve(layout.size());
57+
for (const auto &cfg : layout) {
58+
mems.push_back(std::make_pair(cfg.get_base(), new mem_t(cfg.get_size())));
59+
}
60+
return mems;
61+
}
62+
63+
int main(int argc, char **argv) {
64+
cfg_t cfg;
65+
cfg.isa = "RV64IMAFDCV_xdummyslliuw";
66+
std::vector<device_factory_t *> plugin_devices;
67+
if (argc != 3) {
68+
std::cerr << "Error: invalid arguments\n";
69+
exit(1);
70+
}
71+
std::vector<std::string> htif_args{argv[1] /* pk */, argv[2] /* executable */};
72+
debug_module_config_t dm_config = {.progbufsize = 2,
73+
.max_sba_data_width = 0,
74+
.require_authentication = false,
75+
.abstract_rti = 0,
76+
.support_hasel = true,
77+
.support_abstract_csr_access = true,
78+
.support_abstract_fpr_access = true,
79+
.support_haltgroups = true,
80+
.support_impebreak = true};
81+
std::vector<std::pair<reg_t, abstract_mem_t *>> mems =
82+
make_mems(cfg.mem_layout);
83+
sim_t sim(&cfg, false, mems, plugin_devices, htif_args, dm_config,
84+
nullptr, // log_path
85+
true, // dtb_enabled
86+
nullptr, // dtb_file
87+
false, // socket_enabled
88+
nullptr); // cmd_file
89+
sim.run();
90+
}

ci-tests/test-spike

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ time ../install/bin/spike --isa=rv64gc pk hello | grep "Hello, world! Pi is app
1414

1515
# check that including sim.h in an external project works
1616
g++ -std=c++17 -I../install/include -L../install/lib $DIR/testlib.c -lriscv -o test-libriscv
17-
LD_LIBRARY_PATH=../install/lib ./test-libriscv | grep "Hello, world! Pi is approximately 3.141588."
17+
g++ -std=c++17 -I../install/include -L../install/lib $DIR/test-customext.cc -lriscv -o test-customext
18+
19+
LD_LIBRARY_PATH=../install/lib ./test-libriscv pk hello| grep "Hello, world! Pi is approximately 3.141588."
20+
LD_LIBRARY_PATH=../install/lib ./test-customext pk dummy-slliuw | grep "Executed successfully"

ci-tests/testlib.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ static std::vector<std::pair<reg_t, abstract_mem_t*>> make_mems(const std::vecto
1212
return mems;
1313
}
1414

15-
int main()
16-
{
15+
int main(int argc, char **argv) {
1716
cfg_t cfg;
1817
std::vector<device_factory_t*> plugin_devices;
19-
std::vector<std::string> htif_args {"pk", "hello"};
18+
19+
if (argc != 3) {
20+
std::cerr << "Error: invalid arguments\n";
21+
exit(1);
22+
}
23+
std::vector<std::string> htif_args{argv[1] /* pk */,
24+
argv[2] /* executable */};
2025
debug_module_config_t dm_config;
2126
std::vector<std::pair<reg_t, abstract_mem_t*>> mems =
2227
make_mems(cfg.mem_layout);

riscv/processor.cc

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,24 @@ reg_t illegal_instruction(processor_t UNUSED *p, insn_t insn, reg_t UNUSED pc)
10371037
throw trap_illegal_instruction(insn.bits() & 0xffffffffULL);
10381038
}
10391039

1040+
static insn_desc_t
1041+
propagate_instruction_in_vector(std::vector<insn_desc_t> &instructions,
1042+
std::vector<insn_desc_t>::iterator it) {
1043+
assert(it != instructions.end());
1044+
insn_desc_t desc = *it;
1045+
if (it->mask != 0 && it != instructions.begin() &&
1046+
std::next(it) != instructions.end()) {
1047+
if (it->match != std::prev(it)->match &&
1048+
it->match != std::next(it)->match) {
1049+
// move to front of opcode list to reduce miss penalty
1050+
while (--it >= instructions.begin())
1051+
*std::next(it) = *it;
1052+
instructions[0] = desc;
1053+
}
1054+
}
1055+
return desc;
1056+
}
1057+
10401058
insn_func_t processor_t::decode_insn(insn_t insn)
10411059
{
10421060
// look up opcode in hash table
@@ -1047,34 +1065,33 @@ insn_func_t processor_t::decode_insn(insn_t insn)
10471065

10481066
if (unlikely(insn.bits() != desc.match)) {
10491067
// fall back to linear search
1050-
int cnt = 0;
1051-
insn_desc_t* p = &instructions[0];
1052-
while ((insn.bits() & p->mask) != p->match)
1053-
p++, cnt++;
1054-
desc = *p;
1055-
1056-
if (p->mask != 0 && p > &instructions[0]) {
1057-
if (p->match != (p - 1)->match && p->match != (p + 1)->match) {
1058-
// move to front of opcode list to reduce miss penalty
1059-
while (--p >= &instructions[0])
1060-
*(p + 1) = *p;
1061-
instructions[0] = desc;
1062-
}
1068+
auto matching = [insn_bits = insn.bits()](const insn_desc_t &d) {
1069+
return (insn_bits & d.mask) == d.match;
1070+
};
1071+
auto p = std::find_if(custom_instructions.begin(),
1072+
custom_instructions.end(), matching);
1073+
if (p != custom_instructions.end()) {
1074+
desc = propagate_instruction_in_vector(custom_instructions, p);
1075+
} else {
1076+
p = std::find_if(instructions.begin(), instructions.end(), matching);
1077+
assert(p != instructions.end());
1078+
desc = propagate_instruction_in_vector(instructions, p);
10631079
}
1064-
10651080
opcode_cache[idx] = desc;
10661081
opcode_cache[idx].match = insn.bits();
10671082
}
10681083

10691084
return desc.func(xlen, rve, log_commits_enabled);
10701085
}
10711086

1072-
void processor_t::register_insn(insn_desc_t desc)
1073-
{
1087+
void processor_t::register_insn(insn_desc_t desc, bool is_custom) {
10741088
assert(desc.fast_rv32i && desc.fast_rv64i && desc.fast_rv32e && desc.fast_rv64e &&
10751089
desc.logged_rv32i && desc.logged_rv64i && desc.logged_rv32e && desc.logged_rv64e);
10761090

1077-
instructions.push_back(desc);
1091+
if (is_custom)
1092+
custom_instructions.push_back(desc);
1093+
else
1094+
instructions.push_back(desc);
10781095
}
10791096

10801097
void processor_t::build_opcode_map()
@@ -1086,16 +1103,17 @@ void processor_t::build_opcode_map()
10861103
return lhs.match > rhs.match;
10871104
}
10881105
};
1106+
10891107
std::sort(instructions.begin(), instructions.end(), cmp());
1108+
std::sort(custom_instructions.begin(), custom_instructions.end(), cmp());
10901109

10911110
for (size_t i = 0; i < OPCODE_CACHE_SIZE; i++)
10921111
opcode_cache[i] = insn_desc_t::illegal();
10931112
}
10941113

1095-
void processor_t::register_extension(extension_t* x)
1096-
{
1114+
void processor_t::register_extension(extension_t *x) {
10971115
for (auto insn : x->get_instructions())
1098-
register_insn(insn);
1116+
register_custom_insn(insn);
10991117
build_opcode_map();
11001118

11011119
for (auto disasm_insn : x->get_disasms())
@@ -1131,7 +1149,7 @@ void processor_t::register_base_instructions()
11311149
extern reg_t logged_rv32e_##name(processor_t*, insn_t, reg_t); \
11321150
extern reg_t logged_rv64e_##name(processor_t*, insn_t, reg_t); \
11331151
if (name##_supported) { \
1134-
register_insn((insn_desc_t) { \
1152+
register_base_insn((insn_desc_t) { \
11351153
name##_match, \
11361154
name##_mask, \
11371155
fast_rv32i_##name, \
@@ -1144,10 +1162,8 @@ void processor_t::register_base_instructions()
11441162
logged_rv64e_##name}); \
11451163
}
11461164
#include "insn_list.h"
1147-
#undef DEFINE_INSN
1148-
11491165
// terminate instruction list with a catch-all
1150-
register_insn(insn_desc_t::illegal());
1166+
register_base_insn(insn_desc_t::illegal());
11511167

11521168
build_opcode_map();
11531169
}

riscv/processor.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,12 @@ class processor_t : public abstract_device_t
281281

282282
FILE *get_log_file() { return log_file; }
283283

284-
void register_insn(insn_desc_t);
284+
void register_base_insn(insn_desc_t insn) {
285+
register_insn(insn, false /* is_custom */);
286+
}
287+
void register_custom_insn(insn_desc_t insn) {
288+
register_insn(insn, true /* is_custom */);
289+
}
285290
void register_extension(extension_t*);
286291

287292
// MMIO slave interface
@@ -336,6 +341,7 @@ class processor_t : public abstract_device_t
336341
mutable std::bitset<NUM_ISA_EXTENSIONS> extension_assumed_const;
337342

338343
std::vector<insn_desc_t> instructions;
344+
std::vector<insn_desc_t> custom_instructions;
339345
std::unordered_map<reg_t,uint64_t> pc_histogram;
340346

341347
static const size_t OPCODE_CACHE_SIZE = 8191;
@@ -346,6 +352,7 @@ class processor_t : public abstract_device_t
346352
void take_trap(trap_t& t, reg_t epc); // take an exception
347353
void take_trigger_action(triggers::action_t action, reg_t breakpoint_tval, reg_t epc, bool virt);
348354
void disasm(insn_t insn); // disassemble and print an instruction
355+
void register_insn(insn_desc_t, bool);
349356
int paddr_bits();
350357

351358
void enter_debug_mode(uint8_t cause);

0 commit comments

Comments
 (0)