mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-22 06:45:13 +00:00
Write some docs
This commit is contained in:
parent
78f7d8f91c
commit
ebff8a8f39
@ -1318,8 +1318,9 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
|
||||
let (result_type, argument_types) = loop {
|
||||
match self.lookup_type(llfn.ty) {
|
||||
SpirvType::Pointer { pointee, .. } => {
|
||||
if let Some(func) = self.cx.function_pointers.borrow().get(&llfn) {
|
||||
llfn = *func;
|
||||
// See comment on register_fn_ptr
|
||||
if let Some(func) = self.lookup_fn_ptr(llfn) {
|
||||
llfn = func;
|
||||
} else {
|
||||
llfn = self
|
||||
.emit()
|
||||
|
@ -38,6 +38,7 @@ pub struct Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// See comment on BuilderCursor
|
||||
pub fn emit(&self) -> std::cell::RefMut<rspirv::dr::Builder> {
|
||||
self.emit_with_cursor(self.cursor)
|
||||
}
|
||||
|
@ -36,6 +36,22 @@ pub enum SpirvConst {
|
||||
Undef(Word),
|
||||
}
|
||||
|
||||
/// Cursor system:
|
||||
/// The LLVM module builder model (and therefore codegen_ssa) assumes that there is a central module object, then,
|
||||
/// builder objects are created pointing at that central module object (e.g. for adding instructions to a basic block).
|
||||
/// Several of these builder objects can be live at the same time, mutating the central module object all at once.
|
||||
/// Unfortunately, rspirv doesn't work like that. Instead, there is a single builder object, which owns a module and a
|
||||
/// "cursor". This cursor indicates to the builder where to append instructions when an instruction is added - e.g. if
|
||||
/// add() is called, then OpAdd is appended to the basic block pointed to by the cursor.
|
||||
/// So! We emulate the LLVM system by treating the rspirv Builder as the "central module object", then, when a "builder
|
||||
/// object" is created, we store a reference to a RefCell<rspirv builder>, *as well as* a copy of the cursor for that
|
||||
/// particular builder. Whenever the RefCell is borrowed, then we stomp over the rspirv cursor with our copy, causing the
|
||||
/// duration of that RefCell borrow to use that cursor.
|
||||
/// So, if you're writing code inside crate::builder::Builder, then self.emit() will use self.cursor (the current basic
|
||||
/// block) as that "stomp-over" cursor and return a mutable reference to the rspirv builder. If you're writing code
|
||||
/// elsewhere (codegen_cx::CodegenCx), then self.emit_global() will use the generic "global cursor" and return a mutable
|
||||
/// reference to the rspirv builder with no basic block nor function selected, i.e. any instructions emitted will be in
|
||||
/// the global section.
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
#[must_use = "BuilderCursor should usually be assigned to the Builder.cursor field"]
|
||||
pub struct BuilderCursor {
|
||||
@ -99,8 +115,7 @@ impl BuilderSpirv {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Note: This Builder can be used by multiple things being built at the same time, so, we need to save and restore
|
||||
/// the current cursor every time we do something.
|
||||
/// See comment on BuilderCursor
|
||||
pub fn builder(&self, cursor: BuilderCursor) -> RefMut<Builder> {
|
||||
let mut builder = self.builder.borrow_mut();
|
||||
// select_function does bounds checks and other relatively expensive things, so don't just call it
|
||||
|
@ -52,7 +52,7 @@ pub struct CodegenCx<'tcx> {
|
||||
/// Cache of all the builtin symbols we need
|
||||
pub sym: Box<Symbols>,
|
||||
/// Functions created in get_fn_addr
|
||||
pub function_pointers: RefCell<HashMap<SpirvValue, SpirvValue>>,
|
||||
function_pointers: RefCell<HashMap<SpirvValue, SpirvValue>>,
|
||||
}
|
||||
|
||||
impl<'tcx> CodegenCx<'tcx> {
|
||||
@ -74,6 +74,7 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// See comment on BuilderCursor
|
||||
pub fn emit_global(&self) -> std::cell::RefMut<rspirv::dr::Builder> {
|
||||
self.builder.builder(BuilderCursor {
|
||||
function: None,
|
||||
@ -81,6 +82,7 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// See comment on BuilderCursor
|
||||
pub fn emit_with_cursor(
|
||||
&self,
|
||||
cursor: BuilderCursor,
|
||||
@ -131,6 +133,32 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
once(Operand::LiteralString(name)).chain(once(Operand::LinkageType(linkage))),
|
||||
)
|
||||
}
|
||||
|
||||
/// Function pointer registration:
|
||||
/// LLVM, and therefore codegen_ssa, is very murky with function values vs. function pointers. So, codegen_ssa has a
|
||||
/// pattern where *even for direct function calls*, it uses get_fn_*addr*, and then uses that function *pointer* when
|
||||
/// calling BuilderMethods::call(). However, spir-v doesn't support function pointers! So, instead, when get_fn_addr
|
||||
/// is called, we register a "token" (via OpUndef), storing it in a dictionary. Then, when BuilderMethods::call() is
|
||||
/// called, and it's calling a function pointer, we check the dictionary, and if it is, invoke the function directly.
|
||||
/// It's kind of conceptually similar to a constexpr deref, except specialized to just functions.
|
||||
pub fn register_fn_ptr(&self, function: SpirvValue) -> SpirvValue {
|
||||
let ty = SpirvType::Pointer {
|
||||
storage_class: StorageClass::Function,
|
||||
pointee: function.ty,
|
||||
}
|
||||
.def(self);
|
||||
// We want a unique ID for these undefs, so don't use the caching system.
|
||||
let result = self.emit_global().undef(ty, None).with_type(ty);
|
||||
// It's obviously invalid, so zombie it.
|
||||
self.zombie(result.def, "get_fn_addr");
|
||||
self.function_pointers.borrow_mut().insert(result, function);
|
||||
result
|
||||
}
|
||||
|
||||
/// See comment on register_fn_ptr
|
||||
pub fn lookup_fn_ptr(&self, pointer: SpirvValue) -> Option<SpirvValue> {
|
||||
self.function_pointers.borrow().get(&pointer).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> BackendTypes for CodegenCx<'tcx> {
|
||||
@ -189,16 +217,7 @@ impl<'tcx> MiscMethods<'tcx> for CodegenCx<'tcx> {
|
||||
|
||||
fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value {
|
||||
let function = self.get_fn_ext(instance);
|
||||
let ty = SpirvType::Pointer {
|
||||
storage_class: StorageClass::Function,
|
||||
pointee: function.ty,
|
||||
}
|
||||
.def(self);
|
||||
// We want a unique ID for these undefs, so don't use the caching system.
|
||||
let result = self.emit_global().undef(ty, None).with_type(ty);
|
||||
self.zombie(result.def, "get_fn_addr");
|
||||
self.function_pointers.borrow_mut().insert(result, function);
|
||||
result
|
||||
self.register_fn_ptr(function)
|
||||
}
|
||||
|
||||
fn eh_personality(&self) -> Self::Value {
|
||||
|
Loading…
Reference in New Issue
Block a user