-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run rv32emu inside web browser via WebAssembly translation #75
Comments
We might need RISC-V instruction decoder for web-based execution. See rvcodec.js: an online encoder/decoder for RISC-V instructions |
Recent rv32emu relies on tail-call optimization (TCO), which causes build problems with Emscripten. Thus, we have to turn it off. Apply the following changes: diff --git a/src/common.h b/src/common.h
index 62b66bd..374bcf3 100644
--- a/src/common.h
+++ b/src/common.h
@@ -43,7 +43,7 @@
* around this, the compiler attribute 'musttail' is used, which forces TCO
* even without optimizations enabled.
*/
-#if defined(__has_attribute) && __has_attribute(musttail)
+#if defined(__has_attribute) && __has_attribute(musttail) && !defined(__wasm__)
#define MUST_TAIL __attribute__((musttail))
#else
#define MUST_TAIL
diff --git a/src/io.c b/src/io.c
index 5d7a37d..f3de262 100644
--- a/src/io.c
+++ b/src/io.c
@@ -17,8 +17,8 @@
#include "io.h"
static uint8_t *data_memory_base;
-/* set memory size to 2^32 bytes */
-#define MEM_SIZE 0x100000000ULL
+/* set memory size to 2^31 bytes */
+#define MEM_SIZE 0x10000000ULL
memory_t *memory_new()
{ Then, build this project via make CC=emcc ENABLE_SDL=0 ENABLE_GDBSTUB=0
|
I am going to study and solve this issue. May I know if the original assignee still working on it? |
Go ahead! |
Tail call optimization(TCO) is supported by Chrome (version 112) natively, see roadmap of webassembly. If tail call optimization is enabled during the build, only Chrome can run the wasm. So, Chrome will be the main experimental runtime environment to adapt TCO for now. |
It is reasonably acceptable to restrict the WebAssembly runtime environments, given that the WebAssembly-built rv32emu is highly experimental. |
After the initial WebAssembly build (#293), we can proceed with the following action items:
|
Suppress the warning that generated when exporting functions from the same translation unit of the main function (e.g., |
This wasm branch has attempted to support this. Try it out by following the README. It requires some source code modifications to work:
The following are the main changes:
--- a/src/main.c
+++ b/src/main.c
@@ -44,7 +44,7 @@ static bool opt_misaligned = false;
#define MEMIO(op) on_mem_##op
#define IO_HANDLER_IMPL(type, op, RW) \
- static IIF(RW)( \
+ IIF(RW)( \
/* W */ void MEMIO(op)(riscv_word_t addr, riscv_##type##_t data), \
/* R */ riscv_##type##_t MEMIO(op)(riscv_word_t addr)) \
{ \
@@ -222,7 +222,7 @@ int main(int argc, char **args)
.allow_misalign = opt_misaligned,
};
- state_t *state = state_new();
+ state_t *state = state_new(MEM_SIZE);
/* find the start of the heap */
const struct Elf32_Sym *end;
--- a/src/riscv.h
+++ b/src/riscv.h
@@ -222,7 +222,7 @@ typedef struct {
} state_t;
/* create a state */
-state_t *state_new(void);
+state_t *state_new(uint32_t mem_size);
/* delete a state */
void state_delete(state_t *s);
--- a/src/riscv.c
+++ b/src/riscv.c
@@ -294,11 +294,11 @@ void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args)
rv->halt = false;
}
-state_t *state_new(void)
+state_t *state_new(uint32_t mem_size)
{
state_t *s = malloc(sizeof(state_t));
assert(s);
- s->mem = memory_new();
+ s->mem = memory_new(mem_size);
s->break_addr = 0;
s->fd_map = map_init(int, FILE *, map_cmp_int);
--- a/src/io.h
+++ b/src/io.h
@@ -8,16 +8,23 @@
#include <stdint.h>
#include <string.h>
-/* Directly map a memory with a size of 2^32 bytes. All memory read/write
- * operations can access this memory through the memory subsystem.
+/*
+ * set memory size to 2^32 - 1 bytes
+ *
+ * The memory size is set to 2^32 - 1 bytes in order to make this emulator
+ * portable for both 32-bit and 64-bit platforms. As a result, it can access
+ * any segment of the memory on either platform. Furthermore, it is safe
+ * because most of the test cases' data memory usage will not exceed this
+ * memory size.
*/
+#define MEM_SIZE 0xFFFFFFFFULL
typedef struct {
uint8_t *mem_base;
uint64_t mem_size;
} memory_t;
-memory_t *memory_new(void);
+memory_t *memory_new(uint32_t mem_size);
void memory_delete(memory_t *m);
/* read a C-style string from memory */
--- a/src/io.c
+++ b/src/io.c
@@ -17,44 +17,33 @@
static uint8_t *data_memory_base;
-/*
- * set memory size to 2^32 - 1 bytes
- *
- * The memory size is set to 2^32 - 1 bytes in order to make this emulator
- * portable for both 32-bit and 64-bit platforms. As a result, it can access
- * any segment of the memory on either platform. Furthermore, it is safe
- * because most of the test cases' data memory usage will not exceed this
- * memory size.
- */
-#define MEM_SIZE 0xFFFFFFFFULL
-
-memory_t *memory_new(void)
+memory_t *memory_new(uint32_t mem_size)
{
memory_t *mem = malloc(sizeof(memory_t));
assert(mem);
#if HAVE_MMAP
- data_memory_base = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
+ data_memory_base = mmap(NULL, mem_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (data_memory_base == MAP_FAILED) {
free(mem);
return NULL;
}
#else
- data_memory_base = malloc(MEM_SIZE);
+ data_memory_base = malloc(mem_size);
if (!data_memory_base) {
free(mem);
return NULL; |
Is it feasible for me to host the relevant porting files (such as HTML, JS files, etc.) and then rv32emu submodule for them? So that, rv32emu can adapt to different porting implementation. I am not sure if an HTML submodule may be hosted on a github page. |
I think so. Take Ripes for example. There are two directories resources and appdir preserving resources files.
We can follow the approach of rv32emu-bench. |
Reflecting on the feedback from the wasm branch, it seems appropriate to refine the APIs in the Last year, when developing semu, I improved the APIs to make them cleaner. The riscv.h header was designed to abstract the virtual machine's internals, avoiding explicit cross-references among several implementation-focused headers. Now, I believe it is time to apply a similar approach to the rv32emu project. Without this evolution, rv32emu continually faces challenges in adapting to various runtime environments. If you are in agreement with the statement I've made, let's proceed by gradually refining our existing public headers. The aim is to enhance encapsulation while ensuring there are no disruptions to functionality. |
To sum up, the Github page deployment will be a repository with two submodules: "wasm and corresponding glue js"(generated by |
I agree with that the the RISC-V APIs should have higher level of abstraction. Consequently, they could be easily ported to multiple platforms (e.g., desktop) or maintained. I will try to investigate the abstraction of APIs from libriscv and semu for further discussion. |
Consider awtk as another example - it is a window widget set written in C that also supports a web-based runtime through awtk-web. In the future, with the integration of semu's developments into rv32emu, we should be capable of hosting an online emulator akin to RVVM. |
Test environment: macOS 14.2.1 + Apple Silicon (M1) I have installed emcc via Homebrew.
After executing
The issue was caused by my error in toolchain configuration detection, and I will fix it later. After quick fix, both Drystone and CoreMark run inside Chrome browser. Current rv32emu/wasm performance:
RVVM:
|
WebAssembly performance might be tuned after the basic simulation(e.g., sample ELF and Doom & Quake video game) are done. |
In order to satisfy the browser's event model, the wasm branch supported cooperative multitasking mode. Try it out at demo here(only runable in Chrome). Run "ieee754.elf" to observe a notable change. The line prints out while the program is executing rather than flushing the entire buffer once the run is complete(in other words, update DOM as soon as possible). This can increase the interaction between the user and program. This change requires some modification of source code since it leverages the APIs of Emscripten (e.g., |
Shooting games like Doom and Quake may now be rendered on a web browser, however the frame rate is low(see demo). I will investigate more after sound system is set up successfully because the failure of initialization of sound system will make rv32emu exit early. see issue. |
Firefox version 121 supports TCO, so both Chrome and Firefox can be the experimental runtime environment now. |
As I tweaking the A small changes have to be applied to sound system calls(in "src/syscall_sdl.c"): static void play_sfx(riscv_t *rv)
{
...
pthread_create(&sfx_thread, NULL, sfx_handler, &sfx);
+#ifdef __EMSCRIPTEN__
+ pthread_join(sfx_thread, NULL);
+#endif
}
static void play_music(riscv_t *rv)
{
...
pthread_create(&music_thread, NULL, music_handler, &music);
+#ifdef __EMSCRIPTEN__
+ pthread_join(music_thread, NULL);
+#endif
} I found that emcc compiled web workers are not reused, so adding Demo: Note that due to the large size of the WASM file, the loading may take some time. I might rewrite the UI for noticing that the loading process (maybe a loading bar indicator). Also, please click the canvas to trigger the Web Audio API. Next, I will focus on design, discuss and refactor riscv.[ch] and io.[ch] API for easier porting to different runtime. |
Multithreading in Emscripten requires SharedArrayBuffer. To allow the browser to support "SharedArrayBuffer", we have to add custom response headers: "Cross-Origin-Opener-Policy" and "Cross-Origin-Embedder-Policy", see security requirements. Moreover, it is necessary for the server to have an active SSL certificate for the browser to enable "SharedArrayBuffer". Due to the limitation for the customization of github pages response headers (maybe fixed by this blog but seems cumbersome), github pages is not a suitable place to serve multithreading applications like Quake and Doom but suitable for those sample ELF executable which does not require multithreading. Thus, I believe a better way is that having a dedicated server with an active SSL certificate and the ability to set custom response headers to serve both multithreading and non-multithreading WebAssembly applications. |
rv32emu public APIs might be refactored over the time, writing all exported rv32emu public APIs in Makefile(see EXPORTED_FUNCTIONS in Makefile) is not an ideal choice since keep updating Makefile to adapt new rv32emu public API is weird. Instead, we can create a file to store all exported rv32emu public APIs and make emcc link to it during compile time. It is sufficient to edit the file in order to create a new rv32emu public API. See configs of awtk-web. |
Since #385 is merged, we can move forward to deploy wasm application on GitHub Pages. Shall we host on rv32emu upstream's master or other branch? I am prefer other branch since the root directory of GitHub Pages are limited to "/docs" or "/" and we might not want to mess master branch. |
I created a repository, rv32emu-demo, and specify @ChinYikMing as the administrator. Please follow the way how rv32emu-bench works. |
I would like to leverage a GitHub action workflow to deploy wasm application to rv32emu-demo. The GitHub action workflow plan: detect if |
To deploy multithreading wasm on Github Pages, we have to make the server to response two headers which are "Cross-Origin-Opener-Policy" and "Cross-Origin-Embedder-Policy". Since we cannot directly control the response headers of the Github Pages, coi-serviceworker comes to help. Related: sysprog21#75
Since wasm is deployed on Github Pages, thus it is reasonable to use CI to deploy automatically. The CI supports two event: merged pull request and workflow_dispatch. The former only run after merging and latter is used to deploy by maintainer manually. It also verifies whether any associated files have changed. If so, carry out the CI; if not, stop. Related: sysprog21#75
ELF executables might be rebuilt when new features are supported by the rv32emu such as new SDL-related system calls for video games like Doom and Quake or newlib has been updated. Close: sysprog21#405 See also: sysprog21#75
ELF executables might be rebuilt when new features are supported by the rv32emu such as new SDL-related system calls for video games like Doom and Quake or newlib has been updated. ELF executables are also might be deleted. Change filter from "any_changed" to "any_modified" since the latter includes delete event and this can ensure that deleted ELF executables do not appear in the demo page. Close: sysprog21#405 See also: sysprog21#75
Since "coi-serviceworker.js" solves the security response headers issue, we could temporarily require a specific version of Chrome installed and provide a Makefile target to host the WebAssembly in local environment without any headache configuration of web servers. |
Can you detect the vendors and versions of web browsers, so that we can ensure TCO is supported? |
Yes, according to the supported Feature Extensions of WebAssembly in each popular web browser, we can detect the MAJOR version of web browsers which support the TCO. For example, Chrome with MAJOR 112 and Firefox with MAJOR 121 supports TCO. |
It sounds great. Please send pull request(s) which detects TCO support and decides if the wasm programs should be loaded or not. |
To enhance the user experience of using WebAssembly version of rv32emu, we shall provide a Makefile target (serve-wasm) to serve the WebAssembly demo page in an easy way. Since rv32emu leverages TCO (tail call optimization) when being compiled, so we have to tell if the user's browser supports TCO. Currently, only Chrome with MAJOR 112 and Firefox with MAJOR 121 supports TCO execution runtime, thus we focus on this two browsers. Also, use color text to remind the users if they need to update their Chrome or Firefox. See also: sysprog21#75
To enhance the user experience of using WebAssembly version of rv32emu, we shall provide a Makefile target (serve-wasm) to serve the WebAssembly demo page in an easy way. Since rv32emu leverages TCO (tail call optimization) when being compiled, so we have to tell if the user's browser supports TCO. Currently, only Chrome with MAJOR 112 and Firefox with MAJOR 121 supports TCO execution runtime, thus we focus on this two browsers. Also, use color text to remind the users if they need to update their Chrome or Firefox. See also: sysprog21#75
To enhance the user experience of using WebAssembly version of rv32emu, we shall provide a Makefile target (start-web) to serve the WebAssembly demo page in an easy way. Since rv32emu leverages TCO (tail call optimization) when being compiled, so we have to tell if the user's browser supports TCO. Currently, only Chrome with MAJOR 112 and Firefox with MAJOR 121 supports TCO execution runtime, thus we focus on this two browsers. Also, use color text to remind the users if they need to update their Chrome or Firefox. See also: sysprog21#75
To enhance the user experience of using WebAssembly version of rv32emu, we shall provide a Makefile target (serve-wasm) to serve the WebAssembly demo page in an easy way. Since rv32emu leverages TCO (tail call optimization) when being compiled, so we have to tell if the user's browser supports TCO. Currently, only Chrome with MAJOR 112 and Firefox with MAJOR 121 supports TCO execution runtime, thus we focus on this two browsers. Also, use color text to remind the users if they need to update their Chrome or Firefox. See also: sysprog21#75
This task is considered completed. |
The "emcc --version" command displays information about the version of emcc, which contains the "gcc" or "clang" string. As a result, the build rule should only match one compiler and stop checking anymore. If not, the build could be failed by a mix of different compiler's CFLAGS. For example, -flto from clang and -flto=thin from emcc. After refactoring the riscv.[ch] public APIs in commit 415e55d, it is now possible to adjust the memory size to accommodate varying runtime requirements. The memory size must align to a 4KB page size for the WASM runtime and I set 1GB as default memory size for WASM runtime. The LTO is not supported to build emscripten-port SDL library, so disable it when both ENABLE_LTO=1 and ENABLE_SDL=1 are set. Related to: sysprog21#75 Close sysprog21#375
To play sound effects and musics, we would use software synthesizer, here I choose Timidity. When doom and quake target are specified, the Timidity related data are downloaded and extracted. As previous dicussion in the issue forum, the web workers are not reaped after return, thus adding pthread_join to reap them. As I tested, the sound effects and musics are only playable using the emcc version 3.1.51 which will fetch specific SDL2_mixer library, thus warning the user if the emcc version is not 3.1.51. Embed all ELF and their dependencies into single wasm when building with emcc. Consequently, a ELF executable menu bar can be introduced to select and run desired ELF programs. Since we are running the wasm program inside web browser, we have to yield to the web browser in some interval, so emscripten_set_main_loop_arg provided by emscripten comes to help. To utilize this function, we have to update the rv_step function signature. In order to switch running ELF programs, we have to cancel the browser main loop using emscripten_cancel_main_loop and do some clean up to recollect memory. _indirect_rv_halt is introduced to halt the RISC-V instance without exposing the RISC-V instance. assets directory will be used to store some required static web files. assets/html/index.html is a default main page of the ported wasm rv32emu. In order to switch running ELF programs, we have to delay some time for finishing emscripten_cancel_main_loop. I set delay to 1 second for now. TODO: more clean up have to be done, e.g., clear canvas, terminal and sound thread. Related to: sysprog21#75
To deploy multithreading wasm on Github Pages, we have to make the server to response two headers which are "Cross-Origin-Opener-Policy" and "Cross-Origin-Embedder-Policy". Since we cannot directly control the response headers of the Github Pages, coi-serviceworker comes to help. Related: sysprog21#75
Since wasm is deployed on Github Pages, thus it is reasonable to use CI to deploy automatically. The CI supports two event: merged pull request and workflow_dispatch. The former only run after merging and latter is used to deploy by maintainer manually. It also verifies whether any associated files have changed. If so, carry out the CI; if not, stop. Related: sysprog21#75
ELF executables might be rebuilt when new features are supported by the rv32emu such as new SDL-related system calls for video games like Doom and Quake or newlib has been updated. ELF executables are also might be deleted. Change filter from "any_changed" to "any_modified" since the latter includes delete event and this can ensure that deleted ELF executables do not appear in the demo page. Close: sysprog21#405 See also: sysprog21#75
To enhance the user experience of using WebAssembly version of rv32emu, we shall provide a Makefile target (serve-wasm) to serve the WebAssembly demo page in an easy way. Since rv32emu leverages TCO (tail call optimization) when being compiled, so we have to tell if the user's browser supports TCO. Currently, only Chrome with MAJOR 112 and Firefox with MAJOR 121 supports TCO execution runtime, thus we focus on this two browsers. Also, use color text to remind the users if they need to update their Chrome or Firefox. See also: sysprog21#75
iamlouk/riscv64-sim is written in C and capable of being translated into WebAssembly. Check its web demo. Some web-based (open source) RISC-V simulations:
The goal of this task is to expand
rv32emu
to the simulator, which will be used to teach computer architecture fundamentals, based on the existing RISC-V codebase.Incomplete attempt: rv32emu-wasm
The text was updated successfully, but these errors were encountered: