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 rtabi "internal/abi"
24 )
25
26
27
28 type SymABIs struct {
29 defs map[string]obj.ABI
30 refs map[string]obj.ABISet
31 }
32
33 func NewSymABIs() *SymABIs {
34 return &SymABIs{
35 defs: make(map[string]obj.ABI),
36 refs: make(map[string]obj.ABISet),
37 }
38 }
39
40
41
42
43
44
45 func (s *SymABIs) canonicalize(linksym string) string {
46 if strings.HasPrefix(linksym, `"".`) {
47 panic("non-canonical symbol name: " + linksym)
48 }
49 return linksym
50 }
51
52
53
54
55
56
57
58
59
60
61 func (s *SymABIs) ReadSymABIs(file string) {
62 data, err := os.ReadFile(file)
63 if err != nil {
64 log.Fatalf("-symabis: %v", err)
65 }
66
67 for lineNum, line := range strings.Split(string(data), "\n") {
68 lineNum++
69 line = strings.TrimSpace(line)
70 if line == "" || strings.HasPrefix(line, "#") {
71 continue
72 }
73
74 parts := strings.Fields(line)
75 switch parts[0] {
76 case "def", "ref":
77
78 if len(parts) != 3 {
79 log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
80 }
81 sym, abistr := parts[1], parts[2]
82 abi, valid := obj.ParseABI(abistr)
83 if !valid {
84 log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
85 }
86
87 sym = s.canonicalize(sym)
88
89
90 if parts[0] == "def" {
91 s.defs[sym] = abi
92 base.Ctxt.DwTextCount++
93 } else {
94 s.refs[sym] |= obj.ABISetOf(abi)
95 }
96 default:
97 log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
98 }
99 }
100 }
101
102
103 func (s *SymABIs) HasDef(sym *types.Sym) bool {
104 symName := sym.Linkname
105 if symName == "" {
106 symName = sym.Pkg.Prefix + "." + sym.Name
107 }
108 symName = s.canonicalize(symName)
109
110 _, hasDefABI := s.defs[symName]
111 return hasDefABI
112 }
113
114
115
116 func (s *SymABIs) GenABIWrappers() {
117
118
119
120
121
122
123
124 cgoExports := make(map[string][]*[]string)
125 for i, prag := range typecheck.Target.CgoPragmas {
126 switch prag[0] {
127 case "cgo_export_static", "cgo_export_dynamic":
128 symName := s.canonicalize(prag[1])
129 pprag := &typecheck.Target.CgoPragmas[i]
130 cgoExports[symName] = append(cgoExports[symName], pprag)
131 }
132 }
133
134
135
136
137
138
139 for _, fn := range typecheck.Target.Funcs {
140 nam := fn.Nname
141 if ir.IsBlank(nam) {
142 continue
143 }
144 sym := nam.Sym()
145
146 symName := sym.Linkname
147 if symName == "" {
148 symName = sym.Pkg.Prefix + "." + sym.Name
149 }
150 symName = s.canonicalize(symName)
151
152
153 defABI, hasDefABI := s.defs[symName]
154 if hasDefABI {
155 if len(fn.Body) != 0 {
156 base.ErrorfAt(fn.Pos(), 0, "%v defined in both Go and assembly", fn)
157 }
158 fn.ABI = defABI
159 }
160
161 if fn.Pragma&ir.CgoUnsafeArgs != 0 {
162
163
164
165 fn.ABI = obj.ABI0
166
167
168 if sym.Linksym().IsLinkname() {
169 sym.LinksymABI(fn.ABI).Set(obj.AttrLinkname, true)
170 }
171 if sym.Linksym().IsLinknameStd() {
172 sym.LinksymABI(fn.ABI).Set(obj.AttrLinknameStd, true)
173 }
174 }
175
176
177
178 cgoExport := cgoExports[symName]
179 for _, pprag := range cgoExport {
180
181
182
183
184
185
186
187
188
189
190 if len(*pprag) == 2 {
191 *pprag = append(*pprag, (*pprag)[1])
192 }
193
194 *pprag = append(*pprag, fn.ABI.String())
195 }
196
197
198 if abis, ok := s.refs[symName]; ok {
199 fn.ABIRefs |= abis
200 }
201
202
203
204 fn.ABIRefs.Set(obj.ABIInternal, true)
205
206
207
208
209
210
211
212
213
214
215
216
217 hasBody := len(fn.Body) != 0
218 if sym.Linkname != "" && (hasBody || hasDefABI) && len(cgoExport) == 0 {
219 fn.ABIRefs |= obj.ABISetCallable
220 }
221
222
223
224 if len(cgoExport) > 0 && fn.ABIRefs&^obj.ABISetOf(fn.ABI) != 0 {
225 base.Fatalf("cgo exported function %v cannot have ABI wrappers", fn)
226 }
227
228 if !buildcfg.Experiment.RegabiWrappers {
229 continue
230 }
231
232 forEachWrapperABI(fn, makeABIWrapper)
233 }
234 }
235
236 func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) {
237 need := fn.ABIRefs &^ obj.ABISetOf(fn.ABI)
238 if need == 0 {
239 return
240 }
241
242 for wrapperABI := obj.ABI(0); wrapperABI < obj.ABICount; wrapperABI++ {
243 if !need.Get(wrapperABI) {
244 continue
245 }
246 cb(fn, wrapperABI)
247 }
248 }
249
250
251
252 func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
253 if base.Debug.ABIWrap != 0 {
254 fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %v\n", wrapperABI, f.ABI, f)
255 }
256
257
258 savepos := base.Pos
259
260 pos := base.AutogeneratedPos
261 base.Pos = pos
262
263
264
265 ft := f.Nname.Type()
266 if ft.NumRecvs() != 0 {
267 base.ErrorfAt(f.Pos(), 0, "makeABIWrapper support for wrapping methods not implemented")
268 return
269 }
270
271
272
273 fn := ir.NewFunc(pos, pos, f.Sym(), types.NewSignature(nil,
274 typecheck.NewFuncParams(ft.Params()),
275 typecheck.NewFuncParams(ft.Results())))
276 fn.ABI = wrapperABI
277 typecheck.DeclFunc(fn)
278
279 fn.SetABIWrapper(true)
280 fn.SetDupok(true)
281
282
283 fn.LinksymABI(fn.ABI).Set(obj.AttrLinkname, f.Linksym().IsLinkname())
284 fn.LinksymABI(fn.ABI).Set(obj.AttrLinknameStd, f.Linksym().IsLinknameStd())
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311 fn.Pragma |= ir.Nosplit
312
313
314
315
316
317
318
319
320
321
322 tailcall := fn.Type().NumResults() == 0 && fn.Type().NumParams() == 0 && fn.Type().NumRecvs() == 0
323 if (base.Ctxt.Arch.Name == "ppc64le" || base.Ctxt.Arch.Name == "ppc64") && base.Ctxt.Flag_dynlink {
324
325
326 tailcall = false
327 }
328 if base.Ctxt.Arch.Name == "amd64" && wrapperABI == obj.ABIInternal {
329
330
331 tailcall = false
332 }
333
334 var tail ir.Node
335 call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
336 call.Args = ir.ParamNames(fn.Type())
337 call.IsDDD = fn.Type().IsVariadic()
338 tail = call
339 if tailcall {
340 tail = ir.NewTailCallStmt(base.Pos, call)
341 } else if fn.Type().NumResults() > 0 {
342 n := ir.NewReturnStmt(base.Pos, nil)
343 n.Results = []ir.Node{call}
344 tail = n
345 }
346 fn.Body.Append(tail)
347
348 typecheck.Stmts(fn, fn.Body)
349
350
351 base.Pos = savepos
352 }
353
354
355
356
357 func CreateWasmImportWrapper(fn *ir.Func) bool {
358 if fn.WasmImport == nil {
359 return false
360 }
361 if buildcfg.GOARCH != "wasm" {
362 base.FatalfAt(fn.Pos(), "CreateWasmImportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, fn)
363 }
364
365 ir.InitLSym(fn, true)
366
367 setupWasmImport(fn)
368
369 pp := objw.NewProgs(fn, 0)
370 defer pp.Free()
371 pp.Text.To.Type = obj.TYPE_TEXTSIZE
372 pp.Text.To.Val = int32(types.RoundUp(fn.Type().ArgWidth(), int64(types.RegSize)))
373
374 pp.Text.To.Offset = 0
375 pp.Flush()
376
377 return true
378 }
379
380 func GenWasmExportWrapper(wrapped *ir.Func) {
381 if wrapped.WasmExport == nil {
382 return
383 }
384 if buildcfg.GOARCH != "wasm" {
385 base.FatalfAt(wrapped.Pos(), "GenWasmExportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, wrapped)
386 }
387
388 pos := base.AutogeneratedPos
389 sym := &types.Sym{
390 Name: wrapped.WasmExport.Name,
391 Linkname: wrapped.WasmExport.Name,
392 }
393 ft := wrapped.Nname.Type()
394 fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil,
395 typecheck.NewFuncParams(ft.Params()),
396 typecheck.NewFuncParams(ft.Results())))
397 fn.ABI = obj.ABI0
398
399
400
401
402
403
404 if ft.ArgWidth() > rtabi.StackSmall {
405 base.ErrorfAt(wrapped.Pos(), 0, "wasmexport function argument too large")
406 }
407 fn.Pragma |= ir.Nosplit
408
409 ir.InitLSym(fn, true)
410
411 setupWasmExport(fn, wrapped)
412
413 pp := objw.NewProgs(fn, 0)
414 defer pp.Free()
415
416 pp.Text.To.Type = obj.TYPE_TEXTSIZE
417 pp.Text.To.Val = int32(0)
418 pp.Text.To.Offset = types.RoundUp(ft.ArgWidth(), int64(types.RegSize))
419
420 p := pp.Prog(obj.AFUNCDATA)
421 p.From.SetConst(rtabi.FUNCDATA_LocalsPointerMaps)
422 p.To.Type = obj.TYPE_MEM
423 p.To.Name = obj.NAME_EXTERN
424 p.To.Sym = base.Ctxt.Lookup("no_pointers_stackmap")
425 pp.Flush()
426
427 }
428
429 func paramsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
430 wfs := make([]obj.WasmField, 0, len(abiParams))
431 for _, p := range abiParams {
432 t := p.Type
433 var wt obj.WasmFieldType
434 if t.IsSIMD() {
435 wt = obj.WasmV128
436 } else {
437 switch t.Kind() {
438 case types.TINT32, types.TUINT32:
439 wt = obj.WasmI32
440 case types.TINT64, types.TUINT64:
441 wt = obj.WasmI64
442 case types.TFLOAT32:
443 wt = obj.WasmF32
444 case types.TFLOAT64:
445 wt = obj.WasmF64
446 case types.TUNSAFEPTR, types.TUINTPTR:
447 wt = obj.WasmPtr
448 case types.TBOOL:
449 wt = obj.WasmBool
450 case types.TSTRING:
451
452 wt = obj.WasmPtr
453 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
454 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result) + int64(types.PtrSize)})
455 continue
456 case types.TPTR:
457 if wasmElemTypeAllowed(t.Elem()) {
458 wt = obj.WasmPtr
459 break
460 }
461 fallthrough
462 default:
463 base.ErrorfAt(f.Pos(), 0, "%s: unsupported parameter type %s", pragma, t.String())
464 }
465 }
466 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
467 }
468 return wfs
469 }
470
471 func resultsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
472 if len(abiParams) > 1 {
473 base.ErrorfAt(f.Pos(), 0, "%s: too many return values", pragma)
474 return nil
475 }
476 wfs := make([]obj.WasmField, len(abiParams))
477 for i, p := range abiParams {
478 t := p.Type
479 if t.IsSIMD() {
480 wfs[i].Type = obj.WasmV128
481 } else {
482 switch t.Kind() {
483 case types.TINT32, types.TUINT32:
484 wfs[i].Type = obj.WasmI32
485 case types.TINT64, types.TUINT64:
486 wfs[i].Type = obj.WasmI64
487 case types.TFLOAT32:
488 wfs[i].Type = obj.WasmF32
489 case types.TFLOAT64:
490 wfs[i].Type = obj.WasmF64
491 case types.TUNSAFEPTR, types.TUINTPTR:
492 wfs[i].Type = obj.WasmPtr
493 case types.TBOOL:
494 wfs[i].Type = obj.WasmBool
495 case types.TPTR:
496 if wasmElemTypeAllowed(t.Elem()) {
497 wfs[i].Type = obj.WasmPtr
498 break
499 }
500 fallthrough
501 default:
502 base.ErrorfAt(f.Pos(), 0, "%s: unsupported result type %s", pragma, t.String())
503 }
504 }
505 wfs[i].Offset = p.FrameOffset(result)
506 }
507 return wfs
508 }
509
510
511
512
513 func wasmElemTypeAllowed(t *types.Type) bool {
514 switch t.Kind() {
515 case types.TINT8, types.TUINT8, types.TINT16, types.TUINT16,
516 types.TINT32, types.TUINT32, types.TINT64, types.TUINT64,
517 types.TFLOAT32, types.TFLOAT64, types.TBOOL:
518 return true
519 case types.TARRAY:
520 return wasmElemTypeAllowed(t.Elem())
521 case types.TSTRUCT:
522 if len(t.Fields()) == 0 {
523 return true
524 }
525 seenHostLayout := false
526 for _, f := range t.Fields() {
527 sym := f.Type.Sym()
528 if sym != nil && sym.Name == "HostLayout" && sym.Pkg.Path == "structs" {
529 seenHostLayout = true
530 continue
531 }
532 if !wasmElemTypeAllowed(f.Type) {
533 return false
534 }
535 }
536 return seenHostLayout
537 }
538
539
540
541 return false
542 }
543
544
545
546 func setupWasmImport(f *ir.Func) {
547 wi := obj.WasmImport{
548 Module: f.WasmImport.Module,
549 Name: f.WasmImport.Name,
550 }
551 if wi.Module == wasm.GojsModule {
552
553
554
555
556
557
558
559
560
561
562 wi.Params = []obj.WasmField{{Type: obj.WasmI32}}
563 } else {
564
565
566
567
568
569
570
571
572
573 abiConfig := AbiForBodylessFuncStackMap(f)
574 abiInfo := abiConfig.ABIAnalyzeFuncType(f.Type())
575 wi.Params = paramsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.InParams())
576 wi.Results = resultsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.OutParams())
577 }
578 f.LSym.Func().WasmImport = &wi
579 }
580
581
582
583 func setupWasmExport(f, wrapped *ir.Func) {
584 we := obj.WasmExport{
585 WrappedSym: wrapped.LSym,
586 }
587 abiConfig := AbiForBodylessFuncStackMap(wrapped)
588 abiInfo := abiConfig.ABIAnalyzeFuncType(wrapped.Type())
589 we.Params = paramsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.InParams())
590 we.Results = resultsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.OutParams())
591 f.LSym.Func().WasmExport = &we
592 }
593
View as plain text