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