Skip to content

Commit c7e101c

Browse files
committed
---
yaml --- r: 348606 b: refs/heads/master c: 4a0430b h: refs/heads/master
1 parent 59bc11a commit c7e101c

File tree

7 files changed

+328
-3
lines changed

7 files changed

+328
-3
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
refs/heads/master: d675268238e951b61a63197e9c0a6a24133327cd
2+
refs/heads/master: 4a0430b79c5a4fb485219f2e21a8eeb2455e906c
33
refs/heads/master-next: 203b3026584ecad859eb328b2e12490099409cd5
44
refs/tags/osx-passed: b6b74147ef8a386f532cf9357a1bde006e552c54
55
refs/tags/swift-2.2-SNAPSHOT-2015-12-01-a: 6bb18e013c2284f2b45f5f84f2df2887dc0f7dea

trunk/include/swift/SIL/InstructionUtils.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,38 @@ bool onlyUsedByAssignByWrapper(PartialApplyInst *PAI);
142142
void findClosuresForFunctionValue(SILValue V,
143143
TinyPtrVector<PartialApplyInst *> &results);
144144

145+
/// Given a polymorphic builtin \p bi that may be generic and thus have in/out
146+
/// params, stash all of the information needed for either specializing while
147+
/// inlining or propagating the type in constant propagation.
148+
///
149+
/// NOTE: If we perform this transformation, our builtin will no longer have any
150+
/// substitutions since we only substitute to concrete static overloads.
151+
struct PolymorphicBuiltinSpecializedOverloadInfo {
152+
Identifier staticOverloadIdentifier;
153+
SmallVector<SILType, 8> argTypes;
154+
SILType resultType;
155+
bool hasOutParam = false;
156+
157+
#ifndef NDEBUG
158+
private:
159+
bool isInitialized = false;
160+
#endif
161+
162+
public:
163+
PolymorphicBuiltinSpecializedOverloadInfo() = default;
164+
165+
bool init(SILFunction *fn, BuiltinValueKind builtinKind,
166+
ArrayRef<SILType> oldOperandTypes, SILType oldResultType);
167+
168+
bool init(BuiltinInst *bi);
169+
};
170+
171+
/// Given a polymorphic builtin \p bi, analyze its types and create a builtin
172+
/// for the static overload that the builtin corresponds to. If \p bi is not a
173+
/// polymorphic builtin or does not have any available overload for these types,
174+
/// return SILValue().
175+
SILValue getStaticOverloadForSpecializedPolymorphicBuiltin(BuiltinInst *bi);
176+
145177
} // end namespace swift
146178

147179
#endif

trunk/include/swift/SIL/SILInstruction.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,16 @@ class SILInstruction
523523
getAllOperands()[Num1].swap(getAllOperands()[Num2]);
524524
}
525525

526+
private:
527+
/// Predicate used to filter OperandTypeRange.
528+
struct OperandToType;
529+
530+
public:
531+
using OperandTypeRange =
532+
OptionalTransformRange<ArrayRef<Operand>, OperandToType>;
533+
// NOTE: We always skip type dependent operands.
534+
OperandTypeRange getOperandTypes() const;
535+
526536
/// Return the list of results produced by this instruction.
527537
bool hasResults() const { return !getResults().empty(); }
528538
SILInstructionResultArray getResults() const { return getResultsImpl(); }
@@ -700,6 +710,22 @@ SILInstruction::getOperandValues(bool skipTypeDependentOperands) const
700710
OperandToValue(*this, skipTypeDependentOperands));
701711
}
702712

713+
struct SILInstruction::OperandToType {
714+
const SILInstruction &i;
715+
716+
OperandToType(const SILInstruction &i) : i(i) {}
717+
718+
Optional<SILType> operator()(const Operand &use) const {
719+
if (i.isTypeDependentOperand(use))
720+
return None;
721+
return use.get()->getType();
722+
}
723+
};
724+
725+
inline auto SILInstruction::getOperandTypes() const -> OperandTypeRange {
726+
return OperandTypeRange(getAllOperands(), OperandToType(*this));
727+
}
728+
703729
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
704730
const SILInstruction &I) {
705731
I.print(OS);
@@ -3007,7 +3033,7 @@ class BuiltinInst final
30073033

30083034
/// Looks up the BuiltinKind of this builtin. Returns None if this is
30093035
/// not a builtin.
3010-
llvm::Optional<BuiltinValueKind> getBuiltinKind() const {
3036+
Optional<BuiltinValueKind> getBuiltinKind() const {
30113037
auto I = getBuiltinInfo();
30123038
if (I.ID == BuiltinValueKind::None)
30133039
return None;

trunk/lib/SIL/InstructionUtils.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
#include "swift/SIL/InstructionUtils.h"
1515
#include "swift/AST/SubstitutionMap.h"
1616
#include "swift/Basic/NullablePtr.h"
17+
#include "swift/Basic/STLExtras.h"
1718
#include "swift/SIL/DebugUtils.h"
1819
#include "swift/SIL/Projection.h"
1920
#include "swift/SIL/SILArgument.h"
2021
#include "swift/SIL/SILBasicBlock.h"
22+
#include "swift/SIL/SILBuilder.h"
2123
#include "swift/SIL/SILVisitor.h"
2224

2325
using namespace swift;
@@ -530,3 +532,146 @@ void swift::findClosuresForFunctionValue(
530532
// Ignore other unrecognized values that feed this applied argument.
531533
}
532534
}
535+
536+
bool PolymorphicBuiltinSpecializedOverloadInfo::init(
537+
SILFunction *fn, BuiltinValueKind builtinKind,
538+
ArrayRef<SILType> oldOperandTypes, SILType oldResultType) {
539+
#ifndef NDEBUG
540+
assert(!isInitialized && "Expected uninitialized info");
541+
SWIFT_DEFER { isInitialized = true; };
542+
#endif
543+
if (!isPolymorphicBuiltin(builtinKind))
544+
return false;
545+
546+
// Ok, at this point we know that we have a true polymorphic builtin. See if
547+
// we have an overload for its current operand type.
548+
StringRef name = getBuiltinName(builtinKind);
549+
StringRef prefix = "generic_";
550+
assert(name.startswith(prefix) &&
551+
"Invalid polymorphic builtin name! Prefix should be Generic$OP?!");
552+
SmallString<32> staticOverloadName;
553+
staticOverloadName.append(name.drop_front(prefix.size()));
554+
555+
// If our first argument is an address, we know we have an indirect @out
556+
// parameter by convention since all of these polymorphic builtins today never
557+
// take indirect parameters without an indirect out result parameter. We stash
558+
// this information and validate that if we have an out param, that our result
559+
// is equal to the empty tuple type.
560+
if (oldOperandTypes[0].isAddress()) {
561+
if (oldResultType != fn->getModule().Types.getEmptyTupleType())
562+
return false;
563+
564+
hasOutParam = true;
565+
SILType firstType = oldOperandTypes.front();
566+
567+
// We only handle polymorphic builtins with trivial types today.
568+
if (!firstType.is<BuiltinType>() || !firstType.isTrivial(*fn)) {
569+
return false;
570+
}
571+
572+
resultType = firstType.getObjectType();
573+
oldOperandTypes = oldOperandTypes.drop_front();
574+
} else {
575+
resultType = oldResultType;
576+
}
577+
578+
// Then go through all of our values and bail if any after substitution are
579+
// not concrete builtin types. Otherwise, stash each of them in the argTypes
580+
// array as objects. We will convert them as appropriate.
581+
for (SILType ty : oldOperandTypes) {
582+
// If after specialization, we do not have a trivial builtin type, bail.
583+
if (!ty.is<BuiltinType>() || !ty.isTrivial(*fn)) {
584+
return false;
585+
}
586+
587+
// Otherwise, we have an object builtin type ready to go.
588+
argTypes.push_back(ty.getObjectType());
589+
}
590+
591+
// Ok, we have all builtin types. Infer the underlying polymorphic builtin
592+
// name form our first argument.
593+
CanBuiltinType builtinType = argTypes.front().getAs<BuiltinType>();
594+
SmallString<32> builtinTypeNameStorage;
595+
StringRef typeName = builtinType->getTypeName(builtinTypeNameStorage, false);
596+
staticOverloadName.append("_");
597+
staticOverloadName.append(typeName);
598+
599+
auto &ctx = fn->getASTContext();
600+
staticOverloadIdentifier = ctx.getIdentifier(staticOverloadName);
601+
return true;
602+
}
603+
604+
bool PolymorphicBuiltinSpecializedOverloadInfo::init(BuiltinInst *bi) {
605+
#ifndef NDEBUG
606+
assert(!isInitialized && "Can not init twice?!");
607+
SWIFT_DEFER { isInitialized = true; };
608+
#endif
609+
610+
// First quickly make sure we have a /real/ BuiltinValueKind, not an intrinsic
611+
// or None.
612+
auto kind = bi->getBuiltinKind();
613+
if (!kind)
614+
return false;
615+
616+
SmallVector<SILType, 8> oldOperandTypes;
617+
copy(bi->getOperandTypes(), std::back_inserter(oldOperandTypes));
618+
assert(bi->getNumResults() == 1 &&
619+
"We expect a tuple here instead of real args");
620+
SILType oldResultType = bi->getResult(0)->getType();
621+
return init(bi->getFunction(), *kind, oldOperandTypes, oldResultType);
622+
}
623+
624+
SILValue
625+
swift::getStaticOverloadForSpecializedPolymorphicBuiltin(BuiltinInst *bi) {
626+
627+
PolymorphicBuiltinSpecializedOverloadInfo info;
628+
if (!info.init(bi))
629+
return SILValue();
630+
631+
SmallVector<SILValue, 8> rawArgsData;
632+
copy(bi->getOperandValues(), std::back_inserter(rawArgsData));
633+
634+
SILValue result = bi->getResult(0);
635+
MutableArrayRef<SILValue> rawArgs = rawArgsData;
636+
637+
if (info.hasOutParam) {
638+
result = rawArgs.front();
639+
rawArgs = rawArgs.drop_front();
640+
}
641+
642+
assert(bi->getNumResults() == 1 &&
643+
"We assume that builtins have a single result today. If/when this "
644+
"changes, this code needs to be updated");
645+
646+
SILBuilderWithScope builder(bi);
647+
648+
// Ok, now we know that we can convert this to our specialized
649+
// builtin. Prepare the arguments for the specialized value, loading the
650+
// values if needed and storing the result into an out parameter if needed.
651+
//
652+
// NOTE: We only support polymorphic builtins with trivial types today, so we
653+
// use load/store trivial as a result.
654+
SmallVector<SILValue, 8> newArgs;
655+
for (SILValue arg : rawArgs) {
656+
if (arg->getType().isObject()) {
657+
newArgs.push_back(arg);
658+
continue;
659+
}
660+
661+
SILValue load = builder.emitLoadValueOperation(
662+
bi->getLoc(), arg, LoadOwnershipQualifier::Trivial);
663+
newArgs.push_back(load);
664+
}
665+
666+
BuiltinInst *newBI =
667+
builder.createBuiltin(bi->getLoc(), info.staticOverloadIdentifier,
668+
info.resultType, {}, newArgs);
669+
670+
// If we have an out parameter initialize it now.
671+
if (info.hasOutParam) {
672+
builder.emitStoreValueOperation(newBI->getLoc(), newBI->getResult(0),
673+
result, StoreOwnershipQualifier::Trivial);
674+
}
675+
676+
return newBI;
677+
}

trunk/lib/SIL/SILVerifier.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,27 @@ struct ImmutableAddressUseVerifier {
500500
if (inst->isTypeDependentOperand(*use))
501501
continue;
502502

503+
// TODO: Can this switch be restructured so break -> error, continue ->
504+
// next iteration, return -> return the final result.
503505
switch (inst->getKind()) {
506+
case SILInstructionKind::BuiltinInst: {
507+
// If we are processing a polymorphic builtin that takes an address,
508+
// skip the builtin. This is because the builtin must be specialized to
509+
// a non-memory reading builtin that works on trivial object values
510+
// before the diagnostic passes end (or be DCEed) or we emit a
511+
// diagnostic.
512+
if (auto builtinKind = cast<BuiltinInst>(inst)->getBuiltinKind()) {
513+
if (isPolymorphicBuiltin(*builtinKind)) {
514+
break;
515+
}
516+
}
517+
518+
// Otherwise this is a builtin that we are not expecting to see, so bail
519+
// and assert.
520+
llvm::errs() << "Unhandled, unexpected builtin instruction: " << *inst;
521+
llvm_unreachable("invoking standard assertion failure");
522+
break;
523+
}
504524
case SILInstructionKind::MarkDependenceInst:
505525
case SILInstructionKind::LoadBorrowInst:
506526
case SILInstructionKind::DebugValueAddrInst:

trunk/lib/SILOptimizer/Utils/ConstantFolding.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "swift/AST/DiagnosticsSIL.h"
1616
#include "swift/AST/Expr.h"
17+
#include "swift/SIL/InstructionUtils.h"
1718
#include "swift/SIL/PatternMatch.h"
1819
#include "swift/SIL/SILBuilder.h"
1920
#include "swift/SILOptimizer/Utils/CastOptimizer.h"
@@ -23,7 +24,7 @@
2324
#include "llvm/ADT/Statistic.h"
2425
#include "llvm/Support/Debug.h"
2526

26-
#define DEBUG_TYPE "constant-folding"
27+
#define DEBUG_TYPE "sil-constant-folding"
2728

2829
using namespace swift;
2930

@@ -582,6 +583,21 @@ constantFoldAndCheckDivision(BuiltinInst *BI, BuiltinValueKind ID,
582583
return B.createIntegerLiteral(BI->getLoc(), BI->getType(), ResVal);
583584
}
584585

586+
static SILValue specializePolymorphicBuiltin(BuiltinInst *bi,
587+
BuiltinValueKind id,
588+
Optional<bool> &resultsInError) {
589+
// If we are not a polymorphic builtin, return an empty SILValue()
590+
// so we keep on scanning.
591+
if (!isPolymorphicBuiltin(id))
592+
return SILValue();
593+
594+
// Otherwise, try to perform the mapping.
595+
if (auto newBuiltin = getStaticOverloadForSpecializedPolymorphicBuiltin(bi))
596+
return newBuiltin;
597+
598+
return SILValue();
599+
}
600+
585601
/// Fold binary operations.
586602
///
587603
/// The list of operations we constant fold might not be complete. Start with
@@ -1593,6 +1609,15 @@ void ConstantFolder::initializeWorklist(SILFunction &f) {
15931609
continue;
15941610
}
15951611

1612+
if (auto *bi = dyn_cast<BuiltinInst>(inst)) {
1613+
if (auto kind = bi->getBuiltinKind()) {
1614+
if (isPolymorphicBuiltin(kind.getValue())) {
1615+
WorkList.insert(bi);
1616+
continue;
1617+
}
1618+
}
1619+
}
1620+
15961621
// If we have nominal type literals like struct, tuple, enum visit them
15971622
// like we do in the worklist to see if we can fold any projection
15981623
// manipulation operations.
@@ -1804,6 +1829,29 @@ ConstantFolder::processWorkList() {
18041829
continue;
18051830
}
18061831

1832+
if (auto *bi = dyn_cast<BuiltinInst>(I)) {
1833+
if (auto kind = bi->getBuiltinKind()) {
1834+
Optional<bool> ResultsInError;
1835+
if (EnableDiagnostics)
1836+
ResultsInError = false;
1837+
if (SILValue v = specializePolymorphicBuiltin(bi, kind.getValue(),
1838+
ResultsInError)) {
1839+
// If bi had a result, RAUW.
1840+
if (bi->getResult(0)->getType() !=
1841+
bi->getModule().Types.getEmptyTupleType())
1842+
bi->replaceAllUsesWith(v);
1843+
// Then delete no matter what.
1844+
bi->eraseFromParent();
1845+
InvalidateInstructions = true;
1846+
}
1847+
1848+
// If we did not pass in a None and the optional is set to true, add the
1849+
// user to our error set.
1850+
if (ResultsInError.hasValue() && ResultsInError.getValue())
1851+
ErrorSet.insert(bi);
1852+
}
1853+
}
1854+
18071855
// Go through all users of the constant and try to fold them.
18081856
FoldedUsers.clear();
18091857
for (auto Result : I->getResults()) {

0 commit comments

Comments
 (0)