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