Skip to content

Commit 338fbbb

Browse files
committed
Hack for supporting MSVC x64 instance method calling convention until Rust compiler supports it natively
Instance methods like `ReturnType myMethod(T arg1, U arg2)` where ReturnType is non trivial are passed in MSVC as: rcx - this rdx - address of return value r8, r9 - arg1, arg2 But the extern "C" abi passes arguments to a function `ReturnType myFunction(ThisType* this, T arg1, U arg2)` as follows: rcx - address of return value rdx - this r8, r9 - arg1, arg2 The MSVC convention for x64 is similar to x86 thiscall. Thus, we can rewrite the methods returning non-trivial values to be functions returning void, passing the return value via the second argument (after the this pointer). This will enable them to be called correctly with the new signature using extern "C" abi. See <rust-lang/rust#38258>
1 parent 8a77ca0 commit 338fbbb

File tree

1 file changed

+78
-6
lines changed

1 file changed

+78
-6
lines changed

src/codegen/mod.rs

+78-6
Original file line numberDiff line numberDiff line change
@@ -2035,6 +2035,12 @@ impl MethodCodegen for Method {
20352035
let function_name = ctx.rust_ident(function_item.canonical_name(ctx));
20362036
let mut args = utils::fnsig_arguments(ctx, signature);
20372037
let mut ret = utils::fnsig_return_ty(ctx, signature);
2038+
let msvc_hack = match self.kind() {
2039+
MethodKind::Normal | MethodKind::Virtual {..} => {
2040+
utils::fnsig_needs_msvc_thiscall(ctx, signature)
2041+
},
2042+
_ => false
2043+
};
20382044

20392045
if !self.is_static() && !self.is_constructor() {
20402046
args[0] = if self.is_const() {
@@ -2078,6 +2084,16 @@ impl MethodCodegen for Method {
20782084
};
20792085
};
20802086

2087+
if msvc_hack {
2088+
let prefix = ctx.trait_prefix();
2089+
stmts.push(quote! {
2090+
let mut __bindgen_msvc_hack_ret = ::#prefix::mem::uninitialized()
2091+
});
2092+
exprs.insert(1, quote! {
2093+
&mut __bindgen_msvc_hack_ret
2094+
});
2095+
}
2096+
20812097
let call = quote! {
20822098
#function_name (#( #exprs ),* )
20832099
};
@@ -2090,6 +2106,12 @@ impl MethodCodegen for Method {
20902106
});
20912107
}
20922108

2109+
if msvc_hack {
2110+
stmts.push(quote! {
2111+
__bindgen_msvc_hack_ret
2112+
});
2113+
}
2114+
20932115
let block = quote! {
20942116
#( #stmts );*
20952117
};
@@ -3212,6 +3234,32 @@ impl CodeGenerator for Function {
32123234
let args = utils::fnsig_arguments(ctx, signature);
32133235
let ret = utils::fnsig_return_ty(ctx, signature);
32143236

3237+
// MSVC hack only applies to instance methods w/ non-trivial return value.
3238+
let msvc_hack = match self.kind() {
3239+
FunctionKind::Method(ref method_kind) => {
3240+
match *method_kind {
3241+
MethodKind::Normal | MethodKind::Virtual {..} => {
3242+
utils::fnsig_needs_msvc_thiscall(ctx, signature)
3243+
},
3244+
_ => false
3245+
}
3246+
},
3247+
_ => false
3248+
};
3249+
3250+
let (args, ret) = if msvc_hack {
3251+
let mut args = args.clone();
3252+
let ret_ty = utils::fnsig_return_ty_noarrow(ctx, signature);
3253+
assert!(args.len() >= 1, "instance method must have at least one arg (this) {:?}",
3254+
args);
3255+
args.insert(1, quote! {
3256+
__bindgen_msvc_hack_ret: *mut #ret_ty
3257+
});
3258+
(args, quote! {})
3259+
} else {
3260+
(args, ret)
3261+
};
3262+
32153263
let mut attributes = vec![];
32163264

32173265
if let Some(comment) = item.comment(ctx) {
@@ -3687,18 +3735,42 @@ mod utils {
36873735
})
36883736
}
36893737

3690-
pub fn fnsig_return_ty(
3738+
pub fn fnsig_return_ty_noarrow(
36913739
ctx: &BindgenContext,
36923740
sig: &FunctionSig,
3693-
) -> quote::Tokens {
3741+
) -> Option<quote::Tokens> {
36943742
let return_item = ctx.resolve_item(sig.return_type());
36953743
if let TypeKind::Void = *return_item.kind().expect_type().kind() {
3696-
quote! { }
3744+
None
36973745
} else {
3698-
let ret_ty = return_item.to_rust_ty_or_opaque(ctx, &());
3699-
quote! {
3700-
-> #ret_ty
3746+
Some(return_item.to_rust_ty_or_opaque(ctx, &()))
3747+
}
3748+
}
3749+
3750+
pub fn fnsig_return_ty(
3751+
ctx: &BindgenContext,
3752+
sig: &FunctionSig,
3753+
) -> quote::Tokens {
3754+
let ret_ty = fnsig_return_ty_noarrow(ctx, sig);
3755+
match ret_ty {
3756+
Some(r) => quote! { -> #r },
3757+
None => quote! {}
3758+
}
3759+
}
3760+
3761+
pub fn fnsig_needs_msvc_thiscall(
3762+
ctx: &BindgenContext,
3763+
sig: &FunctionSig,
3764+
) -> bool {
3765+
if cfg!(target_env = "msvc") {
3766+
let return_type = ctx.resolve_item(sig.return_type()).expect_type();
3767+
if let TypeKind::Comp(..) = *return_type.safe_canonical_type(ctx).unwrap().kind() {
3768+
true
3769+
} else {
3770+
false
37013771
}
3772+
} else {
3773+
false
37023774
}
37033775
}
37043776

0 commit comments

Comments
 (0)