diff options
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | flake.nix | 1 | ||||
| -rw-r--r-- | src/cli.c | 6 | ||||
| -rw-r--r-- | src/cli.h | 1 | ||||
| -rw-r--r-- | src/codegen_wasm.c | 119 | ||||
| -rw-r--r-- | src/codegen_wasm.h | 8 | ||||
| -rw-r--r-- | src/main.c | 11 | ||||
| -rw-r--r-- | tests/wasm.sh | 25 |
8 files changed, 170 insertions, 2 deletions
@@ -8,6 +8,7 @@ OBJECTS := \ $(BUILD_DIR)/ast.o \ $(BUILD_DIR)/cli.o \ $(BUILD_DIR)/codegen.o \ + $(BUILD_DIR)/codegen_wasm.o \ $(BUILD_DIR)/common.o \ $(BUILD_DIR)/fs.o \ $(BUILD_DIR)/io.o \ @@ -49,6 +49,7 @@ devShells.default = pkgs.mkShell { packages = [ pkgs.just + pkgs.wabt ]; # Disable some kinds of hardening to disable GCC optimization. # cf. https://nixos.wiki/wiki/C#Hardening_flags @@ -14,6 +14,7 @@ CliArgs* parse_cli_args(int argc, char** argv) { int positional_arguments_start = -1; bool opt_c = false; bool opt_E = false; + bool opt_wasm = false; bool opt_MMD = false; StrArray include_dirs; strings_init(&include_dirs); @@ -62,6 +63,8 @@ CliArgs* parse_cli_args(int argc, char** argv) { exit(0); } else if (strcmp(argv[i], "--std=gnu23") == 0) { // ignore --std=gnu23 + } else if (strcmp(argv[i], "--wasm") == 0) { + opt_wasm = true; } else { fatal_error("unknown option: %s", argv[i]); } @@ -73,10 +76,11 @@ CliArgs* parse_cli_args(int argc, char** argv) { CliArgs* a = calloc(1, sizeof(CliArgs)); a->input_filename = argv[positional_arguments_start]; a->output_filename = output_filename; - a->output_assembly = !output_filename || str_ends_with(output_filename, ".s"); + a->output_assembly = !output_filename || str_ends_with(output_filename, ".s") || opt_wasm; a->only_compile = opt_c; a->preprocess_only = opt_E; a->totally_deligate_to_gcc = false; + a->wasm = opt_wasm; a->gcc_command = NULL; a->generate_deps = opt_MMD; a->include_dirs = include_dirs; @@ -11,6 +11,7 @@ typedef struct { bool preprocess_only; bool generate_deps; bool totally_deligate_to_gcc; + bool wasm; const char* gcc_command; StrArray include_dirs; } CliArgs; diff --git a/src/codegen_wasm.c b/src/codegen_wasm.c new file mode 100644 index 0000000..1072b67 --- /dev/null +++ b/src/codegen_wasm.c @@ -0,0 +1,119 @@ +#include <stdlib.h> +#include <string.h> +#include "codegen.h" +#include "common.h" +#include "preprocess.h" + +typedef enum { + GenMode_lval, + GenMode_rval, +} GenMode; + +typedef struct { + FILE* out; + int next_label; + int* loop_labels; + AstNode* current_func; + int switch_label; +} CodeGen; + +static CodeGen* codegen_new(FILE* out) { + CodeGen* g = calloc(1, sizeof(CodeGen)); + g->out = out; + g->next_label = 1; + g->loop_labels = calloc(1024, sizeof(int)); + g->switch_label = -1; + return g; +} + +static void codegen_expr(CodeGen* g, AstNode* ast, GenMode gen_mode); +static void codegen_stmt(CodeGen* g, AstNode* ast); + +static void codegen_func_prologue(CodeGen* g, AstNode* ast) { + for (int i = 0; i < ast->node_params->node_len; ++i) { + fprintf(g->out, " (param $l_%s i32)", ast->node_params->node_items[i].name); + } + fprintf(g->out, " (result i32)\n"); +} + +static void codegen_func_epilogue(CodeGen* g, AstNode* ast) { +} + +static void codegen_binary_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) { + codegen_expr(g, ast->node_lhs, gen_mode); + codegen_expr(g, ast->node_rhs, gen_mode); + if (ast->node_op == TokenKind_plus) { + fprintf(g->out, "i32.add\n"); + } else { + unreachable(); + } +} + +static void codegen_lvar(CodeGen* g, AstNode* ast, GenMode gen_mode) { + fprintf(g->out, " local.get $l_%s\n", ast->name); +} + +static void codegen_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) { + if (ast->kind == AstNodeKind_binary_expr) { + codegen_binary_expr(g, ast, gen_mode); + } else if (ast->kind == AstNodeKind_lvar) { + codegen_lvar(g, ast, gen_mode); + } else { + unreachable(); + } +} + +static void codegen_return_stmt(CodeGen* g, AstNode* ast) { + if (ast->node_expr) { + codegen_expr(g, ast->node_expr, GenMode_rval); + } + codegen_func_epilogue(g, ast); +} + +static void codegen_nop(CodeGen* g, AstNode* ast) { +} + +static void codegen_block_stmt(CodeGen* g, AstNode* ast) { + for (int i = 0; i < ast->node_len; ++i) { + AstNode* stmt = ast->node_items + i; + codegen_stmt(g, stmt); + } +} + +static void codegen_stmt(CodeGen* g, AstNode* ast) { + if (ast->kind == AstNodeKind_list) { + codegen_block_stmt(g, ast); + } else if (ast->kind == AstNodeKind_return_stmt) { + codegen_return_stmt(g, ast); + } else if (ast->kind == AstNodeKind_nop) { + codegen_nop(g, ast); + } else { + unreachable(); + } +} + +static void codegen_func(CodeGen* g, AstNode* ast) { + g->current_func = ast; + + fprintf(g->out, "(func (export \"%s\") \n", ast->name); + + codegen_func_prologue(g, ast); + codegen_stmt(g, ast->node_body); + codegen_func_epilogue(g, ast); + + fprintf(g->out, ")\n"); + g->current_func = NULL; +} + +void codegen_wasm(Program* prog, FILE* out) { + CodeGen* g = codegen_new(out); + + fprintf(g->out, "(module\n"); + + for (int i = 0; i < prog->funcs->node_len; ++i) { + AstNode* func = prog->funcs->node_items + i; + codegen_func(g, func); + } + + fprintf(g->out, ")\n"); +} diff --git a/src/codegen_wasm.h b/src/codegen_wasm.h new file mode 100644 index 0000000..1089154 --- /dev/null +++ b/src/codegen_wasm.h @@ -0,0 +1,8 @@ +#ifndef DUCC_CODEGEN_WASM_H +#define DUCC_CODEGEN_WASM_H + +#include "ast.h" + +void codegen_wasm(Program* prog, FILE* out); + +#endif @@ -1,6 +1,7 @@ #include "ast.h" #include "cli.h" #include "codegen.h" +#include "codegen_wasm.h" #include "common.h" #include "fs.h" #include "io.h" @@ -44,9 +45,17 @@ int main(int argc, char** argv) { assembly_filename = temp_filename; } FILE* assembly_file = assembly_filename ? fopen(assembly_filename, "wb") : stdout; - codegen(prog, assembly_file); + if (cli_args->wasm) { + codegen_wasm(prog, assembly_file); + } else { + codegen(prog, assembly_file); + } fclose(assembly_file); + if (cli_args->wasm) { + return 0; + } + if (!cli_args->output_assembly) { char cmd_buf[256]; if (cli_args->only_compile) { diff --git a/tests/wasm.sh b/tests/wasm.sh new file mode 100644 index 0000000..d024989 --- /dev/null +++ b/tests/wasm.sh @@ -0,0 +1,25 @@ +cat > main.c <<'EOF' +int add(int a, int b) { + return a + b; +} +EOF + +"$ducc" "${CFLAGS:-}" --wasm -o main.wat main.c + +wat2wasm main.wat -o main.wasm + +cat > main.mjs <<'EOF' +import { readFile } from 'fs/promises'; + +const wasmBuffer = await readFile('./main.wasm'); +const { instance } = await WebAssembly.instantiate(wasmBuffer); + +console.log(instance.exports.add(3, 5)); +EOF +node main.mjs > output + +cat > expected <<'EOF' +8 +EOF + +diff -u expected output |
