Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Compiler option to profile the compilation time #1614

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion Sources/CodeGen/LLVM/LLVMProgram.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,25 @@ public struct LLVMProgram {
/// The LLVM modules in the program.
public private(set) var llvmModules: [ModuleDecl.ID: SwiftyLLVM.Module] = [:]

let profiler: ProfilingMeasurements?

/// Creates a transpiling `ir`, whose main module is `mainModule`, for `target`.
///
/// - Parameters:
/// - target: The machine for which `ir` is transpiled. Defaults to the current host.
public init(
_ ir: IR.Program,
mainModule: ModuleDecl.ID,
for target: SwiftyLLVM.TargetMachine? = nil
for target: SwiftyLLVM.TargetMachine? = nil,
profileWith profiler: ProfilingMeasurements?
) throws {
self.profiler = profiler
self.target = try target ?? SwiftyLLVM.TargetMachine(for: .host())
for m in ir.modules.keys {
var context = CodeGenerationContext(forCompiling: m, of: ir)
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.IRConversion)
defer { probe?.stop() }

let transpilation = SwiftyLLVM.Module(transpiling: m, in: &context)
do {
try transpilation.verify()
Expand All @@ -38,6 +45,8 @@ public struct LLVMProgram {

/// Applies the mandatory IR simplification passes on each module in `self`.
public mutating func applyMandatoryPasses() {
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.MandatoryPass)
defer { probe?.stop() }
for k in llvmModules.keys {
llvmModules[k]!.runDefaultModulePasses(optimization: .none, for: target)
}
Expand All @@ -47,6 +56,8 @@ public struct LLVMProgram {
///
/// Optimization applied are similar to clang's `-O3`.
public mutating func optimize() {
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.Optimizations)
defer { probe?.stop() }
for k in llvmModules.keys {
llvmModules[k]!.runDefaultModulePasses(optimization: .aggressive, for: target)
}
Expand All @@ -63,6 +74,8 @@ public struct LLVMProgram {
var result: [URL] = []
for m in llvmModules.values {
let f = directory.appendingPathComponent(m.name).appendingPathExtension("o")
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.EmitPhase)
defer { probe?.stop() }
try m.write(type, for: target, to: f.fileSystemPath)
result.append(f)
}
Expand Down
62 changes: 48 additions & 14 deletions Sources/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@
help: "Type-check the input file(s).")
private var typeCheckOnly: Bool = false

@Flag(
name: [.customLong("profile-compiler")],
help: "Profile the compilation time")
private var profileCompiler: Bool = false

@Option(
name: [.customLong("trace-inference")],
help: ArgumentHelp(
Expand Down Expand Up @@ -241,13 +246,18 @@

let productName = makeProductName(inputs)

// Pool of measurements for time profiling
var profiler: ProfilingMeasurements? = profileCompiler ? ProfilingMeasurements() : nil
defer { profiler?.printProfilingReport() }

// There's no need to load the standard library under `--emit raw-ast`.
if outputType == .rawAST {
var a = AST()
_ = try a.loadModule(
productName, parsing: sourceFiles(in: inputs),
withBuiltinModuleAccess: importBuiltinModule,
reportingDiagnosticsTo: &log)
reportingDiagnosticsTo: &log,
profileWith: profiler)
try write(a, to: astFile(productName))
return
}
Expand All @@ -266,7 +276,8 @@
annotating: ScopedProgram(a), inParallel: experimentalParallelTypeChecking,
reportingDiagnosticsTo: &log,
tracingInferenceIf: shouldTraceInference,
loggingRequirementSystemIf: shouldLogRequirements)
loggingRequirementSystemIf: shouldLogRequirements,
profileWith: profiler)
}

let (program, sourceModule) = try dependencies.loadModule(
Expand All @@ -277,14 +288,14 @@
try ast.loadModule(
productName, parsing: sourceFiles(in: inputs), inNodeSpace: space,
withBuiltinModuleAccess: importBuiltinModule,
reportingDiagnosticsTo: &log)
reportingDiagnosticsTo: &log,
profileWith: profiler)
}

if typeCheckOnly { return }

// IR

var ir = try lower(program: program, reportingDiagnosticsTo: &log)
var ir = try lower(program: program, reportingDiagnosticsTo: &log, profileWith: profiler)

if outputType == .ir || outputType == .rawIR {
let m = ir.modules[sourceModule]!
Expand All @@ -295,7 +306,7 @@
// LLVM

logVerbose("begin depolymorphization pass.\n")
ir.depolymorphize()
ir.depolymorphize(profileWith: profiler)

logVerbose("create LLVM target machine.\n")
#if os(Windows)
Expand All @@ -305,7 +316,8 @@
#endif

logVerbose("create LLVM program.\n")
var llvmProgram = try LLVMProgram(ir, mainModule: sourceModule, for: target)
var llvmProgram = try LLVMProgram(
ir, mainModule: sourceModule, for: target, profileWith: profiler)

logVerbose("LLVM mandatory passes.\n")
llvmProgram.applyMandatoryPasses()
Expand Down Expand Up @@ -340,11 +352,14 @@
let binaryPath = executableOutputPath(default: productName)

#if os(macOS)
try makeMacOSExecutable(at: binaryPath, linking: objectFiles, diagnostics: &log)
try makeMacOSExecutable(
at: binaryPath, linking: objectFiles, diagnostics: &log, profileWith: profiler)
#elseif os(Linux)
try makeLinuxExecutable(at: binaryPath, linking: objectFiles, diagnostics: &log)
try makeLinuxExecutable(
at: binaryPath, linking: objectFiles, diagnostics: &log, profileWith: profiler)
#elseif os(Windows)
try makeWindowsExecutable(at: binaryPath, linking: objectFiles, diagnostics: &log)
try makeWindowsExecutable(
at: binaryPath, linking: objectFiles, diagnostics: &log, profileWith: profiler)
#else
_ = (objectFiles, binaryPath)
UNIMPLEMENTED()
Expand Down Expand Up @@ -379,8 +394,11 @@
///
/// Mandatory IR passes are applied unless `self.outputType` is `.rawIR`.
private func lower(
program: TypedProgram, reportingDiagnosticsTo log: inout DiagnosticSet
program: TypedProgram, reportingDiagnosticsTo log: inout DiagnosticSet,
profileWith profiler: ProfilingMeasurements?
) throws -> IR.Program {
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.IRLowering)
defer { probe?.stop() }
var loweredModules: [ModuleDecl.ID: IR.Module] = [:]
for d in program.ast.modules {
loweredModules[d] = try lower(d, in: program, reportingDiagnosticsTo: &log)
Expand Down Expand Up @@ -410,9 +428,15 @@
/// Combines the object files located at `objects` into an executable file at `binaryPath`,
/// logging diagnostics to `log`.
private func makeMacOSExecutable(
at binaryPath: String, linking objects: [URL], diagnostics: inout DiagnosticSet
at binaryPath: String, linking objects: [URL], diagnostics: inout DiagnosticSet,
profileWith profiler: ProfilingMeasurements?
) throws {
let xcrun = try findExecutable(invokedAs: "xcrun").fileSystemPath

// linking profiling probe
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.LinkPhase)
defer { probe?.stop() }

Check warning on line 439 in Sources/Driver/Driver.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Driver/Driver.swift#L435-L439

Added lines #L435 - L439 were not covered by tests
let sdk =
try runCommandLine(xcrun, ["--sdk", "macosx", "--show-sdk-path"], diagnostics: &diagnostics)
?? ""
Expand All @@ -434,7 +458,8 @@
private func makeLinuxExecutable(
at binaryPath: String,
linking objects: [URL],
diagnostics: inout DiagnosticSet
diagnostics: inout DiagnosticSet,
profileWith profiler: ProfilingMeasurements?
) throws {
var arguments = [
"-o", binaryPath,
Expand All @@ -443,6 +468,10 @@
arguments.append(contentsOf: objects.map(\.fileSystemPath))
arguments.append(contentsOf: libraries.map({ "-l\($0)" }))

// linking profiling probe
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.LinkPhase)
defer { probe?.stop() }

// Note: We use "clang" rather than "ld" so that to deal with the entry point of the program.
// See https://stackoverflow.com/questions/51677440
try runCommandLine(
Expand All @@ -454,8 +483,13 @@
private func makeWindowsExecutable(
at binaryPath: String,
linking objects: [URL],
diagnostics: inout DiagnosticSet
diagnostics: inout DiagnosticSet,
profileWith profiler: ProfilingMeasurements?
) throws {
// linking profiling probe
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.LinkPhase)
defer { probe?.stop() }

Check warning on line 492 in Sources/Driver/Driver.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Driver/Driver.swift#L489-L492

Added lines #L489 - L492 were not covered by tests
try runCommandLine(
findExecutable(invokedAs: "lld-link").fileSystemPath,
["-defaultlib:HyloLibC", "-defaultlib:msvcrt", "-out:" + binaryPath]
Expand Down
7 changes: 4 additions & 3 deletions Sources/FrontEnd/AST/AST.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,13 @@ public struct AST {
public mutating func loadModule<S: Sequence>(
_ name: String, parsing sourceCode: S, inNodeSpace space: Int? = nil,
withBuiltinModuleAccess builtinModuleAccess: Bool = false,
reportingDiagnosticsTo log: inout DiagnosticSet
reportingDiagnosticsTo log: inout DiagnosticSet,
profileWith profiler: ProfilingMeasurements? = nil
) throws -> ModuleDecl.ID where S.Element == SourceFile {
try loadModule(reportingDiagnosticsTo: &log) { (me, log, k) in
// Suppress thrown diagnostics until all files are parsed.
let translations = sourceCode.compactMap { (f) in
try? Parser.parse(f, inNodeSpace: k, in: &me, diagnostics: &log)
try? Parser.parse(f, inNodeSpace: k, in: &me, diagnostics: &log, profileWith: profiler)
}

let m = me.insert(
Expand Down Expand Up @@ -446,7 +447,7 @@ public struct AST {

case TuplePattern.self:
let x = TuplePattern.ID(pattern)!
for i in 0 ..< self[x].elements.count {
for i in 0..<self[x].elements.count {
visit(pattern: self[x].elements[i].pattern, subfield: subfield + [i], result: &result)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/FrontEnd/AST/NodeIDs/DeclIDs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public struct DeclIDs {

/// The identifiers in `self` denoting type extending declarations.
public var extensions: ArraySlice<AnyDeclID> {
all[0 ..< extensionEndIndex]
all[0..<extensionEndIndex]
}

/// The identifiers in `self` _not_ denoting type extending declarations.
Expand Down
33 changes: 21 additions & 12 deletions Sources/FrontEnd/Parse/Lexer.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Utils

/// A type that tokenize a source file.
public struct Lexer: IteratorProtocol, Sequence {

Expand All @@ -7,10 +9,13 @@ public struct Lexer: IteratorProtocol, Sequence {
/// The current position in the source file.
private(set) var index: String.Index

private let profiler: ProfilingMeasurements?

/// Creates a lexer generating tokens from the contents of `source`.
public init(tokenizing source: SourceFile) {
public init(tokenizing source: SourceFile, profileWith profiler: ProfilingMeasurements? = nil) {
self.sourceCode = source
self.index = source.text.startIndex
self.profiler = profiler
}

/// The current location of the lexer in `sourceCode`.
Expand All @@ -21,6 +26,10 @@ public struct Lexer: IteratorProtocol, Sequence {

/// Advances to the next token and returns it, or returns `nil` if no next token exists.
public mutating func next() -> Token? {
// Start measuring Lexer time
let probe = profiler?.createAndStartProfilingProbe(MeasurementType.Lexer)
defer { probe?.stop() }

// Skip whitespaces and comments.
while true {
if index == sourceCode.text.endIndex { return nil }
Expand Down Expand Up @@ -54,7 +63,7 @@ public struct Lexer: IteratorProtocol, Sequence {
} else {
return Token(
kind: .unterminatedBlockComment,
site: sourceCode.range(start ..< index))
site: sourceCode.range(start..<index))
}
}

Expand All @@ -68,12 +77,12 @@ public struct Lexer: IteratorProtocol, Sequence {

// Scan a new token.
let head = sourceCode.text[index]
var token = Token(kind: .invalid, site: location ..< location)
var token = Token(kind: .invalid, site: location..<location)

// Try scan for exponent if previous token was .int
if previousTokenWasInt, let next = peek(), next == "e" || next == "E" {
discard()
if let _ = scanIntegralLiteral(allowingPlus: true) {
if scanIntegralLiteral(allowingPlus: true) != nil {
token.kind = .exponent
}
token.site.extend(upTo: index)
Expand Down Expand Up @@ -160,7 +169,7 @@ public struct Lexer: IteratorProtocol, Sequence {
if peek() == "`" {
let start = sourceCode.position(sourceCode.text.index(after: token.site.startIndex))
token.kind = .name
token.site = start ..< location
token.site = start..<location
discard()
return token
} else {
Expand Down Expand Up @@ -242,7 +251,7 @@ public struct Lexer: IteratorProtocol, Sequence {
case "<", ">":
// Leading angle brackets are tokenized individually, to parse generic clauses.
discard()
oper = sourceCode.text[token.site.startIndex ..< index]
oper = sourceCode.text[token.site.startIndex..<index]

default:
oper = take(while: { $0.isOperator })
Expand Down Expand Up @@ -348,7 +357,7 @@ public struct Lexer: IteratorProtocol, Sequence {
index = sourceCode.text.index(after: index)
}

return sourceCode.text[start ..< index]
return sourceCode.text[start..<index]
}

/// Consumes an integral literal and returns its kind, or returns `nil` if it fails to scan a valid integer.
Expand Down Expand Up @@ -405,23 +414,23 @@ extension Character {
/// Indicates whether `self` character represents a decimal digit.
fileprivate var isDecDigit: Bool {
guard let ascii = asciiValue else { return false }
return (0x30 ... 0x39) ~= ascii // 0 ... 9
return (0x30...0x39) ~= ascii // 0 ... 9
|| 0x5f == ascii // _
}

/// Indicates whether `self` represents an hexadecimal digit.
fileprivate var isHexDigit: Bool {
guard let ascii = asciiValue else { return false }
return (0x30 ... 0x39) ~= ascii // 0 ... 9
|| (0x41 ... 0x46) ~= ascii // A ... F
|| (0x61 ... 0x66) ~= ascii // a ... f
return (0x30...0x39) ~= ascii // 0 ... 9
|| (0x41...0x46) ~= ascii // A ... F
|| (0x61...0x66) ~= ascii // a ... f
|| 0x5f == ascii // _
}

/// /// Indicates whether `self` represents an octal digit.
fileprivate var isOctDigit: Bool {
guard let ascii = asciiValue else { return false }
return (0x30 ... 0x37) ~= ascii // 0 ... 7
return (0x30...0x37) ~= ascii // 0 ... 7
|| 0x5f == ascii // _
}

Expand Down
6 changes: 3 additions & 3 deletions Sources/FrontEnd/Parse/Parser+Diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension Diagnostic {
static func error(expected subject: String, at location: SourcePosition, notes: [Diagnostic] = [])
-> Diagnostic
{
.error("expected \(subject)", at: location ..< location, notes: notes)
.error("expected \(subject)", at: location..<location, notes: notes)
}

static func error(
Expand Down Expand Up @@ -41,11 +41,11 @@ extension Diagnostic {
}

static func error(unterminatedCommentStartingAt p: SourcePosition) -> Diagnostic {
.error("unterminated comment", at: p ..< p)
.error("unterminated comment", at: p..<p)
}

static func error(unterminatedStringStartingAt p: SourcePosition) -> Diagnostic {
.error("unterminated string", at: p ..< p)
.error("unterminated string", at: p..<p)
}

static func error(duplicateAccessModifier m: SourceRepresentable<AccessModifier>) -> Diagnostic {
Expand Down
Loading