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