Skip to content

Commit 6f086af

Browse files
committed
[SIL Optimization][OSLogOptimization] Improve the OSLogOptimization
pass so that it constant folds array symbolic values inferred by the constant evaluator when evaluting os log calls.
1 parent f2ec557 commit 6f086af

File tree

2 files changed

+413
-1
lines changed

2 files changed

+413
-1
lines changed

lib/SILOptimizer/Mandatory/OSLogOptimization.cpp

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#include "swift/SIL/SILInstruction.h"
9090
#include "swift/SIL/SILLocation.h"
9191
#include "swift/SIL/SILModule.h"
92+
#include "swift/SIL/TypeLowering.h"
9293
#include "swift/SILOptimizer/PassManager/Passes.h"
9394
#include "swift/SILOptimizer/PassManager/Transforms.h"
9495
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
@@ -101,6 +102,7 @@
101102
#include "llvm/ADT/MapVector.h"
102103

103104
using namespace swift;
105+
using namespace Lowering;
104106

105107
template <typename... T, typename... U>
106108
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
@@ -239,6 +241,12 @@ static bool isStringType(SILType silType, ASTContext &astContext) {
239241
return nominalDecl && nominalDecl == astContext.getStringDecl();
240242
}
241243

244+
/// Return true if and only if the given SIL type represents an Array type.
245+
static bool isArrayType(SILType silType, ASTContext &astContext) {
246+
NominalTypeDecl *nominalDecl = silType.getNominalOrBoundGenericNominal();
247+
return nominalDecl && nominalDecl == astContext.getArrayDecl();
248+
}
249+
242250
/// Decide if the given instruction (which could possibly be a call) should
243251
/// be constant evaluated.
244252
///
@@ -290,6 +298,28 @@ static bool isFoldableString(SILValue value, SILInstruction *definingInst,
290298
!getStringMakeUTF8Init(definingInst);
291299
}
292300

301+
/// Return true iff the given value is an array and is not an initialization
302+
/// of an array from an array literal.
303+
static bool isFoldableArray(SILValue value, SILInstruction *definingInst,
304+
ASTContext &astContext) {
305+
assert(definingInst);
306+
if (!isArrayType(value->getType(), astContext))
307+
return false;
308+
309+
// Check if this is not an initialization of an array from a literal.
310+
SILInstruction *constructorInst = definingInst;
311+
if (isa<DestructureTupleInst>(definingInst) ||
312+
isa<TupleExtractInst>(definingInst)) {
313+
constructorInst = definingInst->getOperand(0)->getDefiningInstruction();
314+
}
315+
ApplyInst *apply = dyn_cast<ApplyInst>(definingInst);
316+
if (!apply)
317+
return true;
318+
SILFunction *callee = apply->getCalleeFunction();
319+
return !callee || !callee->hasSemanticsAttr("array.init.empty") ||
320+
!callee->hasSemanticsAttr("array.uninitialized_intrinsic");
321+
}
322+
293323
/// Check whether a SILValue is foldable. String, integer, array and
294324
/// function values are foldable with the following exceptions:
295325
/// - Addresses cannot be folded.
@@ -308,7 +338,8 @@ static bool isSILValueFoldable(SILValue value) {
308338
!isa<BeginBorrowInst>(definingInst) &&
309339
!isa<CopyValueInst>(definingInst) &&
310340
(isFoldableIntOrBool(value, definingInst, astContext) ||
311-
isFoldableString(value, definingInst, astContext)));
341+
isFoldableString(value, definingInst, astContext) ||
342+
isFoldableArray(value, definingInst, astContext)));
312343
}
313344

314345
/// Diagnose failure during evaluation of a call to a constant-evaluable
@@ -450,6 +481,110 @@ static Optional<SymbolicValue> collectConstants(FoldState &foldState) {
450481
return None; // No error.
451482
}
452483

484+
/// Generate SIL code to create an array of constant size from the given
485+
/// SILValues \p elements. This function creates the same sequence of SIL
486+
/// instructions that would be generated for initializing an array from an array
487+
/// literal of the form [element1, element2, ..., elementn].
488+
///
489+
/// \param elements SILValues that the array should contain
490+
/// \param arrayType the type of the array that must be created.
491+
/// \param builder SILBuilder that provides the context for emitting the code
492+
/// for the array.
493+
/// \param loc SILLocation to use in the emitted instructions.
494+
/// \return the SILValue of the array that is created with the given \c
495+
/// elements.
496+
static SILValue emitCodeForConstantArray(ArrayRef<SILValue> elements,
497+
CanType arrayType, SILBuilder &builder,
498+
SILLocation loc) {
499+
ASTContext &astContext = builder.getASTContext();
500+
assert(astContext.getArrayDecl() ==
501+
arrayType->getNominalOrBoundGenericNominal());
502+
SILModule &module = builder.getModule();
503+
SILFunction &fun = builder.getFunction();
504+
505+
// Create a SILValue for the number of elements.
506+
unsigned numElements = elements.size();
507+
SILValue numElementsSIL = builder.createIntegerLiteral(
508+
loc, SILType::getBuiltinWordType(astContext), numElements);
509+
510+
// Find the SILFunction that corresponds to _allocateUninitializedArray.
511+
FuncDecl *arrayAllocateDecl = astContext.getAllocateUninitializedArray();
512+
assert(arrayAllocateDecl);
513+
std::string allocatorMangledName =
514+
SILDeclRef(arrayAllocateDecl, SILDeclRef::Kind::Func).mangle();
515+
SILFunction *arrayAllocateFun =
516+
module.findFunction(allocatorMangledName, SILLinkage::PublicExternal);
517+
assert(arrayAllocateFun);
518+
519+
// Call the _allocateUninitializedArray function with numElementsSIL. The
520+
// call returns a two-element tuple, where the first element is the newly
521+
// created array and the second element is a pointer to the internal storage
522+
// of the array.
523+
SubstitutionMap subMap = arrayType->getContextSubstitutionMap(
524+
module.getSwiftModule(), astContext.getArrayDecl());
525+
FunctionRefInst *arrayAllocateRef =
526+
builder.createFunctionRef(loc, arrayAllocateFun);
527+
ApplyInst *applyInst = builder.createApply(
528+
loc, arrayAllocateRef, subMap, ArrayRef<SILValue>(numElementsSIL), false);
529+
530+
// Extract the elements of the tuple returned by the call to the allocator.
531+
SILValue arraySIL;
532+
SILValue storagePointerSIL;
533+
if (fun.hasOwnership()) {
534+
DestructureTupleInst *destructureInst =
535+
builder.createDestructureTuple(loc, applyInst);
536+
arraySIL = destructureInst->getResults()[0];
537+
storagePointerSIL = destructureInst->getResults()[1];
538+
} else {
539+
SILType arraySILType = SILType::getPrimitiveObjectType(arrayType);
540+
arraySIL = builder.createTupleExtract(loc, applyInst, 0, arraySILType);
541+
storagePointerSIL = builder.createTupleExtract(
542+
loc, applyInst, 1, SILType::getRawPointerType(astContext));
543+
}
544+
545+
if (elements.empty()) {
546+
// Nothing more to be done if we are creating an empty array.
547+
return arraySIL;
548+
}
549+
550+
// Convert the pointer to the storage to an address. The elements will be
551+
// stored into offsets from this address.
552+
SILType elementSILType = elements[0]->getType();
553+
PointerToAddressInst *storageAddr = builder.createPointerToAddress(
554+
loc, storagePointerSIL, elementSILType.getAddressType(),
555+
/*isStrict*/ true,
556+
/*isInvariant*/ false);
557+
558+
// Iterate over the elements and store them into the storage address
559+
// after offsetting it appropriately.
560+
561+
// Create a TypeLowering for emitting stores. Note that TypeLowering
562+
// provides a utility for emitting stores for storing trivial and
563+
// non-trivial values, and also handles OSSA and non-OSSA.
564+
const TypeLowering &elementTypeLowering =
565+
builder.getTypeLowering(elementSILType);
566+
567+
unsigned elementIndex = 0;
568+
for (SILValue elementSIL : elements) {
569+
// Compute the address where the element must be stored.
570+
SILValue currentStorageAddr;
571+
if (elementIndex != 0) {
572+
SILValue indexSIL = builder.createIntegerLiteral(
573+
loc, SILType::getBuiltinWordType(astContext), elementIndex);
574+
currentStorageAddr = builder.createIndexAddr(loc, storageAddr, indexSIL);
575+
} else {
576+
currentStorageAddr = storageAddr;
577+
}
578+
// Store the generated element into the currentStorageAddr. This is an
579+
// initializing store and therefore there is no need to free any existing
580+
// element.
581+
elementTypeLowering.emitStore(builder, loc, elementSIL, currentStorageAddr,
582+
StoreOwnershipQualifier::Init);
583+
elementIndex++;
584+
}
585+
return arraySIL;
586+
}
587+
453588
/// Generate SIL code that computes the constant given by the symbolic value
454589
/// `symVal`. Note that strings and struct-typed constant values will require
455590
/// multiple instructions to be emitted.
@@ -532,6 +667,23 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal,
532667
loc, aggregateType, ArrayRef<SILValue>(newPropertySIL));
533668
return newStructInst;
534669
}
670+
case SymbolicValue::Array: {
671+
assert(expectedType->isEqual(symVal.getArrayType()));
672+
CanType elementType;
673+
ArrayRef<SymbolicValue> arrayElements =
674+
symVal.getStorageOfArray().getStoredElements(elementType);
675+
676+
// Emit code for the symbolic values corresponding to the array elements.
677+
SmallVector<SILValue, 8> elementSILValues;
678+
for (SymbolicValue elementSymVal : arrayElements) {
679+
SILValue elementSIL = emitCodeForSymbolicValue(elementSymVal, elementType,
680+
builder, loc, stringInfo);
681+
elementSILValues.push_back(elementSIL);
682+
}
683+
SILValue arraySIL = emitCodeForConstantArray(
684+
elementSILValues, expectedType->getCanonicalType(), builder, loc);
685+
return arraySIL;
686+
}
535687
default: {
536688
llvm_unreachable("Symbolic value kind is not supported");
537689
}

0 commit comments

Comments
 (0)