From 56b1a2b575c9ddc8b9948d2c78e6a6cc9538d33f Mon Sep 17 00:00:00 2001 From: Trevor Radcliffe Date: Mon, 6 Feb 2023 21:58:30 +0000 Subject: [PATCH] LTO Bp2build Bug: 261733821 Test: Unit Tests Change-Id: I8c3721d35c464e296012145b2e95a7f0866aac37 --- bazel/properties.go | 32 ++++ bp2build/cc_binary_conversion_test.go | 128 +++++++++++++ bp2build/cc_library_conversion_test.go | 169 ++++++++++++++++++ bp2build/cc_library_shared_conversion_test.go | 130 ++++++++++++++ bp2build/cc_library_static_conversion_test.go | 130 ++++++++++++++ cc/bp2build.go | 47 +++++ 6 files changed, 636 insertions(+) diff --git a/bazel/properties.go b/bazel/properties.go index f4acd2601..40d0ba37a 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -17,6 +17,7 @@ package bazel import ( "fmt" "path/filepath" + "reflect" "regexp" "sort" "strings" @@ -533,6 +534,37 @@ func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelL return result, nil } +// ToStringListAttribute creates a StringListAttribute from this BoolAttribute, +// where each bool corresponds to a string list value generated by the provided +// function. +// TODO(b/271425661): Generalize this +func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) { + mainVal := valueFunc(ba.Value, NoConfigAxis, "") + if !ba.HasConfigurableValues() { + return MakeStringListAttribute(mainVal), nil + } + + result := StringListAttribute{} + if err := ba.Collapse(); err != nil { + return result, err + } + + for axis, configToBools := range ba.ConfigurableValues { + if len(configToBools) < 1 { + continue + } + for config, boolPtr := range configToBools { + val := valueFunc(&boolPtr, axis, config) + if !reflect.DeepEqual(val, mainVal) { + result.SetSelectValue(axis, config, val) + } + } + result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal) + } + + return result, nil +} + // Collapse reduces the configurable axes of the boolean attribute to a single axis. // This is necessary for final writing to bp2build, as a configurable boolean // attribute can only be comprised by a single select. diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go index a39ed7de6..031573274 100644 --- a/bp2build/cc_binary_conversion_test.go +++ b/bp2build/cc_binary_conversion_test.go @@ -868,3 +868,131 @@ func TestCcBinaryWithUBSanPropertiesArchSpecific(t *testing.T) { }, }) } + +func TestCcBinaryWithThinLto(t *testing.T) { + runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ + description: "cc_binary has correct features when thin LTO is enabled", + blueprint: ` +{rule_name} { + name: "foo", + lto: { + thin: true, + }, +}`, + targets: []testBazelTarget{ + {"cc_binary", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `["android_thin_lto"]`, + }}, + }, + }) +} + +func TestCcBinaryWithLtoNever(t *testing.T) { + runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ + description: "cc_binary has correct features when LTO is explicitly disabled", + blueprint: ` +{rule_name} { + name: "foo", + lto: { + never: true, + }, +}`, + targets: []testBazelTarget{ + {"cc_binary", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `["-android_thin_lto"]`, + }}, + }, + }) +} + +func TestCcBinaryWithThinLtoArchSpecific(t *testing.T) { + runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ + description: "cc_binary has correct features when LTO differs across arch and os variants", + blueprint: ` +{rule_name} { + name: "foo", + target: { + android: { + lto: { + thin: true, + }, + }, + }, + arch: { + riscv64: { + lto: { + thin: false, + }, + }, + }, +}`, + targets: []testBazelTarget{ + {"cc_binary", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], + "//conditions:default": [], + })`, + }}, + }, + }) +} + +func TestCcBinaryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { + runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ + description: "cc_binary has correct features when LTO disabled by default but enabled on a particular variant", + blueprint: ` +{rule_name} { + name: "foo", + lto: { + never: true, + }, + target: { + android: { + lto: { + thin: true, + never: false, + }, + }, + }, +}`, + targets: []testBazelTarget{ + {"cc_binary", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os:android": ["android_thin_lto"], + "//conditions:default": ["-android_thin_lto"], + })`, + }}, + }, + }) +} + +func TestCcBinaryWithThinLtoAndWholeProgramVtables(t *testing.T) { + runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ + description: "cc_binary has correct features when thin LTO is enabled with whole_program_vtables", + blueprint: ` +{rule_name} { + name: "foo", + lto: { + thin: true, + }, + whole_program_vtables: true, +}`, + targets: []testBazelTarget{ + {"cc_binary", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `[ + "android_thin_lto", + "android_thin_lto_whole_program_vtables", + ]`, + }}, + }, + }) +} diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index af14f6438..e20cffd4a 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -4137,3 +4137,172 @@ cc_library { }, }) } + +func TestCcLibraryWithThinLto(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + Description: "cc_library has correct features when thin LTO is enabled", + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Blueprint: ` +cc_library { + name: "foo", + lto: { + thin: true, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ + "features": `["android_thin_lto"]`, + "local_includes": `["."]`, + }), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "features": `["android_thin_lto"]`, + "local_includes": `["."]`, + }), + }, + }) +} + +func TestCcLibraryWithLtoNever(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + Description: "cc_library has correct features when LTO is explicitly disabled", + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Blueprint: ` +cc_library { + name: "foo", + lto: { + never: true, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ + "features": `["-android_thin_lto"]`, + "local_includes": `["."]`, + }), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "features": `["-android_thin_lto"]`, + "local_includes": `["."]`, + }), + }, + }) +} + +func TestCcLibraryWithThinLtoArchSpecific(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + Description: "cc_library has correct features when LTO differs across arch and os variants", + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Blueprint: ` +cc_library { + name: "foo", + target: { + android: { + lto: { + thin: true, + }, + }, + }, + arch: { + riscv64: { + lto: { + thin: false, + }, + }, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], + "//conditions:default": [], + })`}), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], + "//conditions:default": [], + })`}), + }, + }) +} + +func TestCcLibraryWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + Description: "cc_library has correct features when LTO disabled by default but enabled on a particular variant", + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Blueprint: ` +cc_library { + name: "foo", + lto: { + never: true, + }, + target: { + android: { + lto: { + thin: true, + never: false, + }, + }, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os:android": ["android_thin_lto"], + "//conditions:default": ["-android_thin_lto"], + })`, + }), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os:android": ["android_thin_lto"], + "//conditions:default": ["-android_thin_lto"], + })`, + }), + }, + }) +} + +func TestCcLibraryWithThinLtoWholeProgramVtables(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + Description: "cc_library has correct features when thin LTO is enabled with whole_program_vtables", + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Blueprint: ` +cc_library { + name: "foo", + lto: { + thin: true, + }, + whole_program_vtables: true, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ + "features": `[ + "android_thin_lto", + "android_thin_lto_whole_program_vtables", + ]`, + "local_includes": `["."]`, + }), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "features": `[ + "android_thin_lto", + "android_thin_lto_whole_program_vtables", + ]`, + "local_includes": `["."]`, + }), + }, + }) +} diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go index 620742115..838b29759 100644 --- a/bp2build/cc_library_shared_conversion_test.go +++ b/bp2build/cc_library_shared_conversion_test.go @@ -974,3 +974,133 @@ cc_library_shared { }, }) } + +func TestCcLibrarySharedWithThinLto(t *testing.T) { + runCcLibrarySharedTestCase(t, Bp2buildTestCase{ + Description: "cc_library_shared has correct features when thin lto is enabled", + Blueprint: ` +cc_library_shared { + name: "foo", + lto: { + thin: true, + }, +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "features": `["android_thin_lto"]`, + "local_includes": `["."]`, + }), + }, + }) +} + +func TestCcLibrarySharedWithLtoNever(t *testing.T) { + runCcLibrarySharedTestCase(t, Bp2buildTestCase{ + Description: "cc_library_shared has correct features when thin lto is enabled", + Blueprint: ` +cc_library_shared { + name: "foo", + lto: { + never: true, + }, +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "features": `["-android_thin_lto"]`, + "local_includes": `["."]`, + }), + }, + }) +} + +func TestCcLibrarySharedWithThinLtoArchSpecific(t *testing.T) { + runCcLibrarySharedTestCase(t, Bp2buildTestCase{ + Description: "cc_library_shared has correct features when LTO differs across arch and os variants", + Blueprint: ` +cc_library_shared { + name: "foo", + target: { + android: { + lto: { + thin: true, + }, + }, + }, + arch: { + riscv64: { + lto: { + thin: false, + }, + }, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], + "//conditions:default": [], + })`}), + }, + }) +} + +func TestCcLibrarySharedWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { + runCcLibrarySharedTestCase(t, Bp2buildTestCase{ + Description: "cc_library_shared with thin lto disabled by default but enabled on a particular variant", + Blueprint: ` +cc_library_shared { + name: "foo", + lto: { + never: true, + }, + target: { + android: { + lto: { + thin: true, + never: false, + }, + }, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os:android": ["android_thin_lto"], + "//conditions:default": ["-android_thin_lto"], + })`, + }), + }, + }) +} + +func TestCcLibrarySharedWithThinLtoAndWholeProgramVtables(t *testing.T) { + runCcLibrarySharedTestCase(t, Bp2buildTestCase{ + Description: "cc_library_shared has correct features when thin LTO is enabled with whole_program_vtables", + Blueprint: ` +cc_library_shared { + name: "foo", + lto: { + thin: true, + }, + whole_program_vtables: true, +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "features": `[ + "android_thin_lto", + "android_thin_lto_whole_program_vtables", + ]`, + "local_includes": `["."]`, + }), + }, + }) +} diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go index d5256f687..d16c5cc85 100644 --- a/bp2build/cc_library_static_conversion_test.go +++ b/bp2build/cc_library_static_conversion_test.go @@ -1889,3 +1889,133 @@ cc_library_static { }, }) } + +func TestCcLibraryStaticWithThinLto(t *testing.T) { + runCcLibraryStaticTestCase(t, Bp2buildTestCase{ + Description: "cc_library_static has correct features when thin lto is enabled", + Blueprint: ` +cc_library_static { + name: "foo", + lto: { + thin: true, + }, +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ + "features": `["android_thin_lto"]`, + "local_includes": `["."]`, + }), + }, + }) +} + +func TestCcLibraryStaticWithLtoNever(t *testing.T) { + runCcLibraryStaticTestCase(t, Bp2buildTestCase{ + Description: "cc_library_static has correct features when thin lto is enabled", + Blueprint: ` +cc_library_static { + name: "foo", + lto: { + never: true, + }, +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ + "features": `["-android_thin_lto"]`, + "local_includes": `["."]`, + }), + }, + }) +} + +func TestCcLibraryStaticWithThinLtoArchSpecific(t *testing.T) { + runCcLibraryStaticTestCase(t, Bp2buildTestCase{ + Description: "cc_library_static has correct features when LTO differs across arch and os variants", + Blueprint: ` +cc_library_static { + name: "foo", + target: { + android: { + lto: { + thin: true, + }, + }, + }, + arch: { + riscv64: { + lto: { + thin: false, + }, + }, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os_arch:android_arm": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_arm64": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_riscv64": ["-android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86": ["android_thin_lto"], + "//build/bazel/platforms/os_arch:android_x86_64": ["android_thin_lto"], + "//conditions:default": [], + })`}), + }, + }) +} + +func TestCcLibraryStaticWithThinLtoDisabledDefaultEnabledVariant(t *testing.T) { + runCcLibraryStaticTestCase(t, Bp2buildTestCase{ + Description: "cc_library_static has correct features when LTO disabled by default but enabled on a particular variant", + Blueprint: ` +cc_library_static { + name: "foo", + lto: { + never: true, + }, + target: { + android: { + lto: { + thin: true, + never: false, + }, + }, + }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ + "local_includes": `["."]`, + "features": `select({ + "//build/bazel/platforms/os:android": ["android_thin_lto"], + "//conditions:default": ["-android_thin_lto"], + })`, + }), + }, + }) +} + +func TestCcLibraryStaticWithThinLtoAndWholeProgramVtables(t *testing.T) { + runCcLibraryStaticTestCase(t, Bp2buildTestCase{ + Description: "cc_library_static has correct features when thin lto is enabled with whole_program_vtables", + Blueprint: ` +cc_library_static { + name: "foo", + lto: { + thin: true, + }, + whole_program_vtables: true, +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ + "features": `[ + "android_thin_lto", + "android_thin_lto_whole_program_vtables", + ]`, + "local_includes": `["."]`, + }), + }, + }) +} diff --git a/cc/bp2build.go b/cc/bp2build.go index 9751a2eee..1ea8bdad3 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -817,6 +817,7 @@ func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) compilerAttrs.hdrs.Prepend = true features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module)) + features = features.Append(bp2buildLtoFeatures(ctx, module)) features.DeduplicateAxesFromBase() addMuslSystemDynamicDeps(ctx, linkerAttrs) @@ -1459,3 +1460,49 @@ func bp2buildSanitizerFeatures(ctx android.BazelConversionPathContext, m *Module }) return sanitizerFeatures } + +func bp2buildLtoFeatures(ctx android.BazelConversionPathContext, m *Module) bazel.StringListAttribute { + lto_feature_name := "android_thin_lto" + ltoBoolFeatures := bazel.BoolAttribute{} + bp2BuildPropParseHelper(ctx, m, <OProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) { + if ltoProps, ok := props.(*LTOProperties); ok { + thinProp := ltoProps.Lto.Thin != nil && *ltoProps.Lto.Thin + thinPropSetToFalse := ltoProps.Lto.Thin != nil && !*ltoProps.Lto.Thin + neverProp := ltoProps.Lto.Never != nil && *ltoProps.Lto.Never + if thinProp { + ltoBoolFeatures.SetSelectValue(axis, config, BoolPtr(true)) + return + } + if neverProp || thinPropSetToFalse { + if thinProp { + ctx.ModuleErrorf("lto.thin and lto.never are mutually exclusive but were specified together") + } else { + ltoBoolFeatures.SetSelectValue(axis, config, BoolPtr(false)) + } + return + } + } + ltoBoolFeatures.SetSelectValue(axis, config, nil) + }) + + props := m.GetArchVariantProperties(ctx, <OProperties{}) + ltoStringFeatures, err := ltoBoolFeatures.ToStringListAttribute(func(boolPtr *bool, axis bazel.ConfigurationAxis, config string) []string { + if boolPtr == nil { + return []string{} + } + if !*boolPtr { + return []string{"-" + lto_feature_name} + } + features := []string{lto_feature_name} + if ltoProps, ok := props[axis][config].(*LTOProperties); ok { + if ltoProps.Whole_program_vtables != nil && *ltoProps.Whole_program_vtables { + features = append(features, "android_thin_lto_whole_program_vtables") + } + } + return features + }) + if err != nil { + ctx.ModuleErrorf("Error processing LTO attributes: %s", err) + } + return ltoStringFeatures +}