Skip to content

Commit cfdb0ca

Browse files
committed
Inlinig first work
1 parent 4fe6176 commit cfdb0ca

5 files changed

Lines changed: 190 additions & 7 deletions

File tree

naga/src/inline/mod.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
use core::unreachable;
2+
3+
use crate::{
4+
valid::{FunctionInfo, ModuleInfo},
5+
Block, Function, Handle, Module, Span, Statement, Type, UniqueArena,
6+
};
7+
use nt::FastHashMap;
8+
9+
/// Which functions should be inlined.
10+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
11+
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
12+
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
13+
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
14+
pub enum InlineStrategy {
15+
/// Only inline functions that need to be inlined for shaders using unrestricted_pointer_parameters
16+
/// to be compiled.
17+
#[default]
18+
PointerParametersOnly,
19+
/// Inline all functions.
20+
All,
21+
}
22+
23+
struct InlineStackItem {
24+
pub func: Handle<Function>,
25+
pub statement_index: usize,
26+
}
27+
28+
struct InlineState<'a> {
29+
pub module: &'a mut Module,
30+
pub info: &'a mut ModuleInfo,
31+
/// Collect all functions that may need to be inlined, but inline them lazily, keeping
32+
/// track of which have already been inlined.
33+
pub funcs_needing_inline: FastHashMap<Handle<Function>, bool>,
34+
}
35+
36+
/// Perform the inlining pass on a module. It may leave some unused variables and blocks behind,
37+
/// so should be used with the compaction pass.
38+
pub fn inline(module: &mut Module, info: &mut ModuleInfo, strategy: InlineStrategy) {
39+
let mut funcs_needing_inline: FastHashMap<Handle<Function>, bool>;
40+
if strategy == InlineStrategy::All {
41+
funcs_needing_inline = module.functions.iter().map(|e| (e.0, false)).collect();
42+
} else {
43+
funcs_needing_inline = Default::default();
44+
for (handle, func) in module.functions.iter() {
45+
let args = &func.arguments;
46+
let needs_inline = args
47+
.iter()
48+
.any(|arg| parameter_needs_unrestricted_pointer_params(&module.types, r#arg.ty));
49+
if needs_inline {
50+
funcs_needing_inline.insert(handle, false);
51+
}
52+
}
53+
};
54+
let mut state = InlineState {
55+
module,
56+
info,
57+
funcs_needing_inline,
58+
};
59+
for i in 0..state.module.entry_points.len() {
60+
// Take these out so we can modify them while using the rest of the module without using unsafe code.
61+
let function = core::mem::take(&mut state.module.entry_points[i].function);
62+
let mut function_info = std::mem::take(&mut state.info.entry_points[i]);
63+
64+
let function = state.inline_all_calls(function, &mut function_info, false);
65+
66+
state.module.entry_points[i].function = function;
67+
state.info.entry_points[i] = function_info;
68+
}
69+
}
70+
71+
fn parameter_needs_unrestricted_pointer_params(
72+
arena: &UniqueArena<Type>,
73+
r#type: Handle<Type>,
74+
) -> bool {
75+
core::todo!()
76+
}
77+
78+
impl InlineState<'_> {
79+
/// Inline all function calls in a function, after each of those have been recursively inlined and then prepared.
80+
fn inline_all_calls(
81+
&mut self,
82+
mut function: Function,
83+
info: &mut FunctionInfo,
84+
prepare_for_self_inline: bool,
85+
) -> Function {
86+
if !prepare_for_self_inline {
87+
// If it doesn't need to be inlined itself, and doesn't need any of its calls inlined,
88+
// skip.
89+
'a: {
90+
for st in &function.body {
91+
if let &Statement::Call { function, .. } = st {
92+
if self.funcs_needing_inline.contains_key(&function) {
93+
break 'a;
94+
}
95+
}
96+
}
97+
return function;
98+
}
99+
}
100+
101+
let mut new_block = Block::new();
102+
let bool_type = self.module.types.insert(
103+
Type {
104+
name: None,
105+
inner: crate::TypeInner::Scalar(crate::Scalar::BOOL),
106+
},
107+
Span::UNDEFINED,
108+
);
109+
let mut is_done_var = function.local_variables.append(
110+
crate::LocalVariable {
111+
name: None,
112+
ty: bool_type,
113+
init: None,
114+
},
115+
Span::UNDEFINED,
116+
);
117+
let is_done_var_ptr = function.expressions.append(
118+
crate::Expression::LocalVariable(is_done_var),
119+
Span::UNDEFINED,
120+
);
121+
let false_val = function.expressions.append(
122+
crate::Expression::Literal(crate::Literal::Bool(false)),
123+
Span::UNDEFINED,
124+
);
125+
let true_val = function.expressions.append(
126+
crate::Expression::Literal(crate::Literal::Bool(true)),
127+
Span::UNDEFINED,
128+
);
129+
for (st, span) in function
130+
.body
131+
.body
132+
.into_iter()
133+
.zip(function.body.span_info.into_iter())
134+
{
135+
match st {
136+
Statement::Call {
137+
function: handle,
138+
ref result,
139+
ref arguments,
140+
} => {
141+
if self.funcs_needing_inline.get(&handle) == Some(&false) {
142+
self.funcs_needing_inline.insert(handle, true);
143+
144+
let function = core::mem::take(&mut self.module.functions[handle]);
145+
let mut info = std::mem::take(&mut self.info.functions[handle.index()]);
146+
147+
let function = self.inline_all_calls(function, &mut info, true);
148+
149+
self.module.functions[handle] = function;
150+
self.info.functions[handle.index()] = info;
151+
}
152+
if self.funcs_needing_inline.contains_key(&handle) {
153+
new_block.push(
154+
Statement::Store {
155+
pointer: is_done_var_ptr,
156+
value: false_val,
157+
},
158+
span,
159+
);
160+
// Copy-paste the function body inside of a for-loop.
161+
// We will have to reuse adjust_body from the compact module to realign all local variable and expression indices.
162+
new_block.push(
163+
Statement::Loop {
164+
body: todo!(),
165+
continuing: Block::default(),
166+
break_if: Some(true_val),
167+
},
168+
span,
169+
);
170+
} else {
171+
new_block.push(st, span);
172+
}
173+
}
174+
_ => new_block.push(st, span),
175+
}
176+
}
177+
function.body = new_block;
178+
// Update the expressions in info
179+
function
180+
}
181+
}

naga/src/ir/block.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use crate::{Span, Statement};
99
#[cfg_attr(feature = "serialize", serde(transparent))]
1010
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1111
pub struct Block {
12-
body: Vec<Statement>,
12+
pub(crate) body: Vec<Statement>,
1313
#[cfg_attr(feature = "serialize", serde(skip))]
14-
span_info: Vec<Span>,
14+
pub(crate) span_info: Vec<Span>,
1515
}
1616

1717
impl Block {

naga/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ pub mod compact;
116116
pub mod diagnostic_filter;
117117
pub mod error;
118118
pub mod front;
119+
pub mod inline;
119120
pub mod ir;
120121
pub mod keywords;
121122
mod non_max_u32;

naga/src/valid/analyzer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ bitflags::bitflags! {
2424
/// Kinds of expressions that require uniform control flow.
2525
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
2626
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
27-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
27+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
2828
pub struct UniformityRequirements: u8 {
2929
const WORK_GROUP_BARRIER = 0x1;
3030
const DERIVATIVE = if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE { 0 } else { 0x2 };
@@ -34,7 +34,7 @@ bitflags::bitflags! {
3434
}
3535

3636
/// Uniform control flow characteristics.
37-
#[derive(Clone, Debug)]
37+
#[derive(Clone, Debug, Default)]
3838
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
3939
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
4040
#[cfg_attr(test, derive(PartialEq))]
@@ -234,7 +234,7 @@ struct Sampling {
234234
sampler: GlobalOrArgument,
235235
}
236236

237-
#[derive(Debug, Clone)]
237+
#[derive(Debug, Clone, Default)]
238238
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
239239
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
240240
pub struct FunctionInfo {

naga/src/valid/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,9 @@ bitflags::bitflags! {
342342
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
343343
pub struct ModuleInfo {
344344
type_flags: Vec<TypeFlags>,
345-
functions: Vec<FunctionInfo>,
346-
entry_points: Vec<FunctionInfo>,
345+
// Need to be pub(crate) so they can be modified by the inlining pass.
346+
pub(crate) functions: Vec<FunctionInfo>,
347+
pub(crate) entry_points: Vec<FunctionInfo>,
347348
const_expression_types: Box<[TypeResolution]>,
348349
}
349350

0 commit comments

Comments
 (0)