Skip to content

Commit 3266c36

Browse files
committed
Auto merge of #111768 - oli-obk:pair_const_llvm, r=cjgillot
Optimize scalar and scalar pair representations loaded from ByRef in llvm in #105653 I noticed that we were generating suboptimal LLVM IR if we had a `ConstValue::ByRef` that could be represented by a `ScalarPair`. Before #105653 this is probably rare, but after it, every slice will go down this suboptimal code path that requires LLVM to untangle a bunch of indirections and translate static allocations that are only used once to read a scalar pair from.
2 parents 578bcbc + 164d041 commit 3266c36

File tree

6 files changed

+120
-83
lines changed

6 files changed

+120
-83
lines changed

compiler/rustc_codegen_gcc/src/common.rs

+19-23
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
use gccjit::LValue;
22
use gccjit::{RValue, Type, ToRValue};
3-
use rustc_codegen_ssa::mir::place::PlaceRef;
43
use rustc_codegen_ssa::traits::{
54
BaseTypeMethods,
65
ConstMethods,
7-
DerivedTypeMethods,
86
MiscMethods,
97
StaticMethods,
108
};
119
use rustc_middle::mir::Mutability;
12-
use rustc_middle::ty::layout::{TyAndLayout, LayoutOf};
10+
use rustc_middle::ty::layout::{LayoutOf};
1311
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
14-
use rustc_target::abi::{self, HasDataLayout, Pointer, Size};
12+
use rustc_target::abi::{self, HasDataLayout, Pointer};
1513

1614
use crate::consts::const_alloc_to_gcc;
1715
use crate::context::CodegenCx;
@@ -240,28 +238,26 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
240238
const_alloc_to_gcc(self, alloc)
241239
}
242240

243-
fn from_const_alloc(&self, layout: TyAndLayout<'tcx>, alloc: ConstAllocation<'tcx>, offset: Size) -> PlaceRef<'tcx, RValue<'gcc>> {
244-
assert_eq!(alloc.inner().align, layout.align.abi);
245-
let ty = self.type_ptr_to(layout.gcc_type(self));
246-
let value =
247-
if layout.size == Size::ZERO {
248-
let value = self.const_usize(alloc.inner().align.bytes());
249-
self.const_bitcast(value, ty)
250-
}
251-
else {
252-
let init = const_alloc_to_gcc(self, alloc);
253-
let base_addr = self.static_addr_of(init, alloc.inner().align, None);
254-
255-
let array = self.const_bitcast(base_addr, self.type_i8p());
256-
let value = self.context.new_array_access(None, array, self.const_usize(offset.bytes())).get_address(None);
257-
self.const_bitcast(value, ty)
258-
};
259-
PlaceRef::new_sized(value, layout)
260-
}
261-
262241
fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> {
263242
self.context.new_cast(None, val, ty)
264243
}
244+
245+
fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
246+
if value.get_type() == self.bool_type.make_pointer() {
247+
if let Some(pointee) = typ.get_pointee() {
248+
if pointee.dyncast_vector().is_some() {
249+
panic!()
250+
}
251+
}
252+
}
253+
// NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some
254+
// SIMD builtins require a constant value.
255+
self.bitcast_if_needed(value, typ)
256+
}
257+
258+
fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
259+
self.context.new_array_access(None, base_addr, self.const_usize(offset.bytes())).get_address(None)
260+
}
265261
}
266262

267263
pub trait SignType<'gcc, 'tcx> {

compiler/rustc_codegen_gcc/src/consts.rs

+1-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#[cfg(feature = "master")]
22
use gccjit::FnAttribute;
3-
use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type};
3+
use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue};
44
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods};
55
use rustc_middle::span_bug;
66
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
@@ -16,21 +16,6 @@ use crate::context::CodegenCx;
1616
use crate::errors::InvalidMinimumAlignment;
1717
use crate::type_of::LayoutGccExt;
1818

19-
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
20-
pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
21-
if value.get_type() == self.bool_type.make_pointer() {
22-
if let Some(pointee) = typ.get_pointee() {
23-
if pointee.dyncast_vector().is_some() {
24-
panic!()
25-
}
26-
}
27-
}
28-
// NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some
29-
// SIMD builtins require a constant value.
30-
self.bitcast_if_needed(value, typ)
31-
}
32-
}
33-
3419
fn set_global_alignment<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, gv: LValue<'gcc>, mut align: Align) {
3520
// The target may require greater alignment for globals than the type does.
3621
// Note: GCC and Clang also allow `__attribute__((aligned))` on variables,

compiler/rustc_codegen_llvm/src/common.rs

+17-32
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@ use crate::type_of::LayoutLlvmExt;
88
use crate::value::Value;
99

1010
use rustc_ast::Mutability;
11-
use rustc_codegen_ssa::mir::place::PlaceRef;
1211
use rustc_codegen_ssa::traits::*;
1312
use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
1413
use rustc_hir::def_id::DefId;
1514
use rustc_middle::bug;
1615
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
17-
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
16+
use rustc_middle::ty::layout::LayoutOf;
1817
use rustc_middle::ty::TyCtxt;
1918
use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType};
20-
use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer, Size};
19+
use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer};
2120
use rustc_target::spec::Target;
2221

2322
use libc::{c_char, c_uint};
@@ -307,38 +306,24 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
307306
const_alloc_to_llvm(self, alloc)
308307
}
309308

310-
fn from_const_alloc(
311-
&self,
312-
layout: TyAndLayout<'tcx>,
313-
alloc: ConstAllocation<'tcx>,
314-
offset: Size,
315-
) -> PlaceRef<'tcx, &'ll Value> {
316-
let alloc_align = alloc.inner().align;
317-
assert_eq!(alloc_align, layout.align.abi);
318-
let llty = self.type_ptr_to(layout.llvm_type(self));
319-
let llval = if layout.size == Size::ZERO {
320-
let llval = self.const_usize(alloc_align.bytes());
321-
unsafe { llvm::LLVMConstIntToPtr(llval, llty) }
322-
} else {
323-
let init = const_alloc_to_llvm(self, alloc);
324-
let base_addr = self.static_addr_of(init, alloc_align, None);
325-
326-
let llval = unsafe {
327-
llvm::LLVMRustConstInBoundsGEP2(
328-
self.type_i8(),
329-
self.const_bitcast(base_addr, self.type_i8p()),
330-
&self.const_usize(offset.bytes()),
331-
1,
332-
)
333-
};
334-
self.const_bitcast(llval, llty)
335-
};
336-
PlaceRef::new_sized(llval, layout)
337-
}
338-
339309
fn const_ptrcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
340310
consts::ptrcast(val, ty)
341311
}
312+
313+
fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
314+
self.const_bitcast(val, ty)
315+
}
316+
317+
fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
318+
unsafe {
319+
llvm::LLVMRustConstInBoundsGEP2(
320+
self.type_i8(),
321+
self.const_bitcast(base_addr, self.type_i8p()),
322+
&self.const_usize(offset.bytes()),
323+
1,
324+
)
325+
}
326+
}
342327
}
343328

344329
/// Get the [LLVM type][Type] of a [`Value`].

compiler/rustc_codegen_ssa/src/mir/operand.rs

+72-3
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use crate::traits::*;
88
use crate::MemFlags;
99

1010
use rustc_middle::mir;
11-
use rustc_middle::mir::interpret::{ConstValue, Pointer, Scalar};
11+
use rustc_middle::mir::interpret::{alloc_range, ConstValue, Pointer, Scalar};
1212
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
1313
use rustc_middle::ty::Ty;
14-
use rustc_target::abi::{Abi, Align, Size};
14+
use rustc_target::abi::{self, Abi, Align, Size};
1515

1616
use std::fmt;
1717

@@ -115,13 +115,82 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
115115
OperandValue::Pair(a_llval, b_llval)
116116
}
117117
ConstValue::ByRef { alloc, offset } => {
118-
return bx.load_operand(bx.from_const_alloc(layout, alloc, offset));
118+
return Self::from_const_alloc(bx, layout, alloc, offset);
119119
}
120120
};
121121

122122
OperandRef { val, layout }
123123
}
124124

125+
fn from_const_alloc<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
126+
bx: &mut Bx,
127+
layout: TyAndLayout<'tcx>,
128+
alloc: rustc_middle::mir::interpret::ConstAllocation<'tcx>,
129+
offset: Size,
130+
) -> Self {
131+
let alloc_align = alloc.inner().align;
132+
assert_eq!(alloc_align, layout.align.abi);
133+
let ty = bx.type_ptr_to(bx.cx().backend_type(layout));
134+
135+
let read_scalar = |start, size, s: abi::Scalar, ty| {
136+
let val = alloc
137+
.0
138+
.read_scalar(
139+
bx,
140+
alloc_range(start, size),
141+
/*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)),
142+
)
143+
.unwrap();
144+
bx.scalar_to_backend(val, s, ty)
145+
};
146+
147+
// It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
148+
// However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned --
149+
// and yet cannot be represented by an interpreter `Scalar`, since we have to handle the
150+
// case where some of the bytes are initialized and others are not. So, we need an extra
151+
// check that walks over the type of `mplace` to make sure it is truly correct to treat this
152+
// like a `Scalar` (or `ScalarPair`).
153+
match layout.abi {
154+
Abi::Scalar(s @ abi::Scalar::Initialized { .. }) => {
155+
let size = s.size(bx);
156+
assert_eq!(size, layout.size, "abi::Scalar size does not match layout size");
157+
let val = read_scalar(Size::ZERO, size, s, ty);
158+
OperandRef { val: OperandValue::Immediate(val), layout }
159+
}
160+
Abi::ScalarPair(
161+
a @ abi::Scalar::Initialized { .. },
162+
b @ abi::Scalar::Initialized { .. },
163+
) => {
164+
let (a_size, b_size) = (a.size(bx), b.size(bx));
165+
let b_offset = a_size.align_to(b.align(bx).abi);
166+
assert!(b_offset.bytes() > 0);
167+
let a_val = read_scalar(
168+
Size::ZERO,
169+
a_size,
170+
a,
171+
bx.scalar_pair_element_backend_type(layout, 0, true),
172+
);
173+
let b_val = read_scalar(
174+
b_offset,
175+
b_size,
176+
b,
177+
bx.scalar_pair_element_backend_type(layout, 1, true),
178+
);
179+
OperandRef { val: OperandValue::Pair(a_val, b_val), layout }
180+
}
181+
_ if layout.is_zst() => OperandRef::new_zst(bx, layout),
182+
_ => {
183+
// Neither a scalar nor scalar pair. Load from a place
184+
let init = bx.const_data_from_alloc(alloc);
185+
let base_addr = bx.static_addr_of(init, alloc_align, None);
186+
187+
let llval = bx.const_ptr_byte_offset(base_addr, offset);
188+
let llval = bx.const_bitcast(llval, ty);
189+
bx.load_operand(PlaceRef::new_sized(llval, layout))
190+
}
191+
}
192+
}
193+
125194
/// Asserts that this operand refers to a scalar and returns
126195
/// a reference to its value.
127196
pub fn immediate(self) -> V {

compiler/rustc_codegen_ssa/src/traits/consts.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use super::BackendTypes;
2-
use crate::mir::place::PlaceRef;
32
use rustc_middle::mir::interpret::{ConstAllocation, Scalar};
4-
use rustc_middle::ty::layout::TyAndLayout;
5-
use rustc_target::abi::{self, Size};
3+
use rustc_target::abi;
64

75
pub trait ConstMethods<'tcx>: BackendTypes {
86
// Constant constructors
@@ -30,12 +28,8 @@ pub trait ConstMethods<'tcx>: BackendTypes {
3028
fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value;
3129

3230
fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value;
33-
fn from_const_alloc(
34-
&self,
35-
layout: TyAndLayout<'tcx>,
36-
alloc: ConstAllocation<'tcx>,
37-
offset: Size,
38-
) -> PlaceRef<'tcx, Self::Value>;
3931

4032
fn const_ptrcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
33+
fn const_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
34+
fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value;
4135
}

tests/codegen/const_scalar_pair.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: --crate-type=lib -Copt-level=0 -Zmir-opt-level=0 -C debuginfo=2
2+
3+
#![feature(inline_const)]
4+
5+
pub fn foo() -> (i32, i32) {
6+
// CHECK: ret { i32, i32 } { i32 1, i32 2 }
7+
const { (1, 2) }
8+
}

0 commit comments

Comments
 (0)