tamer: ld::xmle: Narrow Sections types

This moves the logic that sorts identifiers into sections into Sections
itself, and introduces XmleSections to allow for mocking for testing.

This then allows us to narrow the types significantly, eliminating some
runtime checks.  The types can be narrowed further, but I'll be limiting the
work I'll be doing now; this'll be inevitably addressed as we use the ASG
for the compiler.

This also handles moving Sections tests, which was a TODO from the previous
commit.

DEV-10859
main
Mike Gerwitz 2021-10-14 12:37:32 -04:00
parent ea11cf1416
commit f055cb77c2
8 changed files with 626 additions and 599 deletions

View File

@ -27,7 +27,7 @@ use test::Bencher;
use tamer::ir::asg::{
Asg, DataType, DefaultAsg, IdentKind, IdentObject, Source,
};
use tamer::ld::xmle::lower::sort;
use tamer::ld::xmle::{lower::sort, Sections};
use tamer::sym::{GlobalSymbolIntern, SymbolId};
type TestAsg = DefaultAsg<IdentObject>;
@ -61,7 +61,7 @@ fn sort_1_with_1_000_existing_supernode(bench: &mut Bencher) {
});
bench.iter(|| {
drop(sort(&sut, &[root]));
drop(sort(&sut, &[root], Sections::new()));
});
}
@ -95,6 +95,6 @@ fn sort_1_with_1_000_existing_one_edge_per_node_one_path(bench: &mut Bencher) {
let root = orefs[0];
bench.iter(|| {
drop(sort(&sut, &[root]));
drop(sort(&sut, &[root], Sections::new()));
});
}

View File

@ -23,10 +23,7 @@
use super::xmle::{
lower::{sort, SortError},
xir::lower_iter,
Sections,
};
use crate::fs::{
Filesystem, FsCanonicalizer, PathFile, VisitOnceFile, VisitOnceFilesystem,
XmleSections,
};
use crate::global;
use crate::ir::asg::{Asg, DefaultAsg, IdentObject, IdentObjectData};
@ -34,6 +31,13 @@ use crate::ir::xir::writer::XmlWriter;
use crate::obj::xmlo::{AsgBuilder, AsgBuilderState, XmloReader};
use crate::sym::SymbolId;
use crate::sym::{GlobalSymbolIntern, GlobalSymbolResolve};
use crate::{
fs::{
Filesystem, FsCanonicalizer, PathFile, VisitOnceFile,
VisitOnceFilesystem,
},
ld::xmle::Sections,
};
use fxhash::FxBuildHasher;
use petgraph_graphml::GraphMl;
use std::error::Error;
@ -72,7 +76,7 @@ pub fn xmle(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
.filter_map(|sym| depgraph.lookup(sym)),
);
let mut sorted = match sort(&depgraph, &roots) {
let sorted = match sort(&depgraph, &roots, Sections::new()) {
Ok(sections) => sections,
Err(SortError::Cycles(cycles)) => {
let msg: Vec<String> = cycles
@ -105,8 +109,7 @@ pub fn xmle(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
};
output_xmle(
&depgraph,
&mut sorted,
sorted,
name.expect("missing root package name"),
relroot.expect("missing root package relroot"),
output,
@ -200,46 +203,16 @@ fn load_xmlo<'a, P: AsRef<Path>>(
Ok(state)
}
fn get_ident<'a>(
depgraph: &'a LinkerAsg,
name: SymbolId<global::ProgSymSize>,
) -> Result<&'a IdentObject, String> {
depgraph
.lookup(name)
.and_then(|id| depgraph.get(id))
.ok_or(format!("missing identifier: {}", name.lookup_str()))
}
fn output_xmle<'a>(
depgraph: &'a LinkerAsg,
sorted: &mut Sections<'a>,
fn output_xmle<'a, S: XmleSections<'a>>(
sorted: S,
name: SymbolId,
relroot: SymbolId,
output: &str,
) -> Result<(), Box<dyn Error>> {
if !sorted.map.is_empty() {
sorted
.map
.set_head(get_ident(depgraph, ":map:___head".intern())?);
sorted
.map
.set_tail(get_ident(depgraph, ":map:___tail".intern())?);
}
if !sorted.retmap.is_empty() {
sorted
.retmap
.set_head(get_ident(depgraph, ":retmap:___head".intern())?);
sorted
.retmap
.set_tail(get_ident(depgraph, ":retmap:___tail".intern())?);
}
let file = fs::File::create(output)?;
let mut buf = BufWriter::new(file);
lower_iter(&sorted, name, relroot).write(&mut buf, Default::default())?;
lower_iter(sorted, name, relroot).write(&mut buf, Default::default())?;
buf.flush()?;
Ok(())

View File

@ -17,33 +17,37 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! Lowering of the [ASG](crate::ir::asg) into `xmle` [`Sections`].
//! Lowering of the [ASG](crate::ir::asg) into `xmle` [`XmleSections`].
//!
//! See the [parent module](super) for more information.
use super::{section::SectionsError, Sections};
use crate::ir::asg::{
Asg, BaseAsg, IdentKind, IdentObject, IdentObjectData, IndexType, ObjectRef,
use super::section::{SectionsError, XmleSections};
use crate::{
ir::asg::{
Asg, BaseAsg, IdentKind, IdentObject, IdentObjectData, IndexType,
ObjectRef,
},
sym::{st, GlobalSymbolResolve, SymbolId},
};
use petgraph::visit::DfsPostOrder;
// Result of [`sort`].
pub type SortResult<T, Ix> = Result<T, SortError<Ix>>;
/// Lower ASG into [`Sections`] by sorting relocatable text fragments.
/// Lower ASG into [`XmleSections`] by ordering relocatable text fragments.
///
/// This performs the equivalent of a topological sort,
/// although function cycles are permitted.
/// The actual operation performed is a post-order depth-first traversal.
pub fn sort<'a, Ix>(
pub fn sort<'a, Ix, S: XmleSections<'a>>(
asg: &'a BaseAsg<IdentObject, Ix>,
roots: &[ObjectRef<Ix>],
) -> SortResult<Sections<'a>, Ix>
mut dest: S,
) -> SortResult<S, Ix>
where
Ix: IndexType,
S: XmleSections<'a>,
{
let mut deps = Sections::new();
// TODO: we should check for cycles as we sort (as the POC did).
check_cycles(asg)?;
@ -54,12 +58,40 @@ where
dfs.stack.push((*index).into());
}
// These are always generated by the map compiler,
// but do not have edges that would allow them to be properly ordered
// (adding an edge to every map object would be wasteful).
dest.push(get_ident(asg, st::L_MAP_UUUHEAD))?;
dest.push(get_ident(asg, st::L_RETMAP_UUUHEAD))?;
while let Some(index) = dfs.next(&asg.graph) {
let ident = asg.get(index).expect("missing node");
deps.push(ident)?;
dest.push(ident)?;
}
Ok(deps)
dest.push(get_ident(asg, st::L_MAP_UUUTAIL))?;
dest.push(get_ident(asg, st::L_RETMAP_UUUTAIL))?;
Ok(dest)
}
fn get_ident<'a, Ix, S>(
depgraph: &'a BaseAsg<IdentObject, Ix>,
name: S,
) -> &'a IdentObject
where
Ix: IndexType,
S: Into<SymbolId>,
{
let sym = name.into();
depgraph
.lookup(sym)
.and_then(|id| depgraph.get(id))
.expect(&format!(
"missing internal identifier: {}",
sym.lookup_str()
))
}
/// Check graph for cycles
@ -123,7 +155,7 @@ where
/// sorting process.
#[derive(Debug, PartialEq)]
pub enum SortError<Ix: IndexType> {
/// Error while building [`Sections`].
/// Error while lowering into [`XmleSections`].
SectionsError(SectionsError),
/// The graph has a cyclic dependency.
@ -156,95 +188,148 @@ mod test {
use super::*;
use crate::{
ir::{
asg::{DataType, Dim, FragmentText, IdentObject, Source},
asg::{Dim, FragmentText, IdentObject, Source},
legacyir::SymDtype,
},
ld::xmle::{section::PushResult, Sections},
sym::GlobalSymbolIntern,
};
type Sut = BaseAsg<IdentObject, u16>;
type TestAsg = BaseAsg<IdentObject, u16>;
macro_rules! assert_section_sym {
( $iterable:expr, $s:ident ) => {{
let mut pos = 0;
/// Create a graph with the expected {ret,}map head/tail identifiers.
fn make_asg() -> TestAsg {
let mut asg = TestAsg::new();
for obj in $iterable.iter() {
let sym = obj.name().expect("missing object");
assert_eq!($s.get(pos).map(|s| *s), Some(sym));
let text = "dummy fragment".intern();
pos = pos + 1;
}
asg.declare(
st::L_MAP_UUUHEAD.into(),
IdentKind::MapHead,
Default::default(),
)
.and_then(|id| asg.set_fragment(id, text))
.unwrap();
assert_eq!(pos, $s.len());
};};
}
asg.declare(
st::L_MAP_UUUTAIL.into(),
IdentKind::MapTail,
Default::default(),
)
.and_then(|id| asg.set_fragment(id, text))
.unwrap();
macro_rules! add_syms {
($sut:ident, $base:expr, {$($dest:ident <- $name:ident: $kind:expr,)*}) => {
$(
let sym = stringify!($name).intern();
asg.declare(
st::L_RETMAP_UUUHEAD.into(),
IdentKind::RetMapHead,
Default::default(),
)
.and_then(|id| asg.set_fragment(id, text))
.unwrap();
$sut.declare(sym, $kind, Source::default()).unwrap();
let (_, _) = $sut.add_dep_lookup($base, sym);
asg.declare(
st::L_RETMAP_UUUTAIL.into(),
IdentKind::RetMapTail,
Default::default(),
)
.and_then(|id| asg.set_fragment(id, text))
.unwrap();
$dest.push(sym);
)*
};
asg
}
#[test]
fn graph_sort() -> SortResult<(), u16> {
let mut sut = Sut::new();
// We care only about the order of pushes, not the sections they end
// up in.
struct StubSections<'a> {
pushed: Vec<&'a IdentObject>,
}
let mut st = vec![];
let mut map = vec![];
let mut retmap = vec![];
impl<'a> XmleSections<'a> for StubSections<'a> {
fn push(&mut self, ident: &'a IdentObject) -> PushResult {
self.pushed.push(ident);
Ok(())
}
let base = "sym1".intern();
let base_node = sut
.declare(base, IdentKind::Map, Source::default())
fn take_deps(&mut self) -> Vec<&'a IdentObject> {
unimplemented!()
}
fn take_static(&mut self) -> Vec<SymbolId> {
unimplemented!()
}
fn take_map(&mut self) -> Vec<SymbolId> {
unimplemented!()
}
fn take_map_froms(&mut self) -> fxhash::FxHashSet<SymbolId> {
unimplemented!()
}
fn take_retmap(&mut self) -> Vec<SymbolId> {
unimplemented!()
}
fn take_exec(&mut self) -> Vec<SymbolId> {
unimplemented!()
}
}
let mut asg = make_asg();
// Add them in an unsorted order.
let adep = asg
.declare("adep".into(), IdentKind::Meta, Default::default())
.unwrap();
let a = asg
.declare("a".into(), IdentKind::Meta, Default::default())
.unwrap();
let adepdep = asg
.declare("adepdep".into(), IdentKind::Meta, Default::default())
.unwrap();
add_syms!(sut, base, {
st <- meta1: IdentKind::Meta,
st <- work1: IdentKind::Worksheet,
st <- const1: IdentKind::Const(Dim::from_u8(0), DataType::Float),
map <- map1: IdentKind::MapHead,
map <- map2: IdentKind::Map,
map <- map3: IdentKind::MapTail,
retmap <- retmap1: IdentKind::RetMapHead,
retmap <- retmap2: IdentKind::RetMap,
retmap <- retmap3: IdentKind::RetMapTail,
retmap <- retmap4: IdentKind::RetMapTail,
retmap <- retmap5: IdentKind::RetMap,
map <- map4: IdentKind::MapHead,
map <- map5: IdentKind::Map,
st <- meta2: IdentKind::Meta,
st <- work2: IdentKind::Worksheet,
map <- map6: IdentKind::MapTail,
retmap <- retmap6: IdentKind::RetMapHead,
st <- const2: IdentKind::Const(Dim::from_u8(2), DataType::Float),
});
asg.add_dep(a, adep);
asg.add_dep(adep, adepdep);
map.push(base);
let sections =
sort(&asg, &vec![a], StubSections { pushed: Vec::new() })?;
let sections = sort(&sut, &vec![base_node])?;
assert_section_sym!(sections.st, st);
assert_section_sym!(sections.map, map);
assert_section_sym!(sections.retmap, retmap);
assert_eq!(
sections.pushed,
vec![
// Static head
asg.lookup(st::L_MAP_UUUHEAD.into())
.and_then(|id| asg.get(id)),
asg.lookup(st::L_RETMAP_UUUHEAD.into())
.and_then(|id| asg.get(id)),
// Post-order
asg.get(adepdep),
asg.get(adep),
asg.get(a),
// Static tail
asg.lookup(st::L_MAP_UUUTAIL.into())
.and_then(|id| asg.get(id)),
asg.lookup(st::L_RETMAP_UUUTAIL.into())
.and_then(|id| asg.get(id)),
]
.into_iter()
.collect::<Option<Vec<_>>>()
.unwrap()
);
Ok(())
}
#[test]
fn graph_sort_missing_node() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
let sym_node = asg
.declare(
sym,
IdentKind::Tpl,
@ -255,12 +340,12 @@ mod test {
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
asg.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = asg.add_dep_lookup(sym, dep);
match sort(&sut, &vec![sym_node]) {
match sort(&asg, &vec![sym_node], Sections::new()) {
Ok(_) => panic!("Unexpected success - dependency is not in graph"),
Err(SortError::SectionsError(SectionsError::UnresolvedObject(
_,
@ -274,29 +359,33 @@ mod test {
}
#[test]
fn graph_sort_no_roots() -> SortResult<(), u16> {
let mut sut = Sut::new();
fn graph_sort_no_roots_same_as_empty_graph() -> SortResult<(), u16> {
let mut asg_nonempty_no_roots = make_asg();
// "empty" (it has the head/tail {ret,}map objects)
let asg_empty = make_asg();
let sym = "sym".intern();
let dep = "dep".intern();
let (_, _) = sut.add_dep_lookup(sym, dep);
asg_nonempty_no_roots.add_dep_lookup(sym, dep);
let sections = sort(&sut, &vec![])?;
assert_eq!(Sections::new(), sections);
assert_eq!(
sort(&asg_nonempty_no_roots, &vec![], Sections::new()),
sort(&asg_empty, &vec![], Sections::new())
);
Ok(())
}
#[test]
fn graph_sort_simple_cycle() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
let sym_node = asg
.declare(
sym,
IdentKind::Tpl,
@ -307,7 +396,7 @@ mod test {
)
.unwrap();
let dep_node = sut
let dep_node = asg
.declare(
dep,
IdentKind::Tpl,
@ -318,15 +407,15 @@ mod test {
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
asg.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
asg.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(dep, sym);
let (_, _) = asg.add_dep_lookup(sym, dep);
let (_, _) = asg.add_dep_lookup(dep, sym);
let result = sort(&sut, &vec![sym_node]);
let result = sort(&asg, &vec![sym_node], Sections::new());
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![dep_node.into(), sym_node.into()]];
@ -341,14 +430,14 @@ mod test {
#[test]
fn graph_sort_two_simple_cycles() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym = "sym".intern();
let sym2 = "sym2".intern();
let dep = "dep".intern();
let dep2 = "dep2".intern();
let sym_node = sut
let sym_node = asg
.declare(
sym,
IdentKind::Tpl,
@ -359,7 +448,7 @@ mod test {
)
.unwrap();
let sym2_node = sut
let sym2_node = asg
.declare(
sym2,
IdentKind::Tpl,
@ -370,7 +459,7 @@ mod test {
)
.unwrap();
let dep_node = sut
let dep_node = asg
.declare(
dep,
IdentKind::Tpl,
@ -381,7 +470,7 @@ mod test {
)
.unwrap();
let dep2_node = sut
let dep2_node = asg
.declare(
dep2,
IdentKind::Tpl,
@ -392,21 +481,21 @@ mod test {
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
asg.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
asg.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("baz"))
asg.set_fragment(dep_node, FragmentText::from("baz"))
.unwrap();
sut.set_fragment(dep2_node, FragmentText::from("huh"))
asg.set_fragment(dep2_node, FragmentText::from("huh"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(dep, sym);
let (_, _) = sut.add_dep_lookup(sym2, dep2);
let (_, _) = sut.add_dep_lookup(dep2, sym2);
let (_, _) = asg.add_dep_lookup(sym, dep);
let (_, _) = asg.add_dep_lookup(dep, sym);
let (_, _) = asg.add_dep_lookup(sym2, dep2);
let (_, _) = asg.add_dep_lookup(dep2, sym2);
let result = sort(&sut, &vec![sym_node]);
let result = sort(&asg, &vec![sym_node], Sections::new());
let expected: Vec<Vec<ObjectRef<u16>>> = vec![
vec![dep_node.into(), sym_node.into()],
@ -423,12 +512,12 @@ mod test {
#[test]
fn graph_sort_no_cycle_with_edge_to_same_node() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
let sym_node = asg
.declare(
sym,
IdentKind::Tpl,
@ -439,7 +528,7 @@ mod test {
)
.unwrap();
let dep_node = sut
let dep_node = asg
.declare(
dep,
IdentKind::Tpl,
@ -450,15 +539,15 @@ mod test {
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
asg.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
asg.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = asg.add_dep_lookup(sym, dep);
let (_, _) = asg.add_dep_lookup(sym, dep);
let result = sort(&sut, &vec![sym_node]);
let result = sort(&asg, &vec![sym_node], Sections::new());
match result {
Ok(_) => (),
@ -470,13 +559,13 @@ mod test {
#[test]
fn graph_sort_cycle_with_a_few_steps() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
let sym1_node = asg
.declare(
sym1,
IdentKind::Tpl,
@ -487,7 +576,7 @@ mod test {
)
.unwrap();
let sym2_node = sut
let sym2_node = asg
.declare(
sym2,
IdentKind::Tpl,
@ -498,7 +587,7 @@ mod test {
)
.unwrap();
let sym3_node = sut
let sym3_node = asg
.declare(
sym3,
IdentKind::Tpl,
@ -509,18 +598,18 @@ mod test {
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
asg.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
asg.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
asg.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let (_, _) = asg.add_dep_lookup(sym1, sym2);
let (_, _) = asg.add_dep_lookup(sym2, sym3);
let (_, _) = asg.add_dep_lookup(sym3, sym1);
let result = sort(&sut, &vec![sym1_node]);
let result = sort(&asg, &vec![sym1_node], Sections::new());
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
@ -536,13 +625,13 @@ mod test {
#[test]
fn graph_sort_cyclic_function_with_non_function_with_a_few_steps(
) -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
let sym1_node = asg
.declare(
sym1,
IdentKind::Tpl,
@ -553,7 +642,7 @@ mod test {
)
.unwrap();
let sym2_node = sut
let sym2_node = asg
.declare(
sym2,
IdentKind::Tpl,
@ -564,7 +653,7 @@ mod test {
)
.unwrap();
let sym3_node = sut
let sym3_node = asg
.declare(
sym3,
IdentKind::Func(Dim::default(), SymDtype::Empty),
@ -575,18 +664,18 @@ mod test {
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
asg.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
asg.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
asg.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let (_, _) = asg.add_dep_lookup(sym1, sym2);
let (_, _) = asg.add_dep_lookup(sym2, sym3);
let (_, _) = asg.add_dep_lookup(sym3, sym1);
let result = sort(&sut, &vec![sym1_node]);
let result = sort(&asg, &vec![sym1_node], Sections::new());
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
@ -601,13 +690,13 @@ mod test {
#[test]
fn graph_sort_cyclic_bookended_by_functions() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
let sym1_node = asg
.declare(
sym1,
IdentKind::Func(Dim::default(), SymDtype::Empty),
@ -618,7 +707,7 @@ mod test {
)
.unwrap();
let sym2_node = sut
let sym2_node = asg
.declare(
sym2,
IdentKind::Tpl,
@ -629,7 +718,7 @@ mod test {
)
.unwrap();
let sym3_node = sut
let sym3_node = asg
.declare(
sym3,
IdentKind::Func(Dim::default(), SymDtype::Empty),
@ -640,18 +729,18 @@ mod test {
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
asg.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
asg.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
asg.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let (_, _) = asg.add_dep_lookup(sym1, sym2);
let (_, _) = asg.add_dep_lookup(sym2, sym3);
let (_, _) = asg.add_dep_lookup(sym3, sym1);
let result = sort(&sut, &vec![sym1_node]);
let result = sort(&asg, &vec![sym1_node], Sections::new());
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
@ -666,12 +755,12 @@ mod test {
#[test]
fn graph_sort_cyclic_function_ignored() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
let sym_node = asg
.declare(
sym,
IdentKind::Func(Dim::default(), SymDtype::Empty),
@ -682,7 +771,7 @@ mod test {
)
.unwrap();
let dep_node = sut
let dep_node = asg
.declare(
dep,
IdentKind::Func(Dim::default(), SymDtype::Empty),
@ -693,15 +782,15 @@ mod test {
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
asg.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
asg.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(dep, sym);
let (_, _) = asg.add_dep_lookup(sym, dep);
let (_, _) = asg.add_dep_lookup(dep, sym);
let result = sort(&sut, &vec![sym_node]);
let result = sort(&asg, &vec![sym_node], Sections::new());
match result {
Ok(_) => (),
@ -713,13 +802,13 @@ mod test {
#[test]
fn graph_sort_cyclic_function_is_bookended() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
let sym1_node = asg
.declare(
sym1,
IdentKind::Tpl,
@ -730,7 +819,7 @@ mod test {
)
.unwrap();
let sym2_node = sut
let sym2_node = asg
.declare(
sym2,
IdentKind::Func(Dim::default(), SymDtype::Empty),
@ -741,7 +830,7 @@ mod test {
)
.unwrap();
let sym3_node = sut
let sym3_node = asg
.declare(
sym3,
IdentKind::Tpl,
@ -752,18 +841,18 @@ mod test {
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
asg.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
asg.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
asg.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let (_, _) = asg.add_dep_lookup(sym1, sym2);
let (_, _) = asg.add_dep_lookup(sym2, sym3);
let (_, _) = asg.add_dep_lookup(sym3, sym1);
let result = sort(&sut, &vec![sym1_node]);
let result = sort(&asg, &vec![sym1_node], Sections::new());
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
@ -778,13 +867,13 @@ mod test {
#[test]
fn graph_sort_ignore_non_linked() -> SortResult<(), u16> {
let mut sut = Sut::new();
let mut asg = make_asg();
let sym = "sym".intern();
let dep = "dep".intern();
let ignored = "ignored".intern();
let sym_node = sut
let sym_node = asg
.declare(
sym,
IdentKind::Tpl,
@ -795,7 +884,7 @@ mod test {
)
.unwrap();
let dep_node = sut
let dep_node = asg
.declare(
dep,
IdentKind::Tpl,
@ -806,7 +895,7 @@ mod test {
)
.unwrap();
let ignored_node = sut
let ignored_node = asg
.declare(
ignored,
IdentKind::Tpl,
@ -817,17 +906,17 @@ mod test {
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
asg.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
asg.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(ignored_node, FragmentText::from("baz"))
asg.set_fragment(ignored_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(ignored, sym);
let (_, _) = asg.add_dep_lookup(sym, dep);
let (_, _) = asg.add_dep_lookup(ignored, sym);
let result = sort(&sut, &vec![sym_node]);
let result = sort(&asg, &vec![sym_node], Sections::new());
match result {
Ok(_) => (),

View File

@ -65,7 +65,7 @@
//! <l:static>
//! function func_min(args,min1,min2) {return min1;}
//! </l:static>
//! <l:exec>C['CMP_OP_EQ'] = 1;</l:exec>
//! <l:exec>A['foo']=E(A['bar']);</l:exec>
//! </package>
//! ```
@ -74,4 +74,4 @@ mod section;
pub mod lower;
pub mod xir;
pub use section::{Section, SectionIter, Sections, SectionsIter};
pub use section::{Sections, XmleSections};

View File

@ -19,7 +19,10 @@
//! Sections of a linked [`xmle`](super) object file.
//!
//! These sections are the result of [`sort`](super::lower::sort),
//! An [`XmleSections`] object is responsible for placing provided
//! identifiers into the appropriate section,
//! but _it must be provided properly ordered data_.
//! This ordering is the result of [`sort`](super::lower::sort),
//! which places the relocatable object code fragments in the order
//! necessary for execution.
@ -28,110 +31,40 @@ use crate::ir::asg::{
};
use crate::sym::{GlobalSymbolResolve, SymbolId};
use fxhash::FxHashSet;
use std::collections::hash_set;
use std::iter::Chain;
use std::option;
use std::mem::take;
use std::result::Result;
use std::slice::Iter;
/// A section of an [object file](crate::obj).
///
/// Most sections will only need a `body`, but some innlude `head` and `tail`
/// information. Rather than dealing with those differently, each `Section`
/// will have a `head` and `tail` that are empty by default.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Section<'a> {
head: Option<&'a IdentObject>,
body: Vec<&'a IdentObject>,
tail: Option<&'a IdentObject>,
pub type PushResult<T = ()> = Result<T, SectionsError>;
pub trait XmleSections<'a> {
fn push(&mut self, ident: &'a IdentObject) -> PushResult;
fn take_deps(&mut self) -> Vec<&'a IdentObject>;
fn take_static(&mut self) -> Vec<SymbolId>;
fn take_map(&mut self) -> Vec<SymbolId>;
fn take_map_froms(&mut self) -> FxHashSet<SymbolId>;
fn take_retmap(&mut self) -> Vec<SymbolId>;
fn take_exec(&mut self) -> Vec<SymbolId>;
}
impl<'a> Section<'a> {
/// New empty section.
pub fn new() -> Self {
Self {
head: None,
body: Vec::new(),
tail: None,
}
}
/// Check if the `Section` is empty
#[inline]
pub fn is_empty(&self) -> bool {
self.body.len() == 0
}
/// Push an `IdentObject` into a `Section`'s head
#[inline]
pub fn set_head(&mut self, obj: &'a IdentObject) {
self.head.replace(obj);
}
/// Push an `IdentObject` into a `Section`'s body
#[inline]
pub fn push_body(&mut self, obj: &'a IdentObject) {
self.body.push(obj)
}
/// Push an `IdentObject` into a `Section`'s tail
#[inline]
pub fn set_tail(&mut self, obj: &'a IdentObject) {
self.tail.replace(obj);
}
/// Construct a new iterator visiting each head, body, and tail object
/// in order.
#[inline]
pub fn iter(&self) -> SectionIter {
SectionIter(
self.head
.iter()
.chain(self.body.iter())
.chain(self.tail.iter()),
)
}
}
/// Iterator over the head, body, and tail of a [`Section`].
/// Sections of a linked `xmle` file.
///
/// This iterator should be created with [`Section::iter`].
///
/// This hides the complex iterator type from callers.
pub struct SectionIter<'a>(
Chain<
Chain<option::Iter<'a, &'a IdentObject>, Iter<'a, &'a IdentObject>>,
option::Iter<'a, &'a IdentObject>,
>,
);
impl<'a> Iterator for SectionIter<'a> {
type Item = &'a IdentObject;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|x| *x)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, high) = self.0.size_hint();
(low, high.map(|x| x + 2))
}
}
pub type PushResult = Result<(), SectionsError>;
/// ASG objects organized into logical sections.
///
/// These sections may not necessarily correspond directly to sections of an
/// [object file](crate::obj).
// TODO: Remove pub
/// For more information on these sections,
/// see the [parent module](super).
#[derive(Debug, Default, PartialEq)]
pub struct Sections<'a> {
pub map: Section<'a>,
pub retmap: Section<'a>,
pub st: Section<'a>,
pub rater: Section<'a>,
deps: Vec<&'a IdentObject>,
map_froms: FxHashSet<SymbolId>,
map: Vec<SymbolId>,
retmap: Vec<SymbolId>,
st: Vec<SymbolId>,
exec: Vec<SymbolId>,
}
impl<'a> Sections<'a> {
@ -139,34 +72,58 @@ impl<'a> Sections<'a> {
#[inline]
pub fn new() -> Self {
Self {
map: Section::new(),
retmap: Section::new(),
st: Section::new(),
rater: Section::new(),
..Default::default()
}
}
}
impl<'a> XmleSections<'a> for Sections<'a> {
/// Push an object into the appropriate section.
///
/// Objects are expected to be properly sorted relative to their order
/// of execution so that their text fragments are placed in the
/// correct order in the final program text.
pub fn push(&mut self, ident: &'a IdentObject) -> PushResult {
fn push(&mut self, ident: &'a IdentObject) -> PushResult {
self.deps.push(ident);
// TODO: This cannot happen, so use an API without Option.
let name = ident.name().expect("missing identifier name");
let frag = ident.fragment().map(|sym| *sym);
match ident.resolved()?.kind() {
Some(kind) => match kind {
IdentKind::Cgen(_)
| IdentKind::Gen(_, _)
| IdentKind::Lparam(_, _) => {
// These types do not have fragments.
}
IdentKind::Meta
| IdentKind::Worksheet
| IdentKind::Param(_, _)
| IdentKind::Type(_)
| IdentKind::Func(_, _)
| IdentKind::Const(_, _) => self.st.push_body(ident),
| IdentKind::Const(_, _) => {
self.st.push(expect_frag(name, frag)?)
}
IdentKind::MapHead | IdentKind::Map | IdentKind::MapTail => {
self.map.push_body(ident)
self.map.push(expect_frag(name, frag)?);
if let Some(from) =
ident.src().expect("missing map src").from
{
self.map_froms.insert(from);
}
}
IdentKind::RetMapHead
| IdentKind::RetMap
| IdentKind::RetMapTail => self.retmap.push_body(ident),
_ => self.rater.push_body(ident),
| IdentKind::RetMapTail => {
self.retmap.push(expect_frag(name, frag)?)
}
// TODO: Why do templates have fragments?
IdentKind::Class(_) | IdentKind::Rate(_) | IdentKind::Tpl => {
self.exec.push(expect_frag(name, frag)?)
}
},
None => {
// TODO: This should not be possible; ensure that with types
@ -187,75 +144,44 @@ impl<'a> Sections<'a> {
Ok(())
}
/// Construct an iterator over each of the individual sections in
/// arbitrary order.
///
/// Each individual section is ordered as stated in [`Section::iter`],
/// but you should not rely on the order that the sections themselves
/// appear in;
/// they may change or be combined in the future.
/// At the time of writing,
/// they are chained in the same order in which they are defined
/// on the [`Sections`] struct.
#[inline]
pub fn iter_all(&self) -> SectionsIter {
SectionsIter(SectionsIterType::All(
self.map
.iter()
.chain(self.retmap.iter())
.chain(self.st.iter())
.chain(self.rater.iter()),
))
fn take_deps(&mut self) -> Vec<&'a IdentObject> {
take(&mut self.deps)
}
/// Construct an iterator over the static sections in arbitrary order.
///
/// These sections contain fragments that do not depend on any external
/// inputs and can therefore be executed a single time when the
/// program is loaded into memory.
///
/// Each individual section is ordered as stated in [`Section::iter`],
/// but you should not rely on the order that the sections themselves
/// appear in;
/// they may change or be combined in the future.
#[inline]
pub fn iter_static(&self) -> SectionsIter {
SectionsIter(SectionsIterType::Single(self.st.iter()))
fn take_static(&mut self) -> Vec<SymbolId> {
take(&mut self.st)
}
/// Construct an iterator over the map section.
#[inline]
pub fn iter_map(&self) -> SectionsIter {
SectionsIter(SectionsIterType::Single(self.map.iter()))
fn take_map(&mut self) -> Vec<SymbolId> {
take(&mut self.map)
}
/// Iterate over each unique map `from` entry.
///
/// Multiple mappings may reference the same source field,
/// which would produce duplicate values if they are not filtered.
#[inline]
pub fn iter_map_froms_uniq(&self) -> hash_set::IntoIter<SymbolId> {
self.iter_map()
.filter_map(|ident| {
ident.src().expect("internal error: missing map src").from
})
.collect::<FxHashSet<SymbolId>>()
.into_iter()
fn take_map_froms(&mut self) -> FxHashSet<SymbolId> {
take(&mut <