#[macro_use]
extern crate log;
use clap::{App, Arg, ArgMatches};
use llhd;
use moore_circt::{self as circt, mlir, prelude::*, sys::*};
use moore::common::score::NodeRef;
use moore::errors::*;
use moore::name::Name;
use moore::score::{ScoreBoard, ScoreContext};
use moore::source::Span;
use moore::svlog::{ast::AcceptVisitor as _, hir::Visitor as _, QueryDatabase as _};
use moore::*;
use std::path::Path;
#[derive(Debug)]
enum Language {
Verilog,
SystemVerilog,
Vhdl,
}
fn main() {
let mut builder = pretty_env_logger::formatted_builder();
builder.parse_filters(
std::env::var("MOORE_LOG")
.ok()
.as_ref()
.map(|s| s.as_str())
.unwrap_or("off"),
);
builder.try_init().unwrap();
let matches = App::new(env!("CARGO_PKG_NAME"))
.version(clap::crate_version!())
.author(clap::crate_authors!())
.about(clap::crate_description!())
.arg(
Arg::with_name("trace_scoreboard")
.long("trace-scoreboard")
.global(true),
)
.arg(
Arg::with_name("verbosity-opts")
.short("V")
.help("Sets verbosity settings")
.takes_value(true)
.multiple(true)
.number_of_values(1)
.possible_values(&[
"types",
"expr-types",
"type-contexts",
"typeck",
"names",
"casts",
"ports",
"consts",
"insts",
"func-args",
"call-args",
])
.global(true),
)
.arg(
Arg::with_name("inc")
.short("I")
.value_name("DIR")
.help("Add a search path for SystemVerilog includes")
.multiple(true)
.takes_value(true)
.number_of_values(1),
)
.arg(
Arg::with_name("def")
.short("D")
.value_name("DEFINE")
.help("Define a preprocesor macro")
.multiple(true)
.takes_value(true)
.number_of_values(1),
)
.arg(
Arg::with_name("preproc")
.short("E")
.help("Write preprocessed input files to stdout"),
)
.arg(
Arg::with_name("dump-ast")
.long("dump-ast")
.help("Dump the parsed abstract syntax tree"),
)
.arg(
Arg::with_name("check-syntax")
.long("syntax")
.help("Preprocess and check the input for syntax errors"),
)
.arg(
Arg::with_name("emit_pkgs")
.long("emit-pkgs")
.help("Dump VHDL packages for debugging"),
)
.arg(
Arg::with_name("opt-level")
.short("O")
.long("opt-level")
.help("Sets optimization level applied to the output")
.default_value("1")
.takes_value(true)
.number_of_values(1),
)
.arg(
Arg::with_name("lib")
.short("l")
.long("lib")
.value_name("LIB")
.help("Name of the library to compile into")
.takes_value(true)
.number_of_values(1),
)
.arg(
Arg::with_name("elaborate")
.short("e")
.long("elaborate")
.value_name("ENTITY")
.help("Elaborate an entity or module")
.multiple(true)
.takes_value(true)
.number_of_values(1),
)
.arg(
Arg::with_name("output")
.short("o")
.long("output")
.help("Output file (`-` for stdout)")
.takes_value(true),
)
.arg(
Arg::with_name("output-format")
.short("f")
.long("format")
.help("Output format")
.takes_value(true)
.possible_values(&["llhd", "mlir", "mlir-native"]),
)
.arg(
Arg::with_name("debug-info")
.short("g")
.long("debug-info")
.help("Emit location information as part of the output"),
)
.arg(
Arg::with_name("INPUT")
.help("The input files to compile")
.multiple(true)
.required(true),
)
.get_matches();
let mut session = Session::new();
session.opts.trace_scoreboard = matches.is_present("trace_scoreboard");
for v in matches
.values_of("verbosity-opts")
.into_iter()
.flat_map(|v| v)
{
session.opts.verbosity |= match v {
"types" => Verbosity::TYPES,
"expr-types" => Verbosity::EXPR_TYPES,
"type-contexts" => Verbosity::TYPE_CONTEXTS,
"typeck" => Verbosity::TYPECK,
"names" => Verbosity::NAMES,
"casts" => Verbosity::CASTS,
"ports" => Verbosity::PORTS,
"consts" => Verbosity::CONSTS,
"insts" => Verbosity::INSTS,
"func-args" => Verbosity::FUNC_ARGS,
"call-args" => Verbosity::CALL_ARGS,
_ => unreachable!(),
};
}
session.opts.opt_level = matches.value_of("opt-level").unwrap().parse().unwrap();
score(&session, &matches);
}
fn score(sess: &Session, matches: &ArgMatches) {
use crate::name::get_name_table;
let svlog_arenas = svlog::GlobalArenas::default();
let include_paths: Vec<_> = match matches.values_of("inc") {
Some(args) => args.map(|x| std::path::Path::new(x)).collect(),
None => Vec::new(),
};
let defines: Vec<_> = match matches.values_of("def") {
Some(args) => args
.map(|x| {
let mut iter = x.split("=");
(iter.next().unwrap(), iter.next())
})
.collect(),
None => Vec::new(),
};
let lib = get_name_table().intern(matches.value_of("lib").unwrap_or("work"), true);
let mut failed = false;
let mut asts = Vec::new();
for filename in matches.values_of("INPUT").unwrap() {
if filename.is_empty() {
continue;
}
let language = match Path::new(&filename).extension().and_then(|s| s.to_str()) {
Some("sv") | Some("svh") => Language::SystemVerilog,
Some("v") | Some("vh") => Language::Verilog,
Some("vhd") | Some("vhdl") => Language::Vhdl,
Some(ext) => {
sess.emit(
DiagBuilder2::warning(format!("ignoring `{}`", filename)).add_note(format!(
"Cannot determine language from extension `.{}`",
ext
)),
);
continue;
}
None => {
sess.emit(
DiagBuilder2::warning(format!("ignoring `{}`", filename)).add_note(format!(
"No file extension that can be used to guess language"
)),
);
continue;
}
};
let sm = source::get_source_manager();
let source = match sm.open(&filename) {
Some(s) => s,
None => {
sess.emit(DiagBuilder2::fatal(format!(
"unable to open `{}`",
filename
)));
continue;
}
};
match language {
Language::SystemVerilog | Language::Verilog => {
let preproc = svlog::preproc::Preprocessor::new(source, &include_paths, &defines);
if matches.is_present("preproc") {
for token in preproc {
print!(
"{}",
match token {
Ok((_token, span)) => span.extract(),
Err(diag) => {
sess.emit(diag);
failed = true;
continue;
}
}
);
}
continue;
}
let lexer = svlog::lexer::Lexer::new(preproc);
match svlog::parser::parse(lexer, &svlog_arenas.ast) {
Ok(x) => asts.push(score::Ast::Svlog(x)),
Err(()) => failed = true,
}
}
Language::Vhdl => match vhdl::syntax::parse(source) {
Ok(x) => asts.push(score::Ast::Vhdl(x)),
Err(()) => failed = true,
},
}
}
if failed || sess.failed() {
std::process::exit(1);
}
if matches.is_present("preproc") {
return;
}
if matches.is_present("dump-ast") {
println!("{:#99?}", asts);
}
if matches.is_present("emit_pkgs") {
vhdl::debug::emit_pkgs(
sess,
asts.iter()
.flat_map(|ast| match *ast {
score::Ast::Vhdl(ref x) => x.iter(),
_ => [].iter(),
})
.collect(),
);
}
if matches.is_present("check-syntax") {
std::process::exit(0);
}
let arenas = score::Arenas::new();
let sb = ScoreBoard::new(&arenas);
let vhdl_sb = vhdl::score::ScoreBoard::new(&arenas.vhdl);
let svlog_sb = svlog::GlobalContext::new(&sess, &svlog_arenas);
{
let vhdl_phases = vhdl::lazy::LazyPhaseTable::new(&vhdl_sb);
let ctx = ScoreContext {
sess: sess,
sb: &sb,
vhdl: &vhdl_sb,
vhdl_phases: &vhdl_phases,
svlog: &svlog_sb,
};
let lib_id = ctx.add_library(lib, &asts);
for root in ctx.svlog.roots() {
if ctx.sess.has_verbosity(Verbosity::FUNC_ARGS) {
root.accept(&mut svlog::func_args::FuncArgsVerbosityVisitor::new(
ctx.svlog,
));
}
}
for root in ctx.svlog.roots() {
if ctx.sess.has_verbosity(Verbosity::CALL_ARGS) {
root.accept(&mut svlog::call_mapping::CallMappingVerbosityVisitor::new(
ctx.svlog,
));
}
}
if let Some(names) = matches.values_of("elaborate") {
debug!("lib_id = {:?}", lib_id);
debug!("{:?}", sb);
for name in names {
match elaborate_name(matches, &ctx, lib_id, name) {
Ok(_) => (),
Err(_) => failed = true,
};
}
if sess.failed() {
failed = true;
}
}
}
if failed || sess.failed() {
std::process::exit(1);
}
let _vhdl_module = vhdl_sb.llmod.into_inner();
if sess.failed() {
std::process::exit(1);
}
}
fn elaborate_name(
matches: &ArgMatches,
ctx: &ScoreContext,
lib_id: score::LibRef,
input_name: &str,
) -> Result<(), ()> {
let (lib, name, arch) = parse_elaborate_name(input_name)?;
debug!(
"parsed `{}` into (lib: {:?}, name: {:?}, arch: {:?})",
input_name, lib, name, arch
);
let lib = {
if let Some(lib) = lib {
let rid = ctx.sb.root;
let defs = ctx.defs(score::ScopeRef::Root(rid))?;
match defs.get(&lib) {
Some(&score::Def::Lib(d)) => d,
_ => {
let mut d = DiagBuilder2::error(format!("Library `{}` does not exist", lib))
.add_note("The following libraries do exist:");
let mut names: Vec<_> = defs.iter().map(|(&k, _)| k).collect();
names.sort(); for name in names {
d = d.add_note(format!("- {}", name));
}
ctx.sess.emit(d);
return Err(());
}
}
} else {
lib_id
}
};
debug!("using library {:?}", lib);
#[derive(Debug)]
enum Elaborate {
VhdlEntity(vhdl::score::EntityRef, vhdl::score::ArchRef),
VhdlPkg(vhdl::score::PkgDeclRef),
Svlog(NodeId), }
let defs = ctx.defs(lib.into())?;
let elab = match defs.get(&name) {
Some(&score::Def::Vhdl(vhdl::score::Def::Entity(entity))) => {
let archs = ctx
.vhdl()
.archs(vhdl::score::LibRef::new(lib.into()))?
.by_entity
.get(&entity)
.unwrap();
let arch_ref = if let Some(arch) = arch {
match archs.by_name.get(&arch) {
Some(&id) => id,
None => {
ctx.sess.emit(DiagBuilder2::error(format!(
"`{}` is not an architecture of entity `{}`",
arch, name
)));
return Err(());
}
}
} else {
match archs.ordered.last() {
Some(&id) => id,
None => {
ctx.sess.emit(DiagBuilder2::error(format!(
"Entity `{}` has no architecture defined",
name
)));
return Err(());
}
}
};
Elaborate::VhdlEntity(entity, arch_ref)
}
Some(&score::Def::Vhdl(vhdl::score::Def::Pkg(p))) => Elaborate::VhdlPkg(p),
Some(&score::Def::Svlog(e)) => Elaborate::Svlog(e),
_ => {
let mut d = DiagBuilder2::error(format!("Item `{}` does not exist", name))
.add_note("The following items are defined:");
let mut names: Vec<_> = defs.iter().map(|(&k, _)| k).collect();
names.sort(); for name in names {
d = d.add_note(format!("- {}", name));
}
ctx.sess.emit(d);
return Err(());
}
};
debug!("elaborating {:?}", elab);
match elab {
Elaborate::VhdlEntity(_entity, arch) => {
let def = ctx.vhdl().llunit(arch)?;
eprintln!("Architecture declared as {:?}", def);
}
Elaborate::VhdlPkg(pkg) => {
use moore::vhdl::typeck::{Typeck, TypeckContext};
let sbc = ctx.vhdl();
let tyc = TypeckContext::new(&sbc);
tyc.typeck(pkg);
}
Elaborate::Svlog(m) => {
if ctx.sess.has_verbosity(Verbosity::TYPES) {
use svlog::Context;
TypeVerbosityVisitor(ctx.svlog, ctx.svlog.default_param_env())
.visit_node_with_id(m, false);
}
if ctx.sess.has_verbosity(Verbosity::INSTS) {
svlog::InstVerbosityVisitor::new(ctx.svlog).visit_node_with_id(m, false);
}
let mlir_cx = mlir::OwnedContext::new();
mlir_cx.load_dialect(circt::std::dialect());
mlir_cx.load_dialect(circt::hw::dialect());
mlir_cx.load_dialect(circt::comb::dialect());
mlir_cx.load_dialect(circt::llhd::dialect());
mlir_cx.load_dialect(circt::seq::dialect());
unsafe {
mlirContextAttachDiagnosticHandler(
mlir_cx.raw(),
Some(moore_mlir_diagnostic_handler),
ctx.sess as *const _ as *mut _,
None,
);
}
let mlir_module = circt::ModuleOp::new(*mlir_cx);
let mut cg = svlog::CodeGenerator::new(ctx.svlog, mlir_module);
for root in ctx.svlog.roots() {
cg.emit_globals(root)?;
}
cg.emit_module(m)?;
let module = cg.finalize();
if !mlir_module.verify() {
ctx.sess.emit(DiagBuilder2::bug(
"verification failed for emitted MLIR module",
));
return Err(());
}
emit_output(matches, ctx, &module, mlir_module)?
}
}
Ok(())
}
unsafe extern "C" fn moore_mlir_diagnostic_handler(
diag: MlirDiagnostic,
sess: *mut std::ffi::c_void,
) -> MlirLogicalResult {
let sess: &Session = (sess as *const Session).as_ref().unwrap();
#[allow(non_upper_case_globals)]
let severity = match mlirDiagnosticGetSeverity(diag) {
MlirDiagnosticSeverity_MlirDiagnosticError => Severity::Error,
MlirDiagnosticSeverity_MlirDiagnosticWarning => Severity::Warning,
MlirDiagnosticSeverity_MlirDiagnosticNote | MlirDiagnosticSeverity_MlirDiagnosticRemark => {
Severity::Note
}
_ => panic!("unsupported MLIR diagnostic severity"),
};
let mut d = DiagBuilder2::new(severity, mlir_diagnostic_to_string(diag));
let main_loc = mlirDiagnosticGetLocation(diag);
if let Some(main_span) = mlir_location_to_span(main_loc) {
d = d.span(main_span);
}
let mut notes_todo = vec![diag];
while let Some(note) = notes_todo.pop() {
if note.ptr != diag.ptr {
let msg = mlir_diagnostic_to_string(note);
let loc = mlirDiagnosticGetLocation(note);
d = d.add_note(msg);
if !loc.ptr.is_null() && main_loc.ptr != loc.ptr {
if let Some(span) = mlir_location_to_span(loc) {
d = d.span(span);
}
}
}
let num_notes = mlirDiagnosticGetNumNotes(note);
for i in (0..num_notes).rev() {
notes_todo.push(mlirDiagnosticGetNote(note, i));
}
}
sess.emit(d);
MlirLogicalResult { value: 1 }
}
unsafe fn mlir_diagnostic_to_string(diag: MlirDiagnostic) -> String {
unsafe extern "C" fn stringify_callback(string: MlirStringRef, to: *mut std::ffi::c_void) {
let to: &mut String = (to as *mut String).as_mut().unwrap();
to.push_str(
std::str::from_utf8(std::slice::from_raw_parts(
string.data as *const _,
string.length as usize,
))
.expect("utf8 string"),
);
}
let mut string = String::new();
mlirDiagnosticPrint(
diag,
Some(stringify_callback),
&mut string as *mut _ as *mut _,
);
string
}
unsafe fn mlir_location_to_span(loc: MlirLocation) -> Option<Span> {
if loc.ptr.is_null() {
return None;
}
if mlirLocationIsFileLineCol(loc) {
let filename = mlirStringRefToStr(mlirFileLineColLocGetFilename(loc), String::from);
let line = mlirFileLineColLocGetLine(loc) as usize;
let column = mlirFileLineColLocGetColumn(loc) as usize;
let source = source::get_source_manager().open(&filename).unwrap();
let loc = source::Location::with_line_and_column(source, line, column);
Some(loc.into())
} else {
None
}
}
#[derive(Debug)]
enum OutputFormat {
Llhd,
Mlir,
MlirNative,
}
fn emit_output(
matches: &ArgMatches,
ctx: &ScoreContext,
module: &llhd::ir::Module,
mlir_module: circt::ModuleOp,
) -> Result<(), ()> {
let fmt = match matches.value_of("output-format") {
Some("llhd") => Some(OutputFormat::Llhd),
Some("mlir") => Some(OutputFormat::Mlir),
Some("mlir-native") => Some(OutputFormat::MlirNative),
Some(x) => {
ctx.sess.emit(DiagBuilder2::fatal(format!(
"unknown output format: `{}`",
x
)));
return Err(());
}
_ => None,
};
let fmt = fmt.or_else(|| {
match matches
.value_of("output")
.and_then(|x| Path::new(x).extension())
.and_then(|x| x.to_str())
{
Some("llhd") => Some(OutputFormat::Llhd),
Some("mlir") => Some(OutputFormat::Mlir),
_ => None,
}
});
let fmt = fmt.unwrap_or(OutputFormat::Llhd);
debug!("Using {:?} output format", fmt);
let stdout = std::io::stdout();
let output: Box<dyn std::io::Write> = match matches.value_of("output") {
Some("-") | None => Box::new(stdout.lock()),
Some(x) => Box::new(std::fs::File::create(x).map_err(|e| {
ctx.sess.emit(
DiagBuilder2::fatal(format!("unable to create file: `{}`", x))
.add_note(format!("{}", e)),
);
()
})?),
};
match fmt {
OutputFormat::Llhd => llhd::assembly::write_module(output, &module),
OutputFormat::Mlir => llhd::mlir::write_module(output, &module),
OutputFormat::MlirNative => mlir_module.print(output, matches.is_present("debug-info")),
};
Ok(())
}
fn parse_elaborate_name<S: AsRef<str>>(name: S) -> Result<(Option<Name>, Name, Option<Name>), ()> {
use self::name::get_name_table;
let name = name.as_ref();
let nt = get_name_table();
let x: &[_] = &['.', '('];
let (first, rest) = {
if let Some(pos) = name.find(x) {
let (a, b) = name.split_at(pos);
(a, Some(b))
} else {
(name, None)
}
};
let first = nt.intern(first, true);
let (second, rest) = {
if let Some(rest) = rest {
if rest.starts_with('.') {
let rest = &rest[1..];
if let Some(pos) = rest.find('(') {
let (a, b) = rest.split_at(pos);
(Some(a), Some(b))
} else {
(Some(rest), None)
}
} else {
(None, Some(rest))
}
} else {
(None, None)
}
};
let second = second.map(|s| nt.intern(s, true));
let third = {
if let Some(rest) = rest {
if rest.starts_with('(') && rest.ends_with(')') {
Some(&rest[1..rest.len() - 1])
} else {
None
}
} else {
None
}
};
let third = third.map(|t| nt.intern(t, true));
let (lib, ent) = {
if let Some(second) = second {
(Some(first), second)
} else {
(None, first)
}
};
Ok((lib, ent, third))
}
pub struct TypeVerbosityVisitor<'a, 'gcx>(&'a svlog::GlobalContext<'gcx>, svlog::ParamEnv);
impl<'a, 'gcx> svlog::hir::Visitor<'gcx> for TypeVerbosityVisitor<'a, 'gcx> {
type Context = svlog::GlobalContext<'gcx>;
fn context(&self) -> &Self::Context {
self.0
}
fn visit_expr(&mut self, expr: &'gcx svlog::hir::Expr<'gcx>, lvalue: bool) {
self.print(expr.id);
svlog::hir::walk_expr(self, expr, lvalue);
}
fn visit_var_decl(&mut self, decl: &'gcx svlog::hir::VarDecl) {
self.print(decl.id);
svlog::hir::walk_var_decl(self, decl);
}
}
impl<'a, 'gcx> TypeVerbosityVisitor<'a, 'gcx> {
fn print(&mut self, id: NodeId) {
use svlog::Context;
let span = self.0.span(id);
let ext = span.extract();
let line = span.begin().human_line();
if let Ok(ty) = self.0.type_of(id, self.1) {
println!("{}: type({}) = {}", line, ext, ty);
}
if let Some(cast) = self.0.cast_type(id, self.1) {
println!("{}: cast_type({}) = {}", line, ext, cast.ty);
println!("{}: cast_chain({}) = {}", line, ext, cast);
}
if let Some(ty) = self.0.self_determined_type(id, self.1) {
println!("{}: self_type({}) = {}", line, ext, ty);
}
if let Some(ty) = self.0.operation_type(id, self.1) {
println!("{}: operation_type({}) = {}", line, ext, ty);
}
if let Some(expr) = self.0.ast_for_id(id).as_all().get_expr() {
if let Some(ty) = self.0.type_context(svlog::Ref(expr), self.1) {
println!(
"{}: type_context({}) = {}",
line,
ext,
match ty {
svlog::typeck::TypeContext::Type(ty) => format!("{}", ty),
svlog::typeck::TypeContext::Bool => "<bool>".to_string(),
}
);
}
}
}
}