Skip to content

Missing memory alignment Rust generated code #1291

@beltegeuse

Description

@beltegeuse

Hi guys,
I developing a Rust wrapper for the Embree library. This library heavily relies on processor vector operation (SSE, AVX) to provide high performance. For now, I use explicit FFI declaration to interface with Embree. However, I was planning to use rust-bindgen to generate this interface automatically.

However, when I use bindgen, the code does not align correctly compared to what it is specified in the C++ header. Moreover, the test code generated by bindgen does not include a test to check if the current rust code have the proper alignment.

Input C/C++ Header

Extracted code from Embree (v2)

#ifdef _WIN32
#  define RTCORE_ALIGN(...) __declspec(align(__VA_ARGS__))
#else
#  define RTCORE_ALIGN(...) __attribute__((aligned(__VA_ARGS__)))
#endif

// ....

struct RTCORE_ALIGN(16)  RTCRay
{
  /* ray data */
public:
  float org[3];      //!< Ray origin
  float align0;
  
  float dir[3];      //!< Ray direction
  float align1;
  
  float tnear;       //!< Start of ray segment
  float tfar;        //!< End of ray segment (set to hit distance)

  float time;        //!< Time of this ray for motion blur
  unsigned mask;        //!< Used to mask out objects during traversal
  
  /* hit data */
public:
  float Ng[3];       //!< Unnormalized geometry normal
  float align2;
  
  float u;           //!< Barycentric u coordinate of hit
  float v;           //!< Barycentric v coordinate of hit

  unsigned geomID;        //!< geometry ID
  unsigned primID;        //!< primitive ID
  unsigned instID;        //!< instance ID
};

Bindgen Invocation

let bindings = bindgen::Builder::default()
        .clang_args(["-x", "c++", "-std=c++11"].iter())
        .enable_cxx_namespaces()
        .blacklist_type("max_align_t")
        .header("wrapper.h")
        .generate()
       .expect("Unable to generate bindings");

Actual Results

The code generated by rust-bindgen

 #[repr(C)]
    #[derive(Debug, Copy, Clone)]
    pub struct RTCRay { 
        pub org: [f32; 3usize], 
        pub align0: f32, 
        pub dir: [f32; 3usize], 
        pub align1: f32, 
        pub tnear: f32, 
        pub tfar: f32, 
        pub time: f32, 
        pub mask: ::std::os::raw::c_uint, 
        pub Ng: [f32; 3usize], 
        pub align2: f32, 
        pub u: f32, 
        pub v: f32, 
        pub geomID: ::std::os::raw::c_uint, 
        pub primID: ::std::os::raw::c_uint, 
        pub instID: ::std::os::raw::c_uint, 
        pub __bindgen_padding_0: [u32; 3usize] 
    }

    #[test]
    fn bindgen_test_layout_RTCRay() {
        assert_eq!(::std::mem::size_of::<RTCRay>(), 96usize, concat!( "Size of: " , stringify ! ( RTCRay ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).org as *const _ as usize }, 0usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( org ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).align0 as *const _ as usize }, 12usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( align0 ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).dir as *const _ as usize }, 16usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( dir ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).align1 as *const _ as usize }, 28usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( align1 ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).tnear as *const _ as usize }, 32usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( tnear ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).tfar as *const _ as usize }, 36usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( tfar ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).time as *const _ as usize }, 40usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( time ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).mask as *const _ as usize }, 44usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( mask ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).Ng as *const _ as usize }, 48usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( Ng ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).align2 as *const _ as usize }, 60usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( align2 ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).u as *const _ as usize }, 64usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( u ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).v as *const _ as usize }, 68usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( v ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).geomID as *const _ as usize }, 72usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( geomID ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).primID as *const _ as usize }, 76usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( primID ) ));
        assert_eq!(unsafe { &(*(::std::ptr::null::<RTCRay>())).instID as *const _ as usize }, 80usize, concat!( "Offset of field: " , stringify ! ( RTCRay ) , "::" , stringify ! ( instID ) ));
    }

The test that I have inside my lib.rs:

#[test]
fn test_ray_align() {
    assert_eq!(::std::mem::align_of::<root::RTCRay>(), 16usize);
}

this test failed and give me this output:

---- test_ray_align stdout ----
	thread 'test_ray_align' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `16`', src/lib.rs:22:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.


failures:
    test_ray_align

Expected Results

From bindgen call, I was expecting two things:

  • #[repr(C,align(16))] (compared to #[repr(C)])
  • adding: assert_eq!(::std::mem::align_of::<RTCRay>(), 16usize); inside the test suite.

Does rust-bindgen have a way to generate this test and have explicit align automatically?
Thanks guys for your awsome tool. Hope that I can use it inside my project :)

Version

rustup 1.11.0, stable rust 1.25.0 (84203cac6 2018-03-25), version bindgen: "0.35.0"
My embree wrapper: https://github.com/beltegeuse/embree-rs (branch: bindgen)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions