Skip to content

codegen: Use target pointer size consistently for layout calculations #1289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 3, 2018
42 changes: 23 additions & 19 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,33 @@ env:
global:
- CARGO_TARGET_DIR=/tmp/bindgen
matrix:
- LLVM_VERSION="3.8.1" BINDGEN_JOB="test" BINDGEN_PROFILE=
- LLVM_VERSION="3.8.1" BINDGEN_JOB="test" BINDGEN_PROFILE="--release"
- LLVM_VERSION="3.8.1" BINDGEN_JOB="integration" BINDGEN_PROFILE=
- LLVM_VERSION="3.8.1" BINDGEN_JOB="integration" BINDGEN_PROFILE="--release"
- LLVM_VERSION="3.9.0" BINDGEN_JOB="test" BINDGEN_PROFILE=
- LLVM_VERSION="3.9.0" BINDGEN_JOB="test" BINDGEN_PROFILE="--release"
- LLVM_VERSION="3.9.0" BINDGEN_JOB="integration" BINDGEN_PROFILE=
- LLVM_VERSION="3.9.0" BINDGEN_JOB="integration" BINDGEN_PROFILE="--release"
- LLVM_VERSION="4.0.0" BINDGEN_JOB="test" BINDGEN_PROFILE=
- LLVM_VERSION="4.0.0" BINDGEN_JOB="test" BINDGEN_PROFILE="--release"
- LLVM_VERSION="4.0.0" BINDGEN_JOB="test" BINDGEN_PROFILE= BINDGEN_FEATURES="testing_only_extra_assertions"
- LLVM_VERSION="4.0.0" BINDGEN_JOB="test" BINDGEN_PROFILE="--release" BINDGEN_FEATURES="testing_only_extra_assertions"
- LLVM_VERSION="4.0.0" BINDGEN_JOB="integration" BINDGEN_PROFILE=
- LLVM_VERSION="4.0.0" BINDGEN_JOB="integration" BINDGEN_PROFILE="--release"
- LLVM_VERSION="4.0.0" BINDGEN_JOB="expectations" BINDGEN_PROFILE=
- LLVM_VERSION="4.0.0" BINDGEN_JOB="expectations" BINDGEN_PROFILE="--release"
- LLVM_VERSION="4.0.0" BINDGEN_JOB="misc"
- LLVM_VERSION="4.0.0" BINDGEN_JOB="quickchecking"
- LLVM_VERSION="3.8" BINDGEN_JOB="test" BINDGEN_PROFILE=
- LLVM_VERSION="3.8" BINDGEN_JOB="test" BINDGEN_PROFILE="--release"
- LLVM_VERSION="3.8" BINDGEN_JOB="integration" BINDGEN_PROFILE=
- LLVM_VERSION="3.8" BINDGEN_JOB="integration" BINDGEN_PROFILE="--release"
- LLVM_VERSION="3.9" BINDGEN_JOB="test" BINDGEN_PROFILE=
- LLVM_VERSION="3.9" BINDGEN_JOB="test" BINDGEN_PROFILE="--release"
- LLVM_VERSION="3.9" BINDGEN_JOB="integration" BINDGEN_PROFILE=
- LLVM_VERSION="3.9" BINDGEN_JOB="integration" BINDGEN_PROFILE="--release"
- LLVM_VERSION="4.0" BINDGEN_JOB="test" BINDGEN_PROFILE=
- LLVM_VERSION="4.0" BINDGEN_JOB="test" BINDGEN_PROFILE="--release"
- LLVM_VERSION="4.0" BINDGEN_JOB="integration" BINDGEN_PROFILE=
- LLVM_VERSION="4.0" BINDGEN_JOB="integration" BINDGEN_PROFILE="--release"
- LLVM_VERSION="5.0" BINDGEN_JOB="test" BINDGEN_PROFILE=
- LLVM_VERSION="5.0" BINDGEN_JOB="test" BINDGEN_PROFILE="--release"
- LLVM_VERSION="5.0" BINDGEN_JOB="test" BINDGEN_PROFILE= BINDGEN_FEATURES="testing_only_extra_assertions"
- LLVM_VERSION="5.0" BINDGEN_JOB="test" BINDGEN_PROFILE="--release" BINDGEN_FEATURES="testing_only_extra_assertions"
- LLVM_VERSION="5.0" BINDGEN_JOB="integration" BINDGEN_PROFILE=
- LLVM_VERSION="5.0" BINDGEN_JOB="integration" BINDGEN_PROFILE="--release"
- LLVM_VERSION="5.0" BINDGEN_JOB="expectations" BINDGEN_PROFILE=
- LLVM_VERSION="5.0" BINDGEN_JOB="expectations" BINDGEN_PROFILE="--release"
- LLVM_VERSION="5.0" BINDGEN_JOB="misc"
- LLVM_VERSION="5.0" BINDGEN_JOB="quickchecking"

matrix:
fast_finish: true
allow_failures:
- env: LLVM_VERSION=4.0.0 BINDGEN_JOB=rustfmt
- env: LLVM_VERSION=5.0 BINDGEN_JOB=rustfmt

cache:
directories:
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ cexpr = "0.2"
cfg-if = "0.1.0"
# This kinda sucks: https://github.com/rust-lang/cargo/issues/1982
clap = "2"
clang-sys = { version = "0.22.0", features = ["runtime", "clang_3_9"] }
clang-sys = { version = "0.22.0", features = ["runtime", "clang_6_0"] }
lazy_static = "1"
peeking_take_while = "0.1.2"
quote = "0.3.15"
Expand All @@ -71,6 +71,7 @@ static = []
# on bindgen!
testing_only_docs = []
testing_only_extra_assertions = []
testing_only_libclang_5 = []
testing_only_libclang_4 = []
testing_only_libclang_3_9 = []
testing_only_libclang_3_8 = []
6 changes: 6 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ environment:
- TARGET: gnu
LLVM_VERSION: 4.0.0-1
BINDGEN_FEATURES: testing_only_libclang_4
- TARGET: msvc
LLVM_VERSION: 5.0.0-1
BINDGEN_FEATURES: testing_only_libclang_5
- TARGET: msvc
LLVM_VERSION: 3.9.0
BINDGEN_FEATURES: testing_only_libclang_3_9
- TARGET: msvc
LLVM_VERSION: 4.0.0
BINDGEN_FEATURES: testing_only_libclang_4
- TARGET: msvc
LLVM_VERSION: 5.0.0
BINDGEN_FEATURES: testing_only_libclang_5

configuration:
- stable
Expand Down
1 change: 1 addition & 0 deletions bindgen-integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ bindgen = { path = ".." }
gcc = "0.3"

[features]
testing_only_libclang_5 = ["bindgen/testing_only_libclang_5"]
testing_only_libclang_4 = ["bindgen/testing_only_libclang_4"]
testing_only_libclang_3_9 = ["bindgen/testing_only_libclang_3_9"]
testing_only_libclang_3_8 = ["bindgen/testing_only_libclang_3_8"]
56 changes: 40 additions & 16 deletions ci/before_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,55 @@ if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
rvm get head || true
fi

function llvm_download_if_needed() {
export LLVM_VERSION_TRIPLE="${LLVM_VERSION}"
export LLVM=clang+llvm-${LLVM_VERSION_TRIPLE}-x86_64-$1

local llvm_build_dir="$HOME/.llvm-builds/${LLVM}"

if [ -d "${llvm_build_dir}" ]; then
echo "Using cached LLVM build for ${LLVM} in ${llvm_build_dir}";
function llvm_linux_target_triple() {
if [ "$1" == "5.0" ]; then
echo "linux-x86_64-ubuntu14.04"
else
wget http://llvm.org/releases/${LLVM_VERSION_TRIPLE}/${LLVM}.tar.xz
mkdir -p "${llvm_build_dir}"
tar -xf ${LLVM}.tar.xz -C "${llvm_build_dir}" --strip-components=1
echo "x86_64-linux-gnu-ubuntu-14.04"
fi
}

export LLVM_CONFIG_PATH="${llvm_build_dir}/bin/llvm-config"
if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
cp "${llvm_build_dir}/lib/libclang.dylib" /usr/local/lib/libclang.dylib
function llvm_version_triple() {
if [ "$1" == "3.5" ]; then
echo "3.5.2"
elif [ "$1" == "3.6" ]; then
echo "3.6.2"
elif [ "$1" == "3.7" ]; then
echo "3.7.1"
elif [ "$1" == "3.8" ]; then
echo "3.8.1"
elif [ "$1" == "3.9" ]; then
echo "3.9.0"
elif [ "$1" == "4.0" ]; then
echo "4.0.0"
elif [ "$1" == "5.0" ]; then
echo "5.0.0"
fi
}

function llvm_download() {
export LLVM_VERSION_TRIPLE=`llvm_version_triple ${LLVM_VERSION}`
export LLVM=clang+llvm-${LLVM_VERSION_TRIPLE}-$1
export LLVM_DIRECTORY="$HOME/.llvm/${LLVM}"

if [ -d "${LLVM_DIRECTORY}" ]; then
echo "Using cached LLVM download for ${LLVM}..."
else
wget http://releases.llvm.org/${LLVM_VERSION_TRIPLE}/${LLVM}.tar.xz
mkdir -p "${LLVM_DIRECTORY}"
tar xf ${LLVM}.tar.xz -C "${LLVM_DIRECTORY}" --strip-components=1
fi

export LLVM_CONFIG_PATH="${LLVM_DIRECTORY}/bin/llvm-config"
}

if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
llvm_download_if_needed linux-gnu-ubuntu-14.04
llvm_download `llvm_linux_target_triple ${LLVM_VERSION}`
export LD_LIBRARY_PATH="${LLVM_DIRECTORY}/lib":$LD_LIBRARY_PATH
else
llvm_download_if_needed apple-darwin
llvm_download x86_64-apple-darwin
cp "${LLVM_DIRECTORY}/lib/libclang.dylib" /usr/local/lib/libclang.dylib
export DYLD_LIBRARY_PATH="${LLVM_DIRECTORY}/lib":$DYLD_LIBRARY_PATH
fi

popd
Expand Down
32 changes: 32 additions & 0 deletions src/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1812,3 +1812,35 @@ impl Drop for EvalResult {
unsafe { clang_EvalResult_dispose(self.x) };
}
}

/// Target information obtained from libclang.
#[derive(Debug)]
pub struct TargetInfo {
/// The target triple.
pub triple: String,
/// The width of the pointer _in bits_.
pub pointer_width: usize,
}

impl TargetInfo {
/// Tries to obtain target information from libclang.
pub fn new(tu: &TranslationUnit) -> Option<Self> {
if !clang_getTranslationUnitTargetInfo::is_loaded() {
return None;
}
let triple;
let pointer_width;
unsafe {
let ti = clang_getTranslationUnitTargetInfo(tu.x);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, nice -- this is the API you sent patches upstream to add, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right :)

triple = cxstring_into_string(clang_TargetInfo_getTriple(ti));
pointer_width = clang_TargetInfo_getPointerWidth(ti);
clang_TargetInfo_dispose(ti);
}
assert!(pointer_width > 0);
assert_eq!(pointer_width % 8, 0);
Some(TargetInfo {
triple,
pointer_width: pointer_width as usize,
})
}
}
11 changes: 5 additions & 6 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ use std::collections::{HashSet, VecDeque};
use std::collections::hash_map::{Entry, HashMap};
use std::fmt::Write;
use std::iter;
use std::mem;
use std::ops;

// Name of type defined in constified enum module
Expand Down Expand Up @@ -1777,8 +1776,9 @@ impl CodeGenerator for CompInfo {
let align = layout.align;

let check_struct_align =
if align > mem::size_of::<*mut ()>() {
// FIXME when [RFC 1358](https://github.com/rust-lang/rust/issues/33626) ready
if align > ctx.target_pointer_size() &&
!ctx.options().rust_features().repr_align
{
None
} else {
Some(quote! {
Expand Down Expand Up @@ -2720,9 +2720,8 @@ trait TryToOpaque {
/// leverage the blanket impl for this trait.
trait ToOpaque: TryToOpaque {
fn get_layout(&self, ctx: &BindgenContext, extra: &Self::Extra) -> Layout {
self.try_get_layout(ctx, extra).unwrap_or_else(
|_| Layout::for_size(1),
)
self.try_get_layout(ctx, extra)
.unwrap_or_else(|_| Layout::for_size(ctx, 1))
}

fn to_opaque(
Expand Down
43 changes: 25 additions & 18 deletions src/codegen/struct_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use ir::layout::Layout;
use ir::ty::{Type, TypeKind};
use quote;
use std::cmp;
use std::mem;

/// Trace the layout of struct.
#[derive(Debug)]
Expand Down Expand Up @@ -101,7 +100,7 @@ impl<'a> StructLayoutTracker<'a> {
pub fn saw_vtable(&mut self) {
debug!("saw vtable for {}", self.name);

let ptr_size = mem::size_of::<*mut ()>();
let ptr_size = self.ctx.target_pointer_size();
self.latest_offset += ptr_size;
self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size));
self.max_field_align = ptr_size;
Expand Down Expand Up @@ -165,15 +164,13 @@ impl<'a> StructLayoutTracker<'a> {
// can support.
//
// This means that the structs in the array are super-unsafe to
// access, since they won't be properly aligned, but *shrug*.
if let Some(layout) = self.ctx.resolve_type(inner).layout(
self.ctx,
)
{
if layout.align > mem::size_of::<*mut ()>() {
field_layout.size = align_to(layout.size, layout.align) *
len;
field_layout.align = mem::size_of::<*mut ()>();
// access, since they won't be properly aligned, but there's not too
// much we can do about it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For posterity, we can use #[repr(align(..))] to fix this ultra hack now (given a new enough target rust).

if let Some(layout) = self.ctx.resolve_type(inner).layout(self.ctx) {
if layout.align > self.ctx.target_pointer_size() {
field_layout.size =
align_to(layout.size, layout.align) * len;
field_layout.align = self.ctx.target_pointer_size();
}
}
}
Expand All @@ -193,7 +190,7 @@ impl<'a> StructLayoutTracker<'a> {

// Otherwise the padding is useless.
let need_padding = padding_bytes >= field_layout.align ||
field_layout.align > mem::size_of::<*mut ()>();
field_layout.align > self.ctx.target_pointer_size();

self.latest_offset += padding_bytes;

Expand All @@ -215,7 +212,7 @@ impl<'a> StructLayoutTracker<'a> {
if need_padding && padding_bytes != 0 {
Some(Layout::new(
padding_bytes,
cmp::min(field_layout.align, mem::size_of::<*mut ()>()),
cmp::min(field_layout.align, self.ctx.target_pointer_size())
))
} else {
None
Expand Down Expand Up @@ -267,15 +264,15 @@ impl<'a> StructLayoutTracker<'a> {
(self.last_field_was_bitfield &&
padding_bytes >=
self.latest_field_layout.unwrap().align) ||
layout.align > mem::size_of::<*mut ()>())
layout.align > self.ctx.target_pointer_size())
{
let layout = if self.is_packed {
Layout::new(padding_bytes, 1)
} else if self.last_field_was_bitfield ||
layout.align > mem::size_of::<*mut ()>()
layout.align > self.ctx.target_pointer_size()
{
// We've already given up on alignment here.
Layout::for_size(padding_bytes)
Layout::for_size(self.ctx, padding_bytes)
} else {
Layout::new(padding_bytes, layout.align)
};
Expand All @@ -289,8 +286,18 @@ impl<'a> StructLayoutTracker<'a> {
}

pub fn requires_explicit_align(&self, layout: Layout) -> bool {
self.max_field_align < layout.align &&
layout.align <= mem::size_of::<*mut ()>()
if self.max_field_align >= layout.align {
return false;
}
// At this point we require explicit alignment, but we may not be able
// to generate the right bits, let's double check.
if self.ctx.options().rust_features().repr_align {
return true;
}

// We can only generate up-to a word of alignment unless we support
// repr(align).
layout.align <= self.ctx.target_pointer_size()
}

fn padding_bytes(&self, layout: Layout) -> usize {
Expand Down
Loading