aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-06-30 21:28:48 +0000
committerGravatar Lukacs Berki <lberki@google.com>2016-07-01 07:12:00 +0000
commitdf726eae7b6ef11df3b962d720df36f63d5cd3a3 (patch)
tree05304235c401b2f4b8806d6b2e7aa541acfac6ab /src
parent7d9c6505789932e4501587241dc5e7cf0f47cb5b (diff)
ThinLTO changes for upstreamed compiler implementation and other fixes to blaze handling.
- Converted to correct upstreamed options - Handling of bitcode when creating dynamic libraries - New LTOBackendAction derived from SpawnAction that discovers and adds the imported bitcode files identified by the LTO indexing step. -- MOS_MIGRATED_REVID=126344132
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/actions/LTOBackendAction.java252
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java37
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java42
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java21
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/LTOBackendArtifacts.java104
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/actions/LTOBackendActionTest.java192
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java4
8 files changed, 576 insertions, 79 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/LTOBackendAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/LTOBackendAction.java
new file mode 100644
index 0000000000..52d9b3fe7e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/LTOBackendAction.java
@@ -0,0 +1,252 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.analysis.actions;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.actions.AbstractAction;
+import com.google.devtools.build.lib.actions.ActionExecutionContext;
+import com.google.devtools.build.lib.actions.ActionExecutionException;
+import com.google.devtools.build.lib.actions.ActionOwner;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ArtifactResolver;
+import com.google.devtools.build.lib.actions.PackageRootResolutionException;
+import com.google.devtools.build.lib.actions.PackageRootResolver;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Action used by LTOBackendArtifacts to create an LTOBackendAction. Similar to {@link SpawnAction},
+ * except that inputs are discovered from the imports file created by the ThinLTO indexing step for
+ * each backend artifact.
+ *
+ * <p>See {@link LTOBackendArtifacts} for a high level description of the ThinLTO build process. The
+ * LTO indexing step takes all bitcode .o files and decides which other .o file symbols can be
+ * imported/inlined. The additional input files for each backend action are then written to an
+ * imports file. Therefore these new inputs must be discovered here by subsetting the imports paths
+ * from the set of all bitcode artifacts, before executing the backend action.
+ */
+public final class LTOBackendAction extends SpawnAction {
+ // This can be read/written from multiple threads, and so accesses should be synchronized.
+ @GuardedBy("this")
+ private boolean inputsKnown;
+
+ private Collection<Artifact> mandatoryInputs;
+ private Map<PathFragment, Artifact> bitcodeFiles;
+ private Artifact imports;
+
+ private static final String GUID = "72ce1eca-4625-4e24-a0d8-bb91bb8b0e0e";
+
+ public LTOBackendAction(
+ Collection<Artifact> inputs,
+ Map<PathFragment, Artifact> allBitcodeFiles,
+ Artifact importsFile,
+ Collection<Artifact> outputs,
+ ActionOwner owner,
+ CommandLine argv,
+ Map<String, String> environment,
+ Map<String, String> executionInfo,
+ String progressMessage,
+ ImmutableMap<PathFragment, Artifact> inputManifests,
+ String mnemonic) {
+ super(
+ owner,
+ ImmutableList.<Artifact>of(),
+ inputs,
+ outputs,
+ AbstractAction.DEFAULT_RESOURCE_SET,
+ argv,
+ ImmutableMap.copyOf(environment),
+ ImmutableMap.copyOf(executionInfo),
+ progressMessage,
+ ImmutableMap.copyOf(inputManifests),
+ mnemonic,
+ false,
+ null);
+
+ inputsKnown = false;
+ mandatoryInputs = inputs;
+ bitcodeFiles = allBitcodeFiles;
+ imports = importsFile;
+ }
+
+ @Override
+ public boolean discoversInputs() {
+ return true;
+ }
+
+ private Set<Artifact> computeBitcodeInputs(Collection<PathFragment> inputPaths) {
+ HashSet<Artifact> bitcodeInputs = new HashSet<>();
+ for (PathFragment inputPath : inputPaths) {
+ Artifact inputArtifact = bitcodeFiles.get(inputPath);
+ if (inputArtifact != null) {
+ bitcodeInputs.add(inputArtifact);
+ }
+ }
+ return bitcodeInputs;
+ }
+
+ @Nullable
+ @Override
+ public Collection<Artifact> discoverInputs(ActionExecutionContext actionExecutionContext)
+ throws ActionExecutionException, InterruptedException {
+ // Build set of files this LTO backend artifact will import from.
+ HashSet<PathFragment> importSet = new HashSet<>();
+ try {
+ for (String line : FileSystemUtils.iterateLinesAsLatin1(imports.getPath())) {
+ if (!line.isEmpty()) {
+ PathFragment execPath = new PathFragment(line);
+ if (execPath.isAbsolute()) {
+ throw new ActionExecutionException(
+ "Absolute paths not allowed in imports file " + imports.getPath() + ": " + execPath,
+ this,
+ false);
+ }
+ importSet.add(new PathFragment(line));
+ }
+ }
+ } catch (IOException e) {
+ throw new ActionExecutionException(
+ "error iterating imports file " + imports.getPath(), e, this, false);
+ }
+
+ // Convert the import set of paths to the set of bitcode file artifacts.
+ Set<Artifact> bitcodeInputSet = computeBitcodeInputs(importSet);
+ if (bitcodeInputSet.size() != importSet.size()) {
+ throw new ActionExecutionException(
+ "error computing inputs from imports file " + imports.getPath(), this, false);
+ }
+ updateInputs(createInputs(bitcodeInputSet, getMandatoryInputs()));
+ return bitcodeInputSet;
+ }
+
+ @Override
+ public synchronized boolean inputsKnown() {
+ return inputsKnown;
+ }
+
+ @Override
+ public Collection<Artifact> getMandatoryInputs() {
+ return mandatoryInputs;
+ }
+
+ private static Iterable<Artifact> createInputs(
+ Set<Artifact> newInputs, Collection<Artifact> curInputs) {
+ Set<Artifact> result = new LinkedHashSet<>(newInputs);
+ result.addAll(curInputs);
+ return result;
+ }
+
+ @Override
+ public synchronized void updateInputs(Iterable<Artifact> discoveredInputs) {
+ setInputs(discoveredInputs);
+ inputsKnown = true;
+ }
+
+ @Nullable
+ @Override
+ public Iterable<Artifact> resolveInputsFromCache(
+ ArtifactResolver artifactResolver,
+ PackageRootResolver resolver,
+ Collection<PathFragment> inputPaths)
+ throws PackageRootResolutionException {
+ Set<Artifact> bitcodeInputSet = computeBitcodeInputs(inputPaths);
+ return createInputs(bitcodeInputSet, getMandatoryInputs());
+ }
+
+ @Override
+ public void execute(ActionExecutionContext actionExecutionContext)
+ throws ActionExecutionException, InterruptedException {
+ super.execute(actionExecutionContext);
+
+ synchronized (this) {
+ inputsKnown = true;
+ }
+ }
+
+ @Override
+ protected String computeKey() {
+ Fingerprint f = new Fingerprint();
+ f.addString(GUID);
+ f.addStrings(argv.arguments());
+ f.addString(getMnemonic());
+ f.addInt(inputManifests.size());
+ for (Map.Entry<PathFragment, Artifact> input : inputManifests.entrySet()) {
+ f.addString(input.getKey().getPathString() + "/");
+ f.addPath(input.getValue().getExecPath());
+ }
+ for (Artifact input : getMandatoryInputs()) {
+ f.addPath(input.getExecPath());
+ }
+ for (PathFragment bitcodePath : bitcodeFiles.keySet()) {
+ f.addPath(bitcodePath);
+ }
+ f.addPath(imports.getExecPath());
+ f.addStringMap(getEnvironment());
+ f.addStringMap(getExecutionInfo());
+ return f.hexDigestAndReset();
+ }
+
+ /** Builder class to construct {@link LTOBackendAction} instances. */
+ public static class Builder extends SpawnAction.Builder {
+ private Map<PathFragment, Artifact> bitcodeFiles;
+ private Artifact imports;
+
+ public Builder addImportsInfo(
+ Map<PathFragment, Artifact> allBitcodeFiles, Artifact importsFile) {
+ this.bitcodeFiles = allBitcodeFiles;
+ this.imports = importsFile;
+ return this;
+ }
+
+ @Override
+ SpawnAction createSpawnAction(
+ ActionOwner owner,
+ NestedSet<Artifact> tools,
+ NestedSet<Artifact> inputsAndTools,
+ ImmutableList<Artifact> outputs,
+ CommandLine actualCommandLine,
+ ImmutableMap<String, String> env,
+ ImmutableMap<String, String> executionInfo,
+ String progressMessage,
+ ImmutableMap<PathFragment, Artifact> inputAndToolManifests,
+ String mnemonic) {
+ return new LTOBackendAction(
+ inputsAndTools.toCollection(),
+ bitcodeFiles,
+ imports,
+ outputs,
+ owner,
+ actualCommandLine,
+ env,
+ executionInfo,
+ progressMessage,
+ inputAndToolManifests,
+ mnemonic);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
index 75624beca5..8060eb0290 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
@@ -84,13 +84,13 @@ public class SpawnAction extends AbstractAction implements ExecutionInfoSpecifie
private static final String GUID = "ebd6fce3-093e-45ee-adb6-bf513b602f0d";
- private final CommandLine argv;
+ protected final CommandLine argv;
private final boolean executeUnconditionally;
private final String progressMessage;
private final String mnemonic;
// entries are (directory for remote execution, Artifact)
- private final ImmutableMap<PathFragment, Artifact> inputManifests;
+ protected final ImmutableMap<PathFragment, Artifact> inputManifests;
private final ResourceSet resourceSet;
private final ImmutableMap<String, String> environment;
@@ -431,8 +431,7 @@ public class SpawnAction extends AbstractAction implements ExecutionInfoSpecifie
public static class Builder {
private final NestedSetBuilder<Artifact> toolsBuilder = NestedSetBuilder.stableOrder();
- private final NestedSetBuilder<Artifact> inputsBuilder =
- NestedSetBuilder.stableOrder();
+ private final NestedSetBuilder<Artifact> inputsBuilder = NestedSetBuilder.stableOrder();
private final List<Artifact> outputs = new ArrayList<>();
private final Map<PathFragment, Artifact> toolManifests = new LinkedHashMap<>();
private final Map<PathFragment, Artifact> inputManifests = new LinkedHashMap<>();
@@ -593,17 +592,41 @@ public class SpawnAction extends AbstractAction implements ExecutionInfoSpecifie
env = this.environment;
}
- return new SpawnAction(
+ return createSpawnAction(
owner,
tools,
inputsAndTools,
ImmutableList.copyOf(outputs),
- resourceSet,
actualCommandLine,
ImmutableMap.copyOf(env),
- executionInfo,
+ ImmutableMap.copyOf(executionInfo),
progressMessage,
ImmutableMap.copyOf(inputAndToolManifests),
+ mnemonic);
+ }
+
+ SpawnAction createSpawnAction(
+ ActionOwner owner,
+ NestedSet<Artifact> tools,
+ NestedSet<Artifact> inputsAndTools,
+ ImmutableList<Artifact> outputs,
+ CommandLine actualCommandLine,
+ ImmutableMap<String, String> env,
+ ImmutableMap<String, String> executionInfo,
+ String progressMessage,
+ ImmutableMap<PathFragment, Artifact> inputAndToolManifests,
+ String mnemonic) {
+ return new SpawnAction(
+ owner,
+ tools,
+ inputsAndTools,
+ outputs,
+ resourceSet,
+ actualCommandLine,
+ env,
+ executionInfo,
+ progressMessage,
+ inputAndToolManifests,
mnemonic,
executeUnconditionally,
extraActionInfoSupplier);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
index 061e6fea73..78bc18c9f5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcBinary.java
@@ -248,7 +248,8 @@ public abstract class CcBinary implements RuleConfiguredTargetFactory {
ruleContext.registerAction(indexAction);
for (LTOBackendArtifacts ltoArtifacts : indexAction.getAllLTOBackendArtifacts()) {
- ltoArtifacts.scheduleLTOBackendAction(ruleContext);
+ boolean usePic = CppHelper.usePic(ruleContext, !isLinkShared(ruleContext));
+ ltoArtifacts.scheduleLTOBackendAction(ruleContext, usePic);
}
linkActionBuilder.setLTOIndexing(false);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
index c774f1d187..3e17a83721 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
@@ -668,14 +668,14 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
// This flattens the set of object files, so for M binaries and N .o files,
// this is O(M*N). If we had a nested set of .o files, we could have O(M + N) instead.
- NestedSetBuilder<Artifact> bitcodeBuilder = NestedSetBuilder.stableOrder();
+ Map<PathFragment, Artifact> allBitcode = new HashMap<>();
for (LibraryToLink lib : uniqueLibraries) {
if (!lib.containsObjectFiles()) {
continue;
}
for (Artifact a : lib.getObjectFiles()) {
if (compiled.contains(a)) {
- bitcodeBuilder.add(a);
+ allBitcode.put(a.getExecPath(), a);
}
}
}
@@ -684,14 +684,14 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
// field for non-library .o files.
if (CppFileTypes.OBJECT_FILE.matches(input.getArtifact().getExecPath())
|| CppFileTypes.PIC_OBJECT_FILE.matches(input.getArtifact().getExecPath())) {
- bitcodeBuilder.add(input.getArtifact());
+ if (this.ltoBitcodeFiles.contains(input.getArtifact())) {
+ allBitcode.put(input.getArtifact().getExecPath(), input.getArtifact());
+ }
}
}
- NestedSet<Artifact> allBitcode = bitcodeBuilder.build();
-
ImmutableList.Builder<LTOBackendArtifacts> ltoOutputs = ImmutableList.builder();
- for (Artifact a : allBitcode) {
+ for (Artifact a : allBitcode.values()) {
LTOBackendArtifacts ltoArtifacts = new LTOBackendArtifacts(
ltoOutputRootPrefix, a, allBitcode, ruleContext, linkArtifactFactory);
ltoOutputs.add(ltoArtifacts);
@@ -830,11 +830,13 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
// TODO(bazel-team): once the LLVM compiler patches have been finalized, this should
// be converted to a crosstool feature configuration instead.
List<String> opts = new ArrayList<>(linkopts);
- opts.add("-flto");
+ opts.add("-flto=thin");
+ opts.add("-Wl,-plugin-opt,thinlto-index-only");
+ opts.add("-Wl,-plugin-opt,thinlto-emit-imports-files");
opts.add(
- "-Wl,-plugin-opt,thin-lto="
+ "-Wl,-plugin-opt,thinlto-prefix-replace="
+ configuration.getBinDirectory().getExecPathString()
- + ":"
+ + ";"
+ configuration
.getBinDirectory()
.getExecPath()
@@ -857,6 +859,13 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
dependencyInputsBuilder.addTransitive(compilationInputs.build());
}
+ // For backwards compatibility, and for tests, we permit the link action to be
+ // instantiated without a feature configuration. In this case, an empty feature
+ // configuration is used.
+ if (featureConfiguration == null) {
+ this.featureConfiguration = new FeatureConfiguration();
+ }
+
Iterable<Artifact> expandedInputs =
LinkerInputs.toLibraryArtifacts(
Link.mergeInputsDependencies(
@@ -885,6 +894,14 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
renamedNonLibraryInputs.add(renamed == null ? a : renamed);
}
expandedNonLibraryInputs = renamedNonLibraryInputs;
+ } else if (isLTOIndexing && allLTOArtifacts != null) {
+ for (LTOBackendArtifacts a : allLTOArtifacts) {
+ List<String> argv = new ArrayList<>();
+ argv.addAll(cppConfiguration.getLinkOptions());
+ argv.addAll(featureConfiguration.getCommandLine(getActionName(), buildVariables));
+ argv.addAll(cppConfiguration.getCompilerOptions(features));
+ a.setCommandLine(argv);
+ }
}
// getPrimaryInput returns the first element, and that is a public interface - therefore the
@@ -907,13 +924,6 @@ public final class CppLinkAction extends AbstractAction implements ExecutionInfo
analysisEnvironment.registerAction(parameterFileWriteAction);
}
-
- // For backwards compatibility, and for tests,
- // we permit the link action to be instantiated without a feature configuration.
- // In this case, an empty feature configuration is used.
- if (featureConfiguration == null) {
- this.featureConfiguration = new FeatureConfiguration();
- }
Map<String, String> toolchainEnv =
featureConfiguration.getEnvironmentVariables(getActionName(), buildVariables);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
index 480a1ee560..b1305aebaf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModel.java
@@ -842,11 +842,12 @@ public final class CppModel {
// Should we also link in any libraries that this library depends on?
// That is required on some systems...
- CppLinkAction action =
+ CppLinkAction.Builder linkActionBuilder =
newLinkActionBuilder(soImpl)
.setInterfaceOutput(soInterface)
.addNonLibraryInputs(ccOutputs.getObjectFiles(usePicForSharedLibs))
.addNonLibraryInputs(ccOutputs.getHeaderTokenFiles())
+ .addLTOBitcodeFiles(ccOutputs.getLtoBitcodeFiles())
.setLinkType(LinkTargetType.DYNAMIC_LIBRARY)
.setLinkStaticness(LinkStaticness.DYNAMIC)
.addLinkopts(linkopts)
@@ -855,8 +856,22 @@ public final class CppModel {
CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkMiddleman(),
CppHelper.getToolchain(ruleContext).getDynamicRuntimeLinkInputs())
.setFeatureConfiguration(featureConfiguration)
- .setBuildVariables(linkBuildVariables())
- .build();
+ .setBuildVariables(linkBuildVariables());
+
+ if (!ccOutputs.getLtoBitcodeFiles().isEmpty()
+ && featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO)) {
+ linkActionBuilder.setLTOIndexing(true);
+ CppLinkAction indexAction = linkActionBuilder.build();
+ env.registerAction(indexAction);
+
+ for (LTOBackendArtifacts ltoArtifacts : indexAction.getAllLTOBackendArtifacts()) {
+ ltoArtifacts.scheduleLTOBackendAction(ruleContext, usePicForSharedLibs);
+ }
+
+ linkActionBuilder.setLTOIndexing(false);
+ }
+
+ CppLinkAction action = linkActionBuilder.build();
env.registerAction(action);
LibraryToLink dynamicLibrary = action.getOutputLibrary();
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LTOBackendArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LTOBackendArtifacts.java
index 03365e09b3..f5765f0ccf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LTOBackendArtifacts.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LTOBackendArtifacts.java
@@ -15,39 +15,34 @@
package com.google.devtools.build.lib.rules.cpp;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleContext;
-import com.google.devtools.build.lib.analysis.actions.SpawnAction;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.analysis.actions.LTOBackendAction;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
/**
- * LTOBackendArtifacts represents a set of artifacts for a single LTO backend compile.
+ * LTOBackendArtifacts represents a set of artifacts for a single ThinLTO backend compile.
+ *
+ * <p>ThinLTO expands the traditional 2 step compile (N x compile .cc, 1x link (N .o files) into a 4
+ * step process:
*
- * <p>LTO expands the traditional 2 step compile (N x compile .cc, 1x link (N .o files) into a
- * 4 step process:
* <ul>
- * <li>1. Bitcode generation (N times). This is produces intermediate LLVM bitcode from a source
- * file. For this product, it reuses the .o extension.
- * </li>
- * <li>2. Indexing (once on N files). This takes all bitcode .o files, and for each .o file, it
- * decides from which other .o files symbols can be inlined. In addition, it generates an
- * index for looking up these symbols.
- * </li>
- * <li>3. Backend compile (N times). This is the traditional compilation, and uses the same
- * command line
- * as the Bitcode generation in 1). Since the compiler has many bit code files available, it
- * can inline functions and propagate constants across .o files. This step is costly, as it
- * will do traditional optimization. The result is a .lto.o file, a traditional ELF object file.
- * <p>
- * For simplicity, our current prototype step 2. also generates a command line which we execute
- * in step 3.
- * </p>
- * </li>
- * <li>4. Backend link (once). This is the traditional link, and produces the final executable.
- * </li>
+ * <li>1. Bitcode generation (N times). This is produces intermediate LLVM bitcode from a source
+ * file. For this product, it reuses the .o extension.
+ * <li>2. Indexing (once on N files). This takes all bitcode .o files, and for each .o file, it
+ * decides from which other .o files symbols can be inlined. In addition, it generates an index
+ * for looking up these symbols, and an imports file for identifying new input files for each
+ * step 3 {@link LTOBackendAction}.
+ * <li>3. Backend compile (N times). This is the traditional compilation, and uses the same command
+ * line as the Bitcode generation in 1). Since the compiler has many bit code files available,
+ * it can inline functions and propagate constants across .o files. This step is costly, as it
+ * will do traditional optimization. The result is a .lto.o file, a traditional ELF object file.
+ * <li>4. Backend link (once). This is the traditional link, and produces the final executable.
* </ul>
*/
public final class LTOBackendArtifacts {
@@ -61,33 +56,33 @@ public final class LTOBackendArtifacts {
// unused.
private final Artifact imports;
- // A file containing a command-line to run for the backend compile.
- private final Artifact beCommandline;
-
// The result of executing the above command line, an ELF object file.
private final Artifact objectFile;
- // A collection of all of the bitcode files. This is the universe from which the .imports file
- // distills its lists. The nested set is the same across all LTOBackendArtifacts of a given
+ // A map of all of the bitcode files. This is the universe from which the .imports file
+ // distills its lists. The map is the same across all LTOBackendArtifacts of a given
// binary.
- private final NestedSet<Artifact> bitcodeFiles;
+ private final Map<PathFragment, Artifact> bitcodeFiles;
+
+ // Command line arguments to apply to back-end compile action, typically from
+ // the feature configuration and user-provided linkopts.
+ private List<String> commandLine;
LTOBackendArtifacts(
PathFragment ltoOutputRootPrefix,
Artifact bitcodeFile,
- NestedSet<Artifact> allBitCodeFiles,
+ Map<PathFragment, Artifact> allBitCodeFiles,
RuleContext ruleContext,
CppLinkAction.LinkArtifactFactory linkArtifactFactory) {
this.bitcodeFile = bitcodeFile;
PathFragment obj = ltoOutputRootPrefix.getRelative(bitcodeFile.getRootRelativePath());
objectFile = linkArtifactFactory.create(ruleContext, obj);
- imports = linkArtifactFactory.create(
- ruleContext, FileSystemUtils.replaceExtension(obj, ".imports"));
- index = linkArtifactFactory.create(
- ruleContext, FileSystemUtils.replaceExtension(obj, ".thinlto.index"));
- beCommandline = linkArtifactFactory.create(
- ruleContext, FileSystemUtils.replaceExtension(obj, ".thinlto_commandline.txt"));
+ imports =
+ linkArtifactFactory.create(ruleContext, FileSystemUtils.appendExtension(obj, ".imports"));
+ index =
+ linkArtifactFactory.create(
+ ruleContext, FileSystemUtils.appendExtension(obj, ".thinlto.bc"));
bitcodeFiles = allBitCodeFiles;
}
@@ -103,21 +98,20 @@ public final class LTOBackendArtifacts {
public void addIndexingOutputs(ImmutableList.Builder<Artifact> builder) {
builder.add(imports);
builder.add(index);
- builder.add(beCommandline);
}
- public void scheduleLTOBackendAction(RuleContext ruleContext) {
- SpawnAction.Builder builder = new SpawnAction.Builder();
+ public void setCommandLine(List<String> cmdLine) {
+ commandLine = cmdLine;
+ }
+
+ public void scheduleLTOBackendAction(RuleContext ruleContext, boolean usePic) {
+ LTOBackendAction.Builder builder = new LTOBackendAction.Builder();
+ builder.addImportsInfo(bitcodeFiles, imports);
- // TODO(bazel-team): should prune to the files mentioned in .imports.
- builder.addTransitiveInputs(bitcodeFiles);
- builder.addInput(imports);
+ builder.addInput(bitcodeFile);
builder.addInput(index);
- builder.addInput(beCommandline);
builder.addTransitiveInputs(CppHelper.getToolchain(ruleContext).getCompile());
- // The backend compile invokes ld too.
- builder.addTransitiveInputs(CppHelper.getToolchain(ruleContext).getLink());
builder.addOutput(objectFile);
builder.setProgressMessage("LTO Backend Compile " + objectFile.getFilename());
@@ -129,8 +123,20 @@ public final class LTOBackendArtifacts {
PathFragment compiler = cppConfiguration.getCppExecutable();
- builder.setShellCommand(beCommandline.getExecPathString());
- builder.setEnvironment(ImmutableMap.of("CLANG", compiler.replaceName("clang").getPathString()));
+ builder.setExecutable(compiler);
+ List<String> execArgs = new ArrayList<>();
+ execArgs.add("-c");
+ execArgs.add("-fthinlto-index=" + index.getExecPath());
+ execArgs.add("-o");
+ execArgs.add(objectFile.getExecPath().getPathString());
+ execArgs.add("-x");
+ execArgs.add("ir");
+ execArgs.add(bitcodeFile.getExecPath().getPathString());
+ if (usePic) {
+ execArgs.add("-fPIC");
+ }
+ execArgs.addAll(commandLine);
+ builder.addExecutableArguments(execArgs);
ruleContext.registerAction(builder.build(ruleContext));
}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/LTOBackendActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/LTOBackendActionTest.java
new file mode 100644
index 0000000000..f2349e5511
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/LTOBackendActionTest.java
@@ -0,0 +1,192 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.actions;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.google.devtools.build.lib.actions.AbstractAction;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.ActionExecutionContext;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Executor;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.util.ActionTester;
+import com.google.devtools.build.lib.analysis.util.ActionTester.ActionCombinationFactory;
+import com.google.devtools.build.lib.analysis.util.AnalysisTestUtil;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.exec.util.TestExecutorBuilder;
+import com.google.devtools.build.lib.util.io.FileOutErr;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Tests {@link LTOBackendAction}. */
+@RunWith(JUnit4.class)
+public class LTOBackendActionTest extends BuildViewTestCase {
+ private Artifact bitcode1Artifact;
+ private Artifact bitcode2Artifact;
+ private Artifact index1Artifact;
+ private Artifact index2Artifact;
+ private Artifact imports1Artifact;
+ private Artifact imports2Artifact;
+ private Artifact destinationArtifact;
+ private Map<PathFragment, Artifact> allBitcodeFiles;
+ private AnalysisTestUtil.CollectingAnalysisEnvironment collectingAnalysisEnvironment;
+ private Executor executor;
+ private ActionExecutionContext context;
+
+ private LTOBackendAction.Builder builder() {
+ return new LTOBackendAction.Builder();
+ }
+
+ @Before
+ public final void createArtifacts() throws Exception {
+ collectingAnalysisEnvironment =
+ new AnalysisTestUtil.CollectingAnalysisEnvironment(getTestAnalysisEnvironment());
+ bitcode1Artifact = getSourceArtifact("bitcode1.o");
+ bitcode2Artifact = getSourceArtifact("bitcode2.o");
+ index1Artifact = getSourceArtifact("bitcode1.thinlto.bc");
+ index2Artifact = getSourceArtifact("bitcode2.thinlto.bc");
+ scratch.file("bitcode1.imports");
+ scratch.file("bitcode2.imports", "bitcode1.o");
+ imports1Artifact = getSourceArtifact("bitcode1.imports");
+ imports2Artifact = getSourceArtifact("bitcode2.imports");
+ destinationArtifact = getBinArtifactWithNoOwner("output");
+ allBitcodeFiles = new HashMap<>();
+ allBitcodeFiles.put(bitcode1Artifact.getExecPath(), bitcode1Artifact);
+ allBitcodeFiles.put(bitcode2Artifact.getExecPath(), bitcode2Artifact);
+ }
+
+ @Before
+ public final void createExecutorAndContext() throws Exception {
+ executor = new TestExecutorBuilder(directories, binTools).build();
+ context = new ActionExecutionContext(executor, null, null, new FileOutErr(), null);
+ }
+
+ @Test
+ public void testEmptyImports() throws Exception {
+ Action[] actions =
+ builder()
+ .addImportsInfo(allBitcodeFiles, imports1Artifact)
+ .addInput(bitcode1Artifact)
+ .addInput(index1Artifact)
+ .addOutput(destinationArtifact)
+ .setExecutable(scratch.file("/bin/clang").asFragment())
+ .setProgressMessage("Test")
+ .build(ActionsTestUtil.NULL_ACTION_OWNER, collectingAnalysisEnvironment, targetConfig);
+ collectingAnalysisEnvironment.registerAction(actions);
+ LTOBackendAction action = (LTOBackendAction) actions[0];
+ assertEquals(ActionsTestUtil.NULL_ACTION_OWNER.getLabel(), action.getOwner().getLabel());
+ assertThat(action.getInputs()).containsExactly(bitcode1Artifact, index1Artifact);
+ assertThat(action.getOutputs()).containsExactly(destinationArtifact);
+ assertEquals(AbstractAction.DEFAULT_RESOURCE_SET, action.getSpawn().getLocalResources());
+ assertThat(action.getArguments()).containsExactly("/bin/clang");
+ assertEquals("Test", action.getProgressMessage());
+ assertThat(action.inputsKnown()).isFalse();
+
+ // Discover inputs, which should not add any inputs since bitcode1.imports is empty.
+ action.discoverInputs(context);
+ assertThat(action.inputsKnown()).isTrue();
+ assertThat(action.getInputs()).containsExactly(bitcode1Artifact, index1Artifact);
+ }
+
+ @Test
+ public void testNonEmptyImports() throws Exception {
+ Action[] actions =
+ builder()
+ .addImportsInfo(allBitcodeFiles, imports2Artifact)
+ .addInput(bitcode2Artifact)
+ .addInput(index2Artifact)
+ .addOutput(destinationArtifact)
+ .setExecutable(scratch.file("/bin/clang").asFragment())
+ .setProgressMessage("Test")
+ .build(ActionsTestUtil.NULL_ACTION_OWNER, collectingAnalysisEnvironment, targetConfig);
+ collectingAnalysisEnvironment.registerAction(actions);
+ LTOBackendAction action = (LTOBackendAction) actions[0];
+ assertEquals(ActionsTestUtil.NULL_ACTION_OWNER.getLabel(), action.getOwner().getLabel());
+ assertThat(action.getInputs()).containsExactly(bitcode2Artifact, index2Artifact);
+ assertThat(action.getOutputs()).containsExactly(destinationArtifact);
+ assertEquals(AbstractAction.DEFAULT_RESOURCE_SET, action.getSpawn().getLocalResources());
+ assertThat(action.getArguments()).containsExactly("/bin/clang");
+ assertEquals("Test", action.getProgressMessage());
+ assertThat(action.inputsKnown()).isFalse();
+
+ // Discover inputs, which should add bitcode1.o which is listed in bitcode2.imports.
+ action.discoverInputs(context);
+ assertThat(action.inputsKnown()).isTrue();
+ assertThat(action.getInputs())
+ .containsExactly(bitcode1Artifact, bitcode2Artifact, index2Artifact);
+ }
+
+ @Test
+ public void testComputeKey() throws Exception {
+ final Artifact artifactA = getSourceArtifact("a");
+ final Artifact artifactB = getSourceArtifact("b");
+ final Artifact artifactAimports = getSourceArtifact("a.imports");
+ final Artifact artifactBimports = getSourceArtifact("b.imports");
+
+ ActionTester.runTest(
+ 64,
+ new ActionCombinationFactory() {
+ @Override
+ public Action generate(int i) {
+ LTOBackendAction.Builder builder = builder();
+ builder.addOutput(destinationArtifact);
+
+ PathFragment executable =
+ (i & 1) == 0 ? artifactA.getExecPath() : artifactB.getExecPath();
+ builder.setExecutable(executable);
+
+ if ((i & 2) == 0) {
+ builder.addImportsInfo(new HashMap<PathFragment, Artifact>(), artifactAimports);
+ } else {
+ builder.addImportsInfo(new HashMap<PathFragment, Artifact>(), artifactBimports);
+ }
+
+ builder.setMnemonic((i & 4) == 0 ? "a" : "b");
+
+ if ((i & 8) == 0) {
+ builder.addInputManifest(artifactA, new PathFragment("a"));
+ } else {
+ builder.addInputManifest(artifactB, new PathFragment("a"));
+ }
+
+ if ((i & 16) == 0) {
+ builder.addInput(artifactA);
+ } else {
+ builder.addInput(artifactB);
+ }
+
+ Map<String, String> env = new HashMap<>();
+ if ((i & 32) == 0) {
+ env.put("foo", "bar");
+ }
+ builder.setEnvironment(env);
+
+ Action[] actions =
+ builder.build(
+ ActionsTestUtil.NULL_ACTION_OWNER, collectingAnalysisEnvironment, targetConfig);
+ collectingAnalysisEnvironment.registerAction(actions);
+ return actions[0];
+ }
+ });
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java
index 66306be004..79c86260d6 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java
@@ -180,9 +180,7 @@ public abstract class MockCcSupport {
+ " action: 'c-compile'"
+ " action: 'c++-compile'"
+ " flag_group {"
- + " flag: '-Xclang-only=-Wno-inconsistent-missing-override'"
- + " flag: '-flto'"
- + " flag: '-O2'"
+ + " flag: '-flto=thin'"
+ " }"
+ " }"
+ "}";