Skip to content

abi: readjust FnAbis to remove unsupported PassModes, via query hooks. #766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 20, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 97 additions & 119 deletions crates/rustc_codegen_spirv/src/abi.rs
Original file line number Diff line number Diff line change
@@ -8,24 +8,95 @@ use rspirv::spirv::{StorageClass, Word};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorReported;
use rustc_index::vec::Idx;
use rustc_middle::bug;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{
self, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyKind, TypeAndMut,
UintTy,
self, Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyCtxt, TyKind,
TypeAndMut, UintTy,
};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
use rustc_span::Span;
use rustc_span::DUMMY_SP;
use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
use rustc_target::abi::call::{ArgAbi, ArgAttributes, FnAbi, PassMode};
use rustc_target::abi::{Abi, Align, FieldsShape, Primitive, Scalar, Size, VariantIdx, Variants};
use rustc_target::spec::abi::Abi as SpecAbi;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::fmt;

use num_traits::cast::FromPrimitive;

pub(crate) fn provide(providers: &mut Providers) {
// This is a lil weird: so, we obviously don't support C ABIs at all. However, libcore does declare some extern
// C functions:
// https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/slice/cmp.rs#L119
// However, those functions will be implemented by compiler-builtins:
// https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/lib.rs#L23-L27
// This theoretically then should be fine to leave as C, but, there's no backend hook for
// `FnAbi::adjust_for_cabi`, causing it to panic:
// https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/compiler/rustc_target/src/abi/call/mod.rs#L603
// So, treat any `extern "C"` functions as `extern "unadjusted"`, to be able to compile libcore with arch=spirv.
providers.fn_sig = |tcx, def_id| {
// We can't capture the old fn_sig and just call that, because fn_sig is a `fn`, not a `Fn`, i.e. it can't
// capture variables. Fortunately, the defaults are exposed (thanks rustdoc), so use that instead.
let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_sig)(tcx, def_id);
result.map_bound(|mut inner| {
if let SpecAbi::C { .. } = inner.abi {
inner.abi = SpecAbi::Unadjusted;
}
inner
})
};

// For the Rust ABI, `FnAbi` adjustments are backend-agnostic, but they will
// use features like `PassMode::Cast`, that are incompatible with SPIR-V.
// By hooking the queries computing `FnAbi`s, we can recompute the `FnAbi`
// from the return/args layouts, to e.g. prefer using `PassMode::Direct`.
fn readjust_fn_abi<'tcx>(
tcx: TyCtxt<'tcx>,
fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>,
) -> &'tcx FnAbi<'tcx, Ty<'tcx>> {
let readjust_arg_abi = |arg: &ArgAbi<'tcx, Ty<'tcx>>| {
let mut arg = ArgAbi::new(&tcx, arg.layout, |_, _, _| ArgAttributes::new());

// Avoid pointlessly passing ZSTs, just like the official Rust ABI.
if arg.layout.is_zst() {
arg.mode = PassMode::Ignore;
}

arg
};
tcx.arena.alloc(FnAbi {
args: fn_abi.args.iter().map(readjust_arg_abi).collect(),
ret: readjust_arg_abi(&fn_abi.ret),

// FIXME(eddyb) validate some of these, and report errors - however,
// we can't just emit errors from here, since we have no `Span`, so
// we should have instead a check on MIR for e.g. C variadic calls.
c_variadic: fn_abi.c_variadic,
fixed_count: fn_abi.fixed_count,
conv: fn_abi.conv,
can_unwind: fn_abi.can_unwind,
})
}
providers.fn_abi_of_fn_ptr = |tcx, key| {
let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_abi_of_fn_ptr)(tcx, key);
Ok(readjust_fn_abi(tcx, result?))
};
providers.fn_abi_of_instance = |tcx, key| {
let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_abi_of_instance)(tcx, key);
Ok(readjust_fn_abi(tcx, result?))
};
}

pub(crate) fn provide_extern(providers: &mut Providers) {
// Reset providers overriden in `provide`, that need to still go through the
// `rustc_metadata::rmeta` decoding, as opposed to being locally computed.
providers.fn_sig = rustc_interface::DEFAULT_EXTERN_QUERY_PROVIDERS.fn_sig;
}

/// If a struct contains a pointer to itself, even indirectly, then doing a naiive recursive walk
/// of the fields will result in an infinite loop. Because pointers are the only thing that are
/// allowed to be recursive, keep track of what pointers we've translated, or are currently in the
@@ -81,7 +152,9 @@ impl<'tcx> RecursivePointeeCache<'tcx> {
) -> Word {
match self.map.borrow_mut().entry(pointee) {
// We should have hit begin() on this type already, which always inserts an entry.
Entry::Vacant(_) => bug!("RecursivePointeeCache::end should always have entry"),
Entry::Vacant(_) => {
span_bug!(span, "RecursivePointeeCache::end should always have entry")
}
Entry::Occupied(mut entry) => match *entry.get() {
// State: There have been no recursive references to this type while defining it, and so no
// OpTypeForwardPointer has been emitted. This is the most common case.
@@ -103,7 +176,7 @@ impl<'tcx> RecursivePointeeCache<'tcx> {
.def_with_id(cx, span, id)
}
PointeeDefState::Defined(_) => {
bug!("RecursivePointeeCache::end defined pointer twice")
span_bug!(span, "RecursivePointeeCache::end defined pointer twice")
}
},
}
@@ -163,87 +236,6 @@ impl<'tcx> ConvSpirvType<'tcx> for PointeeTy<'tcx> {
}
}

impl<'tcx> ConvSpirvType<'tcx> for Reg {
fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word {
match self.kind {
RegKind::Integer => SpirvType::Integer(self.size.bits() as u32, false).def(span, cx),
RegKind::Float => SpirvType::Float(self.size.bits() as u32).def(span, cx),
RegKind::Vector => SpirvType::Vector {
element: SpirvType::Integer(8, false).def(span, cx),
count: self.size.bytes() as u32,
}
.def(span, cx),
}
}
}

impl<'tcx> ConvSpirvType<'tcx> for CastTarget {
fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word {
let rest_ll_unit = self.rest.unit.spirv_type(span, cx);
let (rest_count, rem_bytes) = if self.rest.unit.size.bytes() == 0 {
(0, 0)
} else {
(
self.rest.total.bytes() / self.rest.unit.size.bytes(),
self.rest.total.bytes() % self.rest.unit.size.bytes(),
)
};

if self.prefix.iter().all(|x| x.is_none()) {
// Simplify to a single unit when there is no prefix and size <= unit size
if self.rest.total <= self.rest.unit.size {
return rest_ll_unit;
}

// Simplify to array when all chunks are the same size and type
if rem_bytes == 0 {
return SpirvType::Array {
element: rest_ll_unit,
count: cx.constant_u32(span, rest_count as u32),
}
.def(span, cx);
}
}

// Create list of fields in the main structure
let mut args: Vec<_> = self
.prefix
.iter()
.flatten()
.map(|&kind| {
Reg {
kind,
size: self.prefix_chunk_size,
}
.spirv_type(span, cx)
})
.chain((0..rest_count).map(|_| rest_ll_unit))
.collect();

// Append final integer
if rem_bytes != 0 {
// Only integers can be really split further.
assert_eq!(self.rest.unit.kind, RegKind::Integer);
args.push(SpirvType::Integer(rem_bytes as u32 * 8, false).def(span, cx));
}

let size = Some(self.size(cx));
let align = self.align(cx);
let (field_offsets, computed_size, computed_align) = auto_struct_layout(cx, &args);
assert_eq!(size, computed_size, "{:#?}", self);
assert_eq!(align, computed_align, "{:#?}", self);
SpirvType::Adt {
def_id: None,
size,
align,
field_types: args,
field_offsets,
field_names: None,
}
.def(span, cx)
}
}

impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word {
let mut argument_types = Vec::new();
@@ -253,14 +245,11 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
PassMode::Direct(_) | PassMode::Pair(..) => {
self.ret.layout.spirv_type_immediate(span, cx)
}
PassMode::Cast(cast_target) => cast_target.spirv_type(span, cx),
PassMode::Indirect { .. } => {
let pointee = self.ret.layout.spirv_type(span, cx);
let pointer = SpirvType::Pointer { pointee }.def(span, cx);
// Important: the return pointer comes *first*, not last.
argument_types.push(pointer);
SpirvType::Void.def(span, cx)
}
PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!(
span,
"query hooks should've made this `PassMode` impossible: {:#?}",
self.ret
),
};

for arg in &self.args {
@@ -276,27 +265,11 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
));
continue;
}
PassMode::Cast(cast_target) => cast_target.spirv_type(span, cx),
PassMode::Indirect {
extra_attrs: Some(_),
..
} => {
let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty);
let ptr_layout = cx.layout_of(ptr_ty);
argument_types.push(scalar_pair_element_backend_type(
cx, span, ptr_layout, 0, true,
));
argument_types.push(scalar_pair_element_backend_type(
cx, span, ptr_layout, 1, true,
));
continue;
}
PassMode::Indirect {
extra_attrs: None, ..
} => {
let pointee = arg.layout.spirv_type(span, cx);
SpirvType::Pointer { pointee }.def(span, cx)
}
PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!(
span,
"query hooks should've made this `PassMode` impossible: {:#?}",
arg
),
};
argument_types.push(arg_type);
}
@@ -395,7 +368,11 @@ pub fn scalar_pair_element_backend_type<'tcx>(
) -> Word {
let [a, b] = match &ty.layout.abi {
Abi::ScalarPair(a, b) => [a, b],
other => bug!("scalar_pair_element_backend_type invalid abi: {:?}", other),
other => span_bug!(
span,
"scalar_pair_element_backend_type invalid abi: {:?}",
other
),
};
let offset = match index {
0 => Size::ZERO,
@@ -524,7 +501,8 @@ fn dig_scalar_pointee<'tcx>(

fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
match ty.fields {
FieldsShape::Primitive => bug!(
FieldsShape::Primitive => span_bug!(
span,
"trans_aggregate called for FieldsShape::Primitive layout {:#?}",
ty
),
@@ -629,7 +607,7 @@ fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -
} else {
if let TyKind::Adt(_, _) = ty.ty.kind() {
} else {
bug!("Variants::Multiple not TyKind::Adt");
span_bug!(span, "Variants::Multiple not TyKind::Adt");
}
if i == 0 {
field_names.push("discriminant".to_string());
25 changes: 8 additions & 17 deletions crates/rustc_codegen_spirv/src/builder/intrinsics.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ use rustc_middle::ty::{FnDef, Instance, ParamEnv, Ty, TyKind};
use rustc_span::source_map::Span;
use rustc_span::sym;
use rustc_target::abi::call::{FnAbi, PassMode};
use std::assert_matches::assert_matches;

fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_>) -> Option<(u64, bool)> {
match ty.kind() {
@@ -100,16 +101,9 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {

sym::volatile_load | sym::unaligned_volatile_load => {
let ptr = args[0].immediate();
if let PassMode::Cast(ty) = fn_abi.ret.mode {
let pointee = ty.spirv_type(self.span(), self);
let pointer = SpirvType::Pointer { pointee }.def(self.span(), self);
let ptr = self.pointercast(ptr, pointer);
self.volatile_load(pointee, ptr)
} else {
let layout = self.layout_of(substs.type_at(0));
let load = self.volatile_load(layout.spirv_type(self.span(), self), ptr);
self.to_immediate(load, layout)
}
let layout = self.layout_of(substs.type_at(0));
let load = self.volatile_load(layout.spirv_type(self.span(), self), ptr);
self.to_immediate(load, layout)
}

sym::prefetch_read_data
@@ -330,13 +324,10 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
};

if !fn_abi.ret.is_ignore() {
if let PassMode::Cast(_ty) = fn_abi.ret.mode {
self.fatal("TODO: PassMode::Cast not implemented yet in intrinsics");
} else {
OperandRef::from_immediate_or_packed_pair(self, value, result.layout)
.val
.store(self, result);
}
assert_matches!(fn_abi.ret.mode, PassMode::Direct(_) | PassMode::Pair(..));
OperandRef::from_immediate_or_packed_pair(self, value, result.layout)
.val
.store(self, result);
}
}

52 changes: 20 additions & 32 deletions crates/rustc_codegen_spirv/src/builder/mod.rs
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ use rustc_errors::DiagnosticBuilder;
use rustc_middle::mir::coverage::{
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op,
};
use rustc_middle::span_bug;
use rustc_middle::ty::layout::{
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers,
TyAndLayout,
@@ -286,27 +287,18 @@ impl<'a, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'tcx> {
val
}
match arg_abi.mode {
PassMode::Ignore => (),
PassMode::Ignore => {}
PassMode::Direct(_) => {
OperandValue::Immediate(next(self, idx)).store(self, dst);
}
PassMode::Pair(..) => {
OperandValue::Pair(next(self, idx), next(self, idx)).store(self, dst);
}
PassMode::Indirect {
extra_attrs: Some(_),
..
} => OperandValue::Ref(
next(self, idx),
Some(next(self, idx)),
arg_abi.layout.align.abi,
)
.store(self, dst),
PassMode::Direct(_)
| PassMode::Indirect {
extra_attrs: None, ..
}
| PassMode::Cast(_) => {
let next_arg = next(self, idx);
self.store_arg(arg_abi, next_arg, dst);
}
PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!(
self.span(),
"query hooks should've made this `PassMode` impossible: {:#?}",
arg_abi
),
}
}

@@ -316,20 +308,16 @@ impl<'a, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'tcx> {
val: Self::Value,
dst: PlaceRef<'tcx, Self::Value>,
) {
if arg_abi.is_ignore() {
return;
}
if arg_abi.is_sized_indirect() {
OperandValue::Ref(val, None, arg_abi.layout.align.abi).store(self, dst);
} else if arg_abi.is_unsized_indirect() {
self.fatal("unsized `ArgAbi` must be handled through `store_fn_arg`");
} else if let PassMode::Cast(cast) = arg_abi.mode {
let cast_ty = cast.spirv_type(self.span(), self);
let cast_ptr_ty = SpirvType::Pointer { pointee: cast_ty }.def(self.span(), self);
let cast_dst = self.pointercast(dst.llval, cast_ptr_ty);
self.store(val, cast_dst, arg_abi.layout.align.abi);
} else {
OperandValue::Immediate(val).store(self, dst);
match arg_abi.mode {
PassMode::Ignore => {}
PassMode::Direct(_) | PassMode::Pair(..) => {
OperandValue::Immediate(val).store(self, dst);
}
PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!(
self.span(),
"query hooks should've made this `PassMode` impossible: {:#?}",
arg_abi
),
}
}

91 changes: 49 additions & 42 deletions crates/rustc_codegen_spirv/src/codegen_cx/entry.rs
Original file line number Diff line number Diff line change
@@ -11,13 +11,12 @@ use rspirv::spirv::{
use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{Instance, Ty, TyKind};
use rustc_middle::ty::{self, Instance, Ty};
use rustc_span::Span;
use rustc_target::abi::{
call::{ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode},
Size,
};
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
use std::assert_matches::assert_matches;

impl<'tcx> CodegenCx<'tcx> {
// Entry points declare their "interface" (all uniforms, inputs, outputs, etc.) as parameters.
@@ -47,43 +46,53 @@ impl<'tcx> CodegenCx<'tcx> {
let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(fn_hir_id));
body.params
};
const EMPTY: ArgAttribute = ArgAttribute::empty();
for (abi, hir_param) in fn_abi.args.iter().zip(hir_params) {
match abi.mode {
PassMode::Direct(_)
| PassMode::Indirect { .. }
// plain DST/RTA/VLA
| PassMode::Pair(
ArgAttributes {
pointee_size: Size::ZERO,
..
},
ArgAttributes { regular: EMPTY, .. },
)
// DST struct with fields before the DST member
| PassMode::Pair(
ArgAttributes { .. },
ArgAttributes {
pointee_size: Size::ZERO,
..
},
) => {}
_ => self.tcx.sess.span_err(
for (arg_abi, hir_param) in fn_abi.args.iter().zip(hir_params) {
match arg_abi.mode {
PassMode::Direct(_) => {}
PassMode::Pair(..) => {
// FIXME(eddyb) implement `ScalarPair` `Input`s, or change
// the `FnAbi` readjustment to only use `PassMode::Pair` for
// pointers to `!Sized` types, but not other `ScalarPair`s.
if !matches!(arg_abi.layout.ty.kind(), ty::Ref(..)) {
self.tcx.sess.span_err(
hir_param.ty_span,
&format!(
"entry point parameter type not yet supported \
(`{}` has `ScalarPair` ABI but is not a `&T`)",
arg_abi.layout.ty
),
);
}
}
// FIXME(eddyb) support these (by just ignoring them) - if there
// is any validation concern, it should be done on the types.
PassMode::Ignore => self.tcx.sess.span_err(
hir_param.ty_span,
&format!(
"entry point parameter type not yet supported \
(`{}` has size `0`)",
arg_abi.layout.ty
),
),
_ => span_bug!(
hir_param.ty_span,
&format!("PassMode {:?} invalid for entry point parameter", abi.mode),
"query hooks should've made this `PassMode` impossible: {:#?}",
arg_abi
),
}
}
if let PassMode::Ignore = fn_abi.ret.mode {
if fn_abi.ret.layout.ty.is_unit() {
assert_matches!(fn_abi.ret.mode, PassMode::Ignore);
} else {
self.tcx.sess.span_err(
span,
&format!(
"PassMode {:?} invalid for entry point return type",
fn_abi.ret.mode
"entry point should return `()`, not `{}`",
fn_abi.ret.layout.ty
),
);
}

// let execution_model = entry.execution_model;
let fn_id = self.shader_entry_stub(
span,
@@ -168,7 +177,7 @@ impl<'tcx> CodegenCx<'tcx> {
// FIXME(eddyb) also check the type for compatibility with being
// part of the interface, including potentially `Sync`ness etc.
let (value_ty, mutbl, is_ref) = match *layout.ty.kind() {
TyKind::Ref(_, pointee_ty, mutbl) => (pointee_ty, mutbl, true),
ty::Ref(_, pointee_ty, mutbl) => (pointee_ty, mutbl, true),
_ => (layout.ty, hir::Mutability::Not, false),
};
let spirv_ty = self.layout_of(value_ty).spirv_type(hir_param.ty_span, self);
@@ -396,7 +405,7 @@ impl<'tcx> CodegenCx<'tcx> {
// Compute call argument(s) to match what the Rust entry `fn` expects,
// starting from the `value_ptr` pointing to a `value_spirv_type`
// (e.g. `Input` doesn't use indirection, so we have to load from it).
if let TyKind::Ref(..) = entry_arg_abi.layout.ty.kind() {
if let ty::Ref(..) = entry_arg_abi.layout.ty.kind() {
call_args.push(value_ptr);
match entry_arg_abi.mode {
PassMode::Direct(_) => assert_eq!(value_len, None),
@@ -405,16 +414,14 @@ impl<'tcx> CodegenCx<'tcx> {
}
} else {
assert_eq!(storage_class, StorageClass::Input);
assert_matches!(entry_arg_abi.mode, PassMode::Direct(_));

call_args.push(match entry_arg_abi.mode {
PassMode::Indirect { .. } => value_ptr,
PassMode::Direct(_) => bx.load(
entry_arg_abi.layout.spirv_type(hir_param.ty_span, bx),
value_ptr,
entry_arg_abi.layout.align.abi,
),
_ => unreachable!(),
});
let value = bx.load(
entry_arg_abi.layout.spirv_type(hir_param.ty_span, bx),
value_ptr,
entry_arg_abi.layout.align.abi,
);
call_args.push(value);
assert_eq!(value_len, None);
}

10 changes: 8 additions & 2 deletions crates/rustc_codegen_spirv/src/codegen_cx/type_.rs
Original file line number Diff line number Diff line change
@@ -72,7 +72,10 @@ impl<'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx> {
}

fn cast_backend_type(&self, ty: &CastTarget) -> Self::Type {
ty.spirv_type(DUMMY_SP, self)
bug!(
"cast_backend_type({:?}): query hooks should've made `PassMode::Cast` impossible",
ty
)
}

fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type {
@@ -84,7 +87,10 @@ impl<'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx> {
}

fn reg_backend_type(&self, ty: &Reg) -> Self::Type {
ty.spirv_type(DUMMY_SP, self)
bug!(
"reg_backend_type({:?}): query hooks should've made `PassMode::Cast` impossible",
ty
)
}

fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type {
35 changes: 2 additions & 33 deletions crates/rustc_codegen_spirv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -176,7 +176,6 @@ use rustc_middle::ty::{self, query, DefIdTree, Instance, InstanceDef, TyCtxt};
use rustc_session::config::{self, OptLevel, OutputFilenames, OutputType};
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
use rustc_target::spec::{Target, TargetTriple};
use std::any::Any;
use std::env;
@@ -286,42 +285,12 @@ impl CodegenBackend for SpirvCodegenBackend {
}

fn provide(&self, providers: &mut query::Providers) {
// This is a lil weird: so, we obviously don't support C ABIs at all. However, libcore does declare some extern
// C functions:
// https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/slice/cmp.rs#L119
// However, those functions will be implemented by compiler-builtins:
// https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/library/core/src/lib.rs#L23-L27
// This theoretically then should be fine to leave as C, but, there's no backend hook for
// FnAbi::adjust_for_cabi, causing it to panic:
// https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/compiler/rustc_target/src/abi/call/mod.rs#L603
// So, treat any extern "C" functions as actually being Rust ABI, to be able to compile libcore with arch=spirv.
providers.fn_sig = |tcx, def_id| {
// We can't capture the old fn_sig and just call that, because fn_sig is a `fn`, not a `Fn`, i.e. it can't
// capture variables. Fortunately, the defaults are exposed (thanks rustdoc), so use that instead.
let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_sig)(tcx, def_id);
result.map_bound(|mut inner| {
if let Abi::C { .. } = inner.abi {
inner.abi = Abi::Rust;
}
inner
})
};

// Extra hooks provided by other parts of `rustc_codegen_spirv`.
crate::abi::provide(providers);
crate::attr::provide(providers);
}

fn provide_extern(&self, providers: &mut query::Providers) {
// See comments in provide(), only this time we use the default *extern* provider.
providers.fn_sig = |tcx, def_id| {
let result = (rustc_interface::DEFAULT_EXTERN_QUERY_PROVIDERS.fn_sig)(tcx, def_id);
result.map_bound(|mut inner| {
if let Abi::C { .. } = inner.abi {
inner.abi = Abi::Rust;
}
inner
})
};
crate::abi::provide_extern(providers);
}

fn codegen_crate(
16 changes: 16 additions & 0 deletions tests/ui/dis/entry-pass-mode-cast-array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// This is a similar setup to the `issue-731` test, but instead of "just" the
// missing copy out of the global (`Input`) `OpVariable`, small enough types
// would fail much earlier (by generating unsupported pointer casts).
// (Just like `issue-373`, the problem was the use of `PassMode::Cast`, through
// the default Rust ABI adjustments, that we now override through query hooks)

// build-pass
// compile-flags: -C llvm-args=--disassemble-entry=main

use spirv_std as _;

#[spirv(fragment)]
pub fn main(mut in_array: [f32; 2], out_array: &mut [f32; 2]) {
in_array[0] += 1.0;
*out_array = in_array;
}
21 changes: 21 additions & 0 deletions tests/ui/dis/entry-pass-mode-cast-array.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
%1 = OpFunction %2 None %3
%4 = OpLabel
%5 = OpVariable %6 Function
%7 = OpVariable %6 Function
OpLine %8 13 12
%9 = OpLoad %10 %11
OpLine %8 13 0
OpStore %5 %9
OpLine %8 14 4
%12 = OpInBoundsAccessChain %13 %5 %14
%15 = OpInBoundsAccessChain %13 %5 %14
%16 = OpLoad %17 %15
%18 = OpFAdd %17 %16 %19
OpStore %12 %18
OpLine %8 15 17
OpCopyMemory %7 %5
OpLine %8 15 4
OpCopyMemory %20 %7
OpLine %8 16 1
OpReturn
OpFunctionEnd
23 changes: 23 additions & 0 deletions tests/ui/dis/issue-373.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Test that returning a single-scalar-field `#[repr(C)] struct` doesn't generate
// unsupported pointer casts (the problem was the use of `PassMode::Cast`, through
// the default Rust ABI adjustments, that we now override through query hooks).

// build-pass
// compile-flags: -C llvm-args=--disassemble-entry=main

use spirv_std as _;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct S {
x: f32,
}

fn f() -> S {
S { x: 2.0 }
}

#[spirv(fragment)]
pub fn main(out: &mut f32) {
*out = f().x;
}
11 changes: 11 additions & 0 deletions tests/ui/dis/issue-373.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
%1 = OpFunction %2 None %3
%4 = OpLabel
OpLine %5 22 11
%6 = OpFunctionCall %7 %8
OpLine %5 22 11
%9 = OpCompositeExtract %10 %6 0
OpLine %5 22 4
OpStore %11 %9
OpLine %5 23 1
OpReturn
OpFunctionEnd
17 changes: 0 additions & 17 deletions tests/ui/dis/issue-723-indirect-input.rs

This file was deleted.

22 changes: 0 additions & 22 deletions tests/ui/dis/issue-723-indirect-input.stderr

This file was deleted.

14 changes: 14 additions & 0 deletions tests/ui/dis/issue-731.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Test that non-immediate (i.e. not one of scalar/scalar-pair/vector) inputs
// get properly copied out of the global (`Input`) `OpVariable` and mutation is
// only ever done on `fn`-local `OpVariable`s, not on the original global.

// build-pass
// compile-flags: -C llvm-args=--disassemble-entry=main

use spirv_std as _;

#[spirv(fragment)]
pub fn main(mut in_array: [f32; 3], out_array: &mut [f32; 3]) {
in_array[0] += 1.0;
*out_array = in_array;
}
21 changes: 21 additions & 0 deletions tests/ui/dis/issue-731.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
%1 = OpFunction %2 None %3
%4 = OpLabel
%5 = OpVariable %6 Function
%7 = OpVariable %6 Function
OpLine %8 11 12
%9 = OpLoad %10 %11
OpLine %8 11 0
OpStore %5 %9
OpLine %8 12 4
%12 = OpInBoundsAccessChain %13 %5 %14
%15 = OpInBoundsAccessChain %13 %5 %14
%16 = OpLoad %17 %15
%18 = OpFAdd %17 %16 %19
OpStore %12 %18
OpLine %8 13 17
OpCopyMemory %7 %5
OpLine %8 13 4
OpCopyMemory %20 %7
OpLine %8 14 1
OpReturn
OpFunctionEnd
30 changes: 30 additions & 0 deletions tests/ui/dis/pass-mode-cast-struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Test that a small enough `struct` doesn't generate unsupported pointer casts.
// (Just like `issue-373`, the problem was the use of `PassMode::Cast`, through
// the default Rust ABI adjustments, that we now override through query hooks)

// build-pass
// compile-flags: -C llvm-args=--disassemble-entry=main

use spirv_std as _;

struct Foo {
a: u32,
b: u8,
c: u8,
}

impl Foo {
fn unpack(data: u64) -> Self {
Self {
a: (data >> 16 & 0xffffff) as u32,
b: (data & 0xff >> 8) as u8,
c: (data & 0xff) as u8,
}
}
}

#[spirv(fragment)]
pub fn main(in_packed: u64, out_sum: &mut u32) {
let foo = Foo::unpack(in_packed);
*out_sum = foo.a + (foo.b + foo.c) as u32;
}
22 changes: 22 additions & 0 deletions tests/ui/dis/pass-mode-cast-struct.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
%1 = OpFunction %2 None %3
%4 = OpLabel
OpLine %5 27 12
%6 = OpLoad %7 %8
OpLine %5 28 14
%9 = OpFunctionCall %10 %11 %6
OpLine %5 29 15
%12 = OpCompositeExtract %13 %9 0
OpLine %5 29 24
%14 = OpCompositeExtract %15 %9 1
OpLine %5 29 32
%16 = OpCompositeExtract %15 %9 2
OpLine %5 29 23
%17 = OpIAdd %15 %14 %16
OpLine %5 29 23
%18 = OpUConvert %13 %17
OpLine %5 29 4
%19 = OpIAdd %13 %12 %18
OpStore %20 %19
OpLine %5 30 1
OpReturn
OpFunctionEnd
4 changes: 2 additions & 2 deletions tests/ui/lang/consts/nested-ref-in-composite.stderr
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ error: constant arrays/structs cannot contain pointers to other constants
|
= note: Stack:
nested_ref_in_composite::main_pair
Unnamed function ID %26
Unnamed function ID %24

error: constant arrays/structs cannot contain pointers to other constants
--> $DIR/nested-ref-in-composite.rs:27:19
@@ -16,7 +16,7 @@ error: constant arrays/structs cannot contain pointers to other constants
|
= note: Stack:
nested_ref_in_composite::main_array3
Unnamed function ID %34
Unnamed function ID %33

error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used.
|