Add reference types R32 and R64
-Add resumable_trap, safepoint, isnull, and null instructions -Add Stackmap struct and StackmapSink trait Co-authored-by: Mir Ahmed <mirahmed753@gmail.com> Co-authored-by: Dan Gohman <sunfish@mozilla.com>
This commit is contained in:
@@ -12,7 +12,7 @@ use crate::cdsl::formats::{
|
||||
};
|
||||
use crate::cdsl::operands::Operand;
|
||||
use crate::cdsl::type_inference::Constraint;
|
||||
use crate::cdsl::types::{LaneType, ValueType, VectorType};
|
||||
use crate::cdsl::types::{LaneType, ReferenceType, ValueType, VectorType};
|
||||
use crate::cdsl::typevar::TypeVar;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
@@ -177,6 +177,10 @@ impl Instruction {
|
||||
bind(self.clone(), Some(lane_type.into()), Vec::new())
|
||||
}
|
||||
|
||||
pub fn bind_ref(&self, reference_type: impl Into<ReferenceType>) -> BoundInstruction {
|
||||
bind_ref(self.clone(), Some(reference_type.into()), Vec::new())
|
||||
}
|
||||
|
||||
pub fn bind_vector(&self, lane_type: impl Into<LaneType>, num_lanes: u64) -> BoundInstruction {
|
||||
bind_vector(self.clone(), lane_type.into(), num_lanes, Vec::new())
|
||||
}
|
||||
@@ -406,6 +410,10 @@ impl BoundInstruction {
|
||||
bind(self.inst, Some(lane_type.into()), self.value_types)
|
||||
}
|
||||
|
||||
pub fn bind_ref(self, reference_type: impl Into<ReferenceType>) -> BoundInstruction {
|
||||
bind_ref(self.inst, Some(reference_type.into()), self.value_types)
|
||||
}
|
||||
|
||||
pub fn bind_vector(self, lane_type: impl Into<LaneType>, num_lanes: u64) -> BoundInstruction {
|
||||
bind_vector(self.inst, lane_type.into(), num_lanes, self.value_types)
|
||||
}
|
||||
@@ -1043,6 +1051,13 @@ impl InstSpec {
|
||||
InstSpec::Bound(inst) => inst.clone().bind(lane_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind_ref(&self, reference_type: impl Into<ReferenceType>) -> BoundInstruction {
|
||||
match self {
|
||||
InstSpec::Inst(inst) => inst.bind_ref(reference_type),
|
||||
InstSpec::Bound(inst) => inst.clone().bind_ref(reference_type),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<InstSpec> for &Instruction {
|
||||
@@ -1077,6 +1092,26 @@ fn bind(
|
||||
BoundInstruction { inst, value_types }
|
||||
}
|
||||
|
||||
/// Helper bind for reference types reused by {Bound,}Instruction::bind_ref.
|
||||
fn bind_ref(
|
||||
inst: Instruction,
|
||||
reference_type: Option<ReferenceType>,
|
||||
mut value_types: Vec<ValueTypeOrAny>,
|
||||
) -> BoundInstruction {
|
||||
match reference_type {
|
||||
Some(reference_type) => {
|
||||
value_types.push(ValueTypeOrAny::ValueType(reference_type.into()));
|
||||
}
|
||||
None => {
|
||||
value_types.push(ValueTypeOrAny::Any);
|
||||
}
|
||||
}
|
||||
|
||||
verify_polymorphic_binding(&inst, &value_types);
|
||||
|
||||
BoundInstruction { inst, value_types }
|
||||
}
|
||||
|
||||
/// Helper bind for vector types reused by {Bound,}Instruction::bind.
|
||||
fn bind_vector(
|
||||
inst: Instruction,
|
||||
|
||||
@@ -11,12 +11,14 @@ use crate::shared::types as shared_types;
|
||||
//
|
||||
// 0: Void
|
||||
// 0x01-0x6f: Special types
|
||||
// 0x70-0x7f: Lane types
|
||||
// 0x70-0x7d: Lane types
|
||||
// 0x7e-0x7f: Reference types
|
||||
// 0x80-0xff: Vector types
|
||||
//
|
||||
// Vector types are encoded with the lane type in the low 4 bits and log2(lanes)
|
||||
// in the high 4 bits, giving a range of 2-256 lanes.
|
||||
static LANE_BASE: u8 = 0x70;
|
||||
static REFERENCE_BASE: u8 = 0x7E;
|
||||
|
||||
// Rust name prefix used for the `rust_name` method.
|
||||
static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
||||
@@ -31,6 +33,7 @@ static _RUST_NAME_PREFIX: &'static str = "ir::types::";
|
||||
pub enum ValueType {
|
||||
BV(BVType),
|
||||
Lane(LaneType),
|
||||
Reference(ReferenceType),
|
||||
Special(SpecialType),
|
||||
Vector(VectorType),
|
||||
}
|
||||
@@ -46,11 +49,16 @@ impl ValueType {
|
||||
SpecialTypeIterator::new()
|
||||
}
|
||||
|
||||
pub fn all_reference_types() -> ReferenceTypeIterator {
|
||||
ReferenceTypeIterator::new()
|
||||
}
|
||||
|
||||
/// Return a string containing the documentation comment for this type.
|
||||
pub fn doc(&self) -> String {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.doc(),
|
||||
ValueType::Lane(l) => l.doc(),
|
||||
ValueType::Reference(r) => r.doc(),
|
||||
ValueType::Special(s) => s.doc(),
|
||||
ValueType::Vector(ref v) => v.doc(),
|
||||
}
|
||||
@@ -61,6 +69,7 @@ impl ValueType {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.lane_bits(),
|
||||
ValueType::Lane(l) => l.lane_bits(),
|
||||
ValueType::Reference(r) => r.lane_bits(),
|
||||
ValueType::Special(s) => s.lane_bits(),
|
||||
ValueType::Vector(ref v) => v.lane_bits(),
|
||||
}
|
||||
@@ -84,6 +93,7 @@ impl ValueType {
|
||||
match *self {
|
||||
ValueType::BV(_) => None,
|
||||
ValueType::Lane(l) => Some(l.number()),
|
||||
ValueType::Reference(r) => Some(r.number()),
|
||||
ValueType::Special(s) => Some(s.number()),
|
||||
ValueType::Vector(ref v) => Some(v.number()),
|
||||
}
|
||||
@@ -112,6 +122,7 @@ impl fmt::Display for ValueType {
|
||||
match *self {
|
||||
ValueType::BV(ref b) => b.fmt(f),
|
||||
ValueType::Lane(l) => l.fmt(f),
|
||||
ValueType::Reference(r) => r.fmt(f),
|
||||
ValueType::Special(s) => s.fmt(f),
|
||||
ValueType::Vector(ref v) => v.fmt(f),
|
||||
}
|
||||
@@ -132,6 +143,13 @@ impl From<LaneType> for ValueType {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given reference type.
|
||||
impl From<ReferenceType> for ValueType {
|
||||
fn from(reference: ReferenceType) -> Self {
|
||||
ValueType::Reference(reference)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ValueType from a given special type.
|
||||
impl From<SpecialType> for ValueType {
|
||||
fn from(spec: SpecialType) -> Self {
|
||||
@@ -515,3 +533,83 @@ impl Iterator for SpecialTypeIterator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference type is scalar type, but not lane type.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ReferenceType(pub shared_types::Reference);
|
||||
|
||||
impl ReferenceType {
|
||||
/// Return a string containing the documentation comment for this reference type.
|
||||
pub fn doc(self) -> String {
|
||||
format!("An opaque reference type with {} bits.", self.lane_bits())
|
||||
}
|
||||
|
||||
/// Return the number of bits in a lane.
|
||||
pub fn lane_bits(self) -> u64 {
|
||||
match self.0 {
|
||||
shared_types::Reference::R32 => 32,
|
||||
shared_types::Reference::R64 => 64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the unique number associated with this reference type.
|
||||
pub fn number(self) -> u8 {
|
||||
REFERENCE_BASE
|
||||
+ match self {
|
||||
ReferenceType(shared_types::Reference::R32) => 0,
|
||||
ReferenceType(shared_types::Reference::R64) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ref_from_bits(num_bits: u16) -> ReferenceType {
|
||||
ReferenceType(match num_bits {
|
||||
32 => shared_types::Reference::R32,
|
||||
64 => shared_types::Reference::R64,
|
||||
_ => unreachable!("unexpected number of bits for a reference type"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ReferenceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "r{}", self.lane_bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ReferenceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ReferenceType(bits={})", self.lane_bits())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a ReferenceType from a given reference variant.
|
||||
impl From<shared_types::Reference> for ReferenceType {
|
||||
fn from(r: shared_types::Reference) -> Self {
|
||||
ReferenceType(r)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator for different reference types.
|
||||
pub struct ReferenceTypeIterator {
|
||||
reference_iter: shared_types::ReferenceIterator,
|
||||
}
|
||||
|
||||
impl ReferenceTypeIterator {
|
||||
/// Create a new reference type iterator.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
reference_iter: shared_types::ReferenceIterator::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ReferenceTypeIterator {
|
||||
type Item = ReferenceType;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(r) = self.reference_iter.next() {
|
||||
Some(ReferenceType::from(r))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::iter::FromIterator;
|
||||
use std::ops;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::cdsl::types::{BVType, LaneType, SpecialType, ValueType};
|
||||
use crate::cdsl::types::{BVType, LaneType, ReferenceType, SpecialType, ValueType};
|
||||
|
||||
const MAX_LANES: u16 = 256;
|
||||
const MAX_BITS: u16 = 64;
|
||||
@@ -64,6 +64,10 @@ impl TypeVar {
|
||||
ValueType::Special(special_type) => {
|
||||
return TypeVar::new(name, doc, builder.specials(vec![special_type]).build());
|
||||
}
|
||||
ValueType::Reference(ReferenceType(reference_type)) => {
|
||||
let bits = reference_type as RangeBound;
|
||||
return TypeVar::new(name, doc, builder.refs(bits..bits).build());
|
||||
}
|
||||
ValueType::Lane(lane_type) => (lane_type, 1),
|
||||
ValueType::Vector(vec_type) => {
|
||||
(vec_type.lane_type(), vec_type.lane_count() as RangeBound)
|
||||
@@ -406,6 +410,7 @@ pub struct TypeSet {
|
||||
pub ints: NumSet,
|
||||
pub floats: NumSet,
|
||||
pub bools: NumSet,
|
||||
pub refs: NumSet,
|
||||
pub bitvecs: NumSet,
|
||||
pub specials: Vec<SpecialType>,
|
||||
}
|
||||
@@ -416,6 +421,7 @@ impl TypeSet {
|
||||
ints: NumSet,
|
||||
floats: NumSet,
|
||||
bools: NumSet,
|
||||
refs: NumSet,
|
||||
bitvecs: NumSet,
|
||||
specials: Vec<SpecialType>,
|
||||
) -> Self {
|
||||
@@ -424,6 +430,7 @@ impl TypeSet {
|
||||
ints,
|
||||
floats,
|
||||
bools,
|
||||
refs,
|
||||
bitvecs,
|
||||
specials,
|
||||
}
|
||||
@@ -432,7 +439,11 @@ impl TypeSet {
|
||||
/// Return the number of concrete types represented by this typeset.
|
||||
pub fn size(&self) -> usize {
|
||||
self.lanes.len()
|
||||
* (self.ints.len() + self.floats.len() + self.bools.len() + self.bitvecs.len())
|
||||
* (self.ints.len()
|
||||
+ self.floats.len()
|
||||
+ self.bools.len()
|
||||
+ self.refs.len()
|
||||
+ self.bitvecs.len())
|
||||
+ self.specials.len()
|
||||
}
|
||||
|
||||
@@ -462,6 +473,7 @@ impl TypeSet {
|
||||
let mut copy = self.clone();
|
||||
copy.ints = NumSet::new();
|
||||
copy.floats = NumSet::new();
|
||||
copy.refs = NumSet::new();
|
||||
copy.bitvecs = NumSet::new();
|
||||
if (&self.lanes - &num_set![1]).len() > 0 {
|
||||
copy.bools = &self.ints | &self.floats;
|
||||
@@ -544,6 +556,7 @@ impl TypeSet {
|
||||
copy.ints = NumSet::new();
|
||||
copy.bools = NumSet::new();
|
||||
copy.floats = NumSet::new();
|
||||
copy.refs = NumSet::new();
|
||||
copy.bitvecs = self
|
||||
.lanes
|
||||
.iter()
|
||||
@@ -568,6 +581,9 @@ impl TypeSet {
|
||||
for &bits in &self.bools {
|
||||
ret.push(LaneType::bool_from_bits(bits).by(num_lanes));
|
||||
}
|
||||
for &bits in &self.refs {
|
||||
ret.push(ReferenceType::ref_from_bits(bits).into());
|
||||
}
|
||||
for &bits in &self.bitvecs {
|
||||
assert_eq!(num_lanes, 1);
|
||||
ret.push(BVType::new(bits).into());
|
||||
@@ -630,6 +646,7 @@ impl TypeSet {
|
||||
let mut ints = range_to_set(Some(8..MAX_BITS));
|
||||
let mut floats = range_to_set(Some(32..64));
|
||||
let mut bools = range_to_set(Some(1..MAX_BITS));
|
||||
let refs = range_to_set(Some(32..64));
|
||||
|
||||
for &l in &all_lanes {
|
||||
for &i in &all_ints {
|
||||
@@ -654,7 +671,7 @@ impl TypeSet {
|
||||
|
||||
let bitvecs = NumSet::new();
|
||||
let specials = Vec::new();
|
||||
TypeSet::new(lanes, ints, floats, bools, bitvecs, specials)
|
||||
TypeSet::new(lanes, ints, floats, bools, refs, bitvecs, specials)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -664,6 +681,7 @@ impl TypeSet {
|
||||
self.ints = &self.ints & &other.ints;
|
||||
self.floats = &self.floats & &other.floats;
|
||||
self.bools = &self.bools & &other.bools;
|
||||
self.refs = &self.refs & &other.refs;
|
||||
self.bitvecs = &self.bitvecs & &other.bitvecs;
|
||||
|
||||
let mut new_specials = Vec::new();
|
||||
@@ -680,6 +698,7 @@ impl TypeSet {
|
||||
&& self.ints.is_subset(&other.ints)
|
||||
&& self.floats.is_subset(&other.floats)
|
||||
&& self.bools.is_subset(&other.bools)
|
||||
&& self.refs.is_subset(&other.refs)
|
||||
&& self.bitvecs.is_subset(&other.bitvecs)
|
||||
&& {
|
||||
let specials: HashSet<SpecialType> = HashSet::from_iter(self.specials.clone());
|
||||
@@ -692,12 +711,14 @@ impl TypeSet {
|
||||
set_wider_or_equal(&self.ints, &other.ints)
|
||||
&& set_wider_or_equal(&self.floats, &other.floats)
|
||||
&& set_wider_or_equal(&self.bools, &other.bools)
|
||||
&& set_wider_or_equal(&self.refs, &other.refs)
|
||||
}
|
||||
|
||||
pub fn is_narrower(&self, other: &TypeSet) -> bool {
|
||||
set_narrower(&self.ints, &other.ints)
|
||||
&& set_narrower(&self.floats, &other.floats)
|
||||
&& set_narrower(&self.bools, &other.bools)
|
||||
&& set_narrower(&self.refs, &other.refs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -738,6 +759,12 @@ impl fmt::Debug for TypeSet {
|
||||
Vec::from_iter(self.bools.iter().map(|x| x.to_string())).join(", ")
|
||||
));
|
||||
}
|
||||
if !self.refs.is_empty() {
|
||||
subsets.push(format!(
|
||||
"refs={{{}}}",
|
||||
Vec::from_iter(self.refs.iter().map(|x| x.to_string())).join(", ")
|
||||
));
|
||||
}
|
||||
if !self.bitvecs.is_empty() {
|
||||
subsets.push(format!(
|
||||
"bitvecs={{{}}}",
|
||||
@@ -760,6 +787,7 @@ pub struct TypeSetBuilder {
|
||||
ints: Interval,
|
||||
floats: Interval,
|
||||
bools: Interval,
|
||||
refs: Interval,
|
||||
bitvecs: Interval,
|
||||
includes_scalars: bool,
|
||||
simd_lanes: Interval,
|
||||
@@ -772,6 +800,7 @@ impl TypeSetBuilder {
|
||||
ints: Interval::None,
|
||||
floats: Interval::None,
|
||||
bools: Interval::None,
|
||||
refs: Interval::None,
|
||||
bitvecs: Interval::None,
|
||||
includes_scalars: true,
|
||||
simd_lanes: Interval::None,
|
||||
@@ -794,6 +823,11 @@ impl TypeSetBuilder {
|
||||
self.bools = interval.into();
|
||||
self
|
||||
}
|
||||
pub fn refs(mut self, interval: impl Into<Interval>) -> Self {
|
||||
assert!(self.refs == Interval::None);
|
||||
self.refs = interval.into();
|
||||
self
|
||||
}
|
||||
pub fn includes_scalars(mut self, includes_scalars: bool) -> Self {
|
||||
self.includes_scalars = includes_scalars;
|
||||
self
|
||||
@@ -827,6 +861,7 @@ impl TypeSetBuilder {
|
||||
range_to_set(self.ints.to_range(8..MAX_BITS, None)),
|
||||
range_to_set(self.floats.to_range(32..64, None)),
|
||||
bools,
|
||||
range_to_set(self.refs.to_range(32..64, None)),
|
||||
range_to_set(self.bitvecs.to_range(1..MAX_BITVEC, None)),
|
||||
self.specials,
|
||||
)
|
||||
@@ -837,6 +872,7 @@ impl TypeSetBuilder {
|
||||
.ints(Interval::All)
|
||||
.floats(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.refs(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.bitvecs(Interval::All)
|
||||
.specials(ValueType::all_special_types().collect())
|
||||
|
||||
@@ -18,10 +18,12 @@ fn gen_recipe(formats: &FormatRegistry, recipe: &EncodingRecipe, fmt: &mut Forma
|
||||
let inst_format = formats.get(recipe.format);
|
||||
let num_value_ops = inst_format.num_value_operands;
|
||||
|
||||
let want_args = recipe.operands_in.iter().any(|c| match c {
|
||||
OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
|
||||
OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
|
||||
});
|
||||
// TODO: Set want_args to true for only MultiAry instructions instead of all formats with value list.
|
||||
let want_args = inst_format.has_value_list
|
||||
|| recipe.operands_in.iter().any(|c| match c {
|
||||
OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
|
||||
OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
|
||||
});
|
||||
assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);
|
||||
|
||||
let want_outs = recipe.operands_out.iter().any(|c| match c {
|
||||
@@ -159,6 +161,7 @@ fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mu
|
||||
fmt.line("inst: Inst,");
|
||||
fmt.line("_divert: &mut RegDiversions,");
|
||||
fmt.line("_sink: &mut CS,");
|
||||
fmt.line("_isa: &dyn TargetIsa,");
|
||||
});
|
||||
fmt.line(") {");
|
||||
fmt.indent(|fmt| {
|
||||
@@ -176,6 +179,7 @@ fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mu
|
||||
fmt.line("inst: Inst,");
|
||||
fmt.line("divert: &mut RegDiversions,");
|
||||
fmt.line("sink: &mut CS,");
|
||||
fmt.line("isa: &dyn TargetIsa,")
|
||||
});
|
||||
|
||||
fmt.line(") {");
|
||||
|
||||
@@ -650,6 +650,9 @@ fn typeset_to_string(ts: &TypeSet) -> String {
|
||||
if ts.specials.len() > 0 {
|
||||
result += &format!(", specials=[{}]", iterable_to_string(&ts.specials));
|
||||
}
|
||||
if ts.refs.len() > 0 {
|
||||
result += &format!(", refs={}", iterable_to_string(&ts.refs));
|
||||
}
|
||||
result += ")";
|
||||
result
|
||||
}
|
||||
@@ -677,6 +680,7 @@ pub fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter)
|
||||
gen_bitset(&ts.ints, "ints", 8, fmt);
|
||||
gen_bitset(&ts.floats, "floats", 8, fmt);
|
||||
gen_bitset(&ts.bools, "bools", 8, fmt);
|
||||
gen_bitset(&ts.refs, "refs", 8, fmt);
|
||||
});
|
||||
fmt.line("},");
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ fn emit_types(fmt: &mut srcgen::Formatter) -> Result<(), error::Error> {
|
||||
emit_type(&ty, fmt)?;
|
||||
}
|
||||
|
||||
// Emit all reference types.
|
||||
for ty in cdsl_types::ValueType::all_reference_types().map(cdsl_types::ValueType::from) {
|
||||
emit_type(&ty, fmt)?;
|
||||
}
|
||||
|
||||
// Emit vector definitions for common SIMD sizes.
|
||||
for vec_size in &[64_u64, 128, 256, 512] {
|
||||
emit_vectors(*vec_size, fmt)?;
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::cdsl::types::ValueType;
|
||||
use crate::shared::types::Bool::{B1, B16, B32, B64, B8};
|
||||
use crate::shared::types::Float::{F32, F64};
|
||||
use crate::shared::types::Int::{I16, I32, I64, I8};
|
||||
use crate::shared::types::Reference::{R32, R64};
|
||||
use crate::shared::Definitions as SharedDefinitions;
|
||||
|
||||
use super::recipes::{RecipeGroup, Template};
|
||||
@@ -175,6 +176,20 @@ impl PerCpuModeEncodings {
|
||||
});
|
||||
}
|
||||
|
||||
/// Add encodings for `inst.r32` to X86_32.
|
||||
/// Add encodings for `inst.r32` to X86_64 with and without REX.
|
||||
/// Add encodings for `inst.r64` to X86_64 with a REX.W prefix.
|
||||
fn enc_r32_r64(&mut self, inst: impl Into<InstSpec>, template: Template) {
|
||||
let inst: InstSpec = inst.into();
|
||||
self.enc32(inst.bind_ref(R32), template.nonrex());
|
||||
|
||||
// REX-less encoding must come after REX encoding so we don't use it by default. Otherwise
|
||||
// reg-alloc would never use r8 and up.
|
||||
self.enc64(inst.bind_ref(R32), template.rex());
|
||||
self.enc64(inst.bind_ref(R32), template.nonrex());
|
||||
self.enc64(inst.bind_ref(R64), template.rex().w());
|
||||
}
|
||||
|
||||
/// Add encodings for `inst` to X86_64 with and without a REX prefix.
|
||||
fn enc_x86_64(&mut self, inst: impl Into<InstSpec> + Clone, template: Template) {
|
||||
// See above comment about the ordering of rex vs non-rex encodings.
|
||||
@@ -331,6 +346,7 @@ pub fn define(
|
||||
let ireduce = shared.by_name("ireduce");
|
||||
let ishl = shared.by_name("ishl");
|
||||
let ishl_imm = shared.by_name("ishl_imm");
|
||||
let is_null = shared.by_name("is_null");
|
||||
let istore16 = shared.by_name("istore16");
|
||||
let istore16_complex = shared.by_name("istore16_complex");
|
||||
let istore32 = shared.by_name("istore32");
|
||||
@@ -344,6 +360,7 @@ pub fn define(
|
||||
let load = shared.by_name("load");
|
||||
let load_complex = shared.by_name("load_complex");
|
||||
let nearest = shared.by_name("nearest");
|
||||
let null = shared.by_name("null");
|
||||
let popcnt = shared.by_name("popcnt");
|
||||
let raw_bitcast = shared.by_name("raw_bitcast");
|
||||
let regfill = shared.by_name("regfill");
|
||||
@@ -354,6 +371,7 @@ pub fn define(
|
||||
let rotl_imm = shared.by_name("rotl_imm");
|
||||
let rotr = shared.by_name("rotr");
|
||||
let rotr_imm = shared.by_name("rotr_imm");
|
||||
let safepoint = shared.by_name("safepoint");
|
||||
let scalar_to_vector = shared.by_name("scalar_to_vector");
|
||||
let selectif = shared.by_name("selectif");
|
||||
let sextend = shared.by_name("sextend");
|
||||
@@ -374,6 +392,7 @@ pub fn define(
|
||||
let trap = shared.by_name("trap");
|
||||
let trapff = shared.by_name("trapff");
|
||||
let trapif = shared.by_name("trapif");
|
||||
let resumable_trap = shared.by_name("resumable_trap");
|
||||
let trueff = shared.by_name("trueff");
|
||||
let trueif = shared.by_name("trueif");
|
||||
let trunc = shared.by_name("trunc");
|
||||
@@ -455,6 +474,7 @@ pub fn define(
|
||||
let rec_icscc_ib = r.template("icscc_ib");
|
||||
let rec_icscc_id = r.template("icscc_id");
|
||||
let rec_indirect_jmp = r.template("indirect_jmp");
|
||||
let rec_is_zero = r.template("is_zero");
|
||||
let rec_jmpb = r.template("jmpb");
|
||||
let rec_jmpd = r.template("jmpd");
|
||||
let rec_jt_base = r.template("jt_base");
|
||||
@@ -473,6 +493,7 @@ pub fn define(
|
||||
let rec_popq = r.template("popq");
|
||||
let rec_pu_id = r.template("pu_id");
|
||||
let rec_pu_id_bool = r.template("pu_id_bool");
|
||||
let rec_pu_id_ref = r.template("pu_id_ref");
|
||||
let rec_pu_iq = r.template("pu_iq");
|
||||
let rec_pushq = r.template("pushq");
|
||||
let rec_ret = r.template("ret");
|
||||
@@ -492,6 +513,7 @@ pub fn define(
|
||||
let rec_rmov = r.template("rmov");
|
||||
let rec_rr = r.template("rr");
|
||||
let rec_rrx = r.template("rrx");
|
||||
let rec_safepoint = r.recipe("safepoint");
|
||||
let rec_setf_abcd = r.template("setf_abcd");
|
||||
let rec_seti_abcd = r.template("seti_abcd");
|
||||
let rec_spaddr4_id = r.template("spaddr4_id");
|
||||
@@ -566,6 +588,7 @@ pub fn define(
|
||||
e.enc_i32_i64(x86_umulx, rec_mulx.opcodes(vec![0xf7]).rrr(4));
|
||||
|
||||
e.enc_i32_i64(copy, rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_r32_r64(copy, rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_both(copy.bind(B1), rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_both(copy.bind(I8), rec_umr.opcodes(vec![0x89]));
|
||||
e.enc_both(copy.bind(I16), rec_umr.opcodes(vec![0x89]));
|
||||
@@ -579,6 +602,12 @@ pub fn define(
|
||||
e.enc64(regmove.bind(I64), rec_rmov.opcodes(vec![0x89]).rex().w());
|
||||
e.enc_both(regmove.bind(B1), rec_rmov.opcodes(vec![0x89]));
|
||||
e.enc_both(regmove.bind(I8), rec_rmov.opcodes(vec![0x89]));
|
||||
e.enc32(regmove.bind_ref(R32), rec_rmov.opcodes(vec![0x89]));
|
||||
e.enc64(regmove.bind_ref(R32), rec_rmov.opcodes(vec![0x89]).rex());
|
||||
e.enc64(
|
||||
regmove.bind_ref(R64),
|
||||
rec_rmov.opcodes(vec![0x89]).rex().w(),
|
||||
);
|
||||
|
||||
e.enc_i32_i64(iadd_imm, rec_r_ib.opcodes(vec![0x83]).rrr(0));
|
||||
e.enc_i32_i64(iadd_imm, rec_r_id.opcodes(vec![0x81]).rrr(0));
|
||||
@@ -841,6 +870,8 @@ pub fn define(
|
||||
|
||||
e.enc_i32_i64(spill, rec_spillSib32.opcodes(vec![0x89]));
|
||||
e.enc_i32_i64(regspill, rec_regspill32.opcodes(vec![0x89]));
|
||||
e.enc_r32_r64(spill, rec_spillSib32.opcodes(vec![0x89]));
|
||||
e.enc_r32_r64(regspill, rec_regspill32.opcodes(vec![0x89]));
|
||||
|
||||
// Use a 32-bit write for spilling `b1`, `i8` and `i16` to avoid
|
||||
// constraining the permitted registers.
|
||||
@@ -865,6 +896,8 @@ pub fn define(
|
||||
|
||||
e.enc_i32_i64(fill, rec_fillSib32.opcodes(vec![0x8b]));
|
||||
e.enc_i32_i64(regfill, rec_regfill32.opcodes(vec![0x8b]));
|
||||
e.enc_r32_r64(fill, rec_fillSib32.opcodes(vec![0x8b]));
|
||||
e.enc_r32_r64(regfill, rec_regfill32.opcodes(vec![0x8b]));
|
||||
|
||||
// Load 32 bits from `b1`, `i8` and `i16` spill slots. See `spill.b1` above.
|
||||
|
||||
@@ -1248,6 +1281,8 @@ pub fn define(
|
||||
// Trap as ud2
|
||||
e.enc32(trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
e.enc64(trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
e.enc32(resumable_trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
e.enc64(resumable_trap, rec_trap.opcodes(vec![0x0f, 0x0b]));
|
||||
|
||||
// Debug trap as int3
|
||||
e.enc32_rec(debugtrap, rec_debugtrap, 0);
|
||||
@@ -1666,5 +1701,20 @@ pub fn define(
|
||||
}
|
||||
}
|
||||
|
||||
// Reference type instructions
|
||||
|
||||
// Null references implemented as iconst 0.
|
||||
e.enc32(null.bind_ref(R32), rec_pu_id_ref.opcodes(vec![0xb8]));
|
||||
|
||||
e.enc64(null.bind_ref(R64), rec_pu_id_ref.rex().opcodes(vec![0xb8]));
|
||||
e.enc64(null.bind_ref(R64), rec_pu_id_ref.opcodes(vec![0xb8]));
|
||||
|
||||
// is_null, implemented by testing whether the value is 0.
|
||||
e.enc_r32_r64(is_null, rec_is_zero.opcodes(vec![0x85]));
|
||||
|
||||
// safepoint instruction calls sink, no actual encoding.
|
||||
e.enc32_rec(safepoint, rec_safepoint, 0);
|
||||
e.enc64_rec(safepoint, rec_safepoint, 0);
|
||||
|
||||
e
|
||||
}
|
||||
|
||||
@@ -888,6 +888,20 @@ pub fn define<'shared>(
|
||||
),
|
||||
);
|
||||
|
||||
// XX+rd id nullary with 0 as 32-bit immediate. Note no recipe predicate.
|
||||
recipes.add_template_recipe(
|
||||
EncodingRecipeBuilder::new("pu_id_ref", f_nullary, 4)
|
||||
.operands_out(vec![gpr])
|
||||
.emit(
|
||||
r#"
|
||||
// The destination register is encoded in the low bits of the opcode.
|
||||
// No ModR/M.
|
||||
{{PUT_OP}}(bits | (out_reg0 & 7), rex1(out_reg0), sink);
|
||||
sink.put4(0);
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
// XX+rd iq unary with 64-bit immediate.
|
||||
recipes.add_template_recipe(
|
||||
EncodingRecipeBuilder::new("pu_iq", f_unary_imm, 8)
|
||||
@@ -2851,5 +2865,28 @@ pub fn define<'shared>(
|
||||
),
|
||||
);
|
||||
|
||||
recipes.add_template_recipe(
|
||||
EncodingRecipeBuilder::new("is_zero", f_unary, 2 + 2)
|
||||
.operands_in(vec![gpr])
|
||||
.operands_out(vec![abcd])
|
||||
.emit(
|
||||
r#"
|
||||
// Test instruction.
|
||||
{{PUT_OP}}(bits, rex2(in_reg0, in_reg0), sink);
|
||||
modrm_rr(in_reg0, in_reg0, sink);
|
||||
// Check ZF = 1 flag to see if register holds 0.
|
||||
sink.put1(0x0f);
|
||||
sink.put1(0x94);
|
||||
modrm_rr(out_reg0, 0, sink);
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
recipes.add_recipe(EncodingRecipeBuilder::new("safepoint", f_multiary, 0).emit(
|
||||
r#"
|
||||
sink.add_stackmap(args, func, isa);
|
||||
"#,
|
||||
));
|
||||
|
||||
recipes
|
||||
}
|
||||
|
||||
@@ -85,6 +85,12 @@ pub fn define(
|
||||
TypeSetBuilder::new().ints(32..64).build(),
|
||||
);
|
||||
|
||||
let Ref = &TypeVar::new(
|
||||
"Ref",
|
||||
"A scalar reference type",
|
||||
TypeSetBuilder::new().refs(Interval::All).build(),
|
||||
);
|
||||
|
||||
let Testable = &TypeVar::new(
|
||||
"Testable",
|
||||
"A scalar boolean or integer type",
|
||||
@@ -118,11 +124,12 @@ pub fn define(
|
||||
|
||||
let Any = &TypeVar::new(
|
||||
"Any",
|
||||
"Any integer, float, or boolean scalar or vector type",
|
||||
"Any integer, float, boolean, or reference scalar or vector type",
|
||||
TypeSetBuilder::new()
|
||||
.ints(Interval::All)
|
||||
.floats(Interval::All)
|
||||
.bools(Interval::All)
|
||||
.refs(Interval::All)
|
||||
.simd_lanes(Interval::All)
|
||||
.includes_scalars(true)
|
||||
.build(),
|
||||
@@ -394,6 +401,19 @@ pub fn define(
|
||||
.can_trap(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"resumable_trap",
|
||||
r#"
|
||||
A resumable trap.
|
||||
|
||||
This instruction allows non-conditional traps to be used as non-terminal instructions.
|
||||
"#,
|
||||
)
|
||||
.operands_in(vec![code])
|
||||
.can_trap(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"trapnz",
|
||||
@@ -1068,6 +1088,20 @@ pub fn define(
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let a = &operand_doc("a", Ref, "A constant reference null value");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"null",
|
||||
r#"
|
||||
Null constant value for reference types.
|
||||
|
||||
Create a scalar reference SSA value with a constant null value.
|
||||
"#,
|
||||
)
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
ig.push(Inst::new(
|
||||
"nop",
|
||||
r#"
|
||||
@@ -1315,6 +1349,24 @@ pub fn define(
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let N = &operand_doc(
|
||||
"args",
|
||||
variable_args,
|
||||
"Variable number of args for Stackmap",
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"safepoint",
|
||||
r#"
|
||||
This instruction will provide live reference values at a point in
|
||||
the function. It can only be used by the compiler.
|
||||
"#,
|
||||
)
|
||||
.operands_in(vec![N])
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let x = &operand_doc("x", TxN, "Vector to split");
|
||||
let lo = &operand_doc("lo", &TxN.half_vector(), "Low-numbered lanes of `x`");
|
||||
let hi = &operand_doc("hi", &TxN.half_vector(), "High-numbered lanes of `x`");
|
||||
@@ -2578,6 +2630,23 @@ pub fn define(
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let a = &operand("a", b1);
|
||||
let x = &operand("x", Ref);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"is_null",
|
||||
r#"
|
||||
Reference verification.
|
||||
|
||||
The condition code determines if the reference type in question is
|
||||
null or not.
|
||||
"#,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let Cond = &operand("Cond", intcc);
|
||||
let f = &operand("f", iflags);
|
||||
let a = &operand("a", b1);
|
||||
|
||||
@@ -92,6 +92,18 @@ pub fn define() -> SettingGroup {
|
||||
true,
|
||||
);
|
||||
|
||||
settings.add_bool(
|
||||
"enable_safepoints",
|
||||
r#"
|
||||
Enable safepoint instruction insertions.
|
||||
|
||||
This will allow the emit_stackmaps() function to insert the safepoint
|
||||
instruction on top of calls and interrupt traps in order to display the
|
||||
live reference values at that point in the program.
|
||||
"#,
|
||||
false,
|
||||
);
|
||||
|
||||
// Settings specific to the `baldrdash` calling convention.
|
||||
|
||||
settings.add_enum(
|
||||
|
||||
@@ -145,6 +145,38 @@ impl Iterator for FlagIterator {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum Reference {
|
||||
/// 32-bit reference.
|
||||
R32 = 32,
|
||||
/// 64-bit reference.
|
||||
R64 = 64,
|
||||
}
|
||||
|
||||
/// This provides an iterator through all of the supported reference variants.
|
||||
pub struct ReferenceIterator {
|
||||
index: u8,
|
||||
}
|
||||
|
||||
impl ReferenceIterator {
|
||||
pub fn new() -> Self {
|
||||
Self { index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ReferenceIterator {
|
||||
type Item = Reference;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let res = match self.index {
|
||||
0 => Some(Reference::R32),
|
||||
1 => Some(Reference::R64),
|
||||
_ => return None,
|
||||
};
|
||||
self.index += 1;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod iter_tests {
|
||||
use super::*;
|
||||
@@ -185,4 +217,12 @@ mod iter_tests {
|
||||
assert_eq!(flag_iter.next(), Some(Flag::FFlags));
|
||||
assert_eq!(flag_iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reference_iter_works() {
|
||||
let mut reference_iter = ReferenceIterator::new();
|
||||
assert_eq!(reference_iter.next(), Some(Reference::R32));
|
||||
assert_eq!(reference_iter.next(), Some(Reference::R64));
|
||||
assert_eq!(reference_iter.next(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
//! 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
|
||||
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.
|
||||
|
||||
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;
|
||||
|
||||
/// A `CodeSink` that writes binary machine code directly into memory.
|
||||
@@ -36,6 +38,7 @@ pub struct MemoryCodeSink<'a> {
|
||||
offset: isize,
|
||||
relocs: &'a mut dyn RelocSink,
|
||||
traps: &'a mut dyn TrapSink,
|
||||
stackmaps: &'a mut dyn StackmapSink,
|
||||
/// Information about the generated code and read-only data.
|
||||
pub info: CodeInfo,
|
||||
}
|
||||
@@ -49,6 +52,7 @@ impl<'a> MemoryCodeSink<'a> {
|
||||
data: *mut u8,
|
||||
relocs: &'a mut dyn RelocSink,
|
||||
traps: &'a mut dyn TrapSink,
|
||||
stackmaps: &'a mut dyn StackmapSink,
|
||||
) -> Self {
|
||||
Self {
|
||||
data,
|
||||
@@ -61,6 +65,7 @@ impl<'a> MemoryCodeSink<'a> {
|
||||
},
|
||||
relocs,
|
||||
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.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
|
||||
@@ -158,3 +169,16 @@ pub struct NullTrapSink {}
|
||||
impl TrapSink for NullTrapSink {
|
||||
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 relaxation;
|
||||
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::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::isa::TargetIsa;
|
||||
pub use crate::regalloc::RegDiversions;
|
||||
use core::fmt;
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -141,6 +146,9 @@ pub trait CodeSink {
|
||||
|
||||
/// Read-only data output is complete, we're done.
|
||||
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.
|
||||
@@ -157,17 +165,17 @@ pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
|
||||
///
|
||||
/// This function is called from the `TargetIsa::emit_function()` implementations with the
|
||||
/// 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
|
||||
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();
|
||||
for ebb in func.layout.ebbs() {
|
||||
divert.clear();
|
||||
debug_assert_eq!(func.offsets[ebb], sink.offset());
|
||||
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.
|
||||
|
||||
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::dominator_tree::DominatorTree;
|
||||
@@ -100,12 +101,14 @@ impl Context {
|
||||
mem: &mut Vec<u8>,
|
||||
relocs: &mut dyn RelocSink,
|
||||
traps: &mut dyn TrapSink,
|
||||
stackmaps: &mut dyn StackmapSink,
|
||||
) -> CodegenResult<CodeInfo> {
|
||||
let info = self.compile(isa)?;
|
||||
let old_len = mem.len();
|
||||
mem.resize(old_len + info.total_size as usize, 0);
|
||||
let new_info =
|
||||
unsafe { self.emit_to_memory(isa, mem.as_mut_ptr().add(old_len), relocs, traps) };
|
||||
let new_info = unsafe {
|
||||
self.emit_to_memory(isa, mem.as_mut_ptr().add(old_len), relocs, traps, stackmaps)
|
||||
};
|
||||
debug_assert!(new_info == info);
|
||||
Ok(info)
|
||||
}
|
||||
@@ -168,9 +171,10 @@ impl Context {
|
||||
mem: *mut u8,
|
||||
relocs: &mut dyn RelocSink,
|
||||
traps: &mut dyn TrapSink,
|
||||
stackmaps: &mut dyn StackmapSink,
|
||||
) -> CodeInfo {
|
||||
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);
|
||||
sink.info
|
||||
}
|
||||
|
||||
@@ -444,6 +444,8 @@ pub struct ValueTypeSet {
|
||||
pub floats: BitSet8,
|
||||
/// Allowed bool widths
|
||||
pub bools: BitSet8,
|
||||
/// Allowed ref widths
|
||||
pub refs: BitSet8,
|
||||
}
|
||||
|
||||
impl ValueTypeSet {
|
||||
@@ -458,6 +460,8 @@ impl ValueTypeSet {
|
||||
self.floats.contains(l2b)
|
||||
} else if scalar.is_bool() {
|
||||
self.bools.contains(l2b)
|
||||
} else if scalar.is_ref() {
|
||||
self.refs.contains(l2b)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -652,6 +656,7 @@ mod tests {
|
||||
ints: BitSet8::from_range(4, 7),
|
||||
floats: BitSet8::from_range(0, 0),
|
||||
bools: BitSet8::from_range(3, 7),
|
||||
refs: BitSet8::from_range(5, 7),
|
||||
};
|
||||
assert!(!vts.contains(I8));
|
||||
assert!(vts.contains(I32));
|
||||
@@ -661,6 +666,8 @@ mod tests {
|
||||
assert!(!vts.contains(B1));
|
||||
assert!(vts.contains(B8));
|
||||
assert!(vts.contains(B64));
|
||||
assert!(vts.contains(R32));
|
||||
assert!(vts.contains(R64));
|
||||
assert_eq!(vts.example().to_string(), "i32");
|
||||
|
||||
let vts = ValueTypeSet {
|
||||
@@ -668,6 +675,7 @@ mod tests {
|
||||
ints: BitSet8::from_range(0, 0),
|
||||
floats: BitSet8::from_range(5, 7),
|
||||
bools: BitSet8::from_range(3, 7),
|
||||
refs: BitSet8::from_range(0, 0),
|
||||
};
|
||||
assert_eq!(vts.example().to_string(), "f32");
|
||||
|
||||
@@ -676,6 +684,7 @@ mod tests {
|
||||
ints: BitSet8::from_range(0, 0),
|
||||
floats: BitSet8::from_range(5, 7),
|
||||
bools: BitSet8::from_range(3, 7),
|
||||
refs: BitSet8::from_range(0, 0),
|
||||
};
|
||||
assert_eq!(vts.example().to_string(), "f32x2");
|
||||
|
||||
@@ -684,6 +693,7 @@ mod tests {
|
||||
ints: BitSet8::from_range(0, 0),
|
||||
floats: BitSet8::from_range(0, 0),
|
||||
bools: BitSet8::from_range(3, 7),
|
||||
refs: BitSet8::from_range(0, 0),
|
||||
};
|
||||
assert!(!vts.contains(B32X2));
|
||||
assert!(vts.contains(B32X4));
|
||||
@@ -695,8 +705,11 @@ mod tests {
|
||||
ints: BitSet8::from_range(3, 7),
|
||||
floats: BitSet8::from_range(0, 0),
|
||||
bools: BitSet8::from_range(0, 0),
|
||||
refs: BitSet8::from_range(0, 0),
|
||||
};
|
||||
assert!(vts.contains(I32));
|
||||
assert!(vts.contains(I32X4));
|
||||
assert!(!vts.contains(R32));
|
||||
assert!(!vts.contains(R64));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ impl Type {
|
||||
B1 => 0,
|
||||
B8 | I8 => 3,
|
||||
B16 | I16 => 4,
|
||||
B32 | I32 | F32 => 5,
|
||||
B64 | I64 | F64 => 6,
|
||||
B32 | I32 | F32 | R32 => 5,
|
||||
B64 | I64 | F64 | R64 => 6,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
@@ -73,8 +73,8 @@ impl Type {
|
||||
B1 => 1,
|
||||
B8 | I8 => 8,
|
||||
B16 | I16 => 16,
|
||||
B32 | I32 | F32 => 32,
|
||||
B64 | I64 | F64 => 64,
|
||||
B32 | I32 | F32 | R32 => 32,
|
||||
B64 | I64 | F64 | R64 => 64,
|
||||
_ => 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
|
||||
/// 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.
|
||||
pub fn as_bool_pedantic(self) -> Self {
|
||||
// Replace the low 4 bits with the boolean version, preserve the high 4 bits.
|
||||
@@ -108,6 +108,7 @@ impl Type {
|
||||
B16 | I16 => B16,
|
||||
B32 | I32 | F32 => B32,
|
||||
B64 | I64 | F64 => B64,
|
||||
R32 | R64 => panic!("Reference types should not convert to bool"),
|
||||
_ => 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.
|
||||
///
|
||||
/// 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())
|
||||
} else if self.is_vector() {
|
||||
write!(f, "{}x{}", self.lane_type(), self.lane_count())
|
||||
} else if self.is_ref() {
|
||||
write!(f, "r{}", self.lane_bits())
|
||||
} else {
|
||||
f.write_str(match *self {
|
||||
IFLAGS => "iflags",
|
||||
@@ -322,6 +333,8 @@ impl Debug for Type {
|
||||
write!(f, "types::F{}", self.lane_bits())
|
||||
} else if self.is_vector() {
|
||||
write!(f, "{:?}X{}", self.lane_type(), self.lane_count())
|
||||
} else if self.is_ref() {
|
||||
write!(f, "types::R{}", self.lane_bits())
|
||||
} else {
|
||||
match *self {
|
||||
INVALID => write!(f, "types::INVALID"),
|
||||
@@ -366,6 +379,8 @@ mod tests {
|
||||
assert_eq!(B1, B1.by(8).unwrap().lane_type());
|
||||
assert_eq!(I32, I32X4.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!(IFLAGS.lane_bits(), 0);
|
||||
@@ -381,6 +396,8 @@ mod tests {
|
||||
assert_eq!(I64.lane_bits(), 64);
|
||||
assert_eq!(F32.lane_bits(), 32);
|
||||
assert_eq!(F64.lane_bits(), 64);
|
||||
assert_eq!(R32.lane_bits(), 32);
|
||||
assert_eq!(R64.lane_bits(), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -450,6 +467,8 @@ mod tests {
|
||||
assert_eq!(I64.to_string(), "i64");
|
||||
assert_eq!(F32.to_string(), "f32");
|
||||
assert_eq!(F64.to_string(), "f64");
|
||||
assert_eq!(R32.to_string(), "r32");
|
||||
assert_eq!(R64.to_string(), "r64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use crate::binemit::{bad_encoding, CodeSink};
|
||||
use crate::ir::{Function, Inst};
|
||||
use crate::isa::TargetIsa;
|
||||
use crate::regalloc::RegDiversions;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/binemit-arm32.rs"));
|
||||
|
||||
@@ -121,11 +121,11 @@ impl TargetIsa for Isa {
|
||||
divert: &mut regalloc::RegDiversions,
|
||||
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) {
|
||||
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::ir::{Function, Inst};
|
||||
use crate::isa::TargetIsa;
|
||||
use crate::regalloc::RegDiversions;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs"));
|
||||
|
||||
@@ -108,11 +108,11 @@ impl TargetIsa for Isa {
|
||||
divert: &mut regalloc::RegDiversions,
|
||||
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) {
|
||||
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::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::regalloc::RegDiversions;
|
||||
use core::u32;
|
||||
|
||||
@@ -115,11 +115,11 @@ impl TargetIsa for Isa {
|
||||
divert: &mut regalloc::RegDiversions,
|
||||
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) {
|
||||
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::ir::condcodes::{CondCode, FloatCC, IntCC};
|
||||
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;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/binemit-x86.rs"));
|
||||
|
||||
@@ -131,11 +131,11 @@ impl TargetIsa for Isa {
|
||||
divert: &mut regalloc::RegDiversions,
|
||||
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) {
|
||||
emit_function(func, binemit::emit_inst, sink)
|
||||
emit_function(func, binemit::emit_inst, sink, self)
|
||||
}
|
||||
|
||||
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::liveness::Liveness;
|
||||
use crate::regalloc::reload::Reload;
|
||||
use crate::regalloc::safepoint::emit_stackmaps;
|
||||
use crate::regalloc::spilling::Spilling;
|
||||
use crate::regalloc::virtregs::VirtRegs;
|
||||
use crate::result::CodegenResult;
|
||||
@@ -192,6 +193,20 @@ impl Context {
|
||||
self.coloring
|
||||
.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() {
|
||||
let ok = verify_context(func, cfg, domtree, isa, &mut errors).is_ok()
|
||||
&& verify_liveness(isa, func, cfg, &self.liveness, &mut errors).is_ok()
|
||||
|
||||
@@ -15,9 +15,11 @@ mod context;
|
||||
mod diversion;
|
||||
mod pressure;
|
||||
mod reload;
|
||||
mod safepoint;
|
||||
mod solver;
|
||||
mod spilling;
|
||||
|
||||
pub use self::context::Context;
|
||||
pub use self::diversion::RegDiversions;
|
||||
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_simd = false\n\
|
||||
enable_atomics = true\n\
|
||||
enable_safepoints = false\n\
|
||||
allones_funcaddrs = false\n\
|
||||
probestack_enabled = true\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.
|
||||
let opcode = self.func.dfg[inst].opcode();
|
||||
|
||||
// The `fallthrough` and `fallthrough_return` instructions are marked as terminators and
|
||||
// branches, but they are not required to have an encoding.
|
||||
if opcode == Opcode::Fallthrough || opcode == Opcode::FallthroughReturn {
|
||||
// The `fallthrough`, `fallthrough_return`, and `safepoint` instructions are not required
|
||||
// to have an encoding.
|
||||
if opcode == Opcode::Fallthrough
|
||||
|| opcode == Opcode::FallthroughReturn
|
||||
|| opcode == Opcode::Safepoint
|
||||
{
|
||||
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<()> {
|
||||
self.verify_global_values(errors)?;
|
||||
self.verify_heaps(errors)?;
|
||||
@@ -1750,6 +1771,7 @@ impl<'a> Verifier<'a> {
|
||||
for inst in self.func.layout.ebb_insts(ebb) {
|
||||
self.ebb_integrity(ebb, inst, errors)?;
|
||||
self.instruction_integrity(inst, errors)?;
|
||||
self.verify_safepoint_unused(inst, errors)?;
|
||||
self.typecheck(inst, errors)?;
|
||||
self.verify_encoding(inst, errors)?;
|
||||
self.immediate_constraints(inst, errors)?;
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
use crate::container;
|
||||
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::{self, binemit, ir};
|
||||
use cranelift_module::{
|
||||
@@ -141,6 +143,8 @@ impl Backend for FaerieBackend {
|
||||
total_size: u32,
|
||||
) -> ModuleResult<FaerieCompiledFunction> {
|
||||
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.
|
||||
{
|
||||
@@ -160,6 +164,7 @@ impl Backend for FaerieBackend {
|
||||
code.as_mut_ptr(),
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stackmap_sink,
|
||||
)
|
||||
};
|
||||
trap_manifest.add_sink(trap_sink);
|
||||
@@ -171,6 +176,7 @@ impl Backend for FaerieBackend {
|
||||
code.as_mut_ptr(),
|
||||
&mut reloc_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_print_cfg;
|
||||
mod test_regalloc;
|
||||
mod test_safepoint;
|
||||
mod test_shrink;
|
||||
mod test_simple_gvn;
|
||||
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),
|
||||
"verifier" => test_verifier::subtest(parsed),
|
||||
"preopt" => test_preopt::subtest(parsed),
|
||||
"safepoint" => test_safepoint::subtest(parsed),
|
||||
_ => 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::ir;
|
||||
use cranelift_codegen::ir::entities::AnyEntity;
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_codegen::settings::OptLevel;
|
||||
use cranelift_reader::TestCommand;
|
||||
@@ -100,9 +101,15 @@ impl binemit::CodeSink for TextSink {
|
||||
fn begin_jumptables(&mut self) {
|
||||
self.code_size = self.offset
|
||||
}
|
||||
|
||||
fn begin_rodata(&mut self) {}
|
||||
fn end_codegen(&mut self) {}
|
||||
fn add_stackmap(
|
||||
&mut self,
|
||||
_: &[ir::entities::Value],
|
||||
_: &ir::Function,
|
||||
_: &dyn isa::TargetIsa,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl SubTest for TestBinEmit {
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::binemit::{self, CodeInfo};
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use log::info;
|
||||
@@ -53,8 +54,9 @@ impl SubTest for TestCompile {
|
||||
let mut sink = SizeSink { offset: 0 };
|
||||
binemit::emit_function(
|
||||
&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,
|
||||
isa,
|
||||
);
|
||||
|
||||
if sink.offset != total_size {
|
||||
@@ -109,4 +111,11 @@ impl binemit::CodeSink for SizeSink {
|
||||
fn begin_jumptables(&mut self) {}
|
||||
fn begin_rodata(&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))
|
||||
} else if ty == F64 {
|
||||
cur.ins().f64const(Ieee64::with_bits(0))
|
||||
} else if ty.is_ref() {
|
||||
cur.ins().null(ty)
|
||||
} else if ty.is_vector() {
|
||||
let scalar_ty = ty.lane_type();
|
||||
if scalar_ty.is_int() {
|
||||
|
||||
@@ -370,6 +370,8 @@ impl<'a> Lexer<'a> {
|
||||
"b16" => types::B16,
|
||||
"b32" => types::B32,
|
||||
"b64" => types::B64,
|
||||
"r32" => types::R32,
|
||||
"r64" => types::R64,
|
||||
_ => return None,
|
||||
};
|
||||
if is_vector {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//! Defines `SimpleJITBackend`.
|
||||
|
||||
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::{self, ir, settings};
|
||||
use cranelift_module::{
|
||||
@@ -128,6 +130,13 @@ struct RelocRecord {
|
||||
addend: Addend,
|
||||
}
|
||||
|
||||
struct StackmapRecord {
|
||||
#[allow(dead_code)]
|
||||
offset: CodeOffset,
|
||||
#[allow(dead_code)]
|
||||
stackmap: Stackmap,
|
||||
}
|
||||
|
||||
pub struct SimpleJITCompiledFunction {
|
||||
code: *mut u8,
|
||||
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
|
||||
// that traps.
|
||||
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 {
|
||||
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.
|
||||
|
||||
use crate::disasm::{PrintRelocs, PrintTraps};
|
||||
use crate::disasm::{PrintRelocs, PrintStackmaps, PrintTraps};
|
||||
use crate::utils::{parse_sets_and_triple, read_to_string};
|
||||
use cranelift_codegen::ir::{
|
||||
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 mut relocs = PrintRelocs::new(false);
|
||||
let mut traps = PrintTraps::new(false);
|
||||
let mut stackmaps = PrintStackmaps::new(false);
|
||||
let mut mem = vec![];
|
||||
|
||||
let _ = self
|
||||
.context
|
||||
.compile_and_emit(self.isa, &mut mem, &mut relocs, &mut traps);
|
||||
let _ = self.context.compile_and_emit(
|
||||
self.isa,
|
||||
&mut mem,
|
||||
&mut relocs,
|
||||
&mut traps,
|
||||
&mut stackmaps,
|
||||
);
|
||||
})) {
|
||||
Ok(()) => CheckResult::Succeed,
|
||||
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.
|
||||
|
||||
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 cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_codegen::settings::FlagsOrIsa;
|
||||
@@ -62,11 +62,12 @@ fn handle_module(
|
||||
|
||||
let mut relocs = PrintRelocs::new(flag_print);
|
||||
let mut traps = PrintTraps::new(flag_print);
|
||||
let mut stackmaps = PrintStackmaps::new(flag_print);
|
||||
let mut mem = vec![];
|
||||
|
||||
// Compile and encode the result to machine code.
|
||||
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))?;
|
||||
|
||||
if flag_print {
|
||||
@@ -81,6 +82,7 @@ fn handle_module(
|
||||
code_info.jumptables_size + code_info.rodata_size,
|
||||
&relocs,
|
||||
&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! {
|
||||
if #[cfg(feature = "disas")] {
|
||||
use capstone::prelude::*;
|
||||
@@ -170,11 +192,12 @@ pub fn print_all(
|
||||
rodata_size: u32,
|
||||
relocs: &PrintRelocs,
|
||||
traps: &PrintTraps,
|
||||
stackmaps: &PrintStackmaps,
|
||||
) -> Result<(), String> {
|
||||
print_bytes(&mem);
|
||||
print_disassembly(isa, &mem[0..code_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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
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 cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error};
|
||||
use cranelift_codegen::settings::FlagsOrIsa;
|
||||
@@ -171,13 +171,14 @@ fn handle_module(
|
||||
let mut mem = vec![];
|
||||
let mut relocs = PrintRelocs::new(flag_print);
|
||||
let mut traps = PrintTraps::new(flag_print);
|
||||
let mut stackmaps = PrintStackmaps::new(flag_print);
|
||||
if flag_check_translation {
|
||||
if let Err(errors) = context.verify(fisa) {
|
||||
return Err(pretty_verifier_error(&context.func, fisa.isa, None, errors));
|
||||
}
|
||||
} else {
|
||||
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))?;
|
||||
|
||||
if flag_print_size {
|
||||
@@ -223,7 +224,7 @@ fn handle_module(
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -834,6 +834,12 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
Operator::F32Le | Operator::F64Le => {
|
||||
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::I32Wait { .. }
|
||||
| Operator::I64Wait { .. }
|
||||
@@ -902,9 +908,6 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
| Operator::I64AtomicRmw32UCmpxchg { .. } => {
|
||||
wasm_unsupported!("proposed thread operator {:?}", op);
|
||||
}
|
||||
Operator::RefNull | Operator::RefIsNull { .. } => {
|
||||
wasm_unsupported!("proposed reference-type operator {:?}", op);
|
||||
}
|
||||
Operator::MemoryInit { .. }
|
||||
| Operator::DataDrop { .. }
|
||||
| Operator::MemoryCopy
|
||||
|
||||
@@ -131,6 +131,17 @@ pub trait FuncEnvironment {
|
||||
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
|
||||
/// identified by `index`.
|
||||
///
|
||||
|
||||
@@ -104,7 +104,7 @@ impl FuncTranslator {
|
||||
builder.append_ebb_params_for_function_returns(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)?;
|
||||
|
||||
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.
|
||||
///
|
||||
/// Declare local variables, starting from `num_params`.
|
||||
fn parse_local_decls(
|
||||
fn parse_local_decls<FE: FuncEnvironment + ?Sized>(
|
||||
reader: &mut BinaryReader,
|
||||
builder: &mut FunctionBuilder,
|
||||
num_params: usize,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
let mut next_local = num_params;
|
||||
let local_count = reader.read_local_count()?;
|
||||
@@ -155,7 +156,7 @@ fn parse_local_decls(
|
||||
for _ in 0..local_count {
|
||||
builder.set_srcloc(cur_srcloc(reader));
|
||||
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(())
|
||||
@@ -164,11 +165,12 @@ fn parse_local_decls(
|
||||
/// 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.
|
||||
fn declare_locals(
|
||||
fn declare_locals<FE: FuncEnvironment + ?Sized>(
|
||||
builder: &mut FunctionBuilder,
|
||||
count: u32,
|
||||
wasm_type: wasmparser::Type,
|
||||
next_local: &mut usize,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
// All locals are initialized to 0.
|
||||
use wasmparser::Type::*;
|
||||
@@ -177,6 +179,7 @@ fn declare_locals(
|
||||
I64 => builder.ins().iconst(ir::types::I64, 0),
|
||||
F32 => builder.ins().f32const(ir::immediates::Ieee32::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),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user