11use std:: ops:: Range ;
22
3- use rustc_abi:: { Align , HasDataLayout , Primitive , Scalar , Size , WrappingRange } ;
3+ use rustc_abi:: { Align , Endian , HasDataLayout , Primitive , Scalar , Size , WrappingRange } ;
44use rustc_codegen_ssa:: common;
55use rustc_codegen_ssa:: traits:: * ;
66use rustc_hir:: LangItem ;
@@ -29,6 +29,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
2929 cx : & CodegenCx < ' ll , ' _ > ,
3030 alloc : & Allocation ,
3131 is_static : bool ,
32+ vtable_base : Option < & ' ll Value > ,
3233) -> & ' ll Value {
3334 // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or
3435 // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be
@@ -44,6 +45,8 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
4445 let dl = cx. data_layout ( ) ;
4546 let pointer_size = dl. pointer_size ( ) ;
4647 let pointer_size_bytes = pointer_size. bytes ( ) as usize ;
48+ let use_relative_layout = cx. sess ( ) . opts . unstable_opts . experimental_relative_rust_abi_vtables
49+ && vtable_base. is_some ( ) ;
4750
4851 // Note: this function may call `inspect_with_uninit_and_ptr_outside_interpreter`, so `range`
4952 // must be within the bounds of `alloc` and not contain or overlap a pointer provenance.
@@ -52,7 +55,11 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
5255 cx : & ' a CodegenCx < ' ll , ' b > ,
5356 alloc : & ' a Allocation ,
5457 range : Range < usize > ,
58+ use_relative_layout : bool ,
5559 ) {
60+ let dl = cx. data_layout ( ) ;
61+ let pointer_size = dl. pointer_size ( ) ;
62+ let pointer_size_bytes = pointer_size. bytes ( ) as usize ;
5663 let chunks = alloc. init_mask ( ) . range_as_init_chunks ( range. clone ( ) . into ( ) ) ;
5764
5865 let chunk_to_llval = move |chunk| match chunk {
@@ -75,7 +82,43 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
7582 let allow_uninit_chunks = chunks. clone ( ) . take ( max. saturating_add ( 1 ) ) . count ( ) <= max;
7683
7784 if allow_uninit_chunks {
78- llvals. extend ( chunks. map ( chunk_to_llval) ) ;
85+ if use_relative_layout {
86+ // Rather than being stored as a struct of pointers or byte-arrays, a relative
87+ // vtable is a pure i32 array, so its components must be chunks of i32s. Here we
88+ // explicitly group any sequence of bytes into i32s.
89+ //
90+ // Normally we can only do this if an 8-byte constant can fit into 4 bytes.
91+ for chunk in chunks {
92+ match chunk {
93+ InitChunk :: Init ( range) => {
94+ let range =
95+ ( range. start . bytes ( ) as usize ) ..( range. end . bytes ( ) as usize ) ;
96+ let bytes =
97+ alloc. inspect_with_uninit_and_ptr_outside_interpreter ( range) ;
98+ for bytes in bytes. chunks_exact ( pointer_size_bytes) {
99+ assert ! (
100+ bytes[ 4 ..pointer_size_bytes] . iter( ) . all( |& x| x == 0 ) ,
101+ "Cannot fit constant into 4-bytes: {:?}" ,
102+ bytes
103+ ) ;
104+ let bytes: [ u8 ; 4 ] = bytes[ 0 ..4 ] . try_into ( ) . unwrap ( ) ;
105+ let val: u32 = match dl. endian {
106+ Endian :: Big => u32:: from_be_bytes ( bytes) ,
107+ Endian :: Little => u32:: from_le_bytes ( bytes) ,
108+ } ;
109+ llvals. push ( cx. const_u32 ( val) ) ;
110+ }
111+ }
112+ InitChunk :: Uninit ( range) => {
113+ let len = range. end . bytes ( ) - range. start . bytes ( ) ;
114+ let val = cx. const_undef ( cx. type_array ( cx. type_i8 ( ) , len / 2 ) ) ;
115+ llvals. push ( val) ;
116+ }
117+ } ;
118+ }
119+ } else {
120+ llvals. extend ( chunks. map ( chunk_to_llval) ) ;
121+ }
79122 } else {
80123 // If this allocation contains any uninit bytes, codegen as if it was initialized
81124 // (using some arbitrary value for uninit bytes).
@@ -93,7 +136,13 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
93136 // This `inspect` is okay since we have checked that there is no provenance, it
94137 // is within the bounds of the allocation, and it doesn't affect interpreter execution
95138 // (we inspect the result after interpreter execution).
96- append_chunks_of_init_and_uninit_bytes ( & mut llvals, cx, alloc, next_offset..offset) ;
139+ append_chunks_of_init_and_uninit_bytes (
140+ & mut llvals,
141+ cx,
142+ alloc,
143+ next_offset..offset,
144+ use_relative_layout,
145+ ) ;
97146 }
98147 let ptr_offset = read_target_uint (
99148 dl. endian ,
@@ -109,38 +158,64 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
109158
110159 let address_space = cx. tcx . global_alloc ( prov. alloc_id ( ) ) . address_space ( cx) ;
111160
112- llvals. push ( cx. scalar_to_backend (
113- InterpScalar :: from_pointer ( Pointer :: new ( prov, Size :: from_bytes ( ptr_offset) ) , & cx. tcx ) ,
114- Scalar :: Initialized {
115- value : Primitive :: Pointer ( address_space) ,
116- valid_range : WrappingRange :: full ( pointer_size) ,
117- } ,
118- cx. type_ptr_ext ( address_space) ,
119- ) ) ;
161+ let s = {
162+ let scalar = cx. scalar_to_backend (
163+ InterpScalar :: from_pointer (
164+ Pointer :: new ( prov, Size :: from_bytes ( ptr_offset) ) ,
165+ & cx. tcx ,
166+ ) ,
167+ Scalar :: Initialized {
168+ value : Primitive :: Pointer ( address_space) ,
169+ valid_range : WrappingRange :: full ( pointer_size) ,
170+ } ,
171+ cx. type_ptr_ext ( address_space) ,
172+ ) ;
173+
174+ if use_relative_layout {
175+ unsafe {
176+ let fptr = llvm:: LLVMDSOLocalEquivalent ( scalar) ;
177+ let sub = llvm:: LLVMConstSub (
178+ llvm:: LLVMConstPtrToInt ( fptr, cx. type_i64 ( ) ) ,
179+ llvm:: LLVMConstPtrToInt ( vtable_base. unwrap ( ) , cx. type_i64 ( ) ) ,
180+ ) ;
181+ llvm:: LLVMConstTrunc ( sub, cx. type_i32 ( ) )
182+ }
183+ } else {
184+ scalar
185+ }
186+ } ;
187+
188+ llvals. push ( s) ;
120189 next_offset = offset + pointer_size_bytes;
121190 }
122191 if alloc. len ( ) >= next_offset {
123192 let range = next_offset..alloc. len ( ) ;
124193 // This `inspect` is okay since we have check that it is after all provenance, it is
125194 // within the bounds of the allocation, and it doesn't affect interpreter execution (we
126195 // inspect the result after interpreter execution).
127- append_chunks_of_init_and_uninit_bytes ( & mut llvals, cx, alloc, range) ;
196+ append_chunks_of_init_and_uninit_bytes ( & mut llvals, cx, alloc, range, use_relative_layout ) ;
128197 }
129198
130199 // Avoid wrapping in a struct if there is only a single value. This ensures
131200 // that LLVM is able to perform the string merging optimization if the constant
132201 // is a valid C string. LLVM only considers bare arrays for this optimization,
133202 // not arrays wrapped in a struct. LLVM handles this at:
134203 // https://github.com/rust-lang/llvm-project/blob/acaea3d2bb8f351b740db7ebce7d7a40b9e21488/llvm/lib/Target/TargetLoweringObjectFile.cpp#L249-L280
135- if let & [ data] = & * llvals { data } else { cx. const_struct ( & llvals, true ) }
204+ if let & [ data] = & * llvals {
205+ data
206+ } else if use_relative_layout {
207+ cx. const_array ( cx. type_i32 ( ) , & llvals)
208+ } else {
209+ cx. const_struct ( & llvals, true )
210+ }
136211}
137212
138213fn codegen_static_initializer < ' ll , ' tcx > (
139214 cx : & CodegenCx < ' ll , ' tcx > ,
140215 def_id : DefId ,
141216) -> Result < ( & ' ll Value , ConstAllocation < ' tcx > ) , ErrorHandled > {
142217 let alloc = cx. tcx . eval_static_initializer ( def_id) ?;
143- Ok ( ( const_alloc_to_llvm ( cx, alloc. inner ( ) , /*static*/ true ) , alloc) )
218+ Ok ( ( const_alloc_to_llvm ( cx, alloc. inner ( ) , /*static*/ true , /*vtable_base*/ None ) , alloc) )
144219}
145220
146221fn set_global_alignment < ' ll > ( cx : & CodegenCx < ' ll , ' _ > , gv : & ' ll Value , mut align : Align ) {
@@ -233,21 +308,31 @@ impl<'ll> CodegenCx<'ll, '_> {
233308 cv : & ' ll Value ,
234309 align : Align ,
235310 kind : Option < & str > ,
311+ ) -> & ' ll Value {
312+ let gv = self . static_addr_of_mut_from_type ( self . val_ty ( cv) , align, kind) ;
313+ llvm:: set_initializer ( gv, cv) ;
314+ gv
315+ }
316+
317+ pub ( crate ) fn static_addr_of_mut_from_type (
318+ & self ,
319+ ty : & ' ll Type ,
320+ align : Align ,
321+ kind : Option < & str > ,
236322 ) -> & ' ll Value {
237323 let gv = match kind {
238324 Some ( kind) if !self . tcx . sess . fewer_names ( ) => {
239325 let name = self . generate_local_symbol_name ( kind) ;
240- let gv = self . define_global ( & name, self . val_ty ( cv ) ) . unwrap_or_else ( || {
326+ let gv = self . define_global ( & name, ty ) . unwrap_or_else ( || {
241327 bug ! ( "symbol `{}` is already defined" , name) ;
242328 } ) ;
243329 gv
244330 }
245- _ => self . define_global ( "" , self . val_ty ( cv ) ) . unwrap_or_else ( || {
331+ _ => self . define_global ( "" , ty ) . unwrap_or_else ( || {
246332 bug ! ( "anonymous global symbol is already defined" ) ;
247333 } ) ,
248334 } ;
249335 llvm:: set_linkage ( gv, llvm:: Linkage :: PrivateLinkage ) ;
250- llvm:: set_initializer ( gv, cv) ;
251336 set_global_alignment ( self , gv, align) ;
252337 llvm:: set_unnamed_address ( gv, llvm:: UnnamedAddr :: Global ) ;
253338 gv
@@ -280,6 +365,15 @@ impl<'ll> CodegenCx<'ll, '_> {
280365 gv
281366 }
282367
368+ pub ( crate ) fn static_addr_of_impl_for_gv ( & self , cv : & ' ll Value , gv : & ' ll Value ) -> & ' ll Value {
369+ assert ! ( !self . const_globals. borrow( ) . contains_key( & cv) ) ;
370+ let mut binding = self . const_globals . borrow_mut ( ) ;
371+ binding. insert ( cv, gv) ;
372+ llvm:: set_initializer ( gv, cv) ;
373+ llvm:: set_global_constant ( gv, true ) ;
374+ gv
375+ }
376+
283377 #[ instrument( level = "debug" , skip( self ) ) ]
284378 pub ( crate ) fn get_static ( & self , def_id : DefId ) -> & ' ll Value {
285379 let instance = Instance :: mono ( self . tcx , def_id) ;
0 commit comments