Skip to content

Commit 07a34df

Browse files
committedFeb 8, 2020
Auto merge of #68452 - msizanoen1:riscv-abi, r=nagisa,eddyb
Implement proper C ABI lowering for RISC-V This is necessary for full RISC-V psABI compliance when passing argument across C FFI boundary. cc @lenary
·
1.90.01.43.0
2 parents 85ffd44 + 3963387 commit 07a34df

File tree

8 files changed

+1162
-16
lines changed

8 files changed

+1162
-16
lines changed
 

‎src/librustc/ty/layout.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,6 +2651,7 @@ where
26512651
.map(|(i, ty)| arg_of(ty, Some(i)))
26522652
.collect(),
26532653
c_variadic: sig.c_variadic,
2654+
fixed_count: inputs.len(),
26542655
conv,
26552656
};
26562657
fn_abi.adjust_for_abi(cx, sig.abi);

‎src/librustc_target/abi/call/mod.rs‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ impl Reg {
120120
reg_ctor!(i16, Integer, 16);
121121
reg_ctor!(i32, Integer, 32);
122122
reg_ctor!(i64, Integer, 64);
123+
reg_ctor!(i128, Integer, 128);
123124

124125
reg_ctor!(f32, Float, 32);
125126
reg_ctor!(f64, Float, 64);
@@ -538,6 +539,12 @@ pub struct FnAbi<'a, Ty> {
538539

539540
pub c_variadic: bool,
540541

542+
/// The count of non-variadic arguments.
543+
///
544+
/// Should only be different from args.len() when c_variadic is true.
545+
/// This can be used to know wether an argument is variadic or not.
546+
pub fixed_count: usize,
547+
541548
pub conv: Conv,
542549
}
543550

@@ -579,8 +586,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
579586
"nvptx" => nvptx::compute_abi_info(self),
580587
"nvptx64" => nvptx64::compute_abi_info(self),
581588
"hexagon" => hexagon::compute_abi_info(self),
582-
"riscv32" => riscv::compute_abi_info(self, 32),
583-
"riscv64" => riscv::compute_abi_info(self, 64),
589+
"riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
584590
"wasm32" if cx.target_spec().target_os != "emscripten" => {
585591
wasm32_bindgen_compat::compute_abi_info(self)
586592
}
Lines changed: 322 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,358 @@
11
// Reference: RISC-V ELF psABI specification
22
// https://github.com/riscv/riscv-elf-psabi-doc
3+
//
4+
// Reference: Clang RISC-V ELF psABI lowering code
5+
// https://github.com/llvm/llvm-project/blob/8e780252a7284be45cf1ba224cabd884847e8e92/clang/lib/CodeGen/TargetInfo.cpp#L9311-L9773
36

4-
use crate::abi::call::{ArgAbi, FnAbi};
7+
use crate::abi::call::{ArgAbi, ArgAttribute, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
8+
use crate::abi::{
9+
self, Abi, FieldPlacement, HasDataLayout, LayoutOf, Size, TyLayout, TyLayoutMethods,
10+
};
11+
use crate::spec::HasTargetSpec;
12+
13+
#[derive(Copy, Clone)]
14+
enum RegPassKind {
15+
Float(Reg),
16+
Integer(Reg),
17+
Unknown,
18+
}
19+
20+
#[derive(Copy, Clone)]
21+
enum FloatConv {
22+
FloatPair(Reg, Reg),
23+
Float(Reg),
24+
MixedPair(Reg, Reg),
25+
}
26+
27+
#[derive(Copy, Clone)]
28+
struct CannotUseFpConv;
29+
30+
fn is_riscv_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool {
31+
match arg.layout.abi {
32+
Abi::Vector { .. } => true,
33+
_ => arg.layout.is_aggregate(),
34+
}
35+
}
36+
37+
fn should_use_fp_conv_helper<'a, Ty, C>(
38+
cx: &C,
39+
arg_layout: &TyLayout<'a, Ty>,
40+
xlen: u64,
41+
flen: u64,
42+
field1_kind: &mut RegPassKind,
43+
field2_kind: &mut RegPassKind,
44+
) -> Result<(), CannotUseFpConv>
45+
where
46+
Ty: TyLayoutMethods<'a, C> + Copy,
47+
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>>,
48+
{
49+
match arg_layout.abi {
50+
Abi::Scalar(ref scalar) => match scalar.value {
51+
abi::Int(..) | abi::Pointer => {
52+
if arg_layout.size.bits() > xlen {
53+
return Err(CannotUseFpConv);
54+
}
55+
match (*field1_kind, *field2_kind) {
56+
(RegPassKind::Unknown, _) => {
57+
*field1_kind = RegPassKind::Integer(Reg {
58+
kind: RegKind::Integer,
59+
size: arg_layout.size,
60+
});
61+
}
62+
(RegPassKind::Float(_), RegPassKind::Unknown) => {
63+
*field2_kind = RegPassKind::Integer(Reg {
64+
kind: RegKind::Integer,
65+
size: arg_layout.size,
66+
});
67+
}
68+
_ => return Err(CannotUseFpConv),
69+
}
70+
}
71+
abi::F32 | abi::F64 => {
72+
if arg_layout.size.bits() > flen {
73+
return Err(CannotUseFpConv);
74+
}
75+
match (*field1_kind, *field2_kind) {
76+
(RegPassKind::Unknown, _) => {
77+
*field1_kind =
78+
RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
79+
}
80+
(_, RegPassKind::Unknown) => {
81+
*field2_kind =
82+
RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
83+
}
84+
_ => return Err(CannotUseFpConv),
85+
}
86+
}
87+
},
88+
Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv),
89+
Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields {
90+
FieldPlacement::Union(_) => {
91+
if !arg_layout.is_zst() {
92+
return Err(CannotUseFpConv);
93+
}
94+
}
95+
FieldPlacement::Array { count, .. } => {
96+
for _ in 0..count {
97+
let elem_layout = arg_layout.field(cx, 0);
98+
should_use_fp_conv_helper(
99+
cx,
100+
&elem_layout,
101+
xlen,
102+
flen,
103+
field1_kind,
104+
field2_kind,
105+
)?;
106+
}
107+
}
108+
FieldPlacement::Arbitrary { .. } => {
109+
match arg_layout.variants {
110+
abi::Variants::Multiple { .. } => return Err(CannotUseFpConv),
111+
abi::Variants::Single { .. } => (),
112+
}
113+
for i in arg_layout.fields.index_by_increasing_offset() {
114+
let field = arg_layout.field(cx, i);
115+
should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?;
116+
}
117+
}
118+
},
119+
}
120+
Ok(())
121+
}
122+
123+
fn should_use_fp_conv<'a, Ty, C>(
124+
cx: &C,
125+
arg: &TyLayout<'a, Ty>,
126+
xlen: u64,
127+
flen: u64,
128+
) -> Option<FloatConv>
129+
where
130+
Ty: TyLayoutMethods<'a, C> + Copy,
131+
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>>,
132+
{
133+
let mut field1_kind = RegPassKind::Unknown;
134+
let mut field2_kind = RegPassKind::Unknown;
135+
if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() {
136+
return None;
137+
}
138+
match (field1_kind, field2_kind) {
139+
(RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)),
140+
(RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)),
141+
(RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)),
142+
(RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)),
143+
_ => None,
144+
}
145+
}
146+
147+
fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool
148+
where
149+
Ty: TyLayoutMethods<'a, C> + Copy,
150+
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>>,
151+
{
152+
if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) {
153+
match conv {
154+
FloatConv::Float(f) => {
155+
arg.cast_to(f);
156+
}
157+
FloatConv::FloatPair(l, r) => {
158+
arg.cast_to(CastTarget::pair(l, r));
159+
}
160+
FloatConv::MixedPair(l, r) => {
161+
arg.cast_to(CastTarget::pair(l, r));
162+
}
163+
}
164+
return false;
165+
}
166+
167+
let total = arg.layout.size;
5168

6-
fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>, xlen: u64) {
7169
// "Scalars wider than 2✕XLEN are passed by reference and are replaced in
8170
// the argument list with the address."
9171
// "Aggregates larger than 2✕XLEN bits are passed by reference and are
10172
// replaced in the argument list with the address, as are C++ aggregates
11173
// with nontrivial copy constructors, destructors, or vtables."
12-
if arg.layout.size.bits() > 2 * xlen {
13-
arg.make_indirect();
174+
if total.bits() > 2 * xlen {
175+
// We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN.
176+
if is_riscv_aggregate(arg) {
177+
arg.make_indirect();
178+
}
179+
return true;
180+
}
181+
182+
let xlen_reg = match xlen {
183+
32 => Reg::i32(),
184+
64 => Reg::i64(),
185+
_ => unreachable!("Unsupported XLEN: {}", xlen),
186+
};
187+
if is_riscv_aggregate(arg) {
188+
if total.bits() <= xlen {
189+
arg.cast_to(xlen_reg);
190+
} else {
191+
arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) });
192+
}
193+
return false;
14194
}
15195

16196
// "When passed in registers, scalars narrower than XLEN bits are widened
17197
// according to the sign of their type up to 32 bits, then sign-extended to
18198
// XLEN bits."
19-
arg.extend_integer_width_to(xlen); // this method only affects integer scalars
199+
extend_integer_width(arg, xlen);
200+
false
20201
}
21202

22-
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>, xlen: u64) {
203+
fn classify_arg<'a, Ty, C>(
204+
cx: &C,
205+
arg: &mut ArgAbi<'a, Ty>,
206+
xlen: u64,
207+
flen: u64,
208+
is_vararg: bool,
209+
avail_gprs: &mut u64,
210+
avail_fprs: &mut u64,
211+
) where
212+
Ty: TyLayoutMethods<'a, C> + Copy,
213+
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>>,
214+
{
215+
if !is_vararg {
216+
match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
217+
Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {
218+
*avail_fprs -= 1;
219+
arg.cast_to(f);
220+
return;
221+
}
222+
Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => {
223+
*avail_fprs -= 2;
224+
arg.cast_to(CastTarget::pair(l, r));
225+
return;
226+
}
227+
Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => {
228+
*avail_gprs -= 1;
229+
*avail_fprs -= 1;
230+
arg.cast_to(CastTarget::pair(l, r));
231+
return;
232+
}
233+
_ => (),
234+
}
235+
}
236+
237+
let total = arg.layout.size;
238+
let align = arg.layout.align.abi.bits();
239+
23240
// "Scalars wider than 2✕XLEN are passed by reference and are replaced in
24241
// the argument list with the address."
25242
// "Aggregates larger than 2✕XLEN bits are passed by reference and are
26243
// replaced in the argument list with the address, as are C++ aggregates
27244
// with nontrivial copy constructors, destructors, or vtables."
28-
if arg.layout.size.bits() > 2 * xlen {
29-
arg.make_indirect();
245+
if total.bits() > 2 * xlen {
246+
// We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN.
247+
if is_riscv_aggregate(arg) {
248+
arg.make_indirect();
249+
}
250+
if *avail_gprs >= 1 {
251+
*avail_gprs -= 1;
252+
}
253+
return;
254+
}
255+
256+
let double_xlen_reg = match xlen {
257+
32 => Reg::i64(),
258+
64 => Reg::i128(),
259+
_ => unreachable!("Unsupported XLEN: {}", xlen),
260+
};
261+
262+
let xlen_reg = match xlen {
263+
32 => Reg::i32(),
264+
64 => Reg::i64(),
265+
_ => unreachable!("Unsupported XLEN: {}", xlen),
266+
};
267+
268+
if total.bits() > xlen {
269+
let align_regs = align > xlen;
270+
if is_riscv_aggregate(arg) {
271+
arg.cast_to(Uniform {
272+
unit: if align_regs { double_xlen_reg } else { xlen_reg },
273+
total: Size::from_bits(xlen * 2),
274+
});
275+
}
276+
if align_regs && is_vararg {
277+
*avail_gprs -= *avail_gprs % 2;
278+
}
279+
if *avail_gprs >= 2 {
280+
*avail_gprs -= 2;
281+
} else {
282+
*avail_gprs = 0;
283+
}
284+
return;
285+
} else if is_riscv_aggregate(arg) {
286+
arg.cast_to(xlen_reg);
287+
if *avail_gprs >= 1 {
288+
*avail_gprs -= 1;
289+
}
290+
return;
30291
}
31292

32293
// "When passed in registers, scalars narrower than XLEN bits are widened
33294
// according to the sign of their type up to 32 bits, then sign-extended to
34295
// XLEN bits."
35-
arg.extend_integer_width_to(xlen); // this method only affects integer scalars
296+
if *avail_gprs >= 1 {
297+
extend_integer_width(arg, xlen);
298+
*avail_gprs -= 1;
299+
}
36300
}
37301

38-
pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>, xlen: u64) {
302+
fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) {
303+
match arg.layout.abi {
304+
Abi::Scalar(ref scalar) => {
305+
match scalar.value {
306+
abi::Int(i, _) => {
307+
// 32-bit integers are always sign-extended
308+
if i.size().bits() == 32 && xlen > 32 {
309+
if let PassMode::Direct(ref mut attrs) = arg.mode {
310+
attrs.set(ArgAttribute::SExt);
311+
return;
312+
}
313+
}
314+
}
315+
_ => (),
316+
}
317+
}
318+
_ => (),
319+
}
320+
arg.extend_integer_width_to(xlen);
321+
}
322+
323+
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
324+
where
325+
Ty: TyLayoutMethods<'a, C> + Copy,
326+
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout + HasTargetSpec,
327+
{
328+
let flen = match &cx.target_spec().options.llvm_abiname[..] {
329+
"ilp32f" | "lp64f" => 32,
330+
"ilp32d" | "lp64d" => 64,
331+
_ => 0,
332+
};
333+
let xlen = cx.data_layout().pointer_size.bits();
334+
335+
let mut avail_gprs = 8;
336+
let mut avail_fprs = 8;
337+
39338
if !fn_abi.ret.is_ignore() {
40-
classify_ret(&mut fn_abi.ret, xlen);
339+
if classify_ret(cx, &mut fn_abi.ret, xlen, flen) {
340+
avail_gprs -= 1;
341+
}
41342
}
42343

43-
for arg in &mut fn_abi.args {
344+
for (i, arg) in fn_abi.args.iter_mut().enumerate() {
44345
if arg.is_ignore() {
45346
continue;
46347
}
47-
classify_arg(arg, xlen);
348+
classify_arg(
349+
cx,
350+
arg,
351+
xlen,
352+
flen,
353+
i >= fn_abi.fixed_count,
354+
&mut avail_gprs,
355+
&mut avail_fprs,
356+
);
48357
}
49358
}

‎src/test/auxiliary/rust_test_helpers.c‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ struct floats {
168168
double c;
169169
};
170170

171+
struct char_char_double {
172+
uint8_t a;
173+
uint8_t b;
174+
double c;
175+
};
176+
177+
struct char_char_float {
178+
uint8_t a;
179+
uint8_t b;
180+
float c;
181+
};
182+
171183
struct quad
172184
rust_dbg_abi_1(struct quad q) {
173185
struct quad qq = { q.c + 1,
@@ -185,6 +197,23 @@ rust_dbg_abi_2(struct floats f) {
185197
return ff;
186198
}
187199

200+
struct char_char_double
201+
rust_dbg_abi_3(struct char_char_double a) {
202+
struct char_char_double ccd = { a.a + 1,
203+
a.b - 1,
204+
a.c + 1.0 };
205+
return ccd;
206+
}
207+
208+
struct char_char_float
209+
rust_dbg_abi_4(struct char_char_float a) {
210+
struct char_char_float ccd = { a.a + 1,
211+
a.b - 1,
212+
a.c + 1.0 };
213+
return ccd;
214+
}
215+
216+
188217
int
189218
rust_dbg_static_mut = 3;
190219

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// ignore-tidy-linelength
2+
// compile-flags: -C no-prepopulate-passes
3+
// only-riscv64
4+
// only-linux
5+
#![crate_type = "lib"]
6+
#![allow(improper_ctypes)]
7+
8+
// CHECK: define void @f_void()
9+
#[no_mangle]
10+
pub extern "C" fn f_void() {}
11+
12+
// CHECK: define zeroext i1 @f_scalar_0(i1 zeroext %a)
13+
#[no_mangle]
14+
pub extern "C" fn f_scalar_0(a: bool) -> bool {
15+
a
16+
}
17+
18+
// CHECK: define signext i8 @f_scalar_1(i8 signext %x)
19+
#[no_mangle]
20+
pub extern "C" fn f_scalar_1(x: i8) -> i8 {
21+
x
22+
}
23+
24+
// CHECK: define zeroext i8 @f_scalar_2(i8 zeroext %x)
25+
#[no_mangle]
26+
pub extern "C" fn f_scalar_2(x: u8) -> u8 {
27+
x
28+
}
29+
30+
// CHECK: define signext i32 @f_scalar_3(i32 signext %x)
31+
#[no_mangle]
32+
pub extern "C" fn f_scalar_3(x: i32) -> u32 {
33+
x as u32
34+
}
35+
36+
// CHECK: define i64 @f_scalar_4(i64 %x)
37+
#[no_mangle]
38+
pub extern "C" fn f_scalar_4(x: i64) -> i64 {
39+
x
40+
}
41+
42+
// CHECK: define float @f_fp_scalar_1(float)
43+
#[no_mangle]
44+
pub extern "C" fn f_fp_scalar_1(x: f32) -> f32 {
45+
x
46+
}
47+
// CHECK: define double @f_fp_scalar_2(double)
48+
#[no_mangle]
49+
pub extern "C" fn f_fp_scalar_2(x: f64) -> f64 {
50+
x
51+
}
52+
53+
#[repr(C)]
54+
pub struct Empty {}
55+
56+
// CHECK: define void @f_agg_empty_struct()
57+
#[no_mangle]
58+
pub extern "C" fn f_agg_empty_struct(e: Empty) -> Empty {
59+
e
60+
}
61+
62+
#[repr(C)]
63+
pub struct Tiny {
64+
a: u16,
65+
b: u16,
66+
c: u16,
67+
d: u16,
68+
}
69+
70+
// CHECK: define void @f_agg_tiny(i64)
71+
#[no_mangle]
72+
pub extern "C" fn f_agg_tiny(mut e: Tiny) {
73+
e.a += e.b;
74+
e.c += e.d;
75+
}
76+
77+
// CHECK: define i64 @f_agg_tiny_ret()
78+
#[no_mangle]
79+
pub extern "C" fn f_agg_tiny_ret() -> Tiny {
80+
Tiny { a: 1, b: 2, c: 3, d: 4 }
81+
}
82+
83+
#[repr(C)]
84+
pub struct Small {
85+
a: i64,
86+
b: *mut i64,
87+
}
88+
89+
// CHECK: define void @f_agg_small([2 x i64])
90+
#[no_mangle]
91+
pub extern "C" fn f_agg_small(mut x: Small) {
92+
x.a += unsafe { *x.b };
93+
x.b = &mut x.a;
94+
}
95+
96+
// CHECK: define [2 x i64] @f_agg_small_ret()
97+
#[no_mangle]
98+
pub extern "C" fn f_agg_small_ret() -> Small {
99+
Small { a: 1, b: core::ptr::null_mut() }
100+
}
101+
102+
#[repr(C)]
103+
pub struct SmallAligned {
104+
a: i128,
105+
}
106+
107+
// CHECK: define void @f_agg_small_aligned(i128)
108+
#[no_mangle]
109+
pub extern "C" fn f_agg_small_aligned(mut x: SmallAligned) {
110+
x.a += x.a;
111+
}
112+
113+
#[repr(C)]
114+
pub struct Large {
115+
a: i64,
116+
b: i64,
117+
c: i64,
118+
d: i64,
119+
}
120+
121+
// CHECK: define void @f_agg_large(%Large* {{.*}}%x)
122+
#[no_mangle]
123+
pub extern "C" fn f_agg_large(mut x: Large) {
124+
x.a = x.b + x.c + x.d;
125+
}
126+
127+
// CHECK: define void @f_agg_large_ret(%Large* {{.*}}sret{{.*}}, i32 signext %i, i8 signext %j)
128+
#[no_mangle]
129+
pub extern "C" fn f_agg_large_ret(i: i32, j: i8) -> Large {
130+
Large { a: 1, b: 2, c: 3, d: 4 }
131+
}
132+
133+
// CHECK: define void @f_scalar_stack_1(i64, [2 x i64], i128, %Large* {{.*}}%d, i8 zeroext %e, i8 signext %f, i8 %g, i8 %h)
134+
#[no_mangle]
135+
pub extern "C" fn f_scalar_stack_1(
136+
a: Tiny,
137+
b: Small,
138+
c: SmallAligned,
139+
d: Large,
140+
e: u8,
141+
f: i8,
142+
g: u8,
143+
h: i8,
144+
) {
145+
}
146+
147+
// CHECK: define void @f_scalar_stack_2(%Large* {{.*}}sret{{.*}}, i64 %a, i128, i128, i64 %d, i8 zeroext %e, i8 %f, i8 %g)
148+
#[no_mangle]
149+
pub extern "C" fn f_scalar_stack_2(
150+
a: u64,
151+
b: SmallAligned,
152+
c: SmallAligned,
153+
d: u64,
154+
e: u8,
155+
f: i8,
156+
g: u8,
157+
) -> Large {
158+
Large { a: a as i64, b: e as i64, c: f as i64, d: g as i64 }
159+
}
160+
161+
extern "C" {
162+
fn f_va_callee(_: i32, ...) -> i32;
163+
}
164+
165+
#[no_mangle]
166+
pub unsafe extern "C" fn f_va_caller() {
167+
// CHECK: call signext i32 (i32, ...) @f_va_callee(i32 signext 1, i32 signext 2, i64 3, double {{.*}}, double {{.*}}, i64 {{.*}}, [2 x i64] {{.*}}, i128 {{.*}}, %Large* {{.*}})
168+
f_va_callee(
169+
1,
170+
2i32,
171+
3i64,
172+
4.0f64,
173+
5.0f64,
174+
Tiny { a: 1, b: 2, c: 3, d: 4 },
175+
Small { a: 10, b: core::ptr::null_mut() },
176+
SmallAligned { a: 11 },
177+
Large { a: 12, b: 13, c: 14, d: 15 },
178+
);
179+
// CHECK: call signext i32 (i32, ...) @f_va_callee(i32 signext 1, i32 signext 2, i32 signext 3, i32 signext 4, i128 {{.*}}, i32 signext 6, i32 signext 7, i32 8, i32 9)
180+
f_va_callee(1, 2i32, 3i32, 4i32, SmallAligned { a: 5 }, 6i32, 7i32, 8i32, 9i32);
181+
}
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
// ignore-tidy-linelength
2+
// compile-flags: -C no-prepopulate-passes
3+
// only-riscv64
4+
// only-linux
5+
#![crate_type = "lib"]
6+
7+
// CHECK: define void @f_fpr_tracking(double, double, double, double, double, double, double, double, i8 zeroext %i)
8+
#[no_mangle]
9+
pub extern "C" fn f_fpr_tracking(
10+
a: f64,
11+
b: f64,
12+
c: f64,
13+
d: f64,
14+
e: f64,
15+
f: f64,
16+
g: f64,
17+
h: f64,
18+
i: u8,
19+
) {
20+
}
21+
22+
#[repr(C)]
23+
pub struct Double {
24+
f: f64,
25+
}
26+
27+
#[repr(C)]
28+
pub struct DoubleDouble {
29+
f: f64,
30+
g: f64,
31+
}
32+
33+
#[repr(C)]
34+
pub struct DoubleFloat {
35+
f: f64,
36+
g: f32,
37+
}
38+
39+
// CHECK: define void @f_double_s_arg(double)
40+
#[no_mangle]
41+
pub extern "C" fn f_double_s_arg(a: Double) {}
42+
43+
// CHECK: define double @f_ret_double_s()
44+
#[no_mangle]
45+
pub extern "C" fn f_ret_double_s() -> Double {
46+
Double { f: 1. }
47+
}
48+
49+
// CHECK: define void @f_double_double_s_arg({ double, double })
50+
#[no_mangle]
51+
pub extern "C" fn f_double_double_s_arg(a: DoubleDouble) {}
52+
53+
// CHECK: define { double, double } @f_ret_double_double_s()
54+
#[no_mangle]
55+
pub extern "C" fn f_ret_double_double_s() -> DoubleDouble {
56+
DoubleDouble { f: 1., g: 2. }
57+
}
58+
59+
// CHECK: define void @f_double_float_s_arg({ double, float })
60+
#[no_mangle]
61+
pub extern "C" fn f_double_float_s_arg(a: DoubleFloat) {}
62+
63+
// CHECK: define { double, float } @f_ret_double_float_s()
64+
#[no_mangle]
65+
pub extern "C" fn f_ret_double_float_s() -> DoubleFloat {
66+
DoubleFloat { f: 1., g: 2. }
67+
}
68+
69+
// CHECK: define void @f_double_double_s_arg_insufficient_fprs(double, double, double, double, double, double, double, [2 x i64])
70+
#[no_mangle]
71+
pub extern "C" fn f_double_double_s_arg_insufficient_fprs(
72+
a: f64,
73+
b: f64,
74+
c: f64,
75+
d: f64,
76+
e: f64,
77+
f: f64,
78+
g: f64,
79+
h: DoubleDouble,
80+
) {
81+
}
82+
83+
#[repr(C)]
84+
pub struct DoubleInt8 {
85+
f: f64,
86+
i: i8,
87+
}
88+
89+
#[repr(C)]
90+
pub struct DoubleUInt8 {
91+
f: f64,
92+
i: u8,
93+
}
94+
95+
#[repr(C)]
96+
pub struct DoubleInt32 {
97+
f: f64,
98+
i: i32,
99+
}
100+
101+
#[repr(C)]
102+
pub struct DoubleInt64 {
103+
f: f64,
104+
i: i64,
105+
}
106+
107+
// CHECK: define void @f_double_int8_s_arg({ double, i8 })
108+
#[no_mangle]
109+
pub extern "C" fn f_double_int8_s_arg(a: DoubleInt8) {}
110+
111+
// CHECK: define { double, i8 } @f_ret_double_int8_s()
112+
#[no_mangle]
113+
pub extern "C" fn f_ret_double_int8_s() -> DoubleInt8 {
114+
DoubleInt8 { f: 1., i: 2 }
115+
}
116+
117+
// CHECK: define void @f_double_int32_s_arg({ double, i32 })
118+
#[no_mangle]
119+
pub extern "C" fn f_double_int32_s_arg(a: DoubleInt32) {}
120+
121+
// CHECK: define { double, i32 } @f_ret_double_int32_s()
122+
#[no_mangle]
123+
pub extern "C" fn f_ret_double_int32_s() -> DoubleInt32 {
124+
DoubleInt32 { f: 1., i: 2 }
125+
}
126+
127+
// CHECK: define void @f_double_uint8_s_arg({ double, i8 })
128+
#[no_mangle]
129+
pub extern "C" fn f_double_uint8_s_arg(a: DoubleUInt8) {}
130+
131+
// CHECK: define { double, i8 } @f_ret_double_uint8_s()
132+
#[no_mangle]
133+
pub extern "C" fn f_ret_double_uint8_s() -> DoubleUInt8 {
134+
DoubleUInt8 { f: 1., i: 2 }
135+
}
136+
137+
// CHECK: define void @f_double_int64_s_arg({ double, i64 })
138+
#[no_mangle]
139+
pub extern "C" fn f_double_int64_s_arg(a: DoubleInt64) {}
140+
141+
// CHECK: define { double, i64 } @f_ret_double_int64_s()
142+
#[no_mangle]
143+
pub extern "C" fn f_ret_double_int64_s() -> DoubleInt64 {
144+
DoubleInt64 { f: 1., i: 2 }
145+
}
146+
147+
// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, [2 x i64])
148+
#[no_mangle]
149+
pub extern "C" fn f_double_int8_s_arg_insufficient_gprs(
150+
a: i32,
151+
b: i32,
152+
c: i32,
153+
d: i32,
154+
e: i32,
155+
f: i32,
156+
g: i32,
157+
h: i32,
158+
i: DoubleInt8,
159+
) {
160+
}
161+
162+
// CHECK: define void @f_struct_double_int8_insufficient_fprs(float, double, double, double, double, double, double, double, [2 x i64])
163+
#[no_mangle]
164+
pub extern "C" fn f_struct_double_int8_insufficient_fprs(
165+
a: f32,
166+
b: f64,
167+
c: f64,
168+
d: f64,
169+
e: f64,
170+
f: f64,
171+
g: f64,
172+
h: f64,
173+
i: DoubleInt8,
174+
) {
175+
}
176+
177+
#[repr(C)]
178+
pub struct DoubleArr1 {
179+
a: [f64; 1],
180+
}
181+
182+
// CHECK: define void @f_doublearr1_s_arg(double)
183+
#[no_mangle]
184+
pub extern "C" fn f_doublearr1_s_arg(a: DoubleArr1) {}
185+
186+
// CHECK: define double @f_ret_doublearr1_s()
187+
#[no_mangle]
188+
pub extern "C" fn f_ret_doublearr1_s() -> DoubleArr1 {
189+
DoubleArr1 { a: [1.] }
190+
}
191+
192+
#[repr(C)]
193+
pub struct DoubleArr2 {
194+
a: [f64; 2],
195+
}
196+
197+
// CHECK: define void @f_doublearr2_s_arg({ double, double })
198+
#[no_mangle]
199+
pub extern "C" fn f_doublearr2_s_arg(a: DoubleArr2) {}
200+
201+
// CHECK: define { double, double } @f_ret_doublearr2_s()
202+
#[no_mangle]
203+
pub extern "C" fn f_ret_doublearr2_s() -> DoubleArr2 {
204+
DoubleArr2 { a: [1., 2.] }
205+
}
206+
207+
#[repr(C)]
208+
pub struct Tricky1 {
209+
f: [f64; 1],
210+
}
211+
212+
#[repr(C)]
213+
pub struct DoubleArr2Tricky1 {
214+
g: [Tricky1; 2],
215+
}
216+
217+
// CHECK: define void @f_doublearr2_tricky1_s_arg({ double, double })
218+
#[no_mangle]
219+
pub extern "C" fn f_doublearr2_tricky1_s_arg(a: DoubleArr2Tricky1) {}
220+
221+
// CHECK: define { double, double } @f_ret_doublearr2_tricky1_s()
222+
#[no_mangle]
223+
pub extern "C" fn f_ret_doublearr2_tricky1_s() -> DoubleArr2Tricky1 {
224+
DoubleArr2Tricky1 { g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
225+
}
226+
227+
#[repr(C)]
228+
pub struct EmptyStruct {}
229+
230+
#[repr(C)]
231+
pub struct DoubleArr2Tricky2 {
232+
s: EmptyStruct,
233+
g: [Tricky1; 2],
234+
}
235+
236+
// CHECK: define void @f_doublearr2_tricky2_s_arg({ double, double })
237+
#[no_mangle]
238+
pub extern "C" fn f_doublearr2_tricky2_s_arg(a: DoubleArr2Tricky2) {}
239+
240+
// CHECK: define { double, double } @f_ret_doublearr2_tricky2_s()
241+
#[no_mangle]
242+
pub extern "C" fn f_ret_doublearr2_tricky2_s() -> DoubleArr2Tricky2 {
243+
DoubleArr2Tricky2 { s: EmptyStruct {}, g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
244+
}
245+
246+
#[repr(C)]
247+
pub struct IntDoubleInt {
248+
a: i32,
249+
b: f64,
250+
c: i32,
251+
}
252+
253+
// CHECK: define void @f_int_double_int_s_arg(%IntDoubleInt* {{.*}}%a)
254+
#[no_mangle]
255+
pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {}
256+
257+
// CHECK: define void @f_ret_int_double_int_s(%IntDoubleInt* {{.*}}sret
258+
#[no_mangle]
259+
pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt {
260+
IntDoubleInt { a: 1, b: 2., c: 3 }
261+
}
262+
263+
#[repr(C)]
264+
pub struct CharCharDouble {
265+
a: u8,
266+
b: u8,
267+
c: f64,
268+
}
269+
270+
// CHECK: define void @f_char_char_double_s_arg([2 x i64])
271+
#[no_mangle]
272+
pub extern "C" fn f_char_char_double_s_arg(a: CharCharDouble) {}
273+
274+
// CHECK: define [2 x i64] @f_ret_char_char_double_s()
275+
#[no_mangle]
276+
pub extern "C" fn f_ret_char_char_double_s() -> CharCharDouble {
277+
CharCharDouble { a: 1, b: 2, c: 3. }
278+
}
279+
280+
#[repr(C)]
281+
pub union DoubleU {
282+
a: f64,
283+
}
284+
285+
// CHECK: define void @f_double_u_arg(i64)
286+
#[no_mangle]
287+
pub extern "C" fn f_double_u_arg(a: DoubleU) {}
288+
289+
// CHECK: define i64 @f_ret_double_u()
290+
#[no_mangle]
291+
pub extern "C" fn f_ret_double_u() -> DoubleU {
292+
unsafe { DoubleU { a: 1. } }
293+
}
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
// ignore-tidy-linelength
2+
// compile-flags: -C no-prepopulate-passes
3+
// only-riscv64
4+
// only-linux
5+
#![crate_type = "lib"]
6+
7+
// CHECK: define void @f_fpr_tracking(float, float, float, float, float, float, float, float, i8 zeroext %i)
8+
#[no_mangle]
9+
pub extern "C" fn f_fpr_tracking(
10+
a: f32,
11+
b: f32,
12+
c: f32,
13+
d: f32,
14+
e: f32,
15+
f: f32,
16+
g: f32,
17+
h: f32,
18+
i: u8,
19+
) {
20+
}
21+
22+
#[repr(C)]
23+
pub struct Float {
24+
f: f32,
25+
}
26+
27+
#[repr(C)]
28+
pub struct FloatFloat {
29+
f: f32,
30+
g: f32,
31+
}
32+
33+
// CHECK: define void @f_float_s_arg(float)
34+
#[no_mangle]
35+
pub extern "C" fn f_float_s_arg(a: Float) {}
36+
37+
// CHECK: define float @f_ret_float_s()
38+
#[no_mangle]
39+
pub extern "C" fn f_ret_float_s() -> Float {
40+
Float { f: 1. }
41+
}
42+
43+
// CHECK: define void @f_float_float_s_arg({ float, float })
44+
#[no_mangle]
45+
pub extern "C" fn f_float_float_s_arg(a: FloatFloat) {}
46+
47+
// CHECK: define { float, float } @f_ret_float_float_s()
48+
#[no_mangle]
49+
pub extern "C" fn f_ret_float_float_s() -> FloatFloat {
50+
FloatFloat { f: 1., g: 2. }
51+
}
52+
53+
// CHECK: define void @f_float_float_s_arg_insufficient_fprs(float, float, float, float, float, float, float, i64)
54+
#[no_mangle]
55+
pub extern "C" fn f_float_float_s_arg_insufficient_fprs(
56+
a: f32,
57+
b: f32,
58+
c: f32,
59+
d: f32,
60+
e: f32,
61+
f: f32,
62+
g: f32,
63+
h: FloatFloat,
64+
) {
65+
}
66+
67+
#[repr(C)]
68+
pub struct FloatInt8 {
69+
f: f32,
70+
i: i8,
71+
}
72+
73+
#[repr(C)]
74+
pub struct FloatUInt8 {
75+
f: f32,
76+
i: u8,
77+
}
78+
79+
#[repr(C)]
80+
pub struct FloatInt32 {
81+
f: f32,
82+
i: i32,
83+
}
84+
85+
#[repr(C)]
86+
pub struct FloatInt64 {
87+
f: f32,
88+
i: i64,
89+
}
90+
91+
// CHECK: define void @f_float_int8_s_arg({ float, i8 })
92+
#[no_mangle]
93+
pub extern "C" fn f_float_int8_s_arg(a: FloatInt8) {}
94+
95+
// CHECK: define { float, i8 } @f_ret_float_int8_s()
96+
#[no_mangle]
97+
pub extern "C" fn f_ret_float_int8_s() -> FloatInt8 {
98+
FloatInt8 { f: 1., i: 2 }
99+
}
100+
101+
// CHECK: define void @f_float_int32_s_arg({ float, i32 })
102+
#[no_mangle]
103+
pub extern "C" fn f_float_int32_s_arg(a: FloatInt32) {}
104+
105+
// CHECK: define { float, i32 } @f_ret_float_int32_s()
106+
#[no_mangle]
107+
pub extern "C" fn f_ret_float_int32_s() -> FloatInt32 {
108+
FloatInt32 { f: 1., i: 2 }
109+
}
110+
111+
// CHECK: define void @f_float_uint8_s_arg({ float, i8 })
112+
#[no_mangle]
113+
pub extern "C" fn f_float_uint8_s_arg(a: FloatUInt8) {}
114+
115+
// CHECK: define { float, i8 } @f_ret_float_uint8_s()
116+
#[no_mangle]
117+
pub extern "C" fn f_ret_float_uint8_s() -> FloatUInt8 {
118+
FloatUInt8 { f: 1., i: 2 }
119+
}
120+
121+
// CHECK: define void @f_float_int64_s_arg({ float, i64 })
122+
#[no_mangle]
123+
pub extern "C" fn f_float_int64_s_arg(a: FloatInt64) {}
124+
125+
// CHECK: define { float, i64 } @f_ret_float_int64_s()
126+
#[no_mangle]
127+
pub extern "C" fn f_ret_float_int64_s() -> FloatInt64 {
128+
FloatInt64 { f: 1., i: 2 }
129+
}
130+
131+
// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, i64)
132+
#[no_mangle]
133+
pub extern "C" fn f_float_int8_s_arg_insufficient_gprs(
134+
a: i32,
135+
b: i32,
136+
c: i32,
137+
d: i32,
138+
e: i32,
139+
f: i32,
140+
g: i32,
141+
h: i32,
142+
i: FloatInt8,
143+
) {
144+
}
145+
146+
// CHECK: define void @f_struct_float_int8_insufficient_fprs(float, float, float, float, float, float, float, float, i64)
147+
#[no_mangle]
148+
pub extern "C" fn f_struct_float_int8_insufficient_fprs(
149+
a: f32,
150+
b: f32,
151+
c: f32,
152+
d: f32,
153+
e: f32,
154+
f: f32,
155+
g: f32,
156+
h: f32,
157+
i: FloatInt8,
158+
) {
159+
}
160+
161+
#[repr(C)]
162+
pub struct FloatArr1 {
163+
a: [f32; 1],
164+
}
165+
166+
// CHECK: define void @f_floatarr1_s_arg(float)
167+
#[no_mangle]
168+
pub extern "C" fn f_floatarr1_s_arg(a: FloatArr1) {}
169+
170+
// CHECK: define float @f_ret_floatarr1_s()
171+
#[no_mangle]
172+
pub extern "C" fn f_ret_floatarr1_s() -> FloatArr1 {
173+
FloatArr1 { a: [1.] }
174+
}
175+
176+
#[repr(C)]
177+
pub struct FloatArr2 {
178+
a: [f32; 2],
179+
}
180+
181+
// CHECK: define void @f_floatarr2_s_arg({ float, float })
182+
#[no_mangle]
183+
pub extern "C" fn f_floatarr2_s_arg(a: FloatArr2) {}
184+
185+
// CHECK: define { float, float } @f_ret_floatarr2_s()
186+
#[no_mangle]
187+
pub extern "C" fn f_ret_floatarr2_s() -> FloatArr2 {
188+
FloatArr2 { a: [1., 2.] }
189+
}
190+
191+
#[repr(C)]
192+
pub struct Tricky1 {
193+
f: [f32; 1],
194+
}
195+
196+
#[repr(C)]
197+
pub struct FloatArr2Tricky1 {
198+
g: [Tricky1; 2],
199+
}
200+
201+
// CHECK: define void @f_floatarr2_tricky1_s_arg({ float, float })
202+
#[no_mangle]
203+
pub extern "C" fn f_floatarr2_tricky1_s_arg(a: FloatArr2Tricky1) {}
204+
205+
// CHECK: define { float, float } @f_ret_floatarr2_tricky1_s()
206+
#[no_mangle]
207+
pub extern "C" fn f_ret_floatarr2_tricky1_s() -> FloatArr2Tricky1 {
208+
FloatArr2Tricky1 { g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
209+
}
210+
211+
#[repr(C)]
212+
pub struct EmptyStruct {}
213+
214+
#[repr(C)]
215+
pub struct FloatArr2Tricky2 {
216+
s: EmptyStruct,
217+
g: [Tricky1; 2],
218+
}
219+
220+
// CHECK: define void @f_floatarr2_tricky2_s_arg({ float, float })
221+
#[no_mangle]
222+
pub extern "C" fn f_floatarr2_tricky2_s_arg(a: FloatArr2Tricky2) {}
223+
224+
// CHECK: define { float, float } @f_ret_floatarr2_tricky2_s()
225+
#[no_mangle]
226+
pub extern "C" fn f_ret_floatarr2_tricky2_s() -> FloatArr2Tricky2 {
227+
FloatArr2Tricky2 { s: EmptyStruct {}, g: [Tricky1 { f: [1.] }, Tricky1 { f: [2.] }] }
228+
}
229+
230+
#[repr(C)]
231+
pub struct IntFloatInt {
232+
a: i32,
233+
b: f32,
234+
c: i32,
235+
}
236+
237+
// CHECK: define void @f_int_float_int_s_arg([2 x i64])
238+
#[no_mangle]
239+
pub extern "C" fn f_int_float_int_s_arg(a: IntFloatInt) {}
240+
241+
// CHECK: define [2 x i64] @f_ret_int_float_int_s()
242+
#[no_mangle]
243+
pub extern "C" fn f_ret_int_float_int_s() -> IntFloatInt {
244+
IntFloatInt { a: 1, b: 2., c: 3 }
245+
}
246+
247+
#[repr(C)]
248+
pub struct CharCharFloat {
249+
a: u8,
250+
b: u8,
251+
c: f32,
252+
}
253+
254+
// CHECK: define void @f_char_char_float_s_arg(i64)
255+
#[no_mangle]
256+
pub extern "C" fn f_char_char_float_s_arg(a: CharCharFloat) {}
257+
258+
// CHECK: define i64 @f_ret_char_char_float_s()
259+
#[no_mangle]
260+
pub extern "C" fn f_ret_char_char_float_s() -> CharCharFloat {
261+
CharCharFloat { a: 1, b: 2, c: 3. }
262+
}
263+
264+
#[repr(C)]
265+
pub union FloatU {
266+
a: f32,
267+
}
268+
269+
// CHECK: define void @f_float_u_arg(i64)
270+
#[no_mangle]
271+
pub extern "C" fn f_float_u_arg(a: FloatU) {}
272+
273+
// CHECK: define i64 @f_ret_float_u()
274+
#[no_mangle]
275+
pub extern "C" fn f_ret_float_u() -> FloatU {
276+
unsafe { FloatU { a: 1. } }
277+
}

‎src/test/ui/abi/struct-enums/struct-return.rs‎

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,23 @@ pub struct Quad { a: u64, b: u64, c: u64, d: u64 }
1010
#[derive(Copy, Clone)]
1111
pub struct Floats { a: f64, b: u8, c: f64 }
1212

13+
#[repr(C)]
14+
#[derive(Copy, Clone)]
15+
pub struct CharCharDouble { a: u8, b: u8, c: f64 }
16+
17+
#[repr(C)]
18+
#[derive(Copy, Clone)]
19+
pub struct CharCharFloat { a: u8, b: u8, c: f32 }
20+
1321
mod rustrt {
14-
use super::{Floats, Quad};
22+
use super::{Floats, Quad, CharCharDouble, CharCharFloat};
1523

1624
#[link(name = "rust_test_helpers", kind = "static")]
1725
extern {
1826
pub fn rust_dbg_abi_1(q: Quad) -> Quad;
1927
pub fn rust_dbg_abi_2(f: Floats) -> Floats;
28+
pub fn rust_dbg_abi_3(a: CharCharDouble) -> CharCharDouble;
29+
pub fn rust_dbg_abi_4(a: CharCharFloat) -> CharCharFloat;
2030
}
2131
}
2232

@@ -58,7 +68,47 @@ fn test2() {
5868
fn test2() {
5969
}
6070

71+
#[cfg(target_pointer_width = "64")]
72+
fn test3() {
73+
unsafe {
74+
let a = CharCharDouble {
75+
a: 1,
76+
b: 2,
77+
c: 3.,
78+
};
79+
let b = rustrt::rust_dbg_abi_3(a);
80+
println!("a: {}", b.a);
81+
println!("b: {}", b.b);
82+
println!("c: {}", b.c);
83+
assert_eq!(b.a, a.a + 1);
84+
assert_eq!(b.b, a.b - 1);
85+
assert_eq!(b.c, a.c + 1.0);
86+
}
87+
}
88+
89+
#[cfg(target_pointer_width = "32")]
90+
fn test3() {}
91+
92+
fn test4() {
93+
unsafe {
94+
let a = CharCharFloat {
95+
a: 1,
96+
b: 2,
97+
c: 3.,
98+
};
99+
let b = rustrt::rust_dbg_abi_4(a);
100+
println!("a: {}", b.a);
101+
println!("b: {}", b.b);
102+
println!("c: {}", b.c);
103+
assert_eq!(b.a, a.a + 1);
104+
assert_eq!(b.b, a.b - 1);
105+
assert_eq!(b.c, a.c + 1.0);
106+
}
107+
}
108+
61109
pub fn main() {
62110
test1();
63111
test2();
112+
test3();
113+
test4();
64114
}

0 commit comments

Comments
 (0)
Please sign in to comment.