// Copyright 2015 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.rules.cpp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration; import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Variables; import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool; import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; import com.google.devtools.build.lib.vfs.FileSystemProvider; 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 ThinLTO backend compile. * *

ThinLTO expands the traditional 2 step compile (N x compile .cc, 1x link (N .o files) into a 4 * step process: * *

*/ @AutoCodec(dependency = FileSystemProvider.class) public final class LtoBackendArtifacts { public static final InjectingObjectCodec CODEC = new LtoBackendArtifacts_AutoCodec(); // A file containing mapping of symbol => bitcode file containing the symbol. private final Artifact index; // The bitcode file which is the input of the compile. private final Artifact bitcodeFile; // A file containing a list of bitcode files necessary to run the backend step. private final Artifact imports; // The result of executing the above command line, an ELF object file. private final Artifact objectFile; // The corresponding dwoFile if fission is used. private Artifact dwoFile; @AutoCodec.Instantiator @VisibleForSerialization LtoBackendArtifacts( Artifact index, Artifact bitcodeFile, Artifact imports, Artifact objectFile, Artifact dwoFile) { this.index = index; this.bitcodeFile = bitcodeFile; this.imports = imports; this.objectFile = objectFile; this.dwoFile = dwoFile; } LtoBackendArtifacts( PathFragment ltoOutputRootPrefix, Artifact bitcodeFile, Map allBitCodeFiles, RuleContext ruleContext, BuildConfiguration configuration, CppLinkAction.LinkArtifactFactory linkArtifactFactory, FeatureConfiguration featureConfiguration, CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport, boolean usePic, boolean generateDwo, List commandLine) { this.bitcodeFile = bitcodeFile; PathFragment obj = ltoOutputRootPrefix.getRelative(bitcodeFile.getRootRelativePath()); objectFile = linkArtifactFactory.create(ruleContext, configuration, obj); imports = linkArtifactFactory.create( ruleContext, configuration, FileSystemUtils.appendExtension(obj, ".imports")); index = linkArtifactFactory.create( ruleContext, configuration, FileSystemUtils.appendExtension(obj, ".thinlto.bc")); scheduleLtoBackendAction( ruleContext, featureConfiguration, ccToolchain, fdoSupport, usePic, generateDwo, configuration, linkArtifactFactory, commandLine, allBitCodeFiles); } // Interface to create an LTO backend that does not perform any cross-module optimization. public LtoBackendArtifacts( PathFragment ltoOutputRootPrefix, Artifact bitcodeFile, RuleContext ruleContext, BuildConfiguration configuration, CppLinkAction.LinkArtifactFactory linkArtifactFactory, FeatureConfiguration featureConfiguration, CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport, boolean usePic, boolean generateDwo, List commandLine) { this.bitcodeFile = bitcodeFile; PathFragment obj = ltoOutputRootPrefix.getRelative(bitcodeFile.getRootRelativePath()); objectFile = linkArtifactFactory.create(ruleContext, configuration, obj); imports = null; index = null; scheduleLtoBackendAction( ruleContext, featureConfiguration, ccToolchain, fdoSupport, usePic, generateDwo, configuration, linkArtifactFactory, commandLine, null); } public Artifact getObjectFile() { return objectFile; } public Artifact getBitcodeFile() { return bitcodeFile; } public Artifact getDwoFile() { return dwoFile; } public void addIndexingOutputs(ImmutableList.Builder builder) { builder.add(imports); builder.add(index); } private void scheduleLtoBackendAction( RuleContext ruleContext, FeatureConfiguration featureConfiguration, CcToolchainProvider ccToolchain, FdoSupportProvider fdoSupport, boolean usePic, boolean generateDwo, BuildConfiguration configuration, CppLinkAction.LinkArtifactFactory linkArtifactFactory, List commandLine, Map bitcodeFiles) { LtoBackendAction.Builder builder = new LtoBackendAction.Builder(); builder.addInput(bitcodeFile); Preconditions.checkState( (index == null) == (imports == null), "Either both or neither index and imports files should be null"); if (imports != null) { builder.addImportsInfo(bitcodeFiles, imports); // Although the imports file is not used by the LTOBackendAction while the action is // executing, it is needed during the input discovery phase, and we must list it as an input // to the action // in order for it to be preserved under // --experimental_discard_orphaned_artifacts. builder.addInput(imports); } if (index != null) { builder.addInput(index); } builder.addTransitiveInputs(ccToolchain.getCompile()); builder.addOutput(objectFile); builder.setProgressMessage("LTO Backend Compile %s", objectFile.getFilename()); builder.setMnemonic("CcLtoBackendCompile"); // The command-line doesn't specify the full path to clang++, so we set it in the // environment. PathFragment compiler = ccToolchain.getToolPathFragment(Tool.GCC); builder.setExecutable(compiler); Variables.Builder buildVariablesBuilder = new Variables.Builder(ccToolchain.getBuildVariables()); if (index != null) { buildVariablesBuilder.addStringVariable("thinlto_index", index.getExecPath().toString()); } else { // An empty input indicates not to perform cross-module optimization. buildVariablesBuilder.addStringVariable("thinlto_index", "/dev/null"); } // The output from the LTO backend step is a native object file. buildVariablesBuilder.addStringVariable( "thinlto_output_object_file", objectFile.getExecPath().toString()); // The input to the LTO backend step is the bitcode file. buildVariablesBuilder.addStringVariable( "thinlto_input_bitcode_file", bitcodeFile.getExecPath().toString()); Artifact autoFdoProfile = fdoSupport.getFdoSupport().buildProfileForLtoBackend( fdoSupport, featureConfiguration, buildVariablesBuilder, ruleContext); if (autoFdoProfile != null) { builder.addInput(autoFdoProfile); } if (generateDwo) { dwoFile = linkArtifactFactory.create( ruleContext, configuration, FileSystemUtils.replaceExtension(objectFile.getRootRelativePath(), ".dwo")); builder.addOutput(dwoFile); buildVariablesBuilder.addStringVariable( "per_object_debug_info_file", dwoFile.getExecPathString()); } List execArgs = new ArrayList<>(); execArgs.addAll(commandLine); Variables buildVariables = buildVariablesBuilder.build(); // Feature options should go after --copt for consistency with compile actions. execArgs.addAll(featureConfiguration.getCommandLine("lto-backend", buildVariables)); // If this is a PIC compile (set based on the CppConfiguration), the PIC // option should be added after the rest of the command line so that it // cannot be overridden. This is consistent with the ordering in the // CppCompileAction's compiler options. if (usePic) { execArgs.add("-fPIC"); } builder.addExecutableArguments(execArgs); ruleContext.registerAction(builder.build(ruleContext)); } }