Implement bool=i8 and some of the DeclareMethods API

This commit is contained in:
khyperia 2020-08-27 10:38:51 +02:00
parent ab6564e8b1
commit a526109042
5 changed files with 206 additions and 136 deletions

View File

@ -1,7 +1,8 @@
use crate::codegen_cx::CodegenCx;
use rspirv::dr::Operand;
use rspirv::spirv::{Decoration, StorageClass, Word};
use rustc_middle::ty::{layout::TyAndLayout, TyKind};
use rustc_middle::ty::{layout::TyAndLayout, Ty, TyKind};
use rustc_target::abi::call::{FnAbi, PassMode};
use rustc_target::abi::{Abi, FieldsShape, LayoutOf, Primitive, Scalar};
use std::fmt;
@ -42,8 +43,6 @@ impl SpirvType {
if let Some(&cached) = cx.type_cache.borrow().get(self) {
return cached;
}
//let cached = cx.type_cache.borrow_mut().entry(self);
// TODO: rspirv does a linear search to dedupe, probably want to cache here.
let result = match *self {
SpirvType::Void => cx.emit_global().type_void(),
SpirvType::Bool => cx.emit_global().type_bool(),
@ -190,6 +189,89 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_, '_> {
}
}
// returns (function_type, return_type, argument_types)
pub fn trans_fnabi<'spv, 'tcx>(
cx: &CodegenCx<'spv, 'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
) -> (Word, Word, Vec<Word>) {
let mut argument_types = Vec::new();
for arg in &fn_abi.args {
let arg_type = match arg.mode {
PassMode::Ignore => panic!(
"TODO: Argument PassMode::Ignore not supported yet: {:?}",
arg
),
PassMode::Direct(_arg_attributes) => trans_type_immediate(cx, arg.layout),
PassMode::Pair(_arg_attributes_1, _arg_attributes_2) => {
// TODO: Make this more efficient, don't generate struct
let tuple = cx.lookup_type(trans_type(cx, arg.layout));
let (left, right) = match tuple {
SpirvType::Adt {
ref field_types,
field_offsets: _,
} => {
if let [left, right] = *field_types.as_slice() {
(left, right)
} else {
panic!("PassMode::Pair did not produce tuple: {:?}", tuple)
}
}
_ => panic!("PassMode::Pair did not produce tuple: {:?}", tuple),
};
argument_types.push(left);
argument_types.push(right);
continue;
}
PassMode::Cast(_cast_target) => trans_type(cx, arg.layout),
// TODO: Deal with wide ptr?
PassMode::Indirect(_arg_attributes, _wide_ptr_attrs) => {
let pointee = trans_type(cx, arg.layout);
SpirvType::Pointer {
storage_class: StorageClass::Generic,
pointee,
}
.def(cx)
}
};
argument_types.push(arg_type);
}
// TODO: Other modes
let return_type = match fn_abi.ret.mode {
PassMode::Ignore => SpirvType::Void.def(cx),
PassMode::Direct(_arg_attributes) => trans_type_immediate(cx, fn_abi.ret.layout),
PassMode::Pair(_arg_attributes_1, _arg_attributes_2) => trans_type(cx, fn_abi.ret.layout),
// TODO: Is this right?
PassMode::Cast(_cast_target) => trans_type(cx, fn_abi.ret.layout),
// TODO: Deal with wide ptr?
PassMode::Indirect(_arg_attributes, _wide_ptr_attrs) => {
let pointee = trans_type(cx, fn_abi.ret.layout);
let pointer = SpirvType::Pointer {
storage_class: StorageClass::Generic,
pointee,
}
.def(cx);
argument_types.push(pointer);
SpirvType::Void.def(cx)
}
};
let function_type = SpirvType::Function {
return_type,
arguments: argument_types.clone(),
}
.def(cx);
(function_type, return_type, argument_types)
}
pub fn trans_type_immediate<'spv, 'tcx>(cx: &CodegenCx<'spv, 'tcx>, ty: TyAndLayout<'tcx>) -> Word {
if let Abi::Scalar(ref scalar) = ty.abi {
if scalar.is_bool() {
return SpirvType::Bool.def(cx);
}
}
trans_type(cx, ty)
}
pub fn trans_type<'spv, 'tcx>(cx: &CodegenCx<'spv, 'tcx>, ty: TyAndLayout<'tcx>) -> Word {
if ty.is_zst() {
// An empty struct is zero-sized
@ -236,20 +318,9 @@ fn trans_scalar<'spv, 'tcx>(
pair_index: Option<usize>,
) -> Word {
match scalar.value {
// TODO: Do we use scalar.valid_range?
Primitive::Int(width, signedness) => {
// let width_bits = width.size().bits() as u128;
// let width_max_val = if width_bits == 128 {
// u128::MAX
// } else {
// (1 << width_bits) - 1
// };
if scalar.valid_range == (0..=1) {
SpirvType::Bool.def(cx)
// } else if scalar.valid_range != (0..=width_max_val) {
// TODO: Do we handle this specially?
} else {
SpirvType::Integer(width.size().bits() as u32, signedness).def(cx)
}
SpirvType::Integer(width.size().bits() as u32, signedness).def(cx)
}
Primitive::F32 => SpirvType::Float(32).def(cx),
Primitive::F64 => SpirvType::Float(64).def(cx),

View File

@ -1,6 +1,7 @@
use super::Builder;
use crate::abi::SpirvType;
use crate::builder_spirv::{BuilderCursor, SpirvValueExt};
use rspirv::dr::Operand;
use rspirv::spirv::StorageClass;
use rustc_codegen_ssa::base::to_immediate;
use rustc_codegen_ssa::common::{
@ -69,7 +70,13 @@ impl<'a, 'spv, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'spv, 'tcx> {
let emit = self.emit_with_cursor(cursor);
let selected_function = emit.selected_function().unwrap();
let selected_function = &emit.module_ref().functions[selected_function];
selected_function.def.as_ref().unwrap().result_id.unwrap()
let def_inst = selected_function.def.as_ref().unwrap();
let def = def_inst.result_id.unwrap();
let ty = match def_inst.operands[1] {
Operand::IdRef(ty) => ty,
ref other => panic!("Invalid operand to function inst: {}", other),
};
def.with_type(ty)
};
self.cursor = cursor;
self.current_fn = current_fn;
@ -77,7 +84,7 @@ impl<'a, 'spv, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'spv, 'tcx> {
}
fn new_block<'b>(cx: &'a Self::CodegenCx, llfn: Self::Function, _name: &'b str) -> Self {
let cursor_fn = cx.builder.select_function_by_id(llfn);
let cursor_fn = cx.builder.select_function_by_id(llfn.def);
let label = cx.emit_with_cursor(cursor_fn).begin_block(None).unwrap();
let cursor = cx.builder.select_block_by_id(label);
Self {
@ -220,12 +227,12 @@ impl<'a, 'spv, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'spv, 'tcx> {
fn alloca(&mut self, ty: Self::Type, _align: Align) -> Self::Value {
let ptr_ty = SpirvType::Pointer {
storage_class: StorageClass::Function,
storage_class: StorageClass::Generic,
pointee: ty,
}
.def(self);
self.emit()
.variable(ptr_ty, None, StorageClass::Function, None)
.variable(ptr_ty, None, StorageClass::Generic, None)
.with_type(ptr_ty)
}
@ -490,6 +497,10 @@ impl<'a, 'spv, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'spv, 'tcx> {
}
fn intcast(&mut self, val: Self::Value, dest_ty: Self::Type, is_signed: bool) -> Self::Value {
if val.ty == dest_ty {
// I guess?
return val;
}
match (self.lookup_type(val.ty), self.lookup_type(dest_ty)) {
// sign change
(
@ -812,10 +823,16 @@ impl<'a, 'spv, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'spv, 'tcx> {
if funclet.is_some() {
panic!("TODO: Funclets are not supported");
}
let result_type = match self.lookup_type(llfn.ty) {
SpirvType::Function { return_type, .. } => return_type,
let (result_type, argument_types) = match self.lookup_type(llfn.ty) {
SpirvType::Function {
return_type,
arguments,
} => (return_type, arguments),
ty => panic!("Calling non-function type: {:?}", ty),
};
for (argument, argument_type) in args.iter().zip(argument_types) {
assert_ty_eq!(self, argument.ty, argument_type);
}
let args = args.iter().map(|arg| arg.def).collect::<Vec<_>>();
self.emit()
.function_call(result_type, None, llfn.def, args)

View File

@ -49,16 +49,22 @@ impl<'a, 'spv, 'tcx> Builder<'a, 'spv, 'tcx> {
// "An OpAccessChain instruction is the equivalent of an LLVM getelementptr instruction where the first index element is zero."
// https://github.com/gpuweb/gpuweb/issues/33
let mut result_indices = Vec::with_capacity(indices.len() - 1);
let /*mut*/ (storage_class, result_pointee_type) = match self.lookup_type(ptr.ty) {
SpirvType::Pointer { storage_class, pointee } => (storage_class, pointee),
let (storage_class, mut result_pointee_type) = match self.lookup_type(ptr.ty) {
SpirvType::Pointer {
storage_class,
pointee,
} => (storage_class, pointee),
other_type => panic!("GEP first deref not implemented for type {:?}", other_type),
};
for index in indices.iter().cloned().skip(1) {
result_indices.push(index.def);
panic!(
"GEP not implemented for type {:?}",
self.lookup_type(result_pointee_type)
);
result_pointee_type = match self.lookup_type(result_pointee_type) {
SpirvType::Array { element, count: _ } => element,
_ => panic!(
"GEP not implemented for type {:?}",
self.debug_type(result_pointee_type)
),
};
}
let result_type = SpirvType::Pointer {
storage_class,
@ -173,7 +179,7 @@ impl<'a, 'spv, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'spv, 'tcx> {
dst: PlaceRef<'tcx, Self::Value>,
) {
fn next<'a, 'spv, 'tcx>(bx: &mut Builder<'a, 'spv, 'tcx>, idx: &mut usize) -> SpirvValue {
let val = bx.function_parameter_values.borrow()[&bx.current_fn][*idx];
let val = bx.function_parameter_values.borrow()[&bx.current_fn.def][*idx];
*idx += 1;
val
}
@ -232,7 +238,7 @@ impl<'a, 'spv, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'spv, 'tcx> {
}
fn get_param(&self, index: usize) -> Self::Value {
self.function_parameter_values.borrow()[&self.current_fn][index]
self.function_parameter_values.borrow()[&self.current_fn.def][index]
}
}

View File

@ -20,7 +20,7 @@ impl ModuleSpirv {
}
}
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq)]
pub struct SpirvValue {
pub def: Word,
pub ty: Word,

View File

@ -14,15 +14,14 @@ use rustc_middle::mir::interpret::Allocation;
use rustc_middle::mir::mono::{CodegenUnit, Linkage, Visibility};
use rustc_middle::mir::Body;
use rustc_middle::ty::layout::{FnAbiExt, HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::{Instance, PolyExistentialTraitRef};
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::{Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt, TypeFoldable};
use rustc_mir::interpret::Scalar;
use rustc_session::Session;
use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_span::symbol::Symbol;
use rustc_span::SourceFile;
use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg};
use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
use rustc_target::abi::{
self, Abi, AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Size, TargetDataLayout,
};
@ -37,6 +36,8 @@ pub struct CodegenCx<'spv, 'tcx> {
pub spirv_module: &'spv ModuleSpirv,
/// Spir-v module builder
pub builder: BuilderSpirv,
/// Used for the DeclareMethods API (not sure what it is yet)
pub declared_values: RefCell<HashMap<String, SpirvValue>>,
/// Map from MIR function to spir-v function ID
pub function_defs: RefCell<HashMap<Instance<'tcx>, SpirvValue>>,
/// Map from function ID to parameter list
@ -58,6 +59,7 @@ impl<'spv, 'tcx> CodegenCx<'spv, 'tcx> {
codegen_unit,
spirv_module,
builder: BuilderSpirv::new(),
declared_values: RefCell::new(HashMap::new()),
function_defs: RefCell::new(HashMap::new()),
function_parameter_values: RefCell::new(HashMap::new()),
type_defs: RefCell::new(HashMap::new()),
@ -79,11 +81,22 @@ impl<'spv, 'tcx> CodegenCx<'spv, 'tcx> {
self.builder.builder(cursor)
}
// returns (function_type, return_type, argument_types)
pub fn trans_fnabi(&self, ty: &FnAbi<'tcx, Ty<'tcx>>) -> (Word, Word, Vec<Word>) {
use crate::abi::trans_fnabi;
trans_fnabi(self, ty)
}
pub fn trans_type(&self, ty: TyAndLayout<'tcx>) -> Word {
use crate::abi::trans_type;
trans_type(self, ty)
}
pub fn trans_type_immediate(&self, ty: TyAndLayout<'tcx>) -> Word {
use crate::abi::trans_type_immediate;
trans_type_immediate(self, ty)
}
pub fn lookup_type(&self, ty: Word) -> SpirvType {
self.type_defs
.borrow()
@ -110,7 +123,7 @@ impl<'spv, 'tcx> CodegenCx<'spv, 'tcx> {
impl<'spv, 'tcx> BackendTypes for CodegenCx<'spv, 'tcx> {
type Value = SpirvValue;
type Function = Word;
type Function = SpirvValue;
type BasicBlock = Word;
type Type = Word;
@ -173,7 +186,7 @@ impl<'spv, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'spv, 'tcx> {
}
fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type {
self.trans_type(layout)
self.trans_type_immediate(layout)
}
fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool {
@ -223,27 +236,30 @@ impl<'spv, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'spv, 'tcx> {
}
impl<'spv, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'spv, 'tcx> {
// TODO: llvm types are signless, as in neither signed nor unsigned (I think?), so these are expected to be
// signless. Do we want a SpirvType::Integer(_, Signless) to indicate the sign is unknown, and to do conversions at
// appropriate places?
fn type_i1(&self) -> Self::Type {
SpirvType::Bool.def(self)
}
fn type_i8(&self) -> Self::Type {
SpirvType::Integer(8, true).def(self)
SpirvType::Integer(8, false).def(self)
}
fn type_i16(&self) -> Self::Type {
SpirvType::Integer(16, true).def(self)
SpirvType::Integer(16, false).def(self)
}
fn type_i32(&self) -> Self::Type {
SpirvType::Integer(32, true).def(self)
SpirvType::Integer(32, false).def(self)
}
fn type_i64(&self) -> Self::Type {
SpirvType::Integer(64, true).def(self)
SpirvType::Integer(64, false).def(self)
}
fn type_i128(&self) -> Self::Type {
SpirvType::Integer(128, true).def(self)
SpirvType::Integer(128, false).def(self)
}
fn type_isize(&self) -> Self::Type {
let ptr_size = self.tcx.data_layout.pointer_size.bits() as u32;
SpirvType::Integer(ptr_size, true).def(self)
SpirvType::Integer(ptr_size, false).def(self)
}
fn type_f32(&self) -> Self::Type {
@ -344,6 +360,29 @@ impl<'spv, 'tcx> StaticMethods for CodegenCx<'spv, 'tcx> {
}
}
pub fn get_fn<'spv, 'tcx>(cx: &CodegenCx<'spv, 'tcx>, instance: Instance<'tcx>) -> SpirvValue {
assert!(!instance.substs.needs_infer());
assert!(!instance.substs.has_escaping_bound_vars());
if let Some(&func) = cx.function_defs.borrow().get(&instance) {
return func;
}
let sym = cx.tcx.symbol_name(instance).name;
let fn_abi = FnAbi::of_instance(cx, instance, &[]);
let llfn = if let Some(llfn) = cx.get_declared_value(&sym) {
llfn
} else {
cx.declare_fn(&sym, &fn_abi)
};
cx.function_defs.borrow_mut().insert(instance, llfn);
llfn
}
impl<'spv, 'tcx> MiscMethods<'tcx> for CodegenCx<'spv, 'tcx> {
#[allow(clippy::type_complexity)]
fn vtables(
@ -357,11 +396,11 @@ impl<'spv, 'tcx> MiscMethods<'tcx> for CodegenCx<'spv, 'tcx> {
}
fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function {
self.function_defs.borrow()[&instance].def
get_fn(self, instance)
}
fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value {
self.function_defs.borrow()[&instance]
get_fn(self, instance)
}
fn eh_personality(&self) -> Self::Value {
@ -409,96 +448,11 @@ impl<'spv, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'spv, 'tcx> {
instance: Instance<'tcx>,
_linkage: Linkage,
_visibility: Visibility,
_symbol_name: &str,
symbol_name: &str,
) {
let fn_abi = FnAbi::of_instance(self, instance, &[]);
let mut argument_types = Vec::new();
for arg in fn_abi.args {
let arg_type = match arg.mode {
PassMode::Ignore => panic!(
"TODO: Argument PassMode::Ignore not supported yet: {:?}",
arg
),
PassMode::Direct(_arg_attributes) => self.trans_type(arg.layout),
PassMode::Pair(_arg_attributes_1, _arg_attributes_2) => {
// TODO: Make this more efficient, don't generate struct
let tuple = self.lookup_type(self.trans_type(arg.layout));
let (left, right) = match tuple {
SpirvType::Adt {
ref field_types,
field_offsets: _,
} => {
if let [left, right] = *field_types.as_slice() {
(left, right)
} else {
panic!("PassMode::Pair did not produce tuple: {:?}", tuple)
}
}
_ => panic!("PassMode::Pair did not produce tuple: {:?}", tuple),
};
argument_types.push(left);
argument_types.push(right);
continue;
}
PassMode::Cast(_cast_target) => self.trans_type(arg.layout),
// TODO: Deal with wide ptr?
PassMode::Indirect(_arg_attributes, _wide_ptr_attrs) => {
let pointee = self.trans_type(arg.layout);
SpirvType::Pointer {
storage_class: StorageClass::Generic,
pointee,
}
.def(self)
}
};
argument_types.push(arg_type);
}
// TODO: Other modes
let return_type = match fn_abi.ret.mode {
PassMode::Ignore => SpirvType::Void.def(self),
PassMode::Direct(_arg_attributes) => self.trans_type(fn_abi.ret.layout),
PassMode::Pair(_arg_attributes_1, _arg_attributes_2) => {
self.trans_type(fn_abi.ret.layout)
}
// TODO: Is this right?
PassMode::Cast(_cast_target) => self.trans_type(fn_abi.ret.layout),
// TODO: Deal with wide ptr?
PassMode::Indirect(_arg_attributes, _wide_ptr_attrs) => {
let pointee = self.trans_type(fn_abi.ret.layout);
let pointer = SpirvType::Pointer {
storage_class: StorageClass::Generic,
pointee,
}
.def(self);
argument_types.push(pointer);
SpirvType::Void.def(self)
}
};
let control = FunctionControl::NONE;
let function_id = None;
let function_type = SpirvType::Function {
return_type,
arguments: argument_types.clone(),
}
.def(self);
let mut emit = self.emit_global();
let fn_id = emit
.begin_function(return_type, function_id, control, function_type)
.unwrap();
let parameter_values = argument_types
.iter()
.map(|&ty| emit.function_parameter(ty).unwrap().with_type(ty))
.collect::<Vec<_>>();
emit.end_function().unwrap();
self.function_defs
.borrow_mut()
.insert(instance, fn_id.with_type(function_type));
self.function_parameter_values
.borrow_mut()
.insert(fn_id, parameter_values);
let declared = self.declare_fn(symbol_name, &fn_abi);
self.function_defs.borrow_mut().insert(instance, declared);
}
}
@ -511,8 +465,30 @@ impl<'spv, 'tcx> DeclareMethods<'tcx> for CodegenCx<'spv, 'tcx> {
todo!()
}
fn declare_fn(&self, _name: &str, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Function {
todo!()
fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Function {
let control = FunctionControl::NONE;
let function_id = None;
let (function_type, return_type, argument_types) = self.trans_fnabi(fn_abi);
let mut emit = self.emit_global();
let fn_id = emit
.begin_function(return_type, function_id, control, function_type)
.unwrap();
let parameter_values = argument_types
.iter()
.map(|&ty| emit.function_parameter(ty).unwrap().with_type(ty))
.collect::<Vec<_>>();
emit.end_function().unwrap();
self.function_parameter_values
.borrow_mut()
.insert(fn_id, parameter_values);
let result = fn_id.with_type(function_type);
self.declared_values
.borrow_mut()
.insert(name.to_string(), result);
result
}
fn define_global(&self, _name: &str, _ty: Self::Type) -> Option<Self::Value> {
@ -523,8 +499,8 @@ impl<'spv, 'tcx> DeclareMethods<'tcx> for CodegenCx<'spv, 'tcx> {
todo!()
}
fn get_declared_value(&self, _name: &str) -> Option<Self::Value> {
todo!()
fn get_declared_value(&self, name: &str) -> Option<Self::Value> {
self.declared_values.borrow().get(name).copied()
}
fn get_defined_value(&self, _name: &str) -> Option<Self::Value> {