Skip to content
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

Updates for S4EMac Extension (PoC for custom register support) #99

Merged
merged 14 commits into from
Aug 22, 2024
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
2 changes: 1 addition & 1 deletion examples/cfg/s4e/s4e-mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
extensions:
s4e-mac:
feature: s4emac
arch: x4emac
arch: xs4emac
version: "1.0"
experimental: true
vendor: true
Expand Down
5 changes: 4 additions & 1 deletion seal5/backends/riscv_instr_info/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ def write_riscv_instruction_info(

operands, fields = process_encoding(enc)

attrs = {key: int(value) if isinstance(value, bool) else value for key, value in attrs.items()}
def attr_helper(value):
return int(value) if isinstance(value, bool) else value

attrs = {key: attr_helper(value) for key, value in attrs.items()}
constraints_str = ", ".join(constraints)

out_str = instr_template.render(
Expand Down
22 changes: 22 additions & 0 deletions seal5/backends/riscv_register_info/templates/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#
# Copyright (c) 2023 TUM Department of Electrical and Computer Engineering.
#
# This file is part of Seal5.
# See https://github.com/tum-ei-eda/seal5.git for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Templates for RISCVRegisterInfo.td patches"""
import pathlib

template_dir = pathlib.Path(__file__).parent.resolve()
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// class RVInst_${name}<dag outs, dag ins> : RVInst<outs, ins, "cv.add.h", "$rd, $rs1, $rs2", [], InstFormatOther> {
class RVInst_${name}<dag outs, dag ins> : Instruction, Sched<${sched_str}> {
// General
let Namespace = "RISCV";
let Size = ${xlen // 8};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

floor is surely safe, in that a size other than 32 or 64 seems unlikely, and even DSP-ish 24 is still sized in bytes. But maybe an assert of the assumption would be documentary? (size % 8 == 0)

bits<32> SoftFail = 0;
bits<${xlen}> Inst;

// Operands
% for operand in operands:
bits<${operand.length}> ${operand.name};
% endfor

// Attributes
% for key, value in attrs.items():
let ${key} = ${value};
% endfor

// Encoding
% for enc in fields:

% if enc.length == 1:
% if enc.extra is None:
let Inst{${enc.start}} = ${enc.name};
% else:
let Inst{${enc.start}} = ${bin(enc.extra)};${" // " + enc.name if enc.name else ""}
% endif
% else:
% if enc.extra is None:
let Inst{${enc.start+enc.length-1}-${enc.start}} = ${enc.name};
% else:
let Inst{${enc.start+enc.length-1}-${enc.start}} = ${f"0b{enc.extra:0{enc.length}b}"};${" // " + enc.name if enc.name else ""}
% endif
% endif
% endfor

dag OutOperandList = outs;
dag InOperandList = ins;

// Assembly
let AsmString = "${real_name}\t${asm_str}";

% if len(constraints_str):
// Constraints
let Constraints = "${constraints_str}";
% endif
}
def ${name} : RVInst_${name}<(outs ${outs_str}), (ins ${ins_str})>;
174 changes: 174 additions & 0 deletions seal5/backends/riscv_register_info/writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# SPDX-License-Identifier: Apache-2.0
#
# This file is part of the M2-ISA-R project: https://github.com/tum-ei-eda/M2-ISA-R
#
# Copyright (C) 2022
# Chair of Electrical Design Automation
# Technical University of Munich

"""Generate Patches for RISCVRegisterInfo.td."""

import argparse
import logging
import pathlib
import pickle
from typing import Union

from m2isar.metamodel import arch

from seal5.index import NamedPatch, write_index_yaml

# from seal5.settings import ExtensionsSettings
from seal5.model import Seal5RegisterClass

logger = logging.getLogger("riscv_instr_info")


def write_riscv_register_info(reg):
# registers_template = Template(filename=str(template_dir / "registers_tablegen.mako"))

# out_str = registers_template.render()
out_str = f'def {reg.name} : RISCVReg<0, "{reg.name.lower()}">;'

return out_str


def gen_riscv_register_info_str(set_def):
registers = set_def.registers
register_groups = set_def.register_groups
print("registers", registers)
print("register_groups", register_groups)
group_regs = {group_name: group.names for group_name, group in register_groups.items()}
all_group_regs = [name for names in group_regs.values() for name in names]
ret = []
for group_name, group in register_groups.items():
if group.reg_class == Seal5RegisterClass.GPR:
continue # Already supported
# TODO: check size and width
elif group.reg_class == Seal5RegisterClass.FPR:
raise NotImplementedError("Floating point registers not supported")
elif group.reg_class == Seal5RegisterClass.CSR:
raise NotImplementedError("CSR registers not yet supported")
elif group.reg_class == Seal5RegisterClass.CUSTOM:
raise NotImplementedError("Custom register goups not yet supported")
else:
raise ValueError(f"Unhandled case: {group.reg_class}")
for reg_name, reg in registers.items():
if reg_name in all_group_regs:
logger.debug("Skipping group register %s", reg_name)
continue
assert reg.reg_class not in [Seal5RegisterClass.GPR, Seal5RegisterClass.FPR, Seal5RegisterClass.CSR]
if reg.reg_class == Seal5RegisterClass.CUSTOM:
assert reg.size == 1
tablegen_str = write_riscv_register_info(reg)
ret.append(tablegen_str)
else:
raise ValueError(f"Unhandled case: {reg.reg_class}")
# width = reg.width
# TODO: use width?
print("ret", ret)
return "\n".join(ret)


def main():
"""Main app entrypoint."""

# read command line args
parser = argparse.ArgumentParser()
parser.add_argument("top_level", help="A .m2isarmodel or .seal5model file.")
parser.add_argument("--log", default="info", choices=["critical", "error", "warning", "info", "debug"])
parser.add_argument("--output", "-o", type=str, default=None)
parser.add_argument("--splitted", action="store_true", help="Split per set")
parser.add_argument("--formats", action="store_true", help="Also generate instruction formats")
parser.add_argument("--metrics", default=None, help="Output metrics to file")
parser.add_argument("--index", default=None, help="Output index to file")
parser.add_argument("--ext", type=str, default="td", help="Default file extension (if using --splitted)")
args = parser.parse_args()

# initialize logging
logging.basicConfig(level=getattr(logging, args.log.upper()))

# resolve model paths
top_level = pathlib.Path(args.top_level)
# abs_top_level = top_level.resolve()

is_seal5_model = False
# print("top_level", top_level)
# print("suffix", top_level.suffix)
if top_level.suffix == ".seal5model":
is_seal5_model = True
if args.output is None:
assert top_level.suffix in [".m2isarmodel", ".seal5model"], "Can not infer model type from file extension."
raise NotImplementedError

# out_path = top_level.parent / (top_level.stem + ".core_desc")
else:
out_path = pathlib.Path(args.output)

logger.info("loading models")
if not is_seal5_model:
raise NotImplementedError

# load models
with open(top_level, "rb") as f:
# models: "dict[str, arch.CoreDef]" = pickle.load(f)
if is_seal5_model:
model: "dict[str, Union[arch.InstructionSet, ...]]" = pickle.load(f)
model["cores"] = {}
else: # TODO: core vs. set!
temp: "dict[str, Union[arch.InstructionSet, arch.CoreDef]]" = pickle.load(f)
assert len(temp) > 0, "Empty model!"
if isinstance(list(temp.values())[0], arch.CoreDef):
model = {"cores": temp, "sets": {}}
elif isinstance(list(temp.values())[0], arch.InstructionSet):
model = {"sets": temp, "cores": {}}
else:
assert False

metrics = {
"n_sets": 0,
"n_skipped": 0,
"n_failed": 0,
"n_success": 0,
}
# preprocess model
# print("model", model)
# settings = model.get("settings", None)
artifacts = {}
artifacts[None] = [] # used for global artifacts
if args.splitted:
raise NotImplementedError("--splitted not supported")
else:
content = ""
for set_name, set_def in model["sets"].items():
content_ = gen_riscv_register_info_str(set_def)
if len(content_) > 0:
content += content_
with open(out_path, "w") as f:
f.write(content)
register_info_patch = NamedPatch(
"llvm/lib/Target/RISCV/RISCVRegisterInfo.td",
key="riscv_register_info",
src_path=out_path,
)
artifacts[None].append(register_info_patch)
if args.metrics:
raise NotImplementedError("--metrics not implemented")
metrics_file = args.metrics
with open(metrics_file, "w") as f:
f.write(",".join(metrics.keys()))
f.write("\n")
f.write(",".join(map(str, metrics.values())))
f.write("\n")
if args.index:
if sum(map(lambda x: len(x), artifacts.values())) > 0:
global_artifacts = artifacts.get(None, [])
set_artifacts = {key: value for key, value in artifacts.items() if key is not None}
index_file = args.index
write_index_yaml(index_file, global_artifacts, set_artifacts, content=True)
else:
logger.warning("No patches generated. No index file will be written.")


if __name__ == "__main__":
main()
3 changes: 2 additions & 1 deletion seal5/flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,12 @@ def create_passes(self):
("process_settings", passes.process_settings, {}),
("write_yaml", passes.write_yaml, {}),
("detect_behavior_constraints", passes.detect_behavior_constraints, {}),
("detect_registers", passes.detect_registers, {}),
("collect_register_operands", passes.collect_register_operands, {}),
("collect_immediate_operands", passes.collect_immediate_operands, {}),
("collect_operand_types", passes.collect_operand_types, {}),
("detect_side_effects", passes.detect_side_effects, {}),
("detect_inouts", passes.detect_inouts, {}),
("detect_registers", passes.detect_registers, {}),
("write_cdsl_full", passes.write_cdsl, {"split": False, "compat": False}),
# TODO: determine static constraints (xlen,...) -> subtargetvmap
# detect memory adressing modes
Expand All @@ -210,6 +210,7 @@ def create_passes(self):
("riscv_features", passes.gen_riscv_features_patch, {}),
("riscv_isa_infos", passes.gen_riscv_isa_info_patch, {}),
# ("riscv_instr_formats", passes.gen_riscv_instr_formats_patch, {}),
("riscv_register_info", passes.gen_riscv_register_info_patch, {}),
("riscv_instr_info", passes.gen_riscv_instr_info_patch, {}),
# subtarget_tests
# register_types
Expand Down
26 changes: 23 additions & 3 deletions seal5/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,35 @@


class Seal5Register:
def __init__(self, name: str, size: int, width: int, reg_class: Seal5RegisterClass):
def __init__(self, name: str, size: int, width: int, signed: bool, reg_class: Seal5RegisterClass):
self.name = name
self.size = size
self.width = width
self.signed = signed # TODO: use
self.reg_class = reg_class
# TODO: attributes

def __repr__(self):
return f"{type(self)}({self.name}, size={self.size}, width={self.width}, signed={self.signed}, reg_class={self.reg_class})"

Check failure on line 92 in seal5/model.py

View workflow job for this annotation

GitHub Actions / Flake8

seal5/model.py#L92

Line too long (131 > 120 characters) (E501)


class Seal5RegisterGroup:
def __init__(self, names: List[str], size: int, width: int, reg_class: Seal5RegisterClass):
def __init__(self, names: List[str], size: int, width: int, signed: bool, reg_class: Seal5RegisterClass):
self.names = names
self.size = size
self.width = width
self.signed = signed # TODO: use
self.reg_class = reg_class

def __repr__(self):
return f"{type(self)}({self.names}, size={self.size}, width={self.width}, signed={self.signed}, reg_class={self.reg_class})"

Check failure on line 104 in seal5/model.py

View workflow job for this annotation

GitHub Actions / Flake8

seal5/model.py#L104

Line too long (132 > 120 characters) (E501)

@property
def registers(self):
return [Seal5Register(name, size=self.size, width=self.width, reg_class=self.reg_class) for name in self.names]
return [
Seal5Register(name, size=self.size, width=self.width, signed=self.signed, reg_class=self.reg_class)
for name in self.names
]

def __len__(self):
return len(self.names)
Expand Down Expand Up @@ -128,6 +140,8 @@
IS_TERMINATOR = auto()
IS_BRANCH = auto()
COMPRESSED = auto()
USES = auto()
DEFS = auto()


class Seal5OperandAttribute(Enum):
Expand Down Expand Up @@ -500,6 +514,12 @@
attrs["isTerminator"] = 1
else:
attrs["isTerminator"] = 0
uses = self.attributes.get(Seal5InstrAttribute.USES, [])
if uses:
attrs["Uses"] = "[" + ", ".join(uses) + "]"
defs = self.attributes.get(Seal5InstrAttribute.DEFS, [])
if defs:
attrs["Defs"] = "[" + ", ".join(defs) + "]"
return attrs

def llvm_get_compressed_pat(self, set_def):
Expand Down
Loading
Loading