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::operands::Operand;
|
||||||
use crate::cdsl::type_inference::Constraint;
|
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;
|
use crate::cdsl::typevar::TypeVar;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
@@ -177,6 +177,10 @@ impl Instruction {
|
|||||||
bind(self.clone(), Some(lane_type.into()), Vec::new())
|
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 {
|
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())
|
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)
|
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 {
|
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)
|
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),
|
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 {
|
impl Into<InstSpec> for &Instruction {
|
||||||
@@ -1077,6 +1092,26 @@ fn bind(
|
|||||||
BoundInstruction { inst, value_types }
|
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.
|
/// Helper bind for vector types reused by {Bound,}Instruction::bind.
|
||||||
fn bind_vector(
|
fn bind_vector(
|
||||||
inst: Instruction,
|
inst: Instruction,
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ use crate::shared::types as shared_types;
|
|||||||
//
|
//
|
||||||
// 0: Void
|
// 0: Void
|
||||||
// 0x01-0x6f: Special types
|
// 0x01-0x6f: Special types
|
||||||
// 0x70-0x7f: Lane types
|
// 0x70-0x7d: Lane types
|
||||||
|
// 0x7e-0x7f: Reference types
|
||||||
// 0x80-0xff: Vector types
|
// 0x80-0xff: Vector types
|
||||||
//
|
//
|
||||||
// Vector types are encoded with the lane type in the low 4 bits and log2(lanes)
|
// 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.
|
// in the high 4 bits, giving a range of 2-256 lanes.
|
||||||
static LANE_BASE: u8 = 0x70;
|
static LANE_BASE: u8 = 0x70;
|
||||||
|
static REFERENCE_BASE: u8 = 0x7E;
|
||||||
|
|
||||||
// Rust name prefix used for the `rust_name` method.
|
// Rust name prefix used for the `rust_name` method.
|
||||||
static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
||||||
@@ -31,6 +33,7 @@ static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
|||||||
pub enum ValueType {
|
pub enum ValueType {
|
||||||
BV(BVType),
|
BV(BVType),
|
||||||
Lane(LaneType),
|
Lane(LaneType),
|
||||||
|
Reference(ReferenceType),
|
||||||
Special(SpecialType),
|
Special(SpecialType),
|
||||||
Vector(VectorType),
|
Vector(VectorType),
|
||||||
}
|
}
|
||||||
@@ -46,11 +49,16 @@ impl ValueType {
|
|||||||
SpecialTypeIterator::new()
|
SpecialTypeIterator::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_reference_types() -> ReferenceTypeIterator {
|
||||||
|
ReferenceTypeIterator::new()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a string containing the documentation comment for this type.
|
/// Return a string containing the documentation comment for this type.
|
||||||
pub fn doc(&self) -> String {
|
pub fn doc(&self) -> String {
|
||||||
match *self {
|
match *self {
|
||||||
ValueType::BV(ref b) => b.doc(),
|
ValueType::BV(ref b) => b.doc(),
|
||||||
ValueType::Lane(l) => l.doc(),
|
ValueType::Lane(l) => l.doc(),
|
||||||
|
ValueType::Reference(r) => r.doc(),
|
||||||
ValueType::Special(s) => s.doc(),
|
ValueType::Special(s) => s.doc(),
|
||||||
ValueType::Vector(ref v) => v.doc(),
|
ValueType::Vector(ref v) => v.doc(),
|
||||||
}
|
}
|
||||||
@@ -61,6 +69,7 @@ impl ValueType {
|
|||||||
match *self {
|
match *self {
|
||||||
ValueType::BV(ref b) => b.lane_bits(),
|
ValueType::BV(ref b) => b.lane_bits(),
|
||||||
ValueType::Lane(l) => l.lane_bits(),
|
ValueType::Lane(l) => l.lane_bits(),
|
||||||
|
ValueType::Reference(r) => r.lane_bits(),
|
||||||
ValueType::Special(s) => s.lane_bits(),
|
ValueType::Special(s) => s.lane_bits(),
|
||||||
ValueType::Vector(ref v) => v.lane_bits(),
|
ValueType::Vector(ref v) => v.lane_bits(),
|
||||||
}
|
}
|
||||||
@@ -84,6 +93,7 @@ impl ValueType {
|
|||||||
match *self {
|
match *self {
|
||||||
ValueType::BV(_) => None,
|
ValueType::BV(_) => None,
|
||||||
ValueType::Lane(l) => Some(l.number()),
|
ValueType::Lane(l) => Some(l.number()),
|
||||||
|
ValueType::Reference(r) => Some(r.number()),
|
||||||
ValueType::Special(s) => Some(s.number()),
|
ValueType::Special(s) => Some(s.number()),
|
||||||
ValueType::Vector(ref v) => Some(v.number()),
|
ValueType::Vector(ref v) => Some(v.number()),
|
||||||
}
|
}
|
||||||
@@ -112,6 +122,7 @@ impl fmt::Display for ValueType {
|
|||||||
match *self {
|
match *self {
|
||||||
ValueType::BV(ref b) => b.fmt(f),
|
ValueType::BV(ref b) => b.fmt(f),
|
||||||
ValueType::Lane(l) => l.fmt(f),
|
ValueType::Lane(l) => l.fmt(f),
|
||||||
|
ValueType::Reference(r) => r.fmt(f),
|
||||||
ValueType::Special(s) => s.fmt(f),
|
ValueType::Special(s) => s.fmt(f),
|
||||||
ValueType::Vector(ref v) => v.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.
|
/// Create a ValueType from a given special type.
|
||||||
impl From<SpecialType> for ValueType {
|
impl From<SpecialType> for ValueType {
|
||||||
fn from(spec: SpecialType) -> Self {
|
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::ops;
|
||||||
use std::rc::Rc;
|
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_LANES: u16 = 256;
|
||||||
const MAX_BITS: u16 = 64;
|
const MAX_BITS: u16 = 64;
|
||||||
@@ -64,6 +64,10 @@ impl TypeVar {
|
|||||||
ValueType::Special(special_type) => {
|
ValueType::Special(special_type) => {
|
||||||
return TypeVar::new(name, doc, builder.specials(vec![special_type]).build());
|
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::Lane(lane_type) => (lane_type, 1),
|
||||||
ValueType::Vector(vec_type) => {
|
ValueType::Vector(vec_type) => {
|
||||||
(vec_type.lane_type(), vec_type.lane_count() as RangeBound)
|
(vec_type.lane_type(), vec_type.lane_count() as RangeBound)
|
||||||
@@ -406,6 +410,7 @@ pub struct TypeSet {
|
|||||||
pub ints: NumSet,
|
pub ints: NumSet,
|
||||||
pub floats: NumSet,
|
pub floats: NumSet,
|
||||||
pub bools: NumSet,
|
pub bools: NumSet,
|
||||||
|
pub refs: NumSet,
|
||||||
pub bitvecs: NumSet,
|
pub bitvecs: NumSet,
|
||||||
pub specials: Vec<SpecialType>,
|
pub specials: Vec<SpecialType>,
|
||||||
}
|
}
|
||||||
@@ -416,6 +421,7 @@ impl TypeSet {
|
|||||||
ints: NumSet,
|
ints: NumSet,
|
||||||
floats: NumSet,
|
floats: NumSet,
|
||||||
bools: NumSet,
|
bools: NumSet,
|
||||||
|
refs: NumSet,
|
||||||
bitvecs: NumSet,
|
bitvecs: NumSet,
|
||||||
specials: Vec<SpecialType>,
|
specials: Vec<SpecialType>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -424,6 +430,7 @@ impl TypeSet {
|
|||||||
ints,
|
ints,
|
||||||
floats,
|
floats,
|
||||||
bools,
|
bools,
|
||||||
|
refs,
|
||||||
bitvecs,
|
bitvecs,
|
||||||
specials,
|
specials,
|
||||||
}
|
}
|
||||||
@@ -432,7 +439,11 @@ impl TypeSet {
|
|||||||
/// Return the number of concrete types represented by this typeset.
|
/// Return the number of concrete types represented by this typeset.
|
||||||
pub fn size(&self) -> usize {
|
pub fn size(&self) -> usize {
|
||||||
self.lanes.len()
|
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()
|
+ self.specials.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +473,7 @@ impl TypeSet {
|
|||||||
let mut copy = self.clone();
|
let mut copy = self.clone();
|
||||||
copy.ints = NumSet::new();
|
copy.ints = NumSet::new();
|
||||||
copy.floats = NumSet::new();
|
copy.floats = NumSet::new();
|
||||||
|
copy.refs = NumSet::new();
|
||||||
copy.bitvecs = NumSet::new();
|
copy.bitvecs = NumSet::new();
|
||||||
if (&self.lanes - &num_set![1]).len() > 0 {
|
if (&self.lanes - &num_set![1]).len() > 0 {
|
||||||
copy.bools = &self.ints | &self.floats;
|
copy.bools = &self.ints | &self.floats;
|
||||||
@@ -544,6 +556,7 @@ impl TypeSet {
|
|||||||
copy.ints = NumSet::new();
|
copy.ints = NumSet::new();
|
||||||
copy.bools = NumSet::new();
|
copy.bools = NumSet::new();
|
||||||
copy.floats = NumSet::new();
|
copy.floats = NumSet::new();
|
||||||
|
copy.refs = NumSet::new();
|
||||||
copy.bitvecs = self
|
copy.bitvecs = self
|
||||||
.lanes
|
.lanes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -568,6 +581,9 @@ impl TypeSet {
|
|||||||
for &bits in &self.bools {
|
for &bits in &self.bools {
|
||||||
ret.push(LaneType::bool_from_bits(bits).by(num_lanes));
|
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 {
|
for &bits in &self.bitvecs {
|
||||||
assert_eq!(num_lanes, 1);
|
assert_eq!(num_lanes, 1);
|
||||||
ret.push(BVType::new(bits).into());
|
ret.push(BVType::new(bits).into());
|
||||||
@@ -630,6 +646,7 @@ impl TypeSet {
|
|||||||
let mut ints = range_to_set(Some(8..MAX_BITS));
|
let mut ints = range_to_set(Some(8..MAX_BITS));
|
||||||
let mut floats = range_to_set(Some(32..64));
|
let mut floats = range_to_set(Some(32..64));
|
||||||
let mut bools = range_to_set(Some(1..MAX_BITS));
|
let mut bools = range_to_set(Some(1..MAX_BITS));
|
||||||
|
let refs = range_to_set(Some(32..64));
|
||||||
|
|
||||||
for &l in &all_lanes {
|
for &l in &all_lanes {
|
||||||
for &i in &all_ints {
|
for &i in &all_ints {
|
||||||
@@ -654,7 +671,7 @@ impl TypeSet {
|
|||||||
|
|
||||||
let bitvecs = NumSet::new();
|
let bitvecs = NumSet::new();
|
||||||
let specials = Vec::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.ints = &self.ints & &other.ints;
|
||||||
self.floats = &self.floats & &other.floats;
|
self.floats = &self.floats & &other.floats;
|
||||||
self.bools = &self.bools & &other.bools;
|
self.bools = &self.bools & &other.bools;
|
||||||
|
self.refs = &self.refs & &other.refs;
|
||||||
self.bitvecs = &self.bitvecs & &other.bitvecs;
|
self.bitvecs = &self.bitvecs & &other.bitvecs;
|
||||||
|
|
||||||
let mut new_specials = Vec::new();
|
let mut new_specials = Vec::new();
|
||||||
@@ -680,6 +698,7 @@ impl TypeSet {
|
|||||||
&& self.ints.is_subset(&other.ints)
|
&& self.ints.is_subset(&other.ints)
|
||||||
&& self.floats.is_subset(&other.floats)
|
&& self.floats.is_subset(&other.floats)
|
||||||
&& self.bools.is_subset(&other.bools)
|
&& self.bools.is_subset(&other.bools)
|
||||||
|
&& self.refs.is_subset(&other.refs)
|
||||||
&& self.bitvecs.is_subset(&other.bitvecs)
|
&& self.bitvecs.is_subset(&other.bitvecs)
|
||||||
&& {
|
&& {
|
||||||
let specials: HashSet<SpecialType> = HashSet::from_iter(self.specials.clone());
|
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.ints, &other.ints)
|
||||||
&& set_wider_or_equal(&self.floats, &other.floats)
|
&& set_wider_or_equal(&self.floats, &other.floats)
|
||||||
&& set_wider_or_equal(&self.bools, &other.bools)
|
&& set_wider_or_equal(&self.bools, &other.bools)
|
||||||
|
&& set_wider_or_equal(&self.refs, &other.refs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_narrower(&self, other: &TypeSet) -> bool {
|
pub fn is_narrower(&self, other: &TypeSet) -> bool {
|
||||||
set_narrower(&self.ints, &other.ints)
|
set_narrower(&self.ints, &other.ints)
|
||||||
&& set_narrower(&self.floats, &other.floats)
|
&& set_narrower(&self.floats, &other.floats)
|
||||||
&& set_narrower(&self.bools, &other.bools)
|
&& 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(", ")
|
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() {
|
if !self.bitvecs.is_empty() {
|
||||||
subsets.push(format!(
|
subsets.push(format!(
|
||||||
"bitvecs={{{}}}",
|
"bitvecs={{{}}}",
|
||||||
@@ -760,6 +787,7 @@ pub struct TypeSetBuilder {
|
|||||||
ints: Interval,
|
ints: Interval,
|
||||||
floats: Interval,
|
floats: Interval,
|
||||||
bools: Interval,
|
bools: Interval,
|
||||||
|
refs: Interval,
|
||||||
bitvecs: Interval,
|
bitvecs: Interval,
|
||||||
includes_scalars: bool,
|
includes_scalars: bool,
|
||||||
simd_lanes: Interval,
|
simd_lanes: Interval,
|
||||||
@@ -772,6 +800,7 @@ impl TypeSetBuilder {
|
|||||||
ints: Interval::None,
|
ints: Interval::None,
|
||||||
floats: Interval::None,
|
floats: Interval::None,
|
||||||
bools: Interval::None,
|
bools: Interval::None,
|
||||||
|
refs: Interval::None,
|
||||||
bitvecs: Interval::None,
|
bitvecs: Interval::None,
|
||||||
includes_scalars: true,
|
includes_scalars: true,
|
||||||
simd_lanes: Interval::None,
|
simd_lanes: Interval::None,
|
||||||
@@ -794,6 +823,11 @@ impl TypeSetBuilder {
|
|||||||
self.bools = interval.into();
|
self.bools = interval.into();
|
||||||
self
|
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 {
|
pub fn includes_scalars(mut self, includes_scalars: bool) -> Self {
|
||||||
self.includes_scalars = includes_scalars;
|
self.includes_scalars = includes_scalars;
|
||||||
self
|
self
|
||||||
@@ -827,6 +861,7 @@ impl TypeSetBuilder {
|
|||||||
range_to_set(self.ints.to_range(8..MAX_BITS, None)),
|
range_to_set(self.ints.to_range(8..MAX_BITS, None)),
|
||||||
range_to_set(self.floats.to_range(32..64, None)),
|
range_to_set(self.floats.to_range(32..64, None)),
|
||||||
bools,
|
bools,
|
||||||
|
range_to_set(self.refs.to_range(32..64, None)),
|
||||||
range_to_set(self.bitvecs.to_range(1..MAX_BITVEC, None)),
|
range_to_set(self.bitvecs.to_range(1..MAX_BITVEC, None)),
|
||||||
self.specials,
|
self.specials,
|
||||||
)
|
)
|
||||||
@@ -837,6 +872,7 @@ impl TypeSetBuilder {
|
|||||||
.ints(Interval::All)
|
.ints(Interval::All)
|
||||||
.floats(Interval::All)
|
.floats(Interval::All)
|
||||||
.bools(Interval::All)
|
.bools(Interval::All)
|
||||||
|
.refs(Interval::All)
|
||||||
.simd_lanes(Interval::All)
|
.simd_lanes(Interval::All)
|
||||||
.bitvecs(Interval::All)
|
.bitvecs(Interval::All)
|
||||||
.specials(ValueType::all_special_types().collect())
|
.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 inst_format = formats.get(recipe.format);
|
||||||
let num_value_ops = inst_format.num_value_operands;
|
let num_value_ops = inst_format.num_value_operands;
|
||||||
|
|
||||||
let want_args = recipe.operands_in.iter().any(|c| match c {
|
// TODO: Set want_args to true for only MultiAry instructions instead of all formats with value list.
|
||||||
OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
|
let want_args = inst_format.has_value_list
|
||||||
OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
|
|| 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);
|
assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);
|
||||||
|
|
||||||
let want_outs = recipe.operands_out.iter().any(|c| match c {
|
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("inst: Inst,");
|
||||||
fmt.line("_divert: &mut RegDiversions,");
|
fmt.line("_divert: &mut RegDiversions,");
|
||||||
fmt.line("_sink: &mut CS,");
|
fmt.line("_sink: &mut CS,");
|
||||||
|
fmt.line("_isa: &dyn TargetIsa,");
|
||||||
});
|
});
|
||||||
fmt.line(") {");
|
fmt.line(") {");
|
||||||
fmt.indent(|fmt| {
|
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("inst: Inst,");
|
||||||
fmt.line("divert: &mut RegDiversions,");
|
fmt.line("divert: &mut RegDiversions,");
|
||||||
fmt.line("sink: &mut CS,");
|
fmt.line("sink: &mut CS,");
|
||||||
|
fmt.line("isa: &dyn TargetIsa,")
|
||||||
});
|
});
|
||||||
|
|
||||||
fmt.line(") {");
|
fmt.line(") {");
|
||||||
|
|||||||
@@ -650,6 +650,9 @@ fn typeset_to_string(ts: &TypeSet) -> String {
|
|||||||
if ts.specials.len() > 0 {
|
if ts.specials.len() > 0 {
|
||||||
result += &format!(", specials=[{}]", iterable_to_string(&ts.specials));
|
result += &format!(", specials=[{}]", iterable_to_string(&ts.specials));
|
||||||
}
|
}
|
||||||
|
if ts.refs.len() > 0 {
|
||||||
|
result += &format!(", refs={}", iterable_to_string(&ts.refs));
|
||||||
|
}
|
||||||
result += ")";
|
result += ")";
|
||||||
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.ints, "ints", 8, fmt);
|
||||||
gen_bitset(&ts.floats, "floats", 8, fmt);
|
gen_bitset(&ts.floats, "floats", 8, fmt);
|
||||||
gen_bitset(&ts.bools, "bools", 8, fmt);
|
gen_bitset(&ts.bools, "bools", 8, fmt);
|
||||||
|
gen_bitset(&ts.refs, "refs", 8, fmt);
|
||||||
});
|
});
|
||||||
fmt.line("},");
|
fmt.line("},");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ fn emit_types(fmt: &mut srcgen::Formatter) -> Result<(), error::Error> {
|
|||||||
emit_type(&ty, fmt)?;
|
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.
|
// Emit vector definitions for common SIMD sizes.
|
||||||
for vec_size in &[64_u64, 128, 256, 512] {
|
for vec_size in &[64_u64, 128, 256, 512] {
|
||||||
emit_vectors(*vec_size, fmt)?;
|
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::Bool::{B1, B16, B32, B64, B8};
|
||||||
use crate::shared::types::Float::{F32, F64};
|
use crate::shared::types::Float::{F32, F64};
|
||||||
use crate::shared::types::Int::{I16, I32, I64, I8};
|
use crate::shared::types::Int::{I16, I32, I64, I8};
|
||||||
|
use crate::shared::types::Reference::{R32, R64};
|
||||||
use crate::shared::Definitions as SharedDefinitions;
|
use crate::shared::Definitions as SharedDefinitions;
|
||||||
|
|
||||||
use super::recipes::{RecipeGroup, Template};
|
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.
|
/// 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) {
|
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.
|
// 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 ireduce = shared.by_name("ireduce");
|
||||||
let ishl = shared.by_name("ishl");
|
let ishl = shared.by_name("ishl");
|
||||||
let ishl_imm = shared.by_name("ishl_imm");
|
let ishl_imm = shared.by_name("ishl_imm");
|
||||||
|
let is_null = shared.by_name("is_null");
|
||||||
let istore16 = shared.by_name("istore16");
|
let istore16 = shared.by_name("istore16");
|
||||||
let istore16_complex = shared.by_name("istore16_complex");
|
let istore16_complex = shared.by_name("istore16_complex");
|
||||||
let istore32 = shared.by_name("istore32");
|
let istore32 = shared.by_name("istore32");
|
||||||
@@ -344,6 +360,7 @@ pub fn define(
|
|||||||
let load = shared.by_name("load");
|
let load = shared.by_name("load");
|
||||||
let load_complex = shared.by_name("load_complex");
|
let load_complex = shared.by_name("load_complex");
|
||||||
let nearest = shared.by_name("nearest");
|
let nearest = shared.by_name("nearest");
|
||||||
|
let null = shared.by_name("null");
|
||||||
let popcnt = shared.by_name("popcnt");
|
let popcnt = shared.by_name("popcnt");
|
||||||
let raw_bitcast = shared.by_name("raw_bitcast");
|
let raw_bitcast = shared.by_name("raw_bitcast");
|
||||||
let regfill = shared.by_name("regfill");
|
let regfill = shared.by_name("regfill");
|
||||||
@@ -354,6 +371,7 @@ pub fn define(
|
|||||||
let rotl_imm = shared.by_name("rotl_imm");
|
let rotl_imm = shared.by_name("rotl_imm");
|
||||||
let rotr = shared.by_name("rotr");
|
let rotr = shared.by_name("rotr");
|
||||||
let rotr_imm = shared.by_name("rotr_imm");
|
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 scalar_to_vector = shared.by_name("scalar_to_vector");
|
||||||
let selectif = shared.by_name("selectif");
|
let selectif = shared.by_name("selectif");
|
||||||
let sextend = shared.by_name("sextend");
|
let sextend = shared.by_name("sextend");
|
||||||
@@ -374,6 +392,7 @@ pub fn define(
|
|||||||
let trap = shared.by_name("trap");
|
let trap = shared.by_name("trap");
|
||||||
let trapff = shared.by_name("trapff");
|
let trapff = shared.by_name("trapff");
|
||||||
let trapif = shared.by_name("trapif");
|
let trapif = shared.by_name("trapif");
|
||||||
|
let resumable_trap = shared.by_name("resumable_trap");
|
||||||
let trueff = shared.by_name("trueff");
|
let trueff = shared.by_name("trueff");
|
||||||
let trueif = shared.by_name("trueif");
|
let trueif = shared.by_name("trueif");
|
||||||
let trunc = shared.by_name("trunc");
|
let trunc = shared.by_name("trunc");
|
||||||
@@ -455,6 +474,7 @@ pub fn define(
|
|||||||
let rec_icscc_ib = r.template("icscc_ib");
|
let rec_icscc_ib = r.template("icscc_ib");
|
||||||
let rec_icscc_id = r.template("icscc_id");
|
let rec_icscc_id = r.template("icscc_id");
|
||||||
let rec_indirect_jmp = r.template("indirect_jmp");
|
let rec_indirect_jmp = r.template("indirect_jmp");
|
||||||
|
let rec_is_zero = r.template("is_zero");
|
||||||
let rec_jmpb = r.template("jmpb");
|
let rec_jmpb = r.template("jmpb");
|
||||||
let rec_jmpd = r.template("jmpd");
|
let rec_jmpd = r.template("jmpd");
|
||||||
let rec_jt_base = r.template("jt_base");
|
let rec_jt_base = r.template("jt_base");
|
||||||
@@ -473,6 +493,7 @@ pub fn define(
|
|||||||
let rec_popq = r.template("popq");
|
let rec_popq = r.template("popq");
|
||||||
let rec_pu_id = r.template("pu_id");
|
let rec_pu_id = r.template("pu_id");
|
||||||
let rec_pu_id_bool = r.template("pu_id_bool");
|
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_pu_iq = r.template("pu_iq");
|
||||||
let rec_pushq = r.template("pushq");
|
let rec_pushq = r.template("pushq");
|
||||||
let rec_ret = r.template("ret");
|
let rec_ret = r.template("ret");
|
||||||
@@ -492,6 +513,7 @@ pub fn define(
|
|||||||
let rec_rmov = r.template("rmov");
|
let rec_rmov = r.template("rmov");
|
||||||
let rec_rr = r.template("rr");
|
let rec_rr = r.template("rr");
|
||||||
let rec_rrx = r.template("rrx");
|
let rec_rrx = r.template("rrx");
|
||||||
|
let rec_safepoint = r.recipe("safepoint");
|
||||||
let rec_setf_abcd = r.template("setf_abcd");
|
let rec_setf_abcd = r.template("setf_abcd");
|
||||||
let rec_seti_abcd = r.template("seti_abcd");
|
let rec_seti_abcd = r.template("seti_abcd");
|
||||||
let rec_spaddr4_id = r.template("spaddr4_id");
|
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(x86_umulx, rec_mulx.opcodes(vec![0xf7]).rrr(4));
|
||||||
|
|
||||||
e.enc_i32_i64(copy, rec_umr.opcodes(vec![0x89]));
|
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(B1), rec_umr.opcodes(vec![0x89]));
|
||||||
e.enc_both(copy.bind(I8), 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]));
|
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.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(B1), rec_rmov.opcodes(vec![0x89]));
|
||||||
e.enc_both(regmove.bind(I8), 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_ib.opcodes(vec![0x83]).rrr(0));
|
||||||
e.enc_i32_i64(iadd_imm, rec_r_id.opcodes(vec![0x81]).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(spill, rec_spillSib32.opcodes(vec![0x89]));
|
||||||
e.enc_i32_i64(regspill, rec_regspill32.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
|
// Use a 32-bit write for spilling `b1`, `i8` and `i16` to avoid
|
||||||
// constraining the permitted registers.
|
// 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(fill, rec_fillSib32.opcodes(vec![0x8b]));
|
||||||
e.enc_i32_i64(regfill, rec_regfill32.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.
|
// Load 32 bits from `b1`, `i8` and `i16` spill slots. See `spill.b1` above.
|
||||||
|
|
||||||
@@ -1248,6 +1281,8 @@ pub fn define(
|
|||||||
// Trap as ud2
|
// Trap as ud2
|
||||||
e.enc32(trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
e.enc32(trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||||
e.enc64(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
|
// Debug trap as int3
|
||||||
e.enc32_rec(debugtrap, rec_debugtrap, 0);
|
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
|
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.
|
// XX+rd iq unary with 64-bit immediate.
|
||||||
recipes.add_template_recipe(
|
recipes.add_template_recipe(
|
||||||
EncodingRecipeBuilder::new("pu_iq", f_unary_imm, 8)
|
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
|
recipes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ pub fn define(
|
|||||||
TypeSetBuilder::new().ints(32..64).build(),
|
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(
|
let Testable = &TypeVar::new(
|
||||||
"Testable",
|
"Testable",
|
||||||
"A scalar boolean or integer type",
|
"A scalar boolean or integer type",
|
||||||
@@ -118,11 +124,12 @@ pub fn define(
|
|||||||
|
|
||||||
let Any = &TypeVar::new(
|
let Any = &TypeVar::new(
|
||||||
"Any",
|
"Any",
|
||||||
"Any integer, float, or boolean scalar or vector type",
|
"Any integer, float, boolean, or reference scalar or vector type",
|
||||||
TypeSetBuilder::new()
|
TypeSetBuilder::new()
|
||||||
.ints(Interval::All)
|
.ints(Interval::All)
|
||||||
.floats(Interval::All)
|
.floats(Interval::All)
|
||||||
.bools(Interval::All)
|
.bools(Interval::All)
|
||||||
|
.refs(Interval::All)
|
||||||
.simd_lanes(Interval::All)
|
.simd_lanes(Interval::All)
|
||||||
.includes_scalars(true)
|
.includes_scalars(true)
|
||||||
.build(),
|
.build(),
|
||||||
@@ -394,6 +401,19 @@ pub fn define(
|
|||||||
.can_trap(true),
|
.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(
|
ig.push(
|
||||||
Inst::new(
|
Inst::new(
|
||||||
"trapnz",
|
"trapnz",
|
||||||
@@ -1068,6 +1088,20 @@ pub fn define(
|
|||||||
.operands_out(vec![a]),
|
.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(
|
ig.push(Inst::new(
|
||||||
"nop",
|
"nop",
|
||||||
r#"
|
r#"
|
||||||
@@ -1315,6 +1349,24 @@ pub fn define(
|
|||||||
.other_side_effects(true),
|
.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 x = &operand_doc("x", TxN, "Vector to split");
|
||||||
let lo = &operand_doc("lo", &TxN.half_vector(), "Low-numbered lanes of `x`");
|
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`");
|
let hi = &operand_doc("hi", &TxN.half_vector(), "High-numbered lanes of `x`");
|
||||||
@@ -2578,6 +2630,23 @@ pub fn define(
|
|||||||
.operands_out(vec![a]),
|
.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 Cond = &operand("Cond", intcc);
|
||||||
let f = &operand("f", iflags);
|
let f = &operand("f", iflags);
|
||||||
let a = &operand("a", b1);
|
let a = &operand("a", b1);
|
||||||
|
|||||||
@@ -92,6 +92,18 @@ pub fn define() -> SettingGroup {
|
|||||||
true,
|
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 specific to the `baldrdash` calling convention.
|
||||||
|
|
||||||
settings.add_enum(
|
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)]
|
#[cfg(test)]
|
||||||
mod iter_tests {
|
mod iter_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -185,4 +217,12 @@ mod iter_tests {
|
|||||||
assert_eq!(flag_iter.next(), Some(Flag::FFlags));
|
assert_eq!(flag_iter.next(), Some(Flag::FFlags));
|
||||||
assert_eq!(flag_iter.next(), None);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@
|
|||||||
//! that a `MemoryCodeSink` will always write binary machine code to raw memory. It forwards any
|
//! that a `MemoryCodeSink` will always write binary machine code to raw memory. It forwards any
|
||||||
//! relocations to a `RelocSink` trait object. Relocations are less frequent than the
|
//! relocations to a `RelocSink` trait object. Relocations are less frequent than the
|
||||||
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.
|
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.
|
||||||
|
|
||||||
use super::{Addend, CodeInfo, CodeOffset, CodeSink, Reloc};
|
use super::{Addend, CodeInfo, CodeOffset, CodeSink, Reloc};
|
||||||
use crate::ir::{ExternalName, JumpTable, SourceLoc, TrapCode};
|
use crate::binemit::stackmap::Stackmap;
|
||||||
|
use crate::ir::entities::Value;
|
||||||
|
use crate::ir::{ExternalName, Function, JumpTable, SourceLoc, TrapCode};
|
||||||
|
use crate::isa::TargetIsa;
|
||||||
use core::ptr::write_unaligned;
|
use core::ptr::write_unaligned;
|
||||||
|
|
||||||
/// A `CodeSink` that writes binary machine code directly into memory.
|
/// A `CodeSink` that writes binary machine code directly into memory.
|
||||||
@@ -36,6 +38,7 @@ pub struct MemoryCodeSink<'a> {
|
|||||||
offset: isize,
|
offset: isize,
|
||||||
relocs: &'a mut dyn RelocSink,
|
relocs: &'a mut dyn RelocSink,
|
||||||
traps: &'a mut dyn TrapSink,
|
traps: &'a mut dyn TrapSink,
|
||||||
|
stackmaps: &'a mut dyn StackmapSink,
|
||||||
/// Information about the generated code and read-only data.
|
/// Information about the generated code and read-only data.
|
||||||
pub info: CodeInfo,
|
pub info: CodeInfo,
|
||||||
}
|
}
|
||||||
@@ -49,6 +52,7 @@ impl<'a> MemoryCodeSink<'a> {
|
|||||||
data: *mut u8,
|
data: *mut u8,
|
||||||
relocs: &'a mut dyn RelocSink,
|
relocs: &'a mut dyn RelocSink,
|
||||||
traps: &'a mut dyn TrapSink,
|
traps: &'a mut dyn TrapSink,
|
||||||
|
stackmaps: &'a mut dyn StackmapSink,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
@@ -61,6 +65,7 @@ impl<'a> MemoryCodeSink<'a> {
|
|||||||
},
|
},
|
||||||
relocs,
|
relocs,
|
||||||
traps,
|
traps,
|
||||||
|
stackmaps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,6 +154,12 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
|
|||||||
self.info.rodata_size = self.offset() - (self.info.jumptables_size + self.info.code_size);
|
self.info.rodata_size = self.offset() - (self.info.jumptables_size + self.info.code_size);
|
||||||
self.info.total_size = self.offset();
|
self.info.total_size = self.offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_stackmap(&mut self, val_list: &[Value], func: &Function, isa: &dyn TargetIsa) {
|
||||||
|
let ofs = self.offset();
|
||||||
|
let stackmap = Stackmap::from_values(&val_list, func, isa);
|
||||||
|
self.stackmaps.add_stackmap(ofs, stackmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `TrapSink` implementation that does nothing, which is convenient when
|
/// A `TrapSink` implementation that does nothing, which is convenient when
|
||||||
@@ -158,3 +169,16 @@ pub struct NullTrapSink {}
|
|||||||
impl TrapSink for NullTrapSink {
|
impl TrapSink for NullTrapSink {
|
||||||
fn trap(&mut self, _offset: CodeOffset, _srcloc: SourceLoc, _code: TrapCode) {}
|
fn trap(&mut self, _offset: CodeOffset, _srcloc: SourceLoc, _code: TrapCode) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait for emitting stackmaps.
|
||||||
|
pub trait StackmapSink {
|
||||||
|
/// Output a bitmap of the stack representing the live reference variables at this code offset.
|
||||||
|
fn add_stackmap(&mut self, _: CodeOffset, _: Stackmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Placeholder StackmapSink that does nothing.
|
||||||
|
pub struct NullStackmapSink {}
|
||||||
|
|
||||||
|
impl StackmapSink for NullStackmapSink {
|
||||||
|
fn add_stackmap(&mut self, _: CodeOffset, _: Stackmap) {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,13 +6,18 @@
|
|||||||
mod memorysink;
|
mod memorysink;
|
||||||
mod relaxation;
|
mod relaxation;
|
||||||
mod shrink;
|
mod shrink;
|
||||||
|
mod stackmap;
|
||||||
|
|
||||||
pub use self::memorysink::{MemoryCodeSink, NullTrapSink, RelocSink, TrapSink};
|
pub use self::memorysink::{
|
||||||
|
MemoryCodeSink, NullStackmapSink, NullTrapSink, RelocSink, StackmapSink, TrapSink,
|
||||||
|
};
|
||||||
pub use self::relaxation::relax_branches;
|
pub use self::relaxation::relax_branches;
|
||||||
pub use self::shrink::shrink_instructions;
|
pub use self::shrink::shrink_instructions;
|
||||||
pub use crate::regalloc::RegDiversions;
|
pub use self::stackmap::Stackmap;
|
||||||
|
use crate::ir::entities::Value;
|
||||||
use crate::ir::{ExternalName, Function, Inst, JumpTable, SourceLoc, TrapCode};
|
use crate::ir::{ExternalName, Function, Inst, JumpTable, SourceLoc, TrapCode};
|
||||||
|
use crate::isa::TargetIsa;
|
||||||
|
pub use crate::regalloc::RegDiversions;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
#[cfg(feature = "enable-serde")]
|
#[cfg(feature = "enable-serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -141,6 +146,9 @@ pub trait CodeSink {
|
|||||||
|
|
||||||
/// Read-only data output is complete, we're done.
|
/// Read-only data output is complete, we're done.
|
||||||
fn end_codegen(&mut self);
|
fn end_codegen(&mut self);
|
||||||
|
|
||||||
|
/// Add a stackmap at the current code offset.
|
||||||
|
fn add_stackmap(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report a bad encoding error.
|
/// Report a bad encoding error.
|
||||||
@@ -157,17 +165,17 @@ pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
|
|||||||
///
|
///
|
||||||
/// This function is called from the `TargetIsa::emit_function()` implementations with the
|
/// This function is called from the `TargetIsa::emit_function()` implementations with the
|
||||||
/// appropriate instruction emitter.
|
/// appropriate instruction emitter.
|
||||||
pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS)
|
pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS, isa: &dyn TargetIsa)
|
||||||
where
|
where
|
||||||
CS: CodeSink,
|
CS: CodeSink,
|
||||||
EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS),
|
EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS, &dyn TargetIsa),
|
||||||
{
|
{
|
||||||
let mut divert = RegDiversions::new();
|
let mut divert = RegDiversions::new();
|
||||||
for ebb in func.layout.ebbs() {
|
for ebb in func.layout.ebbs() {
|
||||||
divert.clear();
|
divert.clear();
|
||||||
debug_assert_eq!(func.offsets[ebb], sink.offset());
|
debug_assert_eq!(func.offsets[ebb], sink.offset());
|
||||||
for inst in func.layout.ebb_insts(ebb) {
|
for inst in func.layout.ebb_insts(ebb) {
|
||||||
emit_inst(func, inst, &mut divert, sink);
|
emit_inst(func, inst, &mut divert, sink, isa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
122
cranelift/codegen/src/binemit/stackmap.rs
Normal file
122
cranelift/codegen/src/binemit/stackmap.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
use crate::bitset::BitSet;
|
||||||
|
use crate::ir;
|
||||||
|
use crate::isa::TargetIsa;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
/// Wrapper class for longer bit vectors that cannot be represented by a single BitSet.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Stackmap {
|
||||||
|
bitmap: Vec<BitSet<u32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stackmap {
|
||||||
|
/// Create a stackmap based on where references are located on a function's stack.
|
||||||
|
pub fn from_values(
|
||||||
|
args: &[ir::entities::Value],
|
||||||
|
func: &ir::Function,
|
||||||
|
isa: &dyn TargetIsa,
|
||||||
|
) -> Self {
|
||||||
|
let loc = &func.locations;
|
||||||
|
let mut live_ref_in_stack_slot = std::collections::HashSet::new();
|
||||||
|
// References can be in registers, and live registers values are pushed onto the stack before calls and traps.
|
||||||
|
// TODO: Implement register maps. If a register containing a reference is spilled and reused after a safepoint,
|
||||||
|
// it could contain a stale reference value if the garbage collector relocated the value.
|
||||||
|
for val in args {
|
||||||
|
if let Some(value_loc) = loc.get(*val) {
|
||||||
|
match *value_loc {
|
||||||
|
ir::ValueLoc::Stack(stack_slot) => live_ref_in_stack_slot.insert(stack_slot),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpiderMonkey stackmap structure:
|
||||||
|
// <trap reg dump> + <general spill> + <frame> + <inbound args>
|
||||||
|
// Bit vector goes from lower addresses to higher addresses.
|
||||||
|
|
||||||
|
// TODO: Get trap register layout from Spidermonkey and prepend to bitvector below.
|
||||||
|
let stack = &func.stack_slots;
|
||||||
|
let frame_size = stack.frame_size.unwrap();
|
||||||
|
let word_size = ir::stackslot::StackSize::from(isa.pointer_bytes());
|
||||||
|
let num_words = (frame_size / word_size) as usize;
|
||||||
|
let mut vec = std::vec::Vec::with_capacity(num_words);
|
||||||
|
|
||||||
|
vec.resize(num_words, false);
|
||||||
|
|
||||||
|
// Frame (includes spills and inbound args).
|
||||||
|
for (ss, ssd) in stack.iter() {
|
||||||
|
if live_ref_in_stack_slot.contains(&ss) {
|
||||||
|
// Assumption: greater magnitude of offset imply higher address.
|
||||||
|
let index = (((ssd.offset.unwrap().abs() as u32) - ssd.size) / word_size) as usize;
|
||||||
|
vec[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Stackmap::from_vec(&vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a vec of Bitsets from a vec of bools.
|
||||||
|
pub fn from_vec(vec: &Vec<bool>) -> Self {
|
||||||
|
let mut rem = vec.len();
|
||||||
|
let num_word = ((rem as f32) / 32.0).ceil() as usize;
|
||||||
|
let mut bitmap = Vec::with_capacity(num_word);
|
||||||
|
|
||||||
|
for i in 0..num_word {
|
||||||
|
let mut curr_word = 0;
|
||||||
|
let count = if rem > 32 { 32 } else { rem };
|
||||||
|
for j in 0..count {
|
||||||
|
if vec[i * 32 + j] {
|
||||||
|
curr_word |= 1 << j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitmap.push(BitSet::<u32>(curr_word));
|
||||||
|
rem -= count;
|
||||||
|
}
|
||||||
|
Self { bitmap }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a specified bit.
|
||||||
|
pub fn get_bit(&self, bit_index: usize) -> bool {
|
||||||
|
assert!(bit_index < 32 * self.bitmap.len());
|
||||||
|
let word_index = bit_index / 32;
|
||||||
|
let word_offset = (bit_index % 32) as u8;
|
||||||
|
self.bitmap[word_index].contains(word_offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stackmaps() {
|
||||||
|
let vec: Vec<bool> = Vec::new();
|
||||||
|
assert!(Stackmap::from_vec(&vec).bitmap.is_empty());
|
||||||
|
|
||||||
|
let mut vec: [bool; 32] = Default::default();
|
||||||
|
let set_true_idx = [5, 7, 24, 31];
|
||||||
|
|
||||||
|
for idx in set_true_idx.iter() {
|
||||||
|
vec[*idx] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vec = vec.to_vec();
|
||||||
|
assert_eq!(
|
||||||
|
vec![BitSet::<u32>(2164261024)],
|
||||||
|
Stackmap::from_vec(&vec).bitmap
|
||||||
|
);
|
||||||
|
|
||||||
|
vec.push(false);
|
||||||
|
vec.push(true);
|
||||||
|
let res = Stackmap::from_vec(&vec);
|
||||||
|
assert_eq!(
|
||||||
|
vec![BitSet::<u32>(2164261024), BitSet::<u32>(2)],
|
||||||
|
res.bitmap
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(res.get_bit(5));
|
||||||
|
assert!(res.get_bit(31));
|
||||||
|
assert!(res.get_bit(33));
|
||||||
|
assert!(!res.get_bit(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,8 @@
|
|||||||
//! single ISA instance.
|
//! single ISA instance.
|
||||||
|
|
||||||
use crate::binemit::{
|
use crate::binemit::{
|
||||||
relax_branches, shrink_instructions, CodeInfo, MemoryCodeSink, RelocSink, TrapSink,
|
relax_branches, shrink_instructions, CodeInfo, MemoryCodeSink, RelocSink, StackmapSink,
|
||||||
|
TrapSink,
|
||||||
};
|
};
|
||||||
use crate::dce::do_dce;
|
use crate::dce::do_dce;
|
||||||
use crate::dominator_tree::DominatorTree;
|
use crate::dominator_tree::DominatorTree;
|
||||||
@@ -100,12 +101,14 @@ impl Context {
|
|||||||
mem: &mut Vec<u8>,
|
mem: &mut Vec<u8>,
|
||||||
relocs: &mut dyn RelocSink,
|
relocs: &mut dyn RelocSink,
|
||||||
traps: &mut dyn TrapSink,
|
traps: &mut dyn TrapSink,
|
||||||
|
stackmaps: &mut dyn StackmapSink,
|
||||||
) -> CodegenResult<CodeInfo> {
|
) -> CodegenResult<CodeInfo> {
|
||||||
let info = self.compile(isa)?;
|
let info = self.compile(isa)?;
|
||||||
let old_len = mem.len();
|
let old_len = mem.len();
|
||||||
mem.resize(old_len + info.total_size as usize, 0);
|
mem.resize(old_len + info.total_size as usize, 0);
|
||||||
let new_info =
|
let new_info = unsafe {
|
||||||
unsafe { self.emit_to_memory(isa, mem.as_mut_ptr().add(old_len), relocs, traps) };
|
self.emit_to_memory(isa, mem.as_mut_ptr().add(old_len), relocs, traps, stackmaps)
|
||||||
|
};
|
||||||
debug_assert!(new_info == info);
|
debug_assert!(new_info == info);
|
||||||
Ok(info)
|
Ok(info)
|
||||||
}
|
}
|
||||||
@@ -168,9 +171,10 @@ impl Context {
|
|||||||
mem: *mut u8,
|
mem: *mut u8,
|
||||||
relocs: &mut dyn RelocSink,
|
relocs: &mut dyn RelocSink,
|
||||||
traps: &mut dyn TrapSink,
|
traps: &mut dyn TrapSink,
|
||||||
|
stackmaps: &mut dyn StackmapSink,
|
||||||
) -> CodeInfo {
|
) -> CodeInfo {
|
||||||
let _tt = timing::binemit();
|
let _tt = timing::binemit();
|
||||||
let mut sink = MemoryCodeSink::new(mem, relocs, traps);
|
let mut sink = MemoryCodeSink::new(mem, relocs, traps, stackmaps);
|
||||||
isa.emit_function_to_memory(&self.func, &mut sink);
|
isa.emit_function_to_memory(&self.func, &mut sink);
|
||||||
sink.info
|
sink.info
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,6 +444,8 @@ pub struct ValueTypeSet {
|
|||||||
pub floats: BitSet8,
|
pub floats: BitSet8,
|
||||||
/// Allowed bool widths
|
/// Allowed bool widths
|
||||||
pub bools: BitSet8,
|
pub bools: BitSet8,
|
||||||
|
/// Allowed ref widths
|
||||||
|
pub refs: BitSet8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueTypeSet {
|
impl ValueTypeSet {
|
||||||
@@ -458,6 +460,8 @@ impl ValueTypeSet {
|
|||||||
self.floats.contains(l2b)
|
self.floats.contains(l2b)
|
||||||
} else if scalar.is_bool() {
|
} else if scalar.is_bool() {
|
||||||
self.bools.contains(l2b)
|
self.bools.contains(l2b)
|
||||||
|
} else if scalar.is_ref() {
|
||||||
|
self.refs.contains(l2b)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -652,6 +656,7 @@ mod tests {
|
|||||||
ints: BitSet8::from_range(4, 7),
|
ints: BitSet8::from_range(4, 7),
|
||||||
floats: BitSet8::from_range(0, 0),
|
floats: BitSet8::from_range(0, 0),
|
||||||
bools: BitSet8::from_range(3, 7),
|
bools: BitSet8::from_range(3, 7),
|
||||||
|
refs: BitSet8::from_range(5, 7),
|
||||||
};
|
};
|
||||||
assert!(!vts.contains(I8));
|
assert!(!vts.contains(I8));
|
||||||
assert!(vts.contains(I32));
|
assert!(vts.contains(I32));
|
||||||
@@ -661,6 +666,8 @@ mod tests {
|
|||||||
assert!(!vts.contains(B1));
|
assert!(!vts.contains(B1));
|
||||||
assert!(vts.contains(B8));
|
assert!(vts.contains(B8));
|
||||||
assert!(vts.contains(B64));
|
assert!(vts.contains(B64));
|
||||||
|
assert!(vts.contains(R32));
|
||||||
|
assert!(vts.contains(R64));
|
||||||
assert_eq!(vts.example().to_string(), "i32");
|
assert_eq!(vts.example().to_string(), "i32");
|
||||||
|
|
||||||
let vts = ValueTypeSet {
|
let vts = ValueTypeSet {
|
||||||
@@ -668,6 +675,7 @@ mod tests {
|
|||||||
ints: BitSet8::from_range(0, 0),
|
ints: BitSet8::from_range(0, 0),
|
||||||
floats: BitSet8::from_range(5, 7),
|
floats: BitSet8::from_range(5, 7),
|
||||||
bools: BitSet8::from_range(3, 7),
|
bools: BitSet8::from_range(3, 7),
|
||||||
|
refs: BitSet8::from_range(0, 0),
|
||||||
};
|
};
|
||||||
assert_eq!(vts.example().to_string(), "f32");
|
assert_eq!(vts.example().to_string(), "f32");
|
||||||
|
|
||||||
@@ -676,6 +684,7 @@ mod tests {
|
|||||||
ints: BitSet8::from_range(0, 0),
|
ints: BitSet8::from_range(0, 0),
|
||||||
floats: BitSet8::from_range(5, 7),
|
floats: BitSet8::from_range(5, 7),
|
||||||
bools: BitSet8::from_range(3, 7),
|
bools: BitSet8::from_range(3, 7),
|
||||||
|
refs: BitSet8::from_range(0, 0),
|
||||||
};
|
};
|
||||||
assert_eq!(vts.example().to_string(), "f32x2");
|
assert_eq!(vts.example().to_string(), "f32x2");
|
||||||
|
|
||||||
@@ -684,6 +693,7 @@ mod tests {
|
|||||||
ints: BitSet8::from_range(0, 0),
|
ints: BitSet8::from_range(0, 0),
|
||||||
floats: BitSet8::from_range(0, 0),
|
floats: BitSet8::from_range(0, 0),
|
||||||
bools: BitSet8::from_range(3, 7),
|
bools: BitSet8::from_range(3, 7),
|
||||||
|
refs: BitSet8::from_range(0, 0),
|
||||||
};
|
};
|
||||||
assert!(!vts.contains(B32X2));
|
assert!(!vts.contains(B32X2));
|
||||||
assert!(vts.contains(B32X4));
|
assert!(vts.contains(B32X4));
|
||||||
@@ -695,8 +705,11 @@ mod tests {
|
|||||||
ints: BitSet8::from_range(3, 7),
|
ints: BitSet8::from_range(3, 7),
|
||||||
floats: BitSet8::from_range(0, 0),
|
floats: BitSet8::from_range(0, 0),
|
||||||
bools: BitSet8::from_range(0, 0),
|
bools: BitSet8::from_range(0, 0),
|
||||||
|
refs: BitSet8::from_range(0, 0),
|
||||||
};
|
};
|
||||||
assert!(vts.contains(I32));
|
assert!(vts.contains(I32));
|
||||||
assert!(vts.contains(I32X4));
|
assert!(vts.contains(I32X4));
|
||||||
|
assert!(!vts.contains(R32));
|
||||||
|
assert!(!vts.contains(R64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ impl Type {
|
|||||||
B1 => 0,
|
B1 => 0,
|
||||||
B8 | I8 => 3,
|
B8 | I8 => 3,
|
||||||
B16 | I16 => 4,
|
B16 | I16 => 4,
|
||||||
B32 | I32 | F32 => 5,
|
B32 | I32 | F32 | R32 => 5,
|
||||||
B64 | I64 | F64 => 6,
|
B64 | I64 | F64 | R64 => 6,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,8 +73,8 @@ impl Type {
|
|||||||
B1 => 1,
|
B1 => 1,
|
||||||
B8 | I8 => 8,
|
B8 | I8 => 8,
|
||||||
B16 | I16 => 16,
|
B16 | I16 => 16,
|
||||||
B32 | I32 | F32 => 32,
|
B32 | I32 | F32 | R32 => 32,
|
||||||
B64 | I64 | F64 => 64,
|
B64 | I64 | F64 | R64 => 64,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ impl Type {
|
|||||||
/// Get a type with the same number of lanes as this type, but with the lanes replaced by
|
/// Get a type with the same number of lanes as this type, but with the lanes replaced by
|
||||||
/// booleans of the same size.
|
/// booleans of the same size.
|
||||||
///
|
///
|
||||||
/// Scalar types are treated as vectors with one lane, so they are converted to the multi-bit
|
/// Lane types are treated as vectors with one lane, so they are converted to the multi-bit
|
||||||
/// boolean types.
|
/// boolean types.
|
||||||
pub fn as_bool_pedantic(self) -> Self {
|
pub fn as_bool_pedantic(self) -> Self {
|
||||||
// Replace the low 4 bits with the boolean version, preserve the high 4 bits.
|
// Replace the low 4 bits with the boolean version, preserve the high 4 bits.
|
||||||
@@ -108,6 +108,7 @@ impl Type {
|
|||||||
B16 | I16 => B16,
|
B16 | I16 => B16,
|
||||||
B32 | I32 | F32 => B32,
|
B32 | I32 | F32 => B32,
|
||||||
B64 | I64 | F64 => B64,
|
B64 | I64 | F64 => B64,
|
||||||
|
R32 | R64 => panic!("Reference types should not convert to bool"),
|
||||||
_ => B1,
|
_ => B1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -210,6 +211,14 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this a ref type?
|
||||||
|
pub fn is_ref(self) -> bool {
|
||||||
|
match self {
|
||||||
|
R32 | R64 => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get log_2 of the number of lanes in this SIMD vector type.
|
/// Get log_2 of the number of lanes in this SIMD vector type.
|
||||||
///
|
///
|
||||||
/// All SIMD types have a lane count that is a power of two and no larger than 256, so this
|
/// All SIMD types have a lane count that is a power of two and no larger than 256, so this
|
||||||
@@ -301,6 +310,8 @@ impl Display for Type {
|
|||||||
write!(f, "f{}", self.lane_bits())
|
write!(f, "f{}", self.lane_bits())
|
||||||
} else if self.is_vector() {
|
} else if self.is_vector() {
|
||||||
write!(f, "{}x{}", self.lane_type(), self.lane_count())
|
write!(f, "{}x{}", self.lane_type(), self.lane_count())
|
||||||
|
} else if self.is_ref() {
|
||||||
|
write!(f, "r{}", self.lane_bits())
|
||||||
} else {
|
} else {
|
||||||
f.write_str(match *self {
|
f.write_str(match *self {
|
||||||
IFLAGS => "iflags",
|
IFLAGS => "iflags",
|
||||||
@@ -322,6 +333,8 @@ impl Debug for Type {
|
|||||||
write!(f, "types::F{}", self.lane_bits())
|
write!(f, "types::F{}", self.lane_bits())
|
||||||
} else if self.is_vector() {
|
} else if self.is_vector() {
|
||||||
write!(f, "{:?}X{}", self.lane_type(), self.lane_count())
|
write!(f, "{:?}X{}", self.lane_type(), self.lane_count())
|
||||||
|
} else if self.is_ref() {
|
||||||
|
write!(f, "types::R{}", self.lane_bits())
|
||||||
} else {
|
} else {
|
||||||
match *self {
|
match *self {
|
||||||
INVALID => write!(f, "types::INVALID"),
|
INVALID => write!(f, "types::INVALID"),
|
||||||
@@ -366,6 +379,8 @@ mod tests {
|
|||||||
assert_eq!(B1, B1.by(8).unwrap().lane_type());
|
assert_eq!(B1, B1.by(8).unwrap().lane_type());
|
||||||
assert_eq!(I32, I32X4.lane_type());
|
assert_eq!(I32, I32X4.lane_type());
|
||||||
assert_eq!(F64, F64X2.lane_type());
|
assert_eq!(F64, F64X2.lane_type());
|
||||||
|
assert_eq!(R32, R32.lane_type());
|
||||||
|
assert_eq!(R64, R64.lane_type());
|
||||||
|
|
||||||
assert_eq!(INVALID.lane_bits(), 0);
|
assert_eq!(INVALID.lane_bits(), 0);
|
||||||
assert_eq!(IFLAGS.lane_bits(), 0);
|
assert_eq!(IFLAGS.lane_bits(), 0);
|
||||||
@@ -381,6 +396,8 @@ mod tests {
|
|||||||
assert_eq!(I64.lane_bits(), 64);
|
assert_eq!(I64.lane_bits(), 64);
|
||||||
assert_eq!(F32.lane_bits(), 32);
|
assert_eq!(F32.lane_bits(), 32);
|
||||||
assert_eq!(F64.lane_bits(), 64);
|
assert_eq!(F64.lane_bits(), 64);
|
||||||
|
assert_eq!(R32.lane_bits(), 32);
|
||||||
|
assert_eq!(R64.lane_bits(), 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -450,6 +467,8 @@ mod tests {
|
|||||||
assert_eq!(I64.to_string(), "i64");
|
assert_eq!(I64.to_string(), "i64");
|
||||||
assert_eq!(F32.to_string(), "f32");
|
assert_eq!(F32.to_string(), "f32");
|
||||||
assert_eq!(F64.to_string(), "f64");
|
assert_eq!(F64.to_string(), "f64");
|
||||||
|
assert_eq!(R32.to_string(), "r32");
|
||||||
|
assert_eq!(R64.to_string(), "r64");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::binemit::{bad_encoding, CodeSink};
|
use crate::binemit::{bad_encoding, CodeSink};
|
||||||
use crate::ir::{Function, Inst};
|
use crate::ir::{Function, Inst};
|
||||||
|
use crate::isa::TargetIsa;
|
||||||
use crate::regalloc::RegDiversions;
|
use crate::regalloc::RegDiversions;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/binemit-arm32.rs"));
|
include!(concat!(env!("OUT_DIR"), "/binemit-arm32.rs"));
|
||||||
|
|||||||
@@ -121,11 +121,11 @@ impl TargetIsa for Isa {
|
|||||||
divert: &mut regalloc::RegDiversions,
|
divert: &mut regalloc::RegDiversions,
|
||||||
sink: &mut dyn CodeSink,
|
sink: &mut dyn CodeSink,
|
||||||
) {
|
) {
|
||||||
binemit::emit_inst(func, inst, divert, sink)
|
binemit::emit_inst(func, inst, divert, sink, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
||||||
emit_function(func, binemit::emit_inst, sink)
|
emit_function(func, binemit::emit_inst, sink, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::binemit::{bad_encoding, CodeSink};
|
use crate::binemit::{bad_encoding, CodeSink};
|
||||||
use crate::ir::{Function, Inst};
|
use crate::ir::{Function, Inst};
|
||||||
|
use crate::isa::TargetIsa;
|
||||||
use crate::regalloc::RegDiversions;
|
use crate::regalloc::RegDiversions;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs"));
|
include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs"));
|
||||||
|
|||||||
@@ -108,11 +108,11 @@ impl TargetIsa for Isa {
|
|||||||
divert: &mut regalloc::RegDiversions,
|
divert: &mut regalloc::RegDiversions,
|
||||||
sink: &mut dyn CodeSink,
|
sink: &mut dyn CodeSink,
|
||||||
) {
|
) {
|
||||||
binemit::emit_inst(func, inst, divert, sink)
|
binemit::emit_inst(func, inst, divert, sink, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
||||||
emit_function(func, binemit::emit_inst, sink)
|
emit_function(func, binemit::emit_inst, sink, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::binemit::{bad_encoding, CodeSink, Reloc};
|
use crate::binemit::{bad_encoding, CodeSink, Reloc};
|
||||||
use crate::ir::{Function, Inst, InstructionData};
|
use crate::ir::{Function, Inst, InstructionData};
|
||||||
use crate::isa::{RegUnit, StackBaseMask, StackRef};
|
use crate::isa::{RegUnit, StackBaseMask, StackRef, TargetIsa};
|
||||||
use crate::predicates::is_signed_int;
|
use crate::predicates::is_signed_int;
|
||||||
use crate::regalloc::RegDiversions;
|
use crate::regalloc::RegDiversions;
|
||||||
use core::u32;
|
use core::u32;
|
||||||
|
|||||||
@@ -115,11 +115,11 @@ impl TargetIsa for Isa {
|
|||||||
divert: &mut regalloc::RegDiversions,
|
divert: &mut regalloc::RegDiversions,
|
||||||
sink: &mut dyn CodeSink,
|
sink: &mut dyn CodeSink,
|
||||||
) {
|
) {
|
||||||
binemit::emit_inst(func, inst, divert, sink)
|
binemit::emit_inst(func, inst, divert, sink, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
||||||
emit_function(func, binemit::emit_inst, sink)
|
emit_function(func, binemit::emit_inst, sink, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use super::registers::RU;
|
|||||||
use crate::binemit::{bad_encoding, CodeSink, Reloc};
|
use crate::binemit::{bad_encoding, CodeSink, Reloc};
|
||||||
use crate::ir::condcodes::{CondCode, FloatCC, IntCC};
|
use crate::ir::condcodes::{CondCode, FloatCC, IntCC};
|
||||||
use crate::ir::{Ebb, Function, Inst, InstructionData, JumpTable, Opcode, TrapCode};
|
use crate::ir::{Ebb, Function, Inst, InstructionData, JumpTable, Opcode, TrapCode};
|
||||||
use crate::isa::{RegUnit, StackBase, StackBaseMask, StackRef};
|
use crate::isa::{RegUnit, StackBase, StackBaseMask, StackRef, TargetIsa};
|
||||||
use crate::regalloc::RegDiversions;
|
use crate::regalloc::RegDiversions;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/binemit-x86.rs"));
|
include!(concat!(env!("OUT_DIR"), "/binemit-x86.rs"));
|
||||||
|
|||||||
@@ -131,11 +131,11 @@ impl TargetIsa for Isa {
|
|||||||
divert: &mut regalloc::RegDiversions,
|
divert: &mut regalloc::RegDiversions,
|
||||||
sink: &mut dyn CodeSink,
|
sink: &mut dyn CodeSink,
|
||||||
) {
|
) {
|
||||||
binemit::emit_inst(func, inst, divert, sink)
|
binemit::emit_inst(func, inst, divert, sink, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
fn emit_function_to_memory(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
|
||||||
emit_function(func, binemit::emit_inst, sink)
|
emit_function(func, binemit::emit_inst, sink, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prologue_epilogue(&self, func: &mut ir::Function) -> CodegenResult<()> {
|
fn prologue_epilogue(&self, func: &mut ir::Function) -> CodegenResult<()> {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::regalloc::coloring::Coloring;
|
|||||||
use crate::regalloc::live_value_tracker::LiveValueTracker;
|
use crate::regalloc::live_value_tracker::LiveValueTracker;
|
||||||
use crate::regalloc::liveness::Liveness;
|
use crate::regalloc::liveness::Liveness;
|
||||||
use crate::regalloc::reload::Reload;
|
use crate::regalloc::reload::Reload;
|
||||||
|
use crate::regalloc::safepoint::emit_stackmaps;
|
||||||
use crate::regalloc::spilling::Spilling;
|
use crate::regalloc::spilling::Spilling;
|
||||||
use crate::regalloc::virtregs::VirtRegs;
|
use crate::regalloc::virtregs::VirtRegs;
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
@@ -192,6 +193,20 @@ impl Context {
|
|||||||
self.coloring
|
self.coloring
|
||||||
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
||||||
|
|
||||||
|
// This function runs after register allocation has taken
|
||||||
|
// place, meaning values have locations assigned already.
|
||||||
|
if isa.flags().enable_safepoints() {
|
||||||
|
emit_stackmaps(func, domtree, &self.liveness, &mut self.tracker, isa);
|
||||||
|
} else {
|
||||||
|
// Make sure no references are used.
|
||||||
|
for val in func.dfg.values() {
|
||||||
|
let ty = func.dfg.value_type(val);
|
||||||
|
if ty.lane_type().is_ref() {
|
||||||
|
panic!("reference types were found but safepoints were not enabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if isa.flags().enable_verifier() {
|
if isa.flags().enable_verifier() {
|
||||||
let ok = verify_context(func, cfg, domtree, isa, &mut errors).is_ok()
|
let ok = verify_context(func, cfg, domtree, isa, &mut errors).is_ok()
|
||||||
&& verify_liveness(isa, func, cfg, &self.liveness, &mut errors).is_ok()
|
&& verify_liveness(isa, func, cfg, &self.liveness, &mut errors).is_ok()
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ mod context;
|
|||||||
mod diversion;
|
mod diversion;
|
||||||
mod pressure;
|
mod pressure;
|
||||||
mod reload;
|
mod reload;
|
||||||
|
mod safepoint;
|
||||||
mod solver;
|
mod solver;
|
||||||
mod spilling;
|
mod spilling;
|
||||||
|
|
||||||
pub use self::context::Context;
|
pub use self::context::Context;
|
||||||
pub use self::diversion::RegDiversions;
|
pub use self::diversion::RegDiversions;
|
||||||
pub use self::register_set::RegisterSet;
|
pub use self::register_set::RegisterSet;
|
||||||
|
pub use self::safepoint::emit_stackmaps;
|
||||||
|
|||||||
72
cranelift/codegen/src/regalloc/safepoint.rs
Normal file
72
cranelift/codegen/src/regalloc/safepoint.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use crate::cursor::{Cursor, FuncCursor};
|
||||||
|
use crate::dominator_tree::DominatorTree;
|
||||||
|
use crate::ir::{Function, InstBuilder, InstructionData, Opcode, TrapCode};
|
||||||
|
use crate::isa::TargetIsa;
|
||||||
|
use crate::regalloc::live_value_tracker::LiveValueTracker;
|
||||||
|
use crate::regalloc::liveness::Liveness;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
fn insert_and_encode_safepoint<'f>(
|
||||||
|
pos: &mut FuncCursor<'f>,
|
||||||
|
tracker: &LiveValueTracker,
|
||||||
|
isa: &dyn TargetIsa,
|
||||||
|
) {
|
||||||
|
// Iterate through all live values, collect only the references.
|
||||||
|
let live_ref_values = tracker
|
||||||
|
.live()
|
||||||
|
.iter()
|
||||||
|
.filter(|live_value| pos.func.dfg.value_type(live_value.value).is_ref())
|
||||||
|
.map(|live_val| live_val.value)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if !live_ref_values.is_empty() {
|
||||||
|
pos.ins().safepoint(&live_ref_values);
|
||||||
|
// Move cursor to the new safepoint instruction to encode it.
|
||||||
|
if let Some(inst) = pos.prev_inst() {
|
||||||
|
let ok = pos.func.update_encoding(inst, isa).is_ok();
|
||||||
|
debug_assert!(ok);
|
||||||
|
}
|
||||||
|
// Restore cursor position.
|
||||||
|
pos.next_inst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The emit_stackmaps() function analyzes each instruction to retrieve the liveness of
|
||||||
|
// the defs and operands by traversing a function's ebbs in layout order.
|
||||||
|
pub fn emit_stackmaps(
|
||||||
|
func: &mut Function,
|
||||||
|
domtree: &DominatorTree,
|
||||||
|
liveness: &Liveness,
|
||||||
|
tracker: &mut LiveValueTracker,
|
||||||
|
isa: &dyn TargetIsa,
|
||||||
|
) {
|
||||||
|
let mut curr = func.layout.entry_block();
|
||||||
|
|
||||||
|
while let Some(ebb) = curr {
|
||||||
|
tracker.ebb_top(ebb, &func.dfg, liveness, &func.layout, domtree);
|
||||||
|
tracker.drop_dead_params();
|
||||||
|
let mut pos = FuncCursor::new(func);
|
||||||
|
|
||||||
|
// From the top of the ebb, step through the instructions.
|
||||||
|
pos.goto_top(ebb);
|
||||||
|
|
||||||
|
while let Some(inst) = pos.next_inst() {
|
||||||
|
if let InstructionData::Trap {
|
||||||
|
code: TrapCode::Interrupt,
|
||||||
|
..
|
||||||
|
} = &pos.func.dfg[inst]
|
||||||
|
{
|
||||||
|
insert_and_encode_safepoint(&mut pos, tracker, isa);
|
||||||
|
} else if pos.func.dfg[inst].opcode().is_call() {
|
||||||
|
insert_and_encode_safepoint(&mut pos, tracker, isa);
|
||||||
|
} else if pos.func.dfg[inst].opcode() == Opcode::Safepoint {
|
||||||
|
panic!("safepoint instruction can only be used by the compiler!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the instruction and get rid of dead values.
|
||||||
|
tracker.process_inst(inst, &pos.func.dfg, liveness);
|
||||||
|
tracker.drop_dead(inst);
|
||||||
|
}
|
||||||
|
curr = func.layout.next_ebb(ebb);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -390,6 +390,7 @@ mod tests {
|
|||||||
enable_nan_canonicalization = false\n\
|
enable_nan_canonicalization = false\n\
|
||||||
enable_simd = false\n\
|
enable_simd = false\n\
|
||||||
enable_atomics = true\n\
|
enable_atomics = true\n\
|
||||||
|
enable_safepoints = false\n\
|
||||||
allones_funcaddrs = false\n\
|
allones_funcaddrs = false\n\
|
||||||
probestack_enabled = true\n\
|
probestack_enabled = true\n\
|
||||||
probestack_func_adjusts_sp = false\n\
|
probestack_func_adjusts_sp = false\n\
|
||||||
|
|||||||
@@ -1672,9 +1672,12 @@ impl<'a> Verifier<'a> {
|
|||||||
// Instructions with side effects are not allowed to be ghost instructions.
|
// Instructions with side effects are not allowed to be ghost instructions.
|
||||||
let opcode = self.func.dfg[inst].opcode();
|
let opcode = self.func.dfg[inst].opcode();
|
||||||
|
|
||||||
// The `fallthrough` and `fallthrough_return` instructions are marked as terminators and
|
// The `fallthrough`, `fallthrough_return`, and `safepoint` instructions are not required
|
||||||
// branches, but they are not required to have an encoding.
|
// to have an encoding.
|
||||||
if opcode == Opcode::Fallthrough || opcode == Opcode::FallthroughReturn {
|
if opcode == Opcode::Fallthrough
|
||||||
|
|| opcode == Opcode::FallthroughReturn
|
||||||
|
|| opcode == Opcode::Safepoint
|
||||||
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1739,6 +1742,24 @@ impl<'a> Verifier<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_safepoint_unused(
|
||||||
|
&self,
|
||||||
|
inst: Inst,
|
||||||
|
errors: &mut VerifierErrors,
|
||||||
|
) -> VerifierStepResult<()> {
|
||||||
|
if let Some(isa) = self.isa {
|
||||||
|
if !isa.flags().enable_safepoints() && self.func.dfg[inst].opcode() == Opcode::Safepoint
|
||||||
|
{
|
||||||
|
return fatal!(
|
||||||
|
errors,
|
||||||
|
inst,
|
||||||
|
"safepoint instruction cannot be used when it is not enabled."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
||||||
self.verify_global_values(errors)?;
|
self.verify_global_values(errors)?;
|
||||||
self.verify_heaps(errors)?;
|
self.verify_heaps(errors)?;
|
||||||
@@ -1750,6 +1771,7 @@ impl<'a> Verifier<'a> {
|
|||||||
for inst in self.func.layout.ebb_insts(ebb) {
|
for inst in self.func.layout.ebb_insts(ebb) {
|
||||||
self.ebb_integrity(ebb, inst, errors)?;
|
self.ebb_integrity(ebb, inst, errors)?;
|
||||||
self.instruction_integrity(inst, errors)?;
|
self.instruction_integrity(inst, errors)?;
|
||||||
|
self.verify_safepoint_unused(inst, errors)?;
|
||||||
self.typecheck(inst, errors)?;
|
self.typecheck(inst, errors)?;
|
||||||
self.verify_encoding(inst, errors)?;
|
self.verify_encoding(inst, errors)?;
|
||||||
self.immediate_constraints(inst, errors)?;
|
self.immediate_constraints(inst, errors)?;
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
use crate::container;
|
use crate::container;
|
||||||
use crate::traps::{FaerieTrapManifest, FaerieTrapSink};
|
use crate::traps::{FaerieTrapManifest, FaerieTrapSink};
|
||||||
use cranelift_codegen::binemit::{Addend, CodeOffset, NullTrapSink, Reloc, RelocSink};
|
use cranelift_codegen::binemit::{
|
||||||
|
Addend, CodeOffset, NullStackmapSink, NullTrapSink, Reloc, RelocSink, Stackmap, StackmapSink,
|
||||||
|
};
|
||||||
use cranelift_codegen::isa::TargetIsa;
|
use cranelift_codegen::isa::TargetIsa;
|
||||||
use cranelift_codegen::{self, binemit, ir};
|
use cranelift_codegen::{self, binemit, ir};
|
||||||
use cranelift_module::{
|
use cranelift_module::{
|
||||||
@@ -141,6 +143,8 @@ impl Backend for FaerieBackend {
|
|||||||
total_size: u32,
|
total_size: u32,
|
||||||
) -> ModuleResult<FaerieCompiledFunction> {
|
) -> ModuleResult<FaerieCompiledFunction> {
|
||||||
let mut code: Vec<u8> = vec![0; total_size as usize];
|
let mut code: Vec<u8> = vec![0; total_size as usize];
|
||||||
|
// TODO: Replace this with FaerieStackmapSink once it is implemented.
|
||||||
|
let mut stackmap_sink = NullStackmapSink {};
|
||||||
|
|
||||||
// Non-lexical lifetimes would obviate the braces here.
|
// Non-lexical lifetimes would obviate the braces here.
|
||||||
{
|
{
|
||||||
@@ -160,6 +164,7 @@ impl Backend for FaerieBackend {
|
|||||||
code.as_mut_ptr(),
|
code.as_mut_ptr(),
|
||||||
&mut reloc_sink,
|
&mut reloc_sink,
|
||||||
&mut trap_sink,
|
&mut trap_sink,
|
||||||
|
&mut stackmap_sink,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
trap_manifest.add_sink(trap_sink);
|
trap_manifest.add_sink(trap_sink);
|
||||||
@@ -171,6 +176,7 @@ impl Backend for FaerieBackend {
|
|||||||
code.as_mut_ptr(),
|
code.as_mut_ptr(),
|
||||||
&mut reloc_sink,
|
&mut reloc_sink,
|
||||||
&mut trap_sink,
|
&mut trap_sink,
|
||||||
|
&mut stackmap_sink,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -425,3 +431,16 @@ impl<'a> RelocSink for FaerieRelocSink<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct FaerieStackmapSink<'a> {
|
||||||
|
artifact: &'a mut faerie::Artifact,
|
||||||
|
namespace: &'a ModuleNamespace<'a, FaerieBackend>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Faerie is currently not used in SpiderMonkey. Methods are unimplemented.
|
||||||
|
impl<'a> StackmapSink for FaerieStackmapSink<'a> {
|
||||||
|
fn add_stackmap(&mut self, _: CodeOffset, _: Stackmap) {
|
||||||
|
unimplemented!("faerie support for stackmaps");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
55
cranelift/filetests/filetests/safepoint/basic.clif
Normal file
55
cranelift/filetests/filetests/safepoint/basic.clif
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
test safepoint
|
||||||
|
|
||||||
|
set enable_safepoints=true
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function %test(i32, r64, r64) -> r64 {
|
||||||
|
ebb0(v0: i32, v1:r64, v2:r64):
|
||||||
|
jump ebb1(v0)
|
||||||
|
ebb1(v3: i32):
|
||||||
|
v4 = irsub_imm v3, 1
|
||||||
|
jump ebb2(v4)
|
||||||
|
ebb2(v5: i32):
|
||||||
|
resumable_trap interrupt
|
||||||
|
brz v5, ebb1(v5)
|
||||||
|
v6 = null.r64
|
||||||
|
v7 = is_null v6
|
||||||
|
brnz v7, ebb2(v0)
|
||||||
|
brnz v0, ebb3
|
||||||
|
jump ebb4
|
||||||
|
ebb3:
|
||||||
|
return v1
|
||||||
|
ebb4:
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
|
||||||
|
; sameln: function %test(i32 [%rdi], r64 [%rsi], r64 [%rdx]) -> r64 [%rax] fast {
|
||||||
|
; nextln: ebb0(v0: i32 [%rdi], v1: r64 [%rsi], v2: r64 [%rdx]):
|
||||||
|
; nextln: v10 = copy v0
|
||||||
|
; nextln: jump ebb1(v10)
|
||||||
|
; nextln:
|
||||||
|
; nextln: ebb1(v3: i32 [%rax]):
|
||||||
|
; nextln: v8 = iconst.i32 1
|
||||||
|
; nextln: v4 = isub v8, v3
|
||||||
|
; nextln: jump ebb2(v4)
|
||||||
|
; nextln:
|
||||||
|
; nextln: ebb2(v5: i32 [%rcx]):
|
||||||
|
; nextln: safepoint v1, v2
|
||||||
|
; nextln: resumable_trap interrupt
|
||||||
|
; nextln: regmove v5, %rcx -> %rax
|
||||||
|
; nextln: brz v5, ebb1(v5)
|
||||||
|
; nextln: v6 = null.r64
|
||||||
|
; nextln: v7 = is_null v6
|
||||||
|
; nextln: v9 = copy.i32 v0
|
||||||
|
; nextln: brnz v7, ebb2(v9)
|
||||||
|
; nextln: brnz.i32 v0, ebb3
|
||||||
|
; nextln: jump ebb4
|
||||||
|
; nextln:
|
||||||
|
; nextln: ebb3:
|
||||||
|
; nextln: regmove.r64 v1, %rsi -> %rax
|
||||||
|
; nextln: return v1
|
||||||
|
; nextln:
|
||||||
|
; nextln: ebb4:
|
||||||
|
; nextln: regmove.r64 v2, %rdx -> %rax
|
||||||
|
; nextln: return v2
|
||||||
|
; nextln: }
|
||||||
52
cranelift/filetests/filetests/safepoint/call.clif
Normal file
52
cranelift/filetests/filetests/safepoint/call.clif
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
test safepoint
|
||||||
|
|
||||||
|
set enable_safepoints=true
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function %direct() -> r64 {
|
||||||
|
fn0 = %none()
|
||||||
|
fn1 = %one() -> r64
|
||||||
|
fn2 = %two() -> i32, r64
|
||||||
|
|
||||||
|
ebb0:
|
||||||
|
call fn0()
|
||||||
|
v1 = call fn1()
|
||||||
|
v2, v3 = call fn2()
|
||||||
|
brz v2, ebb1
|
||||||
|
return v1
|
||||||
|
ebb1:
|
||||||
|
v4 = call fn1()
|
||||||
|
return v3
|
||||||
|
}
|
||||||
|
|
||||||
|
; sameln: function %direct() -> r64 [%rax] fast {
|
||||||
|
; nextln: ss0 = spill_slot 8
|
||||||
|
; nextln: ss1 = spill_slot 8
|
||||||
|
; nextln: sig0 = () fast
|
||||||
|
; nextln: sig1 = () -> r64 [%rax] fast
|
||||||
|
; nextln: sig2 = () -> i32 [%rax], r64 [%rdx] fast
|
||||||
|
; nextln: fn0 = %none sig0
|
||||||
|
; nextln: fn1 = %one sig1
|
||||||
|
; nextln: fn2 = %two sig2
|
||||||
|
; nextln:
|
||||||
|
; nextln: ebb0:
|
||||||
|
; nextln: v5 = func_addr.i64 fn0
|
||||||
|
; nextln: call_indirect sig0, v5()
|
||||||
|
; nextln: v6 = func_addr.i64 fn1
|
||||||
|
; nextln: v9 = call_indirect sig1, v6()
|
||||||
|
; nextln: v1 = spill v9
|
||||||
|
; nextln: v7 = func_addr.i64 fn2
|
||||||
|
; nextln: safepoint v1
|
||||||
|
; nextln: v2, v10 = call_indirect sig2, v7()
|
||||||
|
; nextln: v3 = spill v10
|
||||||
|
; nextln: brz v2, ebb1
|
||||||
|
; nextln: v11 = fill v1
|
||||||
|
; nextln: return v11
|
||||||
|
; nextln:
|
||||||
|
; nextln: ebb1:
|
||||||
|
; nextln: v8 = func_addr.i64 fn1
|
||||||
|
; nextln: safepoint v3
|
||||||
|
; nextln: v4 = call_indirect sig1, v8()
|
||||||
|
; nextln: v12 = fill.r64 v3
|
||||||
|
; nextln: return v12
|
||||||
|
; nextln: }
|
||||||
@@ -46,6 +46,7 @@ mod test_postopt;
|
|||||||
mod test_preopt;
|
mod test_preopt;
|
||||||
mod test_print_cfg;
|
mod test_print_cfg;
|
||||||
mod test_regalloc;
|
mod test_regalloc;
|
||||||
|
mod test_safepoint;
|
||||||
mod test_shrink;
|
mod test_shrink;
|
||||||
mod test_simple_gvn;
|
mod test_simple_gvn;
|
||||||
mod test_simple_preopt;
|
mod test_simple_preopt;
|
||||||
@@ -127,6 +128,7 @@ fn new_subtest(parsed: &TestCommand) -> subtest::SubtestResult<Box<dyn subtest::
|
|||||||
"simple-gvn" => test_simple_gvn::subtest(parsed),
|
"simple-gvn" => test_simple_gvn::subtest(parsed),
|
||||||
"verifier" => test_verifier::subtest(parsed),
|
"verifier" => test_verifier::subtest(parsed),
|
||||||
"preopt" => test_preopt::subtest(parsed),
|
"preopt" => test_preopt::subtest(parsed),
|
||||||
|
"safepoint" => test_safepoint::subtest(parsed),
|
||||||
_ => Err(format!("unknown test command '{}'", parsed.command)),
|
_ => Err(format!("unknown test command '{}'", parsed.command)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use cranelift_codegen::dominator_tree::DominatorTree;
|
|||||||
use cranelift_codegen::flowgraph::ControlFlowGraph;
|
use cranelift_codegen::flowgraph::ControlFlowGraph;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_codegen::ir::entities::AnyEntity;
|
use cranelift_codegen::ir::entities::AnyEntity;
|
||||||
|
use cranelift_codegen::isa;
|
||||||
use cranelift_codegen::print_errors::pretty_error;
|
use cranelift_codegen::print_errors::pretty_error;
|
||||||
use cranelift_codegen::settings::OptLevel;
|
use cranelift_codegen::settings::OptLevel;
|
||||||
use cranelift_reader::TestCommand;
|
use cranelift_reader::TestCommand;
|
||||||
@@ -100,9 +101,15 @@ impl binemit::CodeSink for TextSink {
|
|||||||
fn begin_jumptables(&mut self) {
|
fn begin_jumptables(&mut self) {
|
||||||
self.code_size = self.offset
|
self.code_size = self.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_rodata(&mut self) {}
|
fn begin_rodata(&mut self) {}
|
||||||
fn end_codegen(&mut self) {}
|
fn end_codegen(&mut self) {}
|
||||||
|
fn add_stackmap(
|
||||||
|
&mut self,
|
||||||
|
_: &[ir::entities::Value],
|
||||||
|
_: &ir::Function,
|
||||||
|
_: &dyn isa::TargetIsa,
|
||||||
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubTest for TestBinEmit {
|
impl SubTest for TestBinEmit {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
|||||||
use cranelift_codegen;
|
use cranelift_codegen;
|
||||||
use cranelift_codegen::binemit::{self, CodeInfo};
|
use cranelift_codegen::binemit::{self, CodeInfo};
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_codegen::isa;
|
||||||
use cranelift_codegen::print_errors::pretty_error;
|
use cranelift_codegen::print_errors::pretty_error;
|
||||||
use cranelift_reader::TestCommand;
|
use cranelift_reader::TestCommand;
|
||||||
use log::info;
|
use log::info;
|
||||||
@@ -53,8 +54,9 @@ impl SubTest for TestCompile {
|
|||||||
let mut sink = SizeSink { offset: 0 };
|
let mut sink = SizeSink { offset: 0 };
|
||||||
binemit::emit_function(
|
binemit::emit_function(
|
||||||
&comp_ctx.func,
|
&comp_ctx.func,
|
||||||
|func, inst, div, sink| isa.emit_inst(func, inst, div, sink),
|
|func, inst, div, sink, isa| isa.emit_inst(func, inst, div, sink),
|
||||||
&mut sink,
|
&mut sink,
|
||||||
|
isa,
|
||||||
);
|
);
|
||||||
|
|
||||||
if sink.offset != total_size {
|
if sink.offset != total_size {
|
||||||
@@ -109,4 +111,11 @@ impl binemit::CodeSink for SizeSink {
|
|||||||
fn begin_jumptables(&mut self) {}
|
fn begin_jumptables(&mut self) {}
|
||||||
fn begin_rodata(&mut self) {}
|
fn begin_rodata(&mut self) {}
|
||||||
fn end_codegen(&mut self) {}
|
fn end_codegen(&mut self) {}
|
||||||
|
fn add_stackmap(
|
||||||
|
&mut self,
|
||||||
|
_: &[ir::entities::Value],
|
||||||
|
_: &ir::Function,
|
||||||
|
_: &dyn isa::TargetIsa,
|
||||||
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
cranelift/filetests/src/test_safepoint.rs
Normal file
39
cranelift/filetests/src/test_safepoint.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||||
|
use cranelift_codegen::ir::Function;
|
||||||
|
use cranelift_codegen::print_errors::pretty_error;
|
||||||
|
use cranelift_reader::TestCommand;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
struct TestSafepoint;
|
||||||
|
|
||||||
|
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<SubTest>> {
|
||||||
|
assert_eq!(parsed.command, "safepoint");
|
||||||
|
if !parsed.options.is_empty() {
|
||||||
|
Err(format!("No options allowed on {}", parsed))
|
||||||
|
} else {
|
||||||
|
Ok(Box::new(TestSafepoint))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubTest for TestSafepoint {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"safepoint"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||||
|
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||||
|
|
||||||
|
let isa = context.isa.expect("register allocator needs an ISA");
|
||||||
|
comp_ctx.compute_cfg();
|
||||||
|
comp_ctx
|
||||||
|
.legalize(isa)
|
||||||
|
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||||
|
comp_ctx.compute_domtree();
|
||||||
|
comp_ctx
|
||||||
|
.regalloc(isa)
|
||||||
|
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||||
|
|
||||||
|
let text = comp_ctx.func.display(context.isa).to_string();
|
||||||
|
run_filecheck(&text, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -221,6 +221,8 @@ fn emit_zero(ty: Type, mut cur: FuncCursor) -> Value {
|
|||||||
cur.ins().f32const(Ieee32::with_bits(0))
|
cur.ins().f32const(Ieee32::with_bits(0))
|
||||||
} else if ty == F64 {
|
} else if ty == F64 {
|
||||||
cur.ins().f64const(Ieee64::with_bits(0))
|
cur.ins().f64const(Ieee64::with_bits(0))
|
||||||
|
} else if ty.is_ref() {
|
||||||
|
cur.ins().null(ty)
|
||||||
} else if ty.is_vector() {
|
} else if ty.is_vector() {
|
||||||
let scalar_ty = ty.lane_type();
|
let scalar_ty = ty.lane_type();
|
||||||
if scalar_ty.is_int() {
|
if scalar_ty.is_int() {
|
||||||
|
|||||||
@@ -370,6 +370,8 @@ impl<'a> Lexer<'a> {
|
|||||||
"b16" => types::B16,
|
"b16" => types::B16,
|
||||||
"b32" => types::B32,
|
"b32" => types::B32,
|
||||||
"b64" => types::B64,
|
"b64" => types::B64,
|
||||||
|
"r32" => types::R32,
|
||||||
|
"r64" => types::R64,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
if is_vector {
|
if is_vector {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
//! Defines `SimpleJITBackend`.
|
//! Defines `SimpleJITBackend`.
|
||||||
|
|
||||||
use crate::memory::Memory;
|
use crate::memory::Memory;
|
||||||
use cranelift_codegen::binemit::{Addend, CodeOffset, NullTrapSink, Reloc, RelocSink};
|
use cranelift_codegen::binemit::{
|
||||||
|
Addend, CodeOffset, NullTrapSink, Reloc, RelocSink, Stackmap, StackmapSink,
|
||||||
|
};
|
||||||
use cranelift_codegen::isa::TargetIsa;
|
use cranelift_codegen::isa::TargetIsa;
|
||||||
use cranelift_codegen::{self, ir, settings};
|
use cranelift_codegen::{self, ir, settings};
|
||||||
use cranelift_module::{
|
use cranelift_module::{
|
||||||
@@ -128,6 +130,13 @@ struct RelocRecord {
|
|||||||
addend: Addend,
|
addend: Addend,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StackmapRecord {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
offset: CodeOffset,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
stackmap: Stackmap,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SimpleJITCompiledFunction {
|
pub struct SimpleJITCompiledFunction {
|
||||||
code: *mut u8,
|
code: *mut u8,
|
||||||
size: usize,
|
size: usize,
|
||||||
@@ -254,7 +263,16 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
// Ignore traps for now. For now, frontends should just avoid generating code
|
// Ignore traps for now. For now, frontends should just avoid generating code
|
||||||
// that traps.
|
// that traps.
|
||||||
let mut trap_sink = NullTrapSink {};
|
let mut trap_sink = NullTrapSink {};
|
||||||
unsafe { ctx.emit_to_memory(&*self.isa, ptr, &mut reloc_sink, &mut trap_sink) };
|
let mut stackmap_sink = SimpleJITStackmapSink::new();
|
||||||
|
unsafe {
|
||||||
|
ctx.emit_to_memory(
|
||||||
|
&*self.isa,
|
||||||
|
ptr,
|
||||||
|
&mut reloc_sink,
|
||||||
|
&mut trap_sink,
|
||||||
|
&mut stackmap_sink,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self::CompiledFunction {
|
Ok(Self::CompiledFunction {
|
||||||
code: ptr,
|
code: ptr,
|
||||||
@@ -549,3 +567,21 @@ impl RelocSink for SimpleJITRelocSink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SimpleJITStackmapSink {
|
||||||
|
pub stackmaps: Vec<StackmapRecord>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleJITStackmapSink {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
stackmaps: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackmapSink for SimpleJITStackmapSink {
|
||||||
|
fn add_stackmap(&mut self, offset: CodeOffset, stackmap: Stackmap) {
|
||||||
|
self.stackmaps.push(StackmapRecord { offset, stackmap });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! CLI tool to reduce Cranelift IR files crashing during compilation.
|
//! CLI tool to reduce Cranelift IR files crashing during compilation.
|
||||||
|
|
||||||
use crate::disasm::{PrintRelocs, PrintTraps};
|
use crate::disasm::{PrintRelocs, PrintStackmaps, PrintTraps};
|
||||||
use crate::utils::{parse_sets_and_triple, read_to_string};
|
use crate::utils::{parse_sets_and_triple, read_to_string};
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
Ebb, FuncRef, Function, GlobalValueData, Inst, InstBuilder, InstructionData, StackSlots,
|
Ebb, FuncRef, Function, GlobalValueData, Inst, InstBuilder, InstructionData, StackSlots,
|
||||||
@@ -730,11 +730,16 @@ impl<'a> CrashCheckContext<'a> {
|
|||||||
let res = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
let res = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||||
let mut relocs = PrintRelocs::new(false);
|
let mut relocs = PrintRelocs::new(false);
|
||||||
let mut traps = PrintTraps::new(false);
|
let mut traps = PrintTraps::new(false);
|
||||||
|
let mut stackmaps = PrintStackmaps::new(false);
|
||||||
let mut mem = vec![];
|
let mut mem = vec![];
|
||||||
|
|
||||||
let _ = self
|
let _ = self.context.compile_and_emit(
|
||||||
.context
|
self.isa,
|
||||||
.compile_and_emit(self.isa, &mut mem, &mut relocs, &mut traps);
|
&mut mem,
|
||||||
|
&mut relocs,
|
||||||
|
&mut traps,
|
||||||
|
&mut stackmaps,
|
||||||
|
);
|
||||||
})) {
|
})) {
|
||||||
Ok(()) => CheckResult::Succeed,
|
Ok(()) => CheckResult::Succeed,
|
||||||
Err(err) => CheckResult::Crash(get_panic_string(err)),
|
Err(err) => CheckResult::Crash(get_panic_string(err)),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! CLI tool to read Cranelift IR files and compile them into native code.
|
//! CLI tool to read Cranelift IR files and compile them into native code.
|
||||||
|
|
||||||
use crate::disasm::{print_all, PrintRelocs, PrintTraps};
|
use crate::disasm::{print_all, PrintRelocs, PrintStackmaps, PrintTraps};
|
||||||
use crate::utils::{parse_sets_and_triple, read_to_string};
|
use crate::utils::{parse_sets_and_triple, read_to_string};
|
||||||
use cranelift_codegen::print_errors::pretty_error;
|
use cranelift_codegen::print_errors::pretty_error;
|
||||||
use cranelift_codegen::settings::FlagsOrIsa;
|
use cranelift_codegen::settings::FlagsOrIsa;
|
||||||
@@ -62,11 +62,12 @@ fn handle_module(
|
|||||||
|
|
||||||
let mut relocs = PrintRelocs::new(flag_print);
|
let mut relocs = PrintRelocs::new(flag_print);
|
||||||
let mut traps = PrintTraps::new(flag_print);
|
let mut traps = PrintTraps::new(flag_print);
|
||||||
|
let mut stackmaps = PrintStackmaps::new(flag_print);
|
||||||
let mut mem = vec![];
|
let mut mem = vec![];
|
||||||
|
|
||||||
// Compile and encode the result to machine code.
|
// Compile and encode the result to machine code.
|
||||||
let code_info = context
|
let code_info = context
|
||||||
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stackmaps)
|
||||||
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
|
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
|
||||||
|
|
||||||
if flag_print {
|
if flag_print {
|
||||||
@@ -81,6 +82,7 @@ fn handle_module(
|
|||||||
code_info.jumptables_size + code_info.rodata_size,
|
code_info.jumptables_size + code_info.rodata_size,
|
||||||
&relocs,
|
&relocs,
|
||||||
&traps,
|
&traps,
|
||||||
|
&stackmaps,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,28 @@ impl binemit::TrapSink for PrintTraps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PrintStackmaps {
|
||||||
|
pub flag_print: bool,
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrintStackmaps {
|
||||||
|
pub fn new(flag_print: bool) -> PrintStackmaps {
|
||||||
|
Self {
|
||||||
|
flag_print,
|
||||||
|
text: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl binemit::StackmapSink for PrintStackmaps {
|
||||||
|
fn add_stackmap(&mut self, offset: binemit::CodeOffset, _: binemit::Stackmap) {
|
||||||
|
if self.flag_print {
|
||||||
|
write!(&mut self.text, "add_stackmap at {}\n", offset).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(feature = "disas")] {
|
if #[cfg(feature = "disas")] {
|
||||||
use capstone::prelude::*;
|
use capstone::prelude::*;
|
||||||
@@ -170,11 +192,12 @@ pub fn print_all(
|
|||||||
rodata_size: u32,
|
rodata_size: u32,
|
||||||
relocs: &PrintRelocs,
|
relocs: &PrintRelocs,
|
||||||
traps: &PrintTraps,
|
traps: &PrintTraps,
|
||||||
|
stackmaps: &PrintStackmaps,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
print_bytes(&mem);
|
print_bytes(&mem);
|
||||||
print_disassembly(isa, &mem[0..code_size as usize])?;
|
print_disassembly(isa, &mem[0..code_size as usize])?;
|
||||||
print_readonly_data(&mem[code_size as usize..(code_size + rodata_size) as usize]);
|
print_readonly_data(&mem[code_size as usize..(code_size + rodata_size) as usize]);
|
||||||
println!("\n{}\n{}", &relocs.text, &traps.text);
|
println!("\n{}\n{}\n{}", &relocs.text, &traps.text, &stackmaps.text);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
allow(clippy::too_many_arguments, clippy::cyclomatic_complexity)
|
allow(clippy::too_many_arguments, clippy::cyclomatic_complexity)
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use crate::disasm::{print_all, PrintRelocs, PrintTraps};
|
use crate::disasm::{print_all, PrintRelocs, PrintTraps, PrintStackmaps};
|
||||||
use crate::utils::{parse_sets_and_triple, read_to_end};
|
use crate::utils::{parse_sets_and_triple, read_to_end};
|
||||||
use cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error};
|
use cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error};
|
||||||
use cranelift_codegen::settings::FlagsOrIsa;
|
use cranelift_codegen::settings::FlagsOrIsa;
|
||||||
@@ -171,13 +171,14 @@ fn handle_module(
|
|||||||
let mut mem = vec![];
|
let mut mem = vec![];
|
||||||
let mut relocs = PrintRelocs::new(flag_print);
|
let mut relocs = PrintRelocs::new(flag_print);
|
||||||
let mut traps = PrintTraps::new(flag_print);
|
let mut traps = PrintTraps::new(flag_print);
|
||||||
|
let mut stackmaps = PrintStackmaps::new(flag_print);
|
||||||
if flag_check_translation {
|
if flag_check_translation {
|
||||||
if let Err(errors) = context.verify(fisa) {
|
if let Err(errors) = context.verify(fisa) {
|
||||||
return Err(pretty_verifier_error(&context.func, fisa.isa, None, errors));
|
return Err(pretty_verifier_error(&context.func, fisa.isa, None, errors));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let code_info = context
|
let code_info = context
|
||||||
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stackmaps)
|
||||||
.map_err(|err| pretty_error(&context.func, fisa.isa, err))?;
|
.map_err(|err| pretty_error(&context.func, fisa.isa, err))?;
|
||||||
|
|
||||||
if flag_print_size {
|
if flag_print_size {
|
||||||
@@ -223,7 +224,7 @@ fn handle_module(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some((code_size, rodata_size)) = saved_sizes {
|
if let Some((code_size, rodata_size)) = saved_sizes {
|
||||||
print_all(isa, &mem, code_size, rodata_size, &relocs, &traps)?;
|
print_all(isa, &mem, code_size, rodata_size, &relocs, &traps, &stackmaps)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.clear();
|
context.clear();
|
||||||
|
|||||||
@@ -834,6 +834,12 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
Operator::F32Le | Operator::F64Le => {
|
Operator::F32Le | Operator::F64Le => {
|
||||||
translate_fcmp(FloatCC::LessThanOrEqual, builder, state)
|
translate_fcmp(FloatCC::LessThanOrEqual, builder, state)
|
||||||
}
|
}
|
||||||
|
Operator::RefNull => state.push1(builder.ins().null(environ.reference_type())),
|
||||||
|
Operator::RefIsNull => {
|
||||||
|
let arg = state.pop1();
|
||||||
|
let val = builder.ins().is_null(arg);
|
||||||
|
state.push1(val);
|
||||||
|
}
|
||||||
Operator::Wake { .. }
|
Operator::Wake { .. }
|
||||||
| Operator::I32Wait { .. }
|
| Operator::I32Wait { .. }
|
||||||
| Operator::I64Wait { .. }
|
| Operator::I64Wait { .. }
|
||||||
@@ -902,9 +908,6 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
| Operator::I64AtomicRmw32UCmpxchg { .. } => {
|
| Operator::I64AtomicRmw32UCmpxchg { .. } => {
|
||||||
wasm_unsupported!("proposed thread operator {:?}", op);
|
wasm_unsupported!("proposed thread operator {:?}", op);
|
||||||
}
|
}
|
||||||
Operator::RefNull | Operator::RefIsNull { .. } => {
|
|
||||||
wasm_unsupported!("proposed reference-type operator {:?}", op);
|
|
||||||
}
|
|
||||||
Operator::MemoryInit { .. }
|
Operator::MemoryInit { .. }
|
||||||
| Operator::DataDrop { .. }
|
| Operator::DataDrop { .. }
|
||||||
| Operator::MemoryCopy
|
| Operator::MemoryCopy
|
||||||
|
|||||||
@@ -131,6 +131,17 @@ pub trait FuncEnvironment {
|
|||||||
ReturnMode::NormalReturns
|
ReturnMode::NormalReturns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the Cranelift reference type to use for native references.
|
||||||
|
///
|
||||||
|
/// This returns `R64` for 64-bit architectures and `R32` for 32-bit architectures.
|
||||||
|
fn reference_type(&self) -> ir::Type {
|
||||||
|
match self.pointer_type() {
|
||||||
|
ir::types::I32 => ir::types::R32,
|
||||||
|
ir::types::I64 => ir::types::R64,
|
||||||
|
_ => panic!("unsupported pointer type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set up the necessary preamble definitions in `func` to access the global variable
|
/// Set up the necessary preamble definitions in `func` to access the global variable
|
||||||
/// identified by `index`.
|
/// identified by `index`.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ impl FuncTranslator {
|
|||||||
builder.append_ebb_params_for_function_returns(exit_block);
|
builder.append_ebb_params_for_function_returns(exit_block);
|
||||||
self.state.initialize(&builder.func.signature, exit_block);
|
self.state.initialize(&builder.func.signature, exit_block);
|
||||||
|
|
||||||
parse_local_decls(&mut reader, &mut builder, num_params)?;
|
parse_local_decls(&mut reader, &mut builder, num_params, environ)?;
|
||||||
parse_function_body(reader, &mut builder, &mut self.state, environ)?;
|
parse_function_body(reader, &mut builder, &mut self.state, environ)?;
|
||||||
|
|
||||||
builder.finalize();
|
builder.finalize();
|
||||||
@@ -143,10 +143,11 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> u
|
|||||||
/// Parse the local variable declarations that precede the function body.
|
/// Parse the local variable declarations that precede the function body.
|
||||||
///
|
///
|
||||||
/// Declare local variables, starting from `num_params`.
|
/// Declare local variables, starting from `num_params`.
|
||||||
fn parse_local_decls(
|
fn parse_local_decls<FE: FuncEnvironment + ?Sized>(
|
||||||
reader: &mut BinaryReader,
|
reader: &mut BinaryReader,
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
num_params: usize,
|
num_params: usize,
|
||||||
|
environ: &mut FE,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
let mut next_local = num_params;
|
let mut next_local = num_params;
|
||||||
let local_count = reader.read_local_count()?;
|
let local_count = reader.read_local_count()?;
|
||||||
@@ -155,7 +156,7 @@ fn parse_local_decls(
|
|||||||
for _ in 0..local_count {
|
for _ in 0..local_count {
|
||||||
builder.set_srcloc(cur_srcloc(reader));
|
builder.set_srcloc(cur_srcloc(reader));
|
||||||
let (count, ty) = reader.read_local_decl(&mut locals_total)?;
|
let (count, ty) = reader.read_local_decl(&mut locals_total)?;
|
||||||
declare_locals(builder, count, ty, &mut next_local)?;
|
declare_locals(builder, count, ty, &mut next_local, environ)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -164,11 +165,12 @@ fn parse_local_decls(
|
|||||||
/// Declare `count` local variables of the same type, starting from `next_local`.
|
/// Declare `count` local variables of the same type, starting from `next_local`.
|
||||||
///
|
///
|
||||||
/// Fail of too many locals are declared in the function, or if the type is not valid for a local.
|
/// Fail of too many locals are declared in the function, or if the type is not valid for a local.
|
||||||
fn declare_locals(
|
fn declare_locals<FE: FuncEnvironment + ?Sized>(
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
count: u32,
|
count: u32,
|
||||||
wasm_type: wasmparser::Type,
|
wasm_type: wasmparser::Type,
|
||||||
next_local: &mut usize,
|
next_local: &mut usize,
|
||||||
|
environ: &mut FE,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
// All locals are initialized to 0.
|
// All locals are initialized to 0.
|
||||||
use wasmparser::Type::*;
|
use wasmparser::Type::*;
|
||||||
@@ -177,6 +179,7 @@ fn declare_locals(
|
|||||||
I64 => builder.ins().iconst(ir::types::I64, 0),
|
I64 => builder.ins().iconst(ir::types::I64, 0),
|
||||||
F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
|
F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
|
||||||
F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
|
F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
|
||||||
|
AnyRef => builder.ins().null(environ.reference_type()),
|
||||||
ty => wasm_unsupported!("unsupported local type {:?}", ty),
|
ty => wasm_unsupported!("unsupported local type {:?}", ty),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user