Update spirt for DataInstForm interning.

This commit is contained in:
Eduard-Mihai Burtescu 2023-05-17 20:28:02 +03:00 committed by Eduard-Mihai Burtescu
parent b3670b2303
commit 603f9894d6
7 changed files with 126 additions and 47 deletions

38
Cargo.lock generated
View File

@ -462,6 +462,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -594,6 +600,19 @@ dependencies = [
"syn",
]
[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "diff"
version = "0.1.12"
@ -2032,6 +2051,15 @@ dependencies = [
"serde",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustfix"
version = "0.6.1"
@ -2145,6 +2173,12 @@ dependencies = [
"version-compare",
]
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.156"
@ -2246,11 +2280,11 @@ dependencies = [
[[package]]
name = "spirt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e24fa996f12f3c667efbceaa99c222b8910a295a14d2c43c3880dfab2752def7"
source = "git+https://github.com/EmbarkStudios/spirt?branch=main#e8b4a474b2de791a27ea793a1d9c932c27a667ef"
dependencies = [
"arrayvec",
"bytemuck",
"derive_more",
"elsa",
"indexmap",
"internal-iterator",

View File

@ -52,3 +52,6 @@ codegen-units = 256
opt-level = 3
incremental = true
codegen-units = 256
[patch.crates-io]
spirt = { git = "https://github.com/EmbarkStudios/spirt", branch = "main" }

View File

@ -4,8 +4,8 @@ use crate::custom_insts::{self, CustomInst, CustomOp};
use smallvec::SmallVec;
use spirt::func_at::FuncAt;
use spirt::{
cfg, spv, Attr, AttrSet, ConstCtor, ConstDef, ControlNodeKind, DataInstKind, DeclDef,
EntityDefs, ExportKey, Exportee, Module, Type, TypeCtor, TypeCtorArg, TypeDef, Value,
cfg, spv, Attr, AttrSet, ConstCtor, ConstDef, ControlNodeKind, DataInstFormDef, DataInstKind,
DeclDef, EntityDefs, ExportKey, Exportee, Module, Type, TypeCtor, TypeCtorArg, TypeDef, Value,
};
use std::fmt::Write as _;
@ -108,14 +108,15 @@ pub fn convert_custom_aborts_to_unstructured_returns_in_entry_points(
.into_iter()
.filter_map(|func_at_inst| {
let data_inst_def = func_at_inst.def();
if let DataInstKind::SpvInst(spv_inst) = &data_inst_def.kind {
let data_inst_form_def = &cx[data_inst_def.form];
if let DataInstKind::SpvInst(spv_inst) = &data_inst_form_def.kind {
if spv_inst.opcode == wk.OpLoad {
if let Value::Const(ct) = data_inst_def.inputs[0] {
if let ConstCtor::PtrToGlobalVar(gv) = cx[ct].ctor {
if interface_global_vars.contains(&gv) {
return Some((
gv,
data_inst_def.output_type.unwrap(),
data_inst_form_def.output_type.unwrap(),
Value::DataInstOutput(func_at_inst.position),
));
}
@ -223,7 +224,7 @@ pub fn convert_custom_aborts_to_unstructured_returns_in_entry_points(
let data_inst_def = func_at_inst.def();
(
func_at_inst,
match data_inst_def.kind {
match cx[data_inst_def.form].kind {
DataInstKind::SpvExtInst { ext_set, inst }
if ext_set == custom_ext_inst_set =>
{
@ -381,10 +382,13 @@ pub fn convert_custom_aborts_to_unstructured_returns_in_entry_points(
fmt += "\n";
let abort_inst_def = &mut func_def_body.data_insts[abort_inst];
abort_inst_def.kind = DataInstKind::SpvExtInst {
abort_inst_def.form = cx.intern(DataInstFormDef {
kind: DataInstKind::SpvExtInst {
ext_set: cx.intern("NonSemantic.DebugPrintf"),
inst: 1,
};
},
output_type: cx[abort_inst_def.form].output_type,
});
abort_inst_def.inputs = [Value::Const(mk_const_str(cx.intern(fmt)))]
.into_iter()
.chain(message_debug_printf_args)

View File

@ -25,6 +25,7 @@ pub fn convert_custom_debuginfo_to_spv(module: &mut Module) {
seen_types: FxIndexSet::default(),
seen_consts: FxIndexSet::default(),
seen_data_inst_forms: FxIndexSet::default(),
seen_global_vars: FxIndexSet::default(),
seen_funcs: FxIndexSet::default(),
};
@ -82,7 +83,7 @@ impl Transformer for CustomDebuginfoToSpv<'_> {
if let DataInstKind::SpvExtInst {
ext_set,
inst: ext_inst,
} = data_inst_def.kind
} = self.cx[data_inst_def.form].kind
{
if ext_set == self.custom_ext_inst_set {
let custom_op = CustomOp::decode(ext_inst);

View File

@ -11,8 +11,8 @@ use spirt::func_at::FuncAt;
use spirt::visit::{InnerVisit, Visitor};
use spirt::{
spv, Attr, AttrSet, AttrSetDef, Const, ConstCtor, Context, ControlNode, ControlNodeKind,
DataInstDef, DataInstKind, Diag, DiagLevel, ExportKey, Exportee, Func, FuncDecl, GlobalVar,
InternedStr, Module, Type, Value,
DataInstDef, DataInstForm, DataInstKind, Diag, DiagLevel, ExportKey, Exportee, Func, FuncDecl,
GlobalVar, InternedStr, Module, Type, Value,
};
use std::marker::PhantomData;
use std::{mem, str};
@ -251,7 +251,7 @@ impl UseOrigin<'_> {
let wk = &super::SpvSpecWithExtras::get().well_known;
// FIXME(eddyb) deduplicate with `spirt_passes::diagnostics`.
let custom_op = match debug_inst_def.kind {
let custom_op = match cx[debug_inst_def.form].kind {
DataInstKind::SpvExtInst {
ext_set,
inst: ext_inst,
@ -518,6 +518,11 @@ impl<'a> Visitor<'a> for DiagnosticReporter<'a> {
}
}
}
fn visit_data_inst_form_use(&mut self, data_inst_form: DataInstForm) {
// NOTE(eddyb) this contains no deduplication because each `DataInstDef`
// will have any diagnostics reported separately.
self.visit_data_inst_form_def(&self.cx[data_inst_form]);
}
fn visit_global_var_use(&mut self, gv: GlobalVar) {
if self.seen_global_vars.insert(gv) {
@ -617,7 +622,7 @@ impl<'a> Visitor<'a> for DiagnosticReporter<'a> {
if let DataInstKind::SpvExtInst {
ext_set,
inst: ext_inst,
} = data_inst_def.kind
} = self.cx[data_inst_def.form].kind
{
if ext_set == self.custom_ext_inst_set {
match CustomOp::decode(ext_inst) {
@ -687,7 +692,7 @@ impl<'a> Visitor<'a> for DiagnosticReporter<'a> {
_ => unreachable!(),
}
if let DataInstKind::FuncCall(func) = data_inst_def.kind {
if let DataInstKind::FuncCall(func) = self.cx[data_inst_def.form].kind {
// HACK(eddyb) visit `func` early, to control its `use_stack`, with
// the later visit from `inner_visit_with` ignored as a duplicate.
let old_origin = replace_origin(self, IntraFuncUseOrigin::CallCallee);

View File

@ -13,8 +13,8 @@ use spirt::transform::InnerInPlaceTransform;
use spirt::visit::{InnerVisit, Visitor};
use spirt::{
spv, AttrSet, Const, Context, ControlNode, ControlNodeKind, ControlRegion, DataInstDef,
DataInstKind, DeclDef, EntityOrientedDenseMap, Func, FuncDefBody, GlobalVar, Module, Type,
Value,
DataInstForm, DataInstFormDef, DataInstKind, DeclDef, EntityOrientedDenseMap, Func,
FuncDefBody, GlobalVar, Module, Type, Value,
};
use std::collections::VecDeque;
use std::hash::Hash;
@ -130,6 +130,7 @@ pub(super) fn run_func_passes<P>(
seen_types: FxIndexSet::default(),
seen_consts: FxIndexSet::default(),
seen_data_inst_forms: FxIndexSet::default(),
seen_global_vars: FxIndexSet::default(),
seen_funcs: FxIndexSet::default(),
};
@ -181,7 +182,7 @@ pub(super) fn run_func_passes<P>(
pass_fn(cx, func_def_body);
// FIXME(eddyb) avoid doing this except where changes occurred.
remove_unused_values_in_func(func_def_body);
remove_unused_values_in_func(cx, func_def_body);
}
}
after_pass(full_name, module, profiler);
@ -196,6 +197,7 @@ struct ReachableUseCollector<'a> {
// FIXME(eddyb) build some automation to avoid ever repeating these.
seen_types: FxIndexSet<Type>,
seen_consts: FxIndexSet<Const>,
seen_data_inst_forms: FxIndexSet<DataInstForm>,
seen_global_vars: FxIndexSet<GlobalVar>,
seen_funcs: FxIndexSet<Func>,
}
@ -213,6 +215,11 @@ impl Visitor<'_> for ReachableUseCollector<'_> {
self.visit_const_def(&self.cx[ct]);
}
}
fn visit_data_inst_form_use(&mut self, data_inst_form: DataInstForm) {
if self.seen_data_inst_forms.insert(data_inst_form) {
self.visit_data_inst_form_def(&self.cx[data_inst_form]);
}
}
fn visit_global_var_use(&mut self, gv: GlobalVar) {
if self.seen_global_vars.insert(gv) {
@ -247,6 +254,7 @@ const _: () = {
fn visit_attr_set_use(&mut self, _: AttrSet) {}
fn visit_type_use(&mut self, _: Type) {}
fn visit_const_use(&mut self, _: Const) {}
fn visit_data_inst_form_use(&mut self, _: DataInstForm) {}
fn visit_global_var_use(&mut self, _: GlobalVar) {}
fn visit_func_use(&mut self, _: Func) {}
@ -354,7 +362,7 @@ const _: () = {
/// a function body (both `DataInst`s and `ControlRegion` inputs/outputs).
//
// FIXME(eddyb) should this be a dedicated pass?
fn remove_unused_values_in_func(func_def_body: &mut FuncDefBody) {
fn remove_unused_values_in_func(cx: &Context, func_def_body: &mut FuncDefBody) {
// Avoid having to support unstructured control-flow.
if func_def_body.unstructured_cfg.is_some() {
return;
@ -473,7 +481,9 @@ fn remove_unused_values_in_func(func_def_body: &mut FuncDefBody) {
for func_at_inst in func_at_control_node.at(insts) {
// Ignore pure instructions (i.e. they're only used
// if their output value is used, from somewhere else).
if let DataInstKind::SpvInst(spv_inst) = &func_at_inst.def().kind {
if let DataInstKind::SpvInst(spv_inst) =
&cx[func_at_inst.def().form].kind
{
// HACK(eddyb) small selection relevant for now,
// but should be extended using e.g. a bitset.
if [wk.OpNop, wk.OpCompositeInsert].contains(&spv_inst.opcode) {
@ -518,7 +528,9 @@ fn remove_unused_values_in_func(func_def_body: &mut FuncDefBody) {
let mut all_nops = true;
let mut func_at_inst_iter = func_def_body.at_mut(insts).into_iter();
while let Some(mut func_at_inst) = func_at_inst_iter.next() {
if let DataInstKind::SpvInst(spv_inst) = &func_at_inst.reborrow().def().kind {
if let DataInstKind::SpvInst(spv_inst) =
&cx[func_at_inst.reborrow().def().form].kind
{
if spv_inst.opcode == wk.OpNop {
continue;
}
@ -528,10 +540,14 @@ fn remove_unused_values_in_func(func_def_body: &mut FuncDefBody) {
{
// Replace the removed `DataInstDef` itself with `OpNop`,
// removing the ability to use its "name" as a value.
//
// FIXME(eddyb) cache the interned `OpNop`.
*func_at_inst.def() = DataInstDef {
attrs: Default::default(),
form: cx.intern(DataInstFormDef {
kind: DataInstKind::SpvInst(wk.OpNop.into()),
output_type: None,
}),
inputs: iter::empty().collect(),
};
continue;

View File

@ -6,8 +6,8 @@ use spirt::visit::InnerVisit;
use spirt::{
spv, Const, ConstCtor, ConstDef, Context, ControlNode, ControlNodeDef, ControlNodeKind,
ControlNodeOutputDecl, ControlRegion, ControlRegionInputDecl, DataInst, DataInstDef,
DataInstKind, EntityOrientedDenseMap, FuncDefBody, SelectionKind, Type, TypeCtor, TypeDef,
Value,
DataInstFormDef, DataInstKind, EntityOrientedDenseMap, FuncDefBody, SelectionKind, Type,
TypeCtor, TypeDef, Value,
};
use std::collections::hash_map::Entry;
use std::convert::{TryFrom, TryInto};
@ -65,7 +65,7 @@ pub(crate) fn reduce_in_func(cx: &Context, func_def_body: &mut FuncDefBody) {
..
} => {
for func_at_inst in func_at_control_node.at(insts) {
if let Ok(redu) = Reducible::try_from(func_at_inst.def()) {
if let Ok(redu) = Reducible::try_from((cx, func_at_inst.def())) {
let redu_target = ReductionTarget::DataInst(func_at_inst.position);
reduction_queue.push((redu_target, redu));
}
@ -202,10 +202,14 @@ pub(crate) fn reduce_in_func(cx: &Context, func_def_body: &mut FuncDefBody) {
// Replace the reduced `DataInstDef` itself with `OpNop`,
// removing the ability to use its "name" as a value.
//
// FIXME(eddyb) cache the interned `OpNop`.
*func_def_body.at_mut(inst).def() = DataInstDef {
attrs: Default::default(),
form: cx.intern(DataInstFormDef {
kind: DataInstKind::SpvInst(wk.OpNop.into()),
output_type: None,
}),
inputs: iter::empty().collect(),
};
}
@ -499,12 +503,14 @@ impl<V> Reducible<V> {
}
}
impl TryFrom<&DataInstDef> for Reducible {
// FIXME(eddyb) instead of taking a `&Context`, could `Reducible` hold a `DataInstForm`?
impl TryFrom<(&Context, &DataInstDef)> for Reducible {
type Error = ();
fn try_from(inst_def: &DataInstDef) -> Result<Self, ()> {
if let DataInstKind::SpvInst(spv_inst) = &inst_def.kind {
fn try_from((cx, inst_def): (&Context, &DataInstDef)) -> Result<Self, ()> {
let inst_form_def = &cx[inst_def.form];
if let DataInstKind::SpvInst(spv_inst) = &inst_form_def.kind {
let op = PureOp::try_from(spv_inst)?;
let output_type = inst_def.output_type.unwrap();
let output_type = inst_form_def.output_type.unwrap();
if let [input] = inst_def.inputs[..] {
return Ok(Self {
op,
@ -517,15 +523,21 @@ impl TryFrom<&DataInstDef> for Reducible {
}
}
// HACK(eddyb) `IntToBool` is the only reason this is `TryFrom` not `From`.
impl TryFrom<Reducible> for DataInstDef {
type Error = ();
fn try_from(redu: Reducible) -> Result<Self, ()> {
Ok(Self {
impl Reducible {
// HACK(eddyb) `IntToBool` is the only reason this can return `None`.
fn try_into_inst(self, cx: &Context) -> Option<DataInstDef> {
let Self {
op,
output_type,
input,
} = self;
Some(DataInstDef {
attrs: Default::default(),
kind: DataInstKind::SpvInst(redu.op.try_into()?),
output_type: Some(redu.output_type),
inputs: iter::once(redu.input).collect(),
form: cx.intern(DataInstFormDef {
kind: DataInstKind::SpvInst(op.try_into().ok()?),
output_type: Some(output_type),
}),
inputs: iter::once(input).collect(),
})
}
}
@ -624,11 +636,11 @@ enum ReductionStep {
impl Reducible<&DataInstDef> {
// FIXME(eddyb) force the input to actually be itself some kind of pure op.
fn try_reduce_output_of_data_inst(&self) -> Option<ReductionStep> {
fn try_reduce_output_of_data_inst(&self, cx: &Context) -> Option<ReductionStep> {
let wk = &super::SpvSpecWithExtras::get().well_known;
let input_inst_def = self.input;
if let DataInstKind::SpvInst(input_spv_inst) = &input_inst_def.kind {
if let DataInstKind::SpvInst(input_spv_inst) = &cx[input_inst_def.form].kind {
// NOTE(eddyb) do not destroy information left in e.g. comments.
#[allow(clippy::match_same_arms)]
match self.op {
@ -737,8 +749,9 @@ impl Reducible {
.try_reduce(cx, func.reborrow(), value_replacements, parent_map, cache)?;
// HACK(eddyb) this is here because it can fail, see the comment
// on `output_from_updated_state` for what's actually going on.
let output_from_updated_state_inst =
DataInstDef::try_from(self.with_input(input_from_updated_state)).ok()?;
let output_from_updated_state_inst = self
.with_input(input_from_updated_state)
.try_into_inst(cx)?;
// Now that the reduction succeeded for the initial state,
// we can proceed with augmenting the loop with the extra state.
@ -874,7 +887,10 @@ impl Reducible {
}
Value::DataInstOutput(inst) => {
let inst_def = &*func.reborrow().at(inst).def();
match self.with_input(inst_def).try_reduce_output_of_data_inst()? {
match self
.with_input(inst_def)
.try_reduce_output_of_data_inst(cx)?
{
ReductionStep::Complete(v) => Some(v),
// FIXME(eddyb) actually use a loop instead of recursing here.
ReductionStep::Partial(redu) => {