Skip to content
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

Prepare Rust for opaque pointers #94214

Merged
merged 5 commits into from
May 29, 2022
Merged
Show file tree
Hide file tree
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
9 changes: 7 additions & 2 deletions compiler/rustc_codegen_llvm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,15 +509,20 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
OperandValue::Ref(place.llval, Some(llextra), place.align)
} else if place.layout.is_llvm_immediate() {
let mut const_llval = None;
let llty = place.layout.llvm_type(self);
unsafe {
if let Some(global) = llvm::LLVMIsAGlobalVariable(place.llval) {
if llvm::LLVMIsGlobalConstant(global) == llvm::True {
const_llval = llvm::LLVMGetInitializer(global);
if let Some(init) = llvm::LLVMGetInitializer(global) {
if self.val_ty(init) == llty {
const_llval = Some(init);
}
}
}
}
}
let llval = const_llval.unwrap_or_else(|| {
let load = self.load(place.layout.llvm_type(self), place.llval, place.align);
let load = self.load(llty, place.llval, place.align);
if let abi::Abi::Scalar(scalar) = place.layout.abi {
scalar_load_metadata(self, load, scalar, place.layout, Size::ZERO);
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ impl CodegenBackend for LlvmCodegenBackend {
local stack variable in the ABI.)

basic
Generate stack canaries in functions with:
- local variables of `[T; N]` type, where `T` is byte-sized and `N` > 8.
Generate stack canaries in functions with local variables of `[T; N]`
type, where `T` is byte-sized and `N` >= 8.

none
Do not generate stack canaries.
Expand Down
4 changes: 4 additions & 0 deletions src/bootstrap/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,10 @@ impl Step for Lld {
.define("LLVM_CONFIG_PATH", llvm_config_shim)
.define("LLVM_INCLUDE_TESTS", "OFF");

if builder.config.llvm_allow_old_toolchain {
cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES");
}

// While we're using this horrible workaround to shim the execution of
// llvm-config, let's just pile on more. I can't seem to figure out how
// to build LLD as a standalone project and also cross-compile it at the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub fn array_u8_1(f: fn(*const u8)) {
#[no_mangle]
pub fn array_u8_small(f: fn(*const u8)) {
let a = [0u8; 2];
let b = [0u8; 8];
let b = [0u8; 7];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change somehow related to opaque pointers? Or is it something else incidental on llvm main? I don't see a problem with the change, but it seems curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intended to test a heuristic that does not place a stack protector for arrays (strictly) smaller than 8, so it should be using size 7. The test still worked previously, because the [8 x i8] alloca was converted into an i64 alloca, and did not receive the stack protector for that reason. This doesn't happen anymore with opaque pointers. The way these heuristics are currently implemented (inspecting alloca types in the backend) is unfortunately not stable under optimization (probably it should be a frontend responsibility to add metadata to allocas that should receive a protector, or something like that).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation says that it is for any N > 8, not any N >= 8:

- local variables of `[T; N]` type, where `T` is byte-sized and `N` > 8.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the documentation is wrong as well. I'll fix it. For reference, the actual logic is here: https://github.com/llvm/llvm-project/blob/65af17c7a00df3f8906cded34b63b309e687cf61/llvm/lib/CodeGen/StackProtector.cpp#L134

f(&a as *const _);
f(&b as *const _);

Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/abi-main-signature-32bit-c-int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
fn main() {
}

// CHECK: define i32 @main(i32{{( %0)?}}, i8**{{( %1)?}})
// CHECK: define i32 @main(i32{{( %0)?}}, {{i8\*\*|ptr}}{{( %1)?}})
6 changes: 3 additions & 3 deletions src/test/codegen/adjustments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ pub fn helper(_: usize) {
pub fn no_op_slice_adjustment(x: &[u8]) -> &[u8] {
// We used to generate an extra alloca and memcpy for the block's trailing expression value, so
// check that we copy directly to the return value slot
// CHECK: %0 = insertvalue { [0 x i8]*, [[USIZE]] } undef, [0 x i8]* %x.0, 0
// CHECK: %1 = insertvalue { [0 x i8]*, [[USIZE]] } %0, [[USIZE]] %x.1, 1
// CHECK: ret { [0 x i8]*, [[USIZE]] } %1
// CHECK: %0 = insertvalue { {{\[0 x i8\]\*|ptr}}, [[USIZE]] } undef, {{\[0 x i8\]\*|ptr}} %x.0, 0
// CHECK: %1 = insertvalue { {{\[0 x i8\]\*|ptr}}, [[USIZE]] } %0, [[USIZE]] %x.1, 1
// CHECK: ret { {{\[0 x i8\]\*|ptr}}, [[USIZE]] } %1
{ x }
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/align-enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct Nested64 {
#[no_mangle]
pub fn align64(a: u32) -> Align64 {
// CHECK: %a64 = alloca %Align64, align 64
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 64 %{{.*}}, i8* align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false)
// CHECK: call void @llvm.memcpy.{{.*}}({{i8\*|ptr}} align 64 %{{.*}}, {{i8\*|ptr}} align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false)
let a64 = Align64::A(a);
a64
}
Expand Down
5 changes: 2 additions & 3 deletions src/test/codegen/align-struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub enum Enum64 {
#[no_mangle]
pub fn align64(i : i32) -> Align64 {
// CHECK: %a64 = alloca %Align64, align 64
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 64 %{{.*}}, i8* align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false)
// CHECK: call void @llvm.memcpy.{{.*}}({{i8\*|ptr}} align 64 %{{.*}}, {{i8\*|ptr}} align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false)
let a64 = Align64(i);
a64
}
Expand All @@ -42,8 +42,7 @@ pub fn align64(i : i32) -> Align64 {
// CHECK-LABEL: @align64_load
#[no_mangle]
pub fn align64_load(a: Align64) -> i32 {
// CHECK: [[FIELD:%.*]] = bitcast %Align64* %{{.*}} to i32*
// CHECK: {{%.*}} = load i32, i32* [[FIELD]], align 64
// CHECK: {{%.*}} = load i32, {{i32\*|ptr}} {{%.*}}, align 64
a.0
}

Expand Down
19 changes: 7 additions & 12 deletions src/test/codegen/array-equality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ pub fn array_eq_value(a: [u16; 3], b: [u16; 3]) -> bool {
#[no_mangle]
pub fn array_eq_ref(a: &[u16; 3], b: &[u16; 3]) -> bool {
// CHECK: start:
// CHECK: load i48, i48* %{{.+}}, align 2
// CHECK: load i48, i48* %{{.+}}, align 2
// CHECK: load i48, {{i48\*|ptr}} %{{.+}}, align 2
// CHECK: load i48, {{i48\*|ptr}} %{{.+}}, align 2
// CHECK: icmp eq i48
// CHECK-NEXT: ret
a == b
Expand All @@ -27,9 +27,7 @@ pub fn array_eq_ref(a: &[u16; 3], b: &[u16; 3]) -> bool {
#[no_mangle]
pub fn array_eq_value_still_passed_by_pointer(a: [u16; 9], b: [u16; 9]) -> bool {
// CHECK-NEXT: start:
// CHECK-NEXT: bitcast
// CHECK-NEXT: bitcast
// CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(18) %{{.+}}, i8* {{.*}} dereferenceable(18) %{{.+}}, i64 18)
// CHECK: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}({{i8\*|ptr}} {{.*}} dereferenceable(18) %{{.+}}, {{i8\*|ptr}} {{.*}} dereferenceable(18) %{{.+}}, i64 18)
// CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0
// CHECK-NEXT: ret i1 %[[EQ]]
a == b
Expand All @@ -39,9 +37,7 @@ pub fn array_eq_value_still_passed_by_pointer(a: [u16; 9], b: [u16; 9]) -> bool
#[no_mangle]
pub fn array_eq_long(a: &[u16; 1234], b: &[u16; 1234]) -> bool {
// CHECK-NEXT: start:
// CHECK-NEXT: bitcast
// CHECK-NEXT: bitcast
// CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(2468) %{{.+}}, i8* {{.*}} dereferenceable(2468) %{{.+}}, i64 2468)
// CHECK: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}({{i8\*|ptr}} {{.*}} dereferenceable(2468) %{{.+}}, {{i8\*|ptr}} {{.*}} dereferenceable(2468) %{{.+}}, i64 2468)
// CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0
// CHECK-NEXT: ret i1 %[[EQ]]
a == b
Expand All @@ -56,18 +52,17 @@ pub fn array_eq_zero_short(x: [u16; 3]) -> bool {
x == [0; 3]
}

// CHECK-LABEL: @array_eq_zero_mid([8 x i16]*
// CHECK-LABEL: @array_eq_zero_mid(
#[no_mangle]
pub fn array_eq_zero_mid(x: [u16; 8]) -> bool {
// CHECK-NEXT: start:
// CHECK-NEXT: bitcast
// CHECK-NEXT: %[[LOAD:.+]] = load i128,
// CHECK: %[[LOAD:.+]] = load i128,
// CHECK-NEXT: %[[EQ:.+]] = icmp eq i128 %[[LOAD]], 0
// CHECK-NEXT: ret i1 %[[EQ]]
x == [0; 8]
}

// CHECK-LABEL: @array_eq_zero_long([1234 x i16]*
// CHECK-LABEL: @array_eq_zero_long(
#[no_mangle]
pub fn array_eq_zero_long(x: [u16; 1234]) -> bool {
// CHECK-NEXT: start:
Expand Down
36 changes: 18 additions & 18 deletions src/test/codegen/atomic-operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ use std::sync::atomic::{AtomicI32, Ordering::*};
// CHECK-LABEL: @compare_exchange
#[no_mangle]
pub fn compare_exchange(a: &AtomicI32) {
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 10 monotonic monotonic
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 10 monotonic monotonic
let _ = a.compare_exchange(0, 10, Relaxed, Relaxed);

// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 20 release monotonic
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 20 release monotonic
let _ = a.compare_exchange(0, 20, Release, Relaxed);

// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 30 acquire monotonic
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 31 acquire acquire
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 30 acquire monotonic
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 31 acquire acquire
let _ = a.compare_exchange(0, 30, Acquire, Relaxed);
let _ = a.compare_exchange(0, 31, Acquire, Acquire);

// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 40 acq_rel monotonic
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 41 acq_rel acquire
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 40 acq_rel monotonic
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 41 acq_rel acquire
let _ = a.compare_exchange(0, 40, AcqRel, Relaxed);
let _ = a.compare_exchange(0, 41, AcqRel, Acquire);

// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 50 seq_cst monotonic
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 51 seq_cst acquire
// CHECK: cmpxchg i32* %{{.*}}, i32 0, i32 52 seq_cst seq_cst
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 50 seq_cst monotonic
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 51 seq_cst acquire
// CHECK: cmpxchg {{i32\*|ptr}} %{{.*}}, i32 0, i32 52 seq_cst seq_cst
let _ = a.compare_exchange(0, 50, SeqCst, Relaxed);
let _ = a.compare_exchange(0, 51, SeqCst, Acquire);
let _ = a.compare_exchange(0, 52, SeqCst, SeqCst);
Expand All @@ -35,25 +35,25 @@ pub fn compare_exchange(a: &AtomicI32) {
// CHECK-LABEL: @compare_exchange_weak
#[no_mangle]
pub fn compare_exchange_weak(w: &AtomicI32) {
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 10 monotonic monotonic
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 10 monotonic monotonic
let _ = w.compare_exchange_weak(1, 10, Relaxed, Relaxed);

// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 20 release monotonic
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 20 release monotonic
let _ = w.compare_exchange_weak(1, 20, Release, Relaxed);

// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 30 acquire monotonic
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 31 acquire acquire
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 30 acquire monotonic
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 31 acquire acquire
let _ = w.compare_exchange_weak(1, 30, Acquire, Relaxed);
let _ = w.compare_exchange_weak(1, 31, Acquire, Acquire);

// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 40 acq_rel monotonic
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 41 acq_rel acquire
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 40 acq_rel monotonic
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 41 acq_rel acquire
let _ = w.compare_exchange_weak(1, 40, AcqRel, Relaxed);
let _ = w.compare_exchange_weak(1, 41, AcqRel, Acquire);

// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 50 seq_cst monotonic
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 51 seq_cst acquire
// CHECK: cmpxchg weak i32* %{{.*}}, i32 1, i32 52 seq_cst seq_cst
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 50 seq_cst monotonic
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 51 seq_cst acquire
// CHECK: cmpxchg weak {{i32\*|ptr}} %{{.*}}, i32 1, i32 52 seq_cst seq_cst
let _ = w.compare_exchange_weak(1, 50, SeqCst, Relaxed);
let _ = w.compare_exchange_weak(1, 51, SeqCst, Acquire);
let _ = w.compare_exchange_weak(1, 52, SeqCst, SeqCst);
Expand Down
8 changes: 4 additions & 4 deletions src/test/codegen/c-variadic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() {
// Ensure that we do not remove the `va_list` passed to the foreign function when
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap)
foreign_c_variadic_1(ap);
}

pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42)
foreign_c_variadic_1(ap, 42i32);
}
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42)
foreign_c_variadic_1(ap, 2i32, 42i32);
}

pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/codegen/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ pub fn inline_enum_const() -> E<i8, i16> {
#[no_mangle]
pub fn low_align_const() -> E<i16, [i16; 3]> {
// Check that low_align_const and high_align_const use the same constant
// CHECK: memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 2 %1, i8* align 2 getelementptr inbounds (<{ [4 x i8], [4 x i8] }>, <{ [4 x i8], [4 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i{{(32|64)}} 8, i1 false)
// CHECK: memcpy.{{.+}}({{i8\*|ptr}} align 2 %{{[0-9]+}}, {{i8\*|ptr}} align 2 {{.*}}[[LOW_HIGH]]{{.*}}, i{{(32|64)}} 8, i1 false)
*&E::A(0)
}

// CHECK-LABEL: @high_align_const
#[no_mangle]
pub fn high_align_const() -> E<i16, i32> {
// Check that low_align_const and high_align_const use the same constant
// CHECK: memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [4 x i8], [4 x i8] }>, <{ [4 x i8], [4 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i{{(32|64)}} 8, i1 false)
// CHECK: memcpy.{{.+}}({{i8\*|ptr}} align 4 %{{[0-9]+}}, {{i8\*|ptr}} align 4 {{.*}}[[LOW_HIGH]]{{.*}}, i{{(32|64)}} 8, i1 false)
*&E::A(0)
}
2 changes: 1 addition & 1 deletion src/test/codegen/fastcall-inreg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub mod tests {
#[no_mangle]
pub extern "fastcall" fn f1(_: i32, _: i32, _: i32) {}

// CHECK: @f2(i32* inreg %_1, i32* inreg %_2, i32* %_3)
// CHECK: @f2({{i32\*|ptr}} inreg %_1, {{i32\*|ptr}} inreg %_2, {{i32\*|ptr}} %_3)
#[no_mangle]
pub extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {}

Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/ffi-out-of-bounds-loads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extern "C" {
fn main() {
let s = S { f1: 1, f2: 2, f3: 3 };
unsafe {
// CHECK: load { i64, i32 }, { i64, i32 }* {{.*}}, align 4
// CHECK: load { i64, i32 }, {{.*}}, align 4
// CHECK: call void @foo({ i64, i32 } {{.*}})
foo(s);
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/codegen/function-arguments-noopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub fn boolean_call(x: bool, f: fn(bool) -> bool) -> bool {
f(x)
}

// CHECK: align 4 i32* @borrow(i32* align 4 %x)
// CHECK: align 4 {{i32\*|ptr}} @borrow({{i32\*|ptr}} align 4 %x)
#[no_mangle]
pub fn borrow(x: &i32) -> &i32 {
x
Expand All @@ -32,11 +32,11 @@ pub fn borrow(x: &i32) -> &i32 {
// CHECK-LABEL: @borrow_call
#[no_mangle]
pub fn borrow_call(x: &i32, f: fn(&i32) -> &i32) -> &i32 {
// CHECK: call align 4 i32* %f(i32* align 4 %x)
// CHECK: call align 4 {{i32\*|ptr}} %f({{i32\*|ptr}} align 4 %x)
f(x)
}

// CHECK: void @struct_(%S* sret(%S){{( %0)?}}, %S* %x)
// CHECK: void @struct_({{%S\*|ptr}} sret(%S){{( %0)?}}, {{%S\*|ptr}} %x)
#[no_mangle]
pub fn struct_(x: S) -> S {
x
Expand All @@ -45,7 +45,7 @@ pub fn struct_(x: S) -> S {
// CHECK-LABEL: @struct_call
#[no_mangle]
pub fn struct_call(x: S, f: fn(S) -> S) -> S {
// CHECK: call void %f(%S* sret(%S){{( %0)?}}, %S* %{{.+}})
// CHECK: call void %f({{%S\*|ptr}} sret(%S){{( %0)?}}, {{%S\*|ptr}} %{{.+}})
f(x)
}

Expand Down
Loading