Skip to content

Commit 9962084

Browse files
committed
Implement passing arguments by ref for win64 ABI
1 parent 9e70a64 commit 9962084

File tree

6 files changed

+132
-10
lines changed

6 files changed

+132
-10
lines changed

cranelift/codegen/src/abi.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ pub enum ValueConversion {
5454

5555
/// Unsigned zero-extend value to the required type.
5656
Uext(Type),
57+
58+
/// Pass value by pointer of given integer type.
59+
Pointer(Type),
5760
}
5861

5962
impl ValueConversion {
@@ -63,7 +66,7 @@ impl ValueConversion {
6366
Self::IntSplit => ty.half_width().expect("Integer type too small to split"),
6467
Self::VectorSplit => ty.half_vector().expect("Not a vector"),
6568
Self::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
66-
Self::Sext(nty) | Self::Uext(nty) => nty,
69+
Self::Sext(nty) | Self::Uext(nty) | Self::Pointer(nty) => nty,
6770
}
6871
}
6972

@@ -74,6 +77,11 @@ impl ValueConversion {
7477
_ => false,
7578
}
7679
}
80+
81+
/// Is this a conversion to pointer?
82+
pub fn is_pointer(self) -> bool {
83+
matches!(self, Self::Pointer(_))
84+
}
7785
}
7886

7987
/// Common trait for assigning arguments to registers or stack locations.
@@ -110,10 +118,16 @@ pub fn legalize_args<AA: ArgAssigner>(args: &[AbiParam], aa: &mut AA) -> Option<
110118
}
111119
// Split this argument into two smaller ones. Then revisit both.
112120
ArgAction::Convert(conv) => {
121+
debug_assert!(
122+
!arg.legalized_to_pointer,
123+
"No more conversions allowed after conversion to pointer"
124+
);
113125
let value_type = conv.apply(arg.value_type);
114-
let new_arg = AbiParam { value_type, ..arg };
115126
args.to_mut()[argno].value_type = value_type;
116-
if conv.is_split() {
127+
if conv.is_pointer() {
128+
args.to_mut()[argno].legalized_to_pointer = true;
129+
} else if conv.is_split() {
130+
let new_arg = AbiParam { value_type, ..arg };
117131
args.to_mut().insert(argno + 1, new_arg);
118132
}
119133
}
@@ -152,6 +166,10 @@ pub fn legalize_abi_value(have: Type, arg: &AbiParam) -> ValueConversion {
152166
let have_bits = have.bits();
153167
let arg_bits = arg.value_type.bits();
154168

169+
if arg.legalized_to_pointer {
170+
return ValueConversion::Pointer(arg.value_type);
171+
}
172+
155173
match have_bits.cmp(&arg_bits) {
156174
// We have fewer bits than the ABI argument.
157175
Ordering::Less => {
@@ -226,5 +244,12 @@ mod tests {
226244
legalize_abi_value(types::F64, &arg),
227245
ValueConversion::IntBits
228246
);
247+
248+
// Value is passed by reference
249+
arg.legalized_to_pointer = true;
250+
assert_eq!(
251+
legalize_abi_value(types::F64, &arg),
252+
ValueConversion::Pointer(types::I32)
253+
);
229254
}
230255
}

cranelift/codegen/src/ir/extfunc.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ pub struct AbiParam {
156156
/// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet
157157
/// been legalized.
158158
pub location: ArgumentLoc,
159+
/// Was the argument converted to pointer during legalization?
160+
pub legalized_to_pointer: bool,
159161
}
160162

161163
impl AbiParam {
@@ -166,6 +168,7 @@ impl AbiParam {
166168
extension: ArgumentExtension::None,
167169
purpose: ArgumentPurpose::Normal,
168170
location: Default::default(),
171+
legalized_to_pointer: false,
169172
}
170173
}
171174

@@ -176,6 +179,7 @@ impl AbiParam {
176179
extension: ArgumentExtension::None,
177180
purpose,
178181
location: Default::default(),
182+
legalized_to_pointer: false,
179183
}
180184
}
181185

@@ -186,6 +190,7 @@ impl AbiParam {
186190
extension: ArgumentExtension::None,
187191
purpose,
188192
location: ArgumentLoc::Reg(regunit),
193+
legalized_to_pointer: false,
189194
}
190195
}
191196

@@ -219,6 +224,9 @@ pub struct DisplayAbiParam<'a>(&'a AbiParam, Option<&'a RegInfo>);
219224
impl<'a> fmt::Display for DisplayAbiParam<'a> {
220225
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221226
write!(f, "{}", self.0.value_type)?;
227+
if self.0.legalized_to_pointer {
228+
write!(f, " ptr")?;
229+
}
222230
match self.0.extension {
223231
ArgumentExtension::None => {}
224232
ArgumentExtension::Uext => write!(f, " uext")?,
@@ -415,6 +423,8 @@ mod tests {
415423
assert_eq!(t.sext().to_string(), "i32 sext");
416424
t.purpose = ArgumentPurpose::StructReturn;
417425
assert_eq!(t.to_string(), "i32 uext sret");
426+
t.legalized_to_pointer = true;
427+
assert_eq!(t.to_string(), "i32 ptr uext sret");
418428
}
419429

420430
#[test]

cranelift/codegen/src/isa/x86/abi.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct Args {
7070
shared_flags: shared_settings::Flags,
7171
#[allow(dead_code)]
7272
isa_flags: isa_settings::Flags,
73+
is_rets: bool,
7374
}
7475

7576
impl Args {
@@ -80,6 +81,7 @@ impl Args {
8081
call_conv: CallConv,
8182
shared_flags: &shared_settings::Flags,
8283
isa_flags: &isa_settings::Flags,
84+
is_rets: bool,
8385
) -> Self {
8486
let offset = if call_conv.extends_windows_fastcall() {
8587
WIN_SHADOW_STACK_SPACE
@@ -99,6 +101,7 @@ impl Args {
99101
call_conv,
100102
shared_flags: shared_flags.clone(),
101103
isa_flags: isa_flags.clone(),
104+
is_rets,
102105
}
103106
}
104107
}
@@ -107,6 +110,17 @@ impl ArgAssigner for Args {
107110
fn assign(&mut self, arg: &AbiParam) -> ArgAction {
108111
let ty = arg.value_type;
109112

113+
if ty.bits() > u16::from(self.pointer_bits) {
114+
if !self.is_rets && self.call_conv.extends_windows_fastcall() {
115+
// "Any argument that doesn't fit in 8 bytes, or isn't
116+
// 1, 2, 4, or 8 bytes, must be passed by reference"
117+
return ValueConversion::Pointer(self.pointer_type).into();
118+
} else if !ty.is_vector() && !ty.is_float() {
119+
// On SystemV large integers and booleans are broken down to fit in a register.
120+
return ValueConversion::IntSplit.into();
121+
}
122+
}
123+
110124
// Vectors should stay in vector registers unless SIMD is not enabled--then they are split
111125
if ty.is_vector() {
112126
if self.shared_flags.enable_simd() {
@@ -117,11 +131,6 @@ impl ArgAssigner for Args {
117131
return ValueConversion::VectorSplit.into();
118132
}
119133

120-
// Large integers and booleans are broken down to fit in a register.
121-
if !ty.is_float() && ty.bits() > u16::from(self.pointer_bits) {
122-
return ValueConversion::IntSplit.into();
123-
}
124-
125134
// Small integers are extended to the size of a pointer register.
126135
if ty.is_int() && ty.bits() < u16::from(self.pointer_bits) {
127136
match arg.extension {
@@ -203,7 +212,7 @@ pub fn legalize_signature(
203212
PointerWidth::U16 => panic!(),
204213
PointerWidth::U32 => {
205214
bits = 32;
206-
args = Args::new(bits, &[], 0, sig.call_conv, shared_flags, isa_flags);
215+
args = Args::new(bits, &[], 0, sig.call_conv, shared_flags, isa_flags, false);
207216
}
208217
PointerWidth::U64 => {
209218
bits = 64;
@@ -215,6 +224,7 @@ pub fn legalize_signature(
215224
sig.call_conv,
216225
shared_flags,
217226
isa_flags,
227+
false,
218228
)
219229
} else {
220230
Args::new(
@@ -224,6 +234,7 @@ pub fn legalize_signature(
224234
sig.call_conv,
225235
shared_flags,
226236
isa_flags,
237+
false,
227238
)
228239
};
229240
}
@@ -243,6 +254,7 @@ pub fn legalize_signature(
243254
sig.call_conv,
244255
shared_flags,
245256
isa_flags,
257+
true,
246258
);
247259

248260
// If we don't have enough available return registers
@@ -267,6 +279,7 @@ pub fn legalize_signature(
267279
purpose: ArgumentPurpose::StructReturn,
268280
extension: ArgumentExtension::None,
269281
location: ArgumentLoc::Unassigned,
282+
legalized_to_pointer: false,
270283
};
271284
match args.assign(&ret_ptr_param) {
272285
ArgAction::Assign(ArgumentLoc::Reg(reg)) => {
@@ -283,6 +296,7 @@ pub fn legalize_signature(
283296
purpose: ArgumentPurpose::StructReturn,
284297
extension: ArgumentExtension::None,
285298
location: ArgumentLoc::Unassigned,
299+
legalized_to_pointer: false,
286300
};
287301
match backup_rets.assign(&ret_ptr_return) {
288302
ArgAction::Assign(ArgumentLoc::Reg(reg)) => {

cranelift/codegen/src/legalizer/boundary.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,13 @@ where
504504
// this value.
505505
pos.ins().with_results([into_result]).ireduce(ty, arg)
506506
}
507+
// ABI argument is a pointer to the value we want.
508+
ValueConversion::Pointer(abi_ty) => {
509+
let arg = convert_from_abi(pos, abi_ty, None, get_arg);
510+
pos.ins()
511+
.with_results([into_result])
512+
.load(ty, MemFlags::new(), arg, 0)
513+
}
507514
}
508515
}
509516

@@ -563,6 +570,18 @@ fn convert_to_abi<PutArg>(
563570
let arg = pos.ins().uextend(abi_ty, value);
564571
convert_to_abi(pos, cfg, arg, put_arg);
565572
}
573+
ValueConversion::Pointer(abi_ty) => {
574+
// Note: This conversion can only happen for call arguments,
575+
// so we can allocate the value on stack safely.
576+
let stack_slot = pos.func.create_stack_slot(StackSlotData {
577+
kind: StackSlotKind::ExplicitSlot,
578+
size: ty.bytes(),
579+
offset: None,
580+
});
581+
let arg = pos.ins().stack_addr(abi_ty, stack_slot, 0);
582+
pos.ins().store(MemFlags::new(), value, arg, 0);
583+
convert_to_abi(pos, cfg, arg, put_arg);
584+
}
566585
}
567586
}
568587

@@ -815,7 +834,12 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph
815834
pos.use_srcloc(inst);
816835

817836
legalize_inst_arguments(pos, cfg, abi_args, |func, abi_arg| {
818-
func.signature.returns[abi_arg]
837+
let arg = func.signature.returns[abi_arg];
838+
debug_assert!(
839+
!arg.legalized_to_pointer,
840+
"Return value cannot be legalized to pointer"
841+
);
842+
arg
819843
});
820844
// Append special return arguments for any `sret`, `link`, and `vmctx` return values added to
821845
// the legalized signature. These values should simply be propagated from the entry block

cranelift/filetests/filetests/isa/x86/windows_fastcall_x64.clif

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,20 @@ block0(v0: i64, v1: i64):
117117
}
118118
; check: function %ret_val_i128(i64 [%rdx], i64 [%r8], i64 sret [%rcx], i64 fp [%rbp]) -> i64 sret [%rax], i64 fp [%rbp] windows_fastcall {
119119

120+
; check if i128 is passed by reference
121+
function %i128_arg(i128) windows_fastcall {
122+
block0(v0: i128):
123+
return
124+
}
125+
; check: function %i128_arg(i64 ptr [%rcx], i64 fp [%rbp]) -> i64 fp [%rbp] windows_fastcall {
126+
127+
; check if vector types are passed by reference
128+
function %i32x4_arg(i32x4) windows_fastcall {
129+
block0(v0: i32x4):
130+
return
131+
}
132+
; check: function %i32x4_arg(i64 ptr [%rcx], i64 fp [%rbp]) -> i64 fp [%rbp] windows_fastcall {
133+
120134
function %internal_stack_arg_function_call(i64) -> i64 windows_fastcall {
121135
fn0 = %foo(i64, i64, i64, i64) -> i64 windows_fastcall
122136
fn1 = %foo2(i64, i64, i64, i64) -> i64 windows_fastcall
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
test compile
2+
set opt_level=speed_and_size
3+
set is_pic
4+
target x86_64 haswell
5+
6+
function %legalize_entry(i128) -> i64 windows_fastcall {
7+
block0(v0: i128):
8+
v1, v2 = isplit v0
9+
return v2
10+
}
11+
; check: function %legalize_entry(i64 ptr [%rcx], i64 fp [%rbp]) -> i64 [%rax], i64 fp [%rbp] windows_fastcall {
12+
; check: block0(v3: i64 [%rcx], v6: i64 [%rbp]):
13+
; nextln: x86_push v6
14+
; nextln: copy_special %rsp -> %rbp
15+
; nextln: v4 = load.i64 v3
16+
; nextln: v1 -> v4
17+
; nextln: v5 = load.i64 v3+8
18+
; nextln: v2 -> v5
19+
; nextln: v7 = x86_pop.i64
20+
; nextln: return v5, v7
21+
22+
function %legalize_call() {
23+
fn0 = %foo(i32x4) windows_fastcall
24+
block0:
25+
v0 = vconst.i32x4 [1 2 3 4]
26+
call fn0(v0)
27+
return
28+
}
29+
; check: ss0 = explicit_slot 16, offset -32
30+
; check: sig0 = (i64 ptr [%rcx]) windows_fastcall
31+
; check: v0 = vconst.i32x4 const0
32+
; nextln: v1 = stack_addr.i64 ss0
33+
; nextln: store v0, v1
34+
; nextln: regmove v1, %rax -> %rcx
35+
; nextln: call fn0(v1)

0 commit comments

Comments
 (0)