Skip to content

Commit a442245

Browse files
committed
[IRGen] Lowered open_pack_element.
1 parent 6b22116 commit a442245

File tree

5 files changed

+365
-3
lines changed

5 files changed

+365
-3
lines changed

lib/IRGen/GenPack.cpp

+208
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,214 @@ irgen::emitTypeMetadataPackRef(IRGenFunction &IGF, CanPackType packType,
368368
return response;
369369
}
370370

371+
llvm::Value *
372+
irgen::emitTypeMetadataPackElementRef(IRGenFunction &IGF, CanPackType packType,
373+
llvm::Value *index,
374+
DynamicMetadataRequest request) {
375+
// If the pack has already been materialized, just gep into it.
376+
if (auto pack = tryGetLocalPackTypeMetadata(IGF, packType, request)) {
377+
auto *gep = IGF.Builder.CreateInBoundsGEP(IGF.IGM.TypeMetadataPtrTy,
378+
pack.getMetadata(), index);
379+
auto addr =
380+
Address(gep, IGF.IGM.TypeMetadataPtrTy, IGF.IGM.getPointerAlignment());
381+
auto *metadata = IGF.Builder.CreateLoad(addr);
382+
return metadata;
383+
}
384+
385+
// Otherwise, in general, there's no already available array of metadata
386+
// which can be indexed into.
387+
auto *shape = IGF.emitPackShapeExpression(packType);
388+
389+
// If the shape and the index are both constant, the type for which metadata
390+
// will be emitted is statically available.
391+
auto *constantShape = dyn_cast<llvm::ConstantInt>(shape);
392+
auto *constantIndex = dyn_cast<llvm::ConstantInt>(index);
393+
if (constantShape && constantIndex) {
394+
assert(packType->getNumElements() == constantShape->getValue());
395+
auto index = constantIndex->getValue().getZExtValue();
396+
assert(packType->getNumElements() > index);
397+
auto ty = packType.getElementType(index);
398+
auto response = IGF.emitTypeMetadataRef(ty, request);
399+
auto *metadata = response.getMetadata();
400+
return metadata;
401+
}
402+
403+
// A pack consists of types and pack expansion types. An example:
404+
// {repeat each T, Int, repeat each T, repeat each U, String},
405+
// The above type has length 5. The type "repeat each U" is at index 3.
406+
//
407+
// A pack _explosion_ is notionally obtained by flat-mapping the pack by the
408+
// the operation of "listing elements" in pack expansion types.
409+
//
410+
// The explosion of the example pack looks like
411+
// {T_0, T_1, ..., Int, T_0, T_1, ..., U_0, U_1, ..., String}
412+
// ^^^^^^^^^^^^^
413+
// the runtime components of "each T"
414+
//
415+
// We have an index into the explosion,
416+
//
417+
// {T_0, T_1, ..., Int, T_0, T_1, ..., U_0, U_1, ... String}
418+
// ------------%index------------>
419+
//
420+
// and we need to obtain the element in the explosion corresponding to it.
421+
//
422+
// {T_0, T_1, ..., Int, T_0, T_1, ..., T_k, ..., U_0, U_1, ... String}
423+
// ------------%index---------------> ^^^
424+
//
425+
// Unfortunately, the explosion has not (the first check in this function)
426+
// been materialized--and doing so is likely wasteful--so we can't simply
427+
// index into some array.
428+
//
429+
// Instead, _notionally_, we will "compute"
430+
// (1) the index into the _pack_ and
431+
// {repeat each T, Int, repeat each T, repeat each U, String}
432+
// ------%outer------> ^^^^^^^^^^^^^
433+
// (2) the index within the elements of the pack expansion type
434+
// {T_0, T_2, ..., T_k, ...}
435+
// ----%inner---> ^^^
436+
//
437+
// In fact, we won't ever materialize %outer into any register. Instead, we
438+
// can just brach to materializing the metadata once we've determined which
439+
// outer element's range contains %index.
440+
//
441+
// As for %inner, it will only be materialized in those blocks corresponding
442+
// to pack expansions.
443+
//
444+
// Create the following control flow:
445+
//
446+
// +-------+ t_0 is not t_N _is_ an
447+
// |entry: | an expansion expansion
448+
// |... | +----------+ +----------+ +----------+
449+
// |... | --> |check_0: | -> ... -> |check_N: | -> |trap: |
450+
// | | | %i == %u0| | %i < %uN | | llvm.trap|
451+
// +-------+ +----------+ +----------+ +----------+
452+
// %outer = 0 %outer = N
453+
// | |
454+
// V V
455+
// +----------+ +-----------------------+
456+
// |emit_1: | |emit_N: |
457+
// | %inner=0 | | %inner = %index - %lN |
458+
// | %m_1 = | | %m_N = |
459+
// +----------+ +-----------------------+
460+
// | |
461+
// V V
462+
// +-------------------------------------------
463+
// |exit:
464+
// | %m = phi [ %m_1, %emit_1 ],
465+
// | ...
466+
// | [ %m_N, %emit_N ]
467+
auto *current = IGF.Builder.GetInsertBlock();
468+
469+
// Terminate the block that branches to continue checking or metadata emission
470+
// depending on whether the index is in the pack expansion's bounds.
471+
auto emitCheckBranch = [&IGF](llvm::Value *condition,
472+
llvm::BasicBlock *inBounds,
473+
llvm::BasicBlock *outOfBounds) {
474+
if (condition) {
475+
IGF.Builder.CreateCondBr(condition, inBounds, outOfBounds);
476+
} else {
477+
assert(!inBounds &&
478+
"no condition to check but a metadata materialization block!?");
479+
IGF.Builder.CreateBr(outOfBounds);
480+
}
481+
};
482+
483+
// The block which emission will continue in after we finish emitting metadata
484+
// for this element.
485+
auto *exit = IGF.createBasicBlock("pack-index-element-exit");
486+
IGF.Builder.emitBlock(exit);
487+
auto *metadataPhi = IGF.Builder.CreatePHI(IGF.IGM.TypeMetadataPtrTy,
488+
packType.getElementTypes().size());
489+
490+
IGF.Builder.SetInsertPoint(current);
491+
// The previous checkBounds' block's comparision of %index. Use it to emit a
492+
// branch to the current block or the previous block's metadata emission
493+
// block.
494+
llvm::Value *previousCondition = nullptr;
495+
// The previous type's materializeMetadata block. Use it as the inBounds
496+
// target when branching from the previous block.
497+
llvm::BasicBlock *previousInBounds = nullptr;
498+
// The lower bound of indices for the current pack expansion. Inclusive.
499+
llvm::Value *lowerBound = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
500+
for (auto elementTy : packType.getElementTypes()) {
501+
// The block within which it will be checked whether %index corresponds to
502+
// an element of the pack expansion elementTy.
503+
auto *checkBounds = IGF.createBasicBlock("pack-index-element-bounds");
504+
// Finish emitting the previous block, either entry or check_i-1.
505+
//
506+
// Branch from the previous bounds-check block either to this bounds-check
507+
// block or to the previous metadata-emission block.
508+
emitCheckBranch(previousCondition, previousInBounds, checkBounds);
509+
510+
// (1) Emit check_i {{
511+
IGF.Builder.emitBlock(checkBounds);
512+
513+
// The upper bound for the current pack expansion. Exclusive.
514+
llvm::Value *upperBound = nullptr;
515+
llvm::Value *condition = nullptr;
516+
if (auto expansionTy = dyn_cast<PackExpansionType>(elementTy)) {
517+
auto reducedShape = expansionTy.getCountType();
518+
auto *length = IGF.emitPackShapeExpression(reducedShape);
519+
upperBound = IGF.Builder.CreateAdd(lowerBound, length);
520+
// %index < %upperBound
521+
//
522+
// It's not necessary to check that %index >= %lowerBound. Either
523+
// elementTy is the first element type in packType or we branched here
524+
// from some series of checkBounds blocks in each of which it was
525+
// determined that %index is greater than the indices of the
526+
// corresponding element type.
527+
condition = IGF.Builder.CreateICmpULT(index, upperBound);
528+
} else {
529+
upperBound = IGF.Builder.CreateAdd(
530+
lowerBound, llvm::ConstantInt::get(IGF.IGM.SizeTy, 1));
531+
// %index == %lowerBound
532+
condition = IGF.Builder.CreateICmpEQ(lowerBound, index);
533+
}
534+
// }} Finished emitting check_i, except for the terminator which will be
535+
// emitted in the next iteration once the new outOfBounds block is
536+
// available.
537+
538+
// (2) Emit emit_i {{
539+
// The block within which the metadata corresponding to %inner will be
540+
// materialized.
541+
auto *materializeMetadata =
542+
IGF.createBasicBlock("pack-index-element-metadata");
543+
IGF.Builder.emitBlock(materializeMetadata);
544+
545+
llvm::Value *metadata = nullptr;
546+
if (auto expansionTy = dyn_cast<PackExpansionType>(elementTy)) {
547+
// Actually materialize %inner. Then use it to get the metadata from the
548+
// pack expansion at that index.
549+
auto *relativeIndex = IGF.Builder.CreateSub(index, lowerBound);
550+
metadata = emitPackExpansionElementMetadata(IGF, expansionTy,
551+
relativeIndex, request);
552+
} else {
553+
metadata = IGF.emitTypeMetadataRef(elementTy, request).getMetadata();
554+
}
555+
metadataPhi->addIncoming(metadata, materializeMetadata);
556+
IGF.Builder.CreateBr(exit);
557+
// }} Finished emitting emit_i.
558+
559+
// Switch back to emitting check_i. The next iteration will emit its
560+
// terminator.
561+
IGF.Builder.SetInsertPoint(checkBounds);
562+
563+
// Set up the values for the next iteration.
564+
previousInBounds = materializeMetadata;
565+
previousCondition = condition;
566+
lowerBound = upperBound;
567+
}
568+
auto *trap = IGF.createBasicBlock("pack-index-element-trap");
569+
emitCheckBranch(previousCondition, previousInBounds, trap);
570+
571+
IGF.Builder.emitBlock(trap);
572+
IGF.emitTrap("Variadic generic index out of bounds",
573+
/*EmitUnreachable=*/true);
574+
575+
IGF.Builder.SetInsertPoint(exit);
576+
return metadataPhi;
577+
}
578+
371579
void irgen::cleanupTypeMetadataPack(IRGenFunction &IGF,
372580
StackAddress pack,
373581
Optional<unsigned> elementCount) {

lib/IRGen/GenPack.h

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ emitTypeMetadataPackRef(IRGenFunction &IGF,
5050
CanPackType packType,
5151
DynamicMetadataRequest request);
5252

53+
llvm::Value *emitTypeMetadataPackElementRef(IRGenFunction &IGF,
54+
CanPackType packType,
55+
llvm::Value *index,
56+
DynamicMetadataRequest request);
57+
5358
void cleanupTypeMetadataPack(IRGenFunction &IGF,
5459
StackAddress pack,
5560
Optional<unsigned> elementCount);

lib/IRGen/IRGenSIL.cpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "swift/AST/ASTContext.h"
1919
#include "swift/AST/DiagnosticsIRGen.h"
20+
#include "swift/AST/GenericEnvironment.h"
2021
#include "swift/AST/IRGenOptions.h"
2122
#include "swift/AST/ParameterList.h"
2223
#include "swift/AST/Pattern.h"
@@ -49,8 +50,8 @@
4950
#include "llvm/ADT/TinyPtrVector.h"
5051
#include "llvm/IR/Constant.h"
5152
#include "llvm/IR/Constants.h"
52-
#include "llvm/IR/DebugInfo.h"
5353
#include "llvm/IR/DIBuilder.h"
54+
#include "llvm/IR/DebugInfo.h"
5455
#include "llvm/IR/Function.h"
5556
#include "llvm/IR/InlineAsm.h"
5657
#include "llvm/IR/Instructions.h"
@@ -6863,8 +6864,13 @@ void IRGenSILFunction::visitScalarPackIndexInst(ScalarPackIndexInst *i) {
68636864
void IRGenSILFunction::visitOpenPackElementInst(swift::OpenPackElementInst *i) {
68646865
llvm::Value *index = getLoweredSingletonExplosion(i->getIndexOperand());
68656866

6866-
// FIXME: bind the archetypes
6867-
(void) index;
6867+
i->getOpenedGenericEnvironment()->forEachPackElementBinding(
6868+
[&](auto *archetype, auto *pack) {
6869+
auto *metadata = emitTypeMetadataPackElementRef(
6870+
*this, CanPackType(pack), index, MetadataState::Complete);
6871+
this->bindArchetype(CanElementArchetypeType(archetype), metadata,
6872+
MetadataState::Complete, {});
6873+
});
68686874

68696875
// The result is just used for type dependencies.
68706876
}

test/IRGen/run_variadic_generics.sil

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule
3+
// RUN: %target-codesign %t/%target-library-name(PrintShims)
4+
// RUN: %target-build-swift -enable-experimental-feature VariadicGenerics -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL
5+
// RUN: %target-build-swift -enable-experimental-feature VariadicGenerics -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t)
6+
// RUN: %target-codesign %t/main
7+
// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s
8+
9+
// REQUIRES: executable_test
10+
11+
// Because of -enable-experimental-feature VariadicGenerics
12+
// REQUIRES: asserts
13+
// Because generic specialization does not work yet.
14+
// REQUIRES: swift_test_mode_optimize_none
15+
16+
import Builtin
17+
import Swift
18+
import PrintShims
19+
20+
sil public_external @printGenericType : $@convention(thin) <T> (@thick T.Type) -> ()
21+
22+
struct A {}
23+
struct B {}
24+
struct C {}
25+
struct D {}
26+
struct E {}
27+
struct F {}
28+
struct G {}
29+
30+
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
31+
bb0(%argc : $Int32, %argv : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
32+
%0 = integer_literal $Builtin.Word, 0
33+
%1 = integer_literal $Builtin.Word, 1
34+
%2 = integer_literal $Builtin.Word, 2
35+
%3 = integer_literal $Builtin.Word, 3
36+
%4 = integer_literal $Builtin.Word, 4
37+
%5 = integer_literal $Builtin.Word, 5
38+
39+
%two_archetypes_from_two_params_no_singles = function_ref @two_archetypes_from_two_params_no_singles : $@convention(thin) <T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> ()
40+
// U_1 -> {A, B, C, D, E, F}
41+
// ---0--> ^
42+
// CHECK: A
43+
// U_2 -> {D, E, F, A, B, C}
44+
// ---0--> ^
45+
// CHECK: D
46+
apply %two_archetypes_from_two_params_no_singles<Pack{A, B, C}, Pack{D, E, F}>(%0) : $@convention(thin) <T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> ()
47+
// U_1 -> {A, B, C, D, E, F}
48+
// ----1----> ^
49+
// CHECK: B
50+
// U_2 -> {D, E, F, A, B, C}
51+
// ----1----> ^
52+
// CHECK: E
53+
apply %two_archetypes_from_two_params_no_singles<Pack{A, B, C}, Pack{D, E, F}>(%1) : $@convention(thin) <T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> ()
54+
// U_1 -> {A, B, C, D, E, F}
55+
// ------2-----> ^
56+
// CHECK: C
57+
// U_2 -> {D, E, F, A, B, C}
58+
// ------2-----> ^
59+
// CHECK: F
60+
apply %two_archetypes_from_two_params_no_singles<Pack{A, B, C}, Pack{D, E, F}>(%2) : $@convention(thin) <T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> ()
61+
// U_1 -> {A, B, C, D, E, F}
62+
// -------3-------> ^
63+
// CHECK: D
64+
// U_2 -> {D, E, F, A, B, C}
65+
// -------3-------> ^
66+
// CHECK: A
67+
apply %two_archetypes_from_two_params_no_singles<Pack{A, B, C}, Pack{D, E, F}>(%3) : $@convention(thin) <T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> ()
68+
// U_1 -> {A, B, C, D, E, F}
69+
// ---------4--------> ^
70+
// CHECK: E
71+
// U_2 -> {D, E, F, A, B, C}
72+
// ---------4--------> ^
73+
// CHECK: B
74+
apply %two_archetypes_from_two_params_no_singles<Pack{A, B, C}, Pack{D, E, F}>(%4) : $@convention(thin) <T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> ()
75+
// U_1 -> {A, B, C, D, E, F}
76+
// -----------5---------> ^
77+
// CHECK: F
78+
// U_2 -> {D, E, F, A, B, C}
79+
// -----------5---------> ^
80+
// CHECK: C
81+
apply %two_archetypes_from_two_params_no_singles<Pack{A, B, C}, Pack{D, E, F}>(%5) : $@convention(thin) <T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> ()
82+
83+
%direct_access_from_parameter = function_ref @direct_access_from_parameter : $@convention(thin) <T_1...> (Builtin.Word) -> ()
84+
// U_1 -> {A, B, C, D, E, F}
85+
// --0---> ^
86+
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%0) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
87+
// U_1 -> {A, B, C, D, E, F}
88+
// ----1----> ^
89+
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%1) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
90+
// U_1 -> {A, B, C, D, E, F}
91+
// -----2------> ^
92+
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%2) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
93+
// U_1 -> {A, B, C, D, E, F}
94+
// -------3-------> ^
95+
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%3) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
96+
// U_1 -> {A, B, C, D, E, F}
97+
// --------4---------> ^
98+
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%4) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
99+
// U_1 -> {A, B, C, D, E, F}
100+
// ----------5----------> ^
101+
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%5) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
102+
103+
%outb = integer_literal $Builtin.Int32, 0
104+
%out = struct $Int32 (%outb : $Builtin.Int32)
105+
return %out : $Int32
106+
}
107+
108+
sil @two_archetypes_from_two_params_no_singles : $<T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> () {
109+
entry(%intIndex : $Builtin.Word):
110+
%innerIndex = dynamic_pack_index %intIndex of $Pack{repeat each T_1, repeat each T_2}
111+
%token = open_pack_element %innerIndex of <U_1..., U_2... where (repeat (each U_1, each U_2)): Any> at <Pack{repeat each T_1, repeat each T_2}, Pack{repeat each T_2, repeat each T_1}>, shape $U_2, uuid "01234567-89AB-CDEF-0123-000000000000"
112+
%metatype_1 = metatype $@thick (@pack_element("01234567-89AB-CDEF-0123-000000000000") U_1).Type
113+
%metatype_2 = metatype $@thick (@pack_element("01234567-89AB-CDEF-0123-000000000000") U_2).Type
114+
%printGenericType = function_ref @printGenericType : $@convention(thin) <T> (@thick T.Type) -> ()
115+
// Print the first archetype that is bound.
116+
apply %printGenericType<(@pack_element("01234567-89AB-CDEF-0123-000000000000") U_1)>(%metatype_1) : $@convention(thin) <T> (@thick T.Type) -> ()
117+
// Print the second archetype that is bound.
118+
apply %printGenericType<(@pack_element("01234567-89AB-CDEF-0123-000000000000") U_2)>(%metatype_2) : $@convention(thin) <T> (@thick T.Type) -> ()
119+
%retval = tuple ()
120+
return %retval : $()
121+
}
122+
123+
// Verify that we just gep into a parameter pack when that's all that the pack consists of.
124+
// CHECK-LL: define {{.*}}void @direct_access_from_parameter(i64 [[INDEX:%[^,]+]], i64 {{%[^,]+}}, %swift.type** [[PACK:%[^,]+]])
125+
// CHECK-LL: [[ELEMENT_ADDRESS:%[^,]+]] = getelementptr inbounds %swift.type*, %swift.type** [[PACK]], i64 [[INDEX]]
126+
// CHECK-LL: [[ELEMENT:%[^,]+]] = load %swift.type*, %swift.type** [[ELEMENT_ADDRESS]]
127+
// CHECK-LL: call swiftcc void @printGenericType(%swift.type* [[ELEMENT]], %swift.type* [[ELEMENT]])
128+
sil @direct_access_from_parameter : $<T_1...> (Builtin.Word) -> () {
129+
entry(%intIndex : $Builtin.Word):
130+
%innerIndex = dynamic_pack_index %intIndex of $Pack{repeat each T_1}
131+
%token = open_pack_element %innerIndex of <U_1...> at <Pack{repeat each T_1}>, shape $U_1, uuid "01234567-89AB-CDEF-0123-000000000001"
132+
%metatype_1 = metatype $@thick (@pack_element("01234567-89AB-CDEF-0123-000000000001") U_1).Type
133+
%printGenericType = function_ref @printGenericType : $@convention(thin) <T> (@thick T.Type) -> ()
134+
apply %printGenericType<(@pack_element("01234567-89AB-CDEF-0123-000000000001") U_1)>(%metatype_1) : $@convention(thin) <T> (@thick T.Type) -> ()
135+
%t = tuple ()
136+
return %t : $()
137+
}

0 commit comments

Comments
 (0)