Start refactoring SPIRV module generation in preparation for support of functions

This commit is contained in:
Andrzej Janik
2020-09-03 01:45:08 +02:00
parent 0f4a4c634b
commit de734305cf
5 changed files with 141 additions and 102 deletions

View File

@ -14,10 +14,7 @@ pub enum ModuleCompileError<'a> {
} }
impl<'a> ModuleCompileError<'a> { impl<'a> ModuleCompileError<'a> {
pub fn get_build_log(&self) { pub fn get_build_log(&self) {}
}
} }
impl<'a> From<ptx::SpirvError> for ModuleCompileError<'a> { impl<'a> From<ptx::SpirvError> for ModuleCompileError<'a> {

View File

@ -48,24 +48,25 @@ impl<
pub struct Module<'a> { pub struct Module<'a> {
pub version: (u8, u8), pub version: (u8, u8),
pub functions: Vec<Function<'a>>, pub functions: Vec<ParsedFunction<'a>>,
} }
pub enum FunctionReturn<'a> { pub enum FunctionHeader<'a, P: ArgParams> {
Func(Vec<Argument<'a>>), Func(Vec<Argument<P>>, P::ID),
Kernel, Kernel(&'a str),
} }
pub struct Function<'a> { pub struct Function<'a, P: ArgParams, S> {
pub func_directive: FunctionReturn<'a>, pub func_directive: FunctionHeader<'a, P>,
pub name: &'a str, pub args: Vec<Argument<P>>,
pub args: Vec<Argument<'a>>, pub body: Option<Vec<S>>,
pub body: Option<Vec<Statement<ParsedArgParams<'a>>>>,
} }
pub type ParsedFunction<'a> = Function<'a, ParsedArgParams<'a>, Statement<ParsedArgParams<'a>>>;
#[derive(Default)] #[derive(Default)]
pub struct Argument<'a> { pub struct Argument<P: ArgParams> {
pub name: &'a str, pub name: P::ID,
pub a_type: ScalarType, pub a_type: ScalarType,
pub length: u32, pub length: u32,
} }
@ -231,7 +232,7 @@ pub struct CallData {
pub struct AbsDetails { pub struct AbsDetails {
pub flush_to_zero: bool, pub flush_to_zero: bool,
pub typ: ScalarType pub typ: ScalarType,
} }
pub struct ArgCall<P: ArgParams> { pub struct ArgCall<P: ArgParams> {

View File

@ -189,7 +189,7 @@ TargetSpecifier = {
"map_f64_to_f32" "map_f64_to_f32"
}; };
Directive: Option<ast::Function<'input>> = { Directive: Option<ast::Function<'input, ast::ParsedArgParams<'input>, ast::Statement<ast::ParsedArgParams<'input>>>> = {
AddressSize => None, AddressSize => None,
<f:Function> => Some(f), <f:Function> => Some(f),
File => None, File => None,
@ -200,12 +200,11 @@ AddressSize = {
".address_size" Num ".address_size" Num
}; };
Function: ast::Function<'input> = { Function: ast::Function<'input, ast::ParsedArgParams<'input>, ast::Statement<ast::ParsedArgParams<'input>>> = {
LinkingDirective* LinkingDirective*
<func_directive:FunctionReturn> <func_directive:FunctionHeader>
<name:ExtendedID>
<args:Arguments> <args:Arguments>
<body:FunctionBody> => ast::Function{<>} <body:FunctionBody> => ast::Function{<>}
}; };
LinkingDirective = { LinkingDirective = {
@ -214,17 +213,17 @@ LinkingDirective = {
".weak" ".weak"
}; };
FunctionReturn: ast::FunctionReturn<'input> = { FunctionHeader: ast::FunctionHeader<'input, ast::ParsedArgParams<'input>> = {
".entry" => ast::FunctionReturn::Kernel, ".entry" <name:ExtendedID> => ast::FunctionHeader::Kernel(name),
".func" <args:Arguments?> => ast::FunctionReturn::Func(args.unwrap_or_else(|| Vec::new())) ".func" <args:Arguments?> <name:ExtendedID> => ast::FunctionHeader::Func(args.unwrap_or_else(|| Vec::new()), name)
}; };
Arguments: Vec<ast::Argument<'input>> = { Arguments: Vec<ast::Argument<ast::ParsedArgParams<'input>>> = {
"(" <args:Comma<FunctionInput>> ")" => args "(" <args:Comma<FunctionInput>> ")" => args
} }
// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parameter-state-space // https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parameter-state-space
FunctionInput: ast::Argument<'input> = { FunctionInput: ast::Argument<ast::ParsedArgParams<'input>> = {
".param" <_type:ScalarType> <name:ExtendedID> => { ".param" <_type:ScalarType> <name:ExtendedID> => {
ast::Argument {a_type: _type, name: name, length: 1 } ast::Argument {a_type: _type, name: name, length: 1 }
}, },

View File

@ -52,6 +52,7 @@ test_ptx!(cvt_sat_s_u, [-1i32], [0i32]);
test_ptx!(cvta, [3.0f32], [3.0f32]); test_ptx!(cvta, [3.0f32], [3.0f32]);
test_ptx!(block, [1u64], [2u64]); test_ptx!(block, [1u64], [2u64]);
test_ptx!(local_align, [1u64], [1u64]); test_ptx!(local_align, [1u64], [1u64]);
test_ptx!(call, [1u64], [2u64]);
struct DisplayError<T: Debug> { struct DisplayError<T: Debug> {
err: T, err: T,

View File

@ -154,7 +154,7 @@ impl TypeWordMap {
} }
} }
pub fn to_spirv_module(ast: ast::Module) -> Result<dr::Module, dr::Error> { pub fn to_spirv_module<'a>(ast: ast::Module<'a>) -> Result<dr::Module, dr::Error> {
let mut builder = dr::Builder::new(); let mut builder = dr::Builder::new();
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_logicallayout_a_logical_layout_of_a_module // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_logicallayout_a_logical_layout_of_a_module
builder.set_version(1, 3); builder.set_version(1, 3);
@ -163,13 +163,21 @@ pub fn to_spirv_module(ast: ast::Module) -> Result<dr::Module, dr::Error> {
let opencl_id = emit_opencl_import(&mut builder); let opencl_id = emit_opencl_import(&mut builder);
emit_memory_model(&mut builder); emit_memory_model(&mut builder);
let mut map = TypeWordMap::new(&mut builder); let mut map = TypeWordMap::new(&mut builder);
for f in ast.functions { let mut id_defs = GlobalStringIdResolver::new(builder.id());
emit_function(&mut builder, &mut map, opencl_id, f)?; let ssa_functions = ast
.functions
.into_iter()
.map(|f| to_ssa_function(&mut id_defs, opencl_id, f))
.collect::<Vec<_>>();
for f in ssa_functions {
emit_function_args(&mut builder, &mut map, &*f.args);
emit_function_body_ops(&mut builder, &mut map, opencl_id, &f.body)?;
builder.end_function()?;
} }
Ok(builder.module()) Ok(builder.module())
} }
pub fn to_spirv(ast: ast::Module) -> Result<Vec<u32>, dr::Error> { pub fn to_spirv<'a>(ast: ast::Module<'a>) -> Result<Vec<u32>, dr::Error> {
let module = to_spirv_module(ast)?; let module = to_spirv_module(ast)?;
Ok(module.assemble()) Ok(module.assemble())
} }
@ -196,28 +204,28 @@ fn emit_memory_model(builder: &mut dr::Builder) {
); );
} }
fn emit_function<'a>( fn to_ssa_function<'a>(
builder: &mut dr::Builder, id_defs: &mut GlobalStringIdResolver<'a>,
map: &mut TypeWordMap,
opencl_id: spirv::Word, opencl_id: spirv::Word,
f: ast::Function<'a>, f: ast::ParsedFunction<'a>,
) -> Result<spirv::Word, rspirv::dr::Error> { ) -> ExpandedFunction<'a> {
let func_type = get_function_type(builder, map, &f.args); let ids_start = id_defs.current_id();
let func_id = let fn_resolver = FnStringIdResolver::new(id_defs);
builder.begin_function(map.void(), None, spirv::FunctionControl::NONE, func_type)?; let f_header = match f.func_directive {
match f.func_directive { ast::FunctionHeader::Kernel(name) => todo!(),
ast::FunctionReturn::Kernel => { ast::FunctionHeader::Func(ret_params, name) => todo!(),
builder.entry_point(spirv::ExecutionModel::Kernel, func_id, f.name, &[]) };
} let f_args = todo!();
_ => todo!(), let f_body = Some(to_ssa(
fn_resolver,
&f.args,
f.body.unwrap_or_else(|| todo!()),
));
ExpandedFunction {
func_directive: f_header,
args: f_args,
body: f_body,
} }
let (mut func_body, unique_ids) = to_ssa(&f.args, f.body.unwrap_or_else(|| todo!()));
let id_offset = builder.reserve_ids(unique_ids);
emit_function_args(builder, id_offset, map, &f.args);
func_body = apply_id_offset(func_body, id_offset);
emit_function_body_ops(builder, map, opencl_id, &func_body)?;
builder.end_function()?;
Ok(func_id)
} }
fn apply_id_offset(func_body: Vec<ExpandedStatement>, id_offset: u32) -> Vec<ExpandedStatement> { fn apply_id_offset(func_body: Vec<ExpandedStatement>, id_offset: u32) -> Vec<ExpandedStatement> {
@ -228,16 +236,19 @@ fn apply_id_offset(func_body: Vec<ExpandedStatement>, id_offset: u32) -> Vec<Exp
} }
fn to_ssa<'a, 'b>( fn to_ssa<'a, 'b>(
f_args: &'b [ast::Argument<'a>], mut id_defs: FnStringIdResolver<'a, 'b>,
f_args: &'b [ast::Argument<ast::ParsedArgParams<'a>>],
f_body: Vec<ast::Statement<ast::ParsedArgParams<'a>>>, f_body: Vec<ast::Statement<ast::ParsedArgParams<'a>>>,
) -> (Vec<ExpandedStatement>, spirv::Word) { ) -> Vec<ExpandedStatement> {
let (normalized_ids, mut id_def) = normalize_identifiers(&f_args, f_body); let normalized_ids = normalize_identifiers(&mut id_defs, &f_args, f_body);
let normalized_statements = normalize_predicates(normalized_ids, &mut id_def); let mut numeric_id_defs = id_defs.finish();
let ssa_statements = insert_mem_ssa_statements(normalized_statements, &mut id_def); let normalized_statements = normalize_predicates(normalized_ids, &mut numeric_id_defs);
let expanded_statements = expand_arguments(ssa_statements, &mut id_def); let ssa_statements = insert_mem_ssa_statements(normalized_statements, &mut numeric_id_defs);
let expanded_statements = insert_implicit_conversions(expanded_statements, &mut id_def); let expanded_statements = expand_arguments(ssa_statements, &mut numeric_id_defs);
let labeled_statements = normalize_labels(expanded_statements, &mut id_def); let expanded_statements =
(labeled_statements, id_def.ids_count()) insert_implicit_conversions(expanded_statements, &mut numeric_id_defs);
let labeled_statements = normalize_labels(expanded_statements, &mut numeric_id_defs);
labeled_statements
} }
fn normalize_labels( fn normalize_labels(
@ -391,9 +402,9 @@ fn insert_mem_ssa_statements(
result result
} }
fn expand_arguments( fn expand_arguments<'a, 'b, 'c>(
func: Vec<NormalizedStatement>, func: Vec<NormalizedStatement>,
id_def: &mut NumericIdResolver, id_def: &'c mut NumericIdResolver<'a, 'b>,
) -> Vec<ExpandedStatement> { ) -> Vec<ExpandedStatement> {
let mut result = Vec::with_capacity(func.len()); let mut result = Vec::with_capacity(func.len());
for s in func { for s in func {
@ -416,18 +427,23 @@ fn expand_arguments(
result result
} }
struct FlattenArguments<'a> { struct FlattenArguments<'a, 'b, 'c> {
func: &'a mut Vec<ExpandedStatement>, func: &'c mut Vec<ExpandedStatement>,
id_def: &'a mut NumericIdResolver, id_def: &'c mut NumericIdResolver<'a, 'b>,
} }
impl<'a> FlattenArguments<'a> { impl<'a, 'b, 'c> FlattenArguments<'a, 'b, 'c> {
fn new(func: &'a mut Vec<ExpandedStatement>, id_def: &'a mut NumericIdResolver) -> Self { fn new(
func: &'c mut Vec<ExpandedStatement>,
id_def: &'c mut NumericIdResolver<'a, 'b>,
) -> Self {
FlattenArguments { func, id_def } FlattenArguments { func, id_def }
} }
} }
impl<'a> ArgumentMapVisitor<NormalizedArgParams, ExpandedArgParams> for FlattenArguments<'a> { impl<'a, 'b, 'c> ArgumentMapVisitor<NormalizedArgParams, ExpandedArgParams>
for FlattenArguments<'a, 'b, 'c>
{
fn dst_variable(&mut self, desc: ArgumentDescriptor<spirv::Word>) -> spirv::Word { fn dst_variable(&mut self, desc: ArgumentDescriptor<spirv::Word>) -> spirv::Word {
desc.op desc.op
} }
@ -577,18 +593,17 @@ fn insert_implicit_conversions(
fn get_function_type( fn get_function_type(
builder: &mut dr::Builder, builder: &mut dr::Builder,
map: &mut TypeWordMap, map: &mut TypeWordMap,
args: &[ast::Argument], args: &[ast::Argument<ast::ParsedArgParams>],
) -> spirv::Word { ) -> spirv::Word {
map.get_or_add_fn(builder, args.iter().map(|arg| SpirvType::from(arg.a_type))) map.get_or_add_fn(builder, args.iter().map(|arg| SpirvType::from(arg.a_type)))
} }
fn emit_function_args( fn emit_function_args(
builder: &mut dr::Builder, builder: &mut dr::Builder,
id_offset: spirv::Word,
map: &mut TypeWordMap, map: &mut TypeWordMap,
args: &[ast::Argument], args: &[ast::Argument<ExpandedArgParams>],
) { ) {
let mut id = id_offset; let mut id = todo!();
for arg in args { for arg in args {
let result_type = map.get_or_add_scalar(builder, arg.a_type); let result_type = map.get_or_add_scalar(builder, arg.a_type);
let inst = dr::Instruction::new( let inst = dr::Instruction::new(
@ -606,9 +621,9 @@ fn emit_function_body_ops(
builder: &mut dr::Builder, builder: &mut dr::Builder,
map: &mut TypeWordMap, map: &mut TypeWordMap,
opencl: spirv::Word, opencl: spirv::Word,
func: &[ExpandedStatement], func: &Option<Vec<ExpandedStatement>>,
) -> Result<(), dr::Error> { ) -> Result<(), dr::Error> {
for s in func { for s in func.as_ref().unwrap() {
match s { match s {
Statement::Label(id) => { Statement::Label(id) => {
if builder.block.is_some() { if builder.block.is_some() {
@ -1079,10 +1094,10 @@ fn emit_implicit_conversion(
// TODO: support scopes // TODO: support scopes
fn normalize_identifiers<'a, 'b>( fn normalize_identifiers<'a, 'b>(
args: &'b [ast::Argument<'a>], id_defs: &mut FnStringIdResolver<'a, 'b>,
args: &[ast::Argument<ast::ParsedArgParams<'a>>],
func: Vec<ast::Statement<ast::ParsedArgParams<'a>>>, func: Vec<ast::Statement<ast::ParsedArgParams<'a>>>,
) -> (Vec<ast::Statement<NormalizedArgParams>>, NumericIdResolver) { ) -> Vec<ast::Statement<NormalizedArgParams>> {
let mut id_defs = StringIdResolver::new();
for arg in args { for arg in args {
id_defs.add_def(arg.name, Some(ast::Type::Scalar(arg.a_type))); id_defs.add_def(arg.name, Some(ast::Type::Scalar(arg.a_type)));
} }
@ -1096,13 +1111,13 @@ fn normalize_identifiers<'a, 'b>(
} }
let mut result = Vec::new(); let mut result = Vec::new();
for s in func { for s in func {
expand_map_variables(&mut id_defs, &mut result, s); expand_map_variables(id_defs, &mut result, s);
} }
(result, id_defs.finish()) result
} }
fn expand_map_variables<'a>( fn expand_map_variables<'a, 'b>(
id_defs: &mut StringIdResolver<'a>, id_defs: &mut FnStringIdResolver<'a, 'b>,
result: &mut Vec<ast::Statement<NormalizedArgParams>>, result: &mut Vec<ast::Statement<NormalizedArgParams>>,
s: ast::Statement<ast::ParsedArgParams<'a>>, s: ast::Statement<ast::ParsedArgParams<'a>>,
) { ) {
@ -1145,24 +1160,53 @@ fn expand_map_variables<'a>(
} }
} }
struct StringIdResolver<'a> { struct GlobalStringIdResolver<'a> {
current_id: spirv::Word, current_id: spirv::Word,
variables: HashMap<Cow<'a, str>, spirv::Word>,
}
impl<'a> GlobalStringIdResolver<'a> {
fn new(start_id: spirv::Word) -> Self {
Self {
current_id: start_id,
variables: HashMap::new(),
}
}
fn add_def(&mut self, id: &'a str) -> spirv::Word {
let numeric_id = self.current_id;
self.variables.insert(Cow::Borrowed(id), numeric_id);
self.current_id += 1;
numeric_id
}
fn reserve_id(&mut self) {
self.current_id += 1;
}
fn current_id(&self) -> spirv::Word {
self.current_id
}
}
struct FnStringIdResolver<'a, 'b> {
global: &'b mut GlobalStringIdResolver<'a>,
variables: Vec<HashMap<Cow<'a, str>, spirv::Word>>, variables: Vec<HashMap<Cow<'a, str>, spirv::Word>>,
type_check: HashMap<u32, ast::Type>, type_check: HashMap<u32, ast::Type>,
} }
impl<'a> StringIdResolver<'a> { impl<'a, 'b> FnStringIdResolver<'a, 'b> {
fn new() -> Self { fn new(global: &'b mut GlobalStringIdResolver<'a>) -> Self {
StringIdResolver { Self {
current_id: 0u32, global: global,
variables: vec![HashMap::new(); 1], variables: vec![HashMap::new(); 1],
type_check: HashMap::new(), type_check: HashMap::new(),
} }
} }
fn finish(self) -> NumericIdResolver { fn finish(self) -> NumericIdResolver<'a, 'b> {
NumericIdResolver { NumericIdResolver {
current_id: self.current_id, global: self.global,
type_check: self.type_check, type_check: self.type_check,
} }
} }
@ -1175,18 +1219,18 @@ impl<'a> StringIdResolver<'a> {
self.variables.pop(); self.variables.pop();
} }
fn get_id(&self, id: &'a str) -> spirv::Word { fn get_id(&self, id: &str) -> spirv::Word {
for scope in self.variables.iter().rev() { for scope in self.variables.iter().rev() {
match scope.get(id) { match scope.get(id) {
Some(id) => return *id, Some(id) => return *id,
None => continue, None => continue,
} }
} }
panic!() self.global.variables[id]
} }
fn add_def(&mut self, id: &'a str, typ: Option<ast::Type>) -> spirv::Word { fn add_def(&mut self, id: &'a str, typ: Option<ast::Type>) -> spirv::Word {
let numeric_id = self.current_id; let numeric_id = self.global.current_id;
self.variables self.variables
.last_mut() .last_mut()
.unwrap() .unwrap()
@ -1194,7 +1238,7 @@ impl<'a> StringIdResolver<'a> {
if let Some(typ) = typ { if let Some(typ) = typ {
self.type_check.insert(numeric_id, typ); self.type_check.insert(numeric_id, typ);
} }
self.current_id += 1; self.global.current_id += 1;
numeric_id numeric_id
} }
@ -1205,7 +1249,7 @@ impl<'a> StringIdResolver<'a> {
count: u32, count: u32,
typ: ast::Type, typ: ast::Type,
) -> impl Iterator<Item = spirv::Word> { ) -> impl Iterator<Item = spirv::Word> {
let numeric_id = self.current_id; let numeric_id = self.global.current_id;
for i in 0..count { for i in 0..count {
self.variables self.variables
.last_mut() .last_mut()
@ -1213,33 +1257,29 @@ impl<'a> StringIdResolver<'a> {
.insert(Cow::Owned(format!("{}{}", base_id, i)), numeric_id + i); .insert(Cow::Owned(format!("{}{}", base_id, i)), numeric_id + i);
self.type_check.insert(numeric_id + i, typ); self.type_check.insert(numeric_id + i, typ);
} }
self.current_id += count; self.global.current_id += count;
(0..count).into_iter().map(move |i| i + numeric_id) (0..count).into_iter().map(move |i| i + numeric_id)
} }
} }
struct NumericIdResolver { struct NumericIdResolver<'a, 'b> {
current_id: spirv::Word, global: &'b mut GlobalStringIdResolver<'a>,
type_check: HashMap<u32, ast::Type>, type_check: HashMap<u32, ast::Type>,
} }
impl NumericIdResolver { impl<'a, 'b> NumericIdResolver<'a, 'b> {
fn get_type(&self, id: spirv::Word) -> ast::Type { fn get_type(&self, id: spirv::Word) -> ast::Type {
self.type_check[&id] self.type_check[&id]
} }
fn new_id(&mut self, typ: Option<ast::Type>) -> spirv::Word { fn new_id(&mut self, typ: Option<ast::Type>) -> spirv::Word {
let new_id = self.current_id; let new_id = self.global.current_id;
if let Some(typ) = typ { if let Some(typ) = typ {
self.type_check.insert(new_id, typ); self.type_check.insert(new_id, typ);
} }
self.current_id += 1; self.global.current_id += 1;
new_id new_id
} }
fn ids_count(&self) -> spirv::Word {
self.current_id
}
} }
enum Statement<I> { enum Statement<I> {
@ -1284,6 +1324,7 @@ impl ast::ArgParams for NormalizedArgParams {
enum ExpandedArgParams {} enum ExpandedArgParams {}
type ExpandedStatement = Statement<ast::Instruction<ExpandedArgParams>>; type ExpandedStatement = Statement<ast::Instruction<ExpandedArgParams>>;
type ExpandedFunction<'a> = ast::Function<'a, ExpandedArgParams, ExpandedStatement>;
impl ast::ArgParams for ExpandedArgParams { impl ast::ArgParams for ExpandedArgParams {
type ID = spirv::Word; type ID = spirv::Word;