Skip to content

Commit 7bf40e2

Browse files
committed
Project a non-fixed-size initial element using a bitcast instead
of a GEP. LLVM does not permit GEPs over unsized types, even if the index is a constant zero. This makes simple code involving generic tuples work. Swift SVN r4969
1 parent 1013781 commit 7bf40e2

File tree

3 files changed

+81
-5
lines changed

3 files changed

+81
-5
lines changed

Diff for: lib/IRGen/StructLayout.cpp

+19-1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ Address ElementLayout::project(IRGenFunction &IGF, Address baseAddr,
137137
return IGF.emitByteOffsetGEP(baseAddr.getAddress(), offset, getType(),
138138
baseAddr.getAddress()->getName() + suffix);
139139
}
140+
141+
case Kind::InitialNonFixedSize:
142+
return IGF.Builder.CreateBitCast(baseAddr,
143+
getType().getStorageType()->getPointerTo(),
144+
baseAddr.getAddress()->getName() + suffix);
140145
}
141146
llvm_unreachable("bad element layout kind");
142147
}
@@ -239,7 +244,7 @@ void StructLayoutBuilder::addNonFixedSizeElement(ElementLayout &elt) {
239244
// structure, we can assign it a fixed offset (namely zero) despite
240245
// it not having a fixed size/alignment.
241246
if (isFixedLayout() && CurSize.isZero()) {
242-
addElementAtFixedOffset(elt);
247+
addNonFixedSizeElementAtOffsetZero(elt);
243248
IsFixedLayout = false;
244249
return;
245250
}
@@ -277,6 +282,19 @@ void StructLayoutBuilder::addElementAtNonFixedOffset(ElementLayout &elt) {
277282
NextNonFixedOffsetIndex);
278283
}
279284

285+
/// Add a non-fixed-size element to the aggregate at offset zero.
286+
void StructLayoutBuilder::addNonFixedSizeElementAtOffsetZero(ElementLayout &elt) {
287+
assert(isFixedLayout());
288+
assert(!isa<FixedTypeInfo>(elt.getType()));
289+
assert(CurSize.isZero());
290+
elt.completeInitialNonFixedSize(elt.getType().isPOD(ResilienceScope::Local));
291+
292+
// Add the storage type to make the type unsized. We can't GEP to
293+
// this element, though, because LLVM forbids even 'gep 0' on an
294+
// unsized type.
295+
StructFields.push_back(elt.getType().getStorageType());
296+
}
297+
280298
/// Produce the current fields as an anonymous structure.
281299
llvm::StructType *StructLayoutBuilder::getAsAnonStruct() const {
282300
return llvm::StructType::get(IGM.getLLVMContext(), StructFields);

Diff for: lib/IRGen/StructLayout.h

+16-4
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,16 @@ class ElementLayout {
8989

9090
/// The element cannot be positioned at a fixed offset within the
9191
/// aggregate.
92-
NonFixed
92+
NonFixed,
93+
94+
/// The element is an object lacking a fixed size but located at
95+
/// offset zero. This is necessary because LLVM forbids even a
96+
/// 'gep 0' on an unsized type.
97+
InitialNonFixedSize
9398
};
9499

95100
private:
96-
enum : unsigned { IncompleteKind = 3 };
101+
enum : unsigned { IncompleteKind = 4 };
97102

98103
/// The swift type information for this element.
99104
const TypeInfo *Type;
@@ -103,14 +108,14 @@ class ElementLayout {
103108

104109
/// The index of this element, either in the LLVM struct (if fixed)
105110
/// or in the non-fixed elements array (if non-fixed).
106-
unsigned Index : 29;
111+
unsigned Index : 28;
107112

108113
/// Whether this element is known to be POD in the local resilience
109114
/// domain.
110115
unsigned IsPOD : 1;
111116

112117
/// The kind of layout performed for this element.
113-
unsigned TheKind : 2;
118+
unsigned TheKind : 3;
114119

115120
explicit ElementLayout(const TypeInfo &type)
116121
: Type(&type), TheKind(IncompleteKind) {}
@@ -138,6 +143,12 @@ class ElementLayout {
138143
Index = 0; // make a complete write of the bitfield
139144
}
140145

146+
void completeInitialNonFixedSize(IsPOD_t isPOD) {
147+
TheKind = unsigned(Kind::InitialNonFixedSize);
148+
IsPOD = unsigned(isPOD);
149+
Index = 0; // make a complete write of the bitfield
150+
}
151+
141152
void completeFixed(IsPOD_t isPOD, Size byteOffset, unsigned structIndex) {
142153
TheKind = unsigned(Kind::Fixed);
143154
IsPOD = unsigned(isPOD);
@@ -257,6 +268,7 @@ class StructLayoutBuilder {
257268

258269
void addElementAtFixedOffset(ElementLayout &elt);
259270
void addElementAtNonFixedOffset(ElementLayout &elt);
271+
void addNonFixedSizeElementAtOffsetZero(ElementLayout &elt);
260272
};
261273

262274
/// A struct layout is the result of laying out a complete structure.

Diff for: test/IRGen/generic_tuples.swift

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %swift -triple x86_64-apple-darwin10 -emit-llvm %s | FileCheck %s
2+
3+
// CHECK: [[OPAQUE:%swift.opaque]] = type opaque
4+
// CHECK: [[TYPE:%swift.type]] = type {
5+
// CHECK: [[TUPLE_TYPE:%swift.tuple_type]] = type { [[TYPE]], i64, i8*, [0 x %swift.tuple_element_type] }
6+
// CHECK: %swift.tuple_element_type = type { [[TYPE]]*, i64 }
7+
8+
func dup<T>(x : T) -> (T, T) { return (x,x) }
9+
// CHECK: define void @_T14generic_tuples3dupU__FT1xQ__TQ_Q__({ [[OPAQUE]] }* noalias sret
10+
// CHECK: [[ELTS:%.*]] = alloca [2 x [[TYPE]]*], align 8
11+
// Get value witnesses for T.
12+
// CHECK-NEXT: [[T0:%.*]] = bitcast [[TYPE]]* %T to i8***
13+
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8*** [[T0]], i64 -1
14+
// CHECK-NEXT: %T.value = load i8*** [[T1]], align 8
15+
// Project to first element of tuple.
16+
// CHECK-NEXT: [[FST:%.*]] = bitcast { [[OPAQUE]] }* [[RET:%.*]] to [[OPAQUE]]*
17+
// Get the tuple metadata.
18+
// FIXME: maybe this should get passed in?
19+
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [2 x [[TYPE]]*]* [[ELTS]], i32 0, i32 0
20+
// CHECK-NEXT: store [[TYPE]]* %T, [[TYPE]]** [[T0]], align 8
21+
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [2 x [[TYPE]]*]* [[ELTS]], i32 0, i32 1
22+
// CHECK-NEXT: store [[TYPE]]* %T, [[TYPE]]** [[T1]], align 8
23+
// CHECK-NEXT: [[TT_PAIR:%.*]] = call [[TYPE]]* @swift_getTupleTypeMetadata(i64 2, [[TYPE]]** [[T0]], i8* null, i8** null)
24+
// Pull out the offset of the second element and GEP to that.
25+
// CHECK-NEXT: [[T0:%.*]] = bitcast [[TYPE]]* [[TT_PAIR]] to [[TUPLE_TYPE]]*
26+
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[TUPLE_TYPE]]* [[T0]], i64 0, i32 3, i64 1, i32 1
27+
// CHECK-NEXT: [[T2:%.*]] = load i64* [[T1]], align 8
28+
// CHECK-NEXT: [[T3:%.*]] = bitcast { [[OPAQUE]] }* [[RET]] to i8*
29+
// CHECK-NEXT: [[T4:%.*]] = getelementptr inbounds i8* [[T3]], i64 [[T2]]
30+
// CHECK-NEXT: [[SND:%.*]] = bitcast i8* [[T4]] to [[OPAQUE]]*
31+
// Copy 'x' into the first element.
32+
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8** %T.value, i32 6
33+
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]], align 8
34+
// CHECK-NEXT: [[COPY_FN:%.*]] = bitcast i8* [[T1]] to [[OPAQUE]]* ([[OPAQUE]]*, [[OPAQUE]]*, [[TYPE]]*)*
35+
// CHECK-NEXT: call [[OPAQUE]]* [[COPY_FN]]([[OPAQUE]]* [[FST]], [[OPAQUE]]* [[X:%.*]], [[TYPE]]* %T)
36+
// Copy 'x' into the second element.
37+
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8** %T.value, i32 6
38+
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]], align 8
39+
// CHECK-NEXT: [[COPY_FN:%.*]] = bitcast i8* [[T1]] to [[OPAQUE]]* ([[OPAQUE]]*, [[OPAQUE]]*, [[TYPE]]*)*
40+
// CHECK-NEXT: call [[OPAQUE]]* [[COPY_FN]]([[OPAQUE]]* [[SND]], [[OPAQUE]]* [[X]], [[TYPE]]* %T)
41+
// Destroy 'x'.
42+
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8** %T.value, i32 4
43+
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]], align 8
44+
// CHECK-NEXT: [[DESTROY_FN:%.*]] = bitcast i8* [[T1]] to void ([[OPAQUE]]*, [[TYPE]]*)*
45+
// CHECK-NEXT: call void [[DESTROY_FN]]([[OPAQUE]]* [[X]], [[TYPE]]* %T)
46+
// CHECK-NEXT: ret void

0 commit comments

Comments
 (0)