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