1
2
3
4
5 package wasm
6
7 import (
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/ir"
10 "cmd/compile/internal/logopt"
11 "cmd/compile/internal/objw"
12 "cmd/compile/internal/ssa"
13 "cmd/compile/internal/ssa/block"
14 "cmd/compile/internal/ssagen"
15 "cmd/compile/internal/types"
16 "cmd/internal/obj"
17 "cmd/internal/obj/wasm"
18 )
19
20
133
134 func Init(arch *ssagen.ArchInfo) {
135 arch.LinkArch = &wasm.Linkwasm
136 arch.REGSP = wasm.REG_SP
137 arch.MAXWIDTH = 1 << 50
138
139 arch.ZeroRange = zeroRange
140 arch.Ginsnop = ginsnop
141
142 arch.SSAMarkMoves = ssaMarkMoves
143 arch.SSAGenValue = ssaGenValue
144 arch.SSAGenBlock = ssaGenBlock
145 }
146
147 func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
148 if cnt == 0 {
149 return p
150 }
151 if cnt%8 != 0 {
152 base.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
153 }
154
155 for i := int64(0); i < cnt; i += 8 {
156 p = pp.Append(p, wasm.AGet, obj.TYPE_REG, wasm.REG_SP, 0, 0, 0, 0)
157 p = pp.Append(p, wasm.AI64Const, obj.TYPE_CONST, 0, 0, 0, 0, 0)
158 p = pp.Append(p, wasm.AI64Store, 0, 0, 0, obj.TYPE_CONST, 0, off+i)
159 }
160
161 return p
162 }
163
164 func ginsnop(pp *objw.Progs) *obj.Prog {
165 return pp.Prog(wasm.ANop)
166 }
167
168 func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
169 }
170
171 func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
172 switch b.Kind {
173 case block.BlockPlain, block.BlockDefer:
174 if next != b.Succs[0].Block() {
175 s.Br(obj.AJMP, b.Succs[0].Block())
176 }
177
178 case block.BlockIf:
179 switch next {
180 case b.Succs[0].Block():
181
182 getValue32(s, b.Controls[0])
183 s.Prog(wasm.AI32Eqz)
184 s.Prog(wasm.AIf)
185 s.Br(obj.AJMP, b.Succs[1].Block())
186 s.Prog(wasm.AEnd)
187 case b.Succs[1].Block():
188
189 getValue32(s, b.Controls[0])
190 s.Prog(wasm.AIf)
191 s.Br(obj.AJMP, b.Succs[0].Block())
192 s.Prog(wasm.AEnd)
193 default:
194
195 getValue32(s, b.Controls[0])
196 s.Prog(wasm.AIf)
197 s.Br(obj.AJMP, b.Succs[0].Block())
198 s.Prog(wasm.AEnd)
199 s.Br(obj.AJMP, b.Succs[1].Block())
200 }
201
202 case block.BlockRet:
203 s.Prog(obj.ARET)
204
205 case block.BlockExit, block.BlockRetJmp:
206
207 default:
208 base.FatalfAt(b.Pos, "unexpected block b%d, kind=%v", b.ID, b.Kind)
209 }
210
211
212 s.Prog(wasm.ARESUMEPOINT)
213
214 if s.OnWasmStackSkipped != 0 {
215 panic("wasm: bad stack")
216 }
217 }
218
219 func ssaGenValue(s *ssagen.State, v *ssa.Value) {
220 switch v.Op {
221 case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall, ssa.OpWasmLoweredTailCall, ssa.OpWasmLoweredTailCallInter:
222 s.PrepareCall(v)
223 if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == ir.Syms.Deferreturn {
224
225
226
227
228
229 s.Prog(wasm.ARESUMEPOINT)
230 }
231 if v.Op == ssa.OpWasmLoweredClosureCall {
232 getValue64(s, v.Args[1])
233 setReg(s, wasm.REG_CTXT)
234 }
235 if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn != nil {
236 sym := call.Fn
237 p := s.Prog(obj.ACALL)
238 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
239 p.Pos = v.Pos
240 if v.Op == ssa.OpWasmLoweredTailCall {
241 p.As = obj.ARET
242 }
243 } else {
244 getValue64(s, v.Args[0])
245 p := s.Prog(obj.ACALL)
246 p.To = obj.Addr{Type: obj.TYPE_NONE}
247 p.Pos = v.Pos
248 if v.Op == ssa.OpWasmLoweredTailCallInter {
249 p.As = obj.ARET
250 }
251 }
252
253 case ssa.OpWasmLoweredMove:
254 getValue32(s, v.Args[0])
255 getValue32(s, v.Args[1])
256 i32Const(s, int32(v.AuxInt))
257 s.Prog(wasm.AMemoryCopy)
258
259 case ssa.OpWasmLoweredZero:
260 getValue32(s, v.Args[0])
261 i32Const(s, 0)
262 i32Const(s, int32(v.AuxInt))
263 s.Prog(wasm.AMemoryFill)
264
265 case ssa.OpWasmLoweredNilCheck:
266 getValue64(s, v.Args[0])
267 s.Prog(wasm.AI64Eqz)
268 s.Prog(wasm.AIf)
269 p := s.Prog(wasm.ACALLNORESUME)
270 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.SigPanic}
271 s.Prog(wasm.AEnd)
272 if logopt.Enabled() {
273 logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
274 }
275 if base.Debug.Nil != 0 && v.Pos.Line() > 1 {
276 base.WarnfAt(v.Pos, "generated nil check")
277 }
278
279 case ssa.OpWasmLoweredWB:
280 p := s.Prog(wasm.ACall)
281
282 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.GCWriteBarrier[v.AuxInt-1]}
283 setReg(s, v.Reg0())
284
285 case ssa.OpWasmI64Store8, ssa.OpWasmI64Store16, ssa.OpWasmI64Store32, ssa.OpWasmI64Store, ssa.OpWasmF32Store, ssa.OpWasmF64Store:
286 getValue32(s, v.Args[0])
287 getValue64(s, v.Args[1])
288 p := s.Prog(v.Op.Asm())
289 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
290
291 case ssa.OpWasmV128Store:
292 getValue32(s, v.Args[0])
293 getValue128(s, v.Args[1])
294 p := s.Prog(v.Op.Asm())
295 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
296
297 case ssa.OpStoreReg:
298 getReg(s, wasm.REG_SP)
299 if v.Type.Size() == 16 {
300 getValue128(s, v.Args[0])
301 } else {
302 getValue64(s, v.Args[0])
303 }
304 p := s.Prog(storeOp(v.Type))
305 ssagen.AddrAuto(&p.To, v)
306
307 case ssa.OpClobber, ssa.OpClobberReg:
308
309
310 default:
311 if v.Type.IsMemory() {
312 return
313 }
314 if v.OnWasmStack {
315 s.OnWasmStackSkipped++
316
317
318 return
319 }
320 ssaGenValueOnStack(s, v, true)
321 if s.OnWasmStackSkipped != 0 {
322 panic("wasm: bad stack")
323 }
324 setReg(s, v.Reg())
325 }
326 }
327
328 func ssaGenValueOnStack(s *ssagen.State, v *ssa.Value, extend bool) {
329 switch v.Op {
330 case ssa.OpWasmLoweredGetClosurePtr:
331 getReg(s, wasm.REG_CTXT)
332
333 case ssa.OpWasmLoweredGetCallerPC:
334 p := s.Prog(wasm.AI64Load)
335
336 p.From = obj.Addr{
337 Type: obj.TYPE_MEM,
338 Name: obj.NAME_PARAM,
339 Offset: -8,
340 }
341
342 case ssa.OpWasmLoweredGetCallerSP:
343 p := s.Prog(wasm.AGet)
344
345 p.From = obj.Addr{
346 Type: obj.TYPE_ADDR,
347 Name: obj.NAME_PARAM,
348 Reg: wasm.REG_SP,
349 Offset: 0,
350 }
351
352 case ssa.OpWasmLoweredAddr:
353 if v.Aux == nil {
354 getValue64(s, v.Args[0])
355 i64Const(s, v.AuxInt)
356 s.Prog(wasm.AI64Add)
357 break
358 }
359 p := s.Prog(wasm.AGet)
360 p.From.Type = obj.TYPE_ADDR
361 switch v.Aux.(type) {
362 case *obj.LSym:
363 ssagen.AddAux(&p.From, v)
364 case *ir.Name:
365 p.From.Reg = v.Args[0].Reg()
366 ssagen.AddAux(&p.From, v)
367 default:
368 panic("wasm: bad LoweredAddr")
369 }
370
371 case ssa.OpWasmLoweredConvert:
372 getValue64(s, v.Args[0])
373
374 case ssa.OpWasmSelect:
375 getValue64(s, v.Args[0])
376 getValue64(s, v.Args[1])
377 getValue32(s, v.Args[2])
378 s.Prog(v.Op.Asm())
379
380 case ssa.OpWasmSelectV:
381 getValue128(s, v.Args[0])
382 getValue128(s, v.Args[1])
383 getValue32(s, v.Args[2])
384 s.Prog(v.Op.Asm())
385
386 case ssa.OpWasmI64AddConst:
387 getValue64(s, v.Args[0])
388 i64Const(s, v.AuxInt)
389 s.Prog(v.Op.Asm())
390
391 case ssa.OpWasmI64Const:
392 i64Const(s, v.AuxInt)
393
394 case ssa.OpWasmF32Const:
395 f32Const(s, v.AuxFloat())
396
397 case ssa.OpWasmF64Const:
398 f64Const(s, v.AuxFloat())
399
400 case ssa.OpWasmI64Load8U, ssa.OpWasmI64Load8S, ssa.OpWasmI64Load16U, ssa.OpWasmI64Load16S,
401 ssa.OpWasmI64Load32U, ssa.OpWasmI64Load32S, ssa.OpWasmI64Load, ssa.OpWasmF32Load, ssa.OpWasmF64Load, ssa.OpWasmV128Load:
402 getValue32(s, v.Args[0])
403 p := s.Prog(v.Op.Asm())
404 p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
405
406 case ssa.OpWasmI64Eqz:
407 getValue64(s, v.Args[0])
408 s.Prog(v.Op.Asm())
409 if extend {
410 s.Prog(wasm.AI64ExtendI32U)
411 }
412
413 case ssa.OpWasmI64Eq, ssa.OpWasmI64Ne, ssa.OpWasmI64LtS, ssa.OpWasmI64LtU, ssa.OpWasmI64GtS, ssa.OpWasmI64GtU, ssa.OpWasmI64LeS, ssa.OpWasmI64LeU, ssa.OpWasmI64GeS, ssa.OpWasmI64GeU,
414 ssa.OpWasmF32Eq, ssa.OpWasmF32Ne, ssa.OpWasmF32Lt, ssa.OpWasmF32Gt, ssa.OpWasmF32Le, ssa.OpWasmF32Ge,
415 ssa.OpWasmF64Eq, ssa.OpWasmF64Ne, ssa.OpWasmF64Lt, ssa.OpWasmF64Gt, ssa.OpWasmF64Le, ssa.OpWasmF64Ge:
416 getValue64(s, v.Args[0])
417 getValue64(s, v.Args[1])
418 s.Prog(v.Op.Asm())
419 if extend {
420 s.Prog(wasm.AI64ExtendI32U)
421 }
422
423 case ssa.OpWasmI64Add, ssa.OpWasmI64Sub, ssa.OpWasmI64Mul, ssa.OpWasmI64DivU, ssa.OpWasmI64RemS, ssa.OpWasmI64RemU, ssa.OpWasmI64And, ssa.OpWasmI64Or, ssa.OpWasmI64Xor, ssa.OpWasmI64Shl, ssa.OpWasmI64ShrS, ssa.OpWasmI64ShrU, ssa.OpWasmI64Rotl,
424 ssa.OpWasmF32Add, ssa.OpWasmF32Sub, ssa.OpWasmF32Mul, ssa.OpWasmF32Div, ssa.OpWasmF32Copysign,
425 ssa.OpWasmF64Add, ssa.OpWasmF64Sub, ssa.OpWasmF64Mul, ssa.OpWasmF64Div, ssa.OpWasmF64Copysign:
426 getValue64(s, v.Args[0])
427 getValue64(s, v.Args[1])
428 s.Prog(v.Op.Asm())
429
430 case ssa.OpWasmI32Rotl:
431 getValue32(s, v.Args[0])
432 getValue32(s, v.Args[1])
433 s.Prog(wasm.AI32Rotl)
434 s.Prog(wasm.AI64ExtendI32U)
435
436 case ssa.OpWasmI64DivS:
437 getValue64(s, v.Args[0])
438 getValue64(s, v.Args[1])
439 if v.Type.Size() == 8 {
440
441 p := s.Prog(wasm.ACall)
442 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmDiv}
443 break
444 }
445 s.Prog(wasm.AI64DivS)
446
447 case ssa.OpWasmI64TruncSatF32S, ssa.OpWasmI64TruncSatF64S:
448 getValue64(s, v.Args[0])
449 s.Prog(v.Op.Asm())
450
451 case ssa.OpWasmI64TruncSatF32U, ssa.OpWasmI64TruncSatF64U:
452 getValue64(s, v.Args[0])
453 s.Prog(v.Op.Asm())
454
455 case ssa.OpWasmF32DemoteF64:
456 getValue64(s, v.Args[0])
457 s.Prog(v.Op.Asm())
458
459 case ssa.OpWasmF64PromoteF32:
460 getValue64(s, v.Args[0])
461 s.Prog(v.Op.Asm())
462
463 case ssa.OpWasmF32ConvertI64S, ssa.OpWasmF32ConvertI64U,
464 ssa.OpWasmF64ConvertI64S, ssa.OpWasmF64ConvertI64U,
465 ssa.OpWasmI64Extend8S, ssa.OpWasmI64Extend16S, ssa.OpWasmI64Extend32S,
466 ssa.OpWasmF32Neg, ssa.OpWasmF32Sqrt, ssa.OpWasmF32Trunc, ssa.OpWasmF32Ceil, ssa.OpWasmF32Floor, ssa.OpWasmF32Nearest, ssa.OpWasmF32Abs,
467 ssa.OpWasmF64Neg, ssa.OpWasmF64Sqrt, ssa.OpWasmF64Trunc, ssa.OpWasmF64Ceil, ssa.OpWasmF64Floor, ssa.OpWasmF64Nearest, ssa.OpWasmF64Abs,
468 ssa.OpWasmI64Ctz, ssa.OpWasmI64Clz, ssa.OpWasmI64Popcnt:
469 getValue64(s, v.Args[0])
470 s.Prog(v.Op.Asm())
471
472 case ssa.OpWasmV128Zero:
473 p := s.Prog(wasm.AV128Const)
474 p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
475 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
476
477 case ssa.OpLoadReg:
478 p := s.Prog(loadOp(v.Type))
479 ssagen.AddrAuto(&p.From, v.Args[0])
480
481 case ssa.OpCopy:
482 if v.Type.Size() == 16 {
483 getValue128(s, v.Args[0])
484 } else {
485 getValue64(s, v.Args[0])
486 }
487
488 default:
489 if !ssaGenSIMDValue(s, v, extend) {
490 v.Fatalf("unexpected op: %s", v.Op)
491 }
492
493 }
494 }
495
496 func isAlready32(v *ssa.Value) bool {
497 switch v.Op {
498 case ssa.OpWasmI64Eqz, ssa.OpWasmI64Eq, ssa.OpWasmI64Ne, ssa.OpWasmI64LtS, ssa.OpWasmI64LtU, ssa.OpWasmI64GtS, ssa.OpWasmI64GtU, ssa.OpWasmI64LeS, ssa.OpWasmI64LeU, ssa.OpWasmI64GeS, ssa.OpWasmI64GeU,
499 ssa.OpWasmF32Eq, ssa.OpWasmF32Ne, ssa.OpWasmF32Lt, ssa.OpWasmF32Gt, ssa.OpWasmF32Le, ssa.OpWasmF32Ge,
500 ssa.OpWasmF64Eq, ssa.OpWasmF64Ne, ssa.OpWasmF64Lt, ssa.OpWasmF64Gt, ssa.OpWasmF64Le, ssa.OpWasmF64Ge,
501 ssa.OpWasmI8x16ExtractLaneS, ssa.OpWasmI16x8ExtractLaneS, ssa.OpWasmI32x4ExtractLane,
502 ssa.OpWasmI8x16ExtractLaneU, ssa.OpWasmI16x8ExtractLaneU:
503 return true
504 default:
505 return false
506 }
507 }
508
509 func getValue32(s *ssagen.State, v *ssa.Value) {
510 if v.OnWasmStack {
511 s.OnWasmStackSkipped--
512 ssaGenValueOnStack(s, v, false)
513 if !isAlready32(v) {
514 s.Prog(wasm.AI32WrapI64)
515 }
516 return
517 }
518
519 reg := v.Reg()
520 getReg(s, reg)
521 if reg != wasm.REG_SP {
522 s.Prog(wasm.AI32WrapI64)
523 }
524 }
525
526 func getValue64(s *ssagen.State, v *ssa.Value) {
527 if v.OnWasmStack {
528 s.OnWasmStackSkipped--
529 ssaGenValueOnStack(s, v, true)
530 return
531 }
532
533 reg := v.Reg()
534 getReg(s, reg)
535 if reg == wasm.REG_SP {
536 s.Prog(wasm.AI64ExtendI32U)
537 }
538 }
539
540 func getValue128(s *ssagen.State, v *ssa.Value) {
541 if v.OnWasmStack {
542 s.OnWasmStackSkipped--
543 ssaGenValueOnStack(s, v, true)
544 return
545 }
546
547 reg := v.Reg()
548 getReg(s, reg)
549 }
550
551 func getValueFxx(s *ssagen.State, v *ssa.Value) {
552 if v.OnWasmStack {
553 s.OnWasmStackSkipped--
554 ssaGenValueOnStack(s, v, true)
555 return
556 }
557
558 reg := v.Reg()
559 getReg(s, reg)
560 }
561
562 func i32Const(s *ssagen.State, val int32) {
563 p := s.Prog(wasm.AI32Const)
564 p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(val)}
565 }
566
567 func i64Const(s *ssagen.State, val int64) {
568 p := s.Prog(wasm.AI64Const)
569 p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: val}
570 }
571
572 func f32Const(s *ssagen.State, val float64) {
573 p := s.Prog(wasm.AF32Const)
574 p.From = obj.Addr{Type: obj.TYPE_FCONST, Val: val}
575 }
576
577 func f64Const(s *ssagen.State, val float64) {
578 p := s.Prog(wasm.AF64Const)
579 p.From = obj.Addr{Type: obj.TYPE_FCONST, Val: val}
580 }
581
582 func getReg(s *ssagen.State, reg int16) {
583 p := s.Prog(wasm.AGet)
584 p.From = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
585 }
586
587 func setReg(s *ssagen.State, reg int16) {
588 p := s.Prog(wasm.ASet)
589 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
590 }
591
592 func loadOp(t *types.Type) obj.As {
593 if t.IsFloat() {
594 switch t.Size() {
595 case 4:
596 return wasm.AF32Load
597 case 8:
598 return wasm.AF64Load
599 default:
600 panic("bad load type")
601 }
602 }
603
604 switch t.Size() {
605 case 1:
606 if t.IsSigned() {
607 return wasm.AI64Load8S
608 }
609 return wasm.AI64Load8U
610 case 2:
611 if t.IsSigned() {
612 return wasm.AI64Load16S
613 }
614 return wasm.AI64Load16U
615 case 4:
616 if t.IsSigned() {
617 return wasm.AI64Load32S
618 }
619 return wasm.AI64Load32U
620 case 8:
621 return wasm.AI64Load
622 case 16:
623 return wasm.AV128Load
624 default:
625 panic("bad load type")
626 }
627 }
628
629 func storeOp(t *types.Type) obj.As {
630 if t.IsFloat() {
631 switch t.Size() {
632 case 4:
633 return wasm.AF32Store
634 case 8:
635 return wasm.AF64Store
636 default:
637 panic("bad store type")
638 }
639 }
640
641 switch t.Size() {
642 case 1:
643 return wasm.AI64Store8
644 case 2:
645 return wasm.AI64Store16
646 case 4:
647 return wasm.AI64Store32
648 case 8:
649 return wasm.AI64Store
650 case 16:
651 return wasm.AV128Store
652 default:
653 panic("bad store type")
654 }
655 }
656
View as plain text