Skip to content

Commit 6e1fd43

Browse files
committed
Block function bindings which pass extended floats by value
1 parent a7ff8e1 commit 6e1fd43

File tree

5 files changed

+306
-34
lines changed

5 files changed

+306
-34
lines changed

bindgen/codegen/helpers.rs

+17-27
Original file line numberDiff line numberDiff line change
@@ -189,37 +189,27 @@ pub mod ast_ty {
189189
fk: FloatKind,
190190
layout: Option<Layout>,
191191
) -> TokenStream {
192-
// TODO: we probably should take the type layout into account more
193-
// often?
194-
//
195-
// Also, maybe this one shouldn't be the default?
196-
match (fk, ctx.options().convert_floats) {
197-
(FloatKind::Float, true) => quote! { f32 },
198-
(FloatKind::Double, true) => quote! { f64 },
199-
(FloatKind::Float, false) => raw_type(ctx, "c_float"),
200-
(FloatKind::Double, false) => raw_type(ctx, "c_double"),
201-
(FloatKind::LongDouble, _) => {
202-
match layout {
203-
Some(layout) => {
204-
match layout.size {
205-
4 => quote! { f32 },
206-
8 => quote! { f64 },
207-
// TODO(emilio): If rust ever gains f128 we should
208-
// use it here and below.
209-
_ => super::integer_type(ctx, layout)
210-
.unwrap_or(quote! { f64 }),
211-
}
192+
let bits = layout.map(|l| l.size * 8);
193+
match (fk, ctx.options().convert_floats, bits) {
194+
// TODO: What about narrower floats?
195+
(_, true, Some(32)) => quote! { f32 },
196+
(_, true, Some(64)) => quote! { f64 },
197+
(FloatKind::Float, ..) => raw_type(ctx, "c_float"),
198+
(FloatKind::Double, ..) => raw_type(ctx, "c_double"),
199+
(FloatKind::LongDouble, ..) => {
200+
// TODO(emilio): If rust ever gains f80/f128 we should
201+
// use it here and below.
202+
if ctx.options().enable_cxx_namespaces {
203+
quote! {
204+
root::__BindgenLongDouble
212205
}
213-
None => {
214-
debug_assert!(
215-
false,
216-
"How didn't we know the layout for a primitive type?"
217-
);
218-
quote! { f64 }
206+
} else {
207+
quote! {
208+
__BindgenLongDouble
219209
}
220210
}
221211
}
222-
(FloatKind::Float128, _) => {
212+
(FloatKind::Float128, ..) => {
223213
if ctx.options().rust_features.i128_and_u128 {
224214
quote! { u128 }
225215
} else {

bindgen/codegen/mod.rs

+28
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ struct CodegenResult<'a> {
217217
/// Whether a bitfield allocation unit has been seen at least once.
218218
saw_bitfield_unit: bool,
219219

220+
/// Whether a long double has been seen at least once.
221+
saw_long_double: bool,
222+
220223
items_seen: HashSet<ItemId>,
221224
/// The set of generated function/var names, needed because in C/C++ is
222225
/// legal to do something like:
@@ -253,6 +256,7 @@ impl<'a> CodegenResult<'a> {
253256
saw_objc: false,
254257
saw_block: false,
255258
saw_bitfield_unit: false,
259+
saw_long_double: false,
256260
codegen_id,
257261
items_seen: Default::default(),
258262
functions_seen: Default::default(),
@@ -285,6 +289,10 @@ impl<'a> CodegenResult<'a> {
285289
self.saw_bitfield_unit = true;
286290
}
287291

292+
fn saw_long_double(&mut self) {
293+
self.saw_long_double = true;
294+
}
295+
288296
fn seen<Id: Into<ItemId>>(&self, item: Id) -> bool {
289297
self.items_seen.contains(&item.into())
290298
}
@@ -2454,6 +2462,10 @@ impl Method {
24542462
_ => panic!("How in the world?"),
24552463
};
24562464

2465+
if utils::sig_unsupported_types(ctx, signature) {
2466+
return;
2467+
}
2468+
24572469
let supported_abi = match signature.abi(ctx, Some(&*name)) {
24582470
ClangAbi::Known(Abi::ThisCall) => {
24592471
ctx.options().rust_features().thiscall_abi
@@ -4107,6 +4119,11 @@ impl CodeGenerator for Function {
41074119
abi => abi,
41084120
};
41094121

4122+
if utils::sig_unsupported_types(ctx, signature) {
4123+
warn!("Skipping function which passes or returns by value types not available in Rust.");
4124+
return None;
4125+
}
4126+
41104127
// Handle overloaded functions by giving each overload its own unique
41114128
// suffix.
41124129
let times_seen = result.overload_number(&canonical_name);
@@ -5058,4 +5075,15 @@ pub mod utils {
50585075

50595076
true
50605077
}
5078+
5079+
pub fn sig_unsupported_types(
5080+
ctx: &BindgenContext,
5081+
sig: &FunctionSig
5082+
) -> bool {
5083+
sig.argument_types().iter()
5084+
.map(|(_, ty_id)| ty_id)
5085+
.chain(std::iter::once(&sig.return_type()))
5086+
.find(|ty_id| ctx.lookup_never_by_value(*ty_id))
5087+
.is_some()
5088+
}
50615089
}

bindgen/ir/analysis/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ mod has_type_param_in_array;
5050
pub use self::has_type_param_in_array::HasTypeParameterInArray;
5151
mod has_float;
5252
pub use self::has_float::HasFloat;
53+
mod never_by_value;
54+
pub use self::never_by_value::NeverByValue;
5355
mod sizedness;
5456
pub use self::sizedness::{Sizedness, SizednessAnalysis, SizednessResult};
5557

bindgen/ir/analysis/never_by_value.rs

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
//! Determining which types cannot be passed by value.
2+
3+
use super::{generate_dependencies, ConstrainResult, MonotoneFramework};
4+
use crate::ir::comp::Field;
5+
use crate::ir::comp::FieldMethods;
6+
use crate::ir::context::{BindgenContext, ItemId};
7+
use crate::ir::traversal::EdgeKind;
8+
use crate::ir::ty::TypeKind;
9+
use crate::{HashMap, HashSet};
10+
11+
/// An analysis that finds each IR item which is a type which cannot be passed by value in Rust.
12+
///
13+
/// This is defined as follows:
14+
///
15+
/// * If T is float or complex float with size not 32 or 64 bit (twice that for complex), it cannot
16+
/// be passed by value.
17+
/// * If T is a type alias, a templated alias or an indirection to another type,
18+
/// it matches if the type T refers to does
19+
/// * If T is a compound type, it matches if any of base memter or field does.
20+
/// * If T is an instantiation of an abstract template definition, T matches if any of the template
21+
/// arguments or template definition do.
22+
///
23+
/// A more advanced implementation might be aware of which registers arguments will actually end up
24+
/// in and permit some signatures this rejects on a platform-specific basis.
25+
#[derive(Debug, Clone)]
26+
pub struct NeverByValue<'ctx> {
27+
ctx: &'ctx BindgenContext,
28+
29+
// The incremental result of this analysis's computation. Everything in this
30+
// set has float.
31+
never_by_value: HashSet<ItemId>,
32+
33+
// Dependencies saying that if a key ItemId has been inserted into the
34+
// `never_by_value` set, then each of the ids in Vec<ItemId> need to be
35+
// considered again.
36+
//
37+
// This is a subset of the natural IR graph with reversed edges, where we
38+
// only include the edges from the IR graph that can affect whether a type
39+
// has float or not.
40+
dependencies: HashMap<ItemId, Vec<ItemId>>,
41+
}
42+
43+
impl<'ctx> NeverByValue<'ctx> {
44+
fn consider_edge(kind: EdgeKind) -> bool {
45+
match kind {
46+
EdgeKind::BaseMember |
47+
EdgeKind::Field |
48+
EdgeKind::TypeReference |
49+
EdgeKind::VarType |
50+
EdgeKind::TemplateArgument |
51+
EdgeKind::TemplateDeclaration |
52+
EdgeKind::TemplateParameterDefinition => true,
53+
54+
EdgeKind::Constructor |
55+
EdgeKind::Destructor |
56+
EdgeKind::FunctionReturn |
57+
EdgeKind::FunctionParameter |
58+
EdgeKind::InnerType |
59+
EdgeKind::InnerVar |
60+
EdgeKind::Method => false,
61+
EdgeKind::Generic => false,
62+
}
63+
}
64+
65+
fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
66+
let id = id.into();
67+
info!("inserting {id:?} into the never_by_value set");
68+
69+
let was_not_already_in_set = self.never_by_value.insert(id);
70+
assert!(
71+
was_not_already_in_set,
72+
"We shouldn't try and insert {:?} twice because if it was \
73+
already in the set, `constrain` should have exited early.",
74+
id
75+
);
76+
77+
ConstrainResult::Changed
78+
}
79+
}
80+
81+
impl<'ctx> MonotoneFramework for NeverByValue<'ctx> {
82+
type Node = ItemId;
83+
type Extra = &'ctx BindgenContext;
84+
type Output = HashSet<ItemId>;
85+
86+
fn new(ctx: &'ctx BindgenContext) -> Self {
87+
let never_by_value = HashSet::default();
88+
let dependencies = generate_dependencies(ctx, Self::consider_edge);
89+
90+
NeverByValue {
91+
ctx,
92+
never_by_value,
93+
dependencies,
94+
}
95+
}
96+
97+
fn initial_worklist(&self) -> Vec<ItemId> {
98+
self.ctx.allowlisted_items().iter().cloned().collect()
99+
}
100+
101+
fn constrain(&mut self, id: ItemId) -> ConstrainResult {
102+
info!("constrain: {id:?}");
103+
104+
if self.never_by_value.contains(&id) {
105+
info!(" already in set");
106+
return ConstrainResult::Same;
107+
}
108+
109+
let item = self.ctx.resolve_item(id);
110+
if let Some(ty) = item.kind().as_type() {
111+
match *ty.kind() {
112+
TypeKind::Void |
113+
TypeKind::NullPtr |
114+
TypeKind::Int(..) |
115+
TypeKind::Function(..) |
116+
TypeKind::Enum(..) |
117+
TypeKind::Reference(..) |
118+
TypeKind::TypeParam |
119+
TypeKind::Opaque |
120+
TypeKind::Pointer(..) |
121+
TypeKind::UnresolvedTypeRef(..) |
122+
TypeKind::ObjCInterface(..) |
123+
TypeKind::ObjCId |
124+
TypeKind::ObjCSel |
125+
TypeKind::BlockPointer(_) => {
126+
info!(" simple type that is not float");
127+
ConstrainResult::Same
128+
}
129+
130+
TypeKind::Float(..) | TypeKind::Complex(..) => {
131+
let size = ty.layout(self.ctx).expect("float with unknown layout").size;
132+
match (ty.kind(), size) {
133+
(TypeKind::Float(..), 4 | 8) | (TypeKind::Complex(..), 8 | 16) => {
134+
info!(" skipped f32 or f64");
135+
ConstrainResult::Same
136+
}
137+
_ => {
138+
info!(" extended float size {size}");
139+
self.insert(id)
140+
}
141+
}
142+
}
143+
144+
TypeKind::Alias(t) |
145+
TypeKind::Array(t, _) |
146+
TypeKind::ResolvedTypeRef(t) |
147+
TypeKind::TemplateAlias(t, _) |
148+
TypeKind::Vector(t, _) => {
149+
if self.never_by_value.contains(&t.into()) {
150+
info!(" contains/aliases matching type, so matches");
151+
self.insert(id)
152+
} else {
153+
info!(" does not contain/alias matching type");
154+
ConstrainResult::Same
155+
}
156+
}
157+
158+
TypeKind::Comp(ref info) => {
159+
let bases_have = info
160+
.base_members()
161+
.iter()
162+
.any(|base| self.never_by_value.contains(&base.ty.into()));
163+
if bases_have {
164+
info!(" bases have float, so we also have");
165+
return self.insert(id);
166+
}
167+
let fields_have = info.fields().iter().any(|f| match *f {
168+
Field::DataMember(ref data) => {
169+
self.never_by_value.contains(&data.ty().into())
170+
}
171+
Field::Bitfields(ref bfu) => bfu
172+
.bitfields()
173+
.iter()
174+
.any(|b| self.never_by_value.contains(&b.ty().into())),
175+
});
176+
if fields_have {
177+
info!(" fields have float, so we also have");
178+
return self.insert(id);
179+
}
180+
181+
info!(" comp doesn't have float");
182+
ConstrainResult::Same
183+
}
184+
185+
TypeKind::TemplateInstantiation(ref template) => {
186+
let args_have = template
187+
.template_arguments()
188+
.iter()
189+
.any(|arg| self.never_by_value.contains(&arg.into()));
190+
if args_have {
191+
info!(" template args match, so instantiation also matches");
192+
return self.insert(id);
193+
}
194+
195+
let def_has = self
196+
.never_by_value
197+
.contains(&template.template_definition().into());
198+
if def_has {
199+
info!(" template definition has float, so instantiation also has");
200+
return self.insert(id);
201+
}
202+
203+
info!(" template instantiation does not match");
204+
ConstrainResult::Same
205+
}
206+
}
207+
} else {
208+
info!(" not a type; skipped");
209+
ConstrainResult::Same
210+
}
211+
}
212+
213+
fn each_depending_on<F>(&self, id: ItemId, mut f: F)
214+
where
215+
F: FnMut(ItemId),
216+
{
217+
if let Some(edges) = self.dependencies.get(&id) {
218+
for item in edges {
219+
info!("enqueue {item:?} into worklist");
220+
f(*item);
221+
}
222+
}
223+
}
224+
}
225+
226+
impl<'ctx> From<NeverByValue<'ctx>> for HashSet<ItemId> {
227+
fn from(analysis: NeverByValue<'ctx>) -> Self {
228+
analysis.never_by_value
229+
}
230+
}

0 commit comments

Comments
 (0)