Add encoding size information to EncInfo.
Two new pieces of information are available for all encoding recipes: - The size in bytes of an encoded instruction, and - The range of a branch encoded with the recipe, if any. In the meta language, EncRecipe takes two new constructor arguments. The size is required for all encodings and branch_range is required for all recipes used to encode branches.
This commit is contained in:
@@ -19,6 +19,7 @@ try:
|
||||
# Instruction specification for encodings. Allows for predicated
|
||||
# instructions.
|
||||
InstSpec = Union[MaybeBoundInst, Apply]
|
||||
BranchRange = Sequence[int]
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
@@ -164,17 +165,38 @@ class EncRecipe(object):
|
||||
- An integer indicating that this result is tied to a value operand, so
|
||||
they must use the same register.
|
||||
|
||||
The `branch_range` argument must be provided for recipes that can encode
|
||||
branch instructions. It is an `(origin, bits)` tuple describing the exact
|
||||
range that can be encoded in a branch instruction.
|
||||
|
||||
:param name: Short mnemonic name for this recipe.
|
||||
:param format: All encoded instructions must have this
|
||||
:py:class:`InstructionFormat`.
|
||||
:param size: Number of bytes in the binary encoded instruction.
|
||||
:param: ins Tuple of register constraints for value operands.
|
||||
:param: outs Tuple of register constraints for results.
|
||||
:param: branch_range `(origin, bits)` range for branches.
|
||||
:param: instp Instruction predicate.
|
||||
:param: isap ISA predicate.
|
||||
"""
|
||||
|
||||
def __init__(self, name, format, ins, outs, instp=None, isap=None):
|
||||
# type: (str, InstructionFormat, ConstraintSeq, ConstraintSeq, PredNode, PredNode) -> None # noqa
|
||||
def __init__(
|
||||
self,
|
||||
name, # type: str
|
||||
format, # type: InstructionFormat
|
||||
size, # type: int
|
||||
ins, # type: ConstraintSeq
|
||||
outs, # type: ConstraintSeq
|
||||
branch_range=None, # type: BranchRange
|
||||
instp=None, # type: PredNode
|
||||
isap=None # type: PredNode
|
||||
):
|
||||
# type: (...) -> None
|
||||
self.name = name
|
||||
self.format = format
|
||||
assert size >= 0
|
||||
self.size = size
|
||||
self.branch_range = branch_range
|
||||
self.instp = instp
|
||||
self.isap = isap
|
||||
if instp:
|
||||
@@ -249,6 +271,12 @@ class Encoding(object):
|
||||
assert self.inst.format == recipe.format, (
|
||||
"Format {} must match recipe: {}".format(
|
||||
self.inst.format, recipe.format))
|
||||
|
||||
if self.inst.is_branch:
|
||||
assert recipe.branch_range, (
|
||||
'Recipe {} for {} must have a branch_range'
|
||||
.format(recipe, self.inst.name))
|
||||
|
||||
self.recipe = recipe
|
||||
self.encbits = encbits
|
||||
# Combine recipe predicates with the manually specified ones.
|
||||
|
||||
@@ -498,6 +498,27 @@ def emit_operand_constraints(seq, field, fmt):
|
||||
'Unsupported constraint {}'.format(cons))
|
||||
|
||||
|
||||
def emit_recipe_sizing(isa, fmt):
|
||||
# type: (TargetISA, srcgen.Formatter) -> None
|
||||
"""
|
||||
Emit a table of encoding recipe code size information.
|
||||
"""
|
||||
with fmt.indented(
|
||||
'static RECIPE_SIZING: [RecipeSizing; {}] = ['
|
||||
.format(len(isa.all_recipes)), '];'):
|
||||
for r in isa.all_recipes:
|
||||
fmt.comment(r.name)
|
||||
with fmt.indented('RecipeSizing {', '},'):
|
||||
fmt.format('bytes: {},', r.size)
|
||||
if r.branch_range:
|
||||
fmt.format(
|
||||
'branch_range: '
|
||||
'Some(BranchRange {{ origin: {}, bits: {} }}),',
|
||||
*r.branch_range)
|
||||
else:
|
||||
fmt.line('branch_range: None,')
|
||||
|
||||
|
||||
def gen_isa(isa, fmt):
|
||||
# type: (TargetISA, srcgen.Formatter) -> None
|
||||
# First assign numbers to relevant instruction predicates and generate the
|
||||
@@ -535,10 +556,12 @@ def gen_isa(isa, fmt):
|
||||
|
||||
emit_recipe_names(isa, fmt)
|
||||
emit_recipe_constraints(isa, fmt)
|
||||
emit_recipe_sizing(isa, fmt)
|
||||
|
||||
# Finally, tie it all together in an `EncInfo`.
|
||||
with fmt.indented('pub static INFO: EncInfo = EncInfo {', '};'):
|
||||
fmt.line('constraints: &RECIPE_CONSTRAINTS,')
|
||||
fmt.line('sizing: &RECIPE_SIZING,')
|
||||
fmt.line('names: &RECIPE_NAMES,')
|
||||
|
||||
|
||||
|
||||
@@ -85,37 +85,43 @@ def LUI():
|
||||
|
||||
# R-type 32-bit instructions: These are mostly binary arithmetic instructions.
|
||||
# The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8)
|
||||
R = EncRecipe('R', Binary, ins=(GPR, GPR), outs=GPR)
|
||||
R = EncRecipe('R', Binary, size=4, ins=(GPR, GPR), outs=GPR)
|
||||
|
||||
# R-type with an immediate shift amount instead of rs2.
|
||||
Rshamt = EncRecipe('Rshamt', BinaryImm, ins=GPR, outs=GPR)
|
||||
Rshamt = EncRecipe('Rshamt', BinaryImm, size=4, ins=GPR, outs=GPR)
|
||||
|
||||
# R-type encoding of an integer comparison.
|
||||
Ricmp = EncRecipe('Ricmp', IntCompare, ins=(GPR, GPR), outs=GPR)
|
||||
Ricmp = EncRecipe('Ricmp', IntCompare, size=4, ins=(GPR, GPR), outs=GPR)
|
||||
|
||||
I = EncRecipe(
|
||||
'I', BinaryImm, ins=GPR, outs=GPR,
|
||||
'I', BinaryImm, size=4, ins=GPR, outs=GPR,
|
||||
instp=IsSignedInt(BinaryImm.imm, 12))
|
||||
|
||||
# I-type encoding of an integer comparison.
|
||||
Iicmp = EncRecipe(
|
||||
'Iicmp', IntCompareImm, ins=GPR, outs=GPR,
|
||||
'Iicmp', IntCompareImm, size=4, ins=GPR, outs=GPR,
|
||||
instp=IsSignedInt(IntCompareImm.imm, 12))
|
||||
|
||||
# I-type encoding for `jalr` as a return instruction. We won't use the
|
||||
# immediate offset.
|
||||
# The variable return values are not encoded.
|
||||
Iret = EncRecipe('Iret', MultiAry, ins=GPR, outs=())
|
||||
Iret = EncRecipe('Iret', MultiAry, size=4, ins=GPR, outs=())
|
||||
|
||||
# U-type instructions have a 20-bit immediate that targets bits 12-31.
|
||||
U = EncRecipe(
|
||||
'U', UnaryImm, ins=(), outs=GPR,
|
||||
'U', UnaryImm, size=4, ins=(), outs=GPR,
|
||||
instp=IsSignedInt(UnaryImm.imm, 32, 12))
|
||||
|
||||
# SB-type branch instructions.
|
||||
# TODO: These instructions have a +/- 4 KB branch range. How to encode that
|
||||
# constraint?
|
||||
SB = EncRecipe('SB', BranchIcmp, ins=(GPR, GPR), outs=())
|
||||
SB = EncRecipe(
|
||||
'SB', BranchIcmp, size=4,
|
||||
ins=(GPR, GPR), outs=(),
|
||||
branch_range=(0, 13))
|
||||
|
||||
# SB-type branch instruction with rs2 fixed to zero.
|
||||
SBzero = EncRecipe('SBzero', Branch, ins=(GPR), outs=())
|
||||
SBzero = EncRecipe(
|
||||
'SBzero', Branch, size=4,
|
||||
ins=(GPR), outs=(),
|
||||
branch_range=(0, 13))
|
||||
|
||||
@@ -5,5 +5,6 @@ use ir::types;
|
||||
use isa::EncInfo;
|
||||
use isa::constraints::*;
|
||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||
use isa::encoding::RecipeSizing;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/encoding-arm32.rs"));
|
||||
|
||||
@@ -5,5 +5,6 @@ use ir::types;
|
||||
use isa::EncInfo;
|
||||
use isa::constraints::*;
|
||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||
use isa::encoding::RecipeSizing;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/encoding-arm64.rs"));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//! It is the register allocator's job to make sure that the register constraints on value operands
|
||||
//! are satisfied.
|
||||
|
||||
use binemit::CodeOffset;
|
||||
use isa::{RegClass, RegUnit};
|
||||
|
||||
/// Register constraint for a single value operand or instruction result.
|
||||
@@ -48,7 +49,7 @@ pub enum ConstraintKind {
|
||||
Stack,
|
||||
}
|
||||
|
||||
/// Constraints for an encoding recipe.
|
||||
/// Value operand constraints for an encoding recipe.
|
||||
#[derive(Clone)]
|
||||
pub struct RecipeConstraints {
|
||||
/// Constraints for the instruction's fixed value operands.
|
||||
@@ -66,3 +67,60 @@ pub struct RecipeConstraints {
|
||||
/// constraints must be derived from the calling convention ABI.
|
||||
pub outs: &'static [OperandConstraint],
|
||||
}
|
||||
|
||||
/// Constraints on the range of a branch instruction.
|
||||
///
|
||||
/// A branch instruction usually encodes its destination as a signed n-bit offset from an origin.
|
||||
/// The origin depends on the ISA and the specific instruction:
|
||||
///
|
||||
/// - RISC-V and ARM Aarch64 use the address of the branch instruction, `origin = 0`.
|
||||
/// - 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)]
|
||||
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.
|
||||
pub origin: u8,
|
||||
|
||||
/// Number of bits in the signed byte displacement encoded in the instruction. This does not
|
||||
/// account for branches that can only target aligned addresses.
|
||||
pub bits: u8,
|
||||
}
|
||||
|
||||
impl BranchRange {
|
||||
/// Determine if this branch range can represent the range from `branch` to `dest`, where
|
||||
/// `branch` is the code offset of the branch instruction itself and `dest` is the code offset
|
||||
/// of the destination EBB header.
|
||||
///
|
||||
/// This method does not detect if the range is larger than 2 GB.
|
||||
pub fn contains(self, branch: CodeOffset, dest: CodeOffset) -> bool {
|
||||
let d = dest.wrapping_sub(branch + self.origin as CodeOffset) as i32;
|
||||
let s = 32 - self.bits;
|
||||
d == d << s >> s
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn branch_range() {
|
||||
// ARM T1 branch.
|
||||
let t1 = BranchRange { origin: 4, bits: 9 };
|
||||
assert!(t1.contains(0, 0));
|
||||
assert!(t1.contains(0, 2));
|
||||
assert!(t1.contains(2, 0));
|
||||
assert!(t1.contains(1000, 1000));
|
||||
|
||||
// Forward limit.
|
||||
assert!(t1.contains(1000, 1258));
|
||||
assert!(!t1.contains(1000, 1260));
|
||||
|
||||
// Backward limit
|
||||
assert!(t1.contains(1000, 748));
|
||||
assert!(!t1.contains(1000, 746));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! The `Encoding` struct.
|
||||
|
||||
use binemit::CodeOffset;
|
||||
use isa::constraints::{RecipeConstraints, BranchRange};
|
||||
use std::fmt;
|
||||
use isa::constraints::RecipeConstraints;
|
||||
|
||||
/// Bits needed to encode an instruction as binary machine code.
|
||||
///
|
||||
@@ -78,12 +79,28 @@ impl fmt::Display for DisplayEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
/// Code size information for an encoding recipe.
|
||||
///
|
||||
/// All encoding recipes correspond to an exact instruction size.
|
||||
pub struct RecipeSizing {
|
||||
/// Size in bytes of instructions encoded with this recipe.
|
||||
pub bytes: u8,
|
||||
|
||||
/// Allowed branch range in this recipe, if any.
|
||||
///
|
||||
/// All encoding recipes for branches have exact branch range information.
|
||||
pub branch_range: Option<BranchRange>,
|
||||
}
|
||||
|
||||
/// Information about all the encodings in this ISA.
|
||||
#[derive(Clone)]
|
||||
pub struct EncInfo {
|
||||
/// Constraints on value operands per recipe.
|
||||
pub constraints: &'static [RecipeConstraints],
|
||||
|
||||
/// Code size information per recipe.
|
||||
pub sizing: &'static [RecipeSizing],
|
||||
|
||||
/// Names of encoding recipes.
|
||||
pub names: &'static [&'static str],
|
||||
}
|
||||
@@ -101,4 +118,23 @@ impl EncInfo {
|
||||
recipe_names: self.names,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the exact size in bytes of instructions encoded with `enc`.
|
||||
///
|
||||
/// Returns 0 for illegal encodings.
|
||||
pub fn bytes(&self, enc: Encoding) -> CodeOffset {
|
||||
self.sizing
|
||||
.get(enc.recipe())
|
||||
.map(|s| s.bytes as CodeOffset)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Get the branch range that is supported by `enc`, if any.
|
||||
///
|
||||
/// This will never return `None` for a legal branch encoding.
|
||||
pub fn branch_range(&self, enc: Encoding) -> Option<BranchRange> {
|
||||
self.sizing
|
||||
.get(enc.recipe())
|
||||
.and_then(|s| s.branch_range)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@ use ir::types;
|
||||
use isa::EncInfo;
|
||||
use isa::constraints::*;
|
||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||
use isa::encoding::RecipeSizing;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/encoding-intel.rs"));
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
//! The configured target ISA trait object is a `Box<TargetIsa>` which can be used for multiple
|
||||
//! concurrent function compilations.
|
||||
|
||||
pub use isa::constraints::{RecipeConstraints, OperandConstraint, ConstraintKind};
|
||||
pub use isa::constraints::{RecipeConstraints, OperandConstraint, ConstraintKind, BranchRange};
|
||||
pub use isa::encoding::{Encoding, EncInfo};
|
||||
pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use ir::{Opcode, InstructionData};
|
||||
use isa::EncInfo;
|
||||
use isa::constraints::*;
|
||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||
use isa::encoding::RecipeSizing;
|
||||
use predicates;
|
||||
use super::registers::*;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user