mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-22 06:45:13 +00:00
Poison reason tracking
This commit is contained in:
parent
31ac70ea07
commit
ede5a1ab7f
@ -35,7 +35,6 @@ use rustc_target::abi::{
|
||||
use rustc_target::spec::{HasTargetSpec, Target};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::once;
|
||||
use std::ops::Range;
|
||||
|
||||
@ -68,7 +67,7 @@ pub struct CodegenCx<'tcx> {
|
||||
pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>), SpirvValue>>,
|
||||
pub ext_inst: RefCell<ExtInst>,
|
||||
/// Invalid spir-v IDs that should be stripped from the final binary
|
||||
poisoned_values: RefCell<HashSet<Word>>,
|
||||
poisoned_values: RefCell<HashMap<Word, &'static str>>,
|
||||
pub kernel_mode: bool,
|
||||
}
|
||||
|
||||
@ -135,8 +134,8 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
self.lookup_type(ty).debug(ty, self)
|
||||
}
|
||||
|
||||
pub fn poison(&self, word: Word) {
|
||||
self.poisoned_values.borrow_mut().insert(word);
|
||||
pub fn poison(&self, word: Word, reason: &'static str) {
|
||||
self.poisoned_values.borrow_mut().insert(word, reason);
|
||||
}
|
||||
|
||||
pub fn finalize_module(self) -> Module {
|
||||
@ -213,7 +212,7 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
.with_type(ty),
|
||||
SpirvType::Integer(128, _) => {
|
||||
let result = self.emit_global().undef(ty, None);
|
||||
self.poison(result);
|
||||
self.poison(result, "u128 constant");
|
||||
result.with_type(ty)
|
||||
}
|
||||
other => panic!("constant_int invalid on type {}", other.debug(ty, self)),
|
||||
@ -524,7 +523,7 @@ impl<'tcx> StaticMethods for CodegenCx<'tcx> {
|
||||
.variable(ty, None, StorageClass::Function, Some(cv.def))
|
||||
.with_type(ty);
|
||||
// TODO: These should be StorageClass::UniformConstant, so just poison for now.
|
||||
self.poison(result.def);
|
||||
self.poison(result.def, "static_addr_of");
|
||||
result
|
||||
}
|
||||
|
||||
@ -618,7 +617,7 @@ fn declare_fn<'tcx>(
|
||||
if crate::is_blocklisted_fn(name) {
|
||||
// This can happen if we call a blocklisted function in another crate.
|
||||
let result = emit.undef(function_type, None);
|
||||
cx.poison(result);
|
||||
cx.poison(result, "called blocklisted fn");
|
||||
return result.with_type(function_type);
|
||||
}
|
||||
let fn_id = emit
|
||||
@ -758,7 +757,7 @@ impl<'tcx> DeclareMethods<'tcx> for CodegenCx<'tcx> {
|
||||
.variable(ptr_ty, None, StorageClass::Function, None)
|
||||
.with_type(ptr_ty);
|
||||
// TODO: These should be StorageClass::Private, so just poison for now.
|
||||
self.poison(result.def);
|
||||
self.poison(result.def, "declare_global");
|
||||
self.declared_values
|
||||
.borrow_mut()
|
||||
.insert(name.to_string(), result);
|
||||
@ -909,7 +908,7 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
|
||||
.variable(ty, None, StorageClass::Function, Some(raw_bytes.def))
|
||||
.with_type(ty);
|
||||
// The types don't line up (dynamic array vs. constant array)
|
||||
self.poison(result.def);
|
||||
self.poison(result.def, "constant string");
|
||||
result
|
||||
});
|
||||
// let cs = consts::ptrcast(
|
||||
@ -1040,7 +1039,7 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
|
||||
) => {
|
||||
if a_space != b_space {
|
||||
// TODO: Emit the correct type that is passed into this function.
|
||||
self.poison(value.def);
|
||||
self.poison(value.def, "invalid pointer space in constant");
|
||||
}
|
||||
assert_ty_eq!(self, a, b);
|
||||
}
|
||||
@ -1070,7 +1069,7 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
|
||||
} else {
|
||||
// constant ptrcast is not supported in spir-v
|
||||
let result = val.def.with_type(ty);
|
||||
self.poison(result.def);
|
||||
self.poison(result.def, "const_ptrcast");
|
||||
result
|
||||
}
|
||||
}
|
||||
@ -1190,7 +1189,7 @@ fn create_const_alloc2(
|
||||
*data = *c + asdf->y[*c];
|
||||
}
|
||||
*/
|
||||
cx.poison(result.def);
|
||||
cx.poison(result.def, "constant runtime array value");
|
||||
result
|
||||
}
|
||||
SpirvType::Pointer { .. } => {
|
||||
|
@ -1,34 +1,42 @@
|
||||
use rspirv::dr::{Block, Function, Instruction, Module, Operand};
|
||||
use rspirv::spirv::{Op, Word};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::iter::once;
|
||||
use std::mem::replace;
|
||||
|
||||
fn contains_poison(inst: &Instruction, poison: &HashSet<Word>) -> bool {
|
||||
inst.result_type.map_or(false, |w| poison.contains(&w))
|
||||
|| inst.operands.iter().any(|op| match op {
|
||||
rspirv::dr::Operand::IdMemorySemantics(w)
|
||||
| rspirv::dr::Operand::IdScope(w)
|
||||
| rspirv::dr::Operand::IdRef(w) => poison.contains(w),
|
||||
_ => false,
|
||||
})
|
||||
fn contains_poison(
|
||||
inst: &Instruction,
|
||||
poison: &HashMap<Word, &'static str>,
|
||||
) -> Option<&'static str> {
|
||||
inst.result_type.map_or_else(
|
||||
|| {
|
||||
inst.operands.iter().find_map(|op| match op {
|
||||
rspirv::dr::Operand::IdMemorySemantics(w)
|
||||
| rspirv::dr::Operand::IdScope(w)
|
||||
| rspirv::dr::Operand::IdRef(w) => poison.get(w).copied(),
|
||||
_ => None,
|
||||
})
|
||||
},
|
||||
|w| poison.get(&w).copied(),
|
||||
)
|
||||
}
|
||||
|
||||
fn is_poison(inst: &Instruction, poison: &HashSet<Word>) -> bool {
|
||||
fn is_poison(inst: &Instruction, poison: &HashMap<Word, &'static str>) -> Option<&'static str> {
|
||||
if let Some(result_id) = inst.result_id {
|
||||
poison.contains(&result_id)
|
||||
poison.get(&result_id).copied()
|
||||
} else {
|
||||
contains_poison(inst, poison)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poison_pass(module: &mut Module, poison: &mut HashSet<Word>) {
|
||||
pub fn poison_pass(module: &mut Module, poison: &mut HashMap<Word, &'static str>) {
|
||||
// Note: This is O(n^2).
|
||||
while spread_poison(module, poison) {}
|
||||
|
||||
if option_env!("PRINT_POISON").is_some() {
|
||||
for f in &module.functions {
|
||||
if is_poison(f.def.as_ref().unwrap(), poison) {
|
||||
if let Some(reason) = is_poison(f.def.as_ref().unwrap(), poison) {
|
||||
let name_id = f.def.as_ref().unwrap().result_id.unwrap();
|
||||
let name = module.debugs.iter().find(|inst| {
|
||||
inst.class.opcode == Op::Name && inst.operands[0] == Operand::IdRef(name_id)
|
||||
@ -40,61 +48,81 @@ pub fn poison_pass(module: &mut Module, poison: &mut HashSet<Word>) {
|
||||
},
|
||||
_ => format!("{}", name_id),
|
||||
};
|
||||
println!("Function removed: {}", name)
|
||||
println!("Function removed {:?} because {:?}", name, reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
module.capabilities.retain(|inst| !is_poison(inst, poison));
|
||||
module.extensions.retain(|inst| !is_poison(inst, poison));
|
||||
module
|
||||
.capabilities
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
module
|
||||
.extensions
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
module
|
||||
.ext_inst_imports
|
||||
.retain(|inst| !is_poison(inst, poison));
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
if module
|
||||
.memory_model
|
||||
.as_ref()
|
||||
.map_or(false, |inst| is_poison(inst, poison))
|
||||
.map_or(false, |inst| is_poison(inst, poison).is_some())
|
||||
{
|
||||
module.memory_model = None;
|
||||
}
|
||||
module.entry_points.retain(|inst| !is_poison(inst, poison));
|
||||
module
|
||||
.entry_points
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
module
|
||||
.execution_modes
|
||||
.retain(|inst| !is_poison(inst, poison));
|
||||
module.debugs.retain(|inst| !is_poison(inst, poison));
|
||||
module.annotations.retain(|inst| !is_poison(inst, poison));
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
module
|
||||
.debugs
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
module
|
||||
.annotations
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
module
|
||||
.types_global_values
|
||||
.retain(|inst| !is_poison(inst, poison));
|
||||
.retain(|inst| is_poison(inst, poison).is_none());
|
||||
module
|
||||
.functions
|
||||
.retain(|f| !is_poison(f.def.as_ref().unwrap(), poison));
|
||||
.retain(|f| is_poison(f.def.as_ref().unwrap(), poison).is_none());
|
||||
}
|
||||
|
||||
fn spread_poison(module: &mut Module, poison: &mut HashSet<Word>) -> bool {
|
||||
fn spread_poison(module: &mut Module, poison: &mut HashMap<Word, &'static str>) -> bool {
|
||||
let mut any = false;
|
||||
// globals are easy
|
||||
for inst in module.global_inst_iter() {
|
||||
if let Some(result_id) = inst.result_id {
|
||||
if contains_poison(inst, poison) && poison.insert(result_id) {
|
||||
any = true;
|
||||
if let Some(reason) = contains_poison(inst, poison) {
|
||||
match poison.entry(result_id) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(reason);
|
||||
any = true;
|
||||
}
|
||||
Entry::Occupied(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// function IDs implicitly reference their contents
|
||||
for func in &module.functions {
|
||||
let mut func_poisoned = false;
|
||||
let mut func_poisoned = None;
|
||||
let mut spread_func = |inst: &Instruction| {
|
||||
if let Some(result_id) = inst.result_id {
|
||||
if contains_poison(inst, poison) {
|
||||
if poison.insert(result_id) {
|
||||
any = true;
|
||||
if let Some(reason) = contains_poison(inst, poison) {
|
||||
match poison.entry(result_id) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(reason);
|
||||
any = true;
|
||||
}
|
||||
Entry::Occupied(_) => {}
|
||||
}
|
||||
func_poisoned = true;
|
||||
} else if poison.contains(&result_id) {
|
||||
func_poisoned = true;
|
||||
func_poisoned = Some(func_poisoned.unwrap_or(reason));
|
||||
} else if let Some(reason) = poison.get(&result_id) {
|
||||
func_poisoned = Some(func_poisoned.unwrap_or(reason));
|
||||
}
|
||||
} else if is_poison(inst, poison) {
|
||||
func_poisoned = true;
|
||||
} else if let Some(reason) = is_poison(inst, poison) {
|
||||
func_poisoned = Some(func_poisoned.unwrap_or(reason));
|
||||
}
|
||||
};
|
||||
for def in &func.def {
|
||||
@ -114,8 +142,14 @@ fn spread_poison(module: &mut Module, poison: &mut HashSet<Word>) -> bool {
|
||||
for inst in &func.end {
|
||||
spread_func(inst);
|
||||
}
|
||||
if func_poisoned && poison.insert(func.def.as_ref().unwrap().result_id.unwrap()) {
|
||||
any = true;
|
||||
if let Some(reason) = func_poisoned {
|
||||
match poison.entry(func.def.as_ref().unwrap().result_id.unwrap()) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(reason);
|
||||
any = true;
|
||||
}
|
||||
Entry::Occupied(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
any
|
||||
|
@ -118,7 +118,7 @@ impl SpirvType {
|
||||
.type_int(width, if signedness { 1 } else { 0 });
|
||||
match width {
|
||||
8 | 16 | 32 | 64 => (),
|
||||
128 => cx.poison(result),
|
||||
128 => cx.poison(result, "u128"),
|
||||
other => panic!("Integer width {} invalid for spir-v", other),
|
||||
};
|
||||
result
|
||||
@ -187,7 +187,7 @@ impl SpirvType {
|
||||
SpirvType::RuntimeArray { element } => {
|
||||
let result = cx.emit_global().type_runtime_array(element);
|
||||
if cx.kernel_mode {
|
||||
cx.poison(result);
|
||||
cx.poison(result, "RuntimeArray in kernel mode");
|
||||
}
|
||||
result
|
||||
}
|
||||
@ -198,7 +198,7 @@ impl SpirvType {
|
||||
let result = cx.emit_global().type_pointer(None, storage_class, pointee);
|
||||
// no pointers to functions
|
||||
if let SpirvType::Function { .. } = cx.lookup_type(pointee) {
|
||||
cx.poison(result)
|
||||
cx.poison(result, "pointer to function")
|
||||
}
|
||||
result
|
||||
}
|
||||
@ -228,7 +228,7 @@ impl SpirvType {
|
||||
.type_pointer(Some(id), storage_class, pointee);
|
||||
// no pointers to functions
|
||||
if let SpirvType::Function { .. } = cx.lookup_type(pointee) {
|
||||
cx.poison(result)
|
||||
cx.poison(result, "pointer to function")
|
||||
}
|
||||
result
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user