Skip to content
Open
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
42 changes: 18 additions & 24 deletions compiler/rustc_target/src/callconv/sparc.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,56 @@
use rustc_abi::{HasDataLayout, Size, TyAbiInterface};
use rustc_abi::{BackendRepr, Float, HasDataLayout, Primitive, TyAbiInterface};

use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform};
use crate::callconv::{ArgAbi, FnAbi};

fn classify_ret<Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size)
where
C: HasDataLayout,
{
if !ret.layout.is_aggregate() {
ret.extend_integer_width_to(32);
} else {
fn is_long_double(repr: BackendRepr) -> bool {
matches!(repr, BackendRepr::Scalar(scalar) if scalar.primitive() == Primitive::Float(Float::F128))
}

fn classify_ret<'a, Ty>(ret: &mut ArgAbi<'a, Ty>) {
if is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() {
ret.make_indirect();
*offset += cx.data_layout().pointer_size();
} else {
ret.extend_integer_width_to(32);
}
}

fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, offset: &mut Size)
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !arg.layout.is_sized() {
// FIXME: Update offset?
// Not touching this...
return;
}
let dl = cx.data_layout();
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
*offset += dl.pointer_size();
return;
}
let size = arg.layout.size;
let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align);

if arg.layout.is_aggregate() {
let pad_i32 = !offset.is_aligned(align);
arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32);
if is_long_double(arg.layout.backend_repr) || arg.layout.is_aggregate() {
arg.make_indirect();
} else {
arg.extend_integer_width_to(32);
}

*offset = offset.align_to(align) + size.align_to(align);
}

pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
let mut offset = Size::ZERO;
if !fn_abi.ret.is_ignore() {
classify_ret(cx, &mut fn_abi.ret, &mut offset);
classify_ret(&mut fn_abi.ret);
}

for arg in fn_abi.args.iter_mut() {
if arg.is_ignore() {
if arg.layout.is_zst() {
arg.make_indirect_from_ignore();
}
continue;
}
classify_arg(cx, arg, &mut offset);
classify_arg(cx, arg);
}
}
50 changes: 50 additions & 0 deletions tests/codegen-llvm/f128-sparc-callconv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! Verify that Rust implements the expected calling convention for `f128`

//@ add-minicore
//@ compile-flags: -Copt-level=3 --target=sparc-unknown-linux-gnu
//@ needs-llvm-components: sparc

#![crate_type = "lib"]
#![no_std]
#![no_core]
#![feature(no_core, lang_items, f128)]

extern crate minicore;

extern "C" {
fn extern_call(arg0: f128);
fn extern_ret() -> f128;
}

#[no_mangle]
pub extern "C" fn pass(_arg0: u32, arg1: f128) {
// CHECK-LABEL: @pass(
// an f128 is passed via the stack
// CHECK-SAME: ptr {{.*}}
// CHECK: call void @extern_call
unsafe { extern_call(arg1) };
}

// Check that we produce the correct return ABI
#[no_mangle]
pub extern "C" fn ret(_arg0: u32, arg1: f128) -> f128 {
// CHECK-LABEL: @ret(
// and an f128 is returned via the stack
// CHECK-SAME: sret([16 x i8])
// CHECK: %0 = load fp128, ptr %arg1
// CHECK-NEXT: store fp128 %0, ptr %_0
// CHECK-NEXT: ret void
arg1
}

// Check that we consume the correct return ABI
#[no_mangle]
pub extern "C" fn forward(dst: *mut f128) {
// CHECK-LABEL: @forward
// CHECK-SAME: ptr{{.*}} %dst)
// without optimizatons, an intermediate alloca is used
// CHECK: call void @extern_ret
// CHECK: store fp128
// CHECK: ret void
unsafe { *dst = extern_ret() };
}
4 changes: 1 addition & 3 deletions tests/codegen-llvm/repr/transparent-imm-array.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//@ add-minicore
//@ revisions: arm-linux arm-android armv7-linux armv7-android mips thumb sparc
//@ revisions: arm-linux arm-android armv7-linux armv7-android mips thumb
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes

//@[arm-linux] compile-flags: --target arm-unknown-linux-gnueabi
Expand All @@ -14,8 +14,6 @@
//@[mips] needs-llvm-components: mips
//@[thumb] compile-flags: --target thumbv7neon-linux-androideabi
//@[thumb] needs-llvm-components: arm
//@[sparc] compile-flags: --target sparc-unknown-linux-gnu
//@[sparc] needs-llvm-components: sparc

// See ./transparent.rs
// Some platforms pass large aggregates using immediate arrays in LLVMIR
Expand Down
113 changes: 113 additions & 0 deletions tests/codegen-llvm/repr/transparent-sparc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//@ add-minicore
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes --target sparc-unknown-linux-gnu
//@ needs-llvm-components: sparc

// See ./transparent.rs

#![feature(no_core, lang_items, transparent_unions)]
#![crate_type = "lib"]
#![no_std]
#![no_core]

extern crate minicore;
use minicore::*;
impl Copy for BigS {}
impl Copy for BigU {}

#[repr(C)]
pub struct BigS([u32; 16]);

#[repr(transparent)]
pub struct TsBigS(BigS);

#[repr(transparent)]
pub union TuBigS {
field: BigS,
}

#[repr(transparent)]
pub enum TeBigS {
Variant(BigS),
}

// CHECK: define{{.*}}void @test_BigS(ptr [[BIGS_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGS_RET_ATTRS2:.*]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_BigS(_: BigS) -> BigS {
loop {}
}

// CHECK: define{{.*}}void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS {
loop {}
}

// CHECK: define{{.*}}void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS {
loop {}
}

// CHECK: define{{.*}}void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS {
loop {}
}

#[repr(C)]
pub union BigU {
foo: [u32; 16],
}

#[repr(transparent)]
pub struct TsBigU(BigU);

#[repr(transparent)]
pub union TuBigU {
field: BigU,
}

#[repr(transparent)]
pub enum TeBigU {
Variant(BigU),
}

// CHECK: define{{.*}}void @test_BigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_BigU(_: BigU) -> BigU {
loop {}
}

// CHECK: define{{.*}}void @test_TsBigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU {
loop {}
}

// CHECK: define{{.*}}void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU {
loop {}
}

// CHECK: define{{.*}}void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr
// CHECK-NOT: byval
// CHECK-SAME: %{{[0-9a-z_]+}})
#[no_mangle]
pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU {
loop {}
}
18 changes: 9 additions & 9 deletions tests/ui/abi/c-zst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ extern "C" fn(i32, (), i32);
```
*/

/*
* ZST IN "C" IS ZERO-SIZED
*/
//
// ZST IN "C" IS ZERO-SIZED
//

//@ revisions: aarch64-darwin
//@[aarch64-darwin] compile-flags: --target aarch64-apple-darwin
Expand All @@ -27,10 +27,9 @@ extern "C" fn(i32, (), i32);
//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu
//@[x86_64-linux] needs-llvm-components: x86


/*
* ZST IN "C" IS PASS-BY-POINTER
*/
//
// ZST IN "C" IS PASS-BY-POINTER
//

// according to the SRV4 ABI, an aggregate is always passed in registers,
// and it so happens the GCC extension for ZSTs considers them as structs.
Expand All @@ -42,7 +41,9 @@ extern "C" fn(i32, (), i32);
//@[s390x-linux] compile-flags: --target s390x-unknown-linux-gnu
//@[s390x-linux] needs-llvm-components: systemz

//@ revisions: sparc64-linux
//@ revisions: sparc-linux sparc64-linux
//@[sparc-linux] compile-flags: --target sparc-unknown-linux-gnu
//@[sparc-linux] needs-llvm-components: sparc
//@[sparc64-linux] compile-flags: --target sparc64-unknown-linux-gnu
//@[sparc64-linux] needs-llvm-components: sparc

Expand All @@ -53,7 +54,6 @@ extern "C" fn(i32, (), i32);
//@[x86_64-pc-windows-gnu] needs-llvm-components: x86
//@ ignore-backends: gcc


#![feature(no_core, rustc_attrs)]
#![no_core]
#![crate_type = "lib"]
Expand Down
80 changes: 80 additions & 0 deletions tests/ui/abi/c-zst.sparc-linux.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
error: fn_abi_of(pass_zst) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: (),
layout: Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: $SOME_ALIGN,
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
in_memory_order: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: $SOME_ALIGN,
randomization_seed: 0,
},
},
mode: Indirect {
attrs: ArgAttributes {
regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree,
arg_ext: None,
pointee_size: Size(0 bytes),
pointee_align: Some(
Align(1 bytes),
),
},
meta_attrs: None,
on_stack: false,
},
},
],
ret: ArgAbi {
layout: TyAndLayout {
ty: (),
layout: Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: $SOME_ALIGN,
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
in_memory_order: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: $SOME_ALIGN,
randomization_seed: 0,
},
},
mode: Ignore,
},
c_variadic: false,
fixed_count: 1,
conv: C,
can_unwind: false,
}
--> $DIR/c-zst.rs:65:1
|
LL | extern "C" fn pass_zst(_: ()) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Loading