aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--flake.nix1
-rw-r--r--src/cli.c6
-rw-r--r--src/cli.h1
-rw-r--r--src/codegen_wasm.c119
-rw-r--r--src/codegen_wasm.h8
-rw-r--r--src/main.c11
-rw-r--r--tests/wasm.sh25
8 files changed, 170 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 6709299..514eef9 100644
--- a/Makefile
+++ b/Makefile
@@ -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 \
diff --git a/flake.nix b/flake.nix
index 82e041b..689cd59 100644
--- a/flake.nix
+++ b/flake.nix
@@ -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
diff --git a/src/cli.c b/src/cli.c
index 5448d3f..e6ae89f 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -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;
diff --git a/src/cli.h b/src/cli.h
index f52964c..d97874c 100644
--- a/src/cli.h
+++ b/src/cli.h
@@ -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
diff --git a/src/main.c b/src/main.c
index 92066cf..e8e1030 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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