Skip to content
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

#[repr(align(x))] is limited to 32768 #42960

Closed
Ekleog opened this issue Jun 28, 2017 · 11 comments
Closed

#[repr(align(x))] is limited to 32768 #42960

Ekleog opened this issue Jun 28, 2017 · 11 comments

Comments

@Ekleog
Copy link

Ekleog commented Jun 28, 2017

In some cases, the hardware imposes natural alignment for some data (ie. data aligned to its length). For example, the ARM Memory Protection Unit.

This can happen even when handling large structs (eg. 0x10000-sized buffers). These buffers would thus require an alignment of 0x10000. Which appears not to be possible with #[repr(align(x))], that appears limited to 32768.

I'm currently using the following (ugly, esp. because of the absence of type-level integers) workaround to get the aligned data I need:

pub struct Aligned {                                                        
   unaligned: [u8; 0x100000]                                                    
}                                                                                
pub static mut DATA: Aligned = Aligned { unaligned: [0; 0x100000] };
impl Aligned {                                                              
   pub fn get(&self) -> &[u8] {                                                  
       let begin_addr = &self.unaligned[0] as *const _ as usize;                
       let aligned_begin_addr = (begin_addr & !0x7FFFF) + 0x80000;              
       let offset = aligned_begin_addr - begin_addr;                            
       &self.unaligned[offset..(offset+0x80000)]                                
   }                                                                            
   pub fn get_mut(&mut self) -> &mut [u8] {                                      
       let begin_addr = &self.unaligned[0] as *const _ as usize;                
       let aligned_begin_addr = (begin_addr & !0x7FFFF) + 0x80000;              
       let offset = aligned_begin_addr - begin_addr;                            
       &mut self.unaligned[offset..(offset+0x80000)]                            
   }                                                                            
}  

If I can manually do it, I don't really get why rustc wouldn't be able to do it (esp. with http://www.cs.brandeis.edu/~cs146a/rust/doc-02-21-2015/rustc/lib/llvm/fn.LLVMSetAlignment.html taking an u32 as input)?

Is there a technical issue or is this just a current implementation limitation?

As this behaviour is not specified in RFC1358, cc'ing #33626

Disclaimer: I'm not actually running this code on an ARM MPU-enabled chip, but explaining the actual use case would be too long and complex and the issue is exactly the same, so I just picked this example.

@mattico
Copy link
Contributor

mattico commented Jun 29, 2017

Interesting. I wonder if this limitation is specific to ARM? If #30170 is still current (and it seems to be: #32838) then you should be able to use huge alignments. If you couldn't then you wouldn't be able to trigger the buggy behavior in the OSX allocator.

@Ekleog
Copy link
Author

Ekleog commented Jun 29, 2017

#30170 appears to be only for alignment using the allocator, so not covering all the cases of #[repr(align(x))] (which also covers the “on the stack” case).

I have been pointed to

pub align: u16,
; so I guess it's at least in part an issue in the types used (u16's biggest power-of-two value being 32768)?

@PlasmaPower
Copy link
Contributor

The limit comes from here:

/// Alignment of a type in bytes, both ABI-mandated and preferred.
/// Since alignments are always powers of 2, we can pack both in one byte,
/// giving each a nibble (4 bits) for a maximum alignment of 2<sup>15</sup> = 32768.

@Ekleog
Copy link
Author

Ekleog commented Jul 4, 2017

Thanks! So, the blame seems to show it all came from the same commit that introduced the layout module.

Could that be premature optimization, or do you think there is a strong performance case for having the alignment restricted this way?

@PlasmaPower
Copy link
Contributor

No idea, I just traced the alignment variable through the code. I can try to change it, but I don't know much about this stuff. I'm assuming 2^31 would be more than enough?

@sfackler
Copy link
Member

sfackler commented Jul 6, 2017

@Ekleog It looks like it's just trying to optimize compiler memory usage by keeping the size of the alignment info small. We should just be able to bump that to u16 or whatever's necessary.

@PlasmaPower
Copy link
Contributor

@sfackler I'm currently working on that.

@Ekleog
Copy link
Author

Ekleog commented Jul 6, 2017

Yeah, 2^31 is ~2GB, I think it would be enough for all practical purposes (the only cases I know of required natural alignment for large arrays are for embedded devices, which don't have that much memory anyway -- while 2^15 is 32kB which stays under the “existing” limit)

Thanks @PlasmaPower !

@PlasmaPower
Copy link
Contributor

I understand the code a bit better now. It was dividing a u8 into 2 nibbles, for the abi and preferred alignments. Instead of using a u16, I'm just using 2 u8s.

PR should be up shortly, currently working on tests.

@PlasmaPower
Copy link
Contributor

#43097 created to fix this.

bors added a commit that referenced this issue Jul 8, 2017
Raise alignment limit from 2^15 to 2^31 - 1

Fixes #42960
@Ekleog
Copy link
Author

Ekleog commented Jul 9, 2017

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants