Update rustfmt to 0.9.0.

This commit is contained in:
Dan Gohman
2017-08-31 10:44:59 -07:00
parent 46fb64cbb4
commit 2efdc0ed37
111 changed files with 4692 additions and 3379 deletions

View File

@@ -15,7 +15,7 @@
# With the --install option, also tries to install the right version. # With the --install option, also tries to install the right version.
# This version should always be bumped to the newest version available. # This version should always be bumped to the newest version available.
VERS="0.8.4" VERS="0.9.0"
if cargo install --list | grep -q "^rustfmt v$VERS"; then if cargo install --list | grep -q "^rustfmt v$VERS"; then
exit 0 exit 0

View File

@@ -21,10 +21,12 @@ pub fn run(files: Vec<String>) -> CommandResult {
} }
fn cat_one(filename: String) -> CommandResult { fn cat_one(filename: String) -> CommandResult {
let buffer = read_to_string(&filename) let buffer = read_to_string(&filename).map_err(
.map_err(|e| format!("{}: {}", filename, e))?; |e| format!("{}: {}", filename, e),
let items = parse_functions(&buffer) )?;
.map_err(|e| format!("{}: {}", filename, e))?; let items = parse_functions(&buffer).map_err(
|e| format!("{}: {}", filename, e),
)?;
for (idx, func) in items.into_iter().enumerate() { for (idx, func) in items.into_iter().enumerate() {
if idx != 0 { if idx != 0 {

View File

@@ -80,10 +80,12 @@ fn cton_util() -> CommandResult {
} else if args.cmd_print_cfg { } else if args.cmd_print_cfg {
print_cfg::run(args.arg_file) print_cfg::run(args.arg_file)
} else if args.cmd_wasm { } else if args.cmd_wasm {
wasm::run(args.arg_file, wasm::run(
args.arg_file,
args.flag_verbose, args.flag_verbose,
args.flag_optimize, args.flag_optimize,
args.flag_check) args.flag_check,
)
} else { } else {
// Debugging / shouldn't happen with proper command line handling above. // Debugging / shouldn't happen with proper command line handling above.
Err(format!("Unhandled args: {:?}", args)) Err(format!("Unhandled args: {:?}", args))

View File

@@ -108,9 +108,12 @@ impl SubTest for TestBinEmit {
for ebb in func.layout.ebbs() { for ebb in func.layout.ebbs() {
for inst in func.layout.ebb_insts(ebb) { for inst in func.layout.ebb_insts(ebb) {
if !func.encodings[inst].is_legal() { if !func.encodings[inst].is_legal() {
if let Ok(enc) = isa.encode(&func.dfg, if let Ok(enc) = isa.encode(
&func.dfg,
&func.dfg[inst], &func.dfg[inst],
func.dfg.ctrl_typevar(inst)) { func.dfg.ctrl_typevar(inst),
)
{
func.encodings[inst] = enc; func.encodings[inst] = enc;
} }
} }
@@ -118,8 +121,9 @@ impl SubTest for TestBinEmit {
} }
// Relax branches and compute EBB offsets based on the encodings. // Relax branches and compute EBB offsets based on the encodings.
let code_size = binemit::relax_branches(&mut func, isa) let code_size = binemit::relax_branches(&mut func, isa).map_err(|e| {
.map_err(|e| pretty_error(&func, context.isa, e))?; pretty_error(&func, context.isa, e)
})?;
// Collect all of the 'bin:' directives on instructions. // Collect all of the 'bin:' directives on instructions.
let mut bins = HashMap::new(); let mut bins = HashMap::new();
@@ -128,16 +132,20 @@ impl SubTest for TestBinEmit {
match comment.entity { match comment.entity {
AnyEntity::Inst(inst) => { AnyEntity::Inst(inst) => {
if let Some(prev) = bins.insert(inst, want) { if let Some(prev) = bins.insert(inst, want) {
return Err(format!("multiple 'bin:' directives on {}: '{}' and '{}'", return Err(format!(
"multiple 'bin:' directives on {}: '{}' and '{}'",
func.dfg.display_inst(inst, isa), func.dfg.display_inst(inst, isa),
prev, prev,
want)); want
));
} }
} }
_ => { _ => {
return Err(format!("'bin:' directive on non-inst {}: {}", return Err(format!(
"'bin:' directive on non-inst {}: {}",
comment.entity, comment.entity,
comment.text)) comment.text
))
} }
} }
} }
@@ -152,10 +160,12 @@ impl SubTest for TestBinEmit {
for ebb in func.layout.ebbs() { for ebb in func.layout.ebbs() {
divert.clear(); divert.clear();
// Correct header offsets should have been computed by `relax_branches()`. // Correct header offsets should have been computed by `relax_branches()`.
assert_eq!(sink.offset, assert_eq!(
sink.offset,
func.offsets[ebb], func.offsets[ebb],
"Inconsistent {} header offset", "Inconsistent {} header offset",
ebb); ebb
);
for inst in func.layout.ebb_insts(ebb) { for inst in func.layout.ebb_insts(ebb) {
sink.text.clear(); sink.text.clear();
let enc = func.encodings[inst]; let enc = func.encodings[inst];
@@ -166,34 +176,44 @@ impl SubTest for TestBinEmit {
isa.emit_inst(&func, inst, &mut divert, &mut sink); isa.emit_inst(&func, inst, &mut divert, &mut sink);
let emitted = sink.offset - before; let emitted = sink.offset - before;
// Verify the encoding recipe sizes against the ISAs emit_inst implementation. // Verify the encoding recipe sizes against the ISAs emit_inst implementation.
assert_eq!(emitted, assert_eq!(
emitted,
encinfo.bytes(enc), encinfo.bytes(enc),
"Inconsistent size for [{}] {}", "Inconsistent size for [{}] {}",
encinfo.display(enc), encinfo.display(enc),
func.dfg.display_inst(inst, isa)); func.dfg.display_inst(inst, isa)
);
} }
// Check against bin: directives. // Check against bin: directives.
if let Some(want) = bins.remove(&inst) { if let Some(want) = bins.remove(&inst) {
if !enc.is_legal() { if !enc.is_legal() {
return Err(format!("{} can't be encoded: {}", return Err(format!(
"{} can't be encoded: {}",
inst, inst,
func.dfg.display_inst(inst, isa))); func.dfg.display_inst(inst, isa)
));
} }
let have = sink.text.trim(); let have = sink.text.trim();
if have != want { if have != want {
return Err(format!("Bad machine code for {}: {}\nWant: {}\nGot: {}", return Err(format!(
"Bad machine code for {}: {}\nWant: {}\nGot: {}",
inst, inst,
func.dfg.display_inst(inst, isa), func.dfg.display_inst(inst, isa),
want, want,
have)); have
));
} }
} }
} }
} }
if sink.offset != code_size { if sink.offset != code_size {
return Err(format!("Expected code size {}, got {}", code_size, sink.offset)); return Err(format!(
"Expected code size {}, got {}",
code_size,
sink.offset
));
} }
Ok(()) Ok(())

View File

@@ -41,22 +41,30 @@ impl SubTest for TestCompile {
let mut comp_ctx = cretonne::Context::new(); let mut comp_ctx = cretonne::Context::new();
comp_ctx.func = func.into_owned(); comp_ctx.func = func.into_owned();
let code_size = comp_ctx let code_size = comp_ctx.compile(isa).map_err(|e| {
.compile(isa) pretty_error(&comp_ctx.func, context.isa, e)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; })?;
dbg!("Generated {} bytes of code:\n{}", dbg!(
"Generated {} bytes of code:\n{}",
code_size, code_size,
comp_ctx.func.display(isa)); comp_ctx.func.display(isa)
);
// Finally verify that the returned code size matches the emitted bytes. // Finally verify that the returned code size matches the emitted bytes.
let mut sink = SizeSink { offset: 0 }; let mut sink = SizeSink { offset: 0 };
binemit::emit_function(&comp_ctx.func, binemit::emit_function(
&comp_ctx.func,
|func, inst, div, sink| isa.emit_inst(func, inst, div, sink), |func, inst, div, sink| isa.emit_inst(func, inst, div, sink),
&mut sink); &mut sink,
);
if sink.offset != code_size { if sink.offset != code_size {
return Err(format!("Expected code size {}, got {}", code_size, sink.offset)); return Err(format!(
"Expected code size {}, got {}",
code_size,
sink.offset
));
} }
Ok(()) Ok(())

View File

@@ -46,7 +46,9 @@ impl ConcurrentRunner {
heartbeat_thread(reply_tx.clone()); heartbeat_thread(reply_tx.clone());
let handles = (0..num_cpus::get()) let handles = (0..num_cpus::get())
.map(|num| worker_thread(num, request_mutex.clone(), reply_tx.clone())) .map(|num| {
worker_thread(num, request_mutex.clone(), reply_tx.clone())
})
.collect(); .collect();
ConcurrentRunner { ConcurrentRunner {
@@ -103,10 +105,11 @@ fn heartbeat_thread(replies: Sender<Reply>) -> thread::JoinHandle<()> {
} }
/// Spawn a worker thread running tests. /// Spawn a worker thread running tests.
fn worker_thread(thread_num: usize, fn worker_thread(
thread_num: usize,
requests: Arc<Mutex<Receiver<Request>>>, requests: Arc<Mutex<Receiver<Request>>>,
replies: Sender<Reply>) replies: Sender<Reply>,
-> thread::JoinHandle<()> { ) -> thread::JoinHandle<()> {
thread::Builder::new() thread::Builder::new()
.name(format!("worker #{}", thread_num)) .name(format!("worker #{}", thread_num))
.spawn(move || { .spawn(move || {

View File

@@ -50,9 +50,11 @@ impl SubTest for TestDomtree {
let inst = match comment.entity { let inst = match comment.entity {
AnyEntity::Inst(inst) => inst, AnyEntity::Inst(inst) => inst,
_ => { _ => {
return Err(format!("annotation on non-inst {}: {}", return Err(format!(
"annotation on non-inst {}: {}",
comment.entity, comment.entity,
comment.text)) comment.text
))
} }
}; };
for src_ebb in tail.split_whitespace() { for src_ebb in tail.split_whitespace() {
@@ -69,17 +71,21 @@ impl SubTest for TestDomtree {
// Compare to computed domtree. // Compare to computed domtree.
match domtree.idom(ebb) { match domtree.idom(ebb) {
Some(got_inst) if got_inst != inst => { Some(got_inst) if got_inst != inst => {
return Err(format!("mismatching idoms for {}:\n\ return Err(format!(
"mismatching idoms for {}:\n\
want: {}, got: {}", want: {}, got: {}",
src_ebb, src_ebb,
inst, inst,
got_inst)); got_inst
));
} }
None => { None => {
return Err(format!("mismatching idoms for {}:\n\ return Err(format!(
"mismatching idoms for {}:\n\
want: {}, got: unreachable", want: {}, got: unreachable",
src_ebb, src_ebb,
inst)); inst
));
} }
_ => {} _ => {}
} }
@@ -89,15 +95,17 @@ impl SubTest for TestDomtree {
// Now we know that everything in `expected` is consistent with `domtree`. // Now we know that everything in `expected` is consistent with `domtree`.
// All other EBB's should be either unreachable or the entry block. // All other EBB's should be either unreachable or the entry block.
for ebb in func.layout for ebb in func.layout.ebbs().skip(1).filter(
.ebbs() |ebb| !expected.contains_key(&ebb),
.skip(1) )
.filter(|ebb| !expected.contains_key(&ebb)) { {
if let Some(got_inst) = domtree.idom(ebb) { if let Some(got_inst) = domtree.idom(ebb) {
return Err(format!("mismatching idoms for renumbered {}:\n\ return Err(format!(
"mismatching idoms for renumbered {}:\n\
want: unrechable, got: {}", want: unrechable, got: {}",
ebb, ebb,
got_inst)); got_inst
));
} }
} }

View File

@@ -41,9 +41,9 @@ impl SubTest for TestLegalizer {
let isa = context.isa.expect("legalizer needs an ISA"); let isa = context.isa.expect("legalizer needs an ISA");
comp_ctx.flowgraph(); comp_ctx.flowgraph();
comp_ctx comp_ctx.legalize(isa).map_err(|e| {
.legalize(isa) pretty_error(&comp_ctx.func, context.isa, e)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; })?;
let mut text = String::new(); let mut text = String::new();
write!(&mut text, "{}", &comp_ctx.func.display(Some(isa))) write!(&mut text, "{}", &comp_ctx.func.display(Some(isa)))

View File

@@ -39,13 +39,14 @@ impl SubTest for TestLICM {
comp_ctx.func = func.into_owned(); comp_ctx.func = func.into_owned();
comp_ctx.flowgraph(); comp_ctx.flowgraph();
comp_ctx comp_ctx.licm().map_err(|e| {
.licm() pretty_error(&comp_ctx.func, context.isa, e)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; })?;
let mut text = String::new(); let mut text = String::new();
write!(&mut text, "{}", &comp_ctx.func) write!(&mut text, "{}", &comp_ctx.func).map_err(
.map_err(|e| e.to_string())?; |e| e.to_string(),
)?;
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -46,12 +46,12 @@ impl SubTest for TestRegalloc {
comp_ctx.flowgraph(); comp_ctx.flowgraph();
// TODO: Should we have an option to skip legalization? // TODO: Should we have an option to skip legalization?
comp_ctx comp_ctx.legalize(isa).map_err(|e| {
.legalize(isa) pretty_error(&comp_ctx.func, context.isa, e)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; })?;
comp_ctx comp_ctx.regalloc(isa).map_err(|e| {
.regalloc(isa) pretty_error(&comp_ctx.func, context.isa, e)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; })?;
let mut text = String::new(); let mut text = String::new();
write!(&mut text, "{}", &comp_ctx.func.display(Some(isa))) write!(&mut text, "{}", &comp_ctx.func.display(Some(isa)))

View File

@@ -41,11 +41,13 @@ impl Display for QueueEntry {
let p = self.path.to_string_lossy(); let p = self.path.to_string_lossy();
match self.state { match self.state {
State::Done(Ok(dur)) => { State::Done(Ok(dur)) => {
write!(f, write!(
f,
"{}.{:03} {}", "{}.{:03} {}",
dur.as_secs(), dur.as_secs(),
dur.subsec_nanos() / 1000000, dur.subsec_nanos() / 1000000,
p) p
)
} }
State::Done(Err(ref e)) => write!(f, "FAIL {}: {}", p, e), State::Done(Err(ref e)) => write!(f, "FAIL {}: {}", p, e),
_ => write!(f, "{}", p), _ => write!(f, "{}", p),
@@ -104,8 +106,7 @@ impl TestRunner {
/// ///
/// Any problems reading `file` as a test case file will be reported as a test failure. /// Any problems reading `file` as a test case file will be reported as a test failure.
pub fn push_test<P: Into<PathBuf>>(&mut self, file: P) { pub fn push_test<P: Into<PathBuf>>(&mut self, file: P) {
self.tests self.tests.push(QueueEntry {
.push(QueueEntry {
path: file.into(), path: file.into(),
state: State::New, state: State::New,
}); });
@@ -240,10 +241,12 @@ impl TestRunner {
Reply::Tick => { Reply::Tick => {
self.ticks_since_progress += 1; self.ticks_since_progress += 1;
if self.ticks_since_progress == TIMEOUT_SLOW { if self.ticks_since_progress == TIMEOUT_SLOW {
println!("STALLED for {} seconds with {}/{} tests finished", println!(
"STALLED for {} seconds with {}/{} tests finished",
self.ticks_since_progress, self.ticks_since_progress,
self.reported_tests, self.reported_tests,
self.tests.len()); self.tests.len()
);
for jobid in self.reported_tests..self.tests.len() { for jobid in self.reported_tests..self.tests.len() {
if self.tests[jobid].state == State::Running { if self.tests[jobid].state == State::Running {
println!("slow: {}", self.tests[jobid]); println!("slow: {}", self.tests[jobid]);
@@ -251,8 +254,10 @@ impl TestRunner {
} }
} }
if self.ticks_since_progress >= TIMEOUT_PANIC { if self.ticks_since_progress >= TIMEOUT_PANIC {
panic!("worker threads stalled for {} seconds.", panic!(
self.ticks_since_progress); "worker threads stalled for {} seconds.",
self.ticks_since_progress
);
} }
} }
} }
@@ -304,12 +309,11 @@ impl TestRunner {
return; return;
} }
for t in self.tests for t in self.tests.iter().filter(|entry| match **entry {
.iter()
.filter(|entry| match **entry {
QueueEntry { state: State::Done(Ok(dur)), .. } => dur > cut, QueueEntry { state: State::Done(Ok(dur)), .. } => dur > cut,
_ => false, _ => false,
}) { })
{
println!("slow: {}", t) println!("slow: {}", t)
} }

View File

@@ -76,10 +76,11 @@ pub fn run(path: &Path) -> TestResult {
} }
// Given a slice of tests, generate a vector of (test, flags, isa) tuples. // Given a slice of tests, generate a vector of (test, flags, isa) tuples.
fn test_tuples<'a>(tests: &'a [Box<SubTest>], fn test_tuples<'a>(
tests: &'a [Box<SubTest>],
isa_spec: &'a IsaSpec, isa_spec: &'a IsaSpec,
no_isa_flags: &'a Flags) no_isa_flags: &'a Flags,
-> Result<Vec<(&'a SubTest, &'a Flags, Option<&'a TargetIsa>)>> { ) -> Result<Vec<(&'a SubTest, &'a Flags, Option<&'a TargetIsa>)>> {
let mut out = Vec::new(); let mut out = Vec::new();
for test in tests { for test in tests {
if test.needs_isa() { if test.needs_isa() {
@@ -104,10 +105,11 @@ fn test_tuples<'a>(tests: &'a [Box<SubTest>],
Ok(out) Ok(out)
} }
fn run_one_test<'a>(tuple: (&'a SubTest, &'a Flags, Option<&'a TargetIsa>), fn run_one_test<'a>(
tuple: (&'a SubTest, &'a Flags, Option<&'a TargetIsa>),
func: Cow<Function>, func: Cow<Function>,
context: &mut Context<'a>) context: &mut Context<'a>,
-> Result<()> { ) -> Result<()> {
let (test, flags, isa) = tuple; let (test, flags, isa) = tuple;
let name = format!("{}({})", test.name(), func.name); let name = format!("{}({})", test.name(), func.name);
dbg!("Test: {} {}", name, isa.map(TargetIsa::name).unwrap_or("-")); dbg!("Test: {} {}", name, isa.map(TargetIsa::name).unwrap_or("-"));
@@ -117,11 +119,13 @@ fn run_one_test<'a>(tuple: (&'a SubTest, &'a Flags, Option<&'a TargetIsa>),
// Should we run the verifier before this test? // Should we run the verifier before this test?
if !context.verified && test.needs_verifier() { if !context.verified && test.needs_verifier() {
verify_function(&func, isa) verify_function(&func, isa).map_err(|e| {
.map_err(|e| pretty_verifier_error(&func, isa, e))?; pretty_verifier_error(&func, isa, e)
})?;
context.verified = true; context.verified = true;
} }
test.run(func, context) test.run(func, context).map_err(
.map_err(|e| format!("{}: {}", name, e)) |e| format!("{}: {}", name, e),
)
} }

View File

@@ -39,13 +39,14 @@ impl SubTest for TestSimpleGVN {
comp_ctx.func = func.into_owned(); comp_ctx.func = func.into_owned();
comp_ctx.flowgraph(); comp_ctx.flowgraph();
comp_ctx comp_ctx.simple_gvn().map_err(|e| {
.simple_gvn() pretty_error(&comp_ctx.func, context.isa, e)
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?; })?;
let mut text = String::new(); let mut text = String::new();
write!(&mut text, "{}", &comp_ctx.func) write!(&mut text, "{}", &comp_ctx.func).map_err(
.map_err(|e| e.to_string())?; |e| e.to_string(),
)?;
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -66,25 +66,25 @@ pub trait SubTest {
/// match 'inst10'. /// match 'inst10'.
impl<'a> filecheck::VariableMap for Context<'a> { impl<'a> filecheck::VariableMap for Context<'a> {
fn lookup(&self, varname: &str) -> Option<FCValue> { fn lookup(&self, varname: &str) -> Option<FCValue> {
self.details self.details.map.lookup_str(varname).map(|e| {
.map FCValue::Regex(format!(r"\b{}\b", e).into())
.lookup_str(varname) })
.map(|e| FCValue::Regex(format!(r"\b{}\b", e).into()))
} }
} }
/// Run filecheck on `text`, using directives extracted from `context`. /// Run filecheck on `text`, using directives extracted from `context`.
pub fn run_filecheck(text: &str, context: &Context) -> Result<()> { pub fn run_filecheck(text: &str, context: &Context) -> Result<()> {
let checker = build_filechecker(context)?; let checker = build_filechecker(context)?;
if checker if checker.check(&text, context).map_err(
.check(&text, context) |e| format!("filecheck: {}", e),
.map_err(|e| format!("filecheck: {}", e))? { )?
{
Ok(()) Ok(())
} else { } else {
// Filecheck mismatch. Emit an explanation as output. // Filecheck mismatch. Emit an explanation as output.
let (_, explain) = checker let (_, explain) = checker.explain(&text, context).map_err(
.explain(&text, context) |e| format!("explain: {}", e),
.map_err(|e| format!("explain: {}", e))?; )?;
Err(format!("filecheck failed:\n{}{}", checker, explain)) Err(format!("filecheck failed:\n{}{}", checker, explain))
} }
} }
@@ -94,14 +94,14 @@ pub fn build_filechecker(context: &Context) -> Result<Checker> {
let mut builder = CheckerBuilder::new(); let mut builder = CheckerBuilder::new();
// Preamble comments apply to all functions. // Preamble comments apply to all functions.
for comment in context.preamble_comments { for comment in context.preamble_comments {
builder builder.directive(comment.text).map_err(|e| {
.directive(comment.text) format!("filecheck: {}", e)
.map_err(|e| format!("filecheck: {}", e))?; })?;
} }
for comment in &context.details.comments { for comment in &context.details.comments {
builder builder.directive(comment.text).map_err(|e| {
.directive(comment.text) format!("filecheck: {}", e)
.map_err(|e| format!("filecheck: {}", e))?; })?;
} }
let checker = builder.finish(); let checker = builder.finish();
if checker.is_empty() { if checker.is_empty() {

View File

@@ -65,9 +65,11 @@ impl SubTest for TestVerifier {
if want_loc == got.location { if want_loc == got.location {
Ok(()) Ok(())
} else { } else {
Err(format!("correct error reported on {}, but wanted {}", Err(format!(
"correct error reported on {}, but wanted {}",
got.location, got.location,
want_loc)) want_loc
))
} }
} }
Some(_) => Err(format!("mismatching error: {}", got)), Some(_) => Err(format!("mismatching error: {}", got)),

View File

@@ -91,10 +91,12 @@ impl<'a> Display for CFGPrinter<'a> {
} }
fn print_cfg(filename: String) -> CommandResult { fn print_cfg(filename: String) -> CommandResult {
let buffer = read_to_string(&filename) let buffer = read_to_string(&filename).map_err(
.map_err(|e| format!("{}: {}", filename, e))?; |e| format!("{}: {}", filename, e),
let items = parse_functions(&buffer) )?;
.map_err(|e| format!("{}: {}", filename, e))?; let items = parse_functions(&buffer).map_err(
|e| format!("{}: {}", filename, e),
)?;
for (idx, func) in items.into_iter().enumerate() { for (idx, func) in items.into_iter().enumerate() {
if idx != 0 { if idx != 0 {

View File

@@ -18,14 +18,14 @@ pub fn run(files: Vec<String>, verbose: bool) -> CommandResult {
} }
let mut buffer = String::new(); let mut buffer = String::new();
io::stdin() io::stdin().read_to_string(&mut buffer).map_err(|e| {
.read_to_string(&mut buffer) format!("stdin: {}", e)
.map_err(|e| format!("stdin: {}", e))?; })?;
if verbose { if verbose {
let (success, explain) = checker let (success, explain) = checker.explain(&buffer, NO_VARIABLES).map_err(
.explain(&buffer, NO_VARIABLES) |e| e.to_string(),
.map_err(|e| e.to_string())?; )?;
print!("{}", explain); print!("{}", explain);
if success { if success {
println!("OK"); println!("OK");
@@ -33,25 +33,27 @@ pub fn run(files: Vec<String>, verbose: bool) -> CommandResult {
} else { } else {
Err("Check failed".to_string()) Err("Check failed".to_string())
} }
} else if checker } else if checker.check(&buffer, NO_VARIABLES).map_err(
.check(&buffer, NO_VARIABLES) |e| e.to_string(),
.map_err(|e| e.to_string())? { )?
{
Ok(()) Ok(())
} else { } else {
let (_, explain) = checker let (_, explain) = checker.explain(&buffer, NO_VARIABLES).map_err(
.explain(&buffer, NO_VARIABLES) |e| e.to_string(),
.map_err(|e| e.to_string())?; )?;
print!("{}", explain); print!("{}", explain);
Err("Check failed".to_string()) Err("Check failed".to_string())
} }
} }
fn read_checkfile(filename: &str) -> Result<Checker, String> { fn read_checkfile(filename: &str) -> Result<Checker, String> {
let buffer = read_to_string(&filename) let buffer = read_to_string(&filename).map_err(
.map_err(|e| format!("{}: {}", filename, e))?; |e| format!("{}: {}", filename, e),
)?;
let mut builder = CheckerBuilder::new(); let mut builder = CheckerBuilder::new();
builder builder.text(&buffer).map_err(
.text(&buffer) |e| format!("{}: {}", filename, e),
.map_err(|e| format!("{}: {}", filename, e))?; )?;
Ok(builder.finish()) Ok(builder.finish())
} }

View File

@@ -24,8 +24,10 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
/// ///
/// Return the comment text following the directive. /// Return the comment text following the directive.
pub fn match_directive<'a>(comment: &'a str, directive: &str) -> Option<&'a str> { pub fn match_directive<'a>(comment: &'a str, directive: &str) -> Option<&'a str> {
assert!(directive.ends_with(':'), assert!(
"Directive must include trailing colon"); directive.ends_with(':'),
"Directive must include trailing colon"
);
let text = comment.trim_left_matches(';').trim_left(); let text = comment.trim_left_matches(';').trim_left();
if text.starts_with(directive) { if text.starts_with(directive) {
Some(text[directive.len()..].trim()) Some(text[directive.len()..].trim())
@@ -35,10 +37,11 @@ pub fn match_directive<'a>(comment: &'a str, directive: &str) -> Option<&'a str>
} }
/// Pretty-print a verifier error. /// Pretty-print a verifier error.
pub fn pretty_verifier_error(func: &ir::Function, pub fn pretty_verifier_error(
func: &ir::Function,
isa: Option<&TargetIsa>, isa: Option<&TargetIsa>,
err: verifier::Error) err: verifier::Error,
-> String { ) -> String {
let mut msg = err.to_string(); let mut msg = err.to_string();
match err.location { match err.location {
AnyEntity::Inst(inst) => { AnyEntity::Inst(inst) => {

View File

@@ -51,19 +51,22 @@ fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
} }
pub fn run(files: Vec<String>, pub fn run(
files: Vec<String>,
flag_verbose: bool, flag_verbose: bool,
flag_optimize: bool, flag_optimize: bool,
flag_check: bool) flag_check: bool,
-> Result<(), String> { ) -> Result<(), String> {
for filename in files.iter() { for filename in files.iter() {
let path = Path::new(&filename); let path = Path::new(&filename);
let name = String::from(path.as_os_str().to_string_lossy()); let name = String::from(path.as_os_str().to_string_lossy());
match handle_module(flag_verbose, match handle_module(
flag_verbose,
flag_optimize, flag_optimize,
flag_check, flag_check,
path.to_path_buf(), path.to_path_buf(),
name) { name,
) {
Ok(()) => {} Ok(()) => {}
Err(message) => return Err(message), Err(message) => return Err(message),
} }
@@ -71,12 +74,13 @@ pub fn run(files: Vec<String>,
Ok(()) Ok(())
} }
fn handle_module(flag_verbose: bool, fn handle_module(
flag_verbose: bool,
flag_optimize: bool, flag_optimize: bool,
flag_check: bool, flag_check: bool,
path: PathBuf, path: PathBuf,
name: String) name: String,
-> Result<(), String> { ) -> Result<(), String> {
let mut terminal = term::stdout().unwrap(); let mut terminal = term::stdout().unwrap();
terminal.fg(term::color::YELLOW).unwrap(); terminal.fg(term::color::YELLOW).unwrap();
vprint!(flag_verbose, "Handling: "); vprint!(flag_verbose, "Handling: ");
@@ -221,17 +225,20 @@ fn handle_module(flag_verbose: bool,
} }
/// Pretty-print a verifier error. /// Pretty-print a verifier error.
pub fn pretty_verifier_error(func: &ir::Function, pub fn pretty_verifier_error(
func: &ir::Function,
isa: Option<&TargetIsa>, isa: Option<&TargetIsa>,
err: verifier::Error) err: verifier::Error,
-> String { ) -> String {
let msg = err.to_string(); let msg = err.to_string();
let str1 = match err.location { let str1 = match err.location {
AnyEntity::Inst(inst) => { AnyEntity::Inst(inst) => {
format!("{}\n{}: {}\n\n", format!(
"{}\n{}: {}\n\n",
msg, msg,
inst, inst,
func.dfg.display_inst(inst, isa)) func.dfg.display_inst(inst, isa)
)
} }
_ => String::from(format!("{}\n", msg)), _ => String::from(format!("{}\n", msg)),
}; };

View File

@@ -26,7 +26,8 @@ fn test_reverse_postorder_traversal(function_source: &str, ebb_order: Vec<u32>)
#[test] #[test]
fn simple_traversal() { fn simple_traversal() {
test_reverse_postorder_traversal(" test_reverse_postorder_traversal(
"
function %test(i32) native { function %test(i32) native {
ebb0(v0: i32): ebb0(v0: i32):
brz v0, ebb1 brz v0, ebb1
@@ -50,12 +51,14 @@ fn simple_traversal() {
trap trap
} }
", ",
vec![0, 1, 3, 2, 4, 5]); vec![0, 1, 3, 2, 4, 5],
);
} }
#[test] #[test]
fn loops_one() { fn loops_one() {
test_reverse_postorder_traversal(" test_reverse_postorder_traversal(
"
function %test(i32) native { function %test(i32) native {
ebb0(v0: i32): ebb0(v0: i32):
jump ebb1 jump ebb1
@@ -68,12 +71,14 @@ fn loops_one() {
return return
} }
", ",
vec![0, 1, 3, 2]); vec![0, 1, 3, 2],
);
} }
#[test] #[test]
fn loops_two() { fn loops_two() {
test_reverse_postorder_traversal(" test_reverse_postorder_traversal(
"
function %test(i32) native { function %test(i32) native {
ebb0(v0: i32): ebb0(v0: i32):
brz v0, ebb1 brz v0, ebb1
@@ -93,12 +98,14 @@ fn loops_two() {
return return
} }
", ",
vec![0, 1, 2, 4, 3, 5]); vec![0, 1, 2, 4, 3, 5],
);
} }
#[test] #[test]
fn loops_three() { fn loops_three() {
test_reverse_postorder_traversal(" test_reverse_postorder_traversal(
"
function %test(i32) native { function %test(i32) native {
ebb0(v0: i32): ebb0(v0: i32):
brz v0, ebb1 brz v0, ebb1
@@ -123,12 +130,14 @@ fn loops_three() {
return return
} }
", ",
vec![0, 1, 2, 4, 3, 6, 7, 5]); vec![0, 1, 2, 4, 3, 6, 7, 5],
);
} }
#[test] #[test]
fn back_edge_one() { fn back_edge_one() {
test_reverse_postorder_traversal(" test_reverse_postorder_traversal(
"
function %test(i32) native { function %test(i32) native {
ebb0(v0: i32): ebb0(v0: i32):
brz v0, ebb1 brz v0, ebb1
@@ -146,5 +155,6 @@ fn back_edge_one() {
trap trap
} }
", ",
vec![0, 1, 3, 2, 4]); vec![0, 1, 3, 2, 4],
);
} }

View File

@@ -49,8 +49,10 @@ fn main() {
// Make sure we rebuild is this build script changes. // Make sure we rebuild is this build script changes.
// I guess that won't happen if you have non-UTF8 bytes in your path names. // I guess that won't happen if you have non-UTF8 bytes in your path names.
// The `build.py` script prints out its own dependencies. // The `build.py` script prints out its own dependencies.
println!("cargo:rerun-if-changed={}", println!(
crate_dir.join("build.rs").to_string_lossy()); "cargo:rerun-if-changed={}",
crate_dir.join("build.rs").to_string_lossy()
);
// Scripts are in `$crate_dir/meta`. // Scripts are in `$crate_dir/meta`.
let meta_dir = crate_dir.join("meta"); let meta_dir = crate_dir.join("meta");
@@ -130,8 +132,10 @@ fn isa_targets(cretonne_targets: Option<&str>, target_triple: &str) -> Result<Ve
Isa::from_arch(target_triple.split('-').next().unwrap()) Isa::from_arch(target_triple.split('-').next().unwrap())
.map(|isa| vec![isa]) .map(|isa| vec![isa])
.ok_or_else(|| { .ok_or_else(|| {
format!("no supported isa found for target triple `{}`", format!(
target_triple) "no supported isa found for target triple `{}`",
target_triple
)
}) })
} }
Some(targets) => { Some(targets) => {
@@ -143,7 +147,10 @@ fn isa_targets(cretonne_targets: Option<&str>, target_triple: &str) -> Result<Ve
match (unknown_isa_targets.is_empty(), isa_targets.is_empty()) { match (unknown_isa_targets.is_empty(), isa_targets.is_empty()) {
(true, true) => Ok(Isa::all().to_vec()), (true, true) => Ok(Isa::all().to_vec()),
(true, _) => Ok(isa_targets), (true, _) => Ok(isa_targets),
(_, _) => Err(format!("unknown isa targets: `{}`", unknown_isa_targets.join(", "))), (_, _) => Err(format!(
"unknown isa targets: `{}`",
unknown_isa_targets.join(", ")
)),
} }
} }
None => Ok(Isa::all().to_vec()), None => Ok(Isa::all().to_vec()),

View File

@@ -150,8 +150,10 @@ pub fn legalize_abi_value(have: Type, arg: &ArgumentType) -> ValueConversion {
match have_bits.cmp(&arg_bits) { match have_bits.cmp(&arg_bits) {
// We have fewer bits than the ABI argument. // We have fewer bits than the ABI argument.
Ordering::Less => { Ordering::Less => {
assert!(have.is_int() && arg.value_type.is_int(), assert!(
"Can only extend integer values"); have.is_int() && arg.value_type.is_int(),
"Can only extend integer values"
);
match arg.extension { match arg.extension {
ArgumentExtension::Uext => ValueConversion::Uext(arg.value_type), ArgumentExtension::Uext => ValueConversion::Uext(arg.value_type),
ArgumentExtension::Sext => ValueConversion::Sext(arg.value_type), ArgumentExtension::Sext => ValueConversion::Sext(arg.value_type),
@@ -192,22 +194,34 @@ mod tests {
fn legalize() { fn legalize() {
let mut arg = ArgumentType::new(types::I32); let mut arg = ArgumentType::new(types::I32);
assert_eq!(legalize_abi_value(types::I64X2, &arg), assert_eq!(
ValueConversion::VectorSplit); legalize_abi_value(types::I64X2, &arg),
assert_eq!(legalize_abi_value(types::I64, &arg), ValueConversion::VectorSplit
ValueConversion::IntSplit); );
assert_eq!(
legalize_abi_value(types::I64, &arg),
ValueConversion::IntSplit
);
// Vector of integers is broken down, then sign-extended. // Vector of integers is broken down, then sign-extended.
arg.extension = ArgumentExtension::Sext; arg.extension = ArgumentExtension::Sext;
assert_eq!(legalize_abi_value(types::I16X4, &arg), assert_eq!(
ValueConversion::VectorSplit); legalize_abi_value(types::I16X4, &arg),
assert_eq!(legalize_abi_value(types::I16.by(2).unwrap(), &arg), ValueConversion::VectorSplit
ValueConversion::VectorSplit); );
assert_eq!(legalize_abi_value(types::I16, &arg), assert_eq!(
ValueConversion::Sext(types::I32)); legalize_abi_value(types::I16.by(2).unwrap(), &arg),
ValueConversion::VectorSplit
);
assert_eq!(
legalize_abi_value(types::I16, &arg),
ValueConversion::Sext(types::I32)
);
// 64-bit float is split as an integer. // 64-bit float is split as an integer.
assert_eq!(legalize_abi_value(types::F64, &arg), assert_eq!(
ValueConversion::IntBits); legalize_abi_value(types::F64, &arg),
ValueConversion::IntBits
);
} }
} }

View File

@@ -54,9 +54,11 @@ pub trait CodeSink {
/// Report a bad encoding error. /// Report a bad encoding error.
#[inline(never)] #[inline(never)]
pub fn bad_encoding(func: &Function, inst: Inst) -> ! { pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
panic!("Bad encoding {} for {}", panic!(
"Bad encoding {} for {}",
func.encodings[inst], func.encodings[inst],
func.dfg.display_inst(inst, None)); func.dfg.display_inst(inst, None)
);
} }
/// Emit a function to `sink`, given an instruction emitter function. /// Emit a function to `sink`, given an instruction emitter function.
@@ -64,8 +66,9 @@ pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
/// This function is called from the `TargetIsa::emit_function()` implementations with the /// This function is called from the `TargetIsa::emit_function()` implementations with the
/// appropriate instruction emitter. /// appropriate instruction emitter.
pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS) pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS)
where CS: CodeSink, where
EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS) CS: CodeSink,
EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS),
{ {
let mut divert = RegDiversions::new(); let mut divert = RegDiversions::new();
for ebb in func.layout.ebbs() { for ebb in func.layout.ebbs() {

View File

@@ -60,8 +60,10 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> Result<CodeOffset
while let Some(ebb) = cur.next_ebb() { while let Some(ebb) = cur.next_ebb() {
// Record the offset for `ebb` and make sure we iterate until offsets are stable. // Record the offset for `ebb` and make sure we iterate until offsets are stable.
if cur.func.offsets[ebb] != offset { if cur.func.offsets[ebb] != offset {
assert!(cur.func.offsets[ebb] < offset, assert!(
"Code shrinking during relaxation"); cur.func.offsets[ebb] < offset,
"Code shrinking during relaxation"
);
cur.func.offsets[ebb] = offset; cur.func.offsets[ebb] = offset;
go_again = true; go_again = true;
} }
@@ -102,7 +104,8 @@ fn fallthroughs(func: &mut Function) {
ref mut opcode, ref mut opcode,
destination, destination,
.. ..
} = func.dfg[term] { } = func.dfg[term]
{
match *opcode { match *opcode {
Opcode::Fallthrough => { Opcode::Fallthrough => {
// Somebody used a fall-through instruction before the branch relaxation pass. // Somebody used a fall-through instruction before the branch relaxation pass.
@@ -126,16 +129,19 @@ fn fallthroughs(func: &mut Function) {
/// ///
/// Return the size of the replacement instructions up to and including the location where `pos` is /// Return the size of the replacement instructions up to and including the location where `pos` is
/// left. /// left.
fn relax_branch(cur: &mut FuncCursor, fn relax_branch(
cur: &mut FuncCursor,
offset: CodeOffset, offset: CodeOffset,
dest_offset: CodeOffset, dest_offset: CodeOffset,
encinfo: &EncInfo) encinfo: &EncInfo,
-> CodeOffset { ) -> CodeOffset {
let inst = cur.current_inst().unwrap(); let inst = cur.current_inst().unwrap();
dbg!("Relaxing [{}] {} for {:#x}-{:#x} range", dbg!(
"Relaxing [{}] {} for {:#x}-{:#x} range",
encinfo.display(cur.func.encodings[inst]), encinfo.display(cur.func.encodings[inst]),
cur.func.dfg.display_inst(inst, None), cur.func.dfg.display_inst(inst, None),
offset, offset,
dest_offset); dest_offset
);
unimplemented!(); unimplemented!();
} }

View File

@@ -14,27 +14,34 @@ use std::convert::{Into, From};
pub struct BitSet<T>(pub T); pub struct BitSet<T>(pub T);
impl<T> BitSet<T> impl<T> BitSet<T>
where T: Into<u32> + From<u8> + BitOr<T, Output = T> + Shl<u8, Output = T> + Sub<T, Output=T> + where
Add<T, Output=T> + PartialEq + Copy T: Into<u32>
+ From<u8>
+ BitOr<T, Output = T>
+ Shl<u8, Output = T>
+ Sub<T, Output = T>
+ Add<T, Output = T>
+ PartialEq
+ Copy,
{ {
/// Maximum number of bits supported by this BitSet instance /// Maximum number of bits supported by this BitSet instance
pub fn bits() -> usize { pub fn bits() -> usize {
size_of::<T>() * 8 size_of::<T>() * 8
} }
/// Maximum number of bits supported by any bitset instance atm. /// Maximum number of bits supported by any bitset instance atm.
pub fn max_bits() -> usize { pub fn max_bits() -> usize {
size_of::<u32>() * 8 size_of::<u32>() * 8
} }
/// Check if this BitSet contains the number num /// Check if this BitSet contains the number num
pub fn contains(&self, num: u8) -> bool { pub fn contains(&self, num: u8) -> bool {
assert!((num as usize) < Self::bits()); assert!((num as usize) < Self::bits());
assert!((num as usize) < Self::max_bits()); assert!((num as usize) < Self::max_bits());
self.0.into() & (1 << num) != 0 self.0.into() & (1 << num) != 0
} }
/// Return the smallest number contained in the bitset or None if empty /// Return the smallest number contained in the bitset or None if empty
pub fn min(&self) -> Option<u8> { pub fn min(&self) -> Option<u8> {
if self.0.into() == 0 { if self.0.into() == 0 {
None None
@@ -43,7 +50,7 @@ impl<T> BitSet<T>
} }
} }
/// Return the largest number contained in the bitset or None if empty /// Return the largest number contained in the bitset or None if empty
pub fn max(&self) -> Option<u8> { pub fn max(&self) -> Option<u8> {
if self.0.into() == 0 { if self.0.into() == 0 {
None None
@@ -53,14 +60,14 @@ impl<T> BitSet<T>
} }
} }
/// Construct a BitSet with the half-open range [lo,hi) filled in /// Construct a BitSet with the half-open range [lo,hi) filled in
pub fn from_range(lo: u8, hi: u8) -> BitSet<T> { pub fn from_range(lo: u8, hi: u8) -> BitSet<T> {
assert!(lo <= hi); assert!(lo <= hi);
assert!((hi as usize) <= Self::bits()); assert!((hi as usize) <= Self::bits());
let one : T = T::from(1); let one: T = T::from(1);
// I can't just do (one << hi) - one here as the shift may overflow // I can't just do (one << hi) - one here as the shift may overflow
let hi_rng = if hi >= 1 { let hi_rng = if hi >= 1 {
(one << (hi-1)) + ((one << (hi-1)) - one) (one << (hi - 1)) + ((one << (hi - 1)) - one)
} else { } else {
T::from(0) T::from(0)
}; };
@@ -94,14 +101,15 @@ mod tests {
assert!(!s2.contains(7)); assert!(!s2.contains(7));
let s3 = BitSet::<u8>(2 | 4 | 64); let s3 = BitSet::<u8>(2 | 4 | 64);
assert!(!s3.contains(0) && !s3.contains(3) && !s3.contains(4) && !s3.contains(5) && assert!(!s3.contains(0) && !s3.contains(3) && !s3.contains(4));
!s3.contains(7)); assert!(!s3.contains(5) && !s3.contains(7));
assert!(s3.contains(1) && s3.contains(2) && s3.contains(6)); assert!(s3.contains(1) && s3.contains(2) && s3.contains(6));
let s4 = BitSet::<u16>(4 | 8 | 256 | 1024); let s4 = BitSet::<u16>(4 | 8 | 256 | 1024);
assert!(!s4.contains(0) && !s4.contains(1) && !s4.contains(4) && !s4.contains(5) && assert!(
!s4.contains(6) && !s4.contains(7) && !s4.contains(0) && !s4.contains(1) && !s4.contains(4) && !s4.contains(5) &&
!s4.contains(9) && !s4.contains(11)); !s4.contains(6) && !s4.contains(7) && !s4.contains(9) && !s4.contains(11)
);
assert!(s4.contains(2) && s4.contains(3) && s4.contains(8) && s4.contains(10)); assert!(s4.contains(2) && s4.contains(3) && s4.contains(8) && s4.contains(10));
} }

View File

@@ -63,7 +63,8 @@ impl<K: Default, V: Default> NodePool<K, V> {
} }
impl<K: Default, V: Default> BTree<K, V> { impl<K: Default, V: Default> BTree<K, V> {
/// Search for `key` and return a `Cursor` that either points at `key` or the position where it would be inserted. /// Search for `key` and return a `Cursor` that either points at `key` or the position
/// where it would be inserted.
pub fn search(&mut self, key: K) -> Cursor<K, V> { pub fn search(&mut self, key: K) -> Cursor<K, V> {
unimplemented!() unimplemented!()
} }

View File

@@ -26,10 +26,11 @@ pub trait Table<K: Copy + Eq> {
/// ///
/// Returns `Ok(idx)` with the table index containing the found entry, or `Err(idx)` with the empty /// Returns `Ok(idx)` with the table index containing the found entry, or `Err(idx)` with the empty
/// sentinel entry if no entry could be found. /// sentinel entry if no entry could be found.
pub fn probe<K: Copy + Eq, T: Table<K> + ?Sized>(table: &T, pub fn probe<K: Copy + Eq, T: Table<K> + ?Sized>(
table: &T,
key: K, key: K,
hash: usize) hash: usize,
-> Result<usize, usize> { ) -> Result<usize, usize> {
debug_assert!(table.len().is_power_of_two()); debug_assert!(table.len().is_power_of_two());
let mask = table.len() - 1; let mask = table.len() - 1;

View File

@@ -133,18 +133,24 @@ impl Context {
/// Perform LICM on the function. /// Perform LICM on the function.
pub fn licm(&mut self) -> CtonResult { pub fn licm(&mut self) -> CtonResult {
self.ensure_domtree(); self.ensure_domtree();
do_licm(&mut self.func, do_licm(
&mut self.func,
&mut self.cfg, &mut self.cfg,
&mut self.domtree, &mut self.domtree,
&mut self.loop_analysis); &mut self.loop_analysis,
);
self.verify(None).map_err(Into::into) self.verify(None).map_err(Into::into)
} }
/// Run the register allocator. /// Run the register allocator.
pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult { pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
self.ensure_domtree(); self.ensure_domtree();
self.regalloc self.regalloc.run(
.run(isa, &mut self.func, &self.cfg, &self.domtree) isa,
&mut self.func,
&self.cfg,
&self.domtree,
)
} }
/// Insert prologue and epilogues after computing the stack frame layout. /// Insert prologue and epilogues after computing the stack frame layout.

View File

@@ -146,17 +146,21 @@ impl<'c, 'f> ir::InstInserterBase<'c> for &'c mut EncCursor<'f> {
&mut self.func.dfg &mut self.func.dfg
} }
fn insert_built_inst(self, fn insert_built_inst(
self,
inst: ir::Inst, inst: ir::Inst,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type,
-> &'c mut ir::DataFlowGraph { ) -> &'c mut ir::DataFlowGraph {
// Insert the instruction and remember the reference. // Insert the instruction and remember the reference.
self.insert_inst(inst); self.insert_inst(inst);
self.built_inst = Some(inst); self.built_inst = Some(inst);
// Assign an encoding. // Assign an encoding.
match self.isa match self.isa.encode(
.encode(&self.func.dfg, &self.func.dfg[inst], ctrl_typevar) { &self.func.dfg,
&self.func.dfg[inst],
ctrl_typevar,
) {
Ok(e) => self.func.encodings[inst] = e, Ok(e) => self.func.encodings[inst] = e,
Err(_) => panic!("can't encode {}", self.display_inst(inst)), Err(_) => panic!("can't encode {}", self.display_inst(inst)),
} }

View File

@@ -79,8 +79,7 @@ fn open_file() -> io::BufWriter<File> {
} }
File::create(path) File::create(path)
} }
} }.expect("Can't open tracing file");
.expect("Can't open tracing file");
io::BufWriter::new(file) io::BufWriter::new(file)
} }
@@ -99,10 +98,13 @@ macro_rules! dbg {
} }
/// Helper for printing lists. /// Helper for printing lists.
pub struct DisplayList<'a, T>(pub &'a [T]) where T: 'a + fmt::Display; pub struct DisplayList<'a, T>(pub &'a [T])
where
T: 'a + fmt::Display;
impl<'a, T> fmt::Display for DisplayList<'a, T> impl<'a, T> fmt::Display for DisplayList<'a, T>
where T: 'a + fmt::Display where
T: 'a + fmt::Display,
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0.split_first() { match self.0.split_first() {

View File

@@ -85,13 +85,15 @@ impl DominatorTree {
/// ///
/// If `a` and `b` belong to the same EBB, compare their relative position in the EBB. /// If `a` and `b` belong to the same EBB, compare their relative position in the EBB.
pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
where A: Into<ExpandedProgramPoint>, where
B: Into<ExpandedProgramPoint> A: Into<ExpandedProgramPoint>,
B: Into<ExpandedProgramPoint>,
{ {
let a = a.into(); let a = a.into();
let b = b.into(); let b = b.into();
self.rpo_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b)) self.rpo_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b)).then(
.then(layout.cmp(a, b)) layout.cmp(a, b),
)
} }
/// Returns `true` if `a` dominates `b`. /// Returns `true` if `a` dominates `b`.
@@ -104,8 +106,9 @@ impl DominatorTree {
/// ///
/// An instruction is considered to dominate itself. /// An instruction is considered to dominate itself.
pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool pub fn dominates<A, B>(&self, a: A, b: B, layout: &Layout) -> bool
where A: Into<ExpandedProgramPoint>, where
B: Into<ExpandedProgramPoint> A: Into<ExpandedProgramPoint>,
B: Into<ExpandedProgramPoint>,
{ {
let a = a.into(); let a = a.into();
let b = b.into(); let b = b.into();
@@ -126,12 +129,16 @@ impl DominatorTree {
/// Find the last instruction in `a` that dominates `b`. /// Find the last instruction in `a` that dominates `b`.
/// If no instructions in `a` dominate `b`, return `None`. /// If no instructions in `a` dominate `b`, return `None`.
fn last_dominator<B>(&self, a: Ebb, b: B, layout: &Layout) -> Option<Inst> fn last_dominator<B>(&self, a: Ebb, b: B, layout: &Layout) -> Option<Inst>
where B: Into<ExpandedProgramPoint> where
B: Into<ExpandedProgramPoint>,
{ {
let (mut ebb_b, mut inst_b) = match b.into() { let (mut ebb_b, mut inst_b) = match b.into() {
ExpandedProgramPoint::Ebb(ebb) => (ebb, None), ExpandedProgramPoint::Ebb(ebb) => (ebb, None),
ExpandedProgramPoint::Inst(inst) => { ExpandedProgramPoint::Inst(inst) => {
(layout.inst_ebb(inst).expect("Instruction not in layout."), Some(inst)) (
layout.inst_ebb(inst).expect("Instruction not in layout."),
Some(inst),
)
} }
}; };
let rpo_a = self.nodes[a].rpo_number; let rpo_a = self.nodes[a].rpo_number;
@@ -149,22 +156,29 @@ impl DominatorTree {
/// Compute the common dominator of two basic blocks. /// Compute the common dominator of two basic blocks.
/// ///
/// Both basic blocks are assumed to be reachable. /// Both basic blocks are assumed to be reachable.
pub fn common_dominator(&self, pub fn common_dominator(
&self,
mut a: BasicBlock, mut a: BasicBlock,
mut b: BasicBlock, mut b: BasicBlock,
layout: &Layout) layout: &Layout,
-> BasicBlock { ) -> BasicBlock {
loop { loop {
match self.rpo_cmp_ebb(a.0, b.0) { match self.rpo_cmp_ebb(a.0, b.0) {
Ordering::Less => { Ordering::Less => {
// `a` comes before `b` in the RPO. Move `b` up. // `a` comes before `b` in the RPO. Move `b` up.
let idom = self.nodes[b.0].idom.expect("Unreachable basic block?"); let idom = self.nodes[b.0].idom.expect("Unreachable basic block?");
b = (layout.inst_ebb(idom).expect("Dangling idom instruction"), idom); b = (
layout.inst_ebb(idom).expect("Dangling idom instruction"),
idom,
);
} }
Ordering::Greater => { Ordering::Greater => {
// `b` comes before `a` in the RPO. Move `a` up. // `b` comes before `a` in the RPO. Move `a` up.
let idom = self.nodes[a.0].idom.expect("Unreachable basic block?"); let idom = self.nodes[a.0].idom.expect("Unreachable basic block?");
a = (layout.inst_ebb(idom).expect("Dangling idom instruction"), idom); a = (
layout.inst_ebb(idom).expect("Dangling idom instruction"),
idom,
);
} }
Ordering::Equal => break, Ordering::Equal => break,
} }
@@ -327,15 +341,16 @@ impl DominatorTree {
// Get an iterator with just the reachable, already visited predecessors to `ebb`. // Get an iterator with just the reachable, already visited predecessors to `ebb`.
// Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't // Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
// been visited yet, 0 for unreachable blocks. // been visited yet, 0 for unreachable blocks.
let mut reachable_preds = cfg.get_predecessors(ebb) let mut reachable_preds = cfg.get_predecessors(ebb).iter().cloned().filter(
.iter() |&(pred, _)| {
.cloned() self.nodes[pred].rpo_number > 1
.filter(|&(pred, _)| self.nodes[pred].rpo_number > 1); },
);
// The RPO must visit at least one predecessor before this node. // The RPO must visit at least one predecessor before this node.
let mut idom = reachable_preds let mut idom = reachable_preds.next().expect(
.next() "EBB node must have one reachable predecessor",
.expect("EBB node must have one reachable predecessor"); );
for pred in reachable_preds { for pred in reachable_preds {
idom = self.common_dominator(idom, pred, layout); idom = self.common_dominator(idom, pred, layout);
@@ -383,10 +398,11 @@ impl DominatorTree {
// forward in RPO numbers and backwards in the postorder list of EBBs, renumbering the Ebbs // forward in RPO numbers and backwards in the postorder list of EBBs, renumbering the Ebbs
// until we find a gap // until we find a gap
for (&current_ebb, current_rpo) in for (&current_ebb, current_rpo) in
self.postorder[0..ebb_postorder_index] self.postorder[0..ebb_postorder_index].iter().rev().zip(
.iter() inserted_rpo_number +
.rev() 1..,
.zip(inserted_rpo_number + 1..) { )
{
if self.nodes[current_ebb].rpo_number < current_rpo { if self.nodes[current_ebb].rpo_number < current_rpo {
// There is no gap, we renumber // There is no gap, we renumber
self.nodes[current_ebb].rpo_number = current_rpo; self.nodes[current_ebb].rpo_number = current_rpo;
@@ -457,10 +473,14 @@ mod test {
assert_eq!(dt.rpo_cmp(ebb3, ebb3, &cur.func.layout), Ordering::Equal); assert_eq!(dt.rpo_cmp(ebb3, ebb3, &cur.func.layout), Ordering::Equal);
assert_eq!(dt.rpo_cmp(ebb3, ebb1, &cur.func.layout), Ordering::Less); assert_eq!(dt.rpo_cmp(ebb3, ebb1, &cur.func.layout), Ordering::Less);
assert_eq!(dt.rpo_cmp(ebb3, jmp_ebb3_ebb1, &cur.func.layout), assert_eq!(
Ordering::Less); dt.rpo_cmp(ebb3, jmp_ebb3_ebb1, &cur.func.layout),
assert_eq!(dt.rpo_cmp(jmp_ebb3_ebb1, jmp_ebb1_ebb2, &cur.func.layout), Ordering::Less
Ordering::Less); );
assert_eq!(
dt.rpo_cmp(jmp_ebb3_ebb1, jmp_ebb1_ebb2, &cur.func.layout),
Ordering::Less
);
assert_eq!(dt.cfg_postorder(), &[ebb2, ebb0, ebb1, ebb3]); assert_eq!(dt.cfg_postorder(), &[ebb2, ebb0, ebb1, ebb3]);
} }

View File

@@ -212,12 +212,13 @@ impl<T: EntityRef> ListPool<T> {
/// Reallocate a block to a different size class. /// Reallocate a block to a different size class.
/// ///
/// Copy `elems_to_copy` elements from the old to the new block. /// Copy `elems_to_copy` elements from the old to the new block.
fn realloc(&mut self, fn realloc(
&mut self,
block: usize, block: usize,
from_sclass: SizeClass, from_sclass: SizeClass,
to_sclass: SizeClass, to_sclass: SizeClass,
elems_to_copy: usize) elems_to_copy: usize,
-> usize { ) -> usize {
assert!(elems_to_copy <= sclass_size(from_sclass)); assert!(elems_to_copy <= sclass_size(from_sclass));
assert!(elems_to_copy <= sclass_size(to_sclass)); assert!(elems_to_copy <= sclass_size(to_sclass));
let new_block = self.alloc(to_sclass); let new_block = self.alloc(to_sclass);
@@ -384,7 +385,8 @@ impl<T: EntityRef> EntityList<T> {
/// Appends multiple elements to the back of the list. /// Appends multiple elements to the back of the list.
pub fn extend<I>(&mut self, elements: I, pool: &mut ListPool<T>) pub fn extend<I>(&mut self, elements: I, pool: &mut ListPool<T>)
where I: IntoIterator<Item = T> where
I: IntoIterator<Item = T>,
{ {
// TODO: use `size_hint()` to reduce reallocations. // TODO: use `size_hint()` to reduce reallocations.
for x in elements { for x in elements {
@@ -597,8 +599,10 @@ mod tests {
list.extend([i1, i1, i2, i2, i3, i3, i4, i4].iter().cloned(), pool); list.extend([i1, i1, i2, i2, i3, i3, i4, i4].iter().cloned(), pool);
assert_eq!(list.len(pool), 12); assert_eq!(list.len(pool), 12);
assert_eq!(list.as_slice(pool), assert_eq!(
&[i1, i2, i3, i4, i1, i1, i2, i2, i3, i3, i4, i4]); list.as_slice(pool),
&[i1, i2, i3, i4, i1, i1, i2, i2, i3, i3, i4, i4]
);
} }
#[test] #[test]

View File

@@ -14,8 +14,9 @@ use std::ops::{Index, IndexMut};
/// all keys have a default entry from the beginning. /// all keys have a default entry from the beginning.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EntityMap<K, V> pub struct EntityMap<K, V>
where K: EntityRef, where
V: Clone K: EntityRef,
V: Clone,
{ {
elems: Vec<V>, elems: Vec<V>,
default: V, default: V,
@@ -24,12 +25,14 @@ pub struct EntityMap<K, V>
/// Shared `EntityMap` implementation for all value types. /// Shared `EntityMap` implementation for all value types.
impl<K, V> EntityMap<K, V> impl<K, V> EntityMap<K, V>
where K: EntityRef, where
V: Clone K: EntityRef,
V: Clone,
{ {
/// Create a new empty map. /// Create a new empty map.
pub fn new() -> Self pub fn new() -> Self
where V: Default where
V: Default,
{ {
EntityMap { EntityMap {
elems: Vec::new(), elems: Vec::new(),
@@ -68,8 +71,9 @@ impl<K, V> EntityMap<K, V>
/// ///
/// All keys are permitted. Untouched entries have the default value. /// All keys are permitted. Untouched entries have the default value.
impl<K, V> Index<K> for EntityMap<K, V> impl<K, V> Index<K> for EntityMap<K, V>
where K: EntityRef, where
V: Clone K: EntityRef,
V: Clone,
{ {
type Output = V; type Output = V;
@@ -82,8 +86,9 @@ impl<K, V> Index<K> for EntityMap<K, V>
/// ///
/// The map grows as needed to accommodate new keys. /// The map grows as needed to accommodate new keys.
impl<K, V> IndexMut<K> for EntityMap<K, V> impl<K, V> IndexMut<K> for EntityMap<K, V>
where K: EntityRef, where
V: Clone K: EntityRef,
V: Clone,
{ {
fn index_mut(&mut self, k: K) -> &mut V { fn index_mut(&mut self, k: K) -> &mut V {
let i = k.index(); let i = k.index();

View File

@@ -14,14 +14,16 @@ use std::ops::{Index, IndexMut};
/// conflicting references will be created. Using unknown keys for indexing will cause a panic. /// conflicting references will be created. Using unknown keys for indexing will cause a panic.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PrimaryMap<K, V> pub struct PrimaryMap<K, V>
where K: EntityRef where
K: EntityRef,
{ {
elems: Vec<V>, elems: Vec<V>,
unused: PhantomData<K>, unused: PhantomData<K>,
} }
impl<K, V> PrimaryMap<K, V> impl<K, V> PrimaryMap<K, V>
where K: EntityRef where
K: EntityRef,
{ {
/// Create a new empty map. /// Create a new empty map.
pub fn new() -> Self { pub fn new() -> Self {
@@ -77,7 +79,8 @@ impl<K, V> PrimaryMap<K, V>
/// Immutable indexing into an `PrimaryMap`. /// Immutable indexing into an `PrimaryMap`.
/// The indexed value must be in the map. /// The indexed value must be in the map.
impl<K, V> Index<K> for PrimaryMap<K, V> impl<K, V> Index<K> for PrimaryMap<K, V>
where K: EntityRef where
K: EntityRef,
{ {
type Output = V; type Output = V;
@@ -88,7 +91,8 @@ impl<K, V> Index<K> for PrimaryMap<K, V>
/// Mutable indexing into an `PrimaryMap`. /// Mutable indexing into an `PrimaryMap`.
impl<K, V> IndexMut<K> for PrimaryMap<K, V> impl<K, V> IndexMut<K> for PrimaryMap<K, V>
where K: EntityRef where
K: EntityRef,
{ {
fn index_mut(&mut self, k: K) -> &mut V { fn index_mut(&mut self, k: K) -> &mut V {
&mut self.elems[k.index()] &mut self.elems[k.index()]

View File

@@ -51,16 +51,18 @@ pub trait SparseMapValue<K> {
/// - `SparseMap` requires the values to implement `SparseMapValue<K>` which means that they must /// - `SparseMap` requires the values to implement `SparseMapValue<K>` which means that they must
/// contain their own key. /// contain their own key.
pub struct SparseMap<K, V> pub struct SparseMap<K, V>
where K: EntityRef, where
V: SparseMapValue<K> K: EntityRef,
V: SparseMapValue<K>,
{ {
sparse: EntityMap<K, u32>, sparse: EntityMap<K, u32>,
dense: Vec<V>, dense: Vec<V>,
} }
impl<K, V> SparseMap<K, V> impl<K, V> SparseMap<K, V>
where K: EntityRef, where
V: SparseMapValue<K> K: EntityRef,
V: SparseMapValue<K>,
{ {
/// Create a new empty mapping. /// Create a new empty mapping.
pub fn new() -> Self { pub fn new() -> Self {
@@ -191,8 +193,9 @@ impl<K, V> SparseMap<K, V>
/// Iterating over the elements of a set. /// Iterating over the elements of a set.
impl<'a, K, V> IntoIterator for &'a SparseMap<K, V> impl<'a, K, V> IntoIterator for &'a SparseMap<K, V>
where K: EntityRef, where
V: SparseMapValue<K> K: EntityRef,
V: SparseMapValue<K>,
{ {
type Item = &'a V; type Item = &'a V;
type IntoIter = slice::Iter<'a, V>; type IntoIter = slice::Iter<'a, V>;
@@ -204,7 +207,8 @@ impl<'a, K, V> IntoIterator for &'a SparseMap<K, V>
/// Any `EntityRef` can be used as a sparse map value representing itself. /// Any `EntityRef` can be used as a sparse map value representing itself.
impl<T> SparseMapValue<T> for T impl<T> SparseMapValue<T> for T
where T: EntityRef where
T: EntityRef,
{ {
fn key(&self) -> T { fn key(&self) -> T {
*self *self
@@ -290,8 +294,10 @@ mod tests {
assert_eq!(map.insert(Obj(i0, "baz")), None); assert_eq!(map.insert(Obj(i0, "baz")), None);
// Iteration order = insertion order when nothing has been removed yet. // Iteration order = insertion order when nothing has been removed yet.
assert_eq!(map.values().map(|obj| obj.1).collect::<Vec<_>>(), assert_eq!(
["foo", "bar", "baz"]); map.values().map(|obj| obj.1).collect::<Vec<_>>(),
["foo", "bar", "baz"]
);
assert_eq!(map.len(), 3); assert_eq!(map.len(), 3);
assert_eq!(map.get(i0), Some(&Obj(i0, "baz"))); assert_eq!(map.get(i0), Some(&Obj(i0, "baz")));

View File

@@ -89,7 +89,8 @@ impl<'f, IIB: InstInserterBase<'f>> InsertBuilder<'f, IIB> {
/// ///
/// The `reuse` argument is expected to be an array of `Option<Value>`. /// The `reuse` argument is expected to be an array of `Option<Value>`.
pub fn with_results<Array>(self, reuse: Array) -> InsertReuseBuilder<'f, IIB, Array> pub fn with_results<Array>(self, reuse: Array) -> InsertReuseBuilder<'f, IIB, Array>
where Array: AsRef<[Option<Value>]> where
Array: AsRef<[Option<Value>]>,
{ {
InsertReuseBuilder { InsertReuseBuilder {
inserter: self.inserter, inserter: self.inserter,
@@ -134,8 +135,9 @@ impl<'f, IIB: InstInserterBase<'f>> InstBuilderBase<'f> for InsertBuilder<'f, II
/// Builder that inserts a new instruction like `InsertBuilder`, but reusing result values. /// Builder that inserts a new instruction like `InsertBuilder`, but reusing result values.
pub struct InsertReuseBuilder<'f, IIB, Array> pub struct InsertReuseBuilder<'f, IIB, Array>
where IIB: InstInserterBase<'f>, where
Array: AsRef<[Option<Value>]> IIB: InstInserterBase<'f>,
Array: AsRef<[Option<Value>]>,
{ {
inserter: IIB, inserter: IIB,
reuse: Array, reuse: Array,

View File

@@ -267,7 +267,8 @@ impl FromStr for FloatCC {
mod tests { mod tests {
use super::*; use super::*;
static INT_ALL: [IntCC; 10] = [IntCC::Equal, static INT_ALL: [IntCC; 10] = [
IntCC::Equal,
IntCC::NotEqual, IntCC::NotEqual,
IntCC::SignedLessThan, IntCC::SignedLessThan,
IntCC::SignedGreaterThanOrEqual, IntCC::SignedGreaterThanOrEqual,
@@ -276,7 +277,8 @@ mod tests {
IntCC::UnsignedLessThan, IntCC::UnsignedLessThan,
IntCC::UnsignedGreaterThanOrEqual, IntCC::UnsignedGreaterThanOrEqual,
IntCC::UnsignedGreaterThan, IntCC::UnsignedGreaterThan,
IntCC::UnsignedLessThanOrEqual]; IntCC::UnsignedLessThanOrEqual,
];
#[test] #[test]
fn int_inverse() { fn int_inverse() {
@@ -306,7 +308,8 @@ mod tests {
assert_eq!("bogus".parse::<IntCC>(), Err(())); assert_eq!("bogus".parse::<IntCC>(), Err(()));
} }
static FLOAT_ALL: [FloatCC; 14] = [FloatCC::Ordered, static FLOAT_ALL: [FloatCC; 14] = [
FloatCC::Ordered,
FloatCC::Unordered, FloatCC::Unordered,
FloatCC::Equal, FloatCC::Equal,
FloatCC::NotEqual, FloatCC::NotEqual,
@@ -319,7 +322,8 @@ mod tests {
FloatCC::UnorderedOrLessThan, FloatCC::UnorderedOrLessThan,
FloatCC::UnorderedOrLessThanOrEqual, FloatCC::UnorderedOrLessThanOrEqual,
FloatCC::UnorderedOrGreaterThan, FloatCC::UnorderedOrGreaterThan,
FloatCC::UnorderedOrGreaterThanOrEqual]; FloatCC::UnorderedOrGreaterThanOrEqual,
];
#[test] #[test]
fn float_inverse() { fn float_inverse() {

View File

@@ -153,17 +153,21 @@ impl DataFlowGraph {
pub fn value_def(&self, v: Value) -> ValueDef { pub fn value_def(&self, v: Value) -> ValueDef {
match self.values[v] { match self.values[v] {
ValueData::Inst { inst, num, .. } => { ValueData::Inst { inst, num, .. } => {
assert_eq!(Some(v), assert_eq!(
Some(v),
self.results[inst].get(num as usize, &self.value_lists), self.results[inst].get(num as usize, &self.value_lists),
"Dangling result value {}: {}", "Dangling result value {}: {}",
v, v,
self.display_inst(inst, None)); self.display_inst(inst, None)
);
ValueDef::Res(inst, num as usize) ValueDef::Res(inst, num as usize)
} }
ValueData::Arg { ebb, num, .. } => { ValueData::Arg { ebb, num, .. } => {
assert_eq!(Some(v), assert_eq!(
Some(v),
self.ebbs[ebb].args.get(num as usize, &self.value_lists), self.ebbs[ebb].args.get(num as usize, &self.value_lists),
"Dangling EBB argument value"); "Dangling EBB argument value"
);
ValueDef::Arg(ebb, num as usize) ValueDef::Arg(ebb, num as usize)
} }
ValueData::Alias { original, .. } => { ValueData::Alias { original, .. } => {
@@ -247,19 +251,23 @@ impl DataFlowGraph {
// Try to create short alias chains by finding the original source value. // Try to create short alias chains by finding the original source value.
// This also avoids the creation of loops. // This also avoids the creation of loops.
let original = self.resolve_aliases(src); let original = self.resolve_aliases(src);
assert_ne!(dest, assert_ne!(
dest,
original, original,
"Aliasing {} to {} would create a loop", "Aliasing {} to {} would create a loop",
dest, dest,
src); src
);
let ty = self.value_type(original); let ty = self.value_type(original);
assert_eq!(self.value_type(dest), assert_eq!(
self.value_type(dest),
ty, ty,
"Aliasing {} to {} would change its type {} to {}", "Aliasing {} to {} would change its type {} to {}",
dest, dest,
src, src,
self.value_type(dest), self.value_type(dest),
ty); ty
);
self.values[dest] = ValueData::Alias { ty, original }; self.values[dest] = ValueData::Alias { ty, original };
} }
@@ -274,29 +282,36 @@ impl DataFlowGraph {
/// cleared, so it likely needs to be removed from the graph. /// cleared, so it likely needs to be removed from the graph.
/// ///
pub fn replace_with_aliases(&mut self, dest_inst: Inst, src_inst: Inst) { pub fn replace_with_aliases(&mut self, dest_inst: Inst, src_inst: Inst) {
debug_assert_ne!(dest_inst, debug_assert_ne!(
dest_inst,
src_inst, src_inst,
"Replacing {} with itself would create a loop", "Replacing {} with itself would create a loop",
dest_inst); dest_inst
debug_assert_eq!(self.results[dest_inst].len(&self.value_lists), );
debug_assert_eq!(
self.results[dest_inst].len(&self.value_lists),
self.results[src_inst].len(&self.value_lists), self.results[src_inst].len(&self.value_lists),
"Replacing {} with {} would produce a different number of results.", "Replacing {} with {} would produce a different number of results.",
dest_inst, dest_inst,
src_inst); src_inst
);
for (&dest, &src) in self.results[dest_inst] for (&dest, &src) in self.results[dest_inst]
.as_slice(&self.value_lists) .as_slice(&self.value_lists)
.iter() .iter()
.zip(self.results[src_inst].as_slice(&self.value_lists)) { .zip(self.results[src_inst].as_slice(&self.value_lists))
{
let original = src; let original = src;
let ty = self.value_type(original); let ty = self.value_type(original);
assert_eq!(self.value_type(dest), assert_eq!(
self.value_type(dest),
ty, ty,
"Aliasing {} to {} would change its type {} to {}", "Aliasing {} to {} would change its type {} to {}",
dest, dest,
src, src,
self.value_type(dest), self.value_type(dest),
ty); ty
);
self.values[dest] = ValueData::Alias { ty, original }; self.values[dest] = ValueData::Alias { ty, original };
} }
@@ -371,10 +386,11 @@ impl DataFlowGraph {
} }
/// Returns an object that displays `inst`. /// Returns an object that displays `inst`.
pub fn display_inst<'a, I: Into<Option<&'a TargetIsa>>>(&'a self, pub fn display_inst<'a, I: Into<Option<&'a TargetIsa>>>(
&'a self,
inst: Inst, inst: Inst,
isa: I) isa: I,
-> DisplayInst<'a> { ) -> DisplayInst<'a> {
DisplayInst(self, isa.into(), inst) DisplayInst(self, isa.into(), inst)
} }
@@ -433,12 +449,14 @@ impl DataFlowGraph {
/// Create a new set of result values for `inst` using `ctrl_typevar` to determine the result /// Create a new set of result values for `inst` using `ctrl_typevar` to determine the result
/// types. Any values provided by `reuse` will be reused. When `reuse` is exhausted or when it /// types. Any values provided by `reuse` will be reused. When `reuse` is exhausted or when it
/// produces `None`, a new value is created. /// produces `None`, a new value is created.
pub fn make_inst_results_reusing<I>(&mut self, pub fn make_inst_results_reusing<I>(
&mut self,
inst: Inst, inst: Inst,
ctrl_typevar: Type, ctrl_typevar: Type,
reuse: I) reuse: I,
-> usize ) -> usize
where I: Iterator<Item = Option<Value>> where
I: Iterator<Item = Option<Value>>,
{ {
let mut reuse = reuse.fuse(); let mut reuse = reuse.fuse();
let constraints = self.insts[inst].opcode().constraints(); let constraints = self.insts[inst].opcode().constraints();
@@ -478,9 +496,10 @@ impl DataFlowGraph {
} }
/// Create an `InsertBuilder` that will insert an instruction at the cursor's current position. /// Create an `InsertBuilder` that will insert an instruction at the cursor's current position.
pub fn ins<'c, 'fc: 'c, 'fd>(&'fd mut self, pub fn ins<'c, 'fc: 'c, 'fd>(
at: &'c mut Cursor<'fc>) &'fd mut self,
-> InsertBuilder<'fd, LayoutCursorInserter<'c, 'fc, 'fd>> { at: &'c mut Cursor<'fc>,
) -> InsertBuilder<'fd, LayoutCursorInserter<'c, 'fc, 'fd>> {
InsertBuilder::new(LayoutCursorInserter::new(at, self)) InsertBuilder::new(LayoutCursorInserter::new(at, self))
} }
@@ -542,15 +561,19 @@ impl DataFlowGraph {
inst, inst,
}); });
let num = num as usize; let num = num as usize;
let attached = mem::replace(self.results[inst] let attached = mem::replace(
self.results[inst]
.get_mut(num, &mut self.value_lists) .get_mut(num, &mut self.value_lists)
.expect("Replacing detached result"), .expect("Replacing detached result"),
new_value); new_value,
assert_eq!(attached, );
assert_eq!(
attached,
old_value, old_value,
"{} wasn't detached from {}", "{} wasn't detached from {}",
old_value, old_value,
self.display_inst(inst, None)); self.display_inst(inst, None)
);
new_value new_value
} }
@@ -570,9 +593,9 @@ impl DataFlowGraph {
/// ///
/// Panics if the instruction doesn't support arguments. /// Panics if the instruction doesn't support arguments.
pub fn append_inst_arg(&mut self, inst: Inst, new_arg: Value) { pub fn append_inst_arg(&mut self, inst: Inst, new_arg: Value) {
let mut branch_values = self.insts[inst] let mut branch_values = self.insts[inst].take_value_list().expect(
.take_value_list() "the instruction doesn't have value arguments",
.expect("the instruction doesn't have value arguments"); );
branch_values.push(new_arg, &mut self.value_lists); branch_values.push(new_arg, &mut self.value_lists);
self.insts[inst].put_value_list(branch_values) self.insts[inst].put_value_list(branch_values)
} }
@@ -581,9 +604,9 @@ impl DataFlowGraph {
/// ///
/// This function panics if the instruction doesn't have any result. /// This function panics if the instruction doesn't have any result.
pub fn first_result(&self, inst: Inst) -> Value { pub fn first_result(&self, inst: Inst) -> Value {
self.results[inst] self.results[inst].first(&self.value_lists).expect(
.first(&self.value_lists) "Instruction has no results",
.expect("Instruction has no results") )
} }
/// Test if `inst` has any result values currently. /// Test if `inst` has any result values currently.
@@ -613,11 +636,12 @@ impl DataFlowGraph {
/// called first. /// called first.
/// ///
/// Returns `None` if asked about a result index that is too large. /// Returns `None` if asked about a result index that is too large.
pub fn compute_result_type(&self, pub fn compute_result_type(
&self,
inst: Inst, inst: Inst,
result_idx: usize, result_idx: usize,
ctrl_typevar: Type) ctrl_typevar: Type,
-> Option<Type> { ) -> Option<Type> {
let constraints = self.insts[inst].opcode().constraints(); let constraints = self.insts[inst].opcode().constraints();
let fixed_results = constraints.fixed_results(); let fixed_results = constraints.fixed_results();
@@ -626,8 +650,7 @@ impl DataFlowGraph {
} }
// Not a fixed result, try to extract a return type from the call signature. // Not a fixed result, try to extract a return type from the call signature.
self.call_signature(inst) self.call_signature(inst).and_then(|sigref| {
.and_then(|sigref| {
self.signatures[sigref] self.signatures[sigref]
.return_types .return_types
.get(result_idx - fixed_results) .get(result_idx - fixed_results)
@@ -644,8 +667,9 @@ impl DataFlowGraph {
} else if constraints.requires_typevar_operand() { } else if constraints.requires_typevar_operand() {
// Not all instruction formats have a designated operand, but in that case // Not all instruction formats have a designated operand, but in that case
// `requires_typevar_operand()` should never be true. // `requires_typevar_operand()` should never be true.
self.value_type(self[inst].typevar_operand(&self.value_lists) self.value_type(self[inst].typevar_operand(&self.value_lists).expect(
.expect("Instruction format doesn't have a designated operand, bad opcode.")) "Instruction format doesn't have a designated operand, bad opcode.",
))
} else { } else {
self.value_type(self.first_result(inst)) self.value_type(self.first_result(inst))
} }
@@ -712,9 +736,10 @@ impl DataFlowGraph {
} else { } else {
panic!("{} must be an EBB argument", val); panic!("{} must be an EBB argument", val);
}; };
self.ebbs[ebb] self.ebbs[ebb].args.swap_remove(
.args num as usize,
.swap_remove(num as usize, &mut self.value_lists); &mut self.value_lists,
);
if let Some(last_arg_val) = self.ebbs[ebb].args.get(num as usize, &self.value_lists) { if let Some(last_arg_val) = self.ebbs[ebb].args.get(num as usize, &self.value_lists) {
// We update the position of the old last arg. // We update the position of the old last arg.
if let ValueData::Arg { num: ref mut old_num, .. } = self.values[last_arg_val] { if let ValueData::Arg { num: ref mut old_num, .. } = self.values[last_arg_val] {
@@ -734,9 +759,10 @@ impl DataFlowGraph {
} else { } else {
panic!("{} must be an EBB argument", val); panic!("{} must be an EBB argument", val);
}; };
self.ebbs[ebb] self.ebbs[ebb].args.remove(
.args num as usize,
.remove(num as usize, &mut self.value_lists); &mut self.value_lists,
);
for index in num..(self.ebb_args(ebb).len() as u16) { for index in num..(self.ebb_args(ebb).len() as u16) {
match self.values[self.ebbs[ebb] match self.values[self.ebbs[ebb]
.args .args
@@ -746,11 +772,13 @@ impl DataFlowGraph {
*num -= 1; *num -= 1;
} }
_ => { _ => {
panic!("{} must be an EBB argument", panic!(
"{} must be an EBB argument",
self.ebbs[ebb] self.ebbs[ebb]
.args .args
.get(index as usize, &self.value_lists) .get(index as usize, &self.value_lists)
.unwrap()) .unwrap()
)
} }
} }
} }

View File

@@ -218,7 +218,9 @@ mod tests {
use std::mem; use std::mem;
use packed_option::PackedOption; use packed_option::PackedOption;
// This is the whole point of `PackedOption`. // This is the whole point of `PackedOption`.
assert_eq!(mem::size_of::<Value>(), assert_eq!(
mem::size_of::<PackedOption<Value>>()); mem::size_of::<Value>(),
mem::size_of::<PackedOption<Value>>()
);
} }
} }

View File

@@ -73,10 +73,11 @@ impl Signature {
/// Wrapper type capable of displaying a `Signature` with correct register names. /// Wrapper type capable of displaying a `Signature` with correct register names.
pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>); pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>);
fn write_list(f: &mut fmt::Formatter, fn write_list(
f: &mut fmt::Formatter,
args: &[ArgumentType], args: &[ArgumentType],
regs: Option<&RegInfo>) regs: Option<&RegInfo>,
-> fmt::Result { ) -> fmt::Result {
match args.split_first() { match args.split_first() {
None => {} None => {}
Some((first, rest)) => { Some((first, rest)) => {
@@ -346,12 +347,14 @@ mod tests {
#[test] #[test]
fn argument_purpose() { fn argument_purpose() {
let all_purpose = [ArgumentPurpose::Normal, let all_purpose = [
ArgumentPurpose::Normal,
ArgumentPurpose::StructReturn, ArgumentPurpose::StructReturn,
ArgumentPurpose::Link, ArgumentPurpose::Link,
ArgumentPurpose::FramePointer, ArgumentPurpose::FramePointer,
ArgumentPurpose::CalleeSaved, ArgumentPurpose::CalleeSaved,
ArgumentPurpose::VMContext]; ArgumentPurpose::VMContext,
];
for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) { for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) {
assert_eq!(e.to_string(), n); assert_eq!(e.to_string(), n);
assert_eq!(Ok(e), n.parse()); assert_eq!(Ok(e), n.parse());
@@ -373,8 +376,9 @@ mod tests {
assert_eq!(sig.to_string(), "(i32) spiderwasm"); assert_eq!(sig.to_string(), "(i32) spiderwasm");
sig.return_types.push(ArgumentType::new(F32)); sig.return_types.push(ArgumentType::new(F32));
assert_eq!(sig.to_string(), "(i32) -> f32 spiderwasm"); assert_eq!(sig.to_string(), "(i32) -> f32 spiderwasm");
sig.argument_types sig.argument_types.push(
.push(ArgumentType::new(I32.by(4).unwrap())); ArgumentType::new(I32.by(4).unwrap()),
);
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 spiderwasm"); assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 spiderwasm");
sig.return_types.push(ArgumentType::new(B8)); sig.return_types.push(ArgumentType::new(B8));
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 spiderwasm"); assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 spiderwasm");
@@ -391,7 +395,9 @@ mod tests {
assert_eq!(sig.argument_bytes, Some(28)); assert_eq!(sig.argument_bytes, Some(28));
// Writing ABI-annotated signatures. // Writing ABI-annotated signatures.
assert_eq!(sig.to_string(), assert_eq!(
"(i32 [24], i32x4 [8]) -> f32, b8 spiderwasm"); sig.to_string(),
"(i32 [24], i32x4 [8]) -> f32, b8 spiderwasm"
);
} }
} }

View File

@@ -30,7 +30,8 @@ impl FunctionName {
/// assert_eq!(name.to_string(), "#0a0908"); /// assert_eq!(name.to_string(), "#0a0908");
/// ``` /// ```
pub fn new<T>(v: T) -> FunctionName pub fn new<T>(v: T) -> FunctionName
where T: Into<Vec<u8>> where
T: Into<Vec<u8>>,
{ {
let vec = v.into(); let vec = v.into();
if vec.len() <= NAME_LENGTH_THRESHOLD { if vec.len() <= NAME_LENGTH_THRESHOLD {
@@ -114,11 +115,17 @@ mod tests {
assert_eq!(FunctionName::new("x").to_string(), "%x"); assert_eq!(FunctionName::new("x").to_string(), "%x");
assert_eq!(FunctionName::new("x_1").to_string(), "%x_1"); assert_eq!(FunctionName::new("x_1").to_string(), "%x_1");
assert_eq!(FunctionName::new(" ").to_string(), "#20"); assert_eq!(FunctionName::new(" ").to_string(), "#20");
assert_eq!(FunctionName::new("кретон").to_string(), assert_eq!(
"#d0bad180d0b5d182d0bed0bd"); FunctionName::new("кретон").to_string(),
assert_eq!(FunctionName::new("印花棉布").to_string(), "#d0bad180d0b5d182d0bed0bd"
"#e58db0e88ab1e6a389e5b883"); );
assert_eq!(FunctionName::new(vec![0, 1, 2, 3, 4, 5]).to_string(), assert_eq!(
"#000102030405"); FunctionName::new("印花棉布").to_string(),
"#e58db0e88ab1e6a389e5b883"
);
assert_eq!(
FunctionName::new(vec![0, 1, 2, 3, 4, 5]).to_string(),
"#000102030405"
);
} }
} }

View File

@@ -662,7 +662,8 @@ mod tests {
// Verify that `text` can be parsed as a `T` into a value that displays as `want`. // Verify that `text` can be parsed as a `T` into a value that displays as `want`.
fn parse_ok<T: FromStr + Display>(text: &str, want: &str) fn parse_ok<T: FromStr + Display>(text: &str, want: &str)
where <T as FromStr>::Err: Display where
<T as FromStr>::Err: Display,
{ {
match text.parse::<T>() { match text.parse::<T>() {
Err(s) => panic!("\"{}\".parse() error: {}", text, s), Err(s) => panic!("\"{}\".parse() error: {}", text, s),
@@ -672,7 +673,8 @@ mod tests {
// Verify that `text` fails to parse as `T` with the error `msg`. // Verify that `text` fails to parse as `T` with the error `msg`.
fn parse_err<T: FromStr + Display>(text: &str, msg: &str) fn parse_err<T: FromStr + Display>(text: &str, msg: &str)
where <T as FromStr>::Err: Display where
<T as FromStr>::Err: Display,
{ {
match text.parse::<T>() { match text.parse::<T>() {
Err(s) => assert_eq!(s.to_string(), msg), Err(s) => assert_eq!(s.to_string(), msg),
@@ -781,18 +783,26 @@ mod tests {
assert_eq!(Ieee32::with_float(1.0).to_string(), "0x1.000000p0"); assert_eq!(Ieee32::with_float(1.0).to_string(), "0x1.000000p0");
assert_eq!(Ieee32::with_float(1.5).to_string(), "0x1.800000p0"); assert_eq!(Ieee32::with_float(1.5).to_string(), "0x1.800000p0");
assert_eq!(Ieee32::with_float(0.5).to_string(), "0x1.000000p-1"); assert_eq!(Ieee32::with_float(0.5).to_string(), "0x1.000000p-1");
assert_eq!(Ieee32::with_float(f32::EPSILON).to_string(), assert_eq!(
"0x1.000000p-23"); Ieee32::with_float(f32::EPSILON).to_string(),
"0x1.000000p-23"
);
assert_eq!(Ieee32::with_float(f32::MIN).to_string(), "-0x1.fffffep127"); assert_eq!(Ieee32::with_float(f32::MIN).to_string(), "-0x1.fffffep127");
assert_eq!(Ieee32::with_float(f32::MAX).to_string(), "0x1.fffffep127"); assert_eq!(Ieee32::with_float(f32::MAX).to_string(), "0x1.fffffep127");
// Smallest positive normal number. // Smallest positive normal number.
assert_eq!(Ieee32::with_float(f32::MIN_POSITIVE).to_string(), assert_eq!(
"0x1.000000p-126"); Ieee32::with_float(f32::MIN_POSITIVE).to_string(),
"0x1.000000p-126"
);
// Subnormals. // Subnormals.
assert_eq!(Ieee32::with_float(f32::MIN_POSITIVE / 2.0).to_string(), assert_eq!(
"0x0.800000p-126"); Ieee32::with_float(f32::MIN_POSITIVE / 2.0).to_string(),
assert_eq!(Ieee32::with_float(f32::MIN_POSITIVE * f32::EPSILON).to_string(), "0x0.800000p-126"
"0x0.000002p-126"); );
assert_eq!(
Ieee32::with_float(f32::MIN_POSITIVE * f32::EPSILON).to_string(),
"0x0.000002p-126"
);
assert_eq!(Ieee32::with_float(f32::INFINITY).to_string(), "+Inf"); assert_eq!(Ieee32::with_float(f32::INFINITY).to_string(), "+Inf");
assert_eq!(Ieee32::with_float(f32::NEG_INFINITY).to_string(), "-Inf"); assert_eq!(Ieee32::with_float(f32::NEG_INFINITY).to_string(), "-Inf");
assert_eq!(Ieee32::with_float(f32::NAN).to_string(), "+NaN"); assert_eq!(Ieee32::with_float(f32::NAN).to_string(), "+NaN");
@@ -883,32 +893,48 @@ mod tests {
assert_eq!(Ieee64::with_float(1.0).to_string(), "0x1.0000000000000p0"); assert_eq!(Ieee64::with_float(1.0).to_string(), "0x1.0000000000000p0");
assert_eq!(Ieee64::with_float(1.5).to_string(), "0x1.8000000000000p0"); assert_eq!(Ieee64::with_float(1.5).to_string(), "0x1.8000000000000p0");
assert_eq!(Ieee64::with_float(0.5).to_string(), "0x1.0000000000000p-1"); assert_eq!(Ieee64::with_float(0.5).to_string(), "0x1.0000000000000p-1");
assert_eq!(Ieee64::with_float(f64::EPSILON).to_string(), assert_eq!(
"0x1.0000000000000p-52"); Ieee64::with_float(f64::EPSILON).to_string(),
assert_eq!(Ieee64::with_float(f64::MIN).to_string(), "0x1.0000000000000p-52"
"-0x1.fffffffffffffp1023"); );
assert_eq!(Ieee64::with_float(f64::MAX).to_string(), assert_eq!(
"0x1.fffffffffffffp1023"); Ieee64::with_float(f64::MIN).to_string(),
"-0x1.fffffffffffffp1023"
);
assert_eq!(
Ieee64::with_float(f64::MAX).to_string(),
"0x1.fffffffffffffp1023"
);
// Smallest positive normal number. // Smallest positive normal number.
assert_eq!(Ieee64::with_float(f64::MIN_POSITIVE).to_string(), assert_eq!(
"0x1.0000000000000p-1022"); Ieee64::with_float(f64::MIN_POSITIVE).to_string(),
"0x1.0000000000000p-1022"
);
// Subnormals. // Subnormals.
assert_eq!(Ieee64::with_float(f64::MIN_POSITIVE / 2.0).to_string(), assert_eq!(
"0x0.8000000000000p-1022"); Ieee64::with_float(f64::MIN_POSITIVE / 2.0).to_string(),
assert_eq!(Ieee64::with_float(f64::MIN_POSITIVE * f64::EPSILON).to_string(), "0x0.8000000000000p-1022"
"0x0.0000000000001p-1022"); );
assert_eq!(
Ieee64::with_float(f64::MIN_POSITIVE * f64::EPSILON).to_string(),
"0x0.0000000000001p-1022"
);
assert_eq!(Ieee64::with_float(f64::INFINITY).to_string(), "+Inf"); assert_eq!(Ieee64::with_float(f64::INFINITY).to_string(), "+Inf");
assert_eq!(Ieee64::with_float(f64::NEG_INFINITY).to_string(), "-Inf"); assert_eq!(Ieee64::with_float(f64::NEG_INFINITY).to_string(), "-Inf");
assert_eq!(Ieee64::with_float(f64::NAN).to_string(), "+NaN"); assert_eq!(Ieee64::with_float(f64::NAN).to_string(), "+NaN");
assert_eq!(Ieee64::with_float(-f64::NAN).to_string(), "-NaN"); assert_eq!(Ieee64::with_float(-f64::NAN).to_string(), "-NaN");
// Construct some qNaNs with payloads. // Construct some qNaNs with payloads.
assert_eq!(Ieee64(0x7ff8000000000001).to_string(), "+NaN:0x1"); assert_eq!(Ieee64(0x7ff8000000000001).to_string(), "+NaN:0x1");
assert_eq!(Ieee64(0x7ffc000000000001).to_string(), assert_eq!(
"+NaN:0x4000000000001"); Ieee64(0x7ffc000000000001).to_string(),
"+NaN:0x4000000000001"
);
// Signaling NaNs. // Signaling NaNs.
assert_eq!(Ieee64(0x7ff0000000000001).to_string(), "+sNaN:0x1"); assert_eq!(Ieee64(0x7ff0000000000001).to_string(), "+sNaN:0x1");
assert_eq!(Ieee64(0x7ff4000000000001).to_string(), assert_eq!(
"+sNaN:0x4000000000001"); Ieee64(0x7ff4000000000001).to_string(),
"+sNaN:0x4000000000001"
);
} }
#[test] #[test]

View File

@@ -481,7 +481,8 @@ impl OpcodeConstraints {
pub fn result_type(self, n: usize, ctrl_type: Type) -> Type { pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
assert!(n < self.fixed_results(), "Invalid result index"); assert!(n < self.fixed_results(), "Invalid result index");
if let ResolvedConstraint::Bound(t) = if let ResolvedConstraint::Bound(t) =
OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) { OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type)
{
t t
} else { } else {
panic!("Result constraints can't be free"); panic!("Result constraints can't be free");
@@ -494,8 +495,10 @@ impl OpcodeConstraints {
/// Unlike results, it is possible for some input values to vary freely within a specific /// Unlike results, it is possible for some input values to vary freely within a specific
/// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant. /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant.
pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint { pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
assert!(n < self.fixed_value_arguments(), assert!(
"Invalid value argument index"); n < self.fixed_value_arguments(),
"Invalid value argument index"
);
let offset = self.constraint_offset() + self.fixed_results(); let offset = self.constraint_offset() + self.fixed_results();
OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type) OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
} }
@@ -613,14 +616,14 @@ impl OperandConstraint {
AsBool => Bound(ctrl_type.as_bool()), AsBool => Bound(ctrl_type.as_bool()),
HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")), HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
DoubleWidth => { DoubleWidth => {
Bound(ctrl_type Bound(ctrl_type.double_width().expect(
.double_width() "invalid type for double_width",
.expect("invalid type for double_width")) ))
} }
HalfVector => { HalfVector => {
Bound(ctrl_type Bound(ctrl_type.half_vector().expect(
.half_vector() "invalid type for half_vector",
.expect("invalid type for half_vector")) ))
} }
DoubleVector => Bound(ctrl_type.by(2).expect("invalid type for double_vector")), DoubleVector => Bound(ctrl_type.by(2).expect("invalid type for double_vector")),
} }
@@ -688,10 +691,14 @@ mod tests {
assert_eq!(a.fixed_value_arguments(), 2); assert_eq!(a.fixed_value_arguments(), 2);
assert_eq!(a.result_type(0, types::I32), types::I32); assert_eq!(a.result_type(0, types::I32), types::I32);
assert_eq!(a.result_type(0, types::I8), types::I8); assert_eq!(a.result_type(0, types::I8), types::I8);
assert_eq!(a.value_argument_constraint(0, types::I32), assert_eq!(
ResolvedConstraint::Bound(types::I32)); a.value_argument_constraint(0, types::I32),
assert_eq!(a.value_argument_constraint(1, types::I32), ResolvedConstraint::Bound(types::I32)
ResolvedConstraint::Bound(types::I32)); );
assert_eq!(
a.value_argument_constraint(1, types::I32),
ResolvedConstraint::Bound(types::I32)
);
let b = Opcode::Bitcast.constraints(); let b = Opcode::Bitcast.constraints();
assert!(!b.use_typevar_operand()); assert!(!b.use_typevar_operand());

View File

@@ -71,9 +71,9 @@ impl JumpTableData {
/// Checks if any of the entries branch to `ebb`. /// Checks if any of the entries branch to `ebb`.
pub fn branches_to(&self, ebb: Ebb) -> bool { pub fn branches_to(&self, ebb: Ebb) -> bool {
self.table self.table.iter().any(|target_ebb| {
.iter() target_ebb.expand() == Some(ebb)
.any(|target_ebb| target_ebb.expand() == Some(ebb)) })
} }
/// Access the whole table as a mutable slice. /// Access the whole table as a mutable slice.
@@ -148,8 +148,10 @@ mod tests {
jt.set_entry(0, e2); jt.set_entry(0, e2);
jt.set_entry(10, e1); jt.set_entry(10, e1);
assert_eq!(jt.to_string(), assert_eq!(
"jump_table ebb2, 0, 0, 0, 0, 0, 0, 0, 0, 0, ebb1"); jt.to_string(),
"jump_table ebb2, 0, 0, 0, 0, 0, 0, 0, 0, 0, ebb1"
);
let v: Vec<(usize, Ebb)> = jt.entries().collect(); let v: Vec<(usize, Ebb)> = jt.entries().collect();
assert_eq!(v, [(0, e2), (10, e1)]); assert_eq!(v, [(0, e2), (10, e1)]);

View File

@@ -96,8 +96,9 @@ fn test_midpoint() {
impl ProgramOrder for Layout { impl ProgramOrder for Layout {
fn cmp<A, B>(&self, a: A, b: B) -> cmp::Ordering fn cmp<A, B>(&self, a: A, b: B) -> cmp::Ordering
where A: Into<ExpandedProgramPoint>, where
B: Into<ExpandedProgramPoint> A: Into<ExpandedProgramPoint>,
B: Into<ExpandedProgramPoint>,
{ {
let a_seq = self.seq(a); let a_seq = self.seq(a);
let b_seq = self.seq(b); let b_seq = self.seq(b);
@@ -166,8 +167,9 @@ impl Layout {
/// Assign a valid sequence number to `inst` such that the numbers are still monotonic. This may /// Assign a valid sequence number to `inst` such that the numbers are still monotonic. This may
/// require renumbering. /// require renumbering.
fn assign_inst_seq(&mut self, inst: Inst) { fn assign_inst_seq(&mut self, inst: Inst) {
let ebb = self.inst_ebb(inst) let ebb = self.inst_ebb(inst).expect(
.expect("inst must be inserted before assigning an seq"); "inst must be inserted before assigning an seq",
);
// Get the sequence number immediately before `inst`. // Get the sequence number immediately before `inst`.
let prev_seq = match self.insts[inst].prev.expand() { let prev_seq = match self.insts[inst].prev.expand() {
@@ -283,8 +285,10 @@ impl Layout {
/// Insert `ebb` as the last EBB in the layout. /// Insert `ebb` as the last EBB in the layout.
pub fn append_ebb(&mut self, ebb: Ebb) { pub fn append_ebb(&mut self, ebb: Ebb) {
assert!(!self.is_ebb_inserted(ebb), assert!(
"Cannot append EBB that is already in the layout"); !self.is_ebb_inserted(ebb),
"Cannot append EBB that is already in the layout"
);
{ {
let node = &mut self.ebbs[ebb]; let node = &mut self.ebbs[ebb];
assert!(node.first_inst.is_none() && node.last_inst.is_none()); assert!(node.first_inst.is_none() && node.last_inst.is_none());
@@ -302,10 +306,14 @@ impl Layout {
/// Insert `ebb` in the layout before the existing EBB `before`. /// Insert `ebb` in the layout before the existing EBB `before`.
pub fn insert_ebb(&mut self, ebb: Ebb, before: Ebb) { pub fn insert_ebb(&mut self, ebb: Ebb, before: Ebb) {
assert!(!self.is_ebb_inserted(ebb), assert!(
"Cannot insert EBB that is already in the layout"); !self.is_ebb_inserted(ebb),
assert!(self.is_ebb_inserted(before), "Cannot insert EBB that is already in the layout"
"EBB Insertion point not in the layout"); );
assert!(
self.is_ebb_inserted(before),
"EBB Insertion point not in the layout"
);
let after = self.ebbs[before].prev; let after = self.ebbs[before].prev;
{ {
let node = &mut self.ebbs[ebb]; let node = &mut self.ebbs[ebb];
@@ -322,10 +330,14 @@ impl Layout {
/// Insert `ebb` in the layout *after* the existing EBB `after`. /// Insert `ebb` in the layout *after* the existing EBB `after`.
pub fn insert_ebb_after(&mut self, ebb: Ebb, after: Ebb) { pub fn insert_ebb_after(&mut self, ebb: Ebb, after: Ebb) {
assert!(!self.is_ebb_inserted(ebb), assert!(
"Cannot insert EBB that is already in the layout"); !self.is_ebb_inserted(ebb),
assert!(self.is_ebb_inserted(after), "Cannot insert EBB that is already in the layout"
"EBB Insertion point not in the layout"); );
assert!(
self.is_ebb_inserted(after),
"EBB Insertion point not in the layout"
);
let before = self.ebbs[after].next; let before = self.ebbs[after].next;
{ {
let node = &mut self.ebbs[ebb]; let node = &mut self.ebbs[ebb];
@@ -411,7 +423,8 @@ impl Layout {
/// Get the EBB containing the program point `pp`. Panic if `pp` is not in the layout. /// Get the EBB containing the program point `pp`. Panic if `pp` is not in the layout.
pub fn pp_ebb<PP>(&self, pp: PP) -> Ebb pub fn pp_ebb<PP>(&self, pp: PP) -> Ebb
where PP: Into<ExpandedProgramPoint> where
PP: Into<ExpandedProgramPoint>,
{ {
match pp.into() { match pp.into() {
ExpandedProgramPoint::Ebb(ebb) => ebb, ExpandedProgramPoint::Ebb(ebb) => ebb,
@@ -424,8 +437,10 @@ impl Layout {
/// Append `inst` to the end of `ebb`. /// Append `inst` to the end of `ebb`.
pub fn append_inst(&mut self, inst: Inst, ebb: Ebb) { pub fn append_inst(&mut self, inst: Inst, ebb: Ebb) {
assert_eq!(self.inst_ebb(inst), None); assert_eq!(self.inst_ebb(inst), None);
assert!(self.is_ebb_inserted(ebb), assert!(
"Cannot append instructions to EBB not in layout"); self.is_ebb_inserted(ebb),
"Cannot append instructions to EBB not in layout"
);
{ {
let ebb_node = &mut self.ebbs[ebb]; let ebb_node = &mut self.ebbs[ebb];
{ {
@@ -457,8 +472,9 @@ impl Layout {
/// Insert `inst` before the instruction `before` in the same EBB. /// Insert `inst` before the instruction `before` in the same EBB.
pub fn insert_inst(&mut self, inst: Inst, before: Inst) { pub fn insert_inst(&mut self, inst: Inst, before: Inst) {
assert_eq!(self.inst_ebb(inst), None); assert_eq!(self.inst_ebb(inst), None);
let ebb = self.inst_ebb(before) let ebb = self.inst_ebb(before).expect(
.expect("Instruction before insertion point not in the layout"); "Instruction before insertion point not in the layout",
);
let after = self.insts[before].prev; let after = self.insts[before].prev;
{ {
let inst_node = &mut self.insts[inst]; let inst_node = &mut self.insts[inst];
@@ -531,8 +547,9 @@ impl Layout {
/// i4 /// i4
/// ``` /// ```
pub fn split_ebb(&mut self, new_ebb: Ebb, before: Inst) { pub fn split_ebb(&mut self, new_ebb: Ebb, before: Inst) {
let old_ebb = self.inst_ebb(before) let old_ebb = self.inst_ebb(before).expect(
.expect("The `before` instruction must be in the layout"); "The `before` instruction must be in the layout",
);
assert!(!self.is_ebb_inserted(new_ebb)); assert!(!self.is_ebb_inserted(new_ebb));
// Insert new_ebb after old_ebb. // Insert new_ebb after old_ebb.
@@ -683,7 +700,8 @@ pub trait CursorBase {
/// } /// }
/// ``` /// ```
fn at_inst(mut self, inst: Inst) -> Self fn at_inst(mut self, inst: Inst) -> Self
where Self: Sized where
Self: Sized,
{ {
self.goto_inst(inst); self.goto_inst(inst);
self self
@@ -703,7 +721,8 @@ pub trait CursorBase {
/// } /// }
/// ``` /// ```
fn at_first_inst(mut self, ebb: Ebb) -> Self fn at_first_inst(mut self, ebb: Ebb) -> Self
where Self: Sized where
Self: Sized,
{ {
self.goto_first_inst(ebb); self.goto_first_inst(ebb);
self self
@@ -872,9 +891,9 @@ pub trait CursorBase {
self.set_position(At(next)); self.set_position(At(next));
Some(next) Some(next)
} else { } else {
let pos = After(self.layout() let pos = After(self.layout().inst_ebb(inst).expect(
.inst_ebb(inst) "current instruction removed?",
.expect("current instruction removed?")); ));
self.set_position(pos); self.set_position(pos);
None None
} }
@@ -925,9 +944,9 @@ pub trait CursorBase {
self.set_position(At(prev)); self.set_position(At(prev));
Some(prev) Some(prev)
} else { } else {
let pos = Before(self.layout() let pos = Before(self.layout().inst_ebb(inst).expect(
.inst_ebb(inst) "current instruction removed?",
.expect("current instruction removed?")); ));
self.set_position(pos); self.set_position(pos);
None None
} }
@@ -1057,9 +1076,10 @@ pub struct LayoutCursorInserter<'c, 'fc: 'c, 'fd> {
impl<'c, 'fc: 'c, 'fd> LayoutCursorInserter<'c, 'fc, 'fd> { impl<'c, 'fc: 'c, 'fd> LayoutCursorInserter<'c, 'fc, 'fd> {
/// Create a new inserter. Don't use this, use `dfg.ins(pos)`. /// Create a new inserter. Don't use this, use `dfg.ins(pos)`.
pub fn new(pos: &'c mut Cursor<'fc>, pub fn new(
dfg: &'fd mut DataFlowGraph) pos: &'c mut Cursor<'fc>,
-> LayoutCursorInserter<'c, 'fc, 'fd> { dfg: &'fd mut DataFlowGraph,
) -> LayoutCursorInserter<'c, 'fc, 'fd> {
LayoutCursorInserter { pos, dfg } LayoutCursorInserter { pos, dfg }
} }
} }

View File

@@ -123,7 +123,8 @@ pub trait ProgramOrder {
/// directly. Depending on the implementation, there is a good chance performance will be /// directly. Depending on the implementation, there is a good chance performance will be
/// improved for those cases where the type of either argument is known statically. /// improved for those cases where the type of either argument is known statically.
fn cmp<A, B>(&self, a: A, b: B) -> cmp::Ordering fn cmp<A, B>(&self, a: A, b: B) -> cmp::Ordering
where A: Into<ExpandedProgramPoint>, where
A: Into<ExpandedProgramPoint>,
B: Into<ExpandedProgramPoint>; B: Into<ExpandedProgramPoint>;
/// Is the range from `inst` to `ebb` just the gap between consecutive EBBs? /// Is the range from `inst` to `ebb` just the gap between consecutive EBBs?

View File

@@ -228,9 +228,9 @@ impl StackSlots {
let size = ty.bytes(); let size = ty.bytes();
// Look for an existing outgoing stack slot with the same offset and size. // Look for an existing outgoing stack slot with the same offset and size.
let inspos = match self.outgoing let inspos = match self.outgoing.binary_search_by_key(&(offset, size), |&ss| {
.binary_search_by_key(&(offset, size), (self[ss].offset, self[ss].size)
|&ss| (self[ss].offset, self[ss].size)) { }) {
Ok(idx) => return self.outgoing[idx], Ok(idx) => return self.outgoing[idx],
Err(idx) => idx, Err(idx) => idx,
}; };
@@ -255,10 +255,14 @@ mod tests {
fn stack_slot() { fn stack_slot() {
let mut func = Function::new(); let mut func = Function::new();
let ss0 = func.stack_slots let ss0 = func.stack_slots.push(StackSlotData::new(
.push(StackSlotData::new(StackSlotKind::IncomingArg, 4)); StackSlotKind::IncomingArg,
let ss1 = func.stack_slots 4,
.push(StackSlotData::new(StackSlotKind::SpillSlot, 8)); ));
let ss1 = func.stack_slots.push(StackSlotData::new(
StackSlotKind::SpillSlot,
8,
));
assert_eq!(ss0.to_string(), "ss0"); assert_eq!(ss0.to_string(), "ss0");
assert_eq!(ss1.to_string(), "ss1"); assert_eq!(ss1.to_string(), "ss1");

View File

@@ -7,9 +7,11 @@ use settings as shared_settings;
use super::registers::{S, D, Q, GPR}; use super::registers::{S, D, Q, GPR};
/// Legalize `sig`. /// Legalize `sig`.
pub fn legalize_signature(_sig: &mut ir::Signature, pub fn legalize_signature(
_sig: &mut ir::Signature,
_flags: &shared_settings::Flags, _flags: &shared_settings::Flags,
_current: bool) { _current: bool,
) {
unimplemented!() unimplemented!()
} }

View File

@@ -29,9 +29,10 @@ pub fn isa_builder() -> IsaBuilder {
} }
} }
fn isa_constructor(shared_flags: shared_settings::Flags, fn isa_constructor(
builder: &shared_settings::Builder) shared_flags: shared_settings::Flags,
-> Box<TargetIsa> { builder: &shared_settings::Builder,
) -> Box<TargetIsa> {
let level1 = if shared_flags.is_compressed() { let level1 = if shared_flags.is_compressed() {
&enc_tables::LEVEL1_T32[..] &enc_tables::LEVEL1_T32[..]
} else { } else {
@@ -61,12 +62,14 @@ impl TargetIsa for Isa {
enc_tables::INFO.clone() enc_tables::INFO.clone()
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(
&'a self,
dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type,
-> Encodings<'a> { ) -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(
ctrl_typevar,
inst, inst,
dfg, dfg,
self.cpumode, self.cpumode,
@@ -75,7 +78,8 @@ impl TargetIsa for Isa {
&enc_tables::LEGALIZE_ACTIONS[..], &enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..], &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..], &enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view()) self.isa_flags.predicate_view(),
)
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
@@ -90,11 +94,13 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func) abi::allocatable_registers(func)
} }
fn emit_inst(&self, fn emit_inst(
&self,
func: &ir::Function, func: &ir::Function,
inst: ir::Inst, inst: ir::Inst,
divert: &mut regalloc::RegDiversions, divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) { sink: &mut CodeSink,
) {
binemit::emit_inst(func, inst, divert, sink) binemit::emit_inst(func, inst, divert, sink)
} }

View File

@@ -7,9 +7,11 @@ use settings as shared_settings;
use super::registers::{GPR, FPR}; use super::registers::{GPR, FPR};
/// Legalize `sig`. /// Legalize `sig`.
pub fn legalize_signature(_sig: &mut ir::Signature, pub fn legalize_signature(
_sig: &mut ir::Signature,
_flags: &shared_settings::Flags, _flags: &shared_settings::Flags,
_current: bool) { _current: bool,
) {
unimplemented!() unimplemented!()
} }

View File

@@ -28,9 +28,10 @@ pub fn isa_builder() -> IsaBuilder {
} }
} }
fn isa_constructor(shared_flags: shared_settings::Flags, fn isa_constructor(
builder: &shared_settings::Builder) shared_flags: shared_settings::Flags,
-> Box<TargetIsa> { builder: &shared_settings::Builder,
) -> Box<TargetIsa> {
Box::new(Isa { Box::new(Isa {
isa_flags: settings::Flags::new(&shared_flags, builder), isa_flags: settings::Flags::new(&shared_flags, builder),
shared_flags, shared_flags,
@@ -54,12 +55,14 @@ impl TargetIsa for Isa {
enc_tables::INFO.clone() enc_tables::INFO.clone()
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(
&'a self,
dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type,
-> Encodings<'a> { ) -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(
ctrl_typevar,
inst, inst,
dfg, dfg,
&enc_tables::LEVEL1_A64[..], &enc_tables::LEVEL1_A64[..],
@@ -68,7 +71,8 @@ impl TargetIsa for Isa {
&enc_tables::LEGALIZE_ACTIONS[..], &enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..], &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..], &enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view()) self.isa_flags.predicate_view(),
)
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
@@ -83,11 +87,13 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func) abi::allocatable_registers(func)
} }
fn emit_inst(&self, fn emit_inst(
&self,
func: &ir::Function, func: &ir::Function,
inst: ir::Inst, inst: ir::Inst,
divert: &mut regalloc::RegDiversions, divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) { sink: &mut CodeSink,
) {
binemit::emit_inst(func, inst, divert, sink) binemit::emit_inst(func, inst, divert, sink)
} }

View File

@@ -103,7 +103,8 @@ impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
/// list. /// list.
/// ///
/// Returns an iterator that produces legal encodings for `inst`. /// Returns an iterator that produces legal encodings for `inst`.
pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type, pub fn lookup_enclist<'a, OffT1, OffT2>(
ctrl_typevar: Type,
inst: &'a InstructionData, inst: &'a InstructionData,
dfg: &'a DataFlowGraph, dfg: &'a DataFlowGraph,
level1_table: &'static [Level1Entry<OffT1>], level1_table: &'static [Level1Entry<OffT1>],
@@ -112,10 +113,11 @@ pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
legalize_actions: &'static [Legalize], legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate], recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate], inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>) isa_preds: PredicateView<'a>,
-> Encodings<'a> ) -> Encodings<'a>
where OffT1: Into<u32> + Copy, where
OffT2: Into<u32> + Copy OffT1: Into<u32> + Copy,
OffT2: Into<u32> + Copy,
{ {
let (offset, legalize) = match probe(level1_table, ctrl_typevar, ctrl_typevar.index()) { let (offset, legalize) = match probe(level1_table, ctrl_typevar, ctrl_typevar.index()) {
Err(l1idx) => { Err(l1idx) => {
@@ -144,7 +146,8 @@ pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
// Now we have an offset into `enclist` that is `!0` when no encoding list could be found. // Now we have an offset into `enclist` that is `!0` when no encoding list could be found.
// The default legalization code is always valid. // The default legalization code is always valid.
Encodings::new(offset, Encodings::new(
offset,
legalize, legalize,
inst, inst,
dfg, dfg,
@@ -152,7 +155,8 @@ pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
legalize_actions, legalize_actions,
recipe_preds, recipe_preds,
inst_preds, inst_preds,
isa_preds) isa_preds,
)
} }
/// Encoding list entry. /// Encoding list entry.
@@ -187,7 +191,8 @@ impl<'a> Encodings<'a> {
/// This iterator provides search for encodings that applies to the given instruction. The /// This iterator provides search for encodings that applies to the given instruction. The
/// encoding lists are laid out such that first call to `next` returns valid entry in the list /// encoding lists are laid out such that first call to `next` returns valid entry in the list
/// or `None`. /// or `None`.
pub fn new(offset: usize, pub fn new(
offset: usize,
legalize: LegalizeCode, legalize: LegalizeCode,
inst: &'a InstructionData, inst: &'a InstructionData,
dfg: &'a DataFlowGraph, dfg: &'a DataFlowGraph,
@@ -195,8 +200,8 @@ impl<'a> Encodings<'a> {
legalize_actions: &'static [Legalize], legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate], recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate], inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>) isa_preds: PredicateView<'a>,
-> Self { ) -> Self {
Encodings { Encodings {
offset, offset,
inst, inst,

View File

@@ -66,10 +66,12 @@ pub struct DisplayEncoding {
impl fmt::Display for DisplayEncoding { impl fmt::Display for DisplayEncoding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.encoding.is_legal() { if self.encoding.is_legal() {
write!(f, write!(
f,
"{}#{:02x}", "{}#{:02x}",
self.recipe_names[self.encoding.recipe()], self.recipe_names[self.encoding.recipe()],
self.encoding.bits) self.encoding.bits
)
} else { } else {
write!(f, "-") write!(f, "-")
} }

View File

@@ -87,9 +87,7 @@ impl ArgAssigner for Args {
} }
/// Legalize `sig`. /// Legalize `sig`.
pub fn legalize_signature(sig: &mut ir::Signature, pub fn legalize_signature(sig: &mut ir::Signature, flags: &shared_settings::Flags, _current: bool) {
flags: &shared_settings::Flags,
_current: bool) {
let bits = if flags.is_64bit() { 64 } else { 32 }; let bits = if flags.is_64bit() { 64 } else { 32 };
let mut args = Args::new(bits, &ARG_GPRS, 8); let mut args = Args::new(bits, &ARG_GPRS, 8);
@@ -105,9 +103,10 @@ pub fn regclass_for_abi_type(ty: ir::Type) -> RegClass {
} }
/// Get the set of allocatable registers for `func`. /// Get the set of allocatable registers for `func`.
pub fn allocatable_registers(_func: &ir::Function, pub fn allocatable_registers(
flags: &shared_settings::Flags) _func: &ir::Function,
-> AllocatableSet { flags: &shared_settings::Flags,
) -> AllocatableSet {
let mut regs = AllocatableSet::new(); let mut regs = AllocatableSet::new();
regs.take(GPR, RU::rsp as RegUnit); regs.take(GPR, RU::rsp as RegUnit);
regs.take(GPR, RU::rbp as RegUnit); regs.take(GPR, RU::rbp as RegUnit);

View File

@@ -29,9 +29,10 @@ pub fn isa_builder() -> IsaBuilder {
} }
} }
fn isa_constructor(shared_flags: shared_settings::Flags, fn isa_constructor(
builder: &shared_settings::Builder) shared_flags: shared_settings::Flags,
-> Box<TargetIsa> { builder: &shared_settings::Builder,
) -> Box<TargetIsa> {
let level1 = if shared_flags.is_64bit() { let level1 = if shared_flags.is_64bit() {
&enc_tables::LEVEL1_I64[..] &enc_tables::LEVEL1_I64[..]
} else { } else {
@@ -61,12 +62,14 @@ impl TargetIsa for Isa {
enc_tables::INFO.clone() enc_tables::INFO.clone()
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(
&'a self,
dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type,
-> Encodings<'a> { ) -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(
ctrl_typevar,
inst, inst,
dfg, dfg,
self.cpumode, self.cpumode,
@@ -75,7 +78,8 @@ impl TargetIsa for Isa {
&enc_tables::LEGALIZE_ACTIONS[..], &enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..], &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..], &enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view()) self.isa_flags.predicate_view(),
)
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
@@ -90,11 +94,13 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func, &self.shared_flags) abi::allocatable_registers(func, &self.shared_flags)
} }
fn emit_inst(&self, fn emit_inst(
&self,
func: &ir::Function, func: &ir::Function,
inst: ir::Inst, inst: ir::Inst,
divert: &mut regalloc::RegDiversions, divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) { sink: &mut CodeSink,
) {
binemit::emit_inst(func, inst, divert, sink) binemit::emit_inst(func, inst, divert, sink)
} }

View File

@@ -155,11 +155,12 @@ pub trait TargetIsa {
fn register_info(&self) -> RegInfo; fn register_info(&self) -> RegInfo;
/// Returns an iterartor over legal encodings for the instruction. /// Returns an iterartor over legal encodings for the instruction.
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(
&'a self,
dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type,
-> Encodings<'a>; ) -> Encodings<'a>;
/// Encode an instruction after determining it is legal. /// Encode an instruction after determining it is legal.
/// ///
@@ -167,11 +168,12 @@ pub trait TargetIsa {
/// Otherwise, return `Legalize` action. /// Otherwise, return `Legalize` action.
/// ///
/// This is also the main entry point for determining if an instruction is legal. /// This is also the main entry point for determining if an instruction is legal.
fn encode(&self, fn encode(
&self,
dfg: &ir::DataFlowGraph, dfg: &ir::DataFlowGraph,
inst: &ir::InstructionData, inst: &ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type,
-> Result<Encoding, Legalize> { ) -> Result<Encoding, Legalize> {
let mut iter = self.legal_encodings(dfg, inst, ctrl_typevar); let mut iter = self.legal_encodings(dfg, inst, ctrl_typevar);
iter.next().ok_or_else(|| iter.legalize().into()) iter.next().ok_or_else(|| iter.legalize().into())
} }
@@ -244,11 +246,13 @@ pub trait TargetIsa {
/// ///
/// Note that this will call `put*` methods on the trait object via its vtable which is not the /// Note that this will call `put*` methods on the trait object via its vtable which is not the
/// fastest way of emitting code. /// fastest way of emitting code.
fn emit_inst(&self, fn emit_inst(
&self,
func: &ir::Function, func: &ir::Function,
inst: ir::Inst, inst: ir::Inst,
divert: &mut regalloc::RegDiversions, divert: &mut regalloc::RegDiversions,
sink: &mut binemit::CodeSink); sink: &mut binemit::CodeSink,
);
/// Emit a whole function into memory. /// Emit a whole function into memory.
/// ///

View File

@@ -86,8 +86,7 @@ impl RegBank {
None None
} }
} }
} }.and_then(|offset| if offset < self.units {
.and_then(|offset| if offset < self.units {
Some(offset + self.first_unit) Some(offset + self.first_unit)
} else { } else {
None None

View File

@@ -86,10 +86,12 @@ impl ArgAssigner for Args {
} }
/// Legalize `sig` for RISC-V. /// Legalize `sig` for RISC-V.
pub fn legalize_signature(sig: &mut ir::Signature, pub fn legalize_signature(
sig: &mut ir::Signature,
flags: &shared_settings::Flags, flags: &shared_settings::Flags,
isa_flags: &settings::Flags, isa_flags: &settings::Flags,
current: bool) { current: bool,
) {
let bits = if flags.is_64bit() { 64 } else { 32 }; let bits = if flags.is_64bit() { 64 } else { 32 };
let mut args = Args::new(bits, isa_flags.enable_e()); let mut args = Args::new(bits, isa_flags.enable_e());

View File

@@ -29,11 +29,7 @@ impl Into<Reloc> for RelocKind {
/// 25 20 15 12 7 0 /// 25 20 15 12 7 0
/// ///
/// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`. /// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`.
fn put_r<CS: CodeSink + ?Sized>(bits: u16, fn put_r<CS: CodeSink + ?Sized>(bits: u16, rs1: RegUnit, rs2: RegUnit, rd: RegUnit, sink: &mut CS) {
rs1: RegUnit,
rs2: RegUnit,
rd: RegUnit,
sink: &mut CS) {
let bits = bits as u32; let bits = bits as u32;
let opcode5 = bits & 0x1f; let opcode5 = bits & 0x1f;
let funct3 = (bits >> 5) & 0x7; let funct3 = (bits >> 5) & 0x7;
@@ -63,11 +59,13 @@ fn put_r<CS: CodeSink + ?Sized>(bits: u16,
/// Both funct7 and shamt contribute to bit 25. In RV64, shamt uses it for shifts > 31. /// Both funct7 and shamt contribute to bit 25. In RV64, shamt uses it for shifts > 31.
/// ///
/// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`. /// Encoding bits: `opcode[6:2] | (funct3 << 5) | (funct7 << 8)`.
fn put_rshamt<CS: CodeSink + ?Sized>(bits: u16, fn put_rshamt<CS: CodeSink + ?Sized>(
bits: u16,
rs1: RegUnit, rs1: RegUnit,
shamt: i64, shamt: i64,
rd: RegUnit, rd: RegUnit,
sink: &mut CS) { sink: &mut CS,
) {
let bits = bits as u32; let bits = bits as u32;
let opcode5 = bits & 0x1f; let opcode5 = bits & 0x1f;
let funct3 = (bits >> 5) & 0x7; let funct3 = (bits >> 5) & 0x7;

View File

@@ -29,9 +29,10 @@ pub fn isa_builder() -> IsaBuilder {
} }
} }
fn isa_constructor(shared_flags: shared_settings::Flags, fn isa_constructor(
builder: &shared_settings::Builder) shared_flags: shared_settings::Flags,
-> Box<TargetIsa> { builder: &shared_settings::Builder,
) -> Box<TargetIsa> {
let level1 = if shared_flags.is_64bit() { let level1 = if shared_flags.is_64bit() {
&enc_tables::LEVEL1_RV64[..] &enc_tables::LEVEL1_RV64[..]
} else { } else {
@@ -61,12 +62,14 @@ impl TargetIsa for Isa {
enc_tables::INFO.clone() enc_tables::INFO.clone()
} }
fn legal_encodings<'a>(&'a self, fn legal_encodings<'a>(
&'a self,
dfg: &'a ir::DataFlowGraph, dfg: &'a ir::DataFlowGraph,
inst: &'a ir::InstructionData, inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type) ctrl_typevar: ir::Type,
-> Encodings<'a> { ) -> Encodings<'a> {
lookup_enclist(ctrl_typevar, lookup_enclist(
ctrl_typevar,
inst, inst,
dfg, dfg,
self.cpumode, self.cpumode,
@@ -75,7 +78,8 @@ impl TargetIsa for Isa {
&enc_tables::LEGALIZE_ACTIONS[..], &enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..], &enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..], &enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view()) self.isa_flags.predicate_view(),
)
} }
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) { fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
@@ -90,11 +94,13 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func, &self.isa_flags) abi::allocatable_registers(func, &self.isa_flags)
} }
fn emit_inst(&self, fn emit_inst(
&self,
func: &ir::Function, func: &ir::Function,
inst: ir::Inst, inst: ir::Inst,
divert: &mut regalloc::RegDiversions, divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) { sink: &mut CodeSink,
) {
binemit::emit_inst(func, inst, divert, sink) binemit::emit_inst(func, inst, divert, sink)
} }

View File

@@ -18,14 +18,16 @@ mod tests {
let shared = settings::Flags::new(&settings::builder()); let shared = settings::Flags::new(&settings::builder());
let b = builder(); let b = builder();
let f = Flags::new(&shared, &b); let f = Flags::new(&shared, &b);
assert_eq!(f.to_string(), assert_eq!(
f.to_string(),
"[riscv]\n\ "[riscv]\n\
supports_m = false\n\ supports_m = false\n\
supports_a = false\n\ supports_a = false\n\
supports_f = false\n\ supports_f = false\n\
supports_d = false\n\ supports_d = false\n\
enable_m = true\n\ enable_m = true\n\
enable_e = false\n"); enable_e = false\n"
);
// Predicates are not part of the Display output. // Predicates are not part of the Display output.
assert_eq!(f.full_float(), false); assert_eq!(f.full_float(), false);
} }

View File

@@ -4,37 +4,42 @@
pub trait IteratorExtras: Iterator { pub trait IteratorExtras: Iterator {
/// Create an iterator that produces adjacent pairs of elements from the iterator. /// Create an iterator that produces adjacent pairs of elements from the iterator.
fn adjacent_pairs(mut self) -> AdjacentPairs<Self> fn adjacent_pairs(mut self) -> AdjacentPairs<Self>
where Self: Sized, where
Self::Item: Clone Self: Sized,
Self::Item: Clone,
{ {
let elem = self.next(); let elem = self.next();
AdjacentPairs { iter: self, elem } AdjacentPairs { iter: self, elem }
} }
} }
impl<T> IteratorExtras for T where T: Iterator {} impl<T> IteratorExtras for T
where
T: Iterator,
{
}
/// Adjacent pairs iterator returned by `adjacent_pairs()`. /// Adjacent pairs iterator returned by `adjacent_pairs()`.
/// ///
/// This wraps another iterator and produces a sequence of adjacent pairs of elements. /// This wraps another iterator and produces a sequence of adjacent pairs of elements.
pub struct AdjacentPairs<I> pub struct AdjacentPairs<I>
where I: Iterator, where
I::Item: Clone I: Iterator,
I::Item: Clone,
{ {
iter: I, iter: I,
elem: Option<I::Item>, elem: Option<I::Item>,
} }
impl<I> Iterator for AdjacentPairs<I> impl<I> Iterator for AdjacentPairs<I>
where I: Iterator, where
I::Item: Clone I: Iterator,
I::Item: Clone,
{ {
type Item = (I::Item, I::Item); type Item = (I::Item, I::Item);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.elem self.elem.take().and_then(|e| {
.take()
.and_then(|e| {
self.elem = self.iter.next(); self.elem = self.iter.next();
self.elem.clone().map(|n| (e, n)) self.elem.clone().map(|n| (e, n))
}) })
@@ -47,33 +52,45 @@ mod tests {
fn adjpairs() { fn adjpairs() {
use super::IteratorExtras; use super::IteratorExtras;
assert_eq!([1, 2, 3, 4] assert_eq!(
[1, 2, 3, 4]
.iter() .iter()
.cloned() .cloned()
.adjacent_pairs() .adjacent_pairs()
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
vec![(1, 2), (2, 3), (3, 4)]); vec![(1, 2), (2, 3), (3, 4)]
assert_eq!([2, 3, 4] );
assert_eq!(
[2, 3, 4]
.iter() .iter()
.cloned() .cloned()
.adjacent_pairs() .adjacent_pairs()
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
vec![(2, 3), (3, 4)]); vec![(2, 3), (3, 4)]
assert_eq!([2, 3, 4] );
assert_eq!(
[2, 3, 4]
.iter() .iter()
.cloned() .cloned()
.adjacent_pairs() .adjacent_pairs()
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
vec![(2, 3), (3, 4)]); vec![(2, 3), (3, 4)]
assert_eq!([3, 4].iter().cloned().adjacent_pairs().collect::<Vec<_>>(), );
vec![(3, 4)]); assert_eq!(
assert_eq!([4].iter().cloned().adjacent_pairs().collect::<Vec<_>>(), [3, 4].iter().cloned().adjacent_pairs().collect::<Vec<_>>(),
vec![]); vec![(3, 4)]
assert_eq!([] );
assert_eq!(
[4].iter().cloned().adjacent_pairs().collect::<Vec<_>>(),
vec![]
);
assert_eq!(
[]
.iter() .iter()
.cloned() .cloned()
.adjacent_pairs() .adjacent_pairs()
.collect::<Vec<(i32, i32)>>(), .collect::<Vec<(i32, i32)>>(),
vec![]); vec![]
);
} }
} }

View File

@@ -98,9 +98,11 @@ fn legalize_entry_arguments(func: &mut Function, entry: Ebb) {
// Compute the value we want for `arg` from the legalized ABI arguments. // Compute the value we want for `arg` from the legalized ABI arguments.
let mut get_arg = |dfg: &mut DataFlowGraph, ty| { let mut get_arg = |dfg: &mut DataFlowGraph, ty| {
let abi_type = abi_types[abi_arg]; let abi_type = abi_types[abi_arg];
assert_eq!(abi_type.purpose, assert_eq!(
abi_type.purpose,
ArgumentPurpose::Normal, ArgumentPurpose::Normal,
"Can't legalize special-purpose argument"); "Can't legalize special-purpose argument"
);
if ty == abi_type.value_type { if ty == abi_type.value_type {
abi_arg += 1; abi_arg += 1;
Ok(dfg.append_ebb_arg(entry, ty)) Ok(dfg.append_ebb_arg(entry, ty))
@@ -159,14 +161,17 @@ fn legalize_entry_arguments(func: &mut Function, entry: Ebb) {
/// This function is very similar to the `legalize_entry_arguments` function above. /// This function is very similar to the `legalize_entry_arguments` function above.
/// ///
/// Returns the possibly new instruction representing the call. /// Returns the possibly new instruction representing the call.
fn legalize_inst_results<ResType>(dfg: &mut DataFlowGraph, fn legalize_inst_results<ResType>(
dfg: &mut DataFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
mut get_abi_type: ResType) mut get_abi_type: ResType,
-> Inst ) -> Inst
where ResType: FnMut(&DataFlowGraph, usize) -> ArgumentType where
ResType: FnMut(&DataFlowGraph, usize) -> ArgumentType,
{ {
let call = pos.current_inst() let call = pos.current_inst().expect(
.expect("Cursor must point to a call instruction"); "Cursor must point to a call instruction",
);
// We theoretically allow for call instructions that return a number of fixed results before // We theoretically allow for call instructions that return a number of fixed results before
// the call return values. In practice, it doesn't happen. // the call return values. In practice, it doesn't happen.
@@ -216,13 +221,15 @@ fn legalize_inst_results<ResType>(dfg: &mut DataFlowGraph,
/// - `Err(arg_type)` if further conversions are needed from the ABI argument `arg_type`. /// - `Err(arg_type)` if further conversions are needed from the ABI argument `arg_type`.
/// ///
/// If the `into_result` value is provided, the converted result will be written into that value. /// If the `into_result` value is provided, the converted result will be written into that value.
fn convert_from_abi<GetArg>(dfg: &mut DataFlowGraph, fn convert_from_abi<GetArg>(
dfg: &mut DataFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
ty: Type, ty: Type,
into_result: Option<Value>, into_result: Option<Value>,
get_arg: &mut GetArg) get_arg: &mut GetArg,
-> Value ) -> Value
where GetArg: FnMut(&mut DataFlowGraph, Type) -> Result<Value, ArgumentType> where
GetArg: FnMut(&mut DataFlowGraph, Type) -> Result<Value, ArgumentType>,
{ {
// Terminate the recursion when we get the desired type. // Terminate the recursion when we get the desired type.
let arg_type = match get_arg(dfg, ty) { let arg_type = match get_arg(dfg, ty) {
@@ -246,11 +253,13 @@ fn convert_from_abi<GetArg>(dfg: &mut DataFlowGraph,
let abi_ty = ty.half_width().expect("Invalid type for conversion"); let abi_ty = ty.half_width().expect("Invalid type for conversion");
let lo = convert_from_abi(dfg, pos, abi_ty, None, get_arg); let lo = convert_from_abi(dfg, pos, abi_ty, None, get_arg);
let hi = convert_from_abi(dfg, pos, abi_ty, None, get_arg); let hi = convert_from_abi(dfg, pos, abi_ty, None, get_arg);
dbg!("intsplit {}: {}, {}: {}", dbg!(
"intsplit {}: {}, {}: {}",
lo, lo,
dfg.value_type(lo), dfg.value_type(lo),
hi, hi,
dfg.value_type(hi)); dfg.value_type(hi)
);
dfg.ins(pos).with_results([into_result]).iconcat(lo, hi) dfg.ins(pos).with_results([into_result]).iconcat(lo, hi)
} }
// Construct a `ty` by concatenating two halves of a vector. // Construct a `ty` by concatenating two halves of a vector.
@@ -296,12 +305,14 @@ fn convert_from_abi<GetArg>(dfg: &mut DataFlowGraph,
/// 2. If the suggested argument doesn't have the right value type, don't change anything, but /// 2. If the suggested argument doesn't have the right value type, don't change anything, but
/// return the `Err(ArgumentType)` that is needed. /// return the `Err(ArgumentType)` that is needed.
/// ///
fn convert_to_abi<PutArg>(dfg: &mut DataFlowGraph, fn convert_to_abi<PutArg>(
dfg: &mut DataFlowGraph,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
value: Value, value: Value,
put_arg: &mut PutArg) put_arg: &mut PutArg,
where PutArg: FnMut(&mut DataFlowGraph, Value) -> Result<(), ArgumentType> ) where
PutArg: FnMut(&mut DataFlowGraph, Value) -> Result<(), ArgumentType>,
{ {
// Start by invoking the closure to either terminate the recursion or get the argument type // Start by invoking the closure to either terminate the recursion or get the argument type
// we're trying to match. // we're trying to match.
@@ -360,7 +371,8 @@ fn check_call_signature(dfg: &DataFlowGraph, inst: Inst) -> Result<(), SigRef> {
let sig = &dfg.signatures[sig_ref]; let sig = &dfg.signatures[sig_ref];
if check_arg_types(dfg, args, &sig.argument_types[..]) && if check_arg_types(dfg, args, &sig.argument_types[..]) &&
check_arg_types(dfg, dfg.inst_results(inst), &sig.return_types[..]) { check_arg_types(dfg, dfg.inst_results(inst), &sig.return_types[..])
{
// All types check out. // All types check out.
Ok(()) Ok(())
} else { } else {
@@ -380,20 +392,23 @@ fn check_return_signature(dfg: &DataFlowGraph, inst: Inst, sig: &Signature) -> b
/// - `get_abi_type` is a closure that can provide the desired `ArgumentType` for a given ABI /// - `get_abi_type` is a closure that can provide the desired `ArgumentType` for a given ABI
/// argument number in `0..abi_args`. /// argument number in `0..abi_args`.
/// ///
fn legalize_inst_arguments<ArgType>(dfg: &mut DataFlowGraph, fn legalize_inst_arguments<ArgType>(
dfg: &mut DataFlowGraph,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
abi_args: usize, abi_args: usize,
mut get_abi_type: ArgType) mut get_abi_type: ArgType,
where ArgType: FnMut(&DataFlowGraph, usize) -> ArgumentType ) where
ArgType: FnMut(&DataFlowGraph, usize) -> ArgumentType,
{ {
let inst = pos.current_inst() let inst = pos.current_inst().expect(
.expect("Cursor must point to a call instruction"); "Cursor must point to a call instruction",
);
// Lift the value list out of the call instruction so we modify it. // Lift the value list out of the call instruction so we modify it.
let mut vlist = dfg[inst] let mut vlist = dfg[inst].take_value_list().expect(
.take_value_list() "Call must have a value list",
.expect("Call must have a value list"); );
// The value list contains all arguments to the instruction, including the callee on an // The value list contains all arguments to the instruction, including the callee on an
// indirect call which isn't part of the call arguments that must match the ABI signature. // indirect call which isn't part of the call arguments that must match the ABI signature.
@@ -474,23 +489,23 @@ pub fn handle_call_abi(mut inst: Inst, func: &mut Function, cfg: &ControlFlowGra
// OK, we need to fix the call arguments to match the ABI signature. // OK, we need to fix the call arguments to match the ABI signature.
let abi_args = dfg.signatures[sig_ref].argument_types.len(); let abi_args = dfg.signatures[sig_ref].argument_types.len();
legalize_inst_arguments(dfg, legalize_inst_arguments(dfg, cfg, pos, abi_args, |dfg, abi_arg| {
cfg, dfg.signatures[sig_ref].argument_types[abi_arg]
pos, });
abi_args,
|dfg, abi_arg| dfg.signatures[sig_ref].argument_types[abi_arg]);
if !dfg.signatures[sig_ref].return_types.is_empty() { if !dfg.signatures[sig_ref].return_types.is_empty() {
inst = legalize_inst_results(dfg, inst = legalize_inst_results(dfg, pos, |dfg, abi_res| {
pos, dfg.signatures[sig_ref].return_types[abi_res]
|dfg, abi_res| dfg.signatures[sig_ref].return_types[abi_res]); });
} }
debug_assert!(check_call_signature(dfg, inst).is_ok(), debug_assert!(
check_call_signature(dfg, inst).is_ok(),
"Signature still wrong: {}, {}{}", "Signature still wrong: {}, {}{}",
dfg.display_inst(inst, None), dfg.display_inst(inst, None),
sig_ref, sig_ref,
dfg.signatures[sig_ref]); dfg.signatures[sig_ref]
);
// Go back and insert spills for any stack arguments. // Go back and insert spills for any stack arguments.
pos.goto_inst(inst); pos.goto_inst(inst);
@@ -519,27 +534,30 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph
.iter() .iter()
.rev() .rev()
.take_while(|&rt| { .take_while(|&rt| {
rt.purpose == ArgumentPurpose::Link || rt.purpose == ArgumentPurpose::Link || rt.purpose == ArgumentPurpose::StructReturn ||
rt.purpose == ArgumentPurpose::StructReturn ||
rt.purpose == ArgumentPurpose::VMContext rt.purpose == ArgumentPurpose::VMContext
}) })
.count(); .count();
let abi_args = sig.return_types.len() - special_args; let abi_args = sig.return_types.len() - special_args;
legalize_inst_arguments(dfg, legalize_inst_arguments(
dfg,
cfg, cfg,
pos, pos,
abi_args, abi_args,
|_, abi_arg| sig.return_types[abi_arg]); |_, abi_arg| sig.return_types[abi_arg],
);
assert_eq!(dfg.inst_variable_args(inst).len(), abi_args); assert_eq!(dfg.inst_variable_args(inst).len(), abi_args);
// Append special return arguments for any `sret`, `link`, and `vmctx` return values added to // Append special return arguments for any `sret`, `link`, and `vmctx` return values added to
// the legalized signature. These values should simply be propagated from the entry block // the legalized signature. These values should simply be propagated from the entry block
// arguments. // arguments.
if special_args > 0 { if special_args > 0 {
dbg!("Adding {} special-purpose arguments to {}", dbg!(
"Adding {} special-purpose arguments to {}",
special_args, special_args,
dfg.display_inst(inst, None)); dfg.display_inst(inst, None)
);
let mut vlist = dfg[inst].take_value_list().unwrap(); let mut vlist = dfg[inst].take_value_list().unwrap();
for arg in &sig.return_types[abi_args..] { for arg in &sig.return_types[abi_args..] {
match arg.purpose { match arg.purpose {
@@ -565,10 +583,12 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph
dfg[inst].put_value_list(vlist); dfg[inst].put_value_list(vlist);
} }
debug_assert!(check_return_signature(dfg, inst, sig), debug_assert!(
check_return_signature(dfg, inst, sig),
"Signature still wrong: {} / signature {}", "Signature still wrong: {} / signature {}",
dfg.display_inst(inst, None), dfg.display_inst(inst, None),
sig); sig
);
// Yes, we changed stuff. // Yes, we changed stuff.
true true
@@ -579,10 +599,10 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph
/// Values that are passed into the function on the stack must be assigned to an `IncomingArg` /// Values that are passed into the function on the stack must be assigned to an `IncomingArg`
/// stack slot already during legalization. /// stack slot already during legalization.
fn spill_entry_arguments(func: &mut Function, entry: Ebb) { fn spill_entry_arguments(func: &mut Function, entry: Ebb) {
for (abi, &arg) in func.signature for (abi, &arg) in func.signature.argument_types.iter().zip(
.argument_types func.dfg.ebb_args(entry),
.iter() )
.zip(func.dfg.ebb_args(entry)) { {
if let ArgumentLoc::Stack(offset) = abi.location { if let ArgumentLoc::Stack(offset) = abi.location {
let ss = func.stack_slots.make_incoming_arg(abi.value_type, offset); let ss = func.stack_slots.make_incoming_arg(abi.value_type, offset);
func.locations[arg] = ValueLoc::Stack(ss); func.locations[arg] = ValueLoc::Stack(ss);
@@ -598,15 +618,18 @@ fn spill_entry_arguments(func: &mut Function, entry: Ebb) {
/// TODO: The outgoing stack slots can be written a bit earlier, as long as there are no branches /// TODO: The outgoing stack slots can be written a bit earlier, as long as there are no branches
/// or calls between writing the stack slots and the call instruction. Writing the slots earlier /// or calls between writing the stack slots and the call instruction. Writing the slots earlier
/// could help reduce register pressure before the call. /// could help reduce register pressure before the call.
fn spill_call_arguments(dfg: &mut DataFlowGraph, fn spill_call_arguments(
dfg: &mut DataFlowGraph,
locations: &mut ValueLocations, locations: &mut ValueLocations,
stack_slots: &mut StackSlots, stack_slots: &mut StackSlots,
pos: &mut Cursor) pos: &mut Cursor,
-> bool { ) -> bool {
let inst = pos.current_inst() let inst = pos.current_inst().expect(
.expect("Cursor must point to a call instruction"); "Cursor must point to a call instruction",
let sig_ref = dfg.call_signature(inst) );
.expect("Call instruction expected."); let sig_ref = dfg.call_signature(inst).expect(
"Call instruction expected.",
);
// Start by building a list of stack slots and arguments to be replaced. // Start by building a list of stack slots and arguments to be replaced.
// This requires borrowing `dfg`, so we can't change anything. // This requires borrowing `dfg`, so we can't change anything.

View File

@@ -35,12 +35,14 @@ pub fn expand_heap_addr(inst: ir::Inst, func: &mut ir::Function, _cfg: &mut Cont
} }
/// Expand a `heap_addr` for a dynamic heap. /// Expand a `heap_addr` for a dynamic heap.
fn dynamic_addr(inst: ir::Inst, fn dynamic_addr(
inst: ir::Inst,
heap: ir::Heap, heap: ir::Heap,
offset: ir::Value, offset: ir::Value,
size: u32, size: u32,
bound_gv: ir::GlobalVar, bound_gv: ir::GlobalVar,
func: &mut ir::Function) { func: &mut ir::Function,
) {
let size = size as i64; let size = size as i64;
let offset_ty = func.dfg.value_type(offset); let offset_ty = func.dfg.value_type(offset);
let addr_ty = func.dfg.value_type(func.dfg.first_result(inst)); let addr_ty = func.dfg.value_type(func.dfg.first_result(inst));
@@ -54,21 +56,30 @@ fn dynamic_addr(inst: ir::Inst,
let oob; let oob;
if size == 1 { if size == 1 {
// `offset > bound - 1` is the same as `offset >= bound`. // `offset > bound - 1` is the same as `offset >= bound`.
oob = pos.ins() oob = pos.ins().icmp(
.icmp(IntCC::UnsignedGreaterThanOrEqual, offset, bound); IntCC::UnsignedGreaterThanOrEqual,
offset,
bound,
);
} else if size <= min_size { } else if size <= min_size {
// We know that bound >= min_size, so here we can compare `offset > bound - size` without // We know that bound >= min_size, so here we can compare `offset > bound - size` without
// wrapping. // wrapping.
let adj_bound = pos.ins().iadd_imm(bound, -size); let adj_bound = pos.ins().iadd_imm(bound, -size);
oob = pos.ins() oob = pos.ins().icmp(
.icmp(IntCC::UnsignedGreaterThan, offset, adj_bound); IntCC::UnsignedGreaterThan,
offset,
adj_bound,
);
} else { } else {
// We need an overflow check for the adjusted offset. // We need an overflow check for the adjusted offset.
let size_val = pos.ins().iconst(offset_ty, size); let size_val = pos.ins().iconst(offset_ty, size);
let (adj_offset, overflow) = pos.ins().iadd_cout(offset, size_val); let (adj_offset, overflow) = pos.ins().iadd_cout(offset, size_val);
pos.ins().trapnz(overflow); pos.ins().trapnz(overflow);
oob = pos.ins() oob = pos.ins().icmp(
.icmp(IntCC::UnsignedGreaterThan, adj_offset, bound); IntCC::UnsignedGreaterThan,
adj_offset,
bound,
);
} }
pos.ins().trapnz(oob); pos.ins().trapnz(oob);
@@ -76,12 +87,14 @@ fn dynamic_addr(inst: ir::Inst,
} }
/// Expand a `heap_addr` for a static heap. /// Expand a `heap_addr` for a static heap.
fn static_addr(inst: ir::Inst, fn static_addr(
inst: ir::Inst,
heap: ir::Heap, heap: ir::Heap,
offset: ir::Value, offset: ir::Value,
size: u32, size: u32,
bound: i64, bound: i64,
func: &mut ir::Function) { func: &mut ir::Function,
) {
let size = size as i64; let size = size as i64;
let offset_ty = func.dfg.value_type(offset); let offset_ty = func.dfg.value_type(offset);
let addr_ty = func.dfg.value_type(func.dfg.first_result(inst)); let addr_ty = func.dfg.value_type(func.dfg.first_result(inst));
@@ -104,11 +117,17 @@ fn static_addr(inst: ir::Inst,
let oob = if limit & 1 == 1 { let oob = if limit & 1 == 1 {
// Prefer testing `offset >= limit - 1` when limit is odd because an even number is // Prefer testing `offset >= limit - 1` when limit is odd because an even number is
// likely to be a convenient constant on ARM and other RISC architectures. // likely to be a convenient constant on ARM and other RISC architectures.
pos.ins() pos.ins().icmp_imm(
.icmp_imm(IntCC::UnsignedGreaterThanOrEqual, offset, limit - 1) IntCC::UnsignedGreaterThanOrEqual,
offset,
limit - 1,
)
} else { } else {
pos.ins() pos.ins().icmp_imm(
.icmp_imm(IntCC::UnsignedGreaterThan, offset, limit) IntCC::UnsignedGreaterThan,
offset,
limit,
)
}; };
pos.ins().trapnz(oob); pos.ins().trapnz(oob);
} }
@@ -119,12 +138,14 @@ fn static_addr(inst: ir::Inst,
/// Emit code for the base address computation of a `heap_addr` instruction. /// Emit code for the base address computation of a `heap_addr` instruction.
/// ///
/// ///
fn offset_addr(inst: ir::Inst, fn offset_addr(
inst: ir::Inst,
heap: ir::Heap, heap: ir::Heap,
addr_ty: ir::Type, addr_ty: ir::Type,
mut offset: ir::Value, mut offset: ir::Value,
offset_ty: ir::Type, offset_ty: ir::Type,
func: &mut ir::Function) { func: &mut ir::Function,
) {
let mut pos = FuncCursor::new(func).at_inst(inst); let mut pos = FuncCursor::new(func).at_inst(inst);
// Convert `offset` to `addr_ty`. // Convert `offset` to `addr_ty`.

View File

@@ -66,9 +66,11 @@ pub fn legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, is
split::simplify_branch_arguments(&mut pos.func.dfg, inst); split::simplify_branch_arguments(&mut pos.func.dfg, inst);
} }
match isa.encode(&pos.func.dfg, match isa.encode(
&pos.func.dfg,
&pos.func.dfg[inst], &pos.func.dfg[inst],
pos.func.dfg.ctrl_typevar(inst)) { pos.func.dfg.ctrl_typevar(inst),
) {
Ok(encoding) => pos.func.encodings[inst] = encoding, Ok(encoding) => pos.func.encodings[inst] = encoding,
Err(action) => { Err(action) => {
// We should transform the instruction into legal equivalents. // We should transform the instruction into legal equivalents.

View File

@@ -71,21 +71,23 @@ use std::iter;
/// Split `value` into two values using the `isplit` semantics. Do this by reusing existing values /// Split `value` into two values using the `isplit` semantics. Do this by reusing existing values
/// if possible. /// if possible.
pub fn isplit(dfg: &mut DataFlowGraph, pub fn isplit(
dfg: &mut DataFlowGraph,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
value: Value) value: Value,
-> (Value, Value) { ) -> (Value, Value) {
split_any(dfg, cfg, pos, value, Opcode::Iconcat) split_any(dfg, cfg, pos, value, Opcode::Iconcat)
} }
/// Split `value` into halves using the `vsplit` semantics. Do this by reusing existing values if /// Split `value` into halves using the `vsplit` semantics. Do this by reusing existing values if
/// possible. /// possible.
pub fn vsplit(dfg: &mut DataFlowGraph, pub fn vsplit(
dfg: &mut DataFlowGraph,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
value: Value) value: Value,
-> (Value, Value) { ) -> (Value, Value) {
split_any(dfg, cfg, pos, value, Opcode::Vconcat) split_any(dfg, cfg, pos, value, Opcode::Vconcat)
} }
@@ -107,12 +109,13 @@ struct Repair {
} }
/// Generic version of `isplit` and `vsplit` controlled by the `concat` opcode. /// Generic version of `isplit` and `vsplit` controlled by the `concat` opcode.
fn split_any(dfg: &mut DataFlowGraph, fn split_any(
dfg: &mut DataFlowGraph,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
value: Value, value: Value,
concat: Opcode) concat: Opcode,
-> (Value, Value) { ) -> (Value, Value) {
let saved_pos = pos.position(); let saved_pos = pos.position();
let mut repairs = Vec::new(); let mut repairs = Vec::new();
let result = split_value(dfg, pos, value, concat, &mut repairs); let result = split_value(dfg, pos, value, concat, &mut repairs);
@@ -121,17 +124,20 @@ fn split_any(dfg: &mut DataFlowGraph,
while let Some(repair) = repairs.pop() { while let Some(repair) = repairs.pop() {
for &(_, inst) in cfg.get_predecessors(repair.ebb) { for &(_, inst) in cfg.get_predecessors(repair.ebb) {
let branch_opc = dfg[inst].opcode(); let branch_opc = dfg[inst].opcode();
assert!(branch_opc.is_branch(), assert!(
branch_opc.is_branch(),
"Predecessor not a branch: {}", "Predecessor not a branch: {}",
dfg.display_inst(inst, None)); dfg.display_inst(inst, None)
);
let fixed_args = branch_opc.constraints().fixed_value_arguments(); let fixed_args = branch_opc.constraints().fixed_value_arguments();
let mut args = dfg[inst] let mut args = dfg[inst].take_value_list().expect(
.take_value_list() "Branches must have value lists.",
.expect("Branches must have value lists."); );
let num_args = args.len(&dfg.value_lists); let num_args = args.len(&dfg.value_lists);
// Get the old value passed to the EBB argument we're repairing. // Get the old value passed to the EBB argument we're repairing.
let old_arg = args.get(fixed_args + repair.num, &dfg.value_lists) let old_arg = args.get(fixed_args + repair.num, &dfg.value_lists).expect(
.expect("Too few branch arguments"); "Too few branch arguments",
);
// It's possible that the CFG's predecessor list has duplicates. Detect them here. // It's possible that the CFG's predecessor list has duplicates. Detect them here.
if dfg.value_type(old_arg) == repair.split_type { if dfg.value_type(old_arg) == repair.split_type {
@@ -156,8 +162,10 @@ fn split_any(dfg: &mut DataFlowGraph,
// We need to append one or more arguments. If we're adding more than one argument, // We need to append one or more arguments. If we're adding more than one argument,
// there must be pending repairs on the stack that will fill in the correct values // there must be pending repairs on the stack that will fill in the correct values
// instead of `hi`. // instead of `hi`.
args.extend(iter::repeat(hi).take(1 + fixed_args + repair.hi_num - num_args), args.extend(
&mut dfg.value_lists); iter::repeat(hi).take(1 + fixed_args + repair.hi_num - num_args),
&mut dfg.value_lists,
);
} }
// Put the value list back after manipulating it. // Put the value list back after manipulating it.
@@ -175,12 +183,13 @@ fn split_any(dfg: &mut DataFlowGraph,
/// instruction. /// instruction.
/// ///
/// Return the two new values representing the parts of `value`. /// Return the two new values representing the parts of `value`.
fn split_value(dfg: &mut DataFlowGraph, fn split_value(
dfg: &mut DataFlowGraph,
pos: &mut Cursor, pos: &mut Cursor,
value: Value, value: Value,
concat: Opcode, concat: Opcode,
repairs: &mut Vec<Repair>) repairs: &mut Vec<Repair>,
-> (Value, Value) { ) -> (Value, Value) {
let value = dfg.resolve_copies(value); let value = dfg.resolve_copies(value);
let mut reuse = None; let mut reuse = None;
@@ -228,9 +237,12 @@ fn split_value(dfg: &mut DataFlowGraph,
// need to insert a split instruction before returning. // need to insert a split instruction before returning.
pos.goto_top(ebb); pos.goto_top(ebb);
pos.next_inst(); pos.next_inst();
dfg.ins(pos) dfg.ins(pos).with_result(value).Binary(
.with_result(value) concat,
.Binary(concat, split_type, lo, hi); split_type,
lo,
hi,
);
// Finally, splitting the EBB argument is not enough. We also have to repair all // Finally, splitting the EBB argument is not enough. We also have to repair all
// of the predecessor instructions that branch here. // of the predecessor instructions that branch here.
@@ -254,12 +266,14 @@ fn split_value(dfg: &mut DataFlowGraph,
} }
// Add a repair entry to the work list. // Add a repair entry to the work list.
fn add_repair(concat: Opcode, fn add_repair(
concat: Opcode,
split_type: Type, split_type: Type,
ebb: Ebb, ebb: Ebb,
num: usize, num: usize,
hi_num: usize, hi_num: usize,
repairs: &mut Vec<Repair>) { repairs: &mut Vec<Repair>,
) {
repairs.push(Repair { repairs.push(Repair {
concat, concat,
split_type, split_type,

View File

@@ -10,10 +10,12 @@ use loop_analysis::{Loop, LoopAnalysis};
/// Performs the LICM pass by detecting loops within the CFG and moving /// Performs the LICM pass by detecting loops within the CFG and moving
/// loop-invariant instructions out of them. /// loop-invariant instructions out of them.
/// Changes the CFG and domtree in-place during the operation. /// Changes the CFG and domtree in-place during the operation.
pub fn do_licm(func: &mut Function, pub fn do_licm(
func: &mut Function,
cfg: &mut ControlFlowGraph, cfg: &mut ControlFlowGraph,
domtree: &mut DominatorTree, domtree: &mut DominatorTree,
loop_analysis: &mut LoopAnalysis) { loop_analysis: &mut LoopAnalysis,
) {
loop_analysis.compute(func, cfg, domtree); loop_analysis.compute(func, cfg, domtree);
for lp in loop_analysis.loops() { for lp in loop_analysis.loops() {
// For each loop that we want to optimize we determine the set of loop-invariant // For each loop that we want to optimize we determine the set of loop-invariant
@@ -53,11 +55,12 @@ pub fn do_licm(func: &mut Function,
// Insert a pre-header before the header, modifying the function layout and CFG to reflect it. // Insert a pre-header before the header, modifying the function layout and CFG to reflect it.
// A jump instruction to the header is placed at the end of the pre-header. // A jump instruction to the header is placed at the end of the pre-header.
fn create_pre_header(header: Ebb, fn create_pre_header(
header: Ebb,
func: &mut Function, func: &mut Function,
cfg: &mut ControlFlowGraph, cfg: &mut ControlFlowGraph,
domtree: &DominatorTree) domtree: &DominatorTree,
-> Ebb { ) -> Ebb {
let pool = &mut ListPool::<Value>::new(); let pool = &mut ListPool::<Value>::new();
let header_args_values: Vec<Value> = func.dfg.ebb_args(header).into_iter().cloned().collect(); let header_args_values: Vec<Value> = func.dfg.ebb_args(header).into_iter().cloned().collect();
let header_args_types: Vec<Type> = header_args_values let header_args_types: Vec<Type> = header_args_values
@@ -82,9 +85,10 @@ fn create_pre_header(header: Ebb,
// Inserts the pre-header at the right place in the layout. // Inserts the pre-header at the right place in the layout.
pos.insert_ebb(pre_header); pos.insert_ebb(pre_header);
pos.next_inst(); pos.next_inst();
func.dfg func.dfg.ins(&mut pos).jump(
.ins(&mut pos) header,
.jump(header, pre_header_args_value.as_slice(pool)); pre_header_args_value.as_slice(pool),
);
} }
pre_header pre_header
} }
@@ -94,11 +98,12 @@ fn create_pre_header(header: Ebb,
// A loop header has a pre-header if there is only one predecessor that the header doesn't // A loop header has a pre-header if there is only one predecessor that the header doesn't
// dominate. // dominate.
// Returns the pre-header Ebb and the instruction jumping to the header. // Returns the pre-header Ebb and the instruction jumping to the header.
fn has_pre_header(layout: &Layout, fn has_pre_header(
layout: &Layout,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
domtree: &DominatorTree, domtree: &DominatorTree,
header: Ebb) header: Ebb,
-> Option<(Ebb, Inst)> { ) -> Option<(Ebb, Inst)> {
let mut result = None; let mut result = None;
let mut found = false; let mut found = false;
for &(pred_ebb, last_inst) in cfg.get_predecessors(header) { for &(pred_ebb, last_inst) in cfg.get_predecessors(header) {
@@ -129,11 +134,12 @@ fn change_branch_jump_destination(inst: Inst, new_ebb: Ebb, func: &mut Function)
// Traverses a loop in reverse post-order from a header EBB and identify loop-invariant // Traverses a loop in reverse post-order from a header EBB and identify loop-invariant
// instructions. These loop-invariant instructions are then removed from the code and returned // instructions. These loop-invariant instructions are then removed from the code and returned
// (in reverse post-order) for later use. // (in reverse post-order) for later use.
fn remove_loop_invariant_instructions(lp: Loop, fn remove_loop_invariant_instructions(
lp: Loop,
func: &mut Function, func: &mut Function,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
loop_analysis: &LoopAnalysis) loop_analysis: &LoopAnalysis,
-> Vec<Inst> { ) -> Vec<Inst> {
let mut loop_values: HashSet<Value> = HashSet::new(); let mut loop_values: HashSet<Value> = HashSet::new();
let mut invariant_inst: Vec<Inst> = Vec::new(); let mut invariant_inst: Vec<Inst> = Vec::new();
let mut pos = Cursor::new(&mut func.layout); let mut pos = Cursor::new(&mut func.layout);
@@ -146,10 +152,10 @@ fn remove_loop_invariant_instructions(lp: Loop,
pos.goto_top(*ebb); pos.goto_top(*ebb);
while let Some(inst) = pos.next_inst() { while let Some(inst) = pos.next_inst() {
if func.dfg.has_results(inst) && if func.dfg.has_results(inst) &&
func.dfg func.dfg.inst_args(inst).into_iter().all(|arg| {
.inst_args(inst) !loop_values.contains(arg)
.into_iter() })
.all(|arg| !loop_values.contains(arg)) { {
// If all the instruction's argument are defined outside the loop // If all the instruction's argument are defined outside the loop
// then this instruction is loop-invariant // then this instruction is loop-invariant
invariant_inst.push(inst); invariant_inst.push(inst);

View File

@@ -105,10 +105,12 @@ impl LoopAnalysis {
// Traverses the CFG in reverse postorder and create a loop object for every EBB having a // Traverses the CFG in reverse postorder and create a loop object for every EBB having a
// back edge. // back edge.
fn find_loop_headers(&mut self, fn find_loop_headers(
&mut self,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
domtree: &DominatorTree, domtree: &DominatorTree,
layout: &Layout) { layout: &Layout,
) {
// We traverse the CFG in reverse postorder // We traverse the CFG in reverse postorder
for &ebb in domtree.cfg_postorder().iter().rev() { for &ebb in domtree.cfg_postorder().iter().rev() {
for &(_, pred_inst) in cfg.get_predecessors(ebb) { for &(_, pred_inst) in cfg.get_predecessors(ebb) {
@@ -127,10 +129,12 @@ impl LoopAnalysis {
// Intended to be called after `find_loop_headers`. For each detected loop header, // Intended to be called after `find_loop_headers`. For each detected loop header,
// discovers all the ebb belonging to the loop and its inner loops. After a call to this // discovers all the ebb belonging to the loop and its inner loops. After a call to this
// function, the loop tree is fully constructed. // function, the loop tree is fully constructed.
fn discover_loop_blocks(&mut self, fn discover_loop_blocks(
&mut self,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
domtree: &DominatorTree, domtree: &DominatorTree,
layout: &Layout) { layout: &Layout,
) {
let mut stack: Vec<Ebb> = Vec::new(); let mut stack: Vec<Ebb> = Vec::new();
// We handle each loop header in reverse order, corresponding to a pesudo postorder // We handle each loop header in reverse order, corresponding to a pesudo postorder
// traversal of the graph. // traversal of the graph.

View File

@@ -38,7 +38,8 @@ impl<T: ReservedValue> PackedOption<T> {
/// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value. /// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value.
pub fn map<U, F>(self, f: F) -> Option<U> pub fn map<U, F>(self, f: F) -> Option<U>
where F: FnOnce(T) -> U where
F: FnOnce(T) -> U,
{ {
self.expand().map(f) self.expand().map(f)
} }
@@ -69,8 +70,10 @@ impl<T: ReservedValue> Default for PackedOption<T> {
impl<T: ReservedValue> From<T> for PackedOption<T> { impl<T: ReservedValue> From<T> for PackedOption<T> {
/// Convert `t` into a packed `Some(x)`. /// Convert `t` into a packed `Some(x)`.
fn from(t: T) -> PackedOption<T> { fn from(t: T) -> PackedOption<T> {
debug_assert!(t != T::reserved_value(), debug_assert!(
"Can't make a PackedOption from the reserved value."); t != T::reserved_value(),
"Can't make a PackedOption from the reserved value."
);
PackedOption(t) PackedOption(t)
} }
} }
@@ -92,7 +95,8 @@ impl<T: ReservedValue> Into<Option<T>> for PackedOption<T> {
} }
impl<T> fmt::Debug for PackedOption<T> impl<T> fmt::Debug for PackedOption<T>
where T: ReservedValue + fmt::Debug where
T: ReservedValue + fmt::Debug,
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_none() { if self.is_none() {

View File

@@ -7,7 +7,8 @@
/// ///
/// Returns the number of elements where `p(t)` is true. /// Returns the number of elements where `p(t)` is true.
pub fn partition_slice<'a, T: 'a, F>(s: &'a mut [T], mut p: F) -> usize pub fn partition_slice<'a, T: 'a, F>(s: &'a mut [T], mut p: F) -> usize
where F: FnMut(&T) -> bool where
F: FnMut(&T) -> bool,
{ {
// Count the length of the prefix where `p` returns true. // Count the length of the prefix where `p` returns true.
let mut count = match s.iter().position(|t| !p(t)) { let mut count = match s.iter().position(|t| !p(t)) {

View File

@@ -91,7 +91,8 @@ impl Affinity {
// If the preferred register class is a subclass of the constraint, there's no need // If the preferred register class is a subclass of the constraint, there's no need
// to change anything. // to change anything.
if constraint.kind != ConstraintKind::Stack && if constraint.kind != ConstraintKind::Stack &&
!constraint.regclass.has_subclass(rc) { !constraint.regclass.has_subclass(rc)
{
// If the register classes don't overlap, `intersect` returns `None`, and we // If the register classes don't overlap, `intersect` returns `None`, and we
// just keep our previous affinity. // just keep our previous affinity.
if let Some(subclass) = constraint.regclass.intersect(reg_info.rc(rc)) { if let Some(subclass) = constraint.regclass.intersect(reg_info.rc(rc)) {

View File

@@ -86,10 +86,9 @@ impl AllocatableSet {
/// ///
/// This assumes that unused bits are 1. /// This assumes that unused bits are 1.
pub fn interferes_with(&self, other: &AllocatableSet) -> bool { pub fn interferes_with(&self, other: &AllocatableSet) -> bool {
self.avail self.avail.iter().zip(&other.avail).any(
.iter() |(&x, &y)| (x | y) != !0,
.zip(&other.avail) )
.any(|(&x, &y)| (x | y) != !0)
} }
/// Intersect this set of allocatable registers with `other`. This has the effect of removing /// Intersect this set of allocatable registers with `other`. This has the effect of removing

View File

@@ -132,14 +132,15 @@ impl DomForest {
/// ///
/// If the merge succeeds, returns `Ok(())`. The merged sequence can be extracted with /// If the merge succeeds, returns `Ok(())`. The merged sequence can be extracted with
/// `swap()`. /// `swap()`.
pub fn try_merge(&mut self, pub fn try_merge(
&mut self,
va: &[Value], va: &[Value],
vb: &[Value], vb: &[Value],
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
layout: &Layout, layout: &Layout,
domtree: &DominatorTree, domtree: &DominatorTree,
liveness: &Liveness) liveness: &Liveness,
-> Result<(), (Value, Value)> { ) -> Result<(), (Value, Value)> {
self.stack.clear(); self.stack.clear();
self.values.clear(); self.values.clear();
self.values.reserve(va.len() + vb.len()); self.values.reserve(va.len() + vb.len());
@@ -154,9 +155,9 @@ impl DomForest {
for node in merged { for node in merged {
if let Some(parent) = self.push_node(node, layout, domtree) { if let Some(parent) = self.push_node(node, layout, domtree) {
// Check if `parent` live range contains `node.def`. // Check if `parent` live range contains `node.def`.
let lr = liveness let lr = liveness.get(parent).expect(
.get(parent) "No live range for parent value",
.expect("No live range for parent value"); );
if lr.overlaps_def(node.def, layout.pp_ebb(node.def), layout) { if lr.overlaps_def(node.def, layout.pp_ebb(node.def), layout) {
// Interference detected. Get the `(a, b)` order right in the error. // Interference detected. Get the `(a, b)` order right in the error.
return Err(if node.set == 0 { return Err(if node.set == 0 {
@@ -177,8 +178,9 @@ impl DomForest {
/// Given two ordered sequences of nodes, yield an ordered sequence containing all of them. /// Given two ordered sequences of nodes, yield an ordered sequence containing all of them.
/// Duplicates are removed. /// Duplicates are removed.
struct MergedNodes<'a, IA, IB> struct MergedNodes<'a, IA, IB>
where IA: Iterator<Item = Node>, where
IB: Iterator<Item = Node> IA: Iterator<Item = Node>,
IB: Iterator<Item = Node>,
{ {
a: Peekable<IA>, a: Peekable<IA>,
b: Peekable<IB>, b: Peekable<IB>,
@@ -187,8 +189,9 @@ struct MergedNodes<'a, IA, IB>
} }
impl<'a, IA, IB> Iterator for MergedNodes<'a, IA, IB> impl<'a, IA, IB> Iterator for MergedNodes<'a, IA, IB>
where IA: Iterator<Item = Node>, where
IB: Iterator<Item = Node> IA: Iterator<Item = Node>,
IB: Iterator<Item = Node>,
{ {
type Item = Node; type Item = Node;
@@ -198,9 +201,12 @@ impl<'a, IA, IB> Iterator for MergedNodes<'a, IA, IB>
// If the two values are defined at the same point, compare value numbers instead // If the two values are defined at the same point, compare value numbers instead
// this is going to cause an interference conflict unless its actually the same // this is going to cause an interference conflict unless its actually the same
// value appearing in both streams. // value appearing in both streams.
self.domtree self.domtree.rpo_cmp(a.def, b.def, self.layout).then(
.rpo_cmp(a.def, b.def, self.layout) Ord::cmp(
.then(Ord::cmp(&a.value, &b.value)) &a.value,
&b.value,
),
)
} }
(Some(_), None) => Ordering::Less, (Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater, (None, Some(_)) => Ordering::Greater,
@@ -256,13 +262,15 @@ impl Coalescing {
} }
/// Convert `func` to conventional SSA form and build virtual registers in the process. /// Convert `func` to conventional SSA form and build virtual registers in the process.
pub fn conventional_ssa(&mut self, pub fn conventional_ssa(
&mut self,
isa: &TargetIsa, isa: &TargetIsa,
func: &mut Function, func: &mut Function,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
domtree: &DominatorTree, domtree: &DominatorTree,
liveness: &mut Liveness, liveness: &mut Liveness,
virtregs: &mut VirtRegs) { virtregs: &mut VirtRegs,
) {
dbg!("Coalescing for:\n{}", func.display(isa)); dbg!("Coalescing for:\n{}", func.display(isa));
let mut context = Context { let mut context = Context {
isa, isa,
@@ -329,9 +337,11 @@ impl<'a> Context<'a> {
// //
// Try to catch infinite splitting loops. The values created by splitting should never // Try to catch infinite splitting loops. The values created by splitting should never
// have irreconcilable interferences. // have irreconcilable interferences.
assert!(!self.split_values.contains(&bad_value), assert!(
!self.split_values.contains(&bad_value),
"{} was already isolated", "{} was already isolated",
bad_value); bad_value
);
let split_len = self.split_values.len(); let split_len = self.split_values.len();
// The bad value can be both the successor value and a predecessor value at the same // The bad value can be both the successor value and a predecessor value at the same
@@ -349,18 +359,22 @@ impl<'a> Context<'a> {
} }
// Second loop check. // Second loop check.
assert_ne!(split_len, assert_ne!(
split_len,
self.split_values.len(), self.split_values.len(),
"Couldn't isolate {}", "Couldn't isolate {}",
bad_value); bad_value
);
} }
let vreg = self.virtregs.unify(self.values); let vreg = self.virtregs.unify(self.values);
dbg!("Coalesced {} arg {} into {} = {}", dbg!(
"Coalesced {} arg {} into {} = {}",
ebb, ebb,
argnum, argnum,
vreg, vreg,
DisplayList(self.virtregs.values(vreg))); DisplayList(self.virtregs.values(vreg))
);
} }
/// Reset `self.values` to just the set of split values. /// Reset `self.values` to just the set of split values.
@@ -369,8 +383,7 @@ impl<'a> Context<'a> {
self.values.extend_from_slice(self.split_values); self.values.extend_from_slice(self.split_values);
let domtree = &self.domtree; let domtree = &self.domtree;
let func = &self.func; let func = &self.func;
self.values self.values.sort_by(|&a, &b| {
.sort_by(|&a, &b| {
domtree.rpo_cmp(func.dfg.value_def(a), func.dfg.value_def(b), &func.layout) domtree.rpo_cmp(func.dfg.value_def(a), func.dfg.value_def(b), &func.layout)
}); });
} }
@@ -379,11 +392,12 @@ impl<'a> Context<'a> {
/// ///
/// Returns a value from a congruence class that needs to be split before starting over, or /// Returns a value from a congruence class that needs to be split before starting over, or
/// `None` if everything was successfully coalesced into `self.values`. /// `None` if everything was successfully coalesced into `self.values`.
fn try_coalesce(&mut self, fn try_coalesce(
&mut self,
argnum: usize, argnum: usize,
succ_val: Value, succ_val: Value,
preds: &[BasicBlock]) preds: &[BasicBlock],
-> Option<Value> { ) -> Option<Value> {
// Initialize the value list with the split values. These are guaranteed to be // Initialize the value list with the split values. These are guaranteed to be
// interference free, and anything that interferes with them must be split away. // interference free, and anything that interferes with them must be split away.
self.reset_values(); self.reset_values();
@@ -397,10 +411,12 @@ impl<'a> Context<'a> {
for &(pred_ebb, pred_inst) in preds { for &(pred_ebb, pred_inst) in preds {
let pred_val = self.func.dfg.inst_variable_args(pred_inst)[argnum]; let pred_val = self.func.dfg.inst_variable_args(pred_inst)[argnum];
dbg!("Checking {}: {}: {}", dbg!(
"Checking {}: {}: {}",
pred_val, pred_val,
pred_ebb, pred_ebb,
self.func.dfg.display_inst(pred_inst, self.isa)); self.func.dfg.display_inst(pred_inst, self.isa)
);
// Never coalesce incoming function arguments on the stack. These arguments are // Never coalesce incoming function arguments on the stack. These arguments are
// pre-spilled, and the rest of the virtual register would be forced to spill to the // pre-spilled, and the rest of the virtual register would be forced to spill to the
@@ -409,7 +425,8 @@ impl<'a> Context<'a> {
if Some(def_ebb) == self.func.layout.entry_block() && if Some(def_ebb) == self.func.layout.entry_block() &&
self.func.signature.argument_types[def_num] self.func.signature.argument_types[def_num]
.location .location
.is_stack() { .is_stack()
{
dbg!("Isolating incoming stack parameter {}", pred_val); dbg!("Isolating incoming stack parameter {}", pred_val);
let new_val = self.split_pred(pred_inst, pred_ebb, argnum, pred_val); let new_val = self.split_pred(pred_inst, pred_ebb, argnum, pred_val);
assert!(self.add_class(new_val).is_ok()); assert!(self.add_class(new_val).is_ok());
@@ -426,7 +443,8 @@ impl<'a> Context<'a> {
if self.liveness if self.liveness
.get(a) .get(a)
.expect("No live range for interfering value") .expect("No live range for interfering value")
.reaches_use(pred_inst, pred_ebb, &self.func.layout) { .reaches_use(pred_inst, pred_ebb, &self.func.layout)
{
// Splitting at `pred_inst` wouldn't resolve the interference, so we need to // Splitting at `pred_inst` wouldn't resolve the interference, so we need to
// start over. // start over.
return Some(a); return Some(a);
@@ -435,8 +453,10 @@ impl<'a> Context<'a> {
// The local conflict could be avoided by splitting at this predecessor, so try // The local conflict could be avoided by splitting at this predecessor, so try
// that. This split is not necessarily required, but it allows us to make progress. // that. This split is not necessarily required, but it allows us to make progress.
let new_val = self.split_pred(pred_inst, pred_ebb, argnum, pred_val); let new_val = self.split_pred(pred_inst, pred_ebb, argnum, pred_val);
assert!(self.add_class(new_val).is_ok(), assert!(
"Splitting didn't resolve conflict."); self.add_class(new_val).is_ok(),
"Splitting didn't resolve conflict."
);
} }
} }
@@ -447,42 +467,52 @@ impl<'a> Context<'a> {
/// ///
/// Leave `self.values` unchanged on failure. /// Leave `self.values` unchanged on failure.
fn add_class(&mut self, value: Value) -> Result<(), (Value, Value)> { fn add_class(&mut self, value: Value) -> Result<(), (Value, Value)> {
self.forest self.forest.try_merge(
.try_merge(&self.values, &self.values,
self.virtregs.congruence_class(&value), self.virtregs.congruence_class(&value),
&self.func.dfg, &self.func.dfg,
&self.func.layout, &self.func.layout,
self.domtree, self.domtree,
self.liveness)?; self.liveness,
)?;
self.forest.swap(&mut self.values); self.forest.swap(&mut self.values);
Ok(()) Ok(())
} }
/// Split the congruence class for the `argnum` argument to `pred_inst` by inserting a copy. /// Split the congruence class for the `argnum` argument to `pred_inst` by inserting a copy.
fn split_pred(&mut self, fn split_pred(
&mut self,
pred_inst: Inst, pred_inst: Inst,
pred_ebb: Ebb, pred_ebb: Ebb,
argnum: usize, argnum: usize,
pred_val: Value) pred_val: Value,
-> Value { ) -> Value {
let mut pos = EncCursor::new(self.func, self.isa).at_inst(pred_inst); let mut pos = EncCursor::new(self.func, self.isa).at_inst(pred_inst);
let copy = pos.ins().copy(pred_val); let copy = pos.ins().copy(pred_val);
let inst = pos.built_inst(); let inst = pos.built_inst();
dbg!("Inserted {}, before {}: {}", dbg!(
"Inserted {}, before {}: {}",
pos.display_inst(inst), pos.display_inst(inst),
pred_ebb, pred_ebb,
pos.display_inst(pred_inst)); pos.display_inst(pred_inst)
);
// Create a live range for the new value. // Create a live range for the new value.
let affinity = Affinity::new(&self.encinfo let affinity = Affinity::new(
&self.encinfo
.operand_constraints(pos.func.encodings[inst]) .operand_constraints(pos.func.encodings[inst])
.expect("Bad copy encoding") .expect("Bad copy encoding")
.outs .outs
[0]); [0],
);
self.liveness.create_dead(copy, inst, affinity); self.liveness.create_dead(copy, inst, affinity);
self.liveness self.liveness.extend_locally(
.extend_locally(copy, pred_ebb, pred_inst, &pos.func.layout); copy,
pred_ebb,
pred_inst,
&pos.func.layout,
);
pos.func.dfg.inst_variable_args_mut(pred_inst)[argnum] = copy; pos.func.dfg.inst_variable_args_mut(pred_inst)[argnum] = copy;
self.split_values.push(copy); self.split_values.push(copy);
@@ -500,21 +530,29 @@ impl<'a> Context<'a> {
let inst = pos.built_inst(); let inst = pos.built_inst();
self.liveness.move_def_locally(succ_val, inst); self.liveness.move_def_locally(succ_val, inst);
dbg!("Inserted {}, following {}({}: {})", dbg!(
"Inserted {}, following {}({}: {})",
pos.display_inst(inst), pos.display_inst(inst),
ebb, ebb,
new_val, new_val,
ty); ty
);
// Create a live range for the new value. // Create a live range for the new value.
let affinity = Affinity::new(&self.encinfo let affinity = Affinity::new(
&self.encinfo
.operand_constraints(pos.func.encodings[inst]) .operand_constraints(pos.func.encodings[inst])
.expect("Bad copy encoding") .expect("Bad copy encoding")
.outs .outs
[0]); [0],
);
self.liveness.create_dead(new_val, ebb, affinity); self.liveness.create_dead(new_val, ebb, affinity);
self.liveness self.liveness.extend_locally(
.extend_locally(new_val, ebb, inst, &pos.func.layout); new_val,
ebb,
inst,
&pos.func.layout,
);
self.split_values.push(new_val); self.split_values.push(new_val);
new_val new_val

View File

@@ -105,12 +105,14 @@ impl Coloring {
} }
/// Run the coloring algorithm over `func`. /// Run the coloring algorithm over `func`.
pub fn run(&mut self, pub fn run(
&mut self,
isa: &TargetIsa, isa: &TargetIsa,
func: &mut Function, func: &mut Function,
domtree: &DominatorTree, domtree: &DominatorTree,
liveness: &mut Liveness, liveness: &mut Liveness,
tracker: &mut LiveValueTracker) { tracker: &mut LiveValueTracker,
) {
dbg!("Coloring for:\n{}", func.display(isa)); dbg!("Coloring for:\n{}", func.display(isa));
let mut ctx = Context { let mut ctx = Context {
isa, isa,
@@ -150,7 +152,8 @@ impl<'a> Context<'a> {
pos.goto_top(ebb); pos.goto_top(ebb);
while let Some(inst) = pos.next_inst() { while let Some(inst) = pos.next_inst() {
if let Some(constraints) = self.encinfo.operand_constraints(func.encodings[inst]) { if let Some(constraints) = self.encinfo.operand_constraints(func.encodings[inst]) {
self.visit_inst(inst, self.visit_inst(
inst,
constraints, constraints,
&mut pos, &mut pos,
&mut func.dfg, &mut func.dfg,
@@ -158,7 +161,8 @@ impl<'a> Context<'a> {
&mut regs, &mut regs,
&mut func.locations, &mut func.locations,
&mut func.encodings, &mut func.encodings,
&func.signature); &func.signature,
);
} else { } else {
let (_throughs, kills) = tracker.process_ghost(inst); let (_throughs, kills) = tracker.process_ghost(inst);
self.process_ghost_kills(kills, &mut regs, &func.locations); self.process_ghost_kills(kills, &mut regs, &func.locations);
@@ -170,11 +174,12 @@ impl<'a> Context<'a> {
/// Visit the `ebb` header. /// Visit the `ebb` header.
/// ///
/// Initialize the set of live registers and color the arguments to `ebb`. /// Initialize the set of live registers and color the arguments to `ebb`.
fn visit_ebb_header(&self, fn visit_ebb_header(
&self,
ebb: Ebb, ebb: Ebb,
func: &mut Function, func: &mut Function,
tracker: &mut LiveValueTracker) tracker: &mut LiveValueTracker,
-> AllocatableSet { ) -> AllocatableSet {
// Reposition the live value tracker and deal with the EBB arguments. // Reposition the live value tracker and deal with the EBB arguments.
tracker.ebb_top(ebb, &func.dfg, self.liveness, &func.layout, self.domtree); tracker.ebb_top(ebb, &func.dfg, self.liveness, &func.layout, self.domtree);
@@ -204,10 +209,12 @@ impl<'a> Context<'a> {
.get(value) .get(value)
.expect("No live range for live-in") .expect("No live range for live-in")
.affinity; .affinity;
dbg!("Live-in: {}:{} in {}", dbg!(
"Live-in: {}:{} in {}",
value, value,
affinity.display(&self.reginfo), affinity.display(&self.reginfo),
func.locations[value].display(&self.reginfo)); func.locations[value].display(&self.reginfo)
);
if let Affinity::Reg(rci) = affinity { if let Affinity::Reg(rci) = affinity {
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);
let loc = func.locations[value]; let loc = func.locations[value];
@@ -230,11 +237,12 @@ impl<'a> Context<'a> {
/// function signature. /// function signature.
/// ///
/// Return the set of remaining allocatable registers after filtering out the dead arguments. /// Return the set of remaining allocatable registers after filtering out the dead arguments.
fn color_entry_args(&self, fn color_entry_args(
&self,
sig: &Signature, sig: &Signature,
args: &[LiveValue], args: &[LiveValue],
locations: &mut ValueLocations) locations: &mut ValueLocations,
-> AllocatableSet { ) -> AllocatableSet {
assert_eq!(sig.argument_types.len(), args.len()); assert_eq!(sig.argument_types.len(), args.len());
let mut regs = self.usable_regs.clone(); let mut regs = self.usable_regs.clone();
@@ -250,10 +258,12 @@ impl<'a> Context<'a> {
locations[lv.value] = ValueLoc::Reg(reg); locations[lv.value] = ValueLoc::Reg(reg);
} else { } else {
// This should have been fixed by the reload pass. // This should have been fixed by the reload pass.
panic!("Entry arg {} has {} affinity, but ABI {}", panic!(
"Entry arg {} has {} affinity, but ABI {}",
lv.value, lv.value,
lv.affinity.display(&self.reginfo), lv.affinity.display(&self.reginfo),
abi.display(&self.reginfo)); abi.display(&self.reginfo)
);
} }
} }
@@ -273,7 +283,8 @@ impl<'a> Context<'a> {
/// ///
/// Update `regs` to reflect the allocated registers after `inst`, including removing any dead /// Update `regs` to reflect the allocated registers after `inst`, including removing any dead
/// or killed values from the set. /// or killed values from the set.
fn visit_inst(&mut self, fn visit_inst(
&mut self,
inst: Inst, inst: Inst,
constraints: &RecipeConstraints, constraints: &RecipeConstraints,
pos: &mut Cursor, pos: &mut Cursor,
@@ -282,10 +293,13 @@ impl<'a> Context<'a> {
regs: &mut AllocatableSet, regs: &mut AllocatableSet,
locations: &mut ValueLocations, locations: &mut ValueLocations,
encodings: &mut InstEncodings, encodings: &mut InstEncodings,
func_signature: &Signature) { func_signature: &Signature,
dbg!("Coloring {}\n {}", ) {
dbg!(
"Coloring {}\n {}",
dfg.display_inst(inst, self.isa), dfg.display_inst(inst, self.isa),
regs.display(&self.reginfo)); regs.display(&self.reginfo)
);
// EBB whose arguments should be colored to match the current branch instruction's // EBB whose arguments should be colored to match the current branch instruction's
// arguments. // arguments.
@@ -310,10 +324,12 @@ impl<'a> Context<'a> {
} else { } else {
// This is a multi-way branch like `br_table`. We only support arguments on // This is a multi-way branch like `br_table`. We only support arguments on
// single-destination branches. // single-destination branches.
assert_eq!(dfg.inst_variable_args(inst).len(), assert_eq!(
dfg.inst_variable_args(inst).len(),
0, 0,
"Can't handle EBB arguments: {}", "Can't handle EBB arguments: {}",
dfg.display_inst(inst, self.isa)); dfg.display_inst(inst, self.isa)
);
self.undivert_regs(|lr| !lr.is_local()); self.undivert_regs(|lr| !lr.is_local());
} }
} }
@@ -329,10 +345,11 @@ impl<'a> Context<'a> {
// Get rid of the killed values. // Get rid of the killed values.
for lv in kills { for lv in kills {
if let Affinity::Reg(rci) = lv.affinity { if let Affinity::Reg(rci) = lv.affinity {
self.solver self.solver.add_kill(
.add_kill(lv.value, lv.value,
self.reginfo.rc(rci), self.reginfo.rc(rci),
self.divert.reg(lv.value, locations)); self.divert.reg(lv.value, locations),
);
} }
} }
@@ -350,9 +367,9 @@ impl<'a> Context<'a> {
// Finally, we've fully programmed the constraint solver. // Finally, we've fully programmed the constraint solver.
// We expect a quick solution in most cases. // We expect a quick solution in most cases.
let mut output_regs = self.solver let mut output_regs = self.solver.quick_solve().unwrap_or_else(
.quick_solve() |_| self.iterate_solution(),
.unwrap_or_else(|_| self.iterate_solution()); );
// The solution and/or fixed input constraints may require us to shuffle the set of live // The solution and/or fixed input constraints may require us to shuffle the set of live
@@ -399,30 +416,42 @@ impl<'a> Context<'a> {
} }
/// Program the input-side constraints for `inst` into the constraint solver. /// Program the input-side constraints for `inst` into the constraint solver.
fn program_input_constraints(&mut self, fn program_input_constraints(
&mut self,
inst: Inst, inst: Inst,
constraints: &[OperandConstraint], constraints: &[OperandConstraint],
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
locations: &ValueLocations) { locations: &ValueLocations,
for (op, &value) in constraints ) {
.iter() for (op, &value) in constraints.iter().zip(dfg.inst_args(inst)).filter(
.zip(dfg.inst_args(inst)) |&(op, _)| {
.filter(|&(op, _)| op.kind != ConstraintKind::Stack) { op.kind != ConstraintKind::Stack
},
)
{
// Reload pass is supposed to ensure that all arguments to register operands are // Reload pass is supposed to ensure that all arguments to register operands are
// already in a register. // already in a register.
let cur_reg = self.divert.reg(value, locations); let cur_reg = self.divert.reg(value, locations);
match op.kind { match op.kind {
ConstraintKind::FixedReg(regunit) => { ConstraintKind::FixedReg(regunit) => {
if regunit != cur_reg { if regunit != cur_reg {
self.solver self.solver.reassign_in(
.reassign_in(value, op.regclass, cur_reg, regunit); value,
op.regclass,
cur_reg,
regunit,
);
} }
} }
ConstraintKind::Reg | ConstraintKind::Reg |
ConstraintKind::Tied(_) => { ConstraintKind::Tied(_) => {
if !op.regclass.contains(cur_reg) { if !op.regclass.contains(cur_reg) {
self.solver self.solver.add_var(
.add_var(value, op.regclass, cur_reg, &self.reginfo); value,
op.regclass,
cur_reg,
&self.reginfo,
);
} }
} }
ConstraintKind::Stack => unreachable!(), ConstraintKind::Stack => unreachable!(),
@@ -433,18 +462,21 @@ impl<'a> Context<'a> {
/// Program the input-side ABI constraints for `inst` into the constraint solver. /// Program the input-side ABI constraints for `inst` into the constraint solver.
/// ///
/// ABI constraints are the fixed register assignments used for calls and returns. /// ABI constraints are the fixed register assignments used for calls and returns.
fn program_input_abi(&mut self, fn program_input_abi(
&mut self,
inst: Inst, inst: Inst,
abi_types: &[ArgumentType], abi_types: &[ArgumentType],
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
locations: &ValueLocations) { locations: &ValueLocations,
) {
for (abi, &value) in abi_types.iter().zip(dfg.inst_variable_args(inst)) { for (abi, &value) in abi_types.iter().zip(dfg.inst_variable_args(inst)) {
if let ArgumentLoc::Reg(reg) = abi.location { if let ArgumentLoc::Reg(reg) = abi.location {
if let Affinity::Reg(rci) = if let Affinity::Reg(rci) =
self.liveness self.liveness
.get(value) .get(value)
.expect("ABI register must have live range") .expect("ABI register must have live range")
.affinity { .affinity
{
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);
let cur_reg = self.divert.reg(value, locations); let cur_reg = self.divert.reg(value, locations);
self.solver.reassign_in(value, rc, cur_reg, reg); self.solver.reassign_in(value, rc, cur_reg, reg);
@@ -464,13 +496,14 @@ impl<'a> Context<'a> {
/// ///
/// Returns true if this is the first time a branch to `dest` is seen, so the `dest` argument /// Returns true if this is the first time a branch to `dest` is seen, so the `dest` argument
/// values should be colored after `shuffle_inputs`. /// values should be colored after `shuffle_inputs`.
fn program_ebb_arguments(&mut self, fn program_ebb_arguments(
&mut self,
inst: Inst, inst: Inst,
dest: Ebb, dest: Ebb,
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
layout: &Layout, layout: &Layout,
locations: &ValueLocations) locations: &ValueLocations,
-> bool { ) -> bool {
// Find diverted registers that are live-in to `dest` and reassign them to their global // Find diverted registers that are live-in to `dest` and reassign them to their global
// home. // home.
// //
@@ -523,11 +556,13 @@ impl<'a> Context<'a> {
/// register state. /// register state.
/// ///
/// This function is only called when `program_ebb_arguments()` returned `true`. /// This function is only called when `program_ebb_arguments()` returned `true`.
fn color_ebb_arguments(&mut self, fn color_ebb_arguments(
&mut self,
inst: Inst, inst: Inst,
dest: Ebb, dest: Ebb,
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
locations: &mut ValueLocations) { locations: &mut ValueLocations,
) {
let br_args = dfg.inst_variable_args(inst); let br_args = dfg.inst_variable_args(inst);
let dest_args = dfg.ebb_args(dest); let dest_args = dfg.ebb_args(dest);
assert_eq!(br_args.len(), dest_args.len()); assert_eq!(br_args.len(), dest_args.len());
@@ -549,20 +584,23 @@ impl<'a> Context<'a> {
/// Find all diverted registers where `pred` returns `true` and undo their diversion so they /// Find all diverted registers where `pred` returns `true` and undo their diversion so they
/// are reallocated to their global register assignments. /// are reallocated to their global register assignments.
fn undivert_regs<Pred>(&mut self, mut pred: Pred) fn undivert_regs<Pred>(&mut self, mut pred: Pred)
where Pred: FnMut(&LiveRange) -> bool where
Pred: FnMut(&LiveRange) -> bool,
{ {
for rdiv in self.divert.all() { for rdiv in self.divert.all() {
let lr = self.liveness let lr = self.liveness.get(rdiv.value).expect(
.get(rdiv.value) "Missing live range for diverted register",
.expect("Missing live range for diverted register"); );
if pred(lr) { if pred(lr) {
if let Affinity::Reg(rci) = lr.affinity { if let Affinity::Reg(rci) = lr.affinity {
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);
self.solver.reassign_in(rdiv.value, rc, rdiv.to, rdiv.from); self.solver.reassign_in(rdiv.value, rc, rdiv.to, rdiv.from);
} else { } else {
panic!("Diverted register {} with {} affinity", panic!(
"Diverted register {} with {} affinity",
rdiv.value, rdiv.value,
lr.affinity.display(&self.reginfo)); lr.affinity.display(&self.reginfo)
);
} }
} }
} }
@@ -570,9 +608,7 @@ impl<'a> Context<'a> {
// Find existing live values that conflict with the fixed input register constraints programmed // Find existing live values that conflict with the fixed input register constraints programmed
// into the constraint solver. Convert them to solver variables so they can be diverted. // into the constraint solver. Convert them to solver variables so they can be diverted.
fn divert_fixed_input_conflicts(&mut self, fn divert_fixed_input_conflicts(&mut self, live: &[LiveValue], locations: &mut ValueLocations) {
live: &[LiveValue],
locations: &mut ValueLocations) {
for lv in live { for lv in live {
if let Affinity::Reg(rci) = lv.affinity { if let Affinity::Reg(rci) = lv.affinity {
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);
@@ -587,11 +623,13 @@ impl<'a> Context<'a> {
/// Program any fixed-register output constraints into the solver. This may also detect /// Program any fixed-register output constraints into the solver. This may also detect
/// conflicts between live-through registers and fixed output registers. These live-through /// conflicts between live-through registers and fixed output registers. These live-through
/// values need to be turned into solver variables so they can be reassigned. /// values need to be turned into solver variables so they can be reassigned.
fn program_fixed_outputs(&mut self, fn program_fixed_outputs(
&mut self,
constraints: &[OperandConstraint], constraints: &[OperandConstraint],
defs: &[LiveValue], defs: &[LiveValue],
throughs: &[LiveValue], throughs: &[LiveValue],
locations: &mut ValueLocations) { locations: &mut ValueLocations,
) {
for (op, lv) in constraints.iter().zip(defs) { for (op, lv) in constraints.iter().zip(defs) {
if let ConstraintKind::FixedReg(reg) = op.kind { if let ConstraintKind::FixedReg(reg) = op.kind {
self.add_fixed_output(lv.value, op.regclass, reg, throughs, locations); self.add_fixed_output(lv.value, op.regclass, reg, throughs, locations);
@@ -602,11 +640,13 @@ impl<'a> Context<'a> {
/// Program the output-side ABI constraints for `inst` into the constraint solver. /// Program the output-side ABI constraints for `inst` into the constraint solver.
/// ///
/// That means return values for a call instruction. /// That means return values for a call instruction.
fn program_output_abi(&mut self, fn program_output_abi(
&mut self,
abi_types: &[ArgumentType], abi_types: &[ArgumentType],
defs: &[LiveValue], defs: &[LiveValue],
throughs: &[LiveValue], throughs: &[LiveValue],
locations: &mut ValueLocations) { locations: &mut ValueLocations,
) {
// It's technically possible for a call instruction to have fixed results before the // It's technically possible for a call instruction to have fixed results before the
// variable list of results, but we have no known instances of that. // variable list of results, but we have no known instances of that.
// Just assume all results are variable return values. // Just assume all results are variable return values.
@@ -624,12 +664,14 @@ impl<'a> Context<'a> {
} }
/// Add a single fixed output value to the solver. /// Add a single fixed output value to the solver.
fn add_fixed_output(&mut self, fn add_fixed_output(
&mut self,
value: Value, value: Value,
rc: RegClass, rc: RegClass,
reg: RegUnit, reg: RegUnit,
throughs: &[LiveValue], throughs: &[LiveValue],
locations: &mut ValueLocations) { locations: &mut ValueLocations,
) {
if !self.solver.add_fixed_output(rc, reg) { if !self.solver.add_fixed_output(rc, reg) {
// The fixed output conflicts with some of the live-through registers. // The fixed output conflicts with some of the live-through registers.
for lv in throughs { for lv in throughs {
@@ -656,12 +698,14 @@ impl<'a> Context<'a> {
/// Program the output-side constraints for `inst` into the constraint solver. /// Program the output-side constraints for `inst` into the constraint solver.
/// ///
/// It is assumed that all fixed outputs have already been handled. /// It is assumed that all fixed outputs have already been handled.
fn program_output_constraints(&mut self, fn program_output_constraints(
&mut self,
inst: Inst, inst: Inst,
constraints: &[OperandConstraint], constraints: &[OperandConstraint],
defs: &[LiveValue], defs: &[LiveValue],
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
locations: &mut ValueLocations) { locations: &mut ValueLocations,
) {
for (op, lv) in constraints.iter().zip(defs) { for (op, lv) in constraints.iter().zip(defs) {
match op.kind { match op.kind {
ConstraintKind::FixedReg(_) | ConstraintKind::FixedReg(_) |
@@ -673,8 +717,11 @@ impl<'a> Context<'a> {
// Find the input operand we're tied to. // Find the input operand we're tied to.
// The solver doesn't care about the output value. // The solver doesn't care about the output value.
let arg = dfg.inst_args(inst)[num as usize]; let arg = dfg.inst_args(inst)[num as usize];
self.solver self.solver.add_tied_input(
.add_tied_input(arg, op.regclass, self.divert.reg(arg, locations)); arg,
op.regclass,
self.divert.reg(arg, locations),
);
} }
} }
} }
@@ -695,11 +742,13 @@ impl<'a> Context<'a> {
/// before. /// before.
/// ///
/// The solver needs to be reminded of the available registers before any moves are inserted. /// The solver needs to be reminded of the available registers before any moves are inserted.
fn shuffle_inputs(&mut self, fn shuffle_inputs(
&mut self,
pos: &mut Cursor, pos: &mut Cursor,
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
regs: &mut AllocatableSet, regs: &mut AllocatableSet,
encodings: &mut InstEncodings) { encodings: &mut InstEncodings,
) {
self.solver.schedule_moves(regs); self.solver.schedule_moves(regs);
for m in self.solver.moves() { for m in self.solver.moves() {
@@ -729,10 +778,12 @@ impl<'a> Context<'a> {
/// Process kills on a ghost instruction. /// Process kills on a ghost instruction.
/// - Forget diversions. /// - Forget diversions.
/// - Free killed registers. /// - Free killed registers.
fn process_ghost_kills(&mut self, fn process_ghost_kills(
&mut self,
kills: &[LiveValue], kills: &[LiveValue],
regs: &mut AllocatableSet, regs: &mut AllocatableSet,
locations: &ValueLocations) { locations: &ValueLocations,
) {
for lv in kills { for lv in kills {
if let Affinity::Reg(rci) = lv.affinity { if let Affinity::Reg(rci) = lv.affinity {
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);

View File

@@ -53,12 +53,13 @@ impl Context {
/// ///
/// After register allocation, all values in `func` have been assigned to a register or stack /// After register allocation, all values in `func` have been assigned to a register or stack
/// location that is consistent with instruction encoding constraints. /// location that is consistent with instruction encoding constraints.
pub fn run(&mut self, pub fn run(
&mut self,
isa: &TargetIsa, isa: &TargetIsa,
func: &mut Function, func: &mut Function,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
domtree: &DominatorTree) domtree: &DominatorTree,
-> CtonResult { ) -> CtonResult {
// `Liveness` and `Coloring` are self-clearing. // `Liveness` and `Coloring` are self-clearing.
self.virtregs.clear(); self.virtregs.clear();
@@ -74,13 +75,14 @@ impl Context {
} }
// Pass: Coalesce and create conventional SSA form. // Pass: Coalesce and create conventional SSA form.
self.coalescing self.coalescing.conventional_ssa(
.conventional_ssa(isa, isa,
func, func,
cfg, cfg,
domtree, domtree,
&mut self.liveness, &mut self.liveness,
&mut self.virtregs); &mut self.virtregs,
);
if isa.flags().enable_verifier() { if isa.flags().enable_verifier() {
verify_context(func, cfg, domtree, Some(isa))?; verify_context(func, cfg, domtree, Some(isa))?;
@@ -90,14 +92,15 @@ impl Context {
// Pass: Spilling. // Pass: Spilling.
self.spilling self.spilling.run(
.run(isa, isa,
func, func,
domtree, domtree,
&mut self.liveness, &mut self.liveness,
&self.virtregs, &self.virtregs,
&mut self.topo, &mut self.topo,
&mut self.tracker); &mut self.tracker,
);
if isa.flags().enable_verifier() { if isa.flags().enable_verifier() {
verify_context(func, cfg, domtree, Some(isa))?; verify_context(func, cfg, domtree, Some(isa))?;
@@ -106,13 +109,14 @@ impl Context {
} }
// Pass: Reload. // Pass: Reload.
self.reload self.reload.run(
.run(isa, isa,
func, func,
domtree, domtree,
&mut self.liveness, &mut self.liveness,
&mut self.topo, &mut self.topo,
&mut self.tracker); &mut self.tracker,
);
if isa.flags().enable_verifier() { if isa.flags().enable_verifier() {
verify_context(func, cfg, domtree, Some(isa))?; verify_context(func, cfg, domtree, Some(isa))?;
@@ -121,8 +125,13 @@ impl Context {
} }
// Pass: Coloring. // Pass: Coloring.
self.coloring self.coloring.run(
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker); isa,
func,
domtree,
&mut self.liveness,
&mut self.tracker,
);
if isa.flags().enable_verifier() { if isa.flags().enable_verifier() {
verify_context(func, cfg, domtree, Some(isa))?; verify_context(func, cfg, domtree, Some(isa))?;

View File

@@ -93,10 +93,11 @@ impl RegDiversions {
/// ///
/// Returns the `to` register of the removed diversion. /// Returns the `to` register of the removed diversion.
pub fn remove(&mut self, value: Value) -> Option<RegUnit> { pub fn remove(&mut self, value: Value) -> Option<RegUnit> {
self.current self.current.iter().position(|d| d.value == value).map(
.iter() |i| {
.position(|d| d.value == value) self.current.swap_remove(i).to
.map(|i| self.current.swap_remove(i).to) },
)
} }
} }
@@ -113,12 +114,14 @@ mod tests {
let v2 = Value::new(2); let v2 = Value::new(2);
divs.regmove(v1, 10, 12); divs.regmove(v1, 10, 12);
assert_eq!(divs.diversion(v1), assert_eq!(
divs.diversion(v1),
Some(&Diversion { Some(&Diversion {
value: v1, value: v1,
from: 10, from: 10,
to: 12, to: 12,
})); })
);
assert_eq!(divs.diversion(v2), None); assert_eq!(divs.diversion(v2), None);
divs.regmove(v1, 12, 11); divs.regmove(v1, 12, 11);

View File

@@ -74,8 +74,7 @@ impl LiveValueVec {
/// Add a new live value to `values`. Copy some properties from `lr`. /// Add a new live value to `values`. Copy some properties from `lr`.
fn push(&mut self, value: Value, endpoint: Inst, lr: &LiveRange) { fn push(&mut self, value: Value, endpoint: Inst, lr: &LiveRange) {
self.values self.values.push(LiveValue {
.push(LiveValue {
value, value,
endpoint, endpoint,
affinity: lr.affinity, affinity: lr.affinity,
@@ -157,13 +156,14 @@ impl LiveValueTracker {
/// from the immediate dominator. The second slice is the set of `ebb` arguments that are live. /// from the immediate dominator. The second slice is the set of `ebb` arguments that are live.
/// ///
/// Dead arguments with no uses are included in `args`. Call `drop_dead_args()` to remove them. /// Dead arguments with no uses are included in `args`. Call `drop_dead_args()` to remove them.
pub fn ebb_top(&mut self, pub fn ebb_top(
&mut self,
ebb: Ebb, ebb: Ebb,
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
liveness: &Liveness, liveness: &Liveness,
layout: &Layout, layout: &Layout,
domtree: &DominatorTree) domtree: &DominatorTree,
-> (&[LiveValue], &[LiveValue]) { ) -> (&[LiveValue], &[LiveValue]) {
// Start over, compute the set of live values at the top of the EBB from two sources: // Start over, compute the set of live values at the top of the EBB from two sources:
// //
// 1. Values that were live before `ebb`'s immediate dominator, filtered for those that are // 1. Values that were live before `ebb`'s immediate dominator, filtered for those that are
@@ -179,14 +179,14 @@ impl LiveValueTracker {
// If the immediate dominator exits, we must have a stored list for it. This is a // If the immediate dominator exits, we must have a stored list for it. This is a
// requirement to the order EBBs are visited: All dominators must have been processed // requirement to the order EBBs are visited: All dominators must have been processed
// before the current EBB. // before the current EBB.
let idom_live_list = self.idom_sets let idom_live_list = self.idom_sets.get(&idom).expect(
.get(&idom) "No stored live set for dominator",
.expect("No stored live set for dominator"); );
// Get just the values that are live-in to `ebb`. // Get just the values that are live-in to `ebb`.
for &value in idom_live_list.as_slice(&self.idom_pool) { for &value in idom_live_list.as_slice(&self.idom_pool) {
let lr = liveness let lr = liveness.get(value).expect(
.get(value) "Immediate dominator value has no live range",
.expect("Immediate dominator value has no live range"); );
// Check if this value is live-in here. // Check if this value is live-in here.
if let Some(endpoint) = lr.livein_local_end(ebb, layout) { if let Some(endpoint) = lr.livein_local_end(ebb, layout) {
@@ -198,9 +198,9 @@ impl LiveValueTracker {
// Now add all the live arguments to `ebb`. // Now add all the live arguments to `ebb`.
let first_arg = self.live.values.len(); let first_arg = self.live.values.len();
for &value in dfg.ebb_args(ebb) { for &value in dfg.ebb_args(ebb) {
let lr = liveness let lr = liveness.get(value).expect(
.get(value) "EBB argument value has no live range",
.expect("EBB argument value has no live range"); );
assert_eq!(lr.def(), ebb.into()); assert_eq!(lr.def(), ebb.into());
match lr.def_local_end().into() { match lr.def_local_end().into() {
ExpandedProgramPoint::Inst(endpoint) => { ExpandedProgramPoint::Inst(endpoint) => {
@@ -209,13 +209,18 @@ impl LiveValueTracker {
ExpandedProgramPoint::Ebb(local_ebb) => { ExpandedProgramPoint::Ebb(local_ebb) => {
// This is a dead EBB argument which is not even live into the first // This is a dead EBB argument which is not even live into the first
// instruction in the EBB. // instruction in the EBB.
assert_eq!(local_ebb, assert_eq!(
local_ebb,
ebb, ebb,
"EBB argument live range ends at wrong EBB header"); "EBB argument live range ends at wrong EBB header"
);
// Give this value a fake endpoint that is the first instruction in the EBB. // Give this value a fake endpoint that is the first instruction in the EBB.
// We expect it to be removed by calling `drop_dead_args()`. // We expect it to be removed by calling `drop_dead_args()`.
self.live self.live.push(
.push(value, layout.first_inst(ebb).expect("Empty EBB"), lr); value,
layout.first_inst(ebb).expect("Empty EBB"),
lr,
);
} }
} }
} }
@@ -241,11 +246,12 @@ impl LiveValueTracker {
/// ///
/// The `drop_dead()` method must be called next to actually remove the dead values from the /// The `drop_dead()` method must be called next to actually remove the dead values from the
/// tracked set after the two returned slices are no longer needed. /// tracked set after the two returned slices are no longer needed.
pub fn process_inst(&mut self, pub fn process_inst(
&mut self,
inst: Inst, inst: Inst,
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
liveness: &Liveness) liveness: &Liveness,
-> (&[LiveValue], &[LiveValue], &[LiveValue]) { ) -> (&[LiveValue], &[LiveValue], &[LiveValue]) {
// Save a copy of the live values before any branches or jumps that could be somebody's // Save a copy of the live values before any branches or jumps that could be somebody's
// immediate dominator. // immediate dominator.
match dfg[inst].analyze_branch(&dfg.value_lists) { match dfg[inst].analyze_branch(&dfg.value_lists) {
@@ -272,9 +278,11 @@ impl LiveValueTracker {
} }
} }
(&self.live.values[0..first_kill], (
&self.live.values[0..first_kill],
&self.live.values[first_kill..first_def], &self.live.values[first_kill..first_def],
&self.live.values[first_def..]) &self.live.values[first_def..],
)
} }
/// Prepare to move past a ghost instruction. /// Prepare to move past a ghost instruction.
@@ -310,7 +318,8 @@ impl LiveValueTracker {
/// Any values where `f` returns true are spilled and will be treated as if their affinity was /// Any values where `f` returns true are spilled and will be treated as if their affinity was
/// `Stack`. /// `Stack`.
pub fn process_spills<F>(&mut self, mut f: F) pub fn process_spills<F>(&mut self, mut f: F)
where F: FnMut(Value) -> bool where
F: FnMut(Value) -> bool,
{ {
for lv in &mut self.live.values { for lv in &mut self.live.values {
if f(lv.value) { if f(lv.value) {
@@ -324,9 +333,7 @@ impl LiveValueTracker {
let values = self.live.values.iter().map(|lv| lv.value); let values = self.live.values.iter().map(|lv| lv.value);
let pool = &mut self.idom_pool; let pool = &mut self.idom_pool;
// If there already is a set saved for `idom`, just keep it. // If there already is a set saved for `idom`, just keep it.
self.idom_sets self.idom_sets.entry(idom).or_insert_with(|| {
.entry(idom)
.or_insert_with(|| {
let mut list = ValueList::default(); let mut list = ValueList::default();
list.extend(values, pool); list.extend(values, pool);
list list

View File

@@ -190,12 +190,13 @@ type LiveRangeSet = SparseMap<Value, LiveRange>;
/// Get a mutable reference to the live range for `value`. /// Get a mutable reference to the live range for `value`.
/// Create it if necessary. /// Create it if necessary.
fn get_or_create<'a>(lrset: &'a mut LiveRangeSet, fn get_or_create<'a>(
lrset: &'a mut LiveRangeSet,
value: Value, value: Value,
isa: &TargetIsa, isa: &TargetIsa,
func: &Function, func: &Function,
enc_info: &EncInfo) enc_info: &EncInfo,
-> &'a mut LiveRange { ) -> &'a mut LiveRange {
// It would be better to use `get_mut()` here, but that leads to borrow checker fighting // It would be better to use `get_mut()` here, but that leads to borrow checker fighting
// which can probably only be resolved by non-lexical lifetimes. // which can probably only be resolved by non-lexical lifetimes.
// https://github.com/rust-lang/rfcs/issues/811 // https://github.com/rust-lang/rfcs/issues/811
@@ -233,12 +234,14 @@ fn get_or_create<'a>(lrset: &'a mut LiveRangeSet,
} }
/// Extend the live range for `value` so it reaches `to` which must live in `ebb`. /// Extend the live range for `value` so it reaches `to` which must live in `ebb`.
fn extend_to_use(lr: &mut LiveRange, fn extend_to_use(
lr: &mut LiveRange,
ebb: Ebb, ebb: Ebb,
to: Inst, to: Inst,
worklist: &mut Vec<Ebb>, worklist: &mut Vec<Ebb>,
func: &Function, func: &Function,
cfg: &ControlFlowGraph) { cfg: &ControlFlowGraph,
) {
// This is our scratch working space, and we'll leave it empty when we return. // This is our scratch working space, and we'll leave it empty when we return.
assert!(worklist.is_empty()); assert!(worklist.is_empty());
@@ -309,10 +312,12 @@ impl Liveness {
/// ///
/// This asserts that `value` does not have an existing live range. /// This asserts that `value` does not have an existing live range.
pub fn create_dead<PP>(&mut self, value: Value, def: PP, affinity: Affinity) pub fn create_dead<PP>(&mut self, value: Value, def: PP, affinity: Affinity)
where PP: Into<ProgramPoint> where
PP: Into<ProgramPoint>,
{ {
let old = self.ranges let old = self.ranges.insert(
.insert(LiveRange::new(value, def.into(), affinity)); LiveRange::new(value, def.into(), affinity),
);
assert!(old.is_none(), "{} already has a live range", value); assert!(old.is_none(), "{} already has a live range", value);
} }
@@ -320,7 +325,8 @@ impl Liveness {
/// ///
/// The old and new def points must be in the same EBB, and before the end of the live range. /// The old and new def points must be in the same EBB, and before the end of the live range.
pub fn move_def_locally<PP>(&mut self, value: Value, def: PP) pub fn move_def_locally<PP>(&mut self, value: Value, def: PP)
where PP: Into<ProgramPoint> where
PP: Into<ProgramPoint>,
{ {
let mut lr = self.ranges.get_mut(value).expect("Value has no live range"); let mut lr = self.ranges.get_mut(value).expect("Value has no live range");
lr.move_def_locally(def.into()); lr.move_def_locally(def.into());
@@ -331,12 +337,13 @@ impl Liveness {
/// It is assumed the `value` is already live before `user` in `ebb`. /// It is assumed the `value` is already live before `user` in `ebb`.
/// ///
/// Returns a mutable reference to the value's affinity in case that also needs to be updated. /// Returns a mutable reference to the value's affinity in case that also needs to be updated.
pub fn extend_locally(&mut self, pub fn extend_locally(
&mut self,
value: Value, value: Value,
ebb: Ebb, ebb: Ebb,
user: Inst, user: Inst,
layout: &Layout) layout: &Layout,
-> &mut Affinity { ) -> &mut Affinity {
debug_assert_eq!(Some(ebb), layout.inst_ebb(user)); debug_assert_eq!(Some(ebb), layout.inst_ebb(user));
let mut lr = self.ranges.get_mut(value).expect("Value has no live range"); let mut lr = self.ranges.get_mut(value).expect("Value has no live range");
let livein = lr.extend_in_ebb(ebb, user, layout); let livein = lr.extend_in_ebb(ebb, user, layout);
@@ -401,7 +408,8 @@ impl Liveness {
if let Some(constraint) = operand_constraints.next() { if let Some(constraint) = operand_constraints.next() {
lr.affinity.merge(constraint, &reg_info); lr.affinity.merge(constraint, &reg_info);
} else if lr.affinity.is_none() && encoding.is_legal() && } else if lr.affinity.is_none() && encoding.is_legal() &&
!func.dfg[inst].opcode().is_branch() { !func.dfg[inst].opcode().is_branch()
{
// This is a real encoded instruction using a value that doesn't yet have a // This is a real encoded instruction using a value that doesn't yet have a
// concrete affinity. Most likely a call argument or a return value. Give // concrete affinity. Most likely a call argument or a return value. Give
// the value a register affinity matching the ABI type. // the value a register affinity matching the ABI type.

View File

@@ -250,11 +250,14 @@ impl LiveRange {
// We're assuming here that `to` never precedes `def_begin` in the same EBB, but we can't // We're assuming here that `to` never precedes `def_begin` in the same EBB, but we can't
// check it without a method for getting `to`'s EBB. // check it without a method for getting `to`'s EBB.
if order.cmp(ebb, self.def_end) != Ordering::Greater && if order.cmp(ebb, self.def_end) != Ordering::Greater &&
order.cmp(to, self.def_begin) != Ordering::Less { order.cmp(to, self.def_begin) != Ordering::Less
{
let to_pp = to.into(); let to_pp = to.into();
assert_ne!(to_pp, assert_ne!(
to_pp,
self.def_begin, self.def_begin,
"Can't use value in the defining instruction."); "Can't use value in the defining instruction."
);
if order.cmp(to, self.def_end) == Ordering::Greater { if order.cmp(to, self.def_end) == Ordering::Greater {
self.def_end = to_pp; self.def_end = to_pp;
} }
@@ -288,8 +291,10 @@ impl LiveRange {
let prev = n.checked_sub(1).and_then(|i| self.liveins.get(i)); let prev = n.checked_sub(1).and_then(|i| self.liveins.get(i));
let next = self.liveins.get(n); let next = self.liveins.get(n);
(prev.map_or(false, |prev| order.is_ebb_gap(prev.end, ebb)), (
next.map_or(false, |next| order.is_ebb_gap(to, next.begin))) prev.map_or(false, |prev| order.is_ebb_gap(prev.end, ebb)),
next.map_or(false, |next| order.is_ebb_gap(to, next.begin)),
)
}; };
match (coalesce_prev, coalesce_next) { match (coalesce_prev, coalesce_next) {
@@ -309,12 +314,13 @@ impl LiveRange {
} }
// Cannot coalesce; insert new interval // Cannot coalesce; insert new interval
(false, false) => { (false, false) => {
self.liveins self.liveins.insert(
.insert(n, n,
Interval { Interval {
begin: ebb, begin: ebb,
end: to, end: to,
}); },
);
} }
} }
@@ -372,9 +378,9 @@ impl LiveRange {
/// answer, but it is also possible that an even later program point is returned. So don't /// answer, but it is also possible that an even later program point is returned. So don't
/// depend on the returned `Inst` to belong to `ebb`. /// depend on the returned `Inst` to belong to `ebb`.
pub fn livein_local_end<PO: ProgramOrder>(&self, ebb: Ebb, order: &PO) -> Option<Inst> { pub fn livein_local_end<PO: ProgramOrder>(&self, ebb: Ebb, order: &PO) -> Option<Inst> {
self.find_ebb_interval(ebb, order) self.find_ebb_interval(ebb, order).ok().map(|n| {
.ok() self.liveins[n].end
.map(|n| self.liveins[n].end) })
} }
/// Get all the live-in intervals. /// Get all the live-in intervals.
@@ -384,11 +390,13 @@ impl LiveRange {
/// Check if this live range overlaps a definition in `ebb`. /// Check if this live range overlaps a definition in `ebb`.
pub fn overlaps_def<PO>(&self, def: ExpandedProgramPoint, ebb: Ebb, order: &PO) -> bool pub fn overlaps_def<PO>(&self, def: ExpandedProgramPoint, ebb: Ebb, order: &PO) -> bool
where PO: ProgramOrder where
PO: ProgramOrder,
{ {
// Check for an overlap with the local range. // Check for an overlap with the local range.
if order.cmp(def, self.def_begin) != Ordering::Less && if order.cmp(def, self.def_begin) != Ordering::Less &&
order.cmp(def, self.def_end) == Ordering::Less { order.cmp(def, self.def_end) == Ordering::Less
{
return true; return true;
} }
@@ -401,11 +409,13 @@ impl LiveRange {
/// Check if this live range reaches a use at `user` in `ebb`. /// Check if this live range reaches a use at `user` in `ebb`.
pub fn reaches_use<PO>(&self, user: Inst, ebb: Ebb, order: &PO) -> bool pub fn reaches_use<PO>(&self, user: Inst, ebb: Ebb, order: &PO) -> bool
where PO: ProgramOrder where
PO: ProgramOrder,
{ {
// Check for an overlap with the local range. // Check for an overlap with the local range.
if order.cmp(user, self.def_begin) == Ordering::Greater && if order.cmp(user, self.def_begin) == Ordering::Greater &&
order.cmp(user, self.def_end) != Ordering::Greater { order.cmp(user, self.def_end) != Ordering::Greater
{
return true; return true;
} }
@@ -418,7 +428,8 @@ impl LiveRange {
/// Check if this live range is killed at `user` in `ebb`. /// Check if this live range is killed at `user` in `ebb`.
pub fn killed_at<PO>(&self, user: Inst, ebb: Ebb, order: &PO) -> bool pub fn killed_at<PO>(&self, user: Inst, ebb: Ebb, order: &PO) -> bool
where PO: ProgramOrder where
PO: ProgramOrder,
{ {
self.def_local_end() == user.into() || self.livein_local_end(ebb, order) == Some(user) self.def_local_end() == user.into() || self.livein_local_end(ebb, order) == Some(user)
} }
@@ -447,8 +458,9 @@ mod tests {
impl ProgramOrder for ProgOrder { impl ProgramOrder for ProgOrder {
fn cmp<A, B>(&self, a: A, b: B) -> Ordering fn cmp<A, B>(&self, a: A, b: B) -> Ordering
where A: Into<ExpandedProgramPoint>, where
B: Into<ExpandedProgramPoint> A: Into<ExpandedProgramPoint>,
B: Into<ExpandedProgramPoint>,
{ {
fn idx(pp: ExpandedProgramPoint) -> usize { fn idx(pp: ExpandedProgramPoint) -> usize {
match pp { match pp {
@@ -505,9 +517,11 @@ mod tests {
assert_eq!(self.cmp(e, li.begin), Ordering::Less); assert_eq!(self.cmp(e, li.begin), Ordering::Less);
} }
assert!(self.cmp(lr.def_end, li.begin) == Ordering::Less || assert!(
self.cmp(lr.def_end, li.begin) == Ordering::Less ||
self.cmp(lr.def_begin, li.end) == Ordering::Greater, self.cmp(lr.def_begin, li.end) == Ordering::Greater,
"Interval can't overlap the def EBB"); "Interval can't overlap the def EBB"
);
// Save for next round. // Save for next round.
prev_end = Some(li.end); prev_end = Some(li.end);

View File

@@ -103,10 +103,10 @@ impl Pressure {
} }
// Compute per-class limits from `usable`. // Compute per-class limits from `usable`.
for (toprc, rc) in p.toprc for (toprc, rc) in p.toprc.iter_mut().take_while(|t| t.num_toprcs > 0).zip(
.iter_mut() reginfo.classes,
.take_while(|t| t.num_toprcs > 0) )
.zip(reginfo.classes) { {
toprc.limit = usable.iter(rc).len() as u32; toprc.limit = usable.iter(rc).len() as u32;
toprc.width = rc.width; toprc.width = rc.width;
} }

View File

@@ -54,13 +54,15 @@ impl Reload {
} }
/// Run the reload algorithm over `func`. /// Run the reload algorithm over `func`.
pub fn run(&mut self, pub fn run(
&mut self,
isa: &TargetIsa, isa: &TargetIsa,
func: &mut Function, func: &mut Function,
domtree: &DominatorTree, domtree: &DominatorTree,
liveness: &mut Liveness, liveness: &mut Liveness,
topo: &mut TopoOrder, topo: &mut TopoOrder,
tracker: &mut LiveValueTracker) { tracker: &mut LiveValueTracker,
) {
dbg!("Reload for:\n{}", func.display(isa)); dbg!("Reload for:\n{}", func.display(isa));
let mut ctx = Context { let mut ctx = Context {
cur: EncCursor::new(func, isa), cur: EncCursor::new(func, isa),
@@ -125,11 +127,13 @@ impl<'a> Context<'a> {
/// Process the EBB parameters. Move to the next instruction in the EBB to be processed /// Process the EBB parameters. Move to the next instruction in the EBB to be processed
fn visit_ebb_header(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) { fn visit_ebb_header(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) {
let (liveins, args) = tracker.ebb_top(ebb, let (liveins, args) = tracker.ebb_top(
ebb,
&self.cur.func.dfg, &self.cur.func.dfg,
self.liveness, self.liveness,
&self.cur.func.layout, &self.cur.func.layout,
self.domtree); self.domtree,
);
if self.cur.func.layout.entry_block() == Some(ebb) { if self.cur.func.layout.entry_block() == Some(ebb) {
assert_eq!(liveins.len(), 0); assert_eq!(liveins.len(), 0);
@@ -172,15 +176,17 @@ impl<'a> Context<'a> {
/// Process the instruction pointed to by `pos`, and advance the cursor to the next instruction /// Process the instruction pointed to by `pos`, and advance the cursor to the next instruction
/// that needs processing. /// that needs processing.
fn visit_inst(&mut self, fn visit_inst(
&mut self,
ebb: Ebb, ebb: Ebb,
inst: Inst, inst: Inst,
encoding: Encoding, encoding: Encoding,
tracker: &mut LiveValueTracker) { tracker: &mut LiveValueTracker,
) {
// Get the operand constraints for `inst` that we are trying to satisfy. // Get the operand constraints for `inst` that we are trying to satisfy.
let constraints = self.encinfo let constraints = self.encinfo.operand_constraints(encoding).expect(
.operand_constraints(encoding) "Missing instruction encoding",
.expect("Missing instruction encoding"); );
// Identify reload candidates. // Identify reload candidates.
assert!(self.candidates.is_empty()); assert!(self.candidates.is_empty());
@@ -195,8 +201,7 @@ impl<'a> Context<'a> {
let reg = self.cur.ins().fill(cand.value); let reg = self.cur.ins().fill(cand.value);
let fill = self.cur.built_inst(); let fill = self.cur.built_inst();
self.reloads self.reloads.insert(ReloadedValue {
.insert(ReloadedValue {
stack: cand.value, stack: cand.value,
reg: reg, reg: reg,
}); });
@@ -204,8 +209,12 @@ impl<'a> Context<'a> {
// Create a live range for the new reload. // Create a live range for the new reload.
let affinity = Affinity::Reg(cand.regclass.into()); let affinity = Affinity::Reg(cand.regclass.into());
self.liveness.create_dead(reg, fill, affinity); self.liveness.create_dead(reg, fill, affinity);
self.liveness self.liveness.extend_locally(
.extend_locally(reg, ebb, inst, &self.cur.func.layout); reg,
ebb,
inst,
&self.cur.func.layout,
);
} }
// Rewrite arguments. // Rewrite arguments.
@@ -218,8 +227,8 @@ impl<'a> Context<'a> {
// TODO: Reuse reloads for future instructions. // TODO: Reuse reloads for future instructions.
self.reloads.clear(); self.reloads.clear();
let (_throughs, _kills, defs) = tracker let (_throughs, _kills, defs) =
.process_inst(inst, &self.cur.func.dfg, self.liveness); tracker.process_inst(inst, &self.cur.func.dfg, self.liveness);
// Advance to the next instruction so we can insert any spills after the instruction. // Advance to the next instruction so we can insert any spills after the instruction.
self.cur.next_inst(); self.cur.next_inst();
@@ -255,8 +264,7 @@ impl<'a> Context<'a> {
for (op, &arg) in constraints.ins.iter().zip(args) { for (op, &arg) in constraints.ins.iter().zip(args) {
if op.kind != ConstraintKind::Stack { if op.kind != ConstraintKind::Stack {
if self.liveness[arg].affinity.is_stack() { if self.liveness[arg].affinity.is_stack() {
self.candidates self.candidates.push(ReloadCandidate {
.push(ReloadCandidate {
value: arg, value: arg,
regclass: op.regclass, regclass: op.regclass,
}) })
@@ -272,17 +280,21 @@ impl<'a> Context<'a> {
// Handle ABI arguments. // Handle ABI arguments.
if let Some(sig) = self.cur.func.dfg.call_signature(inst) { if let Some(sig) = self.cur.func.dfg.call_signature(inst) {
handle_abi_args(self.candidates, handle_abi_args(
self.candidates,
&self.cur.func.dfg.signatures[sig].argument_types, &self.cur.func.dfg.signatures[sig].argument_types,
var_args, var_args,
self.cur.isa, self.cur.isa,
self.liveness); self.liveness,
);
} else if self.cur.func.dfg[inst].opcode().is_return() { } else if self.cur.func.dfg[inst].opcode().is_return() {
handle_abi_args(self.candidates, handle_abi_args(
self.candidates,
&self.cur.func.signature.return_types, &self.cur.func.signature.return_types,
var_args, var_args,
self.cur.isa, self.cur.isa,
self.liveness); self.liveness,
);
} }
} }
@@ -297,18 +309,24 @@ impl<'a> Context<'a> {
// Update live ranges. // Update live ranges.
self.liveness.move_def_locally(stack, inst); self.liveness.move_def_locally(stack, inst);
self.liveness self.liveness.extend_locally(
.extend_locally(reg, ebb, inst, &self.cur.func.layout); reg,
ebb,
inst,
&self.cur.func.layout,
);
} }
} }
/// Find reload candidates in the instruction's ABI variable arguments. This handles both /// Find reload candidates in the instruction's ABI variable arguments. This handles both
/// return values and call arguments. /// return values and call arguments.
fn handle_abi_args(candidates: &mut Vec<ReloadCandidate>, fn handle_abi_args(
candidates: &mut Vec<ReloadCandidate>,
abi_types: &[ArgumentType], abi_types: &[ArgumentType],
var_args: &[Value], var_args: &[Value],
isa: &TargetIsa, isa: &TargetIsa,
liveness: &Liveness) { liveness: &Liveness,
) {
assert_eq!(abi_types.len(), var_args.len()); assert_eq!(abi_types.len(), var_args.len());
for (abi, &arg) in abi_types.iter().zip(var_args) { for (abi, &arg) in abi_types.iter().zip(var_args) {
if abi.location.is_reg() { if abi.location.is_reg() {

View File

@@ -231,12 +231,14 @@ impl SparseMapValue<Value> for Assignment {
impl fmt::Display for Assignment { impl fmt::Display for Assignment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, write!(
f,
"{}:{}(%{} -> %{})", "{}:{}(%{} -> %{})",
self.value, self.value,
self.rc, self.rc,
self.from, self.from,
self.to) self.to
)
} }
} }
@@ -363,17 +365,18 @@ impl Solver {
dbg!("-> converting variable {} to a fixed constraint", v); dbg!("-> converting variable {} to a fixed constraint", v);
// The spiller is responsible for ensuring that all constraints on the uses of a // The spiller is responsible for ensuring that all constraints on the uses of a
// value are compatible. // value are compatible.
assert!(v.constraint.contains(to), assert!(
v.constraint.contains(to),
"Incompatible constraints for {}", "Incompatible constraints for {}",
value); value
);
} else { } else {
panic!("Invalid from register for fixed {} constraint", value); panic!("Invalid from register for fixed {} constraint", value);
} }
} }
self.regs_in.free(rc, from); self.regs_in.free(rc, from);
self.regs_out.take(rc, to); self.regs_out.take(rc, to);
self.assignments self.assignments.insert(Assignment {
.insert(Assignment {
value, value,
rc, rc,
from, from,
@@ -388,18 +391,22 @@ impl Solver {
/// ///
/// It is assumed initially that the value is also live on the output side of the instruction. /// It is assumed initially that the value is also live on the output side of the instruction.
/// This can be changed by calling to `add_kill()`. /// This can be changed by calling to `add_kill()`.
pub fn add_var(&mut self, pub fn add_var(
&mut self,
value: Value, value: Value,
constraint: RegClass, constraint: RegClass,
from: RegUnit, from: RegUnit,
reginfo: &RegInfo) { reginfo: &RegInfo,
) {
// Check for existing entries for this value. // Check for existing entries for this value.
if self.regs_in.is_avail(constraint, from) { if self.regs_in.is_avail(constraint, from) {
dbg!("add_var({}:{}, from={}/%{}) for existing entry", dbg!(
"add_var({}:{}, from={}/%{}) for existing entry",
value, value,
constraint, constraint,
reginfo.display_regunit(from), reginfo.display_regunit(from),
from); from
);
// There could be an existing variable entry. // There could be an existing variable entry.
if let Some(v) = self.vars.iter_mut().find(|v| v.value == value) { if let Some(v) = self.vars.iter_mut().find(|v| v.value == value) {
@@ -419,9 +426,11 @@ impl Solver {
// No variable, then it must be a fixed reassignment. // No variable, then it must be a fixed reassignment.
if let Some(a) = self.assignments.get(value) { if let Some(a) = self.assignments.get(value) {
dbg!("-> already fixed assignment {}", a); dbg!("-> already fixed assignment {}", a);
assert!(constraint.contains(a.to), assert!(
constraint.contains(a.to),
"Incompatible constraints for {}", "Incompatible constraints for {}",
value); value
);
return; return;
} }
@@ -430,12 +439,14 @@ impl Solver {
} }
let new_var = Variable::new_live(value, constraint, from); let new_var = Variable::new_live(value, constraint, from);
dbg!("add_var({}:{}, from={}/%{}) new entry: {}", dbg!(
"add_var({}:{}, from={}/%{}) new entry: {}",
value, value,
constraint, constraint,
reginfo.display_regunit(from), reginfo.display_regunit(from),
from, from,
new_var); new_var
);
self.regs_in.free(constraint, from); self.regs_in.free(constraint, from);
if self.inputs_done { if self.inputs_done {
@@ -623,8 +634,7 @@ impl Solver {
// Collect moves from the chosen solution for all non-define variables. // Collect moves from the chosen solution for all non-define variables.
for v in &self.vars { for v in &self.vars {
if let Some(from) = v.from { if let Some(from) = v.from {
self.moves self.moves.push(Assignment {
.push(Assignment {
value: v.value, value: v.value,
from, from,
to: v.solution, to: v.solution,
@@ -635,11 +645,9 @@ impl Solver {
// Convert all of the fixed register assignments into moves, but omit the ones that are // Convert all of the fixed register assignments into moves, but omit the ones that are
// already in the right register. // already in the right register.
self.moves self.moves.extend(self.assignments.values().cloned().filter(
.extend(self.assignments |v| v.from != v.to,
.values() ));
.cloned()
.filter(|v| v.from != v.to));
dbg!("collect_moves: {}", DisplayList(self.moves.as_slice())); dbg!("collect_moves: {}", DisplayList(self.moves.as_slice()));
} }
@@ -661,9 +669,10 @@ impl Solver {
let mut i = 0; let mut i = 0;
while i < self.moves.len() { while i < self.moves.len() {
// Find the first move that can be executed now. // Find the first move that can be executed now.
if let Some(j) = self.moves[i..] if let Some(j) = self.moves[i..].iter().position(
.iter() |m| avail.is_avail(m.rc, m.to),
.position(|m| avail.is_avail(m.rc, m.to)) { )
{
// This move can be executed now. // This move can be executed now.
self.moves.swap(i, i + j); self.moves.swap(i, i + j);
let m = &self.moves[i]; let m = &self.moves[i];
@@ -709,8 +718,7 @@ impl Solver {
// Append a fixup move so we end up in the right place. This move will be scheduled // Append a fixup move so we end up in the right place. This move will be scheduled
// later. That's ok because it is the single remaining move of `m.value` after the // later. That's ok because it is the single remaining move of `m.value` after the
// next iteration. // next iteration.
self.moves self.moves.push(Assignment {
.push(Assignment {
value: m.value, value: m.value,
rc: m.rc, rc: m.rc,
from: reg, from: reg,
@@ -738,9 +746,11 @@ impl Solver {
impl fmt::Display for Solver { impl fmt::Display for Solver {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Solver {{ inputs_done: {},", self.inputs_done)?; writeln!(f, "Solver {{ inputs_done: {},", self.inputs_done)?;
writeln!(f, writeln!(
f,
" assignments: {}", " assignments: {}",
DisplayList(self.assignments.as_slice()))?; DisplayList(self.assignments.as_slice())
)?;
writeln!(f, " vars: {}", DisplayList(self.vars.as_slice()))?; writeln!(f, " vars: {}", DisplayList(self.vars.as_slice()))?;
writeln!(f, " moves: {}", DisplayList(self.moves.as_slice()))?; writeln!(f, " moves: {}", DisplayList(self.moves.as_slice()))?;
writeln!(f, "}}") writeln!(f, "}}")
@@ -817,8 +827,10 @@ mod tests {
solver.inputs_done(); solver.inputs_done();
assert!(solver.quick_solve().is_ok()); assert!(solver.quick_solve().is_ok());
assert_eq!(solver.schedule_moves(&regs), 0); assert_eq!(solver.schedule_moves(&regs), 0);
assert_eq!(solver.moves(), assert_eq!(
&[mov(v11, gpr, r1, r2), mov(v10, gpr, r0, r1)]); solver.moves(),
&[mov(v11, gpr, r1, r2), mov(v10, gpr, r0, r1)]
);
// Swap r0 and r1 in three moves using r2 as a scratch. // Swap r0 and r1 in three moves using r2 as a scratch.
solver.reset(&regs); solver.reset(&regs);
@@ -827,10 +839,14 @@ mod tests {
solver.inputs_done(); solver.inputs_done();
assert!(solver.quick_solve().is_ok()); assert!(solver.quick_solve().is_ok());
assert_eq!(solver.schedule_moves(&regs), 0); assert_eq!(solver.schedule_moves(&regs), 0);
assert_eq!(solver.moves(), assert_eq!(
&[mov(v10, gpr, r0, r2), solver.moves(),
&[
mov(v10, gpr, r0, r2),
mov(v11, gpr, r1, r0), mov(v11, gpr, r1, r0),
mov(v10, gpr, r2, r1)]); mov(v10, gpr, r2, r1),
]
);
} }
#[test] #[test]
@@ -862,11 +878,15 @@ mod tests {
solver.inputs_done(); solver.inputs_done();
assert!(solver.quick_solve().is_ok()); assert!(solver.quick_solve().is_ok());
assert_eq!(solver.schedule_moves(&regs), 0); assert_eq!(solver.schedule_moves(&regs), 0);
assert_eq!(solver.moves(), assert_eq!(
&[mov(v10, d, d0, d2), solver.moves(),
&[
mov(v10, d, d0, d2),
mov(v11, s, s2, s0), mov(v11, s, s2, s0),
mov(v12, s, s3, s1), mov(v12, s, s3, s1),
mov(v10, d, d2, d1)]); mov(v10, d, d2, d1),
]
);
// Same problem in the other direction: Swap (s0, s1) <-> d1. // Same problem in the other direction: Swap (s0, s1) <-> d1.
// //
@@ -879,10 +899,14 @@ mod tests {
solver.inputs_done(); solver.inputs_done();
assert!(solver.quick_solve().is_ok()); assert!(solver.quick_solve().is_ok());
assert_eq!(solver.schedule_moves(&regs), 0); assert_eq!(solver.schedule_moves(&regs), 0);
assert_eq!(solver.moves(), assert_eq!(
&[mov(v10, d, d1, d2), solver.moves(),
&[
mov(v10, d, d1, d2),
mov(v12, s, s1, s3), mov(v12, s, s1, s3),
mov(v11, s, s0, s2), mov(v11, s, s0, s2),
mov(v10, d, d2, d0)]); mov(v10, d, d2, d0),
]
);
} }
} }

View File

@@ -71,14 +71,16 @@ impl Spilling {
} }
/// Run the spilling algorithm over `func`. /// Run the spilling algorithm over `func`.
pub fn run(&mut self, pub fn run(
&mut self,
isa: &TargetIsa, isa: &TargetIsa,
func: &mut Function, func: &mut Function,
domtree: &DominatorTree, domtree: &DominatorTree,
liveness: &mut Liveness, liveness: &mut Liveness,
virtregs: &VirtRegs, virtregs: &VirtRegs,
topo: &mut TopoOrder, topo: &mut TopoOrder,
tracker: &mut LiveValueTracker) { tracker: &mut LiveValueTracker,
) {
dbg!("Spilling for:\n{}", func.display(isa)); dbg!("Spilling for:\n{}", func.display(isa));
let reginfo = isa.register_info(); let reginfo = isa.register_info();
let usable_regs = isa.allocatable_registers(func); let usable_regs = isa.allocatable_registers(func);
@@ -114,8 +116,10 @@ impl<'a> Context<'a> {
while let Some(inst) = self.cur.next_inst() { while let Some(inst) = self.cur.next_inst() {
if let Some(constraints) = if let Some(constraints) =
self.encinfo self.encinfo.operand_constraints(
.operand_constraints(self.cur.func.encodings[inst]) { self.cur.func.encodings[inst],
)
{
self.visit_inst(inst, ebb, constraints, tracker); self.visit_inst(inst, ebb, constraints, tracker);
} else { } else {
let (_throughs, kills) = tracker.process_ghost(inst); let (_throughs, kills) = tracker.process_ghost(inst);
@@ -150,11 +154,13 @@ impl<'a> Context<'a> {
} }
fn visit_ebb_header(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) { fn visit_ebb_header(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) {
let (liveins, args) = tracker.ebb_top(ebb, let (liveins, args) = tracker.ebb_top(
ebb,
&self.cur.func.dfg, &self.cur.func.dfg,
self.liveness, self.liveness,
&self.cur.func.layout, &self.cur.func.layout,
self.domtree); self.domtree,
);
// Count the live-in registers. These should already fit in registers; they did at the // Count the live-in registers. These should already fit in registers; they did at the
// dominator. // dominator.
@@ -167,16 +173,20 @@ impl<'a> Context<'a> {
if let Affinity::Reg(rci) = lv.affinity { if let Affinity::Reg(rci) = lv.affinity {
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);
'try_take: while let Err(mask) = self.pressure.take_transient(rc) { 'try_take: while let Err(mask) = self.pressure.take_transient(rc) {
dbg!("Need {} reg for EBB argument {} from {} live-ins", dbg!(
"Need {} reg for EBB argument {} from {} live-ins",
rc, rc,
lv.value, lv.value,
liveins.len()); liveins.len()
);
match self.spill_candidate(mask, liveins) { match self.spill_candidate(mask, liveins) {
Some(cand) => { Some(cand) => {
dbg!("Spilling live-in {} to make room for {} EBB argument {}", dbg!(
"Spilling live-in {} to make room for {} EBB argument {}",
cand, cand,
rc, rc,
lv.value); lv.value
);
self.spill_reg(cand); self.spill_reg(cand);
} }
None => { None => {
@@ -199,11 +209,13 @@ impl<'a> Context<'a> {
self.pressure.preserve_transient(); self.pressure.preserve_transient();
} }
fn visit_inst(&mut self, fn visit_inst(
&mut self,
inst: Inst, inst: Inst,
ebb: Ebb, ebb: Ebb,
constraints: &RecipeConstraints, constraints: &RecipeConstraints,
tracker: &mut LiveValueTracker) { tracker: &mut LiveValueTracker,
) {
dbg!("Inst {}, {}", self.cur.display_inst(inst), self.pressure); dbg!("Inst {}, {}", self.cur.display_inst(inst), self.pressure);
debug_assert_eq!(self.cur.current_inst(), Some(inst)); debug_assert_eq!(self.cur.current_inst(), Some(inst));
debug_assert_eq!(self.cur.current_ebb(), Some(ebb)); debug_assert_eq!(self.cur.current_ebb(), Some(ebb));
@@ -250,9 +262,11 @@ impl<'a> Context<'a> {
match self.spill_candidate(mask, throughs) { match self.spill_candidate(mask, throughs) {
Some(cand) => self.spill_reg(cand), Some(cand) => self.spill_reg(cand),
None => { None => {
panic!("Ran out of {} registers for {}", panic!(
"Ran out of {} registers for {}",
op.regclass, op.regclass,
self.cur.display_inst(inst)) self.cur.display_inst(inst)
)
} }
} }
} }
@@ -313,12 +327,16 @@ impl<'a> Context<'a> {
.argument_types .argument_types
.iter() .iter()
.zip(args) .zip(args)
.enumerate() { .enumerate()
{
if abi.location.is_reg() { if abi.location.is_reg() {
let (rci, spilled) = match self.liveness[arg].affinity { let (rci, spilled) = match self.liveness[arg].affinity {
Affinity::Reg(rci) => (rci, false), Affinity::Reg(rci) => (rci, false),
Affinity::Stack => { Affinity::Stack => {
(self.cur.isa.regclass_for_abi_type(abi.value_type).into(), true) (
self.cur.isa.regclass_for_abi_type(abi.value_type).into(),
true,
)
} }
Affinity::None => panic!("Missing affinity for {}", arg), Affinity::None => panic!("Missing affinity for {}", arg),
}; };
@@ -374,16 +392,18 @@ impl<'a> Context<'a> {
// Spilling a use wouldn't help. // Spilling a use wouldn't help.
match { match {
let args = self.cur.func.dfg.inst_args(inst); let args = self.cur.func.dfg.inst_args(inst);
self.spill_candidate(mask, self.spill_candidate(
tracker.live().iter().filter(|lv| { mask,
!args.contains(&lv.value) tracker.live().iter().filter(|lv| !args.contains(&lv.value)),
})) )
} { } {
Some(cand) => self.spill_reg(cand), Some(cand) => self.spill_reg(cand),
None => { None => {
panic!("Ran out of {} registers when inserting copy before {}", panic!(
"Ran out of {} registers when inserting copy before {}",
rc, rc,
self.cur.display_inst(inst)) self.cur.display_inst(inst)
)
} }
} }
} }
@@ -395,7 +415,8 @@ impl<'a> Context<'a> {
// Find a spill candidate from `candidates` whose top-level register class is in `mask`. // Find a spill candidate from `candidates` whose top-level register class is in `mask`.
fn spill_candidate<'ii, II>(&self, mask: RegClassMask, candidates: II) -> Option<Value> fn spill_candidate<'ii, II>(&self, mask: RegClassMask, candidates: II) -> Option<Value>
where II: IntoIterator<Item = &'ii LiveValue> where
II: IntoIterator<Item = &'ii LiveValue>,
{ {
// Find the best viable spill candidate. // Find the best viable spill candidate.
// //
@@ -421,10 +442,11 @@ impl<'a> Context<'a> {
}) })
.min_by(|&a, &b| { .min_by(|&a, &b| {
// Find the minimum candidate according to the RPO of their defs. // Find the minimum candidate according to the RPO of their defs.
self.domtree self.domtree.rpo_cmp(
.rpo_cmp(self.cur.func.dfg.value_def(a), self.cur.func.dfg.value_def(a),
self.cur.func.dfg.value_def(b), self.cur.func.dfg.value_def(b),
&self.cur.func.layout) &self.cur.func.layout,
)
}) })
} }
@@ -447,10 +469,9 @@ impl<'a> Context<'a> {
} }
// Assign a spill slot for the whole virtual register. // Assign a spill slot for the whole virtual register.
let ss = self.cur let ss = self.cur.func.stack_slots.make_spill_slot(
.func self.cur.func.dfg.value_type(value),
.stack_slots );
.make_spill_slot(self.cur.func.dfg.value_type(value));
for &v in self.virtregs.congruence_class(&value) { for &v in self.virtregs.congruence_class(&value) {
self.liveness.spill(v); self.liveness.spill(v);
self.cur.func.locations[v] = ValueLoc::Stack(ss); self.cur.func.locations[v] = ValueLoc::Stack(ss);
@@ -481,11 +502,12 @@ impl<'a> Context<'a> {
// Update live ranges. // Update live ranges.
self.liveness.create_dead(copy, inst, Affinity::Reg(rci)); self.liveness.create_dead(copy, inst, Affinity::Reg(rci));
self.liveness self.liveness.extend_locally(
.extend_locally(copy, copy,
self.cur.func.layout.pp_ebb(inst), self.cur.func.layout.pp_ebb(inst),
self.cur.current_inst().expect("must be at an instruction"), self.cur.current_inst().expect("must be at an instruction"),
&self.cur.func.layout); &self.cur.func.layout,
);
copy copy
} }

View File

@@ -81,11 +81,12 @@ impl VirtRegs {
/// If `value` belongs to a virtual register, the congruence class is the values of the virtual /// If `value` belongs to a virtual register, the congruence class is the values of the virtual
/// register. Otherwise it is just the value itself. /// register. Otherwise it is just the value itself.
pub fn congruence_class<'a, 'b>(&'a self, value: &'b Value) -> &'b [Value] pub fn congruence_class<'a, 'b>(&'a self, value: &'b Value) -> &'b [Value]
where 'a: 'b where
'a: 'b,
{ {
self.get(*value) self.get(*value).map(|vr| self.values(vr)).unwrap_or(
.map(|vr| self.values(vr)) ref_slice(value),
.unwrap_or(ref_slice(value)) )
} }
/// Check if `a` and `b` belong to the same congruence class. /// Check if `a` and `b` belong to the same congruence class.
@@ -126,9 +127,11 @@ impl VirtRegs {
.min() .min()
.unwrap_or_else(|| self.vregs.push(Default::default())); .unwrap_or_else(|| self.vregs.push(Default::default()));
assert_eq!(values.len(), assert_eq!(
values.len(),
singletons + cleared, singletons + cleared,
"Can't unify partial virtual registers"); "Can't unify partial virtual registers"
);
self.vregs[vreg].extend(values.iter().cloned(), &mut self.pool); self.vregs[vreg].extend(values.iter().cloned(), &mut self.pool);
for &v in values { for &v in values {

View File

@@ -137,8 +137,8 @@ impl Configurable for Builder {
self.bytes[offset] = value.parse().map_err(|_| Error::BadValue)?; self.bytes[offset] = value.parse().map_err(|_| Error::BadValue)?;
} }
Detail::Enum { last, enumerators } => { Detail::Enum { last, enumerators } => {
self.bytes[offset] = parse_enum_value(value, self.bytes[offset] =
self.template.enums(last, enumerators))?; parse_enum_value(value, self.template.enums(last, enumerators))?;
} }
Detail::Preset => return Err(Error::BadName), Detail::Preset => return Err(Error::BadName),
} }
@@ -218,11 +218,12 @@ pub mod detail {
/// Format a setting value as a TOML string. This is mostly for use by the generated /// Format a setting value as a TOML string. This is mostly for use by the generated
/// `Display` implementation. /// `Display` implementation.
pub fn format_toml_value(&self, pub fn format_toml_value(
&self,
detail: Detail, detail: Detail,
byte: u8, byte: u8,
f: &mut fmt::Formatter) f: &mut fmt::Formatter,
-> fmt::Result { ) -> fmt::Result {
match detail { match detail {
Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0), Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
Detail::Num => write!(f, "{}", byte), Detail::Num => write!(f, "{}", byte),
@@ -312,7 +313,8 @@ mod tests {
fn display_default() { fn display_default() {
let b = builder(); let b = builder();
let f = Flags::new(&b); let f = Flags::new(&b);
assert_eq!(f.to_string(), assert_eq!(
f.to_string(),
"[shared]\n\ "[shared]\n\
opt_level = \"default\"\n\ opt_level = \"default\"\n\
enable_verifier = false\n\ enable_verifier = false\n\
@@ -320,7 +322,8 @@ mod tests {
is_compressed = false\n\ is_compressed = false\n\
enable_float = true\n\ enable_float = true\n\
enable_simd = true\n\ enable_simd = true\n\
enable_atomics = true\n"); enable_atomics = true\n"
);
assert_eq!(f.opt_level(), super::OptLevel::Default); assert_eq!(f.opt_level(), super::OptLevel::Default);
assert_eq!(f.enable_simd(), true); assert_eq!(f.enable_simd(), true);
} }

View File

@@ -51,9 +51,9 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result<Stac
incoming_min = min(incoming_min, slot.offset); incoming_min = min(incoming_min, slot.offset);
} }
StackSlotKind::OutgoingArg => { StackSlotKind::OutgoingArg => {
let offset = slot.offset let offset = slot.offset.checked_add(slot.size as StackOffset).ok_or(
.checked_add(slot.size as StackOffset) CtonError::ImplLimitExceeded,
.ok_or(CtonError::ImplLimitExceeded)?; )?;
outgoing_max = max(outgoing_max, offset); outgoing_max = max(outgoing_max, offset);
} }
StackSlotKind::SpillSlot | StackSlotKind::Local => { StackSlotKind::SpillSlot | StackSlotKind::Local => {
@@ -82,9 +82,9 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result<Stac
_ => continue, _ => continue,
} }
offset = offset offset = offset.checked_sub(slot.size as StackOffset).ok_or(
.checked_sub(slot.size as StackOffset) CtonError::ImplLimitExceeded,
.ok_or(CtonError::ImplLimitExceeded)?; )?;
// Aligning the negative offset can never cause overflow. We're only clearing bits. // Aligning the negative offset can never cause overflow. We're only clearing bits.
offset &= -(min_align as StackOffset); offset &= -(min_align as StackOffset);
@@ -96,9 +96,9 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result<Stac
} }
// Finally, make room for the outgoing arguments. // Finally, make room for the outgoing arguments.
offset = offset offset = offset.checked_sub(outgoing_max).ok_or(
.checked_sub(outgoing_max) CtonError::ImplLimitExceeded,
.ok_or(CtonError::ImplLimitExceeded)?; )?;
offset &= -(alignment as StackOffset); offset &= -(alignment as StackOffset);
let frame_size = (offset as StackSize).wrapping_neg(); let frame_size = (offset as StackSize).wrapping_neg();

View File

@@ -38,7 +38,8 @@ impl TopoOrder {
/// Reset and initialize with a preferred sequence of EBBs. The resulting topological order is /// Reset and initialize with a preferred sequence of EBBs. The resulting topological order is
/// guaranteed to contain all of the EBBs in `preferred` as well as any dominators. /// guaranteed to contain all of the EBBs in `preferred` as well as any dominators.
pub fn reset<Ebbs>(&mut self, preferred: Ebbs) pub fn reset<Ebbs>(&mut self, preferred: Ebbs)
where Ebbs: IntoIterator<Item = Ebb> where
Ebbs: IntoIterator<Item = Ebb>,
{ {
self.preferred.clear(); self.preferred.clear();
self.preferred.extend(preferred); self.preferred.extend(preferred);

View File

@@ -22,12 +22,13 @@ use verifier::Result;
/// - The values in a virtual register are ordered according to the dominator tree's `rpo_cmp()`. /// - The values in a virtual register are ordered according to the dominator tree's `rpo_cmp()`.
/// ///
/// We don't verify that virtual registers are minimal. Minimal CSSA is not required. /// We don't verify that virtual registers are minimal. Minimal CSSA is not required.
pub fn verify_cssa(func: &Function, pub fn verify_cssa(
func: &Function,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
domtree: &DominatorTree, domtree: &DominatorTree,
liveness: &Liveness, liveness: &Liveness,
virtregs: &VirtRegs) virtregs: &VirtRegs,
-> Result { ) -> Result {
let verifier = CssaVerifier { let verifier = CssaVerifier {
func, func,
cfg, cfg,
@@ -77,10 +78,12 @@ impl<'a> CssaVerifier<'a> {
return err!(val, "Value in {} has same def as {}", vreg, prev_val); return err!(val, "Value in {} has same def as {}", vreg, prev_val);
} }
Ordering::Greater => { Ordering::Greater => {
return err!(val, return err!(
val,
"Value in {} in wrong order relative to {}", "Value in {} in wrong order relative to {}",
vreg, vreg,
prev_val); prev_val
);
} }
} }
@@ -102,16 +105,20 @@ impl<'a> CssaVerifier<'a> {
for &(_, pred) in self.cfg.get_predecessors(ebb) { for &(_, pred) in self.cfg.get_predecessors(ebb) {
let pred_args = self.func.dfg.inst_variable_args(pred); let pred_args = self.func.dfg.inst_variable_args(pred);
// This should have been caught by an earlier verifier pass. // This should have been caught by an earlier verifier pass.
assert_eq!(ebb_args.len(), assert_eq!(
ebb_args.len(),
pred_args.len(), pred_args.len(),
"Wrong arguments on branch."); "Wrong arguments on branch."
);
for (&ebb_arg, &pred_arg) in ebb_args.iter().zip(pred_args) { for (&ebb_arg, &pred_arg) in ebb_args.iter().zip(pred_args) {
if !self.virtregs.same_class(ebb_arg, pred_arg) { if !self.virtregs.same_class(ebb_arg, pred_arg) {
return err!(pred, return err!(
pred,
"{} and {} must be in the same virtual register", "{} and {} must be in the same virtual register",
ebb_arg, ebb_arg,
pred_arg); pred_arg
);
} }
} }
} }

View File

@@ -21,11 +21,12 @@ use verifier::Result;
/// ///
/// We don't verify that live ranges are minimal. This would require recomputing live ranges for /// We don't verify that live ranges are minimal. This would require recomputing live ranges for
/// all values. /// all values.
pub fn verify_liveness(isa: &TargetIsa, pub fn verify_liveness(
isa: &TargetIsa,
func: &Function, func: &Function,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
liveness: &Liveness) liveness: &Liveness,
-> Result { ) -> Result {
let verifier = LivenessVerifier { let verifier = LivenessVerifier {
isa, isa,
func, func,
@@ -76,18 +77,22 @@ impl<'a> LivenessVerifier<'a> {
if encoding.is_legal() { if encoding.is_legal() {
// A legal instruction is not allowed to define ghost values. // A legal instruction is not allowed to define ghost values.
if lr.affinity.is_none() { if lr.affinity.is_none() {
return err!(inst, return err!(
inst,
"{} is a ghost value defined by a real [{}] instruction", "{} is a ghost value defined by a real [{}] instruction",
val, val,
self.isa.encoding_info().display(encoding)); self.isa.encoding_info().display(encoding)
);
} }
} else { } else {
// A non-encoded instruction can only define ghost values. // A non-encoded instruction can only define ghost values.
if !lr.affinity.is_none() { if !lr.affinity.is_none() {
return err!(inst, return err!(
inst,
"{} is a real {} value defined by a ghost instruction", "{} is a real {} value defined by a ghost instruction",
val, val,
lr.affinity.display(&self.isa.register_info())); lr.affinity.display(&self.isa.register_info())
);
} }
} }
} }
@@ -108,10 +113,12 @@ impl<'a> LivenessVerifier<'a> {
// A branch argument can be a ghost value if the corresponding destination // A branch argument can be a ghost value if the corresponding destination
// EBB argument is a ghost value. // EBB argument is a ghost value.
if lr.affinity.is_none() && !self.is_ghost_branch_argument(inst, idx) { if lr.affinity.is_none() && !self.is_ghost_branch_argument(inst, idx) {
return err!(inst, return err!(
inst,
"{} is a ghost value used by a real [{}] instruction", "{} is a ghost value used by a real [{}] instruction",
val, val,
self.isa.encoding_info().display(encoding)); self.isa.encoding_info().display(encoding)
);
} }
} }
} }
@@ -126,7 +133,8 @@ impl<'a> LivenessVerifier<'a> {
// Check if `inst` is in the def range, not including the def itself. // Check if `inst` is in the def range, not including the def itself.
if l.cmp(lr.def(), inst) == Ordering::Less && if l.cmp(lr.def(), inst) == Ordering::Less &&
l.cmp(inst, lr.def_local_end()) != Ordering::Greater { l.cmp(inst, lr.def_local_end()) != Ordering::Greater
{
return true; return true;
} }
@@ -205,11 +213,13 @@ impl<'a> LivenessVerifier<'a> {
let end_ebb = match l.inst_ebb(livein.end) { let end_ebb = match l.inst_ebb(livein.end) {
Some(e) => e, Some(e) => e,
None => { None => {
return err!(loc, return err!(
loc,
"{} livein for {} ends at {} which is not in the layout", "{} livein for {} ends at {} which is not in the layout",
val, val,
ebb, ebb,
livein.end) livein.end
)
} }
}; };
@@ -218,10 +228,12 @@ impl<'a> LivenessVerifier<'a> {
// If `val` is live-in at `ebb`, it must be live at all the predecessors. // If `val` is live-in at `ebb`, it must be live at all the predecessors.
for &(_, pred) in self.cfg.get_predecessors(ebb) { for &(_, pred) in self.cfg.get_predecessors(ebb) {
if !self.live_at_use(lr, pred) { if !self.live_at_use(lr, pred) {
return err!(pred, return err!(
pred,
"{} is live in to {} but not live at predecessor", "{} is live in to {} but not live at predecessor",
val, val,
ebb); ebb
);
} }
} }

View File

@@ -127,11 +127,12 @@ pub fn verify_function(func: &Function, isa: Option<&TargetIsa>) -> Result {
/// Verify `func` after checking the integrity of associated context data structures `cfg` and /// Verify `func` after checking the integrity of associated context data structures `cfg` and
/// `domtree`. /// `domtree`.
pub fn verify_context(func: &Function, pub fn verify_context(
func: &Function,
cfg: &ControlFlowGraph, cfg: &ControlFlowGraph,
domtree: &DominatorTree, domtree: &DominatorTree,
isa: Option<&TargetIsa>) isa: Option<&TargetIsa>,
-> Result { ) -> Result {
let verifier = Verifier::new(func, isa); let verifier = Verifier::new(func, isa);
verifier.cfg_integrity(cfg)?; verifier.cfg_integrity(cfg)?;
if domtree.is_valid() { if domtree.is_valid() {
@@ -187,9 +188,11 @@ impl<'a> Verifier<'a> {
if is_terminator && !is_last_inst { if is_terminator && !is_last_inst {
// Terminating instructions only occur at the end of blocks. // Terminating instructions only occur at the end of blocks.
return err!(inst, return err!(
inst,
"a terminator instruction was encountered before the end of {}", "a terminator instruction was encountered before the end of {}",
ebb); ebb
);
} }
if is_last_inst && !is_terminator { if is_last_inst && !is_terminator {
return err!(ebb, "block does not end in a terminator instruction!"); return err!(ebb, "block does not end in a terminator instruction!");
@@ -237,10 +240,12 @@ impl<'a> Verifier<'a> {
// All result values for multi-valued instructions are created // All result values for multi-valued instructions are created
let got_results = dfg.inst_results(inst).len(); let got_results = dfg.inst_results(inst).len();
if got_results != total_results { if got_results != total_results {
return err!(inst, return err!(
inst,
"expected {} result values, found {}", "expected {} result values, found {}",
total_results, total_results,
got_results); got_results
);
} }
self.verify_entity_references(inst) self.verify_entity_references(inst)
@@ -407,22 +412,30 @@ impl<'a> Verifier<'a> {
ValueDef::Res(def_inst, _) => { ValueDef::Res(def_inst, _) => {
// Value is defined by an instruction that exists. // Value is defined by an instruction that exists.
if !dfg.inst_is_valid(def_inst) { if !dfg.inst_is_valid(def_inst) {
return err!(loc_inst, return err!(
loc_inst,
"{} is defined by invalid instruction {}", "{} is defined by invalid instruction {}",
v, v,
def_inst); def_inst
);
} }
// Defining instruction is inserted in an EBB. // Defining instruction is inserted in an EBB.
if self.func.layout.inst_ebb(def_inst) == None { if self.func.layout.inst_ebb(def_inst) == None {
return err!(loc_inst, return err!(
loc_inst,
"{} is defined by {} which has no EBB", "{} is defined by {} which has no EBB",
v, v,
def_inst); def_inst
);
} }
// Defining instruction dominates the instruction that uses the value. // Defining instruction dominates the instruction that uses the value.
if self.domtree.is_reachable(self.func.layout.pp_ebb(loc_inst)) && if self.domtree.is_reachable(self.func.layout.pp_ebb(loc_inst)) &&
!self.domtree !self.domtree.dominates(
.dominates(def_inst, loc_inst, &self.func.layout) { def_inst,
loc_inst,
&self.func.layout,
)
{
return err!(loc_inst, "uses value from non-dominating {}", def_inst); return err!(loc_inst, "uses value from non-dominating {}", def_inst);
} }
} }
@@ -433,14 +446,17 @@ impl<'a> Verifier<'a> {
} }
// Defining EBB is inserted in the layout // Defining EBB is inserted in the layout
if !self.func.layout.is_ebb_inserted(ebb) { if !self.func.layout.is_ebb_inserted(ebb) {
return err!(loc_inst, return err!(
loc_inst,
"{} is defined by {} which is not in the layout", "{} is defined by {} which is not in the layout",
v, v,
ebb); ebb
);
} }
// The defining EBB dominates the instruction using this value. // The defining EBB dominates the instruction using this value.
if self.domtree.is_reachable(ebb) && if self.domtree.is_reachable(ebb) &&
!self.domtree.dominates(ebb, loc_inst, &self.func.layout) { !self.domtree.dominates(ebb, loc_inst, &self.func.layout)
{
return err!(loc_inst, "uses value arg from non-dominating {}", ebb); return err!(loc_inst, "uses value arg from non-dominating {}", ebb);
} }
} }
@@ -456,39 +472,48 @@ impl<'a> Verifier<'a> {
let expected = domtree.idom(ebb); let expected = domtree.idom(ebb);
let got = self.domtree.idom(ebb); let got = self.domtree.idom(ebb);
if got != expected { if got != expected {
return err!(ebb, return err!(
ebb,
"invalid domtree, expected idom({}) = {:?}, got {:?}", "invalid domtree, expected idom({}) = {:?}, got {:?}",
ebb, ebb,
expected, expected,
got); got
);
} }
} }
// We also verify if the postorder defined by `DominatorTree` is sane // We also verify if the postorder defined by `DominatorTree` is sane
if self.domtree.cfg_postorder().len() != domtree.cfg_postorder().len() { if self.domtree.cfg_postorder().len() != domtree.cfg_postorder().len() {
return err!(AnyEntity::Function, return err!(
"incorrect number of Ebbs in postorder traversal"); AnyEntity::Function,
"incorrect number of Ebbs in postorder traversal"
);
} }
for (index, (&true_ebb, &test_ebb)) in for (index, (&true_ebb, &test_ebb)) in
self.domtree self.domtree
.cfg_postorder() .cfg_postorder()
.iter() .iter()
.zip(domtree.cfg_postorder().iter()) .zip(domtree.cfg_postorder().iter())
.enumerate() { .enumerate()
{
if true_ebb != test_ebb { if true_ebb != test_ebb {
return err!(test_ebb, return err!(
test_ebb,
"invalid domtree, postorder ebb number {} should be {}, got {}", "invalid domtree, postorder ebb number {} should be {}, got {}",
index, index,
true_ebb, true_ebb,
test_ebb); test_ebb
);
} }
} }
// We verify rpo_cmp on pairs of adjacent ebbs in the postorder // We verify rpo_cmp on pairs of adjacent ebbs in the postorder
for (&prev_ebb, &next_ebb) in self.domtree.cfg_postorder().iter().adjacent_pairs() { for (&prev_ebb, &next_ebb) in self.domtree.cfg_postorder().iter().adjacent_pairs() {
if domtree.rpo_cmp(prev_ebb, next_ebb, &self.func.layout) != Ordering::Greater { if domtree.rpo_cmp(prev_ebb, next_ebb, &self.func.layout) != Ordering::Greater {
return err!(next_ebb, return err!(
next_ebb,
"invalid domtree, rpo_cmp does not says {} is greater than {}", "invalid domtree, rpo_cmp does not says {} is greater than {}",
prev_ebb, prev_ebb,
next_ebb); next_ebb
);
} }
} }
Ok(()) Ok(())
@@ -506,11 +531,13 @@ impl<'a> Verifier<'a> {
for (i, &arg) in self.func.dfg.ebb_args(ebb).iter().enumerate() { for (i, &arg) in self.func.dfg.ebb_args(ebb).iter().enumerate() {
let arg_type = self.func.dfg.value_type(arg); let arg_type = self.func.dfg.value_type(arg);
if arg_type != expected_types[i].value_type { if arg_type != expected_types[i].value_type {
return err!(ebb, return err!(
ebb,
"entry block argument {} expected to have type {}, got {}", "entry block argument {} expected to have type {}, got {}",
i, i,
expected_types[i], expected_types[i],
arg_type); arg_type
);
} }
} }
} }
@@ -551,12 +578,14 @@ impl<'a> Verifier<'a> {
let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type); let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type);
if let Some(expected_type) = expected_type { if let Some(expected_type) = expected_type {
if result_type != expected_type { if result_type != expected_type {
return err!(inst, return err!(
inst,
"expected result {} ({}) to have type {}, found {}", "expected result {} ({}) to have type {}, found {}",
i, i,
result, result,
expected_type, expected_type,
result_type); result_type
);
} }
} else { } else {
return err!(inst, "has more result values than expected"); return err!(inst, "has more result values than expected");
@@ -579,22 +608,26 @@ impl<'a> Verifier<'a> {
match constraints.value_argument_constraint(i, ctrl_type) { match constraints.value_argument_constraint(i, ctrl_type) {
ResolvedConstraint::Bound(expected_type) => { ResolvedConstraint::Bound(expected_type) => {
if arg_type != expected_type { if arg_type != expected_type {
return err!(inst, return err!(
inst,
"arg {} ({}) has type {}, expected {}", "arg {} ({}) has type {}, expected {}",
i, i,
arg, arg,
arg_type, arg_type,
expected_type); expected_type
);
} }
} }
ResolvedConstraint::Free(type_set) => { ResolvedConstraint::Free(type_set) => {
if !type_set.contains(arg_type) { if !type_set.contains(arg_type) {
return err!(inst, return err!(
inst,
"arg {} ({}) with type {} failed to satisfy type set {:?}", "arg {} ({}) with type {} failed to satisfy type set {:?}",
i, i,
arg, arg,
arg_type, arg_type,
type_set); type_set
);
} }
} }
} }
@@ -605,21 +638,21 @@ impl<'a> Verifier<'a> {
fn typecheck_variable_args(&self, inst: Inst) -> Result { fn typecheck_variable_args(&self, inst: Inst) -> Result {
match self.func.dfg[inst].analyze_branch(&self.func.dfg.value_lists) { match self.func.dfg[inst].analyze_branch(&self.func.dfg.value_lists) {
BranchInfo::SingleDest(ebb, _) => { BranchInfo::SingleDest(ebb, _) => {
let iter = self.func let iter = self.func.dfg.ebb_args(ebb).iter().map(|&v| {
.dfg self.func.dfg.value_type(v)
.ebb_args(ebb) });
.iter()
.map(|&v| self.func.dfg.value_type(v));
self.typecheck_variable_args_iterator(inst, iter)?; self.typecheck_variable_args_iterator(inst, iter)?;
} }
BranchInfo::Table(table) => { BranchInfo::Table(table) => {
for (_, ebb) in self.func.jump_tables[table].entries() { for (_, ebb) in self.func.jump_tables[table].entries() {
let arg_count = self.func.dfg.num_ebb_args(ebb); let arg_count = self.func.dfg.num_ebb_args(ebb);
if arg_count != 0 { if arg_count != 0 {
return err!(inst, return err!(
inst,
"takes no arguments, but had target {} with {} arguments", "takes no arguments, but had target {} with {} arguments",
ebb, ebb,
arg_count); arg_count
);
} }
} }
} }
@@ -649,10 +682,11 @@ impl<'a> Verifier<'a> {
Ok(()) Ok(())
} }
fn typecheck_variable_args_iterator<I: Iterator<Item = Type>>(&self, fn typecheck_variable_args_iterator<I: Iterator<Item = Type>>(
&self,
inst: Inst, inst: Inst,
iter: I) iter: I,
-> Result { ) -> Result {
let variable_args = self.func.dfg.inst_variable_args(inst); let variable_args = self.func.dfg.inst_variable_args(inst);
let mut i = 0; let mut i = 0;
@@ -665,20 +699,24 @@ impl<'a> Verifier<'a> {
let arg = variable_args[i]; let arg = variable_args[i];
let arg_type = self.func.dfg.value_type(arg); let arg_type = self.func.dfg.value_type(arg);
if expected_type != arg_type { if expected_type != arg_type {
return err!(inst, return err!(
inst,
"arg {} ({}) has type {}, expected {}", "arg {} ({}) has type {}, expected {}",
i, i,
variable_args[i], variable_args[i],
arg_type, arg_type,
expected_type); expected_type
);
} }
i += 1; i += 1;
} }
if i != variable_args.len() { if i != variable_args.len() {
return err!(inst, return err!(
inst,
"mismatched argument count, got {}, expected {}", "mismatched argument count, got {}, expected {}",
variable_args.len(), variable_args.len(),
i); i
);
} }
Ok(()) Ok(())
} }
@@ -707,34 +745,42 @@ impl<'a> Verifier<'a> {
self.verify_stack_slot(inst, ss)?; self.verify_stack_slot(inst, ss)?;
let slot = &self.func.stack_slots[ss]; let slot = &self.func.stack_slots[ss];
if slot.kind != StackSlotKind::OutgoingArg { if slot.kind != StackSlotKind::OutgoingArg {
return err!(inst, return err!(
inst,
"Outgoing stack argument {} in wrong stack slot: {} = {}", "Outgoing stack argument {} in wrong stack slot: {} = {}",
arg, arg,
ss, ss,
slot); slot
);
} }
if slot.offset != offset { if slot.offset != offset {
return err!(inst, return err!(
inst,
"Outgoing stack argument {} should have offset {}: {} = {}", "Outgoing stack argument {} should have offset {}: {} = {}",
arg, arg,
offset, offset,
ss, ss,
slot); slot
);
} }
if slot.size != abi.value_type.bytes() { if slot.size != abi.value_type.bytes() {
return err!(inst, return err!(
inst,
"Outgoing stack argument {} wrong size for {}: {} = {}", "Outgoing stack argument {} wrong size for {}: {} = {}",
arg, arg,
abi.value_type, abi.value_type,
ss, ss,
slot); slot
);
} }
} else { } else {
let reginfo = self.isa.map(|i| i.register_info()); let reginfo = self.isa.map(|i| i.register_info());
return err!(inst, return err!(
inst,
"Outgoing stack argument {} in wrong location: {}", "Outgoing stack argument {} in wrong location: {}",
arg, arg,
arg_loc.display(reginfo.as_ref())); arg_loc.display(reginfo.as_ref())
);
} }
} }
} }
@@ -751,12 +797,14 @@ impl<'a> Verifier<'a> {
for (i, (&arg, &expected_type)) in args.iter().zip(expected_types).enumerate() { for (i, (&arg, &expected_type)) in args.iter().zip(expected_types).enumerate() {
let arg_type = self.func.dfg.value_type(arg); let arg_type = self.func.dfg.value_type(arg);
if arg_type != expected_type.value_type { if arg_type != expected_type.value_type {
return err!(inst, return err!(
inst,
"arg {} ({}) has type {}, must match function signature of {}", "arg {} ({}) has type {}, must match function signature of {}",
i, i,
arg, arg,
arg_type, arg_type,
expected_type); expected_type
);
} }
} }
} }
@@ -775,9 +823,11 @@ impl<'a> Verifier<'a> {
let missing_succs: Vec<Ebb> = expected_succs.difference(&got_succs).cloned().collect(); let missing_succs: Vec<Ebb> = expected_succs.difference(&got_succs).cloned().collect();
if !missing_succs.is_empty() { if !missing_succs.is_empty() {
return err!(ebb, return err!(
ebb,
"cfg lacked the following successor(s) {:?}", "cfg lacked the following successor(s) {:?}",
missing_succs); missing_succs
);
} }
let excess_succs: Vec<Ebb> = got_succs.difference(&expected_succs).cloned().collect(); let excess_succs: Vec<Ebb> = got_succs.difference(&expected_succs).cloned().collect();
@@ -790,9 +840,11 @@ impl<'a> Verifier<'a> {
let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect(); let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
if !missing_preds.is_empty() { if !missing_preds.is_empty() {
return err!(ebb, return err!(
ebb,
"cfg lacked the following predecessor(s) {:?}", "cfg lacked the following predecessor(s) {:?}",
missing_preds); missing_preds
);
} }
let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect(); let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
@@ -826,23 +878,28 @@ impl<'a> Verifier<'a> {
let encoding = self.func.encodings[inst]; let encoding = self.func.encodings[inst];
if encoding.is_legal() { if encoding.is_legal() {
let verify_encoding = let verify_encoding = isa.encode(
isa.encode(&self.func.dfg, &self.func.dfg,
&self.func.dfg[inst], &self.func.dfg[inst],
self.func.dfg.ctrl_typevar(inst)); self.func.dfg.ctrl_typevar(inst),
);
match verify_encoding { match verify_encoding {
Ok(verify_encoding) => { Ok(verify_encoding) => {
if verify_encoding != encoding { if verify_encoding != encoding {
return err!(inst, return err!(
inst,
"Instruction re-encoding {} doesn't match {}", "Instruction re-encoding {} doesn't match {}",
isa.encoding_info().display(verify_encoding), isa.encoding_info().display(verify_encoding),
isa.encoding_info().display(encoding)); isa.encoding_info().display(encoding)
);
} }
} }
Err(_) => { Err(_) => {
return err!(inst, return err!(
inst,
"Instruction failed to re-encode {}", "Instruction failed to re-encode {}",
isa.encoding_info().display(encoding)) isa.encoding_info().display(encoding)
)
} }
} }
return Ok(()); return Ok(());
@@ -932,9 +989,9 @@ mod tests {
let mut func = Function::new(); let mut func = Function::new();
let ebb0 = func.dfg.make_ebb(); let ebb0 = func.dfg.make_ebb();
func.layout.append_ebb(ebb0); func.layout.append_ebb(ebb0);
let nullary_with_bad_opcode = let nullary_with_bad_opcode = func.dfg.make_inst(
func.dfg InstructionData::Nullary { opcode: Opcode::Jump },
.make_inst(InstructionData::Nullary { opcode: Opcode::Jump }); );
func.layout.append_inst(nullary_with_bad_opcode, ebb0); func.layout.append_inst(nullary_with_bad_opcode, ebb0);
let verifier = Verifier::new(&func, None); let verifier = Verifier::new(&func, None);
assert_err_with_msg!(verifier.run(), "instruction format"); assert_err_with_msg!(verifier.run(), "instruction format");

View File

@@ -38,10 +38,11 @@ fn write_spec(w: &mut Write, func: &Function, regs: Option<&RegInfo>) -> Result
write!(w, "function {}{}", func.name, func.signature.display(regs)) write!(w, "function {}{}", func.name, func.signature.display(regs))
} }
fn write_preamble(w: &mut Write, fn write_preamble(
w: &mut Write,
func: &Function, func: &Function,
regs: Option<&RegInfo>) regs: Option<&RegInfo>,
-> result::Result<bool, Error> { ) -> result::Result<bool, Error> {
let mut any = false; let mut any = false;
for ss in func.stack_slots.keys() { for ss in func.stack_slots.keys() {
@@ -63,10 +64,12 @@ fn write_preamble(w: &mut Write,
// signatures. // signatures.
for sig in func.dfg.signatures.keys() { for sig in func.dfg.signatures.keys() {
any = true; any = true;
writeln!(w, writeln!(
w,
" {} = {}", " {} = {}",
sig, sig,
func.dfg.signatures[sig].display(regs))?; func.dfg.signatures[sig].display(regs)
)?;
} }
for fnref in func.dfg.ext_funcs.keys() { for fnref in func.dfg.ext_funcs.keys() {
@@ -163,8 +166,10 @@ fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
} }
let rtype = func.dfg.ctrl_typevar(inst); let rtype = func.dfg.ctrl_typevar(inst);
assert!(!rtype.is_void(), assert!(
"Polymorphic instruction must produce a result"); !rtype.is_void(),
"Polymorphic instruction must produce a result"
);
Some(rtype) Some(rtype)
} }
@@ -179,11 +184,12 @@ fn write_value_aliases(w: &mut Write, func: &Function, inst: Inst, indent: usize
Ok(()) Ok(())
} }
fn write_instruction(w: &mut Write, fn write_instruction(
w: &mut Write,
func: &Function, func: &Function,
isa: Option<&TargetIsa>, isa: Option<&TargetIsa>,
inst: Inst) inst: Inst,
-> Result { ) -> Result {
// Indent all instructions to col 24 if any encodings are present. // Indent all instructions to col 24 if any encodings are present.
let indent = if func.encodings.is_empty() { 4 } else { 24 }; let indent = if func.encodings.is_empty() { 4 } else { 24 };
@@ -240,11 +246,12 @@ fn write_instruction(w: &mut Write,
} }
/// Write the operands of `inst` to `w` with a prepended space. /// Write the operands of `inst` to `w` with a prepended space.
pub fn write_operands(w: &mut Write, pub fn write_operands(
w: &mut Write,
dfg: &DataFlowGraph, dfg: &DataFlowGraph,
isa: Option<&TargetIsa>, isa: Option<&TargetIsa>,
inst: Inst) inst: Inst,
-> Result { ) -> Result {
let pool = &dfg.value_lists; let pool = &dfg.value_lists;
use ir::instructions::InstructionData::*; use ir::instructions::InstructionData::*;
match dfg[inst] { match dfg[inst] {
@@ -278,10 +285,12 @@ pub fn write_operands(w: &mut Write,
if args.is_empty() { if args.is_empty() {
write!(w, " {}", destination) write!(w, " {}", destination)
} else { } else {
write!(w, write!(
w,
" {}({})", " {}({})",
destination, destination,
DisplayValues(args.as_slice(pool))) DisplayValues(args.as_slice(pool))
)
} }
} }
Branch { Branch {
@@ -315,11 +324,13 @@ pub fn write_operands(w: &mut Write,
} }
IndirectCall { sig_ref, ref args, .. } => { IndirectCall { sig_ref, ref args, .. } => {
let args = args.as_slice(pool); let args = args.as_slice(pool);
write!(w, write!(
w,
" {}, {}({})", " {}, {}({})",
sig_ref, sig_ref,
args[0], args[0],
DisplayValues(&args[1..])) DisplayValues(&args[1..])
)
} }
StackLoad { stack_slot, offset, .. } => write!(w, " {}{}", stack_slot, offset), StackLoad { stack_slot, offset, .. } => write!(w, " {}{}", stack_slot, offset),
StackStore { StackStore {
@@ -341,11 +352,13 @@ pub fn write_operands(w: &mut Write,
RegMove { arg, src, dst, .. } => { RegMove { arg, src, dst, .. } => {
if let Some(isa) = isa { if let Some(isa) = isa {
let regs = isa.register_info(); let regs = isa.register_info();
write!(w, write!(
w,
" {}, {} -> {}", " {}, {} -> {}",
arg, arg,
regs.display_regunit(src), regs.display_regunit(src),
regs.display_regunit(dst)) regs.display_regunit(dst)
)
} else { } else {
write!(w, " {}, %{} -> %{}", arg, src, dst) write!(w, " {}, %{} -> %{}", arg, src, dst)
} }
@@ -382,22 +395,31 @@ mod tests {
f.name = FunctionName::new("foo"); f.name = FunctionName::new("foo");
assert_eq!(f.to_string(), "function %foo() native {\n}\n"); assert_eq!(f.to_string(), "function %foo() native {\n}\n");
f.stack_slots f.stack_slots.push(
.push(StackSlotData::new(StackSlotKind::Local, 4)); StackSlotData::new(StackSlotKind::Local, 4),
assert_eq!(f.to_string(), );
"function %foo() native {\n ss0 = local 4\n}\n"); assert_eq!(
f.to_string(),
"function %foo() native {\n ss0 = local 4\n}\n"
);
let ebb = f.dfg.make_ebb(); let ebb = f.dfg.make_ebb();
f.layout.append_ebb(ebb); f.layout.append_ebb(ebb);
assert_eq!(f.to_string(), assert_eq!(
"function %foo() native {\n ss0 = local 4\n\nebb0:\n}\n"); f.to_string(),
"function %foo() native {\n ss0 = local 4\n\nebb0:\n}\n"
);
f.dfg.append_ebb_arg(ebb, types::I8); f.dfg.append_ebb_arg(ebb, types::I8);
assert_eq!(f.to_string(), assert_eq!(
"function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8):\n}\n"); f.to_string(),
"function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8):\n}\n"
);
f.dfg.append_ebb_arg(ebb, types::F32.by(4).unwrap()); f.dfg.append_ebb_arg(ebb, types::F32.by(4).unwrap());
assert_eq!(f.to_string(), assert_eq!(
"function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"); f.to_string(),
"function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"
);
} }
} }

View File

@@ -47,9 +47,11 @@ impl Directive {
"unordered" => Ok(Directive::Unordered(pat)), "unordered" => Ok(Directive::Unordered(pat)),
"not" => { "not" => {
if !pat.defs().is_empty() { if !pat.defs().is_empty() {
let msg = format!("can't define variables '$({}=...' in not: {}", let msg = format!(
"can't define variables '$({}=...' in not: {}",
pat.defs()[0], pat.defs()[0],
rest); rest
);
Err(Error::DuplicateDef(msg)) Err(Error::DuplicateDef(msg))
} else { } else {
Ok(Directive::Not(pat)) Ok(Directive::Not(pat))
@@ -63,16 +65,23 @@ impl Directive {
fn regex(rest: &str) -> Result<Directive> { fn regex(rest: &str) -> Result<Directive> {
let varlen = varname_prefix(rest); let varlen = varname_prefix(rest);
if varlen == 0 { if varlen == 0 {
return Err(Error::Syntax(format!("invalid variable name in regex: {}", rest))); return Err(Error::Syntax(
format!("invalid variable name in regex: {}", rest),
));
} }
let var = rest[0..varlen].to_string(); let var = rest[0..varlen].to_string();
if !rest[varlen..].starts_with('=') { if !rest[varlen..].starts_with('=') {
return Err(Error::Syntax(format!("expected '=' after variable '{}' in regex: {}", return Err(Error::Syntax(format!(
"expected '=' after variable '{}' in regex: {}",
var, var,
rest))); rest
)));
} }
// Ignore trailing white space in the regex, including CR. // Ignore trailing white space in the regex, including CR.
Ok(Directive::Regex(var, rest[varlen + 1..].trim_right().to_string())) Ok(Directive::Regex(
var,
rest[varlen + 1..].trim_right().to_string(),
))
} }
} }
@@ -183,13 +192,13 @@ impl Checker {
continue; continue;
} }
Directive::Regex(ref var, ref rx) => { Directive::Regex(ref var, ref rx) => {
state state.vars.insert(
.vars var.clone(),
.insert(var.clone(),
VarDef { VarDef {
value: Value::Regex(Cow::Borrowed(rx)), value: Value::Regex(Cow::Borrowed(rx)),
offset: 0, offset: 0,
}); },
);
continue; continue;
} }
}; };
@@ -210,15 +219,16 @@ impl Checker {
state.recorder.directive(not_idx); state.recorder.directive(not_idx);
if let Some(mat) = rx.find(&text[not_begin..match_begin]) { if let Some(mat) = rx.find(&text[not_begin..match_begin]) {
// Matched `not:` pattern. // Matched `not:` pattern.
state state.recorder.matched_not(rx.as_str(), (
.recorder not_begin + mat.start(),
.matched_not(rx.as_str(), not_begin + mat.end(),
(not_begin + mat.start(), not_begin + mat.end())); ));
return Ok(false); return Ok(false);
} else { } else {
state state.recorder.missed_not(
.recorder rx.as_str(),
.missed_not(rx.as_str(), (not_begin, match_begin)); (not_begin, match_begin),
);
} }
} }
} }
@@ -413,20 +423,32 @@ mod tests {
let mut b = CheckerBuilder::new(); let mut b = CheckerBuilder::new();
assert_eq!(b.directive("not here: more text").map_err(e2s), Ok(false)); assert_eq!(b.directive("not here: more text").map_err(e2s), Ok(false));
assert_eq!(b.directive("not here: regex: X=more text").map_err(e2s), assert_eq!(
Ok(true)); b.directive("not here: regex: X=more text").map_err(e2s),
assert_eq!(b.directive("regex: X = tommy").map_err(e2s), Ok(true)
Err("expected '=' after variable 'X' in regex: X = tommy".to_string())); );
assert_eq!(b.directive("[arm]not: patt $x $(y) here").map_err(e2s), assert_eq!(
Ok(true)); b.directive("regex: X = tommy").map_err(e2s),
assert_eq!(b.directive("[x86]sameln: $x $(y=[^]]*) there").map_err(e2s), Err(
Ok(true)); "expected '=' after variable 'X' in regex: X = tommy".to_string(),
)
);
assert_eq!(
b.directive("[arm]not: patt $x $(y) here").map_err(e2s),
Ok(true)
);
assert_eq!(
b.directive("[x86]sameln: $x $(y=[^]]*) there").map_err(e2s),
Ok(true)
);
// Windows line ending sneaking in. // Windows line ending sneaking in.
assert_eq!(b.directive("regex: Y=foo\r").map_err(e2s), Ok(true)); assert_eq!(b.directive("regex: Y=foo\r").map_err(e2s), Ok(true));
let c = b.finish(); let c = b.finish();
assert_eq!(c.to_string(), assert_eq!(
c.to_string(),
"#0 regex: X=more text\n#1 not: patt $(x) $(y) here\n#2 sameln: $(x) \ "#0 regex: X=more text\n#1 not: patt $(x) $(y) here\n#2 sameln: $(x) \
$(y=[^]]*) there\n#3 regex: Y=foo\n"); $(y=[^]]*) there\n#3 regex: Y=foo\n"
);
} }
} }

View File

@@ -111,16 +111,21 @@ impl<'a> Display for Explainer<'a> {
} }
// Emit the match message itself. // Emit the match message itself.
writeln!(f, writeln!(
f,
"{} #{}{}: {}", "{} #{}{}: {}",
if m.is_match { "Matched" } else { "Missed" }, if m.is_match { "Matched" } else { "Missed" },
m.directive, m.directive,
if m.is_not { " not" } else { "" }, if m.is_not { " not" } else { "" },
m.regex)?; m.regex
)?;
// Emit any variable definitions. // Emit any variable definitions.
if let Ok(found) = self.vardefs if let Ok(found) = self.vardefs.binary_search_by_key(
.binary_search_by_key(&m.directive, |v| v.directive) { &m.directive,
|v| v.directive,
)
{
let mut first = found; let mut first = found;
while first > 0 && self.vardefs[first - 1].directive == m.directive { while first > 0 && self.vardefs[first - 1].directive == m.directive {
first -= 1; first -= 1;
@@ -148,8 +153,7 @@ impl<'a> Recorder for Explainer<'a> {
} }
fn matched_check(&mut self, regex: &str, matched: MatchRange) { fn matched_check(&mut self, regex: &str, matched: MatchRange) {
self.matches self.matches.push(Match {
.push(Match {
directive: self.directive, directive: self.directive,
is_match: true, is_match: true,
is_not: false, is_not: false,
@@ -159,8 +163,7 @@ impl<'a> Recorder for Explainer<'a> {
} }
fn matched_not(&mut self, regex: &str, matched: MatchRange) { fn matched_not(&mut self, regex: &str, matched: MatchRange) {
self.matches self.matches.push(Match {
.push(Match {
directive: self.directive, directive: self.directive,
is_match: true, is_match: true,
is_not: true, is_not: true,
@@ -170,8 +173,7 @@ impl<'a> Recorder for Explainer<'a> {
} }
fn missed_check(&mut self, regex: &str, searched: MatchRange) { fn missed_check(&mut self, regex: &str, searched: MatchRange) {
self.matches self.matches.push(Match {
.push(Match {
directive: self.directive, directive: self.directive,
is_match: false, is_match: false,
is_not: false, is_not: false,
@@ -181,8 +183,7 @@ impl<'a> Recorder for Explainer<'a> {
} }
fn missed_not(&mut self, regex: &str, searched: MatchRange) { fn missed_not(&mut self, regex: &str, searched: MatchRange) {
self.matches self.matches.push(Match {
.push(Match {
directive: self.directive, directive: self.directive,
is_match: false, is_match: false,
is_not: true, is_not: true,
@@ -192,8 +193,7 @@ impl<'a> Recorder for Explainer<'a> {
} }
fn defined_var(&mut self, varname: &str, value: &str) { fn defined_var(&mut self, varname: &str, value: &str) {
self.vardefs self.vardefs.push(VarDef {
.push(VarDef {
directive: self.directive, directive: self.directive,
varname: varname.to_owned(), varname: varname.to_owned(),
value: value.to_owned(), value: value.to_owned(),

View File

@@ -70,7 +70,9 @@ impl Pattern {
/// Return the allocated def number. /// Return the allocated def number.
fn add_def(&mut self, v: &str) -> Result<usize> { fn add_def(&mut self, v: &str) -> Result<usize> {
if self.defines_var(v) { if self.defines_var(v) {
Err(Error::DuplicateDef(format!("duplicate definition of ${} in same pattern", v))) Err(Error::DuplicateDef(
format!("duplicate definition of ${} in same pattern", v),
))
} else { } else {
let idx = self.defs.len(); let idx = self.defs.len();
self.defs.push(v.to_string()); self.defs.push(v.to_string());
@@ -111,8 +113,10 @@ impl Pattern {
// All remaining possibilities start with `$(`. // All remaining possibilities start with `$(`.
if s.len() < 2 || !s.starts_with("$(") { if s.len() < 2 || !s.starts_with("$(") {
return Err(Error::Syntax("pattern syntax error, use $$ to match a single $" return Err(Error::Syntax(
.to_string())); "pattern syntax error, use $$ to match a single $"
.to_string(),
));
} }
// Match the variable name, allowing for an empty varname in `$()`, or `$(=...)`. // Match the variable name, allowing for an empty varname in `$()`, or `$(=...)`.
@@ -137,7 +141,9 @@ impl Pattern {
// Variable definition. Fall through. // Variable definition. Fall through.
} }
Some(ch) => { Some(ch) => {
return Err(Error::Syntax(format!("syntax error in $({}... '{}'", varname, ch))); return Err(Error::Syntax(
format!("syntax error in $({}... '{}'", varname, ch),
));
} }
} }
@@ -155,22 +161,30 @@ impl Pattern {
let refname_begin = varname_end + 2; let refname_begin = varname_end + 2;
let refname_end = refname_begin + varname_prefix(&s[refname_begin..]); let refname_end = refname_begin + varname_prefix(&s[refname_begin..]);
if refname_begin == refname_end { if refname_begin == refname_end {
return Err(Error::Syntax(format!("expected variable name in $({}=$...", varname))); return Err(Error::Syntax(
format!("expected variable name in $({}=$...", varname),
));
} }
if !s[refname_end..].starts_with(')') { if !s[refname_end..].starts_with(')') {
return Err(Error::Syntax(format!("expected ')' after $({}=${}...", return Err(Error::Syntax(format!(
"expected ')' after $({}=${}...",
varname, varname,
&s[refname_begin..refname_end]))); &s[refname_begin..refname_end]
)));
} }
let refname = s[refname_begin..refname_end].to_string(); let refname = s[refname_begin..refname_end].to_string();
return if let Some(defidx) = def { return if let Some(defidx) = def {
Ok((Part::DefVar { Ok((
Part::DefVar {
def: defidx, def: defidx,
var: refname, var: refname,
}, },
refname_end + 1)) refname_end + 1,
))
} else { } else {
Err(Error::Syntax(format!("expected variable name in $(=${})", refname))) Err(Error::Syntax(
format!("expected variable name in $(=${})", refname),
))
}; };
} }
@@ -193,9 +207,11 @@ impl Pattern {
}; };
Ok((part, rx_end + 1)) Ok((part, rx_end + 1))
} else { } else {
Err(Error::Syntax(format!("missing ')' after regex in $({}={}", Err(Error::Syntax(format!(
"missing ')' after regex in $({}={}",
varname, varname,
&s[rx_begin..rx_end]))) &s[rx_begin..rx_end]
)))
} }
} }
} }
@@ -273,9 +289,11 @@ impl FromStr for Pattern {
let (part, len) = pat.parse_part(&s[pos..])?; let (part, len) = pat.parse_part(&s[pos..])?;
if let Some(v) = part.ref_var() { if let Some(v) = part.ref_var() {
if pat.defines_var(v) { if pat.defines_var(v) {
return Err(Error::Backref(format!("unsupported back-reference to '${}' \ return Err(Error::Backref(format!(
"unsupported back-reference to '${}' \
defined in same pattern", defined in same pattern",
v))); v
)));
} }
} }
pat.parts.push(part); pat.parts.push(part);
@@ -410,49 +428,87 @@ mod tests {
// This is dubious, should we panic instead? // This is dubious, should we panic instead?
assert_eq!(pat.parse_part("").unwrap(), (Part::Text("".to_string()), 0)); assert_eq!(pat.parse_part("").unwrap(), (Part::Text("".to_string()), 0));
assert_eq!(pat.parse_part("x").unwrap(), assert_eq!(
(Part::Text("x".to_string()), 1)); pat.parse_part("x").unwrap(),
assert_eq!(pat.parse_part("x2").unwrap(), (Part::Text("x".to_string()), 1)
(Part::Text("x2".to_string()), 2)); );
assert_eq!(pat.parse_part("x$").unwrap(), assert_eq!(pat.parse_part("x2").unwrap(), (
(Part::Text("x".to_string()), 1)); Part::Text("x2".to_string()),
assert_eq!(pat.parse_part("x$$").unwrap(), 2,
(Part::Text("x".to_string()), 1)); ));
assert_eq!(pat.parse_part("x$").unwrap(), (
Part::Text("x".to_string()),
1,
));
assert_eq!(pat.parse_part("x$$").unwrap(), (
Part::Text("x".to_string()),
1,
));
assert_eq!(pat.parse_part("$").unwrap_err().to_string(), assert_eq!(
"pattern syntax error, use $$ to match a single $"); pat.parse_part("$").unwrap_err().to_string(),
"pattern syntax error, use $$ to match a single $"
);
assert_eq!(pat.parse_part("$$").unwrap(), assert_eq!(pat.parse_part("$$").unwrap(), (
(Part::Text("$".to_string()), 2)); Part::Text("$".to_string()),
assert_eq!(pat.parse_part("$$ ").unwrap(), 2,
(Part::Text("$".to_string()), 2)); ));
assert_eq!(pat.parse_part("$$ ").unwrap(), (
Part::Text("$".to_string()),
2,
));
assert_eq!(pat.parse_part("$0").unwrap(), assert_eq!(
(Part::Var("0".to_string()), 2)); pat.parse_part("$0").unwrap(),
assert_eq!(pat.parse_part("$xx=").unwrap(), (Part::Var("0".to_string()), 2)
(Part::Var("xx".to_string()), 3)); );
assert_eq!(pat.parse_part("$xx$").unwrap(), assert_eq!(pat.parse_part("$xx=").unwrap(), (
(Part::Var("xx".to_string()), 3)); Part::Var("xx".to_string()),
3,
));
assert_eq!(pat.parse_part("$xx$").unwrap(), (
Part::Var("xx".to_string()),
3,
));
assert_eq!(pat.parse_part("$(0)").unwrap(), assert_eq!(pat.parse_part("$(0)").unwrap(), (
(Part::Var("0".to_string()), 4)); Part::Var("0".to_string()),
assert_eq!(pat.parse_part("$()").unwrap(), 4,
(Part::Text("".to_string()), 3)); ));
assert_eq!(pat.parse_part("$()").unwrap(), (
Part::Text("".to_string()),
3,
));
assert_eq!(pat.parse_part("$(0").unwrap_err().to_string(), assert_eq!(
("unterminated $(0...")); pat.parse_part("$(0").unwrap_err().to_string(),
assert_eq!(pat.parse_part("$(foo:").unwrap_err().to_string(), ("unterminated $(0...")
("syntax error in $(foo... ':'")); );
assert_eq!(pat.parse_part("$(foo =").unwrap_err().to_string(), assert_eq!(
("syntax error in $(foo... ' '")); pat.parse_part("$(foo:").unwrap_err().to_string(),
assert_eq!(pat.parse_part("$(eo0=$bar").unwrap_err().to_string(), ("syntax error in $(foo... ':'")
("expected ')' after $(eo0=$bar...")); );
assert_eq!(pat.parse_part("$(eo1=$bar}").unwrap_err().to_string(), assert_eq!(
("expected ')' after $(eo1=$bar...")); pat.parse_part("$(foo =").unwrap_err().to_string(),
assert_eq!(pat.parse_part("$(eo2=$)").unwrap_err().to_string(), ("syntax error in $(foo... ' '")
("expected variable name in $(eo2=$...")); );
assert_eq!(pat.parse_part("$(eo3=$-)").unwrap_err().to_string(), assert_eq!(
("expected variable name in $(eo3=$...")); pat.parse_part("$(eo0=$bar").unwrap_err().to_string(),
("expected ')' after $(eo0=$bar...")
);
assert_eq!(
pat.parse_part("$(eo1=$bar}").unwrap_err().to_string(),
("expected ')' after $(eo1=$bar...")
);
assert_eq!(
pat.parse_part("$(eo2=$)").unwrap_err().to_string(),
("expected variable name in $(eo2=$...")
);
assert_eq!(
pat.parse_part("$(eo3=$-)").unwrap_err().to_string(),
("expected variable name in $(eo3=$...")
);
} }
#[test] #[test]
@@ -460,48 +516,65 @@ mod tests {
use super::{Pattern, Part}; use super::{Pattern, Part};
let mut pat = Pattern::new(); let mut pat = Pattern::new();
assert_eq!(pat.parse_part("$(foo=$bar)").unwrap(), assert_eq!(pat.parse_part("$(foo=$bar)").unwrap(), (
(Part::DefVar { Part::DefVar {
def: 0, def: 0,
var: "bar".to_string(), var: "bar".to_string(),
}, },
11)); 11,
assert_eq!(pat.parse_part("$(foo=$bar)").unwrap_err().to_string(), ));
"duplicate definition of $foo in same pattern"); assert_eq!(
pat.parse_part("$(foo=$bar)").unwrap_err().to_string(),
"duplicate definition of $foo in same pattern"
);
assert_eq!(pat.parse_part("$(fxo=$bar)x").unwrap(), assert_eq!(pat.parse_part("$(fxo=$bar)x").unwrap(), (
(Part::DefVar { Part::DefVar {
def: 1, def: 1,
var: "bar".to_string(), var: "bar".to_string(),
}, },
11)); 11,
));
assert_eq!(pat.parse_part("$(fo2=[a-z])").unwrap(), assert_eq!(pat.parse_part("$(fo2=[a-z])").unwrap(), (
(Part::DefLit { Part::DefLit {
def: 2, def: 2,
regex: "(?P<fo2>[a-z])".to_string(), regex: "(?P<fo2>[a-z])".to_string(),
}, },
12)); 12,
assert_eq!(pat.parse_part("$(fo3=[a-)])").unwrap(), ));
(Part::DefLit { assert_eq!(pat.parse_part("$(fo3=[a-)])").unwrap(), (
Part::DefLit {
def: 3, def: 3,
regex: "(?P<fo3>[a-)])".to_string(), regex: "(?P<fo3>[a-)])".to_string(),
}, },
12)); 12,
assert_eq!(pat.parse_part("$(fo4=)").unwrap(), ));
(Part::DefLit { assert_eq!(pat.parse_part("$(fo4=)").unwrap(), (
Part::DefLit {
def: 4, def: 4,
regex: "(?P<fo4>)".to_string(), regex: "(?P<fo4>)".to_string(),
}, },
7)); 7,
));
assert_eq!(pat.parse_part("$(=.*)").unwrap(), assert_eq!(pat.parse_part("$(=.*)").unwrap(), (
(Part::Regex("(?:.*)".to_string()), 6)); Part::Regex(
"(?:.*)".to_string(),
),
6,
));
assert_eq!(pat.parse_part("$(=)").unwrap(), assert_eq!(pat.parse_part("$(=)").unwrap(), (
(Part::Regex("(?:)".to_string()), 4)); Part::Regex(
assert_eq!(pat.parse_part("$()").unwrap(), "(?:)".to_string(),
(Part::Text("".to_string()), 3)); ),
4,
));
assert_eq!(pat.parse_part("$()").unwrap(), (
Part::Text("".to_string()),
3,
));
} }
#[test] #[test]
@@ -512,7 +585,9 @@ mod tests {
assert_eq!(format!("{:?}", p.parts), "[Text(\"Hello world!\")]"); assert_eq!(format!("{:?}", p.parts), "[Text(\"Hello world!\")]");
let p: Pattern = " $foo=$(bar) ".parse().unwrap(); let p: Pattern = " $foo=$(bar) ".parse().unwrap();
assert_eq!(format!("{:?}", p.parts), assert_eq!(
"[Var(\"foo\"), Text(\"=\"), Var(\"bar\")]"); format!("{:?}", p.parts),
"[Var(\"foo\"), Text(\"=\"), Var(\"bar\")]"
);
} }
} }

View File

@@ -42,10 +42,12 @@ fn no_matches() {
#[test] #[test]
fn simple() { fn simple() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
check: one check: one
check: two check: two
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
@@ -71,10 +73,12 @@ fn simple() {
#[test] #[test]
fn sameln() { fn sameln() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
check: one check: one
sameln: two sameln: two
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
@@ -106,10 +110,12 @@ fn sameln() {
#[test] #[test]
fn nextln() { fn nextln() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
check: one check: one
nextln: two nextln: two
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
@@ -149,10 +155,12 @@ fn leading_nextln() {
// A leading nextln directive should match from line 2. // A leading nextln directive should match from line 2.
// This is somewhat arbitrary, but consistent with a preceeding 'check: $()' directive. // This is somewhat arbitrary, but consistent with a preceeding 'check: $()' directive.
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
nextln: one nextln: one
nextln: two nextln: two
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
@@ -174,10 +182,12 @@ fn leading_nextln() {
fn leading_sameln() { fn leading_sameln() {
// A leading sameln directive should match from line 1. // A leading sameln directive should match from line 1.
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
sameln: one sameln: one
sameln: two sameln: two
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
@@ -197,11 +207,13 @@ fn leading_sameln() {
#[test] #[test]
fn not() { fn not() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
check: one$() check: one$()
not: $()eat$() not: $()eat$()
check: $()two check: $()two
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
@@ -221,12 +233,14 @@ fn not() {
#[test] #[test]
fn notnot() { fn notnot() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
check: one$() check: one$()
not: $()eat$() not: $()eat$()
not: half not: half
check: $()two check: $()two
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
@@ -254,87 +268,135 @@ fn notnot() {
#[test] #[test]
fn unordered() { fn unordered() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
check: one check: one
unordered: two unordered: two
unordered: three unordered: three
check: four check: four
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
assert_eq!(c.check("one two three four", NO_VARIABLES).map_err(e2s), assert_eq!(
Ok(true)); c.check("one two three four", NO_VARIABLES).map_err(e2s),
assert_eq!(c.check("one three two four", NO_VARIABLES).map_err(e2s), Ok(true)
Ok(true)); );
assert_eq!(
c.check("one three two four", NO_VARIABLES).map_err(e2s),
Ok(true)
);
assert_eq!(c.check("one two four three four", NO_VARIABLES) assert_eq!(
.map_err(e2s), c.check("one two four three four", NO_VARIABLES).map_err(
Ok(true)); e2s,
assert_eq!(c.check("one three four two four", NO_VARIABLES) ),
.map_err(e2s), Ok(true)
Ok(true)); );
assert_eq!(
c.check("one three four two four", NO_VARIABLES).map_err(
e2s,
),
Ok(true)
);
assert_eq!(c.check("one two four three", NO_VARIABLES).map_err(e2s), assert_eq!(
Ok(false)); c.check("one two four three", NO_VARIABLES).map_err(e2s),
assert_eq!(c.check("one three four two", NO_VARIABLES).map_err(e2s), Ok(false)
Ok(false)); );
assert_eq!(
c.check("one three four two", NO_VARIABLES).map_err(e2s),
Ok(false)
);
} }
#[test] #[test]
fn leading_unordered() { fn leading_unordered() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
unordered: two unordered: two
unordered: three unordered: three
check: four check: four
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
assert_eq!(c.check("one two three four", NO_VARIABLES).map_err(e2s), assert_eq!(
Ok(true)); c.check("one two three four", NO_VARIABLES).map_err(e2s),
assert_eq!(c.check("one three two four", NO_VARIABLES).map_err(e2s), Ok(true)
Ok(true)); );
assert_eq!(
c.check("one three two four", NO_VARIABLES).map_err(e2s),
Ok(true)
);
assert_eq!(c.check("one two four three four", NO_VARIABLES) assert_eq!(
.map_err(e2s), c.check("one two four three four", NO_VARIABLES).map_err(
Ok(true)); e2s,
assert_eq!(c.check("one three four two four", NO_VARIABLES) ),
.map_err(e2s), Ok(true)
Ok(true)); );
assert_eq!(
c.check("one three four two four", NO_VARIABLES).map_err(
e2s,
),
Ok(true)
);
assert_eq!(c.check("one two four three", NO_VARIABLES).map_err(e2s), assert_eq!(
Ok(false)); c.check("one two four three", NO_VARIABLES).map_err(e2s),
assert_eq!(c.check("one three four two", NO_VARIABLES).map_err(e2s), Ok(false)
Ok(false)); );
assert_eq!(
c.check("one three four two", NO_VARIABLES).map_err(e2s),
Ok(false)
);
} }
#[test] #[test]
fn trailing_unordered() { fn trailing_unordered() {
let c = CheckerBuilder::new() let c = CheckerBuilder::new()
.text(" .text(
"
check: one check: one
unordered: two unordered: two
unordered: three unordered: three
") ",
)
.unwrap() .unwrap()
.finish(); .finish();
assert_eq!(c.check("one two three four", NO_VARIABLES).map_err(e2s), assert_eq!(
Ok(true)); c.check("one two three four", NO_VARIABLES).map_err(e2s),
assert_eq!(c.check("one three two four", NO_VARIABLES).map_err(e2s), Ok(true)
Ok(true)); );
assert_eq!(
c.check("one three two four", NO_VARIABLES).map_err(e2s),
Ok(true)
);
assert_eq!(c.check("one two four three four", NO_VARIABLES) assert_eq!(
.map_err(e2s), c.check("one two four three four", NO_VARIABLES).map_err(
Ok(true)); e2s,
assert_eq!(c.check("one three four two four", NO_VARIABLES) ),
.map_err(e2s), Ok(true)
Ok(true)); );
assert_eq!(
c.check("one three four two four", NO_VARIABLES).map_err(
e2s,
),
Ok(true)
);
assert_eq!(c.check("one two four three", NO_VARIABLES).map_err(e2s), assert_eq!(
Ok(true)); c.check("one two four three", NO_VARIABLES).map_err(e2s),
assert_eq!(c.check("one three four two", NO_VARIABLES).map_err(e2s), Ok(true)
Ok(true)); );
assert_eq!(
c.check("one three four two", NO_VARIABLES).map_err(e2s),
Ok(true)
);
} }

View File

@@ -12,7 +12,8 @@ use std::hash::Hash;
/// Permanent structure used for translating into Cretonne IL. /// Permanent structure used for translating into Cretonne IL.
pub struct ILBuilder<Variable> pub struct ILBuilder<Variable>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
ssa: SSABuilder<Variable>, ssa: SSABuilder<Variable>,
ebbs: EntityMap<Ebb, EbbData>, ebbs: EntityMap<Ebb, EbbData>,
@@ -23,7 +24,8 @@ pub struct ILBuilder<Variable>
/// Temporary object used to build a Cretonne IL `Function`. /// Temporary object used to build a Cretonne IL `Function`.
pub struct FunctionBuilder<'a, Variable: 'a> pub struct FunctionBuilder<'a, Variable: 'a>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
func: &'a mut Function, func: &'a mut Function,
builder: &'a mut ILBuilder<Variable>, builder: &'a mut ILBuilder<Variable>,
@@ -44,7 +46,8 @@ struct Position {
} }
impl<Variable> ILBuilder<Variable> impl<Variable> ILBuilder<Variable>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
/// Creates a ILBuilder structure. The structure is automatically cleared each time it is /// Creates a ILBuilder structure. The structure is automatically cleared each time it is
/// passed to a [`FunctionBuilder`](struct.FunctionBuilder.html) for creation. /// passed to a [`FunctionBuilder`](struct.FunctionBuilder.html) for creation.
@@ -68,7 +71,8 @@ impl<Variable> ILBuilder<Variable>
/// Implementation of the [`InstBuilder`](../cretonne/ir/builder/trait.InstBuilder.html) that has /// Implementation of the [`InstBuilder`](../cretonne/ir/builder/trait.InstBuilder.html) that has
/// one convenience method per Cretonne IL instruction. /// one convenience method per Cretonne IL instruction.
pub struct FuncInstBuilder<'short, 'long: 'short, Variable: 'long> pub struct FuncInstBuilder<'short, 'long: 'short, Variable: 'long>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
builder: &'short mut FunctionBuilder<'long, Variable>, builder: &'short mut FunctionBuilder<'long, Variable>,
ebb: Ebb, ebb: Ebb,
@@ -103,7 +107,7 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
self.builder self.builder
.check_return_args(data.arguments(&self.builder.func.dfg.value_lists)) .check_return_args(data.arguments(&self.builder.func.dfg.value_lists))
} }
// We only insert the Ebb in the layout when an instruction is added to it // We only insert the Ebb in the layout when an instruction is added to it
if self.builder.builder.ebbs[self.builder.position.ebb].pristine { if self.builder.builder.ebbs[self.builder.position.ebb].pristine {
if !self.builder if !self.builder
.func .func
@@ -125,9 +129,9 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
if data.opcode().is_branch() { if data.opcode().is_branch() {
match data.branch_destination() { match data.branch_destination() {
Some(dest_ebb) => { Some(dest_ebb) => {
// If the user has supplied jump arguments we must adapt the arguments of // If the user has supplied jump arguments we must adapt the arguments of
// the destination ebb // the destination ebb
// TODO: find a way not to allocate a vector // TODO: find a way not to allocate a vector
let args_types: Vec<Type> = let args_types: Vec<Type> =
match data.analyze_branch(&self.builder.func.dfg.value_lists) { match data.analyze_branch(&self.builder.func.dfg.value_lists) {
BranchInfo::SingleDest(_, args) => { BranchInfo::SingleDest(_, args) => {
@@ -142,14 +146,14 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
self.builder.declare_successor(dest_ebb, inst); self.builder.declare_successor(dest_ebb, inst);
} }
None => { None => {
// branch_destination() doesn't detect jump_tables // branch_destination() doesn't detect jump_tables
match data { match data {
// If jump table we declare all entries successor // If jump table we declare all entries successor
// TODO: not collect with vector? // TODO: not collect with vector?
InstructionData::BranchTable { table, .. } => { InstructionData::BranchTable { table, .. } => {
// Unlike all other jumps/branches, jump tables are // Unlike all other jumps/branches, jump tables are
// capable of having the same successor appear // capable of having the same successor appear
// multiple times. Use a HashSet to deduplicate. // multiple times. Use a HashSet to deduplicate.
let mut unique = HashSet::new(); let mut unique = HashSet::new();
for dest_ebb in self.builder for dest_ebb in self.builder
.func .func
@@ -163,7 +167,7 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
self.builder.declare_successor(dest_ebb, inst) self.builder.declare_successor(dest_ebb, inst)
} }
} }
// If not we do nothing // If not we do nothing
_ => {} _ => {}
} }
} }
@@ -211,13 +215,15 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
/// `Ebb` when you haven't filled the current one with a terminator instruction, inserting a /// `Ebb` when you haven't filled the current one with a terminator instruction, inserting a
/// return instruction with arguments that don't match the function's signature. /// return instruction with arguments that don't match the function's signature.
impl<'a, Variable> FunctionBuilder<'a, Variable> impl<'a, Variable> FunctionBuilder<'a, Variable>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
/// Creates a new FunctionBuilder structure that will operate on a `Function` using a /// Creates a new FunctionBuilder structure that will operate on a `Function` using a
/// `IlBuilder`. /// `IlBuilder`.
pub fn new(func: &'a mut Function, pub fn new(
builder: &'a mut ILBuilder<Variable>) func: &'a mut Function,
-> FunctionBuilder<'a, Variable> { builder: &'a mut ILBuilder<Variable>,
) -> FunctionBuilder<'a, Variable> {
builder.clear(); builder.clear();
FunctionBuilder { FunctionBuilder {
func: func, func: func,
@@ -255,12 +261,16 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
} }
if !self.builder.ebbs[self.position.ebb].pristine { if !self.builder.ebbs[self.position.ebb].pristine {
// First we check that the previous block has been filled. // First we check that the previous block has been filled.
debug_assert!(self.is_unreachable() || self.builder.ebbs[self.position.ebb].filled, debug_assert!(
"you have to fill your block before switching"); self.is_unreachable() || self.builder.ebbs[self.position.ebb].filled,
"you have to fill your block before switching"
);
} }
// We cannot switch to a filled block // We cannot switch to a filled block
debug_assert!(!self.builder.ebbs[ebb].filled, debug_assert!(
"you cannot switch to a block which is already filled"); !self.builder.ebbs[ebb].filled,
"you cannot switch to a block which is already filled"
);
let basic_block = self.builder.ssa.header_block(ebb); let basic_block = self.builder.ssa.header_block(ebb);
// Then we change the cursor position. // Then we change the cursor position.
@@ -278,12 +288,12 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
/// created. Forgetting to call this method on every block will cause inconsistences in the /// created. Forgetting to call this method on every block will cause inconsistences in the
/// produced functions. /// produced functions.
pub fn seal_block(&mut self, ebb: Ebb) { pub fn seal_block(&mut self, ebb: Ebb) {
let side_effects = self.builder let side_effects = self.builder.ssa.seal_ebb_header_block(
.ssa ebb,
.seal_ebb_header_block(ebb,
&mut self.func.dfg, &mut self.func.dfg,
&mut self.func.layout, &mut self.func.layout,
&mut self.func.jump_tables); &mut self.func.jump_tables,
);
self.handle_ssa_side_effects(side_effects); self.handle_ssa_side_effects(side_effects);
} }
@@ -295,18 +305,17 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
/// Returns the Cretonne IL value corresponding to the utilization at the current program /// Returns the Cretonne IL value corresponding to the utilization at the current program
/// position of a previously defined user variable. /// position of a previously defined user variable.
pub fn use_var(&mut self, var: Variable) -> Value { pub fn use_var(&mut self, var: Variable) -> Value {
let ty = *self.builder let ty = *self.builder.types.get(var).expect(
.types "this variable is used but its type has not been declared",
.get(var) );
.expect("this variable is used but its type has not been declared"); let (val, side_effects) = self.builder.ssa.use_var(
let (val, side_effects) = self.builder &mut self.func.dfg,
.ssa
.use_var(&mut self.func.dfg,
&mut self.func.layout, &mut self.func.layout,
&mut self.func.jump_tables, &mut self.func.jump_tables,
var, var,
ty, ty,
self.position.basic_block); self.position.basic_block,
);
self.handle_ssa_side_effects(side_effects); self.handle_ssa_side_effects(side_effects);
val val
} }
@@ -314,11 +323,15 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
/// Register a new definition of a user variable. Panics if the type of the value is not the /// Register a new definition of a user variable. Panics if the type of the value is not the
/// same as the type registered for the variable. /// same as the type registered for the variable.
pub fn def_var(&mut self, var: Variable, val: Value) { pub fn def_var(&mut self, var: Variable, val: Value) {
debug_assert!(self.func.dfg.value_type(val) == self.builder.types[var], debug_assert!(
"the type of the value is not the type registered for the variable"); self.func.dfg.value_type(val) == self.builder.types[var],
self.builder "the type of the value is not the type registered for the variable"
.ssa );
.def_var(var, val, self.position.basic_block); self.builder.ssa.def_var(
var,
val,
self.position.basic_block,
);
} }
/// Returns the value corresponding to the `i`-th argument of the function as defined by /// Returns the value corresponding to the `i`-th argument of the function as defined by
@@ -369,7 +382,8 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
/// function. The functions below help you inspect the function you're creating and modify it /// function. The functions below help you inspect the function you're creating and modify it
/// in ways that can be unsafe if used incorrectly. /// in ways that can be unsafe if used incorrectly.
impl<'a, Variable> FunctionBuilder<'a, Variable> impl<'a, Variable> FunctionBuilder<'a, Variable>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
/// Retrieves all the arguments for an `Ebb` currently infered from the jump instructions /// Retrieves all the arguments for an `Ebb` currently infered from the jump instructions
/// inserted that target it and the SSA construction. /// inserted that target it and the SSA construction.
@@ -402,15 +416,16 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
/// **Note:** You are responsible for maintaining the coherence with the arguments of /// **Note:** You are responsible for maintaining the coherence with the arguments of
/// other jump instructions. /// other jump instructions.
pub fn change_jump_destination(&mut self, inst: Inst, new_dest: Ebb) { pub fn change_jump_destination(&mut self, inst: Inst, new_dest: Ebb) {
let old_dest = let old_dest = self.func.dfg[inst].branch_destination_mut().expect(
self.func.dfg[inst] "you want to change the jump destination of a non-jump instruction",
.branch_destination_mut() );
.expect("you want to change the jump destination of a non-jump instruction");
let pred = self.builder.ssa.remove_ebb_predecessor(*old_dest, inst); let pred = self.builder.ssa.remove_ebb_predecessor(*old_dest, inst);
*old_dest = new_dest; *old_dest = new_dest;
self.builder self.builder.ssa.declare_ebb_predecessor(
.ssa new_dest,
.declare_ebb_predecessor(new_dest, pred, inst); pred,
inst,
);
} }
/// Returns `true` if and only if the current `Ebb` is sealed and has no predecessors declared. /// Returns `true` if and only if the current `Ebb` is sealed and has no predecessors declared.
@@ -446,31 +461,31 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
} }
impl<'a, Variable> Drop for FunctionBuilder<'a, Variable> impl<'a, Variable> Drop for FunctionBuilder<'a, Variable>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
/// When a `FunctionBuilder` goes out of scope, it means that the function is fully built. /// When a `FunctionBuilder` goes out of scope, it means that the function is fully built.
/// We then proceed to check if all the `Ebb`s are filled and sealed /// We then proceed to check if all the `Ebb`s are filled and sealed
fn drop(&mut self) { fn drop(&mut self) {
debug_assert!(self.builder debug_assert!(
.ebbs self.builder.ebbs.keys().all(|ebb| {
.keys()
.all(|ebb| {
self.builder.ebbs[ebb].pristine || self.builder.ebbs[ebb].pristine ||
(self.builder.ssa.is_sealed(ebb) && (self.builder.ssa.is_sealed(ebb) && self.builder.ebbs[ebb].filled)
self.builder.ebbs[ebb].filled)
}), }),
"all blocks should be filled and sealed before dropping a FunctionBuilder") "all blocks should be filled and sealed before dropping a FunctionBuilder"
)
} }
} }
// Helper functions // Helper functions
impl<'a, Variable> FunctionBuilder<'a, Variable> impl<'a, Variable> FunctionBuilder<'a, Variable>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
fn move_to_next_basic_block(&mut self) { fn move_to_next_basic_block(&mut self) {
self.position.basic_block = self.builder self.position.basic_block = self.builder.ssa.declare_ebb_body_block(
.ssa self.position.basic_block,
.declare_ebb_body_block(self.position.basic_block); );
} }
fn fill_current_block(&mut self) { fn fill_current_block(&mut self) {
@@ -478,30 +493,36 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
} }
fn declare_successor(&mut self, dest_ebb: Ebb, jump_inst: Inst) { fn declare_successor(&mut self, dest_ebb: Ebb, jump_inst: Inst) {
self.builder self.builder.ssa.declare_ebb_predecessor(
.ssa dest_ebb,
.declare_ebb_predecessor(dest_ebb, self.position.basic_block, jump_inst); self.position.basic_block,
jump_inst,
);
} }
fn check_return_args(&self, args: &[Value]) { fn check_return_args(&self, args: &[Value]) {
debug_assert_eq!(args.len(), debug_assert_eq!(
args.len(),
self.func.signature.return_types.len(), self.func.signature.return_types.len(),
"the number of returned values doesn't match the function signature "); "the number of returned values doesn't match the function signature "
);
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
let valty = self.func.dfg.value_type(*arg); let valty = self.func.dfg.value_type(*arg);
debug_assert_eq!(valty, debug_assert_eq!(
valty,
self.func.signature.return_types[i].value_type, self.func.signature.return_types[i].value_type,
"the types of the values returned don't match the \ "the types of the values returned don't match the \
function signature"); function signature"
);
} }
} }
fn fill_function_args_values(&mut self, ebb: Ebb) { fn fill_function_args_values(&mut self, ebb: Ebb) {
debug_assert!(self.pristine); debug_assert!(self.pristine);
for argtyp in &self.func.signature.argument_types { for argtyp in &self.func.signature.argument_types {
self.builder self.builder.function_args_values.push(
.function_args_values self.func.dfg.append_ebb_arg(ebb, argtyp.value_type),
.push(self.func.dfg.append_ebb_arg(ebb, argtyp.value_type)); );
} }
self.pristine = false; self.pristine = false;
} }
@@ -510,48 +531,56 @@ impl<'a, Variable> FunctionBuilder<'a, Variable>
fn ebb_args_adjustement(&mut self, dest_ebb: Ebb, jump_args: &[Type]) { fn ebb_args_adjustement(&mut self, dest_ebb: Ebb, jump_args: &[Type]) {
let ty_to_append: Option<Vec<Type>> = let ty_to_append: Option<Vec<Type>> =
if self.builder.ssa.predecessors(dest_ebb).len() == 0 || if self.builder.ssa.predecessors(dest_ebb).len() == 0 ||
self.builder.ebbs[dest_ebb].pristine { self.builder.ebbs[dest_ebb].pristine
{
// This is the first jump instruction targeting this Ebb // This is the first jump instruction targeting this Ebb
// so the jump arguments supplied here are this Ebb' arguments // so the jump arguments supplied here are this Ebb' arguments
// However some of the arguments might already be there // However some of the arguments might already be there
// in the Ebb so we have to check they're consistent // in the Ebb so we have to check they're consistent
let dest_ebb_args = self.func.dfg.ebb_args(dest_ebb); let dest_ebb_args = self.func.dfg.ebb_args(dest_ebb);
debug_assert!(dest_ebb_args debug_assert!(
dest_ebb_args
.iter() .iter()
.zip(jump_args.iter().take(dest_ebb_args.len())) .zip(jump_args.iter().take(dest_ebb_args.len()))
.all(|(dest_arg, jump_arg)| { .all(|(dest_arg, jump_arg)| {
*jump_arg == self.func.dfg.value_type(*dest_arg) *jump_arg == self.func.dfg.value_type(*dest_arg)
}), }),
"the jump argument supplied has not the \ "the jump argument supplied has not the \
same type as the corresponding dest ebb argument"); same type as the corresponding dest ebb argument"
);
self.builder.ebbs[dest_ebb].user_arg_count = jump_args.len(); self.builder.ebbs[dest_ebb].user_arg_count = jump_args.len();
Some(jump_args Some(
jump_args
.iter() .iter()
.skip(dest_ebb_args.len()) .skip(dest_ebb_args.len())
.cloned() .cloned()
.collect()) .collect(),
)
} else { } else {
let dest_ebb_args = self.func.dfg.ebb_args(dest_ebb); let dest_ebb_args = self.func.dfg.ebb_args(dest_ebb);
// The Ebb already has predecessors // The Ebb already has predecessors
// We check that the arguments supplied match those supplied // We check that the arguments supplied match those supplied
// previously. // previously.
debug_assert!(jump_args.len() == self.builder.ebbs[dest_ebb].user_arg_count, debug_assert!(
jump_args.len() == self.builder.ebbs[dest_ebb].user_arg_count,
"the jump instruction doesn't have the same \ "the jump instruction doesn't have the same \
number of arguments as its destination Ebb \ number of arguments as its destination Ebb \
({} vs {}).", ({} vs {}).",
jump_args.len(), jump_args.len(),
dest_ebb_args.len()); dest_ebb_args.len()
debug_assert!(jump_args );
debug_assert!(
jump_args
.iter() .iter()
.zip(dest_ebb_args .zip(dest_ebb_args.iter().take(
.iter() self.builder.ebbs[dest_ebb].user_arg_count,
.take(self.builder.ebbs[dest_ebb].user_arg_count) ))
)
.all(|(jump_arg, dest_arg)| { .all(|(jump_arg, dest_arg)| {
*jump_arg == self.func.dfg.value_type(*dest_arg) *jump_arg == self.func.dfg.value_type(*dest_arg)
}), }),
"the jump argument supplied has not the \ "the jump argument supplied has not the \
same type as the corresponding dest ebb argument"); same type as the corresponding dest ebb argument"
);
None None
}; };
if let Some(ty_args) = ty_to_append { if let Some(ty_args) = ty_to_append {

View File

@@ -33,7 +33,8 @@ use std::collections::HashMap;
/// and it is said _sealed_ if all of its predecessors have been declared. Only filled predecessors /// and it is said _sealed_ if all of its predecessors have been declared. Only filled predecessors
/// can be declared. /// can be declared.
pub struct SSABuilder<Variable> pub struct SSABuilder<Variable>
where Variable: EntityRef + Default where
Variable: EntityRef + Default,
{ {
// Records for every variable and for every revelant block, the last definition of // Records for every variable and for every revelant block, the last definition of
// the variable in the block. // the variable in the block.
@@ -133,7 +134,8 @@ impl ReservedValue for Block {
} }
impl<Variable> SSABuilder<Variable> impl<Variable> SSABuilder<Variable>
where Variable: EntityRef + Default where
Variable: EntityRef + Default,
{ {
/// Allocate a new blank SSA builder struct. Use the API function to interact with the struct. /// Allocate a new blank SSA builder struct. Use the API function to interact with the struct.
pub fn new() -> SSABuilder<Variable> { pub fn new() -> SSABuilder<Variable> {
@@ -191,7 +193,8 @@ enum UseVarCases {
/// Phi functions. /// Phi functions.
/// ///
impl<Variable> SSABuilder<Variable> impl<Variable> SSABuilder<Variable>
where Variable: EntityRef + Hash + Default where
Variable: EntityRef + Hash + Default,
{ {
/// Declares a new definition of a variable in a given basic block. /// Declares a new definition of a variable in a given basic block.
/// The SSA value is passed as an argument because it should be created with /// The SSA value is passed as an argument because it should be created with
@@ -207,14 +210,15 @@ impl<Variable> SSABuilder<Variable>
/// If the variable has never been defined in this blocks or recursively in its predecessors, /// If the variable has never been defined in this blocks or recursively in its predecessors,
/// this method will silently create an initializer with `iconst` or `fconst`. You are /// this method will silently create an initializer with `iconst` or `fconst`. You are
/// responsible for making sure that you initialize your variables. /// responsible for making sure that you initialize your variables.
pub fn use_var(&mut self, pub fn use_var(
&mut self,
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
layout: &mut Layout, layout: &mut Layout,
jts: &mut JumpTables, jts: &mut JumpTables,
var: Variable, var: Variable,
ty: Type, ty: Type,
block: Block) block: Block,
-> (Value, SideEffects) { ) -> (Value, SideEffects) {
// First we lookup for the current definition of the variable in this block // First we lookup for the current definition of the variable in this block
if let Some(var_defs) = self.variables.get(var) { if let Some(var_defs) = self.variables.get(var) {
if let Some(val) = var_defs.get(&block) { if let Some(val) = var_defs.get(&block) {
@@ -281,8 +285,7 @@ impl<Variable> SSABuilder<Variable>
/// here and the block is not sealed. /// here and the block is not sealed.
/// Predecessors have to be added with `declare_ebb_predecessor`. /// Predecessors have to be added with `declare_ebb_predecessor`.
pub fn declare_ebb_header_block(&mut self, ebb: Ebb) -> Block { pub fn declare_ebb_header_block(&mut self, ebb: Ebb) -> Block {
let block = self.blocks let block = self.blocks.push(BlockData::EbbHeader(EbbHeaderBlockData {
.push(BlockData::EbbHeader(EbbHeaderBlockData {
predecessors: Vec::new(), predecessors: Vec::new(),
sealed: false, sealed: false,
ebb: ebb, ebb: ebb,
@@ -331,12 +334,13 @@ impl<Variable> SSABuilder<Variable>
/// ///
/// This method modifies the function's `Layout` by adding arguments to the `Ebb`s to /// This method modifies the function's `Layout` by adding arguments to the `Ebb`s to
/// take into account the Phi function placed by the SSA algorithm. /// take into account the Phi function placed by the SSA algorithm.
pub fn seal_ebb_header_block(&mut self, pub fn seal_ebb_header_block(
&mut self,
ebb: Ebb, ebb: Ebb,
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
layout: &mut Layout, layout: &mut Layout,
jts: &mut JumpTables) jts: &mut JumpTables,
-> SideEffects { ) -> SideEffects {
let block = self.header_block(ebb); let block = self.header_block(ebb);
// Sanity check // Sanity check
@@ -362,19 +366,24 @@ impl<Variable> SSABuilder<Variable>
// jump argument to the branch instruction. // jump argument to the branch instruction.
// Panics if called with a non-header block. // Panics if called with a non-header block.
// Returns the list of newly created ebbs for critical edge splitting. // Returns the list of newly created ebbs for critical edge splitting.
fn resolve_undef_vars(&mut self, fn resolve_undef_vars(
&mut self,
block: Block, block: Block,
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
layout: &mut Layout, layout: &mut Layout,
jts: &mut JumpTables) jts: &mut JumpTables,
-> SideEffects { ) -> SideEffects {
// TODO: find a way to not allocate vectors // TODO: find a way to not allocate vectors
let (predecessors, undef_vars, ebb): (Vec<(Block, Inst)>, let (predecessors, undef_vars, ebb): (Vec<(Block, Inst)>,
Vec<(Variable, Value)>, Vec<(Variable, Value)>,
Ebb) = match self.blocks[block] { Ebb) = match self.blocks[block] {
BlockData::EbbBody { .. } => panic!("this should not happen"), BlockData::EbbBody { .. } => panic!("this should not happen"),
BlockData::EbbHeader(ref mut data) => { BlockData::EbbHeader(ref mut data) => {
(data.predecessors.clone(), data.undef_variables.clone(), data.ebb) (
data.predecessors.clone(),
data.undef_variables.clone(),
data.ebb,
)
} }
}; };
@@ -384,12 +393,13 @@ impl<Variable> SSABuilder<Variable>
for (var, val) in undef_vars { for (var, val) in undef_vars {
let (_, mut local_side_effects) = let (_, mut local_side_effects) =
self.predecessors_lookup(dfg, layout, jts, val, var, ebb, &predecessors); self.predecessors_lookup(dfg, layout, jts, val, var, ebb, &predecessors);
side_effects side_effects.split_ebbs_created.append(
.split_ebbs_created &mut local_side_effects
.append(&mut local_side_effects.split_ebbs_created); .split_ebbs_created,
side_effects );
.instructions_added_to_ebbs side_effects.instructions_added_to_ebbs.append(
.append(&mut local_side_effects.instructions_added_to_ebbs); &mut local_side_effects.instructions_added_to_ebbs,
);
} }
// Then we clear the undef_vars and mark the block as sealed. // Then we clear the undef_vars and mark the block as sealed.
@@ -405,15 +415,16 @@ impl<Variable> SSABuilder<Variable>
/// Look up in the predecessors of an Ebb the def for a value an decides wether or not /// Look up in the predecessors of an Ebb the def for a value an decides wether or not
/// to keep the eeb arg, and act accordingly. Returns the chosen value and optionnaly a /// to keep the eeb arg, and act accordingly. Returns the chosen value and optionnaly a
/// list of Ebb that are the middle of newly created critical edges splits. /// list of Ebb that are the middle of newly created critical edges splits.
fn predecessors_lookup(&mut self, fn predecessors_lookup(
&mut self,
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
layout: &mut Layout, layout: &mut Layout,
jts: &mut JumpTables, jts: &mut JumpTables,
temp_arg_val: Value, temp_arg_val: Value,
temp_arg_var: Variable, temp_arg_var: Variable,
dest_ebb: Ebb, dest_ebb: Ebb,
preds: &[(Block, Inst)]) preds: &[(Block, Inst)],
-> (Value, SideEffects) { ) -> (Value, SideEffects) {
let mut pred_values: ZeroOneOrMore<Value> = ZeroOneOrMore::Zero(); let mut pred_values: ZeroOneOrMore<Value> = ZeroOneOrMore::Zero();
// TODO: find a way not not allocate a vector // TODO: find a way not not allocate a vector
let mut jump_args_to_append: Vec<(Block, Inst, Value)> = Vec::new(); let mut jump_args_to_append: Vec<(Block, Inst, Value)> = Vec::new();
@@ -442,12 +453,13 @@ impl<Variable> SSABuilder<Variable>
ZeroOneOrMore::More() => ZeroOneOrMore::More(), ZeroOneOrMore::More() => ZeroOneOrMore::More(),
}; };
jump_args_to_append.push((pred, last_inst, pred_val)); jump_args_to_append.push((pred, last_inst, pred_val));
side_effects side_effects.split_ebbs_created.append(
.split_ebbs_created &mut local_side_effects
.append(&mut local_side_effects.split_ebbs_created); .split_ebbs_created,
side_effects );
.instructions_added_to_ebbs side_effects.instructions_added_to_ebbs.append(
.append(&mut local_side_effects.instructions_added_to_ebbs); &mut local_side_effects.instructions_added_to_ebbs,
);
} }
match pred_values { match pred_values {
ZeroOneOrMore::Zero() => { ZeroOneOrMore::Zero() => {
@@ -486,14 +498,16 @@ impl<Variable> SSABuilder<Variable>
// There is disagreement in the predecessors on which value to use so we have // There is disagreement in the predecessors on which value to use so we have
// to keep the ebb argument. // to keep the ebb argument.
for (pred_block, last_inst, pred_val) in jump_args_to_append { for (pred_block, last_inst, pred_val) in jump_args_to_append {
match self.append_jump_argument(dfg, match self.append_jump_argument(
dfg,
layout, layout,
last_inst, last_inst,
pred_block, pred_block,
dest_ebb, dest_ebb,
pred_val, pred_val,
temp_arg_var, temp_arg_var,
jts) { jts,
) {
None => (), None => (),
Some(middle_ebb) => side_effects.split_ebbs_created.push(middle_ebb), Some(middle_ebb) => side_effects.split_ebbs_created.push(middle_ebb),
}; };
@@ -505,7 +519,8 @@ impl<Variable> SSABuilder<Variable>
/// Appends a jump argument to a jump instruction, returns ebb created in case of /// Appends a jump argument to a jump instruction, returns ebb created in case of
/// critical edge splitting. /// critical edge splitting.
fn append_jump_argument(&mut self, fn append_jump_argument(
&mut self,
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
layout: &mut Layout, layout: &mut Layout,
jump_inst: Inst, jump_inst: Inst,
@@ -513,8 +528,8 @@ impl<Variable> SSABuilder<Variable>
dest_ebb: Ebb, dest_ebb: Ebb,
val: Value, val: Value,
var: Variable, var: Variable,
jts: &mut JumpTables) jts: &mut JumpTables,
-> Option<Ebb> { ) -> Option<Ebb> {
match dfg[jump_inst].analyze_branch(&dfg.value_lists) { match dfg[jump_inst].analyze_branch(&dfg.value_lists) {
BranchInfo::NotABranch => { BranchInfo::NotABranch => {
panic!("you have declared a non-branch instruction as a predecessor to an ebb"); panic!("you have declared a non-branch instruction as a predecessor to an ebb");
@@ -529,14 +544,17 @@ impl<Variable> SSABuilder<Variable>
// In the case of a jump table, the situation is tricky because br_table doesn't // In the case of a jump table, the situation is tricky because br_table doesn't
// support arguments. // support arguments.
// We have to split the critical edge // We have to split the critical edge
let indexes: Vec<usize> = jts[jt] let indexes: Vec<usize> = jts[jt].entries().fold(
.entries() Vec::new(),
.fold(Vec::new(), |mut acc, (index, dest)| if dest == dest_ebb { |mut acc, (index, dest)| if dest ==
dest_ebb
{
acc.push(index); acc.push(index);
acc acc
} else { } else {
acc acc
}); },
);
let middle_ebb = dfg.make_ebb(); let middle_ebb = dfg.make_ebb();
layout.append_ebb(middle_ebb); layout.append_ebb(middle_ebb);
let block = self.declare_ebb_header_block(middle_ebb); let block = self.declare_ebb_header_block(middle_ebb);
@@ -632,79 +650,95 @@ mod tests {
}; };
ssa.def_var(y_var, y_ssa, block); ssa.def_var(y_var, y_ssa, block);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block) block,
.0, ).0,
x_ssa); x_ssa
assert_eq!(ssa.use_var(&mut func.dfg, );
assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block) block,
.0, ).0,
y_ssa); y_ssa
);
let z_var = Variable(2); let z_var = Variable(2);
let x_use1 = ssa.use_var(&mut func.dfg, let x_use1 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block) block,
.0; ).0;
let y_use1 = ssa.use_var(&mut func.dfg, let y_use1 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block) block,
.0; ).0;
let z1_ssa = { let z1_ssa = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb0); cur.goto_bottom(ebb0);
func.dfg.ins(cur).iadd(x_use1, y_use1) func.dfg.ins(cur).iadd(x_use1, y_use1)
}; };
ssa.def_var(z_var, z1_ssa, block); ssa.def_var(z_var, z1_ssa, block);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block) block,
.0, ).0,
z1_ssa); z1_ssa
let x_use2 = ssa.use_var(&mut func.dfg, );
let x_use2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block) block,
.0; ).0;
let z_use1 = ssa.use_var(&mut func.dfg, let z_use1 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block) block,
.0; ).0;
let z2_ssa = { let z2_ssa = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb0); cur.goto_bottom(ebb0);
func.dfg.ins(cur).iadd(x_use2, z_use1) func.dfg.ins(cur).iadd(x_use2, z_use1)
}; };
ssa.def_var(z_var, z2_ssa, block); ssa.def_var(z_var, z2_ssa, block);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block) block,
.0, ).0,
z2_ssa); z2_ssa
);
} }
#[test] #[test]
@@ -740,79 +774,93 @@ mod tests {
func.dfg.ins(cur).iconst(I32, 2) func.dfg.ins(cur).iconst(I32, 2)
}; };
ssa.def_var(y_var, y_ssa, block0); ssa.def_var(y_var, y_ssa, block0);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block0) block0,
.0, ).0,
x_ssa); x_ssa
assert_eq!(ssa.use_var(&mut func.dfg, );
assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block0) block0,
.0, ).0,
y_ssa); y_ssa
);
let z_var = Variable(2); let z_var = Variable(2);
let x_use1 = ssa.use_var(&mut func.dfg, let x_use1 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block0) block0,
.0; ).0;
let y_use1 = ssa.use_var(&mut func.dfg, let y_use1 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block0) block0,
.0; ).0;
let z1_ssa = { let z1_ssa = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb0); cur.goto_bottom(ebb0);
func.dfg.ins(cur).iadd(x_use1, y_use1) func.dfg.ins(cur).iadd(x_use1, y_use1)
}; };
ssa.def_var(z_var, z1_ssa, block0); ssa.def_var(z_var, z1_ssa, block0);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block0) block0,
.0, ).0,
z1_ssa); z1_ssa
let y_use2 = ssa.use_var(&mut func.dfg, );
let y_use2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block0) block0,
.0; ).0;
let jump_inst: Inst = { let jump_inst: Inst = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb0); cur.goto_bottom(ebb0);
func.dfg.ins(cur).brnz(y_use2, ebb1, &[]) func.dfg.ins(cur).brnz(y_use2, ebb1, &[])
}; };
let block1 = ssa.declare_ebb_body_block(block0); let block1 = ssa.declare_ebb_body_block(block0);
let x_use2 = ssa.use_var(&mut func.dfg, let x_use2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block1) block1,
.0; ).0;
assert_eq!(x_use2, x_ssa); assert_eq!(x_use2, x_ssa);
let z_use1 = ssa.use_var(&mut func.dfg, let z_use1 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block1) block1,
.0; ).0;
assert_eq!(z_use1, z1_ssa); assert_eq!(z_use1, z1_ssa);
let z2_ssa = { let z2_ssa = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -820,33 +868,38 @@ mod tests {
func.dfg.ins(cur).iadd(x_use2, z_use1) func.dfg.ins(cur).iadd(x_use2, z_use1)
}; };
ssa.def_var(z_var, z2_ssa, block1); ssa.def_var(z_var, z2_ssa, block1);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block1) block1,
.0, ).0,
z2_ssa); z2_ssa
);
ssa.seal_ebb_header_block(ebb0, &mut func.dfg, &mut func.layout, &mut func.jump_tables); ssa.seal_ebb_header_block(ebb0, &mut func.dfg, &mut func.layout, &mut func.jump_tables);
let block2 = ssa.declare_ebb_header_block(ebb1); let block2 = ssa.declare_ebb_header_block(ebb1);
ssa.declare_ebb_predecessor(ebb1, block0, jump_inst); ssa.declare_ebb_predecessor(ebb1, block0, jump_inst);
ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables); ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables);
let x_use3 = ssa.use_var(&mut func.dfg, let x_use3 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block2) block2,
.0; ).0;
assert_eq!(x_ssa, x_use3); assert_eq!(x_ssa, x_use3);
let y_use3 = ssa.use_var(&mut func.dfg, let y_use3 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block2) block2,
.0; ).0;
assert_eq!(y_ssa, y_use3); assert_eq!(y_ssa, y_use3);
let y2_ssa = { let y2_ssa = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -897,14 +950,17 @@ mod tests {
func.dfg.ins(cur).iconst(I32, 1) func.dfg.ins(cur).iconst(I32, 1)
}; };
ssa.def_var(x_var, x1, block0); ssa.def_var(x_var, x1, block0);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block0) block0,
.0, ).0,
x1); x1
);
let y_var = Variable(1); let y_var = Variable(1);
let y1 = { let y1 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -912,30 +968,35 @@ mod tests {
func.dfg.ins(cur).iconst(I32, 2) func.dfg.ins(cur).iconst(I32, 2)
}; };
ssa.def_var(y_var, y1, block0); ssa.def_var(y_var, y1, block0);
assert_eq!(ssa.use_var(&mut func.dfg, assert_eq!(
ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block0) block0,
.0, ).0,
y1); y1
);
let z_var = Variable(2); let z_var = Variable(2);
let x2 = ssa.use_var(&mut func.dfg, let x2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block0) block0,
.0; ).0;
assert_eq!(x2, x1); assert_eq!(x2, x1);
let y2 = ssa.use_var(&mut func.dfg, let y2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block0) block0,
.0; ).0;
assert_eq!(y2, y1); assert_eq!(y2, y1);
let z1 = { let z1 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -950,33 +1011,36 @@ mod tests {
}; };
let block1 = ssa.declare_ebb_header_block(ebb1); let block1 = ssa.declare_ebb_header_block(ebb1);
ssa.declare_ebb_predecessor(ebb1, block0, jump_ebb0_ebb1); ssa.declare_ebb_predecessor(ebb1, block0, jump_ebb0_ebb1);
let z2 = ssa.use_var(&mut func.dfg, let z2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block1) block1,
.0; ).0;
let y3 = ssa.use_var(&mut func.dfg, let y3 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block1) block1,
.0; ).0;
let z3 = { let z3 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb1); cur.goto_bottom(ebb1);
func.dfg.ins(cur).iadd(z2, y3) func.dfg.ins(cur).iadd(z2, y3)
}; };
ssa.def_var(z_var, z3, block1); ssa.def_var(z_var, z3, block1);
let y4 = ssa.use_var(&mut func.dfg, let y4 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block1) block1,
.0; ).0;
assert_eq!(y4, y3); assert_eq!(y4, y3);
let jump_ebb1_ebb2 = { let jump_ebb1_ebb2 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -984,34 +1048,37 @@ mod tests {
func.dfg.ins(cur).brnz(y4, ebb2, &[]) func.dfg.ins(cur).brnz(y4, ebb2, &[])
}; };
let block2 = ssa.declare_ebb_body_block(block1); let block2 = ssa.declare_ebb_body_block(block1);
let z4 = ssa.use_var(&mut func.dfg, let z4 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block2) block2,
.0; ).0;
assert_eq!(z4, z3); assert_eq!(z4, z3);
let x3 = ssa.use_var(&mut func.dfg, let x3 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block2) block2,
.0; ).0;
let z5 = { let z5 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb1); cur.goto_bottom(ebb1);
func.dfg.ins(cur).isub(z4, x3) func.dfg.ins(cur).isub(z4, x3)
}; };
ssa.def_var(z_var, z5, block2); ssa.def_var(z_var, z5, block2);
let y5 = ssa.use_var(&mut func.dfg, let y5 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block2) block2,
.0; ).0;
assert_eq!(y5, y3); assert_eq!(y5, y3);
{ {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -1022,21 +1089,23 @@ mod tests {
let block3 = ssa.declare_ebb_header_block(ebb2); let block3 = ssa.declare_ebb_header_block(ebb2);
ssa.declare_ebb_predecessor(ebb2, block1, jump_ebb1_ebb2); ssa.declare_ebb_predecessor(ebb2, block1, jump_ebb1_ebb2);
ssa.seal_ebb_header_block(ebb2, &mut func.dfg, &mut func.layout, &mut func.jump_tables); ssa.seal_ebb_header_block(ebb2, &mut func.dfg, &mut func.layout, &mut func.jump_tables);
let y6 = ssa.use_var(&mut func.dfg, let y6 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block3) block3,
.0; ).0;
assert_eq!(y6, y3); assert_eq!(y6, y3);
let x4 = ssa.use_var(&mut func.dfg, let x4 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block3) block3,
.0; ).0;
assert_eq!(x4, x3); assert_eq!(x4, x3);
let y7 = { let y7 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -1089,13 +1158,14 @@ mod tests {
let mut jt_data = JumpTableData::new(); let mut jt_data = JumpTableData::new();
jt_data.set_entry(0, ebb1); jt_data.set_entry(0, ebb1);
let jt = func.jump_tables.push(jt_data); let jt = func.jump_tables.push(jt_data);
ssa.use_var(&mut func.dfg, ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block0) block0,
.0; ).0;
let br_table = { let br_table = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb0); cur.goto_bottom(ebb0);
@@ -1117,13 +1187,14 @@ mod tests {
ssa.declare_ebb_predecessor(ebb1, block1, jump_inst); ssa.declare_ebb_predecessor(ebb1, block1, jump_inst);
ssa.declare_ebb_predecessor(ebb1, block0, br_table); ssa.declare_ebb_predecessor(ebb1, block0, br_table);
ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables); ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables);
let x4 = ssa.use_var(&mut func.dfg, let x4 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block2) block2,
.0; ).0;
{ {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
cur.goto_bottom(ebb1); cur.goto_bottom(ebb1);
@@ -1189,21 +1260,23 @@ mod tests {
}; };
let block1 = ssa.declare_ebb_header_block(ebb1); let block1 = ssa.declare_ebb_header_block(ebb1);
ssa.declare_ebb_predecessor(ebb1, block0, jump_inst); ssa.declare_ebb_predecessor(ebb1, block0, jump_inst);
let z2 = ssa.use_var(&mut func.dfg, let z2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
z_var, z_var,
I32, I32,
block1) block1,
.0; ).0;
assert_eq!(func.dfg.ebb_args(ebb1)[0], z2); assert_eq!(func.dfg.ebb_args(ebb1)[0], z2);
let x2 = ssa.use_var(&mut func.dfg, let x2 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block1) block1,
.0; ).0;
assert_eq!(func.dfg.ebb_args(ebb1)[1], x2); assert_eq!(func.dfg.ebb_args(ebb1)[1], x2);
let x3 = { let x3 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);
@@ -1211,20 +1284,22 @@ mod tests {
func.dfg.ins(cur).iadd(x2, z2) func.dfg.ins(cur).iadd(x2, z2)
}; };
ssa.def_var(x_var, x3, block1); ssa.def_var(x_var, x3, block1);
let x4 = ssa.use_var(&mut func.dfg, let x4 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
x_var, x_var,
I32, I32,
block1) block1,
.0; ).0;
let y3 = ssa.use_var(&mut func.dfg, let y3 = ssa.use_var(
&mut func.dfg,
&mut func.layout, &mut func.layout,
&mut func.jump_tables, &mut func.jump_tables,
y_var, y_var,
I32, I32,
block1) block1,
.0; ).0;
assert_eq!(func.dfg.ebb_args(ebb1)[2], y3); assert_eq!(func.dfg.ebb_args(ebb1)[2], y3);
let y4 = { let y4 = {
let cur = &mut Cursor::new(&mut func.layout); let cur = &mut Cursor::new(&mut func.layout);

View File

@@ -36,7 +36,8 @@ impl IsaSpec {
/// Parse an iterator of command line options and apply them to `config`. /// Parse an iterator of command line options and apply them to `config`.
pub fn parse_options<'a, I>(iter: I, config: &mut Configurable, loc: &Location) -> Result<()> pub fn parse_options<'a, I>(iter: I, config: &mut Configurable, loc: &Location) -> Result<()>
where I: Iterator<Item = &'a str> where
I: Iterator<Item = &'a str>,
{ {
for opt in iter.map(TestOption::new) { for opt in iter.map(TestOption::new) {
match opt { match opt {

View File

@@ -180,10 +180,11 @@ impl<'a> Lexer<'a> {
} }
// Scan a multi-char token. // Scan a multi-char token.
fn scan_chars(&mut self, fn scan_chars(
&mut self,
count: usize, count: usize,
tok: Token<'a>) tok: Token<'a>,
-> Result<LocatedToken<'a>, LocatedError> { ) -> Result<LocatedToken<'a>, LocatedError> {
let loc = self.loc(); let loc = self.loc();
for _ in 0..count { for _ in 0..count {
assert_ne!(self.lookahead, None); assert_ne!(self.lookahead, None);
@@ -294,13 +295,16 @@ impl<'a> Lexer<'a> {
let text = &self.source[begin..self.pos]; let text = &self.source[begin..self.pos];
// Look for numbered well-known entities like ebb15, v45, ... // Look for numbered well-known entities like ebb15, v45, ...
token(split_entity_name(text) token(
split_entity_name(text)
.and_then(|(prefix, number)| { .and_then(|(prefix, number)| {
Self::numbered_entity(prefix, number) Self::numbered_entity(prefix, number).or_else(|| {
.or_else(|| Self::value_type(text, prefix, number)) Self::value_type(text, prefix, number)
})
}) })
.unwrap_or(Token::Identifier(text)), .unwrap_or(Token::Identifier(text)),
loc) loc,
)
} }
// If prefix is a well-known entity prefix and suffix is a valid entity number, return the // If prefix is a well-known entity prefix and suffix is a valid entity number, return the
@@ -530,14 +534,20 @@ mod tests {
#[test] #[test]
fn lex_identifiers() { fn lex_identifiers() {
let mut lex = Lexer::new("v0 v00 vx01 ebb1234567890 ebb5234567890 v1x vx1 vxvx4 \ let mut lex = Lexer::new(
function0 function b1 i32x4 f32x5"); "v0 v00 vx01 ebb1234567890 ebb5234567890 v1x vx1 vxvx4 \
assert_eq!(lex.next(), function0 function b1 i32x4 f32x5",
token(Token::Value(Value::with_number(0).unwrap()), 1)); );
assert_eq!(
lex.next(),
token(Token::Value(Value::with_number(0).unwrap()), 1)
);
assert_eq!(lex.next(), token(Token::Identifier("v00"), 1)); assert_eq!(lex.next(), token(Token::Identifier("v00"), 1));
assert_eq!(lex.next(), token(Token::Identifier("vx01"), 1)); assert_eq!(lex.next(), token(Token::Identifier("vx01"), 1));
assert_eq!(lex.next(), assert_eq!(
token(Token::Ebb(Ebb::with_number(1234567890).unwrap()), 1)); lex.next(),
token(Token::Ebb(Ebb::with_number(1234567890).unwrap()), 1)
);
assert_eq!(lex.next(), token(Token::Identifier("ebb5234567890"), 1)); assert_eq!(lex.next(), token(Token::Identifier("ebb5234567890"), 1));
assert_eq!(lex.next(), token(Token::Identifier("v1x"), 1)); assert_eq!(lex.next(), token(Token::Identifier("v1x"), 1));
assert_eq!(lex.next(), token(Token::Identifier("vx1"), 1)); assert_eq!(lex.next(), token(Token::Identifier("vx1"), 1));

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More