Return a function pointer from TargetIsa::encode().

Replace the isa::Legalize enumeration with a function pointer. This
allows an ISA to define its own specific legalization actions instead of
relying on the default two.

Generate a LEGALIZE_ACTIONS table for each ISA which contains
legalization function pointers indexed by the legalization codes that
are already in the encoding tables. Include this table in
isa/*/enc_tables.rs.

Give the `Encodings` iterator a reference to the action table and change
its `legalize()` method to return a function pointer instead of an
ISA-specific code.

The Result<> returned from TargetIsa::encode() no longer implements
Debug, so eliminate uses of unwrap and expect on that type.
This commit is contained in:
Jakob Stoklund Olesen
2017-07-27 14:46:56 -07:00
parent d1353bba05
commit 2aca35a9aa
19 changed files with 140 additions and 102 deletions

View File

@@ -1,9 +1,10 @@
//! Encoding tables for ARM32 ISA.
use ir::types;
use isa::EncInfo;
use isa;
use isa::constraints::*;
use isa::enc_tables::*;
use isa::encoding::RecipeSizing;
include!(concat!(env!("OUT_DIR"), "/encoding-arm32.rs"));
include!(concat!(env!("OUT_DIR"), "/legalize-arm32.rs"));

View File

@@ -72,6 +72,7 @@ impl TargetIsa for Isa {
self.cpumode,
&enc_tables::LEVEL2[..],
&enc_tables::ENCLISTS[..],
&enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view())

View File

@@ -1,9 +1,10 @@
//! Encoding tables for ARM64 ISA.
use ir::types;
use isa::EncInfo;
use isa;
use isa::constraints::*;
use isa::enc_tables::*;
use isa::encoding::RecipeSizing;
include!(concat!(env!("OUT_DIR"), "/encoding-arm64.rs"));
include!(concat!(env!("OUT_DIR"), "/legalize-arm64.rs"));

View File

@@ -65,6 +65,7 @@ impl TargetIsa for Isa {
&enc_tables::LEVEL1_A64[..],
&enc_tables::LEVEL2[..],
&enc_tables::ENCLISTS[..],
&enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view())

View File

@@ -5,7 +5,7 @@
use constant_hash::{Table, probe};
use ir::{Type, Opcode, DataFlowGraph, InstructionData};
use isa::Encoding;
use isa::{Encoding, Legalize};
use settings::PredicateView;
use std::ops::Range;
@@ -109,6 +109,7 @@ pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
level1_table: &'static [Level1Entry<OffT1>],
level2_table: &'static [Level2Entry<OffT2>],
enclist: &'static [EncListEntry],
legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>)
@@ -148,6 +149,7 @@ pub fn lookup_enclist<'a, OffT1, OffT2>(ctrl_typevar: Type,
inst,
dfg,
enclist,
legalize_actions,
recipe_preds,
inst_preds,
isa_preds)
@@ -173,6 +175,7 @@ pub struct Encodings<'a> {
inst: &'a InstructionData,
dfg: &'a DataFlowGraph,
enclist: &'static [EncListEntry],
legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>,
@@ -189,6 +192,7 @@ impl<'a> Encodings<'a> {
inst: &'a InstructionData,
dfg: &'a DataFlowGraph,
enclist: &'static [EncListEntry],
legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>)
@@ -202,6 +206,7 @@ impl<'a> Encodings<'a> {
recipe_preds,
inst_preds,
enclist,
legalize_actions,
}
}
@@ -210,9 +215,9 @@ impl<'a> Encodings<'a> {
/// instruction.
///
/// This method must only be called after the iterator returns `None`.
pub fn legalize(&self) -> LegalizeCode {
pub fn legalize(&self) -> Legalize {
debug_assert_eq!(self.offset, !0, "Premature Encodings::legalize()");
self.legalize
self.legalize_actions[self.legalize as usize]
}
/// Check if the `rpred` recipe predicate s satisfied.

View File

@@ -1,7 +1,7 @@
//! Encoding tables for Intel ISAs.
use ir::{self, types, Opcode};
use isa::EncInfo;
use isa;
use isa::constraints::*;
use isa::enc_tables::*;
use isa::encoding::RecipeSizing;
@@ -9,3 +9,4 @@ use predicates;
use super::registers::*;
include!(concat!(env!("OUT_DIR"), "/encoding-intel.rs"));
include!(concat!(env!("OUT_DIR"), "/legalize-intel.rs"));

View File

@@ -72,6 +72,7 @@ impl TargetIsa for Isa {
self.cpumode,
&enc_tables::LEVEL2[..],
&enc_tables::ENCLISTS[..],
&enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view())

View File

@@ -45,6 +45,7 @@ pub use isa::encoding::{Encoding, EncInfo};
pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex, regs_overlap};
use binemit;
use flowgraph;
use settings;
use ir;
use regalloc;
@@ -116,29 +117,11 @@ impl settings::Configurable for Builder {
/// After determining that an instruction doesn't have an encoding, how should we proceed to
/// legalize it?
///
/// These actions correspond to the transformation groups defined in `meta/cretonne/legalize.py`.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Legalize {
/// Legalize in terms of narrower types.
Narrow,
/// Expanding in terms of other instructions using the same types.
Expand,
}
/// Translate a legalization code into a `Legalize` enum.
///
/// This mapping is going away soon. It depends on matching the `TargetISA.legalize_code()`
/// mapping.
impl From<u8> for Legalize {
fn from(x: u8) -> Legalize {
match x {
0 => Legalize::Narrow,
1 => Legalize::Expand,
_ => panic!("Unknown legalization code {}"),
}
}
}
/// The `Encodings` iterator returns a legalization function to call.
pub type Legalize = fn(&mut ir::DataFlowGraph,
&mut flowgraph::ControlFlowGraph,
&mut ir::Cursor)
-> bool;
/// Methods that are specialized to a target ISA.
pub trait TargetIsa {

View File

@@ -2,7 +2,7 @@
use ir::condcodes::IntCC;
use ir::{self, types, Opcode};
use isa::EncInfo;
use isa;
use isa::constraints::*;
use isa::enc_tables::*;
use isa::encoding::RecipeSizing;
@@ -16,3 +16,4 @@ use super::registers::*;
// - `ENCLIST`
// - `INFO`
include!(concat!(env!("OUT_DIR"), "/encoding-riscv.rs"));
include!(concat!(env!("OUT_DIR"), "/legalize-riscv.rs"));

View File

@@ -72,6 +72,7 @@ impl TargetIsa for Isa {
self.cpumode,
&enc_tables::LEVEL2[..],
&enc_tables::ENCLISTS[..],
&enc_tables::LEGALIZE_ACTIONS[..],
&enc_tables::RECIPE_PREDICATES[..],
&enc_tables::INST_PREDICATES[..],
self.isa_flags.predicate_view())
@@ -113,8 +114,11 @@ mod tests {
use ir::{DataFlowGraph, InstructionData, Opcode};
use ir::{types, immediates};
fn encstr(isa: &isa::TargetIsa, enc: isa::Encoding) -> String {
isa.encoding_info().display(enc).to_string()
fn encstr(isa: &isa::TargetIsa, enc: Result<isa::Encoding, isa::Legalize>) -> String {
match enc {
Ok(e) => isa.encoding_info().display(e).to_string(),
Err(_) => "no encoding".to_string(),
}
}
#[test]
@@ -137,8 +141,7 @@ mod tests {
};
// ADDI is I/0b00100
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst64, types::I64).unwrap()),
"I#04");
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst64, types::I64)), "I#04");
// Try to encode iadd_imm.i64 v1, -10000.
let inst64_large = InstructionData::BinaryImm {
@@ -148,8 +151,7 @@ mod tests {
};
// Immediate is out of range for ADDI.
assert_eq!(isa.encode(&dfg, &inst64_large, types::I64),
Err(isa::Legalize::Expand));
assert!(isa.encode(&dfg, &inst64_large, types::I64).is_err());
// Create an iadd_imm.i32 which is encodable in RV64.
let inst32 = InstructionData::BinaryImm {
@@ -159,8 +161,7 @@ mod tests {
};
// ADDIW is I/0b00110
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32).unwrap()),
"I#06");
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32)), "I#06");
}
// Same as above, but for RV32.
@@ -184,8 +185,7 @@ mod tests {
};
// In 32-bit mode, an i64 bit add should be narrowed.
assert_eq!(isa.encode(&dfg, &inst64, types::I64),
Err(isa::Legalize::Narrow));
assert!(isa.encode(&dfg, &inst64, types::I64).is_err());
// Try to encode iadd_imm.i64 v1, -10000.
let inst64_large = InstructionData::BinaryImm {
@@ -195,8 +195,7 @@ mod tests {
};
// In 32-bit mode, an i64 bit add should be narrowed.
assert_eq!(isa.encode(&dfg, &inst64_large, types::I64),
Err(isa::Legalize::Narrow));
assert!(isa.encode(&dfg, &inst64_large, types::I64).is_err());
// Create an iadd_imm.i32 which is encodable in RV32.
let inst32 = InstructionData::BinaryImm {
@@ -206,8 +205,7 @@ mod tests {
};
// ADDI is I/0b00100
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32).unwrap()),
"I#04");
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32)), "I#04");
// Create an imul.i32 which is encodable in RV32, but only when use_m is true.
let mul32 = InstructionData::Binary {
@@ -215,8 +213,7 @@ mod tests {
args: [arg32, arg32],
};
assert_eq!(isa.encode(&dfg, &mul32, types::I32),
Err(isa::Legalize::Expand));
assert!(isa.encode(&dfg, &mul32, types::I32).is_err());
}
#[test]
@@ -241,7 +238,6 @@ mod tests {
opcode: Opcode::Imul,
args: [arg32, arg32],
};
assert_eq!(encstr(&*isa, isa.encode(&dfg, &mul32, types::I32).unwrap()),
"R#10c");
assert_eq!(encstr(&*isa, isa.encode(&dfg, &mul32, types::I32)), "R#10c");
}
}