Move the ctrl_typevar function into dfg.
Soon, InstructionData won't have sufficient information to compute this. Give TargetIsa::encode() an explicit ctrl_typevar argument. This function does not require the instruction to be inserted in the DFG tables.
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
//! Data flow graph tracking Instructions, Values, and EBBs.
|
//! 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 entity_map::{EntityMap, PrimaryEntityData};
|
||||||
use ir::builder::{InsertBuilder, ReplaceBuilder};
|
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::layout::Cursor;
|
||||||
|
use ir::types;
|
||||||
|
use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef, ValueList, ValueListPool};
|
||||||
use write::write_operands;
|
use write::write_operands;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -541,6 +542,22 @@ impl DataFlowGraph {
|
|||||||
.map(|&arg| arg.value_type)
|
.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.
|
/// 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() {
|
if typevar.is_void() {
|
||||||
write!(f, "{}", inst.opcode())?;
|
write!(f, "{}", inst.opcode())?;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32};
|
||||||
use ir::condcodes::*;
|
use ir::condcodes::*;
|
||||||
use ir::types;
|
use ir::types;
|
||||||
use ir::DataFlowGraph;
|
|
||||||
|
|
||||||
use entity_list;
|
use entity_list;
|
||||||
use ref_slice::{ref_slice, ref_slice_mut};
|
use ref_slice::{ref_slice, ref_slice_mut};
|
||||||
@@ -397,27 +396,6 @@ impl InstructionData {
|
|||||||
_ => CallInfo::NotACall,
|
_ => 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.
|
/// Information about branch and jump instructions.
|
||||||
|
|||||||
@@ -60,10 +60,11 @@ impl TargetIsa for Isa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self,
|
fn encode(&self,
|
||||||
dfg: &ir::DataFlowGraph,
|
_dfg: &ir::DataFlowGraph,
|
||||||
inst: &ir::InstructionData)
|
inst: &ir::InstructionData,
|
||||||
|
ctrl_typevar: ir::Type)
|
||||||
-> Result<Encoding, Legalize> {
|
-> Result<Encoding, Legalize> {
|
||||||
lookup_enclist(inst.ctrl_typevar(dfg),
|
lookup_enclist(ctrl_typevar,
|
||||||
inst.opcode(),
|
inst.opcode(),
|
||||||
self.cpumode,
|
self.cpumode,
|
||||||
&enc_tables::LEVEL2[..])
|
&enc_tables::LEVEL2[..])
|
||||||
|
|||||||
@@ -53,10 +53,11 @@ impl TargetIsa for Isa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self,
|
fn encode(&self,
|
||||||
dfg: &ir::DataFlowGraph,
|
_dfg: &ir::DataFlowGraph,
|
||||||
inst: &ir::InstructionData)
|
inst: &ir::InstructionData,
|
||||||
|
ctrl_typevar: ir::Type)
|
||||||
-> Result<Encoding, Legalize> {
|
-> Result<Encoding, Legalize> {
|
||||||
lookup_enclist(inst.ctrl_typevar(dfg),
|
lookup_enclist(ctrl_typevar,
|
||||||
inst.opcode(),
|
inst.opcode(),
|
||||||
&enc_tables::LEVEL1_A64[..],
|
&enc_tables::LEVEL1_A64[..],
|
||||||
&enc_tables::LEVEL2[..])
|
&enc_tables::LEVEL2[..])
|
||||||
|
|||||||
@@ -60,10 +60,11 @@ impl TargetIsa for Isa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self,
|
fn encode(&self,
|
||||||
dfg: &ir::DataFlowGraph,
|
_dfg: &ir::DataFlowGraph,
|
||||||
inst: &ir::InstructionData)
|
inst: &ir::InstructionData,
|
||||||
|
ctrl_typevar: ir::Type)
|
||||||
-> Result<Encoding, Legalize> {
|
-> Result<Encoding, Legalize> {
|
||||||
lookup_enclist(inst.ctrl_typevar(dfg),
|
lookup_enclist(ctrl_typevar,
|
||||||
inst.opcode(),
|
inst.opcode(),
|
||||||
self.cpumode,
|
self.cpumode,
|
||||||
&enc_tables::LEVEL2[..])
|
&enc_tables::LEVEL2[..])
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex};
|
|||||||
|
|
||||||
use binemit::CodeSink;
|
use binemit::CodeSink;
|
||||||
use settings;
|
use settings;
|
||||||
use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature};
|
use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature, Type};
|
||||||
|
|
||||||
pub mod riscv;
|
pub mod riscv;
|
||||||
pub mod intel;
|
pub mod intel;
|
||||||
@@ -141,7 +141,11 @@ pub trait TargetIsa {
|
|||||||
/// Otherwise, return `None`.
|
/// Otherwise, return `None`.
|
||||||
///
|
///
|
||||||
/// This is also the main entry point for determining if an instruction is legal.
|
/// This is also the main entry point for determining if an instruction is legal.
|
||||||
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize>;
|
fn encode(&self,
|
||||||
|
dfg: &DataFlowGraph,
|
||||||
|
inst: &InstructionData,
|
||||||
|
ctrl_typevar: Type)
|
||||||
|
-> Result<Encoding, Legalize>;
|
||||||
|
|
||||||
/// Get a data structure describing the instruction encodings in this ISA.
|
/// Get a data structure describing the instruction encodings in this ISA.
|
||||||
fn encoding_info(&self) -> EncInfo;
|
fn encoding_info(&self) -> EncInfo;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use binemit::CodeSink;
|
|||||||
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
||||||
use isa::Builder as IsaBuilder;
|
use isa::Builder as IsaBuilder;
|
||||||
use isa::{TargetIsa, RegInfo, EncInfo, Encoding, Legalize};
|
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)]
|
#[allow(dead_code)]
|
||||||
struct Isa {
|
struct Isa {
|
||||||
@@ -60,8 +60,12 @@ impl TargetIsa for Isa {
|
|||||||
enc_tables::INFO.clone()
|
enc_tables::INFO.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize> {
|
fn encode(&self,
|
||||||
lookup_enclist(inst.ctrl_typevar(dfg),
|
_dfg: &DataFlowGraph,
|
||||||
|
inst: &InstructionData,
|
||||||
|
ctrl_typevar: Type)
|
||||||
|
-> Result<Encoding, Legalize> {
|
||||||
|
lookup_enclist(ctrl_typevar,
|
||||||
inst.opcode(),
|
inst.opcode(),
|
||||||
self.cpumode,
|
self.cpumode,
|
||||||
&enc_tables::LEVEL2[..])
|
&enc_tables::LEVEL2[..])
|
||||||
@@ -120,7 +124,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ADDI is I/0b00100
|
// 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.
|
// Try to encode iadd_imm.i64 vx1, -10000.
|
||||||
let inst64_large = InstructionData::BinaryImm {
|
let inst64_large = InstructionData::BinaryImm {
|
||||||
@@ -131,7 +136,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Immediate is out of range for ADDI.
|
// 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.
|
// Create an iadd_imm.i32 which is encodable in RV64.
|
||||||
let inst32 = InstructionData::BinaryImm {
|
let inst32 = InstructionData::BinaryImm {
|
||||||
@@ -142,7 +148,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ADDIW is I/0b00110
|
// 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.
|
// Same as above, but for RV32.
|
||||||
@@ -167,7 +174,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// In 32-bit mode, an i64 bit add should be narrowed.
|
// 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.
|
// Try to encode iadd_imm.i64 vx1, -10000.
|
||||||
let inst64_large = InstructionData::BinaryImm {
|
let inst64_large = InstructionData::BinaryImm {
|
||||||
@@ -178,7 +186,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// In 32-bit mode, an i64 bit add should be narrowed.
|
// 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.
|
// Create an iadd_imm.i32 which is encodable in RV32.
|
||||||
let inst32 = InstructionData::BinaryImm {
|
let inst32 = InstructionData::BinaryImm {
|
||||||
@@ -189,7 +198,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ADDI is I/0b00100
|
// 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.
|
// Create an imul.i32 which is encodable in RV32, but only when use_m is true.
|
||||||
let mul32 = InstructionData::Binary {
|
let mul32 = InstructionData::Binary {
|
||||||
@@ -198,7 +208,8 @@ mod tests {
|
|||||||
args: [arg32, arg32],
|
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]
|
#[test]
|
||||||
@@ -224,6 +235,7 @@ mod tests {
|
|||||||
ty: types::I32,
|
ty: types::I32,
|
||||||
args: [arg32, arg32],
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ pub fn legalize_function(func: &mut Function, cfg: &mut ControlFlowGraph, isa: &
|
|||||||
split::simplify_branch_arguments(&mut func.dfg, inst);
|
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,
|
Ok(encoding) => *func.encodings.ensure(inst) = encoding,
|
||||||
Err(action) => {
|
Err(action) => {
|
||||||
// We should transform the instruction into legal equivalents.
|
// We should transform the instruction into legal equivalents.
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ impl<'a> Verifier<'a> {
|
|||||||
|
|
||||||
let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
|
let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
|
||||||
// For polymorphic opcodes, determine the controlling type variable first.
|
// 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) {
|
if !value_typeset.contains(ctrl_type) {
|
||||||
return err!(inst, "has an invalid controlling type {}", ctrl_type);
|
return err!(inst, "has an invalid controlling type {}", ctrl_type);
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rtype = inst_data.ctrl_typevar(&func.dfg);
|
let rtype = func.dfg.ctrl_typevar(inst);
|
||||||
assert!(!rtype.is_void(),
|
assert!(!rtype.is_void(),
|
||||||
"Polymorphic instruction must produce a result");
|
"Polymorphic instruction must produce a result");
|
||||||
Some(rtype)
|
Some(rtype)
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ impl SubTest for TestBinEmit {
|
|||||||
.get(inst)
|
.get(inst)
|
||||||
.map(|e| e.is_legal())
|
.map(|e| e.is_legal())
|
||||||
.unwrap_or(false) {
|
.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;
|
*func.encodings.ensure(inst) = enc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user