Skip to content

Commit 0ca00fe

Browse files
Merge pull request #4554 from swiftwasm/main
[pull] swiftwasm from main
2 parents 98dc522 + 727d3fe commit 0ca00fe

File tree

36 files changed

+605
-227
lines changed

36 files changed

+605
-227
lines changed

benchmark/scripts/compare_perf_tests.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ class PerformanceTestResult(object):
229229
statistics for normal distribution (MEAN, SD):
230230
#,TEST,SAMPLES,MIN(μs),MAX(μs),MEAN(μs),SD(μs),MEDIAN(μs),MAX_RSS(B)
231231
And new quantiles format with variable number of columns:
232-
#,TEST,SAMPLES,MIN(μs),MEDIAN(μs),MAX(μs)
233-
#,TEST,SAMPLES,MIN(μs),Q1(μs),Q2(μs),Q3(μs),MAX(μs),MAX_RSS(B)
232+
#,TEST,SAMPLES,QMIN(μs),MEDIAN(μs),MAX(μs)
233+
#,TEST,SAMPLES,QMIN(μs),Q1(μs),Q2(μs),Q3(μs),MAX(μs),MAX_RSS(B)
234234
The number of columns between MIN and MAX depends on the test driver's
235235
`--quantile`parameter. In both cases, the last column, MAX_RSS is optional.
236236
"""
@@ -244,9 +244,10 @@ def __init__(self, csv_row, quantiles=False, memory=False, delta=False, meta=Fal
244244
self.name = csv_row[1] # Name of the performance test
245245
self.num_samples = int(csv_row[2]) # Number of measurements taken
246246

247+
mem_index = (-1 if memory else 0) + (-3 if meta else 0)
247248
if quantiles: # Variable number of columns representing quantiles
248-
mem_index = (-1 if memory else 0) + (-3 if meta else 0)
249249
runtimes = csv_row[3:mem_index] if memory or meta else csv_row[3:]
250+
last_runtime_index = mem_index - 1
250251
if delta:
251252
runtimes = [int(x) if x else 0 for x in runtimes]
252253
runtimes = functools.reduce(
@@ -277,20 +278,21 @@ def __init__(self, csv_row, quantiles=False, memory=False, delta=False, meta=Fal
277278
sams.mean,
278279
sams.sd,
279280
)
280-
self.max_rss = ( # Maximum Resident Set Size (B)
281-
int(csv_row[mem_index]) if memory else None
282-
)
283281
else: # Legacy format with statistics for normal distribution.
284282
self.min = int(csv_row[3]) # Minimum runtime (μs)
285283
self.max = int(csv_row[4]) # Maximum runtime (μs)
286284
self.mean = float(csv_row[5]) # Mean (average) runtime (μs)
287285
self.sd = float(csv_row[6]) # Standard Deviation (μs)
288286
self.median = int(csv_row[7]) # Median runtime (μs)
289-
self.max_rss = ( # Maximum Resident Set Size (B)
290-
int(csv_row[8]) if len(csv_row) > 8 else None
291-
)
287+
last_runtime_index = 7
292288
self.samples = None
293289

290+
self.max_rss = ( # Maximum Resident Set Size (B)
291+
int(csv_row[mem_index]) if (
292+
memory and len(csv_row) > (last_runtime_index + 1)
293+
) else None
294+
)
295+
294296
# Optional measurement metadata. The number of:
295297
# memory pages used, involuntary context switches and voluntary yields
296298
self.mem_pages, self.involuntary_cs, self.yield_count = (
@@ -427,7 +429,7 @@ def _store_memory_stats(self, max_rss, mem_pages):
427429
self.mem_pages = int(mem_pages)
428430

429431
def _configure_format(self, header):
430-
self.quantiles = "MEAN" not in header
432+
self.quantiles = "QMIN" in header
431433
self.memory = "MAX_RSS" in header
432434
self.meta = "PAGES" in header
433435
self.delta = "𝚫" in header
@@ -453,7 +455,7 @@ def _configure_format(self, header):
453455
Yield(len(self.samples), int(since_last_yield))
454456
)
455457
),
456-
re.compile(r"( *#[, \t]+TEST[, \t]+SAMPLES[, \t]+MIN.*)"): _configure_format,
458+
re.compile(r"( *#[, \t]+TEST[, \t]+SAMPLES[, \t].*)"): _configure_format,
457459
# Environmental statistics: memory usage and context switches
458460
re.compile(
459461
r"\s+MAX_RSS \d+ - \d+ = (\d+) \((\d+) pages\)"

benchmark/scripts/test_compare_perf_tests.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def test_init(self):
205205
self.assertEqual(r.samples, None)
206206

207207
log_line = "1,AngryPhonebook,1,12045,12045,12045,0,12045,10510336"
208-
r = PerformanceTestResult(log_line.split(","))
208+
r = PerformanceTestResult(log_line.split(","), memory=True)
209209
self.assertEqual(r.max_rss, 10510336)
210210

211211
def test_init_quantiles(self):
@@ -379,7 +379,11 @@ def test_merge(self):
379379
)[
380380
1:
381381
]
382-
results = list(map(PerformanceTestResult, [line.split(",") for line in tests]))
382+
383+
def makeResult(csv_row):
384+
return PerformanceTestResult(csv_row, memory=True)
385+
386+
results = list(map(makeResult, [line.split(",") for line in tests]))
383387
results[2].setup = 9
384388
results[3].setup = 7
385389

@@ -489,11 +493,14 @@ class OldAndNewLog(unittest.TestCase):
489493
3,Array2D,20,335831,400221,346622,0,346622
490494
1,AngryPhonebook,20,10458,12714,11000,0,11000"""
491495

496+
def makeResult(csv_row):
497+
return PerformanceTestResult(csv_row, memory=True)
498+
492499
old_results = dict(
493500
[
494501
(r.name, r)
495502
for r in map(
496-
PerformanceTestResult,
503+
makeResult,
497504
[line.split(",") for line in old_log_content.splitlines()],
498505
)
499506
]
@@ -503,7 +510,7 @@ class OldAndNewLog(unittest.TestCase):
503510
[
504511
(r.name, r)
505512
for r in map(
506-
PerformanceTestResult,
513+
makeResult,
507514
[line.split(",") for line in new_log_content.splitlines()],
508515
)
509516
]
@@ -557,14 +564,14 @@ def test_parse_results_formatted_text(self):
557564
def test_parse_quantiles(self):
558565
"""Gathers samples from reported quantiles. Handles optional memory."""
559566
r = LogParser.results_from_string(
560-
"""#,TEST,SAMPLES,MIN(μs),MEDIAN(μs),MAX(μs)
567+
"""#,TEST,SAMPLES,QMIN(μs),MEDIAN(μs),MAX(μs)
561568
1,Ackermann,3,54383,54512,54601"""
562569
)["Ackermann"]
563570
self.assertEqual(
564571
[s.runtime for s in r.samples.all_samples], [54383, 54512, 54601]
565572
)
566573
r = LogParser.results_from_string(
567-
"""#,TEST,SAMPLES,MIN(μs),MEDIAN(μs),MAX(μs),MAX_RSS(B)
574+
"""#,TEST,SAMPLES,QMIN(μs),MEDIAN(μs),MAX(μs),MAX_RSS(B)
568575
1,Ackermann,3,54529,54760,55807,266240"""
569576
)["Ackermann"]
570577
self.assertEqual(
@@ -574,21 +581,21 @@ def test_parse_quantiles(self):
574581

575582
def test_parse_delta_quantiles(self):
576583
r = LogParser.results_from_string( # 2-quantile aka. median
577-
"#,TEST,SAMPLES,MIN(μs),𝚫MEDIAN,𝚫MAX\n0,B,1,101,,"
584+
"#,TEST,SAMPLES,QMIN(μs),𝚫MEDIAN,𝚫MAX\n0,B,1,101,,"
578585
)["B"]
579586
self.assertEqual(
580587
(r.num_samples, r.min, r.median, r.max, r.samples.count),
581588
(1, 101, 101, 101, 1),
582589
)
583590
r = LogParser.results_from_string(
584-
"#,TEST,SAMPLES,MIN(μs),𝚫MEDIAN,𝚫MAX\n0,B,2,101,,1"
591+
"#,TEST,SAMPLES,QMIN(μs),𝚫MEDIAN,𝚫MAX\n0,B,2,101,,1"
585592
)["B"]
586593
self.assertEqual(
587594
(r.num_samples, r.min, r.median, r.max, r.samples.count),
588595
(2, 101, 101, 102, 2),
589596
)
590597
r = LogParser.results_from_string( # 20-quantiles aka. ventiles
591-
"#,TEST,SAMPLES,MIN(μs),𝚫V1,𝚫V2,𝚫V3,𝚫V4,𝚫V5,𝚫V6,𝚫V7,𝚫V8,"
598+
"#,TEST,SAMPLES,QMIN(μs),𝚫V1,𝚫V2,𝚫V3,𝚫V4,𝚫V5,𝚫V6,𝚫V7,𝚫V8,"
592599
+ "𝚫V9,𝚫VA,𝚫VB,𝚫VC,𝚫VD,𝚫VE,𝚫VF,𝚫VG,𝚫VH,𝚫VI,𝚫VJ,𝚫MAX\n"
593600
+ "202,DropWhileArray,200,214,,,,,,,,,,,,1,,,,,,2,16,464"
594601
)["DropWhileArray"]
@@ -617,13 +624,13 @@ def test_parse_meta(self):
617624
(3, 9, 50, 15, 36864),
618625
)
619626
r = LogParser.results_from_string(
620-
"#,TEST,SAMPLES,MIN(μs),MAX(μs),PAGES,ICS,YIELD\n" + "0,B,1,4,4,8,31,15"
627+
"#,TEST,SAMPLES,QMIN(μs),MAX(μs),PAGES,ICS,YIELD\n" + "0,B,1,4,4,8,31,15"
621628
)["B"]
622629
self.assertEqual(
623630
(r.min, r.mem_pages, r.involuntary_cs, r.yield_count), (4, 8, 31, 15)
624631
)
625632
r = LogParser.results_from_string(
626-
"#,TEST,SAMPLES,MIN(μs),MAX(μs),MAX_RSS(B),PAGES,ICS,YIELD\n"
633+
"#,TEST,SAMPLES,QMIN(μs),MAX(μs),MAX_RSS(B),PAGES,ICS,YIELD\n"
627634
+ "0,B,1,5,5,32768,8,28,15"
628635
)["B"]
629636
self.assertEqual(
@@ -831,7 +838,8 @@ def test_values(self):
831838
self.assertEqual(
832839
ReportFormatter.values(
833840
PerformanceTestResult(
834-
"1,AngryPhonebook,1,12045,12045,12045,0,12045,10510336".split(",")
841+
"1,AngryPhonebook,1,12045,12045,12045,0,12045,10510336".split(","),
842+
memory=True
835843
)
836844
),
837845
("AngryPhonebook", "12045", "12045", "12045", "10510336"),

benchmark/utils/DriverUtils.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,8 @@ final class TestRunner {
634634
let index: (Int) -> String =
635635
{ q == 2 ? "" : q <= 20 ? base20[$0] : String($0) }
636636
let tail = (1..<q).map { prefix + index($0) } + ["MAX"]
637-
return [withUnit("MIN")] + tail.map(c.delta ? withDelta : withUnit)
637+
// QMIN identifies the quantile format, distinct from formats using "MIN"
638+
return [withUnit("QMIN")] + tail.map(c.delta ? withDelta : withUnit)
638639
}
639640
return (
640641
["#", "TEST", "SAMPLES"] +

cmake/modules/SwiftCXXUtils.cmake

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
set(SWIFT_LIBSTDCXX_PLATFORMS
33
"LINUX"
44
"FREEBSD"
5-
"OPENBSD"
65
"CYGWIN"
76
"HAIKU")

include/swift/AST/Types.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4460,6 +4460,32 @@ class SILFunctionType final
44604460
SILType getDirectFormalResultsType(SILModule &M,
44614461
TypeExpansionContext expansion);
44624462

4463+
unsigned getNumIndirectFormalYields() const {
4464+
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
4465+
}
4466+
/// Does this function have any formally indirect yields?
4467+
bool hasIndirectFormalYields() const {
4468+
return getNumIndirectFormalYields() != 0;
4469+
}
4470+
unsigned getNumDirectFormalYields() const {
4471+
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
4472+
}
4473+
4474+
struct IndirectFormalYieldFilter {
4475+
bool operator()(SILYieldInfo yield) const {
4476+
return !yield.isFormalIndirect();
4477+
}
4478+
};
4479+
4480+
using IndirectFormalYieldIter =
4481+
llvm::filter_iterator<const SILYieldInfo *, IndirectFormalYieldFilter>;
4482+
using IndirectFormalYieldRange = iterator_range<IndirectFormalYieldIter>;
4483+
4484+
/// A range of SILYieldInfo for all formally Indirect Yields.
4485+
IndirectFormalYieldRange getIndirectFormalYields() const {
4486+
return llvm::make_filter_range(getYields(), IndirectFormalYieldFilter());
4487+
}
4488+
44634489
/// Get a single non-address SILType for all SIL results regardless of whether
44644490
/// they are formally indirect. The actual SIL result type of an apply
44654491
/// instruction that calls this function depends on the current SIL stage and

include/swift/IDE/Utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ bool initCompilerInvocation(
8989
FrontendOptions::ActionType Action, DiagnosticEngine &Diags,
9090
StringRef UnresolvedPrimaryFile,
9191
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
92+
const std::string &swiftExecutablePath,
9293
const std::string &runtimeResourcePath,
9394
const std::string &diagnosticDocumentationPath, time_t sessionTimestamp,
9495
std::string &Error);

include/swift/SIL/SILValue.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ struct ValueOwnershipKind {
255255
explicit ValueOwnershipKind(unsigned newValue) : value(innerty(newValue)) {}
256256
ValueOwnershipKind(const SILFunction &f, SILType type,
257257
SILArgumentConvention convention);
258+
ValueOwnershipKind(const SILFunction &f, SILType type,
259+
SILArgumentConvention convention,
260+
SILModuleConventions moduleConventions);
258261

259262
/// Parse Value into a ValueOwnershipKind.
260263
///

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3949,7 +3949,10 @@ SILFunctionType::SILFunctionType(
39493949
} else {
39503950
assert(normalResults.empty());
39513951
NumAnyResults = yields.size();
3952-
NumAnyIndirectFormalResults = 0; // unused
3952+
NumAnyIndirectFormalResults = std::count_if(
3953+
yields.begin(), yields.end(), [](const SILYieldInfo &yieldInfo) {
3954+
return yieldInfo.isFormalIndirect();
3955+
});
39533956
memcpy(getMutableYields().data(), yields.data(),
39543957
yields.size() * sizeof(SILYieldInfo));
39553958
}

lib/ClangImporter/ClangImporter.cpp

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#include "llvm/Support/Path.h"
7474
#include "llvm/Support/YAMLParser.h"
7575
#include "llvm/Support/YAMLTraits.h"
76+
#include "llvm/Support/VirtualFileSystem.h"
7677
#include <algorithm>
7778
#include <memory>
7879

@@ -740,14 +741,6 @@ importer::getNormalInvocationArguments(
740741
} else {
741742
// FIXME: Emit a warning of some kind.
742743
}
743-
744-
if (EnableCXXInterop) {
745-
if (auto path =
746-
getLibStdCxxModuleMapPath(searchPathOpts, triple, buffer)) {
747-
invocationArgStrs.push_back(
748-
(Twine("-fmodule-map-file=") + *path).str());
749-
}
750-
}
751744
}
752745

753746
if (searchPathOpts.getSDKPath().empty()) {
@@ -917,6 +910,92 @@ importer::addCommonInvocationArguments(
917910
}
918911
}
919912

913+
/// On Linux, some platform libraries (glibc, libstdc++) are not modularized.
914+
/// We inject modulemaps for those libraries into their include directories
915+
/// to allow using them from Swift.
916+
static SmallVector<std::pair<std::string, std::string>>
917+
getClangInvocationFileMapping(ASTContext &ctx) {
918+
using Path = SmallString<128>;
919+
920+
const llvm::Triple &triple = ctx.LangOpts.Target;
921+
// We currently only need this when building for Linux.
922+
if (!triple.isOSLinux())
923+
return {};
924+
925+
SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
926+
927+
Path sdkPath(searchPathOpts.getSDKPath());
928+
if (sdkPath.empty())
929+
sdkPath = "/";
930+
931+
// Currently only a modulemap for libstdc++ is injected.
932+
if (!ctx.LangOpts.EnableCXXInterop)
933+
return {};
934+
935+
Path actualModuleMapPath;
936+
Path buffer;
937+
if (auto path = getLibStdCxxModuleMapPath(searchPathOpts, triple, buffer))
938+
actualModuleMapPath = path.getValue();
939+
else
940+
return {};
941+
942+
// Only inject the module map if it actually exists. It may not, for example
943+
// if `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
944+
// a Swift compiler not built for Linux targets.
945+
if (!llvm::sys::fs::exists(actualModuleMapPath))
946+
// FIXME: emit a warning of some kind.
947+
return {};
948+
949+
// TODO: remove the libstdcxx.h header and reference all libstdc++ headers
950+
// directly from the modulemap.
951+
Path actualHeaderPath = actualModuleMapPath;
952+
llvm::sys::path::remove_filename(actualHeaderPath);
953+
llvm::sys::path::append(actualHeaderPath, "libstdcxx.h");
954+
955+
Path cxxStdlibsRoot(sdkPath);
956+
llvm::sys::path::append(cxxStdlibsRoot, "usr", "include", "c++");
957+
if (!llvm::sys::fs::exists(cxxStdlibsRoot))
958+
return {};
959+
960+
// Collect all installed versions of libstdc++. We currently have no way to
961+
// know which libstdc++ version will be used for this Clang invocation.
962+
// TODO: extract this information from the Clang driver.
963+
SmallVector<Path, 1> cxxStdlibDirs;
964+
std::error_code errorCode;
965+
for (llvm::vfs::directory_iterator
966+
iter = ctx.SourceMgr.getFileSystem()->dir_begin(cxxStdlibsRoot,
967+
errorCode),
968+
endIter;
969+
!errorCode && iter != endIter; iter = iter.increment(errorCode)) {
970+
cxxStdlibDirs.push_back(Path(iter->path()));
971+
}
972+
973+
SmallVector<std::pair<std::string, std::string>> result;
974+
// Inject a modulemap into the VFS for each of the libstdc++ versions.
975+
for (const Path &cxxStdlibDir : cxxStdlibDirs) {
976+
// Only inject the module map if the module does not already exist at
977+
// {sysroot}/usr/include/module.{map,modulemap}.
978+
Path injectedModuleMapLegacyPath(cxxStdlibDir);
979+
llvm::sys::path::append(injectedModuleMapLegacyPath, "module.map");
980+
if (llvm::sys::fs::exists(injectedModuleMapLegacyPath))
981+
continue;
982+
983+
Path injectedModuleMapPath = cxxStdlibDir;
984+
llvm::sys::path::append(injectedModuleMapPath, "module.modulemap");
985+
if (llvm::sys::fs::exists(injectedModuleMapPath))
986+
continue;
987+
988+
Path injectedHeaderPath = cxxStdlibDir;
989+
llvm::sys::path::append(injectedHeaderPath, "libstdcxx.h");
990+
991+
result.push_back(
992+
{std::string(injectedModuleMapPath), std::string(actualModuleMapPath)});
993+
result.push_back(
994+
{std::string(injectedHeaderPath), std::string(actualHeaderPath)});
995+
}
996+
return result;
997+
}
998+
920999
bool ClangImporter::canReadPCH(StringRef PCHFilename) {
9211000
if (!llvm::sys::fs::exists(PCHFilename))
9221001
return false;
@@ -1169,9 +1248,10 @@ ClangImporter::create(ASTContext &ctx,
11691248
}
11701249
}
11711250

1251+
auto fileMapping = getClangInvocationFileMapping(ctx);
11721252
// Wrap Swift's FS to allow Clang to override the working directory
11731253
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
1174-
llvm::vfs::RedirectingFileSystem::create({}, true,
1254+
llvm::vfs::RedirectingFileSystem::create(fileMapping, true,
11751255
*ctx.SourceMgr.getFileSystem());
11761256

11771257
// Create a new Clang compiler invocation.

0 commit comments

Comments
 (0)