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