1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package gc 6 7import ( 8 "internal/race" 9 "math/rand" 10 "sort" 11 "sync" 12 13 "cmd/compile/internal/base" 14 "cmd/compile/internal/ir" 15 "cmd/compile/internal/liveness" 16 "cmd/compile/internal/objw" 17 "cmd/compile/internal/pgoir" 18 "cmd/compile/internal/ssagen" 19 "cmd/compile/internal/staticinit" 20 "cmd/compile/internal/types" 21 "cmd/compile/internal/walk" 22 "cmd/internal/obj" 23) 24 25// "Portable" code generation. 26 27var ( 28 compilequeue []*ir.Func // functions waiting to be compiled 29) 30 31func enqueueFunc(fn *ir.Func) { 32 if ir.CurFunc != nil { 33 base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc) 34 } 35 36 if ir.FuncName(fn) == "_" { 37 // Skip compiling blank functions. 38 // Frontend already reported any spec-mandated errors (#29870). 39 return 40 } 41 42 // Don't try compiling dead hidden closure. 43 if fn.IsDeadcodeClosure() { 44 return 45 } 46 47 if clo := fn.OClosure; clo != nil && !ir.IsTrivialClosure(clo) { 48 return // we'll get this as part of its enclosing function 49 } 50 51 if ssagen.CreateWasmImportWrapper(fn) { 52 return 53 } 54 55 if len(fn.Body) == 0 { 56 // Initialize ABI wrappers if necessary. 57 ir.InitLSym(fn, false) 58 types.CalcSize(fn.Type()) 59 a := ssagen.AbiForBodylessFuncStackMap(fn) 60 abiInfo := a.ABIAnalyzeFuncType(fn.Type()) // abiInfo has spill/home locations for wrapper 61 if fn.ABI == obj.ABI0 { 62 // The current args_stackmap generation assumes the function 63 // is ABI0, and only ABI0 assembly function can have a FUNCDATA 64 // reference to args_stackmap (see cmd/internal/obj/plist.go:Flushplist). 65 // So avoid introducing an args_stackmap if the func is not ABI0. 66 liveness.WriteFuncMap(fn, abiInfo) 67 68 x := ssagen.EmitArgInfo(fn, abiInfo) 69 objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL) 70 } 71 return 72 } 73 74 errorsBefore := base.Errors() 75 76 todo := []*ir.Func{fn} 77 for len(todo) > 0 { 78 next := todo[len(todo)-1] 79 todo = todo[:len(todo)-1] 80 81 prepareFunc(next) 82 todo = append(todo, next.Closures...) 83 } 84 85 if base.Errors() > errorsBefore { 86 return 87 } 88 89 // Enqueue just fn itself. compileFunctions will handle 90 // scheduling compilation of its closures after it's done. 91 compilequeue = append(compilequeue, fn) 92} 93 94// prepareFunc handles any remaining frontend compilation tasks that 95// aren't yet safe to perform concurrently. 96func prepareFunc(fn *ir.Func) { 97 // Set up the function's LSym early to avoid data races with the assemblers. 98 // Do this before walk, as walk needs the LSym to set attributes/relocations 99 // (e.g. in MarkTypeUsedInInterface). 100 ir.InitLSym(fn, true) 101 102 // If this function is a compiler-generated outlined global map 103 // initializer function, register its LSym for later processing. 104 if staticinit.MapInitToVar != nil { 105 if _, ok := staticinit.MapInitToVar[fn]; ok { 106 ssagen.RegisterMapInitLsym(fn.Linksym()) 107 } 108 } 109 110 // Calculate parameter offsets. 111 types.CalcSize(fn.Type()) 112 113 ir.CurFunc = fn 114 walk.Walk(fn) 115 ir.CurFunc = nil // enforce no further uses of CurFunc 116} 117 118// compileFunctions compiles all functions in compilequeue. 119// It fans out nBackendWorkers to do the work 120// and waits for them to complete. 121func compileFunctions(profile *pgoir.Profile) { 122 if race.Enabled { 123 // Randomize compilation order to try to shake out races. 124 tmp := make([]*ir.Func, len(compilequeue)) 125 perm := rand.Perm(len(compilequeue)) 126 for i, v := range perm { 127 tmp[v] = compilequeue[i] 128 } 129 copy(compilequeue, tmp) 130 } else { 131 // Compile the longest functions first, 132 // since they're most likely to be the slowest. 133 // This helps avoid stragglers. 134 sort.Slice(compilequeue, func(i, j int) bool { 135 return len(compilequeue[i].Body) > len(compilequeue[j].Body) 136 }) 137 } 138 139 // By default, we perform work right away on the current goroutine 140 // as the solo worker. 141 queue := func(work func(int)) { 142 work(0) 143 } 144 145 if nWorkers := base.Flag.LowerC; nWorkers > 1 { 146 // For concurrent builds, we allow the work queue 147 // to grow arbitrarily large, but only nWorkers work items 148 // can be running concurrently. 149 workq := make(chan func(int)) 150 done := make(chan int) 151 go func() { 152 ids := make([]int, nWorkers) 153 for i := range ids { 154 ids[i] = i 155 } 156 var pending []func(int) 157 for { 158 select { 159 case work := <-workq: 160 pending = append(pending, work) 161 case id := <-done: 162 ids = append(ids, id) 163 } 164 for len(pending) > 0 && len(ids) > 0 { 165 work := pending[len(pending)-1] 166 id := ids[len(ids)-1] 167 pending = pending[:len(pending)-1] 168 ids = ids[:len(ids)-1] 169 go func() { 170 work(id) 171 done <- id 172 }() 173 } 174 } 175 }() 176 queue = func(work func(int)) { 177 workq <- work 178 } 179 } 180 181 var wg sync.WaitGroup 182 var compile func([]*ir.Func) 183 compile = func(fns []*ir.Func) { 184 wg.Add(len(fns)) 185 for _, fn := range fns { 186 fn := fn 187 queue(func(worker int) { 188 ssagen.Compile(fn, worker, profile) 189 compile(fn.Closures) 190 wg.Done() 191 }) 192 } 193 } 194 195 types.CalcSizeDisabled = true // not safe to calculate sizes concurrently 196 base.Ctxt.InParallel = true 197 198 compile(compilequeue) 199 compilequeue = nil 200 wg.Wait() 201 202 base.Ctxt.InParallel = false 203 types.CalcSizeDisabled = false 204} 205