[IRGen] Lowered open_pack_element.

This commit is contained in:
Nate Chandler
2023-01-31 17:19:51 -08:00
parent 6b22116abf
commit a442245925
5 changed files with 365 additions and 3 deletions

View File

@@ -368,6 +368,214 @@ irgen::emitTypeMetadataPackRef(IRGenFunction &IGF, CanPackType packType,
return response;
}
llvm::Value *
irgen::emitTypeMetadataPackElementRef(IRGenFunction &IGF, CanPackType packType,
llvm::Value *index,
DynamicMetadataRequest request) {
// If the pack has already been materialized, just gep into it.
if (auto pack = tryGetLocalPackTypeMetadata(IGF, packType, request)) {
auto *gep = IGF.Builder.CreateInBoundsGEP(IGF.IGM.TypeMetadataPtrTy,
pack.getMetadata(), index);
auto addr =
Address(gep, IGF.IGM.TypeMetadataPtrTy, IGF.IGM.getPointerAlignment());
auto *metadata = IGF.Builder.CreateLoad(addr);
return metadata;
}
// Otherwise, in general, there's no already available array of metadata
// which can be indexed into.
auto *shape = IGF.emitPackShapeExpression(packType);
// If the shape and the index are both constant, the type for which metadata
// will be emitted is statically available.
auto *constantShape = dyn_cast<llvm::ConstantInt>(shape);
auto *constantIndex = dyn_cast<llvm::ConstantInt>(index);
if (constantShape && constantIndex) {
assert(packType->getNumElements() == constantShape->getValue());
auto index = constantIndex->getValue().getZExtValue();
assert(packType->getNumElements() > index);
auto ty = packType.getElementType(index);
auto response = IGF.emitTypeMetadataRef(ty, request);
auto *metadata = response.getMetadata();
return metadata;
}
// A pack consists of types and pack expansion types. An example:
// {repeat each T, Int, repeat each T, repeat each U, String},
// The above type has length 5. The type "repeat each U" is at index 3.
//
// A pack _explosion_ is notionally obtained by flat-mapping the pack by the
// the operation of "listing elements" in pack expansion types.
//
// The explosion of the example pack looks like
// {T_0, T_1, ..., Int, T_0, T_1, ..., U_0, U_1, ..., String}
// ^^^^^^^^^^^^^
// the runtime components of "each T"
//
// We have an index into the explosion,
//
// {T_0, T_1, ..., Int, T_0, T_1, ..., U_0, U_1, ... String}
// ------------%index------------>
//
// and we need to obtain the element in the explosion corresponding to it.
//
// {T_0, T_1, ..., Int, T_0, T_1, ..., T_k, ..., U_0, U_1, ... String}
// ------------%index---------------> ^^^
//
// Unfortunately, the explosion has not (the first check in this function)
// been materialized--and doing so is likely wasteful--so we can't simply
// index into some array.
//
// Instead, _notionally_, we will "compute"
// (1) the index into the _pack_ and
// {repeat each T, Int, repeat each T, repeat each U, String}
// ------%outer------> ^^^^^^^^^^^^^
// (2) the index within the elements of the pack expansion type
// {T_0, T_2, ..., T_k, ...}
// ----%inner---> ^^^
//
// In fact, we won't ever materialize %outer into any register. Instead, we
// can just brach to materializing the metadata once we've determined which
// outer element's range contains %index.
//
// As for %inner, it will only be materialized in those blocks corresponding
// to pack expansions.
//
// Create the following control flow:
//
// +-------+ t_0 is not t_N _is_ an
// |entry: | an expansion expansion
// |... | +----------+ +----------+ +----------+
// |... | --> |check_0: | -> ... -> |check_N: | -> |trap: |
// | | | %i == %u0| | %i < %uN | | llvm.trap|
// +-------+ +----------+ +----------+ +----------+
// %outer = 0 %outer = N
// | |
// V V
// +----------+ +-----------------------+
// |emit_1: | |emit_N: |
// | %inner=0 | | %inner = %index - %lN |
// | %m_1 = | | %m_N = |
// +----------+ +-----------------------+
// | |
// V V
// +-------------------------------------------
// |exit:
// | %m = phi [ %m_1, %emit_1 ],
// | ...
// | [ %m_N, %emit_N ]
auto *current = IGF.Builder.GetInsertBlock();
// Terminate the block that branches to continue checking or metadata emission
// depending on whether the index is in the pack expansion's bounds.
auto emitCheckBranch = [&IGF](llvm::Value *condition,
llvm::BasicBlock *inBounds,
llvm::BasicBlock *outOfBounds) {
if (condition) {
IGF.Builder.CreateCondBr(condition, inBounds, outOfBounds);
} else {
assert(!inBounds &&
"no condition to check but a metadata materialization block!?");
IGF.Builder.CreateBr(outOfBounds);
}
};
// The block which emission will continue in after we finish emitting metadata
// for this element.
auto *exit = IGF.createBasicBlock("pack-index-element-exit");
IGF.Builder.emitBlock(exit);
auto *metadataPhi = IGF.Builder.CreatePHI(IGF.IGM.TypeMetadataPtrTy,
packType.getElementTypes().size());
IGF.Builder.SetInsertPoint(current);
// The previous checkBounds' block's comparision of %index. Use it to emit a
// branch to the current block or the previous block's metadata emission
// block.
llvm::Value *previousCondition = nullptr;
// The previous type's materializeMetadata block. Use it as the inBounds
// target when branching from the previous block.
llvm::BasicBlock *previousInBounds = nullptr;
// The lower bound of indices for the current pack expansion. Inclusive.
llvm::Value *lowerBound = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
for (auto elementTy : packType.getElementTypes()) {
// The block within which it will be checked whether %index corresponds to
// an element of the pack expansion elementTy.
auto *checkBounds = IGF.createBasicBlock("pack-index-element-bounds");
// Finish emitting the previous block, either entry or check_i-1.
//
// Branch from the previous bounds-check block either to this bounds-check
// block or to the previous metadata-emission block.
emitCheckBranch(previousCondition, previousInBounds, checkBounds);
// (1) Emit check_i {{
IGF.Builder.emitBlock(checkBounds);
// The upper bound for the current pack expansion. Exclusive.
llvm::Value *upperBound = nullptr;
llvm::Value *condition = nullptr;
if (auto expansionTy = dyn_cast<PackExpansionType>(elementTy)) {
auto reducedShape = expansionTy.getCountType();
auto *length = IGF.emitPackShapeExpression(reducedShape);
upperBound = IGF.Builder.CreateAdd(lowerBound, length);
// %index < %upperBound
//
// It's not necessary to check that %index >= %lowerBound. Either
// elementTy is the first element type in packType or we branched here
// from some series of checkBounds blocks in each of which it was
// determined that %index is greater than the indices of the
// corresponding element type.
condition = IGF.Builder.CreateICmpULT(index, upperBound);
} else {
upperBound = IGF.Builder.CreateAdd(
lowerBound, llvm::ConstantInt::get(IGF.IGM.SizeTy, 1));
// %index == %lowerBound
condition = IGF.Builder.CreateICmpEQ(lowerBound, index);
}
// }} Finished emitting check_i, except for the terminator which will be
// emitted in the next iteration once the new outOfBounds block is
// available.
// (2) Emit emit_i {{
// The block within which the metadata corresponding to %inner will be
// materialized.
auto *materializeMetadata =
IGF.createBasicBlock("pack-index-element-metadata");
IGF.Builder.emitBlock(materializeMetadata);
llvm::Value *metadata = nullptr;
if (auto expansionTy = dyn_cast<PackExpansionType>(elementTy)) {
// Actually materialize %inner. Then use it to get the metadata from the
// pack expansion at that index.
auto *relativeIndex = IGF.Builder.CreateSub(index, lowerBound);
metadata = emitPackExpansionElementMetadata(IGF, expansionTy,
relativeIndex, request);
} else {
metadata = IGF.emitTypeMetadataRef(elementTy, request).getMetadata();
}
metadataPhi->addIncoming(metadata, materializeMetadata);
IGF.Builder.CreateBr(exit);
// }} Finished emitting emit_i.
// Switch back to emitting check_i. The next iteration will emit its
// terminator.
IGF.Builder.SetInsertPoint(checkBounds);
// Set up the values for the next iteration.
previousInBounds = materializeMetadata;
previousCondition = condition;
lowerBound = upperBound;
}
auto *trap = IGF.createBasicBlock("pack-index-element-trap");
emitCheckBranch(previousCondition, previousInBounds, trap);
IGF.Builder.emitBlock(trap);
IGF.emitTrap("Variadic generic index out of bounds",
/*EmitUnreachable=*/true);
IGF.Builder.SetInsertPoint(exit);
return metadataPhi;
}
void irgen::cleanupTypeMetadataPack(IRGenFunction &IGF,
StackAddress pack,
Optional<unsigned> elementCount) {

View File

@@ -50,6 +50,11 @@ emitTypeMetadataPackRef(IRGenFunction &IGF,
CanPackType packType,
DynamicMetadataRequest request);
llvm::Value *emitTypeMetadataPackElementRef(IRGenFunction &IGF,
CanPackType packType,
llvm::Value *index,
DynamicMetadataRequest request);
void cleanupTypeMetadataPack(IRGenFunction &IGF,
StackAddress pack,
Optional<unsigned> elementCount);

View File

@@ -17,6 +17,7 @@
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticsIRGen.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
@@ -49,8 +50,8 @@
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
@@ -6863,8 +6864,13 @@ void IRGenSILFunction::visitScalarPackIndexInst(ScalarPackIndexInst *i) {
void IRGenSILFunction::visitOpenPackElementInst(swift::OpenPackElementInst *i) {
llvm::Value *index = getLoweredSingletonExplosion(i->getIndexOperand());
// FIXME: bind the archetypes
(void) index;
i->getOpenedGenericEnvironment()->forEachPackElementBinding(
[&](auto *archetype, auto *pack) {
auto *metadata = emitTypeMetadataPackElementRef(
*this, CanPackType(pack), index, MetadataState::Complete);
this->bindArchetype(CanElementArchetypeType(archetype), metadata,
MetadataState::Complete, {});
});
// The result is just used for type dependencies.
}

View File

@@ -0,0 +1,137 @@
// RUN: %empty-directory(%t)
// 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
// RUN: %target-codesign %t/%target-library-name(PrintShims)
// 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
// 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)
// RUN: %target-codesign %t/main
// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s
// REQUIRES: executable_test
// Because of -enable-experimental-feature VariadicGenerics
// REQUIRES: asserts
// Because generic specialization does not work yet.
// REQUIRES: swift_test_mode_optimize_none
import Builtin
import Swift
import PrintShims
sil public_external @printGenericType : $@convention(thin) <T> (@thick T.Type) -> ()
struct A {}
struct B {}
struct C {}
struct D {}
struct E {}
struct F {}
struct G {}
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%argc : $Int32, %argv : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
%0 = integer_literal $Builtin.Word, 0
%1 = integer_literal $Builtin.Word, 1
%2 = integer_literal $Builtin.Word, 2
%3 = integer_literal $Builtin.Word, 3
%4 = integer_literal $Builtin.Word, 4
%5 = integer_literal $Builtin.Word, 5
%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) -> ()
// U_1 -> {A, B, C, D, E, F}
// ---0--> ^
// CHECK: A
// U_2 -> {D, E, F, A, B, C}
// ---0--> ^
// CHECK: D
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) -> ()
// U_1 -> {A, B, C, D, E, F}
// ----1----> ^
// CHECK: B
// U_2 -> {D, E, F, A, B, C}
// ----1----> ^
// CHECK: E
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) -> ()
// U_1 -> {A, B, C, D, E, F}
// ------2-----> ^
// CHECK: C
// U_2 -> {D, E, F, A, B, C}
// ------2-----> ^
// CHECK: F
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) -> ()
// U_1 -> {A, B, C, D, E, F}
// -------3-------> ^
// CHECK: D
// U_2 -> {D, E, F, A, B, C}
// -------3-------> ^
// CHECK: A
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) -> ()
// U_1 -> {A, B, C, D, E, F}
// ---------4--------> ^
// CHECK: E
// U_2 -> {D, E, F, A, B, C}
// ---------4--------> ^
// CHECK: B
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) -> ()
// U_1 -> {A, B, C, D, E, F}
// -----------5---------> ^
// CHECK: F
// U_2 -> {D, E, F, A, B, C}
// -----------5---------> ^
// CHECK: C
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) -> ()
%direct_access_from_parameter = function_ref @direct_access_from_parameter : $@convention(thin) <T_1...> (Builtin.Word) -> ()
// U_1 -> {A, B, C, D, E, F}
// --0---> ^
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%0) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
// U_1 -> {A, B, C, D, E, F}
// ----1----> ^
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%1) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
// U_1 -> {A, B, C, D, E, F}
// -----2------> ^
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%2) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
// U_1 -> {A, B, C, D, E, F}
// -------3-------> ^
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%3) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
// U_1 -> {A, B, C, D, E, F}
// --------4---------> ^
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%4) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
// U_1 -> {A, B, C, D, E, F}
// ----------5----------> ^
apply %direct_access_from_parameter<Pack{A, B, C, E, F, G}>(%5) : $@convention(thin) <T_1...> (Builtin.Word) -> ()
%outb = integer_literal $Builtin.Int32, 0
%out = struct $Int32 (%outb : $Builtin.Int32)
return %out : $Int32
}
sil @two_archetypes_from_two_params_no_singles : $<T_1..., T_2... where (repeat (each T_1, each T_2)): Any> (Builtin.Word) -> () {
entry(%intIndex : $Builtin.Word):
%innerIndex = dynamic_pack_index %intIndex of $Pack{repeat each T_1, repeat each T_2}
%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"
%metatype_1 = metatype $@thick (@pack_element("01234567-89AB-CDEF-0123-000000000000") U_1).Type
%metatype_2 = metatype $@thick (@pack_element("01234567-89AB-CDEF-0123-000000000000") U_2).Type
%printGenericType = function_ref @printGenericType : $@convention(thin) <T> (@thick T.Type) -> ()
// Print the first archetype that is bound.
apply %printGenericType<(@pack_element("01234567-89AB-CDEF-0123-000000000000") U_1)>(%metatype_1) : $@convention(thin) <T> (@thick T.Type) -> ()
// Print the second archetype that is bound.
apply %printGenericType<(@pack_element("01234567-89AB-CDEF-0123-000000000000") U_2)>(%metatype_2) : $@convention(thin) <T> (@thick T.Type) -> ()
%retval = tuple ()
return %retval : $()
}
// Verify that we just gep into a parameter pack when that's all that the pack consists of.
// CHECK-LL: define {{.*}}void @direct_access_from_parameter(i64 [[INDEX:%[^,]+]], i64 {{%[^,]+}}, %swift.type** [[PACK:%[^,]+]])
// CHECK-LL: [[ELEMENT_ADDRESS:%[^,]+]] = getelementptr inbounds %swift.type*, %swift.type** [[PACK]], i64 [[INDEX]]
// CHECK-LL: [[ELEMENT:%[^,]+]] = load %swift.type*, %swift.type** [[ELEMENT_ADDRESS]]
// CHECK-LL: call swiftcc void @printGenericType(%swift.type* [[ELEMENT]], %swift.type* [[ELEMENT]])
sil @direct_access_from_parameter : $<T_1...> (Builtin.Word) -> () {
entry(%intIndex : $Builtin.Word):
%innerIndex = dynamic_pack_index %intIndex of $Pack{repeat each T_1}
%token = open_pack_element %innerIndex of <U_1...> at <Pack{repeat each T_1}>, shape $U_1, uuid "01234567-89AB-CDEF-0123-000000000001"
%metatype_1 = metatype $@thick (@pack_element("01234567-89AB-CDEF-0123-000000000001") U_1).Type
%printGenericType = function_ref @printGenericType : $@convention(thin) <T> (@thick T.Type) -> ()
apply %printGenericType<(@pack_element("01234567-89AB-CDEF-0123-000000000001") U_1)>(%metatype_1) : $@convention(thin) <T> (@thick T.Type) -> ()
%t = tuple ()
return %t : $()
}

View File

@@ -22,3 +22,9 @@ public func printBool(_ bool: Bool) {
public func printAny(_ any: Any) {
print(any)
}
@_silgen_name("printGenericType")
public func printGenericType<T>(_ t: T.Type) {
print(T.self)
}