@@ -28,14 +28,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
2828 self . const_uint ( self . type_u16 ( ) , i as u64 )
2929 }
3030
31- fn global_const_string ( & self , string : & str ) -> LValue < ' gcc > {
32- // The bytes of a Rust string are not NUL- terminated and can contain NULs .
33- let string = bytes_in_context ( self , string . as_bytes ( ) ) ;
31+ fn global_string ( & self , string : & str ) -> LValue < ' gcc > {
32+ // FIXME(antoyo): handle non-null- terminated strings .
33+ let string = self . context . new_string_literal ( string ) ;
3434 let sym = self . generate_local_symbol_name ( "str" ) ;
3535 let global = self . declare_private_global ( & sym, self . val_ty ( string) ) ;
3636 global. global_set_initializer_rvalue ( string) ;
37- #[ cfg( feature = "master" ) ]
38- global. global_set_readonly ( ) ;
3937 global
4038 // FIXME(antoyo): set linkage.
4139 }
@@ -128,6 +126,18 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
128126 }
129127}
130128
129+ /// The element type and element count of the array used to represent a run of `len` constant bytes.
130+ ///
131+ /// Larger integers are used where possible: this reduces the number of rvalues, which is a
132+ /// significant memory saving on constant-heavy crates.
133+ fn byte_run_shape < ' gcc > ( cx : & CodegenCx < ' gcc , ' _ > , len : usize ) -> ( Type < ' gcc > , u64 ) {
134+ match len % 8 {
135+ 0 => ( cx. context . new_type :: < u64 > ( ) , len as u64 / 8 ) ,
136+ 4 => ( cx. context . new_type :: < u32 > ( ) , len as u64 / 4 ) ,
137+ _ => ( cx. context . new_type :: < u8 > ( ) , len as u64 ) ,
138+ }
139+ }
140+
131141/// The type [`bytes_in_context`] gives a run of `len` constant bytes.
132142///
133143/// Exposed separately so that the type of a constant allocation can be computed before any of its
@@ -137,30 +147,65 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
137147/// Two equal-but-distinct array types would key [`CodegenCx::type_struct`] differently and so
138148/// produce two distinct anonymous structs, and libgccjit compares struct types by identity.
139149pub fn bytes_type_in_context < ' gcc > ( cx : & CodegenCx < ' gcc , ' _ > , len : usize ) -> Type < ' gcc > {
140- if let Some ( & typ) = cx. byte_array_types . borrow ( ) . get ( & len) {
150+ let ( element_type, count) = byte_run_shape ( cx, len) ;
151+ if let Some ( & typ) = cx. byte_array_types . borrow ( ) . get ( & ( element_type, count) ) {
141152 return typ;
142153 }
143- let typ = new_array_type ( cx. context , None , cx . type_u8 ( ) , len as u64 ) ;
144- cx. byte_array_types . borrow_mut ( ) . insert ( len , typ) ;
154+ let typ = new_array_type ( cx. context , None , element_type , count ) ;
155+ cx. byte_array_types . borrow_mut ( ) . insert ( ( element_type , count ) , typ) ;
145156 typ
146157}
147158
159+ // FIXME(FractalFir): Consider using `global_set_initializer` instead. Before this is done, we need to confirm that
160+ // `global_set_initializer` is more memory efficient than the current solution.
161+ // `global_set_initializer` calls `global_set_initializer_rvalue` under the hood - does it generate an array of rvalues,
162+ // or is it using a more efficient representation?
148163pub fn bytes_in_context < ' gcc , ' tcx > ( cx : & CodegenCx < ' gcc , ' tcx > , bytes : & [ u8 ] ) -> RValue < ' gcc > {
149164 let typ = bytes_type_in_context ( cx, bytes. len ( ) ) ;
150- // Raw bytes take far less RAM in libgccjit than one rvalue per array element.
151- #[ cfg( feature = "master" ) ]
152- {
153- cx. context . new_array_from_bytes ( None , typ, bytes)
154- }
155- #[ cfg( not( feature = "master" ) ) ]
156- {
157- let byte_type = cx. type_u8 ( ) ;
158- let elements: Vec < _ > = bytes
165+ let ( element_type, _) = byte_run_shape ( cx, bytes. len ( ) ) ;
166+ let context = & cx. context ;
167+ // Since we are representing arbitrary byte runs as integers, we need to follow the target
168+ // endianness.
169+ let endian = cx. sess ( ) . target . options . endian ;
170+ let elements: Vec < _ > = match bytes. len ( ) % 8 {
171+ 0 => {
172+ let ( arrays, remainder) = bytes. as_chunks :: < 8 > ( ) ;
173+ debug_assert ! ( remainder. is_empty( ) ) ;
174+ arrays
175+ . iter ( )
176+ . map ( |& arr| {
177+ context. new_rvalue_from_long (
178+ element_type,
179+ match endian {
180+ rustc_abi:: Endian :: Little => u64:: from_le_bytes ( arr) as i64 ,
181+ rustc_abi:: Endian :: Big => u64:: from_be_bytes ( arr) as i64 ,
182+ } ,
183+ )
184+ } )
185+ . collect ( )
186+ }
187+ 4 => {
188+ let ( arrays, remainder) = bytes. as_chunks :: < 4 > ( ) ;
189+ debug_assert ! ( remainder. is_empty( ) ) ;
190+ arrays
191+ . iter ( )
192+ . map ( |& arr| {
193+ context. new_rvalue_from_int (
194+ element_type,
195+ match endian {
196+ rustc_abi:: Endian :: Little => u32:: from_le_bytes ( arr) as i32 ,
197+ rustc_abi:: Endian :: Big => u32:: from_be_bytes ( arr) as i32 ,
198+ } ,
199+ )
200+ } )
201+ . collect ( )
202+ }
203+ _ => bytes
159204 . iter ( )
160- . map ( |& byte| cx . context . new_rvalue_from_int ( byte_type , byte as i32 ) )
161- . collect ( ) ;
162- cx . context . new_array_constructor ( None , typ , & elements )
163- }
205+ . map ( |& byte| context. new_rvalue_from_int ( element_type , byte as i32 ) )
206+ . collect ( ) ,
207+ } ;
208+ context . new_array_constructor ( None , typ , & elements )
164209}
165210
166211pub fn type_is_pointer ( typ : Type < ' _ > ) -> bool {
@@ -247,7 +292,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
247292 fn const_str ( & self , s : & str ) -> ( RValue < ' gcc > , RValue < ' gcc > ) {
248293 let mut const_str_cache = self . const_str_cache . borrow_mut ( ) ;
249294 let str_global = const_str_cache. get ( s) . copied ( ) . unwrap_or_else ( || {
250- let g = self . global_const_string ( s) ;
295+ let g = self . global_string ( s) ;
251296 const_str_cache. insert ( s. to_owned ( ) , g) ;
252297 g
253298 } ) ;
0 commit comments