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