rustfmt 0.8.1
This commit is contained in:
@@ -51,7 +51,11 @@ pub type CommandResult = Result<(), String>;
|
|||||||
fn cton_util() -> CommandResult {
|
fn cton_util() -> CommandResult {
|
||||||
// Parse comand line arguments.
|
// Parse comand line arguments.
|
||||||
let args: Args = Docopt::new(USAGE)
|
let args: Args = Docopt::new(USAGE)
|
||||||
.and_then(|d| d.help(true).version(Some(format!("Cretonne {}", VERSION))).decode())
|
.and_then(|d| {
|
||||||
|
d.help(true)
|
||||||
|
.version(Some(format!("Cretonne {}", VERSION)))
|
||||||
|
.decode()
|
||||||
|
})
|
||||||
.unwrap_or_else(|e| e.exit());
|
.unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
// Find the sub-command to execute.
|
// Find the sub-command to execute.
|
||||||
|
|||||||
@@ -119,7 +119,8 @@ fn worker_thread(thread_num: usize,
|
|||||||
|
|
||||||
// Tell them we're starting this job.
|
// Tell them we're starting this job.
|
||||||
// The receiver should always be present for this as long as we have jobs.
|
// The receiver should always be present for this as long as we have jobs.
|
||||||
replies.send(Reply::Starting {
|
replies
|
||||||
|
.send(Reply::Starting {
|
||||||
jobid: jobid,
|
jobid: jobid,
|
||||||
thread_num: thread_num,
|
thread_num: thread_num,
|
||||||
})
|
})
|
||||||
@@ -141,7 +142,8 @@ fn worker_thread(thread_num: usize,
|
|||||||
dbg!("FAIL: {}", msg);
|
dbg!("FAIL: {}", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
replies.send(Reply::Done {
|
replies
|
||||||
|
.send(Reply::Done {
|
||||||
jobid: jobid,
|
jobid: jobid,
|
||||||
result: result,
|
result: result,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ impl SubTest for TestLegalizer {
|
|||||||
|
|
||||||
comp_ctx.flowgraph();
|
comp_ctx.flowgraph();
|
||||||
comp_ctx.legalize(isa);
|
comp_ctx.legalize(isa);
|
||||||
comp_ctx.verify(isa).map_err(|e| pretty_verifier_error(&comp_ctx.func, e))?;
|
comp_ctx.verify(isa)
|
||||||
|
.map_err(|e| pretty_verifier_error(&comp_ctx.func, e))?;
|
||||||
|
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
write_function(&mut text, &comp_ctx.func, Some(isa)).map_err(|e| e.to_string())?;
|
write_function(&mut text, &comp_ctx.func, Some(isa)).map_err(|e| e.to_string())?;
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ impl SubTest for TestRegalloc {
|
|||||||
// TODO: Should we have an option to skip legalization?
|
// TODO: Should we have an option to skip legalization?
|
||||||
comp_ctx.legalize(isa);
|
comp_ctx.legalize(isa);
|
||||||
comp_ctx.regalloc(isa);
|
comp_ctx.regalloc(isa);
|
||||||
comp_ctx.verify(isa).map_err(|e| pretty_verifier_error(&comp_ctx.func, e))?;
|
comp_ctx.verify(isa)
|
||||||
|
.map_err(|e| pretty_verifier_error(&comp_ctx.func, e))?;
|
||||||
|
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
write_function(&mut text, &comp_ctx.func, Some(isa)).map_err(|e| e.to_string())?;
|
write_function(&mut text, &comp_ctx.func, Some(isa)).map_err(|e| e.to_string())?;
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ 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.push(QueueEntry {
|
self.tests
|
||||||
|
.push(QueueEntry {
|
||||||
path: file.into(),
|
path: file.into(),
|
||||||
state: State::New,
|
state: State::New,
|
||||||
});
|
});
|
||||||
@@ -206,7 +207,9 @@ impl TestRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for any asynchronous replies without blocking.
|
// Check for any asynchronous replies without blocking.
|
||||||
while let Some(reply) = self.threads.as_mut().and_then(ConcurrentRunner::try_get) {
|
while let Some(reply) = self.threads
|
||||||
|
.as_mut()
|
||||||
|
.and_then(ConcurrentRunner::try_get) {
|
||||||
self.handle_reply(reply);
|
self.handle_reply(reply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,10 +306,10 @@ impl TestRunner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for t in self.tests.iter().filter(|entry| match **entry {
|
for t in self.tests
|
||||||
QueueEntry { state: State::Done(Ok(dur)), .. } => {
|
.iter()
|
||||||
dur > cut
|
.filter(|entry| match **entry {
|
||||||
}
|
QueueEntry { state: State::Done(Ok(dur)), .. } => dur > cut,
|
||||||
_ => false,
|
_ => false,
|
||||||
}) {
|
}) {
|
||||||
println!("slow: {}", t)
|
println!("slow: {}", t)
|
||||||
|
|||||||
@@ -120,5 +120,6 @@ fn run_one_test<'a>(tuple: (&'a SubTest, &'a Flags, Option<&'a TargetIsa>),
|
|||||||
context.verified = true;
|
context.verified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
test.run(func, context).map_err(|e| format!("{}: {}", name, e))
|
test.run(func, context)
|
||||||
|
.map_err(|e| format!("{}: {}", name, e))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,11 +76,13 @@ impl<'a> filecheck::VariableMap for Context<'a> {
|
|||||||
/// 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.check(&text, context).map_err(|e| format!("filecheck: {}", e))? {
|
if checker.check(&text, context)
|
||||||
|
.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.explain(&text, context).map_err(|e| format!("explain: {}", e))?;
|
let (_, explain) = checker.explain(&text, context)
|
||||||
|
.map_err(|e| format!("explain: {}", e))?;
|
||||||
Err(format!("filecheck failed:\n{}{}", checker, explain))
|
Err(format!("filecheck failed:\n{}{}", checker, explain))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,10 +92,12 @@ 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.directive(comment.text).map_err(|e| format!("filecheck: {}", e))?;
|
builder.directive(comment.text)
|
||||||
|
.map_err(|e| format!("filecheck: {}", e))?;
|
||||||
}
|
}
|
||||||
for comment in &context.details.comments {
|
for comment in &context.details.comments {
|
||||||
builder.directive(comment.text).map_err(|e| format!("filecheck: {}", e))?;
|
builder.directive(comment.text)
|
||||||
|
.map_err(|e| format!("filecheck: {}", e))?;
|
||||||
}
|
}
|
||||||
let checker = builder.finish();
|
let checker = builder.finish();
|
||||||
if checker.is_empty() {
|
if checker.is_empty() {
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ pub fn run(files: Vec<String>, verbose: bool) -> CommandResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
io::stdin().read_to_string(&mut buffer).map_err(|e| format!("stdin: {}", e))?;
|
io::stdin().read_to_string(&mut buffer)
|
||||||
|
.map_err(|e| format!("stdin: {}", e))?;
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
let (success, explain) = checker.explain(&buffer, NO_VARIABLES).map_err(|e| e.to_string())?;
|
let (success, explain) = checker.explain(&buffer, NO_VARIABLES)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
print!("{}", explain);
|
print!("{}", explain);
|
||||||
if success {
|
if success {
|
||||||
println!("OK");
|
println!("OK");
|
||||||
@@ -29,10 +31,12 @@ pub fn run(files: Vec<String>, verbose: bool) -> CommandResult {
|
|||||||
} else {
|
} else {
|
||||||
Err("Check failed".to_string())
|
Err("Check failed".to_string())
|
||||||
}
|
}
|
||||||
} else if checker.check(&buffer, NO_VARIABLES).map_err(|e| e.to_string())? {
|
} else if checker.check(&buffer, NO_VARIABLES)
|
||||||
|
.map_err(|e| e.to_string())? {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let (_, explain) = checker.explain(&buffer, NO_VARIABLES).map_err(|e| e.to_string())?;
|
let (_, explain) = checker.explain(&buffer, NO_VARIABLES)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
print!("{}", explain);
|
print!("{}", explain);
|
||||||
Err("Check failed".to_string())
|
Err("Check failed".to_string())
|
||||||
}
|
}
|
||||||
@@ -41,6 +45,7 @@ pub fn run(files: Vec<String>, verbose: bool) -> CommandResult {
|
|||||||
fn read_checkfile(filename: &str) -> Result<Checker, String> {
|
fn read_checkfile(filename: &str) -> Result<Checker, String> {
|
||||||
let buffer = read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e))?;
|
let buffer = read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
let mut builder = CheckerBuilder::new();
|
let mut builder = CheckerBuilder::new();
|
||||||
builder.text(&buffer).map_err(|e| format!("{}: {}", filename, e))?;
|
builder.text(&buffer)
|
||||||
|
.map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
Ok(builder.finish())
|
Ok(builder.finish())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ function banner() {
|
|||||||
# rustfmt is installed.
|
# rustfmt is installed.
|
||||||
#
|
#
|
||||||
# This version should always be bumped to the newest version available.
|
# This version should always be bumped to the newest version available.
|
||||||
RUSTFMT_VERSION="0.8.0"
|
RUSTFMT_VERSION="0.8.1"
|
||||||
|
|
||||||
if cargo install --list | grep -q "^rustfmt v$RUSTFMT_VERSION"; then
|
if cargo install --list | grep -q "^rustfmt v$RUSTFMT_VERSION"; then
|
||||||
banner "Rust formatting"
|
banner "Rust formatting"
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ use self::cton_reader::parse_functions;
|
|||||||
fn test_reverse_postorder_traversal(function_source: &str, ebb_order: Vec<u32>) {
|
fn test_reverse_postorder_traversal(function_source: &str, ebb_order: Vec<u32>) {
|
||||||
let func = &parse_functions(function_source).unwrap()[0];
|
let func = &parse_functions(function_source).unwrap()[0];
|
||||||
let cfg = ControlFlowGraph::with_function(&func);
|
let cfg = ControlFlowGraph::with_function(&func);
|
||||||
let ebbs = ebb_order.iter().map(|n| Ebb::with_number(*n).unwrap()).collect::<Vec<Ebb>>();
|
let ebbs = ebb_order
|
||||||
|
.iter()
|
||||||
|
.map(|n| Ebb::with_number(*n).unwrap())
|
||||||
|
.collect::<Vec<Ebb>>();
|
||||||
|
|
||||||
let mut postorder_ebbs = cfg.postorder_ebbs();
|
let mut postorder_ebbs = cfg.postorder_ebbs();
|
||||||
let mut postorder_map = EntityMap::with_capacity(postorder_ebbs.len());
|
let mut postorder_map = EntityMap::with_capacity(postorder_ebbs.len());
|
||||||
|
|||||||
@@ -108,7 +108,10 @@ pub fn legalize_args<AA: ArgAssigner>(args: &mut Vec<ArgumentType>, aa: &mut AA)
|
|||||||
}
|
}
|
||||||
// Split this argument into two smaller ones. Then revisit both.
|
// Split this argument into two smaller ones. Then revisit both.
|
||||||
ArgAction::Convert(conv) => {
|
ArgAction::Convert(conv) => {
|
||||||
let new_arg = ArgumentType { value_type: conv.apply(arg.value_type), ..arg };
|
let new_arg = ArgumentType {
|
||||||
|
value_type: conv.apply(arg.value_type),
|
||||||
|
..arg
|
||||||
|
};
|
||||||
args[argno].value_type = new_arg.value_type;
|
args[argno].value_type = new_arg.value_type;
|
||||||
if conv.is_split() {
|
if conv.is_split() {
|
||||||
args.insert(argno + 1, new_arg);
|
args.insert(argno + 1, new_arg);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ impl Context {
|
|||||||
|
|
||||||
/// Run the register allocator.
|
/// Run the register allocator.
|
||||||
pub fn regalloc(&mut self, isa: &TargetIsa) {
|
pub fn regalloc(&mut self, isa: &TargetIsa) {
|
||||||
self.regalloc.run(isa, &mut self.func, &self.cfg, &self.domtree);
|
self.regalloc
|
||||||
|
.run(isa, &mut self.func, &self.cfg, &self.domtree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ impl DominatorTree {
|
|||||||
// Run a finger up the dominator tree from b until we see a.
|
// Run a finger up the dominator tree from b until we see a.
|
||||||
// Do nothing if b is unreachable.
|
// Do nothing if b is unreachable.
|
||||||
while rpo_a < self.nodes[ebb_b].rpo_number {
|
while rpo_a < self.nodes[ebb_b].rpo_number {
|
||||||
b = self.idom(ebb_b).expect("Shouldn't meet unreachable here.");
|
b = self.idom(ebb_b)
|
||||||
|
.expect("Shouldn't meet unreachable here.");
|
||||||
ebb_b = layout.inst_ebb(b).expect("Dominator got removed.");
|
ebb_b = layout.inst_ebb(b).expect("Dominator got removed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,8 +210,9 @@ impl DominatorTree {
|
|||||||
.filter(|&(ebb, _)| self.is_reachable(ebb));
|
.filter(|&(ebb, _)| self.is_reachable(ebb));
|
||||||
|
|
||||||
// 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 =
|
let mut idom = reachable_preds
|
||||||
reachable_preds.next().expect("EBB node must have one reachable predecessor");
|
.next()
|
||||||
|
.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);
|
||||||
|
|||||||
@@ -135,7 +135,9 @@ impl<T: EntityRef> ListPool<T> {
|
|||||||
// The `wrapping_sub` handles the special case 0, which is the empty list. This way, the
|
// The `wrapping_sub` handles the special case 0, which is the empty list. This way, the
|
||||||
// cost of the bounds check that we have to pay anyway is co-opted to handle the special
|
// cost of the bounds check that we have to pay anyway is co-opted to handle the special
|
||||||
// case of the empty list.
|
// case of the empty list.
|
||||||
self.data.get(idx.wrapping_sub(1)).map(|len| len.index())
|
self.data
|
||||||
|
.get(idx.wrapping_sub(1))
|
||||||
|
.map(|len| len.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a storage block with a size given by `sclass`.
|
/// Allocate a storage block with a size given by `sclass`.
|
||||||
|
|||||||
@@ -65,10 +65,7 @@ impl<'c, 'fc, 'fd> InsertBuilder<'c, 'fc, 'fd> {
|
|||||||
pub fn new(dfg: &'fd mut DataFlowGraph,
|
pub fn new(dfg: &'fd mut DataFlowGraph,
|
||||||
pos: &'c mut Cursor<'fc>)
|
pos: &'c mut Cursor<'fc>)
|
||||||
-> InsertBuilder<'c, 'fc, 'fd> {
|
-> InsertBuilder<'c, 'fc, 'fd> {
|
||||||
InsertBuilder {
|
InsertBuilder { dfg: dfg, pos: pos }
|
||||||
dfg: dfg,
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,8 +179,9 @@ impl<'f> InstBuilderBase<'f> for ReplaceBuilder<'f> {
|
|||||||
|
|
||||||
// Normally, make_inst_results() would also set the first result type, but we're not
|
// Normally, make_inst_results() would also set the first result type, but we're not
|
||||||
// going to call that, so set it manually.
|
// going to call that, so set it manually.
|
||||||
*self.dfg[self.inst].first_type_mut() =
|
*self.dfg[self.inst].first_type_mut() = self.dfg
|
||||||
self.dfg.compute_result_type(self.inst, 0, ctrl_typevar).unwrap_or_default();
|
.compute_result_type(self.inst, 0, ctrl_typevar)
|
||||||
|
.unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
(self.inst, self.dfg)
|
(self.inst, self.dfg)
|
||||||
|
|||||||
@@ -356,25 +356,37 @@ impl DataFlowGraph {
|
|||||||
|
|
||||||
/// Get the fixed value arguments on `inst` as a slice.
|
/// Get the fixed value arguments on `inst` as a slice.
|
||||||
pub fn inst_fixed_args(&self, inst: Inst) -> &[Value] {
|
pub fn inst_fixed_args(&self, inst: Inst) -> &[Value] {
|
||||||
let fixed_args = self[inst].opcode().constraints().fixed_value_arguments();
|
let fixed_args = self[inst]
|
||||||
|
.opcode()
|
||||||
|
.constraints()
|
||||||
|
.fixed_value_arguments();
|
||||||
&self.inst_args(inst)[..fixed_args]
|
&self.inst_args(inst)[..fixed_args]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the fixed value arguments on `inst` as a mutable slice.
|
/// Get the fixed value arguments on `inst` as a mutable slice.
|
||||||
pub fn inst_fixed_args_mut(&mut self, inst: Inst) -> &mut [Value] {
|
pub fn inst_fixed_args_mut(&mut self, inst: Inst) -> &mut [Value] {
|
||||||
let fixed_args = self[inst].opcode().constraints().fixed_value_arguments();
|
let fixed_args = self[inst]
|
||||||
|
.opcode()
|
||||||
|
.constraints()
|
||||||
|
.fixed_value_arguments();
|
||||||
&mut self.inst_args_mut(inst)[..fixed_args]
|
&mut self.inst_args_mut(inst)[..fixed_args]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the variable value arguments on `inst` as a slice.
|
/// Get the variable value arguments on `inst` as a slice.
|
||||||
pub fn inst_variable_args(&self, inst: Inst) -> &[Value] {
|
pub fn inst_variable_args(&self, inst: Inst) -> &[Value] {
|
||||||
let fixed_args = self[inst].opcode().constraints().fixed_value_arguments();
|
let fixed_args = self[inst]
|
||||||
|
.opcode()
|
||||||
|
.constraints()
|
||||||
|
.fixed_value_arguments();
|
||||||
&self.inst_args(inst)[fixed_args..]
|
&self.inst_args(inst)[fixed_args..]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the variable value arguments on `inst` as a mutable slice.
|
/// Get the variable value arguments on `inst` as a mutable slice.
|
||||||
pub fn inst_variable_args_mut(&mut self, inst: Inst) -> &mut [Value] {
|
pub fn inst_variable_args_mut(&mut self, inst: Inst) -> &mut [Value] {
|
||||||
let fixed_args = self[inst].opcode().constraints().fixed_value_arguments();
|
let fixed_args = self[inst]
|
||||||
|
.opcode()
|
||||||
|
.constraints()
|
||||||
|
.fixed_value_arguments();
|
||||||
&mut self.inst_args_mut(inst)[fixed_args..]
|
&mut self.inst_args_mut(inst)[fixed_args..]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,7 +517,12 @@ impl DataFlowGraph {
|
|||||||
(inst, 1)
|
(inst, 1)
|
||||||
}
|
}
|
||||||
ExpandedValue::Table(idx) => {
|
ExpandedValue::Table(idx) => {
|
||||||
if let ValueData::Inst { num, inst, ref mut next, .. } = self.extended_values[idx] {
|
if let ValueData::Inst {
|
||||||
|
num,
|
||||||
|
inst,
|
||||||
|
ref mut next,
|
||||||
|
..
|
||||||
|
} = self.extended_values[idx] {
|
||||||
assert!(next.is_none(), "last_res is not the last result");
|
assert!(next.is_none(), "last_res is not the last result");
|
||||||
*next = res.into();
|
*next = res.into();
|
||||||
assert!(num < u16::MAX, "Too many arguments to EBB");
|
assert!(num < u16::MAX, "Too many arguments to EBB");
|
||||||
@@ -518,8 +535,12 @@ impl DataFlowGraph {
|
|||||||
|
|
||||||
// Now update `res` itself.
|
// Now update `res` itself.
|
||||||
if let ExpandedValue::Table(idx) = res.expand() {
|
if let ExpandedValue::Table(idx) = res.expand() {
|
||||||
if let ValueData::Inst { ref mut num, ref mut inst, ref mut next, .. } =
|
if let ValueData::Inst {
|
||||||
self.extended_values[idx] {
|
ref mut num,
|
||||||
|
ref mut inst,
|
||||||
|
ref mut next,
|
||||||
|
..
|
||||||
|
} = self.extended_values[idx] {
|
||||||
*num = res_num;
|
*num = res_num;
|
||||||
*inst = res_inst;
|
*inst = res_inst;
|
||||||
*next = None.into();
|
*next = None.into();
|
||||||
@@ -565,7 +586,8 @@ impl DataFlowGraph {
|
|||||||
///
|
///
|
||||||
/// Returns the new `Inst` reference where the original instruction has been moved.
|
/// Returns the new `Inst` reference where the original instruction has been moved.
|
||||||
pub fn redefine_first_value(&mut self, pos: &mut Cursor) -> Inst {
|
pub fn redefine_first_value(&mut self, pos: &mut Cursor) -> Inst {
|
||||||
let orig = pos.current_inst().expect("Cursor must point at an instruction");
|
let orig = pos.current_inst()
|
||||||
|
.expect("Cursor must point at an instruction");
|
||||||
let data = self[orig].clone();
|
let data = self[orig].clone();
|
||||||
// After cloning, any secondary values are attached to both copies. Don't do that, we only
|
// After cloning, any secondary values are attached to both copies. Don't do that, we only
|
||||||
// want them on the new clone.
|
// want them on the new clone.
|
||||||
@@ -630,7 +652,8 @@ 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).and_then(|sigref| {
|
self.call_signature(inst)
|
||||||
|
.and_then(|sigref| {
|
||||||
self.signatures[sigref]
|
self.signatures[sigref]
|
||||||
.return_types
|
.return_types
|
||||||
.get(result_idx - fixed_results)
|
.get(result_idx - fixed_results)
|
||||||
@@ -815,8 +838,12 @@ impl DataFlowGraph {
|
|||||||
// Now update `arg` itself.
|
// Now update `arg` itself.
|
||||||
let arg_ebb = ebb;
|
let arg_ebb = ebb;
|
||||||
if let ExpandedValue::Table(idx) = arg.expand() {
|
if let ExpandedValue::Table(idx) = arg.expand() {
|
||||||
if let ValueData::Arg { ref mut num, ebb, ref mut next, .. } =
|
if let ValueData::Arg {
|
||||||
self.extended_values[idx] {
|
ref mut num,
|
||||||
|
ebb,
|
||||||
|
ref mut next,
|
||||||
|
..
|
||||||
|
} = self.extended_values[idx] {
|
||||||
*num = arg_num;
|
*num = arg_num;
|
||||||
*next = None.into();
|
*next = None.into();
|
||||||
assert_eq!(arg_ebb, ebb, "{} should already belong to EBB", arg);
|
assert_eq!(arg_ebb, ebb, "{} should already belong to EBB", arg);
|
||||||
|
|||||||
@@ -286,14 +286,18 @@ mod tests {
|
|||||||
assert_eq!(Value::table_with_number(1).unwrap().to_string(), "vx1");
|
assert_eq!(Value::table_with_number(1).unwrap().to_string(), "vx1");
|
||||||
|
|
||||||
assert_eq!(Value::direct_with_number(u32::MAX / 2), None);
|
assert_eq!(Value::direct_with_number(u32::MAX / 2), None);
|
||||||
assert_eq!(match Value::direct_with_number(u32::MAX / 2 - 1).unwrap().expand() {
|
assert_eq!(match Value::direct_with_number(u32::MAX / 2 - 1)
|
||||||
|
.unwrap()
|
||||||
|
.expand() {
|
||||||
ExpandedValue::Direct(i) => i.index() as u32,
|
ExpandedValue::Direct(i) => i.index() as u32,
|
||||||
_ => u32::MAX,
|
_ => u32::MAX,
|
||||||
},
|
},
|
||||||
u32::MAX / 2 - 1);
|
u32::MAX / 2 - 1);
|
||||||
|
|
||||||
assert_eq!(Value::table_with_number(u32::MAX / 2), None);
|
assert_eq!(Value::table_with_number(u32::MAX / 2), None);
|
||||||
assert_eq!(match Value::table_with_number(u32::MAX / 2 - 1).unwrap().expand() {
|
assert_eq!(match Value::table_with_number(u32::MAX / 2 - 1)
|
||||||
|
.unwrap()
|
||||||
|
.expand() {
|
||||||
ExpandedValue::Table(i) => i as u32,
|
ExpandedValue::Table(i) => i as u32,
|
||||||
_ => u32::MAX,
|
_ => u32::MAX,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -221,7 +221,8 @@ mod tests {
|
|||||||
assert_eq!(sig.to_string(), "(i32)");
|
assert_eq!(sig.to_string(), "(i32)");
|
||||||
sig.return_types.push(ArgumentType::new(F32));
|
sig.return_types.push(ArgumentType::new(F32));
|
||||||
assert_eq!(sig.to_string(), "(i32) -> f32");
|
assert_eq!(sig.to_string(), "(i32) -> f32");
|
||||||
sig.argument_types.push(ArgumentType::new(I32.by(4).unwrap()));
|
sig.argument_types
|
||||||
|
.push(ArgumentType::new(I32.by(4).unwrap()));
|
||||||
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32");
|
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32");
|
||||||
sig.return_types.push(ArgumentType::new(B8));
|
sig.return_types.push(ArgumentType::new(B8));
|
||||||
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8");
|
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8");
|
||||||
|
|||||||
@@ -304,15 +304,21 @@ impl InstructionData {
|
|||||||
/// here.
|
/// here.
|
||||||
pub fn analyze_branch<'a>(&'a self, pool: &'a ValueListPool) -> BranchInfo<'a> {
|
pub fn analyze_branch<'a>(&'a self, pool: &'a ValueListPool) -> BranchInfo<'a> {
|
||||||
match self {
|
match self {
|
||||||
&InstructionData::Jump { destination, ref args, .. } => {
|
&InstructionData::Jump {
|
||||||
BranchInfo::SingleDest(destination, &args.as_slice(pool))
|
destination,
|
||||||
}
|
ref args,
|
||||||
&InstructionData::Branch { destination, ref args, .. } => {
|
..
|
||||||
BranchInfo::SingleDest(destination, &args.as_slice(pool)[1..])
|
} => BranchInfo::SingleDest(destination, &args.as_slice(pool)),
|
||||||
}
|
&InstructionData::Branch {
|
||||||
&InstructionData::BranchIcmp { destination, ref args, .. } => {
|
destination,
|
||||||
BranchInfo::SingleDest(destination, &args.as_slice(pool)[2..])
|
ref args,
|
||||||
}
|
..
|
||||||
|
} => BranchInfo::SingleDest(destination, &args.as_slice(pool)[1..]),
|
||||||
|
&InstructionData::BranchIcmp {
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} => BranchInfo::SingleDest(destination, &args.as_slice(pool)[2..]),
|
||||||
&InstructionData::BranchTable { table, .. } => BranchInfo::Table(table),
|
&InstructionData::BranchTable { table, .. } => BranchInfo::Table(table),
|
||||||
_ => BranchInfo::NotABranch,
|
_ => BranchInfo::NotABranch,
|
||||||
}
|
}
|
||||||
@@ -601,9 +607,21 @@ impl OperandConstraint {
|
|||||||
Same => Bound(ctrl_type),
|
Same => Bound(ctrl_type),
|
||||||
LaneOf => Bound(ctrl_type.lane_type()),
|
LaneOf => Bound(ctrl_type.lane_type()),
|
||||||
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 => {
|
||||||
DoubleWidth => Bound(ctrl_type.double_width().expect("invalid type for double_width")),
|
Bound(ctrl_type
|
||||||
HalfVector => Bound(ctrl_type.half_vector().expect("invalid type for half_vector")),
|
.half_width()
|
||||||
|
.expect("invalid type for half_width"))
|
||||||
|
}
|
||||||
|
DoubleWidth => {
|
||||||
|
Bound(ctrl_type
|
||||||
|
.double_width()
|
||||||
|
.expect("invalid type for double_width"))
|
||||||
|
}
|
||||||
|
HalfVector => {
|
||||||
|
Bound(ctrl_type
|
||||||
|
.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")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,15 +66,14 @@ impl JumpTableData {
|
|||||||
///
|
///
|
||||||
/// This returns an iterator that skips any empty slots in the table.
|
/// This returns an iterator that skips any empty slots in the table.
|
||||||
pub fn entries<'a>(&'a self) -> Entries {
|
pub fn entries<'a>(&'a self) -> Entries {
|
||||||
Entries(self.table
|
Entries(self.table.iter().cloned().enumerate())
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.enumerate())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.iter().any(|target_ebb| target_ebb.expand() == Some(ebb))
|
self.table
|
||||||
|
.iter()
|
||||||
|
.any(|target_ebb| target_ebb.expand() == Some(ebb))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the whole table as a mutable slice.
|
/// Access the whole table as a mutable slice.
|
||||||
@@ -109,10 +108,7 @@ impl Display for JumpTableData {
|
|||||||
Some(first) => write!(fmt, "jump_table {}", first)?,
|
Some(first) => write!(fmt, "jump_table {}", first)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
for dest in self.table
|
for dest in self.table.iter().skip(1).map(|e| e.expand()) {
|
||||||
.iter()
|
|
||||||
.skip(1)
|
|
||||||
.map(|e| e.expand()) {
|
|
||||||
match dest {
|
match dest {
|
||||||
None => write!(fmt, ", 0")?,
|
None => write!(fmt, ", 0")?,
|
||||||
Some(ebb) => write!(fmt, ", {}", ebb)?,
|
Some(ebb) => write!(fmt, ", {}", ebb)?,
|
||||||
|
|||||||
@@ -125,7 +125,10 @@ impl Layout {
|
|||||||
/// Get the last sequence number in `ebb`.
|
/// Get the last sequence number in `ebb`.
|
||||||
fn last_ebb_seq(&self, ebb: Ebb) -> SequenceNumber {
|
fn last_ebb_seq(&self, ebb: Ebb) -> SequenceNumber {
|
||||||
// Get the seq of the last instruction if it exists, otherwise use the EBB header seq.
|
// Get the seq of the last instruction if it exists, otherwise use the EBB header seq.
|
||||||
self.ebbs[ebb].last_inst.map(|inst| self.insts[inst].seq).unwrap_or(self.ebbs[ebb].seq)
|
self.ebbs[ebb]
|
||||||
|
.last_inst
|
||||||
|
.map(|inst| self.insts[inst].seq)
|
||||||
|
.unwrap_or(self.ebbs[ebb].seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign a valid sequence number to `ebb` such that the numbers are still monotonic. This may
|
/// Assign a valid sequence number to `ebb` such that the numbers are still monotonic. This may
|
||||||
@@ -134,7 +137,10 @@ impl Layout {
|
|||||||
assert!(self.is_ebb_inserted(ebb));
|
assert!(self.is_ebb_inserted(ebb));
|
||||||
|
|
||||||
// Get the sequence number immediately before `ebb`, or 0.
|
// Get the sequence number immediately before `ebb`, or 0.
|
||||||
let prev_seq = self.ebbs[ebb].prev.map(|prev_ebb| self.last_ebb_seq(prev_ebb)).unwrap_or(0);
|
let prev_seq = self.ebbs[ebb]
|
||||||
|
.prev
|
||||||
|
.map(|prev_ebb| self.last_ebb_seq(prev_ebb))
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
// Get the sequence number immediately following `ebb`.
|
// Get the sequence number immediately following `ebb`.
|
||||||
let next_seq = if let Some(inst) = self.ebbs[ebb].first_inst.expand() {
|
let next_seq = if let Some(inst) = self.ebbs[ebb].first_inst.expand() {
|
||||||
@@ -159,7 +165,8 @@ 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).expect("inst must be inserted before assigning an seq");
|
let ebb = self.inst_ebb(inst)
|
||||||
|
.expect("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() {
|
||||||
@@ -436,8 +443,8 @@ 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 =
|
let ebb = self.inst_ebb(before)
|
||||||
self.inst_ebb(before).expect("Instruction before insertion point not in the layout");
|
.expect("Instruction before insertion point not in the layout");
|
||||||
let after = self.insts[before].prev;
|
let after = self.insts[before].prev;
|
||||||
{
|
{
|
||||||
let inst_node = self.insts.ensure(inst);
|
let inst_node = self.insts.ensure(inst);
|
||||||
@@ -485,8 +492,8 @@ 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 =
|
let old_ebb = self.inst_ebb(before)
|
||||||
self.inst_ebb(before).expect("The `before` instruction must be in the layout");
|
.expect("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.
|
||||||
@@ -786,8 +793,9 @@ impl<'f> Cursor<'f> {
|
|||||||
self.pos = At(next);
|
self.pos = At(next);
|
||||||
Some(next)
|
Some(next)
|
||||||
} else {
|
} else {
|
||||||
self.pos =
|
self.pos = After(self.layout
|
||||||
After(self.layout.inst_ebb(inst).expect("current instruction removed?"));
|
.inst_ebb(inst)
|
||||||
|
.expect("current instruction removed?"));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -837,8 +845,9 @@ impl<'f> Cursor<'f> {
|
|||||||
self.pos = At(prev);
|
self.pos = At(prev);
|
||||||
Some(prev)
|
Some(prev)
|
||||||
} else {
|
} else {
|
||||||
self.pos =
|
self.pos = Before(self.layout
|
||||||
Before(self.layout.inst_ebb(inst).expect("current instruction removed?"));
|
.inst_ebb(inst)
|
||||||
|
.expect("current instruction removed?"));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,12 +348,7 @@ mod tests {
|
|||||||
assert_eq!(big.bits(), 64 * 256);
|
assert_eq!(big.bits(), 64 * 256);
|
||||||
|
|
||||||
assert_eq!(big.half_vector().unwrap().to_string(), "f64x128");
|
assert_eq!(big.half_vector().unwrap().to_string(), "f64x128");
|
||||||
assert_eq!(B1.by(2)
|
assert_eq!(B1.by(2).unwrap().half_vector().unwrap().to_string(), "b1");
|
||||||
.unwrap()
|
|
||||||
.half_vector()
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
"b1");
|
|
||||||
assert_eq!(I32.half_vector(), None);
|
assert_eq!(I32.half_vector(), None);
|
||||||
assert_eq!(VOID.half_vector(), None);
|
assert_eq!(VOID.half_vector(), None);
|
||||||
|
|
||||||
@@ -383,12 +378,7 @@ mod tests {
|
|||||||
assert_eq!(B1.by(8).unwrap().to_string(), "b1x8");
|
assert_eq!(B1.by(8).unwrap().to_string(), "b1x8");
|
||||||
assert_eq!(B8.by(1).unwrap().to_string(), "b8");
|
assert_eq!(B8.by(1).unwrap().to_string(), "b8");
|
||||||
assert_eq!(B16.by(256).unwrap().to_string(), "b16x256");
|
assert_eq!(B16.by(256).unwrap().to_string(), "b16x256");
|
||||||
assert_eq!(B32.by(4)
|
assert_eq!(B32.by(4).unwrap().by(2).unwrap().to_string(), "b32x8");
|
||||||
.unwrap()
|
|
||||||
.by(2)
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
"b32x8");
|
|
||||||
assert_eq!(B64.by(8).unwrap().to_string(), "b64x8");
|
assert_eq!(B64.by(8).unwrap().to_string(), "b64x8");
|
||||||
assert_eq!(I8.by(64).unwrap().to_string(), "i8x64");
|
assert_eq!(I8.by(64).unwrap().to_string(), "i8x64");
|
||||||
assert_eq!(F64.by(2).unwrap().to_string(), "f64x2");
|
assert_eq!(F64.by(2).unwrap().to_string(), "f64x2");
|
||||||
|
|||||||
@@ -235,7 +235,11 @@ fn put_sb<CS: CodeSink + ?Sized>(bits: u16, rs1: RegUnit, rs2: RegUnit, sink: &m
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn recipe_sb<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
|
fn recipe_sb<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
|
||||||
if let InstructionData::BranchIcmp { destination, ref args, .. } = func.dfg[inst] {
|
if let InstructionData::BranchIcmp {
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} = func.dfg[inst] {
|
||||||
let args = &args.as_slice(&func.dfg.value_lists)[0..2];
|
let args = &args.as_slice(&func.dfg.value_lists)[0..2];
|
||||||
sink.reloc_ebb(RelocKind::Branch.into(), destination);
|
sink.reloc_ebb(RelocKind::Branch.into(), destination);
|
||||||
put_sb(func.encodings[inst].bits(),
|
put_sb(func.encodings[inst].bits(),
|
||||||
@@ -248,7 +252,11 @@ fn recipe_sb<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn recipe_sbzero<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
|
fn recipe_sbzero<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
|
||||||
if let InstructionData::Branch { destination, ref args, .. } = func.dfg[inst] {
|
if let InstructionData::Branch {
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} = func.dfg[inst] {
|
||||||
let args = &args.as_slice(&func.dfg.value_lists)[0..1];
|
let args = &args.as_slice(&func.dfg.value_lists)[0..1];
|
||||||
sink.reloc_ebb(RelocKind::Branch.into(), destination);
|
sink.reloc_ebb(RelocKind::Branch.into(), destination);
|
||||||
put_sb(func.encodings[inst].bits(),
|
put_sb(func.encodings[inst].bits(),
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ fn legalize_inst_results<ResType>(dfg: &mut DataFlowGraph,
|
|||||||
-> Inst
|
-> Inst
|
||||||
where ResType: FnMut(&DataFlowGraph, usize) -> ArgumentType
|
where ResType: FnMut(&DataFlowGraph, usize) -> ArgumentType
|
||||||
{
|
{
|
||||||
let mut call = pos.current_inst().expect("Cursor must point to a call instruction");
|
let mut call = pos.current_inst()
|
||||||
|
.expect("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.
|
||||||
@@ -364,10 +365,13 @@ fn legalize_inst_arguments<ArgType>(dfg: &mut DataFlowGraph,
|
|||||||
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().expect("Cursor must point to a call instruction");
|
let inst = pos.current_inst()
|
||||||
|
.expect("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].take_value_list().expect("Call must have a value list");
|
let mut vlist = dfg[inst]
|
||||||
|
.take_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.
|
||||||
@@ -405,7 +409,9 @@ fn legalize_inst_arguments<ArgType>(dfg: &mut DataFlowGraph,
|
|||||||
|
|
||||||
let mut abi_arg = 0;
|
let mut abi_arg = 0;
|
||||||
for old_arg in 0..have_args {
|
for old_arg in 0..have_args {
|
||||||
let old_value = vlist.get(old_arg_offset + old_arg, &dfg.value_lists).unwrap();
|
let old_value = vlist
|
||||||
|
.get(old_arg_offset + old_arg, &dfg.value_lists)
|
||||||
|
.unwrap();
|
||||||
let mut put_arg = |dfg: &mut DataFlowGraph, arg| {
|
let mut put_arg = |dfg: &mut DataFlowGraph, arg| {
|
||||||
let abi_type = get_abi_type(dfg, abi_arg);
|
let abi_type = get_abi_type(dfg, abi_arg);
|
||||||
if dfg.value_type(arg) == abi_type.value_type {
|
if dfg.value_type(arg) == abi_type.value_type {
|
||||||
@@ -435,7 +441,8 @@ fn legalize_inst_arguments<ArgType>(dfg: &mut DataFlowGraph,
|
|||||||
///
|
///
|
||||||
/// Returns `true` if any instructions were inserted.
|
/// Returns `true` if any instructions were inserted.
|
||||||
pub fn handle_call_abi(dfg: &mut DataFlowGraph, cfg: &ControlFlowGraph, pos: &mut Cursor) -> bool {
|
pub fn handle_call_abi(dfg: &mut DataFlowGraph, cfg: &ControlFlowGraph, pos: &mut Cursor) -> bool {
|
||||||
let mut inst = pos.current_inst().expect("Cursor must point to a call instruction");
|
let mut inst = pos.current_inst()
|
||||||
|
.expect("Cursor must point to a call instruction");
|
||||||
|
|
||||||
// Start by checking if the argument types already match the signature.
|
// Start by checking if the argument types already match the signature.
|
||||||
let sig_ref = match check_call_signature(dfg, inst) {
|
let sig_ref = match check_call_signature(dfg, inst) {
|
||||||
@@ -475,7 +482,8 @@ pub fn handle_return_abi(dfg: &mut DataFlowGraph,
|
|||||||
pos: &mut Cursor,
|
pos: &mut Cursor,
|
||||||
sig: &Signature)
|
sig: &Signature)
|
||||||
-> bool {
|
-> bool {
|
||||||
let inst = pos.current_inst().expect("Cursor must point to a return instruction");
|
let inst = pos.current_inst()
|
||||||
|
.expect("Cursor must point to a return instruction");
|
||||||
|
|
||||||
// Check if the returned types already match the signature.
|
// Check if the returned types already match the signature.
|
||||||
if check_return_signature(dfg, inst, sig) {
|
if check_return_signature(dfg, inst, sig) {
|
||||||
|
|||||||
@@ -125,7 +125,9 @@ fn split_any(dfg: &mut DataFlowGraph,
|
|||||||
"Predecessor not a branch: {}",
|
"Predecessor not a branch: {}",
|
||||||
dfg.display_inst(inst));
|
dfg.display_inst(inst));
|
||||||
let fixed_args = branch_opc.constraints().fixed_value_arguments();
|
let fixed_args = branch_opc.constraints().fixed_value_arguments();
|
||||||
let mut args = dfg[inst].take_value_list().expect("Branches must have value lists.");
|
let mut args = dfg[inst]
|
||||||
|
.take_value_list()
|
||||||
|
.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)
|
||||||
@@ -142,12 +144,14 @@ fn split_any(dfg: &mut DataFlowGraph,
|
|||||||
let (lo, hi) = split_value(dfg, pos, old_arg, repair.concat, &mut repairs);
|
let (lo, hi) = split_value(dfg, pos, old_arg, repair.concat, &mut repairs);
|
||||||
|
|
||||||
// The `lo` part replaces the original argument.
|
// The `lo` part replaces the original argument.
|
||||||
*args.get_mut(fixed_args + repair.num, &mut dfg.value_lists).unwrap() = lo;
|
*args.get_mut(fixed_args + repair.num, &mut dfg.value_lists)
|
||||||
|
.unwrap() = lo;
|
||||||
|
|
||||||
// The `hi` part goes at the end. Since multiple repairs may have been scheduled to the
|
// The `hi` part goes at the end. Since multiple repairs may have been scheduled to the
|
||||||
// same EBB, there could be multiple arguments missing.
|
// same EBB, there could be multiple arguments missing.
|
||||||
if num_args > fixed_args + repair.hi_num {
|
if num_args > fixed_args + repair.hi_num {
|
||||||
*args.get_mut(fixed_args + repair.hi_num, &mut dfg.value_lists).unwrap() = hi;
|
*args.get_mut(fixed_args + repair.hi_num, &mut dfg.value_lists)
|
||||||
|
.unwrap() = hi;
|
||||||
} else {
|
} else {
|
||||||
// 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
|
||||||
|
|||||||
@@ -35,10 +35,7 @@ mod tests {
|
|||||||
|
|
||||||
fn check(x: &[u32], want: &[u32]) {
|
fn check(x: &[u32], want: &[u32]) {
|
||||||
assert_eq!(x.len(), want.len());
|
assert_eq!(x.len(), want.len());
|
||||||
let want_count = want.iter()
|
let want_count = want.iter().cloned().filter(|&x| x % 10 == 0).count();
|
||||||
.cloned()
|
|
||||||
.filter(|&x| x % 10 == 0)
|
|
||||||
.count();
|
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
v.extend(x.iter().cloned());
|
v.extend(x.iter().cloned());
|
||||||
let count = partition_slice(&mut v[..], |&x| x % 10 == 0);
|
let count = partition_slice(&mut v[..], |&x| x % 10 == 0);
|
||||||
|
|||||||
@@ -232,7 +232,9 @@ impl<'a> Context<'a> {
|
|||||||
if let Affinity::Reg(rc_index) = lv.affinity {
|
if let Affinity::Reg(rc_index) = lv.affinity {
|
||||||
let regclass = self.reginfo.rc(rc_index);
|
let regclass = self.reginfo.rc(rc_index);
|
||||||
// TODO: Fall back to a top-level super-class. Sub-classes are only hints.
|
// TODO: Fall back to a top-level super-class. Sub-classes are only hints.
|
||||||
let regunit = regs.iter(regclass).next().expect("Out of registers for arguments");
|
let regunit = regs.iter(regclass)
|
||||||
|
.next()
|
||||||
|
.expect("Out of registers for arguments");
|
||||||
regs.take(regclass, regunit);
|
regs.take(regclass, regunit);
|
||||||
*locations.ensure(lv.value) = ValueLoc::Reg(regunit);
|
*locations.ensure(lv.value) = ValueLoc::Reg(regunit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ impl Context {
|
|||||||
// TODO: Second pass: Spilling.
|
// TODO: Second pass: Spilling.
|
||||||
|
|
||||||
// Third pass: Reload and coloring.
|
// Third pass: Reload and coloring.
|
||||||
self.coloring.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
self.coloring
|
||||||
|
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ impl LiveValueVec {
|
|||||||
|
|
||||||
/// Add a new live value to `values`.
|
/// Add a new live value to `values`.
|
||||||
fn push(&mut self, value: Value, endpoint: Inst, affinity: Affinity) {
|
fn push(&mut self, value: Value, endpoint: Inst, affinity: Affinity) {
|
||||||
self.values.push(LiveValue {
|
self.values
|
||||||
|
.push(LiveValue {
|
||||||
value: value,
|
value: value,
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
affinity: affinity,
|
affinity: affinity,
|
||||||
@@ -163,11 +164,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 =
|
let idom_live_list = self.idom_sets
|
||||||
self.idom_sets.get(&idom).expect("No stored live set for dominator");
|
.get(&idom)
|
||||||
|
.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.get(value).expect("Immediate dominator value has no live range");
|
let lr = liveness
|
||||||
|
.get(value)
|
||||||
|
.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, program_order) {
|
if let Some(endpoint) = lr.livein_local_end(ebb, program_order) {
|
||||||
@@ -179,7 +183,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.get(value).expect("EBB argument value has no live range");
|
let lr = liveness
|
||||||
|
.get(value)
|
||||||
|
.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) => {
|
||||||
@@ -259,13 +265,12 @@ impl LiveValueTracker {
|
|||||||
|
|
||||||
/// Save the current set of live values so it is associated with `idom`.
|
/// Save the current set of live values so it is associated with `idom`.
|
||||||
fn save_idom_live_set(&mut self, idom: Inst) {
|
fn save_idom_live_set(&mut self, idom: Inst) {
|
||||||
let values = self.live
|
let values = self.live.values.iter().map(|lv| lv.value);
|
||||||
.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.entry(idom).or_insert_with(|| {
|
self.idom_sets
|
||||||
|
.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
|
||||||
|
|||||||
@@ -205,7 +205,8 @@ fn get_or_create<'a>(lrset: &'a mut LiveRangeSet,
|
|||||||
def = inst.into();
|
def = inst.into();
|
||||||
// Initialize the affinity from the defining instruction's result constraints.
|
// Initialize the affinity from the defining instruction's result constraints.
|
||||||
// Don't do this for call return values which are always tied to a single register.
|
// Don't do this for call return values which are always tied to a single register.
|
||||||
affinity = recipe_constraints.get(func.encodings[inst].recipe())
|
affinity = recipe_constraints
|
||||||
|
.get(func.encodings[inst].recipe())
|
||||||
.and_then(|rc| rc.outs.get(rnum))
|
.and_then(|rc| rc.outs.get(rnum))
|
||||||
.map(Affinity::new)
|
.map(Affinity::new)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
@@ -315,7 +316,8 @@ impl Liveness {
|
|||||||
let recipe = func.encodings[inst].recipe();
|
let recipe = func.encodings[inst].recipe();
|
||||||
// Iterator of constraints, one per value operand.
|
// Iterator of constraints, one per value operand.
|
||||||
// TODO: Should we fail here if the instruction doesn't have a valid encoding?
|
// TODO: Should we fail here if the instruction doesn't have a valid encoding?
|
||||||
let mut operand_constraints = recipe_constraints.get(recipe)
|
let mut operand_constraints = recipe_constraints
|
||||||
|
.get(recipe)
|
||||||
.map(|c| c.ins)
|
.map(|c| c.ins)
|
||||||
.unwrap_or(&[])
|
.unwrap_or(&[])
|
||||||
.iter();
|
.iter();
|
||||||
|
|||||||
@@ -221,7 +221,9 @@ impl LiveRange {
|
|||||||
/// Return `Ok(n)` if `liveins[n]` already contains `ebb`.
|
/// Return `Ok(n)` if `liveins[n]` already contains `ebb`.
|
||||||
/// Otherwise, return `Err(n)` with the index where such an interval should be inserted.
|
/// Otherwise, return `Err(n)` with the index where such an interval should be inserted.
|
||||||
fn find_ebb_interval<PO: ProgramOrder>(&self, ebb: Ebb, order: &PO) -> Result<usize, usize> {
|
fn find_ebb_interval<PO: ProgramOrder>(&self, ebb: Ebb, order: &PO) -> Result<usize, usize> {
|
||||||
self.liveins.binary_search_by(|intv| order.cmp(intv.begin, ebb)).or_else(|n| {
|
self.liveins
|
||||||
|
.binary_search_by(|intv| order.cmp(intv.begin, ebb))
|
||||||
|
.or_else(|n| {
|
||||||
// The interval at `n-1` may cover `ebb`.
|
// The interval at `n-1` may cover `ebb`.
|
||||||
if n > 0 && order.cmp(self.liveins[n - 1].end, ebb) == Ordering::Greater {
|
if n > 0 && order.cmp(self.liveins[n - 1].end, ebb) == Ordering::Greater {
|
||||||
Ok(n - 1)
|
Ok(n - 1)
|
||||||
@@ -307,7 +309,8 @@ impl LiveRange {
|
|||||||
}
|
}
|
||||||
// Cannot coalesce; insert new interval
|
// Cannot coalesce; insert new interval
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
self.liveins.insert(n,
|
self.liveins
|
||||||
|
.insert(n,
|
||||||
Interval {
|
Interval {
|
||||||
begin: ebb,
|
begin: ebb,
|
||||||
end: to,
|
end: to,
|
||||||
@@ -361,7 +364,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).ok().map(|n| self.liveins[n].end)
|
self.find_ebb_interval(ebb, order)
|
||||||
|
.ok()
|
||||||
|
.map(|n| self.liveins[n].end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,8 +177,9 @@ impl<'a> Verifier<'a> {
|
|||||||
|
|
||||||
let fixed_results = inst_data.opcode().constraints().fixed_results();
|
let fixed_results = inst_data.opcode().constraints().fixed_results();
|
||||||
// var_results is 0 if we aren't a call instruction
|
// var_results is 0 if we aren't a call instruction
|
||||||
let var_results =
|
let var_results = dfg.call_signature(inst)
|
||||||
dfg.call_signature(inst).map(|sig| dfg.signatures[sig].return_types.len()).unwrap_or(0);
|
.map(|sig| dfg.signatures[sig].return_types.len())
|
||||||
|
.unwrap_or(0);
|
||||||
let total_results = fixed_results + var_results;
|
let total_results = fixed_results + var_results;
|
||||||
|
|
||||||
if total_results == 0 {
|
if total_results == 0 {
|
||||||
@@ -218,9 +219,21 @@ impl<'a> Verifier<'a> {
|
|||||||
&MultiAry { ref args, .. } => {
|
&MultiAry { ref args, .. } => {
|
||||||
self.verify_value_list(inst, args)?;
|
self.verify_value_list(inst, args)?;
|
||||||
}
|
}
|
||||||
&Jump { destination, ref args, .. } |
|
&Jump {
|
||||||
&Branch { destination, ref args, .. } |
|
destination,
|
||||||
&BranchIcmp { destination, ref args, .. } => {
|
ref args,
|
||||||
|
..
|
||||||
|
} |
|
||||||
|
&Branch {
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} |
|
||||||
|
&BranchIcmp {
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
self.verify_ebb(inst, destination)?;
|
self.verify_ebb(inst, destination)?;
|
||||||
self.verify_value_list(inst, args)?;
|
self.verify_value_list(inst, args)?;
|
||||||
}
|
}
|
||||||
@@ -265,10 +278,7 @@ impl<'a> Verifier<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_sig_ref(&self, inst: Inst, s: SigRef) -> Result<()> {
|
fn verify_sig_ref(&self, inst: Inst, s: SigRef) -> Result<()> {
|
||||||
if !self.func
|
if !self.func.dfg.signatures.is_valid(s) {
|
||||||
.dfg
|
|
||||||
.signatures
|
|
||||||
.is_valid(s) {
|
|
||||||
err!(inst, "invalid signature reference {}", s)
|
err!(inst, "invalid signature reference {}", s)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -276,10 +286,7 @@ impl<'a> Verifier<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_func_ref(&self, inst: Inst, f: FuncRef) -> Result<()> {
|
fn verify_func_ref(&self, inst: Inst, f: FuncRef) -> Result<()> {
|
||||||
if !self.func
|
if !self.func.dfg.ext_funcs.is_valid(f) {
|
||||||
.dfg
|
|
||||||
.ext_funcs
|
|
||||||
.is_valid(f) {
|
|
||||||
err!(inst, "invalid function reference {}", f)
|
err!(inst, "invalid function reference {}", f)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -326,7 +333,8 @@ impl<'a> Verifier<'a> {
|
|||||||
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.dominates(def_inst, loc_inst, &self.func.layout) {
|
if !self.domtree
|
||||||
|
.dominates(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,7 +351,8 @@ impl<'a> Verifier<'a> {
|
|||||||
ebb);
|
ebb);
|
||||||
}
|
}
|
||||||
// The defining EBB dominates the instruction using this value.
|
// The defining EBB dominates the instruction using this value.
|
||||||
if !self.domtree.ebb_dominates(ebb, loc_inst, &self.func.layout) {
|
if !self.domtree
|
||||||
|
.ebb_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -378,10 +387,7 @@ impl<'a> Verifier<'a> {
|
|||||||
return err!(ebb, "entry block arguments must match function signature");
|
return err!(ebb, "entry block arguments must match function signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, arg) in self.func
|
for (i, arg) in self.func.dfg.ebb_args(ebb).enumerate() {
|
||||||
.dfg
|
|
||||||
.ebb_args(ebb)
|
|
||||||
.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,
|
||||||
@@ -452,11 +458,7 @@ impl<'a> Verifier<'a> {
|
|||||||
fn typecheck_fixed_args(&self, inst: Inst, ctrl_type: Type) -> Result<()> {
|
fn typecheck_fixed_args(&self, inst: Inst, ctrl_type: Type) -> Result<()> {
|
||||||
let constraints = self.func.dfg[inst].opcode().constraints();
|
let constraints = self.func.dfg[inst].opcode().constraints();
|
||||||
|
|
||||||
for (i, &arg) in self.func
|
for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {
|
||||||
.dfg
|
|
||||||
.inst_fixed_args(inst)
|
|
||||||
.iter()
|
|
||||||
.enumerate() {
|
|
||||||
let arg_type = self.func.dfg.value_type(arg);
|
let arg_type = self.func.dfg.value_type(arg);
|
||||||
match constraints.value_argument_constraint(i, ctrl_type) {
|
match constraints.value_argument_constraint(i, ctrl_type) {
|
||||||
ResolvedConstraint::Bound(expected_type) => {
|
ResolvedConstraint::Bound(expected_type) => {
|
||||||
@@ -510,13 +512,17 @@ impl<'a> Verifier<'a> {
|
|||||||
match self.func.dfg[inst].analyze_call(&self.func.dfg.value_lists) {
|
match self.func.dfg[inst].analyze_call(&self.func.dfg.value_lists) {
|
||||||
CallInfo::Direct(func_ref, _) => {
|
CallInfo::Direct(func_ref, _) => {
|
||||||
let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
|
let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
|
||||||
let arg_types =
|
let arg_types = self.func.dfg.signatures[sig_ref]
|
||||||
self.func.dfg.signatures[sig_ref].argument_types.iter().map(|a| a.value_type);
|
.argument_types
|
||||||
|
.iter()
|
||||||
|
.map(|a| a.value_type);
|
||||||
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
||||||
}
|
}
|
||||||
CallInfo::Indirect(sig_ref, _) => {
|
CallInfo::Indirect(sig_ref, _) => {
|
||||||
let arg_types =
|
let arg_types = self.func.dfg.signatures[sig_ref]
|
||||||
self.func.dfg.signatures[sig_ref].argument_types.iter().map(|a| a.value_type);
|
.argument_types
|
||||||
|
.iter()
|
||||||
|
.map(|a| a.value_type);
|
||||||
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
||||||
}
|
}
|
||||||
CallInfo::NotACall => {}
|
CallInfo::NotACall => {}
|
||||||
@@ -673,7 +679,8 @@ 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 = func.dfg.make_inst(InstructionData::Nullary {
|
let nullary_with_bad_opcode = func.dfg
|
||||||
|
.make_inst(InstructionData::Nullary {
|
||||||
opcode: Opcode::Jump,
|
opcode: Opcode::Jump,
|
||||||
ty: types::VOID,
|
ty: types::VOID,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -262,7 +262,11 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result
|
|||||||
IntCompare { cond, args, .. } => write!(w, " {}, {}, {}", cond, args[0], args[1]),
|
IntCompare { cond, args, .. } => write!(w, " {}, {}, {}", cond, args[0], args[1]),
|
||||||
IntCompareImm { cond, arg, imm, .. } => write!(w, " {}, {}, {}", cond, arg, imm),
|
IntCompareImm { cond, arg, imm, .. } => write!(w, " {}, {}, {}", cond, arg, imm),
|
||||||
FloatCompare { cond, args, .. } => write!(w, " {}, {}, {}", cond, args[0], args[1]),
|
FloatCompare { cond, args, .. } => write!(w, " {}, {}, {}", cond, args[0], args[1]),
|
||||||
Jump { destination, ref args, .. } => {
|
Jump {
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
write!(w, " {}", destination)
|
write!(w, " {}", destination)
|
||||||
} else {
|
} else {
|
||||||
@@ -272,7 +276,11 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result
|
|||||||
DisplayValues(args.as_slice(pool)))
|
DisplayValues(args.as_slice(pool)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Branch { destination, ref args, .. } => {
|
Branch {
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let args = args.as_slice(pool);
|
let args = args.as_slice(pool);
|
||||||
write!(w, " {}, {}", args[0], destination)?;
|
write!(w, " {}, {}", args[0], destination)?;
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
@@ -280,7 +288,12 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
BranchIcmp { cond, destination, ref args, .. } => {
|
BranchIcmp {
|
||||||
|
cond,
|
||||||
|
destination,
|
||||||
|
ref args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let args = args.as_slice(pool);
|
let args = args.as_slice(pool);
|
||||||
write!(w, " {}, {}, {}, {}", cond, args[0], args[1], destination)?;
|
write!(w, " {}, {}, {}, {}", cond, args[0], args[1], destination)?;
|
||||||
if args.len() > 2 {
|
if args.len() > 2 {
|
||||||
|
|||||||
@@ -183,7 +183,9 @@ impl Checker {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Directive::Regex(ref var, ref rx) => {
|
Directive::Regex(ref var, ref rx) => {
|
||||||
state.vars.insert(var.clone(),
|
state
|
||||||
|
.vars
|
||||||
|
.insert(var.clone(),
|
||||||
VarDef {
|
VarDef {
|
||||||
value: Value::Regex(Cow::Borrowed(rx)),
|
value: Value::Regex(Cow::Borrowed(rx)),
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@@ -208,10 +210,14 @@ impl Checker {
|
|||||||
state.recorder.directive(not_idx);
|
state.recorder.directive(not_idx);
|
||||||
if let Some((s, e)) = rx.find(&text[not_begin..match_begin]) {
|
if let Some((s, e)) = rx.find(&text[not_begin..match_begin]) {
|
||||||
// Matched `not:` pattern.
|
// Matched `not:` pattern.
|
||||||
state.recorder.matched_not(rx.as_str(), (not_begin + s, not_begin + e));
|
state
|
||||||
|
.recorder
|
||||||
|
.matched_not(rx.as_str(), (not_begin + s, not_begin + e));
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
} else {
|
} else {
|
||||||
state.recorder.missed_not(rx.as_str(), (not_begin, match_begin));
|
state
|
||||||
|
.recorder
|
||||||
|
.missed_not(rx.as_str(), (not_begin, match_begin));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,9 +416,11 @@ mod tests {
|
|||||||
Ok(true));
|
Ok(true));
|
||||||
assert_eq!(b.directive("regex: X = tommy").map_err(e2s),
|
assert_eq!(b.directive("regex: X = tommy").map_err(e2s),
|
||||||
Err("expected '=' after variable 'X' in regex: X = tommy".to_string()));
|
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!(b.directive("[arm]not: patt $x $(y) here")
|
||||||
|
.map_err(e2s),
|
||||||
Ok(true));
|
Ok(true));
|
||||||
assert_eq!(b.directive("[x86]sameln: $x $(y=[^]]*) there").map_err(e2s),
|
assert_eq!(b.directive("[x86]sameln: $x $(y=[^]]*) there")
|
||||||
|
.map_err(e2s),
|
||||||
Ok(true));
|
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));
|
||||||
|
|||||||
@@ -119,7 +119,8 @@ impl<'a> Display for Explainer<'a> {
|
|||||||
m.regex)?;
|
m.regex)?;
|
||||||
|
|
||||||
// Emit any variable definitions.
|
// Emit any variable definitions.
|
||||||
if let Ok(found) = self.vardefs.binary_search_by_key(&m.directive, |v| v.directive) {
|
if let Ok(found) = self.vardefs
|
||||||
|
.binary_search_by_key(&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;
|
||||||
@@ -147,7 +148,8 @@ 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.push(Match {
|
self.matches
|
||||||
|
.push(Match {
|
||||||
directive: self.directive,
|
directive: self.directive,
|
||||||
is_match: true,
|
is_match: true,
|
||||||
is_not: false,
|
is_not: false,
|
||||||
@@ -157,7 +159,8 @@ 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.push(Match {
|
self.matches
|
||||||
|
.push(Match {
|
||||||
directive: self.directive,
|
directive: self.directive,
|
||||||
is_match: true,
|
is_match: true,
|
||||||
is_not: true,
|
is_not: true,
|
||||||
@@ -167,7 +170,8 @@ 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.push(Match {
|
self.matches
|
||||||
|
.push(Match {
|
||||||
directive: self.directive,
|
directive: self.directive,
|
||||||
is_match: false,
|
is_match: false,
|
||||||
is_not: false,
|
is_not: false,
|
||||||
@@ -177,7 +181,8 @@ 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.push(Match {
|
self.matches
|
||||||
|
.push(Match {
|
||||||
directive: self.directive,
|
directive: self.directive,
|
||||||
is_match: false,
|
is_match: false,
|
||||||
is_not: true,
|
is_not: true,
|
||||||
@@ -187,7 +192,8 @@ 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.push(VarDef {
|
self.vardefs
|
||||||
|
.push(VarDef {
|
||||||
directive: self.directive,
|
directive: self.directive,
|
||||||
varname: varname.to_owned(),
|
varname: varname.to_owned(),
|
||||||
value: value.to_owned(),
|
value: value.to_owned(),
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ impl<'a> Context<'a> {
|
|||||||
// Get the index of a recipe name if it exists.
|
// Get the index of a recipe name if it exists.
|
||||||
fn find_recipe_index(&self, recipe_name: &str) -> Option<u16> {
|
fn find_recipe_index(&self, recipe_name: &str) -> Option<u16> {
|
||||||
if let Some(unique_isa) = self.unique_isa {
|
if let Some(unique_isa) = self.unique_isa {
|
||||||
unique_isa.recipe_names()
|
unique_isa
|
||||||
|
.recipe_names()
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&name| name == recipe_name)
|
.position(|&name| name == recipe_name)
|
||||||
.map(|idx| idx as u16)
|
.map(|idx| idx as u16)
|
||||||
@@ -118,17 +119,14 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Allocate a new stack slot and add a mapping number -> StackSlot.
|
// Allocate a new stack slot and add a mapping number -> StackSlot.
|
||||||
fn add_ss(&mut self, number: u32, data: StackSlotData, loc: &Location) -> Result<()> {
|
fn add_ss(&mut self, number: u32, data: StackSlotData, loc: &Location) -> Result<()> {
|
||||||
self.map.def_ss(number, self.function.stack_slots.push(data), loc)
|
self.map
|
||||||
|
.def_ss(number, self.function.stack_slots.push(data), loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a new signature and add a mapping number -> SigRef.
|
// Allocate a new signature and add a mapping number -> SigRef.
|
||||||
fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> {
|
fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> {
|
||||||
self.map.def_sig(number,
|
self.map
|
||||||
self.function
|
.def_sig(number, self.function.dfg.signatures.push(data), loc)
|
||||||
.dfg
|
|
||||||
.signatures
|
|
||||||
.push(data),
|
|
||||||
loc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a reference to a signature.
|
// Resolve a reference to a signature.
|
||||||
@@ -141,12 +139,8 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Allocate a new external function and add a mapping number -> FuncRef.
|
// Allocate a new external function and add a mapping number -> FuncRef.
|
||||||
fn add_fn(&mut self, number: u32, data: ExtFuncData, loc: &Location) -> Result<()> {
|
fn add_fn(&mut self, number: u32, data: ExtFuncData, loc: &Location) -> Result<()> {
|
||||||
self.map.def_fn(number,
|
self.map
|
||||||
self.function
|
.def_fn(number, self.function.dfg.ext_funcs.push(data), loc)
|
||||||
.dfg
|
|
||||||
.ext_funcs
|
|
||||||
.push(data),
|
|
||||||
loc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a reference to a function.
|
// Resolve a reference to a function.
|
||||||
@@ -159,7 +153,8 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Allocate a new jump table and add a mapping number -> JumpTable.
|
// Allocate a new jump table and add a mapping number -> JumpTable.
|
||||||
fn add_jt(&mut self, number: u32, data: JumpTableData, loc: &Location) -> Result<()> {
|
fn add_jt(&mut self, number: u32, data: JumpTableData, loc: &Location) -> Result<()> {
|
||||||
self.map.def_jt(number, self.function.jump_tables.push(data), loc)
|
self.map
|
||||||
|
.def_jt(number, self.function.jump_tables.push(data), loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a reference to a jump table.
|
// Resolve a reference to a jump table.
|
||||||
@@ -238,19 +233,34 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
InstructionData::MultiAry { ref mut args, .. } => {
|
InstructionData::MultiAry { ref mut args, .. } => {
|
||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
self.map
|
||||||
|
.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionData::Jump { ref mut destination, ref mut args, .. } |
|
InstructionData::Jump {
|
||||||
InstructionData::Branch { ref mut destination, ref mut args, .. } |
|
ref mut destination,
|
||||||
InstructionData::BranchIcmp { ref mut destination, ref mut args, .. } => {
|
ref mut args,
|
||||||
|
..
|
||||||
|
} |
|
||||||
|
InstructionData::Branch {
|
||||||
|
ref mut destination,
|
||||||
|
ref mut args,
|
||||||
|
..
|
||||||
|
} |
|
||||||
|
InstructionData::BranchIcmp {
|
||||||
|
ref mut destination,
|
||||||
|
ref mut args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
self.map.rewrite_ebb(destination, loc)?;
|
self.map.rewrite_ebb(destination, loc)?;
|
||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
self.map
|
||||||
|
.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionData::Call { ref mut args, .. } |
|
InstructionData::Call { ref mut args, .. } |
|
||||||
InstructionData::IndirectCall { ref mut args, .. } => {
|
InstructionData::IndirectCall { ref mut args, .. } => {
|
||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
self.map
|
||||||
|
.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,7 +317,8 @@ impl<'a> Parser<'a> {
|
|||||||
Token::Comment(text) => {
|
Token::Comment(text) => {
|
||||||
// Gather comments, associate them with `comment_entity`.
|
// Gather comments, associate them with `comment_entity`.
|
||||||
if let Some(entity) = self.comment_entity {
|
if let Some(entity) = self.comment_entity {
|
||||||
self.comments.push(Comment {
|
self.comments
|
||||||
|
.push(Comment {
|
||||||
entity: entity,
|
entity: entity,
|
||||||
text: text,
|
text: text,
|
||||||
});
|
});
|
||||||
@@ -464,7 +475,8 @@ impl<'a> Parser<'a> {
|
|||||||
self.consume();
|
self.consume();
|
||||||
// Lexer just gives us raw text that looks like an integer.
|
// Lexer just gives us raw text that looks like an integer.
|
||||||
// Parse it as a u8 to check for overflow and other issues.
|
// Parse it as a u8 to check for overflow and other issues.
|
||||||
text.parse().map_err(|_| self.error("expected u8 decimal immediate"))
|
text.parse()
|
||||||
|
.map_err(|_| self.error("expected u8 decimal immediate"))
|
||||||
} else {
|
} else {
|
||||||
err!(self.loc, err_msg)
|
err!(self.loc, err_msg)
|
||||||
}
|
}
|
||||||
@@ -477,7 +489,8 @@ impl<'a> Parser<'a> {
|
|||||||
self.consume();
|
self.consume();
|
||||||
// Lexer just gives us raw text that looks like an integer.
|
// Lexer just gives us raw text that looks like an integer.
|
||||||
// Parse it as a u32 to check for overflow and other issues.
|
// Parse it as a u32 to check for overflow and other issues.
|
||||||
text.parse().map_err(|_| self.error("expected u32 decimal immediate"))
|
text.parse()
|
||||||
|
.map_err(|_| self.error("expected u32 decimal immediate"))
|
||||||
} else {
|
} else {
|
||||||
err!(self.loc, err_msg)
|
err!(self.loc, err_msg)
|
||||||
}
|
}
|
||||||
@@ -714,7 +727,9 @@ impl<'a> Parser<'a> {
|
|||||||
sig.return_types = self.parse_argument_list(unique_isa)?;
|
sig.return_types = self.parse_argument_list(unique_isa)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if sig.argument_types.iter().all(|a| a.location.is_assigned()) {
|
if sig.argument_types
|
||||||
|
.iter()
|
||||||
|
.all(|a| a.location.is_assigned()) {
|
||||||
sig.compute_argument_bytes();
|
sig.compute_argument_bytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,35 +831,23 @@ impl<'a> Parser<'a> {
|
|||||||
match self.token() {
|
match self.token() {
|
||||||
Some(Token::StackSlot(..)) => {
|
Some(Token::StackSlot(..)) => {
|
||||||
self.gather_comments(ctx.function.stack_slots.next_key());
|
self.gather_comments(ctx.function.stack_slots.next_key());
|
||||||
self.parse_stack_slot_decl().and_then(|(num, dat)| {
|
self.parse_stack_slot_decl()
|
||||||
ctx.add_ss(num, dat, &self.loc)
|
.and_then(|(num, dat)| ctx.add_ss(num, dat, &self.loc))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Some(Token::SigRef(..)) => {
|
Some(Token::SigRef(..)) => {
|
||||||
self.gather_comments(ctx.function
|
self.gather_comments(ctx.function.dfg.signatures.next_key());
|
||||||
.dfg
|
self.parse_signature_decl(ctx.unique_isa)
|
||||||
.signatures
|
.and_then(|(num, dat)| ctx.add_sig(num, dat, &self.loc))
|
||||||
.next_key());
|
|
||||||
self.parse_signature_decl(ctx.unique_isa).and_then(|(num, dat)| {
|
|
||||||
ctx.add_sig(num,
|
|
||||||
dat,
|
|
||||||
&self.loc)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Some(Token::FuncRef(..)) => {
|
Some(Token::FuncRef(..)) => {
|
||||||
self.gather_comments(ctx.function
|
self.gather_comments(ctx.function.dfg.ext_funcs.next_key());
|
||||||
.dfg
|
self.parse_function_decl(ctx)
|
||||||
.ext_funcs
|
.and_then(|(num, dat)| ctx.add_fn(num, dat, &self.loc))
|
||||||
.next_key());
|
|
||||||
self.parse_function_decl(ctx).and_then(|(num, dat)| {
|
|
||||||
ctx.add_fn(num, dat, &self.loc)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Some(Token::JumpTable(..)) => {
|
Some(Token::JumpTable(..)) => {
|
||||||
self.gather_comments(ctx.function.jump_tables.next_key());
|
self.gather_comments(ctx.function.jump_tables.next_key());
|
||||||
self.parse_jump_table_decl().and_then(|(num, dat)| {
|
self.parse_jump_table_decl()
|
||||||
ctx.add_jt(num, dat, &self.loc)
|
.and_then(|(num, dat)| ctx.add_jt(num, dat, &self.loc))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// More to come..
|
// More to come..
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
@@ -861,7 +864,8 @@ impl<'a> Parser<'a> {
|
|||||||
self.match_identifier("stack_slot", "expected 'stack_slot'")?;
|
self.match_identifier("stack_slot", "expected 'stack_slot'")?;
|
||||||
|
|
||||||
// stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" * Bytes {"," stack-slot-flag}
|
// stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" * Bytes {"," stack-slot-flag}
|
||||||
let bytes: i64 = self.match_imm64("expected byte-size in stack_slot decl")?.into();
|
let bytes: i64 = self.match_imm64("expected byte-size in stack_slot decl")?
|
||||||
|
.into();
|
||||||
if bytes < 0 {
|
if bytes < 0 {
|
||||||
return err!(self.loc, "negative stack slot size");
|
return err!(self.loc, "negative stack slot size");
|
||||||
}
|
}
|
||||||
@@ -903,11 +907,10 @@ impl<'a> Parser<'a> {
|
|||||||
let data = match self.token() {
|
let data = match self.token() {
|
||||||
Some(Token::Identifier("function")) => {
|
Some(Token::Identifier("function")) => {
|
||||||
let (loc, name, sig) = self.parse_function_spec(ctx.unique_isa)?;
|
let (loc, name, sig) = self.parse_function_spec(ctx.unique_isa)?;
|
||||||
let sigref = ctx.function
|
let sigref = ctx.function.dfg.signatures.push(sig);
|
||||||
.dfg
|
ctx.map
|
||||||
.signatures
|
.def_entity(sigref.into(), &loc)
|
||||||
.push(sig);
|
.expect("duplicate SigRef entities created");
|
||||||
ctx.map.def_entity(sigref.into(), &loc).expect("duplicate SigRef entities created");
|
|
||||||
ExtFuncData {
|
ExtFuncData {
|
||||||
name: name,
|
name: name,
|
||||||
signature: sigref,
|
signature: sigref,
|
||||||
@@ -1224,7 +1227,9 @@ impl<'a> Parser<'a> {
|
|||||||
let inst = ctx.function.dfg.make_inst(inst_data);
|
let inst = ctx.function.dfg.make_inst(inst_data);
|
||||||
let num_results = ctx.function.dfg.make_inst_results(inst, ctrl_typevar);
|
let num_results = ctx.function.dfg.make_inst_results(inst, ctrl_typevar);
|
||||||
ctx.function.layout.append_inst(inst, ebb);
|
ctx.function.layout.append_inst(inst, ebb);
|
||||||
ctx.map.def_entity(inst.into(), &opcode_loc).expect("duplicate inst references created");
|
ctx.map
|
||||||
|
.def_entity(inst.into(), &opcode_loc)
|
||||||
|
.expect("duplicate inst references created");
|
||||||
|
|
||||||
if let Some(encoding) = encoding {
|
if let Some(encoding) = encoding {
|
||||||
*ctx.function.encodings.ensure(inst) = encoding;
|
*ctx.function.encodings.ensure(inst) = encoding;
|
||||||
@@ -1282,16 +1287,19 @@ impl<'a> Parser<'a> {
|
|||||||
inst_data: &InstructionData)
|
inst_data: &InstructionData)
|
||||||
-> Result<Type> {
|
-> Result<Type> {
|
||||||
let constraints = opcode.constraints();
|
let constraints = opcode.constraints();
|
||||||
let ctrl_type = match explicit_ctrl_type {
|
let ctrl_type =
|
||||||
|
match explicit_ctrl_type {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => {
|
None => {
|
||||||
if constraints.use_typevar_operand() {
|
if constraints.use_typevar_operand() {
|
||||||
// This is an opcode that supports type inference, AND there was no explicit
|
// This is an opcode that supports type inference, AND there was no
|
||||||
// type specified. Look up `ctrl_value` to see if it was defined already.
|
// explicit type specified. Look up `ctrl_value` to see if it was defined
|
||||||
// TBD: If it is defined in another block, the type should have been specified
|
// already.
|
||||||
// explicitly. It is unfortunate that the correctness of IL depends on the
|
// TBD: If it is defined in another block, the type should have been
|
||||||
// layout of the blocks.
|
// specified explicitly. It is unfortunate that the correctness of IL
|
||||||
let ctrl_src_value = inst_data.typevar_operand(&ctx.function.dfg.value_lists)
|
// depends on the layout of the blocks.
|
||||||
|
let ctrl_src_value = inst_data
|
||||||
|
.typevar_operand(&ctx.function.dfg.value_lists)
|
||||||
.expect("Constraints <-> Format inconsistency");
|
.expect("Constraints <-> Format inconsistency");
|
||||||
ctx.function.dfg.value_type(match ctx.map.get_value(ctrl_src_value) {
|
ctx.function.dfg.value_type(match ctx.map.get_value(ctrl_src_value) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
@@ -1309,8 +1317,8 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if constraints.is_polymorphic() {
|
} else if constraints.is_polymorphic() {
|
||||||
// This opcode does not support type inference, so the explicit type variable
|
// This opcode does not support type inference, so the explicit type
|
||||||
// is required.
|
// variable is required.
|
||||||
return err!(self.loc,
|
return err!(self.loc,
|
||||||
"type variable required for polymorphic opcode, e.g. '{}.{}'",
|
"type variable required for polymorphic opcode, e.g. '{}.{}'",
|
||||||
opcode,
|
opcode,
|
||||||
@@ -1629,7 +1637,8 @@ impl<'a> Parser<'a> {
|
|||||||
InstructionFormat::BranchTable => {
|
InstructionFormat::BranchTable => {
|
||||||
let arg = self.match_value("expected SSA value operand")?;
|
let arg = self.match_value("expected SSA value operand")?;
|
||||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
let table = self.match_jt().and_then(|num| ctx.get_jt(num, &self.loc))?;
|
let table = self.match_jt()
|
||||||
|
.and_then(|num| ctx.get_jt(num, &self.loc))?;
|
||||||
InstructionData::BranchTable {
|
InstructionData::BranchTable {
|
||||||
opcode: opcode,
|
opcode: opcode,
|
||||||
ty: VOID,
|
ty: VOID,
|
||||||
@@ -1679,7 +1688,8 @@ mod tests {
|
|||||||
assert_eq!(v4.to_string(), "v0");
|
assert_eq!(v4.to_string(), "v0");
|
||||||
let vx3 = details.map.lookup_str("vx3").unwrap();
|
let vx3 = details.map.lookup_str("vx3").unwrap();
|
||||||
assert_eq!(vx3.to_string(), "vx0");
|
assert_eq!(vx3.to_string(), "vx0");
|
||||||
let aliased_to = func.dfg.resolve_aliases(Value::table_with_number(0).unwrap());
|
let aliased_to = func.dfg
|
||||||
|
.resolve_aliases(Value::table_with_number(0).unwrap());
|
||||||
assert_eq!(aliased_to.to_string(), "v0");
|
assert_eq!(aliased_to.to_string(), "v0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1696,11 +1706,20 @@ mod tests {
|
|||||||
"(i8 uext inreg, f32, f64) -> i32 sext, f64");
|
"(i8 uext inreg, f32, f64) -> i32 sext, f64");
|
||||||
|
|
||||||
// `void` is not recognized as a type by the lexer. It should not appear in files.
|
// `void` is not recognized as a type by the lexer. It should not appear in files.
|
||||||
assert_eq!(Parser::new("() -> void").parse_signature(None).unwrap_err().to_string(),
|
assert_eq!(Parser::new("() -> void")
|
||||||
|
.parse_signature(None)
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
"1: expected argument type");
|
"1: expected argument type");
|
||||||
assert_eq!(Parser::new("i8 -> i8").parse_signature(None).unwrap_err().to_string(),
|
assert_eq!(Parser::new("i8 -> i8")
|
||||||
|
.parse_signature(None)
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
"1: expected function signature: ( args... )");
|
"1: expected function signature: ( args... )");
|
||||||
assert_eq!(Parser::new("(i8 -> i8").parse_signature(None).unwrap_err().to_string(),
|
assert_eq!(Parser::new("(i8 -> i8")
|
||||||
|
.parse_signature(None)
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
"1: expected ')' after function arguments");
|
"1: expected ')' after function arguments");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ impl<'a> TestCommand<'a> {
|
|||||||
let cmd = parts.next().unwrap_or("");
|
let cmd = parts.next().unwrap_or("");
|
||||||
TestCommand {
|
TestCommand {
|
||||||
command: cmd,
|
command: cmd,
|
||||||
options: parts.filter(|s| !s.is_empty()).map(TestOption::new).collect(),
|
options: parts
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.map(TestOption::new)
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user