1
2
3
4
5 package midway
6
7 import (
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/syntax"
10 "cmd/compile/internal/types2"
11 "fmt"
12 "internal/buildcfg"
13 "internal/simd/variants"
14 "strings"
15 )
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
163
164 type Rewriter struct {
165 pkg *types2.Package
166 analyzer *Analyzer
167 info *types2.Info
168 sizes []int
169 }
170
171 func NewRewriter(pkg *types2.Package, info *types2.Info, analyzer *Analyzer, sizes []int) *Rewriter {
172 return &Rewriter{
173 pkg: pkg,
174 info: info,
175 analyzer: analyzer,
176 sizes: sizes,
177 }
178 }
179
180 func (r *Rewriter) Rewrite(files []*syntax.File) {
181
182
183 for _, fileAST := range files {
184
185 var newDecls []syntax.Decl
186 for _, k := range r.sizes {
187 newDecls = r.generateForSize(fileAST, k, "", newDecls)
188 if v := variants.Variants[variants.Key{Arch: buildcfg.GOARCH, Size: k}]; v != nil {
189 newDecls = r.generateForSize(fileAST, k, v.Suffix, newDecls)
190 }
191 }
192
193
194
195 r.generateDispatchers(fileAST)
196
197 fileAST.DeclList = append(fileAST.DeclList, newDecls...)
198 }
199 }
200
201 func (r *Rewriter) generateDispatchers(fileAST *syntax.File) {
202 var newDecls []syntax.Decl
203
204 change := false
205
206 for _, decl := range fileAST.DeclList {
207 switch d := decl.(type) {
208 case *syntax.FuncDecl:
209 if d.Name == nil {
210 newDecls = append(newDecls, d)
211 continue
212 }
213 obj := r.info.Defs[d.Name]
214 if !r.analyzer.isDependentObj[obj] || r.analyzer.inSimd {
215 newDecls = append(newDecls, d)
216 continue
217 }
218
219 sig, ok := obj.Type().(*types2.Signature)
220 if !ok {
221 newDecls = append(newDecls, d)
222 continue
223 }
224
225 change = true
226 if r.analyzer.HasDependentSignature(sig) {
227 if base.Debug.Simd > 0 {
228 base.Warn("%s: removing body of dependent-sig original function %v", d.Pos().String(), d.Name.Value)
229 }
230 d.Body = r.blockOf(d.Pos(), r.panicStmt(d.Pos(),
231 "unexpected call of original function rewritten to specialized SIMD"))
232 newDecls = append(newDecls, d)
233 continue
234 }
235
236
237 d.Body = r.createDispatcherBody(d, sig)
238 newDecls = append(newDecls, d)
239
240 case *syntax.VarDecl:
241
242
243 newDecls = append(newDecls, d)
244
245 case *syntax.TypeDecl:
246
247
248 newDecls = append(newDecls, d)
249 default:
250 newDecls = append(newDecls, decl)
251 }
252 }
253
254 if !change {
255 return
256 }
257
258 fileAST.DeclList = newDecls
259
260 if !r.analyzer.inSimd {
261
262 hasArchSimd := false
263 var simdImport *syntax.ImportDecl
264 p := fileAST.Pos()
265 for _, decl := range fileAST.DeclList {
266 if imp, ok := decl.(*syntax.ImportDecl); ok {
267 if imp.Path.Value == `"`+archFullPkg+`"` {
268 hasArchSimd = true
269 if simdImport == nil {
270 p = imp.Pos()
271 }
272 }
273 if imp.Path.Value == `"`+simdPkg+`"` {
274 simdImport = imp
275 p = imp.Pos()
276 }
277 }
278 }
279
280 if !hasArchSimd {
281 r.injectImport(fileAST, archFullPkg, p)
282 }
283
284
285
286 fun := &syntax.SelectorExpr{
287 X: syntax.NewName(p, simdPkg),
288 Sel: syntax.NewName(p, vectorSizeFn),
289 }
290 fun.SetPos(p)
291 call := &syntax.CallExpr{Fun: fun}
292 call.SetPos(p)
293
294 name := syntax.NewName(p, "_")
295
296 varDecl := &syntax.VarDecl{NameList: []*syntax.Name{name}, Values: call}
297 varDecl.SetPos(p)
298 fileAST.DeclList = append(fileAST.DeclList, varDecl)
299 }
300 }
301
302 func (r *Rewriter) injectImport(fileAST *syntax.File, toImport string, simdImportPos syntax.Pos) {
303 importDecl := &syntax.ImportDecl{
304 Path: &syntax.BasicLit{Value: `"` + toImport + `"`, Kind: syntax.StringLit},
305 }
306 importDecl.Path.SetPos(simdImportPos)
307 importDecl.SetPos(simdImportPos)
308 fileAST.DeclList = append([]syntax.Decl{importDecl}, fileAST.DeclList...)
309 }
310
311 func (r *Rewriter) createDispatcherBody(d *syntax.FuncDecl, sig *types2.Signature) *syntax.BlockStmt {
312
313
314 args := func() []syntax.Expr {
315 var args []syntax.Expr
316 if d.Type.ParamList != nil {
317 for _, field := range d.Type.ParamList {
318 if field.Name != nil {
319 paramName := syntax.NewName(field.Pos(), field.Name.Value)
320 args = append(args, paramName)
321 }
322 }
323 }
324 return args
325 }
326
327
328 pe := func(e syntax.Expr) syntax.Expr {
329 e.SetPos(d.Pos())
330 return e
331 }
332
333 ps := func(e syntax.Stmt) syntax.Stmt {
334 e.SetPos(d.Pos())
335 return e
336 }
337
338
339
340
341
342
343
344
345
346
347
348 switchStmt := &syntax.SwitchStmt{
349 Tag: pe(&syntax.CallExpr{
350 Fun: pe(&syntax.SelectorExpr{
351 X: syntax.NewName(d.Pos(), simdPkg),
352 Sel: syntax.NewName(d.Pos(), vectorSizeFn),
353 }),
354 }),
355 Body: []*syntax.CaseClause{},
356 }
357
358 var emulation syntax.Stmt
359
360 makeCallReturnStmt := func(k int, variantSuffix string) syntax.Stmt {
361 fnName := fmt.Sprintf("%s@simd%d%s", d.Name.Value, k, variantSuffix)
362 fnIdent := syntax.NewName(d.Pos(), fnName)
363
364 callExpr := pe(&syntax.CallExpr{
365 Fun: pe(fnIdent),
366 ArgList: args(),
367 })
368
369
370 var callReturnStmt syntax.Stmt
371 if d.Type.ResultList != nil && len(d.Type.ResultList) > 0 {
372 callReturnStmt = &syntax.ReturnStmt{Results: callExpr}
373 } else {
374 callReturnStmt = &syntax.BlockStmt{
375 List: []syntax.Stmt{
376 ps(&syntax.ExprStmt{X: callExpr}),
377 ps(&syntax.ReturnStmt{}),
378 },
379 Rbrace: d.Pos(),
380 }
381 }
382 callReturnStmt.SetPos(d.Pos())
383 return callReturnStmt
384 }
385
386 guardCallWithCondition := func(require string, stmt syntax.Stmt) syntax.Stmt {
387 cond := pe(&syntax.CallExpr{
388 Fun: pe(&syntax.SelectorExpr{
389 X: syntax.NewName(d.Pos(), simdPkg),
390 Sel: syntax.NewName(d.Pos(), require),
391 })})
392
393 blockStmt, ok := stmt.(*syntax.BlockStmt)
394 if !ok {
395 blockStmt = &syntax.BlockStmt{
396 List: []syntax.Stmt{stmt},
397 Rbrace: d.Pos(),
398 }
399 blockStmt.SetPos(d.Pos())
400 }
401
402 guarded := ps(&syntax.IfStmt{
403 Cond: cond,
404 Then: blockStmt,
405 })
406 return guarded
407 }
408
409 for _, k := range r.sizes {
410
411 callReturnStmt := makeCallReturnStmt(k, "")
412
413 if k == 0 {
414 emulation = guardCallWithCondition(emulatedFn, callReturnStmt)
415 continue
416 }
417
418 var caseBody []syntax.Stmt
419
420
421 if emulation != nil && k == 128 {
422 caseBody = append(caseBody, emulation)
423 emulation = nil
424 }
425
426
427
428
429
430 if v := variants.Variants[variants.Key{Arch: buildcfg.GOARCH, Size: k}]; v != nil {
431 callReturnStmt = guardCallWithCondition(v.DefaultRequires, callReturnStmt)
432 caseBody = append(caseBody, callReturnStmt)
433 callReturnStmt = makeCallReturnStmt(k, v.Suffix)
434 }
435
436 caseBody = append(caseBody, callReturnStmt)
437
438 caseClause := &syntax.CaseClause{
439 Cases: pe(&syntax.BasicLit{Kind: syntax.IntLit, Value: fmt.Sprintf("%d", k)}),
440 Body: caseBody,
441 }
442 caseClause.SetPos(d.Pos())
443 switchStmt.Body = append(switchStmt.Body, caseClause)
444 }
445
446 panicStmt := r.panicStmt(d.Pos(), "unsupported vector size in simd-rewritten code")
447 return r.blockOf(d.Pos(), switchStmt, panicStmt)
448 }
449
450 func (r *Rewriter) blockOf(p syntax.Pos, stmts ...syntax.Stmt) *syntax.BlockStmt {
451 for _, s := range stmts {
452 s.SetPos(p)
453 }
454 blockStmt := &syntax.BlockStmt{List: stmts}
455 blockStmt.SetPos(p)
456 return blockStmt
457 }
458
459 func (r *Rewriter) panicStmt(p syntax.Pos, unquotedMessage string) *syntax.ExprStmt {
460 pe := func(e syntax.Expr) syntax.Expr {
461 e.SetPos(p)
462 return e
463 }
464 fnName := "panic"
465 fnIdent := pe(syntax.NewName(p, fnName))
466 callExpr := pe(&syntax.CallExpr{
467 Fun: fnIdent,
468 ArgList: []syntax.Expr{pe(&syntax.BasicLit{Value: `"` + unquotedMessage + `"`, Kind: syntax.StringLit})},
469 })
470 panicStmt := &syntax.ExprStmt{X: callExpr}
471 panicStmt.SetPos(p)
472 return panicStmt
473 }
474
475 func (r *Rewriter) generateForSize(fileAST *syntax.File, k int, variantSuffix string, newDecls []syntax.Decl) []syntax.Decl {
476 copier := NewDeepCopier(r.pkg, r.info, k, r.analyzer, fmt.Sprintf("@simd%d%s", k, variantSuffix), variantSuffix)
477 for _, decl := range fileAST.DeclList {
478 if r.shouldIncludeDecl(decl) {
479 newDecl := copier.CopyDecl(decl)
480 newDecls = append(newDecls, newDecl)
481 }
482 }
483 return newDecls
484 }
485
486 func nameToElemBitWidth(name string) int {
487 var width int
488 switch name {
489 case "Int8s", "Uint8s", "Mask8s":
490 width = 8
491 case "Int16s", "Uint16s", "Mask16s":
492 width = 16
493 case "Int32s", "Uint32s", "Float32s", "Mask32s":
494 width = 32
495 case "Int64s", "Uint64s", "Float64s", "Mask64s":
496 width = 64
497 }
498 return width
499 }
500
501 func (r *Rewriter) shouldIncludeDecl(decl syntax.Decl) bool {
502
503
504
505 if r.analyzer.inSimd {
506 theFile := decl.Pos().Base().Filename()
507
508 lastSlash := strings.LastIndex(theFile, simdPkg+"/")
509 lastBackslash := strings.LastIndex(theFile, simdPkg+"\\")
510
511
512
513
514 maxSlash := max(lastSlash, lastBackslash)
515 if maxSlash == -1 {
516 return false
517 }
518 if !strings.HasPrefix(theFile[maxSlash:], simdPkg+"/tofrom_") &&
519 !strings.HasPrefix(theFile[maxSlash:], simdPkg+"\\tofrom_") {
520 return false
521 }
522 }
523
524 switch d := decl.(type) {
525 case *syntax.FuncDecl:
526 if d.Name != nil {
527 return r.analyzer.isDependentObj[r.info.Defs[d.Name]]
528 }
529 case *syntax.TypeDecl:
530 return r.analyzer.isDependentObj[r.info.Defs[d.Name]]
531 case *syntax.VarDecl:
532 for _, name := range d.NameList {
533 if r.analyzer.isDependentObj[r.info.Defs[name]] {
534 return true
535 }
536 }
537 }
538 return false
539 }
540
541
542 func RewriteWrapper(pkg *types2.Package, info *types2.Info, files []*syntax.File) bool {
543 if !buildcfg.Experiment.SIMD {
544 return false
545 }
546
547 switch buildcfg.GOARCH {
548 case "wasm", "amd64", "arm64":
549 default:
550 return false
551 }
552
553 sizes := rewriteSizes()
554 if len(sizes) == 0 {
555 return false
556 }
557 analyzer := NewAnalyzer(pkg, info)
558 if !analyzer.Analyze(files) {
559 return false
560 }
561
562 CheckPositions(files, "before midway")
563
564 rewriter := NewRewriter(pkg, info, analyzer, sizes)
565 rewriter.Rewrite(files)
566
567 CheckPositions(files, "after midway")
568
569 return true
570 }
571
View as plain text