1 // Copyright (c) 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <cassert>
16 #include <cstdio>
17 #include <cstring>
18 #include <functional>
19 #include <iostream>
20 #include <memory>
21 #include <vector>
22
23 #include "source/spirv_target_env.h"
24 #include "source/table.h"
25 #include "spirv-tools/markv.h"
26 #include "tools/io.h"
27
28 namespace {
29
30 enum Task {
31 kNoTask = 0,
32 kEncode,
33 kDecode,
34 };
35
36 struct ScopedContext {
ScopedContext__anon1132de7f0111::ScopedContext37 ScopedContext(spv_target_env env) : context(spvContextCreate(env)) {}
~ScopedContext__anon1132de7f0111::ScopedContext38 ~ScopedContext() { spvContextDestroy(context); }
39 spv_context context;
40 };
41
print_usage(char * argv0)42 void print_usage(char* argv0) {
43 printf(
44 R"(%s - Encodes or decodes a SPIR-V binary to or from a MARK-V binary.
45
46 USAGE: %s [e|d] [options] [<filename>]
47
48 The input binary is read from <filename>. If no file is specified,
49 or if the filename is "-", then the binary is read from standard input.
50
51 If no output is specified then the output is printed to stdout in a human
52 readable format.
53
54 WIP: MARK-V codec is in early stages of development. At the moment it only
55 can encode and decode some SPIR-V files and only if exacly the same build of
56 software is used (is doesn't write or handle version numbers yet).
57
58 Tasks:
59 e Encode SPIR-V to MARK-V.
60 d Decode MARK-V to SPIR-V.
61
62 Options:
63 -h, --help Print this help.
64 --comments Write codec comments to stdout.
65 --version Display MARK-V codec version.
66
67 -o <filename> Set the output filename.
68 Output goes to standard output if this option is
69 not specified, or if the filename is "-".
70 )",
71 argv0, argv0);
72 }
73
DiagnosticsMessageHandler(spv_message_level_t level,const char *,const spv_position_t & position,const char * message)74 void DiagnosticsMessageHandler(spv_message_level_t level, const char*,
75 const spv_position_t& position,
76 const char* message) {
77 switch (level) {
78 case SPV_MSG_FATAL:
79 case SPV_MSG_INTERNAL_ERROR:
80 case SPV_MSG_ERROR:
81 std::cerr << "error: " << position.index << ": " << message
82 << std::endl;
83 break;
84 case SPV_MSG_WARNING:
85 std::cout << "warning: " << position.index << ": " << message
86 << std::endl;
87 break;
88 case SPV_MSG_INFO:
89 std::cout << "info: " << position.index << ": " << message << std::endl;
90 break;
91 default:
92 break;
93 }
94 }
95
96 } // namespace
97
main(int argc,char ** argv)98 int main(int argc, char** argv) {
99 const char* input_filename = nullptr;
100 const char* output_filename = nullptr;
101
102 Task task = kNoTask;
103
104 if (argc < 3) {
105 print_usage(argv[0]);
106 return 0;
107 }
108
109 const char* task_char = argv[1];
110 if (0 == strcmp("e", task_char)) {
111 task = kEncode;
112 } else if (0 == strcmp("d", task_char)) {
113 task = kDecode;
114 }
115
116 if (task == kNoTask) {
117 print_usage(argv[0]);
118 return 1;
119 }
120
121 bool want_comments = false;
122
123 for (int argi = 2; argi < argc; ++argi) {
124 if ('-' == argv[argi][0]) {
125 switch (argv[argi][1]) {
126 case 'h':
127 print_usage(argv[0]);
128 return 0;
129 case 'o': {
130 if (!output_filename && argi + 1 < argc) {
131 output_filename = argv[++argi];
132 } else {
133 print_usage(argv[0]);
134 return 1;
135 }
136 } break;
137 case '-': {
138 if (0 == strcmp(argv[argi], "--help")) {
139 print_usage(argv[0]);
140 return 0;
141 } else if (0 == strcmp(argv[argi], "--comments")) {
142 want_comments = true;
143 } else if (0 == strcmp(argv[argi], "--version")) {
144 fprintf(stderr, "error: Not implemented\n");
145 return 1;
146 } else {
147 print_usage(argv[0]);
148 return 1;
149 }
150 } break;
151 case '\0': {
152 // Setting a filename of "-" to indicate stdin.
153 if (!input_filename) {
154 input_filename = argv[argi];
155 } else {
156 fprintf(stderr, "error: More than one input file specified\n");
157 return 1;
158 }
159 } break;
160 default:
161 print_usage(argv[0]);
162 return 1;
163 }
164 } else {
165 if (!input_filename) {
166 input_filename = argv[argi];
167 } else {
168 fprintf(stderr, "error: More than one input file specified\n");
169 return 1;
170 }
171 }
172 }
173
174 if (task == kDecode && want_comments) {
175 fprintf(stderr, "warning: Decoder comments not yet implemented\n");
176 want_comments = false;
177 }
178
179 const bool write_to_stdout = output_filename == nullptr ||
180 0 == strcmp(output_filename, "-");
181
182 spv_text comments = nullptr;
183 spv_text* comments_ptr = want_comments ? &comments : nullptr;
184
185 ScopedContext ctx(SPV_ENV_UNIVERSAL_1_2);
186 SetContextMessageConsumer(ctx.context, DiagnosticsMessageHandler);
187
188 if (task == kEncode) {
189 std::vector<uint32_t> contents;
190 if (!ReadFile<uint32_t>(input_filename, "rb", &contents)) return 1;
191
192 std::unique_ptr<spv_markv_encoder_options_t,
193 std::function<void(spv_markv_encoder_options_t*)>> options(
194 spvMarkvEncoderOptionsCreate(), &spvMarkvEncoderOptionsDestroy);
195 spv_markv_binary markv_binary = nullptr;
196
197 if (SPV_SUCCESS !=
198 spvSpirvToMarkv(ctx.context, contents.data(), contents.size(),
199 options.get(), &markv_binary, comments_ptr, nullptr)) {
200 std::cerr << "error: Failed to encode " << input_filename << " to MARK-V "
201 << std::endl;
202 return 1;
203 }
204
205 if (want_comments) {
206 if (!WriteFile<char>(nullptr, "w", comments->str,
207 comments->length)) return 1;
208 }
209
210 if (!want_comments || !write_to_stdout) {
211 if (!WriteFile<uint8_t>(output_filename, "wb", markv_binary->data,
212 markv_binary->length)) return 1;
213 }
214 } else if (task == kDecode) {
215 std::vector<uint8_t> contents;
216 if (!ReadFile<uint8_t>(input_filename, "rb", &contents)) return 1;
217
218 std::unique_ptr<spv_markv_decoder_options_t,
219 std::function<void(spv_markv_decoder_options_t*)>> options(
220 spvMarkvDecoderOptionsCreate(), &spvMarkvDecoderOptionsDestroy);
221 spv_binary spirv_binary = nullptr;
222
223 if (SPV_SUCCESS !=
224 spvMarkvToSpirv(ctx.context, contents.data(), contents.size(),
225 options.get(), &spirv_binary, comments_ptr, nullptr)) {
226 std::cerr << "error: Failed to encode " << input_filename << " to MARK-V "
227 << std::endl;
228 return 1;
229 }
230
231 if (want_comments) {
232 if (!WriteFile<char>(nullptr, "w", comments->str,
233 comments->length)) return 1;
234 }
235
236 if (!want_comments || !write_to_stdout) {
237 if (!WriteFile<uint32_t>(output_filename, "wb", spirv_binary->code,
238 spirv_binary->wordCount)) return 1;
239 }
240 } else {
241 assert(false && "Unknown task");
242 }
243
244 spvTextDestroy(comments);
245
246 return 0;
247 }
248