1
2
3
4
5 package ssagen
6
7 import (
8 "fmt"
9 "internal/buildcfg"
10 "log"
11 "os"
12 "strings"
13
14 "cmd/compile/internal/abi"
15 "cmd/compile/internal/base"
16 "cmd/compile/internal/ir"
17 "cmd/compile/internal/objw"
18 "cmd/compile/internal/typecheck"
19 "cmd/compile/internal/types"
20 "cmd/internal/obj"
21 "cmd/internal/obj/wasm"
22 )
23
24
25
26 type SymABIs struct {
27 defs map[string]obj.ABI
28 refs map[string]obj.ABISet
29 }
30
31 func NewSymABIs() *SymABIs {
32 return &SymABIs{
33 defs: make(map[string]obj.ABI),
34 refs: make(map[string]obj.ABISet),
35 }
36 }
37
38
39
40
41
42
43 func (s *SymABIs) canonicalize(linksym string) string {
44 if strings.HasPrefix(linksym, `"".`) {
45 panic("non-canonical symbol name: " + linksym)
46 }
47 return linksym
48 }
49
50
51
52
53
54
55
56
57
58
59 func (s *SymABIs) ReadSymABIs(file string) {
60 data, err := os.ReadFile(file)
61 if err != nil {
62 log.Fatalf("-symabis: %v", err)
63 }
64
65 for lineNum, line := range strings.Split(string(data), "\n") {
66 lineNum++
67 line = strings.TrimSpace(line)
68 if line == "" || strings.HasPrefix(line, "#") {
69 continue
70 }
71
72 parts := strings.Fields(line)
73 switch parts[0] {
74 case "def", "ref":
75
76 if len(parts) != 3 {
77 log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
78 }
79 sym, abistr := parts[1], parts[2]
80 abi, valid := obj.ParseABI(abistr)
81 if !valid {
82 log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
83 }
84
85 sym = s.canonicalize(sym)
86
87
88 if parts[0] == "def" {
89 s.defs[sym] = abi
90 } else {
91 s.refs[sym] |= obj.ABISetOf(abi)
92 }
93 default:
94 log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
95 }
96 }
97 }
98
99
100
101 func (s *SymABIs) GenABIWrappers() {
102
103
104
105
106
107
108
109 cgoExports := make(map[string][]*[]string)
110 for i, prag := range typecheck.Target.CgoPragmas {
111 switch prag[0] {
112 case "cgo_export_static", "cgo_export_dynamic":
113 symName := s.canonicalize(prag[1])
114 pprag := &typecheck.Target.CgoPragmas[i]
115 cgoExports[symName] = append(cgoExports[symName], pprag)
116 }
117 }
118
119
120
121
122
123
124 for _, fn := range typecheck.Target.Funcs {
125 nam := fn.Nname
126 if ir.IsBlank(nam) {
127 continue
128 }
129 sym := nam.Sym()
130
131 symName := sym.Linkname
132 if symName == "" {
133 symName = sym.Pkg.Prefix + "." + sym.Name
134 }
135 symName = s.canonicalize(symName)
136
137
138 defABI, hasDefABI := s.defs[symName]
139 if hasDefABI {
140 if len(fn.Body) != 0 {
141 base.ErrorfAt(fn.Pos(), 0, "%v defined in both Go and assembly", fn)
142 }
143 fn.ABI = defABI
144 }
145
146 if fn.Pragma&ir.CgoUnsafeArgs != 0 {
147
148
149
150 fn.ABI = obj.ABI0
151
152
153 if sym.Linksym().IsLinkname() {
154 sym.LinksymABI(fn.ABI).Set(obj.AttrLinkname, true)
155 }
156 }
157
158
159
160 cgoExport := cgoExports[symName]
161 for _, pprag := range cgoExport {
162
163
164
165
166
167
168
169
170
171
172 if len(*pprag) == 2 {
173 *pprag = append(*pprag, (*pprag)[1])
174 }
175
176 *pprag = append(*pprag, fn.ABI.String())
177 }
178
179
180 if abis, ok := s.refs[symName]; ok {
181 fn.ABIRefs |= abis
182 }
183
184
185
186 fn.ABIRefs.Set(obj.ABIInternal, true)
187
188
189
190
191
192
193
194
195
196
197
198
199 hasBody := len(fn.Body) != 0
200 if sym.Linkname != "" && (hasBody || hasDefABI) && len(cgoExport) == 0 {
201 fn.ABIRefs |= obj.ABISetCallable
202 }
203
204
205
206 if len(cgoExport) > 0 && fn.ABIRefs&^obj.ABISetOf(fn.ABI) != 0 {
207 base.Fatalf("cgo exported function %v cannot have ABI wrappers", fn)
208 }
209
210 if !buildcfg.Experiment.RegabiWrappers {
211 continue
212 }
213
214 forEachWrapperABI(fn, makeABIWrapper)
215 }
216 }
217
218 func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) {
219 need := fn.ABIRefs &^ obj.ABISetOf(fn.ABI)
220 if need == 0 {
221 return
222 }
223
224 for wrapperABI := obj.ABI(0); wrapperABI < obj.ABICount; wrapperABI++ {
225 if !need.Get(wrapperABI) {
226 continue
227 }
228 cb(fn, wrapperABI)
229 }
230 }
231
232
233
234 func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
235 if base.Debug.ABIWrap != 0 {
236 fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %v\n", wrapperABI, f.ABI, f)
237 }
238
239
240 savepos := base.Pos
241 savedcurfn := ir.CurFunc
242
243 pos := base.AutogeneratedPos
244 base.Pos = pos
245
246
247
248 ft := f.Nname.Type()
249 if ft.NumRecvs() != 0 {
250 base.ErrorfAt(f.Pos(), 0, "makeABIWrapper support for wrapping methods not implemented")
251 return
252 }
253
254
255
256 fn := ir.NewFunc(pos, pos, f.Sym(), types.NewSignature(nil,
257 typecheck.NewFuncParams(ft.Params()),
258 typecheck.NewFuncParams(ft.Results())))
259 fn.ABI = wrapperABI
260 typecheck.DeclFunc(fn)
261
262 fn.SetABIWrapper(true)
263 fn.SetDupok(true)
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 fn.Pragma |= ir.Nosplit
291
292
293
294
295
296
297
298
299
300
301 tailcall := fn.Type().NumResults() == 0 && fn.Type().NumParams() == 0 && fn.Type().NumRecvs() == 0
302 if base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink {
303
304
305 tailcall = false
306 }
307 if base.Ctxt.Arch.Name == "amd64" && wrapperABI == obj.ABIInternal {
308
309
310 tailcall = false
311 }
312
313 var tail ir.Node
314 call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
315 call.Args = ir.ParamNames(fn.Type())
316 call.IsDDD = fn.Type().IsVariadic()
317 tail = call
318 if tailcall {
319 tail = ir.NewTailCallStmt(base.Pos, call)
320 } else if fn.Type().NumResults() > 0 {
321 n := ir.NewReturnStmt(base.Pos, nil)
322 n.Results = []ir.Node{call}
323 tail = n
324 }
325 fn.Body.Append(tail)
326
327 typecheck.FinishFuncBody()
328
329 ir.CurFunc = fn
330 typecheck.Stmts(fn.Body)
331
332
333 base.Pos = savepos
334 ir.CurFunc = savedcurfn
335 }
336
337
338
339
340 func CreateWasmImportWrapper(fn *ir.Func) bool {
341 if fn.WasmImport == nil {
342 return false
343 }
344 if buildcfg.GOARCH != "wasm" {
345 base.FatalfAt(fn.Pos(), "CreateWasmImportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, fn)
346 }
347
348 ir.InitLSym(fn, true)
349
350 setupWasmABI(fn)
351
352 pp := objw.NewProgs(fn, 0)
353 defer pp.Free()
354 pp.Text.To.Type = obj.TYPE_TEXTSIZE
355 pp.Text.To.Val = int32(types.RoundUp(fn.Type().ArgWidth(), int64(types.RegSize)))
356
357 pp.Text.To.Offset = 0
358 pp.Flush()
359
360 return true
361 }
362
363 func paramsToWasmFields(f *ir.Func, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
364 wfs := make([]obj.WasmField, len(abiParams))
365 for i, p := range abiParams {
366 t := p.Type
367 switch t.Kind() {
368 case types.TINT32, types.TUINT32:
369 wfs[i].Type = obj.WasmI32
370 case types.TINT64, types.TUINT64:
371 wfs[i].Type = obj.WasmI64
372 case types.TFLOAT32:
373 wfs[i].Type = obj.WasmF32
374 case types.TFLOAT64:
375 wfs[i].Type = obj.WasmF64
376 case types.TUNSAFEPTR:
377 wfs[i].Type = obj.WasmPtr
378 default:
379 base.ErrorfAt(f.Pos(), 0, "go:wasmimport %s %s: unsupported parameter type %s", f.WasmImport.Module, f.WasmImport.Name, t.String())
380 }
381 wfs[i].Offset = p.FrameOffset(result)
382 }
383 return wfs
384 }
385
386 func resultsToWasmFields(f *ir.Func, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
387 if len(abiParams) > 1 {
388 base.ErrorfAt(f.Pos(), 0, "go:wasmimport %s %s: too many return values", f.WasmImport.Module, f.WasmImport.Name)
389 return nil
390 }
391 wfs := make([]obj.WasmField, len(abiParams))
392 for i, p := range abiParams {
393 t := p.Type
394 switch t.Kind() {
395 case types.TINT32, types.TUINT32:
396 wfs[i].Type = obj.WasmI32
397 case types.TINT64, types.TUINT64:
398 wfs[i].Type = obj.WasmI64
399 case types.TFLOAT32:
400 wfs[i].Type = obj.WasmF32
401 case types.TFLOAT64:
402 wfs[i].Type = obj.WasmF64
403 default:
404 base.ErrorfAt(f.Pos(), 0, "go:wasmimport %s %s: unsupported result type %s", f.WasmImport.Module, f.WasmImport.Name, t.String())
405 }
406 wfs[i].Offset = p.FrameOffset(result)
407 }
408 return wfs
409 }
410
411
412 func setupWasmABI(f *ir.Func) {
413 wi := obj.WasmImport{
414 Module: f.WasmImport.Module,
415 Name: f.WasmImport.Name,
416 }
417 if wi.Module == wasm.GojsModule {
418
419
420
421
422
423
424
425
426
427
428 wi.Params = []obj.WasmField{{Type: obj.WasmI32}}
429 } else {
430
431
432
433
434
435
436
437
438
439 abiConfig := AbiForBodylessFuncStackMap(f)
440 abiInfo := abiConfig.ABIAnalyzeFuncType(f.Type())
441 wi.Params = paramsToWasmFields(f, abiInfo, abiInfo.InParams())
442 wi.Results = resultsToWasmFields(f, abiInfo, abiInfo.OutParams())
443 }
444 f.LSym.Func().WasmImport = &wi
445 }
446
View as plain text