OLD | NEW |
---|---|
1 //===- subzero/runtime/wasm-runtime.cpp - Subzero WASM runtime source -----===// | 1 //===- subzero/runtime/wasm-runtime.cpp - Subzero WASM runtime source -----===// |
2 // | 2 // |
3 // The Subzero Code Generator | 3 // The Subzero Code Generator |
4 // | 4 // |
5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
7 // | 7 // |
8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
9 // | 9 // |
10 // This file implements the system calls required by the libc that is included | 10 // This file implements the system calls required by the libc that is included |
11 // in WebAssembly programs. | 11 // in WebAssembly programs. |
12 // | 12 // |
13 //===----------------------------------------------------------------------===// | 13 //===----------------------------------------------------------------------===// |
14 | 14 |
15 #include <cassert> | 15 #include <cassert> |
16 #include <cmath> | 16 #include <cmath> |
John
2016/04/27 02:28:20
extreme nit ** 10: space between c++ and c headers
| |
17 // TODO (eholk): change to cstdint | 17 #include <errno.h> |
18 #include <fcntl.h> | |
19 #include <iostream> | |
John
2016/04/27 02:28:20
move this to the "c++ header section?"
| |
20 #include <math.h> | |
18 #include <stdint.h> | 21 #include <stdint.h> |
22 #include <stdio.h> | |
23 #include <stdlib.h> | |
24 #include <string.h> | |
25 #include <sys/ioctl.h> | |
26 #include <sys/types.h> | |
27 #include <sys/stat.h> | |
28 #include <termios.h> | |
29 #include <time.h> | |
30 #include <unistd.h> | |
31 | |
32 #ifdef WASM_TRACE_RUNTIME | |
33 #define TRACE_ENTRY() \ | |
34 { std::cerr << __func__ << "(...) = "; } | |
35 template <typename T> T trace(T x) { | |
36 std::cerr << x << std::endl; | |
37 return x; | |
38 } | |
39 void trace() { std::cerr << "(void)" << std::endl; } | |
40 #else | |
41 #define TRACE_ENTRY() | |
42 template <typename T> T trace(T x) { return x; } | |
43 void trace() {} | |
44 #endif // WASM_TRACE_RUNTIME | |
19 | 45 |
20 namespace { | 46 namespace { |
21 uint32_t HeapBreak; | 47 uint32_t HeapBreak; |
22 | 48 |
23 // TODO (eholk): make all of these constexpr. | 49 // TODO (eholk): make all of these constexpr. |
24 const uint32_t PageSizeLog2 = 16; | 50 const uint32_t PageSizeLog2 = 16; |
25 const uint32_t PageSize = 1 << PageSizeLog2; // 64KB | 51 const uint32_t PageSize = 1 << PageSizeLog2; // 64KB |
26 const uint32_t StackPtrLoc = 1024; // defined by emscripten | 52 const uint32_t StackPtrLoc = 1024; // defined by emscripten |
27 | 53 |
28 uint32_t pageNum(uint32_t Index) { return Index >> PageSizeLog2; } | 54 uint32_t pageNum(uint32_t Index) { return Index >> PageSizeLog2; } |
29 } // end of anonymous namespace | 55 } // end of anonymous namespace |
30 | 56 |
31 namespace env { | 57 namespace env { |
32 double floor(double X) { return std::floor(X); } | 58 double floor(double X) { return std::floor(X); } |
33 | 59 |
34 float floor(float X) { return std::floor(X); } | 60 float floor(float X) { return std::floor(X); } |
35 } // end of namespace env | 61 } // end of namespace env |
36 | 62 |
37 // TODO (eholk): move the C parts outside and use C++ name mangling. | 63 // TODO (eholk): move the C parts outside and use C++ name mangling. |
64 | |
65 typedef int32_t WasmPtr; | |
66 | |
67 namespace { | |
68 | |
69 /// Some runtime functions need to return pointers. The WasmData struct is used | |
70 /// to preallocate space for these on the heap. | |
71 struct WasmData { | |
72 | |
73 /// StrBuf is returned by functions that return strings. | |
74 char StrBuf[256]; | |
75 }; | |
76 | |
77 WasmData *GlobalData = NULL; | |
78 | |
79 } // end of anonymous namespace | |
80 | |
81 // TODO (eholk): move the C parts outside and use C++ name mangling. | |
38 extern "C" { | 82 extern "C" { |
39 #include <errno.h> | 83 |
40 #include <fcntl.h> | 84 void __sz_bounds_fail() { |
41 #include <math.h> | 85 std::cerr << "Bounds check failure" << std::endl; |
42 #include <stdio.h> | 86 abort(); |
43 #include <stdlib.h> | 87 } |
44 #include <string.h> | 88 |
45 #include <sys/types.h> | 89 void __sz_indirect_fail() { |
46 #include <sys/stat.h> | 90 std::cerr << "Invalid indirect call target" << std::endl; |
47 #include <unistd.h> | 91 abort(); |
92 } | |
48 | 93 |
49 extern char WASM_MEMORY[]; | 94 extern char WASM_MEMORY[]; |
50 extern uint32_t WASM_DATA_SIZE; | 95 extern uint32_t WASM_DATA_SIZE; |
51 extern uint32_t WASM_NUM_PAGES; | 96 extern uint32_t WASM_NUM_PAGES; |
52 | 97 |
53 void env$$abort() { | 98 void env$$abort() { |
54 fprintf(stderr, "Aborting...\n"); | 99 fprintf(stderr, "Aborting...\n"); |
55 abort(); | 100 abort(); |
56 } | 101 } |
57 | 102 |
58 void env$$_abort() { env$$abort(); } | 103 void env$$_abort() { env$$abort(); } |
59 | 104 |
60 double env$$floor_f(float X) { return env::floor(X); } | 105 double env$$floor_f(float X) { |
61 double env$$floor_d(double X) { return env::floor(X); } | 106 TRACE_ENTRY(); |
107 return env::floor(X); | |
108 } | |
109 double env$$floor_d(double X) { | |
110 TRACE_ENTRY(); | |
111 return env::floor(X); | |
112 } | |
62 | 113 |
63 void env$$exit(int Status) { exit(Status); } | 114 void env$$exit(int Status) { |
64 void env$$_exit(int Status) { env$$exit(Status); } | 115 TRACE_ENTRY(); |
116 exit(Status); | |
117 } | |
118 void env$$_exit(int Status) { | |
119 TRACE_ENTRY(); | |
120 env$$exit(Status); | |
121 } | |
65 | 122 |
66 #define UNIMPLEMENTED(f) \ | 123 #define UNIMPLEMENTED(f) \ |
67 void env$$##f() { \ | 124 void env$$##f() { \ |
68 fprintf(stderr, "Unimplemented: " #f "\n"); \ | 125 fprintf(stderr, "Unimplemented: " #f "\n"); \ |
69 abort(); \ | 126 abort(); \ |
70 } | 127 } |
71 | 128 |
72 int32_t env$$sbrk(int32_t Increment) { | 129 int32_t env$$sbrk(int32_t Increment) { |
130 TRACE_ENTRY(); | |
131 uint32_t OldBreak = HeapBreak; | |
73 HeapBreak += Increment; | 132 HeapBreak += Increment; |
74 return HeapBreak; | 133 return trace(OldBreak); |
75 } | 134 } |
76 | 135 |
77 UNIMPLEMENTED(setjmp) | 136 UNIMPLEMENTED(__addtf3) |
78 UNIMPLEMENTED(longjmp) | |
79 UNIMPLEMENTED(__assert_fail) | 137 UNIMPLEMENTED(__assert_fail) |
80 UNIMPLEMENTED(__builtin_malloc) | 138 UNIMPLEMENTED(__builtin_apply) |
139 UNIMPLEMENTED(__builtin_apply_args) | |
81 UNIMPLEMENTED(__builtin_isinff) | 140 UNIMPLEMENTED(__builtin_isinff) |
82 UNIMPLEMENTED(__builtin_isinfl) | 141 UNIMPLEMENTED(__builtin_isinfl) |
83 UNIMPLEMENTED(__builtin_apply) | 142 UNIMPLEMENTED(__builtin_malloc) |
84 UNIMPLEMENTED(__builtin_apply_args) | 143 UNIMPLEMENTED(__divtf3) |
85 UNIMPLEMENTED(pthread_cleanup_push) | 144 UNIMPLEMENTED(__eqtf2) |
86 UNIMPLEMENTED(pthread_cleanup_pop) | 145 UNIMPLEMENTED(__extenddftf2) |
87 UNIMPLEMENTED(pthread_self) | 146 UNIMPLEMENTED(__extendsftf2) |
147 UNIMPLEMENTED(__fixsfti) | |
148 UNIMPLEMENTED(__fixtfdi) | |
149 UNIMPLEMENTED(__fixtfsi) | |
150 UNIMPLEMENTED(__fixunstfsi) | |
88 UNIMPLEMENTED(__floatditf) | 151 UNIMPLEMENTED(__floatditf) |
89 UNIMPLEMENTED(__floatsitf) | 152 UNIMPLEMENTED(__floatsitf) |
90 UNIMPLEMENTED(__fixtfdi) | 153 UNIMPLEMENTED(__floatunsitf) |
91 UNIMPLEMENTED(__fixtfsi) | |
92 UNIMPLEMENTED(__fixsfti) | |
93 UNIMPLEMENTED(__netf2) | |
94 UNIMPLEMENTED(__getf2) | 154 UNIMPLEMENTED(__getf2) |
95 UNIMPLEMENTED(__eqtf2) | 155 UNIMPLEMENTED(__letf2) |
96 UNIMPLEMENTED(__lttf2) | 156 UNIMPLEMENTED(__lttf2) |
97 UNIMPLEMENTED(__addtf3) | |
98 UNIMPLEMENTED(__subtf3) | |
99 UNIMPLEMENTED(__divtf3) | |
100 UNIMPLEMENTED(__multf3) | 157 UNIMPLEMENTED(__multf3) |
101 UNIMPLEMENTED(__multi3) | 158 UNIMPLEMENTED(__multi3) |
102 UNIMPLEMENTED(__lock) | 159 UNIMPLEMENTED(__netf2) |
103 UNIMPLEMENTED(__unlock) | 160 UNIMPLEMENTED(__subtf3) |
104 UNIMPLEMENTED(__syscall6) // sys_close | |
105 UNIMPLEMENTED(__syscall140) // sys_llseek | 161 UNIMPLEMENTED(__syscall140) // sys_llseek |
162 UNIMPLEMENTED(__syscall221) // sys_fcntl64 | |
163 UNIMPLEMENTED(__trunctfdf2) | |
164 UNIMPLEMENTED(__trunctfsf2) | |
106 UNIMPLEMENTED(__unordtf2) | 165 UNIMPLEMENTED(__unordtf2) |
107 UNIMPLEMENTED(__fixunstfsi) | 166 UNIMPLEMENTED(longjmp) |
108 UNIMPLEMENTED(__floatunsitf) | 167 UNIMPLEMENTED(pthread_cleanup_pop) |
109 UNIMPLEMENTED(__extenddftf2) | 168 UNIMPLEMENTED(pthread_cleanup_push) |
110 | 169 UNIMPLEMENTED(pthread_self) |
111 void *wasmPtr(uint32_t Index) { | 170 UNIMPLEMENTED(setjmp) |
171 | |
172 void *wasmPtr(int Index) { | |
112 if (pageNum(Index) < WASM_NUM_PAGES) { | 173 if (pageNum(Index) < WASM_NUM_PAGES) { |
113 return WASM_MEMORY + Index; | 174 return WASM_MEMORY + Index; |
114 } | 175 } |
115 abort(); | 176 abort(); |
116 } | 177 } |
117 | 178 |
118 extern int __szwasm_main(int, const char **); | 179 WasmPtr toWasm(void *Ptr) { |
180 return reinterpret_cast<WasmPtr>(reinterpret_cast<char *>(Ptr) - WASM_MEMORY); | |
181 } | |
182 | |
183 extern int __szwasm_main(int, WasmPtr); | |
119 | 184 |
120 #define WASM_REF(Type, Index) ((Type *)wasmPtr(Index)) | 185 #define WASM_REF(Type, Index) ((Type *)wasmPtr(Index)) |
121 #define WASM_DEREF(Type, Index) (*WASM_REF(Type, Index)) | 186 #define WASM_DEREF(Type, Index) (*WASM_REF(Type, Index)) |
122 | 187 |
123 int main(int argc, const char **argv) { | 188 int main(int argc, const char **argv) { |
189 // fprintf(stderr, "main(%d)\n", argc); | |
190 | |
191 // TODO (eholk): align these allocations correctly. | |
192 | |
193 // Allocate space for the global data. | |
194 HeapBreak = WASM_DATA_SIZE; | |
195 GlobalData = WASM_REF(WasmData, HeapBreak); | |
196 HeapBreak += sizeof(WasmData); | |
197 | |
198 // copy the command line arguments. | |
199 WasmPtr WasmArgV = HeapBreak; | |
200 WasmPtr *WasmArgVPtr = WASM_REF(WasmPtr, WasmArgV); | |
201 HeapBreak += argc * sizeof(WasmPtr); | |
202 | |
203 for (int i = 0; i < argc; ++i) { | |
204 WasmArgVPtr[i] = HeapBreak; | |
205 strcpy(WASM_REF(char, HeapBreak), argv[i]); | |
206 HeapBreak += strlen(argv[i]) + 1; | |
207 } | |
208 | |
124 // Initialize the break to the nearest page boundary after the data segment | 209 // Initialize the break to the nearest page boundary after the data segment |
125 HeapBreak = (WASM_DATA_SIZE + PageSize - 1) & ~(PageSize - 1); | 210 HeapBreak = (WASM_DATA_SIZE + PageSize - 1) & ~(PageSize - 1); |
126 | 211 |
127 // Initialize the stack pointer. | 212 // Initialize the stack pointer. |
128 WASM_DEREF(int32_t, StackPtrLoc) = WASM_NUM_PAGES << PageSizeLog2; | 213 WASM_DEREF(int32_t, StackPtrLoc) = WASM_NUM_PAGES << PageSizeLog2; |
129 | 214 |
130 return __szwasm_main(argc, argv); | 215 return __szwasm_main(argc, WasmArgV); |
131 } | 216 } |
132 | 217 |
133 int env$$abs(int a) { return abs(a); } | 218 int env$$abs(int a) { |
134 | 219 TRACE_ENTRY(); |
135 double env$$pow(double x, double y) { return pow(x, y); } | 220 return trace(abs(a)); |
136 | 221 } |
137 /// sys_write | 222 |
138 int env$$__syscall4(int Which, int VarArgs) { | 223 clock_t env$$clock() { |
224 TRACE_ENTRY(); | |
225 return trace(clock()); | |
226 } | |
227 | |
228 WasmPtr env$$ctime(WasmPtr Time) { | |
229 TRACE_ENTRY(); | |
230 time_t *TimePtr = WASM_REF(time_t, Time); | |
231 char *CTime = ctime(TimePtr); | |
232 strncpy(GlobalData->StrBuf, CTime, sizeof(GlobalData->StrBuf)); | |
233 GlobalData->StrBuf[sizeof(GlobalData->StrBuf) - 1] = '\0'; | |
234 return trace(toWasm(GlobalData->StrBuf)); | |
235 } | |
236 | |
237 double env$$pow(double x, double y) { | |
238 TRACE_ENTRY(); | |
239 return trace(pow(x, y)); | |
240 } | |
241 | |
242 time_t env$$time(WasmPtr Time) { | |
243 TRACE_ENTRY(); | |
244 time_t *TimePtr = WASM_REF(time_t, Time); | |
245 return trace(time(TimePtr)); | |
246 } | |
247 | |
248 // lock and unlock are no-ops in wasm.js, so we mimic that behavior. | |
249 void env$$__lock(int32_t _) { | |
250 TRACE_ENTRY(); | |
251 (void)_; | |
252 trace(); | |
253 } | |
254 | |
255 void env$$__unlock(int32_t _) { | |
256 TRACE_ENTRY(); | |
257 (void)_; | |
258 trace(); | |
259 } | |
260 | |
261 /// sys_read | |
262 int env$$__syscall3(int Which, int VarArgs) { | |
263 TRACE_ENTRY(); | |
139 int Fd = WASM_DEREF(int, VarArgs + 0 * sizeof(int)); | 264 int Fd = WASM_DEREF(int, VarArgs + 0 * sizeof(int)); |
140 int Buffer = WASM_DEREF(int, VarArgs + 1 * sizeof(int)); | 265 int Buffer = WASM_DEREF(int, VarArgs + 1 * sizeof(int)); |
141 int Length = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); | 266 int Length = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); |
142 | 267 |
143 return write(Fd, WASM_REF(char *, Buffer), Length); | 268 return trace(read(Fd, WASM_REF(char *, Buffer), Length)); |
269 } | |
270 | |
271 /// sys_write | |
272 int env$$__syscall4(int Which, int VarArgs) { | |
273 TRACE_ENTRY(); | |
274 int Fd = WASM_DEREF(int, VarArgs + 0 * sizeof(int)); | |
275 int Buffer = WASM_DEREF(int, VarArgs + 1 * sizeof(int)); | |
276 int Length = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); | |
277 | |
278 return trace(write(Fd, WASM_REF(char *, Buffer), Length)); | |
144 } | 279 } |
145 | 280 |
146 /// sys_open | 281 /// sys_open |
147 int env$$__syscall5(int Which, int VarArgs) { | 282 int env$$__syscall5(int Which, int VarArgs) { |
283 TRACE_ENTRY(); | |
148 int WasmPath = WASM_DEREF(int, VarArgs); | 284 int WasmPath = WASM_DEREF(int, VarArgs); |
149 int Flags = WASM_DEREF(int, VarArgs + 4); | 285 int Flags = WASM_DEREF(int, VarArgs + 4); |
150 int Mode = WASM_DEREF(int, VarArgs + 8); | 286 int Mode = WASM_DEREF(int, VarArgs + 8); |
151 const char *Path = WASM_REF(char, WasmPath); | 287 const char *Path = WASM_REF(char, WasmPath); |
152 | 288 |
153 fprintf(stderr, "sys_open(%s, %d, %d)\n", Path, Flags, Mode); | 289 return trace(open(Path, Flags, Mode)); |
154 | 290 } |
155 return open(Path, Flags, Mode); | 291 |
292 /// sys_close | |
293 int env$$__syscall6(int Which, int VarArgs) { | |
294 TRACE_ENTRY(); | |
295 int Fd = WASM_DEREF(int, VarArgs + 0 * sizeof(int)); | |
296 | |
297 return trace(close(Fd)); | |
298 } | |
299 | |
300 /// sys_unlink | |
301 int env$$__syscall10(int Which, int VarArgs) { | |
302 TRACE_ENTRY(); | |
303 int WasmPath = WASM_DEREF(int, VarArgs); | |
304 const char *Path = WASM_REF(char, WasmPath); | |
305 | |
306 return trace(unlink(Path)); | |
156 } | 307 } |
157 | 308 |
158 /// sys_getpid | 309 /// sys_getpid |
159 int env$$__syscall20(int Which, int VarArgs) { | 310 int env$$__syscall20(int Which, int VarArgs) { |
311 TRACE_ENTRY(); | |
160 (void)Which; | 312 (void)Which; |
161 (void)VarArgs; | 313 (void)VarArgs; |
162 | 314 |
163 return getpid(); | 315 return trace(getpid()); |
316 } | |
317 | |
318 /// sys_rmdir | |
319 int env$$__syscall40(int Which, int VarArgs) { | |
320 TRACE_ENTRY(); | |
321 int WasmPath = WASM_DEREF(int, VarArgs); | |
322 const char *Path = WASM_REF(char, WasmPath); | |
323 | |
324 return trace(rmdir(Path)); | |
164 } | 325 } |
165 | 326 |
166 /// sys_ioctl | 327 /// sys_ioctl |
167 int env$$__syscall54(int A, int B) { | 328 int env$$__syscall54(int A, int B) { |
329 TRACE_ENTRY(); | |
168 int Fd = WASM_DEREF(int, B + 0 * sizeof(int)); | 330 int Fd = WASM_DEREF(int, B + 0 * sizeof(int)); |
169 int Op = WASM_DEREF(int, B + 1 * sizeof(int)); | 331 int Op = WASM_DEREF(int, B + 1 * sizeof(int)); |
170 int ArgP = WASM_DEREF(int, B + 2 * sizeof(int)); | 332 int ArgP = WASM_DEREF(int, B + 2 * sizeof(int)); |
171 // TODO (eholk): implement sys_ioctl | 333 |
172 return -ENOTTY; | 334 switch (Op) { |
173 } | 335 case TCGETS: { |
174 | 336 // struct termios has no pointers. Otherwise, we'd have to rewrite them. |
175 /// sys_write | 337 struct termios *TermIOS = WASM_REF(struct termios, ArgP); |
176 int env$$__syscall146(int Which, int VarArgs) { | 338 return trace(ioctl(Fd, TCGETS, TermIOS)); |
177 | 339 } |
340 default: | |
341 // TODO (eholk): implement more ioctls | |
342 return trace(-ENOTTY); | |
343 } | |
344 } | |
345 | |
346 /// sys_readv | |
347 int env$$__syscall145(int Which, int VarArgs) { | |
348 TRACE_ENTRY(); | |
178 int Fd = WASM_DEREF(int, VarArgs); | 349 int Fd = WASM_DEREF(int, VarArgs); |
179 int Iov = WASM_DEREF(int, VarArgs + sizeof(int)); | 350 int Iov = WASM_DEREF(int, VarArgs + sizeof(int)); |
180 int Iovcnt = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); | 351 int Iovcnt = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); |
352 | |
353 int Count = 0; | |
354 | |
355 for (int I = 0; I < Iovcnt; ++I) { | |
356 void *Ptr = WASM_REF(void, WASM_DEREF(int, Iov + I * 8)); | |
357 int Length = WASM_DEREF(int, Iov + I * 8 + 4); | |
358 | |
359 int Curr = read(Fd, Ptr, Length); | |
360 | |
361 if (Curr < 0) { | |
362 return trace(-1); | |
363 } | |
364 Count += Curr; | |
365 } | |
366 return trace(Count); | |
367 } | |
368 | |
369 /// sys_writev | |
370 int env$$__syscall146(int Which, int VarArgs) { | |
371 TRACE_ENTRY(); | |
372 int Fd = WASM_DEREF(int, VarArgs); | |
373 int Iov = WASM_DEREF(int, VarArgs + sizeof(int)); | |
374 int Iovcnt = WASM_DEREF(int, VarArgs + 2 * sizeof(int)); | |
181 | 375 |
182 int Count = 0; | 376 int Count = 0; |
183 | 377 |
184 for (int I = 0; I < Iovcnt; ++I) { | 378 for (int I = 0; I < Iovcnt; ++I) { |
185 void *Ptr = WASM_REF(void, WASM_DEREF(int, Iov + I * 8)); | 379 void *Ptr = WASM_REF(void, WASM_DEREF(int, Iov + I * 8)); |
186 int Length = WASM_DEREF(int, Iov + I * 8 + 4); | 380 int Length = WASM_DEREF(int, Iov + I * 8 + 4); |
187 | 381 |
188 int Curr = write(Fd, Ptr, Length); | 382 int Curr = write(Fd, Ptr, Length); |
189 | 383 |
190 if (Curr < 0) { | 384 if (Curr < 0) { |
191 return -1; | 385 return trace(-1); |
192 } | 386 } |
193 Count += Curr; | 387 Count += Curr; |
194 } | 388 } |
195 return Count; | 389 return trace(Count); |
196 } | 390 } |
197 | 391 |
198 /// sys_mmap_pgoff | 392 /// sys_mmap_pgoff |
199 int env$$__syscall192(int Which, int VarArgs) { | 393 int env$$__syscall192(int Which, int VarArgs) { |
394 TRACE_ENTRY(); | |
200 (void)Which; | 395 (void)Which; |
201 (void)VarArgs; | 396 (void)VarArgs; |
202 | 397 |
203 // TODO (eholk): figure out how to implement this. | 398 // TODO (eholk): figure out how to implement this. |
204 | 399 |
205 return -ENOMEM; | 400 return trace(-ENOMEM); |
206 } | 401 } |
207 } // end of extern "C" | 402 } // end of extern "C" |
OLD | NEW |