Skip to content

Commit 77c9c08

Browse files
authored
Merge pull request #34533 from alexcrichton/beta-next
Backport #34141 to beta
2 parents d18e321 + da85ffb commit 77c9c08

File tree

5 files changed

+123
-128
lines changed

5 files changed

+123
-128
lines changed

src/librustc_trans/abi.rs

+53-15
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
use llvm::{self, ValueRef};
1212
use base;
13-
use builder::Builder;
14-
use common::{type_is_fat_ptr, BlockAndBuilder};
13+
use build::AllocaFcx;
14+
use common::{type_is_fat_ptr, BlockAndBuilder, C_uint};
1515
use context::CrateContext;
1616
use cabi_x86;
1717
use cabi_x86_64;
@@ -22,14 +22,15 @@ use cabi_powerpc;
2222
use cabi_powerpc64;
2323
use cabi_mips;
2424
use cabi_asmjs;
25-
use machine::{llalign_of_min, llsize_of, llsize_of_real};
25+
use machine::{llalign_of_min, llsize_of, llsize_of_real, llsize_of_store};
2626
use type_::Type;
2727
use type_of;
2828

2929
use rustc::hir;
3030
use rustc::ty::{self, Ty};
3131

3232
use libc::c_uint;
33+
use std::cmp;
3334

3435
pub use syntax::abi::Abi;
3536
pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
@@ -150,26 +151,63 @@ impl ArgType {
150151
/// lvalue for the original Rust type of this argument/return.
151152
/// Can be used for both storing formal arguments into Rust variables
152153
/// or results of call/invoke instructions into their destinations.
153-
pub fn store(&self, b: &Builder, mut val: ValueRef, dst: ValueRef) {
154+
pub fn store(&self, bcx: &BlockAndBuilder, mut val: ValueRef, dst: ValueRef) {
154155
if self.is_ignore() {
155156
return;
156157
}
158+
let ccx = bcx.ccx();
157159
if self.is_indirect() {
158-
let llsz = llsize_of(b.ccx, self.ty);
159-
let llalign = llalign_of_min(b.ccx, self.ty);
160-
base::call_memcpy(b, dst, val, llsz, llalign as u32);
160+
let llsz = llsize_of(ccx, self.ty);
161+
let llalign = llalign_of_min(ccx, self.ty);
162+
base::call_memcpy(bcx, dst, val, llsz, llalign as u32);
161163
} else if let Some(ty) = self.cast {
162-
let cast_dst = b.pointercast(dst, ty.ptr_to());
163-
let store = b.store(val, cast_dst);
164-
let llalign = llalign_of_min(b.ccx, self.ty);
165-
unsafe {
166-
llvm::LLVMSetAlignment(store, llalign);
164+
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
165+
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
166+
let can_store_through_cast_ptr = false;
167+
if can_store_through_cast_ptr {
168+
let cast_dst = bcx.pointercast(dst, ty.ptr_to());
169+
let store = bcx.store(val, cast_dst);
170+
let llalign = llalign_of_min(ccx, self.ty);
171+
unsafe {
172+
llvm::LLVMSetAlignment(store, llalign);
173+
}
174+
} else {
175+
// The actual return type is a struct, but the ABI
176+
// adaptation code has cast it into some scalar type. The
177+
// code that follows is the only reliable way I have
178+
// found to do a transform like i64 -> {i32,i32}.
179+
// Basically we dump the data onto the stack then memcpy it.
180+
//
181+
// Other approaches I tried:
182+
// - Casting rust ret pointer to the foreign type and using Store
183+
// is (a) unsafe if size of foreign type > size of rust type and
184+
// (b) runs afoul of strict aliasing rules, yielding invalid
185+
// assembly under -O (specifically, the store gets removed).
186+
// - Truncating foreign type to correct integral type and then
187+
// bitcasting to the struct type yields invalid cast errors.
188+
189+
// We instead thus allocate some scratch space...
190+
let llscratch = AllocaFcx(bcx.fcx(), ty, "abi_cast");
191+
base::Lifetime::Start.call(bcx, llscratch);
192+
193+
// ...where we first store the value...
194+
bcx.store(val, llscratch);
195+
196+
// ...and then memcpy it to the intended destination.
197+
base::call_memcpy(bcx,
198+
bcx.pointercast(dst, Type::i8p(ccx)),
199+
bcx.pointercast(llscratch, Type::i8p(ccx)),
200+
C_uint(ccx, llsize_of_store(ccx, self.ty)),
201+
cmp::min(llalign_of_min(ccx, self.ty),
202+
llalign_of_min(ccx, ty)) as u32);
203+
204+
base::Lifetime::End.call(bcx, llscratch);
167205
}
168206
} else {
169-
if self.original_ty == Type::i1(b.ccx) {
170-
val = b.zext(val, Type::i8(b.ccx));
207+
if self.original_ty == Type::i1(ccx) {
208+
val = bcx.zext(val, Type::i8(ccx));
171209
}
172-
b.store(val, dst);
210+
bcx.store(val, dst);
173211
}
174212
}
175213

src/librustc_trans/base.rs

+42-52
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,7 @@ pub fn with_cond<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, val: ValueRef, f: F) ->
10421042
next_cx
10431043
}
10441044

1045-
enum Lifetime { Start, End }
1045+
pub enum Lifetime { Start, End }
10461046

10471047
// If LLVM lifetime intrinsic support is enabled (i.e. optimizations
10481048
// on), and `ptr` is nonzero-sized, then extracts the size of `ptr`
@@ -1079,24 +1079,25 @@ fn core_lifetime_emit<'blk, 'tcx, F>(ccx: &'blk CrateContext<'blk, 'tcx>,
10791079
emit(ccx, size, lifetime_intrinsic)
10801080
}
10811081

1082-
pub fn call_lifetime_start(cx: Block, ptr: ValueRef) {
1083-
core_lifetime_emit(cx.ccx(), ptr, Lifetime::Start, |ccx, size, lifetime_start| {
1084-
let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
1085-
Call(cx,
1086-
lifetime_start,
1087-
&[C_u64(ccx, size), ptr],
1088-
DebugLoc::None);
1089-
})
1082+
impl Lifetime {
1083+
pub fn call(self, b: &Builder, ptr: ValueRef) {
1084+
core_lifetime_emit(b.ccx, ptr, self, |ccx, size, lifetime_intrinsic| {
1085+
let ptr = b.pointercast(ptr, Type::i8p(ccx));
1086+
b.call(lifetime_intrinsic, &[C_u64(ccx, size), ptr], None);
1087+
});
1088+
}
10901089
}
10911090

1092-
pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
1093-
core_lifetime_emit(cx.ccx(), ptr, Lifetime::End, |ccx, size, lifetime_end| {
1094-
let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
1095-
Call(cx,
1096-
lifetime_end,
1097-
&[C_u64(ccx, size), ptr],
1098-
DebugLoc::None);
1099-
})
1091+
pub fn call_lifetime_start(bcx: Block, ptr: ValueRef) {
1092+
if !bcx.unreachable.get() {
1093+
Lifetime::Start.call(&bcx.build(), ptr);
1094+
}
1095+
}
1096+
1097+
pub fn call_lifetime_end(bcx: Block, ptr: ValueRef) {
1098+
if !bcx.unreachable.get() {
1099+
Lifetime::End.call(&bcx.build(), ptr);
1100+
}
11001101
}
11011102

11021103
// Generates code for resumption of unwind at the end of a landing pad.
@@ -1663,29 +1664,21 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
16631664
arg_ty,
16641665
datum::Lvalue::new("FunctionContext::bind_args"))
16651666
} else {
1666-
let lltmp = if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
1667-
let lltemp = alloc_ty(bcx, arg_ty, "");
1668-
let b = &bcx.build();
1669-
// we pass fat pointers as two words, but we want to
1670-
// represent them internally as a pointer to two words,
1671-
// so make an alloca to store them in.
1672-
let meta = &self.fn_ty.args[idx];
1673-
idx += 1;
1674-
arg.store_fn_arg(b, &mut llarg_idx, expr::get_dataptr(bcx, lltemp));
1675-
meta.store_fn_arg(b, &mut llarg_idx, expr::get_meta(bcx, lltemp));
1676-
lltemp
1677-
} else {
1678-
// otherwise, arg is passed by value, so store it into a temporary.
1679-
let llarg_ty = arg.cast.unwrap_or(arg.memory_ty(bcx.ccx()));
1680-
let lltemp = alloca(bcx, llarg_ty, "");
1667+
unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "",
1668+
uninit_reason,
1669+
arg_scope_id, |bcx, dst| {
1670+
debug!("FunctionContext::bind_args: {:?}: {:?}", hir_arg, arg_ty);
16811671
let b = &bcx.build();
1682-
arg.store_fn_arg(b, &mut llarg_idx, lltemp);
1683-
// And coerce the temporary into the type we expect.
1684-
b.pointercast(lltemp, arg.memory_ty(bcx.ccx()).ptr_to())
1685-
};
1686-
bcx.fcx.schedule_drop_mem(arg_scope_id, lltmp, arg_ty, None);
1687-
datum::Datum::new(lltmp, arg_ty,
1688-
datum::Lvalue::new("bind_args"))
1672+
if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
1673+
let meta = &self.fn_ty.args[idx];
1674+
idx += 1;
1675+
arg.store_fn_arg(b, &mut llarg_idx, expr::get_dataptr(bcx, dst));
1676+
meta.store_fn_arg(b, &mut llarg_idx, expr::get_meta(bcx, dst));
1677+
} else {
1678+
arg.store_fn_arg(b, &mut llarg_idx, dst);
1679+
}
1680+
bcx
1681+
}))
16891682
}
16901683
} else {
16911684
// FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
@@ -1720,19 +1713,16 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
17201713
};
17211714

17221715
let pat = &hir_arg.pat;
1723-
bcx = match simple_name(pat) {
1724-
// The check for alloca is necessary because above for the immediate argument case
1725-
// we had to cast. At this point arg_datum is not an alloca anymore and thus
1726-
// breaks debuginfo if we allow this optimisation.
1727-
Some(name)
1728-
if unsafe { llvm::LLVMIsAAllocaInst(arg_datum.val) != ::std::ptr::null_mut() } => {
1729-
// Generate nicer LLVM for the common case of fn a pattern
1730-
// like `x: T`
1731-
set_value_name(arg_datum.val, &bcx.name(name));
1732-
self.lllocals.borrow_mut().insert(pat.id, arg_datum);
1733-
bcx
1734-
},
1735-
_ => _match::bind_irrefutable_pat(bcx, pat, arg_datum.match_input(), arg_scope_id)
1716+
bcx = if let Some(name) = simple_name(pat) {
1717+
// Generate nicer LLVM for the common case of fn a pattern
1718+
// like `x: T`
1719+
set_value_name(arg_datum.val, &bcx.name(name));
1720+
self.lllocals.borrow_mut().insert(pat.id, arg_datum);
1721+
bcx
1722+
} else {
1723+
// General path. Copy out the values that are used in the
1724+
// pattern.
1725+
_match::bind_irrefutable_pat(bcx, pat, arg_datum.match_input(), arg_scope_id)
17361726
};
17371727
debuginfo::create_argument_metadata(bcx, hir_arg);
17381728
}

src/librustc_trans/callee.rs

+10-46
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ use build::*;
3434
use cleanup;
3535
use cleanup::CleanupMethods;
3636
use closure;
37-
use common::{self, Block, Result, CrateContext, FunctionContext};
38-
use common::{C_uint, C_undef};
37+
use common::{self, Block, Result, CrateContext, FunctionContext, C_undef};
3938
use consts;
4039
use datum::*;
4140
use debuginfo::DebugLoc;
@@ -44,7 +43,7 @@ use expr;
4443
use glue;
4544
use inline;
4645
use intrinsic;
47-
use machine::{llalign_of_min, llsize_of_store};
46+
use machine::llalign_of_min;
4847
use meth;
4948
use monomorphize::{self, Instance};
5049
use type_::Type;
@@ -58,8 +57,6 @@ use syntax::codemap::DUMMY_SP;
5857
use syntax::errors;
5958
use syntax::ptr::P;
6059

61-
use std::cmp;
62-
6360
#[derive(Debug)]
6461
pub enum CalleeData {
6562
/// Constructor for enum variant/tuple-like-struct.
@@ -708,49 +705,16 @@ fn trans_call_inner<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
708705
let (llret, mut bcx) = base::invoke(bcx, llfn, &llargs, debug_loc);
709706
if !bcx.unreachable.get() {
710707
fn_ty.apply_attrs_callsite(llret);
711-
}
712708

713-
// If the function we just called does not use an outpointer,
714-
// store the result into the rust outpointer. Cast the outpointer
715-
// type to match because some ABIs will use a different type than
716-
// the Rust type. e.g., a {u32,u32} struct could be returned as
717-
// u64.
718-
if !fn_ty.ret.is_ignore() && !fn_ty.ret.is_indirect() {
719-
if let Some(llforeign_ret_ty) = fn_ty.ret.cast {
720-
let llrust_ret_ty = fn_ty.ret.original_ty;
721-
let llretslot = opt_llretslot.unwrap();
722-
723-
// The actual return type is a struct, but the ABI
724-
// adaptation code has cast it into some scalar type. The
725-
// code that follows is the only reliable way I have
726-
// found to do a transform like i64 -> {i32,i32}.
727-
// Basically we dump the data onto the stack then memcpy it.
728-
//
729-
// Other approaches I tried:
730-
// - Casting rust ret pointer to the foreign type and using Store
731-
// is (a) unsafe if size of foreign type > size of rust type and
732-
// (b) runs afoul of strict aliasing rules, yielding invalid
733-
// assembly under -O (specifically, the store gets removed).
734-
// - Truncating foreign type to correct integral type and then
735-
// bitcasting to the struct type yields invalid cast errors.
736-
let llscratch = base::alloca(bcx, llforeign_ret_ty, "__cast");
737-
base::call_lifetime_start(bcx, llscratch);
738-
Store(bcx, llret, llscratch);
739-
let llscratch_i8 = PointerCast(bcx, llscratch, Type::i8(ccx).ptr_to());
740-
let llretptr_i8 = PointerCast(bcx, llretslot, Type::i8(ccx).ptr_to());
741-
let llrust_size = llsize_of_store(ccx, llrust_ret_ty);
742-
let llforeign_align = llalign_of_min(ccx, llforeign_ret_ty);
743-
let llrust_align = llalign_of_min(ccx, llrust_ret_ty);
744-
let llalign = cmp::min(llforeign_align, llrust_align);
745-
debug!("llrust_size={}", llrust_size);
746-
747-
if !bcx.unreachable.get() {
748-
base::call_memcpy(&B(bcx), llretptr_i8, llscratch_i8,
749-
C_uint(ccx, llrust_size), llalign as u32);
709+
// If the function we just called does not use an outpointer,
710+
// store the result into the rust outpointer. Cast the outpointer
711+
// type to match because some ABIs will use a different type than
712+
// the Rust type. e.g., a {u32,u32} struct could be returned as
713+
// u64.
714+
if !fn_ty.ret.is_indirect() {
715+
if let Some(llretslot) = opt_llretslot {
716+
fn_ty.ret.store(&bcx.build(), llret, llretslot);
750717
}
751-
base::call_lifetime_end(bcx, llscratch);
752-
} else if let Some(llretslot) = opt_llretslot {
753-
base::store_ty(bcx, llret, llretslot, output.unwrap());
754718
}
755719
}
756720

src/librustc_trans/mir/mod.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -327,28 +327,23 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
327327
llarg_idx += 1;
328328
llarg
329329
} else {
330+
let lltemp = bcx.with_block(|bcx| {
331+
base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
332+
});
330333
if common::type_is_fat_ptr(tcx, arg_ty) {
331-
let lltemp = bcx.with_block(|bcx| {
332-
base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
333-
});
334334
// we pass fat pointers as two words, but we want to
335335
// represent them internally as a pointer to two words,
336336
// so make an alloca to store them in.
337337
let meta = &fcx.fn_ty.args[idx];
338338
idx += 1;
339339
arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, lltemp));
340340
meta.store_fn_arg(bcx, &mut llarg_idx, get_meta(bcx, lltemp));
341-
lltemp
342341
} else {
343-
// otherwise, arg is passed by value, so store it into a temporary.
344-
let llarg_ty = arg.cast.unwrap_or(arg.memory_ty(bcx.ccx()));
345-
let lltemp = bcx.with_block(|bcx| {
346-
base::alloca(bcx, llarg_ty, &format!("arg{}", arg_index))
347-
});
342+
// otherwise, arg is passed by value, so make a
343+
// temporary and store it there
348344
arg.store_fn_arg(bcx, &mut llarg_idx, lltemp);
349-
// And coerce the temporary into the type we expect.
350-
bcx.pointercast(lltemp, arg.memory_ty(bcx.ccx()).ptr_to())
351345
}
346+
lltemp
352347
};
353348
bcx.with_block(|bcx| arg_scope.map(|scope| {
354349
// Is this a regular argument?

src/test/codegen/stores.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ pub struct Bytes {
2626
#[no_mangle]
2727
#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
2828
pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) {
29-
// CHECK: store i32 %{{.*}}, i32* %{{.*}}, align 1
30-
// CHECK: [[VAR:%[0-9]+]] = bitcast i32* %{{.*}} to [4 x i8]*
29+
// CHECK: %y = alloca [4 x i8]
30+
// CHECK: [[TMP:%.+]] = alloca i32
31+
// CHECK: store i32 %1, i32* [[TMP]]
32+
// CHECK: [[Y8:%[0-9]+]] = bitcast [4 x i8]* %y to i8*
33+
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
34+
// CHECK: call void @llvm.memcpy.{{.*}}(i8* [[Y8]], i8* [[TMP8]], i{{[0-9]+}} 4, i32 1, i1 false)
3135
*x = y;
3236
}
3337

@@ -37,7 +41,11 @@ pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) {
3741
#[no_mangle]
3842
#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
3943
pub fn small_struct_alignment(x: &mut Bytes, y: Bytes) {
40-
// CHECK: store i32 %{{.*}}, i32* %{{.*}}, align 1
41-
// CHECK: [[VAR:%[0-9]+]] = bitcast i32* %{{.*}} to %Bytes*
44+
// CHECK: %y = alloca %Bytes
45+
// CHECK: [[TMP:%.+]] = alloca i32
46+
// CHECK: store i32 %1, i32* [[TMP]]
47+
// CHECK: [[Y8:%[0-9]+]] = bitcast %Bytes* %y to i8*
48+
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
49+
// CHECK: call void @llvm.memcpy.{{.*}}(i8* [[Y8]], i8* [[TMP8]], i{{[0-9]+}} 4, i32 1, i1 false)
4250
*x = y;
4351
}

0 commit comments

Comments
 (0)