diff --git a/Android.bp b/Android.bp index 0c79bf539..d1b8f0022 100644 --- a/Android.bp +++ b/Android.bp @@ -115,6 +115,7 @@ bootstrap_go_package { "cc/check.go", "cc/coverage.go", "cc/gen.go", + "cc/lto.go", "cc/makevars.go", "cc/prebuilt.go", "cc/proto.go", diff --git a/cc/cc.go b/cc/cc.go index 0f754a661..bda9c9d14 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -50,6 +50,9 @@ func init() { ctx.BottomUp("coverage", coverageLinkingMutator).Parallel() ctx.TopDown("vndk_deps", sabiDepsMutator) + + ctx.TopDown("lto_deps", ltoDepsMutator) + ctx.BottomUp("lto", ltoMutator).Parallel() }) pctx.Import("android/soong/cc/config") @@ -294,6 +297,7 @@ type Module struct { coverage *coverage sabi *sabi vndkdep *vndkdep + lto *lto androidMkSharedLibDeps []string @@ -333,6 +337,9 @@ func (c *Module) Init() android.Module { if c.vndkdep != nil { c.AddProperties(c.vndkdep.props()...) } + if c.lto != nil { + c.AddProperties(c.lto.props()...) + } for _, feature := range c.features { c.AddProperties(feature.props()...) } @@ -488,6 +495,7 @@ func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Mo module.coverage = &coverage{} module.sabi = &sabi{} module.vndkdep = &vndkdep{} + module.lto = <o{} return module } @@ -536,6 +544,9 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if c.coverage != nil { flags = c.coverage.flags(ctx, flags) } + if c.lto != nil { + flags = c.lto.flags(ctx, flags) + } for _, feature := range c.features { flags = feature.flags(ctx, flags) } @@ -619,6 +630,9 @@ func (c *Module) begin(ctx BaseModuleContext) { if c.vndkdep != nil { c.vndkdep.begin(ctx) } + if c.lto != nil { + c.lto.begin(ctx) + } for _, feature := range c.features { feature.begin(ctx) } @@ -655,6 +669,9 @@ func (c *Module) deps(ctx DepsContext) Deps { if c.vndkdep != nil { deps = c.vndkdep.deps(ctx, deps) } + if c.lto != nil { + deps = c.lto.deps(ctx, deps) + } for _, feature := range c.features { deps = feature.deps(ctx, deps) } @@ -1190,6 +1207,7 @@ func DefaultsFactory(props ...interface{}) android.Module { &CoverageProperties{}, &SAbiProperties{}, &VndkProperties{}, + <OProperties{}, ) android.InitDefaultsModule(module) diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index 025d3a5f6..0d22ed498 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -60,7 +60,7 @@ var ( "-Wl,--build-id=md5", "-Wl,--warn-shared-textrel", "-Wl,--fatal-warnings", - "-Wl,-maarch64linux", + "-Wl,-m,aarch64_elf64_le_vec", "-Wl,--hash-style=gnu", "-Wl,--fix-cortex-a53-843419", "-fuse-ld=gold", diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go index 38816aabe..d50de2b4f 100644 --- a/cc/config/arm_device.go +++ b/cc/config/arm_device.go @@ -67,6 +67,7 @@ var ( "-Wl,--icf=safe", "-Wl,--hash-style=gnu", "-Wl,--no-undefined-version", + "-Wl,-m,armelf", } armArmCflags = []string{ diff --git a/cc/config/global.go b/cc/config/global.go index 65a211c6c..d25f6be5d 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -16,6 +16,7 @@ package config import ( "fmt" + "runtime" "strings" "android/soong/android" @@ -149,6 +150,11 @@ func init() { return ClangDefaultShortVersion, nil }) pctx.StaticVariable("ClangAsanLibDir", "${ClangPath}/lib64/clang/${ClangShortVersion}/lib/linux") + if runtime.GOOS == "darwin" { + pctx.StaticVariable("LLVMGoldPlugin", "${ClangPath}/lib64/LLVMgold.dylib") + } else { + pctx.StaticVariable("LLVMGoldPlugin", "${ClangPath}/lib64/LLVMgold.so") + } // These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts // being used for the rest of the build process. diff --git a/cc/lto.go b/cc/lto.go new file mode 100644 index 000000000..f49677244 --- /dev/null +++ b/cc/lto.go @@ -0,0 +1,117 @@ +// Copyright 2017 Google Inc. 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 cc + +import ( + "github.com/google/blueprint" + + "android/soong/android" +) + +// LTO (link-time optimization) allows the compiler to optimize and generate +// code for the entire module at link time, rather than per-compilation +// unit. LTO is required for Clang CFI and other whole-program optimization +// techniques. LTO also allows cross-compilation unit optimizations that should +// result in faster and smaller code, at the expense of additional compilation +// time. +// +// To properly build a module with LTO, the module and all recursive static +// dependencies should be compiled with -flto which directs the compiler to emit +// bitcode rather than native object files. These bitcode files are then passed +// by the linker to the LLVM plugin for compilation at link time. Static +// dependencies not built as bitcode will still function correctly but cannot be +// optimized at link time and may not be compatible with features that require +// LTO, such as CFI. +// +// This file adds support to soong to automatically propogate LTO options to a +// new variant of all static dependencies for each module with LTO enabled. + +type LTOProperties struct { + // Lto must violate capitialization style for acronyms so that it can be + // referred to in blueprint files as "lto" + Lto *bool `android:"arch_variant"` + LTODep bool `blueprint:"mutated"` +} + +type lto struct { + Properties LTOProperties +} + +func (lto *lto) props() []interface{} { + return []interface{}{<o.Properties} +} + +func (lto *lto) begin(ctx BaseModuleContext) { +} + +func (lto *lto) deps(ctx BaseModuleContext, deps Deps) Deps { + return deps +} + +func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags { + if Bool(lto.Properties.Lto) { + flags.CFlags = append(flags.CFlags, "-flto") + flags.LdFlags = append(flags.LdFlags, "-flto") + if ctx.Device() { + // Work around bug in Clang that doesn't pass correct emulated + // TLS option to target + flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-emulated-tls") + } + flags.ArFlags = append(flags.ArFlags, " --plugin ${config.LLVMGoldPlugin}") + } + return flags +} + +// Can be called with a null receiver +func (lto *lto) LTO() bool { + if lto == nil { + return false + } + + return Bool(lto.Properties.Lto) +} + +// Propagate lto requirements down from binaries +func ltoDepsMutator(mctx android.TopDownMutatorContext) { + if c, ok := mctx.Module().(*Module); ok && c.lto.LTO() { + mctx.VisitDepsDepthFirst(func(m blueprint.Module) { + tag := mctx.OtherModuleDependencyTag(m) + switch tag { + case staticDepTag, staticExportDepTag, lateStaticDepTag, wholeStaticDepTag, objDepTag, reuseObjTag: + if cc, ok := m.(*Module); ok && cc.lto != nil { + cc.lto.Properties.LTODep = true + } + } + }) + } +} + +// Create lto variants for modules that need them +func ltoMutator(mctx android.BottomUpMutatorContext) { + if c, ok := mctx.Module().(*Module); ok && c.lto != nil { + if c.lto.LTO() { + mctx.SetDependencyVariation("lto") + } else if c.lto.Properties.LTODep { + modules := mctx.CreateVariations("", "lto") + modules[0].(*Module).lto.Properties.Lto = boolPtr(false) + modules[1].(*Module).lto.Properties.Lto = boolPtr(true) + modules[0].(*Module).lto.Properties.LTODep = false + modules[1].(*Module).lto.Properties.LTODep = false + modules[1].(*Module).Properties.PreventInstall = true + modules[1].(*Module).Properties.HideFromMake = true + } + c.lto.Properties.LTODep = false + } +}