Skip to content

Commit d2d35db

Browse files
committed
auto merge of #20755 : dotdash/rust/fca, r=Aatch
Currently, small aggregates are passed to functions as immediate values as is. This has two consequences. One is that aggregates are passed component-wise by LLVM, so e.g. a struct containing four u8 values (e.g. an RGBA struct) will be passed as four individual values. The other is that LLVM isn't very good at optimizing loads/stores of first class attributes. What clang does is converting the aggregate to an appropriately sized integer type (e.g. i32 for the four u8 values), and using that for the function argument. This allows LLVM to create code that is a lot better. Fixes #20450 #20149 #16506 #13927
2 parents 431105a + 6f1b06e commit d2d35db

File tree

7 files changed

+59
-13
lines changed

7 files changed

+59
-13
lines changed

src/librustc_trans/trans/base.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,11 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
10541054
C_undef(type_of::type_of(cx.ccx(), t))
10551055
} else if ty::type_is_bool(t) {
10561056
Trunc(cx, LoadRangeAssert(cx, ptr, 0, 2, llvm::False), Type::i1(cx.ccx()))
1057+
} else if type_is_immediate(cx.ccx(), t) && type_of::type_of(cx.ccx(), t).is_aggregate() {
1058+
// We want to pass small aggregates as immediate values, but using an aggregate LLVM type
1059+
// for this leads to bad optimizations, so its arg type is an appropriately sized integer
1060+
// and we have to convert it
1061+
Load(cx, BitCast(cx, ptr, type_of::arg_type_of(cx.ccx(), t).ptr_to()))
10571062
} else if ty::type_is_char(t) {
10581063
// a char is a Unicode codepoint, and so takes values from 0
10591064
// to 0x10FFFF inclusive only.
@@ -1065,9 +1070,14 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
10651070

10661071
/// Helper for storing values in memory. Does the necessary conversion if the in-memory type
10671072
/// differs from the type used for SSA values.
1068-
pub fn store_ty(cx: Block, v: ValueRef, dst: ValueRef, t: Ty) {
1073+
pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t: Ty<'tcx>) {
10691074
if ty::type_is_bool(t) {
10701075
Store(cx, ZExt(cx, v, Type::i8(cx.ccx())), dst);
1076+
} else if type_is_immediate(cx.ccx(), t) && type_of::type_of(cx.ccx(), t).is_aggregate() {
1077+
// We want to pass small aggregates as immediate values, but using an aggregate LLVM type
1078+
// for this leads to bad optimizations, so its arg type is an appropriately sized integer
1079+
// and we have to convert it
1080+
Store(cx, v, BitCast(cx, dst, type_of::arg_type_of(cx.ccx(), t).ptr_to()));
10711081
} else {
10721082
Store(cx, v, dst);
10731083
};

src/librustc_trans/trans/common.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,7 @@ fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
222222
match ty.sty {
223223
ty::ty_struct(def_id, substs) => {
224224
let fields = ty::struct_fields(ccx.tcx(), def_id, substs);
225-
fields.len() == 1 &&
226-
fields[0].name ==
227-
token::special_idents::unnamed_field.name &&
228-
type_is_immediate(ccx, fields[0].mt.ty)
225+
fields.len() == 1 && type_is_immediate(ccx, fields[0].mt.ty)
229226
}
230227
_ => false
231228
}
@@ -247,7 +244,7 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
247244
return false;
248245
}
249246
match ty.sty {
250-
ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) |
247+
ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) | ty::ty_vec(_, Some(_)) |
251248
ty::ty_unboxed_closure(..) => {
252249
let llty = sizing_type_of(ccx, ty);
253250
llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type())

src/librustc_trans/trans/foreign.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,13 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
736736
if ty::type_is_bool(rust_ty) {
737737
let tmp = builder.load_range_assert(llforeign_arg, 0, 2, llvm::False);
738738
builder.trunc(tmp, Type::i1(ccx))
739+
} else if type_of::type_of(ccx, rust_ty).is_aggregate() {
740+
// We want to pass small aggregates as immediate values, but using an aggregate
741+
// LLVM type for this leads to bad optimizations, so its arg type is an
742+
// appropriately sized integer and we have to convert it
743+
let tmp = builder.bitcast(llforeign_arg,
744+
type_of::arg_type_of(ccx, rust_ty).ptr_to());
745+
builder.load(tmp)
739746
} else {
740747
builder.load(llforeign_arg)
741748
}
@@ -834,10 +841,10 @@ fn foreign_signature<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
834841
fn_sig: &ty::FnSig<'tcx>,
835842
arg_tys: &[Ty<'tcx>])
836843
-> LlvmSignature {
837-
let llarg_tys = arg_tys.iter().map(|&arg| arg_type_of(ccx, arg)).collect();
844+
let llarg_tys = arg_tys.iter().map(|&arg| foreign_arg_type_of(ccx, arg)).collect();
838845
let (llret_ty, ret_def) = match fn_sig.output {
839846
ty::FnConverging(ret_ty) =>
840-
(type_of::arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)),
847+
(type_of::foreign_arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)),
841848
ty::FnDiverging =>
842849
(Type::nil(ccx), false)
843850
};

src/librustc_trans/trans/glue.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
138138
-> Block<'blk, 'tcx> {
139139
let _icx = push_ctxt("drop_ty_immediate");
140140
let vp = alloca(bcx, type_of(bcx.ccx(), t), "");
141-
Store(bcx, v, vp);
141+
store_ty(bcx, v, vp, t);
142142
drop_ty(bcx, vp, t, source_location)
143143
}
144144

src/librustc_trans/trans/intrinsic.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,11 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
357357
&ccx.link_meta().crate_hash);
358358
// NB: This needs to be kept in lockstep with the TypeId struct in
359359
// the intrinsic module
360-
C_named_struct(llret_ty, &[C_u64(ccx, hash)])
360+
C_u64(ccx, hash)
361361
}
362362
(_, "init") => {
363363
let tp_ty = *substs.types.get(FnSpace, 0);
364-
let lltp_ty = type_of::type_of(ccx, tp_ty);
364+
let lltp_ty = type_of::arg_type_of(ccx, tp_ty);
365365
if return_type_is_void(ccx, tp_ty) {
366366
C_nil(ccx)
367367
} else {
@@ -686,6 +686,11 @@ fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, name: &'static st
686686
let ret = C_undef(type_of::type_of(bcx.ccx(), t));
687687
let ret = InsertValue(bcx, ret, result, 0);
688688
let ret = InsertValue(bcx, ret, overflow, 1);
689-
690-
ret
689+
if type_is_immediate(bcx.ccx(), t) {
690+
let tmp = alloc_ty(bcx, t, "tmp");
691+
Store(bcx, ret, tmp);
692+
load_ty(bcx, tmp, t)
693+
} else {
694+
ret
695+
}
691696
}

src/librustc_trans/trans/type_.rs

+12
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ impl Type {
8282
ty!(llvm::LLVMInt64TypeInContext(ccx.llcx()))
8383
}
8484

85+
// Creates an integer type with the given number of bits, e.g. i24
86+
pub fn ix(ccx: &CrateContext, num_bits: u64) -> Type {
87+
ty!(llvm::LLVMIntTypeInContext(ccx.llcx(), num_bits as c_uint))
88+
}
89+
8590
pub fn f32(ccx: &CrateContext) -> Type {
8691
ty!(llvm::LLVMFloatTypeInContext(ccx.llcx()))
8792
}
@@ -260,6 +265,13 @@ impl Type {
260265
ty!(llvm::LLVMPointerType(self.to_ref(), 0))
261266
}
262267

268+
pub fn is_aggregate(&self) -> bool {
269+
match self.kind() {
270+
TypeKind::Struct | TypeKind::Array => true,
271+
_ => false
272+
}
273+
}
274+
263275
pub fn is_packed(&self) -> bool {
264276
unsafe {
265277
llvm::LLVMIsPackedStruct(self.to_ref()) == True

src/librustc_trans/trans/type_of.rs

+15
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,24 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
243243
llsizingty
244244
}
245245

246+
pub fn foreign_arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
247+
if ty::type_is_bool(t) {
248+
Type::i1(cx)
249+
} else {
250+
type_of(cx, t)
251+
}
252+
}
253+
246254
pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
247255
if ty::type_is_bool(t) {
248256
Type::i1(cx)
257+
} else if type_is_immediate(cx, t) && type_of(cx, t).is_aggregate() {
258+
// We want to pass small aggregates as immediate values, but using an aggregate LLVM type
259+
// for this leads to bad optimizations, so its arg type is an appropriately sized integer
260+
match machine::llsize_of_alloc(cx, sizing_type_of(cx, t)) {
261+
0 => type_of(cx, t),
262+
n => Type::ix(cx, n * 8),
263+
}
249264
} else {
250265
type_of(cx, t)
251266
}

0 commit comments

Comments
 (0)