Skip to content

Commit 800f2c1

Browse files
committed
Implement simple codegen for unsized rvalues.
1 parent e2b95cb commit 800f2c1

File tree

15 files changed

+388
-20
lines changed

15 files changed

+388
-20
lines changed

src/librustc_codegen_llvm/abi.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
189189
let cx = bx.cx;
190190
if self.is_indirect() {
191191
OperandValue::Ref(val, self.layout.align).store(bx, dst)
192+
} else if self.is_unsized_indirect() {
193+
bug!("unsized ArgType must be handled through store_fn_arg");
192194
} else if let PassMode::Cast(cast) = self.mode {
193195
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
194196
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
@@ -246,6 +248,9 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
246248
PassMode::Pair(..) => {
247249
OperandValue::Pair(next(), next()).store(bx, dst);
248250
}
251+
PassMode::UnsizedIndirect(..) => {
252+
OperandValue::UnsizedRef(next(), next()).store(bx, dst);
253+
}
249254
PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => {
250255
self.store(bx, next(), dst);
251256
}
@@ -302,6 +307,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
302307
// Don't pass the vtable, it's not an argument of the virtual fn.
303308
// Instead, pass just the (thin pointer) first field of `*dyn Trait`.
304309
if arg_idx == Some(0) {
310+
if layout.is_unsized() {
311+
unimplemented!("by-value trait object is not \
312+
yet implemented in #![feature(unsized_locals)]");
313+
}
305314
// FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g.
306315
// `Box<dyn Trait>` has a few newtype wrappers around the raw
307316
// pointer, so we'd have to "dig down" to find `*dyn Trait`.
@@ -538,7 +547,9 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
538547
}
539548

540549
let size = arg.layout.size;
541-
if size > layout::Pointer.size(cx) {
550+
if arg.layout.is_unsized() {
551+
arg.make_unsized_indirect(None);
552+
} else if size > layout::Pointer.size(cx) {
542553
arg.make_indirect();
543554
} else {
544555
// We want to pass small aggregates as immediates, but using
@@ -584,6 +595,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
584595
llargument_tys.push(self.ret.memory_ty(cx).ptr_to());
585596
Type::void(cx)
586597
}
598+
PassMode::UnsizedIndirect(..) => bug!("return type must be sized"),
587599
};
588600

589601
for arg in &self.args {
@@ -600,6 +612,13 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
600612
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
601613
continue;
602614
}
615+
PassMode::UnsizedIndirect(..) => {
616+
let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty);
617+
let ptr_layout = cx.layout_of(ptr_ty);
618+
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
619+
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
620+
continue;
621+
}
603622
PassMode::Cast(cast) => cast.llvm_type(cx),
604623
PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(),
605624
};
@@ -651,6 +670,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
651670
PassMode::Ignore => {}
652671
PassMode::Direct(ref attrs) |
653672
PassMode::Indirect(ref attrs) => apply(attrs),
673+
PassMode::UnsizedIndirect(ref attrs, ref extra_attrs) => {
674+
apply(attrs);
675+
apply(extra_attrs);
676+
}
654677
PassMode::Pair(ref a, ref b) => {
655678
apply(a);
656679
apply(b);
@@ -695,6 +718,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
695718
PassMode::Ignore => {}
696719
PassMode::Direct(ref attrs) |
697720
PassMode::Indirect(ref attrs) => apply(attrs),
721+
PassMode::UnsizedIndirect(ref attrs, ref extra_attrs) => {
722+
apply(attrs);
723+
apply(extra_attrs);
724+
}
698725
PassMode::Pair(ref a, ref b) => {
699726
apply(a);
700727
apply(b);

src/librustc_codegen_llvm/base.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ pub fn coerce_unsized_into(
295295
OperandValue::Immediate(base) => {
296296
unsize_thin_ptr(bx, base, src_ty, dst_ty)
297297
}
298-
OperandValue::Ref(..) => bug!()
298+
OperandValue::Ref(..) | OperandValue::UnsizedRef(..) => bug!()
299299
};
300300
OperandValue::Pair(base, info).store(bx, dst);
301301
};

src/librustc_codegen_llvm/mir/block.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use syntax_pos::Pos;
3232
use super::{FunctionCx, LocalRef};
3333
use super::place::PlaceRef;
3434
use super::operand::OperandRef;
35-
use super::operand::OperandValue::{Pair, Ref, Immediate};
35+
use super::operand::OperandValue::{Pair, Ref, UnsizedRef, Immediate};
3636

3737
impl FunctionCx<'a, 'll, 'tcx> {
3838
pub fn codegen_block(&mut self, bb: mir::BasicBlock) {
@@ -234,6 +234,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
234234
let op = self.codegen_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE));
235235
if let Ref(llval, align) = op.val {
236236
bx.load(llval, align)
237+
} else if let UnsizedRef(..) = op.val {
238+
bug!("return type must be sized");
237239
} else {
238240
op.immediate_or_packed_pair(&bx)
239241
}
@@ -249,6 +251,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
249251
layout: cg_place.layout
250252
}
251253
}
254+
LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
252255
};
253256
let llslot = match op.val {
254257
Immediate(_) | Pair(..) => {
@@ -261,11 +264,14 @@ impl FunctionCx<'a, 'll, 'tcx> {
261264
"return place is unaligned!");
262265
llval
263266
}
267+
UnsizedRef(..) => bug!("return type must be sized"),
264268
};
265269
bx.load(
266270
bx.pointercast(llslot, cast_ty.llvm_type(bx.cx).ptr_to()),
267271
self.fn_ty.ret.layout.align)
268272
}
273+
274+
PassMode::UnsizedIndirect(..) => bug!("return value must be sized"),
269275
};
270276
bx.ret(llval);
271277
}
@@ -607,6 +613,10 @@ impl FunctionCx<'a, 'll, 'tcx> {
607613
op.val.store(&bx, tmp);
608614
op.val = Ref(tmp.llval, tmp.align);
609615
}
616+
(&mir::Operand::Copy(_), UnsizedRef(..)) |
617+
(&mir::Operand::Constant(_), UnsizedRef(..)) => {
618+
bug!("tried to pass an unsized argument by copy or constant")
619+
}
610620
_ => {}
611621
}
612622

@@ -657,6 +667,15 @@ impl FunctionCx<'a, 'll, 'tcx> {
657667
}
658668
_ => bug!("codegen_argument: {:?} invalid for pair arugment", op)
659669
}
670+
} else if let PassMode::UnsizedIndirect(..) = arg.mode {
671+
match op.val {
672+
UnsizedRef(a, b) => {
673+
llargs.push(a);
674+
llargs.push(b);
675+
return;
676+
}
677+
_ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op)
678+
}
660679
}
661680

662681
// Force by-ref if we have to load through a cast pointer.
@@ -686,6 +705,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
686705
(llval, align, true)
687706
}
688707
}
708+
UnsizedRef(..) =>
709+
bug!("codegen_argument: tried to pass unsized operand to sized argument"),
689710
};
690711

691712
if by_ref && !arg.is_indirect() {
@@ -727,6 +748,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
727748
let field_ptr = tuple_ptr.project_field(bx, i);
728749
self.codegen_argument(bx, field_ptr.load(bx), llargs, &args[i]);
729750
}
751+
} else if let UnsizedRef(..) = tuple.val {
752+
bug!("closure arguments must be sized")
730753
} else {
731754
// If the tuple is immediate, the elements are as well.
732755
for i in 0..tuple.layout.fields.count() {
@@ -820,6 +843,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
820843
let dest = if let mir::Place::Local(index) = *dest {
821844
match self.locals[index] {
822845
LocalRef::Place(dest) => dest,
846+
LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
823847
LocalRef::Operand(None) => {
824848
// Handle temporary places, specifically Operand ones, as
825849
// they don't have allocas
@@ -871,6 +895,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
871895
if let mir::Place::Local(index) = *dst {
872896
match self.locals[index] {
873897
LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place),
898+
LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
874899
LocalRef::Operand(None) => {
875900
let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst));
876901
assert!(!dst_layout.ty.has_erasable_regions());

src/librustc_codegen_llvm/mir/mod.rs

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
180180

181181
enum LocalRef<'ll, 'tcx> {
182182
Place(PlaceRef<'ll, 'tcx>),
183+
/// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place).
184+
/// `*p` is the fat pointer that references the actual unsized place.
185+
/// Every time it is initialized, we have to reallocate the place
186+
/// and update the fat pointer. That's the reason why it is indirect.
187+
UnsizedPlace(PlaceRef<'ll, 'tcx>),
183188
Operand(Option<OperandRef<'ll, 'tcx>>),
184189
}
185190

@@ -275,17 +280,24 @@ pub fn codegen_mir(
275280
}
276281

277282
debug!("alloc: {:?} ({}) -> place", local, name);
278-
let place = PlaceRef::alloca(&bx, layout, &name.as_str());
279-
if dbg {
280-
let (scope, span) = fx.debug_loc(mir::SourceInfo {
281-
span: decl.source_info.span,
282-
scope: decl.visibility_scope,
283-
});
284-
declare_local(&bx, &fx.debug_context, name, layout.ty, scope.unwrap(),
285-
VariableAccess::DirectVariable { alloca: place.llval },
286-
VariableKind::LocalVariable, span);
283+
if layout.is_unsized() {
284+
let indirect_place =
285+
PlaceRef::alloca_unsized_indirect(&bx, layout, &name.as_str());
286+
// FIXME: add an appropriate debuginfo
287+
LocalRef::UnsizedPlace(indirect_place)
288+
} else {
289+
let place = PlaceRef::alloca(&bx, layout, &name.as_str());
290+
if dbg {
291+
let (scope, span) = fx.debug_loc(mir::SourceInfo {
292+
span: decl.source_info.span,
293+
scope: decl.visibility_scope,
294+
});
295+
declare_local(&bx, &fx.debug_context, name, layout.ty, scope.unwrap(),
296+
VariableAccess::DirectVariable { alloca: place.llval },
297+
VariableKind::LocalVariable, span);
298+
}
299+
LocalRef::Place(place)
287300
}
288-
LocalRef::Place(place)
289301
} else {
290302
// Temporary or return place
291303
if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() {
@@ -294,7 +306,13 @@ pub fn codegen_mir(
294306
LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align))
295307
} else if memory_locals.contains(local) {
296308
debug!("alloc: {:?} -> place", local);
297-
LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local)))
309+
if layout.is_unsized() {
310+
let indirect_place =
311+
PlaceRef::alloca_unsized_indirect(&bx, layout, &format!("{:?}", local));
312+
LocalRef::UnsizedPlace(indirect_place)
313+
} else {
314+
LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local)))
315+
}
298316
} else {
299317
// If this is an immediate local, we do not create an
300318
// alloca in advance. Instead we wait until we see the
@@ -531,6 +549,18 @@ fn arg_local_refs(
531549
bx.set_value_name(llarg, &name);
532550
llarg_idx += 1;
533551
PlaceRef::new_sized(llarg, arg.layout, arg.layout.align)
552+
} else if arg.is_unsized_indirect() {
553+
// As the storage for the indirect argument lives during
554+
// the whole function call, we just copy the fat pointer.
555+
let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint);
556+
llarg_idx += 1;
557+
let llextra = llvm::get_param(bx.llfn(), llarg_idx as c_uint);
558+
llarg_idx += 1;
559+
let indirect_operand = OperandValue::Pair(llarg, llextra);
560+
561+
let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout, &name);
562+
indirect_operand.store(&bx, tmp);
563+
tmp
534564
} else {
535565
let tmp = PlaceRef::alloca(bx, arg.layout, &name);
536566
arg.store_fn_arg(bx, &mut llarg_idx, tmp);
@@ -632,7 +662,11 @@ fn arg_local_refs(
632662
);
633663
}
634664
});
635-
LocalRef::Place(place)
665+
if arg.is_unsized_indirect() {
666+
LocalRef::UnsizedPlace(place)
667+
} else {
668+
LocalRef::Place(place)
669+
}
636670
}).collect()
637671
}
638672

src/librustc_codegen_llvm/mir/operand.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use common::{CodegenCx, C_undef, C_usize};
2121
use builder::{Builder, MemFlags};
2222
use value::Value;
2323
use type_of::LayoutLlvmExt;
24+
use type_::Type;
25+
use glue;
2426

2527
use std::fmt;
2628

@@ -36,6 +38,10 @@ pub enum OperandValue<'ll> {
3638
/// A reference to the actual operand. The data is guaranteed
3739
/// to be valid for the operand's lifetime.
3840
Ref(&'ll Value, Align),
41+
/// A reference to the unsized operand. The data is guaranteed
42+
/// to be valid for the operand's lifetime.
43+
/// The second field is the extra.
44+
UnsizedRef(&'ll Value, &'ll Value),
3945
/// A single LLVM value.
4046
Immediate(&'ll Value),
4147
/// A pair of immediate LLVM values. Used by fat pointers too.
@@ -148,7 +154,8 @@ impl OperandRef<'ll, 'tcx> {
148154
let (llptr, llextra) = match self.val {
149155
OperandValue::Immediate(llptr) => (llptr, None),
150156
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
151-
OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self)
157+
OperandValue::Ref(..) |
158+
OperandValue::UnsizedRef(..) => bug!("Deref of by-Ref operand {:?}", self)
152159
};
153160
let layout = cx.layout_of(projected_ty);
154161
PlaceRef {
@@ -243,7 +250,8 @@ impl OperandRef<'ll, 'tcx> {
243250
*a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0, true));
244251
*b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1, true));
245252
}
246-
OperandValue::Ref(..) => bug!()
253+
OperandValue::Ref(..) |
254+
OperandValue::UnsizedRef(..) => bug!()
247255
}
248256

249257
OperandRef {
@@ -287,6 +295,9 @@ impl OperandValue<'ll> {
287295
base::memcpy_ty(bx, dest.llval, r, dest.layout,
288296
source_align.min(dest.align), flags)
289297
}
298+
OperandValue::UnsizedRef(..) => {
299+
bug!("cannot directly store unsized values");
300+
}
290301
OperandValue::Immediate(s) => {
291302
let val = base::from_immediate(bx, s);
292303
bx.store_with_flags(val, dest.llval, dest.align, flags);
@@ -300,6 +311,35 @@ impl OperandValue<'ll> {
300311
}
301312
}
302313
}
314+
315+
pub fn store_unsized(self, bx: &Builder<'a, 'll, 'tcx>, indirect_dest: PlaceRef<'ll, 'tcx>) {
316+
debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest);
317+
let flags = MemFlags::empty();
318+
319+
// `indirect_dest` must have `*mut T` type. We extract `T` out of it.
320+
let unsized_ty = indirect_dest.layout.ty.builtin_deref(true)
321+
.unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest)).ty;
322+
323+
let (llptr, llextra) =
324+
if let OperandValue::UnsizedRef(llptr, llextra) = self {
325+
(llptr, llextra)
326+
} else {
327+
bug!("store_unsized called with a sized value")
328+
};
329+
330+
// FIXME: choose an appropriate alignment, or use dynamic align somehow
331+
let max_align = Align::from_bits(128, 128).unwrap();
332+
let min_align = Align::from_bits(8, 8).unwrap();
333+
334+
// Allocate an appropriate region on the stack, and copy the value into it
335+
let (llsize, _) = glue::size_and_align_of_dst(&bx, unsized_ty, Some(llextra));
336+
let lldst = bx.array_alloca(Type::i8(bx.cx), llsize, "unsized_tmp", max_align);
337+
base::call_memcpy(&bx, lldst, llptr, llsize, min_align, flags);
338+
339+
// Store the allocated region and the extra to the indirect place.
340+
let indirect_operand = OperandValue::Pair(lldst, llextra);
341+
indirect_operand.store(&bx, indirect_dest);
342+
}
303343
}
304344

305345
impl FunctionCx<'a, 'll, 'tcx> {
@@ -320,7 +360,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
320360
LocalRef::Operand(None) => {
321361
bug!("use of {:?} before def", place);
322362
}
323-
LocalRef::Place(..) => {
363+
LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
324364
// use path below
325365
}
326366
}

0 commit comments

Comments
 (0)