Return a Result from the TargetIsa::encode() method.
When an instruction can't be encoded, provide a viable legalization action in the form of a Legalize enum.
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
//! This module contains types and functions for working with the encoding tables generated by
|
//! This module contains types and functions for working with the encoding tables generated by
|
||||||
//! `lib/cretonne/meta/gen_encoding.py`.
|
//! `lib/cretonne/meta/gen_encoding.py`.
|
||||||
use ir::{Type, Opcode};
|
use ir::{Type, Opcode};
|
||||||
use isa::Encoding;
|
use isa::{Encoding, Legalize};
|
||||||
use constant_hash::{Table, probe};
|
use constant_hash::{Table, probe};
|
||||||
|
|
||||||
/// Level 1 hash table entry.
|
/// Level 1 hash table entry.
|
||||||
@@ -83,16 +83,21 @@ pub fn lookup_enclist<OffT1, OffT2>(ctrl_typevar: Type,
|
|||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
level1_table: &[Level1Entry<OffT1>],
|
level1_table: &[Level1Entry<OffT1>],
|
||||||
level2_table: &[Level2Entry<OffT2>])
|
level2_table: &[Level2Entry<OffT2>])
|
||||||
-> Option<usize>
|
-> Result<usize, Legalize>
|
||||||
where OffT1: Into<u32> + Copy,
|
where OffT1: Into<u32> + Copy,
|
||||||
OffT2: Into<u32> + Copy
|
OffT2: Into<u32> + Copy
|
||||||
{
|
{
|
||||||
probe(level1_table, ctrl_typevar, ctrl_typevar.index()).and_then(|l1idx| {
|
// TODO: The choice of legalization actions here is naive. This needs to be configurable.
|
||||||
let l1ent = &level1_table[l1idx];
|
probe(level1_table, ctrl_typevar, ctrl_typevar.index())
|
||||||
let l2off = l1ent.offset.into() as usize;
|
.ok_or(Legalize::Narrow)
|
||||||
let l2tab = &level2_table[l2off..l2off + (1 << l1ent.log2len)];
|
.and_then(|l1idx| {
|
||||||
probe(l2tab, opcode, opcode as usize).map(|l2idx| l2tab[l2idx].offset.into() as usize)
|
let l1ent = &level1_table[l1idx];
|
||||||
})
|
let l2off = l1ent.offset.into() as usize;
|
||||||
|
let l2tab = &level2_table[l2off..l2off + (1 << l1ent.log2len)];
|
||||||
|
probe(l2tab, opcode, opcode as usize)
|
||||||
|
.map(|l2idx| l2tab[l2idx].offset.into() as usize)
|
||||||
|
.ok_or(Legalize::Expand)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encoding list entry.
|
/// Encoding list entry.
|
||||||
|
|||||||
@@ -87,6 +87,19 @@ 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,
|
||||||
|
}
|
||||||
|
|
||||||
/// Methods that are specialized to a target ISA.
|
/// Methods that are specialized to a target ISA.
|
||||||
pub trait TargetIsa {
|
pub trait TargetIsa {
|
||||||
/// Get the name of this ISA.
|
/// Get the name of this ISA.
|
||||||
@@ -101,7 +114,7 @@ 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) -> Option<Encoding>;
|
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize>;
|
||||||
|
|
||||||
/// Get a static array of names associated with encoding recipes in this ISA. Encoding recipes
|
/// Get a static array of names associated with encoding recipes in this ISA. Encoding recipes
|
||||||
/// are numbered starting from 0, corresponding to indexes into th name array.
|
/// are numbered starting from 0, corresponding to indexes into th name array.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ mod enc_tables;
|
|||||||
use super::super::settings as shared_settings;
|
use super::super::settings as shared_settings;
|
||||||
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, Encoding};
|
use isa::{TargetIsa, Encoding, Legalize};
|
||||||
use ir::{InstructionData, DataFlowGraph};
|
use ir::{InstructionData, DataFlowGraph};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -48,7 +48,7 @@ impl TargetIsa for Isa {
|
|||||||
&self.shared_flags
|
&self.shared_flags
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Option<Encoding> {
|
fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize> {
|
||||||
lookup_enclist(inst.first_type(),
|
lookup_enclist(inst.first_type(),
|
||||||
inst.opcode(),
|
inst.opcode(),
|
||||||
self.cpumode,
|
self.cpumode,
|
||||||
@@ -58,6 +58,7 @@ impl TargetIsa for Isa {
|
|||||||
&enc_tables::ENCLISTS[..],
|
&enc_tables::ENCLISTS[..],
|
||||||
|instp| enc_tables::check_instp(inst, instp),
|
|instp| enc_tables::check_instp(inst, instp),
|
||||||
|isap| self.isa_flags.numbered_predicate(isap as usize))
|
|isap| self.isa_flags.numbered_predicate(isap as usize))
|
||||||
|
.ok_or(Legalize::Expand)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Immediate is out of range for ADDI.
|
// Immediate is out of range for ADDI.
|
||||||
assert_eq!(isa.encode(&dfg, &inst64_large), None);
|
assert_eq!(isa.encode(&dfg, &inst64_large), 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 {
|
||||||
@@ -144,8 +145,8 @@ mod tests {
|
|||||||
imm: immediates::Imm64::new(-10),
|
imm: immediates::Imm64::new(-10),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ADDI is I/0b00100
|
// In 32-bit mode, an i64 bit add should be narrowed.
|
||||||
assert_eq!(isa.encode(&dfg, &inst64), None);
|
assert_eq!(isa.encode(&dfg, &inst64), 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 {
|
||||||
@@ -155,8 +156,8 @@ mod tests {
|
|||||||
imm: immediates::Imm64::new(-10000),
|
imm: immediates::Imm64::new(-10000),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Immediate is out of range for ADDI.
|
// In 32-bit mode, an i64 bit add should be narrowed.
|
||||||
assert_eq!(isa.encode(&dfg, &inst64_large), None);
|
assert_eq!(isa.encode(&dfg, &inst64_large), 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 {
|
||||||
@@ -176,7 +177,7 @@ mod tests {
|
|||||||
args: [arg32, arg32],
|
args: [arg32, arg32],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(isa.encode(&dfg, &mul32), None);
|
assert_eq!(isa.encode(&dfg, &mul32), Err(isa::Legalize::Expand));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, Inst, InstBuilder};
|
use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, Inst, InstBuilder};
|
||||||
use ir::condcodes::IntCC;
|
use ir::condcodes::IntCC;
|
||||||
use isa::TargetIsa;
|
use isa::{TargetIsa, Legalize};
|
||||||
|
|
||||||
/// Legalize `func` for `isa`.
|
/// Legalize `func` for `isa`.
|
||||||
///
|
///
|
||||||
@@ -25,30 +25,35 @@ use isa::TargetIsa;
|
|||||||
pub fn legalize_function(func: &mut Function, isa: &TargetIsa) {
|
pub fn legalize_function(func: &mut Function, isa: &TargetIsa) {
|
||||||
// TODO: This is very simplified and incomplete.
|
// TODO: This is very simplified and incomplete.
|
||||||
func.encodings.resize(func.dfg.num_insts());
|
func.encodings.resize(func.dfg.num_insts());
|
||||||
for ebb in func.layout.ebbs() {
|
let mut pos = Cursor::new(&mut func.layout);
|
||||||
for inst in func.layout.ebb_insts(ebb) {
|
while let Some(_ebb) = pos.next_ebb() {
|
||||||
|
while let Some(inst) = pos.next_inst() {
|
||||||
match isa.encode(&func.dfg, &func.dfg[inst]) {
|
match isa.encode(&func.dfg, &func.dfg[inst]) {
|
||||||
Some(encoding) => func.encodings[inst] = encoding,
|
Ok(encoding) => func.encodings[inst] = encoding,
|
||||||
None => {
|
Err(Legalize::Expand) => {
|
||||||
// TODO: We should transform the instruction into legal equivalents.
|
expand(&mut pos, &mut func.dfg);
|
||||||
// Possible strategies are:
|
|
||||||
// 1. Expand instruction into sequence of legal instructions. Possibly
|
|
||||||
// iteratively.
|
|
||||||
// 2. Split the controlling type variable into high and low parts. This applies
|
|
||||||
// both to SIMD vector types which can be halved and to integer types such
|
|
||||||
// as `i64` used on a 32-bit ISA.
|
|
||||||
// 3. Promote the controlling type variable to a larger type. This typically
|
|
||||||
// means expressing `i8` and `i16` arithmetic in terms if `i32` operations
|
|
||||||
// on RISC targets. (It may or may not be beneficial to promote small vector
|
|
||||||
// types versus splitting them.)
|
|
||||||
// 4. Convert to library calls. For example, floating point operations on an
|
|
||||||
// ISA with no IEEE 754 support.
|
|
||||||
//
|
|
||||||
// The iteration scheme used here is not going to cut it. Transforming
|
|
||||||
// instructions involves changing `function.layout` which is impossiblr while
|
|
||||||
// it is referenced by the two iterators. We need a layout cursor that can
|
|
||||||
// maintain a position *and* permit inserting and replacing instructions.
|
|
||||||
}
|
}
|
||||||
|
Err(Legalize::Narrow) => {
|
||||||
|
narrow(&mut pos, &mut func.dfg);
|
||||||
|
}
|
||||||
|
// TODO: We should transform the instruction into legal equivalents.
|
||||||
|
// Possible strategies are:
|
||||||
|
// 1. Expand instruction into sequence of legal instructions. Possibly
|
||||||
|
// iteratively.
|
||||||
|
// 2. Split the controlling type variable into high and low parts. This applies
|
||||||
|
// both to SIMD vector types which can be halved and to integer types such
|
||||||
|
// as `i64` used on a 32-bit ISA.
|
||||||
|
// 3. Promote the controlling type variable to a larger type. This typically
|
||||||
|
// means expressing `i8` and `i16` arithmetic in terms if `i32` operations
|
||||||
|
// on RISC targets. (It may or may not be beneficial to promote small vector
|
||||||
|
// types versus splitting them.)
|
||||||
|
// 4. Convert to library calls. For example, floating point operations on an
|
||||||
|
// ISA with no IEEE 754 support.
|
||||||
|
//
|
||||||
|
// The iteration scheme used here is not going to cut it. Transforming
|
||||||
|
// instructions involves changing `function.layout` which is impossiblr while
|
||||||
|
// it is referenced by the two iterators. We need a layout cursor that can
|
||||||
|
// maintain a position *and* permit inserting and replacing instructions.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user