From 81c126619b3386f1ba3364e1ede499300516e34f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 26 Feb 2018 15:24:25 -0800 Subject: [PATCH 01/58] Ignore unknown custom wasm sections. --- lib/wasm/src/module_translator.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/wasm/src/module_translator.rs b/lib/wasm/src/module_translator.rs index d75f60a35e..d50031fb04 100644 --- a/lib/wasm/src/module_translator.rs +++ b/lib/wasm/src/module_translator.rs @@ -124,6 +124,10 @@ pub fn translate_module<'data>( } } } + ParserState::BeginSection { code: SectionCode::Custom { .. }, .. } => { + // Ignore unknown custom sections. + next_input = ParserInput::SkipSection; + } _ => return Err(String::from("wrong content in the preamble")), }; } From 0e22c740858ae18f4a213ba8306108fdd00264b0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 26 Feb 2018 14:12:13 -0800 Subject: [PATCH 02/58] Track wasm reachability explicitly. Maintain an explicit "reachable" flag when decoding wasm. Push placeholder frames on the control-flow stack instead of just maintaining a count of the stack depth in unreachable code, so that we can whether If blocks have Elses, and whether block exits are branched to, in all contexts. Fixes #217. --- cranelift/wasmtests/unreachable_code.wat | 77 +++++++++ lib/wasm/src/code_translator.rs | 206 ++++++++++++----------- lib/wasm/src/func_translator.rs | 8 +- lib/wasm/src/state.rs | 49 +++--- 4 files changed, 211 insertions(+), 129 deletions(-) create mode 100644 cranelift/wasmtests/unreachable_code.wat diff --git a/cranelift/wasmtests/unreachable_code.wat b/cranelift/wasmtests/unreachable_code.wat new file mode 100644 index 0000000000..38c1a315ce --- /dev/null +++ b/cranelift/wasmtests/unreachable_code.wat @@ -0,0 +1,77 @@ +(module + (type (;0;) (func (param i32 i64 f64) (result f64))) + (type (;1;) (func)) + (type (;2;) (func (result f32))) + (type (;3;) (func (result f64))) + (type (;4;) (func (param f64 f64) (result f64))) + (type (;5;) (func (result i32))) + (func (result i32) + block (result i32) + unreachable + end + block + end + i32.clz + ) + (func (result i32) + loop (result i32) + unreachable + end + block + end + i32.clz + ) + (func (;0;) (type 5) (result i32) + nop + block (result i32) ;; label = @1 + block ;; label = @2 + block ;; label = @3 + nop + block ;; label = @4 + i32.const 1 + if ;; label = @5 + nop + block ;; label = @6 + nop + nop + loop (result i32) ;; label = @7 + nop + block (result i32) ;; label = @8 + nop + nop + block (result i32) ;; label = @9 + nop + unreachable + end + end + end + block (result i32) ;; label = @7 + block ;; label = @8 + nop + end + i32.const 0 + end + br_if 5 (;@1;) + drop + end + else + nop + end + nop + end + end + end + unreachable + end) + (func + block (result i32) + block (result i32) + i32.const 1 + br 1 + end + end + drop + ) + (table (;0;) 16 anyfunc) + (elem (i32.const 0)) +) diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 903b9cd9e1..abaaaef3bf 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -25,6 +25,7 @@ use cretonne::ir::{self, InstBuilder, MemFlags, JumpTableData}; use cretonne::ir::types::*; use cretonne::ir::condcodes::{IntCC, FloatCC}; +use cretonne::packed_option::ReservedValue; use cton_frontend::FunctionBuilder; use wasmparser::{Operator, MemoryImmediate}; use translation_utils::{f32_translation, f64_translation, type_to_type, num_return_values, Local}; @@ -42,7 +43,7 @@ pub fn translate_operator( state: &mut TranslationState, environ: &mut FE, ) { - if state.in_unreachable_code() { + if !state.reachable { return translate_unreachable_operator(op, builder, state); } @@ -105,7 +106,7 @@ pub fn translate_operator( // We use `trap user0` to indicate a user-generated trap. // We could make the trap code configurable if need be. builder.ins().trap(ir::TrapCode::User(0)); - state.real_unreachable_stack_depth = 1; + state.reachable = false; } /***************************** Control flow blocks ********************************** * When starting a control flow block, we create a new `Ebb` that will hold the code @@ -155,15 +156,24 @@ pub fn translate_operator( // and push a new control frame with a new ebb for the code after the if/then/else // At the end of the then clause we jump to the destination let i = state.control_stack.len() - 1; - let (destination, return_count, branch_inst) = match state.control_stack[i] { - ControlStackFrame::If { - destination, - num_return_values, - branch_inst, - .. - } => (destination, num_return_values, branch_inst), - _ => panic!("should not happen"), - }; + let (destination, return_count, branch_inst, ref mut reachable_from_top) = + match state.control_stack[i] { + ControlStackFrame::If { + destination, + num_return_values, + branch_inst, + reachable_from_top, + .. + } => ( + destination, + num_return_values, + branch_inst, + reachable_from_top, + ), + _ => panic!("should not happen"), + }; + // The if has an else, so there's no branch to the end from the top. + *reachable_from_top = false; builder.ins().jump(destination, state.peekn(return_count)); state.popn(return_count); // We change the target of the branch instruction @@ -219,7 +229,7 @@ pub fn translate_operator( let (return_count, br_destination) = { let frame = &mut state.control_stack[i]; // We signal that all the code that follows until the next End is unreachable - frame.set_reachable(); + frame.set_branched_to_exit(); let return_count = if frame.is_loop() { 0 } else { @@ -232,7 +242,7 @@ pub fn translate_operator( state.peekn(return_count), ); state.popn(return_count); - state.real_unreachable_stack_depth = 1 + relative_depth as usize; + state.reachable = false; } Operator::BrIf { relative_depth } => { let val = state.pop1(); @@ -241,7 +251,7 @@ pub fn translate_operator( let frame = &mut state.control_stack[i]; // The values returned by the branch are still available for the reachable // code that comes after it - frame.set_reachable(); + frame.set_branched_to_exit(); let return_count = if frame.is_loop() { 0 } else { @@ -277,20 +287,23 @@ pub fn translate_operator( let val = state.pop1(); let mut data = JumpTableData::with_capacity(depths.len()); for depth in depths { - let i = state.control_stack.len() - 1 - (depth as usize); - let frame = &mut state.control_stack[i]; - let ebb = frame.br_destination(); + let ebb = { + let i = state.control_stack.len() - 1 - (depth as usize); + let frame = &mut state.control_stack[i]; + frame.set_branched_to_exit(); + frame.br_destination() + }; data.push_entry(ebb); - frame.set_reachable(); } let jt = builder.create_jump_table(data); builder.ins().br_table(val, jt); - let i = state.control_stack.len() - 1 - (default as usize); - let frame = &mut state.control_stack[i]; - let ebb = frame.br_destination(); + let ebb = { + let i = state.control_stack.len() - 1 - (default as usize); + let frame = &mut state.control_stack[i]; + frame.set_branched_to_exit(); + frame.br_destination() + }; builder.ins().jump(ebb, &[]); - state.real_unreachable_stack_depth = 1 + min_depth as usize; - frame.set_reachable(); } else { // Here we have jump arguments, but Cretonne's br_table doesn't support them // We then proceed to split the edges going out of the br_table @@ -312,29 +325,32 @@ pub fn translate_operator( } let jt = builder.create_jump_table(data); builder.ins().br_table(val, jt); - let default_ebb = state.control_stack[state.control_stack.len() - 1 - - (default as usize)] - .br_destination(); + let default_ebb = { + let i = state.control_stack.len() - 1 - (default as usize); + let frame = &mut state.control_stack[i]; + frame.set_branched_to_exit(); + frame.br_destination() + }; builder.ins().jump(default_ebb, state.peekn(return_count)); for (depth, dest_ebb) in dest_ebb_sequence { builder.switch_to_block(dest_ebb); builder.seal_block(dest_ebb); - let i = state.control_stack.len() - 1 - depth; let real_dest_ebb = { + let i = state.control_stack.len() - 1 - depth; let frame = &mut state.control_stack[i]; - frame.set_reachable(); + frame.set_branched_to_exit(); frame.br_destination() }; builder.ins().jump(real_dest_ebb, state.peekn(return_count)); } state.popn(return_count); - state.real_unreachable_stack_depth = 1 + min_depth as usize; } + state.reachable = false; } Operator::Return => { let (return_count, br_destination) = { let frame = &mut state.control_stack[0]; - frame.set_reachable(); + frame.set_branched_to_exit(); let return_count = frame.num_return_values(); (return_count, frame.br_destination()) }; @@ -347,7 +363,7 @@ pub fn translate_operator( } } state.popn(return_count); - state.real_unreachable_stack_depth = 1; + state.reachable = false; } /************************************ Calls **************************************** * The call instructions pop off their arguments from the stack and append their @@ -900,76 +916,74 @@ fn translate_unreachable_operator( builder: &mut FunctionBuilder, state: &mut TranslationState, ) { - let stack = &mut state.stack; - let control_stack = &mut state.control_stack; - - // We don't translate because the code is unreachable - // Nevertheless we have to record a phantom stack for this code - // to know when the unreachable code ends match *op { - Operator::If { ty: _ } | + Operator::If { ty: _ } => { + // Push a placeholder control stack entry. The if isn't reachable, + // so we don't have any branches anywhere. + state.push_if(ir::Inst::reserved_value(), ir::Ebb::reserved_value(), 0); + } Operator::Loop { ty: _ } | Operator::Block { ty: _ } => { - state.phantom_unreachable_stack_depth += 1; - } - Operator::End => { - if state.phantom_unreachable_stack_depth > 0 { - state.phantom_unreachable_stack_depth -= 1; - } else { - // This End corresponds to a real control stack frame - // We switch to the destination block but we don't insert - // a jump instruction since the code is still unreachable - let frame = control_stack.pop().unwrap(); - - builder.switch_to_block(frame.following_code()); - builder.seal_block(frame.following_code()); - match frame { - // If it is a loop we also have to seal the body loop block - ControlStackFrame::Loop { header, .. } => builder.seal_block(header), - // If it is an if then the code after is reachable again - ControlStackFrame::If { .. } => { - state.real_unreachable_stack_depth = 1; - } - _ => {} - } - if frame.is_reachable() { - state.real_unreachable_stack_depth = 1; - } - // Now we have to split off the stack the values not used - // by unreachable code that hasn't been translated - stack.truncate(frame.original_stack_size()); - // And add the return values of the block but only if the next block is reachble - // (which corresponds to testing if the stack depth is 1) - if state.real_unreachable_stack_depth == 1 { - stack.extend_from_slice(builder.ebb_params(frame.following_code())); - } - state.real_unreachable_stack_depth -= 1; - } + state.push_block(ir::Ebb::reserved_value(), 0); } Operator::Else => { - if state.phantom_unreachable_stack_depth > 0 { - // This is part of a phantom if-then-else, we do nothing - } else { - // Encountering an real else means that the code in the else - // clause is reachable again - let (branch_inst, original_stack_size) = match control_stack[control_stack.len() - - 1] { - ControlStackFrame::If { - branch_inst, - original_stack_size, - .. - } => (branch_inst, original_stack_size), - _ => panic!("should not happen"), - }; - // We change the target of the branch instruction - let else_ebb = builder.create_ebb(); - builder.change_jump_destination(branch_inst, else_ebb); - builder.seal_block(else_ebb); - builder.switch_to_block(else_ebb); - // Now we have to split off the stack the values not used - // by unreachable code that hasn't been translated - stack.truncate(original_stack_size); - state.real_unreachable_stack_depth = 0; + let i = state.control_stack.len() - 1; + match state.control_stack[i] { + ControlStackFrame::If { + branch_inst, + ref mut reachable_from_top, + .. + } => { + if *reachable_from_top { + // We have a branch from the top of the if to the else. + state.reachable = true; + // And because there's an else, there can no longer be a + // branch from the top directly to the end. + *reachable_from_top = false; + + // We change the target of the branch instruction + let else_ebb = builder.create_ebb(); + builder.change_jump_destination(branch_inst, else_ebb); + builder.seal_block(else_ebb); + builder.switch_to_block(else_ebb); + } + } + _ => {} + } + } + Operator::End => { + let stack = &mut state.stack; + let control_stack = &mut state.control_stack; + let frame = control_stack.pop().unwrap(); + + // Now we have to split off the stack the values not used + // by unreachable code that hasn't been translated + stack.truncate(frame.original_stack_size()); + + let reachable_anyway = match frame { + // If it is a loop we also have to seal the body loop block + ControlStackFrame::Loop { header, .. } => { + builder.seal_block(header); + // And loops can't have branches to the end. + false + } + ControlStackFrame::If { reachable_from_top, .. } => { + // A reachable if without an else has a branch from the top + // directly to the bottom. + reachable_from_top + } + // All other control constructs are already handled. + _ => false, + }; + + if frame.exit_is_branched_to() || reachable_anyway { + builder.switch_to_block(frame.following_code()); + builder.seal_block(frame.following_code()); + + // And add the return values of the block but only if the next block is reachble + // (which corresponds to testing if the stack depth is 1) + stack.extend_from_slice(builder.ebb_params(frame.following_code())); + state.reachable = true; } } _ => { diff --git a/lib/wasm/src/func_translator.rs b/lib/wasm/src/func_translator.rs index 5e8555c194..a9389342e9 100644 --- a/lib/wasm/src/func_translator.rs +++ b/lib/wasm/src/func_translator.rs @@ -206,9 +206,11 @@ fn parse_function_body( // // If the exit block is unreachable, it may not have the correct arguments, so we would // generate a return instruction that doesn't match the signature. - debug_assert!(builder.is_pristine()); - if !builder.is_unreachable() { - builder.ins().return_(&state.stack); + if state.reachable { + debug_assert!(builder.is_pristine()); + if !builder.is_unreachable() { + builder.ins().return_(&state.stack); + } } // Discard any remaining values on the stack. Either we just returned them, diff --git a/lib/wasm/src/state.rs b/lib/wasm/src/state.rs index f9f76d2934..1943f31326 100644 --- a/lib/wasm/src/state.rs +++ b/lib/wasm/src/state.rs @@ -25,20 +25,20 @@ pub enum ControlStackFrame { branch_inst: Inst, num_return_values: usize, original_stack_size: usize, - reachable: bool, + exit_is_branched_to: bool, + reachable_from_top: bool, }, Block { destination: Ebb, num_return_values: usize, original_stack_size: usize, - reachable: bool, + exit_is_branched_to: bool, }, Loop { destination: Ebb, header: Ebb, num_return_values: usize, original_stack_size: usize, - reachable: bool, }, } @@ -80,19 +80,21 @@ impl ControlStackFrame { } } - pub fn is_reachable(&self) -> bool { + pub fn exit_is_branched_to(&self) -> bool { match *self { - ControlStackFrame::If { reachable, .. } | - ControlStackFrame::Block { reachable, .. } | - ControlStackFrame::Loop { reachable, .. } => reachable, + ControlStackFrame::If { exit_is_branched_to, .. } | + ControlStackFrame::Block { exit_is_branched_to, .. } => exit_is_branched_to, + ControlStackFrame::Loop { .. } => false, } } - pub fn set_reachable(&mut self) { + pub fn set_branched_to_exit(&mut self) { match *self { - ControlStackFrame::If { ref mut reachable, .. } | - ControlStackFrame::Block { ref mut reachable, .. } | - ControlStackFrame::Loop { ref mut reachable, .. } => *reachable = true, + ControlStackFrame::If { ref mut exit_is_branched_to, .. } | + ControlStackFrame::Block { ref mut exit_is_branched_to, .. } => { + *exit_is_branched_to = true + } + ControlStackFrame::Loop { .. } => {} } } } @@ -105,8 +107,7 @@ impl ControlStackFrame { pub struct TranslationState { pub stack: Vec, pub control_stack: Vec, - pub phantom_unreachable_stack_depth: usize, - pub real_unreachable_stack_depth: usize, + pub reachable: bool, // Map of global variables that have already been created by `FuncEnvironment::make_global`. globals: HashMap, @@ -130,8 +131,7 @@ impl TranslationState { Self { stack: Vec::new(), control_stack: Vec::new(), - phantom_unreachable_stack_depth: 0, - real_unreachable_stack_depth: 0, + reachable: true, globals: HashMap::new(), heaps: HashMap::new(), signatures: HashMap::new(), @@ -142,8 +142,7 @@ impl TranslationState { fn clear(&mut self) { debug_assert!(self.stack.is_empty()); debug_assert!(self.control_stack.is_empty()); - debug_assert_eq!(self.phantom_unreachable_stack_depth, 0); - debug_assert_eq!(self.real_unreachable_stack_depth, 0); + self.reachable = true; self.globals.clear(); self.heaps.clear(); self.signatures.clear(); @@ -219,7 +218,7 @@ impl TranslationState { destination: following_code, original_stack_size: self.stack.len(), num_return_values: num_result_types, - reachable: false, + exit_is_branched_to: false, }); } @@ -230,7 +229,6 @@ impl TranslationState { destination: following_code, original_stack_size: self.stack.len(), num_return_values: num_result_types, - reachable: false, }); } @@ -241,19 +239,10 @@ impl TranslationState { destination: following_code, original_stack_size: self.stack.len(), num_return_values: num_result_types, - reachable: false, + exit_is_branched_to: false, + reachable_from_top: self.reachable, }); } - - /// Test if the translation state is currently in unreachable code. - pub fn in_unreachable_code(&self) -> bool { - if self.real_unreachable_stack_depth > 0 { - true - } else { - debug_assert_eq!(self.phantom_unreachable_stack_depth, 0, "in reachable code"); - false - } - } } /// Methods for handling entity references. From 234d097f656b468492ddc73573e9a18d8ae1839f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 21 Feb 2018 12:43:36 -0800 Subject: [PATCH 03/58] Change `println!("")` to `println!()`. https://rust-lang-nursery.github.io/rust-clippy/v0.0.186/index.html#print_with_newline --- cranelift/src/cat.rs | 4 ++-- cranelift/src/print_cfg.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cranelift/src/cat.rs b/cranelift/src/cat.rs index e8519b10d9..36ae818e2c 100644 --- a/cranelift/src/cat.rs +++ b/cranelift/src/cat.rs @@ -13,7 +13,7 @@ use filetest::subtest::{self, SubTest, Context, Result as STResult}; pub fn run(files: Vec) -> CommandResult { for (i, f) in files.into_iter().enumerate() { if i != 0 { - println!(""); + println!(); } cat_one(f)? } @@ -30,7 +30,7 @@ fn cat_one(filename: String) -> CommandResult { for (idx, func) in items.into_iter().enumerate() { if idx != 0 { - println!(""); + println!(); } print!("{}", func); } diff --git a/cranelift/src/print_cfg.rs b/cranelift/src/print_cfg.rs index f3b958be77..df624993ad 100644 --- a/cranelift/src/print_cfg.rs +++ b/cranelift/src/print_cfg.rs @@ -17,7 +17,7 @@ use utils::read_to_string; pub fn run(files: Vec) -> CommandResult { for (i, f) in files.into_iter().enumerate() { if i != 0 { - println!(""); + println!(); } print_cfg(f)? } @@ -100,7 +100,7 @@ fn print_cfg(filename: String) -> CommandResult { for (idx, func) in items.into_iter().enumerate() { if idx != 0 { - println!(""); + println!(); } print!("{}", CFGPrinter::new(&func)); } From e943d932b90f4d3467a4f14fc064ff2324bbce45 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 21 Feb 2018 12:42:30 -0800 Subject: [PATCH 04/58] Change `match self` to `match *self` to avoid adding `&` to all patterns in a match. https://rust-lang-nursery.github.io/rust-clippy/v0.0.186/index.html#match_ref_pats --- lib/cretonne/src/bforest/node.rs | 60 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/lib/cretonne/src/bforest/node.rs b/lib/cretonne/src/bforest/node.rs index 53a96f9b14..3c6b8c8774 100644 --- a/lib/cretonne/src/bforest/node.rs +++ b/lib/cretonne/src/bforest/node.rs @@ -54,8 +54,8 @@ impl Clone for NodeData { impl NodeData { /// Is this a free/unused node? pub fn is_free(&self) -> bool { - match self { - &NodeData::Free { .. } => true, + match *self { + NodeData::Free { .. } => true, _ => false, } } @@ -65,10 +65,10 @@ impl NodeData { /// This is the number of outgoing edges in an inner node, or the number of key-value pairs in /// a leaf node. pub fn entries(&self) -> usize { - match self { - &NodeData::Inner { size, .. } => usize::from(size) + 1, - &NodeData::Leaf { size, .. } => usize::from(size), - &NodeData::Free { .. } => panic!("freed node"), + match *self { + NodeData::Inner { size, .. } => usize::from(size) + 1, + NodeData::Leaf { size, .. } => usize::from(size), + NodeData::Free { .. } => panic!("freed node"), } } @@ -96,8 +96,8 @@ impl NodeData { /// Unwrap an inner node into two slices (keys, trees). pub fn unwrap_inner(&self) -> (&[F::Key], &[Node]) { - match self { - &NodeData::Inner { + match *self { + NodeData::Inner { size, ref keys, ref tree, @@ -113,8 +113,8 @@ impl NodeData { /// Unwrap a leaf node into two slices (keys, values) of the same length. pub fn unwrap_leaf(&self) -> (&[F::Key], &[F::Value]) { - match self { - &NodeData::Leaf { + match *self { + NodeData::Leaf { size, ref keys, ref vals, @@ -132,8 +132,8 @@ impl NodeData { /// Unwrap a mutable leaf node into two slices (keys, values) of the same length. pub fn unwrap_leaf_mut(&mut self) -> (&mut [F::Key], &mut [F::Value]) { - match self { - &mut NodeData::Leaf { + match *self { + NodeData::Leaf { size, ref mut keys, ref mut vals, @@ -152,8 +152,8 @@ impl NodeData { /// Get the critical key for a leaf node. /// This is simply the first key. pub fn leaf_crit_key(&self) -> F::Key { - match self { - &NodeData::Leaf { size, ref keys, .. } => { + match *self { + NodeData::Leaf { size, ref keys, .. } => { debug_assert!(size > 0, "Empty leaf node"); keys.borrow()[0] } @@ -165,8 +165,8 @@ impl NodeData { /// This means that `key` is inserted at `keys[i]` and `node` is inserted at `tree[i + 1]`. /// If the node is full, this leaves the node unchanged and returns false. pub fn try_inner_insert(&mut self, index: usize, key: F::Key, node: Node) -> bool { - match self { - &mut NodeData::Inner { + match *self { + NodeData::Inner { ref mut size, ref mut keys, ref mut tree, @@ -191,8 +191,8 @@ impl NodeData { /// Try to insert `key, value` at `index` in a leaf node, but fail and return false if the node /// is full. pub fn try_leaf_insert(&mut self, index: usize, key: F::Key, value: F::Value) -> bool { - match self { - &mut NodeData::Leaf { + match *self { + NodeData::Leaf { ref mut size, ref mut keys, ref mut vals, @@ -222,8 +222,8 @@ impl NodeData { /// The `insert_index` parameter is the position where an insertion was tried and failed. The /// node will be split in half with a bias towards an even split after the insertion is retried. pub fn split(&mut self, insert_index: usize) -> SplitOff { - match self { - &mut NodeData::Inner { + match *self { + NodeData::Inner { ref mut size, ref keys, ref tree, @@ -262,7 +262,7 @@ impl NodeData { }, } } - &mut NodeData::Leaf { + NodeData::Leaf { ref mut size, ref keys, ref vals, @@ -307,8 +307,8 @@ impl NodeData { /// /// Return an indication of the node's health (i.e. below half capacity). pub fn inner_remove(&mut self, index: usize) -> Removed { - match self { - &mut NodeData::Inner { + match *self { + NodeData::Inner { ref mut size, ref mut keys, ref mut tree, @@ -332,8 +332,8 @@ impl NodeData { /// /// Return an indication of the node's health (i.e. below half capacity). pub fn leaf_remove(&mut self, index: usize) -> Removed { - match self { - &mut NodeData::Leaf { + match *self { + NodeData::Leaf { ref mut size, ref mut keys, ref mut vals, @@ -553,15 +553,15 @@ where F::Value: ValDisp, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &NodeData::Inner { size, keys, tree } => { + match *self { + NodeData::Inner { size, keys, tree } => { write!(f, "[ {}", tree[0])?; for i in 0..usize::from(size) { write!(f, " {} {}", keys[i], tree[i + 1])?; } write!(f, " ]") } - &NodeData::Leaf { size, keys, vals } => { + NodeData::Leaf { size, keys, vals } => { let keys = keys.borrow(); let vals = vals.borrow(); write!(f, "[")?; @@ -571,8 +571,8 @@ where } write!(f, " ]") } - &NodeData::Free { next: Some(n) } => write!(f, "[ free -> {} ]", n), - &NodeData::Free { next: None } => write!(f, "[ free ]"), + NodeData::Free { next: Some(n) } => write!(f, "[ free -> {} ]", n), + NodeData::Free { next: None } => write!(f, "[ free ]"), } } } From af154655d7e45c7756a06b5920f405f27f1b96ee Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 21 Feb 2018 12:35:37 -0800 Subject: [PATCH 05/58] Replace `as` casts with type-conversion functions. https://github.com/rust-lang-nursery/rust-clippy/wiki#cast_lossless --- lib/cretonne/src/isa/intel/abi.rs | 2 +- lib/cretonne/src/isa/riscv/binemit.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cretonne/src/isa/intel/abi.rs b/lib/cretonne/src/isa/intel/abi.rs index 21442250c5..cf30058364 100644 --- a/lib/cretonne/src/isa/intel/abi.rs +++ b/lib/cretonne/src/isa/intel/abi.rs @@ -224,7 +224,7 @@ pub fn native_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> res }); let total_stack_size = layout_stack(&mut func.stack_slots, stack_align)? as i32; - let local_stack_size = (total_stack_size - csr_stack_size) as i64; + let local_stack_size = i64::from(total_stack_size - csr_stack_size); // Add CSRs to function signature let fp_arg = ir::AbiParam::special_reg( diff --git a/lib/cretonne/src/isa/riscv/binemit.rs b/lib/cretonne/src/isa/riscv/binemit.rs index c20b1ca004..369a9bf09f 100644 --- a/lib/cretonne/src/isa/riscv/binemit.rs +++ b/lib/cretonne/src/isa/riscv/binemit.rs @@ -106,7 +106,7 @@ fn put_i(bits: u16, rs1: RegUnit, imm: i64, rd: RegUnit, /// /// Encoding bits: `opcode[6:2] | (funct3 << 5)` fn put_u(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS) { - let bits = bits as u32; + let bits = u32::from(bits); let opcode5 = bits & 0x1f; let rd = u32::from(rd) & 0x1f; From 6a962e8b2c6359bd8d9f6eb66565da2d39138731 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Feb 2018 15:26:06 -0800 Subject: [PATCH 06/58] Add links to the docs.rs API documentation. --- cranelift/docs/index.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cranelift/docs/index.rst b/cranelift/docs/index.rst index 301346a9e3..c2e5b6d5ef 100644 --- a/cranelift/docs/index.rst +++ b/cranelift/docs/index.rst @@ -12,6 +12,28 @@ Contents: regalloc compare-llvm +Rust Crate Documentation +======================== + +`cretonne `_ + This is the core code generator crate. It takes Cretonne IR as input + and emits encoded machine instructions, along with symbolic relocations, + as output. + +`cretonne-wasm `_ + This crate translates WebAssembly code into Cretonne IR. + +`cretonne-frontend `_ + This crate provides utilities for translating code into Cretonne IR. + +`cretonne-native `_ + This crate performs auto-detection of the host, allowing Cretonne to + generate code optimized for the machine it's running on. + +`cretonne-reader `_ + This crate translates from Cretonne IR's text format into Cretonne IR + in in-memory data structures. + Indices and tables ================== From d394ae0902824bcfd27b83dd97d5550465c08ee0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Feb 2018 15:32:21 -0800 Subject: [PATCH 07/58] Enable "set -euo pipefail" in all bash scripts. This enables "set -e", "set -u", and "set -o pipefail", which catch common errors. --- check-rustfmt.sh | 3 ++- cranelift/format-all.sh | 4 +--- cranelift/publish-all.sh | 2 +- cranelift/test-all.sh | 4 +--- lib/cretonne/meta/check.sh | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/check-rustfmt.sh b/check-rustfmt.sh index 483a45396a..1983493342 100755 --- a/check-rustfmt.sh +++ b/check-rustfmt.sh @@ -1,5 +1,6 @@ #!/bin/bash -# +set -euo pipefail + # Usage: check-rustfmt.sh [--install] # # Check that the desired version of rustfmt is installed. diff --git a/cranelift/format-all.sh b/cranelift/format-all.sh index 6ccac9c289..a99da7db09 100755 --- a/cranelift/format-all.sh +++ b/cranelift/format-all.sh @@ -1,10 +1,8 @@ #!/bin/bash +set -euo pipefail # Format all sources using rustfmt. -# Exit immediately on errors. -set -e - cd $(dirname "$0") # Make sure we can find rustfmt. diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index 144aa519f8..1826287213 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -e +set -euo pipefail cd $(dirname "$0") topdir=$(pwd) diff --git a/cranelift/test-all.sh b/cranelift/test-all.sh index a5b2e92066..6a9291d82e 100755 --- a/cranelift/test-all.sh +++ b/cranelift/test-all.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail # This is the top-level test script: # @@ -10,9 +11,6 @@ # # All tests run by this script should be passing at all times. -# Exit immediately on errors. -set -e - # Repository top-level directory. cd $(dirname "$0") topdir=$(pwd) diff --git a/lib/cretonne/meta/check.sh b/lib/cretonne/meta/check.sh index 655092e6cb..aa0e88ce1e 100755 --- a/lib/cretonne/meta/check.sh +++ b/lib/cretonne/meta/check.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -e +set -euo pipefail cd $(dirname "$0") runif() { From 111c97df6dcdcd8e5ef52322ac43aa7c526a292c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Feb 2018 15:43:13 -0800 Subject: [PATCH 08/58] Quote shell variables in more places. --- cranelift/publish-all.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index 1826287213..b914a46394 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -1,7 +1,7 @@ #!/bin/bash set -euo pipefail cd $(dirname "$0") -topdir=$(pwd) +topdir="$(pwd)" # All the cretonne-* crates have the same version number # The filecheck crate version is managed independently. @@ -16,9 +16,9 @@ for crate in . lib/*; do continue fi # Update the version number of this crate to $version. - sed -i "" -e "s/^version = .*/version = \"$version\"/" $crate/Cargo.toml + sed -i "" -e "s/^version = .*/version = \"$version\"/" "$crate/Cargo.toml" # Update the required version number of any cretonne* dependencies. - sed -i "" -e "/^cretonne/s/version = \"[^\"]*\"/version = \"$version\"/" $crate/Cargo.toml + sed -i "" -e "/^cretonne/s/version = \"[^\"]*\"/version = \"$version\"/" "$crate/Cargo.toml" done # Update our local Cargo.lock (not checked in). @@ -30,5 +30,5 @@ cargo update # Note that libraries need to be published in topological order. for crate in filecheck cretonne frontend native reader wasm; do - echo cargo publish --manifest-path lib/$crate/Cargo.toml + echo cargo publish --manifest-path "lib/$crate/Cargo.toml" done From 42a0c2e44b6297bedfea568781409fdd19744773 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Feb 2018 15:44:36 -0800 Subject: [PATCH 09/58] Enable backup files when setting version numbers with sed. --- cranelift/publish-all.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index b914a46394..91e6be826a 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -16,9 +16,9 @@ for crate in . lib/*; do continue fi # Update the version number of this crate to $version. - sed -i "" -e "s/^version = .*/version = \"$version\"/" "$crate/Cargo.toml" + sed -i.bk -e "s/^version = .*/version = \"$version\"/" "$crate/Cargo.toml" # Update the required version number of any cretonne* dependencies. - sed -i "" -e "/^cretonne/s/version = \"[^\"]*\"/version = \"$version\"/" "$crate/Cargo.toml" + sed -i.bk -e "/^cretonne/s/version = \"[^\"]*\"/version = \"$version\"/" "$crate/Cargo.toml" done # Update our local Cargo.lock (not checked in). From 2addcfd6429531d6f212ffe5287f61cfda5ff485 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Feb 2018 15:54:29 -0800 Subject: [PATCH 10/58] Make publish-all.sh print the git commit line too, for convenience. --- cranelift/publish-all.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index 91e6be826a..607a39d37e 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -29,6 +29,7 @@ cargo update # # Note that libraries need to be published in topological order. +echo git commit -a -m "Bump version to $version" for crate in filecheck cretonne frontend native reader wasm; do echo cargo publish --manifest-path "lib/$crate/Cargo.toml" done From dabfc55c6ecdad4380c07987a8db4f43a19a9b6c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Feb 2018 15:58:25 -0800 Subject: [PATCH 11/58] Bump version to 0.2.0 --- cranelift/Cargo.toml | 12 ++++++------ cranelift/publish-all.sh | 4 ++-- lib/cretonne/Cargo.toml | 2 +- lib/frontend/Cargo.toml | 4 ++-- lib/native/Cargo.toml | 4 ++-- lib/reader/Cargo.toml | 4 ++-- lib/wasm/Cargo.toml | 6 +++--- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index 91fe8ed6d9..20ec002a29 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cretonne-tools" authors = ["The Cretonne Project Developers"] -version = "0.1.0" +version = "0.2.0" description = "Binaries for testing the Cretonne library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -13,11 +13,11 @@ name = "cton-util" path = "src/cton-util.rs" [dependencies] -cretonne = { path = "lib/cretonne", version = "0.1.0" } -cretonne-reader = { path = "lib/reader", version = "0.1.0" } -cretonne-frontend = { path = "lib/frontend", version = "0.1.0" } -cretonne-wasm = { path = "lib/wasm", version = "0.1.0" } -cretonne-native = { path = "lib/native", version = "0.1.0" } +cretonne = { path = "lib/cretonne", version = "0.2.0" } +cretonne-reader = { path = "lib/reader", version = "0.2.0" } +cretonne-frontend = { path = "lib/frontend", version = "0.2.0" } +cretonne-wasm = { path = "lib/wasm", version = "0.2.0" } +cretonne-native = { path = "lib/native", version = "0.2.0" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index 607a39d37e..5844621079 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -5,7 +5,7 @@ topdir="$(pwd)" # All the cretonne-* crates have the same version number # The filecheck crate version is managed independently. -version="0.1.0" +version="0.2.0" # Update all of the Cargo.toml files. # @@ -29,7 +29,7 @@ cargo update # # Note that libraries need to be published in topological order. -echo git commit -a -m "Bump version to $version" +echo git commit -a -m "\"Bump version to $version"\" for crate in filecheck cretonne frontend native reader wasm; do echo cargo publish --manifest-path "lib/$crate/Cargo.toml" done diff --git a/lib/cretonne/Cargo.toml b/lib/cretonne/Cargo.toml index 88ae712c07..e9bed118cd 100644 --- a/lib/cretonne/Cargo.toml +++ b/lib/cretonne/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne" -version = "0.1.0" +version = "0.2.0" description = "Low-level code generator library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" diff --git a/lib/frontend/Cargo.toml b/lib/frontend/Cargo.toml index eea524ef1c..7ef8cee698 100644 --- a/lib/frontend/Cargo.toml +++ b/lib/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-frontend" -version = "0.1.0" +version = "0.2.0" description = "Cretonne IL builder helper" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_frontend" [dependencies] -cretonne = { path = "../cretonne", version = "0.1.0" } +cretonne = { path = "../cretonne", version = "0.2.0" } diff --git a/lib/native/Cargo.toml b/lib/native/Cargo.toml index 13556b0eb0..4401e41aab 100644 --- a/lib/native/Cargo.toml +++ b/lib/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-native" -version = "0.1.0" +version = "0.2.0" authors = ["The Cretonne Project Developers"] description = "Support for targeting the host with Cretonne" repository = "https://github.com/Cretonne/cretonne" @@ -11,7 +11,7 @@ readme = "README.md" name = "cton_native" [dependencies] -cretonne = { path = "../cretonne", version = "0.1.0" } +cretonne = { path = "../cretonne", version = "0.2.0" } [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies] raw-cpuid = "3.0.0" diff --git a/lib/reader/Cargo.toml b/lib/reader/Cargo.toml index 636f6b40e0..a732eda896 100644 --- a/lib/reader/Cargo.toml +++ b/lib/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-reader" -version = "0.1.0" +version = "0.2.0" description = "Cretonne textual IL reader" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_reader" [dependencies] -cretonne = { path = "../cretonne", version = "0.1.0" } +cretonne = { path = "../cretonne", version = "0.2.0" } diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index 2bad5f8d2a..efd4641a0e 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-wasm" -version = "0.1.0" +version = "0.2.0" authors = ["The Cretonne Project Developers"] description = "Translator from WebAssembly to Cretonne IL" repository = "https://github.com/Cretonne/cretonne" @@ -12,8 +12,8 @@ name = "cton_wasm" [dependencies] wasmparser = "0.14.1" -cretonne = { path = "../cretonne", version = "0.1.0" } -cretonne-frontend = { path = "../frontend", version = "0.1.0" } +cretonne = { path = "../cretonne", version = "0.2.0" } +cretonne-frontend = { path = "../frontend", version = "0.2.0" } [dev-dependencies] tempdir = "0.3.5" From 7dda61543f0769254f141daada24b69792bb1ba2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Feb 2018 17:01:02 -0800 Subject: [PATCH 12/58] Add more release instructions. --- cranelift/publish-all.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index 5844621079..5cdc281387 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -30,6 +30,9 @@ cargo update # Note that libraries need to be published in topological order. echo git commit -a -m "\"Bump version to $version"\" +echo git push for crate in filecheck cretonne frontend native reader wasm; do echo cargo publish --manifest-path "lib/$crate/Cargo.toml" done +echo +echo Then, go to https://github.com/Cretonne/cretonne/releases/ and define a new release. From 60c6154b9438bcad93991c83ddc5a6e4ca92d2f5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 03:38:48 -0800 Subject: [PATCH 13/58] Add some crate keywords. --- lib/cretonne/Cargo.toml | 1 + lib/wasm/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/cretonne/Cargo.toml b/lib/cretonne/Cargo.toml index e9bed118cd..5dea4bb3f7 100644 --- a/lib/cretonne/Cargo.toml +++ b/lib/cretonne/Cargo.toml @@ -7,6 +7,7 @@ license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" repository = "https://github.com/Cretonne/cretonne" readme = "README.md" +keywords = [ "compile", "compiler", "jit" ] build = "build.rs" [lib] diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index efd4641a0e..cd48f36959 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -6,6 +6,7 @@ description = "Translator from WebAssembly to Cretonne IL" repository = "https://github.com/Cretonne/cretonne" license = "Apache-2.0" readme = "README.md" +keywords = [ "webassembly", "wasm" ] [lib] name = "cton_wasm" From d71756f298d5140e86faa0385be4197a17159a88 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 03:43:55 -0800 Subject: [PATCH 14/58] Delete an extraneous file. --- lib/cretonne/src/isa/riscv/registers.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 lib/cretonne/src/isa/riscv/registers.py diff --git a/lib/cretonne/src/isa/riscv/registers.py b/lib/cretonne/src/isa/riscv/registers.py deleted file mode 100644 index 8b13789179..0000000000 --- a/lib/cretonne/src/isa/riscv/registers.py +++ /dev/null @@ -1 +0,0 @@ - From 9010e576dd047edff867f29c71303c9b4893ec2d Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 10:10:42 -0800 Subject: [PATCH 15/58] Simply instruction result value handling in the parser. Also, move the handling earlier so that they're checked before the rest of the instruction is checked. --- lib/reader/src/parser.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 43c149956a..b4252d4cb9 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -1632,6 +1632,11 @@ impl<'a> Parser<'a> { ctx: &mut Context, ebb: Ebb, ) -> Result<()> { + // Define the result values. + for val in &results { + ctx.map.def_value(*val, &self.loc)?; + } + // Collect comments for the next instruction. self.start_gathering_comments(); @@ -1709,11 +1714,6 @@ impl<'a> Parser<'a> { } } - // Now map the source result values to the just created instruction results. - // Pass a reference to `ctx.values` instead of `ctx` itself since the `Values` iterator - // holds a reference to `ctx.function`. - self.add_values(&mut ctx.map, results.into_iter())?; - if let Some(result_locations) = result_locations { for (&value, loc) in ctx.function.dfg.inst_results(inst).iter().zip( result_locations, @@ -1798,17 +1798,6 @@ impl<'a> Parser<'a> { Ok(ctrl_type) } - // Add mappings for a list of source values to their corresponding new values. - fn add_values(&self, map: &mut SourceMap, new_results: V) -> Result<()> - where - V: Iterator, - { - for val in new_results { - map.def_value(val, &self.loc)?; - } - Ok(()) - } - // Parse comma-separated value list into a VariableArgs struct. // // value_list ::= [ value { "," value } ] From ab9298eafa835b19388538d5aa9b1de87150cca2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 10:12:40 -0800 Subject: [PATCH 16/58] Make the `fst` recipe use the deref-safe register class as well. --- cranelift/filetests/isa/intel/binary64-float.cton | 10 ++++++++++ lib/cretonne/meta/isa/intel/recipes.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cranelift/filetests/isa/intel/binary64-float.cton b/cranelift/filetests/isa/intel/binary64-float.cton index a6f47e53db..69bfeb6c36 100644 --- a/cranelift/filetests/isa/intel/binary64-float.cton +++ b/cranelift/filetests/isa/intel/binary64-float.cton @@ -20,6 +20,7 @@ ebb0: [-,%rsi] v1 = iconst.i32 2 [-,%rax] v2 = iconst.i64 11 [-,%r14] v3 = iconst.i64 12 + [-,%r13] v4 = iconst.i64 13 ; asm: cvtsi2ssl %r11d, %xmm5 [-,%xmm5] v10 = fcvt_from_sint.f32 v0 ; bin: f3 41 0f 2a eb @@ -173,6 +174,10 @@ ebb0: [-] store.f32 v100, v3 ; bin: 66 41 0f 7e 2e ; asm: movd %xmm10, (%rax) [-] store.f32 v101, v2 ; bin: 66 44 0f 7e 10 + ; asm: movd %xmm5, (%r13) + [-] store.f32 v100, v4 ; bin: 66 41 0f 7e 6d 00 + ; asm: movd %xmm10, (%r13) + [-] store.f32 v101, v4 ; bin: 66 45 0f 7e 55 00 ; asm: movd %xmm5, 50(%r14) [-] store.f32 v100, v3+50 ; bin: 66 41 0f 7e 6e 32 ; asm: movd %xmm10, -50(%rax) @@ -250,6 +255,7 @@ ebb0: [-,%rsi] v1 = iconst.i32 2 [-,%rax] v2 = iconst.i64 11 [-,%r14] v3 = iconst.i64 12 + [-,%r13] v4 = iconst.i64 13 ; asm: cvtsi2sdl %r11d, %xmm5 [-,%xmm5] v10 = fcvt_from_sint.f64 v0 ; bin: f2 41 0f 2a eb @@ -403,6 +409,10 @@ ebb0: [-] store.f64 v100, v3 ; bin: 66 41 0f d6 2e ; asm: movq %xmm10, (%rax) [-] store.f64 v101, v2 ; bin: 66 44 0f d6 10 + ; asm: movq %xmm5, (%r13) + [-] store.f64 v100, v4 ; bin: 66 41 0f d6 6d 00 + ; asm: movq %xmm10, (%r13) + [-] store.f64 v101, v4 ; bin: 66 45 0f d6 55 00 ; asm: movq %xmm5, 50(%r14) [-] store.f64 v100, v3+50 ; bin: 66 41 0f d6 6e 32 ; asm: movq %xmm10, -50(%rax) diff --git a/lib/cretonne/meta/isa/intel/recipes.py b/lib/cretonne/meta/isa/intel/recipes.py index 965bc4331d..133837fc78 100644 --- a/lib/cretonne/meta/isa/intel/recipes.py +++ b/lib/cretonne/meta/isa/intel/recipes.py @@ -675,7 +675,7 @@ st_abcd = TailRecipe( # XX /r register-indirect store of FPR with no offset. fst = TailRecipe( - 'fst', Store, size=1, ins=(FPR, GPR), outs=(), + 'fst', Store, size=1, ins=(FPR, GPR_ZERO_DEREF_SAFE), outs=(), instp=IsEqual(Store.offset, 0), clobbers_flags=False, emit=''' From 6fcbb20e10a7e489deda28191ca8fabcc19ea7ce Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 10:31:00 -0800 Subject: [PATCH 17/58] Bump version to 0.3.0 --- cranelift/Cargo.toml | 12 ++++++------ cranelift/publish-all.sh | 2 +- lib/cretonne/Cargo.toml | 2 +- lib/frontend/Cargo.toml | 4 ++-- lib/native/Cargo.toml | 4 ++-- lib/reader/Cargo.toml | 4 ++-- lib/wasm/Cargo.toml | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index 20ec002a29..d762d64d82 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cretonne-tools" authors = ["The Cretonne Project Developers"] -version = "0.2.0" +version = "0.3.0" description = "Binaries for testing the Cretonne library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -13,11 +13,11 @@ name = "cton-util" path = "src/cton-util.rs" [dependencies] -cretonne = { path = "lib/cretonne", version = "0.2.0" } -cretonne-reader = { path = "lib/reader", version = "0.2.0" } -cretonne-frontend = { path = "lib/frontend", version = "0.2.0" } -cretonne-wasm = { path = "lib/wasm", version = "0.2.0" } -cretonne-native = { path = "lib/native", version = "0.2.0" } +cretonne = { path = "lib/cretonne", version = "0.3.0" } +cretonne-reader = { path = "lib/reader", version = "0.3.0" } +cretonne-frontend = { path = "lib/frontend", version = "0.3.0" } +cretonne-wasm = { path = "lib/wasm", version = "0.3.0" } +cretonne-native = { path = "lib/native", version = "0.3.0" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index 5cdc281387..e8b22b4f5d 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -5,7 +5,7 @@ topdir="$(pwd)" # All the cretonne-* crates have the same version number # The filecheck crate version is managed independently. -version="0.2.0" +version="0.3.0" # Update all of the Cargo.toml files. # diff --git a/lib/cretonne/Cargo.toml b/lib/cretonne/Cargo.toml index 5dea4bb3f7..12ddbf2abe 100644 --- a/lib/cretonne/Cargo.toml +++ b/lib/cretonne/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne" -version = "0.2.0" +version = "0.3.0" description = "Low-level code generator library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" diff --git a/lib/frontend/Cargo.toml b/lib/frontend/Cargo.toml index 7ef8cee698..67297562b8 100644 --- a/lib/frontend/Cargo.toml +++ b/lib/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-frontend" -version = "0.2.0" +version = "0.3.0" description = "Cretonne IL builder helper" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_frontend" [dependencies] -cretonne = { path = "../cretonne", version = "0.2.0" } +cretonne = { path = "../cretonne", version = "0.3.0" } diff --git a/lib/native/Cargo.toml b/lib/native/Cargo.toml index 4401e41aab..1fe35a7190 100644 --- a/lib/native/Cargo.toml +++ b/lib/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-native" -version = "0.2.0" +version = "0.3.0" authors = ["The Cretonne Project Developers"] description = "Support for targeting the host with Cretonne" repository = "https://github.com/Cretonne/cretonne" @@ -11,7 +11,7 @@ readme = "README.md" name = "cton_native" [dependencies] -cretonne = { path = "../cretonne", version = "0.2.0" } +cretonne = { path = "../cretonne", version = "0.3.0" } [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies] raw-cpuid = "3.0.0" diff --git a/lib/reader/Cargo.toml b/lib/reader/Cargo.toml index a732eda896..14b721af35 100644 --- a/lib/reader/Cargo.toml +++ b/lib/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-reader" -version = "0.2.0" +version = "0.3.0" description = "Cretonne textual IL reader" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_reader" [dependencies] -cretonne = { path = "../cretonne", version = "0.2.0" } +cretonne = { path = "../cretonne", version = "0.3.0" } diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index cd48f36959..45e153357c 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-wasm" -version = "0.2.0" +version = "0.3.0" authors = ["The Cretonne Project Developers"] description = "Translator from WebAssembly to Cretonne IL" repository = "https://github.com/Cretonne/cretonne" @@ -13,8 +13,8 @@ name = "cton_wasm" [dependencies] wasmparser = "0.14.1" -cretonne = { path = "../cretonne", version = "0.2.0" } -cretonne-frontend = { path = "../frontend", version = "0.2.0" } +cretonne = { path = "../cretonne", version = "0.3.0" } +cretonne-frontend = { path = "../frontend", version = "0.3.0" } [dev-dependencies] tempdir = "0.3.5" From e4b30d328467c3e30976a4a55710b79bd3229bb7 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 28 Feb 2018 14:33:28 -0500 Subject: [PATCH 18/58] fix typo in build.rs --- lib/cretonne/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cretonne/build.rs b/lib/cretonne/build.rs index 4d1b0edf72..d2ef3905b1 100644 --- a/lib/cretonne/build.rs +++ b/lib/cretonne/build.rs @@ -46,7 +46,7 @@ fn main() { let cur_dir = env::current_dir().expect("Can't access current working directory"); let crate_dir = cur_dir.as_path(); - // Make sure we rebuild is this build script changes. + // Make sure we rebuild if this build script changes. // 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. println!( From 7054f25abbb009796e4009661f62fb33c130cdaa Mon Sep 17 00:00:00 2001 From: Julian Seward Date: Wed, 28 Feb 2018 12:28:55 +0100 Subject: [PATCH 19/58] Adds support to transform integer div and rem by constants into cheaper equivalents. Adds support for transforming integer division and remainder by constants into sequences that do not involve division instructions. * div/rem by constant powers of two are turned into right shifts, plus some fixups for the signed cases. * div/rem by constant non-powers of two are turned into double length multiplies by a magic constant, plus some fixups involving shifts, addition and subtraction, that depends on the constant, the word size and the signedness involved. * The following cases are transformed: div and rem, signed or unsigned, 32 or 64 bit. The only un-transformed cases are: unsigned div and rem by zero, signed div and rem by zero or -1. * This is all incorporated within a new transformation pass, "preopt", in lib/cretonne/src/preopt.rs. * In preopt.rs, fn do_preopt() is the main driver. It is designed to be extensible to transformations of other kinds of instructions. Currently it merely uses a helper to identify div/rem transformation candidates and another helper to perform the transformation. * In preopt.rs, fn get_div_info() pattern matches to find candidates, both cases where the second arg is an immediate, and cases where the second arg is an identifier bound to an immediate at its definition point. * In preopt.rs, fn do_divrem_transformation() does the heavy lifting of the transformation proper. It in turn uses magic{S,U}{32,64} to calculate the magic numbers required for the transformations. * There are many test cases for the transformation proper: filetests/preopt/div_by_const_non_power_of_2.cton filetests/preopt/div_by_const_power_of_2.cton filetests/preopt/rem_by_const_non_power_of_2.cton filetests/preopt/rem_by_const_power_of_2.cton filetests/preopt/div_by_const_indirect.cton preopt.rs also contains a set of tests for magic number generation. * The main (non-power-of-2) transformation requires instructions that return the high word of a double-length multiply. For this, instructions umulhi and smulhi have been added to the core instruction set. These will map directly to single instructions on most non-intel targets. * intel does not have an instruction exactly like that. For intel, instructions x86_umulx and x86_smulx have been added. These map to real instructions and return both result words. The intel legaliser will rewrite {s,u}mulhi into x86_{s,u}mulx uses that throw away the lower half word. Tests: filetests/isa/intel/legalize-mulhi.cton (new file) filetests/isa/intel/binary64.cton (added x86_{s,u}mulx encoding tests) --- cranelift/filetests/isa/intel/binary64.cton | 22 + .../filetests/isa/intel/legalize-mulhi.cton | 45 ++ .../preopt/div_by_const_indirect.cton | 60 ++ .../preopt/div_by_const_non_power_of_2.cton | 267 +++++++++ .../preopt/div_by_const_power_of_2.cton | 293 ++++++++++ .../preopt/rem_by_const_non_power_of_2.cton | 286 +++++++++ .../preopt/rem_by_const_power_of_2.cton | 292 ++++++++++ cranelift/src/filetest/mod.rs | 2 + cranelift/src/filetest/preopt.rs | 50 ++ lib/cretonne/meta/base/instructions.py | 20 + lib/cretonne/meta/isa/intel/encodings.py | 3 + lib/cretonne/meta/isa/intel/instructions.py | 22 + lib/cretonne/meta/isa/intel/legalize.py | 17 + lib/cretonne/meta/isa/intel/recipes.py | 9 + lib/cretonne/src/context.rs | 9 + lib/cretonne/src/divconst_magic_numbers.rs | 542 ++++++++++++++++++ lib/cretonne/src/isa/intel/enc_tables.rs | 1 + lib/cretonne/src/lib.rs | 2 + lib/cretonne/src/preopt.rs | 521 +++++++++++++++++ lib/cretonne/src/timing.rs | 1 + 20 files changed, 2464 insertions(+) create mode 100644 cranelift/filetests/isa/intel/legalize-mulhi.cton create mode 100644 cranelift/filetests/preopt/div_by_const_indirect.cton create mode 100644 cranelift/filetests/preopt/div_by_const_non_power_of_2.cton create mode 100644 cranelift/filetests/preopt/div_by_const_power_of_2.cton create mode 100644 cranelift/filetests/preopt/rem_by_const_non_power_of_2.cton create mode 100644 cranelift/filetests/preopt/rem_by_const_power_of_2.cton create mode 100644 cranelift/src/filetest/preopt.rs create mode 100644 lib/cretonne/src/divconst_magic_numbers.rs create mode 100644 lib/cretonne/src/preopt.rs diff --git a/cranelift/filetests/isa/intel/binary64.cton b/cranelift/filetests/isa/intel/binary64.cton index 0b546961d8..b579b4d58d 100644 --- a/cranelift/filetests/isa/intel/binary64.cton +++ b/cranelift/filetests/isa/intel/binary64.cton @@ -336,6 +336,28 @@ ebb0: ; asm: divq %r10 [-,%rax,%rdx] v202, v203 = x86_udivmodx v190, v191, v3 ; bin: 49 f7 f2 + ; double-length multiply instructions, 64 bit + [-,%rax] v1001 = iconst.i64 1 + [-,%r15] v1002 = iconst.i64 2 + ; asm: mulq %r15 + [-,%rax,%rdx] v1003, v1004 = x86_umulx v1001, v1002 ; bin: 49 f7 e7 + ; asm: imulq %r15 + [-,%rax,%rdx] v1005, v1006 = x86_smulx v1001, v1002 ; bin: 49 f7 ef + + ; double-length multiply instructions, 32 bit + [-,%rax] v1011 = iconst.i32 1 + [-,%r15] v1012 = iconst.i32 2 + [-,%rcx] v1017 = iconst.i32 3 + ; asm: mull %r15d + [-,%rax,%rdx] v1013, v1014 = x86_umulx v1011, v1012 ; bin: 41 f7 e7 + ; asm: imull %r15d + [-,%rax,%rdx] v1015, v1016 = x86_smulx v1011, v1012 ; bin: 41 f7 ef + + ; asm: mull %ecx + [-,%rax,%rdx] v1018, v1019 = x86_umulx v1011, v1017 ; bin: f7 e1 + ; asm: imull %ecx + [-,%rax,%rdx] v1020, v1021 = x86_smulx v1011, v1017 ; bin: f7 e9 + ; Bit-counting instructions. ; asm: popcntq %rsi, %rcx diff --git a/cranelift/filetests/isa/intel/legalize-mulhi.cton b/cranelift/filetests/isa/intel/legalize-mulhi.cton new file mode 100644 index 0000000000..673a19db3b --- /dev/null +++ b/cranelift/filetests/isa/intel/legalize-mulhi.cton @@ -0,0 +1,45 @@ + +test compile +set is_64bit +isa intel baseline + +; umulhi/smulhi on 64 bit operands + +function %i64_umulhi(i64, i64) -> i64 { +ebb0(v10: i64, v11: i64): + v12 = umulhi v10, v11 + ; check: %rdi -> %rax + ; check: x86_umulx + ; check: %rdx -> %rax + return v12 +} + +function %i64_smulhi(i64, i64) -> i64 { +ebb0(v20: i64, v21: i64): + v22 = smulhi v20, v21 + ; check: %rdi -> %rax + ; check: x86_smulx + ; check: %rdx -> %rax + return v22 +} + + +; umulhi/smulhi on 32 bit operands + +function %i32_umulhi(i32, i32) -> i32 { +ebb0(v30: i32, v31: i32): + v32 = umulhi v30, v31 + ; check: %rdi -> %rax + ; check: x86_umulx + ; check: %rdx -> %rax + return v32 +} + +function %i32_smulhi(i32, i32) -> i32 { +ebb0(v40: i32, v41: i32): + v42 = smulhi v40, v41 + ; check: %rdi -> %rax + ; check: x86_smulx + ; check: %rdx -> %rax + return v42 +} diff --git a/cranelift/filetests/preopt/div_by_const_indirect.cton b/cranelift/filetests/preopt/div_by_const_indirect.cton new file mode 100644 index 0000000000..ccc83cd49b --- /dev/null +++ b/cranelift/filetests/preopt/div_by_const_indirect.cton @@ -0,0 +1,60 @@ + +test preopt +isa intel baseline + +; Cases where the denominator is created by an iconst + +function %indir_udiv32(i32) -> i32 { +ebb0(v0: i32): + v1 = iconst.i32 7 + v2 = udiv v0, v1 + ; check: iconst.i32 7 + ; check: iconst.i32 0x2492_4925 + ; check: umulhi v0, v3 + ; check: isub v0, v4 + ; check: ushr_imm v5, 1 + ; check: iadd v6, v4 + ; check: ushr_imm v7, 2 + ; check: copy v8 + return v2 +} + +function %indir_sdiv32(i32) -> i32 { +ebb0(v0: i32): + v1 = iconst.i32 -17 + v2 = sdiv v0, v1 + ; check: iconst.i32 -17 + ; check: iconst.i32 0xffff_ffff_8787_8787 + ; check: smulhi v0, v3 + ; check: sshr_imm v4, 3 + ; check: ushr_imm v5, 31 + ; check: iadd v5, v6 + ; check: copy v7 + return v2 +} + +function %indir_udiv64(i64) -> i64 { +ebb0(v0: i64): + v1 = iconst.i64 1337 + v2 = udiv v0, v1 + ; check: iconst.i64 1337 + ; check: iconst.i64 0xc411_9d95_2866_a139 + ; check: umulhi v0, v3 + ; check: ushr_imm v4, 10 + ; check: copy v5 + return v2 +} + +function %indir_sdiv64(i64) -> i64 { +ebb0(v0: i64): + v1 = iconst.i64 -90210 + v2 = sdiv v0, v1 + ; check: iconst.i64 0xffff_ffff_fffe_9f9e + ; check: iconst.i64 0xd181_4ee8_939c_b8bb + ; check: smulhi v0, v3 + ; check: sshr_imm v4, 14 + ; check: ushr_imm v5, 63 + ; check: iadd v5, v6 + ; check: copy v7 + return v2 +} diff --git a/cranelift/filetests/preopt/div_by_const_non_power_of_2.cton b/cranelift/filetests/preopt/div_by_const_non_power_of_2.cton new file mode 100644 index 0000000000..18811fcd82 --- /dev/null +++ b/cranelift/filetests/preopt/div_by_const_non_power_of_2.cton @@ -0,0 +1,267 @@ + +test preopt +isa intel baseline + +; -------- U32 -------- + +; complex case (mul, sub, shift, add, shift) +function %t_udiv32_p7(i32) -> i32 { +ebb0(v0: i32): + v1 = udiv_imm v0, 7 + ; check: iconst.i32 0x2492_4925 + ; check: umulhi v0, v2 + ; check: isub v0, v3 + ; check: ushr_imm v4, 1 + ; check: iadd v5, v3 + ; check: ushr_imm v6, 2 + ; check: copy v7 + return v1 +} + +; simple case (mul, shift) +function %t_udiv32_p125(i32) -> i32 { +ebb0(v0: i32): + v1 = udiv_imm v0, 125 + ; check: iconst.i32 0x1062_4dd3 + ; check: umulhi v0, v2 + ; check: ushr_imm v3, 3 + ; check: copy v4 + return v1 +} + +; simple case w/ shift by zero (mul) +function %t_udiv32_p641(i32) -> i32 { +ebb0(v0: i32): + v1 = udiv_imm v0, 641 + ; check: iconst.i32 0x0066_3d81 + ; check: umulhi v0, v2 + ; check: copy v3 + return v1 +} + + +; -------- S32 -------- + +; simple case w/ shift by zero (mul, add-sign-bit) +function %t_sdiv32_n6(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -6 + ; check: iconst.i32 0xffff_ffff_d555_5555 + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 31 + ; check: iadd v3, v4 + ; check: copy v5 + return v1 +} + +; simple case (mul, shift, add-sign-bit) +function %t_sdiv32_n5(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -5 + ; check: iconst.i32 0xffff_ffff_9999_9999 + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 1 + ; check: ushr_imm v4, 31 + ; check: iadd v4, v5 + ; check: copy v6 + return v1 +} + +; case d < 0 && M > 0 (mul, sub, shift, add-sign-bit) +function %t_sdiv32_n3(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -3 + ; check: iconst.i32 0x5555_5555 + ; check: smulhi v0, v2 + ; check: isub v3, v0 + ; check: sshr_imm v4, 1 + ; check: ushr_imm v5, 31 + ; check: iadd v5, v6 + ; check: copy v7 + return v1 +} + +; simple case w/ shift by zero (mul, add-sign-bit) +function %t_sdiv32_p6(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 6 + ; check: iconst.i32 0x2aaa_aaab + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 31 + ; check: iadd v3, v4 + ; check: copy v5 + return v1 +} + +; case d > 0 && M < 0 (mull, add, shift, add-sign-bit) +function %t_sdiv32_p7(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 7 + ; check: iconst.i32 0xffff_ffff_9249_2493 + ; check: smulhi v0, v2 + ; check: iadd v3, v0 + ; check: sshr_imm v4, 2 + ; check: ushr_imm v5, 31 + ; check: iadd v5, v6 + ; check: copy v7 + return v1 +} + +; simple case (mul, shift, add-sign-bit) +function %t_sdiv32_p625(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 625 + ; check: iconst.i32 0x68db_8bad + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 8 + ; check: ushr_imm v4, 31 + ; check: iadd v4, v5 + ; check: copy v6 + return v1 +} + + +; -------- U64 -------- + +; complex case (mul, sub, shift, add, shift) +function %t_udiv64_p7(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 7 + ; check: iconst.i64 0x2492_4924_9249_2493 + ; check: umulhi v0, v2 + ; check: isub v0, v3 + ; check: ushr_imm v4, 1 + ; check: iadd v5, v3 + ; check: ushr_imm v6, 2 + ; check: copy v7 + return v1 +} + +; simple case (mul, shift) +function %t_udiv64_p9(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 9 + ; check: iconst.i64 0xe38e_38e3_8e38_e38f + ; check: umulhi v0, v2 + ; check: ushr_imm v3, 3 + ; check: copy v4 + return v1 +} + +; complex case (mul, sub, shift, add, shift) +function %t_udiv64_p125(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 125 + ; check: iconst.i64 0x0624_dd2f_1a9f_be77 + ; check: umulhi v0, v2 + ; check: isub v0, v3 + ; check: ushr_imm v4, 1 + ; check: iadd v5, v3 + ; check: ushr_imm v6, 6 + ; check: copy v7 + return v1 +} + +; simple case w/ shift by zero (mul) +function %t_udiv64_p274177(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 274177 + ; check: iconst.i64 0x3d30_f19c_d101 + ; check: umulhi v0, v2 + ; check: copy v3 + return v1 +} + + +; -------- S64 -------- + +; simple case (mul, shift, add-sign-bit) +function %t_sdiv64_n625(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -625 + ; check: iconst.i64 0xcb92_3a29_c779_a6b5 + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 7 + ; check: ushr_imm v4, 63 + ; check: iadd v4, v5 + ; check: copy v6 + return v1 +} + +; simple case w/ zero shift (mul, add-sign-bit) +function %t_sdiv64_n6(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -6 + ; check: iconst.i64 0xd555_5555_5555_5555 + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 63 + ; check: iadd v3, v4 + ; check: copy v5 + return v1 +} + +; simple case w/ zero shift (mul, add-sign-bit) +function %t_sdiv64_n5(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -5 + ; check: iconst.i64 0x9999_9999_9999_9999 + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 1 + ; check: ushr_imm v4, 63 + ; check: iadd v4, v5 + ; check: copy v6 + return v1 +} + +; case d < 0 && M > 0 (mul, sub, shift, add-sign-bit) +function %t_sdiv64_n3(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -3 + ; check: iconst.i64 0x5555_5555_5555_5555 + ; check: smulhi v0, v2 + ; check: isub v3, v0 + ; check: sshr_imm v4, 1 + ; check: ushr_imm v5, 63 + ; check: iadd v5, v6 + ; check: copy v7 + return v1 +} + +; simple case w/ zero shift (mul, add-sign-bit) +function %t_sdiv64_p6(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 6 + ; check: iconst.i64 0x2aaa_aaaa_aaaa_aaab + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 63 + ; check: iadd v3, v4 + ; check: copy v5 + return v1 +} + +; case d > 0 && M < 0 (mul, add, shift, add-sign-bit) +function %t_sdiv64_p15(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 15 + ; check: iconst.i64 0x8888_8888_8888_8889 + ; check: smulhi v0, v2 + ; check: iadd v3, v0 + ; check: sshr_imm v4, 3 + ; check: ushr_imm v5, 63 + ; check: iadd v5, v6 + ; check: copy v7 + return v1 +} + +; simple case (mul, shift, add-sign-bit) +function %t_sdiv64_p625(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 625 + ; check: iconst.i64 0x346d_c5d6_3886_594b + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 7 + ; check: ushr_imm v4, 63 + ; check: iadd v4, v5 + ; check: copy v6 + return v1 +} diff --git a/cranelift/filetests/preopt/div_by_const_power_of_2.cton b/cranelift/filetests/preopt/div_by_const_power_of_2.cton new file mode 100644 index 0000000000..dc51c5395d --- /dev/null +++ b/cranelift/filetests/preopt/div_by_const_power_of_2.cton @@ -0,0 +1,293 @@ + +test preopt +isa intel baseline + +; -------- U32 -------- + +; ignored +function %t_udiv32_p0(i32) -> i32 { +ebb0(v0: i32): + v1 = udiv_imm v0, 0 + ; check: udiv_imm v0, 0 + return v1 +} + +; converted to a copy +function %t_udiv32_p1(i32) -> i32 { +ebb0(v0: i32): + v1 = udiv_imm v0, 1 + ; check: copy v0 + return v1 +} + +; shift +function %t_udiv32_p2(i32) -> i32 { +ebb0(v0: i32): + v1 = udiv_imm v0, 2 + ; check: ushr_imm v0, 1 + return v1 +} + +; shift +function %t_udiv32_p2p31(i32) -> i32 { +ebb0(v0: i32): + v1 = udiv_imm v0, 0x8000_0000 + ; check: ushr_imm v0, 31 + return v1 +} + + +; -------- U64 -------- + +; ignored +function %t_udiv64_p0(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 0 + ; check: udiv_imm v0, 0 + return v1 +} + +; converted to a copy +function %t_udiv64_p1(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 1 + ; check: copy v0 + return v1 +} + +; shift +function %t_udiv64_p2(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 2 + ; check: ushr_imm v0, 1 + return v1 +} + +; shift +function %t_udiv64_p2p63(i64) -> i64 { +ebb0(v0: i64): + v1 = udiv_imm v0, 0x8000_0000_0000_0000 + ; check: ushr_imm v0, 63 + return v1 +} + + +; -------- S32 -------- + +; ignored +function %t_sdiv32_p0(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 0 + ; check: sdiv_imm v0, 0 + return v1 +} + +; converted to a copy +function %t_sdiv32_p1(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 1 + ; check: copy v0 + return v1 +} + +; ignored +function %t_sdiv32_n1(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -1 + ; check: sdiv_imm v0, -1 + return v1 +} + +; shift +function %t_sdiv32_p2(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 2 + ; check: ushr_imm v0, 31 + ; check: iadd v0, v2 + ; check: sshr_imm v3, 1 + ; check: copy v4 + return v1 +} + +; shift +function %t_sdiv32_n2(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -2 + ; check: ushr_imm v0, 31 + ; check: iadd v0, v2 + ; check: sshr_imm v3, 1 + ; check: irsub_imm v4, 0 + return v1 +} + +; shift +function %t_sdiv32_p4(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 4 + ; check: v2 = sshr_imm v0, 1 + ; check: ushr_imm v2, 30 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 2 + ; check: copy v5 + + return v1 +} + +; shift +function %t_sdiv32_n4(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -4 + ; check: sshr_imm v0, 1 + ; check: ushr_imm v2, 30 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 2 + ; check: irsub_imm v5, 0 + return v1 +} + +; shift +function %t_sdiv32_p2p30(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, 0x4000_0000 + ; check: sshr_imm v0, 29 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 30 + ; check: copy v5 + return v1 +} + +; shift +function %t_sdiv32_n2p30(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -0x4000_0000 + ; check: sshr_imm v0, 29 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 30 + ; check: irsub_imm v5, 0 + return v1 +} + +; there's no positive version of this, since -(-0x8000_0000) isn't +; representable. +function %t_sdiv32_n2p31(i32) -> i32 { +ebb0(v0: i32): + v1 = sdiv_imm v0, -0x8000_0000 + ; check: sshr_imm v0, 30 + ; check: ushr_imm v2, 1 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 31 + ; check: irsub_imm v5, 0 + return v1 +} + + +; -------- S64 -------- + +; ignored +function %t_sdiv64_p0(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 0 + ; check: sdiv_imm v0, 0 + return v1 +} + +; converted to a copy +function %t_sdiv64_p1(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 1 + ; check: copy v0 + return v1 +} + +; ignored +function %t_sdiv64_n1(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -1 + ; check: sdiv_imm v0, -1 + return v1 +} + +; shift +function %t_sdiv64_p2(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 2 + ; check: ushr_imm v0, 63 + ; check: iadd v0, v2 + ; check: sshr_imm v3, 1 + ; check: copy v4 + return v1 +} + +; shift +function %t_sdiv64_n2(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -2 + ; check: ushr_imm v0, 63 + ; check: iadd v0, v2 + ; check: sshr_imm v3, 1 + ; check: irsub_imm v4, 0 + return v1 +} + +; shift +function %t_sdiv64_p4(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 4 + ; check: sshr_imm v0, 1 + ; check: ushr_imm v2, 62 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 2 + ; check: copy v5 + return v1 +} + +; shift +function %t_sdiv64_n4(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -4 + ; check: sshr_imm v0, 1 + ; check: ushr_imm v2, 62 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 2 + ; check: irsub_imm v5, 0 + return v1 +} + +; shift +function %t_sdiv64_p2p62(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, 0x4000_0000_0000_0000 + ; check: sshr_imm v0, 61 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 62 + ; check: copy v5 + return v1 +} + +; shift +function %t_sdiv64_n2p62(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -0x4000_0000_0000_0000 + ; check: sshr_imm v0, 61 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 62 + ; check: irsub_imm v5, 0 + return v1 +} + +; there's no positive version of this, since -(-0x8000_0000_0000_0000) isn't +; representable. +function %t_sdiv64_n2p63(i64) -> i64 { +ebb0(v0: i64): + v1 = sdiv_imm v0, -0x8000_0000_0000_0000 + ; check: sshr_imm v0, 62 + ; check: ushr_imm v2, 1 + ; check: iadd v0, v3 + ; check: sshr_imm v4, 63 + ; check: irsub_imm v5, 0 + return v1 +} diff --git a/cranelift/filetests/preopt/rem_by_const_non_power_of_2.cton b/cranelift/filetests/preopt/rem_by_const_non_power_of_2.cton new file mode 100644 index 0000000000..c142a16359 --- /dev/null +++ b/cranelift/filetests/preopt/rem_by_const_non_power_of_2.cton @@ -0,0 +1,286 @@ + +test preopt +isa intel baseline + +; -------- U32 -------- + +; complex case (mul, sub, shift, add, shift) +function %t_urem32_p7(i32) -> i32 { +ebb0(v0: i32): + v1 = urem_imm v0, 7 + ; check: iconst.i32 0x2492_4925 + ; check: umulhi v0, v2 + ; check: isub v0, v3 + ; check: ushr_imm v4, 1 + ; check: iadd v5, v3 + ; check: ushr_imm v6, 2 + ; check: imul_imm v7, 7 + ; check: isub v0, v8 + return v1 +} + +; simple case (mul, shift) +function %t_urem32_p125(i32) -> i32 { +ebb0(v0: i32): + v1 = urem_imm v0, 125 + ; check: iconst.i32 0x1062_4dd3 + ; check: umulhi v0, v2 + ; check: ushr_imm v3, 3 + ; check: imul_imm v4, 125 + ; check: isub v0, v5 + return v1 +} + +; simple case w/ shift by zero (mul) +function %t_urem32_p641(i32) -> i32 { +ebb0(v0: i32): + v1 = urem_imm v0, 641 + ; check: iconst.i32 0x0066_3d81 + ; check: umulhi v0, v2 + ; check: imul_imm v3, 641 + ; check: isub v0, v4 + return v1 +} + + +; -------- S32 -------- + +; simple case w/ shift by zero (mul, add-sign-bit) +function %t_srem32_n6(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -6 + ; check: iconst.i32 0xffff_ffff_d555_5555 + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 31 + ; check: iadd v3, v4 + ; check: imul_imm v5, -6 + ; check: isub v0, v6 + return v1 +} + +; simple case (mul, shift, add-sign-bit) +function %t_srem32_n5(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -5 + ; check: iconst.i32 0xffff_ffff_9999_9999 + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 1 + ; check: ushr_imm v4, 31 + ; check: iadd v4, v5 + ; check: imul_imm v6, -5 + ; check: isub v0, v7 + return v1 +} + +; case d < 0 && M > 0 (mul, sub, shift, add-sign-bit) +function %t_srem32_n3(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -3 + ; check: iconst.i32 0x5555_5555 + ; check: smulhi v0, v2 + ; check: isub v3, v0 + ; check: sshr_imm v4, 1 + ; check: ushr_imm v5, 31 + ; check: iadd v5, v6 + ; check: imul_imm v7, -3 + ; check: isub v0, v8 + return v1 +} + +; simple case w/ shift by zero (mul, add-sign-bit) +function %t_srem32_p6(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 6 + ; check: iconst.i32 0x2aaa_aaab + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 31 + ; check: iadd v3, v4 + ; check: imul_imm v5, 6 + ; check: isub v0, v6 + return v1 +} + +; case d > 0 && M < 0 (mull, add, shift, add-sign-bit) +function %t_srem32_p7(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 7 + ; check: iconst.i32 0xffff_ffff_9249_2493 + ; check: smulhi v0, v2 + ; check: iadd v3, v0 + ; check: sshr_imm v4, 2 + ; check: ushr_imm v5, 31 + ; check: iadd v5, v6 + ; check: imul_imm v7, 7 + ; check: isub v0, v8 + return v1 +} + +; simple case (mul, shift, add-sign-bit) +function %t_srem32_p625(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 625 + ; check: iconst.i32 0x68db_8bad + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 8 + ; check: ushr_imm v4, 31 + ; check: iadd v4, v5 + ; check: imul_imm v6, 625 + ; check: isub v0, v7 + return v1 +} + + +; -------- U64 -------- + +; complex case (mul, sub, shift, add, shift) +function %t_urem64_p7(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 7 + ; check: umulhi v0, v2 + ; check: isub v0, v3 + ; check: ushr_imm v4, 1 + ; check: iadd v5, v3 + ; check: ushr_imm v6, 2 + ; check: imul_imm v7, 7 + ; check: isub v0, v8 + return v1 +} + +; simple case (mul, shift) +function %t_urem64_p9(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 9 + ; check: iconst.i64 0xe38e_38e3_8e38_e38f + ; check: umulhi v0, v2 + ; check: ushr_imm v3, 3 + ; check: imul_imm v4, 9 + ; check: isub v0, v5 + return v1 +} + +; complex case (mul, sub, shift, add, shift) +function %t_urem64_p125(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 125 + ; check: iconst.i64 0x0624_dd2f_1a9f_be77 + ; check: umulhi v0, v2 + ; check: isub v0, v3 + ; check: ushr_imm v4, 1 + ; check: iadd v5, v3 + ; check: ushr_imm v6, 6 + ; check: imul_imm v7, 125 + ; check: isub v0, v8 + return v1 +} + +; simple case w/ shift by zero (mul) +function %t_urem64_p274177(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 274177 + ; check: iconst.i64 0x3d30_f19c_d101 + ; check: umulhi v0, v2 + ; check: imul_imm v3, 0x0004_2f01 + ; check: isub v0, v4 + return v1 +} + + +; -------- S64 -------- + +; simple case (mul, shift, add-sign-bit) +function %t_srem64_n625(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -625 + ; check: iconst.i64 0xcb92_3a29_c779_a6b5 + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 7 + ; check: ushr_imm v4, 63 + ; check: iadd v4, v5 + ; check: imul_imm v6, -625 + ; check: isub v0, v7 + return v1 +} + +; simple case w/ zero shift (mul, add-sign-bit) +function %t_srem64_n6(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -6 + ; check: iconst.i64 0xd555_5555_5555_5555 + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 63 + ; check: iadd v3, v4 + ; check: imul_imm v5, -6 + ; check: isub v0, v6 + return v1 +} + +; simple case w/ zero shift (mul, add-sign-bit) +function %t_srem64_n5(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -5 + ; check: iconst.i64 0x9999_9999_9999_9999 + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 1 + ; check: ushr_imm v4, 63 + ; check: iadd v4, v5 + ; check: imul_imm v6, -5 + ; check: isub v0, v7 + return v1 +} + +; case d < 0 && M > 0 (mul, sub, shift, add-sign-bit) +function %t_srem64_n3(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -3 + ; check: iconst.i64 0x5555_5555_5555_5555 + ; check: smulhi v0, v2 + ; check: isub v3, v0 + ; check: sshr_imm v4, 1 + ; check: ushr_imm v5, 63 + ; check: iadd v5, v6 + ; check: imul_imm v7, -3 + ; check: isub v0, v8 + return v1 +} + +; simple case w/ zero shift (mul, add-sign-bit) +function %t_srem64_p6(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 6 + ; check: iconst.i64 0x2aaa_aaaa_aaaa_aaab + ; check: smulhi v0, v2 + ; check: ushr_imm v3, 63 + ; check: iadd v3, v4 + ; check: imul_imm v5, 6 + ; check: isub v0, v6 + return v1 +} + +; case d > 0 && M < 0 (mul, add, shift, add-sign-bit) +function %t_srem64_p15(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 15 + ; check: iconst.i64 0x8888_8888_8888_8889 + ; check: smulhi v0, v2 + ; check: iadd v3, v0 + ; check: sshr_imm v4, 3 + ; check: ushr_imm v5, 63 + ; check: iadd v5, v6 + ; check: imul_imm v7, 15 + ; check: isub v0, v8 + return v1 +} + +; simple case (mul, shift, add-sign-bit) +function %t_srem64_p625(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 625 + ; check: iconst.i64 0x346d_c5d6_3886_594b + ; check: smulhi v0, v2 + ; check: sshr_imm v3, 7 + ; check: ushr_imm v4, 63 + ; check: iadd v4, v5 + ; check: imul_imm v6, 625 + ; check: isub v0, v7 + return v1 +} diff --git a/cranelift/filetests/preopt/rem_by_const_power_of_2.cton b/cranelift/filetests/preopt/rem_by_const_power_of_2.cton new file mode 100644 index 0000000000..931623d2e7 --- /dev/null +++ b/cranelift/filetests/preopt/rem_by_const_power_of_2.cton @@ -0,0 +1,292 @@ + +test preopt +isa intel baseline + +; -------- U32 -------- + +; ignored +function %t_urem32_p0(i32) -> i32 { +ebb0(v0: i32): + v1 = urem_imm v0, 0 + ; check: urem_imm v0, 0 + return v1 +} + +; converted to constant zero +function %t_urem32_p1(i32) -> i32 { +ebb0(v0: i32): + v1 = urem_imm v0, 1 + ; check: iconst.i32 0 + return v1 +} + +; shift +function %t_urem32_p2(i32) -> i32 { +ebb0(v0: i32): + v1 = urem_imm v0, 2 + ; check: band_imm v0, 1 + return v1 +} + +; shift +function %t_urem32_p2p31(i32) -> i32 { +ebb0(v0: i32): + v1 = urem_imm v0, 0x8000_0000 + ; check: band_imm v0, 0x7fff_ffff + return v1 +} + + +; -------- U64 -------- + +; ignored +function %t_urem64_p0(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 0 + ; check: urem_imm v0, 0 + return v1 +} + +; converted to constant zero +function %t_urem64_p1(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 1 + ; check: iconst.i64 0 + return v1 +} + +; shift +function %t_urem64_p2(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 2 + ; check: band_imm v0, 1 + return v1 +} + +; shift +function %t_urem64_p2p63(i64) -> i64 { +ebb0(v0: i64): + v1 = urem_imm v0, 0x8000_0000_0000_0000 + ; check: band_imm v0, 0x7fff_ffff_ffff_ffff + return v1 +} + + +; -------- S32 -------- + +; ignored +function %t_srem32_n1(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -1 + ; check: srem_imm v0, -1 + return v1 +} + +; ignored +function %t_srem32_p0(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 0 + ; check: srem_imm v0, 0 + return v1 +} + +; converted to constant zero +function %t_srem32_p1(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 1 + ; check: iconst.i32 0 + return v1 +} + +; shift +function %t_srem32_p2(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 2 + ; check: ushr_imm v0, 31 + ; check: iadd v0, v2 + ; check: band_imm v3, -2 + ; check: isub v0, v4 + return v1 +} + +; shift +function %t_srem32_n2(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -2 + ; check: ushr_imm v0, 31 + ; check: iadd v0, v2 + ; check: band_imm v3, -2 + ; check: isub v0, v4 + return v1 +} + +; shift +function %t_srem32_p4(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 4 + ; check: sshr_imm v0, 1 + ; check: ushr_imm v2, 30 + ; check: iadd v0, v3 + ; check: band_imm v4, -4 + ; check: isub v0, v5 + return v1 +} + +; shift +function %t_srem32_n4(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -4 + ; check: sshr_imm v0, 1 + ; check: ushr_imm v2, 30 + ; check: iadd v0, v3 + ; check: band_imm v4, -4 + ; check: isub v0, v5 + return v1 +} + +; shift +function %t_srem32_p2p30(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, 0x4000_0000 + ; check: sshr_imm v0, 29 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: band_imm v4, 0xffff_ffff_c000_0000 + ; check: isub v0, v5 + return v1 +} + +; shift +function %t_srem32_n2p30(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -0x4000_0000 + ; check: sshr_imm v0, 29 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: band_imm v4, 0xffff_ffff_c000_0000 + ; check: isub v0, v5 + return v1 +} + +; there's no positive version of this, since -(-0x8000_0000) isn't +; representable. +function %t_srem32_n2p31(i32) -> i32 { +ebb0(v0: i32): + v1 = srem_imm v0, -0x8000_0000 + ; check: sshr_imm v0, 30 + ; check: ushr_imm v2, 1 + ; check: iadd v0, v3 + ; check: band_imm v4, 0xffff_ffff_8000_0000 + ; check: isub v0, v5 + return v1 +} + + +; -------- S64 -------- + +; ignored +function %t_srem64_n1(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -1 + ; check: srem_imm v0, -1 + return v1 +} + +; ignored +function %t_srem64_p0(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 0 + ; check: srem_imm v0, 0 + return v1 +} + +; converted to constant zero +function %t_srem64_p1(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 1 + ; check: iconst.i64 0 + return v1 +} + +; shift +function %t_srem64_p2(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 2 + ; check: ushr_imm v0, 63 + ; check: iadd v0, v2 + ; check: band_imm v3, -2 + ; check: isub v0, v4 + return v1 +} + +; shift +function %t_srem64_n2(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -2 + ; check: ushr_imm v0, 63 + ; check: iadd v0, v2 + ; check: band_imm v3, -2 + ; check: isub v0, v4 + return v1 +} + +; shift +function %t_srem64_p4(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 4 + ; check: sshr_imm v0, 1 + ; check: ushr_imm v2, 62 + ; check: iadd v0, v3 + ; check: band_imm v4, -4 + ; check: isub v0, v5 + return v1 +} + +; shift +function %t_srem64_n4(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -4 + ; check: sshr_imm v0, 1 + ; check: ushr_imm v2, 62 + ; check: iadd v0, v3 + ; check: band_imm v4, -4 + ; check: isub v0, v5 + return v1 +} + +; shift +function %t_srem64_p2p62(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, 0x4000_0000_0000_0000 + ; check: sshr_imm v0, 61 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: band_imm v4, 0xc000_0000_0000_0000 + ; check: isub v0, v5 + return v1 +} + +; shift +function %t_srem64_n2p62(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -0x4000_0000_0000_0000 + ; check: sshr_imm v0, 61 + ; check: ushr_imm v2, 2 + ; check: iadd v0, v3 + ; check: band_imm v4, 0xc000_0000_0000_0000 + ; check: isub v0, v5 + return v1 +} + +; there's no positive version of this, since -(-0x8000_0000_0000_0000) isn't +; representable. +function %t_srem64_n2p63(i64) -> i64 { +ebb0(v0: i64): + v1 = srem_imm v0, -0x8000_0000_0000_0000 + ; check: sshr_imm v0, 62 + ; check: ushr_imm v2, 1 + ; check: iadd v0, v3 + ; check: band_imm v4, 0x8000_0000_0000_0000 + ; check: isub v0, v5 + return v1 +} diff --git a/cranelift/src/filetest/mod.rs b/cranelift/src/filetest/mod.rs index 2d2a9c6cac..5286307811 100644 --- a/cranelift/src/filetest/mod.rs +++ b/cranelift/src/filetest/mod.rs @@ -19,6 +19,7 @@ mod concurrent; mod domtree; mod legalizer; mod licm; +mod preopt; mod regalloc; mod runner; mod runone; @@ -64,6 +65,7 @@ fn new_subtest(parsed: &TestCommand) -> subtest::Result> { "domtree" => domtree::subtest(parsed), "legalizer" => legalizer::subtest(parsed), "licm" => licm::subtest(parsed), + "preopt" => preopt::subtest(parsed), "print-cfg" => print_cfg::subtest(parsed), "regalloc" => regalloc::subtest(parsed), "simple-gvn" => simple_gvn::subtest(parsed), diff --git a/cranelift/src/filetest/preopt.rs b/cranelift/src/filetest/preopt.rs new file mode 100644 index 0000000000..60d03f8207 --- /dev/null +++ b/cranelift/src/filetest/preopt.rs @@ -0,0 +1,50 @@ +//! Test command for testing the preopt pass. +//! +//! The resulting function is sent to `filecheck`. + +use cretonne::ir::Function; +use cretonne; +use cton_reader::TestCommand; +use filetest::subtest::{SubTest, Context, Result, run_filecheck}; +use std::borrow::Cow; +use std::fmt::Write; +use utils::pretty_error; + +struct TestPreopt; + +pub fn subtest(parsed: &TestCommand) -> Result> { + assert_eq!(parsed.command, "preopt"); + if !parsed.options.is_empty() { + Err(format!("No options allowed on {}", parsed)) + } else { + Ok(Box::new(TestPreopt)) + } +} + +impl SubTest for TestPreopt { + fn name(&self) -> Cow { + Cow::from("preopt") + } + + fn is_mutating(&self) -> bool { + true + } + + fn run(&self, func: Cow, context: &Context) -> Result<()> { + // Create a compilation context, and drop in the function. + let mut comp_ctx = cretonne::Context::new(); + comp_ctx.func = func.into_owned(); + let isa = context.isa.expect("preopt needs an ISA"); + + comp_ctx.flowgraph(); + comp_ctx.preopt(isa).map_err(|e| { + pretty_error(&comp_ctx.func, context.isa, Into::into(e)) + })?; + + let mut text = String::new(); + write!(&mut text, "{}", &comp_ctx.func).map_err( + |e| e.to_string(), + )?; + run_filecheck(&text, context) + } +} diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index 7200e45e7d..7db8bed202 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -833,6 +833,26 @@ imul = Instruction( """, ins=(x, y), outs=a) +umulhi = Instruction( + 'umulhi', r""" + Unsigned integer multiplication, producing the high half of a + double-length result. + + Polymorphic over all scalar integer types, but does not support vector + types. + """, + ins=(x, y), outs=a) + +smulhi = Instruction( + 'smulhi', """ + Signed integer multiplication, producing the high half of a + double-length result. + + Polymorphic over all scalar integer types, but does not support vector + types. + """, + ins=(x, y), outs=a) + udiv = Instruction( 'udiv', r""" Unsigned integer division: :math:`a := \lfloor {x \over y} \rfloor`. diff --git a/lib/cretonne/meta/isa/intel/encodings.py b/lib/cretonne/meta/isa/intel/encodings.py index 1f47a5da06..f9b77a04bd 100644 --- a/lib/cretonne/meta/isa/intel/encodings.py +++ b/lib/cretonne/meta/isa/intel/encodings.py @@ -120,6 +120,9 @@ enc_i32_i64(base.imul, r.rrx, 0x0f, 0xaf) enc_i32_i64(x86.sdivmodx, r.div, 0xf7, rrr=7) enc_i32_i64(x86.udivmodx, r.div, 0xf7, rrr=6) +enc_i32_i64(x86.smulx, r.mulx, 0xf7, rrr=5) +enc_i32_i64(x86.umulx, r.mulx, 0xf7, rrr=4) + enc_i32_i64(base.copy, r.umr, 0x89) enc_both(base.copy.b1, r.umr, 0x89) enc_i32_i64(base.regmove, r.rmov, 0x89) diff --git a/lib/cretonne/meta/isa/intel/instructions.py b/lib/cretonne/meta/isa/intel/instructions.py index 277cf62b4a..e5265e2d15 100644 --- a/lib/cretonne/meta/isa/intel/instructions.py +++ b/lib/cretonne/meta/isa/intel/instructions.py @@ -47,6 +47,28 @@ sdivmodx = Instruction( """, ins=(nlo, nhi, d), outs=(q, r), can_trap=True) +argL = Operand('argL', iWord) +argR = Operand('argR', iWord) +resLo = Operand('resLo', iWord) +resHi = Operand('resHi', iWord) + +umulx = Instruction( + 'x86_umulx', r""" + Unsigned integer multiplication, producing a double-length result. + + Polymorphic over all scalar integer types, but does not support vector + types. + """, + ins=(argL, argR), outs=(resLo, resHi)) + +smulx = Instruction( + 'x86_smulx', r""" + Signed integer multiplication, producing a double-length result. + + Polymorphic over all scalar integer types, but does not support vector + types. + """, + ins=(argL, argR), outs=(resLo, resHi)) Float = TypeVar( 'Float', 'A scalar or vector floating point number', diff --git a/lib/cretonne/meta/isa/intel/legalize.py b/lib/cretonne/meta/isa/intel/legalize.py index 32f0a98153..5806bb9284 100644 --- a/lib/cretonne/meta/isa/intel/legalize.py +++ b/lib/cretonne/meta/isa/intel/legalize.py @@ -37,6 +37,23 @@ intel_expand.custom_legalize(insts.srem, 'expand_sdivrem') intel_expand.custom_legalize(insts.udiv, 'expand_udivrem') intel_expand.custom_legalize(insts.urem, 'expand_udivrem') +# +# Double length (widening) multiplication +# +resLo = Var('resLo') +resHi = Var('resHi') +intel_expand.legalize( + resHi << insts.umulhi(x, y), + Rtl( + (resLo, resHi) << x86.umulx(x, y) + )) + +intel_expand.legalize( + resHi << insts.smulhi(x, y), + Rtl( + (resLo, resHi) << x86.smulx(x, y) + )) + # Floating point condition codes. # # The 8 condition codes in `supported_floatccs` are directly supported by a diff --git a/lib/cretonne/meta/isa/intel/recipes.py b/lib/cretonne/meta/isa/intel/recipes.py index 133837fc78..9d03d02053 100644 --- a/lib/cretonne/meta/isa/intel/recipes.py +++ b/lib/cretonne/meta/isa/intel/recipes.py @@ -453,6 +453,15 @@ div = TailRecipe( modrm_r_bits(in_reg2, bits, sink); ''') +# XX /n for {s,u}mulx: inputs in %rax, r. Outputs in %rdx(hi):%rax(lo) +mulx = TailRecipe( + 'mulx', Binary, size=1, + ins=(GPR.rax, GPR), outs=(GPR.rax, GPR.rdx), + emit=''' + PUT_OP(bits, rex1(in_reg1), sink); + modrm_r_bits(in_reg1, bits, sink); + ''') + # XX /n ib with 8-bit immediate sign-extended. rib = TailRecipe( 'rib', BinaryImm, size=2, ins=GPR, outs=0, diff --git a/lib/cretonne/src/context.rs b/lib/cretonne/src/context.rs index 8aff5aa706..78b5d5bebf 100644 --- a/lib/cretonne/src/context.rs +++ b/lib/cretonne/src/context.rs @@ -23,6 +23,7 @@ use unreachable_code::eliminate_unreachable_code; use verifier; use simple_gvn::do_simple_gvn; use licm::do_licm; +use preopt::do_preopt; use timing; /// Persistent data structures and compilation pipeline. @@ -87,6 +88,7 @@ impl Context { self.verify_if(isa)?; self.compute_cfg(); + self.preopt(isa)?; self.legalize(isa)?; /* TODO: Enable additional optimization passes. if isa.flags().opt_level() == OptLevel::Best { @@ -131,6 +133,13 @@ impl Context { } } + /// Perform pre-legalization rewrites on the function. + pub fn preopt(&mut self, isa: &TargetIsa) -> CtonResult { + do_preopt(&mut self.func); + self.verify_if(isa)?; + Ok(()) + } + /// Run the legalizer for `isa` on the function. pub fn legalize(&mut self, isa: &TargetIsa) -> CtonResult { // Legalization invalidates the domtree and loop_analysis by mutating the CFG. diff --git a/lib/cretonne/src/divconst_magic_numbers.rs b/lib/cretonne/src/divconst_magic_numbers.rs new file mode 100644 index 0000000000..64416e48fe --- /dev/null +++ b/lib/cretonne/src/divconst_magic_numbers.rs @@ -0,0 +1,542 @@ +//! Compute "magic numbers" for division-by-constants transformations. + +#![allow(non_snake_case)] + +//---------------------------------------------------------------------- +// +// Math helpers for division by (non-power-of-2) constants. This is based +// on the presentation in "Hacker's Delight" by Henry Warren, 2003. There +// are four cases: {unsigned, signed} x {32 bit, 64 bit}. The word size +// makes little difference, but the signed-vs-unsigned aspect has a large +// effect. Therefore everything is presented in the order U32 U64 S32 S64 +// so as to emphasise the similarity of the U32 and U64 cases and the S32 +// and S64 cases. + +// Structures to hold the "magic numbers" computed. + +#[derive(PartialEq, Debug)] +pub struct MU32 { + pub mulBy: u32, + pub doAdd: bool, + pub shiftBy: i32, +} + +#[derive(PartialEq, Debug)] +pub struct MU64 { + pub mulBy: u64, + pub doAdd: bool, + pub shiftBy: i32, +} + +#[derive(PartialEq, Debug)] +pub struct MS32 { + pub mulBy: i32, + pub shiftBy: i32, +} + +#[derive(PartialEq, Debug)] +pub struct MS64 { + pub mulBy: i64, + pub shiftBy: i32, +} + +// The actual "magic number" generators follow. + +pub fn magicU32(d: u32) -> MU32 { + assert_ne!(d, 0); + assert_ne!(d, 1); // d==1 generates out of range shifts. + + let mut do_add: bool = false; + let mut p: i32 = 31; + let nc: u32 = 0xFFFFFFFFu32 - u32::wrapping_neg(d) % d; + let mut q1: u32 = 0x80000000u32 / nc; + let mut r1: u32 = 0x80000000u32 - q1 * nc; + let mut q2: u32 = 0x7FFFFFFFu32 / d; + let mut r2: u32 = 0x7FFFFFFFu32 - q2 * d; + loop { + p = p + 1; + if r1 >= nc - r1 { + q1 = u32::wrapping_add(u32::wrapping_mul(2, q1), 1); + r1 = u32::wrapping_sub(u32::wrapping_mul(2, r1), nc); + } else { + q1 = 2 * q1; + r1 = 2 * r1; + } + if r2 + 1 >= d - r2 { + if q2 >= 0x7FFFFFFFu32 { + do_add = true; + } + q2 = 2 * q2 + 1; + r2 = u32::wrapping_sub(u32::wrapping_add(u32::wrapping_mul(2, r2), 1), d); + } else { + if q2 >= 0x80000000u32 { + do_add = true; + } + q2 = u32::wrapping_mul(2, q2); + r2 = 2 * r2 + 1; + } + let delta: u32 = d - 1 - r2; + if !(p < 64 && (q1 < delta || (q1 == delta && r1 == 0))) { + break; + } + } + + MU32 { + mulBy: q2 + 1, + doAdd: do_add, + shiftBy: p - 32, + } +} + +pub fn magicU64(d: u64) -> MU64 { + assert_ne!(d, 0); + assert_ne!(d, 1); // d==1 generates out of range shifts. + + let mut do_add: bool = false; + let mut p: i32 = 63; + let nc: u64 = 0xFFFFFFFFFFFFFFFFu64 - u64::wrapping_neg(d) % d; + let mut q1: u64 = 0x8000000000000000u64 / nc; + let mut r1: u64 = 0x8000000000000000u64 - q1 * nc; + let mut q2: u64 = 0x7FFFFFFFFFFFFFFFu64 / d; + let mut r2: u64 = 0x7FFFFFFFFFFFFFFFu64 - q2 * d; + loop { + p = p + 1; + if r1 >= nc - r1 { + q1 = u64::wrapping_add(u64::wrapping_mul(2, q1), 1); + r1 = u64::wrapping_sub(u64::wrapping_mul(2, r1), nc); + } else { + q1 = 2 * q1; + r1 = 2 * r1; + } + if r2 + 1 >= d - r2 { + if q2 >= 0x7FFFFFFFFFFFFFFFu64 { + do_add = true; + } + q2 = 2 * q2 + 1; + r2 = u64::wrapping_sub(u64::wrapping_add(u64::wrapping_mul(2, r2), 1), d); + } else { + if q2 >= 0x8000000000000000u64 { + do_add = true; + } + q2 = u64::wrapping_mul(2, q2); + r2 = 2 * r2 + 1; + } + let delta: u64 = d - 1 - r2; + if !(p < 128 && (q1 < delta || (q1 == delta && r1 == 0))) { + break; + } + } + + MU64 { + mulBy: q2 + 1, + doAdd: do_add, + shiftBy: p - 64, + } +} + +pub fn magicS32(d: i32) -> MS32 { + assert_ne!(d, -1); + assert_ne!(d, 0); + assert_ne!(d, 1); + let two31: u32 = 0x80000000u32; + let mut p: i32 = 31; + let ad: u32 = i32::wrapping_abs(d) as u32; + let t: u32 = two31 + ((d as u32) >> 31); + let anc: u32 = u32::wrapping_sub(t - 1, t % ad); + let mut q1: u32 = two31 / anc; + let mut r1: u32 = two31 - q1 * anc; + let mut q2: u32 = two31 / ad; + let mut r2: u32 = two31 - q2 * ad; + loop { + p = p + 1; + q1 = 2 * q1; + r1 = 2 * r1; + if r1 >= anc { + q1 = q1 + 1; + r1 = r1 - anc; + } + q2 = 2 * q2; + r2 = 2 * r2; + if r2 >= ad { + q2 = q2 + 1; + r2 = r2 - ad; + } + let delta: u32 = ad - r2; + if !(q1 < delta || (q1 == delta && r1 == 0)) { + break; + } + } + + MS32 { + mulBy: (if d < 0 { + u32::wrapping_neg(q2 + 1) + } else { + q2 + 1 + }) as i32, + shiftBy: p - 32, + } +} + +pub fn magicS64(d: i64) -> MS64 { + assert_ne!(d, -1); + assert_ne!(d, 0); + assert_ne!(d, 1); + let two63: u64 = 0x8000000000000000u64; + let mut p: i32 = 63; + let ad: u64 = i64::wrapping_abs(d) as u64; + let t: u64 = two63 + ((d as u64) >> 63); + let anc: u64 = u64::wrapping_sub(t - 1, t % ad); + let mut q1: u64 = two63 / anc; + let mut r1: u64 = two63 - q1 * anc; + let mut q2: u64 = two63 / ad; + let mut r2: u64 = two63 - q2 * ad; + loop { + p = p + 1; + q1 = 2 * q1; + r1 = 2 * r1; + if r1 >= anc { + q1 = q1 + 1; + r1 = r1 - anc; + } + q2 = 2 * q2; + r2 = 2 * r2; + if r2 >= ad { + q2 = q2 + 1; + r2 = r2 - ad; + } + let delta: u64 = ad - r2; + if !(q1 < delta || (q1 == delta && r1 == 0)) { + break; + } + } + + MS64 { + mulBy: (if d < 0 { + u64::wrapping_neg(q2 + 1) + } else { + q2 + 1 + }) as i64, + shiftBy: p - 64, + } +} + +#[cfg(test)] +mod tests { + use super::{magicU32, magicU64, magicS32, magicS64}; + use super::{MU32, MU64, MS32, MS64}; + + fn mkMU32(mulBy: u32, doAdd: bool, shiftBy: i32) -> MU32 { + MU32 { + mulBy, + doAdd, + shiftBy, + } + } + + fn mkMU64(mulBy: u64, doAdd: bool, shiftBy: i32) -> MU64 { + MU64 { + mulBy, + doAdd, + shiftBy, + } + } + + fn mkMS32(mulBy: i32, shiftBy: i32) -> MS32 { + MS32 { mulBy, shiftBy } + } + + fn mkMS64(mulBy: i64, shiftBy: i32) -> MS64 { + MS64 { mulBy, shiftBy } + } + + #[test] + fn test_magicU32() { + assert_eq!(magicU32(2u32), mkMU32(0x80000000u32, false, 0)); + assert_eq!(magicU32(3u32), mkMU32(0xaaaaaaabu32, false, 1)); + assert_eq!(magicU32(4u32), mkMU32(0x40000000u32, false, 0)); + assert_eq!(magicU32(5u32), mkMU32(0xcccccccdu32, false, 2)); + assert_eq!(magicU32(6u32), mkMU32(0xaaaaaaabu32, false, 2)); + assert_eq!(magicU32(7u32), mkMU32(0x24924925u32, true, 3)); + assert_eq!(magicU32(9u32), mkMU32(0x38e38e39u32, false, 1)); + assert_eq!(magicU32(10u32), mkMU32(0xcccccccdu32, false, 3)); + assert_eq!(magicU32(11u32), mkMU32(0xba2e8ba3u32, false, 3)); + assert_eq!(magicU32(12u32), mkMU32(0xaaaaaaabu32, false, 3)); + assert_eq!(magicU32(25u32), mkMU32(0x51eb851fu32, false, 3)); + assert_eq!(magicU32(125u32), mkMU32(0x10624dd3u32, false, 3)); + assert_eq!(magicU32(625u32), mkMU32(0xd1b71759u32, false, 9)); + assert_eq!(magicU32(1337u32), mkMU32(0x88233b2bu32, true, 11)); + assert_eq!(magicU32(65535u32), mkMU32(0x80008001u32, false, 15)); + assert_eq!(magicU32(65536u32), mkMU32(0x00010000u32, false, 0)); + assert_eq!(magicU32(65537u32), mkMU32(0xffff0001u32, false, 16)); + assert_eq!(magicU32(31415927u32), mkMU32(0x445b4553u32, false, 23)); + assert_eq!(magicU32(0xdeadbeefu32), mkMU32(0x93275ab3u32, false, 31)); + assert_eq!(magicU32(0xfffffffdu32), mkMU32(0x40000001u32, false, 30)); + assert_eq!(magicU32(0xfffffffeu32), mkMU32(0x00000003u32, true, 32)); + assert_eq!(magicU32(0xffffffffu32), mkMU32(0x80000001u32, false, 31)); + } + #[test] + fn test_magicU64() { + assert_eq!(magicU64(2u64), mkMU64(0x8000000000000000u64, false, 0)); + assert_eq!(magicU64(3u64), mkMU64(0xaaaaaaaaaaaaaaabu64, false, 1)); + assert_eq!(magicU64(4u64), mkMU64(0x4000000000000000u64, false, 0)); + assert_eq!(magicU64(5u64), mkMU64(0xcccccccccccccccdu64, false, 2)); + assert_eq!(magicU64(6u64), mkMU64(0xaaaaaaaaaaaaaaabu64, false, 2)); + assert_eq!(magicU64(7u64), mkMU64(0x2492492492492493u64, true, 3)); + assert_eq!(magicU64(9u64), mkMU64(0xe38e38e38e38e38fu64, false, 3)); + assert_eq!(magicU64(10u64), mkMU64(0xcccccccccccccccdu64, false, 3)); + assert_eq!(magicU64(11u64), mkMU64(0x2e8ba2e8ba2e8ba3u64, false, 1)); + assert_eq!(magicU64(12u64), mkMU64(0xaaaaaaaaaaaaaaabu64, false, 3)); + assert_eq!(magicU64(25u64), mkMU64(0x47ae147ae147ae15u64, true, 5)); + assert_eq!(magicU64(125u64), mkMU64(0x0624dd2f1a9fbe77u64, true, 7)); + assert_eq!(magicU64(625u64), mkMU64(0x346dc5d63886594bu64, false, 7)); + assert_eq!(magicU64(1337u64), mkMU64(0xc4119d952866a139u64, false, 10)); + assert_eq!( + magicU64(31415927u64), + mkMU64(0x116d154b9c3d2f85u64, true, 25) + ); + assert_eq!( + magicU64(0x00000000deadbeefu64), + mkMU64(0x93275ab2dfc9094bu64, false, 31) + ); + assert_eq!( + magicU64(0x00000000fffffffdu64), + mkMU64(0x8000000180000005u64, false, 31) + ); + assert_eq!( + magicU64(0x00000000fffffffeu64), + mkMU64(0x0000000200000005u64, true, 32) + ); + assert_eq!( + magicU64(0x00000000ffffffffu64), + mkMU64(0x8000000080000001u64, false, 31) + ); + assert_eq!( + magicU64(0x0000000100000000u64), + mkMU64(0x0000000100000000u64, false, 0) + ); + assert_eq!( + magicU64(0x0000000100000001u64), + mkMU64(0xffffffff00000001u64, false, 32) + ); + assert_eq!( + magicU64(0x0ddc0ffeebadf00du64), + mkMU64(0x2788e9d394b77da1u64, true, 60) + ); + assert_eq!( + magicU64(0xfffffffffffffffdu64), + mkMU64(0x4000000000000001u64, false, 62) + ); + assert_eq!( + magicU64(0xfffffffffffffffeu64), + mkMU64(0x0000000000000003u64, true, 64) + ); + assert_eq!( + magicU64(0xffffffffffffffffu64), + mkMU64(0x8000000000000001u64, false, 63) + ); + } + #[test] + fn test_magicS32() { + assert_eq!(magicS32(-0x80000000i32), mkMS32(0x7fffffffu32 as i32, 30)); + assert_eq!(magicS32(-0x7FFFFFFFi32), mkMS32(0xbfffffffu32 as i32, 29)); + assert_eq!(magicS32(-0x7FFFFFFEi32), mkMS32(0x7ffffffdu32 as i32, 30)); + assert_eq!(magicS32(-31415927i32), mkMS32(0xbba4baadu32 as i32, 23)); + assert_eq!(magicS32(-1337i32), mkMS32(0x9df73135u32 as i32, 9)); + assert_eq!(magicS32(-256i32), mkMS32(0x7fffffffu32 as i32, 7)); + assert_eq!(magicS32(-5i32), mkMS32(0x99999999u32 as i32, 1)); + assert_eq!(magicS32(-3i32), mkMS32(0x55555555u32 as i32, 1)); + assert_eq!(magicS32(-2i32), mkMS32(0x7fffffffu32 as i32, 0)); + assert_eq!(magicS32(2i32), mkMS32(0x80000001u32 as i32, 0)); + assert_eq!(magicS32(3i32), mkMS32(0x55555556u32 as i32, 0)); + assert_eq!(magicS32(4i32), mkMS32(0x80000001u32 as i32, 1)); + assert_eq!(magicS32(5i32), mkMS32(0x66666667u32 as i32, 1)); + assert_eq!(magicS32(6i32), mkMS32(0x2aaaaaabu32 as i32, 0)); + assert_eq!(magicS32(7i32), mkMS32(0x92492493u32 as i32, 2)); + assert_eq!(magicS32(9i32), mkMS32(0x38e38e39u32 as i32, 1)); + assert_eq!(magicS32(10i32), mkMS32(0x66666667u32 as i32, 2)); + assert_eq!(magicS32(11i32), mkMS32(0x2e8ba2e9u32 as i32, 1)); + assert_eq!(magicS32(12i32), mkMS32(0x2aaaaaabu32 as i32, 1)); + assert_eq!(magicS32(25i32), mkMS32(0x51eb851fu32 as i32, 3)); + assert_eq!(magicS32(125i32), mkMS32(0x10624dd3u32 as i32, 3)); + assert_eq!(magicS32(625i32), mkMS32(0x68db8badu32 as i32, 8)); + assert_eq!(magicS32(1337i32), mkMS32(0x6208cecbu32 as i32, 9)); + assert_eq!(magicS32(31415927i32), mkMS32(0x445b4553u32 as i32, 23)); + assert_eq!(magicS32(0x7ffffffei32), mkMS32(0x80000003u32 as i32, 30)); + assert_eq!(magicS32(0x7fffffffi32), mkMS32(0x40000001u32 as i32, 29)); + } + #[test] + fn test_magicS64() { + assert_eq!( + magicS64(-0x8000000000000000i64), + mkMS64(0x7fffffffffffffffu64 as i64, 62) + ); + assert_eq!( + magicS64(-0x7FFFFFFFFFFFFFFFi64), + mkMS64(0xbfffffffffffffffu64 as i64, 61) + ); + assert_eq!( + magicS64(-0x7FFFFFFFFFFFFFFEi64), + mkMS64(0x7ffffffffffffffdu64 as i64, 62) + ); + assert_eq!( + magicS64(-0x0ddC0ffeeBadF00di64), + mkMS64(0x6c3b8b1635a4412fu64 as i64, 59) + ); + assert_eq!( + magicS64(-0x100000001i64), + mkMS64(0x800000007fffffffu64 as i64, 31) + ); + assert_eq!( + magicS64(-0x100000000i64), + mkMS64(0x7fffffffffffffffu64 as i64, 31) + ); + assert_eq!( + magicS64(-0xFFFFFFFFi64), + mkMS64(0x7fffffff7fffffffu64 as i64, 31) + ); + assert_eq!( + magicS64(-0xFFFFFFFEi64), + mkMS64(0x7ffffffefffffffdu64 as i64, 31) + ); + assert_eq!( + magicS64(-0xFFFFFFFDi64), + mkMS64(0x7ffffffe7ffffffbu64 as i64, 31) + ); + assert_eq!( + magicS64(-0xDeadBeefi64), + mkMS64(0x6cd8a54d2036f6b5u64 as i64, 31) + ); + assert_eq!( + magicS64(-31415927i64), + mkMS64(0x7749755a31e1683du64 as i64, 24) + ); + assert_eq!(magicS64(-1337i64), mkMS64(0x9df731356bccaf63u64 as i64, 9)); + assert_eq!(magicS64(-256i64), mkMS64(0x7fffffffffffffffu64 as i64, 7)); + assert_eq!(magicS64(-5i64), mkMS64(0x9999999999999999u64 as i64, 1)); + assert_eq!(magicS64(-3i64), mkMS64(0x5555555555555555u64 as i64, 1)); + assert_eq!(magicS64(-2i64), mkMS64(0x7fffffffffffffffu64 as i64, 0)); + assert_eq!(magicS64(2i64), mkMS64(0x8000000000000001u64 as i64, 0)); + assert_eq!(magicS64(3i64), mkMS64(0x5555555555555556u64 as i64, 0)); + assert_eq!(magicS64(4i64), mkMS64(0x8000000000000001u64 as i64, 1)); + assert_eq!(magicS64(5i64), mkMS64(0x6666666666666667u64 as i64, 1)); + assert_eq!(magicS64(6i64), mkMS64(0x2aaaaaaaaaaaaaabu64 as i64, 0)); + assert_eq!(magicS64(7i64), mkMS64(0x4924924924924925u64 as i64, 1)); + assert_eq!(magicS64(9i64), mkMS64(0x1c71c71c71c71c72u64 as i64, 0)); + assert_eq!(magicS64(10i64), mkMS64(0x6666666666666667u64 as i64, 2)); + assert_eq!(magicS64(11i64), mkMS64(0x2e8ba2e8ba2e8ba3u64 as i64, 1)); + assert_eq!(magicS64(12i64), mkMS64(0x2aaaaaaaaaaaaaabu64 as i64, 1)); + assert_eq!(magicS64(25i64), mkMS64(0xa3d70a3d70a3d70bu64 as i64, 4)); + assert_eq!(magicS64(125i64), mkMS64(0x20c49ba5e353f7cfu64 as i64, 4)); + assert_eq!(magicS64(625i64), mkMS64(0x346dc5d63886594bu64 as i64, 7)); + assert_eq!(magicS64(1337i64), mkMS64(0x6208ceca9433509du64 as i64, 9)); + assert_eq!( + magicS64(31415927i64), + mkMS64(0x88b68aa5ce1e97c3u64 as i64, 24) + ); + assert_eq!( + magicS64(0x00000000deadbeefi64), + mkMS64(0x93275ab2dfc9094bu64 as i64, 31) + ); + assert_eq!( + magicS64(0x00000000fffffffdi64), + mkMS64(0x8000000180000005u64 as i64, 31) + ); + assert_eq!( + magicS64(0x00000000fffffffei64), + mkMS64(0x8000000100000003u64 as i64, 31) + ); + assert_eq!( + magicS64(0x00000000ffffffffi64), + mkMS64(0x8000000080000001u64 as i64, 31) + ); + assert_eq!( + magicS64(0x0000000100000000i64), + mkMS64(0x8000000000000001u64 as i64, 31) + ); + assert_eq!( + magicS64(0x0000000100000001i64), + mkMS64(0x7fffffff80000001u64 as i64, 31) + ); + assert_eq!( + magicS64(0x0ddc0ffeebadf00di64), + mkMS64(0x93c474e9ca5bbed1u64 as i64, 59) + ); + assert_eq!( + magicS64(0x7ffffffffffffffdi64), + mkMS64(0x2000000000000001u64 as i64, 60) + ); + assert_eq!( + magicS64(0x7ffffffffffffffei64), + mkMS64(0x8000000000000003u64 as i64, 62) + ); + assert_eq!( + magicS64(0x7fffffffffffffffi64), + mkMS64(0x4000000000000001u64 as i64, 61) + ); + } + #[test] + fn test_magic_generators_dont_panic() { + // The point of this is to check that the magic number generators + // don't panic with integer wraparounds, especially at boundary + // cases for their arguments. The actual results are thrown away. + let mut total: u64 = 0; + println!("Testing UP magicU32"); + for x in 2..(200 * 1000u32) { + let m = magicU32(x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + total = total - (if m.doAdd { 123 } else { 456 }); + } + println!("Testing DOWN magicU32"); + for x in 0..(200 * 1000u32) { + let m = magicU32(0xFFFF_FFFFu32 - x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + total = total - (if m.doAdd { 123 } else { 456 }); + } + + println!("Testing UP magicU64"); + for x in 2..(200 * 1000u64) { + let m = magicU64(x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + total = total - (if m.doAdd { 123 } else { 456 }); + } + println!("Testing DOWN magicU64"); + for x in 0..(200 * 1000u64) { + let m = magicU64(0xFFFF_FFFF_FFFF_FFFFu64 - x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + total = total - (if m.doAdd { 123 } else { 456 }); + } + + println!("Testing UP magicS32"); + for x in 0..(200 * 1000i32) { + let m = magicS32(-0x8000_0000i32 + x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + } + println!("Testing DOWN magicS32"); + for x in 0..(200 * 1000i32) { + let m = magicS32(0x7FFF_FFFFi32 - x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + } + + println!("Testing UP magicS64"); + for x in 0..(200 * 1000i64) { + let m = magicS64(-0x8000_0000_0000_0000i64 + x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + } + println!("Testing DOWN magicS64"); + for x in 0..(200 * 1000i64) { + let m = magicS64(0x7FFF_FFFF_FFFF_FFFFi64 - x); + total = total ^ (m.mulBy as u64); + total = total + (m.shiftBy as u64); + } + // Force `total` -- and hence, the entire computation -- to + // be used, so that rustc can't optimise it out. + assert_eq!(total, 7547519887532559585u64); + } +} diff --git a/lib/cretonne/src/isa/intel/enc_tables.rs b/lib/cretonne/src/isa/intel/enc_tables.rs index 1a602cf8a1..00c5ab99cb 100644 --- a/lib/cretonne/src/isa/intel/enc_tables.rs +++ b/lib/cretonne/src/isa/intel/enc_tables.rs @@ -1,5 +1,6 @@ //! Encoding tables for Intel ISAs. +use bitset::BitSet; use cursor::{Cursor, FuncCursor}; use flowgraph::ControlFlowGraph; use ir::{self, InstBuilder}; diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index b4c386477c..f34bed638b 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -33,11 +33,13 @@ mod abi; mod bitset; mod constant_hash; mod context; +mod divconst_magic_numbers; mod iterators; mod legalizer; mod licm; mod partition_slice; mod predicates; +mod preopt; mod ref_slice; mod regalloc; mod scoped_hash_map; diff --git a/lib/cretonne/src/preopt.rs b/lib/cretonne/src/preopt.rs new file mode 100644 index 0000000000..0e4582d7a7 --- /dev/null +++ b/lib/cretonne/src/preopt.rs @@ -0,0 +1,521 @@ +//! A pre-legalization rewriting pass. + +#![allow(non_snake_case)] + +use cursor::{Cursor, FuncCursor}; +use ir::dfg::ValueDef; +use ir::{Function, InstructionData, Value, DataFlowGraph, InstBuilder, Type}; +use ir::Inst; +use ir::types::{I32, I64}; +use ir::instructions::Opcode; +use divconst_magic_numbers::{MU32, MU64, MS32, MS64}; +use divconst_magic_numbers::{magicU32, magicU64, magicS32, magicS64}; +use timing; + + +//---------------------------------------------------------------------- +// +// Pattern-match helpers and transformation for div and rem by constants. + +// Simple math helpers + +// if `x` is a power of two, or the negation thereof, return the power along +// with a boolean that indicates whether `x` is negative. Else return None. +#[inline] +fn isPowerOf2_S32(x: i32) -> Option<(bool, u32)> { + // We have to special-case this because abs(x) isn't representable. + if x == -0x8000_0000 { + return Some((true, 31)); + } + let abs_x = i32::wrapping_abs(x) as u32; + if abs_x.is_power_of_two() { + return Some((x < 0, abs_x.trailing_zeros())); + } + None +} + +// Same comments as for isPowerOf2_S64 apply. +#[inline] +fn isPowerOf2_S64(x: i64) -> Option<(bool, u32)> { + // We have to special-case this because abs(x) isn't representable. + if x == -0x8000_0000_0000_0000 { + return Some((true, 63)); + } + let abs_x = i64::wrapping_abs(x) as u64; + if abs_x.is_power_of_two() { + return Some((x < 0, abs_x.trailing_zeros())); + } + None +} + +#[derive(Debug)] +enum DivRemByConstInfo { + DivU32(Value, u32), // In all cases, the arguments are: + DivU64(Value, u64), // left operand, right operand + DivS32(Value, i32), + DivS64(Value, i64), + RemU32(Value, u32), + RemU64(Value, u64), + RemS32(Value, i32), + RemS64(Value, i64), +} + +// Possibly create a DivRemByConstInfo from the given components, by +// figuring out which, if any, of the 8 cases apply, and also taking care to +// sanity-check the immediate. +fn package_up_divrem_info( + argL: Value, + argL_ty: Type, + argRs: i64, + isSigned: bool, + isRem: bool, +) -> Option { + let argRu: u64 = argRs as u64; + if !isSigned && argL_ty == I32 && argRu < 0x1_0000_0000 { + let con = if isRem { + DivRemByConstInfo::RemU32 + } else { + DivRemByConstInfo::DivU32 + }; + return Some(con(argL, argRu as u32)); + } + if !isSigned && argL_ty == I64 { + // unsigned 64, no range constraint + let con = if isRem { + DivRemByConstInfo::RemU64 + } else { + DivRemByConstInfo::DivU64 + }; + return Some(con(argL, argRu)); + } + if isSigned && argL_ty == I32 && (argRu <= 0x7fff_ffff || argRu >= 0xffff_ffff_8000_0000) { + let con = if isRem { + DivRemByConstInfo::RemS32 + } else { + DivRemByConstInfo::DivS32 + }; + return Some(con(argL, argRu as i32)); + } + if isSigned && argL_ty == I64 { + // signed 64, no range constraint + let con = if isRem { + DivRemByConstInfo::RemS64 + } else { + DivRemByConstInfo::DivS64 + }; + return Some(con(argL, argRu as i64)); + } + None +} + +// Examine `idata` to see if it is a div or rem by a constant, and if so +// return the operands, signedness, operation size and div-vs-rem-ness in a +// handy bundle. +fn get_div_info(inst: Inst, dfg: &DataFlowGraph) -> Option { + let idata: &InstructionData = &dfg[inst]; + + if let &InstructionData::BinaryImm { opcode, arg, imm } = idata { + let (isSigned, isRem) = match opcode { + Opcode::UdivImm => (false, false), + Opcode::UremImm => (false, true), + Opcode::SdivImm => (true, false), + Opcode::SremImm => (true, true), + _other => return None, + }; + // Pull the operation size (type) from the left arg + let argL_ty = dfg.value_type(arg); + return package_up_divrem_info(arg, argL_ty, imm.into(), isSigned, isRem); + } + + // TODO: should we actually bother to do this (that is, manually match + // the case that the second argument is an iconst)? Or should we assume + // that some previous constant propagation pass has pushed all such + // immediates to their use points, creating BinaryImm instructions + // instead? For now we take the conservative approach. + if let &InstructionData::Binary { opcode, args } = idata { + let (isSigned, isRem) = match opcode { + Opcode::Udiv => (false, false), + Opcode::Urem => (false, true), + Opcode::Sdiv => (true, false), + Opcode::Srem => (true, true), + _other => return None, + }; + let argR: Value = args[1]; + if let Some(simm64) = get_const(argR, dfg) { + let argL: Value = args[0]; + // Pull the operation size (type) from the left arg + let argL_ty = dfg.value_type(argL); + return package_up_divrem_info(argL, argL_ty, simm64, isSigned, isRem); + } + } + + None +} + +// Actually do the transformation given a bundle containing the relevant +// information. `divrem_info` describes a div or rem by a constant, that +// `pos` currently points at, and `inst` is the associated instruction. +// `inst` is replaced by a sequence of other operations that calculate the +// same result. Note that there are various `divrem_info` cases where we +// cannot do any transformation, in which case `inst` is left unchanged. +fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCursor, inst: Inst) { + let isRem = match *divrem_info { + DivRemByConstInfo::DivU32(_, _) | + DivRemByConstInfo::DivU64(_, _) | + DivRemByConstInfo::DivS32(_, _) | + DivRemByConstInfo::DivS64(_, _) => false, + DivRemByConstInfo::RemU32(_, _) | + DivRemByConstInfo::RemU64(_, _) | + DivRemByConstInfo::RemS32(_, _) | + DivRemByConstInfo::RemS64(_, _) => true, + }; + + match divrem_info { + + // -------------------- U32 -------------------- + + // U32 div, rem by zero: ignore + &DivRemByConstInfo::DivU32(_n1, 0) | + &DivRemByConstInfo::RemU32(_n1, 0) => {} + + // U32 div by 1: identity + // U32 rem by 1: zero + &DivRemByConstInfo::DivU32(n1, 1) | + &DivRemByConstInfo::RemU32(n1, 1) => { + if isRem { + pos.func.dfg.replace(inst).iconst(I32, 0); + } else { + pos.func.dfg.replace(inst).copy(n1); + } + } + + // U32 div, rem by a power-of-2 + &DivRemByConstInfo::DivU32(n1, d) | + &DivRemByConstInfo::RemU32(n1, d) if d.is_power_of_two() => { + assert!(d >= 2); + // compute k where d == 2^k + let k = d.trailing_zeros(); + assert!(k >= 1 && k <= 31); + if isRem { + let mask = (1u64 << k) - 1; + pos.func.dfg.replace(inst).band_imm(n1, mask as i64); + } else { + pos.func.dfg.replace(inst).ushr_imm(n1, k as i64); + } + } + + // U32 div, rem by non-power-of-2 + &DivRemByConstInfo::DivU32(n1, d) | + &DivRemByConstInfo::RemU32(n1, d) => { + assert!(d >= 3); + let MU32 { + mulBy, + doAdd, + shiftBy, + } = magicU32(d); + let qf; // final quotient + let q0 = pos.ins().iconst(I32, mulBy as i64); + let q1 = pos.ins().umulhi(n1, q0); + if doAdd { + assert!(shiftBy >= 1 && shiftBy <= 32); + let t1 = pos.ins().isub(n1, q1); + let t2 = pos.ins().ushr_imm(t1, 1); + let t3 = pos.ins().iadd(t2, q1); + // I never found any case where shiftBy == 1 here. + // So there's no attempt to fold out a zero shift. + debug_assert!(shiftBy != 1); + qf = pos.ins().ushr_imm(t3, (shiftBy - 1) as i64); + } else { + assert!(shiftBy >= 0 && shiftBy <= 31); + // Whereas there are known cases here for shiftBy == 0. + if shiftBy > 0 { + qf = pos.ins().ushr_imm(q1, shiftBy as i64); + } else { + qf = q1; + } + } + // Now qf holds the final quotient. If necessary calculate the + // remainder instead. + if isRem { + let tt = pos.ins().imul_imm(qf, d as i64); + pos.func.dfg.replace(inst).isub(n1, tt); + } else { + pos.func.dfg.replace(inst).copy(qf); + } + } + + // -------------------- U64 -------------------- + + // U64 div, rem by zero: ignore + &DivRemByConstInfo::DivU64(_n1, 0) | + &DivRemByConstInfo::RemU64(_n1, 0) => {} + + // U64 div by 1: identity + // U64 rem by 1: zero + &DivRemByConstInfo::DivU64(n1, 1) | + &DivRemByConstInfo::RemU64(n1, 1) => { + if isRem { + pos.func.dfg.replace(inst).iconst(I64, 0); + } else { + pos.func.dfg.replace(inst).copy(n1); + } + } + + // U64 div, rem by a power-of-2 + &DivRemByConstInfo::DivU64(n1, d) | + &DivRemByConstInfo::RemU64(n1, d) if d.is_power_of_two() => { + assert!(d >= 2); + // compute k where d == 2^k + let k = d.trailing_zeros(); + assert!(k >= 1 && k <= 63); + if isRem { + let mask = (1u64 << k) - 1; + pos.func.dfg.replace(inst).band_imm(n1, mask as i64); + } else { + pos.func.dfg.replace(inst).ushr_imm(n1, k as i64); + } + } + + // U64 div, rem by non-power-of-2 + &DivRemByConstInfo::DivU64(n1, d) | + &DivRemByConstInfo::RemU64(n1, d) => { + assert!(d >= 3); + let MU64 { + mulBy, + doAdd, + shiftBy, + } = magicU64(d); + let qf; // final quotient + let q0 = pos.ins().iconst(I64, mulBy as i64); + let q1 = pos.ins().umulhi(n1, q0); + if doAdd { + assert!(shiftBy >= 1 && shiftBy <= 64); + let t1 = pos.ins().isub(n1, q1); + let t2 = pos.ins().ushr_imm(t1, 1); + let t3 = pos.ins().iadd(t2, q1); + // I never found any case where shiftBy == 1 here. + // So there's no attempt to fold out a zero shift. + debug_assert!(shiftBy != 1); + qf = pos.ins().ushr_imm(t3, (shiftBy - 1) as i64); + } else { + assert!(shiftBy >= 0 && shiftBy <= 63); + // Whereas there are known cases here for shiftBy == 0. + if shiftBy > 0 { + qf = pos.ins().ushr_imm(q1, shiftBy as i64); + } else { + qf = q1; + } + } + // Now qf holds the final quotient. If necessary calculate the + // remainder instead. + if isRem { + let tt = pos.ins().imul_imm(qf, d as i64); + pos.func.dfg.replace(inst).isub(n1, tt); + } else { + pos.func.dfg.replace(inst).copy(qf); + } + } + + // -------------------- S32 -------------------- + + // S32 div, rem by zero or -1: ignore + &DivRemByConstInfo::DivS32(_n1, -1) | + &DivRemByConstInfo::RemS32(_n1, -1) | + &DivRemByConstInfo::DivS32(_n1, 0) | + &DivRemByConstInfo::RemS32(_n1, 0) => {} + + // S32 div by 1: identity + // S32 rem by 1: zero + &DivRemByConstInfo::DivS32(n1, 1) | + &DivRemByConstInfo::RemS32(n1, 1) => { + if isRem { + pos.func.dfg.replace(inst).iconst(I32, 0); + } else { + pos.func.dfg.replace(inst).copy(n1); + } + } + + &DivRemByConstInfo::DivS32(n1, d) | + &DivRemByConstInfo::RemS32(n1, d) => { + if let Some((isNeg, k)) = isPowerOf2_S32(d) { + // k can be 31 only in the case that d is -2^31. + assert!(k >= 1 && k <= 31); + let t1 = if k - 1 == 0 { + n1 + } else { + pos.ins().sshr_imm(n1, (k - 1) as i64) + }; + let t2 = pos.ins().ushr_imm(t1, (32 - k) as i64); + let t3 = pos.ins().iadd(n1, t2); + if isRem { + // S32 rem by a power-of-2 + let t4 = pos.ins().band_imm(t3, i32::wrapping_neg(1 << k) as i64); + // Curiously, we don't care here what the sign of d is. + pos.func.dfg.replace(inst).isub(n1, t4); + } else { + // S32 div by a power-of-2 + let t4 = pos.ins().sshr_imm(t3, k as i64); + if isNeg { + pos.func.dfg.replace(inst).irsub_imm(t4, 0); + } else { + pos.func.dfg.replace(inst).copy(t4); + } + } + } else { + // S32 div, rem by a non-power-of-2 + assert!(d < -2 || d > 2); + let MS32 { mulBy, shiftBy } = magicS32(d); + let q0 = pos.ins().iconst(I32, mulBy as i64); + let q1 = pos.ins().smulhi(n1, q0); + let q2 = if d > 0 && mulBy < 0 { + pos.ins().iadd(q1, n1) + } else if d < 0 && mulBy > 0 { + pos.ins().isub(q1, n1) + } else { + q1 + }; + assert!(shiftBy >= 0 && shiftBy <= 31); + let q3 = if shiftBy == 0 { + q2 + } else { + pos.ins().sshr_imm(q2, shiftBy as i64) + }; + let t1 = pos.ins().ushr_imm(q3, 31); + let qf = pos.ins().iadd(q3, t1); + // Now qf holds the final quotient. If necessary calculate + // the remainder instead. + if isRem { + let tt = pos.ins().imul_imm(qf, d as i64); + pos.func.dfg.replace(inst).isub(n1, tt); + } else { + pos.func.dfg.replace(inst).copy(qf); + } + } + } + + // -------------------- S64 -------------------- + + // S64 div, rem by zero or -1: ignore + &DivRemByConstInfo::DivS64(_n1, -1) | + &DivRemByConstInfo::RemS64(_n1, -1) | + &DivRemByConstInfo::DivS64(_n1, 0) | + &DivRemByConstInfo::RemS64(_n1, 0) => {} + + // S64 div by 1: identity + // S64 rem by 1: zero + &DivRemByConstInfo::DivS64(n1, 1) | + &DivRemByConstInfo::RemS64(n1, 1) => { + if isRem { + pos.func.dfg.replace(inst).iconst(I64, 0); + } else { + pos.func.dfg.replace(inst).copy(n1); + } + } + + &DivRemByConstInfo::DivS64(n1, d) | + &DivRemByConstInfo::RemS64(n1, d) => { + if let Some((isNeg, k)) = isPowerOf2_S64(d) { + // k can be 63 only in the case that d is -2^63. + assert!(k >= 1 && k <= 63); + let t1 = if k - 1 == 0 { + n1 + } else { + pos.ins().sshr_imm(n1, (k - 1) as i64) + }; + let t2 = pos.ins().ushr_imm(t1, (64 - k) as i64); + let t3 = pos.ins().iadd(n1, t2); + if isRem { + // S64 rem by a power-of-2 + let t4 = pos.ins().band_imm(t3, i64::wrapping_neg(1 << k)); + // Curiously, we don't care here what the sign of d is. + pos.func.dfg.replace(inst).isub(n1, t4); + } else { + // S64 div by a power-of-2 + let t4 = pos.ins().sshr_imm(t3, k as i64); + if isNeg { + pos.func.dfg.replace(inst).irsub_imm(t4, 0); + } else { + pos.func.dfg.replace(inst).copy(t4); + } + } + } else { + // S64 div, rem by a non-power-of-2 + assert!(d < -2 || d > 2); + let MS64 { mulBy, shiftBy } = magicS64(d); + let q0 = pos.ins().iconst(I64, mulBy); + let q1 = pos.ins().smulhi(n1, q0); + let q2 = if d > 0 && mulBy < 0 { + pos.ins().iadd(q1, n1) + } else if d < 0 && mulBy > 0 { + pos.ins().isub(q1, n1) + } else { + q1 + }; + assert!(shiftBy >= 0 && shiftBy <= 63); + let q3 = if shiftBy == 0 { + q2 + } else { + pos.ins().sshr_imm(q2, shiftBy as i64) + }; + let t1 = pos.ins().ushr_imm(q3, 63); + let qf = pos.ins().iadd(q3, t1); + // Now qf holds the final quotient. If necessary calculate + // the remainder instead. + if isRem { + let tt = pos.ins().imul_imm(qf, d); + pos.func.dfg.replace(inst).isub(n1, tt); + } else { + pos.func.dfg.replace(inst).copy(qf); + } + } + } + + } +} + + +//---------------------------------------------------------------------- +// +// General pattern-match helpers. + +// Find out if `value` actually resolves to a constant, and if so what its +// value is. +fn get_const(value: Value, dfg: &DataFlowGraph) -> Option { + match dfg.value_def(value) { + ValueDef::Result(definingInst, resultNo) => { + let definingIData: &InstructionData = &dfg[definingInst]; + if let &InstructionData::UnaryImm { opcode, imm } = definingIData { + if opcode == Opcode::Iconst && resultNo == 0 { + return Some(imm.into()); + } + } + None + } + ValueDef::Param(_definingEbb, _paramNo) => None, + } +} + + +//---------------------------------------------------------------------- +// +// The main pre-opt pass. + +pub fn do_preopt(func: &mut Function) { + let _tt = timing::preopt(); + let mut pos = FuncCursor::new(func); + while let Some(_ebb) = pos.next_ebb() { + + while let Some(inst) = pos.next_inst() { + + //-- BEGIN -- division by constants ---------------- + + let mb_dri = get_div_info(inst, &pos.func.dfg); + if let Some(divrem_info) = mb_dri { + do_divrem_transformation(&divrem_info, &mut pos, inst); + continue; + } + + //-- END -- division by constants ------------------ + } + } +} diff --git a/lib/cretonne/src/timing.rs b/lib/cretonne/src/timing.rs index d120d43c32..5419895fdc 100644 --- a/lib/cretonne/src/timing.rs +++ b/lib/cretonne/src/timing.rs @@ -55,6 +55,7 @@ define_passes!{ flowgraph: "Control flow graph", domtree: "Dominator tree", loop_analysis: "Loop analysis", + preopt: "Pre-legalization rewriting", legalize: "Legalization", gvn: "Global value numbering", licm: "Loop invariant code motion", From 227baaadb8652eda9fa09fbfa73ee7d3d7ec32bd Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 11:42:43 -0800 Subject: [PATCH 20/58] Enable the simple_gvn and licm passes at OptLevel::Best. --- lib/cretonne/src/context.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cretonne/src/context.rs b/lib/cretonne/src/context.rs index 78b5d5bebf..5b8db2414c 100644 --- a/lib/cretonne/src/context.rs +++ b/lib/cretonne/src/context.rs @@ -18,7 +18,7 @@ use isa::TargetIsa; use legalize_function; use regalloc; use result::{CtonError, CtonResult}; -use settings::FlagsOrIsa; +use settings::{FlagsOrIsa, OptLevel}; use unreachable_code::eliminate_unreachable_code; use verifier; use simple_gvn::do_simple_gvn; @@ -90,14 +90,12 @@ impl Context { self.compute_cfg(); self.preopt(isa)?; self.legalize(isa)?; - /* TODO: Enable additional optimization passes. if isa.flags().opt_level() == OptLevel::Best { self.compute_domtree(); self.compute_loop_analysis(); self.licm(isa)?; self.simple_gvn(isa)?; } - */ self.compute_domtree(); self.eliminate_unreachable_code(isa)?; self.regalloc(isa)?; From 1cf9a8d669d453fd9e505db49c1cf7085b0d8f0a Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 12:02:14 -0800 Subject: [PATCH 21/58] Implement the wasm sign-extension-ops proposal. https://github.com/WebAssembly/sign-extension-ops/ --- lib/wasm/src/code_translator.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index abaaaef3bf..21df608f74 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -634,12 +634,35 @@ pub fn translate_operator( let val = state.pop1(); state.push1(builder.ins().bitcast(I64, val)); } - Operator::I32Extend8S | - Operator::I32Extend16S | - Operator::I64Extend8S | - Operator::I64Extend16S | + Operator::I32Extend8S => { + let val = state.pop1(); + state.push1(builder.ins().ireduce(I8, val)); + let val = state.pop1(); + state.push1(builder.ins().sextend(I32, val)); + } + Operator::I32Extend16S => { + let val = state.pop1(); + state.push1(builder.ins().ireduce(I16, val)); + let val = state.pop1(); + state.push1(builder.ins().sextend(I32, val)); + } + Operator::I64Extend8S => { + let val = state.pop1(); + state.push1(builder.ins().ireduce(I8, val)); + let val = state.pop1(); + state.push1(builder.ins().sextend(I64, val)); + } + Operator::I64Extend16S => { + let val = state.pop1(); + state.push1(builder.ins().ireduce(I8, val)); + let val = state.pop1(); + state.push1(builder.ins().sextend(I64, val)); + } Operator::I64Extend32S => { - panic!("proposed sign-extend operators not yet supported"); + let val = state.pop1(); + state.push1(builder.ins().ireduce(I32, val)); + let val = state.pop1(); + state.push1(builder.ins().sextend(I64, val)); } /****************************** Binary Operators ************************************/ Operator::I32Add | Operator::I64Add => { From b9f51d7850e969d714936bc0d4a12c15e29e83ae Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Feb 2018 22:27:52 -0800 Subject: [PATCH 22/58] Enable more compliler lints. --- lib/cretonne/src/lib.rs | 4 +++- lib/filecheck/src/lib.rs | 4 +++- lib/frontend/src/lib.rs | 4 +++- lib/native/src/lib.rs | 4 +++- lib/reader/src/lib.rs | 4 +++- lib/wasm/src/lib.rs | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index f34bed638b..ab7bb51f03 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -1,6 +1,8 @@ //! Cretonne code generation library. -#![deny(missing_docs)] +#![deny(missing_docs, + trivial_numeric_casts, + unused_extern_crates)] pub use context::Context; pub use legalizer::legalize_function; diff --git a/lib/filecheck/src/lib.rs b/lib/filecheck/src/lib.rs index a189acebdb..4c8a242897 100644 --- a/lib/filecheck/src/lib.rs +++ b/lib/filecheck/src/lib.rs @@ -236,7 +236,9 @@ //! This will match `"one, two"` , but not `"one,two"`. Without the `$()`, trailing whitespace //! would be trimmed from the pattern. -#![deny(missing_docs)] +#![deny(missing_docs, + trivial_numeric_casts, + unused_extern_crates)] pub use error::{Error, Result}; pub use variable::{VariableMap, Value, NO_VARIABLES}; diff --git a/lib/frontend/src/lib.rs b/lib/frontend/src/lib.rs index 9db5a4a34b..8d2de2237b 100644 --- a/lib/frontend/src/lib.rs +++ b/lib/frontend/src/lib.rs @@ -142,7 +142,9 @@ //! } //! ``` -#![deny(missing_docs)] +#![deny(missing_docs, + trivial_numeric_casts, + unused_extern_crates)] extern crate cretonne; diff --git a/lib/native/src/lib.rs b/lib/native/src/lib.rs index 6cdb39a15e..d7c6a8d0ab 100644 --- a/lib/native/src/lib.rs +++ b/lib/native/src/lib.rs @@ -1,7 +1,9 @@ //! Performs autodetection of the host for the purposes of running //! Cretonne to generate code to run on the same machine. -#![deny(missing_docs)] +#![deny(missing_docs, + trivial_numeric_casts, + unused_extern_crates)] extern crate cretonne; diff --git a/lib/reader/src/lib.rs b/lib/reader/src/lib.rs index 06a03dd6fe..85ea048950 100644 --- a/lib/reader/src/lib.rs +++ b/lib/reader/src/lib.rs @@ -3,7 +3,9 @@ //! The cton_reader library supports reading .cton files. This functionality is needed for testing //! Cretonne, but is not essential for a JIT compiler. -#![deny(missing_docs)] +#![deny(missing_docs, + trivial_numeric_casts, + unused_extern_crates)] extern crate cretonne; diff --git a/lib/wasm/src/lib.rs b/lib/wasm/src/lib.rs index b5b79049fe..6e9a6a365a 100644 --- a/lib/wasm/src/lib.rs +++ b/lib/wasm/src/lib.rs @@ -9,7 +9,9 @@ //! //! The main function of this module is [`translate_module`](fn.translate_module.html). -#![deny(missing_docs)] +#![deny(missing_docs, + trivial_numeric_casts, + unused_extern_crates)] extern crate wasmparser; extern crate cton_frontend; From c93f29ad1e2d4b2228d788506f6d1d00d7c0b322 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 13:30:37 -0800 Subject: [PATCH 23/58] Remove more obsolete comments about entity number remapping. --- lib/reader/src/parser.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index b4252d4cb9..47f14129a9 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -82,9 +82,6 @@ pub struct Parser<'a> { } // Context for resolving references when parsing a single function. -// -// Many entities like values, stack slots, and function signatures are referenced in the `.cton` -// file by number. We need to map these numbers to real references. struct Context<'a> { function: Function, map: SourceMap, @@ -119,7 +116,7 @@ impl<'a> Context<'a> { } } - // Allocate a new stack slot and add a mapping number -> StackSlot. + // Allocate a new stack slot. fn add_ss(&mut self, ss: StackSlot, data: StackSlotData, loc: &Location) -> Result<()> { while self.function.stack_slots.next_key().index() <= ss.index() { self.function.create_stack_slot( @@ -139,7 +136,7 @@ impl<'a> Context<'a> { } } - // Allocate a global variable slot and add a mapping number -> GlobalVar. + // Allocate a global variable slot. fn add_gv(&mut self, gv: GlobalVar, data: GlobalVarData, loc: &Location) -> Result<()> { while self.function.global_vars.next_key().index() <= gv.index() { self.function.create_global_var(GlobalVarData::Sym { @@ -159,7 +156,7 @@ impl<'a> Context<'a> { } } - // Allocate a heap slot and add a mapping number -> Heap. + // Allocate a heap slot. fn add_heap(&mut self, heap: Heap, data: HeapData, loc: &Location) -> Result<()> { while self.function.heaps.next_key().index() <= heap.index() { self.function.create_heap(HeapData { @@ -182,7 +179,7 @@ impl<'a> Context<'a> { } } - // Allocate a new signature and add a mapping number -> SigRef. + // Allocate a new signature. fn add_sig(&mut self, sig: SigRef, data: Signature, loc: &Location) -> Result<()> { while self.function.dfg.signatures.next_key().index() <= sig.index() { self.function.import_signature( @@ -202,7 +199,7 @@ impl<'a> Context<'a> { } } - // Allocate a new external function and add a mapping number -> FuncRef. + // Allocate a new external function. fn add_fn(&mut self, fn_: FuncRef, data: ExtFuncData, loc: &Location) -> Result<()> { while self.function.dfg.ext_funcs.next_key().index() <= fn_.index() { self.function.import_function(ExtFuncData { @@ -223,7 +220,7 @@ impl<'a> Context<'a> { } } - // Allocate a new jump table and add a mapping number -> JumpTable. + // Allocate a new jump table. fn add_jt(&mut self, jt: JumpTable, data: JumpTableData, loc: &Location) -> Result<()> { while self.function.jump_tables.next_key().index() <= jt.index() { self.function.create_jump_table(JumpTableData::new()); @@ -241,7 +238,7 @@ impl<'a> Context<'a> { } } - // Allocate a new EBB and add a mapping src_ebb -> Ebb. + // Allocate a new EBB. fn add_ebb(&mut self, ebb: Ebb, loc: &Location) -> Result { while self.function.dfg.num_ebbs() <= ebb.index() { self.function.dfg.make_ebb(); @@ -446,7 +443,6 @@ impl<'a> Parser<'a> { } // Match and consume a value reference, direct or vtable. - // This does not convert from the source value numbering to our in-memory value numbering. fn match_value(&mut self, err_msg: &str) -> Result { if let Some(Token::Value(v)) = self.token() { self.consume(); @@ -1427,7 +1423,7 @@ impl<'a> Parser<'a> { } // Parse parenthesized list of EBB parameters. Returns a vector of (u32, Type) pairs with the - // source value numbers of the defined values and the defined types. + // value numbers of the defined values and the defined types. // // ebb-params ::= * "(" ebb-param { "," ebb-param } ")" fn parse_ebb_params(&mut self, ctx: &mut Context, ebb: Ebb) -> Result<()> { @@ -1476,7 +1472,7 @@ impl<'a> Parser<'a> { } let t = self.match_type("expected EBB argument type")?; - // Allocate the EBB argument and add the mapping. + // Allocate the EBB argument. ctx.function.dfg.append_ebb_param_for_parser(ebb, t, v); ctx.map.def_value(v, &v_location)?; @@ -1936,7 +1932,7 @@ impl<'a> Parser<'a> { } InstructionFormat::NullAry => InstructionData::NullAry { opcode }, InstructionFormat::Jump => { - // Parse the destination EBB number. Don't translate source to local numbers yet. + // Parse the destination EBB number. let ebb_num = self.match_ebb("expected jump destination EBB")?; let args = self.parse_opt_value_list()?; InstructionData::Jump { From 5dc449ec9e331edebeb4e7419d8c88a4a302c3a1 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 28 Feb 2018 13:58:46 -0800 Subject: [PATCH 24/58] Rename "local variables" to "explicit stack slots". The term "local variables" predated the SSA builder in the front-end crate, which also provides a way to implement source-language local variables. The name "explicit stack slot" makes it clear what this construct is. --- cranelift/docs/example.cton | 2 +- cranelift/docs/langref.rst | 23 ++++++++++++------- .../isa/intel/prologue-epilogue.cton | 6 ++--- cranelift/filetests/parser/tiny.cton | 4 ++-- lib/cretonne/src/ir/stackslot.rs | 12 +++++----- lib/cretonne/src/stack_layout.rs | 17 +++++++------- lib/cretonne/src/write.rs | 10 ++++---- lib/reader/src/parser.rs | 2 +- 8 files changed, 42 insertions(+), 34 deletions(-) diff --git a/cranelift/docs/example.cton b/cranelift/docs/example.cton index 1a3117c521..a36fef3238 100644 --- a/cranelift/docs/example.cton +++ b/cranelift/docs/example.cton @@ -1,7 +1,7 @@ test verifier function %average(i32, i32) -> f32 native { - ss1 = local 8 ; Stack slot for ``sum``. + ss1 = explicit_slot 8 ; Stack slot for ``sum``. ebb1(v1: i32, v2: i32): v3 = f64const 0x0.0 diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index d80554dc67..476e0a262d 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -37,7 +37,7 @@ The first line of a function definition provides the function *name* and the :term:`function signature` which declares the parameter and return types. Then follows the :term:`function preamble` which declares a number of entities that can be referenced inside the function. In the example above, the preamble -declares a single local variable, ``ss1``. +declares a single explicit stack slot, ``ss1``. After the preamble follows the :term:`function body` which consists of :term:`extended basic block`\s (EBBs), the first of which is the @@ -471,8 +471,8 @@ the expected alignment. By default, misaligned loads and stores are allowed, but when the ``aligned`` flag is set, a misaligned memory access is allowed to :term:`trap`. -Local variables ---------------- +Explicit Stack Slots +-------------------- One set of restricted memory operations access the current function's stack frame. The stack frame is divided into fixed-size stack slots that are @@ -480,9 +480,9 @@ allocated in the :term:`function preamble`. Stack slots are not typed, they simply represent a contiguous sequence of :term:`accessible` bytes in the stack frame. -.. inst:: SS = local Bytes, Flags... +.. inst:: SS = explicit_slot Bytes, Flags... - Allocate a stack slot for a local variable in the preamble. + Allocate a stack slot in the preamble. If no alignment is specified, Cretonne will pick an appropriate alignment for the stack slot based on its size and access patterns. @@ -1135,7 +1135,7 @@ Glossary A list of declarations of entities that are used by the function body. Some of the entities that can be declared in the preamble are: - - Local variables. + - Stack slots. - Functions that are called directly. - Function signatures for indirect function calls. - Function flags and attributes that are not part of the signature. @@ -1158,9 +1158,16 @@ Glossary intermediate representation. Cretonne's IR can be converted to text losslessly. - stack slot + explicit stack slot A fixed size memory allocation in the current function's activation - frame. Also called a local variable. + frame. These differ from :term:`spill stack slots` in that they can + be created by frontends and they may have their addresses taken. + + spill stack slot + A fixed size memory allocation in the current function's activation + frame. These differ from :term:`explicit stack slots` in that they are + only created during register allocation, and they may not have their + address taken. terminator instruction A control flow instruction that unconditionally directs the flow of diff --git a/cranelift/filetests/isa/intel/prologue-epilogue.cton b/cranelift/filetests/isa/intel/prologue-epilogue.cton index 425b790fbf..fc53f72008 100644 --- a/cranelift/filetests/isa/intel/prologue-epilogue.cton +++ b/cranelift/filetests/isa/intel/prologue-epilogue.cton @@ -4,13 +4,13 @@ set is_compressed isa intel haswell function %foo() { - ss0 = local 168 + ss0 = explicit_slot 168 ebb0: return } ; check: function %foo(i64 fp [%rbp], i64 csr [%rbx], i64 csr [%r12], i64 csr [%r13], i64 csr [%r14], i64 csr [%r15]) -> i64 fp [%rbp], i64 csr [%rbx], i64 csr [%r12], i64 csr [%r13], i64 csr [%r14], i64 csr [%r15] native { -; nextln: ss0 = local 168, offset -224 +; nextln: ss0 = explicit_slot 168, offset -224 ; nextln: ss1 = incoming_arg 56, offset -56 ; check: ebb0(v0: i64 [%rbp], v1: i64 [%rbx], v2: i64 [%r12], v3: i64 [%r13], v4: i64 [%r14], v5: i64 [%r15]): ; nextln: x86_push v0 @@ -29,4 +29,4 @@ ebb0: ; nextln: v7 = x86_pop.i64 ; nextln: v6 = x86_pop.i64 ; nextln: return v6, v7, v8, v9, v10, v11 -; nextln: } \ No newline at end of file +; nextln: } diff --git a/cranelift/filetests/parser/tiny.cton b/cranelift/filetests/parser/tiny.cton index a5a6489808..0c619565a0 100644 --- a/cranelift/filetests/parser/tiny.cton +++ b/cranelift/filetests/parser/tiny.cton @@ -124,7 +124,7 @@ ebb0(v90: i32, v91: f32): ; Stack slot references function %stack() { ss10 = spill_slot 8 - ss2 = local 4 + ss2 = explicit_slot 4 ss3 = incoming_arg 4, offset 8 ss4 = outgoing_arg 4 ss5 = emergency_slot 4 @@ -136,7 +136,7 @@ ebb0: stack_store v2, ss2 } ; sameln: function %stack() native { -; check: ss2 = local 4 +; check: ss2 = explicit_slot 4 ; check: ss3 = incoming_arg 4, offset 8 ; check: ss4 = outgoing_arg 4 ; check: ss5 = emergency_slot 4 diff --git a/lib/cretonne/src/ir/stackslot.rs b/lib/cretonne/src/ir/stackslot.rs index 4a0804787b..172a43e3af 100644 --- a/lib/cretonne/src/ir/stackslot.rs +++ b/lib/cretonne/src/ir/stackslot.rs @@ -40,9 +40,9 @@ pub enum StackSlotKind { /// A spill slot. This is a stack slot created by the register allocator. SpillSlot, - /// A local variable. This is a chunk of local stack memory for use by the `stack_load` and - /// `stack_store` instructions. - Local, + /// An explicit stack slot. This is a chunk of stack memory for use by the `stack_load` + /// and `stack_store` instructions. + ExplicitSlot, /// An incoming function argument. /// @@ -71,7 +71,7 @@ impl FromStr for StackSlotKind { fn from_str(s: &str) -> Result { use self::StackSlotKind::*; match s { - "local" => Ok(Local), + "explicit_slot" => Ok(ExplicitSlot), "spill_slot" => Ok(SpillSlot), "incoming_arg" => Ok(IncomingArg), "outgoing_arg" => Ok(OutgoingArg), @@ -85,7 +85,7 @@ impl fmt::Display for StackSlotKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::StackSlotKind::*; f.write_str(match *self { - Local => "local", + ExplicitSlot => "explicit_slot", SpillSlot => "spill_slot", IncomingArg => "incoming_arg", OutgoingArg => "outgoing_arg", @@ -366,7 +366,7 @@ mod tests { assert_eq!(slot.alignment(8), 8); assert_eq!(slot.alignment(16), 8); - let slot2 = StackSlotData::new(StackSlotKind::Local, 24); + let slot2 = StackSlotData::new(StackSlotKind::ExplicitSlot, 24); assert_eq!(slot2.alignment(4), 4); assert_eq!(slot2.alignment(8), 8); diff --git a/lib/cretonne/src/stack_layout.rs b/lib/cretonne/src/stack_layout.rs index 796850e059..7d0550dbba 100644 --- a/lib/cretonne/src/stack_layout.rs +++ b/lib/cretonne/src/stack_layout.rs @@ -7,8 +7,8 @@ use std::cmp::{min, max}; /// Compute the stack frame layout. /// -/// Determine the total size of this stack frame and assign offsets to all `Spill` and `Local` -/// stack slots. +/// Determine the total size of this stack frame and assign offsets to all `Spill` and +/// `Explicit` stack slots. /// /// The total frame size will be a multiple of `alignment` which must be a power of two. /// @@ -25,7 +25,7 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result Result { - // Determine the smallest alignment of any local or spill slot. + // Determine the smallest alignment of any explicit or spill slot. min_align = slot.alignment(min_align); } } } - // Lay out spill slots and locals below the incoming arguments. + // Lay out spill slots and explicit slots below the incoming arguments. // The offset is negative, growing downwards. // Start with the smallest alignments for better packing. let mut offset = incoming_min; @@ -74,9 +74,10 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result { + StackSlotKind::SpillSlot | + StackSlotKind::ExplicitSlot => { if slot.alignment(alignment) != min_align { continue; } diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index d9853d5768..02f0fa3ab2 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -475,29 +475,29 @@ mod tests { f.name = ExternalName::testcase("foo"); assert_eq!(f.to_string(), "function %foo() native {\n}\n"); - f.create_stack_slot(StackSlotData::new(StackSlotKind::Local, 4)); + f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); assert_eq!( f.to_string(), - "function %foo() native {\n ss0 = local 4\n}\n" + "function %foo() native {\n ss0 = explicit_slot 4\n}\n" ); let ebb = f.dfg.make_ebb(); f.layout.append_ebb(ebb); assert_eq!( f.to_string(), - "function %foo() native {\n ss0 = local 4\n\nebb0:\n}\n" + "function %foo() native {\n ss0 = explicit_slot 4\n\nebb0:\n}\n" ); f.dfg.append_ebb_param(ebb, types::I8); assert_eq!( f.to_string(), - "function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8):\n}\n" + "function %foo() native {\n ss0 = explicit_slot 4\n\nebb0(v0: i8):\n}\n" ); f.dfg.append_ebb_param(ebb, types::F32.by(4).unwrap()); assert_eq!( f.to_string(), - "function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8, v1: f32x4):\n}\n" + "function %foo() native {\n ss0 = explicit_slot 4\n\nebb0(v0: i8, v1: f32x4):\n}\n" ); } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 47f14129a9..62a26128ca 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -1044,7 +1044,7 @@ impl<'a> Parser<'a> { // Parse a stack slot decl. // // stack-slot-decl ::= * StackSlot(ss) "=" stack-slot-kind Bytes {"," stack-slot-flag} - // stack-slot-kind ::= "local" + // stack-slot-kind ::= "explicit_slot" // | "spill_slot" // | "incoming_arg" // | "outgoing_arg" From 162ca42b08c83403985389c1a267b6ef6a5df1b7 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Mon, 5 Mar 2018 10:52:19 +0700 Subject: [PATCH 25/58] Update term dep to 0.5 --- cranelift/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index d762d64d82..37b4456e82 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -24,7 +24,7 @@ serde = "1.0.8" serde_derive = "1.0.8" num_cpus = "1.5.1" tempdir="0.3.5" -term = "0.4.6" +term = "0.5" [workspace] From 125270e2b0aed7efcd0fa41c23c6648d4bb13d6a Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Mon, 5 Mar 2018 10:48:48 +0700 Subject: [PATCH 26/58] Fix some typos. --- cranelift/docs/langref.rst | 4 ++-- cranelift/src/compile.rs | 2 +- cranelift/src/filetest/concurrent.rs | 2 +- cranelift/src/filetest/runner.rs | 2 +- lib/cretonne/src/regalloc/reload.rs | 2 +- lib/filecheck/src/error.rs | 2 +- lib/filecheck/src/pattern.rs | 6 +++--- lib/filecheck/src/variable.rs | 2 +- lib/filecheck/tests/basic.rs | 2 +- lib/frontend/src/frontend.rs | 6 +++--- lib/frontend/src/ssa.rs | 16 ++++++++-------- lib/reader/src/testcommand.rs | 2 +- lib/reader/src/testfile.rs | 2 +- lib/wasm/src/code_translator.rs | 4 ++-- lib/wasm/src/environ/spec.rs | 2 +- lib/wasm/src/module_translator.rs | 2 +- lib/wasm/src/sections_translator.rs | 2 +- 17 files changed, 30 insertions(+), 30 deletions(-) diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index 476e0a262d..b91f1b3f47 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -440,7 +440,7 @@ Cretonne provides fully general :inst:`load` and :inst:`store` instructions for accessing memory, as well as :ref:`extending loads and truncating stores `. -If the memory at the given addresss is not :term:`addressable`, the behavior of +If the memory at the given address is not :term:`addressable`, the behavior of these instructions is undefined. If it is addressable but not :term:`accessible`, they :term:`trap`. @@ -559,7 +559,7 @@ runtime data structures. The address of GV can be computed by first loading a pointer from BaseGV and adding Offset to it. - It is assumed the BaseGV resides in readable memory with the apropriate + It is assumed the BaseGV resides in readable memory with the appropriate alignment for storing a pointer. Chains of ``deref`` global variables are possible, but cycles are not diff --git a/cranelift/src/compile.rs b/cranelift/src/compile.rs index ae0c87ad36..fd2e5d5552 100644 --- a/cranelift/src/compile.rs +++ b/cranelift/src/compile.rs @@ -37,7 +37,7 @@ fn handle_module( let test_file = parse_test(&buffer).map_err(|e| format!("{}: {}", name, e))?; // If we have an isa from the command-line, use that. Otherwise if the - // file contins a unique isa, use that. + // file contains a unique isa, use that. let isa = if let Some(isa) = fisa.isa { isa } else if let Some(isa) = test_file.isa_spec.unique_isa() { diff --git a/cranelift/src/filetest/concurrent.rs b/cranelift/src/filetest/concurrent.rs index cb188577e0..a651c14071 100644 --- a/cranelift/src/filetest/concurrent.rs +++ b/cranelift/src/filetest/concurrent.rs @@ -119,7 +119,7 @@ fn worker_thread( loop { // Lock the mutex only long enough to extract a request. let Request(jobid, path) = match requests.lock().unwrap().recv() { - Err(..) => break, // TX end shuit down. exit thread. + Err(..) => break, // TX end shut down. exit thread. Ok(req) => req, }; diff --git a/cranelift/src/filetest/runner.rs b/cranelift/src/filetest/runner.rs index 320db9d942..09ec3a19a9 100644 --- a/cranelift/src/filetest/runner.rs +++ b/cranelift/src/filetest/runner.rs @@ -221,7 +221,7 @@ impl TestRunner { } self.tests[jobid].state = State::Done(result); - // Rports jobs in order. + // Reports jobs in order. while self.report_job() { self.reported_tests += 1; } diff --git a/lib/cretonne/src/regalloc/reload.rs b/lib/cretonne/src/regalloc/reload.rs index 2cb37d7d6a..12fd8d310a 100644 --- a/lib/cretonne/src/regalloc/reload.rs +++ b/lib/cretonne/src/regalloc/reload.rs @@ -298,7 +298,7 @@ impl<'a> Context<'a> { } } - // Find reload candidates for `inst` and add them to `self.condidates`. + // Find reload candidates for `inst` and add them to `self.candidates`. // // These are uses of spilled values where the operand constraint requires a register. fn find_candidates(&mut self, inst: Inst, constraints: &RecipeConstraints) { diff --git a/lib/filecheck/src/error.rs b/lib/filecheck/src/error.rs index 5580cc4c3c..f15cb17124 100644 --- a/lib/filecheck/src/error.rs +++ b/lib/filecheck/src/error.rs @@ -19,7 +19,7 @@ pub enum Error { UndefVariable(String), /// A pattern contains a back-reference to a variable that was defined in the same pattern. /// - /// For example, `check: Hello $(world=.*) $world`. Backreferences are not support. Often the + /// For example, `check: Hello $(world=.*) $world`. Backreferences are not supported. Often the /// desired effect can be achieved with the `sameln` check: /// /// ```text diff --git a/lib/filecheck/src/pattern.rs b/lib/filecheck/src/pattern.rs index 934150dca7..97977b191c 100644 --- a/lib/filecheck/src/pattern.rs +++ b/lib/filecheck/src/pattern.rs @@ -42,7 +42,7 @@ pub enum Part { } impl Part { - /// Get the variabled referenced by this part, if any. + /// Get the variable referenced by this part, if any. pub fn ref_var(&self) -> Option<&str> { match *self { Part::Var(ref var) | @@ -217,10 +217,10 @@ impl Pattern { } /// Compute the length of a regular expression terminated by `)` or `}`. -/// Handle nested and escaped parentheses in the rx, but don't actualy parse it. +/// Handle nested and escaped parentheses in the rx, but don't actually parse it. /// Return the position of the terminating brace or the length of the string. fn regex_prefix(s: &str) -> usize { - // The prevous char was a backslash. + // The previous char was a backslash. let mut escape = false; // State around parsing charsets. enum State { diff --git a/lib/filecheck/src/variable.rs b/lib/filecheck/src/variable.rs index 1a43f1428a..5977f06354 100644 --- a/lib/filecheck/src/variable.rs +++ b/lib/filecheck/src/variable.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; /// A variable name is one or more ASCII alphanumerical characters, including underscore. /// Note that numerical variable names like `$45` are allowed too. /// -/// Try to parse a variable name from the begining of `s`. +/// Try to parse a variable name from the beginning of `s`. /// Return the index of the character following the varname. /// This returns 0 if `s` doesn't have a prefix that is a variable name. pub fn varname_prefix(s: &str) -> usize { diff --git a/lib/filecheck/tests/basic.rs b/lib/filecheck/tests/basic.rs index debced2a1b..1ccc5991a5 100644 --- a/lib/filecheck/tests/basic.rs +++ b/lib/filecheck/tests/basic.rs @@ -153,7 +153,7 @@ fn nextln() { #[test] fn leading_nextln() { // 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 preceding 'check: $()' directive. let c = CheckerBuilder::new() .text( " diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs index 432288e4eb..fba2f89a01 100644 --- a/lib/frontend/src/frontend.rs +++ b/lib/frontend/src/frontend.rs @@ -12,7 +12,7 @@ use cretonne::packed_option::PackedOption; /// Structure used for translating a series of functions into Cretonne IL. /// -/// In order to reduce memory reallocations whem compiling multiple functions, +/// In order to reduce memory reallocations when compiling multiple functions, /// `ILBuilder` holds various data structures which are cleared between /// functions, rather than dropped, preserving the underlying allocations. pub struct ILBuilder @@ -259,7 +259,7 @@ where /// block, in the order they are declared. You must declare the types of the Ebb arguments /// you will use here. /// - /// When inserting the terminator instruction (which doesn't have a falltrough to its immediate + /// When inserting the terminator instruction (which doesn't have a fallthrough to its immediate /// successor), the block will be declared filled and it will not be possible to append /// instructions to it. pub fn switch_to_block(&mut self, ebb: Ebb) { @@ -283,7 +283,7 @@ where /// Declares that all the predecessors of this block are known. /// /// Function to call with `ebb` as soon as the last branch instruction to `ebb` has been - /// 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 inconsistencies in the /// produced functions. pub fn seal_block(&mut self, ebb: Ebb) { let side_effects = self.builder.ssa.seal_ebb_header_block(ebb, self.func); diff --git a/lib/frontend/src/ssa.rs b/lib/frontend/src/ssa.rs index 2b758ef18b..aa6aa81166 100644 --- a/lib/frontend/src/ssa.rs +++ b/lib/frontend/src/ssa.rs @@ -35,7 +35,7 @@ pub struct SSABuilder where Variable: EntityRef, { - // Records for every variable and for every revelant block, the last definition of + // Records for every variable and for every relevant block, the last definition of // the variable in the block. variables: EntityMap>>, // Records the position of the basic blocks and the list of values used but not defined in the @@ -81,7 +81,7 @@ enum BlockData { // A block at the top of an `Ebb`. EbbHeader(EbbHeaderBlockData), // A block inside an `Ebb` with an unique other block as its predecessor. - // The block is implicitely sealed at creation. + // The block is implicitly sealed at creation. EbbBody { predecessor: Block }, } @@ -394,7 +394,7 @@ where /// Remove a previously declared Ebb predecessor by giving a reference to the jump /// instruction. Returns the basic block containing the instruction. /// - /// Note: use only when you know what you are doing, this might break the SSA bbuilding problem + /// Note: use only when you know what you are doing, this might break the SSA building problem pub fn remove_ebb_predecessor(&mut self, ebb: Ebb, inst: Inst) -> Block { debug_assert!(!self.is_sealed(ebb)); let header_block = self.header_block(ebb); @@ -422,7 +422,7 @@ where pub fn seal_all_ebb_header_blocks(&mut self, func: &mut Function) -> SideEffects { // Seal all `Ebb`s currently in the function. This can entail splitting // and creation of new blocks, however such new blocks are sealed on - // the fly, so we don't need to accout for them here. + // the fly, so we don't need to account for them here. for ebb in self.ebb_headers.keys() { self.seal_one_ebb_header_block(ebb, func); } @@ -470,8 +470,8 @@ where } } - /// 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 + /// Look up in the predecessors of an Ebb the def for a value an decides whether or not + /// to keep the eeb arg, and act accordingly. Returns the chosen value and optionally a /// list of Ebb that are the middle of newly created critical edges splits. fn predecessors_lookup( &mut self, @@ -554,7 +554,7 @@ where ZeroOneOrMore::One(pred_val) => { // Here all the predecessors use a single value to represent our variable // so we don't need to have it as an ebb argument. - // We need to replace all the occurences of val with pred_val but since + // We need to replace all the occurrences of val with pred_val but since // we can't afford a re-writing pass right now we just declare an alias. // Resolve aliases eagerly so that we can check for cyclic aliasing, // which can occur in unreachable code. @@ -1119,7 +1119,7 @@ mod tests { #[test] fn undef() { - // Use vars of varous types which have not been defined. + // Use vars of various types which have not been defined. let mut func = Function::new(); let mut ssa: SSABuilder = SSABuilder::new(); let ebb0 = func.dfg.make_ebb(); diff --git a/lib/reader/src/testcommand.rs b/lib/reader/src/testcommand.rs index ad856e9949..2b848a9348 100644 --- a/lib/reader/src/testcommand.rs +++ b/lib/reader/src/testcommand.rs @@ -9,7 +9,7 @@ //! //! The options are either a single identifier flag, or setting values like `identifier=value`. //! -//! The parser does not understand the test commands or which options are alid. It simply parses +//! The parser does not understand the test commands or which options are valid. It simply parses //! the general format into a `TestCommand` data structure. use std::fmt::{self, Display, Formatter}; diff --git a/lib/reader/src/testfile.rs b/lib/reader/src/testfile.rs index 20e03939a1..8ec5271a07 100644 --- a/lib/reader/src/testfile.rs +++ b/lib/reader/src/testfile.rs @@ -42,7 +42,7 @@ pub struct Details<'a> { /// A comment in a parsed function. /// -/// The comment belongs to the immediately preceeding entity, whether that is an EBB header, and +/// The comment belongs to the immediately preceding entity, whether that is an EBB header, and /// instruction, or one of the preamble declarations. /// /// Comments appearing inside the function but before the preamble, as well as comments appearing diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 21df608f74..086646fa29 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -51,7 +51,7 @@ pub fn translate_operator( match *op { /********************************** Locals **************************************** * `get_local` and `set_local` are treated as non-SSA variables and will completely - * diseappear in the Cretonne Code + * disappear in the Cretonne Code ***********************************************************************************/ Operator::GetLocal { local_index } => state.push1(builder.use_var(Local(local_index))), Operator::SetLocal { local_index } => { @@ -1003,7 +1003,7 @@ fn translate_unreachable_operator( builder.switch_to_block(frame.following_code()); builder.seal_block(frame.following_code()); - // And add the return values of the block but only if the next block is reachble + // And add the return values of the block but only if the next block is reachable // (which corresponds to testing if the stack depth is 1) stack.extend_from_slice(builder.ebb_params(frame.following_code())); state.reachable = true; diff --git a/lib/wasm/src/environ/spec.rs b/lib/wasm/src/environ/spec.rs index 31e4782918..732438a573 100644 --- a/lib/wasm/src/environ/spec.rs +++ b/lib/wasm/src/environ/spec.rs @@ -146,7 +146,7 @@ pub trait FuncEnvironment { ) -> ir::Value; } -/// An object satisfyng the `ModuleEnvironment` trait can be passed as argument to the +/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the /// [`translate_module`](fn.translate_module.html) function. These methods should not be called /// by the user, they are only for `cretonne-wasm` internal use. pub trait ModuleEnvironment<'data> { diff --git a/lib/wasm/src/module_translator.rs b/lib/wasm/src/module_translator.rs index d50031fb04..df640cbb49 100644 --- a/lib/wasm/src/module_translator.rs +++ b/lib/wasm/src/module_translator.rs @@ -1,4 +1,4 @@ -//! Translation skeletton that traverses the whole WebAssembly module and call helper functions +//! Translation skeleton that traverses the whole WebAssembly module and call helper functions //! to deal with each part of it. use cretonne::timing; use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder, BinaryReaderError}; diff --git a/lib/wasm/src/sections_translator.rs b/lib/wasm/src/sections_translator.rs index d02a4a7c2a..359510547a 100644 --- a/lib/wasm/src/sections_translator.rs +++ b/lib/wasm/src/sections_translator.rs @@ -115,7 +115,7 @@ pub fn parse_import_section<'data>( Ok(()) } -/// Retrieves the correspondances between functions and signatures from the function section +/// Retrieves the correspondences between functions and signatures from the function section pub fn parse_function_section( parser: &mut Parser, environ: &mut ModuleEnvironment, From 505fe9277af946cc683fcf856f3129148217f5e0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 1 Mar 2018 06:43:16 -0800 Subject: [PATCH 27/58] Tidy up calls to `analyze_branch`. --- lib/cretonne/src/dominator_tree.rs | 2 +- lib/cretonne/src/flowgraph.rs | 2 +- lib/cretonne/src/regalloc/coloring.rs | 2 +- lib/cretonne/src/regalloc/live_value_tracker.rs | 2 +- lib/cretonne/src/verifier/locations.rs | 2 +- lib/cretonne/src/verifier/mod.rs | 2 +- lib/frontend/src/ssa.rs | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/cretonne/src/dominator_tree.rs b/lib/cretonne/src/dominator_tree.rs index f7b7273681..27caf338dc 100644 --- a/lib/cretonne/src/dominator_tree.rs +++ b/lib/cretonne/src/dominator_tree.rs @@ -340,7 +340,7 @@ impl DominatorTree { /// post-order except for the insertion of the new EBB header at the split point. fn push_successors(&mut self, func: &Function, ebb: Ebb) { for inst in func.layout.ebb_insts(ebb) { - match func.dfg[inst].analyze_branch(&func.dfg.value_lists) { + match func.dfg.analyze_branch(inst) { BranchInfo::SingleDest(succ, _) => { if self.nodes[succ].rpo_number == 0 { self.nodes[succ].rpo_number = SEEN; diff --git a/lib/cretonne/src/flowgraph.rs b/lib/cretonne/src/flowgraph.rs index d1e45312ac..fbc74b79d0 100644 --- a/lib/cretonne/src/flowgraph.rs +++ b/lib/cretonne/src/flowgraph.rs @@ -108,7 +108,7 @@ impl ControlFlowGraph { fn compute_ebb(&mut self, func: &Function, ebb: Ebb) { for inst in func.layout.ebb_insts(ebb) { - match func.dfg[inst].analyze_branch(&func.dfg.value_lists) { + match func.dfg.analyze_branch(inst) { BranchInfo::SingleDest(dest, _) => { self.add_edge((ebb, inst), dest); } diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index 74d80e1832..fb60a979c8 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -908,7 +908,7 @@ impl<'a> Context<'a> { let inst = self.cur.current_inst().expect("Not on an instruction"); let ctx = self.liveness.context(&self.cur.func.layout); - match self.cur.func.dfg[inst].analyze_branch(&self.cur.func.dfg.value_lists) { + match self.cur.func.dfg.analyze_branch(inst) { NotABranch => false, SingleDest(ebb, _) => { let lr = &self.liveness[value]; diff --git a/lib/cretonne/src/regalloc/live_value_tracker.rs b/lib/cretonne/src/regalloc/live_value_tracker.rs index d0810a5cf7..3d7eeaa6e5 100644 --- a/lib/cretonne/src/regalloc/live_value_tracker.rs +++ b/lib/cretonne/src/regalloc/live_value_tracker.rs @@ -260,7 +260,7 @@ impl LiveValueTracker { ) -> (&[LiveValue], &[LiveValue], &[LiveValue]) { // Save a copy of the live values before any branches or jumps that could be somebody's // immediate dominator. - match dfg[inst].analyze_branch(&dfg.value_lists) { + match dfg.analyze_branch(inst) { BranchInfo::NotABranch => {} _ => self.save_idom_live_set(inst), } diff --git a/lib/cretonne/src/verifier/locations.rs b/lib/cretonne/src/verifier/locations.rs index 2c93d0de3e..535d04f0ef 100644 --- a/lib/cretonne/src/verifier/locations.rs +++ b/lib/cretonne/src/verifier/locations.rs @@ -274,7 +274,7 @@ impl<'a> LocationVerifier<'a> { }; let dfg = &self.func.dfg; - match dfg[inst].analyze_branch(&dfg.value_lists) { + match dfg.analyze_branch(inst) { NotABranch => { panic!( "No branch information for {}", diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index a346573e67..9bc4c0459d 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -693,7 +693,7 @@ impl<'a> Verifier<'a> { } fn typecheck_variable_args(&self, inst: Inst) -> Result { - match self.func.dfg[inst].analyze_branch(&self.func.dfg.value_lists) { + match self.func.dfg.analyze_branch(inst) { BranchInfo::SingleDest(ebb, _) => { let iter = self.func.dfg.ebb_params(ebb).iter().map(|&v| { self.func.dfg.value_type(v) diff --git a/lib/frontend/src/ssa.rs b/lib/frontend/src/ssa.rs index aa6aa81166..06dba9d4ed 100644 --- a/lib/frontend/src/ssa.rs +++ b/lib/frontend/src/ssa.rs @@ -620,7 +620,7 @@ where val: Value, var: Variable, ) -> Option<(Ebb, Block, Inst)> { - match func.dfg[jump_inst].analyze_branch(&func.dfg.value_lists) { + match func.dfg.analyze_branch(jump_inst) { BranchInfo::NotABranch => { panic!("you have declared a non-branch instruction as a predecessor to an ebb"); } @@ -855,7 +855,7 @@ mod tests { cur.ins().iadd(x_use3, y_use3) }; ssa.def_var(y_var, y2_ssa, block2); - match func.dfg[jump_inst].analyze_branch(&func.dfg.value_lists) { + match func.dfg.analyze_branch(jump_inst) { BranchInfo::SingleDest(dest, jump_args) => { assert_eq!(dest, ebb1); assert_eq!(jump_args.len(), 0); From 804b56d0f2b254ee5f0eb9d05b153bf961777551 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 2 Mar 2018 13:16:01 -0800 Subject: [PATCH 28/58] Document that "enable_float=false" isn't implemented yet. --- lib/cretonne/meta/base/settings.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/cretonne/meta/base/settings.py b/lib/cretonne/meta/base/settings.py index 8d0522af79..e8785fbc08 100644 --- a/lib/cretonne/meta/base/settings.py +++ b/lib/cretonne/meta/base/settings.py @@ -56,7 +56,11 @@ avoid_div_traps = BoolSetting( is_compressed = BoolSetting("Enable compressed instructions") enable_float = BoolSetting( - """Enable the use of floating-point instructions""", + """ + Enable the use of floating-point instructions + + Disabling use of floating-point instructions is not yet implemented. + """, default=True) enable_simd = BoolSetting( From aac006ed4933c230a99736f4ebb8afe4929677ca Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 2 Mar 2018 13:20:11 -0800 Subject: [PATCH 29/58] Avoid trivial numeric casts. --- lib/cretonne/src/divconst_magic_numbers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cretonne/src/divconst_magic_numbers.rs b/lib/cretonne/src/divconst_magic_numbers.rs index 64416e48fe..a34608359f 100644 --- a/lib/cretonne/src/divconst_magic_numbers.rs +++ b/lib/cretonne/src/divconst_magic_numbers.rs @@ -498,14 +498,14 @@ mod tests { println!("Testing UP magicU64"); for x in 2..(200 * 1000u64) { let m = magicU64(x); - total = total ^ (m.mulBy as u64); + total = total ^ m.mulBy; total = total + (m.shiftBy as u64); total = total - (if m.doAdd { 123 } else { 456 }); } println!("Testing DOWN magicU64"); for x in 0..(200 * 1000u64) { let m = magicU64(0xFFFF_FFFF_FFFF_FFFFu64 - x); - total = total ^ (m.mulBy as u64); + total = total ^ m.mulBy; total = total + (m.shiftBy as u64); total = total - (if m.doAdd { 123 } else { 456 }); } From a301280d9456ffc1f3170b5038dbc192ec290ff0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 2 Mar 2018 13:22:09 -0800 Subject: [PATCH 30/58] Change the stack alignment for 32-bit x86 to 16. Spiderwasm on 32-bit x86 always uses a 16-byte-aligned stack pointer. Change the setting for the "native" convention as well, for compatibility with Linux and Darwin ABIs, and so that if a platform has different ABI rules, the problem will be detected in code emitted by Cretonne, rather than somewhere else. --- lib/cretonne/src/isa/intel/abi.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/cretonne/src/isa/intel/abi.rs b/lib/cretonne/src/isa/intel/abi.rs index cf30058364..c7dd1af93c 100644 --- a/lib/cretonne/src/isa/intel/abi.rs +++ b/lib/cretonne/src/isa/intel/abi.rs @@ -180,11 +180,9 @@ pub fn spiderwasm_prologue_epilogue( func: &mut ir::Function, isa: &TargetIsa, ) -> result::CtonResult { - let (word_size, stack_align) = if isa.flags().is_64bit() { - (8, 16) - } else { - (4, 4) - }; + // Spiderwasm on 32-bit x86 always aligns its stack pointer to 16 bytes. + let stack_align = 16; + let word_size = if isa.flags().is_64bit() { 8 } else { 4 }; let bytes = StackSize::from(isa.flags().spiderwasm_prologue_words()) * word_size; let mut ss = ir::StackSlotData::new(ir::StackSlotKind::IncomingArg, bytes); @@ -197,11 +195,10 @@ pub fn spiderwasm_prologue_epilogue( /// Insert a System V-compatible prologue and epilogue. pub fn native_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> result::CtonResult { - let (word_size, stack_align) = if isa.flags().is_64bit() { - (8, 16) - } else { - (4, 4) - }; + // The original 32-bit x86 ELF ABI had a 4-byte aligned stack pointer, but + // newer versions use a 16-byte aligned stack pointer. + let stack_align = 16; + let word_size = if isa.flags().is_64bit() { 8 } else { 4 }; let csr_type = if isa.flags().is_64bit() { ir::types::I64 } else { From 13b167770c112930604b6da18c4897f808d518bf Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 3 Mar 2018 13:21:10 -0800 Subject: [PATCH 31/58] Include emergency stack slots when laying out the stack. Emergency stack slots are a new kind of stack slot added relatively recently. They need to be allocated a stack offset just like explicit and spill slots. Also, make StackSlotData's offset field an Option, to catch problems like this in the future. Previously the value 0 was used when offsets weren't assigned yet, however that made it non-obvious when the field meant "not assigned yet" and when it meant "assigned the value 0". --- cranelift/src/filetest/binemit.rs | 2 +- lib/cretonne/src/ir/stackslot.rs | 22 +++--- lib/cretonne/src/isa/intel/abi.rs | 4 +- lib/cretonne/src/isa/mod.rs | 2 +- lib/cretonne/src/isa/stack.rs | 4 +- lib/cretonne/src/stack_layout.rs | 99 ++++++++++++++++---------- lib/cretonne/src/verifier/locations.rs | 4 +- lib/cretonne/src/verifier/mod.rs | 2 +- lib/reader/src/parser.rs | 2 +- 9 files changed, 81 insertions(+), 60 deletions(-) diff --git a/cranelift/src/filetest/binemit.rs b/cranelift/src/filetest/binemit.rs index 550703f0bf..d2d179fea8 100644 --- a/cranelift/src/filetest/binemit.rs +++ b/cranelift/src/filetest/binemit.rs @@ -126,7 +126,7 @@ impl SubTest for TestBinEmit { // Fix the stack frame layout so we can test spill/fill encodings. let min_offset = func.stack_slots .keys() - .map(|ss| func.stack_slots[ss].offset) + .map(|ss| func.stack_slots[ss].offset.unwrap()) .min(); func.stack_slots.frame_size = min_offset.map(|off| (-off) as u32); diff --git a/lib/cretonne/src/ir/stackslot.rs b/lib/cretonne/src/ir/stackslot.rs index 172a43e3af..d3578a7570 100644 --- a/lib/cretonne/src/ir/stackslot.rs +++ b/lib/cretonne/src/ir/stackslot.rs @@ -111,7 +111,7 @@ pub struct StackSlotData { /// /// For `OutgoingArg` stack slots, the offset is relative to the current function's stack /// pointer immediately before the call. - pub offset: StackOffset, + pub offset: Option, } impl StackSlotData { @@ -120,7 +120,7 @@ impl StackSlotData { StackSlotData { kind, size, - offset: 0, + offset: None, } } @@ -138,8 +138,8 @@ impl StackSlotData { impl fmt::Display for StackSlotData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} {}", self.kind, self.size)?; - if self.offset != 0 { - write!(f, ", offset {}", self.offset)?; + if let Some(offset) = self.offset { + write!(f, ", offset {}", offset)?; } Ok(()) } @@ -204,7 +204,7 @@ impl StackSlots { /// Set the offset of a stack slot. pub fn set_offset(&mut self, ss: StackSlot, offset: StackOffset) { - self.slots[ss].offset = offset; + self.slots[ss].offset = Some(offset); } /// Get an iterator over all the stack slot keys. @@ -245,7 +245,7 @@ impl StackSlots { pub fn make_incoming_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot { let mut data = StackSlotData::new(StackSlotKind::IncomingArg, ty.bytes()); assert!(offset <= StackOffset::max_value() - data.size as StackOffset); - data.offset = offset; + data.offset = Some(offset); self.push(data) } @@ -261,7 +261,7 @@ impl StackSlots { // Look for an existing outgoing stack slot with the same offset and size. let inspos = match self.outgoing.binary_search_by_key(&(offset, size), |&ss| { - (self[ss].offset, self[ss].size) + (self[ss].offset.unwrap(), self[ss].size) }) { Ok(idx) => return self.outgoing[idx], Err(idx) => idx, @@ -270,7 +270,7 @@ impl StackSlots { // No existing slot found. Make one and insert it into `outgoing`. let mut data = StackSlotData::new(StackSlotKind::OutgoingArg, size); assert!(offset <= StackOffset::max_value() - size as StackOffset); - data.offset = offset; + data.offset = Some(offset); let ss = self.slots.push(data); self.outgoing.insert(inspos, ss); ss @@ -344,13 +344,13 @@ mod tests { let ss1 = sss.get_outgoing_arg(types::I32, 4); let ss2 = sss.get_outgoing_arg(types::I64, 8); - assert_eq!(sss[ss0].offset, 8); + assert_eq!(sss[ss0].offset, Some(8)); assert_eq!(sss[ss0].size, 4); - assert_eq!(sss[ss1].offset, 4); + assert_eq!(sss[ss1].offset, Some(4)); assert_eq!(sss[ss1].size, 4); - assert_eq!(sss[ss2].offset, 8); + assert_eq!(sss[ss2].offset, Some(8)); assert_eq!(sss[ss2].size, 8); assert_eq!(sss.get_outgoing_arg(types::I32, 8), ss0); diff --git a/lib/cretonne/src/isa/intel/abi.rs b/lib/cretonne/src/isa/intel/abi.rs index c7dd1af93c..2296a9292b 100644 --- a/lib/cretonne/src/isa/intel/abi.rs +++ b/lib/cretonne/src/isa/intel/abi.rs @@ -186,7 +186,7 @@ pub fn spiderwasm_prologue_epilogue( let bytes = StackSize::from(isa.flags().spiderwasm_prologue_words()) * word_size; let mut ss = ir::StackSlotData::new(ir::StackSlotKind::IncomingArg, bytes); - ss.offset = -(bytes as StackOffset); + ss.offset = Some(-(bytes as StackOffset)); func.stack_slots.push(ss); layout_stack(&mut func.stack_slots, stack_align)?; @@ -217,7 +217,7 @@ pub fn native_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> res func.create_stack_slot(ir::StackSlotData { kind: ir::StackSlotKind::IncomingArg, size: csr_stack_size as u32, - offset: -csr_stack_size, + offset: Some(-csr_stack_size), }); let total_stack_size = layout_stack(&mut func.stack_slots, stack_align)? as i32; diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index 38164e5167..28b297860c 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -251,7 +251,7 @@ pub trait TargetIsa: fmt::Display { if func.signature.call_conv == ir::CallConv::SpiderWASM { let bytes = StackSize::from(self.flags().spiderwasm_prologue_words()) * word_size; let mut ss = ir::StackSlotData::new(ir::StackSlotKind::IncomingArg, bytes); - ss.offset = -(bytes as StackOffset); + ss.offset = Some(-(bytes as StackOffset)); func.stack_slots.push(ss); } diff --git a/lib/cretonne/src/isa/stack.rs b/lib/cretonne/src/isa/stack.rs index 730db28e18..e8a9b6981b 100644 --- a/lib/cretonne/src/isa/stack.rs +++ b/lib/cretonne/src/isa/stack.rs @@ -41,12 +41,12 @@ impl StackRef { let slot = &frame[ss]; let offset = if slot.kind == StackSlotKind::OutgoingArg { // Outgoing argument slots have offsets relative to our stack pointer. - slot.offset + slot.offset.unwrap() } else { // All other slots have offsets relative to our caller's stack frame. // Offset where SP is pointing. (All ISAs have stacks growing downwards.) let sp_offset = -(size as StackOffset); - slot.offset - sp_offset + slot.offset.unwrap() - sp_offset }; StackRef { base: StackBase::SP, diff --git a/lib/cretonne/src/stack_layout.rs b/lib/cretonne/src/stack_layout.rs index 7d0550dbba..16dcea8d5f 100644 --- a/lib/cretonne/src/stack_layout.rs +++ b/lib/cretonne/src/stack_layout.rs @@ -48,12 +48,13 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result { - incoming_min = min(incoming_min, slot.offset); + incoming_min = min(incoming_min, slot.offset.unwrap()); } StackSlotKind::OutgoingArg => { - let offset = slot.offset.checked_add(slot.size as StackOffset).ok_or( - CtonError::ImplLimitExceeded, - )?; + let offset = slot.offset + .unwrap() + .checked_add(slot.size as StackOffset) + .ok_or(CtonError::ImplLimitExceeded)?; outgoing_max = max(outgoing_max, offset); } StackSlotKind::SpillSlot | @@ -77,12 +78,14 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result { + StackSlotKind::ExplicitSlot | + StackSlotKind::EmergencySlot => { if slot.alignment(alignment) != min_align { continue; } } - _ => continue, + StackSlotKind::IncomingArg | + StackSlotKind::OutgoingArg => continue, } offset = offset.checked_sub(slot.size as StackOffset).ok_or( @@ -111,7 +114,7 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result LocationVerifier<'a> { slot.kind ); } - if slot.offset != offset { + if slot.offset.unwrap() != offset { return err!( inst, "ABI expects {} at stack offset {}, but {} is at {}", value, offset, ss, - slot.offset + slot.offset.unwrap() ); } } else { diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index 9bc4c0459d..33fd3582cb 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -808,7 +808,7 @@ impl<'a> Verifier<'a> { slot ); } - if slot.offset != offset { + if slot.offset != Some(offset) { return err!( inst, "Outgoing stack argument {} should have offset {}: {} = {}", diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 62a26128ca..a9568942ed 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -1070,7 +1070,7 @@ impl<'a> Parser<'a> { // Take additional options. while self.optional(Token::Comma) { match self.match_any_identifier("expected stack slot flags")? { - "offset" => data.offset = self.match_imm32("expected byte offset")?, + "offset" => data.offset = Some(self.match_imm32("expected byte offset")?), other => return err!(self.loc, "Unknown stack slot flag '{}'", other), } } From d2d02565fbf2d550a9cccf4fd1c8a844e6a400ed Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Mon, 5 Mar 2018 20:23:50 +0700 Subject: [PATCH 32/58] I64Extend16S should operate on 16 bits, not 8. This showed up in clippy warnings as the code for this case was the same as for `I64Extend8S`. --- lib/wasm/src/code_translator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 086646fa29..765b1a4577 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -654,7 +654,7 @@ pub fn translate_operator( } Operator::I64Extend16S => { let val = state.pop1(); - state.push1(builder.ins().ireduce(I8, val)); + state.push1(builder.ins().ireduce(I16, val)); let val = state.pop1(); state.push1(builder.ins().sextend(I64, val)); } From 0ea0146e58a7b54793435e347f8a26de9f8b8b26 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 2 Mar 2018 22:53:07 -0800 Subject: [PATCH 33/58] Update to sphinx-autogen 1.5.6. --- cranelift/docs/Makefile | 192 ++----------------------------- cranelift/docs/conf.py | 63 +++++++--- cranelift/docs/make.bat | 247 ++-------------------------------------- 3 files changed, 70 insertions(+), 432 deletions(-) diff --git a/cranelift/docs/Makefile b/cranelift/docs/Makefile index 635aafde1c..335779f1f3 100644 --- a/cranelift/docs/Makefile +++ b/cranelift/docs/Makefile @@ -1,196 +1,24 @@ -# Makefile for Sphinx documentation +# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXABUILD = sphinx-autobuild -PAPER = +SPHINXPROJ = cretonne +SOURCEDIR = . BUILDDIR = _build -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext - +# Put it first so that "make" without argument is like "make help". help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) autohtml: html $(SPHINXABUILD) -z ../lib/cretonne/meta --ignore '.*' -b html -E $(ALLSPHINXOPTS) $(BUILDDIR)/html -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." +.PHONY: help Makefile -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/cretonne.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/cretonne.qhc" - -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/cretonne" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/cretonne" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/cranelift/docs/conf.py b/cranelift/docs/conf.py index 0603a27bfd..20cfd32f91 100644 --- a/cranelift/docs/conf.py +++ b/cranelift/docs/conf.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # cretonne documentation build configuration file, created by -# sphinx-quickstart on Fri Jan 8 10:11:19 2016. +# sphinx-quickstart on Fri Mar 2 12:49:24 2018. # # This file is execfile()d with the current directory set to its # containing dir. @@ -12,14 +12,13 @@ # All configuration values have a default; values that are commented out # serve to show the default. -from __future__ import absolute_import -import sys -import os - - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. + +from __future__ import absolute_import +import os +import sys sys.path.insert(0, os.path.abspath('.')) # Also add the meta directory to sys.path so autodoc can find the Cretonne meta @@ -28,6 +27,10 @@ sys.path.insert(0, os.path.abspath('../lib/cretonne/meta')) # -- General configuration ------------------------------------------------ +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. @@ -47,6 +50,7 @@ templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: +# # source_suffix = ['.rst', '.md'] source_suffix = '.rst' @@ -55,7 +59,7 @@ master_doc = 'index' # General information about the project. project = u'cretonne' -copyright = u'2016, Cretonne Developers' +copyright = u'2018, Cretonne Developers' author = u'Cretonne Developers' # The version info for the project you're documenting, acts as replacement for @@ -76,7 +80,8 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -89,22 +94,54 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. +# html_theme = 'sphinx_rtd_theme' +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# +# html_static_path = ['_static'] + + +# -- Options for HTMLHelp output ------------------------------------------ + # Output file base name for HTML help builder. htmlhelp_basename = 'cretonnedoc' + # -- Options for LaTeX output --------------------------------------------- latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'cretonne.tex', u'cretonne Documentation', - author, 'manual'), + (master_doc, 'cretonne.tex', u'cretonne Documentation', + author, 'manual'), ] @@ -124,9 +161,9 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'cretonne', u'cretonne Documentation', - author, 'cretonne', 'One line description of project.', - 'Miscellaneous'), + (master_doc, 'cretonne', u'cretonne Documentation', + author, 'cretonne', 'One line description of project.', + 'Miscellaneous'), ] diff --git a/cranelift/docs/make.bat b/cranelift/docs/make.bat index 3f6fe2e48d..9267ac038e 100644 --- a/cranelift/docs/make.bat +++ b/cranelift/docs/make.bat @@ -1,62 +1,19 @@ @ECHO OFF +pushd %~dp0 + REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) +set SOURCEDIR=. set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) +set SPHINXPROJ=cretonne if "%1" == "" goto help -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - echo. coverage to run coverage check of the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -REM Check if sphinx-build is available and fallback to Python version if any -%SPHINXBUILD% 1>NUL 2>NUL -if errorlevel 9009 goto sphinx_python -goto sphinx_ok - -:sphinx_python - -set SPHINXBUILD=python -m sphinx.__init__ -%SPHINXBUILD% 2> nul +%SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx @@ -69,195 +26,11 @@ if errorlevel 9009 ( exit /b 1 ) -:sphinx_ok +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\cretonne.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\cretonne.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "coverage" ( - %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage - if errorlevel 1 exit /b 1 - echo. - echo.Testing of coverage in the sources finished, look at the ^ -results in %BUILDDIR%/coverage/python.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end +popd From c59e9180decea441e783979caaa4df84b35273f5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sun, 4 Mar 2018 22:37:34 -0800 Subject: [PATCH 34/58] Tidy up whitespace. --- lib/cretonne/meta/base/instructions.py | 6 +++--- lib/cretonne/meta/cdsl/test_ti.py | 2 +- lib/cretonne/meta/cdsl/ti.py | 12 ++++++------ lib/cretonne/meta/cdsl/xform.py | 4 ++-- lib/cretonne/meta/gen_legalizer.py | 2 +- lib/cretonne/meta/isa/intel/instructions.py | 4 ++-- lib/cretonne/meta/semantics/__init__.py | 2 +- lib/cretonne/src/bforest/path.rs | 2 +- lib/cretonne/src/divconst_magic_numbers.rs | 10 +++++----- lib/cretonne/src/legalizer/boundary.rs | 2 +- lib/cretonne/src/preopt.rs | 18 +++++++++--------- lib/wasm/src/code_translator.rs | 2 +- 12 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index 7db8bed202..8ba1aa65fa 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -1699,7 +1699,7 @@ fpromote = Instruction( This is an exact operation. Cretonne currently only supports two floating point formats - - :type:`f32` and :type:`f64`. This may change in the future. + - :type:`f32` and :type:`f64`. This may change in the future. The result type must have the same number of vector lanes as the input, and the result lanes must not have fewer bits than the input lanes. If @@ -1715,10 +1715,10 @@ fdemote = Instruction( by rounding to nearest, ties to even. Cretonne currently only supports two floating point formats - - :type:`f32` and :type:`f64`. This may change in the future. + - :type:`f32` and :type:`f64`. This may change in the future. The result type must have the same number of vector lanes as the input, - and the result lanes must not have more bits than the input lanes. If + and the result lanes must not have more bits than the input lanes. If the input and output types are the same, this is a no-op. """, ins=x, outs=a, constraints=WiderOrEq(Float, FloatTo)) diff --git a/lib/cretonne/meta/cdsl/test_ti.py b/lib/cretonne/meta/cdsl/test_ti.py index b9b3e0b646..d3baa4d3c5 100644 --- a/lib/cretonne/meta/cdsl/test_ti.py +++ b/lib/cretonne/meta/cdsl/test_ti.py @@ -96,7 +96,7 @@ def check_concrete_typing_rtl(var_types, rtl): # type: (VarTyping, Rtl) -> None """ Check that a concrete type assignment var_types (Dict[Var, TypeVar]) is - valid for an Rtl rtl. Specifically check that: + valid for an Rtl rtl. Specifically check that: 1) For each Var v \in rtl, v is defined in var_types diff --git a/lib/cretonne/meta/cdsl/ti.py b/lib/cretonne/meta/cdsl/ti.py index b2673366be..bc2c16d5b1 100644 --- a/lib/cretonne/meta/cdsl/ti.py +++ b/lib/cretonne/meta/cdsl/ti.py @@ -322,7 +322,7 @@ class TypeEnv(object): # type: (TypeVar, TypeVar) -> None """ Record a that the free tv1 is part of the same equivalence class as - tv2. The canonical representative of the merged class is tv2's + tv2. The canonical representative of the merged class is tv2's cannonical representative. """ assert not tv1.is_derived @@ -364,9 +364,9 @@ class TypeEnv(object): # type: (TypeVar) -> int """ Get the rank of tv in the partial order. TVs directly associated with a - Var get their rank from the Var (see register()). Internally generated + Var get their rank from the Var (see register()). Internally generated non-derived TVs implicitly get the lowest rank (0). Derived variables - get their rank from their free typevar. Singletons have the highest + get their rank from their free typevar. Singletons have the highest rank. TVs associated with vars in a source pattern have a higher rank than TVs associted with temporary vars. """ @@ -381,7 +381,7 @@ class TypeEnv(object): def register(self, v): # type: (Var) -> None """ - Register a new Var v. This computes a rank for the associated TypeVar + Register a new Var v. This computes a rank for the associated TypeVar for v, which is used to impose a partial order on type variables. """ self.vars.add(v) @@ -848,7 +848,7 @@ def ti_def(definition, typ): def ti_rtl(rtl, typ): # type: (Rtl, TypeEnv) -> TypingOrError """ - Perform type inference on an Rtl in a starting type env typ. Return an + Perform type inference on an Rtl in a starting type env typ. Return an updated type environment or error. """ for (i, d) in enumerate(rtl.rtl): @@ -866,7 +866,7 @@ def ti_rtl(rtl, typ): def ti_xform(xform, typ): # type: (XForm, TypeEnv) -> TypingOrError """ - Perform type inference on an Rtl in a starting type env typ. Return an + Perform type inference on an Rtl in a starting type env typ. Return an updated type environment or error. """ typ_or_err = ti_rtl(xform.src, typ) diff --git a/lib/cretonne/meta/cdsl/xform.py b/lib/cretonne/meta/cdsl/xform.py index bad15e0245..607c1776a2 100644 --- a/lib/cretonne/meta/cdsl/xform.py +++ b/lib/cretonne/meta/cdsl/xform.py @@ -113,8 +113,8 @@ class Rtl(object): # type: (Rtl) -> None """ Given that there is only 1 possible concrete typing T for self, assign - a singleton TV with type t=T[v] for each Var v \in self. Its an error - to call this on an Rtl with more than 1 possible typing. This modifies + a singleton TV with type t=T[v] for each Var v \in self. Its an error + to call this on an Rtl with more than 1 possible typing. This modifies the Rtl in-place. """ from .ti import ti_rtl, TypeEnv diff --git a/lib/cretonne/meta/gen_legalizer.py b/lib/cretonne/meta/gen_legalizer.py index 51368fea44..0f7b1f68e4 100644 --- a/lib/cretonne/meta/gen_legalizer.py +++ b/lib/cretonne/meta/gen_legalizer.py @@ -211,7 +211,7 @@ def unwrap_inst(iref, node, fmt): fmt.format('let typeof_{0} = pos.func.dfg.value_type({0});', v) # If the node has results, detach the values. - # Place the values in locals. + # Place the values in locals. replace_inst = False if len(node.defs) > 0: if node.defs == node.defs[0].dst_def.defs: diff --git a/lib/cretonne/meta/isa/intel/instructions.py b/lib/cretonne/meta/isa/intel/instructions.py index e5265e2d15..1fe75ff984 100644 --- a/lib/cretonne/meta/isa/intel/instructions.py +++ b/lib/cretonne/meta/isa/intel/instructions.py @@ -154,7 +154,7 @@ rflags = Operand('rflags', iflags) bsr = Instruction( 'x86_bsr', r""" Bit Scan Reverse -- returns the bit-index of the most significant 1 - in the word. Result is undefined if the argument is zero. However, it + in the word. Result is undefined if the argument is zero. However, it sets the Z flag depending on the argument, so it is at least easy to detect and handle that case. @@ -166,7 +166,7 @@ bsr = Instruction( bsf = Instruction( 'x86_bsf', r""" Bit Scan Forwards -- returns the bit-index of the least significant 1 - in the word. Is otherwise identical to 'bsr', just above. + in the word. Is otherwise identical to 'bsr', just above. """, ins=x, outs=(y, rflags)) diff --git a/lib/cretonne/meta/semantics/__init__.py b/lib/cretonne/meta/semantics/__init__.py index 94e32b652c..8a55b1d595 100644 --- a/lib/cretonne/meta/semantics/__init__.py +++ b/lib/cretonne/meta/semantics/__init__.py @@ -17,7 +17,7 @@ def verify_semantics(inst, src, xforms): # type: (Instruction, Rtl, InstructionSemantics) -> None """ Verify that the semantics transforms in xforms correctly describe the - instruction described by the src Rtl. This involves checking that: + instruction described by the src Rtl. This involves checking that: 0) src is a single instance of inst 1) For all x\in xforms x.src is a single instance of inst 2) For any concrete values V of Literals in inst: diff --git a/lib/cretonne/src/bforest/path.rs b/lib/cretonne/src/bforest/path.rs index 3393014fee..c0f9550645 100644 --- a/lib/cretonne/src/bforest/path.rs +++ b/lib/cretonne/src/bforest/path.rs @@ -285,7 +285,7 @@ impl Path { fn split_and_insert(&mut self, mut key: F::Key, value: F::Value, pool: &mut NodePool) { let orig_root = self.node[0]; - // Loop invariant: We need to split the node at `level` and then retry a failed insertion. + // Loop invariant: We need to split the node at `level` and then retry a failed insertion. // The items to insert are either `(key, ins_node)` or `(key, value)`. let mut ins_node = None; let mut split; diff --git a/lib/cretonne/src/divconst_magic_numbers.rs b/lib/cretonne/src/divconst_magic_numbers.rs index a34608359f..968ca67f5c 100644 --- a/lib/cretonne/src/divconst_magic_numbers.rs +++ b/lib/cretonne/src/divconst_magic_numbers.rs @@ -4,11 +4,11 @@ //---------------------------------------------------------------------- // -// Math helpers for division by (non-power-of-2) constants. This is based -// on the presentation in "Hacker's Delight" by Henry Warren, 2003. There -// are four cases: {unsigned, signed} x {32 bit, 64 bit}. The word size +// Math helpers for division by (non-power-of-2) constants. This is based +// on the presentation in "Hacker's Delight" by Henry Warren, 2003. There +// are four cases: {unsigned, signed} x {32 bit, 64 bit}. The word size // makes little difference, but the signed-vs-unsigned aspect has a large -// effect. Therefore everything is presented in the order U32 U64 S32 S64 +// effect. Therefore everything is presented in the order U32 U64 S32 S64 // so as to emphasise the similarity of the U32 and U64 cases and the S32 // and S64 cases. @@ -478,7 +478,7 @@ mod tests { fn test_magic_generators_dont_panic() { // The point of this is to check that the magic number generators // don't panic with integer wraparounds, especially at boundary - // cases for their arguments. The actual results are thrown away. + // cases for their arguments. The actual results are thrown away. let mut total: u64 = 0; println!("Testing UP magicU32"); for x in 2..(200 * 1000u32) { diff --git a/lib/cretonne/src/legalizer/boundary.rs b/lib/cretonne/src/legalizer/boundary.rs index ea81c1ff95..9ea50b202d 100644 --- a/lib/cretonne/src/legalizer/boundary.rs +++ b/lib/cretonne/src/legalizer/boundary.rs @@ -180,7 +180,7 @@ where // We theoretically allow for call instructions that return a number of fixed results before // the call return values. In practice, it doesn't happen. let fixed_results = pos.func.dfg[call].opcode().constraints().fixed_results(); - assert_eq!(fixed_results, 0, "Fixed results on calls not supported"); + assert_eq!(fixed_results, 0, "Fixed results on calls not supported"); let results = pos.func.dfg.detach_results(call); let mut next_res = 0; diff --git a/lib/cretonne/src/preopt.rs b/lib/cretonne/src/preopt.rs index 0e4582d7a7..1fdee0ad80 100644 --- a/lib/cretonne/src/preopt.rs +++ b/lib/cretonne/src/preopt.rs @@ -20,7 +20,7 @@ use timing; // Simple math helpers // if `x` is a power of two, or the negation thereof, return the power along -// with a boolean that indicates whether `x` is negative. Else return None. +// with a boolean that indicates whether `x` is negative. Else return None. #[inline] fn isPowerOf2_S32(x: i32) -> Option<(bool, u32)> { // We have to special-case this because abs(x) isn't representable. @@ -128,10 +128,10 @@ fn get_div_info(inst: Inst, dfg: &DataFlowGraph) -> Option { } // TODO: should we actually bother to do this (that is, manually match - // the case that the second argument is an iconst)? Or should we assume + // the case that the second argument is an iconst)? Or should we assume // that some previous constant propagation pass has pushed all such // immediates to their use points, creating BinaryImm instructions - // instead? For now we take the conservative approach. + // instead? For now we take the conservative approach. if let &InstructionData::Binary { opcode, args } = idata { let (isSigned, isRem) = match opcode { Opcode::Udiv => (false, false), @@ -153,10 +153,10 @@ fn get_div_info(inst: Inst, dfg: &DataFlowGraph) -> Option { } // Actually do the transformation given a bundle containing the relevant -// information. `divrem_info` describes a div or rem by a constant, that +// information. `divrem_info` describes a div or rem by a constant, that // `pos` currently points at, and `inst` is the associated instruction. // `inst` is replaced by a sequence of other operations that calculate the -// same result. Note that there are various `divrem_info` cases where we +// same result. Note that there are various `divrem_info` cases where we // cannot do any transformation, in which case `inst` is left unchanged. fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCursor, inst: Inst) { let isRem = match *divrem_info { @@ -234,7 +234,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso qf = q1; } } - // Now qf holds the final quotient. If necessary calculate the + // Now qf holds the final quotient. If necessary calculate the // remainder instead. if isRem { let tt = pos.ins().imul_imm(qf, d as i64); @@ -306,7 +306,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso qf = q1; } } - // Now qf holds the final quotient. If necessary calculate the + // Now qf holds the final quotient. If necessary calculate the // remainder instead. if isRem { let tt = pos.ins().imul_imm(qf, d as i64); @@ -382,7 +382,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso }; let t1 = pos.ins().ushr_imm(q3, 31); let qf = pos.ins().iadd(q3, t1); - // Now qf holds the final quotient. If necessary calculate + // Now qf holds the final quotient. If necessary calculate // the remainder instead. if isRem { let tt = pos.ins().imul_imm(qf, d as i64); @@ -459,7 +459,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso }; let t1 = pos.ins().ushr_imm(q3, 63); let qf = pos.ins().iadd(q3, t1); - // Now qf holds the final quotient. If necessary calculate + // Now qf holds the final quotient. If necessary calculate // the remainder instead. if isRem { let tt = pos.ins().imul_imm(qf, d); diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 765b1a4577..5d7e59f829 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -90,7 +90,7 @@ pub fn translate_operator( } } /********************************* Stack misc *************************************** - * `drop`, `nop`, `unreachable` and `select`. + * `drop`, `nop`, `unreachable` and `select`. ***********************************************************************************/ Operator::Drop => { state.pop1(); From be84db8e86d44c3d07f80febea32e319e0b3933c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sun, 4 Mar 2018 22:40:36 -0800 Subject: [PATCH 35/58] Fix glossary references. --- cranelift/docs/langref.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index b91f1b3f47..3219f14b08 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -1158,14 +1158,19 @@ Glossary intermediate representation. Cretonne's IR can be converted to text losslessly. + stack slot + A fixed size memory allocation in the current function's activation + frame. These include :term:`explicit stack slot`\s and + :term:`spill stack slot`\s. + explicit stack slot A fixed size memory allocation in the current function's activation - frame. These differ from :term:`spill stack slots` in that they can + frame. These differ from :term:`spill stack slot`\s in that they can be created by frontends and they may have their addresses taken. spill stack slot A fixed size memory allocation in the current function's activation - frame. These differ from :term:`explicit stack slots` in that they are + frame. These differ from :term:`explicit stack slot`\s in that they are only created during register allocation, and they may not have their address taken. From 6e94e70f308e97c1a96eb07ca3d674426e928bc2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 05:37:19 -0800 Subject: [PATCH 36/58] Use an https URL rather than http. Found by sphinx's linkcheck. --- lib/cretonne/meta/isa/riscv/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cretonne/meta/isa/riscv/__init__.py b/lib/cretonne/meta/isa/riscv/__init__.py index cf61cbdcf5..f40086414d 100644 --- a/lib/cretonne/meta/isa/riscv/__init__.py +++ b/lib/cretonne/meta/isa/riscv/__init__.py @@ -2,7 +2,7 @@ RISC-V Target ------------- -`RISC-V `_ is an open instruction set architecture +`RISC-V `_ is an open instruction set architecture originally developed at UC Berkeley. It is a RISC-style ISA with either a 32-bit (RV32I) or 64-bit (RV32I) base instruction set and a number of optional extensions: From bf480c341b4624ebd0114a0b9fd732a4c409a9ad Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 05:47:56 -0800 Subject: [PATCH 37/58] Use https rather than http for several URLs. --- README.rst | 4 ++-- cranelift/docs/compare-llvm.rst | 14 +++++++------- cranelift/docs/langref.rst | 2 +- cranelift/docs/make.bat | 2 +- lib/cretonne/src/regalloc/coalescing.rs | 2 +- lib/cretonne/src/result.rs | 2 +- lib/filecheck/src/lib.rs | 2 +- lib/wasm/src/func_translator.rs | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index 05ddeb6b70..6e6ab59bb3 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Cretonne Code Generator ======================= Cretonne is a low-level retargetable code generator. It translates a `target-independent -intermediate language `_ into executable +intermediate language `_ into executable machine code. *This is a work in progress that is not yet functional.* @@ -61,7 +61,7 @@ Building the documentation -------------------------- To build the Cretonne documentation, you need the `Sphinx documentation -generator `_:: +generator `_:: $ pip install sphinx sphinx-autobuild sphinx_rtd_theme $ cd cretonne/docs diff --git a/cranelift/docs/compare-llvm.rst b/cranelift/docs/compare-llvm.rst index 03b82dc7f7..0180f6cc92 100644 --- a/cranelift/docs/compare-llvm.rst +++ b/cranelift/docs/compare-llvm.rst @@ -2,9 +2,9 @@ Cretonne compared to LLVM ************************* -`LLVM `_ is a collection of compiler components implemented as +`LLVM `_ is a collection of compiler components implemented as a set of C++ libraries. It can be used to build both JIT compilers and static -compilers like `Clang `_, and it is deservedly very +compilers like `Clang `_, and it is deservedly very popular. `Chris Lattner's chapter about LLVM `_ in the `Architecture of Open Source Applications `_ book gives an excellent @@ -40,7 +40,7 @@ Intermediate representations LLVM uses multiple intermediate representations as it translates a program to binary machine code: -`LLVM IR `_ +`LLVM IR `_ This is the primary intermediate language which has textual, binary, and in-memory representations. It serves two main purposes: @@ -49,7 +49,7 @@ binary machine code: - Intermediate representation for common mid-level optimizations. A large library of code analysis and transformation passes operate on LLVM IR. -`SelectionDAG `_ +`SelectionDAG `_ A graph-based representation of the code in a single basic block is used by the instruction selector. It has both ISA-agnostic and ISA-specific opcodes. These main passes are run on the SelectionDAG representation: @@ -65,7 +65,7 @@ binary machine code: The SelectionDAG representation automatically eliminates common subexpressions and dead code. -`MachineInstr `_ +`MachineInstr `_ A linear representation of ISA-specific instructions that initially is in SSA form, but it can also represent non-SSA form during and after register allocation. Many low-level optimizations run on MI code. The most important @@ -74,7 +74,7 @@ binary machine code: - Scheduling. - Register allocation. -`MC `_ +`MC `_ MC serves as the output abstraction layer and is the basis for LLVM's integrated assembler. It is used for: @@ -126,7 +126,7 @@ condition is false. The Cretonne representation is closer to how machine code works; LLVM's representation is more abstract. LLVM uses `phi instructions -`_ in its SSA +`_ in its SSA representation. Cretonne passes arguments to EBBs instead. The two representations are equivalent, but the EBB arguments are better suited to handle EBBs that may contain multiple branches to the same destination block diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index 3219f14b08..3a855c32b4 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -782,7 +782,7 @@ Integer operations For example, see `llvm.sadd.with.overflow.*` and `llvm.ssub.with.overflow.*` in - `LLVM `_. + `LLVM `_. .. autoinst:: imul .. autoinst:: imul_imm diff --git a/cranelift/docs/make.bat b/cranelift/docs/make.bat index 9267ac038e..2958a2ba6e 100644 --- a/cranelift/docs/make.bat +++ b/cranelift/docs/make.bat @@ -22,7 +22,7 @@ if errorlevel 9009 ( echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ + echo.https://sphinx-doc.org/ exit /b 1 ) diff --git a/lib/cretonne/src/regalloc/coalescing.rs b/lib/cretonne/src/regalloc/coalescing.rs index 416783b6c0..1dc06c6c2b 100644 --- a/lib/cretonne/src/regalloc/coalescing.rs +++ b/lib/cretonne/src/regalloc/coalescing.rs @@ -26,7 +26,7 @@ use timing; // The coalescing algorithm implemented follows this paper fairly closely: // // Budimlic, Z., Cooper, K. D., Harvey, T. J., et al. (2002). Fast copy coalescing and -// live-range identification (Vol. 37, pp. 25–32). ACM. http://doi.org/10.1145/543552.512534 +// live-range identification (Vol. 37, pp. 25–32). ACM. https://doi.org/10.1145/543552.512534 // // We use a more efficient dominator forest representation (a linear stack) described here: // diff --git a/lib/cretonne/src/result.rs b/lib/cretonne/src/result.rs index 387eaa9a2a..aa209a0845 100644 --- a/lib/cretonne/src/result.rs +++ b/lib/cretonne/src/result.rs @@ -26,7 +26,7 @@ pub enum CtonError { /// Cretonne can compile very large and complicated functions, but the [implementation has /// limits][limits] that cause compilation to fail when they are exceeded. /// - /// [limits]: http://cretonne.readthedocs.io/en/latest/langref.html#implementation-limits + /// [limits]: https://cretonne.readthedocs.io/en/latest/langref.html#implementation-limits ImplLimitExceeded, /// The code size for the function is too large. diff --git a/lib/filecheck/src/lib.rs b/lib/filecheck/src/lib.rs index 4c8a242897..4dec7a9b65 100644 --- a/lib/filecheck/src/lib.rs +++ b/lib/filecheck/src/lib.rs @@ -1,5 +1,5 @@ //! This crate provides a text pattern matching library with functionality similar to the LLVM -//! project's [FileCheck command](http://llvm.org/docs/CommandGuide/FileCheck.html). +//! project's [FileCheck command](https://llvm.org/docs/CommandGuide/FileCheck.html). //! //! A list of directives is typically extracted from a file containing a test case. The test case //! is then run through the program under test, and its output matched against the directives. diff --git a/lib/wasm/src/func_translator.rs b/lib/wasm/src/func_translator.rs index a9389342e9..7e8955e816 100644 --- a/lib/wasm/src/func_translator.rs +++ b/lib/wasm/src/func_translator.rs @@ -45,7 +45,7 @@ impl FuncTranslator { /// /// See [the WebAssembly specification][wasm]. /// - /// [wasm]: http://webassembly.github.io/spec/binary/modules.html#code-section + /// [wasm]: https://webassembly.github.io/spec/binary/modules.html#code-section /// /// The Cretonne IR function `func` should be completely empty except for the `func.signature` /// and `func.name` fields. The signature may contain special-purpose arguments which are not From d119524c90b98d9e293d0f850a191273b1151ab0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 07:11:23 -0800 Subject: [PATCH 38/58] Bump version to 0.3.1 --- cranelift/Cargo.toml | 12 ++++++------ cranelift/publish-all.sh | 2 +- lib/cretonne/Cargo.toml | 2 +- lib/frontend/Cargo.toml | 4 ++-- lib/native/Cargo.toml | 4 ++-- lib/reader/Cargo.toml | 4 ++-- lib/wasm/Cargo.toml | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index 37b4456e82..c948ea54ff 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cretonne-tools" authors = ["The Cretonne Project Developers"] -version = "0.3.0" +version = "0.3.1" description = "Binaries for testing the Cretonne library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -13,11 +13,11 @@ name = "cton-util" path = "src/cton-util.rs" [dependencies] -cretonne = { path = "lib/cretonne", version = "0.3.0" } -cretonne-reader = { path = "lib/reader", version = "0.3.0" } -cretonne-frontend = { path = "lib/frontend", version = "0.3.0" } -cretonne-wasm = { path = "lib/wasm", version = "0.3.0" } -cretonne-native = { path = "lib/native", version = "0.3.0" } +cretonne = { path = "lib/cretonne", version = "0.3.1" } +cretonne-reader = { path = "lib/reader", version = "0.3.1" } +cretonne-frontend = { path = "lib/frontend", version = "0.3.1" } +cretonne-wasm = { path = "lib/wasm", version = "0.3.1" } +cretonne-native = { path = "lib/native", version = "0.3.1" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index e8b22b4f5d..746d85a1fc 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -5,7 +5,7 @@ topdir="$(pwd)" # All the cretonne-* crates have the same version number # The filecheck crate version is managed independently. -version="0.3.0" +version="0.3.1" # Update all of the Cargo.toml files. # diff --git a/lib/cretonne/Cargo.toml b/lib/cretonne/Cargo.toml index 12ddbf2abe..d830e1ece5 100644 --- a/lib/cretonne/Cargo.toml +++ b/lib/cretonne/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne" -version = "0.3.0" +version = "0.3.1" description = "Low-level code generator library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" diff --git a/lib/frontend/Cargo.toml b/lib/frontend/Cargo.toml index 67297562b8..ace336de71 100644 --- a/lib/frontend/Cargo.toml +++ b/lib/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-frontend" -version = "0.3.0" +version = "0.3.1" description = "Cretonne IL builder helper" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_frontend" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.0" } +cretonne = { path = "../cretonne", version = "0.3.1" } diff --git a/lib/native/Cargo.toml b/lib/native/Cargo.toml index 1fe35a7190..3158a88a4e 100644 --- a/lib/native/Cargo.toml +++ b/lib/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-native" -version = "0.3.0" +version = "0.3.1" authors = ["The Cretonne Project Developers"] description = "Support for targeting the host with Cretonne" repository = "https://github.com/Cretonne/cretonne" @@ -11,7 +11,7 @@ readme = "README.md" name = "cton_native" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.0" } +cretonne = { path = "../cretonne", version = "0.3.1" } [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies] raw-cpuid = "3.0.0" diff --git a/lib/reader/Cargo.toml b/lib/reader/Cargo.toml index 14b721af35..250f296066 100644 --- a/lib/reader/Cargo.toml +++ b/lib/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-reader" -version = "0.3.0" +version = "0.3.1" description = "Cretonne textual IL reader" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_reader" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.0" } +cretonne = { path = "../cretonne", version = "0.3.1" } diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index 45e153357c..7acb1660ce 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-wasm" -version = "0.3.0" +version = "0.3.1" authors = ["The Cretonne Project Developers"] description = "Translator from WebAssembly to Cretonne IL" repository = "https://github.com/Cretonne/cretonne" @@ -13,8 +13,8 @@ name = "cton_wasm" [dependencies] wasmparser = "0.14.1" -cretonne = { path = "../cretonne", version = "0.3.0" } -cretonne-frontend = { path = "../frontend", version = "0.3.0" } +cretonne = { path = "../cretonne", version = "0.3.1" } +cretonne-frontend = { path = "../frontend", version = "0.3.1" } [dev-dependencies] tempdir = "0.3.5" From 1a4723831b6f5bc8e8a5dee33b530caa9b34a7c3 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 14:02:45 -0800 Subject: [PATCH 39/58] Add an encoding step to "cton-util compile". --- cranelift/src/compile.rs | 58 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/cranelift/src/compile.rs b/cranelift/src/compile.rs index fd2e5d5552..9a9644b1cf 100644 --- a/cranelift/src/compile.rs +++ b/cranelift/src/compile.rs @@ -6,9 +6,45 @@ use cton_reader::parse_test; use std::path::PathBuf; use cretonne::Context; use cretonne::settings::FlagsOrIsa; +use cretonne::{binemit, ir}; use std::path::Path; use utils::{pretty_error, read_to_string, parse_sets_and_isa}; +struct PrintRelocs { + flag_print: bool, +} + +impl binemit::RelocSink for PrintRelocs { + fn reloc_ebb( + &mut self, + where_: binemit::CodeOffset, + r: binemit::Reloc, + offset: binemit::CodeOffset, + ) { + if self.flag_print { + println!("reloc_ebb: {} {} at {}", r, offset, where_); + } + } + + fn reloc_external( + &mut self, + where_: binemit::CodeOffset, + r: binemit::Reloc, + name: &ir::ExternalName, + addend: binemit::Addend, + ) { + if self.flag_print { + println!("reloc_ebb: {} {} {} at {}", r, name, addend, where_); + } + } + + fn reloc_jt(&mut self, where_: binemit::CodeOffset, r: binemit::Reloc, jt: ir::JumpTable) { + if self.flag_print { + println!("reloc_ebb: {} {} at {}", r, jt, where_); + } + } +} + pub fn run( files: Vec, flag_print: bool, @@ -49,12 +85,32 @@ fn handle_module( for (func, _) in test_file.functions { let mut context = Context::new(); context.func = func; - context.compile(isa).map_err(|err| { + let size = context.compile(isa).map_err(|err| { pretty_error(&context.func, Some(isa), err) })?; if flag_print { println!("{}", context.func.display(isa)); } + + // Encode the result as machine code. + let mut mem = Vec::new(); + let mut relocs = PrintRelocs { flag_print }; + mem.resize(size as usize, 0); + context.emit_to_memory(mem.as_mut_ptr(), &mut relocs, &*isa); + + if flag_print { + print!(".byte "); + let mut first = true; + for byte in &mem { + if first { + first = false; + } else { + print!(", "); + } + print!("{}", byte); + } + println!(); + } } Ok(()) From ae0801e23b4a17be08fd24f0b41da836f43abf23 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 14:05:13 -0800 Subject: [PATCH 40/58] Disable creation of .pyc files. Cretonne's python scripts aren't run very often, so there's little benefit in creating .pyc files. And the .pyc files cause trouble for some vendoring scripts. So disable them. --- cranelift/test-all.sh | 4 ++++ lib/cretonne/build.rs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/cranelift/test-all.sh b/cranelift/test-all.sh index 6a9291d82e..2402eb7a39 100755 --- a/cranelift/test-all.sh +++ b/cranelift/test-all.sh @@ -11,6 +11,10 @@ set -euo pipefail # # All tests run by this script should be passing at all times. +# Disable generation of .pyc files because they cause trouble for vendoring +# scripts, and this is a build step that isn't run very often anyway. +export PYTHONDONTWRITEBYTECODE=1 + # Repository top-level directory. cd $(dirname "$0") topdir=$(pwd) diff --git a/lib/cretonne/build.rs b/lib/cretonne/build.rs index d2ef3905b1..69be14de94 100644 --- a/lib/cretonne/build.rs +++ b/lib/cretonne/build.rs @@ -59,8 +59,11 @@ fn main() { let build_script = meta_dir.join("build.py"); // Launch build script with Python. We'll just find python in the path. + // Use -B to disable .pyc files, because they cause trouble for vendoring + // scripts, and this is a build step that isn't run very often anyway. let status = process::Command::new("python") .current_dir(crate_dir) + .arg("-B") .arg(build_script) .arg("--out-dir") .arg(out_dir) From b1697dd1dc9e7479ee331603d76d04f4f93ea660 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 14:34:35 -0800 Subject: [PATCH 41/58] Style: Don't use `else` after a `return`. --- cranelift/src/filetest/binemit.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cranelift/src/filetest/binemit.rs b/cranelift/src/filetest/binemit.rs index d2d179fea8..3c52a63b2b 100644 --- a/cranelift/src/filetest/binemit.rs +++ b/cranelift/src/filetest/binemit.rs @@ -271,13 +271,12 @@ impl SubTest for TestBinEmit { "No encodings found for: {}", func.dfg.display_inst(inst, isa) )); - } else { - return Err(format!( + } + return Err(format!( "No matching encodings for {} in {}", func.dfg.display_inst(inst, isa), DisplayList(&encodings), )); - } } let have = sink.text.trim(); if have != want { From 136d6f5c4b0ee9727d5db24c545547d6099134bd Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 15:13:59 -0800 Subject: [PATCH 42/58] Implement ireduce, sextend, and uextend between i8/i16 and i32/i64. --- cranelift/filetests/isa/intel/binary32.cton | 32 ++++++ cranelift/filetests/isa/intel/binary64.cton | 112 ++++++++++++++++++++ lib/cretonne/meta/isa/intel/encodings.py | 48 ++++++++- 3 files changed, 191 insertions(+), 1 deletion(-) diff --git a/cranelift/filetests/isa/intel/binary32.cton b/cranelift/filetests/isa/intel/binary32.cton index 7d3e3a683b..df98a86515 100644 --- a/cranelift/filetests/isa/intel/binary32.cton +++ b/cranelift/filetests/isa/intel/binary32.cton @@ -552,3 +552,35 @@ ebb1: return } + +; Tests for i32/i8 conversion instructions. +function %I32_I8() { +ebb0: + [-,%rcx] v1 = iconst.i32 1 + + [-,%rcx] v11 = ireduce.i8 v1 ; bin: + + ; asm: movsbl %cl, %esi + [-,%rsi] v20 = sextend.i32 v11 ; bin: 0f be f1 + + ; asm: movzbl %cl, %esi + [-,%rsi] v30 = uextend.i32 v11 ; bin: 0f b6 f1 + + trap user0 ; bin: 0f 0b +} + +; Tests for i32/i16 conversion instructions. +function %I32_I16() { +ebb0: + [-,%rcx] v1 = iconst.i32 1 + + [-,%rcx] v11 = ireduce.i16 v1 ; bin: + + ; asm: movswl %cx, %esi + [-,%rsi] v20 = sextend.i32 v11 ; bin: 0f bf f1 + + ; asm: movzwl %cx, %esi + [-,%rsi] v30 = uextend.i32 v11 ; bin: 0f b7 f1 + + trap user0 ; bin: 0f 0b +} diff --git a/cranelift/filetests/isa/intel/binary64.cton b/cranelift/filetests/isa/intel/binary64.cton index b579b4d58d..9eca950ee6 100644 --- a/cranelift/filetests/isa/intel/binary64.cton +++ b/cranelift/filetests/isa/intel/binary64.cton @@ -1084,6 +1084,118 @@ ebb2: jump ebb1 ; bin: eb fd } +; Tests for i32/i8 conversion instructions. +function %I32_I8() { +ebb0: + [-,%rcx] v1 = iconst.i32 1 + [-,%rsi] v2 = iconst.i32 2 + [-,%r10] v3 = iconst.i32 3 + + [-,%rcx] v11 = ireduce.i8 v1 ; bin: + [-,%rsi] v12 = ireduce.i8 v2 ; bin: + [-,%r10] v13 = ireduce.i8 v3 ; bin: + + ; asm: movsbl %cl, %esi + [-,%rsi] v20 = sextend.i32 v11 ; bin: 0f be f1 + ; asm: movsbl %sil, %r10d + [-,%r10] v21 = sextend.i32 v12 ; bin: 44 0f be d6 + ; asm: movsbl %r10b, %ecx + [-,%rcx] v22 = sextend.i32 v13 ; bin: 41 0f be ca + + ; asm: movzbl %cl, %esi + [-,%rsi] v30 = uextend.i32 v11 ; bin: 0f b6 f1 + ; asm: movzbl %sil, %r10d + [-,%r10] v31 = uextend.i32 v12 ; bin: 44 0f b6 d6 + ; asm: movzbl %r10b, %ecx + [-,%rcx] v32 = uextend.i32 v13 ; bin: 41 0f b6 ca + + trap user0 ; bin: 0f 0b +} + +; Tests for i32/i16 conversion instructions. +function %I32_I16() { +ebb0: + [-,%rcx] v1 = iconst.i32 1 + [-,%rsi] v2 = iconst.i32 2 + [-,%r10] v3 = iconst.i32 3 + + [-,%rcx] v11 = ireduce.i16 v1 ; bin: + [-,%rsi] v12 = ireduce.i16 v2 ; bin: + [-,%r10] v13 = ireduce.i16 v3 ; bin: + + ; asm: movswl %cx, %esi + [-,%rsi] v20 = sextend.i32 v11 ; bin: 0f bf f1 + ; asm: movswl %si, %r10d + [-,%r10] v21 = sextend.i32 v12 ; bin: 44 0f bf d6 + ; asm: movswl %r10w, %ecx + [-,%rcx] v22 = sextend.i32 v13 ; bin: 41 0f bf ca + + ; asm: movzwl %cx, %esi + [-,%rsi] v30 = uextend.i32 v11 ; bin: 0f b7 f1 + ; asm: movzwl %si, %r10d + [-,%r10] v31 = uextend.i32 v12 ; bin: 44 0f b7 d6 + ; asm: movzwl %r10w, %ecx + [-,%rcx] v32 = uextend.i32 v13 ; bin: 41 0f b7 ca + + trap user0 ; bin: 0f 0b +} + +; Tests for i64/i8 conversion instructions. +function %I64_I8() { +ebb0: + [-,%rcx] v1 = iconst.i64 1 + [-,%rsi] v2 = iconst.i64 2 + [-,%r10] v3 = iconst.i64 3 + + [-,%rcx] v11 = ireduce.i8 v1 ; bin: + [-,%rsi] v12 = ireduce.i8 v2 ; bin: + [-,%r10] v13 = ireduce.i8 v3 ; bin: + + ; asm: movsbq %cl, %rsi + [-,%rsi] v20 = sextend.i64 v11 ; bin: 48 0f be f1 + ; asm: movsbq %sil, %r10 + [-,%r10] v21 = sextend.i64 v12 ; bin: 4c 0f be d6 + ; asm: movsbq %r10b, %rcx + [-,%rcx] v22 = sextend.i64 v13 ; bin: 49 0f be ca + + ; asm: movzbl %cl, %esi + [-,%rsi] v30 = uextend.i64 v11 ; bin: 0f b6 f1 + ; asm: movzbl %sil, %r10d + [-,%r10] v31 = uextend.i64 v12 ; bin: 44 0f b6 d6 + ; asm: movzbl %r10b, %ecx + [-,%rcx] v32 = uextend.i64 v13 ; bin: 41 0f b6 ca + + trap user0 ; bin: 0f 0b +} + +; Tests for i64/i16 conversion instructions. +function %I64_I16() { +ebb0: + [-,%rcx] v1 = iconst.i64 1 + [-,%rsi] v2 = iconst.i64 2 + [-,%r10] v3 = iconst.i64 3 + + [-,%rcx] v11 = ireduce.i16 v1 ; bin: + [-,%rsi] v12 = ireduce.i16 v2 ; bin: + [-,%r10] v13 = ireduce.i16 v3 ; bin: + + ; asm: movswq %cx, %rsi + [-,%rsi] v20 = sextend.i64 v11 ; bin: 48 0f bf f1 + ; asm: movswq %si, %r10 + [-,%r10] v21 = sextend.i64 v12 ; bin: 4c 0f bf d6 + ; asm: movswq %r10w, %rcx + [-,%rcx] v22 = sextend.i64 v13 ; bin: 49 0f bf ca + + ; asm: movzwl %cx, %esi + [-,%rsi] v30 = uextend.i64 v11 ; bin: 0f b7 f1 + ; asm: movzwl %si, %r10d + [-,%r10] v31 = uextend.i64 v12 ; bin: 44 0f b7 d6 + ; asm: movzwl %r10w, %ecx + [-,%rcx] v32 = uextend.i64 v13 ; bin: 41 0f b7 ca + + trap user0 ; bin: 0f 0b +} + ; Tests for i64/i32 conversion instructions. function %I64_I32() { ebb0: diff --git a/lib/cretonne/meta/isa/intel/encodings.py b/lib/cretonne/meta/isa/intel/encodings.py index f9b77a04bd..162caccf65 100644 --- a/lib/cretonne/meta/isa/intel/encodings.py +++ b/lib/cretonne/meta/isa/intel/encodings.py @@ -406,9 +406,55 @@ I64.enc(base.bint.i32.b1, *r.urm_abcd(0x0f, 0xb6)) # Numerical conversions. -# Converting i64 to i32 is a no-op in 64-bit mode. +# Reducing an integer is a no-op. +I32.enc(base.ireduce.i8.i32, r.null, 0) +I32.enc(base.ireduce.i16.i32, r.null, 0) +I64.enc(base.ireduce.i8.i32, r.null, 0) +I64.enc(base.ireduce.i16.i32, r.null, 0) +I64.enc(base.ireduce.i8.i64, r.null, 0) +I64.enc(base.ireduce.i16.i64, r.null, 0) I64.enc(base.ireduce.i32.i64, r.null, 0) + +# TODO: Add encodings for cbw, cwde, cdqe, which are sign-extending +# instructions for %al/%ax/%eax to %ax/%eax/%rax. + +# movsbl +I32.enc(base.sextend.i32.i8, *r.urm(0x0f, 0xbe)) +I64.enc(base.sextend.i32.i8, *r.urm.rex(0x0f, 0xbe)) +I64.enc(base.sextend.i32.i8, *r.urm(0x0f, 0xbe)) + +# movswl +I32.enc(base.sextend.i32.i16, *r.urm(0x0f, 0xbf)) +I64.enc(base.sextend.i32.i16, *r.urm.rex(0x0f, 0xbf)) +I64.enc(base.sextend.i32.i16, *r.urm(0x0f, 0xbf)) + +# movsbq +I64.enc(base.sextend.i64.i8, *r.urm.rex(0x0f, 0xbe, w=1)) + +# movswq +I64.enc(base.sextend.i64.i16, *r.urm.rex(0x0f, 0xbf, w=1)) + +# movslq I64.enc(base.sextend.i64.i32, *r.urm.rex(0x63, w=1)) + +# movzbl +I32.enc(base.uextend.i32.i8, *r.urm(0x0f, 0xb6)) +I64.enc(base.uextend.i32.i8, *r.urm.rex(0x0f, 0xb6)) +I64.enc(base.uextend.i32.i8, *r.urm(0x0f, 0xb6)) + +# movzwl +I32.enc(base.uextend.i32.i16, *r.urm(0x0f, 0xb7)) +I64.enc(base.uextend.i32.i16, *r.urm.rex(0x0f, 0xb7)) +I64.enc(base.uextend.i32.i16, *r.urm(0x0f, 0xb7)) + +# movzbq, encoded as movzbl because it's equivalent and shorter +I64.enc(base.uextend.i64.i8, *r.urm.rex(0x0f, 0xb6)) +I64.enc(base.uextend.i64.i8, *r.urm(0x0f, 0xb6)) + +# movzwq, encoded as movzwl because it's equivalent and shorter +I64.enc(base.uextend.i64.i16, *r.urm.rex(0x0f, 0xb7)) +I64.enc(base.uextend.i64.i16, *r.urm(0x0f, 0xb7)) + # A 32-bit register copy clears the high 32 bits. I64.enc(base.uextend.i64.i32, *r.umr.rex(0x89)) I64.enc(base.uextend.i64.i32, *r.umr(0x89)) From ee0bce4106711b686be12bf813e428ac8b58e0d7 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 5 Mar 2018 16:17:00 -0800 Subject: [PATCH 43/58] Bump version to 0.3.2 --- cranelift/Cargo.toml | 12 ++++++------ cranelift/publish-all.sh | 2 +- lib/cretonne/Cargo.toml | 2 +- lib/frontend/Cargo.toml | 4 ++-- lib/native/Cargo.toml | 4 ++-- lib/reader/Cargo.toml | 4 ++-- lib/wasm/Cargo.toml | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index c948ea54ff..a1479c63fc 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cretonne-tools" authors = ["The Cretonne Project Developers"] -version = "0.3.1" +version = "0.3.2" description = "Binaries for testing the Cretonne library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -13,11 +13,11 @@ name = "cton-util" path = "src/cton-util.rs" [dependencies] -cretonne = { path = "lib/cretonne", version = "0.3.1" } -cretonne-reader = { path = "lib/reader", version = "0.3.1" } -cretonne-frontend = { path = "lib/frontend", version = "0.3.1" } -cretonne-wasm = { path = "lib/wasm", version = "0.3.1" } -cretonne-native = { path = "lib/native", version = "0.3.1" } +cretonne = { path = "lib/cretonne", version = "0.3.2" } +cretonne-reader = { path = "lib/reader", version = "0.3.2" } +cretonne-frontend = { path = "lib/frontend", version = "0.3.2" } +cretonne-wasm = { path = "lib/wasm", version = "0.3.2" } +cretonne-native = { path = "lib/native", version = "0.3.2" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index 746d85a1fc..b380211212 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -5,7 +5,7 @@ topdir="$(pwd)" # All the cretonne-* crates have the same version number # The filecheck crate version is managed independently. -version="0.3.1" +version="0.3.2" # Update all of the Cargo.toml files. # diff --git a/lib/cretonne/Cargo.toml b/lib/cretonne/Cargo.toml index d830e1ece5..d6e91e695d 100644 --- a/lib/cretonne/Cargo.toml +++ b/lib/cretonne/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne" -version = "0.3.1" +version = "0.3.2" description = "Low-level code generator library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" diff --git a/lib/frontend/Cargo.toml b/lib/frontend/Cargo.toml index ace336de71..a09c692126 100644 --- a/lib/frontend/Cargo.toml +++ b/lib/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-frontend" -version = "0.3.1" +version = "0.3.2" description = "Cretonne IL builder helper" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_frontend" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.1" } +cretonne = { path = "../cretonne", version = "0.3.2" } diff --git a/lib/native/Cargo.toml b/lib/native/Cargo.toml index 3158a88a4e..5d2920a67a 100644 --- a/lib/native/Cargo.toml +++ b/lib/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-native" -version = "0.3.1" +version = "0.3.2" authors = ["The Cretonne Project Developers"] description = "Support for targeting the host with Cretonne" repository = "https://github.com/Cretonne/cretonne" @@ -11,7 +11,7 @@ readme = "README.md" name = "cton_native" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.1" } +cretonne = { path = "../cretonne", version = "0.3.2" } [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies] raw-cpuid = "3.0.0" diff --git a/lib/reader/Cargo.toml b/lib/reader/Cargo.toml index 250f296066..b0d32b2a61 100644 --- a/lib/reader/Cargo.toml +++ b/lib/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-reader" -version = "0.3.1" +version = "0.3.2" description = "Cretonne textual IL reader" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_reader" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.1" } +cretonne = { path = "../cretonne", version = "0.3.2" } diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index 7acb1660ce..086e8c9735 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-wasm" -version = "0.3.1" +version = "0.3.2" authors = ["The Cretonne Project Developers"] description = "Translator from WebAssembly to Cretonne IL" repository = "https://github.com/Cretonne/cretonne" @@ -13,8 +13,8 @@ name = "cton_wasm" [dependencies] wasmparser = "0.14.1" -cretonne = { path = "../cretonne", version = "0.3.1" } -cretonne-frontend = { path = "../frontend", version = "0.3.1" } +cretonne = { path = "../cretonne", version = "0.3.2" } +cretonne-frontend = { path = "../frontend", version = "0.3.2" } [dev-dependencies] tempdir = "0.3.5" From 6cf9bf36b8c66ad4ea82245d2f3aaffd25dc1c7e Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 8 Mar 2018 02:26:15 -0800 Subject: [PATCH 44/58] Fix a typo in a comment. --- lib/cretonne/src/isa/enc_tables.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cretonne/src/isa/enc_tables.rs b/lib/cretonne/src/isa/enc_tables.rs index 5154138c1b..7453b7c044 100644 --- a/lib/cretonne/src/isa/enc_tables.rs +++ b/lib/cretonne/src/isa/enc_tables.rs @@ -225,7 +225,7 @@ impl<'a> Encodings<'a> { self.legalize_actions[self.legalize as usize] } - /// Check if the `rpred` recipe predicate s satisfied. + /// Check if the `rpred` recipe predicate is satisfied. fn check_recipe(&self, rpred: RecipePredicate) -> bool { match rpred { Some(p) => p(self.isa_preds, self.inst), From 40ec50d0b6036f6c97804e8d99b994a5d43e1ca1 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 8 Mar 2018 02:26:26 -0800 Subject: [PATCH 45/58] Don't relax a branch to have different input constraints. When relaxing a branch, restrict the set of candidate encodings to those which have the same input constraints as the original encoding choice. This prevents situations where relaxation prefers a non-REX-prefixed encoding over a REX prefixed one because the end of the instruction can be one byte closer to the destination, in a situation where the encoding needs to be REX-prefixed because of one of the operand registers. This also makes the Context class perform encoding verification after relaxation, to catch similar problems in the future. Fixes #256. --- lib/cretonne/src/binemit/relaxation.rs | 24 +++++++++++++++++------- lib/cretonne/src/context.rs | 19 ++++++++++++++++++- lib/cretonne/src/isa/constraints.rs | 7 ++++--- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/lib/cretonne/src/binemit/relaxation.rs b/lib/cretonne/src/binemit/relaxation.rs index 94835b736e..c6ea41f451 100644 --- a/lib/cretonne/src/binemit/relaxation.rs +++ b/lib/cretonne/src/binemit/relaxation.rs @@ -152,13 +152,23 @@ fn relax_branch( if let Some(enc) = isa.legal_encodings(dfg, &dfg[inst], ctrl_type).find( |&enc| { let range = encinfo.branch_range(enc).expect("Branch with no range"); - let in_range = range.contains(offset, dest_offset); - dbg!( - " trying [{}]: {}", - encinfo.display(enc), - if in_range { "OK" } else { "out of range" } - ); - in_range + if !range.contains(offset, dest_offset) { + dbg!(" trying [{}]: out of range", encinfo.display(enc)); + false + } else if encinfo.operand_constraints(enc) != + encinfo.operand_constraints(cur.func.encodings[inst]) + { + // Conservatively give up if the encoding has different constraints + // than the original, so that we don't risk picking a new encoding + // which the existing operands don't satisfy. We can't check for + // validity directly because we don't have a RegDiversions active so + // we don't know which registers are actually in use. + dbg!(" trying [{}]: constraints differ", encinfo.display(enc)); + false + } else { + dbg!(" trying [{}]: OK", encinfo.display(enc)); + true + } }, ) { diff --git a/lib/cretonne/src/context.rs b/lib/cretonne/src/context.rs index 5b8db2414c..0eb7ff5c05 100644 --- a/lib/cretonne/src/context.rs +++ b/lib/cretonne/src/context.rs @@ -131,6 +131,20 @@ impl Context { } } + /// Run the locations verifier on the function. + pub fn verify_locations<'a>(&self, isa: &TargetIsa) -> verifier::Result { + verifier::verify_locations(isa, &self.func, None) + } + + /// Run the locations verifier only if the `enable_verifier` setting is true. + pub fn verify_locations_if<'a>(&self, isa: &TargetIsa) -> CtonResult { + if isa.flags().enable_verifier() { + self.verify_locations(isa).map_err(Into::into) + } else { + Ok(()) + } + } + /// Perform pre-legalization rewrites on the function. pub fn preopt(&mut self, isa: &TargetIsa) -> CtonResult { do_preopt(&mut self.func); @@ -212,13 +226,16 @@ impl Context { /// Insert prologue and epilogues after computing the stack frame layout. pub fn prologue_epilogue(&mut self, isa: &TargetIsa) -> CtonResult { isa.prologue_epilogue(&mut self.func)?; - self.verify_if(isa) + self.verify_if(isa)?; + self.verify_locations_if(isa)?; + Ok(()) } /// Run the branch relaxation pass and return the final code size. pub fn relax_branches(&mut self, isa: &TargetIsa) -> Result { let code_size = relax_branches(&mut self.func, isa)?; self.verify_if(isa)?; + self.verify_locations_if(isa)?; Ok(code_size) } diff --git a/lib/cretonne/src/isa/constraints.rs b/lib/cretonne/src/isa/constraints.rs index eb6b33c3cb..f831c0e187 100644 --- a/lib/cretonne/src/isa/constraints.rs +++ b/lib/cretonne/src/isa/constraints.rs @@ -13,6 +13,7 @@ use ir::{Function, ValueLoc, Inst}; use regalloc::RegDiversions; /// Register constraint for a single value operand or instruction result. +#[derive(PartialEq, Debug)] pub struct OperandConstraint { /// The kind of constraint. pub kind: ConstraintKind, @@ -53,7 +54,7 @@ impl OperandConstraint { } /// The different kinds of operand constraints. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum ConstraintKind { /// This operand or result must be a register from the given register class. Reg, @@ -89,7 +90,7 @@ pub enum ConstraintKind { } /// Value operand constraints for an encoding recipe. -#[derive(Clone)] +#[derive(PartialEq, Clone)] pub struct RecipeConstraints { /// Constraints for the instruction's fixed value operands. /// @@ -160,7 +161,7 @@ impl RecipeConstraints { /// - Intel uses the address of the instruction following the branch, `origin = 2` for a 2-byte /// branch instruction. /// - ARM's A32 encoding uses the address of the branch instruction + 8 bytes, `origin = 8`. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct BranchRange { /// Offset in bytes from the address of the branch instruction to the origin used for computing /// the branch displacement. This is the destination of a branch that encodes a 0 displacement. From b5f428b6f285d5482aa02db3bc14c4c07d9ba987 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 8 Mar 2018 02:50:09 -0800 Subject: [PATCH 46/58] Bump version to 0.3.3 --- cranelift/Cargo.toml | 12 ++++++------ cranelift/publish-all.sh | 2 +- lib/cretonne/Cargo.toml | 2 +- lib/frontend/Cargo.toml | 4 ++-- lib/native/Cargo.toml | 4 ++-- lib/reader/Cargo.toml | 4 ++-- lib/wasm/Cargo.toml | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index a1479c63fc..cddfc4ff35 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cretonne-tools" authors = ["The Cretonne Project Developers"] -version = "0.3.2" +version = "0.3.3" description = "Binaries for testing the Cretonne library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -13,11 +13,11 @@ name = "cton-util" path = "src/cton-util.rs" [dependencies] -cretonne = { path = "lib/cretonne", version = "0.3.2" } -cretonne-reader = { path = "lib/reader", version = "0.3.2" } -cretonne-frontend = { path = "lib/frontend", version = "0.3.2" } -cretonne-wasm = { path = "lib/wasm", version = "0.3.2" } -cretonne-native = { path = "lib/native", version = "0.3.2" } +cretonne = { path = "lib/cretonne", version = "0.3.3" } +cretonne-reader = { path = "lib/reader", version = "0.3.3" } +cretonne-frontend = { path = "lib/frontend", version = "0.3.3" } +cretonne-wasm = { path = "lib/wasm", version = "0.3.3" } +cretonne-native = { path = "lib/native", version = "0.3.3" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index b380211212..ac0f3218cc 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -5,7 +5,7 @@ topdir="$(pwd)" # All the cretonne-* crates have the same version number # The filecheck crate version is managed independently. -version="0.3.2" +version="0.3.3" # Update all of the Cargo.toml files. # diff --git a/lib/cretonne/Cargo.toml b/lib/cretonne/Cargo.toml index d6e91e695d..bbf8901ae4 100644 --- a/lib/cretonne/Cargo.toml +++ b/lib/cretonne/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne" -version = "0.3.2" +version = "0.3.3" description = "Low-level code generator library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" diff --git a/lib/frontend/Cargo.toml b/lib/frontend/Cargo.toml index a09c692126..8a22f61efc 100644 --- a/lib/frontend/Cargo.toml +++ b/lib/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-frontend" -version = "0.3.2" +version = "0.3.3" description = "Cretonne IL builder helper" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_frontend" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.2" } +cretonne = { path = "../cretonne", version = "0.3.3" } diff --git a/lib/native/Cargo.toml b/lib/native/Cargo.toml index 5d2920a67a..c8ca693e71 100644 --- a/lib/native/Cargo.toml +++ b/lib/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-native" -version = "0.3.2" +version = "0.3.3" authors = ["The Cretonne Project Developers"] description = "Support for targeting the host with Cretonne" repository = "https://github.com/Cretonne/cretonne" @@ -11,7 +11,7 @@ readme = "README.md" name = "cton_native" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.2" } +cretonne = { path = "../cretonne", version = "0.3.3" } [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies] raw-cpuid = "3.0.0" diff --git a/lib/reader/Cargo.toml b/lib/reader/Cargo.toml index b0d32b2a61..311ffe31cf 100644 --- a/lib/reader/Cargo.toml +++ b/lib/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-reader" -version = "0.3.2" +version = "0.3.3" description = "Cretonne textual IL reader" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_reader" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.2" } +cretonne = { path = "../cretonne", version = "0.3.3" } diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index 086e8c9735..1df8e0da15 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-wasm" -version = "0.3.2" +version = "0.3.3" authors = ["The Cretonne Project Developers"] description = "Translator from WebAssembly to Cretonne IL" repository = "https://github.com/Cretonne/cretonne" @@ -13,8 +13,8 @@ name = "cton_wasm" [dependencies] wasmparser = "0.14.1" -cretonne = { path = "../cretonne", version = "0.3.2" } -cretonne-frontend = { path = "../frontend", version = "0.3.2" } +cretonne = { path = "../cretonne", version = "0.3.3" } +cretonne-frontend = { path = "../frontend", version = "0.3.3" } [dev-dependencies] tempdir = "0.3.5" From b9d7a43439ba2fc63671106e25065d5c67109f40 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 8 Mar 2018 06:07:18 -0800 Subject: [PATCH 47/58] Elaborate on Cretonne's rustc backend goals. (#257) * Elaborate on Cretonne's rustc backend goals. * Remove these extra newlines. They don't show up in the rendered form anyway. * Fix typo. * The document is meant to speak with the voice of the project. --- rustc.rst | 68 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/rustc.rst b/rustc.rst index 90ab7df8b4..20cebeb5d6 100644 --- a/rustc.rst +++ b/rustc.rst @@ -2,15 +2,63 @@ Cretonne in Rustc ================= -The Rust compiler currently uses LLVM as its optimizer and code generator for both debug and -release builds. The Cretonne project does not intend to compete with LLVM when it comes to -optimizing release builds, but for debug builds where compilation speed is paramount, it makes -sense to use Cretonne instead of LLVM. +One goal for Cretonne is to be usable as a backend suitable for compiling Rust +in debug mode. This mode doesn't require a lot of mid-level optimization, and it +does want very fast compile times, and this matches up fairly well with what we +expect Cretonne's initial strengths and weaknesses will be. Cretonne is being +designed to take aggressive advantage of multiple cores, and to be very efficient +with its use of memory. -- Cretonne is designed to take advantage of multi-core CPUs, making parallel code generation quite - easy. This is harder with LLVM which was designed before multi-core CPUs where mainstream. -- Cretonne is designed with compilation speed in mind. It makes engineering tradeoffs that favor - compilation speed over advanced optimizations. +Another goal is a "pretty good" backend. The idea here is to do the work to get +MIR-level inlining enabled, do some basic optimizations in Cretonne to capture the +low-hanging fruit, and then use that along with good low-level optimizations to +produce code which has a chance of being decently fast, with quite fast compile +times. It obviously wouldn't compete with LLVM-based release builds in terms of +optimization, but for some users, completely unoptimized code is too slow to test +with, so a "pretty good" mode might be good enough. -See `the discussion on the Rust internals forum -`_. +There's plenty of work to do to achieve these goals, and if achieve them, we'll have +enabled a Rust compiler written entirely in Rust, and enabled faster Rust compile +times for important use cases. + +With all that said, there is a potential goal beyond that, which is to build a +full optimizing release-capable backend. We can't predict how far Cretonne will go +yet, but we do have some crazy ideas about what such a thing might look like, +including: + +- Take advantage of Rust language properties in the optimizer. With LLVM, Rust is + able to use annotations to describe some of its aliasing guarantees, however the + annotations are awkward and limited. An optimizer that can represent the core + aliasing relationships that Rust provides directly has the potential to be very + powerful without the need for complex alias analysis logic. Unsafe blocks are an + interesting challenge, however in many simple cases, like Vec, it may be possible + to recover what the optimizer needs to know. + +- Design for superoptimization. Traditionally, compiler development teams have + spent many years of manual effort to identify patterns of code that can be + matched and replaced. Superoptimizers have been contributing some to this + effort, but in the future, we may be able to reverse roles. + Superoptimizers will do the bulk of the work, and humans will contribute + specialized optimizations that superoptimizers miss. This has the potential to + take a new optimizer from scratch to diminishing-returns territory with much + less manual effort. + +- Build an optimizer IR without the constraints of fast-debug-build compilation. + Cretonne's base IR is focused on Codegen, so a full-strength optimizer would either + use an IR layer on top of it (possibly using Cretonne's flexible EntityMap system), + or possibly an independent IR that could be translated to/from the base IR. Either + way, this overall architecture would keep the optimizer out of the way of the + non-optimizing build path, which keeps that path fast and simple, and gives the + optimizer more flexibility. If we then want to base the IR on a powerful data + structure like the Value State Dependence Graph (VSDG), we can do so with fewer + compromises. + +And, these ideas build on each other. For example, one of the challenges for +dependence-graph-oriented IRs like the VSDG is getting good enough memory dependence +information. But if we can get high-quality aliasing information directly from the +Rust front-end, we should be in great shape. As another example, it's often harder +for superoptimizers to reason about control flow than expression graphs. But, +graph-oriented IRs like the VSDG represent control flow as control dependencies. +It's difficult to say how powerful this combination will be until we try it, but +if nothing else, it should be very convenient to express pattern-matching over a +single graph that includes both data and control dependencies. From 8df9fe6c8787e84e15905f094a325259cd29c6fd Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 8 Mar 2018 12:12:10 -0800 Subject: [PATCH 48/58] Fix obsolete paths in comments. --- cranelift/docs/testing.rst | 2 +- lib/cretonne/src/isa/arm32/settings.rs | 2 +- lib/cretonne/src/isa/arm64/settings.rs | 2 +- lib/cretonne/src/isa/intel/settings.rs | 2 +- lib/cretonne/src/isa/riscv/settings.rs | 2 +- lib/cretonne/src/legalizer/mod.rs | 2 +- lib/cretonne/src/predicates.rs | 2 +- lib/cretonne/src/settings.rs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cranelift/docs/testing.rst b/cranelift/docs/testing.rst index 951149c044..2b84b1c19b 100644 --- a/cranelift/docs/testing.rst +++ b/cranelift/docs/testing.rst @@ -119,7 +119,7 @@ All types of tests allow shared Cretonne settings to be modified: option : flag | setting "=" value The shared settings available for all target ISAs are defined in -:file:`lib/cretonne/meta/cretonne/settings.py`. +:file:`lib/cretonne/meta/base/settings.py`. The ``set`` lines apply settings cumulatively:: diff --git a/lib/cretonne/src/isa/arm32/settings.rs b/lib/cretonne/src/isa/arm32/settings.rs index e857716a64..b502deee40 100644 --- a/lib/cretonne/src/isa/arm32/settings.rs +++ b/lib/cretonne/src/isa/arm32/settings.rs @@ -5,5 +5,5 @@ use std::fmt; // Include code generated by `lib/cretonne/meta/gen_settings.py`. This file contains a public // `Flags` struct with an impl for all of the settings defined in -// `lib/cretonne/meta/cretonne/settings.py`. +// `lib/cretonne/meta/isa/arm32/settings.py`. include!(concat!(env!("OUT_DIR"), "/settings-arm32.rs")); diff --git a/lib/cretonne/src/isa/arm64/settings.rs b/lib/cretonne/src/isa/arm64/settings.rs index 6427d7be99..b575168361 100644 --- a/lib/cretonne/src/isa/arm64/settings.rs +++ b/lib/cretonne/src/isa/arm64/settings.rs @@ -5,5 +5,5 @@ use std::fmt; // Include code generated by `lib/cretonne/meta/gen_settings.py`. This file contains a public // `Flags` struct with an impl for all of the settings defined in -// `lib/cretonne/meta/cretonne/settings.py`. +// `lib/cretonne/meta/isa/arm64/settings.py`. include!(concat!(env!("OUT_DIR"), "/settings-arm64.rs")); diff --git a/lib/cretonne/src/isa/intel/settings.rs b/lib/cretonne/src/isa/intel/settings.rs index f933e91b87..147af4c2fa 100644 --- a/lib/cretonne/src/isa/intel/settings.rs +++ b/lib/cretonne/src/isa/intel/settings.rs @@ -5,7 +5,7 @@ use std::fmt; // Include code generated by `lib/cretonne/meta/gen_settings.py`. This file contains a public // `Flags` struct with an impl for all of the settings defined in -// `lib/cretonne/meta/cretonne/settings.py`. +// `lib/cretonne/meta/isa/intel/settings.py`. include!(concat!(env!("OUT_DIR"), "/settings-intel.rs")); #[cfg(test)] diff --git a/lib/cretonne/src/isa/riscv/settings.rs b/lib/cretonne/src/isa/riscv/settings.rs index 8cb376e5da..2aa78fa853 100644 --- a/lib/cretonne/src/isa/riscv/settings.rs +++ b/lib/cretonne/src/isa/riscv/settings.rs @@ -5,7 +5,7 @@ use std::fmt; // Include code generated by `lib/cretonne/meta/gen_settings.py`. This file contains a public // `Flags` struct with an impl for all of the settings defined in -// `lib/cretonne/meta/cretonne/settings.py`. +// `lib/cretonne/meta/isa/riscv/settings.py`. include!(concat!(env!("OUT_DIR"), "/settings-riscv.rs")); #[cfg(test)] diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 81dde2f457..189a3e735d 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -107,7 +107,7 @@ pub fn legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, is } // Include legalization patterns that were generated by `gen_legalizer.py` from the `XForms` in -// `meta/cretonne/legalize.py`. +// `lib/cretonne/meta/base/legalize.py`. // // Concretely, this defines private functions `narrow()`, and `expand()`. include!(concat!(env!("OUT_DIR"), "/legalizer.rs")); diff --git a/lib/cretonne/src/predicates.rs b/lib/cretonne/src/predicates.rs index 6ce3e0a799..63d2e79af3 100644 --- a/lib/cretonne/src/predicates.rs +++ b/lib/cretonne/src/predicates.rs @@ -1,7 +1,7 @@ //! Predicate functions for testing instruction fields. //! //! This module defines functions that are used by the instruction predicates defined by -//! `lib/cretonne/meta/cretonne/predicates.py` classes. +//! `lib/cretonne/meta/cdsl/predicates.py` classes. //! //! The predicates the operate on integer fields use `Into` as a shared trait bound. This //! bound is implemented by all the native integer types as well as `Imm64`. diff --git a/lib/cretonne/src/settings.rs b/lib/cretonne/src/settings.rs index 36a48e04b4..a87ceda695 100644 --- a/lib/cretonne/src/settings.rs +++ b/lib/cretonne/src/settings.rs @@ -311,7 +311,7 @@ pub mod detail { } // Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct -// with an impl for all of the settings defined in `meta/cretonne/settings.py`. +// with an impl for all of the settings defined in `lib/cretonne/meta/base/settings.py`. include!(concat!(env!("OUT_DIR"), "/settings.rs")); /// Wrapper containing flags and optionally a `TargetIsa` trait object. From 55d0efcb1495029fe96a808052e2da6626761537 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 9 Mar 2018 14:30:26 -0800 Subject: [PATCH 49/58] Pass the wasmparser::Operator by value, simplifying the code. --- lib/wasm/src/code_translator.rs | 10 +++++----- lib/wasm/src/func_translator.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 5d7e59f829..f08df8b0e1 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -38,7 +38,7 @@ use std::{i32, u32}; /// Translates wasm operators into Cretonne IL instructions. Returns `true` if it inserted /// a return. pub fn translate_operator( - op: &Operator, + op: Operator, builder: &mut FunctionBuilder, state: &mut TranslationState, environ: &mut FE, @@ -48,7 +48,7 @@ pub fn translate_operator( } // This big match treats all Wasm code operators. - match *op { + match op { /********************************** Locals **************************************** * `get_local` and `set_local` are treated as non-SSA variables and will completely * disappear in the Cretonne Code @@ -265,7 +265,7 @@ pub fn translate_operator( state.peekn(return_count), ); } - Operator::BrTable { ref table } => { + Operator::BrTable { table } => { let (depths, default) = table.read_table(); let mut min_depth = default; for depth in &depths { @@ -935,11 +935,11 @@ pub fn translate_operator( /// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable /// portion so the translation state muts be updated accordingly. fn translate_unreachable_operator( - op: &Operator, + op: Operator, builder: &mut FunctionBuilder, state: &mut TranslationState, ) { - match *op { + match op { Operator::If { ty: _ } => { // Push a placeholder control stack entry. The if isn't reachable, // so we don't have any branches anywhere. diff --git a/lib/wasm/src/func_translator.rs b/lib/wasm/src/func_translator.rs index 7e8955e816..d5737bd446 100644 --- a/lib/wasm/src/func_translator.rs +++ b/lib/wasm/src/func_translator.rs @@ -198,7 +198,7 @@ fn parse_function_body( while !state.control_stack.is_empty() { builder.set_srcloc(cur_srcloc(&reader)); let op = reader.read_operator().map_err(|_| CtonError::InvalidInput)?; - translate_operator(&op, builder, state, environ); + translate_operator(op, builder, state, environ); } // The final `End` operator left us in the exit block where we need to manually add a return From 56c7d857276b2d4d47e6112f444133aa9760f793 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 9 Mar 2018 14:31:12 -0800 Subject: [PATCH 50/58] More minor code simplifications. --- lib/wasm/src/code_translator.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index f08df8b0e1..100dfbab41 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -282,10 +282,10 @@ pub fn translate_operator( min_depth_frame.num_return_values() } }; + let val = state.pop1(); + let mut data = JumpTableData::with_capacity(depths.len()); if jump_args_count == 0 { // No jump arguments - let val = state.pop1(); - let mut data = JumpTableData::with_capacity(depths.len()); for depth in depths { let ebb = { let i = state.control_stack.len() - 1 - (depth as usize); @@ -307,9 +307,7 @@ pub fn translate_operator( } else { // Here we have jump arguments, but Cretonne's br_table doesn't support them // We then proceed to split the edges going out of the br_table - let val = state.pop1(); let return_count = jump_args_count; - let mut data = JumpTableData::with_capacity(depths.len()); let mut dest_ebb_sequence = Vec::new(); let mut dest_ebb_map = HashMap::new(); for depth in depths { From 1c72ccfe0ab8a6b83cc2a8946c68dad339436c61 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 9 Mar 2018 15:01:13 -0800 Subject: [PATCH 51/58] Define a `Variable` struct so that frontends don't have to. Frontends can still use their own types with `ILBuilder` and `FunctionBuilder`. This just provides a basic `Variable` struct for frontends that want it. --- lib/frontend/src/frontend.rs | 27 +++++----------- lib/frontend/src/lib.rs | 25 ++++----------- lib/frontend/src/ssa.rs | 60 ++++++++++++++---------------------- lib/frontend/src/variable.rs | 24 +++++++++++++++ 4 files changed, 61 insertions(+), 75 deletions(-) create mode 100644 lib/frontend/src/variable.rs diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs index fba2f89a01..6802884b6a 100644 --- a/lib/frontend/src/frontend.rs +++ b/lib/frontend/src/frontend.rs @@ -15,6 +15,10 @@ use cretonne::packed_option::PackedOption; /// In order to reduce memory reallocations when compiling multiple functions, /// `ILBuilder` holds various data structures which are cleared between /// functions, rather than dropped, preserving the underlying allocations. +/// +/// The `Variable` parameter can be any index-like type that can be made to +/// implement `EntityRef`. For frontends that don't have an obvious type to +/// use here, `variable::Variable` can be used. pub struct ILBuilder where Variable: EntityRef, @@ -590,22 +594,7 @@ mod tests { use frontend::{ILBuilder, FunctionBuilder}; use cretonne::verifier::verify_function; use cretonne::settings; - - use std::u32; - - // An opaque reference to variable. - #[derive(Copy, Clone, PartialEq, Eq, Debug)] - pub struct Variable(u32); - impl EntityRef for Variable { - fn new(index: usize) -> Self { - assert!(index < (u32::MAX as usize)); - Variable(index as u32) - } - - fn index(self) -> usize { - self.0 as usize - } - } + use Variable; fn sample_function(lazy_seal: bool) { let mut sig = Signature::new(CallConv::Native); @@ -620,9 +609,9 @@ mod tests { let block0 = builder.create_ebb(); let block1 = builder.create_ebb(); let block2 = builder.create_ebb(); - let x = Variable(0); - let y = Variable(1); - let z = Variable(2); + let x = Variable::new(0); + let y = Variable::new(1); + let z = Variable::new(2); builder.declare_var(x, I32); builder.declare_var(y, I32); builder.declare_var(z, I32); diff --git a/lib/frontend/src/lib.rs b/lib/frontend/src/lib.rs index 8d2de2237b..a95d662893 100644 --- a/lib/frontend/src/lib.rs +++ b/lib/frontend/src/lib.rs @@ -39,23 +39,8 @@ //! use cretonne::ir::{ExternalName, CallConv, Function, Signature, AbiParam, InstBuilder}; //! use cretonne::ir::types::*; //! use cretonne::settings; -//! use cton_frontend::{ILBuilder, FunctionBuilder}; +//! use cton_frontend::{ILBuilder, FunctionBuilder, Variable}; //! use cretonne::verifier::verify_function; -//! use std::u32; -//! -//! // An opaque reference to variable. -//! #[derive(Copy, Clone, PartialEq, Eq, Debug)] -//! pub struct Variable(u32); -//! impl EntityRef for Variable { -//! fn new(index: usize) -> Self { -//! assert!(index < (u32::MAX as usize)); -//! Variable(index as u32) -//! } -//! -//! fn index(self) -> usize { -//! self.0 as usize -//! } -//! } //! //! fn main() { //! let mut sig = Signature::new(CallConv::Native); @@ -69,9 +54,9 @@ //! let block0 = builder.create_ebb(); //! let block1 = builder.create_ebb(); //! let block2 = builder.create_ebb(); -//! let x = Variable(0); -//! let y = Variable(1); -//! let z = Variable(2); +//! let x = Variable::new(0); +//! let y = Variable::new(1); +//! let z = Variable::new(2); //! builder.declare_var(x, I32); //! builder.declare_var(y, I32); //! builder.declare_var(z, I32); @@ -149,6 +134,8 @@ extern crate cretonne; pub use frontend::{ILBuilder, FunctionBuilder}; +pub use variable::Variable; mod frontend; mod ssa; +mod variable; diff --git a/lib/frontend/src/ssa.rs b/lib/frontend/src/ssa.rs index 06dba9d4ed..5b974799bf 100644 --- a/lib/frontend/src/ssa.rs +++ b/lib/frontend/src/ssa.rs @@ -720,21 +720,7 @@ mod tests { use cretonne::ir::instructions::BranchInfo; use cretonne::settings; use ssa::SSABuilder; - use std::u32; - - /// An opaque reference to variable. - #[derive(Copy, Clone, PartialEq, Eq, Debug)] - pub struct Variable(u32); - impl EntityRef for Variable { - fn new(index: usize) -> Self { - assert!(index < (u32::MAX as usize)); - Variable(index as u32) - } - - fn index(self) -> usize { - self.0 as usize - } - } + use Variable; #[test] fn simple_block() { @@ -748,14 +734,14 @@ mod tests { // z = x + z; let block = ssa.declare_ebb_header_block(ebb0); - let x_var = Variable(0); + let x_var = Variable::new(0); let x_ssa = { let mut cur = FuncCursor::new(&mut func); cur.insert_ebb(ebb0); cur.ins().iconst(I32, 1) }; ssa.def_var(x_var, x_ssa, block); - let y_var = Variable(1); + let y_var = Variable::new(1); let y_ssa = { let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0); cur.ins().iconst(I32, 2) @@ -764,7 +750,7 @@ mod tests { assert_eq!(ssa.use_var(&mut func, x_var, I32, block).0, x_ssa); assert_eq!(ssa.use_var(&mut func, y_var, I32, block).0, y_ssa); - let z_var = Variable(2); + let z_var = Variable::new(2); let x_use1 = ssa.use_var(&mut func, x_var, I32, block).0; let y_use1 = ssa.use_var(&mut func, y_var, I32, block).0; let z1_ssa = { @@ -800,7 +786,7 @@ mod tests { // y = x + y; let block0 = ssa.declare_ebb_header_block(ebb0); - let x_var = Variable(0); + let x_var = Variable::new(0); let x_ssa = { let mut cur = FuncCursor::new(&mut func); cur.insert_ebb(ebb0); @@ -809,7 +795,7 @@ mod tests { cur.ins().iconst(I32, 1) }; ssa.def_var(x_var, x_ssa, block0); - let y_var = Variable(1); + let y_var = Variable::new(1); let y_ssa = { let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0); cur.ins().iconst(I32, 2) @@ -817,7 +803,7 @@ mod tests { ssa.def_var(y_var, y_ssa, block0); assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa); assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa); - let z_var = Variable(2); + let z_var = Variable::new(2); let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0; let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0; let z1_ssa = { @@ -888,7 +874,7 @@ mod tests { let block0 = ssa.declare_ebb_header_block(ebb0); ssa.seal_ebb_header_block(ebb0, &mut func); - let x_var = Variable(0); + let x_var = Variable::new(0); let x1 = { let mut cur = FuncCursor::new(&mut func); cur.insert_ebb(ebb0); @@ -899,14 +885,14 @@ mod tests { }; ssa.def_var(x_var, x1, block0); assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x1); - let y_var = Variable(1); + let y_var = Variable::new(1); let y1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0); cur.ins().iconst(I32, 2) }; ssa.def_var(y_var, y1, block0); assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y1); - let z_var = Variable(2); + let z_var = Variable::new(2); let x2 = ssa.use_var(&mut func, x_var, I32, block0).0; assert_eq!(x2, x1); let y2 = ssa.use_var(&mut func, y_var, I32, block0).0; @@ -995,7 +981,7 @@ mod tests { // let block0 = ssa.declare_ebb_header_block(ebb0); ssa.seal_ebb_header_block(ebb0, &mut func); - let x_var = Variable(0); + let x_var = Variable::new(0); let x1 = { let mut cur = FuncCursor::new(&mut func); cur.insert_ebb(ebb0); @@ -1060,9 +1046,9 @@ mod tests { // jump ebb1 // let block0 = ssa.declare_ebb_header_block(ebb0); - let x_var = Variable(0); - let y_var = Variable(1); - let z_var = Variable(2); + let x_var = Variable::new(0); + let y_var = Variable::new(1); + let z_var = Variable::new(2); ssa.seal_ebb_header_block(ebb0, &mut func); let x1 = { let mut cur = FuncCursor::new(&mut func); @@ -1125,11 +1111,11 @@ mod tests { let ebb0 = func.dfg.make_ebb(); let block = ssa.declare_ebb_header_block(ebb0); ssa.seal_ebb_header_block(ebb0, &mut func); - let i32_var = Variable(0); - let f32_var = Variable(1); - let f64_var = Variable(2); - let b1_var = Variable(3); - let f32x4_var = Variable(4); + let i32_var = Variable::new(0); + let f32_var = Variable::new(1); + let f64_var = Variable::new(2); + let b1_var = Variable::new(3); + let f32x4_var = Variable::new(4); ssa.use_var(&mut func, i32_var, I32, block); ssa.use_var(&mut func, f32_var, F32, block); ssa.use_var(&mut func, f64_var, F64, block); @@ -1147,7 +1133,7 @@ mod tests { let ebb0 = func.dfg.make_ebb(); let block = ssa.declare_ebb_header_block(ebb0); ssa.seal_ebb_header_block(ebb0, &mut func); - let x_var = Variable(0); + let x_var = Variable::new(0); assert_eq!(func.dfg.num_ebb_params(ebb0), 0); ssa.use_var(&mut func, x_var, I32, block); assert_eq!(func.dfg.num_ebb_params(ebb0), 0); @@ -1166,7 +1152,7 @@ mod tests { let mut ssa: SSABuilder = SSABuilder::new(); let ebb0 = func.dfg.make_ebb(); let block = ssa.declare_ebb_header_block(ebb0); - let x_var = Variable(0); + let x_var = Variable::new(0); assert_eq!(func.dfg.num_ebb_params(ebb0), 0); ssa.use_var(&mut func, x_var, I32, block); assert_eq!(func.dfg.num_ebb_params(ebb0), 1); @@ -1200,7 +1186,7 @@ mod tests { cur.insert_ebb(ebb1); cur.goto_bottom(ebb0); cur.ins().return_(&[]); - let x_var = Variable(0); + let x_var = Variable::new(0); cur.goto_bottom(ebb1); let val = ssa.use_var(&mut cur.func, x_var, I32, block1).0; let brz = cur.ins().brz(val, ebb1, &[]); @@ -1237,7 +1223,7 @@ mod tests { let block2 = ssa.declare_ebb_header_block(ebb2); { let mut cur = FuncCursor::new(&mut func); - let x_var = Variable(0); + let x_var = Variable::new(0); cur.insert_ebb(ebb0); cur.insert_ebb(ebb1); cur.insert_ebb(ebb2); diff --git a/lib/frontend/src/variable.rs b/lib/frontend/src/variable.rs new file mode 100644 index 0000000000..6b322e184f --- /dev/null +++ b/lib/frontend/src/variable.rs @@ -0,0 +1,24 @@ +//! A basic `Variable` implementation. +//! +//! `ILBuilder`, `FunctionBuilder`, and related types have a `Variable` +//! type parameter, to allow frontends that identify variables with +//! their own index types to use them directly. Frontends which don't +//! can use the `Variable` defined here. + +use cretonne::entity::EntityRef; +use std::u32; + +///! An opaque reference to a variable. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Variable(u32); + +impl EntityRef for Variable { + fn new(index: usize) -> Self { + assert!(index < (u32::MAX as usize)); + Variable(index as u32) + } + + fn index(self) -> usize { + self.0 as usize + } +} From e441337e4bb8cac1a8cee1ca469067d0de0a9526 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 9 Mar 2018 16:32:13 -0800 Subject: [PATCH 52/58] Bump version to 0.3.4 --- cranelift/Cargo.toml | 12 ++++++------ cranelift/publish-all.sh | 2 +- lib/cretonne/Cargo.toml | 2 +- lib/frontend/Cargo.toml | 4 ++-- lib/native/Cargo.toml | 4 ++-- lib/reader/Cargo.toml | 4 ++-- lib/wasm/Cargo.toml | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index cddfc4ff35..297e8c31b3 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cretonne-tools" authors = ["The Cretonne Project Developers"] -version = "0.3.3" +version = "0.3.4" description = "Binaries for testing the Cretonne library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -13,11 +13,11 @@ name = "cton-util" path = "src/cton-util.rs" [dependencies] -cretonne = { path = "lib/cretonne", version = "0.3.3" } -cretonne-reader = { path = "lib/reader", version = "0.3.3" } -cretonne-frontend = { path = "lib/frontend", version = "0.3.3" } -cretonne-wasm = { path = "lib/wasm", version = "0.3.3" } -cretonne-native = { path = "lib/native", version = "0.3.3" } +cretonne = { path = "lib/cretonne", version = "0.3.4" } +cretonne-reader = { path = "lib/reader", version = "0.3.4" } +cretonne-frontend = { path = "lib/frontend", version = "0.3.4" } +cretonne-wasm = { path = "lib/wasm", version = "0.3.4" } +cretonne-native = { path = "lib/native", version = "0.3.4" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/cranelift/publish-all.sh b/cranelift/publish-all.sh index ac0f3218cc..7d8b7f7f3c 100755 --- a/cranelift/publish-all.sh +++ b/cranelift/publish-all.sh @@ -5,7 +5,7 @@ topdir="$(pwd)" # All the cretonne-* crates have the same version number # The filecheck crate version is managed independently. -version="0.3.3" +version="0.3.4" # Update all of the Cargo.toml files. # diff --git a/lib/cretonne/Cargo.toml b/lib/cretonne/Cargo.toml index bbf8901ae4..597ff1efd6 100644 --- a/lib/cretonne/Cargo.toml +++ b/lib/cretonne/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne" -version = "0.3.3" +version = "0.3.4" description = "Low-level code generator library" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" diff --git a/lib/frontend/Cargo.toml b/lib/frontend/Cargo.toml index 8a22f61efc..0085036ed0 100644 --- a/lib/frontend/Cargo.toml +++ b/lib/frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-frontend" -version = "0.3.3" +version = "0.3.4" description = "Cretonne IL builder helper" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_frontend" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.3" } +cretonne = { path = "../cretonne", version = "0.3.4" } diff --git a/lib/native/Cargo.toml b/lib/native/Cargo.toml index c8ca693e71..10856d98a5 100644 --- a/lib/native/Cargo.toml +++ b/lib/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-native" -version = "0.3.3" +version = "0.3.4" authors = ["The Cretonne Project Developers"] description = "Support for targeting the host with Cretonne" repository = "https://github.com/Cretonne/cretonne" @@ -11,7 +11,7 @@ readme = "README.md" name = "cton_native" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.3" } +cretonne = { path = "../cretonne", version = "0.3.4" } [target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies] raw-cpuid = "3.0.0" diff --git a/lib/reader/Cargo.toml b/lib/reader/Cargo.toml index 311ffe31cf..0d2061e361 100644 --- a/lib/reader/Cargo.toml +++ b/lib/reader/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["The Cretonne Project Developers"] name = "cretonne-reader" -version = "0.3.3" +version = "0.3.4" description = "Cretonne textual IL reader" license = "Apache-2.0" documentation = "https://cretonne.readthedocs.io/" @@ -12,4 +12,4 @@ readme = "README.md" name = "cton_reader" [dependencies] -cretonne = { path = "../cretonne", version = "0.3.3" } +cretonne = { path = "../cretonne", version = "0.3.4" } diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index 1df8e0da15..8e61d440c0 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cretonne-wasm" -version = "0.3.3" +version = "0.3.4" authors = ["The Cretonne Project Developers"] description = "Translator from WebAssembly to Cretonne IL" repository = "https://github.com/Cretonne/cretonne" @@ -13,8 +13,8 @@ name = "cton_wasm" [dependencies] wasmparser = "0.14.1" -cretonne = { path = "../cretonne", version = "0.3.3" } -cretonne-frontend = { path = "../frontend", version = "0.3.3" } +cretonne = { path = "../cretonne", version = "0.3.4" } +cretonne-frontend = { path = "../frontend", version = "0.3.4" } [dev-dependencies] tempdir = "0.3.5" From b8a106adf010743b86cce8ef24fae03b7883de05 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 12 Mar 2018 10:28:19 -0700 Subject: [PATCH 53/58] Remove the "has_sse2" flag. Cretonne currently requires SSE2 support pervasively, so it's not meaningful to have a setting for it. --- lib/cretonne/meta/isa/intel/settings.py | 7 ++----- lib/native/src/lib.rs | 15 ++++++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/cretonne/meta/isa/intel/settings.py b/lib/cretonne/meta/isa/intel/settings.py index 5817c48c0e..c62012e0c1 100644 --- a/lib/cretonne/meta/isa/intel/settings.py +++ b/lib/cretonne/meta/isa/intel/settings.py @@ -11,9 +11,6 @@ ISA.settings = SettingGroup('intel', parent=shared.group) # The has_* settings here correspond to CPUID bits. -# CPUID.01H:EDX -has_sse2 = BoolSetting("SSE2: CPUID.01H:EDX.SSE2[bit 26]") - # CPUID.01H:ECX has_sse3 = BoolSetting("SSE3: CPUID.01H:ECX.SSE3[bit 0]") has_ssse3 = BoolSetting("SSSE3: CPUID.01H:ECX.SSSE3[bit 9]") @@ -40,9 +37,9 @@ use_lzcnt = And(has_lzcnt) # Presets corresponding to Intel CPUs. -baseline = Preset(has_sse2) +baseline = Preset() nehalem = Preset( - has_sse2, has_sse3, has_ssse3, has_sse41, has_sse42, has_popcnt) + has_sse3, has_ssse3, has_sse41, has_sse42, has_popcnt) haswell = Preset(nehalem, has_bmi1, has_lzcnt) ISA.settings.close(globals()) diff --git a/lib/native/src/lib.rs b/lib/native/src/lib.rs index d7c6a8d0ab..ea828ea252 100644 --- a/lib/native/src/lib.rs +++ b/lib/native/src/lib.rs @@ -19,7 +19,7 @@ use raw_cpuid::CpuId; /// Return `settings` and `isa` builders configured for the current host /// machine, or `Err(())` if the host machine is not supported /// in the current configuration. -pub fn builders() -> Result<(settings::Builder, isa::Builder), ()> { +pub fn builders() -> Result<(settings::Builder, isa::Builder), &'static str> { let mut flag_builder = settings::builder(); // TODO: Add RISC-V support once Rust supports it. @@ -35,28 +35,28 @@ pub fn builders() -> Result<(settings::Builder, isa::Builder), ()> { } else if cfg!(target_arch = "aarch64") { "arm64" } else { - return Err(()); + return Err("unrecognized architecture"); }; let mut isa_builder = isa::lookup(name).map_err(|err| match err { isa::LookupError::Unknown => panic!(), - isa::LookupError::Unsupported => (), + isa::LookupError::Unsupported => "unsupported architecture", })?; if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { - parse_x86_cpuid(&mut isa_builder); + parse_x86_cpuid(&mut isa_builder)?; } Ok((flag_builder, isa_builder)) } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn parse_x86_cpuid(isa_builder: &mut isa::Builder) { +fn parse_x86_cpuid(isa_builder: &mut isa::Builder) -> Result<(), &'static str> { let cpuid = CpuId::new(); if let Some(info) = cpuid.get_feature_info() { - if info.has_sse2() { - isa_builder.enable("has_sse2").unwrap(); + if !info.has_sse2() { + return Err("x86 support requires SSE2"); } if info.has_sse3() { isa_builder.enable("has_sse3").unwrap(); @@ -87,4 +87,5 @@ fn parse_x86_cpuid(isa_builder: &mut isa::Builder) { isa_builder.enable("has_lzcnt").unwrap(); } } + Ok(()) } From ad363d7e6be281782311f30e88d5c591f1830672 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 12 Mar 2018 10:28:25 -0700 Subject: [PATCH 54/58] Replace cretonne-wasm's Local with cretonne-frontend's Variable. Previously, cretonne-wasm used its own Local struct for identifying local variables. However, now that cretonne-frontend provides a Variable struct, just use that instead. --- lib/frontend/src/variable.rs | 8 ++++++++ lib/wasm/src/code_translator.rs | 22 ++++++++++++---------- lib/wasm/src/func_translator.rs | 17 ++++++++--------- lib/wasm/src/translation_utils.rs | 19 ------------------- 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/lib/frontend/src/variable.rs b/lib/frontend/src/variable.rs index 6b322e184f..5b35a3426b 100644 --- a/lib/frontend/src/variable.rs +++ b/lib/frontend/src/variable.rs @@ -12,6 +12,14 @@ use std::u32; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Variable(u32); +impl Variable { + /// Create a new Variable with the given index. + pub fn with_u32(index: u32) -> Self { + assert!(index < u32::MAX); + Variable(index) + } +} + impl EntityRef for Variable { fn new(index: usize) -> Self { assert!(index < (u32::MAX as usize)); diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 100dfbab41..60b432a582 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -26,9 +26,9 @@ use cretonne::ir::{self, InstBuilder, MemFlags, JumpTableData}; use cretonne::ir::types::*; use cretonne::ir::condcodes::{IntCC, FloatCC}; use cretonne::packed_option::ReservedValue; -use cton_frontend::FunctionBuilder; +use cton_frontend::{FunctionBuilder, Variable}; use wasmparser::{Operator, MemoryImmediate}; -use translation_utils::{f32_translation, f64_translation, type_to_type, num_return_values, Local}; +use translation_utils::{f32_translation, f64_translation, type_to_type, num_return_values}; use translation_utils::{TableIndex, SignatureIndex, FunctionIndex, MemoryIndex}; use state::{TranslationState, ControlStackFrame}; use std::collections::{HashMap, hash_map}; @@ -39,7 +39,7 @@ use std::{i32, u32}; /// a return. pub fn translate_operator( op: Operator, - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, state: &mut TranslationState, environ: &mut FE, ) { @@ -53,14 +53,16 @@ pub fn translate_operator( * `get_local` and `set_local` are treated as non-SSA variables and will completely * disappear in the Cretonne Code ***********************************************************************************/ - Operator::GetLocal { local_index } => state.push1(builder.use_var(Local(local_index))), + Operator::GetLocal { local_index } => { + state.push1(builder.use_var(Variable::with_u32(local_index))) + } Operator::SetLocal { local_index } => { let val = state.pop1(); - builder.def_var(Local(local_index), val); + builder.def_var(Variable::with_u32(local_index), val); } Operator::TeeLocal { local_index } => { let val = state.peek1(); - builder.def_var(Local(local_index), val); + builder.def_var(Variable::with_u32(local_index), val); } /********************************** Globals **************************************** * `get_global` and `set_global` are handled by the environment. @@ -934,7 +936,7 @@ pub fn translate_operator( /// portion so the translation state muts be updated accordingly. fn translate_unreachable_operator( op: Operator, - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, state: &mut TranslationState, ) { match op { @@ -1019,7 +1021,7 @@ fn get_heap_addr( addr32: ir::Value, offset: u32, addr_ty: ir::Type, - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, ) -> (ir::Value, i32) { use std::cmp::min; @@ -1055,7 +1057,7 @@ fn translate_load( offset: u32, opcode: ir::Opcode, result_ty: ir::Type, - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, state: &mut TranslationState, environ: &mut FE, ) { @@ -1078,7 +1080,7 @@ fn translate_load( fn translate_store( offset: u32, opcode: ir::Opcode, - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, state: &mut TranslationState, environ: &mut FE, ) { diff --git a/lib/wasm/src/func_translator.rs b/lib/wasm/src/func_translator.rs index d5737bd446..0632630095 100644 --- a/lib/wasm/src/func_translator.rs +++ b/lib/wasm/src/func_translator.rs @@ -9,10 +9,9 @@ use cretonne::entity::EntityRef; use cretonne::ir::{self, InstBuilder, Ebb}; use cretonne::result::{CtonResult, CtonError}; use cretonne::timing; -use cton_frontend::{ILBuilder, FunctionBuilder}; +use cton_frontend::{ILBuilder, FunctionBuilder, Variable}; use environ::FuncEnvironment; use state::TranslationState; -use translation_utils::Local; use wasmparser::{self, BinaryReader}; /// WebAssembly to Cretonne IL function translator. @@ -21,7 +20,7 @@ use wasmparser::{self, BinaryReader}; /// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple /// functions which will reduce heap allocation traffic. pub struct FuncTranslator { - il_builder: ILBuilder, + il_builder: ILBuilder, state: TranslationState, } @@ -107,7 +106,7 @@ impl FuncTranslator { /// Declare local variables for the signature parameters that correspond to WebAssembly locals. /// /// Return the number of local variables declared. -fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> usize { +fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> usize { let sig_len = builder.func.signature.params.len(); let mut next_local = 0; for i in 0..sig_len { @@ -116,7 +115,7 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Eb // signature parameters. For example, a `vmctx` pointer. if param_type.purpose == ir::ArgumentPurpose::Normal { // This is a normal WebAssembly signature parameter, so create a local for it. - let local = Local::new(next_local); + let local = Variable::new(next_local); builder.declare_var(local, param_type.value_type); next_local += 1; @@ -133,7 +132,7 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Eb /// Declare local variables, starting from `num_params`. fn parse_local_decls( reader: &mut BinaryReader, - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, num_params: usize, ) -> CtonResult { let mut next_local = num_params; @@ -157,7 +156,7 @@ fn parse_local_decls( /// /// Fail of too many locals are declared in the function, or if the type is not valid for a local. fn declare_locals( - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, count: u32, wasm_type: wasmparser::Type, next_local: &mut usize, @@ -174,7 +173,7 @@ fn declare_locals( let ty = builder.func.dfg.value_type(zeroval); for _ in 0..count { - let local = Local::new(*next_local); + let local = Variable::new(*next_local); builder.declare_var(local, ty); builder.def_var(local, zeroval); *next_local += 1; @@ -187,7 +186,7 @@ fn declare_locals( /// arguments and locals are declared in the builder. fn parse_function_body( mut reader: BinaryReader, - builder: &mut FunctionBuilder, + builder: &mut FunctionBuilder, state: &mut TranslationState, environ: &mut FE, ) -> CtonResult { diff --git a/lib/wasm/src/translation_utils.rs b/lib/wasm/src/translation_utils.rs index 4c70d708ed..44c03fb6dd 100644 --- a/lib/wasm/src/translation_utils.rs +++ b/lib/wasm/src/translation_utils.rs @@ -71,25 +71,6 @@ pub struct Memory { pub shared: bool, } -/// Wrapper to a `get_local` and `set_local` index. They are WebAssembly's non-SSA variables. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct Local(pub u32); -impl cretonne::entity::EntityRef for Local { - fn new(index: usize) -> Self { - debug_assert!(index < (u32::MAX as usize)); - Local(index as u32) - } - - fn index(self) -> usize { - self.0 as usize - } -} -impl Default for Local { - fn default() -> Self { - Local(u32::MAX) - } -} - /// Helper function translating wasmparser types to Cretonne types when possible. pub fn type_to_type(ty: &wasmparser::Type) -> Result { match *ty { From e81a27fb5d57cc2782bc442e33e87a6ceb9160cd Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 12 Mar 2018 10:28:32 -0700 Subject: [PATCH 55/58] Implement Debug for `ArgAction` and `Affinity`. --- lib/cretonne/src/abi.rs | 2 +- lib/cretonne/src/regalloc/affinity.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cretonne/src/abi.rs b/lib/cretonne/src/abi.rs index 5c121824aa..05c96c79d5 100644 --- a/lib/cretonne/src/abi.rs +++ b/lib/cretonne/src/abi.rs @@ -11,7 +11,7 @@ use std::cmp::Ordering; /// /// An argument may go through a sequence of legalization steps before it reaches the final /// `Assign` action. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum ArgAction { /// Assign the argument to the given location. Assign(ArgumentLoc), diff --git a/lib/cretonne/src/regalloc/affinity.rs b/lib/cretonne/src/regalloc/affinity.rs index 1c50cbbbab..7d85ae8721 100644 --- a/lib/cretonne/src/regalloc/affinity.rs +++ b/lib/cretonne/src/regalloc/affinity.rs @@ -13,7 +13,7 @@ use ir::{AbiParam, ArgumentLoc}; use isa::{TargetIsa, RegInfo, RegClassIndex, OperandConstraint, ConstraintKind}; /// Preferred register allocation for an SSA value. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum Affinity { /// No affinity. /// From 30f8daa9d646e51903d5e3440cfae7f3681a158e Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 12 Mar 2018 10:28:35 -0700 Subject: [PATCH 56/58] Replace `assert!` with `debug_assert!` in production code paths. This allows the assertions to be disabled in release builds, so that the code is faster and smaller, at the expense of not performing the checks. Assertions can be re-enabled in release builds with the debug-assertions flag in Cargo.toml, as the top-level Cargo.toml file does. --- lib/cretonne/meta/gen_instr.py | 2 +- lib/cretonne/meta/gen_legalizer.py | 3 +- lib/cretonne/meta/gen_settings.py | 2 +- lib/cretonne/src/abi.rs | 6 ++-- lib/cretonne/src/bforest/path.rs | 6 ++-- lib/cretonne/src/binemit/mod.rs | 2 +- lib/cretonne/src/binemit/relaxation.rs | 4 +-- lib/cretonne/src/bitset.rs | 8 ++--- lib/cretonne/src/cursor.rs | 6 ++-- lib/cretonne/src/divconst_magic_numbers.rs | 20 ++++++------ lib/cretonne/src/dominator_tree.rs | 8 ++--- lib/cretonne/src/entity/list.rs | 16 +++++----- lib/cretonne/src/entity/mod.rs | 2 +- lib/cretonne/src/entity/sparse.rs | 2 +- lib/cretonne/src/ir/dfg.rs | 26 +++++++-------- lib/cretonne/src/ir/immediates.rs | 2 +- lib/cretonne/src/ir/instructions.rs | 4 +-- lib/cretonne/src/ir/layout.rs | 30 ++++++++--------- lib/cretonne/src/ir/progpoint.rs | 4 +-- lib/cretonne/src/ir/stackslot.rs | 4 +-- lib/cretonne/src/isa/intel/abi.rs | 2 +- lib/cretonne/src/isa/riscv/abi.rs | 2 +- lib/cretonne/src/isa/riscv/binemit.rs | 4 +-- lib/cretonne/src/legalizer/boundary.rs | 30 ++++++++--------- lib/cretonne/src/legalizer/globalvar.rs | 2 +- lib/cretonne/src/legalizer/heap.rs | 2 +- lib/cretonne/src/legalizer/mod.rs | 2 +- lib/cretonne/src/legalizer/split.rs | 4 +-- lib/cretonne/src/preopt.rs | 32 +++++++++---------- lib/cretonne/src/regalloc/coalescing.rs | 9 +++--- lib/cretonne/src/regalloc/coloring.rs | 23 ++++++------- .../src/regalloc/live_value_tracker.rs | 6 ++-- lib/cretonne/src/regalloc/liveness.rs | 6 ++-- lib/cretonne/src/regalloc/liverange.rs | 2 +- lib/cretonne/src/regalloc/reload.rs | 10 +++--- lib/cretonne/src/regalloc/solver.rs | 12 +++---- lib/cretonne/src/regalloc/spilling.rs | 2 +- lib/cretonne/src/regalloc/virtregs.rs | 4 +-- lib/cretonne/src/stack_layout.rs | 4 +-- lib/cretonne/src/timing.rs | 2 +- lib/frontend/src/variable.rs | 4 +-- lib/wasm/src/code_translator.rs | 2 +- lib/wasm/src/func_translator.rs | 6 ++-- 43 files changed, 165 insertions(+), 164 deletions(-) diff --git a/lib/cretonne/meta/gen_instr.py b/lib/cretonne/meta/gen_instr.py index 2bd34a926c..ac8e338eda 100644 --- a/lib/cretonne/meta/gen_instr.py +++ b/lib/cretonne/meta/gen_instr.py @@ -211,7 +211,7 @@ def gen_instruction_data_impl(fmt): if f.has_value_list: fmt.line(n + ' { ref mut args, .. } => args,') fmt.line('_ => panic!("No value list: {:?}", self),') - fmt.line('assert!(args.is_empty(), "Value list already in use");') + fmt.line('debug_assert!(args.is_empty(), "Value list already in use");') fmt.line('*args = vlist;') diff --git a/lib/cretonne/meta/gen_legalizer.py b/lib/cretonne/meta/gen_legalizer.py index 0f7b1f68e4..04c48ade51 100644 --- a/lib/cretonne/meta/gen_legalizer.py +++ b/lib/cretonne/meta/gen_legalizer.py @@ -348,7 +348,8 @@ def gen_xform(xform, fmt, type_sets): # Delete the original instruction if we didn't have an opportunity to # replace it. if not replace_inst: - fmt.line('assert_eq!(pos.remove_inst(), inst);') + fmt.line('let removed = pos.remove_inst();') + fmt.line('debug_assert_eq!(removed, inst);') fmt.line('return true;') diff --git a/lib/cretonne/meta/gen_settings.py b/lib/cretonne/meta/gen_settings.py index 0cd4e0e860..dfc2c2b70a 100644 --- a/lib/cretonne/meta/gen_settings.py +++ b/lib/cretonne/meta/gen_settings.py @@ -245,7 +245,7 @@ def gen_constructor(sgrp, parent, fmt): 'pub fn new({}) -> Flags {{'.format(args), '}'): fmt.line('let bvec = builder.state_for("{}");'.format(sgrp.name)) fmt.line('let mut bytes = [0; {}];'.format(sgrp.byte_size())) - fmt.line('assert_eq!(bvec.len(), {});'.format(sgrp.settings_size)) + fmt.line('debug_assert_eq!(bvec.len(), {});'.format(sgrp.settings_size)) with fmt.indented( 'for (i, b) in bvec.iter().enumerate() {', '}'): fmt.line('bytes[i] = *b;') diff --git a/lib/cretonne/src/abi.rs b/lib/cretonne/src/abi.rs index 05c96c79d5..801d4bfe9e 100644 --- a/lib/cretonne/src/abi.rs +++ b/lib/cretonne/src/abi.rs @@ -150,7 +150,7 @@ pub fn legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion { match have_bits.cmp(&arg_bits) { // We have fewer bits than the ABI argument. Ordering::Less => { - assert!( + debug_assert!( have.is_int() && arg.value_type.is_int(), "Can only extend integer values" ); @@ -163,8 +163,8 @@ pub fn legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion { // We have the same number of bits as the argument. Ordering::Equal => { // This must be an integer vector that is split and then extended. - assert!(arg.value_type.is_int()); - assert!(have.is_vector()); + debug_assert!(arg.value_type.is_int()); + debug_assert!(have.is_vector()); ValueConversion::VectorSplit } // We have more bits than the argument. diff --git a/lib/cretonne/src/bforest/path.rs b/lib/cretonne/src/bforest/path.rs index c0f9550645..1add8f6e63 100644 --- a/lib/cretonne/src/bforest/path.rs +++ b/lib/cretonne/src/bforest/path.rs @@ -316,7 +316,8 @@ impl Path { // Now that we have a not-full node, it must be possible to insert. match ins_node { None => { - assert!(pool[node].try_leaf_insert(entry, key, value)); + let inserted = pool[node].try_leaf_insert(entry, key, value); + debug_assert!(inserted); // If we inserted at the front of the new rhs_node leaf, we need to propagate // the inserted key as the critical key instead of the previous front key. if entry == 0 && node == rhs_node { @@ -324,7 +325,8 @@ impl Path { } } Some(n) => { - assert!(pool[node].try_inner_insert(entry, key, n)); + let inserted = pool[node].try_inner_insert(entry, key, n); + debug_assert!(inserted); // The lower level was moved to the new RHS node, so make sure that is // reflected here. if n == self.node[level + 1] { diff --git a/lib/cretonne/src/binemit/mod.rs b/lib/cretonne/src/binemit/mod.rs index 713d25bed5..bca1f6fa4d 100644 --- a/lib/cretonne/src/binemit/mod.rs +++ b/lib/cretonne/src/binemit/mod.rs @@ -110,7 +110,7 @@ where let mut divert = RegDiversions::new(); for ebb in func.layout.ebbs() { divert.clear(); - assert_eq!(func.offsets[ebb], sink.offset()); + debug_assert_eq!(func.offsets[ebb], sink.offset()); for inst in func.layout.ebb_insts(ebb) { emit_inst(func, inst, &mut divert, sink); } diff --git a/lib/cretonne/src/binemit/relaxation.rs b/lib/cretonne/src/binemit/relaxation.rs index c6ea41f451..b0bf6196c6 100644 --- a/lib/cretonne/src/binemit/relaxation.rs +++ b/lib/cretonne/src/binemit/relaxation.rs @@ -60,7 +60,7 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> Result { // Somebody used a fall-through instruction before the branch relaxation pass. // Make sure it is correct, i.e. the destination is the layout successor. - assert_eq!(destination, succ, "Illegal fall-through in {}", ebb) + debug_assert_eq!(destination, succ, "Illegal fall-through in {}", ebb) } Opcode::Jump => { // If this is a jump to the successor EBB, change it to a fall-through. diff --git a/lib/cretonne/src/bitset.rs b/lib/cretonne/src/bitset.rs index 62e0286bb3..f212790be0 100644 --- a/lib/cretonne/src/bitset.rs +++ b/lib/cretonne/src/bitset.rs @@ -36,8 +36,8 @@ where /// Check if this BitSet contains the number num pub fn contains(&self, num: u8) -> bool { - assert!((num as usize) < Self::bits()); - assert!((num as usize) < Self::max_bits()); + debug_assert!((num as usize) < Self::bits()); + debug_assert!((num as usize) < Self::max_bits()); self.0.into() & (1 << num) != 0 } @@ -62,8 +62,8 @@ where /// Construct a BitSet with the half-open range [lo,hi) filled in pub fn from_range(lo: u8, hi: u8) -> Self { - assert!(lo <= hi); - assert!((hi as usize) <= Self::bits()); + debug_assert!(lo <= hi); + debug_assert!((hi as usize) <= Self::bits()); let one: T = T::from(1); // I can't just do (one << hi) - one here as the shift may overflow let hi_rng = if hi >= 1 { diff --git a/lib/cretonne/src/cursor.rs b/lib/cretonne/src/cursor.rs index 56ad657043..ed2528d5be 100644 --- a/lib/cretonne/src/cursor.rs +++ b/lib/cretonne/src/cursor.rs @@ -256,7 +256,7 @@ pub trait Cursor { /// Go to a specific instruction which must be inserted in the layout. /// New instructions will be inserted before `inst`. fn goto_inst(&mut self, inst: ir::Inst) { - assert!(self.layout().inst_ebb(inst).is_some()); + debug_assert!(self.layout().inst_ebb(inst).is_some()); self.set_position(CursorPosition::At(inst)); } @@ -287,14 +287,14 @@ pub trait Cursor { /// At this position, instructions cannot be inserted, but `next_inst()` will move to the first /// instruction in `ebb`. fn goto_top(&mut self, ebb: ir::Ebb) { - assert!(self.layout().is_ebb_inserted(ebb)); + debug_assert!(self.layout().is_ebb_inserted(ebb)); self.set_position(CursorPosition::Before(ebb)); } /// Go to the bottom of `ebb` which must be inserted into the layout. /// At this position, inserted instructions will be appended to `ebb`. fn goto_bottom(&mut self, ebb: ir::Ebb) { - assert!(self.layout().is_ebb_inserted(ebb)); + debug_assert!(self.layout().is_ebb_inserted(ebb)); self.set_position(CursorPosition::After(ebb)); } diff --git a/lib/cretonne/src/divconst_magic_numbers.rs b/lib/cretonne/src/divconst_magic_numbers.rs index 968ca67f5c..b046e22411 100644 --- a/lib/cretonne/src/divconst_magic_numbers.rs +++ b/lib/cretonne/src/divconst_magic_numbers.rs @@ -43,8 +43,8 @@ pub struct MS64 { // The actual "magic number" generators follow. pub fn magicU32(d: u32) -> MU32 { - assert_ne!(d, 0); - assert_ne!(d, 1); // d==1 generates out of range shifts. + debug_assert_ne!(d, 0); + debug_assert_ne!(d, 1); // d==1 generates out of range shifts. let mut do_add: bool = false; let mut p: i32 = 31; @@ -89,8 +89,8 @@ pub fn magicU32(d: u32) -> MU32 { } pub fn magicU64(d: u64) -> MU64 { - assert_ne!(d, 0); - assert_ne!(d, 1); // d==1 generates out of range shifts. + debug_assert_ne!(d, 0); + debug_assert_ne!(d, 1); // d==1 generates out of range shifts. let mut do_add: bool = false; let mut p: i32 = 63; @@ -135,9 +135,9 @@ pub fn magicU64(d: u64) -> MU64 { } pub fn magicS32(d: i32) -> MS32 { - assert_ne!(d, -1); - assert_ne!(d, 0); - assert_ne!(d, 1); + debug_assert_ne!(d, -1); + debug_assert_ne!(d, 0); + debug_assert_ne!(d, 1); let two31: u32 = 0x80000000u32; let mut p: i32 = 31; let ad: u32 = i32::wrapping_abs(d) as u32; @@ -178,9 +178,9 @@ pub fn magicS32(d: i32) -> MS32 { } pub fn magicS64(d: i64) -> MS64 { - assert_ne!(d, -1); - assert_ne!(d, 0); - assert_ne!(d, 1); + debug_assert_ne!(d, -1); + debug_assert_ne!(d, 0); + debug_assert_ne!(d, 1); let two63: u64 = 0x8000000000000000u64; let mut p: i32 = 63; let ad: u64 = i64::wrapping_abs(d) as u64; diff --git a/lib/cretonne/src/dominator_tree.rs b/lib/cretonne/src/dominator_tree.rs index 27caf338dc..e643a42592 100644 --- a/lib/cretonne/src/dominator_tree.rs +++ b/lib/cretonne/src/dominator_tree.rs @@ -197,7 +197,7 @@ impl DominatorTree { } } - assert_eq!(a.0, b.0, "Unreachable block passed to common_dominator?"); + debug_assert_eq!(a.0, b.0, "Unreachable block passed to common_dominator?"); // We're in the same EBB. The common dominator is the earlier instruction. if layout.cmp(a.1, b.1) == Ordering::Less { @@ -241,7 +241,7 @@ impl DominatorTree { pub fn clear(&mut self) { self.nodes.clear(); self.postorder.clear(); - assert!(self.stack.is_empty()); + debug_assert!(self.stack.is_empty()); self.valid = false; } @@ -539,7 +539,7 @@ impl DominatorTreePreorder { /// Recompute this data structure to match `domtree`. pub fn compute(&mut self, domtree: &DominatorTree, layout: &Layout) { self.nodes.clear(); - assert_eq!(self.stack.len(), 0); + debug_assert_eq!(self.stack.len(), 0); // Step 1: Populate the child and sibling links. // @@ -557,7 +557,7 @@ impl DominatorTreePreorder { } // Step 2. Assign pre-order numbers from a DFS of the dominator tree. - assert!(self.stack.len() <= 1); + debug_assert!(self.stack.len() <= 1); let mut n = 0; while let Some(ebb) = self.stack.pop() { n += 1; diff --git a/lib/cretonne/src/entity/list.rs b/lib/cretonne/src/entity/list.rs index c9bb5b1a69..6c963bc377 100644 --- a/lib/cretonne/src/entity/list.rs +++ b/lib/cretonne/src/entity/list.rs @@ -219,8 +219,8 @@ impl ListPool { to_sclass: SizeClass, elems_to_copy: usize, ) -> usize { - assert!(elems_to_copy <= sclass_size(from_sclass)); - assert!(elems_to_copy <= sclass_size(to_sclass)); + debug_assert!(elems_to_copy <= sclass_size(from_sclass)); + debug_assert!(elems_to_copy <= sclass_size(to_sclass)); let new_block = self.alloc(to_sclass); if elems_to_copy > 0 { @@ -301,7 +301,7 @@ impl EntityList { pub fn clear(&mut self, pool: &mut ListPool) { let idx = self.index as usize; match pool.len_of(self) { - None => assert_eq!(idx, 0, "Invalid pool"), + None => debug_assert_eq!(idx, 0, "Invalid pool"), Some(len) => pool.free(idx - 1, sclass_for_length(len)), } // Switch back to the empty list representation which has no storage. @@ -322,7 +322,7 @@ impl EntityList { match pool.len_of(self) { None => { // This is an empty list. Allocate a block and set length=1. - assert_eq!(idx, 0, "Invalid pool"); + debug_assert_eq!(idx, 0, "Invalid pool"); let block = pool.alloc(sclass_for_length(1)); pool.data[block] = T::new(1); pool.data[block + 1] = element; @@ -358,7 +358,7 @@ impl EntityList { match pool.len_of(self) { None => { // This is an empty list. Allocate a block. - assert_eq!(idx, 0, "Invalid pool"); + debug_assert_eq!(idx, 0, "Invalid pool"); if count == 0 { return &mut []; } @@ -409,7 +409,7 @@ impl EntityList { } tail[0] = element; } else { - assert_eq!(index, seq.len()); + debug_assert_eq!(index, seq.len()); } } @@ -419,7 +419,7 @@ impl EntityList { { let seq = self.as_mut_slice(pool); len = seq.len(); - assert!(index < len); + debug_assert!(index < len); // Copy elements down. for i in index..len - 1 { @@ -449,7 +449,7 @@ impl EntityList { /// the list. pub fn swap_remove(&mut self, index: usize, pool: &mut ListPool) { let len = self.len(pool); - assert!(index < len); + debug_assert!(index < len); if index == len - 1 { self.remove(index, pool); } else { diff --git a/lib/cretonne/src/entity/mod.rs b/lib/cretonne/src/entity/mod.rs index 3cc8ec6f6d..6457e47f56 100644 --- a/lib/cretonne/src/entity/mod.rs +++ b/lib/cretonne/src/entity/mod.rs @@ -61,7 +61,7 @@ macro_rules! entity_impl { ($entity:ident) => { impl $crate::entity::EntityRef for $entity { fn new(index: usize) -> Self { - assert!(index < (::std::u32::MAX as usize)); + debug_assert!(index < (::std::u32::MAX as usize)); $entity(index as u32) } diff --git a/lib/cretonne/src/entity/sparse.rs b/lib/cretonne/src/entity/sparse.rs index dacbf66031..82e1b49546 100644 --- a/lib/cretonne/src/entity/sparse.rs +++ b/lib/cretonne/src/entity/sparse.rs @@ -149,7 +149,7 @@ where // There was no previous entry for `key`. Add it to the end of `dense`. let idx = self.dense.len(); - assert!(idx <= u32::MAX as usize, "SparseMap overflow"); + debug_assert!(idx <= u32::MAX as usize, "SparseMap overflow"); self.dense.push(value); self.sparse[key] = idx as u32; None diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index 023d8751b6..ccd08f1bb5 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -217,11 +217,11 @@ impl DataFlowGraph { /// /// The `dest` value can't be attached to an instruction or EBB. pub fn change_to_alias(&mut self, dest: Value, src: Value) { - assert!(!self.value_is_attached(dest)); + debug_assert!(!self.value_is_attached(dest)); // Try to create short alias chains by finding the original source value. // This also avoids the creation of loops. let original = self.resolve_aliases(src); - assert_ne!( + debug_assert_ne!( dest, original, "Aliasing {} to {} would create a loop", @@ -229,7 +229,7 @@ impl DataFlowGraph { src ); let ty = self.value_type(original); - assert_eq!( + debug_assert_eq!( self.value_type(dest), ty, "Aliasing {} to {} would change its type {} to {}", @@ -273,7 +273,7 @@ impl DataFlowGraph { { let original = src; let ty = self.value_type(original); - assert_eq!( + debug_assert_eq!( self.value_type(dest), ty, "Aliasing {} to {} would change its type {} to {}", @@ -498,9 +498,9 @@ impl DataFlowGraph { /// This is a very low-level operation. Usually, instruction results with the correct types are /// created automatically. The `res` value must not be attached to anything else. pub fn attach_result(&mut self, inst: Inst, res: Value) { - assert!(!self.value_is_attached(res)); + debug_assert!(!self.value_is_attached(res)); let num = self.results[inst].push(res, &mut self.value_lists); - assert!(num <= u16::MAX as usize, "Too many result values"); + debug_assert!(num <= u16::MAX as usize, "Too many result values"); let ty = self.value_type(res); self.values[res] = ValueData::Inst { ty, @@ -533,7 +533,7 @@ impl DataFlowGraph { .expect("Replacing detached result"), new_value, ); - assert_eq!( + debug_assert_eq!( attached, old_value, "{} wasn't detached from {}", @@ -547,7 +547,7 @@ impl DataFlowGraph { pub fn append_result(&mut self, inst: Inst, ty: Type) -> Value { let res = self.values.next_key(); let num = self.results[inst].push(res, &mut self.value_lists); - assert!(num <= u16::MAX as usize, "Too many result values"); + debug_assert!(num <= u16::MAX as usize, "Too many result values"); self.make_value(ValueData::Inst { ty, inst, @@ -684,7 +684,7 @@ impl DataFlowGraph { pub fn append_ebb_param(&mut self, ebb: Ebb, ty: Type) -> Value { let param = self.values.next_key(); let num = self.ebbs[ebb].params.push(param, &mut self.value_lists); - assert!(num <= u16::MAX as usize, "Too many parameters on EBB"); + debug_assert!(num <= u16::MAX as usize, "Too many parameters on EBB"); self.make_value(ValueData::Param { ty, num: num as u16, @@ -761,9 +761,9 @@ impl DataFlowGraph { /// /// In almost all cases, you should be using `append_ebb_param()` instead of this method. pub fn attach_ebb_param(&mut self, ebb: Ebb, param: Value) { - assert!(!self.value_is_attached(param)); + debug_assert!(!self.value_is_attached(param)); let num = self.ebbs[ebb].params.push(param, &mut self.value_lists); - assert!(num <= u16::MAX as usize, "Too many parameters on EBB"); + debug_assert!(num <= u16::MAX as usize, "Too many parameters on EBB"); let ty = self.value_type(param); self.values[param] = ValueData::Param { ty, @@ -859,7 +859,7 @@ impl DataFlowGraph { /// to create invalid values for index padding which may be reassigned later. #[cold] fn set_value_type_for_parser(&mut self, v: Value, t: Type) { - debug_assert!( + assert!( self.value_type(v) == types::VOID, "this function is only for assigning types to previously invalid values" ); @@ -882,7 +882,7 @@ impl DataFlowGraph { ) -> usize { // Get the call signature if this is a function call. if let Some(sig) = self.call_signature(inst) { - debug_assert_eq!(self.insts[inst].opcode().constraints().fixed_results(), 0); + assert_eq!(self.insts[inst].opcode().constraints().fixed_results(), 0); for res_idx in 0..self.signatures[sig].returns.len() { let ty = self.signatures[sig].returns[res_idx].value_type; if let Some(v) = reuse.get(res_idx) { diff --git a/lib/cretonne/src/ir/immediates.rs b/lib/cretonne/src/ir/immediates.rs index 69607754a2..2d3b1236ef 100644 --- a/lib/cretonne/src/ir/immediates.rs +++ b/lib/cretonne/src/ir/immediates.rs @@ -490,7 +490,7 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result { significand <<= adjust; exponent -= i32::from(adjust); } - assert_eq!(significand >> t, 1); + debug_assert_eq!(significand >> t, 1); // Trailing significand excludes the high bit. let t_bits = significand & ((1 << t) - 1); diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index af570f7ad5..2d462064d1 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -560,7 +560,7 @@ impl OpcodeConstraints { /// Get the value type of result number `n`, having resolved the controlling type variable to /// `ctrl_type`. pub fn result_type(self, n: usize, ctrl_type: Type) -> Type { - assert!(n < self.fixed_results(), "Invalid result index"); + debug_assert!(n < self.fixed_results(), "Invalid result index"); if let ResolvedConstraint::Bound(t) = OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) { @@ -576,7 +576,7 @@ impl OpcodeConstraints { /// Unlike results, it is possible for some input values to vary freely within a specific /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant. pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint { - assert!( + debug_assert!( n < self.fixed_value_arguments(), "Invalid value argument index" ); diff --git a/lib/cretonne/src/ir/layout.rs b/lib/cretonne/src/ir/layout.rs index e18ffd7a33..de117b3ed8 100644 --- a/lib/cretonne/src/ir/layout.rs +++ b/lib/cretonne/src/ir/layout.rs @@ -88,7 +88,7 @@ const LOCAL_LIMIT: SequenceNumber = 100 * MINOR_STRIDE; // Compute the midpoint between `a` and `b`. // Return `None` if the midpoint would be equal to either. fn midpoint(a: SequenceNumber, b: SequenceNumber) -> Option { - assert!(a < b); + debug_assert!(a < b); // Avoid integer overflow. let m = a + (b - a) / 2; if m > a { Some(m) } else { None } @@ -148,7 +148,7 @@ impl Layout { /// Assign a valid sequence number to `ebb` such that the numbers are still monotonic. This may /// require renumbering. fn assign_ebb_seq(&mut self, ebb: Ebb) { - assert!(self.is_ebb_inserted(ebb)); + debug_assert!(self.is_ebb_inserted(ebb)); // Get the sequence number immediately before `ebb`, or 0. let prev_seq = self.ebbs[ebb] @@ -334,13 +334,13 @@ impl Layout { /// Insert `ebb` as the last EBB in the layout. pub fn append_ebb(&mut self, ebb: Ebb) { - assert!( + debug_assert!( !self.is_ebb_inserted(ebb), "Cannot append EBB that is already in the layout" ); { let node = &mut self.ebbs[ebb]; - assert!(node.first_inst.is_none() && node.last_inst.is_none()); + debug_assert!(node.first_inst.is_none() && node.last_inst.is_none()); node.prev = self.last_ebb.into(); node.next = None.into(); } @@ -355,11 +355,11 @@ impl Layout { /// Insert `ebb` in the layout before the existing EBB `before`. pub fn insert_ebb(&mut self, ebb: Ebb, before: Ebb) { - assert!( + debug_assert!( !self.is_ebb_inserted(ebb), "Cannot insert EBB that is already in the layout" ); - assert!( + debug_assert!( self.is_ebb_inserted(before), "EBB Insertion point not in the layout" ); @@ -379,11 +379,11 @@ impl Layout { /// Insert `ebb` in the layout *after* the existing EBB `after`. pub fn insert_ebb_after(&mut self, ebb: Ebb, after: Ebb) { - assert!( + debug_assert!( !self.is_ebb_inserted(ebb), "Cannot insert EBB that is already in the layout" ); - assert!( + debug_assert!( self.is_ebb_inserted(after), "EBB Insertion point not in the layout" ); @@ -403,8 +403,8 @@ impl Layout { /// Remove `ebb` from the layout. pub fn remove_ebb(&mut self, ebb: Ebb) { - assert!(self.is_ebb_inserted(ebb), "EBB not in the layout"); - assert!(self.first_inst(ebb).is_none(), "EBB must be empty."); + debug_assert!(self.is_ebb_inserted(ebb), "EBB not in the layout"); + debug_assert!(self.first_inst(ebb).is_none(), "EBB must be empty."); // Clear the `ebb` node and extract links. let prev; @@ -521,8 +521,8 @@ impl Layout { /// Append `inst` to the end of `ebb`. pub fn append_inst(&mut self, inst: Inst, ebb: Ebb) { - assert_eq!(self.inst_ebb(inst), None); - assert!( + debug_assert_eq!(self.inst_ebb(inst), None); + debug_assert!( self.is_ebb_inserted(ebb), "Cannot append instructions to EBB not in layout" ); @@ -532,7 +532,7 @@ impl Layout { let inst_node = &mut self.insts[inst]; inst_node.ebb = ebb.into(); inst_node.prev = ebb_node.last_inst; - assert!(inst_node.next.is_none()); + debug_assert!(inst_node.next.is_none()); } if ebb_node.first_inst.is_none() { ebb_node.first_inst = inst.into(); @@ -566,7 +566,7 @@ impl Layout { /// Insert `inst` before the instruction `before` in the same EBB. pub fn insert_inst(&mut self, inst: Inst, before: Inst) { - assert_eq!(self.inst_ebb(inst), None); + debug_assert_eq!(self.inst_ebb(inst), None); let ebb = self.inst_ebb(before).expect( "Instruction before insertion point not in the layout", ); @@ -645,7 +645,7 @@ impl Layout { let old_ebb = self.inst_ebb(before).expect( "The `before` instruction must be in the layout", ); - assert!(!self.is_ebb_inserted(new_ebb)); + debug_assert!(!self.is_ebb_inserted(new_ebb)); // Insert new_ebb after old_ebb. let next_ebb = self.ebbs[old_ebb].next; diff --git a/lib/cretonne/src/ir/progpoint.rs b/lib/cretonne/src/ir/progpoint.rs index 480160ea96..b7b2994c3b 100644 --- a/lib/cretonne/src/ir/progpoint.rs +++ b/lib/cretonne/src/ir/progpoint.rs @@ -19,7 +19,7 @@ pub struct ProgramPoint(u32); impl From for ProgramPoint { fn from(inst: Inst) -> ProgramPoint { let idx = inst.index(); - assert!(idx < (u32::MAX / 2) as usize); + debug_assert!(idx < (u32::MAX / 2) as usize); ProgramPoint((idx * 2) as u32) } } @@ -27,7 +27,7 @@ impl From for ProgramPoint { impl From for ProgramPoint { fn from(ebb: Ebb) -> ProgramPoint { let idx = ebb.index(); - assert!(idx < (u32::MAX / 2) as usize); + debug_assert!(idx < (u32::MAX / 2) as usize); ProgramPoint((idx * 2 + 1) as u32) } } diff --git a/lib/cretonne/src/ir/stackslot.rs b/lib/cretonne/src/ir/stackslot.rs index d3578a7570..ebae8e2391 100644 --- a/lib/cretonne/src/ir/stackslot.rs +++ b/lib/cretonne/src/ir/stackslot.rs @@ -244,7 +244,7 @@ impl StackSlots { /// Create a stack slot representing an incoming function argument. pub fn make_incoming_arg(&mut self, ty: Type, offset: StackOffset) -> StackSlot { let mut data = StackSlotData::new(StackSlotKind::IncomingArg, ty.bytes()); - assert!(offset <= StackOffset::max_value() - data.size as StackOffset); + debug_assert!(offset <= StackOffset::max_value() - data.size as StackOffset); data.offset = Some(offset); self.push(data) } @@ -269,7 +269,7 @@ impl StackSlots { // No existing slot found. Make one and insert it into `outgoing`. let mut data = StackSlotData::new(StackSlotKind::OutgoingArg, size); - assert!(offset <= StackOffset::max_value() - size as StackOffset); + debug_assert!(offset <= StackOffset::max_value() - size as StackOffset); data.offset = Some(offset); let ss = self.slots.push(data); self.outgoing.insert(inspos, ss); diff --git a/lib/cretonne/src/isa/intel/abi.rs b/lib/cretonne/src/isa/intel/abi.rs index 2296a9292b..0a14566b50 100644 --- a/lib/cretonne/src/isa/intel/abi.rs +++ b/lib/cretonne/src/isa/intel/abi.rs @@ -107,7 +107,7 @@ impl ArgAssigner for Args { // Assign a stack location. let loc = ArgumentLoc::Stack(self.offset as i32); self.offset += self.pointer_bytes; - assert!(self.offset <= i32::MAX as u32); + debug_assert!(self.offset <= i32::MAX as u32); loc.into() } } diff --git a/lib/cretonne/src/isa/riscv/abi.rs b/lib/cretonne/src/isa/riscv/abi.rs index 52e715be43..1db377b0ef 100644 --- a/lib/cretonne/src/isa/riscv/abi.rs +++ b/lib/cretonne/src/isa/riscv/abi.rs @@ -80,7 +80,7 @@ impl ArgAssigner for Args { // Assign a stack location. let loc = ArgumentLoc::Stack(self.offset as i32); self.offset += self.pointer_bytes; - assert!(self.offset <= i32::MAX as u32); + debug_assert!(self.offset <= i32::MAX as u32); loc.into() } } diff --git a/lib/cretonne/src/isa/riscv/binemit.rs b/lib/cretonne/src/isa/riscv/binemit.rs index 369a9bf09f..8cc1ef17d1 100644 --- a/lib/cretonne/src/isa/riscv/binemit.rs +++ b/lib/cretonne/src/isa/riscv/binemit.rs @@ -133,7 +133,7 @@ fn put_sb(bits: u16, imm: i64, rs1: RegUnit, rs2: RegUnit let rs1 = u32::from(rs1) & 0x1f; let rs2 = u32::from(rs2) & 0x1f; - assert!(is_signed_int(imm, 13, 1), "SB out of range {:#x}", imm); + debug_assert!(is_signed_int(imm, 13, 1), "SB out of range {:#x}", imm); let imm = imm as u32; // 0-6: opcode @@ -164,7 +164,7 @@ fn put_uj(bits: u16, imm: i64, rd: RegUnit, sink: &mut CS let opcode5 = bits & 0x1f; let rd = u32::from(rd) & 0x1f; - assert!(is_signed_int(imm, 21, 1), "UJ out of range {:#x}", imm); + debug_assert!(is_signed_int(imm, 21, 1), "UJ out of range {:#x}", imm); let imm = imm as u32; // 0-6: opcode diff --git a/lib/cretonne/src/legalizer/boundary.rs b/lib/cretonne/src/legalizer/boundary.rs index 9ea50b202d..29af50b3ad 100644 --- a/lib/cretonne/src/legalizer/boundary.rs +++ b/lib/cretonne/src/legalizer/boundary.rs @@ -85,15 +85,15 @@ fn legalize_entry_params(func: &mut Function, entry: Ebb) { ArgumentPurpose::FramePointer => {} ArgumentPurpose::CalleeSaved => {} ArgumentPurpose::StructReturn => { - assert!(!has_sret, "Multiple sret arguments found"); + debug_assert!(!has_sret, "Multiple sret arguments found"); has_sret = true; } ArgumentPurpose::VMContext => { - assert!(!has_vmctx, "Multiple vmctx arguments found"); + debug_assert!(!has_vmctx, "Multiple vmctx arguments found"); has_vmctx = true; } ArgumentPurpose::SignatureId => { - assert!(!has_sigid, "Multiple sigid arguments found"); + debug_assert!(!has_sigid, "Multiple sigid arguments found"); has_sigid = true; } _ => panic!("Unexpected special-purpose arg {}", abi_type), @@ -103,7 +103,7 @@ fn legalize_entry_params(func: &mut Function, entry: Ebb) { // Compute the value we want for `arg` from the legalized ABI parameters. let mut get_arg = |func: &mut Function, ty| { let abi_type = func.signature.params[abi_arg]; - assert_eq!( + debug_assert_eq!( abi_type.purpose, ArgumentPurpose::Normal, "Can't legalize special-purpose argument" @@ -118,7 +118,7 @@ fn legalize_entry_params(func: &mut Function, entry: Ebb) { let converted = convert_from_abi(&mut pos, arg_type, Some(arg), &mut get_arg); // The old `arg` is no longer an attached EBB argument, but there are probably still // uses of the value. - assert_eq!(pos.func.dfg.resolve_aliases(arg), converted); + debug_assert_eq!(pos.func.dfg.resolve_aliases(arg), converted); } } @@ -138,19 +138,19 @@ fn legalize_entry_params(func: &mut Function, entry: Ebb) { } // These can be meaningfully added by `legalize_signature()`. ArgumentPurpose::Link => { - assert!(!has_link, "Multiple link parameters found"); + debug_assert!(!has_link, "Multiple link parameters found"); has_link = true; } ArgumentPurpose::StructReturn => { - assert!(!has_sret, "Multiple sret parameters found"); + debug_assert!(!has_sret, "Multiple sret parameters found"); has_sret = true; } ArgumentPurpose::VMContext => { - assert!(!has_vmctx, "Multiple vmctx parameters found"); + debug_assert!(!has_vmctx, "Multiple vmctx parameters found"); has_vmctx = true; } ArgumentPurpose::SignatureId => { - assert!(!has_sigid, "Multiple sigid parameters found"); + debug_assert!(!has_sigid, "Multiple sigid parameters found"); has_sigid = true; } } @@ -180,7 +180,7 @@ where // We theoretically allow for call instructions that return a number of fixed results before // the call return values. In practice, it doesn't happen. let fixed_results = pos.func.dfg[call].opcode().constraints().fixed_results(); - assert_eq!(fixed_results, 0, "Fixed results on calls not supported"); + debug_assert_eq!(fixed_results, 0, "Fixed results on calls not supported"); let results = pos.func.dfg.detach_results(call); let mut next_res = 0; @@ -209,7 +209,7 @@ where } }; let v = convert_from_abi(pos, res_type, Some(res), &mut get_res); - assert_eq!(pos.func.dfg.resolve_aliases(res), v); + debug_assert_eq!(pos.func.dfg.resolve_aliases(res), v); } } @@ -238,7 +238,7 @@ where let arg_type = match get_arg(pos.func, ty) { Ok(v) => { debug_assert_eq!(pos.func.dfg.value_type(v), ty); - assert_eq!(into_result, None); + debug_assert_eq!(into_result, None); return v; } Err(t) => t, @@ -274,7 +274,7 @@ where } // Construct a `ty` by bit-casting from an integer type. ValueConversion::IntBits => { - assert!(!ty.is_int()); + debug_assert!(!ty.is_int()); let abi_ty = Type::int(ty.bits()).expect("Invalid type for conversion"); let arg = convert_from_abi(pos, abi_ty, None, get_arg); pos.ins().with_results([into_result]).bitcast(ty, arg) @@ -340,7 +340,7 @@ fn convert_to_abi( convert_to_abi(pos, cfg, hi, put_arg); } ValueConversion::IntBits => { - assert!(!ty.is_int()); + debug_assert!(!ty.is_int()); let abi_ty = Type::int(ty.bits()).expect("Invalid type for conversion"); let arg = pos.ins().bitcast(abi_ty, value); convert_to_abi(pos, cfg, arg, put_arg); @@ -555,7 +555,7 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph legalize_inst_arguments(pos, cfg, abi_args, |func, abi_arg| { func.signature.returns[abi_arg] }); - assert_eq!(pos.func.dfg.inst_variable_args(inst).len(), abi_args); + debug_assert_eq!(pos.func.dfg.inst_variable_args(inst).len(), abi_args); // 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 diff --git a/lib/cretonne/src/legalizer/globalvar.rs b/lib/cretonne/src/legalizer/globalvar.rs index b945923f2f..5f6427822b 100644 --- a/lib/cretonne/src/legalizer/globalvar.rs +++ b/lib/cretonne/src/legalizer/globalvar.rs @@ -18,7 +18,7 @@ pub fn expand_global_addr( // Unpack the instruction. let gv = match func.dfg[inst] { ir::InstructionData::UnaryGlobalVar { opcode, global_var } => { - assert_eq!(opcode, ir::Opcode::GlobalAddr); + debug_assert_eq!(opcode, ir::Opcode::GlobalAddr); global_var } _ => panic!("Wanted global_addr: {}", func.dfg.display_inst(inst, None)), diff --git a/lib/cretonne/src/legalizer/heap.rs b/lib/cretonne/src/legalizer/heap.rs index af40181d1a..37ce226351 100644 --- a/lib/cretonne/src/legalizer/heap.rs +++ b/lib/cretonne/src/legalizer/heap.rs @@ -24,7 +24,7 @@ pub fn expand_heap_addr( arg, imm, } => { - assert_eq!(opcode, ir::Opcode::HeapAddr); + debug_assert_eq!(opcode, ir::Opcode::HeapAddr); (heap, arg, imm.into()) } _ => panic!("Wanted heap_addr: {}", func.dfg.display_inst(inst, None)), diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 189a3e735d..51198d05a0 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -248,7 +248,7 @@ fn expand_fconst( _isa: &TargetIsa, ) { let ty = func.dfg.value_type(func.dfg.first_result(inst)); - assert!(!ty.is_vector(), "Only scalar fconst supported: {}", ty); + debug_assert!(!ty.is_vector(), "Only scalar fconst supported: {}", ty); // In the future, we may want to generate constant pool entries for these constants, but for // now use an `iconst` and a bit cast. diff --git a/lib/cretonne/src/legalizer/split.rs b/lib/cretonne/src/legalizer/split.rs index 0c73e7c2d4..aafc050c26 100644 --- a/lib/cretonne/src/legalizer/split.rs +++ b/lib/cretonne/src/legalizer/split.rs @@ -127,7 +127,7 @@ fn split_any( while let Some(repair) = repairs.pop() { for (_, inst) in cfg.pred_iter(repair.ebb) { let branch_opc = pos.func.dfg[inst].opcode(); - assert!( + debug_assert!( branch_opc.is_branch(), "Predecessor not a branch: {}", pos.func.dfg.display_inst(inst, None) @@ -198,7 +198,7 @@ fn split_value( // This is an instruction result. See if the value was created by a `concat` // instruction. if let InstructionData::Binary { opcode, args, .. } = pos.func.dfg[inst] { - assert_eq!(num, 0); + debug_assert_eq!(num, 0); if opcode == concat { reuse = Some((args[0], args[1])); } diff --git a/lib/cretonne/src/preopt.rs b/lib/cretonne/src/preopt.rs index 1fdee0ad80..8e4bef8da3 100644 --- a/lib/cretonne/src/preopt.rs +++ b/lib/cretonne/src/preopt.rs @@ -192,10 +192,10 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // U32 div, rem by a power-of-2 &DivRemByConstInfo::DivU32(n1, d) | &DivRemByConstInfo::RemU32(n1, d) if d.is_power_of_two() => { - assert!(d >= 2); + debug_assert!(d >= 2); // compute k where d == 2^k let k = d.trailing_zeros(); - assert!(k >= 1 && k <= 31); + debug_assert!(k >= 1 && k <= 31); if isRem { let mask = (1u64 << k) - 1; pos.func.dfg.replace(inst).band_imm(n1, mask as i64); @@ -207,7 +207,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // U32 div, rem by non-power-of-2 &DivRemByConstInfo::DivU32(n1, d) | &DivRemByConstInfo::RemU32(n1, d) => { - assert!(d >= 3); + debug_assert!(d >= 3); let MU32 { mulBy, doAdd, @@ -217,7 +217,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso let q0 = pos.ins().iconst(I32, mulBy as i64); let q1 = pos.ins().umulhi(n1, q0); if doAdd { - assert!(shiftBy >= 1 && shiftBy <= 32); + debug_assert!(shiftBy >= 1 && shiftBy <= 32); let t1 = pos.ins().isub(n1, q1); let t2 = pos.ins().ushr_imm(t1, 1); let t3 = pos.ins().iadd(t2, q1); @@ -226,7 +226,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso debug_assert!(shiftBy != 1); qf = pos.ins().ushr_imm(t3, (shiftBy - 1) as i64); } else { - assert!(shiftBy >= 0 && shiftBy <= 31); + debug_assert!(shiftBy >= 0 && shiftBy <= 31); // Whereas there are known cases here for shiftBy == 0. if shiftBy > 0 { qf = pos.ins().ushr_imm(q1, shiftBy as i64); @@ -264,10 +264,10 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // U64 div, rem by a power-of-2 &DivRemByConstInfo::DivU64(n1, d) | &DivRemByConstInfo::RemU64(n1, d) if d.is_power_of_two() => { - assert!(d >= 2); + debug_assert!(d >= 2); // compute k where d == 2^k let k = d.trailing_zeros(); - assert!(k >= 1 && k <= 63); + debug_assert!(k >= 1 && k <= 63); if isRem { let mask = (1u64 << k) - 1; pos.func.dfg.replace(inst).band_imm(n1, mask as i64); @@ -279,7 +279,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso // U64 div, rem by non-power-of-2 &DivRemByConstInfo::DivU64(n1, d) | &DivRemByConstInfo::RemU64(n1, d) => { - assert!(d >= 3); + debug_assert!(d >= 3); let MU64 { mulBy, doAdd, @@ -289,7 +289,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso let q0 = pos.ins().iconst(I64, mulBy as i64); let q1 = pos.ins().umulhi(n1, q0); if doAdd { - assert!(shiftBy >= 1 && shiftBy <= 64); + debug_assert!(shiftBy >= 1 && shiftBy <= 64); let t1 = pos.ins().isub(n1, q1); let t2 = pos.ins().ushr_imm(t1, 1); let t3 = pos.ins().iadd(t2, q1); @@ -298,7 +298,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso debug_assert!(shiftBy != 1); qf = pos.ins().ushr_imm(t3, (shiftBy - 1) as i64); } else { - assert!(shiftBy >= 0 && shiftBy <= 63); + debug_assert!(shiftBy >= 0 && shiftBy <= 63); // Whereas there are known cases here for shiftBy == 0. if shiftBy > 0 { qf = pos.ins().ushr_imm(q1, shiftBy as i64); @@ -339,7 +339,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso &DivRemByConstInfo::RemS32(n1, d) => { if let Some((isNeg, k)) = isPowerOf2_S32(d) { // k can be 31 only in the case that d is -2^31. - assert!(k >= 1 && k <= 31); + debug_assert!(k >= 1 && k <= 31); let t1 = if k - 1 == 0 { n1 } else { @@ -363,7 +363,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso } } else { // S32 div, rem by a non-power-of-2 - assert!(d < -2 || d > 2); + debug_assert!(d < -2 || d > 2); let MS32 { mulBy, shiftBy } = magicS32(d); let q0 = pos.ins().iconst(I32, mulBy as i64); let q1 = pos.ins().smulhi(n1, q0); @@ -374,7 +374,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso } else { q1 }; - assert!(shiftBy >= 0 && shiftBy <= 31); + debug_assert!(shiftBy >= 0 && shiftBy <= 31); let q3 = if shiftBy == 0 { q2 } else { @@ -416,7 +416,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso &DivRemByConstInfo::RemS64(n1, d) => { if let Some((isNeg, k)) = isPowerOf2_S64(d) { // k can be 63 only in the case that d is -2^63. - assert!(k >= 1 && k <= 63); + debug_assert!(k >= 1 && k <= 63); let t1 = if k - 1 == 0 { n1 } else { @@ -440,7 +440,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso } } else { // S64 div, rem by a non-power-of-2 - assert!(d < -2 || d > 2); + debug_assert!(d < -2 || d > 2); let MS64 { mulBy, shiftBy } = magicS64(d); let q0 = pos.ins().iconst(I64, mulBy); let q1 = pos.ins().smulhi(n1, q0); @@ -451,7 +451,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso } else { q1 }; - assert!(shiftBy >= 0 && shiftBy <= 63); + debug_assert!(shiftBy >= 0 && shiftBy <= 63); let q3 = if shiftBy == 0 { q2 } else { diff --git a/lib/cretonne/src/regalloc/coalescing.rs b/lib/cretonne/src/regalloc/coalescing.rs index 1dc06c6c2b..10d0df2e62 100644 --- a/lib/cretonne/src/regalloc/coalescing.rs +++ b/lib/cretonne/src/regalloc/coalescing.rs @@ -238,7 +238,7 @@ impl<'a> Context<'a> { // 1. It is defined in a dominating EBB and live-in to `ebb`. // 2. If is itself a parameter value for `ebb`. This case should already have been // eliminated by `isolate_conflicting_params()`. - assert!( + debug_assert!( lr.def() != ebb.into(), "{} parameter {} was missed by isolate_conflicting_params()", ebb, @@ -494,8 +494,8 @@ impl<'a> Context<'a> { // Second everything else in reverse layout order. Again, short forward branches get merged // first. There can also be backwards branches mixed in here, though, as long as they are // not loop backedges. - assert!(self.predecessors.is_empty()); - assert!(self.backedges.is_empty()); + debug_assert!(self.predecessors.is_empty()); + debug_assert!(self.backedges.is_empty()); for (pred_ebb, pred_inst) in self.cfg.pred_iter(ebb) { if self.preorder.dominates(ebb, pred_ebb) { self.backedges.push(pred_inst); @@ -957,7 +957,8 @@ impl VirtualCopies { /// Indicate that `param` is now fully merged. pub fn merged_param(&mut self, param: Value, func: &Function) { - assert_eq!(self.params.pop(), Some(param)); + let popped = self.params.pop(); + debug_assert_eq!(popped, Some(param)); // The domtree pre-order in `self.params` guarantees that all parameters defined at the // same EBB will be adjacent. This means we can see when all parameters at an EBB have been diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index fb60a979c8..2de41e67c6 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -246,7 +246,7 @@ impl<'a> Context<'a> { /// Return the set of remaining allocatable registers after filtering out the dead arguments. fn color_entry_params(&mut self, args: &[LiveValue]) -> AvailableRegs { let sig = &self.cur.func.signature; - assert_eq!(sig.params.len(), args.len()); + debug_assert_eq!(sig.params.len(), args.len()); let mut regs = AvailableRegs::new(&self.usable_regs); @@ -271,7 +271,7 @@ impl<'a> Context<'a> { } // The spiller will have assigned an incoming stack slot already. - Affinity::Stack => assert!(abi.location.is_stack()), + Affinity::Stack => debug_assert!(abi.location.is_stack()), // This is a ghost value, unused in the function. Don't assign it to a location // either. Affinity::None => {} @@ -340,7 +340,7 @@ impl<'a> Context<'a> { } else { // This is a multi-way branch like `br_table`. We only support arguments on // single-destination branches. - assert_eq!( + debug_assert_eq!( self.cur.func.dfg.inst_variable_args(inst).len(), 0, "Can't handle EBB arguments: {}", @@ -586,7 +586,7 @@ impl<'a> Context<'a> { // Now handle the EBB arguments. let br_args = self.cur.func.dfg.inst_variable_args(inst); let dest_args = self.cur.func.dfg.ebb_params(dest); - assert_eq!(br_args.len(), dest_args.len()); + debug_assert_eq!(br_args.len(), dest_args.len()); for (&dest_arg, &br_arg) in dest_args.iter().zip(br_args) { // The first time we encounter a branch to `dest`, we get to pick the location. The // following times we see a branch to `dest`, we must follow suit. @@ -631,7 +631,7 @@ impl<'a> Context<'a> { fn color_ebb_params(&mut self, inst: Inst, dest: Ebb) { let br_args = self.cur.func.dfg.inst_variable_args(inst); let dest_args = self.cur.func.dfg.ebb_params(dest); - assert_eq!(br_args.len(), dest_args.len()); + debug_assert_eq!(br_args.len(), dest_args.len()); for (&dest_arg, &br_arg) in dest_args.iter().zip(br_args) { match self.cur.func.locations[dest_arg] { ValueLoc::Unassigned => { @@ -741,7 +741,7 @@ impl<'a> Context<'a> { // 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. // Just assume all results are variable return values. - assert_eq!(defs.len(), self.cur.func.dfg.signatures[sig].returns.len()); + debug_assert_eq!(defs.len(), self.cur.func.dfg.signatures[sig].returns.len()); for (i, lv) in defs.iter().enumerate() { let abi = self.cur.func.dfg.signatures[sig].returns[i]; if let ArgumentLoc::Reg(reg) = abi.location { @@ -787,7 +787,7 @@ impl<'a> Context<'a> { } let ok = self.solver.add_fixed_output(rc, reg); - assert!(ok, "Couldn't clear fixed output interference for {}", value); + debug_assert!(ok, "Couldn't clear fixed output interference for {}", value); } self.cur.func.locations[value] = ValueLoc::Reg(reg); } @@ -858,11 +858,8 @@ impl<'a> Context<'a> { Ok(regs) => return regs, Err(SolverError::Divert(rc)) => { // Do we have any live-through `rc` registers that are not already variables? - assert!( - self.try_add_var(rc, throughs), - "Ran out of registers in {}", - rc - ); + let added = self.try_add_var(rc, throughs); + debug_assert!(added, "Ran out of registers in {}", rc); } Err(SolverError::Global(value)) => { dbg!("Not enough global registers for {}, trying as local", value); @@ -941,7 +938,7 @@ impl<'a> Context<'a> { // It is very unlikely (impossible?) that we would need more than one spill per top-level // register class, so avoid allocation by using a fixed array here. let mut slot = [PackedOption::default(); 8]; - assert!(spills <= slot.len(), "Too many spills ({})", spills); + debug_assert!(spills <= slot.len(), "Too many spills ({})", spills); for m in self.solver.moves() { match *m { diff --git a/lib/cretonne/src/regalloc/live_value_tracker.rs b/lib/cretonne/src/regalloc/live_value_tracker.rs index 3d7eeaa6e5..f53b5ec8bd 100644 --- a/lib/cretonne/src/regalloc/live_value_tracker.rs +++ b/lib/cretonne/src/regalloc/live_value_tracker.rs @@ -207,7 +207,7 @@ impl LiveValueTracker { let first_arg = self.live.values.len(); for &value in dfg.ebb_params(ebb) { let lr = &liveness[value]; - assert_eq!(lr.def(), ebb.into()); + debug_assert_eq!(lr.def(), ebb.into()); match lr.def_local_end().into() { ExpandedProgramPoint::Inst(endpoint) => { self.live.push(value, endpoint, lr); @@ -215,7 +215,7 @@ impl LiveValueTracker { ExpandedProgramPoint::Ebb(local_ebb) => { // This is a dead EBB parameter which is not even live into the first // instruction in the EBB. - assert_eq!( + debug_assert_eq!( local_ebb, ebb, "EBB parameter live range ends at wrong EBB header" @@ -273,7 +273,7 @@ impl LiveValueTracker { let first_def = self.live.values.len(); for &value in dfg.inst_results(inst) { let lr = &liveness[value]; - assert_eq!(lr.def(), inst.into()); + debug_assert_eq!(lr.def(), inst.into()); match lr.def_local_end().into() { ExpandedProgramPoint::Inst(endpoint) => { self.live.push(value, endpoint, lr); diff --git a/lib/cretonne/src/regalloc/liveness.rs b/lib/cretonne/src/regalloc/liveness.rs index 2778f47f63..11e7d8cc3d 100644 --- a/lib/cretonne/src/regalloc/liveness.rs +++ b/lib/cretonne/src/regalloc/liveness.rs @@ -251,7 +251,7 @@ fn extend_to_use( forest: &mut LiveRangeForest, ) { // This is our scratch working space, and we'll leave it empty when we return. - assert!(worklist.is_empty()); + debug_assert!(worklist.is_empty()); // Extend the range locally in `ebb`. // If there already was a live interval in that block, we're done. @@ -338,7 +338,7 @@ impl Liveness { let old = self.ranges.insert( LiveRange::new(value, def.into(), affinity), ); - assert!(old.is_none(), "{} already has a live range", value); + debug_assert!(old.is_none(), "{} already has a live range", value); } /// Move the definition of `value` to `def`. @@ -367,7 +367,7 @@ impl Liveness { debug_assert_eq!(Some(ebb), layout.inst_ebb(user)); let lr = self.ranges.get_mut(value).expect("Value has no live range"); let livein = lr.extend_in_ebb(ebb, user, layout, &mut self.forest); - assert!(!livein, "{} should already be live in {}", value, ebb); + debug_assert!(!livein, "{} should already be live in {}", value, ebb); &mut lr.affinity } diff --git a/lib/cretonne/src/regalloc/liverange.rs b/lib/cretonne/src/regalloc/liverange.rs index 7d7d6a8775..9462d32ad0 100644 --- a/lib/cretonne/src/regalloc/liverange.rs +++ b/lib/cretonne/src/regalloc/liverange.rs @@ -253,7 +253,7 @@ impl GenLiveRange { order.cmp(to, self.def_begin) != Ordering::Less { let to_pp = to.into(); - assert_ne!( + debug_assert_ne!( to_pp, self.def_begin, "Can't use value in the defining instruction." diff --git a/lib/cretonne/src/regalloc/reload.rs b/lib/cretonne/src/regalloc/reload.rs index 12fd8d310a..2967081807 100644 --- a/lib/cretonne/src/regalloc/reload.rs +++ b/lib/cretonne/src/regalloc/reload.rs @@ -145,7 +145,7 @@ impl<'a> Context<'a> { ); if self.cur.func.layout.entry_block() == Some(ebb) { - assert_eq!(liveins.len(), 0); + debug_assert_eq!(liveins.len(), 0); self.visit_entry_params(ebb, args); } else { self.visit_ebb_params(ebb, args); @@ -155,7 +155,7 @@ impl<'a> Context<'a> { /// Visit the parameters on the entry block. /// These values have ABI constraints from the function signature. fn visit_entry_params(&mut self, ebb: Ebb, args: &[LiveValue]) { - assert_eq!(self.cur.func.signature.params.len(), args.len()); + debug_assert_eq!(self.cur.func.signature.params.len(), args.len()); self.cur.goto_first_inst(ebb); for (arg_idx, arg) in args.iter().enumerate() { @@ -175,7 +175,7 @@ impl<'a> Context<'a> { } } ArgumentLoc::Stack(_) => { - assert!(arg.affinity.is_stack()); + debug_assert!(arg.affinity.is_stack()); } ArgumentLoc::Unassigned => panic!("Unexpected ABI location"), } @@ -203,7 +203,7 @@ impl<'a> Context<'a> { ); // Identify reload candidates. - assert!(self.candidates.is_empty()); + debug_assert!(self.candidates.is_empty()); self.find_candidates(inst, constraints); // Insert fill instructions before `inst` and replace `cand.value` with the filled value. @@ -375,7 +375,7 @@ fn handle_abi_args( isa: &TargetIsa, liveness: &Liveness, ) { - assert_eq!(abi_types.len(), var_args.len()); + debug_assert_eq!(abi_types.len(), var_args.len()); for ((abi, &arg), argidx) in abi_types.iter().zip(var_args).zip(offset..) { if abi.location.is_reg() { let lv = liveness.get(arg).expect("Missing live range for ABI arg"); diff --git a/lib/cretonne/src/regalloc/solver.rs b/lib/cretonne/src/regalloc/solver.rs index a3b732799c..0e68099a67 100644 --- a/lib/cretonne/src/regalloc/solver.rs +++ b/lib/cretonne/src/regalloc/solver.rs @@ -565,7 +565,7 @@ impl Solver { dbg!("-> converting variable {} to a fixed constraint", v); // The spiller is responsible for ensuring that all constraints on the uses of a // value are compatible. - assert!( + debug_assert!( v.constraint.contains(to), "Incompatible constraints for {}", value @@ -665,7 +665,7 @@ impl Solver { // No variable, then it must be a fixed reassignment. if let Some(a) = self.assignments.get(value) { dbg!("-> already fixed assignment {}", a); - assert!( + debug_assert!( constraint.contains(a.to), "Incompatible constraints for {}", value @@ -708,7 +708,7 @@ impl Solver { /// Call this method to indicate that there will be no more fixed input reassignments added /// and prepare for the output side constraints. pub fn inputs_done(&mut self) { - assert!(!self.has_fixed_input_conflicts()); + debug_assert!(!self.has_fixed_input_conflicts()); // At this point, `regs_out` contains the `to` side of the input reassignments, and the // `from` side has already been marked as available in `regs_in`. @@ -746,7 +746,7 @@ impl Solver { // interference constraints on the output side. // Variables representing tied operands will get their `is_output` flag set again later. if let Some(v) = self.vars.iter_mut().find(|v| v.value == value) { - assert!(v.is_input); + debug_assert!(v.is_input); v.is_output = false; return; } @@ -782,7 +782,7 @@ impl Solver { // Check if a variable was created. if let Some(v) = self.vars.iter_mut().find(|v| v.value == value) { - assert!(v.is_input); + debug_assert!(v.is_input); v.is_output = true; v.is_global = is_global; return None; @@ -1026,7 +1026,7 @@ impl Solver { /// Returns the number of spills that had to be emitted. pub fn schedule_moves(&mut self, regs: &AllocatableSet) -> usize { self.collect_moves(); - assert!(self.fills.is_empty()); + debug_assert!(self.fills.is_empty()); let mut num_spill_slots = 0; let mut avail = regs.clone(); diff --git a/lib/cretonne/src/regalloc/spilling.rs b/lib/cretonne/src/regalloc/spilling.rs index 84ef76fdff..15739cd260 100644 --- a/lib/cretonne/src/regalloc/spilling.rs +++ b/lib/cretonne/src/regalloc/spilling.rs @@ -242,7 +242,7 @@ impl<'a> Context<'a> { debug_assert_eq!(self.cur.current_ebb(), Some(ebb)); // We may need to resolve register constraints if there are any noteworthy uses. - assert!(self.reg_uses.is_empty()); + debug_assert!(self.reg_uses.is_empty()); self.collect_reg_uses(inst, ebb, constraints); // Calls usually have fixed register uses. diff --git a/lib/cretonne/src/regalloc/virtregs.rs b/lib/cretonne/src/regalloc/virtregs.rs index 5116e8f02a..fca213a62b 100644 --- a/lib/cretonne/src/regalloc/virtregs.rs +++ b/lib/cretonne/src/regalloc/virtregs.rs @@ -140,7 +140,7 @@ impl VirtRegs { func: &Function, preorder: &DominatorTreePreorder, ) -> VirtReg { - assert_eq!(self.get(single), None, "Expected singleton {}", single); + debug_assert_eq!(self.get(single), None, "Expected singleton {}", single); // Make sure `big` has a vreg. let vreg = self.get(big).unwrap_or_else(|| { @@ -208,7 +208,7 @@ impl VirtRegs { } } - assert_eq!( + debug_assert_eq!( values.len(), singletons + cleared, "Can't unify partial virtual registers" diff --git a/lib/cretonne/src/stack_layout.rs b/lib/cretonne/src/stack_layout.rs index 16dcea8d5f..f1cf35f150 100644 --- a/lib/cretonne/src/stack_layout.rs +++ b/lib/cretonne/src/stack_layout.rs @@ -19,7 +19,7 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result Result Self { - assert!(index < u32::MAX); + debug_assert!(index < u32::MAX); Variable(index) } } impl EntityRef for Variable { fn new(index: usize) -> Self { - assert!(index < (u32::MAX as usize)); + debug_assert!(index < (u32::MAX as usize)); Variable(index as u32) } diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 60b432a582..5535a1555e 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -1026,7 +1026,7 @@ fn get_heap_addr( use std::cmp::min; let guard_size: i64 = builder.func.heaps[heap].guard_size.into(); - assert!(guard_size > 0, "Heap guard pages currently required"); + debug_assert!(guard_size > 0, "Heap guard pages currently required"); // Generate `heap_addr` instructions that are friendly to CSE by checking offsets that are // multiples of the guard size. Add one to make sure that we check the pointer itself is in diff --git a/lib/wasm/src/func_translator.rs b/lib/wasm/src/func_translator.rs index 0632630095..cef629c7dc 100644 --- a/lib/wasm/src/func_translator.rs +++ b/lib/wasm/src/func_translator.rs @@ -74,8 +74,8 @@ impl FuncTranslator { func.name, func.signature ); - assert_eq!(func.dfg.num_ebbs(), 0, "Function must be empty"); - assert_eq!(func.dfg.num_insts(), 0, "Function must be empty"); + debug_assert_eq!(func.dfg.num_ebbs(), 0, "Function must be empty"); + debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty"); // This clears the `ILBuilder`. let mut builder = FunctionBuilder::new(func, &mut self.il_builder); @@ -191,7 +191,7 @@ fn parse_function_body( environ: &mut FE, ) -> CtonResult { // The control stack is initialized with a single block representing the whole function. - assert_eq!(state.control_stack.len(), 1, "State not initialized"); + debug_assert_eq!(state.control_stack.len(), 1, "State not initialized"); // Keep going until the final `End` operator which pops the outermost block. while !state.control_stack.is_empty() { From f04e02c0a15850e141347d5d4e36ebedaa901b30 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 12 Mar 2018 10:28:39 -0700 Subject: [PATCH 57/58] Clarify comments about Conventional SSA form. Captialize "Conventional" so that it's clear that "Conventional SSA" is a specific concept being referenced. --- lib/cretonne/src/regalloc/coalescing.rs | 12 ++++++------ lib/cretonne/src/regalloc/coloring.rs | 2 +- lib/cretonne/src/regalloc/context.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/cretonne/src/regalloc/coalescing.rs b/lib/cretonne/src/regalloc/coalescing.rs index 10d0df2e62..3b9d6d03bb 100644 --- a/lib/cretonne/src/regalloc/coalescing.rs +++ b/lib/cretonne/src/regalloc/coalescing.rs @@ -1,9 +1,9 @@ -//! Constructing conventional SSA form. +//! Constructing Conventional SSA form. //! -//! Conventional SSA form is a subset of SSA form where any (transitively) phi-related values do -//! not interfere. We construct CSSA by building virtual registers that are as large as possible -//! and inserting copies where necessary such that all argument values passed to an EBB parameter -//! will belong to the same virtual register as the EBB parameter value itself. +//! Conventional SSA (CSSA) form is a subset of SSA form where any (transitively) phi-related +//! values do not interfere. We construct CSSA by building virtual registers that are as large as +//! possible and inserting copies where necessary such that all argument values passed to an EBB +//! parameter will belong to the same virtual register as the EBB parameter value itself. use cursor::{Cursor, EncCursor}; use dbg::DisplayList; @@ -103,7 +103,7 @@ impl Coalescing { self.backedges.clear(); } - /// 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, isa: &TargetIsa, diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index 2de41e67c6..d7ab5dd0f3 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -23,7 +23,7 @@ //! operands are allowed to read spilled values, but each such instance must be counted as using //! a register. //! -//! 5. The code must be in conventional SSA form. Among other things, this means that values passed +//! 5. The code must be in Conventional SSA form. Among other things, this means that values passed //! as arguments when branching to an EBB must belong to the same virtual register as the //! corresponding EBB argument value. //! diff --git a/lib/cretonne/src/regalloc/context.rs b/lib/cretonne/src/regalloc/context.rs index 765ecf155c..a8284a9579 100644 --- a/lib/cretonne/src/regalloc/context.rs +++ b/lib/cretonne/src/regalloc/context.rs @@ -90,7 +90,7 @@ impl Context { verify_liveness(isa, func, cfg, &self.liveness)?; } - // Pass: Coalesce and create conventional SSA form. + // Pass: Coalesce and create Conventional SSA form. self.coalescing.conventional_ssa( isa, func, From 11eddafef8cea4ced9cc36a47bb511119320f8ff Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 12 Mar 2018 10:28:42 -0700 Subject: [PATCH 58/58] Avoid using floating-point values in expand_fcvt_to_sint. Compute the bound values for expand_fcvt_to_sint using bitwise integer arithmetic rather than floating-point arithmetic, to avoid relying on host floating point arithmetic. --- lib/cretonne/src/ir/immediates.rs | 40 ++++++++++++++++++++++++ lib/cretonne/src/isa/intel/enc_tables.rs | 17 +++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/lib/cretonne/src/ir/immediates.rs b/lib/cretonne/src/ir/immediates.rs index 2d3b1236ef..69c9aab51a 100644 --- a/lib/cretonne/src/ir/immediates.rs +++ b/lib/cretonne/src/ir/immediates.rs @@ -538,6 +538,17 @@ impl Ieee32 { Ieee32(exponent << t) } + /// Create an `Ieee32` number representing the greatest negative value + /// not convertable from f32 to a signed integer with width n. + pub fn fcvt_to_sint_negative_overflow>(n: I) -> Ieee32 { + let n = n.into(); + debug_assert!(n < 32); + debug_assert!(23 + 1 - n < 32); + Self::with_bits( + (1u32 << (32 - 1)) | Self::pow2(n - 1).0 | (1u32 << (23 + 1 - n)), + ) + } + /// Return self negated. pub fn neg(self) -> Ieee32 { Ieee32(self.0 ^ (1 << 31)) @@ -590,6 +601,17 @@ impl Ieee64 { Ieee64(exponent << t) } + /// Create an `Ieee64` number representing the greatest negative value + /// not convertable from f64 to a signed integer with width n. + pub fn fcvt_to_sint_negative_overflow>(n: I) -> Ieee64 { + let n = n.into(); + debug_assert!(n < 64); + debug_assert!(52 + 1 - n < 64); + Self::with_bits( + (1u64 << (64 - 1)) | Self::pow2(n - 1).0 | (1u64 << (52 + 1 - n)), + ) + } + /// Return self negated. pub fn neg(self) -> Ieee64 { Ieee64(self.0 ^ (1 << 63)) @@ -857,6 +879,15 @@ mod tests { assert_eq!(Ieee32::pow2(1).neg().to_string(), "-0x1.000000p1"); } + #[test] + fn fcvt_to_sint_negative_overflow_ieee32() { + for n in &[8, 16] { + assert_eq!(-((1u32 << (n - 1)) as f32) - 1.0, unsafe { + mem::transmute(Ieee32::fcvt_to_sint_negative_overflow(*n)) + }); + } + } + #[test] fn format_ieee64() { assert_eq!(Ieee64::with_float(0.0).to_string(), "0.0"); @@ -985,4 +1016,13 @@ mod tests { assert_eq!(Ieee64::pow2(1).neg().to_string(), "-0x1.0000000000000p1"); } + + #[test] + fn fcvt_to_sint_negative_overflow_ieee64() { + for n in &[8, 16, 32] { + assert_eq!(-((1u64 << (n - 1)) as f64) - 1.0, unsafe { + mem::transmute(Ieee64::fcvt_to_sint_negative_overflow(*n)) + }); + } + } } diff --git a/lib/cretonne/src/isa/intel/enc_tables.rs b/lib/cretonne/src/isa/intel/enc_tables.rs index 00c5ab99cb..d3f62adf7c 100644 --- a/lib/cretonne/src/isa/intel/enc_tables.rs +++ b/lib/cretonne/src/isa/intel/enc_tables.rs @@ -376,13 +376,22 @@ fn expand_fcvt_to_sint( let mut overflow_cc = FloatCC::LessThan; let output_bits = ty.lane_bits(); let flimit = match xty { - ir::types::F32 => pos.ins().f32const(Ieee32::pow2(output_bits - 1).neg()), + // An f32 can represent `i16::min_value() - 1` exactly with precision to spare, so + // there are values less than -2^(N-1) that convert correctly to INT_MIN. + ir::types::F32 => { + pos.ins().f32const(if output_bits < 32 { + overflow_cc = FloatCC::LessThanOrEqual; + Ieee32::fcvt_to_sint_negative_overflow(output_bits) + } else { + Ieee32::pow2(output_bits - 1).neg() + }) + } ir::types::F64 => { // An f64 can represent `i32::min_value() - 1` exactly with precision to spare, so // there are values less than -2^(N-1) that convert correctly to INT_MIN. pos.ins().f64const(if output_bits < 64 { overflow_cc = FloatCC::LessThanOrEqual; - Ieee64::with_float(-((1u64 << (output_bits - 1)) as f64) - 1.0) + Ieee64::fcvt_to_sint_negative_overflow(output_bits) } else { Ieee64::pow2(output_bits - 1).neg() }) @@ -394,8 +403,8 @@ fn expand_fcvt_to_sint( // Finally, we could have a positive value that is too large. let fzero = match xty { - ir::types::F32 => pos.ins().f32const(Ieee32::with_float(0.0)), - ir::types::F64 => pos.ins().f64const(Ieee64::with_float(0.0)), + ir::types::F32 => pos.ins().f32const(Ieee32::with_bits(0)), + ir::types::F64 => pos.ins().f64const(Ieee64::with_bits(0)), _ => panic!("Can't convert {}", xty), }; let overflow = pos.ins().fcmp(FloatCC::GreaterThanOrEqual, x, fzero);