master
  1#ifndef FUNC_GEN_H
  2#define FUNC_GEN_H
  3
  4#include "panic.h"
  5#include "wasm.h"
  6
  7#include <inttypes.h>
  8#include <stdbool.h>
  9#include <stdint.h>
 10#include <stdio.h>
 11#include <stdlib.h>
 12#include <string.h>
 13
 14struct Block {
 15    uint32_t type;
 16    uint32_t label;
 17    uint32_t stack_i;
 18    uint32_t reuse_i;
 19};
 20
 21struct FuncGen {
 22    int8_t *type;
 23    uint32_t *reuse;
 24    uint32_t *stack;
 25    struct Block *block;
 26    uint32_t type_i;
 27    uint32_t reuse_i;
 28    uint32_t stack_i;
 29    uint32_t block_i;
 30    uint32_t type_len;
 31    uint32_t reuse_len;
 32    uint32_t stack_len;
 33    uint32_t block_len;
 34};
 35
 36static void FuncGen_init(struct FuncGen *self) {
 37    memset(self, 0, sizeof(struct FuncGen));
 38}
 39
 40static void FuncGen_reset(struct FuncGen *self) {
 41    self->type_i = 0;
 42    self->reuse_i = 0;
 43    self->stack_i = 0;
 44    self->block_i = 0;
 45}
 46
 47static void FuncGen_free(struct FuncGen *self) {
 48    free(self->block);
 49    free(self->stack);
 50    free(self->reuse);
 51    free(self->type);
 52}
 53
 54static void FuncGen_outdent(struct FuncGen *self, FILE *out) {
 55    for (uint32_t i = 0; i < self->block_i; i += 1) fputs("    ", out);
 56}
 57
 58static void FuncGen_indent(struct FuncGen *self, FILE *out) {
 59    FuncGen_outdent(self, out);
 60    fputs("    ", out);
 61}
 62
 63static void FuncGen_cont(struct FuncGen *self, FILE *out) {
 64    FuncGen_indent(self, out);
 65    fputs("    ", out);
 66}
 67
 68static uint32_t FuncGen_localAlloc(struct FuncGen *self, int8_t type) {
 69    if (self->type_i == self->type_len) {
 70        self->type_len += 10;
 71        self->type_len *= 2;
 72        self->type = realloc(self->type, sizeof(int8_t) * self->type_len);
 73        if (self->type == NULL) panic("out of memory");
 74    }
 75    uint32_t local_idx = self->type_i;
 76    self->type[local_idx] = type;
 77    self->type_i += 1;
 78    return local_idx;
 79}
 80
 81static enum WasmValType FuncGen_localType(const struct FuncGen *self, uint32_t local_idx) {
 82    return self->type[local_idx];
 83}
 84
 85static uint32_t FuncGen_localDeclare(struct FuncGen *self, FILE *out, enum WasmValType val_type) {
 86    uint32_t local_idx = FuncGen_localAlloc(self, (int8_t)val_type);
 87    fprintf(out, "%s l%" PRIu32, WasmValType_toC(val_type), local_idx);
 88    return local_idx;
 89}
 90
 91static uint32_t FuncGen_reuseTop(const struct FuncGen *self) {
 92    return self->block_i > 0 ? self->block[self->block_i - 1].reuse_i : 0;
 93}
 94
 95static void FuncGen_reuseReset(struct FuncGen *self) {
 96    self->reuse_i = FuncGen_reuseTop(self);
 97}
 98
 99static uint32_t FuncGen_reuseLocal(struct FuncGen *self, FILE *out, enum WasmValType val_type) {
100    for (uint32_t i = FuncGen_reuseTop(self); i < self->reuse_i; i += 1) {
101        uint32_t local_idx = self->reuse[i];
102        if (FuncGen_localType(self, local_idx) == val_type) {
103            self->reuse_i -= 1;
104            self->reuse[i] = self->reuse[self->reuse_i];
105            fprintf(out, "l%" PRIu32, local_idx);
106            return local_idx;
107        }
108    }
109    return FuncGen_localDeclare(self, out, val_type);
110}
111
112static void FuncGen_stackPush(struct FuncGen *self, FILE *out, enum WasmValType val_type) {
113    if (self->stack_i == self->stack_len) {
114        self->stack_len += 10;
115        self->stack_len *= 2;
116        self->stack = realloc(self->stack, sizeof(uint32_t) * self->stack_len);
117        if (self->stack == NULL) panic("out of memory");
118    }
119    FuncGen_indent(self, out);
120    self->stack[self->stack_i] = FuncGen_reuseLocal(self, out, val_type);
121    self->stack_i += 1;
122    fputs(" = ", out);
123}
124
125static uint32_t FuncGen_stackAt(const struct FuncGen *self, uint32_t stack_idx) {
126    return self->stack[self->stack_i - 1 - stack_idx];
127}
128
129static uint32_t FuncGen_stackPop(struct FuncGen *self) {
130    if (self->reuse_i == self->reuse_len) {
131        self->reuse_len += 10;
132        self->reuse_len *= 2;
133        self->reuse = realloc(self->reuse, sizeof(uint32_t) * self->reuse_len);
134        if (self->reuse == NULL) panic("out of memory");
135    }
136    self->stack_i -= 1;
137    uint32_t local_idx = self->stack[self->stack_i];
138    self->reuse[self->reuse_i] = local_idx;
139    self->reuse_i += 1;
140    return local_idx;
141}
142
143static void FuncGen_label(struct FuncGen *self, FILE *out, uint32_t label) {
144    FuncGen_indent(self, out);
145    fprintf(out, "goto l%" PRIu32 ";\n", label);
146    FuncGen_outdent(self, out);
147    fprintf(out, "l%" PRIu32 ":;\n", label);
148}
149
150static void FuncGen_blockBegin(struct FuncGen *self, FILE *out, enum WasmOpcode kind, int64_t type) {
151    if (self->block_i == self->block_len) {
152        self->block_len += 10;
153        self->block_len *= 2;
154        self->block = realloc(self->block, sizeof(struct Block) * self->block_len);
155        if (self->block == NULL) panic("out of memory");
156    }
157
158    if (kind == WasmOpcode_if) {
159        FuncGen_indent(self, out);
160        fprintf(out, "if (l%" PRIu32 ") {\n", FuncGen_stackPop(self));
161    } else if (EXTRA_BRACES) {
162        FuncGen_indent(self, out);
163        fputs("{\n", out);
164    }
165
166    uint32_t label = FuncGen_localAlloc(self, type < 0 ? ~(int8_t)kind : (int8_t)kind);
167    self->block[self->block_i].type = type < 0 ? ~type : type;
168    self->block[self->block_i].label = label;
169    self->block[self->block_i].stack_i = self->stack_i;
170    self->block[self->block_i].reuse_i = self->reuse_i;
171    self->block_i += 1;
172    if (kind == WasmOpcode_loop) FuncGen_label(self, out, label);
173
174    uint32_t reuse_top = FuncGen_reuseTop(self);
175    uint32_t reuse_n = self->reuse_i - reuse_top;
176    if (reuse_n > self->reuse_len - self->reuse_i) {
177        self->reuse_len += 10;
178        self->reuse_len *= 2;
179        self->reuse = realloc(self->reuse, sizeof(uint32_t) * self->reuse_len);
180        if (self->reuse == NULL) panic("out of memory");
181    }
182    if (reuse_n != 0) {
183        memcpy(&self->reuse[self->reuse_i], &self->reuse[reuse_top], sizeof(uint32_t) * reuse_n);
184        self->reuse_i += reuse_n;
185    }
186}
187
188static enum WasmOpcode FuncGen_blockKind(const struct FuncGen *self, uint32_t label_idx) {
189    int8_t kind = self->type[self->block[self->block_i - 1 - label_idx].label];
190    return (enum WasmOpcode)(kind < 0 ? ~kind : kind);
191}
192
193static int64_t FuncGen_blockType(const struct FuncGen *self, uint32_t label_idx) {
194    struct Block *block = &self->block[self->block_i - 1 - label_idx];
195    return self->type[block->label] < 0 ? ~(int64_t)block->type : (int64_t)block->type;
196}
197
198static uint32_t FuncGen_blockLabel(const struct FuncGen *self, uint32_t label_idx) {
199    return self->block[self->block_i - 1 - label_idx].label;
200}
201
202static void FuncGen_blockEnd(struct FuncGen *self, FILE *out) {
203    enum WasmOpcode kind = FuncGen_blockKind(self, 0);
204    uint32_t label = FuncGen_blockLabel(self, 0);
205    if (kind != WasmOpcode_loop) FuncGen_label(self, out, label);
206    self->block_i -= 1;
207
208    if (EXTRA_BRACES || kind == WasmOpcode_if) {
209        FuncGen_indent(self, out);
210        fputs("}\n", out);
211    }
212
213    if (self->stack_i != self->block[self->block_i].stack_i) {
214        FuncGen_indent(self, out);
215        fprintf(out, "// stack mismatch %u != %u\n", self->stack_i, self->block[self->block_i].stack_i);
216    }
217    self->stack_i = self->block[self->block_i].stack_i;
218
219    self->reuse_i = self->block[self->block_i].reuse_i;
220}
221
222static bool FuncGen_done(const struct FuncGen *self) {
223    return self->block_i == 0;
224}
225
226#endif /* FUNC_GEN_H */