Optimize mutator contexts

Mutator contexts are created for every module, and in the case of
transition mutator contexts for every dependency of every module.
Unlike the Blueprint mutator contexts that they wrap, the Soong
mutator contexts can be relatively large.  Add global pools for all
of them that can avoid the repeated allocations.

Test: SOONG_PROFILE_MEM=/tmp/mem.pprof m nothing
Change-Id: I64a5f3c91292cff6352300f99c11ac50c713f96d
diff --git a/android/mutator.go b/android/mutator.go
index 22e9160..0ff4f48 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"sync"
+
 	"github.com/google/blueprint"
 )
 
@@ -328,29 +330,52 @@
 	SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{})
 }
 
+// An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
+// for each transition mutator.  bottomUpMutatorContext and topDownMutatorContext are created once for every module
+// for every BottomUp or TopDown mutator.  Use a global pool for each to avoid reallocating every time.
+var (
+	outgoingTransitionContextPool = sync.Pool{
+		New: func() any { return &outgoingTransitionContextImpl{} },
+	}
+	incomingTransitionContextPool = sync.Pool{
+		New: func() any { return &incomingTransitionContextImpl{} },
+	}
+	bottomUpMutatorContextPool = sync.Pool{
+		New: func() any { return &bottomUpMutatorContext{} },
+	}
+
+	topDownMutatorContextPool = sync.Pool{
+		New: func() any { return &topDownMutatorContext{} },
+	}
+)
+
 type bottomUpMutatorContext struct {
 	bp blueprint.BottomUpMutatorContext
 	baseModuleContext
 	finalPhase bool
 }
 
+// callers must immediately follow the call to this function with defer bottomUpMutatorContextPool.Put(mctx).
 func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module,
 	finalPhase bool) BottomUpMutatorContext {
 
 	moduleContext := a.base().baseModuleContextFactory(ctx)
-
-	return &bottomUpMutatorContext{
+	mctx := bottomUpMutatorContextPool.Get().(*bottomUpMutatorContext)
+	*mctx = bottomUpMutatorContext{
 		bp:                ctx,
 		baseModuleContext: moduleContext,
 		finalPhase:        finalPhase,
 	}
+	return mctx
 }
 
 func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
 	finalPhase := x.finalPhase
 	f := func(ctx blueprint.BottomUpMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
-			m(bottomUpMutatorContextFactory(ctx, a, finalPhase))
+			mctx := bottomUpMutatorContextFactory(ctx, a, finalPhase)
+			defer bottomUpMutatorContextPool.Put(mctx)
+			m(mctx)
 		}
 	}
 	mutator := &mutator{name: x.mutatorName(name), bottomUpMutator: f}
@@ -514,7 +539,9 @@
 
 func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
 	if m, ok := bpctx.Module().(Module); ok {
-		ctx := &outgoingTransitionContextImpl{
+		ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
+		defer outgoingTransitionContextPool.Put(ctx)
+		*ctx = outgoingTransitionContextImpl{
 			archModuleContext: m.base().archModuleContextFactory(bpctx),
 			bp:                bpctx,
 		}
@@ -543,7 +570,9 @@
 
 func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string {
 	if m, ok := bpctx.Module().(Module); ok {
-		ctx := &incomingTransitionContextImpl{
+		ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
+		defer incomingTransitionContextPool.Put(ctx)
+		*ctx = incomingTransitionContextImpl{
 			archModuleContext: m.base().archModuleContextFactory(bpctx),
 			bp:                bpctx,
 		}
@@ -555,7 +584,9 @@
 
 func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) {
 	if am, ok := ctx.Module().(Module); ok {
-		a.mutator.Mutate(bottomUpMutatorContextFactory(ctx, am, a.finalPhase), variation)
+		mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
+		defer bottomUpMutatorContextPool.Put(mctx)
+		a.mutator.Mutate(mctx, variation)
 	}
 }
 
@@ -578,7 +609,9 @@
 	f := func(ctx blueprint.TopDownMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			moduleContext := a.base().baseModuleContextFactory(ctx)
-			actx := &topDownMutatorContext{
+			actx := topDownMutatorContextPool.Get().(*topDownMutatorContext)
+			defer topDownMutatorContextPool.Put(actx)
+			*actx = topDownMutatorContext{
 				bp:                ctx,
 				baseModuleContext: moduleContext,
 			}