Skip to content

Commit

Permalink
[silgen] When emitting a curry thunk, if the canonical type of the fu…
Browse files Browse the repository at this point in the history
…nction differs from the actual function type in a non-ABI compatible way, use a canonical function thunk instead of a convert_function.

convert_function can only be used for to convert in between ABI compatible
functions.

rdar://34222540
  • Loading branch information
gottesmm committed Dec 30, 2017
1 parent af56e84 commit 5f5ed55
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 3 deletions.
8 changes: 7 additions & 1 deletion lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
CanAnyFunctionType funcTy,
CanAnyFunctionType blockTy,
CanSILFunctionType loweredBlockTy);


/// Given a non-canonical function type, create a thunk for the function's
/// canonical type.
ManagedValue emitCanonicalFunctionThunk(SILLocation loc, ManagedValue fn,
CanSILFunctionType nonCanonicalTy,
CanSILFunctionType canonicalTy);

/// Thunk with the signature of a base class method calling a derived class
/// method.
///
Expand Down
145 changes: 145 additions & 0 deletions lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3407,3 +3407,148 @@ void SILGenFunction::emitProtocolWitness(Type selfType,
scope.pop();
B.createReturn(loc, reqtResultValue);
}

//===----------------------------------------------------------------------===//
// Conversion to Canonical SILFunctionType Thunks
//===----------------------------------------------------------------------===//

static void translateParametersForCanonicalFunctionThunk(
SILGenFunction &SGF, SILLocation loc,
ArrayRef<ManagedValue> origParamValues,
ArrayRef<SILParameterInfo> newParamInfos,
SmallVectorImpl<ManagedValue> &newParams) {
assert(origParamValues.size() == newParamInfos.size());

for (auto T : llvm::zip(origParamValues, newParamInfos)) {
ManagedValue origParam;
SILParameterInfo newParamInfo;
std::tie(origParam, newParamInfo) = T;

if (origParam.getType().isTrivial(SGF.getModule())) {
newParams.emplace_back(origParam);
continue;
}

if (origParam.hasCleanup()) {
// If we have a +1 value and the non-canonical function expects a
// guaranteed parameter, borrow the parameter. Otherwise just pass off the
// +1 value.
if (newParamInfo.isGuaranteed()) {
origParam = origParam.borrow(SGF, loc);
}
newParams.emplace_back(origParam);
continue;
}

// Otherwise, if we have a +0 value and we want to pass it off as a +1
// value, perform the copy.
if (newParamInfo.isConsumed()) {
origParam = origParam.copy(SGF, loc);
}
newParams.emplace_back(origParam);
}
}

static void buildCanonicalFunctionThunkBody(SILGenFunction &SGF,
SILLocation loc,
CanSILFunctionType nonCanonicalTy,
CanSILFunctionType canonicalTy) {
SGF.F.setBare(IsBare);
SGF.F.setThunk(IsThunk);

FullExpr scope(SGF.Cleanups, CleanupLocation::get(loc));
FormalEvaluationScope formalEvalScope(SGF);

// Collect the thunk params, creating arguments for each parameter.
SmallVector<ManagedValue, 8> origParams;
SGF.collectThunkParams(loc, origParams);

// Then translate our parameters into new params.
SmallVector<ManagedValue, 8> newParams;
translateParametersForCanonicalFunctionThunk(
SGF, loc,
// We drop the front so we don't process the thunked function here. We
// handle that later.
llvm::makeArrayRef(origParams).drop_back(1),
nonCanonicalTy->getParameters(), newParams);

// Then grab the function we are going to call from the last parameter.
ManagedValue fn = origParams.back();

// Collect the arguments.
SmallVector<SILValue, 8> args;

// Add all of the indirect results. We can just add the SILValues directly to
// the args array since we do not need to perform any transformations upon
// them because:
//
// 1. Reabstraction can not occur as a result of a canonical/non-canonical
// mismatch.
// 2. SILGenFunction::collectThunkParams(...) does not create cleanups when it
// creates arguments.
SILFunctionConventions fnConv(canonicalTy, SGF.SGM.M);
transform(range(fnConv.getNumIndirectSILResults()), std::back_inserter(args),
[&](unsigned Index) -> SILValue {
return SGF.F.begin()->getArgument(Index);
});

// and then the rest of the arguments besides the first argument. Here we have
// to forward the arguments since collectThunkParams /does/ create cleanups
// for the parameters.
forwardFunctionArguments(SGF, loc, nonCanonicalTy, newParams, args);

// Perform the call.
SILValue result =
SGF.emitApplyWithRethrow(loc, fn.forward(SGF), fn.getType(), {}, args);

formalEvalScope.pop();
scope.pop();
SGF.B.createReturn(loc, result);
}

ManagedValue
SILGenFunction::emitCanonicalFunctionThunk(SILLocation loc, ManagedValue fn,
CanSILFunctionType nonCanonicalTy,
CanSILFunctionType canonicalTy) {
canonicalTy = canonicalTy->getWithRepresentation(
SILFunctionType::Representation::Thick);

SubstitutionMap contextSubs, interfaceSubs;
GenericEnvironment *genericEnv = nullptr;

// These two are not used here -- but really, bridging thunks
// should be emitted using the formal AST type, not the lowered
// type
CanType inputSubstType;
CanType outputSubstType;
auto thunkTy = buildThunkType(nonCanonicalTy, canonicalTy, inputSubstType,
outputSubstType, genericEnv, interfaceSubs);
auto thunk = SGM.getOrCreateReabstractionThunk(thunkTy, nonCanonicalTy,
canonicalTy, F.isSerialized());
if (thunk->empty()) {
thunk->setGenericEnvironment(genericEnv);
SILGenFunction thunkSGF(SGM, *thunk);
auto loc = RegularLocation::getAutoGeneratedLocation();
buildCanonicalFunctionThunkBody(thunkSGF, loc, nonCanonicalTy, canonicalTy);
}

CanSILFunctionType substFnTy = thunkTy;

SmallVector<Substitution, 4> subs;
if (auto genericSig = thunkTy->getGenericSignature()) {
genericSig->getSubstitutions(interfaceSubs, subs);
substFnTy = thunkTy->substGenericArgs(F.getModule(), interfaceSubs);
}

// Create it in the current function.
auto thunkValue = B.createFunctionRef(loc, thunk);
ManagedValue thunkedFn = B.createPartialApply(
loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), subs, {fn},
SILType::getPrimitiveObjectType(canonicalTy));
if (canonicalTy->isNoEscape()) {
auto &funcTL = getTypeLowering(canonicalTy);
thunkedFn =
B.createConvertFunction(loc, thunkedFn, funcTL.getLoweredType());
}
return thunkedFn;
}
14 changes: 12 additions & 2 deletions lib/SILGen/SILGenThunk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,18 @@ void SILGenFunction::emitCurryThunk(SILDeclRef thunk) {
toFn->getType(), /*appliedParams=*/1, SGM.M, subs, calleeConvention);
SILValue toClosure =
B.createPartialApply(vd, toFn, substTy, subs, {selfArg}, closureTy);
if (resultTy != closureTy)
toClosure = B.createConvertFunction(vd, toClosure, resultTy);
if (resultTy != closureTy) {
CanSILFunctionType resultFnTy = resultTy.castTo<SILFunctionType>();
CanSILFunctionType closureFnTy = closureTy.castTo<SILFunctionType>();
if (resultFnTy->isABICompatibleWith(closureFnTy).isCompatible()) {
toClosure = B.createConvertFunction(vd, toClosure, resultTy);
} else {
toClosure =
emitCanonicalFunctionThunk(vd, ManagedValue::forUnmanaged(toClosure),
closureFnTy, resultFnTy)
.forward(*this);
}
}
B.createReturn(ImplicitReturnLocation::getImplicitReturnLoc(vd), toClosure);
}

Expand Down
9 changes: 9 additions & 0 deletions test/SILGen/guaranteed_normal_args.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ case none
case some(T)
}

func _diagnoseUnexpectedNilOptional(_filenameStart: Builtin.RawPointer,
_filenameLength: Builtin.Word,
_filenameIsASCII: Builtin.Int1,
_line: Builtin.Word) {
// This would usually contain an assert, but we don't need one since we are
// just emitting SILGen.
}

class Klass {
init() {}
}
Expand Down Expand Up @@ -99,3 +107,4 @@ struct ReabstractionThunkTest : Protocol {
processInput(input)
}
}

Loading

0 comments on commit 5f5ed55

Please sign in to comment.