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 }
172
173
174
175 cgoExport := cgoExports[symName]
176 for _, pprag := range cgoExport {
177
178
179
180
181
182
183
184
185
186
187 if len(*pprag) == 2 {
188 *pprag = append(*pprag, (*pprag)[1])
189 }
190
191 *pprag = append(*pprag, fn.ABI.String())
192 }
193
194
195 if abis, ok := s.refs[symName]; ok {
196 fn.ABIRefs |= abis
197 }
198
199
200
201 fn.ABIRefs.Set(obj.ABIInternal, true)
202
203
204
205
206
207
208
209
210
211
212
213
214 hasBody := len(fn.Body) != 0
215 if sym.Linkname != "" && (hasBody || hasDefABI) && len(cgoExport) == 0 {
216 fn.ABIRefs |= obj.ABISetCallable
217 }
218
219
220
221 if len(cgoExport) > 0 && fn.ABIRefs&^obj.ABISetOf(fn.ABI) != 0 {
222 base.Fatalf("cgo exported function %v cannot have ABI wrappers", fn)
223 }
224
225 if !buildcfg.Experiment.RegabiWrappers {
226 continue
227 }
228
229 forEachWrapperABI(fn, makeABIWrapper)
230 }
231 }
232
233 func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) {
234 need := fn.ABIRefs &^ obj.ABISetOf(fn.ABI)
235 if need == 0 {
236 return
237 }
238
239 for wrapperABI := obj.ABI(0); wrapperABI < obj.ABICount; wrapperABI++ {
240 if !need.Get(wrapperABI) {
241 continue
242 }
243 cb(fn, wrapperABI)
244 }
245 }
246
247
248
249 func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
250 if base.Debug.ABIWrap != 0 {
251 fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %v\n", wrapperABI, f.ABI, f)
252 }
253
254
255 savepos := base.Pos
256 savedcurfn := ir.CurFunc
257
258 pos := base.AutogeneratedPos
259 base.Pos = pos
260
261
262
263 ft := f.Nname.Type()
264 if ft.NumRecvs() != 0 {
265 base.ErrorfAt(f.Pos(), 0, "makeABIWrapper support for wrapping methods not implemented")
266 return
267 }
268
269
270
271 fn := ir.NewFunc(pos, pos, f.Sym(), types.NewSignature(nil,
272 typecheck.NewFuncParams(ft.Params()),
273 typecheck.NewFuncParams(ft.Results())))
274 fn.ABI = wrapperABI
275 typecheck.DeclFunc(fn)
276
277 fn.SetABIWrapper(true)
278 fn.SetDupok(true)
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305 fn.Pragma |= ir.Nosplit
306
307
308
309
310
311
312
313
314
315
316 tailcall := fn.Type().NumResults() == 0 && fn.Type().NumParams() == 0 && fn.Type().NumRecvs() == 0
317 if base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink {
318
319
320 tailcall = false
321 }
322 if base.Ctxt.Arch.Name == "amd64" && wrapperABI == obj.ABIInternal {
323
324
325 tailcall = false
326 }
327
328 var tail ir.Node
329 call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
330 call.Args = ir.ParamNames(fn.Type())
331 call.IsDDD = fn.Type().IsVariadic()
332 tail = call
333 if tailcall {
334 tail = ir.NewTailCallStmt(base.Pos, call)
335 } else if fn.Type().NumResults() > 0 {
336 n := ir.NewReturnStmt(base.Pos, nil)
337 n.Results = []ir.Node{call}
338 tail = n
339 }
340 fn.Body.Append(tail)
341
342 typecheck.FinishFuncBody()
343
344 ir.CurFunc = fn
345 typecheck.Stmts(fn.Body)
346
347
348 base.Pos = savepos
349 ir.CurFunc = savedcurfn
350 }
351
352
353
354
355 func CreateWasmImportWrapper(fn *ir.Func) bool {
356 if fn.WasmImport == nil {
357 return false
358 }
359 if buildcfg.GOARCH != "wasm" {
360 base.FatalfAt(fn.Pos(), "CreateWasmImportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, fn)
361 }
362
363 ir.InitLSym(fn, true)
364
365 setupWasmImport(fn)
366
367 pp := objw.NewProgs(fn, 0)
368 defer pp.Free()
369 pp.Text.To.Type = obj.TYPE_TEXTSIZE
370 pp.Text.To.Val = int32(types.RoundUp(fn.Type().ArgWidth(), int64(types.RegSize)))
371
372 pp.Text.To.Offset = 0
373 pp.Flush()
374
375 return true
376 }
377
378 func GenWasmExportWrapper(wrapped *ir.Func) {
379 if wrapped.WasmExport == nil {
380 return
381 }
382 if buildcfg.GOARCH != "wasm" {
383 base.FatalfAt(wrapped.Pos(), "GenWasmExportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, wrapped)
384 }
385
386 pos := base.AutogeneratedPos
387 sym := &types.Sym{
388 Name: wrapped.WasmExport.Name,
389 Linkname: wrapped.WasmExport.Name,
390 }
391 ft := wrapped.Nname.Type()
392 fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil,
393 typecheck.NewFuncParams(ft.Params()),
394 typecheck.NewFuncParams(ft.Results())))
395 fn.ABI = obj.ABI0
396
397
398
399
400
401
402 if ft.ArgWidth() > rtabi.StackSmall {
403 base.ErrorfAt(wrapped.Pos(), 0, "wasmexport function argument too large")
404 }
405 fn.Pragma |= ir.Nosplit
406
407 ir.InitLSym(fn, true)
408
409 setupWasmExport(fn, wrapped)
410
411 pp := objw.NewProgs(fn, 0)
412 defer pp.Free()
413
414 pp.Text.To.Type = obj.TYPE_TEXTSIZE
415 pp.Text.To.Val = int32(0)
416 pp.Text.To.Offset = types.RoundUp(ft.ArgWidth(), int64(types.RegSize))
417
418 p := pp.Prog(obj.AFUNCDATA)
419 p.From.SetConst(rtabi.FUNCDATA_LocalsPointerMaps)
420 p.To.Type = obj.TYPE_MEM
421 p.To.Name = obj.NAME_EXTERN
422 p.To.Sym = base.Ctxt.Lookup("no_pointers_stackmap")
423 pp.Flush()
424
425 }
426
427 func paramsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
428 wfs := make([]obj.WasmField, 0, len(abiParams))
429 for _, p := range abiParams {
430 t := p.Type
431 var wt obj.WasmFieldType
432 switch t.Kind() {
433 case types.TINT32, types.TUINT32:
434 wt = obj.WasmI32
435 case types.TINT64, types.TUINT64:
436 wt = obj.WasmI64
437 case types.TFLOAT32:
438 wt = obj.WasmF32
439 case types.TFLOAT64:
440 wt = obj.WasmF64
441 case types.TUNSAFEPTR, types.TUINTPTR:
442 wt = obj.WasmPtr
443 case types.TBOOL:
444 wt = obj.WasmBool
445 case types.TSTRING:
446
447 wt = obj.WasmPtr
448 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
449 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result) + int64(types.PtrSize)})
450 continue
451 case types.TPTR:
452 if wasmElemTypeAllowed(t.Elem()) {
453 wt = obj.WasmPtr
454 break
455 }
456 fallthrough
457 default:
458 base.ErrorfAt(f.Pos(), 0, "%s: unsupported parameter type %s", pragma, t.String())
459 }
460 wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
461 }
462 return wfs
463 }
464
465 func resultsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
466 if len(abiParams) > 1 {
467 base.ErrorfAt(f.Pos(), 0, "%s: too many return values", pragma)
468 return nil
469 }
470 wfs := make([]obj.WasmField, len(abiParams))
471 for i, p := range abiParams {
472 t := p.Type
473 switch t.Kind() {
474 case types.TINT32, types.TUINT32:
475 wfs[i].Type = obj.WasmI32
476 case types.TINT64, types.TUINT64:
477 wfs[i].Type = obj.WasmI64
478 case types.TFLOAT32:
479 wfs[i].Type = obj.WasmF32
480 case types.TFLOAT64:
481 wfs[i].Type = obj.WasmF64
482 case types.TUNSAFEPTR, types.TUINTPTR:
483 wfs[i].Type = obj.WasmPtr
484 case types.TBOOL:
485 wfs[i].Type = obj.WasmBool
486 case types.TPTR:
487 if wasmElemTypeAllowed(t.Elem()) {
488 wfs[i].Type = obj.WasmPtr
489 break
490 }
491 fallthrough
492 default:
493 base.ErrorfAt(f.Pos(), 0, "%s: unsupported result type %s", pragma, t.String())
494 }
495 wfs[i].Offset = p.FrameOffset(result)
496 }
497 return wfs
498 }
499
500
501
502
503 func wasmElemTypeAllowed(t *types.Type) bool {
504 switch t.Kind() {
505 case types.TINT8, types.TUINT8, types.TINT16, types.TUINT16,
506 types.TINT32, types.TUINT32, types.TINT64, types.TUINT64,
507 types.TFLOAT32, types.TFLOAT64, types.TBOOL:
508 return true
509 case types.TARRAY:
510 return wasmElemTypeAllowed(t.Elem())
511 case types.TSTRUCT:
512 if len(t.Fields()) == 0 {
513 return true
514 }
515 seenHostLayout := false
516 for _, f := range t.Fields() {
517 sym := f.Type.Sym()
518 if sym != nil && sym.Name == "HostLayout" && sym.Pkg.Path == "structs" {
519 seenHostLayout = true
520 continue
521 }
522 if !wasmElemTypeAllowed(f.Type) {
523 return false
524 }
525 }
526 return seenHostLayout
527 }
528
529
530
531 return false
532 }
533
534
535
536 func setupWasmImport(f *ir.Func) {
537 wi := obj.WasmImport{
538 Module: f.WasmImport.Module,
539 Name: f.WasmImport.Name,
540 }
541 if wi.Module == wasm.GojsModule {
542
543
544
545
546
547
548
549
550
551
552 wi.Params = []obj.WasmField{{Type: obj.WasmI32}}
553 } else {
554
555
556
557
558
559
560
561
562
563 abiConfig := AbiForBodylessFuncStackMap(f)
564 abiInfo := abiConfig.ABIAnalyzeFuncType(f.Type())
565 wi.Params = paramsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.InParams())
566 wi.Results = resultsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.OutParams())
567 }
568 f.LSym.Func().WasmImport = &wi
569 }
570
571
572
573 func setupWasmExport(f, wrapped *ir.Func) {
574 we := obj.WasmExport{
575 WrappedSym: wrapped.LSym,
576 }
577 abiConfig := AbiForBodylessFuncStackMap(wrapped)
578 abiInfo := abiConfig.ABIAnalyzeFuncType(wrapped.Type())
579 we.Params = paramsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.InParams())
580 we.Results = resultsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.OutParams())
581 f.LSym.Func().WasmExport = &we
582 }
583
View as plain text