1fd4e5da5Sopenharmony_ci// Copyright (C) 2019 Google Inc. 2fd4e5da5Sopenharmony_ci// 3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License. 5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at 6fd4e5da5Sopenharmony_ci// 7fd4e5da5Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8fd4e5da5Sopenharmony_ci// 9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and 13fd4e5da5Sopenharmony_ci// limitations under the License. 14fd4e5da5Sopenharmony_ci 15fd4e5da5Sopenharmony_ci// gen-grammar generates the spirv.json grammar file from the official SPIR-V 16fd4e5da5Sopenharmony_ci// grammar JSON file. 17fd4e5da5Sopenharmony_cipackage main 18fd4e5da5Sopenharmony_ci 19fd4e5da5Sopenharmony_ciimport ( 20fd4e5da5Sopenharmony_ci "bytes" 21fd4e5da5Sopenharmony_ci "encoding/json" 22fd4e5da5Sopenharmony_ci "flag" 23fd4e5da5Sopenharmony_ci "fmt" 24fd4e5da5Sopenharmony_ci "io/ioutil" 25fd4e5da5Sopenharmony_ci "net/http" 26fd4e5da5Sopenharmony_ci "os" 27fd4e5da5Sopenharmony_ci "path/filepath" 28fd4e5da5Sopenharmony_ci "runtime" 29fd4e5da5Sopenharmony_ci "strings" 30fd4e5da5Sopenharmony_ci "text/template" 31fd4e5da5Sopenharmony_ci 32fd4e5da5Sopenharmony_ci "github.com/pkg/errors" 33fd4e5da5Sopenharmony_ci 34fd4e5da5Sopenharmony_ci "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/grammar" 35fd4e5da5Sopenharmony_ci) 36fd4e5da5Sopenharmony_ci 37fd4e5da5Sopenharmony_citype grammarDefinition struct { 38fd4e5da5Sopenharmony_ci name string 39fd4e5da5Sopenharmony_ci url string 40fd4e5da5Sopenharmony_ci} 41fd4e5da5Sopenharmony_ci 42fd4e5da5Sopenharmony_civar ( 43fd4e5da5Sopenharmony_ci spirvGrammar = grammarDefinition{ 44fd4e5da5Sopenharmony_ci name: "SPIR-V", 45fd4e5da5Sopenharmony_ci url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json", 46fd4e5da5Sopenharmony_ci } 47fd4e5da5Sopenharmony_ci 48fd4e5da5Sopenharmony_ci extensionGrammars = []grammarDefinition{ 49fd4e5da5Sopenharmony_ci { 50fd4e5da5Sopenharmony_ci name: "GLSL.std.450", 51fd4e5da5Sopenharmony_ci url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.glsl.std.450.grammar.json", 52fd4e5da5Sopenharmony_ci }, { 53fd4e5da5Sopenharmony_ci name: "OpenCL.std", 54fd4e5da5Sopenharmony_ci url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.std.100.grammar.json", 55fd4e5da5Sopenharmony_ci }, { 56fd4e5da5Sopenharmony_ci name: "OpenCL.DebugInfo.100", 57fd4e5da5Sopenharmony_ci url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json", 58fd4e5da5Sopenharmony_ci }, 59fd4e5da5Sopenharmony_ci } 60fd4e5da5Sopenharmony_ci 61fd4e5da5Sopenharmony_ci templatePath = flag.String("template", "", "Path to input template file (required)") 62fd4e5da5Sopenharmony_ci outputPath = flag.String("out", "", "Path to output generated file (required)") 63fd4e5da5Sopenharmony_ci cachePath = flag.String("cache", "", "Cache directory for downloaded files (optional)") 64fd4e5da5Sopenharmony_ci 65fd4e5da5Sopenharmony_ci thisDir = func() string { 66fd4e5da5Sopenharmony_ci _, file, _, _ := runtime.Caller(1) 67fd4e5da5Sopenharmony_ci return filepath.Dir(file) 68fd4e5da5Sopenharmony_ci }() 69fd4e5da5Sopenharmony_ci) 70fd4e5da5Sopenharmony_ci 71fd4e5da5Sopenharmony_cifunc main() { 72fd4e5da5Sopenharmony_ci flag.Parse() 73fd4e5da5Sopenharmony_ci if *templatePath == "" || *outputPath == "" { 74fd4e5da5Sopenharmony_ci flag.Usage() 75fd4e5da5Sopenharmony_ci os.Exit(1) 76fd4e5da5Sopenharmony_ci } 77fd4e5da5Sopenharmony_ci if err := run(); err != nil { 78fd4e5da5Sopenharmony_ci fmt.Fprintln(os.Stderr, err) 79fd4e5da5Sopenharmony_ci os.Exit(1) 80fd4e5da5Sopenharmony_ci } 81fd4e5da5Sopenharmony_ci} 82fd4e5da5Sopenharmony_ci 83fd4e5da5Sopenharmony_cifunc run() error { 84fd4e5da5Sopenharmony_ci tf, err := ioutil.ReadFile(*templatePath) 85fd4e5da5Sopenharmony_ci if err != nil { 86fd4e5da5Sopenharmony_ci return errors.Wrap(err, "Could not open template file") 87fd4e5da5Sopenharmony_ci } 88fd4e5da5Sopenharmony_ci 89fd4e5da5Sopenharmony_ci type extension struct { 90fd4e5da5Sopenharmony_ci grammar.Root 91fd4e5da5Sopenharmony_ci Name string 92fd4e5da5Sopenharmony_ci } 93fd4e5da5Sopenharmony_ci 94fd4e5da5Sopenharmony_ci args := struct { 95fd4e5da5Sopenharmony_ci SPIRV grammar.Root 96fd4e5da5Sopenharmony_ci Extensions []extension 97fd4e5da5Sopenharmony_ci All grammar.Root // Combination of SPIRV + Extensions 98fd4e5da5Sopenharmony_ci }{} 99fd4e5da5Sopenharmony_ci 100fd4e5da5Sopenharmony_ci if args.SPIRV, err = parseGrammar(spirvGrammar); err != nil { 101fd4e5da5Sopenharmony_ci return errors.Wrap(err, "Failed to parse SPIR-V grammar file") 102fd4e5da5Sopenharmony_ci } 103fd4e5da5Sopenharmony_ci args.All.Instructions = append(args.All.Instructions, args.SPIRV.Instructions...) 104fd4e5da5Sopenharmony_ci args.All.OperandKinds = append(args.All.OperandKinds, args.SPIRV.OperandKinds...) 105fd4e5da5Sopenharmony_ci 106fd4e5da5Sopenharmony_ci for _, ext := range extensionGrammars { 107fd4e5da5Sopenharmony_ci root, err := parseGrammar(ext) 108fd4e5da5Sopenharmony_ci if err != nil { 109fd4e5da5Sopenharmony_ci return errors.Wrap(err, "Failed to parse extension grammar file: "+ext.name) 110fd4e5da5Sopenharmony_ci } 111fd4e5da5Sopenharmony_ci args.Extensions = append(args.Extensions, extension{Root: root, Name: ext.name}) 112fd4e5da5Sopenharmony_ci args.All.Instructions = append(args.All.Instructions, root.Instructions...) 113fd4e5da5Sopenharmony_ci args.All.OperandKinds = append(args.All.OperandKinds, root.OperandKinds...) 114fd4e5da5Sopenharmony_ci } 115fd4e5da5Sopenharmony_ci 116fd4e5da5Sopenharmony_ci t, err := template.New("tmpl"). 117fd4e5da5Sopenharmony_ci Funcs(template.FuncMap{ 118fd4e5da5Sopenharmony_ci "GenerateArguments": func() string { 119fd4e5da5Sopenharmony_ci relPath := func(path string) string { 120fd4e5da5Sopenharmony_ci rel, err := filepath.Rel(thisDir, path) 121fd4e5da5Sopenharmony_ci if err != nil { 122fd4e5da5Sopenharmony_ci return path 123fd4e5da5Sopenharmony_ci } 124fd4e5da5Sopenharmony_ci return rel 125fd4e5da5Sopenharmony_ci } 126fd4e5da5Sopenharmony_ci escape := func(str string) string { 127fd4e5da5Sopenharmony_ci return strings.ReplaceAll(str, `\`, `/`) 128fd4e5da5Sopenharmony_ci } 129fd4e5da5Sopenharmony_ci args := []string{ 130fd4e5da5Sopenharmony_ci "--template=" + escape(relPath(*templatePath)), 131fd4e5da5Sopenharmony_ci "--out=" + escape(relPath(*outputPath)), 132fd4e5da5Sopenharmony_ci } 133fd4e5da5Sopenharmony_ci return "gen-grammar.go " + strings.Join(args, " ") 134fd4e5da5Sopenharmony_ci }, 135fd4e5da5Sopenharmony_ci "OperandKindsMatch": func(k grammar.OperandKind) string { 136fd4e5da5Sopenharmony_ci sb := strings.Builder{} 137fd4e5da5Sopenharmony_ci for i, e := range k.Enumerants { 138fd4e5da5Sopenharmony_ci if i > 0 { 139fd4e5da5Sopenharmony_ci sb.WriteString("|") 140fd4e5da5Sopenharmony_ci } 141fd4e5da5Sopenharmony_ci sb.WriteString(e.Enumerant) 142fd4e5da5Sopenharmony_ci } 143fd4e5da5Sopenharmony_ci return sb.String() 144fd4e5da5Sopenharmony_ci }, 145fd4e5da5Sopenharmony_ci "AllExtOpcodes": func() string { 146fd4e5da5Sopenharmony_ci sb := strings.Builder{} 147fd4e5da5Sopenharmony_ci for _, ext := range args.Extensions { 148fd4e5da5Sopenharmony_ci for _, inst := range ext.Root.Instructions { 149fd4e5da5Sopenharmony_ci if sb.Len() > 0 { 150fd4e5da5Sopenharmony_ci sb.WriteString("|") 151fd4e5da5Sopenharmony_ci } 152fd4e5da5Sopenharmony_ci sb.WriteString(inst.Opname) 153fd4e5da5Sopenharmony_ci } 154fd4e5da5Sopenharmony_ci } 155fd4e5da5Sopenharmony_ci return sb.String() 156fd4e5da5Sopenharmony_ci }, 157fd4e5da5Sopenharmony_ci "Title": strings.Title, 158fd4e5da5Sopenharmony_ci "Replace": strings.ReplaceAll, 159fd4e5da5Sopenharmony_ci "Global": func(s string) string { 160fd4e5da5Sopenharmony_ci return strings.ReplaceAll(strings.Title(s), ".", "") 161fd4e5da5Sopenharmony_ci }, 162fd4e5da5Sopenharmony_ci }).Parse(string(tf)) 163fd4e5da5Sopenharmony_ci if err != nil { 164fd4e5da5Sopenharmony_ci return errors.Wrap(err, "Failed to parse template") 165fd4e5da5Sopenharmony_ci } 166fd4e5da5Sopenharmony_ci 167fd4e5da5Sopenharmony_ci buf := bytes.Buffer{} 168fd4e5da5Sopenharmony_ci if err := t.Execute(&buf, args); err != nil { 169fd4e5da5Sopenharmony_ci return errors.Wrap(err, "Failed to execute template") 170fd4e5da5Sopenharmony_ci } 171fd4e5da5Sopenharmony_ci 172fd4e5da5Sopenharmony_ci out := buf.String() 173fd4e5da5Sopenharmony_ci out = strings.ReplaceAll(out, "•", "") 174fd4e5da5Sopenharmony_ci 175fd4e5da5Sopenharmony_ci if err := ioutil.WriteFile(*outputPath, []byte(out), 0777); err != nil { 176fd4e5da5Sopenharmony_ci return errors.Wrap(err, "Failed to write output file") 177fd4e5da5Sopenharmony_ci } 178fd4e5da5Sopenharmony_ci 179fd4e5da5Sopenharmony_ci return nil 180fd4e5da5Sopenharmony_ci} 181fd4e5da5Sopenharmony_ci 182fd4e5da5Sopenharmony_ci// parseGrammar downloads (or loads from the cache) the grammar file and returns 183fd4e5da5Sopenharmony_ci// the parsed grammar.Root. 184fd4e5da5Sopenharmony_cifunc parseGrammar(def grammarDefinition) (grammar.Root, error) { 185fd4e5da5Sopenharmony_ci file, err := getOrDownload(def.name, def.url) 186fd4e5da5Sopenharmony_ci if err != nil { 187fd4e5da5Sopenharmony_ci return grammar.Root{}, errors.Wrap(err, "Failed to load grammar file") 188fd4e5da5Sopenharmony_ci } 189fd4e5da5Sopenharmony_ci 190fd4e5da5Sopenharmony_ci g := grammar.Root{} 191fd4e5da5Sopenharmony_ci if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil { 192fd4e5da5Sopenharmony_ci return grammar.Root{}, errors.Wrap(err, "Failed to parse grammar file") 193fd4e5da5Sopenharmony_ci } 194fd4e5da5Sopenharmony_ci 195fd4e5da5Sopenharmony_ci return g, nil 196fd4e5da5Sopenharmony_ci} 197fd4e5da5Sopenharmony_ci 198fd4e5da5Sopenharmony_ci// getOrDownload loads the specific file from the cache, or downloads the file 199fd4e5da5Sopenharmony_ci// from the given url. 200fd4e5da5Sopenharmony_cifunc getOrDownload(name, url string) ([]byte, error) { 201fd4e5da5Sopenharmony_ci if *cachePath != "" { 202fd4e5da5Sopenharmony_ci if err := os.MkdirAll(*cachePath, 0777); err == nil { 203fd4e5da5Sopenharmony_ci path := filepath.Join(*cachePath, name) 204fd4e5da5Sopenharmony_ci if isFile(path) { 205fd4e5da5Sopenharmony_ci return ioutil.ReadFile(path) 206fd4e5da5Sopenharmony_ci } 207fd4e5da5Sopenharmony_ci } 208fd4e5da5Sopenharmony_ci } 209fd4e5da5Sopenharmony_ci resp, err := http.Get(url) 210fd4e5da5Sopenharmony_ci if err != nil { 211fd4e5da5Sopenharmony_ci return nil, err 212fd4e5da5Sopenharmony_ci } 213fd4e5da5Sopenharmony_ci data, err := ioutil.ReadAll(resp.Body) 214fd4e5da5Sopenharmony_ci if err != nil { 215fd4e5da5Sopenharmony_ci return nil, err 216fd4e5da5Sopenharmony_ci } 217fd4e5da5Sopenharmony_ci if *cachePath != "" { 218fd4e5da5Sopenharmony_ci ioutil.WriteFile(filepath.Join(*cachePath, name), data, 0777) 219fd4e5da5Sopenharmony_ci } 220fd4e5da5Sopenharmony_ci return data, nil 221fd4e5da5Sopenharmony_ci} 222fd4e5da5Sopenharmony_ci 223fd4e5da5Sopenharmony_ci// isFile returns true if path is a file. 224fd4e5da5Sopenharmony_cifunc isFile(path string) bool { 225fd4e5da5Sopenharmony_ci s, err := os.Stat(path) 226fd4e5da5Sopenharmony_ci if err != nil { 227fd4e5da5Sopenharmony_ci return false 228fd4e5da5Sopenharmony_ci } 229fd4e5da5Sopenharmony_ci return !s.IsDir() 230fd4e5da5Sopenharmony_ci} 231fd4e5da5Sopenharmony_ci 232fd4e5da5Sopenharmony_ci// isDir returns true if path is a directory. 233fd4e5da5Sopenharmony_cifunc isDir(path string) bool { 234fd4e5da5Sopenharmony_ci s, err := os.Stat(path) 235fd4e5da5Sopenharmony_ci if err != nil { 236fd4e5da5Sopenharmony_ci return false 237fd4e5da5Sopenharmony_ci } 238fd4e5da5Sopenharmony_ci return s.IsDir() 239fd4e5da5Sopenharmony_ci} 240