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:
Carmen Kwan
2019-07-23 16:28:54 -07:00
committed by Dan Gohman
parent b659262d2a
commit 19257f80c1
47 changed files with 1027 additions and 62 deletions

View File

@@ -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,

View File

@@ -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
}
}
}

View File

@@ -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())

View File

@@ -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(") {");

View File

@@ -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("},");
}

View File

@@ -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)?;

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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(

View File

@@ -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);
}
}