1
2
3
4
5 package specgen
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/token"
11 "go/types"
12 "simd/archsimd/_gen/specgen/specexpr"
13 "strings"
14
15 "golang.org/x/tools/go/packages"
16 )
17
18
19 type specPackage struct {
20 Fset *token.FileSet
21 Pkg *types.Package
22 TypesInfo *types.Info
23 Funcs []*specFunc
24
25 TypeElems map[types.Type]specexpr.Basic
26 TypeWidths map[types.Type]specexpr.Num
27
28 ElemTypes map[specexpr.Basic]types.Type
29 WidthTypes map[specexpr.Num]types.Type
30
31 VecType types.Type
32 ArrayType types.Type
33 UintNType types.Type
34 }
35
36
37 type specFunc struct {
38 Pkg *specPackage
39 Name string
40 NameTmpl specTemplate
41 Pos token.Pos
42 Doc specTemplate
43 Sig *types.Signature
44 TypeParams []*types.TypeParam
45 Params []*types.Var
46 Results []*types.Var
47 Requirements []specexpr.Expr
48 }
49
50
51
52 type specTemplate struct {
53 tmpl string
54 fields [][2]int
55 }
56
57
58 func loadSpecPackage(ctx context, dir string, opts *LoadOptions) *specPackage {
59 cfg := &packages.Config{
60 Mode: packages.LoadSyntax,
61 Dir: dir,
62 Fset: &ctx.root.fset,
63 }
64
65 pkgs, err := packages.Load(cfg, ".")
66 if err != nil {
67 ctx.errorf("failed to load package: %s", err)
68 return nil
69 }
70 if len(pkgs) == 0 {
71 ctx.errorf("no package found in directory %s", dir)
72 return nil
73 }
74 if len(pkgs[0].Errors) > 0 {
75 for _, err := range pkgs[0].Errors {
76 ctx.errorf("%s", err)
77 }
78 return nil
79 }
80
81 srcPkg := pkgs[0]
82 fset := srcPkg.Fset
83 info := srcPkg.TypesInfo
84
85 var pkg specPackage
86
87
88 var funcs []*specFunc
89 for _, file := range srcPkg.Syntax {
90 for _, decl := range file.Decls {
91 d, ok := decl.(*ast.FuncDecl)
92 if !ok || !d.Name.IsExported() {
93 continue
94 }
95 if opts.Filter != nil && !opts.Filter(d) {
96 continue
97 }
98
99 obj := srcPkg.Types.Scope().Lookup(d.Name.Name)
100 if obj == nil {
101 continue
102 }
103 fn, ok := obj.(*types.Func)
104 if !ok {
105 continue
106 }
107
108 sig := fn.Type().(*types.Signature)
109
110 var typeParams []*types.TypeParam
111 tparams := sig.TypeParams()
112 for tparam := range tparams.TypeParams() {
113 typeParams = append(typeParams, tparam)
114 }
115
116 var params []*types.Var
117 p := sig.Params()
118 for v := range p.Variables() {
119 params = append(params, v)
120 }
121
122 var results []*types.Var
123 r := sig.Results()
124 for v := range r.Variables() {
125 results = append(results, v)
126 }
127
128 f := &specFunc{
129 Pkg: &pkg,
130 Name: d.Name.Name,
131 Pos: decl.Pos(),
132 Sig: sig,
133 TypeParams: typeParams,
134 Params: params,
135 Results: results,
136 }
137 f.NameTmpl = specTemplate{tmpl: f.Name}
138 if d.Doc != nil {
139 f.Doc, err = newSpecTemplate(d.Doc.Text())
140 if err != nil {
141 ctx.at(d.Doc.Pos()).errorf("malformed doc comment: %s", err)
142 }
143 for _, comment := range d.Doc.List {
144 if dir, ok := ast.ParseDirective(comment.Slash, comment.Text); ok && dir.Tool == "specgen" {
145 switch dir.Name {
146 default:
147 ctx.at(dir.Pos()).errorf("unknown //specgen directive")
148 case "name":
149 f.NameTmpl, err = newSpecTemplate(dir.Args)
150 if err != nil {
151 ctx.at(dir.Pos()).errorf("malformed //specgen:name directive: %s", err)
152 }
153 case "require":
154 args, err := dir.ParseArgs()
155 if err != nil {
156 ctx.at(dir.Pos()).errorf("malformed //specgen:require directive: %s", err)
157 break
158 }
159 for _, arg := range args {
160 expr, err := specexpr.ParseExpr(arg.Arg)
161 if err != nil {
162 ctx.at(arg.Pos).errorf("failed to parse require argument %q: %s", arg.Arg, err)
163 continue
164 }
165 f.Requirements = append(f.Requirements, expr)
166 }
167 }
168 }
169 }
170 }
171
172 funcs = append(funcs, f)
173 }
174 }
175
176 lookupType := func(name string) types.Type {
177 obj := srcPkg.Types.Scope().Lookup(name)
178 if obj == nil {
179 ctx.errorf("type %q missing from package %s", name, srcPkg.PkgPath)
180 return nil
181 }
182 tn, ok := obj.(*types.TypeName)
183 if !ok {
184 ctx.at(obj.Pos()).errorf("%s expected to be a type", obj.String())
185 return nil
186 }
187 return tn.Type()
188 }
189
190
191 typeElems := make(map[types.Type]specexpr.Basic)
192 elemTypes := make(map[specexpr.Basic]types.Type)
193 if eltOrMask := lookupType("EltOrMask"); eltOrMask != nil {
194 for _, elt := range typeSet(eltOrMask) {
195 basic := shapeElemType(elt)
196 typeElems[elt] = basic
197 elemTypes[basic] = elt
198 }
199 }
200 typeWidths := make(map[types.Type]specexpr.Num)
201 widthTypes := make(map[specexpr.Num]types.Type)
202 if width := lookupType("Width"); width != nil {
203 for _, width := range typeSet(width) {
204 val := shapeWidthVal(width)
205 typeWidths[width] = val
206 widthTypes[val] = width
207 }
208 }
209
210
211 vecType := lookupType("Vec")
212 arrayType := lookupType("Array")
213 uintNType := lookupType("UintN")
214
215 pkg = specPackage{
216 Fset: fset,
217 Pkg: srcPkg.Types,
218 TypesInfo: info,
219 Funcs: funcs,
220 TypeElems: typeElems,
221 TypeWidths: typeWidths,
222 ElemTypes: elemTypes,
223 WidthTypes: widthTypes,
224 VecType: vecType,
225 ArrayType: arrayType,
226 UintNType: uintNType,
227 }
228 return &pkg
229 }
230
231
232 func newSpecTemplate(tmpl string) (specTemplate, error) {
233 if !strings.ContainsAny(tmpl, "{}") {
234 return specTemplate{tmpl, nil}, nil
235 }
236
237 var fields [][2]int
238 for i := 0; i < len(tmpl); i++ {
239 switch tmpl[i] {
240 case '{':
241 j := i + strings.IndexByte(tmpl[i:], '}') + 1
242 if j <= i {
243 return specTemplate{}, fmt.Errorf("unclosed '{' in template %q", tmpl)
244 }
245 fields = append(fields, [2]int{i, j})
246 i = j - 1
247 case '}':
248 return specTemplate{}, fmt.Errorf("unmatched '}' in template %q", tmpl)
249 }
250 }
251 return specTemplate{
252 tmpl: tmpl,
253 fields: fields,
254 }, nil
255 }
256
257
258
259 func (s *specTemplate) expand(lookup func(string) string) string {
260 if len(s.fields) == 0 {
261 return s.tmpl
262 }
263 var buf strings.Builder
264 pos := 0
265 for _, field := range s.fields {
266 buf.WriteString(s.tmpl[pos:field[0]])
267 val := lookup(s.tmpl[field[0]+1 : field[1]-1])
268 buf.WriteString(val)
269 pos = field[1]
270 }
271 buf.WriteString(s.tmpl[pos:])
272 return buf.String()
273 }
274
View as plain text