ssa_pkg_builder.mx raw
1 package ssa
2
3 import (
4 "git.smesh.lol/moxie/pkg/token"
5 "git.smesh.lol/moxie/pkg/syntax"
6 "git.smesh.lol/moxie/pkg/types"
7 )
8
9 func asType(t types.Type) (tv types.Type) { return t }
10
11 // CreatePackage builds the SSA Package from type-checked syntax files.
12 func (prog *SSAProgram) CreatePackage(pkg *types.TCPackage, files []*syntax.File, info *types.Info) (s *SSAPackage) {
13 p := &SSAPackage{
14 Prog: prog,
15 Pkg: pkg,
16 Members: map[string]SSAMember{},
17 }
18 prog.packages[pkg] = p
19 prog.imported[pkg.Path()] = p
20
21 pb := &ssaPkgBuilder{pkg: p, info: info, prog: prog}
22
23 for _, f := range files {
24 pb.registerImportsAndTypes(f)
25 }
26 for _, f := range files {
27 pb.registerFile(f)
28 }
29 for _, f := range files {
30 pb.buildFile(f)
31 }
32 if len(pb.varInits) > 0 {
33 pb.buildVarInits()
34 }
35 return p
36 }
37
38 // ssaPkgBuilder builds one SSAPackage.
39 type ssaPkgBuilder struct {
40 pkg *SSAPackage
41 info *types.Info
42 prog *SSAProgram
43 varInits []*syntax.VarDecl
44 }
45
46 func (pb *ssaPkgBuilder) registerImportsAndTypes(f *syntax.File) {
47 for _, d := range f.DeclList {
48 switch d := d.(type) {
49 case *syntax.ImportDecl:
50 pb.registerImport(d)
51 case *types.TypeDecl:
52 pb.registerType(d)
53 }
54 }
55 }
56
57 func (pb *ssaPkgBuilder) registerFile(f *syntax.File) {
58 for _, d := range f.DeclList {
59 switch d := d.(type) {
60 case *syntax.FuncDecl:
61 pb.registerFunc(d)
62 case *syntax.VarDecl:
63 pb.registerVar(d)
64 case *syntax.ConstDecl:
65 pb.registerConst(d)
66 }
67 }
68 }
69
70 func (pb *ssaPkgBuilder) registerImport(d *syntax.ImportDecl) {
71 if d.Path == nil {
72 return
73 }
74 path := d.Path.Value
75 if len(path) >= 2 && path[0] == '"' {
76 path = path[1 : len(path)-1]
77 }
78 localName := ""
79 if d.LocalPkgName != nil {
80 localName = d.LocalPkgName.Value
81 }
82 if localName == "" {
83 for i := len(path) - 1; i >= 0; i-- {
84 if path[i] == '/' {
85 localName = path[i+1:]
86 break
87 }
88 }
89 if localName == "" {
90 localName = path
91 }
92 }
93 if localName == "." || localName == "_" {
94 return
95 }
96 existing := pb.pkg.Pkg.Scope().Lookup(localName)
97 if existing != nil {
98 return
99 }
100 impPkg := pb.prog.ImportedPackage(path)
101 if impPkg != nil && impPkg.Pkg != nil {
102 pn := types.NewPkgName(nil, localName, impPkg.Pkg)
103 pb.pkg.Pkg.Scope().Insert(pn)
104 } else {
105 ensureImportRegistry()
106 regPkg := importRegistry[path]
107 if regPkg != nil {
108 pn := types.NewPkgName(nil, localName, regPkg)
109 pb.pkg.Pkg.Scope().Insert(pn)
110 }
111 }
112 }
113
114 func pkgResolveTypeAST(pkg *SSAPackage, e syntax.Expr) (t types.Type) {
115 if e == nil {
116 return nil
117 }
118 switch e := e.(type) {
119 case *syntax.Name:
120 if _, obj := types.Universe.LookupParent(e.Value); obj != nil {
121 if tn, ok := obj.(*types.TypeName); ok {
122 return tn.Type()
123 }
124 }
125 if pkg != nil {
126 obj := pkg.Pkg.Scope().Lookup(e.Value)
127 if tn, ok := obj.(*types.TypeName); ok {
128 return tn.Type()
129 }
130 }
131 case *syntax.Operation:
132 if e.Y == nil && e.Op == token.Mul {
133 base := pkgResolveTypeAST(pkg, e.X)
134 if base != nil {
135 return types.NewPointer(base)
136 }
137 }
138 case *syntax.ArrayType:
139 elem := pkgResolveTypeAST(pkg, e.Elem)
140 if elem != nil {
141 n := int64(-1)
142 if lit, ok := e.Len.(*syntax.BasicLit); ok {
143 n = ssaParseInt64(lit.Value)
144 }
145 return types.NewArray(elem, n)
146 }
147 case *syntax.SliceType:
148 elem := pkgResolveTypeAST(pkg, e.Elem)
149 if elem != nil {
150 if b, ok := elem.(*types.Basic); ok && b.Kind() == types.Uint8 {
151 return types.Typ[types.TCString]
152 }
153 return types.NewSlice(elem)
154 }
155 case *syntax.MapType:
156 key := pkgResolveTypeAST(pkg, e.Key)
157 val := pkgResolveTypeAST(pkg, e.Value)
158 if key != nil && val != nil {
159 return types.NewTCMap(key, val)
160 }
161 case *syntax.FuncType:
162 return pkgResolveSigAST(pkg, e)
163 case *syntax.SelectorExpr:
164 if x, ok := e.X.(*syntax.Name); ok {
165 pn, _ := pkg.Pkg.Scope().Lookup(x.Value).(*types.PkgName)
166 if pn != nil && pn.Imported() != nil {
167 sel := pn.Imported().Scope().Lookup(e.Sel.Value)
168 if tn, ok2 := sel.(*types.TypeName); ok2 {
169 return tn.Type()
170 }
171 }
172 }
173 case *syntax.InterfaceType:
174 return types.NewTCInterface(nil, nil)
175 case *syntax.DotsType:
176 elem := pkgResolveTypeAST(pkg, e.Elem)
177 if elem != nil {
178 if b, ok := elem.(*types.Basic); ok && b.Kind() == types.Uint8 {
179 return types.Typ[types.TCString]
180 }
181 return types.NewSlice(elem)
182 }
183 }
184 return nil
185 }
186
187 func pkgResolveSigAST(pkg *SSAPackage, ft *syntax.FuncType) (s *types.Signature) {
188 if ft == nil {
189 return nil
190 }
191 var params []*types.TCVar
192 for _, p := range ft.ParamList {
193 typ := pkgResolveTypeAST(pkg, p.Type)
194 pname := ""
195 if p.Name != nil {
196 pname = p.Name.Value
197 }
198 params = append(params, types.NewTCVar(nil, pname, typ))
199 }
200 var results []*types.TCVar
201 for _, r := range ft.ResultList {
202 typ := pkgResolveTypeAST(pkg, r.Type)
203 rname := ""
204 if r.Name != nil {
205 rname = r.Name.Value
206 }
207 results = append(results, types.NewTCVar(nil, rname, typ))
208 }
209 variadic := false
210 if len(ft.ParamList) > 0 {
211 if _, ok := ft.ParamList[len(ft.ParamList)-1].Type.(*syntax.DotsType); ok {
212 variadic = true
213 }
214 }
215 var pTuple, rTuple *types.Tuple
216 if len(params) > 0 {
217 pTuple = types.NewTuple(params...)
218 }
219 if len(results) > 0 {
220 rTuple = types.NewTuple(results...)
221 }
222 return types.NewSignature(nil, pTuple, rTuple, variadic)
223 }
224
225 func (pb *ssaPkgBuilder) registerFunc(d *syntax.FuncDecl) {
226 if len(d.TParamList) > 0 {
227 return
228 }
229 if d.Recv != nil {
230 pb.registerMethod(d)
231 return
232 }
233 if d.Name.Value == "init" {
234 writeStr(2, "compile error: init() is not allowed; use main() for package initialization\n")
235 return
236 }
237 var obj *types.TCFunc
238 if o := pb.pkg.Pkg.Scope().Lookup(d.Name.Value); o != nil {
239 obj, _ = o.(*types.TCFunc)
240 }
241 var sig *types.Signature
242 if obj != nil {
243 sig, _ = obj.Type().(*types.Signature)
244 }
245 if sig == nil && d.Type != nil {
246 sig = pkgResolveSigAST(pb.pkg, d.Type)
247 }
248 fn := &SSAFunction{
249 name: d.Name.Value,
250 object: obj,
251 Signature: sig,
252 pos: 0,
253 Pkg: pb.pkg,
254 Prog: pb.prog,
255 }
256 if CompileExportMap != nil {
257 if ename, ok := CompileExportMap[d.Name.Value]; ok {
258 fn.externalSymbol = ename
259 fn.isExternC = true
260 }
261 }
262 if d.Body == nil && isNoContextExtern(pb.pkg.Pkg.Path(), d.Name.Value) {
263 fn.isExternC = true
264 }
265 pb.pkg.Members[fn.name] = fn
266 }
267
268 func checkInitBody(body *syntax.BlockStmt) (s string) {
269 for _, s := range body.List {
270 if err := checkInitStmt(s); err != "" {
271 return err
272 }
273 }
274 return ""
275 }
276
277 func checkInitStmt(s syntax.Stmt) (sv string) {
278 switch s := s.(type) {
279 case *syntax.SelectStmt:
280 return "select{} not allowed in init()"
281 case *syntax.BlockStmt:
282 for _, sub := range s.List {
283 if err := checkInitStmt(sub); err != "" {
284 return err
285 }
286 }
287 case *syntax.IfStmt:
288 if s.Then != nil {
289 if err := checkInitStmt(s.Then); err != "" {
290 return err
291 }
292 }
293 if s.Else != nil {
294 if err := checkInitStmt(s.Else); err != "" {
295 return err
296 }
297 }
298 case *syntax.ForStmt:
299 if s.Body != nil {
300 if err := checkInitStmt(s.Body); err != "" {
301 return err
302 }
303 }
304 case *syntax.SwitchStmt:
305 for _, cc := range s.Body {
306 for _, sub := range cc.Body {
307 if err := checkInitStmt(sub); err != "" {
308 return err
309 }
310 }
311 }
312 case *syntax.ExprStmt:
313 if err := checkInitExpr(s.X); err != "" {
314 return err
315 }
316 }
317 return ""
318 }
319
320 func checkInitExpr(x syntax.Expr) (s string) {
321 if x == nil {
322 return ""
323 }
324 if c, ok := x.(*syntax.CallExpr); ok {
325 if n, ok2 := c.Fun.(*syntax.Name); ok2 && n.Value == "spawn" {
326 return "spawn() not allowed in init()"
327 }
328 }
329 return ""
330 }
331
332 func isNoContextExtern(pkg, name string) (ok bool) {
333 if pkg == "internal/runtime/syscall" && name == "Syscall6" {
334 return true
335 }
336 if pkg == "syscall" {
337 switch name {
338 case "runtime_entersyscall", "runtime_exitsyscall",
339 "Exit", "Getpagesize", "gettimeofday",
340 "runtime_BeforeExec", "runtime_AfterExec",
341 "runtime_BeforeFork", "runtime_AfterFork", "runtime_AfterForkInChild":
342 return true
343 }
344 }
345 if pkg == "os" {
346 switch name {
347 case "putchar", "getchar", "buffered", "gosched", "runtime_args":
348 return true
349 }
350 }
351 return false
352 }
353
354 func (pb *ssaPkgBuilder) registerMethod(d *syntax.FuncDecl) {
355 recvTypeName := ssaRecvTypeName(d.Recv)
356 if recvTypeName == "" {
357 return
358 }
359 mangledName := recvTypeName | "." | d.Name.Value
360 var obj *types.TCFunc
361 obj = pb.findMethod(recvTypeName, d.Name.Value)
362 var sig *types.Signature
363 if obj != nil {
364 sig, _ = obj.Type().(*types.Signature)
365 }
366 if sig == nil && d.Type != nil {
367 sig = pkgResolveSigAST(pb.pkg, d.Type)
368 }
369 fn := &SSAFunction{
370 name: mangledName,
371 object: obj,
372 Signature: sig,
373 pos: 0,
374 Pkg: pb.pkg,
375 Prog: pb.prog,
376 }
377 pb.pkg.Members[mangledName] = fn
378 }
379
380 func ssaRecvTypeName(recv *syntax.Field) (s string) {
381 if recv == nil {
382 return ""
383 }
384 switch t := recv.Type.(type) {
385 case *syntax.Name:
386 return t.Value
387 case *syntax.Operation:
388 if t.Y == nil && t.Op == token.Mul {
389 if n, ok := t.X.(*syntax.Name); ok {
390 return n.Value
391 }
392 }
393 }
394 return ""
395 }
396
397 func (pb *ssaPkgBuilder) findMethod(typeName string, methodName string) (t *types.TCFunc) {
398 obj := pb.pkg.Pkg.Scope().Lookup(typeName)
399 if obj == nil && types.Universe != nil {
400 obj = types.Universe.Lookup(typeName)
401 }
402 if obj == nil {
403 return nil
404 }
405 tn, ok := obj.(*types.TypeName)
406 if !ok {
407 return nil
408 }
409 switch t := tn.Type().(type) {
410 case *types.Named:
411 for i := int32(0); i < t.NumMethods(); i++ {
412 m := t.Method(i)
413 if m.Name() == methodName {
414 return m
415 }
416 }
417 case *types.Basic:
418 for i := int32(0); i < t.NumMethods(); i++ {
419 m := t.Method(i)
420 if m.Name() == methodName {
421 return m
422 }
423 }
424 }
425 return nil
426 }
427
428 func (pb *ssaPkgBuilder) registerVar(d *syntax.VarDecl) {
429 for _, name := range d.NameList {
430 var obj *types.TCVar
431 if o := pb.pkg.Pkg.Scope().Lookup(name.Value); o != nil {
432 obj, _ = o.(*types.TCVar)
433 }
434 var typ types.Type
435 if obj != nil {
436 typ = obj.Type()
437 }
438 if typ == nil && d.Type != nil {
439 typ = pkgResolveTypeAST(pb.pkg, d.Type)
440 }
441 if typ == nil && d.Values != nil {
442 typ = pkgResolveTypeAST(pb.pkg, d.Values)
443 }
444 g := &SSAGlobal{
445 name: name.Value,
446 object: obj,
447 typ: types.NewPointer(typ),
448 pos: 0,
449 pkg: pb.pkg,
450 }
451 pb.pkg.Members[name.Value] = g
452 }
453 if d.Values != nil {
454 pb.varInits = append(pb.varInits, d)
455 }
456 }
457
458 func (pb *ssaPkgBuilder) registerType(d *types.TypeDecl) {
459 var obj *types.TypeName
460 if o := pb.pkg.Pkg.Scope().Lookup(d.Name.Value); o != nil {
461 obj, _ = o.(*types.TypeName)
462 }
463 if obj == nil {
464 underlying := pkgResolveTypeAST(pb.pkg, d.Type)
465 if underlying == nil {
466 underlying = &types.TCStruct{}
467 }
468 named := types.NewNamed(types.NewTypeName(pb.pkg.Pkg, d.Name.Value, nil), underlying)
469 named.Obj().SetType(named)
470 pb.pkg.Pkg.Scope().Insert(named.Obj())
471 obj = named.Obj()
472 }
473 t := &SSAType_{object: obj, pkg: pb.pkg}
474 pb.pkg.Members[d.Name.Value] = t
475 }
476
477 func (pb *ssaPkgBuilder) registerConst(d *syntax.ConstDecl) {
478 for _, name := range d.NameList {
479 var obj *types.TCConst
480 if o := pb.pkg.Pkg.Scope().Lookup(name.Value); o != nil {
481 obj, _ = o.(*types.TCConst)
482 }
483 if obj == nil {
484 continue
485 }
486 c := &SSANamedConst{
487 object: obj,
488 Value: &SSAConst{typ: obj.Type(), val: localizeConstVal(obj.Val(), obj.Type())},
489 pkg: pb.pkg,
490 }
491 pb.pkg.Members[name.Value] = c
492 }
493 }
494
495 func (pb *ssaPkgBuilder) buildFile(f *syntax.File) {
496 for _, d := range f.DeclList {
497 if fd, ok := d.(*syntax.FuncDecl); ok {
498 pb.buildFunc(fd)
499 }
500 }
501 }
502
503 func (pb *ssaPkgBuilder) buildVarInits() {
504 sig := types.NewSignature(nil, types.NewTuple(), types.NewTuple(), false)
505 fn, _ := pb.pkg.Members["main"].(*SSAFunction)
506 if fn == nil {
507 fn = &SSAFunction{
508 name: "main",
509 Signature: sig,
510 Pkg: pb.pkg,
511 Prog: pb.prog,
512 }
513 pb.pkg.Members["main"] = fn
514 }
515 if len(fn.Blocks) > 0 {
516 initSig := types.NewSignature(nil, types.NewTuple(), types.NewTuple(), false)
517 initFn := &SSAFunction{
518 name: "__varinit",
519 Signature: initSig,
520 Pkg: pb.pkg,
521 Prog: pb.prog,
522 }
523 pb.pkg.Members["__varinit"] = initFn
524 fb := newSSAFuncBuilder(initFn, pb.info)
525 entry := fb.newBlock("entry")
526 fb.currentBlock = entry
527 for _, d := range pb.varInits {
528 if d.Values == nil {
529 continue
530 }
531 rhs := fb.buildExpr(d.Values)
532 if rhs == nil {
533 continue
534 }
535 for _, name := range d.NameList {
536 if name.Value == "_" {
537 continue
538 }
539 fb.buildStore(name, rhs)
540 }
541 }
542 fb.emit(&SSAReturn{})
543 // Inject call to __varinit at start of main
544 callInstr := &SSACall{Call: SSACallCommon{Value: initFn}}
545 callInstr.name = "vinit"
546 oldEntry := fn.Blocks[0]
547 n := int32(len(oldEntry.Instrs))
548 combined := []SSAInstruction{:n+1}
549 combined[0] = callInstr
550 for i := int32(0); i < n; i++ {
551 combined[i+1] = oldEntry.Instrs[i]
552 }
553 oldEntry.Instrs = combined
554 return
555 }
556 fb := newSSAFuncBuilder(fn, pb.info)
557 entry := fb.newBlock("entry")
558 fb.currentBlock = entry
559 for _, d := range pb.varInits {
560 if d.Values == nil {
561 continue
562 }
563 rhs := fb.buildExpr(d.Values)
564 if rhs == nil {
565 continue
566 }
567 for _, name := range d.NameList {
568 if name.Value == "_" {
569 continue
570 }
571 fb.buildStore(name, rhs)
572 }
573 }
574 fb.emit(&SSAReturn{})
575 }
576
577 func (pb *ssaPkgBuilder) buildFunc(d *syntax.FuncDecl) {
578 if len(d.TParamList) > 0 {
579 return
580 }
581 if d.Body == nil {
582 return
583 }
584 name := d.Name.Value
585 if d.Recv != nil {
586 rtn := ssaRecvTypeName(d.Recv)
587 if rtn != "" {
588 name = rtn | "." | d.Name.Value
589 }
590 }
591 fn, _ := pb.pkg.Members[name].(*SSAFunction)
592 if fn == nil {
593 return
594 }
595 fb := newSSAFuncBuilder(fn, pb.info)
596 fb.buildBody(d)
597 }
598
599 // ssaFuncBuilder builds one SSAFunction body.
600 type ssaFuncBuilder struct {
601 fn *SSAFunction
602 info *types.Info
603 parent *ssaFuncBuilder
604 currentBlock *SSABasicBlock
605 vars map[types.Object]*SSAAlloc
606 localTypes map[string]types.Type
607 localConsts map[string]*SSAConst
608 freeVarPtrs map[string]*SSAFreeVar
609 namedResults []*SSAAlloc
610 paramAllocas []*SSAAlloc
611 tailBlock *SSABasicBlock
612 counter int32
613
614 loops []ssaLoopState
615 pendingLabel string
616 labels map[string]*SSABasicBlock
617 deferred int32
618 typeSubst map[string]types.Type
619 srcScope *types.Scope
620 scopeID int32
621 nextScopeID int32
622 }
623
624 type ssaLoopState struct {
625 label string
626 body *SSABasicBlock
627 post *SSABasicBlock
628 done *SSABasicBlock
629 }
630