Skip to content

Commit cf59e54

Browse files
committed
Respect read_scalar errors in read_from_const_alloc.
1 parent b9d7877 commit cf59e54

File tree

1 file changed

+75
-28
lines changed

1 file changed

+75
-28
lines changed

crates/rustc_codegen_spirv/src/codegen_cx/constant.rs

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use itertools::Itertools as _;
99
use rspirv::spirv::Word;
1010
use rustc_abi::{self as abi, AddressSpace, Float, HasDataLayout, Integer, Primitive, Size};
1111
use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods};
12-
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar, alloc_range};
12+
use rustc_middle::mir::interpret::{AllocError, ConstAllocation, GlobalAlloc, Scalar, alloc_range};
1313
use rustc_middle::ty::layout::LayoutOf;
1414
use rustc_span::{DUMMY_SP, Span};
1515

@@ -298,24 +298,7 @@ impl ConstCodegenMethods for CodegenCx<'_> {
298298
(self.get_static(def_id), AddressSpace::DATA)
299299
}
300300
};
301-
let value = if offset.bytes() == 0 {
302-
base_addr
303-
} else {
304-
self.tcx
305-
.dcx()
306-
.fatal("Non-zero scalar_to_backend ptr.offset not supported")
307-
// let offset = self.constant_bit64(ptr.offset.bytes());
308-
// self.gep(base_addr, once(offset))
309-
};
310-
if let Primitive::Pointer(_) = layout.primitive() {
311-
assert_ty_eq!(self, value.ty, ty);
312-
value
313-
} else {
314-
self.tcx
315-
.dcx()
316-
.fatal("Non-pointer-typed scalar_to_backend Scalar::Ptr not supported");
317-
// unsafe { llvm::LLVMConstPtrToInt(llval, llty) }
318-
}
301+
self.const_bitcast(self.const_ptr_byte_offset(base_addr, offset), ty)
319302
}
320303
}
321304
}
@@ -448,18 +431,82 @@ impl<'tcx> CodegenCx<'tcx> {
448431
SpirvType::Pointer { .. } => Primitive::Pointer(AddressSpace::DATA),
449432
_ => unreachable!(),
450433
};
451-
let value = match alloc.inner().read_scalar(
452-
self,
453-
alloc_range(offset, size),
454-
matches!(primitive, Primitive::Pointer(_)),
455-
) {
434+
435+
let range = alloc_range(offset, size);
436+
let read_provenance = matches!(primitive, Primitive::Pointer(_));
437+
438+
let mut primitive = primitive;
439+
let mut read_result = alloc.inner().read_scalar(self, range, read_provenance);
440+
441+
// HACK(eddyb) while reading a pointer as an integer will fail,
442+
// the pointer itself can be read as a pointer, and then passed
443+
// to `scalar_to_backend`, which will `const_bitcast` it to `ty`.
444+
if read_result.is_err()
445+
&& !read_provenance
446+
&& let read_ptr_result @ Ok(Scalar::Ptr(ptr, _)) = alloc
447+
.inner()
448+
.read_scalar(self, range, /* read_provenance */ true)
449+
{
450+
let (prov, _offset) = ptr.into_parts();
451+
primitive = Primitive::Pointer(
452+
self.tcx.global_alloc(prov.alloc_id()).address_space(self),
453+
);
454+
read_result = read_ptr_result;
455+
}
456+
457+
let scalar_or_zombie = match read_result {
456458
Ok(scalar) => {
457-
self.scalar_to_backend(scalar, self.primitive_to_scalar(primitive), ty)
459+
Ok(self.scalar_to_backend(scalar, self.primitive_to_scalar(primitive), ty))
458460
}
459-
// FIXME(eddyb) this is really unsound, could be an error!
460-
_ => self.undef(ty),
461+
462+
// FIXME(eddyb) could some of these use e.g. `const_bitcast`?
463+
// (or, in general, assembling one constant out of several)
464+
Err(err) => match err {
465+
// The scalar is only `undef` if the entire byte range
466+
// it covers is completely uninitialized - all other
467+
// failure modes of `read_scalar` are various errors.
468+
AllocError::InvalidUninitBytes(_) => {
469+
let uninit_range = alloc
470+
.inner()
471+
.init_mask()
472+
.is_range_initialized(range)
473+
.unwrap_err();
474+
let uninit_size = {
475+
let [start, end] = [uninit_range.start, uninit_range.end()]
476+
.map(|x| x.clamp(range.start, range.end()));
477+
end - start
478+
};
479+
if uninit_size == size {
480+
Ok(self.undef(ty))
481+
} else {
482+
Err(format!(
483+
"overlaps {} uninitialized bytes",
484+
uninit_size.bytes()
485+
))
486+
}
487+
}
488+
AllocError::ReadPointerAsInt(_) => Err("overlaps pointer bytes".into()),
489+
AllocError::ReadPartialPointer(_) => {
490+
Err("partially overlaps another pointer".into())
491+
}
492+
493+
// HACK(eddyb) these should never happen when using
494+
// `read_scalar`, but better not outright crash.
495+
AllocError::ScalarSizeMismatch(_)
496+
| AllocError::OverwritePartialPointer(_) => {
497+
Err(format!("unrecognized `AllocError::{err:?}`"))
498+
}
499+
},
461500
};
462-
(value, size)
501+
let result = scalar_or_zombie.unwrap_or_else(|reason| {
502+
let result = self.undef(ty);
503+
self.zombie_no_span(
504+
result.def_cx(self),
505+
&format!("unsupported `{}` constant: {reason}", self.debug_type(ty),),
506+
);
507+
result
508+
});
509+
(result, size)
463510
}
464511
SpirvType::Adt {
465512
field_types,

0 commit comments

Comments
 (0)