]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/wasm/ssa.go
internal/buildcfg: move build configuration out of cmd/internal/objabi
[gostls13.git] / src / cmd / compile / internal / wasm / ssa.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
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 func Init(arch *ssagen.ArchInfo) {
21         arch.LinkArch = &wasm.Linkwasm
22         arch.REGSP = wasm.REG_SP
23         arch.MAXWIDTH = 1 << 50
24
25         arch.ZeroRange = zeroRange
26         arch.Ginsnop = ginsnop
27         arch.Ginsnopdefer = ginsnop
28
29         arch.SSAMarkMoves = ssaMarkMoves
30         arch.SSAGenValue = ssaGenValue
31         arch.SSAGenBlock = ssaGenBlock
32 }
33
34 func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
35         if cnt == 0 {
36                 return p
37         }
38         if cnt%8 != 0 {
39                 base.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
40         }
41
42         for i := int64(0); i < cnt; i += 8 {
43                 p = pp.Append(p, wasm.AGet, obj.TYPE_REG, wasm.REG_SP, 0, 0, 0, 0)
44                 p = pp.Append(p, wasm.AI64Const, obj.TYPE_CONST, 0, 0, 0, 0, 0)
45                 p = pp.Append(p, wasm.AI64Store, 0, 0, 0, obj.TYPE_CONST, 0, off+i)
46         }
47
48         return p
49 }
50
51 func ginsnop(pp *objw.Progs) *obj.Prog {
52         return pp.Prog(wasm.ANop)
53 }
54
55 func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
56 }
57
58 func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
59         switch b.Kind {
60         case ssa.BlockPlain:
61                 if next != b.Succs[0].Block() {
62                         s.Br(obj.AJMP, b.Succs[0].Block())
63                 }
64
65         case ssa.BlockIf:
66                 switch next {
67                 case b.Succs[0].Block():
68                         // if false, jump to b.Succs[1]
69                         getValue32(s, b.Controls[0])
70                         s.Prog(wasm.AI32Eqz)
71                         s.Prog(wasm.AIf)
72                         s.Br(obj.AJMP, b.Succs[1].Block())
73                         s.Prog(wasm.AEnd)
74                 case b.Succs[1].Block():
75                         // if true, jump to b.Succs[0]
76                         getValue32(s, b.Controls[0])
77                         s.Prog(wasm.AIf)
78                         s.Br(obj.AJMP, b.Succs[0].Block())
79                         s.Prog(wasm.AEnd)
80                 default:
81                         // if true, jump to b.Succs[0], else jump to b.Succs[1]
82                         getValue32(s, b.Controls[0])
83                         s.Prog(wasm.AIf)
84                         s.Br(obj.AJMP, b.Succs[0].Block())
85                         s.Prog(wasm.AEnd)
86                         s.Br(obj.AJMP, b.Succs[1].Block())
87                 }
88
89         case ssa.BlockRet:
90                 s.Prog(obj.ARET)
91
92         case ssa.BlockRetJmp:
93                 p := s.Prog(obj.ARET)
94                 p.To.Type = obj.TYPE_MEM
95                 p.To.Name = obj.NAME_EXTERN
96                 p.To.Sym = b.Aux.(*obj.LSym)
97
98         case ssa.BlockExit:
99
100         case ssa.BlockDefer:
101                 p := s.Prog(wasm.AGet)
102                 p.From = obj.Addr{Type: obj.TYPE_REG, Reg: wasm.REG_RET0}
103                 s.Prog(wasm.AI64Eqz)
104                 s.Prog(wasm.AI32Eqz)
105                 s.Prog(wasm.AIf)
106                 s.Br(obj.AJMP, b.Succs[1].Block())
107                 s.Prog(wasm.AEnd)
108                 if next != b.Succs[0].Block() {
109                         s.Br(obj.AJMP, b.Succs[0].Block())
110                 }
111
112         default:
113                 panic("unexpected block")
114         }
115
116         // Entry point for the next block. Used by the JMP in goToBlock.
117         s.Prog(wasm.ARESUMEPOINT)
118
119         if s.OnWasmStackSkipped != 0 {
120                 panic("wasm: bad stack")
121         }
122 }
123
124 func ssaGenValue(s *ssagen.State, v *ssa.Value) {
125         switch v.Op {
126         case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall:
127                 s.PrepareCall(v)
128                 if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == ir.Syms.Deferreturn {
129                         // add a resume point before call to deferreturn so it can be called again via jmpdefer
130                         s.Prog(wasm.ARESUMEPOINT)
131                 }
132                 if v.Op == ssa.OpWasmLoweredClosureCall {
133                         getValue64(s, v.Args[1])
134                         setReg(s, wasm.REG_CTXT)
135                 }
136                 if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn != nil {
137                         sym := call.Fn
138                         p := s.Prog(obj.ACALL)
139                         p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
140                         p.Pos = v.Pos
141                 } else {
142                         getValue64(s, v.Args[0])
143                         p := s.Prog(obj.ACALL)
144                         p.To = obj.Addr{Type: obj.TYPE_NONE}
145                         p.Pos = v.Pos
146                 }
147
148         case ssa.OpWasmLoweredMove:
149                 getValue32(s, v.Args[0])
150                 getValue32(s, v.Args[1])
151                 i32Const(s, int32(v.AuxInt))
152                 p := s.Prog(wasm.ACall)
153                 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmMove}
154
155         case ssa.OpWasmLoweredZero:
156                 getValue32(s, v.Args[0])
157                 i32Const(s, int32(v.AuxInt))
158                 p := s.Prog(wasm.ACall)
159                 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmZero}
160
161         case ssa.OpWasmLoweredNilCheck:
162                 getValue64(s, v.Args[0])
163                 s.Prog(wasm.AI64Eqz)
164                 s.Prog(wasm.AIf)
165                 p := s.Prog(wasm.ACALLNORESUME)
166                 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.SigPanic}
167                 s.Prog(wasm.AEnd)
168                 if logopt.Enabled() {
169                         logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
170                 }
171                 if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
172                         base.WarnfAt(v.Pos, "generated nil check")
173                 }
174
175         case ssa.OpWasmLoweredWB:
176                 getValue64(s, v.Args[0])
177                 getValue64(s, v.Args[1])
178                 p := s.Prog(wasm.ACALLNORESUME) // TODO(neelance): If possible, turn this into a simple wasm.ACall).
179                 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: v.Aux.(*obj.LSym)}
180
181         case ssa.OpWasmI64Store8, ssa.OpWasmI64Store16, ssa.OpWasmI64Store32, ssa.OpWasmI64Store, ssa.OpWasmF32Store, ssa.OpWasmF64Store:
182                 getValue32(s, v.Args[0])
183                 getValue64(s, v.Args[1])
184                 p := s.Prog(v.Op.Asm())
185                 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
186
187         case ssa.OpStoreReg:
188                 getReg(s, wasm.REG_SP)
189                 getValue64(s, v.Args[0])
190                 p := s.Prog(storeOp(v.Type))
191                 ssagen.AddrAuto(&p.To, v)
192
193         case ssa.OpClobber, ssa.OpClobberReg:
194                 // TODO: implement for clobberdead experiment. Nop is ok for now.
195
196         default:
197                 if v.Type.IsMemory() {
198                         return
199                 }
200                 if v.OnWasmStack {
201                         s.OnWasmStackSkipped++
202                         // If a Value is marked OnWasmStack, we don't generate the value and store it to a register now.
203                         // Instead, we delay the generation to when the value is used and then directly generate it on the WebAssembly stack.
204                         return
205                 }
206                 ssaGenValueOnStack(s, v, true)
207                 if s.OnWasmStackSkipped != 0 {
208                         panic("wasm: bad stack")
209                 }
210                 setReg(s, v.Reg())
211         }
212 }
213
214 func ssaGenValueOnStack(s *ssagen.State, v *ssa.Value, extend bool) {
215         switch v.Op {
216         case ssa.OpWasmLoweredGetClosurePtr:
217                 getReg(s, wasm.REG_CTXT)
218
219         case ssa.OpWasmLoweredGetCallerPC:
220                 p := s.Prog(wasm.AI64Load)
221                 // Caller PC is stored 8 bytes below first parameter.
222                 p.From = obj.Addr{
223                         Type:   obj.TYPE_MEM,
224                         Name:   obj.NAME_PARAM,
225                         Offset: -8,
226                 }
227
228         case ssa.OpWasmLoweredGetCallerSP:
229                 p := s.Prog(wasm.AGet)
230                 // Caller SP is the address of the first parameter.
231                 p.From = obj.Addr{
232                         Type:   obj.TYPE_ADDR,
233                         Name:   obj.NAME_PARAM,
234                         Reg:    wasm.REG_SP,
235                         Offset: 0,
236                 }
237
238         case ssa.OpWasmLoweredAddr:
239                 if v.Aux == nil { // address of off(SP), no symbol
240                         getValue64(s, v.Args[0])
241                         i64Const(s, v.AuxInt)
242                         s.Prog(wasm.AI64Add)
243                         break
244                 }
245                 p := s.Prog(wasm.AGet)
246                 p.From.Type = obj.TYPE_ADDR
247                 switch v.Aux.(type) {
248                 case *obj.LSym:
249                         ssagen.AddAux(&p.From, v)
250                 case *ir.Name:
251                         p.From.Reg = v.Args[0].Reg()
252                         ssagen.AddAux(&p.From, v)
253                 default:
254                         panic("wasm: bad LoweredAddr")
255                 }
256
257         case ssa.OpWasmLoweredConvert:
258                 getValue64(s, v.Args[0])
259
260         case ssa.OpWasmSelect:
261                 getValue64(s, v.Args[0])
262                 getValue64(s, v.Args[1])
263                 getValue32(s, v.Args[2])
264                 s.Prog(v.Op.Asm())
265
266         case ssa.OpWasmI64AddConst:
267                 getValue64(s, v.Args[0])
268                 i64Const(s, v.AuxInt)
269                 s.Prog(v.Op.Asm())
270
271         case ssa.OpWasmI64Const:
272                 i64Const(s, v.AuxInt)
273
274         case ssa.OpWasmF32Const:
275                 f32Const(s, v.AuxFloat())
276
277         case ssa.OpWasmF64Const:
278                 f64Const(s, v.AuxFloat())
279
280         case ssa.OpWasmI64Load8U, ssa.OpWasmI64Load8S, ssa.OpWasmI64Load16U, ssa.OpWasmI64Load16S, ssa.OpWasmI64Load32U, ssa.OpWasmI64Load32S, ssa.OpWasmI64Load, ssa.OpWasmF32Load, ssa.OpWasmF64Load:
281                 getValue32(s, v.Args[0])
282                 p := s.Prog(v.Op.Asm())
283                 p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
284
285         case ssa.OpWasmI64Eqz:
286                 getValue64(s, v.Args[0])
287                 s.Prog(v.Op.Asm())
288                 if extend {
289                         s.Prog(wasm.AI64ExtendI32U)
290                 }
291
292         case ssa.OpWasmI64Eq, ssa.OpWasmI64Ne, ssa.OpWasmI64LtS, ssa.OpWasmI64LtU, ssa.OpWasmI64GtS, ssa.OpWasmI64GtU, ssa.OpWasmI64LeS, ssa.OpWasmI64LeU, ssa.OpWasmI64GeS, ssa.OpWasmI64GeU,
293                 ssa.OpWasmF32Eq, ssa.OpWasmF32Ne, ssa.OpWasmF32Lt, ssa.OpWasmF32Gt, ssa.OpWasmF32Le, ssa.OpWasmF32Ge,
294                 ssa.OpWasmF64Eq, ssa.OpWasmF64Ne, ssa.OpWasmF64Lt, ssa.OpWasmF64Gt, ssa.OpWasmF64Le, ssa.OpWasmF64Ge:
295                 getValue64(s, v.Args[0])
296                 getValue64(s, v.Args[1])
297                 s.Prog(v.Op.Asm())
298                 if extend {
299                         s.Prog(wasm.AI64ExtendI32U)
300                 }
301
302         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,
303                 ssa.OpWasmF32Add, ssa.OpWasmF32Sub, ssa.OpWasmF32Mul, ssa.OpWasmF32Div, ssa.OpWasmF32Copysign,
304                 ssa.OpWasmF64Add, ssa.OpWasmF64Sub, ssa.OpWasmF64Mul, ssa.OpWasmF64Div, ssa.OpWasmF64Copysign:
305                 getValue64(s, v.Args[0])
306                 getValue64(s, v.Args[1])
307                 s.Prog(v.Op.Asm())
308
309         case ssa.OpWasmI32Rotl:
310                 getValue32(s, v.Args[0])
311                 getValue32(s, v.Args[1])
312                 s.Prog(wasm.AI32Rotl)
313                 s.Prog(wasm.AI64ExtendI32U)
314
315         case ssa.OpWasmI64DivS:
316                 getValue64(s, v.Args[0])
317                 getValue64(s, v.Args[1])
318                 if v.Type.Size() == 8 {
319                         // Division of int64 needs helper function wasmDiv to handle the MinInt64 / -1 case.
320                         p := s.Prog(wasm.ACall)
321                         p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmDiv}
322                         break
323                 }
324                 s.Prog(wasm.AI64DivS)
325
326         case ssa.OpWasmI64TruncSatF32S, ssa.OpWasmI64TruncSatF64S:
327                 getValue64(s, v.Args[0])
328                 if buildcfg.GOWASM.SatConv {
329                         s.Prog(v.Op.Asm())
330                 } else {
331                         if v.Op == ssa.OpWasmI64TruncSatF32S {
332                                 s.Prog(wasm.AF64PromoteF32)
333                         }
334                         p := s.Prog(wasm.ACall)
335                         p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncS}
336                 }
337
338         case ssa.OpWasmI64TruncSatF32U, ssa.OpWasmI64TruncSatF64U:
339                 getValue64(s, v.Args[0])
340                 if buildcfg.GOWASM.SatConv {
341                         s.Prog(v.Op.Asm())
342                 } else {
343                         if v.Op == ssa.OpWasmI64TruncSatF32U {
344                                 s.Prog(wasm.AF64PromoteF32)
345                         }
346                         p := s.Prog(wasm.ACall)
347                         p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncU}
348                 }
349
350         case ssa.OpWasmF32DemoteF64:
351                 getValue64(s, v.Args[0])
352                 s.Prog(v.Op.Asm())
353
354         case ssa.OpWasmF64PromoteF32:
355                 getValue64(s, v.Args[0])
356                 s.Prog(v.Op.Asm())
357
358         case ssa.OpWasmF32ConvertI64S, ssa.OpWasmF32ConvertI64U,
359                 ssa.OpWasmF64ConvertI64S, ssa.OpWasmF64ConvertI64U,
360                 ssa.OpWasmI64Extend8S, ssa.OpWasmI64Extend16S, ssa.OpWasmI64Extend32S,
361                 ssa.OpWasmF32Neg, ssa.OpWasmF32Sqrt, ssa.OpWasmF32Trunc, ssa.OpWasmF32Ceil, ssa.OpWasmF32Floor, ssa.OpWasmF32Nearest, ssa.OpWasmF32Abs,
362                 ssa.OpWasmF64Neg, ssa.OpWasmF64Sqrt, ssa.OpWasmF64Trunc, ssa.OpWasmF64Ceil, ssa.OpWasmF64Floor, ssa.OpWasmF64Nearest, ssa.OpWasmF64Abs,
363                 ssa.OpWasmI64Ctz, ssa.OpWasmI64Clz, ssa.OpWasmI64Popcnt:
364                 getValue64(s, v.Args[0])
365                 s.Prog(v.Op.Asm())
366
367         case ssa.OpLoadReg:
368                 p := s.Prog(loadOp(v.Type))
369                 ssagen.AddrAuto(&p.From, v.Args[0])
370
371         case ssa.OpCopy:
372                 getValue64(s, v.Args[0])
373
374         default:
375                 v.Fatalf("unexpected op: %s", v.Op)
376
377         }
378 }
379
380 func isCmp(v *ssa.Value) bool {
381         switch v.Op {
382         case ssa.OpWasmI64Eqz, ssa.OpWasmI64Eq, ssa.OpWasmI64Ne, ssa.OpWasmI64LtS, ssa.OpWasmI64LtU, ssa.OpWasmI64GtS, ssa.OpWasmI64GtU, ssa.OpWasmI64LeS, ssa.OpWasmI64LeU, ssa.OpWasmI64GeS, ssa.OpWasmI64GeU,
383                 ssa.OpWasmF32Eq, ssa.OpWasmF32Ne, ssa.OpWasmF32Lt, ssa.OpWasmF32Gt, ssa.OpWasmF32Le, ssa.OpWasmF32Ge,
384                 ssa.OpWasmF64Eq, ssa.OpWasmF64Ne, ssa.OpWasmF64Lt, ssa.OpWasmF64Gt, ssa.OpWasmF64Le, ssa.OpWasmF64Ge:
385                 return true
386         default:
387                 return false
388         }
389 }
390
391 func getValue32(s *ssagen.State, v *ssa.Value) {
392         if v.OnWasmStack {
393                 s.OnWasmStackSkipped--
394                 ssaGenValueOnStack(s, v, false)
395                 if !isCmp(v) {
396                         s.Prog(wasm.AI32WrapI64)
397                 }
398                 return
399         }
400
401         reg := v.Reg()
402         getReg(s, reg)
403         if reg != wasm.REG_SP {
404                 s.Prog(wasm.AI32WrapI64)
405         }
406 }
407
408 func getValue64(s *ssagen.State, v *ssa.Value) {
409         if v.OnWasmStack {
410                 s.OnWasmStackSkipped--
411                 ssaGenValueOnStack(s, v, true)
412                 return
413         }
414
415         reg := v.Reg()
416         getReg(s, reg)
417         if reg == wasm.REG_SP {
418                 s.Prog(wasm.AI64ExtendI32U)
419         }
420 }
421
422 func i32Const(s *ssagen.State, val int32) {
423         p := s.Prog(wasm.AI32Const)
424         p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(val)}
425 }
426
427 func i64Const(s *ssagen.State, val int64) {
428         p := s.Prog(wasm.AI64Const)
429         p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: val}
430 }
431
432 func f32Const(s *ssagen.State, val float64) {
433         p := s.Prog(wasm.AF32Const)
434         p.From = obj.Addr{Type: obj.TYPE_FCONST, Val: val}
435 }
436
437 func f64Const(s *ssagen.State, val float64) {
438         p := s.Prog(wasm.AF64Const)
439         p.From = obj.Addr{Type: obj.TYPE_FCONST, Val: val}
440 }
441
442 func getReg(s *ssagen.State, reg int16) {
443         p := s.Prog(wasm.AGet)
444         p.From = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
445 }
446
447 func setReg(s *ssagen.State, reg int16) {
448         p := s.Prog(wasm.ASet)
449         p.To = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
450 }
451
452 func loadOp(t *types.Type) obj.As {
453         if t.IsFloat() {
454                 switch t.Size() {
455                 case 4:
456                         return wasm.AF32Load
457                 case 8:
458                         return wasm.AF64Load
459                 default:
460                         panic("bad load type")
461                 }
462         }
463
464         switch t.Size() {
465         case 1:
466                 if t.IsSigned() {
467                         return wasm.AI64Load8S
468                 }
469                 return wasm.AI64Load8U
470         case 2:
471                 if t.IsSigned() {
472                         return wasm.AI64Load16S
473                 }
474                 return wasm.AI64Load16U
475         case 4:
476                 if t.IsSigned() {
477                         return wasm.AI64Load32S
478                 }
479                 return wasm.AI64Load32U
480         case 8:
481                 return wasm.AI64Load
482         default:
483                 panic("bad load type")
484         }
485 }
486
487 func storeOp(t *types.Type) obj.As {
488         if t.IsFloat() {
489                 switch t.Size() {
490                 case 4:
491                         return wasm.AF32Store
492                 case 8:
493                         return wasm.AF64Store
494                 default:
495                         panic("bad store type")
496                 }
497         }
498
499         switch t.Size() {
500         case 1:
501                 return wasm.AI64Store8
502         case 2:
503                 return wasm.AI64Store16
504         case 4:
505                 return wasm.AI64Store32
506         case 8:
507                 return wasm.AI64Store
508         default:
509                 panic("bad store type")
510         }
511 }