Skip to content

Commit 7348f33

Browse files
authored
builder: constant-fold bx.mul(a, b) in order to support ptr::{read,write}. (#485)
1 parent 556e12f commit 7348f33

File tree

3 files changed

+177
-5
lines changed

3 files changed

+177
-5
lines changed

crates/rustc_codegen_spirv/src/builder/builder_methods.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::Builder;
2-
use crate::builder_spirv::{BuilderCursor, SpirvValue, SpirvValueExt, SpirvValueKind};
2+
use crate::builder_spirv::{BuilderCursor, SpirvConst, SpirvValue, SpirvValueExt, SpirvValueKind};
33
use crate::spirv_type::SpirvType;
44
use rspirv::dr::{InsertPoint, Instruction, Operand};
55
use rspirv::spirv::{Capability, MemoryModel, MemorySemantics, Op, Scope, StorageClass, Word};
@@ -21,10 +21,44 @@ use std::iter::empty;
2121
use std::ops::Range;
2222

2323
macro_rules! simple_op {
24-
($func_name:ident, $inst_name:ident) => {
24+
(
25+
$func_name:ident, $inst_name:ident
26+
$(, fold_const {
27+
$(int($fold_int_lhs:ident, $fold_int_rhs:ident) => $fold_int:expr)?
28+
})?
29+
) => {
2530
fn $func_name(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
2631
assert_ty_eq!(self, lhs.ty, rhs.ty);
2732
let result_type = lhs.ty;
33+
34+
$(if let Some(const_lhs) = self.builder.lookup_const(lhs) {
35+
if let Some(const_rhs) = self.builder.lookup_const(rhs) {
36+
match self.lookup_type(result_type) {
37+
$(SpirvType::Integer(bits, signed) => {
38+
let size = Size::from_bits(bits);
39+
let as_u128 = |const_val| {
40+
let x = match const_val {
41+
SpirvConst::U32(_, x) => x as u128,
42+
SpirvConst::U64(_, x) => x as u128,
43+
_ => return None,
44+
};
45+
Some(if signed {
46+
size.sign_extend(x)
47+
} else {
48+
size.truncate(x)
49+
})
50+
};
51+
if let Some($fold_int_lhs) = as_u128(const_lhs) {
52+
if let Some($fold_int_rhs) = as_u128(const_rhs) {
53+
return self.const_uint_big(result_type, $fold_int);
54+
}
55+
}
56+
})?
57+
_ => {}
58+
}
59+
}
60+
})?
61+
2862
self.emit()
2963
.$inst_name(result_type, None, lhs.def(self), rhs.def(self))
3064
.unwrap()
@@ -600,7 +634,14 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
600634
simple_op! {sub, i_sub}
601635
simple_op! {fsub, f_sub}
602636
simple_op! {fsub_fast, f_sub} // fast=normal
603-
simple_op! {mul, i_mul}
637+
simple_op! {
638+
mul, i_mul,
639+
// HACK(eddyb) `rustc_codegen_ssa` relies on `Builder` methods doing
640+
// on-the-fly constant-folding, for e.g. intrinsics that copy memory.
641+
fold_const {
642+
int(a, b) => a * b
643+
}
644+
}
604645
simple_op! {fmul, f_mul}
605646
simple_op! {fmul_fast, f_mul} // fast=normal
606647
simple_op! {udiv, u_div}

crates/spirv-builder/src/test/basic.rs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,6 @@ pub fn main() {
440440
}
441441

442442
#[test]
443-
#[ignore]
444443
fn create_uninitialized_memory() {
445444
val(r#"
446445
use core::mem::MaybeUninit;
@@ -598,3 +597,129 @@ pub fn main(image: UniformConstant<Image2d>, mut output: Output<glam::Vec4>) {
598597
}
599598
"#);
600599
}
600+
601+
/// Helper to generate all of the `ptr_*` tests below, which test that the various
602+
/// ways to use raw pointer `read`/`write`/`copy`, to copy a single value, work,
603+
/// and that the resulting SPIR-V uses either a pair of `OpLoad` and `OpStore`,
604+
/// and/or the `OpCopyMemory` instruction, but *not* `OpCopyMemorySized`.
605+
macro_rules! test_copy_via_raw_ptr {
606+
($copy_expr:literal => $spirv:literal) => {
607+
dis_fn(
608+
concat!(
609+
r#"
610+
fn copy_via_raw_ptr(src: &f32, dst: &mut f32) {
611+
unsafe {
612+
"#,
613+
$copy_expr,
614+
r#"
615+
}
616+
}
617+
#[spirv(fragment)]
618+
pub fn main(i: Input<f32>, mut o: Output<f32>) {
619+
copy_via_raw_ptr(&i, &mut o);
620+
// FIXME(eddyb) above call results in inlining `copy_via_raw_ptr`,
621+
// due to the to `Input`/`Output` storage classes, so to get the
622+
// disassembled function we also need `Function`-local pointers:
623+
let (src, mut dst) = (0.0, 0.0);
624+
copy_via_raw_ptr(&src, &mut dst);
625+
}
626+
"#
627+
),
628+
"copy_via_raw_ptr",
629+
concat!(
630+
r#"%1 = OpFunction %2 None %3
631+
%4 = OpFunctionParameter %5
632+
%6 = OpFunctionParameter %5
633+
%7 = OpLabel"#,
634+
$spirv,
635+
r#"OpReturn
636+
OpFunctionEnd"#
637+
),
638+
);
639+
};
640+
}
641+
642+
#[test]
643+
fn ptr_read() {
644+
test_copy_via_raw_ptr!(
645+
"*dst = core::ptr::read(src)"=> r#"
646+
%8 = OpVariable %5 Function
647+
OpStore %8 %9
648+
OpCopyMemory %8 %4
649+
%10 = OpLoad %11 %8
650+
OpStore %6 %10
651+
"#
652+
);
653+
}
654+
655+
#[test]
656+
fn ptr_read_method() {
657+
test_copy_via_raw_ptr!(
658+
"*dst = (src as *const f32).read()" => r#"
659+
%8 = OpVariable %5 Function
660+
OpStore %8 %9
661+
OpCopyMemory %8 %4
662+
%10 = OpLoad %11 %8
663+
OpStore %6 %10
664+
"#
665+
);
666+
}
667+
668+
#[test]
669+
fn ptr_write() {
670+
test_copy_via_raw_ptr!(
671+
"core::ptr::write(dst, *src)" => r#"
672+
%8 = OpVariable %5 Function
673+
%9 = OpLoad %10 %4
674+
OpStore %8 %9
675+
OpCopyMemory %6 %8
676+
"#
677+
);
678+
}
679+
680+
#[test]
681+
fn ptr_write_method() {
682+
test_copy_via_raw_ptr!(
683+
"(dst as *mut f32).write(*src)" => r#"
684+
%8 = OpVariable %5 Function
685+
%9 = OpLoad %10 %4
686+
OpStore %8 %9
687+
OpCopyMemory %6 %8
688+
"#
689+
);
690+
}
691+
692+
#[test]
693+
fn ptr_copy() {
694+
test_copy_via_raw_ptr!(
695+
"core::ptr::copy(src, dst, 1)" => r#"
696+
OpCopyMemory %6 %4
697+
"#
698+
);
699+
}
700+
701+
#[test]
702+
// FIXME(eddyb) doesn't work because `<*const T>::copy_to` is a method that wraps
703+
// the actual `core::ptr::copy` intrinsic - this requires either MIR inlining, or
704+
// making the methods themselves intrinsic (via attributes instead of pseudo-ABI).
705+
#[ignore]
706+
fn ptr_copy_to_method() {
707+
test_copy_via_raw_ptr!(
708+
"(src as *const f32).copy_to(dst, 1)" => r#"
709+
OpCopyMemory %6 %4
710+
"#
711+
);
712+
}
713+
714+
#[test]
715+
// FIXME(eddyb) doesn't work because `<*mut T>::copy_from` is a method that wraps
716+
// the actual `core::ptr::copy` intrinsic - this requires either MIR inlining, or
717+
// making the methods themselves intrinsic (via attributes instead of pseudo-ABI).
718+
#[ignore]
719+
fn ptr_copy_from_method() {
720+
test_copy_via_raw_ptr!(
721+
"(dst as *mut f32).copy_from(src, 1)" => r#"
722+
OpCopyMemory %6 %4
723+
"#
724+
);
725+
}

crates/spirv-builder/src/test/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,13 @@ fn dis_fn(src: &str, func: &str, expect: &str) {
136136
inst.class.opcode == rspirv::spirv::Op::Name
137137
&& inst.operands[1].unwrap_literal_string() == abs_func_path
138138
})
139-
.expect("No function with that name found")
139+
.unwrap_or_else(|| {
140+
panic!(
141+
"no function with the name `{}` found in:\n{}\n",
142+
abs_func_path,
143+
module.disassemble()
144+
)
145+
})
140146
.operands[0]
141147
.unwrap_id_ref();
142148
let mut func = module

0 commit comments

Comments
 (0)