Skip to content

Avoid wrapping constant allocations in packed structs when not necessary #138503

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

Merged
merged 2 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_codegen_gcc/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ pub fn const_alloc_to_gcc<'gcc>(
llvals.push(cx.const_bytes(bytes));
}

// FIXME(bjorn3) avoid wrapping in a struct when there is only a single element.
cx.const_struct(&llvals, true)
}

Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, range);
}

cx.const_struct(&llvals, true)
// Avoid wrapping in a struct if there is only a single value. This ensures
// that LLVM is able to perform the string merging optimization if the constant
// is a valid C string. LLVM only considers bare arrays for this optimization,
// not arrays wrapped in a struct. LLVM handles this at:
// https://github.com/rust-lang/llvm-project/blob/acaea3d2bb8f351b740db7ebce7d7a40b9e21488/llvm/lib/Target/TargetLoweringObjectFile.cpp#L249-L280
if let &[data] = &*llvals { data } else { cx.const_struct(&llvals, true) }
}

fn codegen_static_initializer<'ll, 'tcx>(
Expand Down
27 changes: 27 additions & 0 deletions tests/assembly/cstring-merging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//@ only-linux
//@ assembly-output: emit-asm
//@ compile-flags: --crate-type=lib -Copt-level=3 --edition 2024

use std::ffi::CStr;

// CHECK: .section .rodata.str1.1,"aMS"
// CHECK: .Lanon.{{.+}}:
// CHECK-NEXT: .asciz "foo"
#[unsafe(no_mangle)]
static CSTR: &[u8; 4] = b"foo\0";

// CHECK-NOT: .section
// CHECK: .Lanon.{{.+}}:
// CHECK-NEXT: .asciz "bar"
#[unsafe(no_mangle)]
pub fn cstr() -> &'static CStr {
c"bar"
}

// CHECK-NOT: .section
// CHECK: .Lanon.{{.+}}:
// CHECK-NEXT: .asciz "baz"
#[unsafe(no_mangle)]
pub fn manual_cstr() -> &'static str {
"baz\0"
}
4 changes: 2 additions & 2 deletions tests/codegen/const-array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#![crate_type = "lib"]

const LUT: [u8; 2] = [1, 1];
const LUT: [u8; 4] = [1, 1, 1, 1];

// CHECK-LABEL: @decode
#[no_mangle]
Expand All @@ -11,5 +11,5 @@ pub fn decode(i: u8) -> u8 {
// CHECK-NEXT: icmp
// CHECK-NEXT: select
// CHECK-NEXT: ret
if i < 2 { LUT[i as usize] } else { 2 }
if i < 4 { LUT[i as usize] } else { 2 }
}
2 changes: 1 addition & 1 deletion tests/codegen/debug-vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

// Make sure that vtables don't have the unnamed_addr attribute when debuginfo is enabled.
// This helps debuggers more reliably map from dyn pointer to concrete type.
// CHECK: @vtable.2 = private constant <{
// CHECK: @vtable.2 = private constant [
// CHECK: @vtable.3 = private constant <{
// CHECK: @vtable.4 = private constant <{

Expand Down
32 changes: 16 additions & 16 deletions tests/codegen/external-no-mangle-statics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,72 @@
// `#[no_mangle]`d static variables always have external linkage, i.e., no `internal` in their
// definitions

// CHECK: @A = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @A = {{(dso_local )?}}local_unnamed_addr constant
#[no_mangle]
static A: u8 = 0;

// CHECK: @B = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @B = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
static mut B: u8 = 0;

// CHECK: @C = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @C = {{(dso_local )?}}local_unnamed_addr constant
#[no_mangle]
pub static C: u8 = 0;

// CHECK: @D = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @D = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
pub static mut D: u8 = 0;

mod private {
// CHECK: @E = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @E = {{(dso_local )?}}local_unnamed_addr constant
#[no_mangle]
static E: u8 = 0;

// CHECK: @F = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @F = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
static mut F: u8 = 0;

// CHECK: @G = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @G = {{(dso_local )?}}local_unnamed_addr constant
#[no_mangle]
pub static G: u8 = 0;

// CHECK: @H = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @H = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
pub static mut H: u8 = 0;
}

const HIDDEN: () = {
// CHECK: @I = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @I = {{(dso_local )?}}local_unnamed_addr constant
#[no_mangle]
static I: u8 = 0;

// CHECK: @J = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @J = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
static mut J: u8 = 0;

// CHECK: @K = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @K = {{(dso_local )?}}local_unnamed_addr constant
#[no_mangle]
pub static K: u8 = 0;

// CHECK: @L = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @L = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
pub static mut L: u8 = 0;
};

fn x() {
// CHECK: @M = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @M = {{(dso_local )?}}local_unnamed_addr constant
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM decided to put this definition before A. Using CHECK-DAG everywhere allows filecheck to match independent of the order.

#[no_mangle]
static M: fn() = x;

// CHECK: @N = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @N = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
static mut N: u8 = 0;

// CHECK: @O = {{(dso_local )?}}local_unnamed_addr constant
// CHECK-DAG: @O = {{(dso_local )?}}local_unnamed_addr constant
#[no_mangle]
pub static O: u8 = 0;

// CHECK: @P = {{(dso_local )?}}local_unnamed_addr global
// CHECK-DAG: @P = {{(dso_local )?}}local_unnamed_addr global
#[no_mangle]
pub static mut P: u8 = 0;
}
2 changes: 1 addition & 1 deletion tests/codegen/link_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#![crate_type = "lib"]

// CHECK: @VAR1 = {{(dso_local )?}}constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one"
// CHECK: @VAR1 = {{(dso_local )?}}constant [4 x i8] c"\01\00\00\00", section ".test_one"
#[no_mangle]
#[link_section = ".test_one"]
#[cfg(target_endian = "little")]
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/remap_path_prefix/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod aux_mod;
include!("aux_mod.rs");

// Here we check that the expansion of the file!() macro is mapped.
// CHECK: @alloc_5761061597a97f66e13ef2ff92712c4b = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>
// CHECK: @alloc_5761061597a97f66e13ef2ff92712c4b = private unnamed_addr constant [34 x i8] c"/the/src/remap_path_prefix/main.rs"
pub static FILE_PATH: &'static str = file!();

fn main() {
Expand Down
6 changes: 3 additions & 3 deletions tests/codegen/uninit-consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ pub struct PartiallyUninit {
y: MaybeUninit<[u8; 10]>,
}

// CHECK: [[FULLY_UNINIT:@.*]] = private unnamed_addr constant <{ [10 x i8] }> undef
// CHECK: [[FULLY_UNINIT:@.*]] = private unnamed_addr constant [10 x i8] undef

// CHECK: [[PARTIALLY_UNINIT:@.*]] = private unnamed_addr constant <{ [4 x i8], [12 x i8] }> <{ [4 x i8] c"{{\\EF\\BE\\AD\\DE|\\DE\\AD\\BE\\EF}}", [12 x i8] undef }>, align 4

// This shouldn't contain undef, since it contains more chunks
// than the default value of uninit_const_chunk_threshold.
// CHECK: [[UNINIT_PADDING_HUGE:@.*]] = private unnamed_addr constant <{ [32768 x i8] }> <{ [32768 x i8] c"{{.+}}" }>, align 4
// CHECK: [[UNINIT_PADDING_HUGE:@.*]] = private unnamed_addr constant [32768 x i8] c"{{.+}}", align 4

// CHECK: [[FULLY_UNINIT_HUGE:@.*]] = private unnamed_addr constant <{ [16384 x i8] }> undef
// CHECK: [[FULLY_UNINIT_HUGE:@.*]] = private unnamed_addr constant [16384 x i8] undef

// CHECK-LABEL: @fully_uninit
#[no_mangle]
Expand Down
Loading