diff options
author | 2016-06-30 21:28:48 +0000 | |
---|---|---|
committer | 2016-07-01 07:12:00 +0000 | |
commit | df726eae7b6ef11df3b962d720df36f63d5cd3a3 (patch) | |
tree | 05304235c401b2f4b8806d6b2e7aa541acfac6ab /src | |
parent | 7d9c6505789932e4501587241dc5e7cf0f47cb5b (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')
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'" + " }" + " }" + "}"; |