19
19
#include " swift/SILOptimizer/Analysis/ValueTracking.h"
20
20
#include " swift/SIL/SILFunction.h"
21
21
#include " swift/SIL/SILBasicBlock.h"
22
+ #include " swift/SIL/SILGlobalVariable.h"
22
23
#include " swift/SIL/SILBuilder.h"
23
24
#include " swift/AST/SemanticAttrs.h"
24
25
#include " swift/AST/ParameterList.h"
@@ -58,13 +59,15 @@ class StringOptimization {
58
59
StringRef str;
59
60
60
61
// / Negative means: not constant
61
- int numCodeUnits = -1 ;
62
-
63
- // / Not 0 for the empty-string initializer which reserves a capacity.
64
62
int reservedCapacity = 0 ;
65
63
66
- bool isConstant () const { return numCodeUnits >= 0 ; }
64
+ StringInfo (StringRef str, int reservedCapacity = 0 ) :
65
+ str (str), reservedCapacity(reservedCapacity) { }
66
+
67
+ bool isConstant () const { return reservedCapacity >= 0 ; }
67
68
bool isEmpty () const { return isConstant () && str.empty (); }
69
+
70
+ static StringInfo unknown () { return StringInfo (StringRef (), -1 ); }
68
71
};
69
72
70
73
// / The stdlib's String type.
@@ -96,6 +99,8 @@ class StringOptimization {
96
99
static void invalidateModifiedObjects (SILInstruction *inst,
97
100
llvm::DenseMap<SILValue, SILValue> &storedStrings);
98
101
static StringInfo getStringInfo (SILValue value);
102
+ static StringInfo getStringFromStaticLet (SILValue value);
103
+
99
104
static Optional<int > getIntConstant (SILValue value);
100
105
static void replaceAppendWith (ApplyInst *appendCall, SILValue newValue,
101
106
bool copyNewValue);
@@ -368,32 +373,31 @@ void StringOptimization::invalidateModifiedObjects(SILInstruction *inst,
368
373
369
374
// / Returns information about value if it's a constant string.
370
375
StringOptimization::StringInfo StringOptimization::getStringInfo (SILValue value) {
371
- // Start with a non-constant result.
372
- StringInfo result ;
376
+ if (!value)
377
+ return StringInfo::unknown () ;
373
378
374
- auto *apply = dyn_cast_or_null<ApplyInst>(value);
375
- if (!apply)
376
- return result;
379
+ auto *apply = dyn_cast<ApplyInst>(value);
380
+ if (!apply) {
381
+ return getStringFromStaticLet (value);
382
+ }
377
383
378
384
SILFunction *callee = apply->getReferencedFunctionOrNull ();
379
385
if (!callee)
380
- return result ;
386
+ return StringInfo::unknown () ;
381
387
382
388
if (callee->hasSemanticsAttr (semantics::STRING_INIT_EMPTY)) {
383
389
// An empty string initializer.
384
- result.numCodeUnits = 0 ;
385
- return result;
390
+ return StringInfo (" " );
386
391
}
387
392
388
393
if (callee->hasSemanticsAttr (semantics::STRING_INIT_EMPTY_WITH_CAPACITY)) {
389
394
// An empty string initializer with initial capacity.
390
- result.numCodeUnits = 0 ;
391
- result.reservedCapacity = std::numeric_limits<int >::max ();
395
+ int reservedCapacity = std::numeric_limits<int >::max ();
392
396
if (apply->getNumArguments () > 0 ) {
393
397
if (Optional<int > capacity = getIntConstant (apply->getArgument (0 )))
394
- result. reservedCapacity = capacity.getValue ();
398
+ reservedCapacity = capacity.getValue ();
395
399
}
396
- return result ;
400
+ return StringInfo ( " " , reservedCapacity) ;
397
401
}
398
402
399
403
if (callee->hasSemanticsAttr (semantics::STRING_MAKE_UTF8)) {
@@ -404,13 +408,79 @@ StringOptimization::StringInfo StringOptimization::getStringInfo(SILValue value)
404
408
auto *intLiteral = dyn_cast<IntegerLiteralInst>(lengthVal);
405
409
if (intLiteral && stringLiteral &&
406
410
// For simplicity, we only support UTF8 string literals.
407
- stringLiteral->getEncoding () == StringLiteralInst::Encoding::UTF8) {
408
- result.str = stringLiteral->getValue ();
409
- result.numCodeUnits = intLiteral->getValue ().getSExtValue ();
410
- return result;
411
+ stringLiteral->getEncoding () == StringLiteralInst::Encoding::UTF8 &&
412
+ // This passed number of code units should always match the size of the
413
+ // string in the string literal. Just to be on the safe side, check it.
414
+ intLiteral->getValue () == stringLiteral->getValue ().size ()) {
415
+ return StringInfo (stringLiteral->getValue ());
411
416
}
412
417
}
413
- return result;
418
+ return StringInfo::unknown ();
419
+ }
420
+
421
+ // / Return the string if \p value is a load from a global static let, which is
422
+ // / initialized with a String constant.
423
+ StringOptimization::StringInfo
424
+ StringOptimization::getStringFromStaticLet (SILValue value) {
425
+ // Match the pattern
426
+ // %ptr_to_global = apply %addressor()
427
+ // %global_addr = pointer_to_address %ptr_to_global
428
+ // %value = load %global_addr
429
+ auto *load = dyn_cast<LoadInst>(value);
430
+ if (!load)
431
+ return StringInfo::unknown ();
432
+
433
+ auto *pta = dyn_cast<PointerToAddressInst>(load->getOperand ());
434
+ if (!pta)
435
+ return StringInfo::unknown ();
436
+
437
+ auto *addressorCall = dyn_cast<ApplyInst>(pta->getOperand ());
438
+ if (!addressorCall)
439
+ return StringInfo::unknown ();
440
+
441
+ SILFunction *addressorFunc = addressorCall->getReferencedFunctionOrNull ();
442
+ if (!addressorFunc)
443
+ return StringInfo::unknown ();
444
+
445
+ // The addressor function has a builtin.once call to the initializer.
446
+ BuiltinInst *onceCall = nullptr ;
447
+ SILFunction *initializer = findInitializer (addressorFunc, onceCall);
448
+ if (!initializer)
449
+ return StringInfo::unknown ();
450
+
451
+ if (initializer->size () != 1 )
452
+ return StringInfo::unknown ();
453
+
454
+ // Match the pattern
455
+ // %addr = global_addr @staticStringLet
456
+ // ...
457
+ // %str = apply %stringInitializer(...)
458
+ // store %str to %addr
459
+ GlobalAddrInst *gAddr = nullptr ;
460
+ for (SILInstruction &inst : initializer->front ()) {
461
+ if (auto *ga = dyn_cast<GlobalAddrInst>(&inst)) {
462
+ if (gAddr )
463
+ return StringInfo::unknown ();
464
+ gAddr = ga;
465
+ }
466
+ }
467
+ if (!gAddr || !gAddr ->getReferencedGlobal ()->isLet ())
468
+ return StringInfo::unknown ();
469
+
470
+ Operand *gUse = gAddr ->getSingleUse ();
471
+ auto *store = dyn_cast<StoreInst>(gUse ->getUser ());
472
+ if (!store || store->getDest () != gAddr )
473
+ return StringInfo::unknown ();
474
+
475
+ SILValue initVal = store->getSrc ();
476
+
477
+ // This check is probably not needed, but let's be on the safe side:
478
+ // it prevents an infinite recursion if the initializer of the global is
479
+ // itself a load of another global, and so on.
480
+ if (isa<LoadInst>(initVal))
481
+ return StringInfo::unknown ();
482
+
483
+ return getStringInfo (initVal);
414
484
}
415
485
416
486
// / Returns the constant integer value if \a value is an Int or Bool struct with
0 commit comments