Skip to content

Commit aaa537f

Browse files
add py generator for decoder based on binary decode tree
1 parent 65171e1 commit aaa537f

17 files changed

Lines changed: 436 additions & 13 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ build
22
*.log
33
compile_commands.json
44
.cache
5+
perf.data
6+
perf.data.old
7+
__pycache__
58

69
# tests:
710
*.o

src/isa/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,5 @@ target_link_libraries(isa
2121
sim-flags
2222
sim
2323
helpers
24-
2524
softfloat
2625
)

src/isa/include/isa/ext_f.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// name mask match
1+
// name mask match
22
MNEMONIC(FLW, 0x0000707f, 0x00002007)
33
MNEMONIC(FSW, 0x0000707f, 0x00002027)
44

src/sim/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
add_library(sim STATIC)
22

3+
add_subdirectory(decode)
4+
35
target_sources(sim
46
PRIVATE
57
source/elf_loader.cpp
6-
source/decode.cpp
78
source/memory.cpp
89
source/rv_sim.cpp
910
source/dispatch.cpp
@@ -19,6 +20,7 @@ target_link_libraries(sim
1920
sim-flags
2021
helpers
2122
isa
23+
decode
2224

2325
spdlog
2426
elfio::elfio

src/sim/decode/CMakeLists.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
add_library(decode STATIC)
2+
3+
add_subdirectory(pygen)
4+
5+
set_source_files_properties("${DECODE_CPP_OUT}" PROPERTIES GENERATED TRUE)
6+
7+
target_sources(decode PRIVATE "${DECODE_CPP_OUT}")
8+
9+
add_dependencies(decode ${DECODE_CODEGEN_TARGET})
10+
11+
target_include_directories(decode
12+
PUBLIC
13+
include
14+
)
15+
16+
target_link_libraries(decode
17+
PRIVATE
18+
sim-flags
19+
helpers
20+
isa
21+
22+
spdlog
23+
softfloat
24+
)
25+
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
#include "isa/mnemonics.hpp"
55
#include "isa/isa_defs.hpp"
66

7-
namespace sim {
7+
namespace sim::decode {
88

99
isa::InsnMnemonic Decode(isa::UndecodedInsn insn);
1010

11-
}
11+
} // namespace sim::decode
1212

1313
#endif // DECODE_HPP_
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
find_package(Python3 REQUIRED COMPONENTS Interpreter)
2+
3+
set(DECODE_PYGEN_DIR "${CMAKE_CURRENT_LIST_DIR}")
4+
set(DECODE_DIR "${DECODE_PYGEN_DIR}/..") # decode/
5+
get_filename_component(DECODE_DIR "${DECODE_DIR}" ABSOLUTE) # normilize path
6+
7+
set(GEN_SCRIPT "${DECODE_PYGEN_DIR}/gen_decoder.py")
8+
set(GEN_DEPS
9+
"${DECODE_PYGEN_DIR}/gen_decoder.py"
10+
"${DECODE_PYGEN_DIR}/insn_mnem.py"
11+
"${DECODE_PYGEN_DIR}/opcode_dtree.py"
12+
"${DECODE_PYGEN_DIR}/mnem_parse.py"
13+
"${DECODE_PYGEN_DIR}/emit_cpp.py"
14+
)
15+
16+
set(LIN_CPP "${DECODE_DIR}/source/lin_decode.cpp")
17+
18+
set(ISA_EXT_INC "${CMAKE_SOURCE_DIR}/src/isa/include/isa/isa_ext.inc")
19+
20+
set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
21+
set(DECODE_CPP_OUT "${GEN_DIR}/decode.cpp")
22+
set(DOT_OUT "${GEN_DIR}/dtree.dot")
23+
set(COMPILE_DOT_SH "compile_dot.sh")
24+
25+
# options
26+
set(DECODE_BACKEND "generated" CACHE STRING "Decode backend: linear|generated")
27+
set_property(CACHE DECODE_BACKEND PROPERTY STRINGS linear generated)
28+
29+
option(DECODE_EMIT_DOT "Emit decode decision tree .dot during generation" OFF)
30+
set(DECODE_GEN_EXTRA_ARGS "" CACHE STRING "Extra args for gen_decoder.py (CMake list separated by ';')")
31+
32+
# Build command depending on backend
33+
if (DECODE_BACKEND STREQUAL "generated")
34+
if (DECODE_EMIT_DOT)
35+
set(DOT_ARGS --dot "${DOT_OUT}")
36+
else()
37+
set(DOT_ARGS)
38+
endif()
39+
40+
add_custom_command(
41+
OUTPUT "${DECODE_CPP_OUT}"
42+
COMMAND "${CMAKE_COMMAND}" -E make_directory "${GEN_DIR}"
43+
COMMAND "${Python3_EXECUTABLE}" "${GEN_SCRIPT}"
44+
--isa "${ISA_EXT_INC}"
45+
--output "${DECODE_CPP_OUT}"
46+
${DOT_ARGS}
47+
${DECODE_GEN_EXTRA_ARGS}
48+
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${DECODE_PYGEN_DIR}/${COMPILE_DOT_SH}" "${GEN_DIR}/${COMPILE_DOT_SH}"
49+
DEPENDS
50+
"${ISA_EXT_INC}"
51+
"${DECODE_PYGEN_DIR}/${COMPILE_DOT_SH}"
52+
${GEN_DEPS}
53+
WORKING_DIRECTORY "${DECODE_PYGEN_DIR}"
54+
COMMENT "Generating decoder.cpp (backend=generated)"
55+
VERBATIM
56+
)
57+
elseif (DECODE_BACKEND STREQUAL "linear")
58+
add_custom_command(
59+
OUTPUT "${DECODE_CPP_OUT}"
60+
COMMAND "${CMAKE_COMMAND}" -E make_directory "${GEN_DIR}"
61+
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${LIN_CPP}" "${DECODE_CPP_OUT}"
62+
DEPENDS "${LIN_CPP}"
63+
COMMENT "Preparing decoder.cpp (backend=linear)"
64+
VERBATIM
65+
)
66+
else()
67+
message(FATAL_ERROR "Unknown DECODE_BACKEND='${DECODE_BACKEND}'. Use linear|generated.")
68+
endif()
69+
70+
add_custom_target(decode_codegen DEPENDS "${DECODE_CPP_OUT}")
71+
72+
# Export outputs to decode
73+
set(DECODE_CPP_OUT "${DECODE_CPP_OUT}" PARENT_SCOPE)
74+
set(DECODE_CODEGEN_TARGET decode_codegen PARENT_SCOPE)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#/bin/bash
2+
3+
dot -Tsvg $1 -o $1.svg

src/sim/decode/pygen/emit_cpp.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from opcode_dtree import DTree, DTreeLeaf, DTreeNode, DTreeTestBit
2+
from pathlib import Path
3+
4+
def _node_gen(node: DTreeNode, depth: int = 0) -> str:
5+
indent = " " * depth
6+
7+
if isinstance(node, DTreeLeaf):
8+
return f"{indent}return isa::InsnMnemonic::{node.insn_name};\n"
9+
10+
cond_if = f"{indent}if (isa::IsBitSet(insn, {node.bit_idx})) {{\n"
11+
cond_else = f"{indent}}} else {{\n"
12+
cond_end = f"{indent}}}\n"
13+
14+
return cond_if + _node_gen(node.one, depth=depth + 1) + cond_else + _node_gen(node.zero, depth=depth + 1) + cond_end
15+
16+
def gen(op_dtree: DTree, namespace: str = "sim::decode") -> str:
17+
prologue = f"""
18+
// THIS CODE IS GENERATED
19+
// by decode/gen_decoder.py
20+
#include "decode/decode.hpp"
21+
22+
#include "isa/isa_hlp.hpp"
23+
#include "isa/mnemonics.hpp"
24+
25+
namespace {namespace} {{
26+
27+
isa::InsnMnemonic Decode(isa::UndecodedInsn insn) {{
28+
"""
29+
30+
epilogue = f"""
31+
}}
32+
33+
}} // namespace {namespace}
34+
"""
35+
36+
decode_body = _node_gen(op_dtree.get_root(), depth=1)
37+
38+
return prologue + decode_body + epilogue
39+
40+
def write_gen(gen_path: Path, opcode_dtree: DTree, namespace: str = "sim::decode"):
41+
gen_code = gen(opcode_dtree, namespace=namespace)
42+
gen_path.write_text(gen_code)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import argparse
2+
from pathlib import Path
3+
4+
import mnem_parse
5+
import opcode_dtree
6+
import emit_cpp
7+
8+
if __name__ == "__main__":
9+
parser = argparse.ArgumentParser()
10+
parser.add_argument("--isa", required=True, help="isa_ext.inc file path")
11+
parser.add_argument("--output", required=True, help="output file path")
12+
parser.add_argument("--dot", help="file path to .dot representation of decode tree")
13+
args = parser.parse_args()
14+
15+
isa_path = Path(args.isa).resolve()
16+
17+
mnems = mnem_parse.parse(isa_path)
18+
dtree = opcode_dtree.build_dtree(mnems)
19+
if args.dot:
20+
dot_path = Path(args.dot)
21+
dtree.write_dot(dot_path)
22+
23+
cpp_path = Path(args.output)
24+
emit_cpp.write_gen(cpp_path, dtree)

0 commit comments

Comments
 (0)