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 savedcurfn := ir.CurFunc
260
261 pos := base.AutogeneratedPos
262 base.Pos = pos
263
264
265
266 ft := f.Nname.Type()
267 if ft.NumRecvs() != 0 {
268 base.ErrorfAt(f.Pos(), 0, "makeABIWrapper support for wrapping methods not implemented")
269 return
270 }
271
272
273
274 fn := ir.NewFunc(pos, pos, f.Sym(), types.NewSignature(nil,
275 typecheck.NewFuncParams(ft.Params()),
276 typecheck.NewFuncParams(ft.Results())))
277 fn.ABI = wrapperABI
278 typecheck.DeclFunc(fn)
279
280 fn.SetABIWrapper(true)
281 fn.SetDupok(true)
282
283
284 fn.LinksymABI(fn.ABI).Set(obj.AttrLinkname, f.Linksym().IsLinkname())
285 fn.LinksymABI(fn.ABI).Set(obj.AttrLinknameStd, f.Linksym().IsLinknameStd())
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
312 fn.Pragma |= ir.Nosplit
313
314
315
316
317
318
319
320
321
322
323 tailcall := fn.Type().NumResults() == 0 && fn.Type().NumParams() == 0 && fn.Type().NumRecvs() == 0
324 if (base.Ctxt.Arch.Name == "ppc64le" || base.Ctxt.Arch.Name == "ppc64") && base.Ctxt.Flag_dynlink {
325
326
327 tailcall = false
328 }
329 if base.Ctxt.Arch.Name == "amd64" && wrapperABI == obj.ABIInternal {
330
331
332 tailcall = false
333 }
334
335 var tail ir.Node
336 call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
337 call.Args = ir.ParamNames(fn.Type())
338 call.IsDDD = fn.Type().IsVariadic()
339 tail = call
340 if tailcall {
341 tail = ir.NewTailCallStmt(base.Pos, call)
342 } else if fn.Type().NumResults() > 0 {
343 n := ir.NewReturnStmt(base.Pos, nil)
344 n.Results = []ir.Node{call}
345 tail = n
346 }
347 fn.Body.Append(tail)
348
349 typecheck.FinishFuncBody()
350
351 ir.CurFunc = fn
352 typecheck.Stmts(fn.Body)
353
354
355 base.Pos = savepos
356 ir.CurFunc = savedcurfn
357 }
358
359
360
361
362 func CreateWasmImportWrapper(fn *ir.Func) bool {
363 if fn.WasmImport == nil {
364 return false
365 }
366 if buildcfg.GOARCH != "wasm" {
367 base.FatalfAt(fn.Pos(), "CreateWasmImportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, fn)
368 }
369
370 ir.InitLSym(fn, true)
371
372 setupWasmImport(fn)
373
374 pp := objw.NewProgs(fn, 0)
375 defer pp.Free()
376 pp.Text.To.Type = obj.TYPE_TEXTSIZE
377 pp.Text.To.Val = int32(types.RoundUp(fn.Type().ArgWidth(), int64(types.RegSize)))
378
379 pp.Text.To.Offset = 0
380 pp.Flush()
381
382 return true
383 }
384
385 func GenWasmExportWrapper(wrapped *ir.Func) {
386 if wrapped.WasmExport == nil {
387 return
388 }
389 if buildcfg.GOARCH != "wasm" {
390 base.FatalfAt(wrapped.Pos(), "GenWasmExportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, wrapped)
391 }
392
393 pos := base.AutogeneratedPos
394 sym := &types.Sym{
395 Name: wrapped.WasmExport.Name,
396 Linkname: wrapped.WasmExport.Name,
397 }
398 ft := wrapped.Nname.Type()
399 fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil,
400 typecheck.NewFuncParams(ft.Params()),
401 typecheck.NewFuncParams(ft.Results())))
402 fn.ABI = obj.ABI0
403
404
405
406
407
408
409 if ft.ArgWidth() > rtabi.StackSmall {
410 base.ErrorfAt(wrapped.Pos(), 0, "wasmexport function argument too large")
411 }
412 fn.Pragma |= ir.Nosplit
413
414 ir.InitLSym(fn, true)
415
416 setupWasmExport(fn, wrapped)
417
418 pp := objw.NewProgs(fn, 0)
419 defer pp.Free()
420
421 pp.Text.To.Type = obj.TYPE_TEXTSIZE
422 pp.Text.To.Val = int32(0)
423 pp.Text.To.Offset = types.RoundUp(ft.ArgWidth(), int64(types.RegSize))
424
425 p := pp.Prog(obj.AFUNCDATA)
426 p.From.SetConst(rtabi.FUNCDATA_LocalsPointerMaps)
427 p.To.Type = obj.TYPE_MEM
428 p.To.Name = obj.NAME_EXTERN
429 p.To.Sym = base.Ctxt.Lookup("no_pointers_stackmap")
430 pp.Flush()
431
432 }
433
434 func paramsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
435 wfs := make([]obj.WasmField, 0, len(abiParams))
436 for _, p := range abiParams {
437 t := p.Type
438 var wt obj.WasmFieldType
439 if t.IsSIMD() {
440 wt = obj.WasmV128
441 } else {
442 switch t.Kind() {
443 case types.TINT32, types.TUINT32:
444 wt = obj.WasmI32
445 case types.TINT64, types.TUINT64:
446 wt = obj.WasmI64
447 case types.TFLOAT32:
448 wt = obj.WasmF32
449 case types.TFLOAT64:
450 wt = obj.WasmF64
451 case types.TUNSAFEPTR, types.TUINTPTR:
452 wt = obj.WasmPtr
453 case types.TBOOL:
454 wt = obj.WasmBool
455 case types.TSTRING:
456
457 wt = obj.WasmPtr
458 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
459 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result) + int64(types.PtrSize)})
460 continue
461 case types.TPTR:
462 if wasmElemTypeAllowed(t.Elem()) {
463 wt = obj.WasmPtr
464 break
465 }
466 fallthrough
467 default:
468 base.ErrorfAt(f.Pos(), 0, "%s: unsupported parameter type %s", pragma, t.String())
469 }
470 }
471 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
472 }
473 return wfs
474 }
475
476 func resultsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
477 if len(abiParams) > 1 {
478 base.ErrorfAt(f.Pos(), 0, "%s: too many return values", pragma)
479 return nil
480 }
481 wfs := make([]obj.WasmField, len(abiParams))
482 for i, p := range abiParams {
483 t := p.Type
484 if t.IsSIMD() {
485 wfs[i].Type = obj.WasmV128
486 } else {
487 switch t.Kind() {
488 case types.TINT32, types.TUINT32:
489 wfs[i].Type = obj.WasmI32
490 case types.TINT64, types.TUINT64:
491 wfs[i].Type = obj.WasmI64
492 case types.TFLOAT32:
493 wfs[i].Type = obj.WasmF32
494 case types.TFLOAT64:
495 wfs[i].Type = obj.WasmF64
496 case types.TUNSAFEPTR, types.TUINTPTR:
497 wfs[i].Type = obj.WasmPtr
498 case types.TBOOL:
499 wfs[i].Type = obj.WasmBool
500 case types.TPTR:
501 if wasmElemTypeAllowed(t.Elem()) {
502 wfs[i].Type = obj.WasmPtr
503 break
504 }
505 fallthrough
506 default:
507 base.ErrorfAt(f.Pos(), 0, "%s: unsupported result type %s", pragma, t.String())
508 }
509 }
510 wfs[i].Offset = p.FrameOffset(result)
511 }
512 return wfs
513 }
514
515
516
517
518 func wasmElemTypeAllowed(t *types.Type) bool {
519 switch t.Kind() {
520 case types.TINT8, types.TUINT8, types.TINT16, types.TUINT16,
521 types.TINT32, types.TUINT32, types.TINT64, types.TUINT64,
522 types.TFLOAT32, types.TFLOAT64, types.TBOOL:
523 return true
524 case types.TARRAY:
525 return wasmElemTypeAllowed(t.Elem())
526 case types.TSTRUCT:
527 if len(t.Fields()) == 0 {
528 return true
529 }
530 seenHostLayout := false
531 for _, f := range t.Fields() {
532 sym := f.Type.Sym()
533 if sym != nil && sym.Name == "HostLayout" && sym.Pkg.Path == "structs" {
534 seenHostLayout = true
535 continue
536 }
537 if !wasmElemTypeAllowed(f.Type) {
538 return false
539 }
540 }
541 return seenHostLayout
542 }
543
544
545
546 return false
547 }
548
549
550
551 func setupWasmImport(f *ir.Func) {
552 wi := obj.WasmImport{
553 Module: f.WasmImport.Module,
554 Name: f.WasmImport.Name,
555 }
556 if wi.Module == wasm.GojsModule {
557
558
559
560
561
562
563
564
565
566
567 wi.Params = []obj.WasmField{{Type: obj.WasmI32}}
568 } else {
569
570
571
572
573
574
575
576
577
578 abiConfig := AbiForBodylessFuncStackMap(f)
579 abiInfo := abiConfig.ABIAnalyzeFuncType(f.Type())
580 wi.Params = paramsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.InParams())
581 wi.Results = resultsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.OutParams())
582 }
583 f.LSym.Func().WasmImport = &wi
584 }
585
586
587
588 func setupWasmExport(f, wrapped *ir.Func) {
589 we := obj.WasmExport{
590 WrappedSym: wrapped.LSym,
591 }
592 abiConfig := AbiForBodylessFuncStackMap(wrapped)
593 abiInfo := abiConfig.ABIAnalyzeFuncType(wrapped.Type())
594 we.Params = paramsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.InParams())
595 we.Results = resultsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.OutParams())
596 f.LSym.Func().WasmExport = &we
597 }
598
View as plain text