Skip to content

Commit

Permalink
[IRGen] Dynamically fill in conditional conformance wtables.
Browse files Browse the repository at this point in the history
This requires the witness table accessor function to gain two new parameters: a
pointer to an array of witness tables and their count. These are then passed down
to the instantiation function which reads them out of the array and writes them
into the newly-allocated witness table.

We use the count to assert that the number of conditional witness tables passed
in is what the protocol conformance expects, which is especially useful while
the feature is still experimental: it is a compiler/runtime bug if an incorrect
number is passed.
  • Loading branch information
huonw committed Nov 9, 2017
1 parent 4f53475 commit b9336c7
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 51 deletions.
4 changes: 3 additions & 1 deletion include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -2421,7 +2421,9 @@ template <typename Runtime>
struct TargetProtocolConformanceRecord {
public:
using WitnessTableAccessorFn
= const WitnessTable *(const TargetMetadata<Runtime>*);
= const WitnessTable *(const TargetMetadata<Runtime>*,
const WitnessTable **,
size_t);

private:
/// The protocol being conformed to.
Expand Down
15 changes: 8 additions & 7 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3309,11 +3309,9 @@ IRGenModule::getAddrOfGenericWitnessTableInstantiationFunction(
return entry;
}

auto fnType = llvm::FunctionType::get(VoidTy,
{ WitnessTablePtrTy,
TypeMetadataPtrTy,
Int8PtrPtrTy },
/*varargs*/ false);
auto fnType = llvm::FunctionType::get(
VoidTy, {WitnessTablePtrTy, TypeMetadataPtrTy, Int8PtrPtrTy},
/*varargs*/ false);
Signature signature(fnType, llvm::AttributeList(), DefaultCC);
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
entry = createFunction(*this, link, signature);
Expand Down Expand Up @@ -3357,8 +3355,11 @@ IRGenModule::getAddrOfWitnessTableAccessFunction(

llvm::FunctionType *fnType;
if (conf->getDeclContext()->isGenericContext()) {
fnType = llvm::FunctionType::get(WitnessTablePtrTy, {TypeMetadataPtrTy},
false);
// conditional requirements are passed indirectly, as an array of witness
// tables.
fnType = llvm::FunctionType::get(
WitnessTablePtrTy, {TypeMetadataPtrTy, WitnessTablePtrPtrTy, SizeTy},
false);
} else {
fnType = llvm::FunctionType::get(WitnessTablePtrTy, false);
}
Expand Down
118 changes: 101 additions & 17 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,13 @@ emitWitnessTableAccessorCall(IRGenFunction &IGF,
if (!*srcMetadataCache) {
*srcMetadataCache = IGF.emitTypeMetadataRef(conformingType);
}
call = IGF.Builder.CreateCall(accessor, {*srcMetadataCache});

auto conditionalTables = llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrPtrTy);
auto numConditionalTables = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);

call = IGF.Builder.CreateCall(
accessor, {*srcMetadataCache, conditionalTables, numConditionalTables});

} else {
call = IGF.Builder.CreateCall(accessor, {});
}
Expand Down Expand Up @@ -1648,11 +1654,18 @@ void WitnessTableBuilder::buildAccessFunction(llvm::Constant *wtable) {

Explosion params = IGF.collectParameters();
llvm::Value *metadata;
llvm::Value *conditionalReqtWtables;
llvm::Value *numConditionalReqtWtables;

if (Conformance.getDeclContext()->isGenericContext())
if (Conformance.getDeclContext()->isGenericContext()) {
metadata = params.claimNext();
else
conditionalReqtWtables = params.claimNext();
numConditionalReqtWtables = params.claimNext();
} else {
metadata = llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy);
conditionalReqtWtables = nullptr;
numConditionalReqtWtables = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
}

// Okay, we need a cache. Build the cache structure.
// struct GenericWitnessTable {
Expand Down Expand Up @@ -1724,10 +1737,31 @@ void WitnessTableBuilder::buildAccessFunction(llvm::Constant *wtable) {
cacheData.finishAndSetAsInitializer(cache);
cache->setConstant(true);

llvm::Value *instantiationArgs =
llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy);
auto call = IGF.Builder.CreateCall(IGM.getGetGenericWitnessTableFn(),
{ cache, metadata, instantiationArgs });
// Create the slice structure for the conditional conformances, which is
// passed as the last argument to the instantiation function.
// FIXME(cond. conf. assert): Elide this local/replace it with `undef` once
// the size assertion in the instantiation function is gone.
auto conditionalSlice =
IGF.createAlloca(IGF.IGM.WitnessTableSliceTy,
IGF.IGM.getPointerAlignment(), "conditional.tables");

// Only store a pointer if it will be read. If not (i.e. size == 0/this
// conformance is unconditional), we can leave it as undef.
if (conditionalReqtWtables) {
auto tableSlot = IGF.Builder.CreateStructGEP(conditionalSlice, 0, Size(0));
IGF.Builder.CreateStore(conditionalReqtWtables, tableSlot);
}

auto sizeSlot = IGF.Builder.CreateStructGEP(conditionalSlice, 1,
Size(IGF.IGM.getPointerSize()));
IGF.Builder.CreateStore(numConditionalReqtWtables, sizeSlot);

auto instantiationArgs =
IGF.Builder.CreateBitCast(conditionalSlice, IGM.Int8PtrPtrTy);

auto call =
IGF.Builder.CreateCall(IGM.getGetGenericWitnessTableFn(),
{cache, metadata, instantiationArgs.getAddress()});
call->setDoesNotThrow();

IGF.Builder.CreateRet(call);
Expand All @@ -1740,32 +1774,82 @@ llvm::Constant *WitnessTableBuilder::buildInstantiationFunction() {
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, fn);

auto PointerAlignment = IGM.getPointerAlignment();
auto PointerSize = IGM.getPointerSize();

// Break out the parameters.
Explosion params = IGF.collectParameters();
Address wtable(params.claimNext(), IGM.getPointerAlignment());
Address wtable(params.claimNext(), PointerAlignment);
llvm::Value *metadata = params.claimNext();
llvm::Value *instantiationArgs = params.claimNext();
(void) instantiationArgs; // unused for now

// TODO: store any required conditional-conformance information
// in the private data.
Address instantiationArgs(params.claimNext(), PointerAlignment);

// The details of any conditional conformances are in the last argument, as a
// pointer/size pair.
auto conditionalTablesSlice = IGF.Builder.CreateElementBitCast(
instantiationArgs, IGF.IGM.WitnessTableSliceTy);

auto conditionalTablesPtr =
IGF.Builder.CreateStructGEP(conditionalTablesSlice, 0, Size(0));
Address conditionalTables(IGF.Builder.CreateLoad(conditionalTablesPtr),
PointerAlignment);

auto numConditionalTablesPtr =
IGF.Builder.CreateStructGEP(conditionalTablesSlice, 1, Size(PointerSize));
auto numConditionalTables = IGF.Builder.CreateLoad(numConditionalTablesPtr);

// Assert that the number of witness tables passed in is the number required
// for the conditional conformances of this witness table.

// FIXME(cond. conf. assert): remove this once we're confident in conditional
// conformances and especially dynamic casting. (Look for the other
// 'cond. conf. assert' FIXMEs too.)
auto failBB = IGF.createBasicBlock("bad_witness_table_count");
auto contBB = IGF.createBasicBlock("cont");

auto cmp = IGF.Builder.CreateICmpEQ(
numConditionalTables,
llvm::ConstantInt::get(IGM.SizeTy,
ConditionalRequirementPrivateDataIndices.size()));
IGF.Builder.CreateCondBr(cmp, contBB, failBB);

// All good: now we can actually fill in the witness table.
IGF.Builder.emitBlock(contBB);

// Initialize all the specialized base conformances.
for (auto &base : SpecializedBaseConformances) {
// Ask the ConformanceInfo to emit the wtable.
// TODO: we may need to bind extra information in the IGF in order
// to make conditional conformances work.
llvm::Value *baseWTable =
base.second->getTable(IGF, ConcreteType, &metadata);
baseWTable = IGF.Builder.CreateBitCast(baseWTable, IGM.Int8PtrTy);

// Store that to the appropriate slot in the new witness table.
Address slot = IGF.Builder.CreateConstArrayGEP(wtable, base.first,
IGM.getPointerSize());
Address slot =
IGF.Builder.CreateConstArrayGEP(wtable, base.first, PointerSize);
IGF.Builder.CreateStore(baseWTable, slot);
}

/// Run through the conditional conformance witness tables, pulling them out
/// of the slice and putting them into the private data of the witness table.
for (auto idx : indices(ConditionalRequirementPrivateDataIndices)) {
Address conditionalTablePtr =
IGF.Builder.CreateConstArrayGEP(conditionalTables, idx, PointerSize);
Address slot = getAddressOfPrivateDataSlot(
IGF, wtable, ConditionalRequirementPrivateDataIndices[idx]);
auto conditionalTable = IGF.Builder.CreateLoad(conditionalTablePtr);
auto coercedSlot =
IGF.Builder.CreateElementBitCast(slot, conditionalTable->getType());
IGF.Builder.CreateStore(conditionalTable, coercedSlot);
}

IGF.Builder.CreateRetVoid();

// The counts didn't match; abort.
IGF.Builder.emitBlock(failBB);
llvm::Function *trapIntrinsic =
llvm::Intrinsic::getDeclaration(&IGM.Module, llvm::Intrinsic::ID::trap);
IGF.Builder.CreateCall(trapIntrinsic, {});
IGF.Builder.CreateUnreachable();

return fn;
}

Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,10 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
/*packed*/ false);
OpenedErrorTriplePtrTy = OpenedErrorTripleTy->getPointerTo(DefaultAS);

WitnessTablePtrPtrTy = WitnessTablePtrTy->getPointerTo(DefaultAS);
WitnessTableSliceTy = createStructType(*this, "swift.witness_table_slice",
{WitnessTablePtrPtrTy, SizeTy});

InvariantMetadataID = LLVMContext.getMDKindID("invariant.load");
InvariantNode = llvm::MDNode::get(LLVMContext, {});
DereferenceableID = LLVMContext.getMDKindID("dereferenceable");
Expand Down
2 changes: 2 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,8 @@ class IRGenModule {
llvm::PointerType *ErrorPtrTy; /// %swift.error*
llvm::StructType *OpenedErrorTripleTy; /// { %swift.opaque*, %swift.type*, i8** }
llvm::PointerType *OpenedErrorTriplePtrTy; /// { %swift.opaque*, %swift.type*, i8** }*
llvm::PointerType *WitnessTablePtrPtrTy; /// i8***
llvm::StructType *WitnessTableSliceTy; /// { witness_table**, i64 }

/// Used to create unique names for class layout types with tail allocated
/// elements.
Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/runtime/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ const {
return getStaticWitnessTable();

case ProtocolConformanceReferenceKind::WitnessTableAccessor:
return getWitnessTableAccessor()(type);
// FIXME: this needs information about conditional conformances.
return getWitnessTableAccessor()(type, nullptr, 0);
}

swift_runtime_unreachable(
Expand Down
28 changes: 20 additions & 8 deletions test/IRGen/associated_type_witness.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,16 @@ struct Computed<T, U> : Assocked {
// CHECK-NEXT: store atomic %swift.type* [[FETCH_RESULT]], %swift.type** [[CACHE]] release, align 8
// CHECK-NEXT: br label %cont

// Witness table instantiation function for Computed : Assocked.
// CHECK-LABEL: define hidden i8** @_T023associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWa(%swift.type*)
// Witness table accessor function for Computed : Assocked.
// CHECK-LABEL: define hidden i8** @_T023associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWa(%swift.type*, i8***, i64)
// CHECK: entry:
// CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_rt_swift_getGenericWitnessTable(%swift.generic_witness_table_cache* @_T023associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWG, %swift.type* %0, i8** null)
// CHECK-NEXT: %conditional.tables = alloca %swift.witness_table_slice, align 8
// CHECK-NEXT: [[TABLES:%.*]] = getelementptr inbounds %swift.witness_table_slice, %swift.witness_table_slice* %conditional.tables, i32 0, i32 0
// CHECK-NEXT: store i8*** %1, i8**** [[TABLES]], align 8
// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds %swift.witness_table_slice, %swift.witness_table_slice* %conditional.tables, i32 0, i32 1
// CHECK-NEXT: store i64 %2, i64* [[COUNT]], align 8
// CHECK-NEXT: [[INSTANTIATION_ARGS:%.*]] = bitcast %swift.witness_table_slice* %conditional.tables to i8**
// CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_rt_swift_getGenericWitnessTable(%swift.generic_witness_table_cache* @_T023associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWG, %swift.type* %0, i8** [[INSTANTIATION_ARGS]])
// CHECK-NEXT: ret i8** [[WTABLE]]


Expand Down Expand Up @@ -151,17 +157,23 @@ struct GenericComputed<T: P> : DerivedFromSimpleAssoc {

// Instantiation function for GenericComputed : DerivedFromSimpleAssoc.
// CHECK-LABEL: define internal void @_T023associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWI(i8**, %swift.type*, i8**)
// CHECK: [[T0:%.*]] = call i8** @_T023associated_type_witness15GenericComputedVyxGAA14HasSimpleAssocAAWa(%swift.type* %1)
// CHECK: [[T0:%.*]] = call i8** @_T023associated_type_witness15GenericComputedVyxGAA14HasSimpleAssocAAWa(%swift.type* %1, i8*** null, i64 0)
// CHECK-NEXT: [[T1:%.*]] = bitcast i8** [[T0]] to i8*
// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i8*, i8** %0, i32 0
// CHECK-NEXT: store i8* [[T1]], i8** [[T2]], align 8
// CHECK-NEXT: ret void

// Witness table instantiation function for GenericComputed : HasSimpleAssoc..
// CHECK-LABEL: define hidden i8** @_T023associated_type_witness15GenericComputedVyxGAA14HasSimpleAssocAAWa(%swift.type*)
// Witness table accessor function for GenericComputed : HasSimpleAssoc..
// CHECK-LABEL: define hidden i8** @_T023associated_type_witness15GenericComputedVyxGAA14HasSimpleAssocAAWa(%swift.type*, i8***, i64)
// CHECK-NEXT: entry:
// CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_rt_swift_getGenericWitnessTable(%swift.generic_witness_table_cache* @_T023associated_type_witness15GenericComputedVyxGAA14HasSimpleAssocAAWG, %swift.type* %0, i8** null)
// CHECK-NEXT: ret i8** %1
// CHECK-NEXT: %conditional.tables = alloca %swift.witness_table_slice, align 8
// CHECK-NEXT: [[TABLES:%.*]] = getelementptr inbounds %swift.witness_table_slice, %swift.witness_table_slice* %conditional.tables, i32 0, i32 0
// CHECK-NEXT: store i8*** %1, i8**** [[TABLES]], align 8
// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds %swift.witness_table_slice, %swift.witness_table_slice* %conditional.tables, i32 0, i32 1
// CHECK-NEXT: store i64 %2, i64* [[COUNT]], align 8
// CHECK-NEXT: [[INSTANTIATION_ARGS:%.*]] = bitcast %swift.witness_table_slice* %conditional.tables to i8**
// CHECK-NEXT: [[WTABLE:%.*]] = call i8** @swift_rt_swift_getGenericWitnessTable(%swift.generic_witness_table_cache* @_T023associated_type_witness15GenericComputedVyxGAA14HasSimpleAssocAAWG, %swift.type* %0, [[INSTANTIATION_ARGS]])
// CHECK-NEXT: ret i8** [[WTABLE]]


protocol HasAssocked {
Expand Down
6 changes: 3 additions & 3 deletions test/IRGen/partial_apply_forwarder.sil
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ sil hidden_external @takingEmptyAndQ : $@convention(thin) <τ_0_0 where τ_0_0
// CHECK: [[GENPARAMSADDR:%.*]] = bitcast %swift.type* [[TY]] to %swift.type**
// CHECK: [[PARAMTYADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[GENPARAMSADDR]], {{(i64 10|i32 13)}}
// CHECK: %"BaseProducer<\CF\84_0_1>" = load %swift.type*, %swift.type** [[PARAMTYADDR]]
// CHECK: [[WITNESS:%.*]] = call i8** @_T023partial_apply_forwarder12BaseProducerVyxGAA1QAAWa(%swift.type* %"BaseProducer<\CF\84_0_1>")
// CHECK: [[WITNESS:%.*]] = call i8** @_T023partial_apply_forwarder12BaseProducerVyxGAA1QAAWa(%swift.type* %"BaseProducer<\CF\84_0_1>", i8*** null, {{i32|i64}} 0)
// CHECK: [[OBJ:%.*]] = bitcast %swift.refcounted* %0 to %T23partial_apply_forwarder7WeakBoxC.1*
// CHECK: tail call swiftcc void @takingQ(%T23partial_apply_forwarder7WeakBoxC.1* [[OBJ]], i8** [[WITNESS]])
// CHECK: }
Expand Down Expand Up @@ -151,7 +151,7 @@ bb0(%0 : $*τ_0_1, %2: $EmptyType):
// CHECK: [[TYPARAMSADDR:%.*]] = bitcast %swift.type* [[TY]] to %swift.type**
// CHECK: [[TYPARAM:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[TYPARAMSADDR]], {{(i64 10|i32 13)}}
// CHECK: %"BaseProducer<\CF\84_0_1>" = load %swift.type*, %swift.type** [[TYPARAM]]
// CHECK: [[WITNESS:%.*]] = call i8** @_T023partial_apply_forwarder12BaseProducerVyxGAA1QAAWa(%swift.type* %"BaseProducer<\CF\84_0_1>")
// CHECK: [[WITNESS:%.*]] = call i8** @_T023partial_apply_forwarder12BaseProducerVyxGAA1QAAWa(%swift.type* %"BaseProducer<\CF\84_0_1>", i8*** null, {{i32|i64}} 0)
// CHECK: tail call swiftcc void @takingQ(%T23partial_apply_forwarder7WeakBoxC.1* [[CAST]], i8** [[WITNESS]])
// CHECK: ret void

Expand Down Expand Up @@ -203,7 +203,7 @@ sil hidden_external @takingQAndS : $@convention(thin) <τ_0_0 where τ_0_0 : Q>
// CHECK: [[GENPARAMSADDR:%.*]] = bitcast %swift.type* [[TY]] to %swift.type**
// CHECK: [[GENPARAMADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[GENPARAMSADDR]], {{(i64 10|i32 13)}}
// CHECK: %"BaseProducer<\CF\84_0_1>" = load %swift.type*, %swift.type** [[GENPARAMADDR]]
// CHECK: [[WITNESS:%.*]] = call i8** @_T023partial_apply_forwarder12BaseProducerVyxGAA1QAAWa(%swift.type* %"BaseProducer<\CF\84_0_1>")
// CHECK: [[WITNESS:%.*]] = call i8** @_T023partial_apply_forwarder12BaseProducerVyxGAA1QAAWa(%swift.type* %"BaseProducer<\CF\84_0_1>", i8*** null, {{i32|i64}} 0)
// CHECK: tail call swiftcc void @takingQAndS(i64 [[X]], %T23partial_apply_forwarder7WeakBoxC.1* %.asUnsubstituted, i8** [[WITNESS]])

sil public @bind_polymorphic_param_from_context_with_layout : $@convention(thin) <τ_0_1>(@in τ_0_1, S) -> @owned @callee_owned () -> () {
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/witness_method.sil
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ struct SyncUp<Deliverable> : Synergy {
// CHECK-LABEL: define{{( protected)?}} swiftcc void @testGenericWitnessMethod(%swift.opaque* noalias nocapture sret, %T14witness_method6SyncUpV* noalias nocapture, %swift.type* %T)
// CHECK: entry:
// CHECK: [[METADATA:%.*]] = call %swift.type* @_T014witness_method6SyncUpVMa(%swift.type* %T)
// CHECK: [[WTABLE:%.*]] = call i8** @_T014witness_method6SyncUpVyxGAA7SynergyAAWa(%swift.type* [[METADATA]])
// CHECK: [[WTABLE:%.*]] = call i8** @_T014witness_method6SyncUpVyxGAA7SynergyAAWa(%swift.type* [[METADATA]], i8*** null, {{i32|i64}} 0)
// CHECK: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[WTABLE]], i32 1
// CHECK: [[WITNESS_FN:%.*]] = load i8*, i8** [[WITNESS_ADDR]]
// CHECK: [[WITNESS:%.*]] = bitcast i8* [[WITNESS_FN]] to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
Expand Down
Loading

0 comments on commit b9336c7

Please sign in to comment.