Add arch features

Allow architecture toolchains to register "features" supported by the
current variant, and then apply properties from the selected features.
Equivalent to the ARCH_*_HAS_* variables in the combo makefiles.

Change-Id: Ib6823be1c1a52da677d081db9f24336a072eaf39
diff --git a/cc/arm64_device.go b/cc/arm64_device.go
index b818ce4..754b276 100644
--- a/cc/arm64_device.go
+++ b/cc/arm64_device.go
@@ -136,7 +136,7 @@
 	return "${arm64Ldflags}"
 }
 
-func arm64ToolchainFactory(archVariant string, cpuVariant string) Toolchain {
+func arm64ToolchainFactory(arch common.Arch) Toolchain {
 	return toolchainArm64Singleton
 }
 
diff --git a/cc/arm_device.go b/cc/arm_device.go
index 8f3485e..f168811 100644
--- a/cc/arm_device.go
+++ b/cc/arm_device.go
@@ -306,9 +306,9 @@
 	}
 }
 
-func armToolchainFactory(archVariant string, cpuVariant string) Toolchain {
+func armToolchainFactory(arch common.Arch) Toolchain {
 	var fixCortexA8 string
-	switch cpuVariant {
+	switch arch.CpuVariant {
 	case "cortex-a8", "":
 		// Generic ARM might be a Cortex A8 -- better safe than sorry
 		fixCortexA8 = "-Wl,--fix-cortex-a8"
@@ -319,8 +319,8 @@
 	return &toolchainArm{
 		cflags: strings.Join([]string{
 			"${armCflags}",
-			armArchVariantCflagsVar[archVariant],
-			armCpuVariantCflagsVar[cpuVariant],
+			armArchVariantCflagsVar[arch.ArchVariant],
+			armCpuVariantCflagsVar[arch.CpuVariant],
 		}, " "),
 		ldflags: strings.Join([]string{
 			"${armLdflags}",
@@ -328,8 +328,8 @@
 		}, " "),
 		clangCflags: strings.Join([]string{
 			"${armClangCflags}",
-			armClangArchVariantCflagsVar[archVariant],
-			armClangCpuVariantCflagsVar[cpuVariant],
+			armClangArchVariantCflagsVar[arch.ArchVariant],
+			armClangCpuVariantCflagsVar[arch.CpuVariant],
 		}, " "),
 	}
 }
diff --git a/cc/cc.go b/cc/cc.go
index aa10d72..d65b8db 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -412,7 +412,7 @@
 		ctx.ModuleErrorf("Toolchain not found for %s arch %q", hod.String(), arch.String())
 		return nil
 	}
-	return factory(arch.ArchVariant, arch.CpuVariant)
+	return factory(arch)
 }
 
 func (c *CCBase) ModifyProperties(ctx CCModuleContext) {
diff --git a/cc/toolchain.go b/cc/toolchain.go
index e17e345..8a8fc2d 100644
--- a/cc/toolchain.go
+++ b/cc/toolchain.go
@@ -20,7 +20,7 @@
 	"android/soong/common"
 )
 
-type toolchainFactory func(archVariant string, cpuVariant string) Toolchain
+type toolchainFactory func(arch common.Arch) Toolchain
 
 var toolchainFactories = map[common.HostOrDevice]map[common.ArchType]toolchainFactory{
 	common.Host:   make(map[common.ArchType]toolchainFactory),
diff --git a/cc/x86_darwin_host.go b/cc/x86_darwin_host.go
index be40933..4195dae 100644
--- a/cc/x86_darwin_host.go
+++ b/cc/x86_darwin_host.go
@@ -198,11 +198,11 @@
 var toolchainDarwinX86Singleton Toolchain = &toolchainDarwinX86{}
 var toolchainDarwinX8664Singleton Toolchain = &toolchainDarwinX8664{}
 
-func darwinX86ToolchainFactory(archVariant string, cpuVariant string) Toolchain {
+func darwinX86ToolchainFactory(arch common.Arch) Toolchain {
 	return toolchainDarwinX86Singleton
 }
 
-func darwinX8664ToolchainFactory(archVariant string, cpuVariant string) Toolchain {
+func darwinX8664ToolchainFactory(arch common.Arch) Toolchain {
 	return toolchainDarwinX8664Singleton
 }
 
diff --git a/cc/x86_linux_host.go b/cc/x86_linux_host.go
index 8f24b05..f544476 100644
--- a/cc/x86_linux_host.go
+++ b/cc/x86_linux_host.go
@@ -224,11 +224,11 @@
 var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{}
 var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{}
 
-func linuxX86ToolchainFactory(archVariant string, cpuVariant string) Toolchain {
+func linuxX86ToolchainFactory(arch common.Arch) Toolchain {
 	return toolchainLinuxX86Singleton
 }
 
-func linuxX8664ToolchainFactory(archVariant string, cpuVariant string) Toolchain {
+func linuxX8664ToolchainFactory(arch common.Arch) Toolchain {
 	return toolchainLinuxX8664Singleton
 }
 
diff --git a/common/arch.go b/common/arch.go
index 21f638a..3669e80 100644
--- a/common/arch.go
+++ b/common/arch.go
@@ -208,12 +208,34 @@
 	}
 }
 
+var archFeatureMap = map[ArchType]map[string][]string{}
+
+func RegisterArchFeatures(arch ArchType, variant string, features ...string) {
+	field := proptools.FieldNameForProperty(variant)
+	if variant != "" {
+		if !reflect.ValueOf(archProperties{}.Arch).FieldByName(field).IsValid() {
+			panic(fmt.Errorf("Invalid variant %q for arch %q", variant, arch))
+		}
+	}
+	for _, feature := range features {
+		field := proptools.FieldNameForProperty(feature)
+		if !reflect.ValueOf(archProperties{}.Arch).FieldByName(field).IsValid() {
+			panic(fmt.Errorf("Invalid feature %q for arch %q variant %q", feature, arch, variant))
+		}
+	}
+	if archFeatureMap[arch] == nil {
+		archFeatureMap[arch] = make(map[string][]string)
+	}
+	archFeatureMap[arch][variant] = features
+}
+
 // An Arch indicates a single CPU architecture.
 type Arch struct {
-	ArchType    ArchType
-	ArchVariant string
-	CpuVariant  string
-	Abi         []string
+	ArchType     ArchType
+	ArchVariant  string
+	CpuVariant   string
+	Abi          []string
+	ArchFeatures []string
 }
 
 func (a Arch) String() string {
@@ -516,6 +538,18 @@
 			a.appendProperties(ctx, genProps, archProps.Arch, field, prefix)
 		}
 
+		// Handle arch-feature-specific properties in the form:
+		// arch: {
+		//     feature: {
+		//         key: value,
+		//     },
+		// },
+		for _, feature := range arch.ArchFeatures {
+			field := proptools.FieldNameForProperty(feature)
+			prefix := "arch." + feature
+			a.appendProperties(ctx, genProps, archProps.Arch, field, prefix)
+		}
+
 		// Handle multilib-specific properties in the form:
 		// multilib: {
 		//     lib32: {
@@ -729,6 +763,10 @@
 		}
 	}
 
+	if featureMap, ok := archFeatureMap[archType]; ok {
+		a.ArchFeatures = featureMap[stringPtr(archVariant)]
+	}
+
 	return a, nil
 }