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