Skip to content

Support -embed-bitcode and -driver-print-bindings #101

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

Merged
merged 3 commits into from
May 20, 2020
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ The goal of the new Swift driver is to provide a drop-in replacement for the exi
* [x] Immediate mode
* Features
* [x] Precompiled bridging headers
* [ ] Support embedding of bitcode
* [x] Support embedding of bitcode
* [ ] Incremental compilation
* [x] Parseable output, as used by SwiftPM
* [x] Response files
Expand Down
56 changes: 50 additions & 6 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ public struct Driver {
/// The name of the Swift module being built.
public let moduleName: String

/// Was the module name picked by the driver instead of the user.
public let moduleNameIsFallback: Bool

/// The path of the SDK.
public let sdkPath: String?

Expand Down Expand Up @@ -322,7 +325,7 @@ public struct Driver {
Self.computeDebugInfo(&parsedOptions, diagnosticsEngine: diagnosticEngine)

// Determine the module we're building and whether/how the module file itself will be emitted.
(self.moduleOutput, self.moduleName) = try Self.computeModuleInfo(
(self.moduleOutput, self.moduleName, self.moduleNameIsFallback) = try Self.computeModuleInfo(
&parsedOptions, compilerOutputType: compilerOutputType, compilerMode: compilerMode, linkerOutputType: linkerOutputType,
debugInfoLevel: debugInfoLevel, diagnosticsEngine: diagnosticEngine)

Expand Down Expand Up @@ -650,6 +653,13 @@ extension Driver {
return
}

if parsedOptions.contains(.driverPrintBindings) {
for job in jobs {
try printBindings(job)
}
return
}

if parsedOptions.contains(.driverPrintGraphviz) {
var serializer = DOTJobGraphSerializer(jobs: jobs)
serializer.writeDOT(to: &stdoutStream)
Expand Down Expand Up @@ -723,6 +733,21 @@ extension Driver {
print(result)
}

private func printBindings(_ job: Job) throws {
try stdoutStream <<< #"# ""# <<< toolchain.hostTargetTriple().triple
stdoutStream <<< #"" - ""# <<< job.tool.basename
stdoutStream <<< #"", inputs: ["#
stdoutStream <<< job.inputs.map { "\"" + $0.file.name + "\"" }.joined(separator: ", ")

stdoutStream <<< "], output: {"

stdoutStream <<< job.outputs.map { $0.type.name + ": \"" + $0.file.name + "\"" }.joined(separator: ", ")

stdoutStream <<< "}"
stdoutStream <<< "\n"
stdoutStream.flush()
}

private func printVersion<S: OutputByteStream>(outputStream: inout S) throws {
outputStream.write(try Process.checkNonZeroExit(args: toolchain.getToolPath(.swiftCompiler).pathString, "--version"))
outputStream.flush()
Expand Down Expand Up @@ -981,6 +1006,16 @@ extension Driver {
linkerOutputType = .executable
}

// warn if -embed-bitcode is set and the output type is not an object
if parsedOptions.hasArgument(.embedBitcode) && compilerOutputType != .object {
diagnosticsEngine.emit(.warn_ignore_embed_bitcode)
parsedOptions.eraseArgument(.embedBitcode)
}
if parsedOptions.hasArgument(.embedBitcodeMarker) && compilerOutputType != .object {
diagnosticsEngine.emit(.warn_ignore_embed_bitcode_marker)
parsedOptions.eraseArgument(.embedBitcodeMarker)
}

return (compilerOutputType, linkerOutputType)
}
}
Expand All @@ -994,6 +1029,14 @@ extension Diagnostic.Message {
"""
)
}

static var warn_ignore_embed_bitcode: Diagnostic.Message {
.warning("ignoring -embed-bitcode since no object file is being generated")
}

static var warn_ignore_embed_bitcode_marker: Diagnostic.Message {
.warning("ignoring -embed-bitcode-marker since no object file is being generated")
}
}

// Multithreading
Expand Down Expand Up @@ -1249,7 +1292,7 @@ extension Driver {
linkerOutputType: LinkOutputType?,
debugInfoLevel: DebugInfoLevel?,
diagnosticsEngine: DiagnosticsEngine
) throws -> (ModuleOutput?, String) {
) throws -> (ModuleOutput?, String, Bool) {
// Figure out what kind of module we will output.
enum ModuleOutputKind {
case topLevel
Expand Down Expand Up @@ -1285,6 +1328,7 @@ extension Driver {

// Determine the name of the module.
var moduleName: String
var moduleNameIsFallback = false
if let arg = parsedOptions.getLastArgument(.moduleName) {
moduleName = arg.asSingle
} else if compilerMode == .repl {
Expand All @@ -1308,7 +1352,7 @@ extension Driver {
}

func fallbackOrDiagnose(_ error: Diagnostic.Message) {
// FIXME: Current driver notes that this is a "fallback module name".
moduleNameIsFallback = true
if compilerOutputType == nil || maybeBuildingExecutable(&parsedOptions, linkerOutputType: linkerOutputType) {
moduleName = "main"
}
Expand All @@ -1326,7 +1370,7 @@ extension Driver {

// If we're not emiting a module, we're done.
if moduleOutputKind == nil {
return (nil, moduleName)
return (nil, moduleName, moduleNameIsFallback)
}

// Determine the module file to output.
Expand All @@ -1352,9 +1396,9 @@ extension Driver {

switch moduleOutputKind! {
case .topLevel:
return (.topLevel(moduleOutputPath), moduleName)
return (.topLevel(moduleOutputPath), moduleName, moduleNameIsFallback)
case .auxiliary:
return (.auxiliary(moduleOutputPath), moduleName)
return (.auxiliary(moduleOutputPath), moduleName, moduleNameIsFallback)
}
}
}
Expand Down
77 changes: 77 additions & 0 deletions Sources/SwiftDriver/Jobs/BackendJob.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===--------------- BackendJob.swift - Swift Backend Job -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation

extension Driver {
/// Form a backend job.
mutating func backendJob(input: TypedVirtualPath,
allOutputs: inout [TypedVirtualPath]) throws -> Job {
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
var inputs = [TypedVirtualPath]()
var outputs = [TypedVirtualPath]()

commandLine.appendFlag("-frontend")
addCompileModeOption(outputType: compilerOutputType, commandLine: &commandLine)

// Add input arguments.
commandLine.appendFlag(.primaryFile)
commandLine.appendPath(input.file)
inputs.append(input)

commandLine.appendFlag(.embedBitcode)

// -embed-bitcode only supports a restricted set of flags.
commandLine.appendFlag(.target)
commandLine.appendFlag(targetTriple.triple)

// Enable address top-byte ignored in the ARM64 backend.
if targetTriple.arch == .aarch64 {
commandLine.appendFlag(.Xllvm)
commandLine.appendFlag("-aarch64-use-tbi")
}

// Handle the CPU and its preferences.
try commandLine.appendLast(.targetCpu, from: &parsedOptions)
Comment on lines +33 to +44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bunch of this is repeated fromaddCommonFrontendOptions. Can you factor some of it our so there's less duplication here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there is some duplication here, but I don't know how much I can factor out without changing the order of the arguments (That shouldn't really matter, but there are probably some lit tests that rely on the current ordering.

The interesting part from addCommonFrontendOptions is:

    switch compilerMode {
    case .standardCompile, .singleCompile, .batchCompile, .compilePCM:
      commandLine.appendFlag(.target)
      commandLine.appendFlag(targetTriple.triple)

    case .repl, .immediate:
      if parsedOptions.hasArgument(.target) {
        commandLine.appendFlag(.target)
        commandLine.appendFlag(targetTriple.triple)
      }
    }

    if let variant = parsedOptions.getLastArgument(.targetVariant)?.asSingle {
      commandLine.appendFlag(.targetVariant)
      commandLine.appendFlag(Triple(variant, normalizing: true).triple)
    }

    // Enable address top-byte ignored in the ARM64 backend.
    if (targetTriple.arch == .aarch64) {
      commandLine.appendFlag(.Xllvm)
      commandLine.appendFlag("-aarch64-use-tbi")
    }

    // Enable or disable ObjC interop appropriately for the platform
    if targetTriple.isDarwin {
      commandLine.appendFlag(.enableObjcInterop)
    } else {
      commandLine.appendFlag(.disableObjcInterop)
    }

    // Handle the CPU and its preferences.
    try commandLine.appendLast(.targetCpu, from: &parsedOptions)

The switch is fine, because we don't have backend jobs for repl and immediate builds, but would have to move these out of order.

    if let variant = parsedOptions.getLastArgument(.targetVariant)?.asSingle {
      commandLine.appendFlag(.targetVariant)
      commandLine.appendFlag(Triple(variant, normalizing: true).triple)
    }
    // Enable or disable ObjC interop appropriately for the platform
    if targetTriple.isDarwin {
      commandLine.appendFlag(.enableObjcInterop)
    } else {
      commandLine.appendFlag(.disableObjcInterop)
    }

Looking at this further. Is there any reason that the embed-bitcode job doesn't use the targetVariant parameter. Seems like it should, but the c++ driver doesn't.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I see what you mean. We can keep it like this, and maybe there’s some future refactoring we can do. I worry about maintainability when we’re doing the same argument translation in several places.

Regarding -target-variant, yes, I think we should pass it through for bitcode compilation. It gets embedded in the binary. This is probably a bug in the C++ driver

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I add the -target-variant here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please add the -target-variant


// Enable optimizations, but disable all LLVM-IR-level transformations.
try commandLine.appendLast(in: .O, from: &parsedOptions)
commandLine.appendFlag(.disableLlvmOptzns)

try commandLine.appendLast(.parseStdlib, from: &parsedOptions)

commandLine.appendFlag(.moduleName)
commandLine.appendFlag(moduleName)

// Add the output file argument if necessary.
if let compilerOutputType = compilerOutputType {
let output = computePrimaryOutput(for: input,
outputType: compilerOutputType,
isTopLevel: isTopLevelOutput(type: compilerOutputType))
commandLine.appendFlag(.o)
commandLine.appendPath(output.file)
outputs.append(output)
}

allOutputs += outputs

return Job(
kind: .backend,
tool: .absolute(try toolchain.getToolPath(.swiftCompiler)),
commandLine: commandLine,
displayInputs: inputs,
inputs: inputs,
outputs: outputs,
supportsResponseFiles: true
)
}
}
46 changes: 26 additions & 20 deletions Sources/SwiftDriver/Jobs/CompileJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import SwiftOptions

extension Driver {
/// Add the appropriate compile mode option to the command line for a compile job.
private mutating func addCompileModeOption(outputType: FileType?, commandLine: inout [Job.ArgTemplate]) {
mutating func addCompileModeOption(outputType: FileType?, commandLine: inout [Job.ArgTemplate]) {
if let compileOption = outputType?.frontendCompileOption {
commandLine.appendFlag(compileOption)
} else {
Expand All @@ -26,7 +26,7 @@ extension Driver {
}
}

fileprivate mutating func computePrimaryOutput(for input: TypedVirtualPath, outputType: FileType,
mutating func computePrimaryOutput(for input: TypedVirtualPath, outputType: FileType,
isTopLevel: Bool) -> TypedVirtualPath {
if let path = outputFileMap?.existingOutput(inputFile: input.file, outputType: outputType) {
return TypedVirtualPath(file: path, type: outputType)
Expand Down Expand Up @@ -56,28 +56,31 @@ extension Driver {
return TypedVirtualPath(file: .relative(.init(baseName.appendingFileTypeExtension(outputType))), type: outputType)
}

/// Add the compiler inputs for a frontend compilation job, and return the
/// corresponding primary set of outputs.
mutating func addCompileInputs(primaryInputs: [TypedVirtualPath],
inputs: inout [TypedVirtualPath],
commandLine: inout [Job.ArgTemplate]) -> [TypedVirtualPath] {
// Is this compile job top-level
let isTopLevel: Bool

switch compilerOutputType {
/// Is this compile job top-level
func isTopLevelOutput(type: FileType?) -> Bool {
switch type {
case .assembly, .sil, .raw_sil, .llvmIR, .ast, .jsonDependencies:
isTopLevel = true
return true
case .object:
isTopLevel = (linkerOutputType == nil)
return (linkerOutputType == nil)
case .swiftModule:
isTopLevel = compilerMode.isSingleCompilation && moduleOutput?.isTopLevel ?? false
return compilerMode.isSingleCompilation && moduleOutput?.isTopLevel ?? false
case .swift, .sib, .image, .dSYM, .dependencies, .autolink,
.swiftDocumentation, .swiftInterface,
.swiftSourceInfoFile, .raw_sib, .llvmBitcode, .diagnostics,
.objcHeader, .swiftDeps, .remap, .importedModules, .tbd, .moduleTrace,
.indexData, .optimizationRecord, .pcm, .pch, nil:
isTopLevel = false
return false
}
}

/// Add the compiler inputs for a frontend compilation job, and return the
/// corresponding primary set of outputs.
mutating func addCompileInputs(primaryInputs: [TypedVirtualPath],
inputs: inout [TypedVirtualPath],
outputType: FileType?,
commandLine: inout [Job.ArgTemplate]) -> [TypedVirtualPath] {
let isTopLevel = isTopLevelOutput(type: outputType)

// Collect the set of input files that are part of the Swift compilation.
let swiftInputFiles: [TypedVirtualPath] = inputFiles.filter { $0.type.isPartOfSwiftCompilation }
Expand All @@ -104,16 +107,16 @@ extension Driver {

// If there is a primary output or we are doing multithreaded compiles,
// add an output for the input.
if let compilerOutputType = compilerOutputType,
isPrimary || (!usesPrimaryFileInputs && isMultithreaded && compilerOutputType.isAfterLLVM) {
if let outputType = outputType,
isPrimary || (!usesPrimaryFileInputs && isMultithreaded && outputType.isAfterLLVM) {
primaryOutputs.append(computePrimaryOutput(for: input,
outputType: compilerOutputType,
outputType: outputType,
isTopLevel: isTopLevel))
}
}

// When not using primary file inputs or multithreading, add a single output.
if let outputType = compilerOutputType,
if let outputType = outputType,
!usesPrimaryFileInputs && !(isMultithreaded && outputType.isAfterLLVM) {
primaryOutputs.append(computePrimaryOutput(
for: TypedVirtualPath(file: try! VirtualPath(path: ""),
Expand All @@ -133,7 +136,10 @@ extension Driver {

commandLine.appendFlag("-frontend")
addCompileModeOption(outputType: outputType, commandLine: &commandLine)
let primaryOutputs = addCompileInputs(primaryInputs: primaryInputs, inputs: &inputs, commandLine: &commandLine)
let primaryOutputs = addCompileInputs(primaryInputs: primaryInputs,
inputs: &inputs,
outputType: outputType,
commandLine: &commandLine)
outputs += primaryOutputs
outputs += try addFrontendSupplementaryOutputArguments(commandLine: &commandLine, primaryInputs: primaryInputs)

Expand Down
1 change: 1 addition & 0 deletions Sources/SwiftDriver/Jobs/Job.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Foundation
public struct Job: Codable, Equatable, Hashable {
public enum Kind: String, Codable {
case compile
case backend
case mergeModule = "merge-module"
case link
case generateDSYM = "generate-dsym"
Expand Down
11 changes: 5 additions & 6 deletions Sources/SwiftDriver/Jobs/LinkJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ import TSCBasic
extension Driver {

/// Compute the output file for an image output.
private func outputFileForImage(inputs: [TypedVirtualPath]) -> VirtualPath {
// FIXME: The check for __bad__ here, is
if inputs.count == 1 && moduleName == "__bad__" && inputs.first!.file != .standardInput {
// FIXME: llvm::sys::path::stem(BaseInput);
private var outputFileForImage: VirtualPath {
if inputFiles.count == 1 && moduleNameIsFallback && inputFiles[0].file != .standardInput {
return .relative(RelativePath(inputFiles[0].file.basenameWithoutExt))
}

let outputName =
Expand All @@ -35,7 +34,7 @@ extension Driver {
if let output = parsedOptions.getLastArgument(.o) {
outputFile = try VirtualPath(path: output.asSingle)
} else {
outputFile = outputFileForImage(inputs: inputs)
outputFile = outputFileForImage
}

// Defer to the toolchain for platform-specific linking
Expand All @@ -58,7 +57,7 @@ extension Driver {
tool: .absolute(toolPath),
commandLine: commandLine,
inputs: inputs,
outputs: [.init(file: outputFile, type: .object)]
outputs: [.init(file: outputFile, type: .image)]
)
}
}
Loading