@@ -28,7 +28,7 @@ use rustc_middle::ty::layout::{FnAbiOfHelpers, LayoutOfHelpers};
2828use rustc_middle:: ty:: { Ty , TypeVisitableExt } ;
2929use rustc_middle:: { bug, span_bug, ty} ;
3030use rustc_middle:: {
31- mir:: mono:: CodegenUnit ,
31+ mir:: mono:: { CodegenUnit , MonoItem } ,
3232 ty:: { Instance , TyCtxt } ,
3333} ;
3434use rustc_session:: Session ;
@@ -110,6 +110,9 @@ pub(crate) struct CodegenCx<'ll, 'tcx> {
110110
111111 /// Tracks cumulative constant memory usage in bytes for compile-time diagnostics
112112 constant_memory_usage : Cell < u64 > ,
113+ /// Pre-reserved constant memory bytes for statics with explicit placement overrides.
114+ /// Computed lazily on first call to `static_addrspace`.
115+ constant_memory_reserved : Cell < Option < u64 > > ,
113116}
114117
115118impl < ' ll , ' tcx > CodegenCx < ' ll , ' tcx > {
@@ -181,6 +184,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
181184 codegen_args : CodegenArgs :: from_session ( tcx. sess ( ) ) ,
182185 last_call_llfn : Cell :: new ( None ) ,
183186 constant_memory_usage : Cell :: new ( 0 ) ,
187+ constant_memory_reserved : Cell :: new ( None ) ,
184188 } ;
185189 cx. build_intrinsics_map ( ) ;
186190 cx
@@ -307,13 +311,68 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
307311 None
308312 }
309313
314+ /// Computes the total constant memory bytes reserved for statics with explicit
315+ /// placement overrides (`place_static`/`crate_memory_space` requesting constant).
316+ /// This is computed lazily on the first call and cached.
317+ ///
318+ /// By pre-reserving this space, automatic placement only fills whatever remains,
319+ /// ensuring explicitly requested statics always fit (as long as they fit together).
320+ fn get_constant_memory_reserved ( & self ) -> u64 {
321+ if let Some ( reserved) = self . constant_memory_reserved . get ( ) {
322+ return reserved;
323+ }
324+
325+ let mut reserved: u64 = 0 ;
326+ for ( & item, _) in self . codegen_unit . items ( ) {
327+ if let MonoItem :: Static ( def_id) = item {
328+ let instance = Instance :: mono ( self . tcx , def_id) ;
329+ let ty = instance. ty ( self . tcx , self . typing_env ( ) ) ;
330+
331+ // Skip statics that can't go in constant memory
332+ let is_mutable = self . tcx ( ) . is_mutable_static ( def_id) ;
333+ if is_mutable || !self . type_is_freeze ( ty) {
334+ continue ;
335+ }
336+
337+ // Skip statics with explicit #[address_space] attributes
338+ let attrs = self . tcx . get_all_attrs ( def_id) ;
339+ let nvvm_attrs = NvvmAttributes :: parse ( self , attrs) ;
340+ if nvvm_attrs. addrspace . is_some ( ) {
341+ continue ;
342+ }
343+
344+ // Only reserve for explicit overrides requesting constant memory
345+ if let Some ( MemorySpace :: Constant ) = self . resolve_memory_space ( instance) {
346+ let layout = self . layout_of ( ty) ;
347+ reserved += layout. size . bytes ( ) ;
348+ }
349+ }
350+ }
351+
352+ if reserved > CONSTANT_MEMORY_SIZE_LIMIT_BYTES {
353+ self . tcx . sess . dcx ( ) . warn ( format ! (
354+ "explicitly placed statics require {reserved} bytes of constant memory, \
355+ which exceeds the {} byte limit; this will likely cause runtime errors",
356+ CONSTANT_MEMORY_SIZE_LIMIT_BYTES
357+ ) ) ;
358+ }
359+
360+ self . constant_memory_reserved . set ( Some ( reserved) ) ;
361+ trace ! ( "Pre-reserved {reserved} bytes of constant memory for explicitly placed statics" ) ;
362+ reserved
363+ }
364+
310365 /// Computes the address space for a static.
311366 ///
312367 /// Priority system (highest to lowest):
313368 /// 1. Explicit `#[cuda_std::address_space(...)]` attribute
314369 /// 2. Per-static path override via CudaBuilder (`place_static`)
315370 /// 3. Per-crate override via CudaBuilder (`crate_memory_space`)
316371 /// 4. Global `use_constant_memory_space` flag
372+ ///
373+ /// Statics with explicit overrides (priorities 2-3) requesting constant memory
374+ /// have their space pre-reserved, so automatic placement (priority 4) only fills
375+ /// whatever remains. This ensures explicitly placed statics are packed first.
317376 pub fn static_addrspace ( & self , instance : Instance < ' tcx > ) -> AddressSpace {
318377 let ty = instance. ty ( self . tcx , self . typing_env ( ) ) ;
319378 let is_mutable = self . tcx ( ) . is_mutable_static ( instance. def_id ( ) ) ;
@@ -331,7 +390,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
331390 }
332391
333392 // Resolve memory space from overrides (priorities 2-4)
334- let want_constant = match self . resolve_memory_space ( instance) {
393+ let resolved = self . resolve_memory_space ( instance) ;
394+ let explicit_constant = matches ! ( resolved, Some ( MemorySpace :: Constant ) ) ;
395+ let want_constant = match resolved {
335396 Some ( MemorySpace :: Constant ) => true ,
336397 Some ( MemorySpace :: Global ) => false ,
337398 None => self . codegen_args . use_constant_memory_space ,
@@ -347,15 +408,24 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
347408 let current_usage = self . constant_memory_usage . get ( ) ;
348409 let new_usage = current_usage + size_bytes;
349410
411+ // For automatic placement, the effective limit is reduced by the space
412+ // reserved for explicitly placed statics (so they always fit).
413+ // For explicit overrides, use the full limit.
414+ let effective_limit = if explicit_constant {
415+ CONSTANT_MEMORY_SIZE_LIMIT_BYTES
416+ } else {
417+ let reserved = self . get_constant_memory_reserved ( ) ;
418+ CONSTANT_MEMORY_SIZE_LIMIT_BYTES . saturating_sub ( reserved)
419+ } ;
420+
350421 // Check if this single static is too large for constant memory
351- if size_bytes > CONSTANT_MEMORY_SIZE_LIMIT_BYTES {
422+ if size_bytes > effective_limit {
352423 let def_id = instance. def_id ( ) ;
353424 let span = self . tcx . def_span ( def_id) ;
354425 let mut diag = self . tcx . sess . dcx ( ) . struct_span_warn (
355426 span,
356427 format ! (
357- "static `{instance}` is {size_bytes} bytes, exceeds the constant memory limit of {} bytes" ,
358- CONSTANT_MEMORY_SIZE_LIMIT_BYTES
428+ "static `{instance}` is {size_bytes} bytes, exceeds the constant memory limit of {effective_limit} bytes" ,
359429 ) ,
360430 ) ;
361431 diag. span_label ( span, "static exceeds constant memory limit" ) ;
@@ -367,23 +437,29 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
367437
368438 // Check if adding this static would exceed the cumulative limit
369439 // Auto-spill to global memory with a warning instead of failing
370- if new_usage > CONSTANT_MEMORY_SIZE_LIMIT_BYTES {
440+ if new_usage > effective_limit {
371441 let def_id = instance. def_id ( ) ;
372442 let span = self . tcx . def_span ( def_id) ;
373- let remaining = CONSTANT_MEMORY_SIZE_LIMIT_BYTES . saturating_sub ( current_usage) ;
443+ let remaining = effective_limit . saturating_sub ( current_usage) ;
374444 let mut diag = self . tcx . sess . dcx ( ) . struct_span_warn (
375445 span,
376446 format ! (
377447 "constant memory overflow: static `{instance}` ({size_bytes} bytes) does not fit in remaining \
378- constant memory ({remaining} bytes free of {} bytes total)",
379- CONSTANT_MEMORY_SIZE_LIMIT_BYTES
448+ constant memory ({remaining} bytes free of {effective_limit} bytes available)",
380449 ) ,
381450 ) ;
382451 diag. span_label ( span, "automatically placed in global memory" ) ;
383452 diag. note ( format ! (
384- "current constant memory usage: {current_usage} / {} bytes" ,
385- CONSTANT_MEMORY_SIZE_LIMIT_BYTES
453+ "current constant memory usage: {current_usage} / {effective_limit} bytes" ,
386454 ) ) ;
455+ if !explicit_constant {
456+ let reserved = self . get_constant_memory_reserved ( ) ;
457+ if reserved > 0 {
458+ diag. note ( format ! (
459+ "{reserved} bytes reserved for explicitly placed statics" ,
460+ ) ) ;
461+ }
462+ }
387463 diag. help ( "use `.place_static(\" path\" , MemorySpace::Constant)` in build.rs to prioritize specific statics for constant memory" ) ;
388464 diag. emit ( ) ;
389465 return AddressSpace ( 1 ) ;
0 commit comments