Skip to content

Commit cf460dd

Browse files
authored
Merge pull request #955 from swiftwasm/maxd/master-merge
Resolve conflicts with master
2 parents 9fe521e + c3e2f54 commit cf460dd

File tree

19 files changed

+758
-114
lines changed

19 files changed

+758
-114
lines changed

CMakeLists.txt

+5-9
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ set(SWIFT_ANDROID_DEPLOY_DEVICE_PATH "" CACHE STRING
261261
"Path on an Android device where build products will be pushed. These are used when running the test suite against the device")
262262

263263
#
264-
# User-configurable ICU specific options for Android, FreeBSD, Linux, Haiku, and WebAssembly.
264+
# User-configurable ICU specific options for Android, FreeBSD, Linux, Haiku, and WASI.
265265
#
266266

267267
foreach(sdk ANDROID;FREEBSD;LINUX;WINDOWS;HAIKU;WASI)
@@ -608,6 +608,8 @@ else()
608608
set(SWIFT_HOST_VARIANT_ARCH_default "itanium")
609609
elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(x86|i686)")
610610
set(SWIFT_HOST_VARIANT_ARCH_default "i686")
611+
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "wasm32")
612+
set(SWIFT_HOST_VARIANT_ARCH_default "wasm32")
611613
else()
612614
message(FATAL_ERROR "Unrecognized architecture on host system: ${CMAKE_SYSTEM_PROCESSOR}")
613615
endif()
@@ -792,16 +794,10 @@ if(swift_build_windows AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
792794
configure_sdk_windows("Windows" "msvc" "${SWIFT_SDK_WINDOWS_ARCHITECTURES}")
793795
endif()
794796

795-
# Should we cross-compile the standard library for WebAssembly (WASI)?
797+
# Should we cross-compile the standard library for WASI?
796798
is_sdk_requested(WASI swift_build_wasm)
797799
if(swift_build_wasm AND NOT "${SWIFT_HOST_VARIANT_SDK}" STREQUAL "WASI")
798-
if(SWIFT_BUILD_DYNAMIC_SDK_OVERLAY OR SWIFT_BUILD_DYNAMIC_STDLIB)
799-
message(FATAL_ERROR "Unable to build dynamic overlay/stdlib for WASI. WASM does not support dynamic linking.")
800-
endif()
801-
if("${SWIFT_SDK_WASI_ARCHITECTURES}" STREQUAL "")
802-
set(SWIFT_SDK_WASI_ARCHITECTURES wasm32)
803-
endif()
804-
configure_sdk_unix("WASI" "${SWIFT_SDK_WASI_ARCHITECTURES}")
800+
configure_sdk_unix(WASI wasm32)
805801
endif()
806802

807803
if("${SWIFT_SDKS}" STREQUAL "")

include/swift/SIL/LinearLifetimeChecker.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ class LinearLifetimeChecker {
7979
SILValue value, Operand *consumingUse,
8080
function_ref<void(SILBasicBlock::iterator insertPt)> visitor);
8181

82+
/// Given a linear lifetime defined by \p value and \p consumingUses, return
83+
/// true if all uses in \p usesToTest are strictly not contained within the
84+
/// region where the Linear Lifetime defined by \p value and \p consumingUses
85+
/// is live. Otherwise, returns false.
86+
bool usesNotContainedWithinLifetime(SILValue value,
87+
ArrayRef<Operand *> consumingUses,
88+
ArrayRef<Operand *> usesToTest);
89+
8290
private:
8391
/// Returns true if:
8492
///
@@ -108,7 +116,9 @@ class LinearLifetimeChecker {
108116
Error checkValueImpl(
109117
SILValue value, ArrayRef<Operand *> consumingUses,
110118
ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder,
111-
Optional<function_ref<void(SILBasicBlock *)>> leakingBlockCallback);
119+
Optional<function_ref<void(SILBasicBlock *)>> leakingBlockCallback,
120+
Optional<function_ref<void(Operand *)>>
121+
nonConsumingUsesOutsideLifetimeCallback);
112122
};
113123

114124
} // namespace swift

lib/SIL/Verifier/LinearLifetimeChecker.cpp

+102-36
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/SIL/SILBasicBlock.h"
3131
#include "swift/SIL/SILFunction.h"
3232
#include "llvm/ADT/DenseMap.h"
33+
#include "llvm/ADT/MapVector.h"
3334
#include "llvm/ADT/STLExtras.h"
3435
#include "llvm/Support/Debug.h"
3536

@@ -72,6 +73,11 @@ struct State {
7273
/// put in missing destroys.
7374
Optional<function_ref<void(SILBasicBlock *)>> leakingBlockCallback;
7475

76+
/// If non-null a callback that we should pass all uses that we detect are not
77+
/// within the linear lifetime we are checking.
78+
Optional<function_ref<void(Operand *)>>
79+
nonConsumingUseOutsideLifetimeCallback;
80+
7581
/// The list of passed in consuming uses.
7682
ArrayRef<Operand *> consumingUses;
7783

@@ -101,20 +107,28 @@ struct State {
101107
State(SILValue value, SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
102108
LinearLifetimeChecker::ErrorBuilder &errorBuilder,
103109
Optional<function_ref<void(SILBasicBlock *)>> leakingBlockCallback,
110+
Optional<function_ref<void(Operand *)>>
111+
nonConsumingUseOutsideLifetimeCallback,
104112
ArrayRef<Operand *> consumingUses, ArrayRef<Operand *> nonConsumingUses)
105113
: value(value), beginInst(value->getDefiningInsertionPoint()),
106114
errorBuilder(errorBuilder), visitedBlocks(visitedBlocks),
107115
leakingBlockCallback(leakingBlockCallback),
116+
nonConsumingUseOutsideLifetimeCallback(
117+
nonConsumingUseOutsideLifetimeCallback),
108118
consumingUses(consumingUses), nonConsumingUses(nonConsumingUses) {}
109119

110120
State(SILBasicBlock *beginBlock,
111121
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
112122
LinearLifetimeChecker::ErrorBuilder &errorBuilder,
113123
Optional<function_ref<void(SILBasicBlock *)>> leakingBlockCallback,
124+
Optional<function_ref<void(Operand *)>>
125+
nonConsumingUseOutsideLifetimeCallback,
114126
ArrayRef<Operand *> consumingUses, ArrayRef<Operand *> nonConsumingUses)
115127
: value(), beginInst(&*beginBlock->begin()), errorBuilder(errorBuilder),
116128
visitedBlocks(visitedBlocks),
117129
leakingBlockCallback(leakingBlockCallback),
130+
nonConsumingUseOutsideLifetimeCallback(
131+
nonConsumingUseOutsideLifetimeCallback),
118132
consumingUses(consumingUses), nonConsumingUses(nonConsumingUses) {}
119133

120134
SILBasicBlock *getBeginBlock() const { return beginInst->getParent(); }
@@ -199,16 +213,21 @@ void State::initializeAllNonConsumingUses(
199213
continue;
200214
}
201215

216+
if (nonConsumingUseOutsideLifetimeCallback) {
217+
(*nonConsumingUseOutsideLifetimeCallback)(use);
218+
}
219+
202220
// Otherwise, we emit an error since we found a use before our def. We do
203221
// not bail early here since we want to gather up /all/ that we find.
204-
errorBuilder.handleUseAfterFree([&] {
205-
llvm::errs() << "Found use before def?!\n"
222+
errorBuilder.handleUseOutsideOfLifetime([&] {
223+
llvm::errs() << "Found use outside of lifetime?!\n"
206224
<< "Value: ";
207225
if (auto v = value) {
208226
llvm::errs() << *v;
209227
} else {
210228
llvm::errs() << "N/A. \n";
211229
}
230+
llvm::errs() << "User: " << use->getUser();
212231
});
213232
}
214233
}
@@ -293,10 +312,14 @@ void State::checkForSameBlockUseAfterFree(Operand *consumingUse,
293312
continue;
294313
}
295314

315+
if (nonConsumingUseOutsideLifetimeCallback) {
316+
(*nonConsumingUseOutsideLifetimeCallback)(nonConsumingUse);
317+
}
318+
296319
// NOTE: We do not exit here since we want to catch /all/ errors that we can
297320
// find.
298-
errorBuilder.handleUseAfterFree([&] {
299-
llvm::errs() << "Found use after free?!\n"
321+
errorBuilder.handleUseOutsideOfLifetime([&] {
322+
llvm::errs() << "Found outside of lifetime use?!\n"
300323
<< "Value: ";
301324
if (auto v = value) {
302325
llvm::errs() << *v;
@@ -482,30 +505,33 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
482505
}
483506

484507
// If we do have remaining blocks, then these non lifetime ending uses must be
485-
// outside of our "alive" blocks implying a use-after free.
508+
// outside of our "alive" blocks implying an outside of lifetime use. It could
509+
// be a use-before-def or a use-after-free.
486510
for (auto pair : blocksWithNonConsumingUses.getRange()) {
487511
auto *block = pair.first;
488512
if (deBlocks.isDeadEnd(block)) {
489513
continue;
490514
}
491515

492-
errorBuilder.handleUseAfterFree([&] {
493-
llvm::errs() << "Found outside of lifetime uses!\n"
494-
<< "Value: ";
495-
if (auto v = value) {
496-
llvm::errs() << *v;
497-
} else {
498-
llvm::errs() << "N/A. \n";
516+
auto useList = pair.second;
517+
for (auto *use : useList) {
518+
if (nonConsumingUseOutsideLifetimeCallback) {
519+
(*nonConsumingUseOutsideLifetimeCallback)(use);
499520
}
500521

501-
auto uses = pair.second;
502-
llvm::errs() << "User List:\n";
503-
for (auto *op : uses) {
504-
llvm::errs() << "User:" << *op->getUser() << "Block: bb"
522+
errorBuilder.handleUseOutsideOfLifetime([&] {
523+
llvm::errs() << "Found outside of lifetime use!\n"
524+
<< "Value: ";
525+
if (auto v = value) {
526+
llvm::errs() << *v;
527+
} else {
528+
llvm::errs() << "N/A. \n";
529+
}
530+
531+
llvm::errs() << "User:" << *use->getUser() << "Block: bb"
505532
<< block->getDebugID() << "\n";
506-
llvm::errs() << "\n";
507-
}
508-
});
533+
});
534+
}
509535
}
510536
}
511537

@@ -516,12 +542,15 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
516542
LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl(
517543
SILValue value, ArrayRef<Operand *> consumingUses,
518544
ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder,
519-
Optional<function_ref<void(SILBasicBlock *)>> leakingBlockCallback) {
545+
Optional<function_ref<void(SILBasicBlock *)>> leakingBlockCallback,
546+
Optional<function_ref<void(Operand *)>>
547+
nonConsumingUseOutsideLifetimeCallback) {
520548
assert((!consumingUses.empty() || !deadEndBlocks.empty()) &&
521549
"Must have at least one consuming user?!");
522550

523551
State state(value, visitedBlocks, errorBuilder, leakingBlockCallback,
524-
consumingUses, nonConsumingUses);
552+
nonConsumingUseOutsideLifetimeCallback, consumingUses,
553+
nonConsumingUses);
525554

526555
// First add our non-consuming uses and their blocks to the
527556
// blocksWithNonConsumingUses map. While we do this, if we have multiple uses
@@ -551,19 +580,23 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl(
551580
// Check if any of our non consuming uses are not in the parent block and
552581
// are reachable. We flag those as additional use after frees. Any in the
553582
// same block, we would have flagged.
554-
if (llvm::any_of(nonConsumingUses, [&](Operand *use) {
555-
auto *useParent = use->getUser()->getParent();
556-
return useParent != value->getParentBlock() &&
557-
!deadEndBlocks.isDeadEnd(useParent);
558-
})) {
559-
state.errorBuilder.handleUseAfterFree([&] {
560-
llvm::errs() << "Found use after free due to unvisited non lifetime "
561-
"ending uses?!\n"
562-
<< "Value: " << *value << "Remaining Users:\n";
563-
for (const auto &use : nonConsumingUses) {
564-
llvm::errs() << "User: " << *use->getUser();
565-
}
566-
llvm::errs() << "\n";
583+
for (auto *use : nonConsumingUses) {
584+
auto *useParent = use->getUser()->getParent();
585+
if (useParent == value->getParentBlock() ||
586+
deadEndBlocks.isDeadEnd(useParent)) {
587+
continue;
588+
}
589+
590+
if (nonConsumingUseOutsideLifetimeCallback) {
591+
(*nonConsumingUseOutsideLifetimeCallback)(use);
592+
}
593+
594+
state.errorBuilder.handleUseOutsideOfLifetime([&] {
595+
llvm::errs() << "Function: '" << value->getFunction()->getName()
596+
<< "'\n"
597+
<< "Found non consuming use outside of the lifetime being "
598+
"verified.\n"
599+
<< "Value: " << *value << "User: " << *use->getUser();
567600
});
568601
}
569602

@@ -609,15 +642,15 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValue(
609642
SILValue value, ArrayRef<Operand *> consumingUses,
610643
ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder) {
611644
return checkValueImpl(value, consumingUses, nonConsumingUses, errorBuilder,
612-
None);
645+
None, None);
613646
}
614647

615648
LinearLifetimeChecker::Error LinearLifetimeChecker::checkValue(
616649
SILValue value, ArrayRef<Operand *> consumingUses,
617650
ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder,
618651
function_ref<void(SILBasicBlock *)> leakingBlocksCallback) {
619652
return checkValueImpl(value, consumingUses, nonConsumingUses, errorBuilder,
620-
leakingBlocksCallback);
653+
leakingBlocksCallback, None);
621654
}
622655

623656
bool LinearLifetimeChecker::completeConsumingUseSet(
@@ -645,3 +678,36 @@ bool LinearLifetimeChecker::validateLifetime(
645678
return !checkValue(value, consumingUses, nonConsumingUses, errorBuilder)
646679
.getFoundError();
647680
}
681+
682+
bool LinearLifetimeChecker::usesNotContainedWithinLifetime(
683+
SILValue value, ArrayRef<Operand *> consumingUses,
684+
ArrayRef<Operand *> usesToTest) {
685+
686+
auto errorBehavior = ErrorBehaviorKind(
687+
ErrorBehaviorKind::ReturnFalse |
688+
ErrorBehaviorKind::StoreNonConsumingUsesOutsideLifetime);
689+
ErrorBuilder errorBuilder(*value->getFunction(), errorBehavior);
690+
691+
using OptType = Optional<function_ref<void(Operand *)>>;
692+
#ifndef NDEBUG
693+
SmallVector<Operand *, 32> uniqueUsers;
694+
#endif
695+
unsigned numFoundUses = 0;
696+
auto error = checkValueImpl(value, consumingUses, usesToTest, errorBuilder,
697+
None, OptType([&](Operand *use) {
698+
#ifndef NDEBUG
699+
uniqueUsers.push_back(use);
700+
#endif
701+
++numFoundUses;
702+
}));
703+
704+
#ifndef NDEBUG
705+
// Make sure in assert builds that we did not double count any operands.
706+
sortUnique(uniqueUsers);
707+
assert(numFoundUses == uniqueUsers.size());
708+
#endif
709+
710+
// Return true if we /did/ found an error and when emitting that error, we
711+
// found /all/ uses we were looking for.
712+
return error.getFoundError() && numFoundUses == usesToTest.size();
713+
}

lib/SIL/Verifier/LinearLifetimeCheckerPrivate.h

+25-1
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,22 @@ struct LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::ErrorBehaviorKind {
2525
PrintMessage = 2,
2626
Assert = 4,
2727
ReturnFalseOnLeak = 8,
28+
StoreNonConsumingUsesOutsideLifetime = 16,
2829
PrintMessageAndReturnFalse = PrintMessage | ReturnFalse,
2930
PrintMessageAndAssert = PrintMessage | Assert,
3031
ReturnFalseOnLeakAssertOtherwise = ReturnFalseOnLeak | Assert,
3132
} Value;
3233

3334
ErrorBehaviorKind() : Value(Invalid) {}
3435
ErrorBehaviorKind(inner_t Inner) : Value(Inner) { assert(Value != Invalid); }
36+
ErrorBehaviorKind(unsigned Inner) : Value(inner_t(Inner)) {
37+
assert(Value != Invalid);
38+
}
39+
40+
bool shouldStoreNonConsumingUsesOutsideLifetime() const {
41+
assert(Value != Invalid);
42+
return Value & StoreNonConsumingUsesOutsideLifetime;
43+
}
3544

3645
bool shouldAssert() const {
3746
assert(Value != Invalid);
@@ -60,19 +69,25 @@ class LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::Error {
6069
bool foundUseAfterFree = false;
6170
bool foundLeak = false;
6271
bool foundOverConsume = false;
72+
bool foundUseOutsideOfLifetime = false;
6373

6474
public:
6575
Error() {}
6676

6777
bool getFoundError() const {
68-
return foundUseAfterFree || foundLeak || foundOverConsume;
78+
return foundUseAfterFree || foundLeak || foundOverConsume ||
79+
foundUseOutsideOfLifetime;
6980
}
7081

7182
bool getFoundLeak() const { return foundLeak; }
7283

7384
bool getFoundUseAfterFree() const { return foundUseAfterFree; }
7485

7586
bool getFoundOverConsume() const { return foundOverConsume; }
87+
88+
bool getFoundUseOutsideOfLifetime() const {
89+
return foundUseOutsideOfLifetime;
90+
}
7691
};
7792

7893
class LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::ErrorBuilder {
@@ -162,6 +177,15 @@ class LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::ErrorBuilder {
162177
return handleError(std::move(messagePrinterFunc));
163178
}
164179

180+
/// Handle a case where we either found a use-after-free due to a
181+
/// non-consuming use after our lifetime has ended /or/ if we found a use
182+
/// before def of a non consuming value.
183+
void
184+
handleUseOutsideOfLifetime(llvm::function_ref<void()> &&messagePrinterFunc) {
185+
error->foundUseOutsideOfLifetime = true;
186+
handleError(std::move(messagePrinterFunc));
187+
}
188+
165189
private:
166190
bool handleError(llvm::function_ref<void()> &&messagePrinterFunc,
167191
bool quiet = false) const {

0 commit comments

Comments
 (0)