diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index e00c1fe951..1aebfbb03e 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -1,12 +1,13 @@ //! Data flow graph tracking Instructions, Values, and EBBs. -use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef, ValueList, ValueListPool}; -use ir::entities::ExpandedValue; -use ir::instructions::{Opcode, InstructionData, CallInfo}; -use ir::extfunc::ExtFuncData; use entity_map::{EntityMap, PrimaryEntityData}; use ir::builder::{InsertBuilder, ReplaceBuilder}; +use ir::entities::ExpandedValue; +use ir::extfunc::ExtFuncData; +use ir::instructions::{Opcode, InstructionData, CallInfo}; use ir::layout::Cursor; +use ir::types; +use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef, ValueList, ValueListPool}; use write::write_operands; use std::fmt; @@ -541,6 +542,22 @@ impl DataFlowGraph { .map(|&arg| arg.value_type) }) } + + /// Get the controlling type variable, or `VOID` if `inst` isn't polymorphic. + pub fn ctrl_typevar(&self, inst: Inst) -> Type { + let constraints = self[inst].opcode().constraints(); + + if !constraints.is_polymorphic() { + types::VOID + } else if constraints.requires_typevar_operand() { + // Not all instruction formats have a designated operand, but in that case + // `requires_typevar_operand()` should never be true. + self.value_type(self[inst].typevar_operand(&self.value_lists) + .expect("Instruction format doesn't have a designated operand, bad opcode.")) + } else { + self.value_type(self.first_result(inst)) + } + } } /// Allow immutable access to instructions via indexing. @@ -688,7 +705,7 @@ impl<'a> fmt::Display for DisplayInst<'a> { } - let typevar = inst.ctrl_typevar(dfg); + let typevar = dfg.ctrl_typevar(self.1); if typevar.is_void() { write!(f, "{}", inst.opcode())?; } else { diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 29e3e6662f..1e1262a507 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -14,7 +14,6 @@ use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot, MemFlags}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32}; use ir::condcodes::*; use ir::types; -use ir::DataFlowGraph; use entity_list; use ref_slice::{ref_slice, ref_slice_mut}; @@ -397,27 +396,6 @@ impl InstructionData { _ => CallInfo::NotACall, } } - - /// Get the controlling type variable, or `VOID` if this instruction isn't polymorphic. - /// - /// In most cases, the controlling type variable is the same as the first result type, but some - /// opcodes require us to read the type of the designated type variable operand from `dfg`. - pub fn ctrl_typevar(&self, dfg: &DataFlowGraph) -> Type { - let constraints = self.opcode().constraints(); - - if !constraints.is_polymorphic() { - types::VOID - } else if constraints.requires_typevar_operand() { - // Not all instruction formats have a designated operand, but in that case - // `requires_typevar_operand()` should never be true. - dfg.value_type(self.typevar_operand(&dfg.value_lists) - .expect("Instruction format doesn't have a designated operand, bad opcode.")) - } else { - // For locality of reference, we prefer to get the controlling type variable from - // `idata` itself, when possible. - self.first_type() - } - } } /// Information about branch and jump instructions. diff --git a/lib/cretonne/src/isa/arm32/mod.rs b/lib/cretonne/src/isa/arm32/mod.rs index c9c7f2af5c..b76ffa32cb 100644 --- a/lib/cretonne/src/isa/arm32/mod.rs +++ b/lib/cretonne/src/isa/arm32/mod.rs @@ -60,10 +60,11 @@ impl TargetIsa for Isa { } fn encode(&self, - dfg: &ir::DataFlowGraph, - inst: &ir::InstructionData) + _dfg: &ir::DataFlowGraph, + inst: &ir::InstructionData, + ctrl_typevar: ir::Type) -> Result { - lookup_enclist(inst.ctrl_typevar(dfg), + lookup_enclist(ctrl_typevar, inst.opcode(), self.cpumode, &enc_tables::LEVEL2[..]) diff --git a/lib/cretonne/src/isa/arm64/mod.rs b/lib/cretonne/src/isa/arm64/mod.rs index a8cfe834b9..2499028919 100644 --- a/lib/cretonne/src/isa/arm64/mod.rs +++ b/lib/cretonne/src/isa/arm64/mod.rs @@ -53,10 +53,11 @@ impl TargetIsa for Isa { } fn encode(&self, - dfg: &ir::DataFlowGraph, - inst: &ir::InstructionData) + _dfg: &ir::DataFlowGraph, + inst: &ir::InstructionData, + ctrl_typevar: ir::Type) -> Result { - lookup_enclist(inst.ctrl_typevar(dfg), + lookup_enclist(ctrl_typevar, inst.opcode(), &enc_tables::LEVEL1_A64[..], &enc_tables::LEVEL2[..]) diff --git a/lib/cretonne/src/isa/intel/mod.rs b/lib/cretonne/src/isa/intel/mod.rs index b48a119632..60ce3a795d 100644 --- a/lib/cretonne/src/isa/intel/mod.rs +++ b/lib/cretonne/src/isa/intel/mod.rs @@ -60,10 +60,11 @@ impl TargetIsa for Isa { } fn encode(&self, - dfg: &ir::DataFlowGraph, - inst: &ir::InstructionData) + _dfg: &ir::DataFlowGraph, + inst: &ir::InstructionData, + ctrl_typevar: ir::Type) -> Result { - lookup_enclist(inst.ctrl_typevar(dfg), + lookup_enclist(ctrl_typevar, inst.opcode(), self.cpumode, &enc_tables::LEVEL2[..]) diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index 469142ab56..c40a839836 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -46,7 +46,7 @@ pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex}; use binemit::CodeSink; use settings; -use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature}; +use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature, Type}; pub mod riscv; pub mod intel; @@ -141,7 +141,11 @@ pub trait TargetIsa { /// Otherwise, return `None`. /// /// This is also the main entry point for determining if an instruction is legal. - fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result; + fn encode(&self, + dfg: &DataFlowGraph, + inst: &InstructionData, + ctrl_typevar: Type) + -> Result; /// Get a data structure describing the instruction encodings in this ISA. fn encoding_info(&self) -> EncInfo; diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index 7658db392e..b1f399665b 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -11,7 +11,7 @@ use binemit::CodeSink; use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding}; use isa::Builder as IsaBuilder; use isa::{TargetIsa, RegInfo, EncInfo, Encoding, Legalize}; -use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature}; +use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature, Type}; #[allow(dead_code)] struct Isa { @@ -60,8 +60,12 @@ impl TargetIsa for Isa { enc_tables::INFO.clone() } - fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result { - lookup_enclist(inst.ctrl_typevar(dfg), + fn encode(&self, + _dfg: &DataFlowGraph, + inst: &InstructionData, + ctrl_typevar: Type) + -> Result { + lookup_enclist(ctrl_typevar, inst.opcode(), self.cpumode, &enc_tables::LEVEL2[..]) @@ -120,7 +124,8 @@ mod tests { }; // ADDI is I/0b00100 - assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst64).unwrap()), "I#04"); + assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst64, types::I64).unwrap()), + "I#04"); // Try to encode iadd_imm.i64 vx1, -10000. let inst64_large = InstructionData::BinaryImm { @@ -131,7 +136,8 @@ mod tests { }; // Immediate is out of range for ADDI. - assert_eq!(isa.encode(&dfg, &inst64_large), Err(isa::Legalize::Expand)); + assert_eq!(isa.encode(&dfg, &inst64_large, types::I64), + Err(isa::Legalize::Expand)); // Create an iadd_imm.i32 which is encodable in RV64. let inst32 = InstructionData::BinaryImm { @@ -142,7 +148,8 @@ mod tests { }; // ADDIW is I/0b00110 - assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32).unwrap()), "I#06"); + assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32).unwrap()), + "I#06"); } // Same as above, but for RV32. @@ -167,7 +174,8 @@ mod tests { }; // In 32-bit mode, an i64 bit add should be narrowed. - assert_eq!(isa.encode(&dfg, &inst64), Err(isa::Legalize::Narrow)); + assert_eq!(isa.encode(&dfg, &inst64, types::I64), + Err(isa::Legalize::Narrow)); // Try to encode iadd_imm.i64 vx1, -10000. let inst64_large = InstructionData::BinaryImm { @@ -178,7 +186,8 @@ mod tests { }; // In 32-bit mode, an i64 bit add should be narrowed. - assert_eq!(isa.encode(&dfg, &inst64_large), Err(isa::Legalize::Narrow)); + assert_eq!(isa.encode(&dfg, &inst64_large, types::I64), + Err(isa::Legalize::Narrow)); // Create an iadd_imm.i32 which is encodable in RV32. let inst32 = InstructionData::BinaryImm { @@ -189,7 +198,8 @@ mod tests { }; // ADDI is I/0b00100 - assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32).unwrap()), "I#04"); + assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32).unwrap()), + "I#04"); // Create an imul.i32 which is encodable in RV32, but only when use_m is true. let mul32 = InstructionData::Binary { @@ -198,7 +208,8 @@ mod tests { args: [arg32, arg32], }; - assert_eq!(isa.encode(&dfg, &mul32), Err(isa::Legalize::Expand)); + assert_eq!(isa.encode(&dfg, &mul32, types::I32), + Err(isa::Legalize::Expand)); } #[test] @@ -224,6 +235,7 @@ mod tests { ty: types::I32, args: [arg32, arg32], }; - assert_eq!(encstr(&*isa, isa.encode(&dfg, &mul32).unwrap()), "R#10c"); + assert_eq!(encstr(&*isa, isa.encode(&dfg, &mul32, types::I32).unwrap()), + "R#10c"); } } diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 00354822a1..7949aed311 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -64,7 +64,7 @@ pub fn legalize_function(func: &mut Function, cfg: &mut ControlFlowGraph, isa: & split::simplify_branch_arguments(&mut func.dfg, inst); } - match isa.encode(&func.dfg, &func.dfg[inst]) { + match isa.encode(&func.dfg, &func.dfg[inst], func.dfg.ctrl_typevar(inst)) { Ok(encoding) => *func.encodings.ensure(inst) = encoding, Err(action) => { // We should transform the instruction into legal equivalents. diff --git a/lib/cretonne/src/verifier.rs b/lib/cretonne/src/verifier.rs index d6018a8f5c..0b84ad82bd 100644 --- a/lib/cretonne/src/verifier.rs +++ b/lib/cretonne/src/verifier.rs @@ -425,7 +425,7 @@ impl<'a> Verifier<'a> { let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() { // For polymorphic opcodes, determine the controlling type variable first. - let ctrl_type = inst_data.ctrl_typevar(&self.func.dfg); + let ctrl_type = self.func.dfg.ctrl_typevar(inst); if !value_typeset.contains(ctrl_type) { return err!(inst, "has an invalid controlling type {}", ctrl_type); diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index ae4a38284f..023eb4ee7a 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -152,7 +152,7 @@ fn type_suffix(func: &Function, inst: Inst) -> Option { } } - let rtype = inst_data.ctrl_typevar(&func.dfg); + let rtype = func.dfg.ctrl_typevar(inst); assert!(!rtype.is_void(), "Polymorphic instruction must produce a result"); Some(rtype) diff --git a/src/filetest/binemit.rs b/src/filetest/binemit.rs index f4ec6262c6..449e0887e6 100644 --- a/src/filetest/binemit.rs +++ b/src/filetest/binemit.rs @@ -110,7 +110,9 @@ impl SubTest for TestBinEmit { .get(inst) .map(|e| e.is_legal()) .unwrap_or(false) { - if let Ok(enc) = isa.encode(&func.dfg, &func.dfg[inst]) { + if let Ok(enc) = isa.encode(&func.dfg, + &func.dfg[inst], + func.dfg.ctrl_typevar(inst)) { *func.encodings.ensure(inst) = enc; } }