Add reference types R32 and R64
-Add resumable_trap, safepoint, isnull, and null instructions -Add Stackmap struct and StackmapSink trait Co-authored-by: Mir Ahmed <mirahmed753@gmail.com> Co-authored-by: Dan Gohman <sunfish@mozilla.com>
This commit is contained in:
@@ -12,7 +12,7 @@ use crate::cdsl::formats::{
|
||||
};
|
||||
use crate::cdsl::operands::Operand;
|
||||
use crate::cdsl::type_inference::Constraint;
|
||||
use crate::cdsl::types::{LaneType, ValueType, VectorType};
|
||||
use crate::cdsl::types::{LaneType, ReferenceType, ValueType, VectorType};
|
||||
use crate::cdsl::typevar::TypeVar;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
@@ -177,6 +177,10 @@ impl Instruction {
|
||||
bind(self.clone(), Some(lane_type.into()), Vec::new())
|
||||
}
|
||||
|
||||
pub fn bind_ref(&self, reference_type: impl Into<ReferenceType>) -> BoundInstruction {
|
||||
bind_ref(self.clone(), Some(reference_type.into()), Vec::new())
|
||||
}
|
||||
|
||||
pub fn bind_vector(&self, lane_type: impl Into<LaneType>, num_lanes: u64) -> BoundInstruction {
|
||||
bind_vector(self.clone(), lane_type.into(), num_lanes, Vec::new())
|
||||
}
|
||||
@@ -406,6 +410,10 @@ impl BoundInstruction {
|
||||
bind(self.inst, Some(lane_type.into()), self.value_types)
|
||||
}
|
||||
|
||||
pub fn bind_ref(self, reference_type: impl Into<ReferenceType>) -> BoundInstruction {
|
||||
bind_ref(self.inst, Some(reference_type.into()), self.value_types)
|
||||
}
|
||||
|
||||
pub fn bind_vector(self, lane_type: impl Into<LaneType>, num_lanes: u64) -> BoundInstruction {
|
||||
bind_vector(self.inst, lane_type.into(), num_lanes, self.value_types)
|
||||
}
|
||||
@@ -1043,6 +1051,13 @@ impl InstSpec {
|
||||
InstSpec::Bound(inst) => inst.clone().bind(lane_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_ref(&self, reference_type: impl Into<ReferenceType>) -> BoundInstruction {
|
||||
match self {
|
||||
InstSpec::Inst(inst) => inst.bind_ref(reference_type),
|
||||
InstSpec::Bound(inst) => inst.clone().bind_ref(reference_type),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<InstSpec> for &Instruction {
|
||||
@@ -1077,6 +1092,26 @@ fn bind(
|
||||
BoundInstruction { inst, value_types }
|
||||
}
|
||||
|
||||
/// Helper bind for reference types reused by {Bound,}Instruction::bind_ref.
|
||||
fn bind_ref(
|
||||
inst: Instruction,
|
||||
reference_type: Option<ReferenceType>,
|
||||
mut value_types: Vec<ValueTypeOrAny>,
|
||||
) -> BoundInstruction {
|
||||
match reference_type {
|
||||
Some(reference_type) => {
|
||||
value_types.push(ValueTypeOrAny::ValueType(reference_type.into()));
|
||||
}
|
||||
None => {
|
||||
value_types.push(ValueTypeOrAny::Any);
|
||||
}
|
||||
}
|
||||
|
||||
verify_polymorphic_binding(&inst, &value_types);
|
||||
|
||||
BoundInstruction { inst, value_types }
|
||||
}
|
||||
|
||||
/// Helper bind for vector types reused by {Bound,}Instruction::bind.
|
||||
fn bind_vector(
|
||||
inst: Instruction,
|
||||
|
||||
@@ -11,12 +11,14 @@ use crate::shared::types as shared_types;
|
||||
//
|
||||
// 0: Void
|
||||
// 0x01-0x6f: Special types
|
||||
// 0x70-0x7f: Lane types
|
||||
// 0x70-0x7d: Lane types
|
||||
// 0x7e-0x7f: Reference types
|
||||
// 0x80-0xff: Vector types
|
||||
//
|
||||
// Vector types are encoded with the lane type in the low 4 bits and log2(lanes)
|
||||
// in the high 4 bits, giving a range of 2-256 lanes.
|
||||
static LANE_BASE: u8 = 0x70;
|
||||
static REFERENCE_BASE: u8 = 0x7E;
|
||||
|
||||
// Rust name prefix used for the `rust_name` method.
|
||||
static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
||||
@@ -31,6 +33,7 @@ static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
||||
pub enum ValueType {
|
||||
BV(BVType),
|
||||
Lane(LaneType),
|
||||
Reference(ReferenceType),
|
||||
Special(SpecialType),
|
||||
Vector(VectorType),
|
||||
}
|
||||
@@ -46,11 +49,16 @@ impl ValueType {
|
||||
SpecialTypeIterator::new()
|
||||
}
|
||||
|
||||
pub fn all_reference_types() -> ReferenceTypeIterator {
|
||||
ReferenceTypeIterator::new()
|
||||
}
|
||||
|
||||
/// Return a string containing the documentation comment for this type.
|
||||
pub fn doc(&self) -> String {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.doc(),
|
||||
ValueType::Lane(l) => l.doc(),
|
||||
ValueType::Reference(r) => r.doc(),
|
||||
ValueType::Special(s) => s.doc(),
|
||||
ValueType::Vector(ref v) => v.doc(),
|
||||
}
|
||||
@@ -61,6 +69,7 @@ impl ValueType {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.lane_bits(),
|
||||
ValueType::Lane(l) => l.lane_bits(),
|
||||
ValueType::Reference(r) => r.lane_bits(),
|
||||
ValueType::Special(s) => s.lane_bits(),
|
||||
ValueType::Vector(ref v) => v.lane_bits(),
|
||||
}
|
||||
@@ -84,6 +93,7 @@ impl ValueType {
|
||||
match *self {
|
||||
ValueType::BV(_) => None,
|
||||
ValueType::Lane(l) => Some(l.number()),
|
||||
ValueType::Reference(r) => Some(r.number()),
|
||||
ValueType::Special(s) => Some(s.number()),
|
||||
ValueType::Vector(ref v) => Some(v.number()),
|
||||
}
|
||||
@@ -112,6 +122,7 @@ impl fmt::Display for ValueType {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.fmt(f),
|
||||
ValueType::Lane(l) => l.fmt(f),
|
||||
ValueType::Reference(r) => r.fmt(f),
|
||||
ValueType::Special(s) => s.fmt(f),
|
||||
ValueType::Vector(ref v) => v.fmt(f),
|
||||
}
|
||||
@@ -132,6 +143,13 @@ impl From<LaneType> for ValueType {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given reference type.
|
||||
impl From<ReferenceType> for ValueType {
|
||||
fn from(reference: ReferenceType) -> Self {
|
||||
ValueType::Reference(reference)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given special type.
|
||||
impl From<SpecialType> for ValueType {
|
||||
fn from(spec: SpecialType) -> Self {
|
||||
@@ -515,3 +533,83 @@ impl Iterator for SpecialTypeIterator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference type is scalar type, but not lane type.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ReferenceType(pub shared_types::Reference);
|
||||
|
||||
impl ReferenceType {
|
||||
/// Return a string containing the documentation comment for this reference type.
|
||||
pub fn doc(self) -> String {
|
||||
format!("An opaque reference type with {} bits.", self.lane_bits())
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(self) -> u64 {
|
||||
match self.0 {
|
||||
shared_types::Reference::R32 => 32,
|
||||
shared_types::Reference::R64 => 64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the unique number associated with this reference type.
|
||||
pub fn number(self) -> u8 {
|
||||
REFERENCE_BASE
|
||||
+ match self {
|
||||
ReferenceType(shared_types::Reference::R32) => 0,
|
||||
ReferenceType(shared_types::Reference::R64) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ref_from_bits(num_bits: u16) -> ReferenceType {
|
||||
ReferenceType(match num_bits {
|
||||
32 => shared_types::Reference::R32,
|
||||
64 => shared_types::Reference::R64,
|
||||
_ => unreachable!("unexpected number of bits for a reference type"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ReferenceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "r{}", self.lane_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ReferenceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ReferenceType(bits={})", self.lane_bits())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ReferenceType from a given reference variant.
|
||||
impl From<shared_types::Reference> for ReferenceType {
|
||||
fn from(r: shared_types::Reference) -> Self {
|
||||
ReferenceType(r)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator for different reference types.
|
||||
pub struct ReferenceTypeIterator {
|
||||
reference_iter: shared_types::ReferenceIterator,
|
||||
}
|
||||
|
||||
impl ReferenceTypeIterator {
|
||||
/// Create a new reference type iterator.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
reference_iter: shared_types::ReferenceIterator::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ReferenceTypeIterator {
|
||||
type Item = ReferenceType;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(r) = self.reference_iter.next() {
|
||||
Some(ReferenceType::from(r))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::iter::FromIterator;
|
||||
use std::ops;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::cdsl::types::{BVType, LaneType, SpecialType, ValueType};
|
||||
use crate::cdsl::types::{BVType, LaneType, ReferenceType, SpecialType, ValueType};
|
||||
|
||||
const MAX_LANES: u16 = 256;
|
||||
const MAX_BITS: u16 = 64;
|
||||
@@ -64,6 +64,10 @@ impl TypeVar {
|
||||
ValueType::Special(special_type) => {
|
||||
return TypeVar::new(name, doc, builder.specials(vec![special_type]).build());
|
||||
}
|
||||
ValueType::Reference(ReferenceType(reference_type)) => {
|
||||
let bits = reference_type as RangeBound;
|
||||
return TypeVar::new(name, doc, builder.refs(bits..bits).build());
|
||||
}
|
||||
ValueType::Lane(lane_type) => (lane_type, 1),
|
||||
ValueType::Vector(vec_type) => {
|
||||
(vec_type.lane_type(), vec_type.lane_count() as RangeBound)
|
||||
@@ -406,6 +410,7 @@ pub struct TypeSet {
|
||||
pub ints: NumSet,
|
||||
pub floats: NumSet,
|
||||
pub bools: NumSet,
|
||||
pub refs: NumSet,
|
||||
pub bitvecs: NumSet,
|
||||
pub specials: Vec<SpecialType>,
|
||||
}
|
||||
@@ -416,6 +421,7 @@ impl TypeSet {
|
||||
ints: NumSet,
|
||||
floats: NumSet,
|
||||
bools: NumSet,
|
||||
refs: NumSet,
|
||||
bitvecs: NumSet,
|
||||
specials: Vec<SpecialType>,
|
||||
) -> Self {
|
||||
@@ -424,6 +430,7 @@ impl TypeSet {
|
||||
ints,
|
||||
floats,
|
||||
bools,
|
||||
refs,
|
||||
bitvecs,
|
||||
specials,
|
||||
}
|
||||
@@ -432,7 +439,11 @@ impl TypeSet {
|
||||
/// Return the number of concrete types represented by this typeset.
|
||||
pub fn size(&self) -> usize {
|
||||
self.lanes.len()
|
||||
* (self.ints.len() + self.floats.len() + self.bools.len() + self.bitvecs.len())
|
||||
* (self.ints.len()
|
||||
+ self.floats.len()
|
||||
+ self.bools.len()
|
||||
+ self.refs.len()
|
||||
+ self.bitvecs.len())
|
||||
+ self.specials.len()
|
||||
}
|
||||
|
||||
@@ -462,6 +473,7 @@ impl TypeSet {
|
||||
let mut copy = self.clone();
|
||||
copy.ints = NumSet::new();
|
||||
copy.floats = NumSet::new();
|
||||
copy.refs = NumSet::new();
|
||||
copy.bitvecs = NumSet::new();
|
||||
if (&self.lanes - &num_set![1]).len() > 0 {
|
||||
copy.bools = &self.ints | &self.floats;
|
||||
@@ -544,6 +556,7 @@ impl TypeSet {
|
||||
copy.ints = NumSet::new();
|
||||
copy.bools = NumSet::new();
|
||||
copy.floats = NumSet::new();
|
||||
copy.refs = NumSet::new();
|
||||
copy.bitvecs = self
|
||||
.lanes
|
||||
.iter()
|
||||
@@ -568,6 +581,9 @@ impl TypeSet {
|
||||
for &bits in &self.bools {
|
||||
ret.push(LaneType::bool_from_bits(bits).by(num_lanes));
|
||||
}
|
||||
for &bits in &self.refs {
|
||||
ret.push(ReferenceType::ref_from_bits(bits).into());
|
||||
}
|
||||
for &bits in &self.bitvecs {
|
||||
assert_eq!(num_lanes, 1);
|
||||
ret.push(BVType::new(bits).into());
|
||||
@@ -630,6 +646,7 @@ impl TypeSet {
|
||||
let mut ints = range_to_set(Some(8..MAX_BITS));
|
||||
let mut floats = range_to_set(Some(32..64));
|
||||
let mut bools = range_to_set(Some(1..MAX_BITS));
|
||||
let refs = range_to_set(Some(32..64));
|
||||
|
||||
for &l in &all_lanes {
|
||||
for &i in &all_ints {
|
||||
@@ -654,7 +671,7 @@ impl TypeSet {
|
||||
|
||||
let bitvecs = NumSet::new();
|
||||
let specials = Vec::new();
|
||||
TypeSet::new(lanes, ints, floats, bools, bitvecs, specials)
|
||||
TypeSet::new(lanes, ints, floats, bools, refs, bitvecs, specials)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -664,6 +681,7 @@ impl TypeSet {
|
||||
self.ints = &self.ints & &other.ints;
|
||||
self.floats = &self.floats & &other.floats;
|
||||
self.bools = &self.bools & &other.bools;
|
||||
self.refs = &self.refs & &other.refs;
|
||||
self.bitvecs = &self.bitvecs & &other.bitvecs;
|
||||
|
||||
let mut new_specials = Vec::new();
|
||||
@@ -680,6 +698,7 @@ impl TypeSet {
|
||||
&& self.ints.is_subset(&other.ints)
|
||||
&& self.floats.is_subset(&other.floats)
|
||||
&& self.bools.is_subset(&other.bools)
|
||||
&& self.refs.is_subset(&other.refs)
|
||||
&& self.bitvecs.is_subset(&other.bitvecs)
|
||||
&& {
|
||||
let specials: HashSet<SpecialType> = HashSet::from_iter(self.specials.clone());
|
||||
@@ -692,12 +711,14 @@ impl TypeSet {
|
||||
set_wider_or_equal(&self.ints, &other.ints)
|
||||
&& set_wider_or_equal(&self.floats, &other.floats)
|
||||
&& set_wider_or_equal(&self.bools, &other.bools)
|
||||
&& set_wider_or_equal(&self.refs, &other.refs)
|
||||
}
|
||||
|
||||
pub fn is_narrower(&self, other: &TypeSet) -> bool {
|
||||
set_narrower(&self.ints, &other.ints)
|
||||
&& set_narrower(&self.floats, &other.floats)
|
||||
&& set_narrower(&self.bools, &other.bools)
|
||||
&& set_narrower(&self.refs, &other.refs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -738,6 +759,12 @@ impl fmt::Debug for TypeSet {
|
||||
Vec::from_iter(self.bools.iter().map(|x| x.to_string())).join(", ")
|
||||
));
|
||||
}
|
||||
if !self.refs.is_empty() {
|
||||
subsets.push(format!(
|
||||
"refs={{{}}}",
|
||||
Vec::from_iter(self.refs.iter().map(|x| x.to_string())).join(", ")
|
||||
));
|
||||
}
|
||||
if !self.bitvecs.is_empty() {
|
||||
subsets.push(format!(
|
||||
"bitvecs={{{}}}",
|
||||
@@ -760,6 +787,7 @@ pub struct TypeSetBuilder {
|
||||
ints: Interval,
|
||||
floats: Interval,
|
||||
bools: Interval,
|
||||
refs: Interval,
|
||||
bitvecs: Interval,
|
||||
includes_scalars: bool,
|
||||
simd_lanes: Interval,
|
||||
@@ -772,6 +800,7 @@ impl TypeSetBuilder {
|
||||
ints: Interval::None,
|
||||
floats: Interval::None,
|
||||
bools: Interval::None,
|
||||
refs: Interval::None,
|
||||
bitvecs: Interval::None,
|
||||
includes_scalars: true,
|
||||
simd_lanes: Interval::None,
|
||||
@@ -794,6 +823,11 @@ impl TypeSetBuilder {
|
||||
self.bools = interval.into();
|
||||
self
|
||||
}
|
||||
pub fn refs(mut self, interval: impl Into<Interval>) -> Self {
|
||||
assert!(self.refs == Interval::None);
|
||||
self.refs = interval.into();
|
||||
self
|
||||
}
|
||||
pub fn includes_scalars(mut self, includes_scalars: bool) -> Self {
|
||||
self.includes_scalars = includes_scalars;
|
||||
self
|
||||
@@ -827,6 +861,7 @@ impl TypeSetBuilder {
|
||||
range_to_set(self.ints.to_range(8..MAX_BITS, None)),
|
||||
range_to_set(self.floats.to_range(32..64, None)),
|
||||
bools,
|
||||
range_to_set(self.refs.to_range(32..64, None)),
|
||||
range_to_set(self.bitvecs.to_range(1..MAX_BITVEC, None)),
|
||||
self.specials,
|
||||
)
|
||||
@@ -837,6 +872,7 @@ impl TypeSetBuilder {
|
||||
.ints(Interval::All)
|
||||
.floats(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.refs(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.bitvecs(Interval::All)
|
||||
.specials(ValueType::all_special_types().collect())
|
||||
|
||||
@@ -18,10 +18,12 @@ fn gen_recipe(formats: &FormatRegistry, recipe: &EncodingRecipe, fmt: &mut Forma
|
||||
let inst_format = formats.get(recipe.format);
|
||||
let num_value_ops = inst_format.num_value_operands;
|
||||
|
||||
let want_args = recipe.operands_in.iter().any(|c| match c {
|
||||
OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
|
||||
OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
|
||||
});
|
||||
// TODO: Set want_args to true for only MultiAry instructions instead of all formats with value list.
|
||||
let want_args = inst_format.has_value_list
|
||||
|| recipe.operands_in.iter().any(|c| match c {
|
||||
OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
|
||||
OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
|
||||
});
|
||||
assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);
|
||||
|
||||
let want_outs = recipe.operands_out.iter().any(|c| match c {
|
||||
@@ -159,6 +161,7 @@ fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mu
|
||||
fmt.line("inst: Inst,");
|
||||
fmt.line("_divert: &mut RegDiversions,");
|
||||
fmt.line("_sink: &mut CS,");
|
||||
fmt.line("_isa: &dyn TargetIsa,");
|
||||
});
|
||||
fmt.line(") {");
|
||||
fmt.indent(|fmt| {
|
||||
@@ -176,6 +179,7 @@ fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mu
|
||||
fmt.line("inst: Inst,");
|
||||
fmt.line("divert: &mut RegDiversions,");
|
||||
fmt.line("sink: &mut CS,");
|
||||
fmt.line("isa: &dyn TargetIsa,")
|
||||
});
|
||||
|
||||
fmt.line(") {");
|
||||
|
||||
@@ -650,6 +650,9 @@ fn typeset_to_string(ts: &TypeSet) -> String {
|
||||
if ts.specials.len() > 0 {
|
||||
result += &format!(", specials=[{}]", iterable_to_string(&ts.specials));
|
||||
}
|
||||
if ts.refs.len() > 0 {
|
||||
result += &format!(", refs={}", iterable_to_string(&ts.refs));
|
||||
}
|
||||
result += ")";
|
||||
result
|
||||
}
|
||||
@@ -677,6 +680,7 @@ pub fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter)
|
||||
gen_bitset(&ts.ints, "ints", 8, fmt);
|
||||
gen_bitset(&ts.floats, "floats", 8, fmt);
|
||||
gen_bitset(&ts.bools, "bools", 8, fmt);
|
||||
gen_bitset(&ts.refs, "refs", 8, fmt);
|
||||
});
|
||||
fmt.line("},");
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ fn emit_types(fmt: &mut srcgen::Formatter) -> Result<(), error::Error> {
|
||||
emit_type(&ty, fmt)?;
|
||||
}
|
||||
|
||||
// Emit all reference types.
|
||||
for ty in cdsl_types::ValueType::all_reference_types().map(cdsl_types::ValueType::from) {
|
||||
emit_type(&ty, fmt)?;
|
||||
}
|
||||
|
||||
// Emit vector definitions for common SIMD sizes.
|
||||
for vec_size in &[64_u64, 128, 256, 512] {
|
||||
emit_vectors(*vec_size, fmt)?;
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::cdsl::types::ValueType;
|
||||
use crate::shared::types::Bool::{B1, B16, B32, B64, B8};
|
||||
use crate::shared::types::Float::{F32, F64};
|
||||
use crate::shared::types::Int::{I16, I32, I64, I8};
|
||||
use crate::shared::types::Reference::{R32, R64};
|
||||
use crate::shared::Definitions as SharedDefinitions;
|
||||
|
||||
use super::recipes::{RecipeGroup, Template};
|
||||
@@ -175,6 +176,20 @@ impl PerCpuModeEncodings {
|
||||
});
|
||||
}
|
||||
|
||||
/// Add encodings for `inst.r32` to X86_32.
|
||||
/// Add encodings for `inst.r32` to X86_64 with and without REX.
|
||||
/// Add encodings for `inst.r64` to X86_64 with a REX.W prefix.
|
||||
fn enc_r32_r64(&mut self, inst: impl Into<InstSpec>, template: Template) {
|
||||
let inst: InstSpec = inst.into();
|
||||
self.enc32(inst.bind_ref(R32), template.nonrex());
|
||||
|
||||
// REX-less encoding must come after REX encoding so we don't use it by default. Otherwise
|
||||
// reg-alloc would never use r8 and up.
|
||||
self.enc64(inst.bind_ref(R32), template.rex());
|
||||
self.enc64(inst.bind_ref(R32), template.nonrex());
|
||||
self.enc64(inst.bind_ref(R64), template.rex().w());
|
||||
}
|
||||
|
||||
/// Add encodings for `inst` to X86_64 with and without a REX prefix.
|
||||
fn enc_x86_64(&mut self, inst: impl Into<InstSpec> + Clone, template: Template) {
|
||||
// See above comment about the ordering of rex vs non-rex encodings.
|
||||
@@ -331,6 +346,7 @@ pub fn define(
|
||||
let ireduce = shared.by_name("ireduce");
|
||||
let ishl = shared.by_name("ishl");
|
||||
let ishl_imm = shared.by_name("ishl_imm");
|
||||
let is_null = shared.by_name("is_null");
|
||||
let istore16 = shared.by_name("istore16");
|
||||
let istore16_complex = shared.by_name("istore16_complex");
|
||||
let istore32 = shared.by_name("istore32");
|
||||
@@ -344,6 +360,7 @@ pub fn define(
|
||||
let load = shared.by_name("load");
|
||||
let load_complex = shared.by_name("load_complex");
|
||||
let nearest = shared.by_name("nearest");
|
||||
let null = shared.by_name("null");
|
||||
let popcnt = shared.by_name("popcnt");
|
||||
let raw_bitcast = shared.by_name("raw_bitcast");
|
||||
let regfill = shared.by_name("regfill");
|
||||
@@ -354,6 +371,7 @@ pub fn define(
|
||||
let rotl_imm = shared.by_name("rotl_imm");
|
||||
let rotr = shared.by_name("rotr");
|
||||
let rotr_imm = shared.by_name("rotr_imm");
|
||||
let safepoint = shared.by_name("safepoint");
|
||||
let scalar_to_vector = shared.by_name("scalar_to_vector");
|
||||
let selectif = shared.by_name("selectif");
|
||||
let sextend = shared.by_name("sextend");
|
||||
@@ -374,6 +392,7 @@ pub fn define(
|
||||
let trap = shared.by_name("trap");
|
||||
let trapff = shared.by_name("trapff");
|
||||
let trapif = shared.by_name("trapif");
|
||||
let resumable_trap = shared.by_name("resumable_trap");
|
||||
let trueff = shared.by_name("trueff");
|
||||
let trueif = shared.by_name("trueif");
|
||||
let trunc = shared.by_name("trunc");
|
||||
@@ -455,6 +474,7 @@ pub fn define(
|
||||
let rec_icscc_ib = r.template("icscc_ib");
|
||||
let rec_icscc_id = r.template("icscc_id");
|
||||
let rec_indirect_jmp = r.template("indirect_jmp");
|
||||
let rec_is_zero = r.template("is_zero");
|
||||
let rec_jmpb = r.template("jmpb");
|
||||
let rec_jmpd = r.template("jmpd");
|
||||
let rec_jt_base = r.template("jt_base");
|
||||
@@ -473,6 +493,7 @@ pub fn define(
|
||||
let rec_popq = r.template("popq");
|
||||
let rec_pu_id = r.template("pu_id");
|
||||
let rec_pu_id_bool = r.template("pu_id_bool");
|
||||
let rec_pu_id_ref = r.template("pu_id_ref");
|
||||
let rec_pu_iq = r.template("pu_iq");
|
||||
let rec_pushq = r.template("pushq");
|
||||
let rec_ret = r.template("ret");
|
||||
@@ -492,6 +513,7 @@ pub fn define(
|
||||
let rec_rmov = r.template("rmov");
|
||||
let rec_rr = r.template("rr");
|
||||
let rec_rrx = r.template("rrx");
|
||||
let rec_safepoint = r.recipe("safepoint");
|
||||
let rec_setf_abcd = r.template("setf_abcd");
|
||||
let rec_seti_abcd = r.template("seti_abcd");
|
||||
let rec_spaddr4_id = r.template("spaddr4_id");
|
||||
@@ -566,6 +588,7 @@ pub fn define(
|
||||
e.enc_i32_i64(x86_umulx, rec_mulx.opcodes(vec![0xf7]).rrr(4));
|
||||
|
||||
e.enc_i32_i64(copy, rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_r32_r64(copy, rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_both(copy.bind(B1), rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_both(copy.bind(I8), rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_both(copy.bind(I16), rec_umr.opcodes(vec![0x89]));
|
||||
@@ -579,6 +602,12 @@ pub fn define(
|
||||
e.enc64(regmove.bind(I64), rec_rmov.opcodes(vec![0x89]).rex().w());
|
||||
e.enc_both(regmove.bind(B1), rec_rmov.opcodes(vec![0x89]));
|
||||
e.enc_both(regmove.bind(I8), rec_rmov.opcodes(vec![0x89]));
|
||||
e.enc32(regmove.bind_ref(R32), rec_rmov.opcodes(vec![0x89]));
|
||||
e.enc64(regmove.bind_ref(R32), rec_rmov.opcodes(vec![0x89]).rex());
|
||||
e.enc64(
|
||||
regmove.bind_ref(R64),
|
||||
rec_rmov.opcodes(vec![0x89]).rex().w(),
|
||||
);
|
||||
|
||||
e.enc_i32_i64(iadd_imm, rec_r_ib.opcodes(vec![0x83]).rrr(0));
|
||||
e.enc_i32_i64(iadd_imm, rec_r_id.opcodes(vec![0x81]).rrr(0));
|
||||
@@ -841,6 +870,8 @@ pub fn define(
|
||||
|
||||
e.enc_i32_i64(spill, rec_spillSib32.opcodes(vec![0x89]));
|
||||
e.enc_i32_i64(regspill, rec_regspill32.opcodes(vec![0x89]));
|
||||
e.enc_r32_r64(spill, rec_spillSib32.opcodes(vec![0x89]));
|
||||
e.enc_r32_r64(regspill, rec_regspill32.opcodes(vec![0x89]));
|
||||
|
||||
// Use a 32-bit write for spilling `b1`, `i8` and `i16` to avoid
|
||||
// constraining the permitted registers.
|
||||
@@ -865,6 +896,8 @@ pub fn define(
|
||||
|
||||
e.enc_i32_i64(fill, rec_fillSib32.opcodes(vec![0x8b]));
|
||||
e.enc_i32_i64(regfill, rec_regfill32.opcodes(vec![0x8b]));
|
||||
e.enc_r32_r64(fill, rec_fillSib32.opcodes(vec![0x8b]));
|
||||
e.enc_r32_r64(regfill, rec_regfill32.opcodes(vec![0x8b]));
|
||||
|
||||
// Load 32 bits from `b1`, `i8` and `i16` spill slots. See `spill.b1` above.
|
||||
|
||||
@@ -1248,6 +1281,8 @@ pub fn define(
|
||||
// Trap as ud2
|
||||
e.enc32(trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
e.enc64(trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
e.enc32(resumable_trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
e.enc64(resumable_trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
|
||||
// Debug trap as int3
|
||||
e.enc32_rec(debugtrap, rec_debugtrap, 0);
|
||||
@@ -1666,5 +1701,20 @@ pub fn define(
|
||||
}
|
||||
}
|
||||
|
||||
// Reference type instructions
|
||||
|
||||
// Null references implemented as iconst 0.
|
||||
e.enc32(null.bind_ref(R32), rec_pu_id_ref.opcodes(vec![0xb8]));
|
||||
|
||||
e.enc64(null.bind_ref(R64), rec_pu_id_ref.rex().opcodes(vec![0xb8]));
|
||||
e.enc64(null.bind_ref(R64), rec_pu_id_ref.opcodes(vec![0xb8]));
|
||||
|
||||
// is_null, implemented by testing whether the value is 0.
|
||||
e.enc_r32_r64(is_null, rec_is_zero.opcodes(vec![0x85]));
|
||||
|
||||
// safepoint instruction calls sink, no actual encoding.
|
||||
e.enc32_rec(safepoint, rec_safepoint, 0);
|
||||
e.enc64_rec(safepoint, rec_safepoint, 0);
|
||||
|
||||
e
|
||||
}
|
||||
|
||||
@@ -888,6 +888,20 @@ pub fn define<'shared>(
|
||||
),
|
||||
);
|
||||
|
||||
// XX+rd id nullary with 0 as 32-bit immediate. Note no recipe predicate.
|
||||
recipes.add_template_recipe(
|
||||
EncodingRecipeBuilder::new("pu_id_ref", f_nullary, 4)
|
||||
.operands_out(vec![gpr])
|
||||
.emit(
|
||||
r#"
|
||||
// The destination register is encoded in the low bits of the opcode.
|
||||
// No ModR/M.
|
||||
{{PUT_OP}}(bits | (out_reg0 & 7), rex1(out_reg0), sink);
|
||||
sink.put4(0);
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
// XX+rd iq unary with 64-bit immediate.
|
||||
recipes.add_template_recipe(
|
||||
EncodingRecipeBuilder::new("pu_iq", f_unary_imm, 8)
|
||||
@@ -2851,5 +2865,28 @@ pub fn define<'shared>(
|
||||
),
|
||||
);
|
||||
|
||||
recipes.add_template_recipe(
|
||||
EncodingRecipeBuilder::new("is_zero", f_unary, 2 + 2)
|
||||
.operands_in(vec![gpr])
|
||||
.operands_out(vec![abcd])
|
||||
.emit(
|
||||
r#"
|
||||
// Test instruction.
|
||||
{{PUT_OP}}(bits, rex2(in_reg0, in_reg0), sink);
|
||||
modrm_rr(in_reg0, in_reg0, sink);
|
||||
// Check ZF = 1 flag to see if register holds 0.
|
||||
sink.put1(0x0f);
|
||||
sink.put1(0x94);
|
||||
modrm_rr(out_reg0, 0, sink);
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
recipes.add_recipe(EncodingRecipeBuilder::new("safepoint", f_multiary, 0).emit(
|
||||
r#"
|
||||
sink.add_stackmap(args, func, isa);
|
||||
"#,
|
||||
));
|
||||
|
||||
recipes
|
||||
}
|
||||
|
||||
@@ -85,6 +85,12 @@ pub fn define(
|
||||
TypeSetBuilder::new().ints(32..64).build(),
|
||||
);
|
||||
|
||||
let Ref = &TypeVar::new(
|
||||
"Ref",
|
||||
"A scalar reference type",
|
||||
TypeSetBuilder::new().refs(Interval::All).build(),
|
||||
);
|
||||
|
||||
let Testable = &TypeVar::new(
|
||||
"Testable",
|
||||
"A scalar boolean or integer type",
|
||||
@@ -118,11 +124,12 @@ pub fn define(
|
||||
|
||||
let Any = &TypeVar::new(
|
||||
"Any",
|
||||
"Any integer, float, or boolean scalar or vector type",
|
||||
"Any integer, float, boolean, or reference scalar or vector type",
|
||||
TypeSetBuilder::new()
|
||||
.ints(Interval::All)
|
||||
.floats(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.refs(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.includes_scalars(true)
|
||||
.build(),
|
||||
@@ -394,6 +401,19 @@ pub fn define(
|
||||
.can_trap(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"resumable_trap",
|
||||
r#"
|
||||
A resumable trap.
|
||||
|
||||
This instruction allows non-conditional traps to be used as non-terminal instructions.
|
||||
"#,
|
||||
)
|
||||
.operands_in(vec![code])
|
||||
.can_trap(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"trapnz",
|
||||
@@ -1068,6 +1088,20 @@ pub fn define(
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let a = &operand_doc("a", Ref, "A constant reference null value");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"null",
|
||||
r#"
|
||||
Null constant value for reference types.
|
||||
|
||||
Create a scalar reference SSA value with a constant null value.
|
||||
"#,
|
||||
)
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
ig.push(Inst::new(
|
||||
"nop",
|
||||
r#"
|
||||
@@ -1315,6 +1349,24 @@ pub fn define(
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let N = &operand_doc(
|
||||
"args",
|
||||
variable_args,
|
||||
"Variable number of args for Stackmap",
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"safepoint",
|
||||
r#"
|
||||
This instruction will provide live reference values at a point in
|
||||
the function. It can only be used by the compiler.
|
||||
"#,
|
||||
)
|
||||
.operands_in(vec![N])
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let x = &operand_doc("x", TxN, "Vector to split");
|
||||
let lo = &operand_doc("lo", &TxN.half_vector(), "Low-numbered lanes of `x`");
|
||||
let hi = &operand_doc("hi", &TxN.half_vector(), "High-numbered lanes of `x`");
|
||||
@@ -2578,6 +2630,23 @@ pub fn define(
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let a = &operand("a", b1);
|
||||
let x = &operand("x", Ref);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"is_null",
|
||||
r#"
|
||||
Reference verification.
|
||||
|
||||
The condition code determines if the reference type in question is
|
||||
null or not.
|
||||
"#,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let Cond = &operand("Cond", intcc);
|
||||
let f = &operand("f", iflags);
|
||||
let a = &operand("a", b1);
|
||||
|
||||
@@ -92,6 +92,18 @@ pub fn define() -> SettingGroup {
|
||||
true,
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"enable_safepoints",
|
||||
r#"
|
||||
Enable safepoint instruction insertions.
|
||||
|
||||
This will allow the emit_stackmaps() function to insert the safepoint
|
||||
instruction on top of calls and interrupt traps in order to display the
|
||||
live reference values at that point in the program.
|
||||
"#,
|
||||
false,
|
||||
);
|
||||
|
||||
// Settings specific to the `baldrdash` calling convention.
|
||||
|
||||
settings.add_enum(
|
||||
|
||||
@@ -145,6 +145,38 @@ impl Iterator for FlagIterator {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum Reference {
|
||||
/// 32-bit reference.
|
||||
R32 = 32,
|
||||
/// 64-bit reference.
|
||||
R64 = 64,
|
||||
}
|
||||
|
||||
/// This provides an iterator through all of the supported reference variants.
|
||||
pub struct ReferenceIterator {
|
||||
index: u8,
|
||||
}
|
||||
|
||||
impl ReferenceIterator {
|
||||
pub fn new() -> Self {
|
||||
Self { index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ReferenceIterator {
|
||||
type Item = Reference;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let res = match self.index {
|
||||
0 => Some(Reference::R32),
|
||||
1 => Some(Reference::R64),
|
||||
_ => return None,
|
||||
};
|
||||
self.index += 1;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod iter_tests {
|
||||
use super::*;
|
||||
@@ -185,4 +217,12 @@ mod iter_tests {
|
||||
assert_eq!(flag_iter.next(), Some(Flag::FFlags));
|
||||
assert_eq!(flag_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reference_iter_works() {
|
||||
let mut reference_iter = ReferenceIterator::new();
|
||||
assert_eq!(reference_iter.next(), Some(Reference::R32));
|
||||
assert_eq!(reference_iter.next(), Some(Reference::R64));
|
||||
assert_eq!(reference_iter.next(), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user