summaryrefslogtreecommitdiff
path: root/src/wasm-js.cpp
blob: c0b2aadc2c4c7e1f1e8d12bbb9213b3c3adc8591 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

//
// wasm intepreter for asm2wasm output, in a js environment. receives asm.js,
// generates a runnable module suitable as a polyfill.
//
// this polyfills an emscripten --separate-asm *.asm.js file, as a drop-in
// replacement. it writes the wasm module to Module.asm, and sets Module.buffer.
//

#include <emscripten.h>

#include "asm2wasm.h"
#include "wasm-interpreter.h"

using namespace cashew;
using namespace wasm;

// receives asm.js code, parses into wasm and returns an instance handle.
// this creates a module, an external interface, and a module instance,
// all of which are then the responsibility of the caller to free.
// note: this modifies the input.
extern "C" ModuleInstance* EMSCRIPTEN_KEEPALIVE load_asm(char *input) {
  // emcc --separate-asm modules look like
  //
  //    Module["asm"] = (function(global, env, buffer) {
  //      ..
  //    });
  //
  // we need to clean that up.
  size_t num = strlen(input);
  assert(*input == 'M');
  while (*input != 'f') {
    input++;
    num--;
  }
  char *end = input + num - 1;
  while (*end != '}') {
    *end = 0;
    end--;
  }

  if (debug) std::cerr << "parsing...\n";
  cashew::Parser<Ref, DotZeroValueBuilder> builder;
  Ref asmjs = builder.parseToplevel(input);

  Module* wasm = new Module();

  if (debug) std::cerr << "wasming...\n";
  Asm2WasmBuilder asm2wasm(*wasm);
  asm2wasm.processAsm(asmjs);

  if (debug) std::cerr << "optimizing...\n";
  asm2wasm.optimize();

  if (debug) std::cerr << "returning instance.\n";

  struct JSExternalInterface : ModuleInstance::ExternalInterface {
    Literal callImport(Import *import, ModuleInstance::LiteralList& arguments) override {
    }

    Literal load(Load* load, Literal ptr) override {
      size_t addr = ptr.geti32();
      assert(load->align == load->bytes);
      if (!load->float_) {
        if (load->bytes == 1) {
          if (load->signed_) {
            return Literal(EM_ASM_INT({ return Module['instance'].parent['HEAP8'][$0] }, addr));
          } else {
            return Literal(EM_ASM_INT({ return Module['instance'].parent['HEAPU8'][$0] }, addr));
          }
        } else if (load->bytes == 2) {
          if (load->signed_) {
            return Literal(EM_ASM_INT({ return Module['instance'].parent['HEAP16'][$0] }, addr));
          } else {
            return Literal(EM_ASM_INT({ return Module['instance'].parent['HEAPU16'][$0] }, addr));
          }
        } else if (load->bytes == 4) {
          if (load->signed_) {
            return Literal(EM_ASM_INT({ return Module['instance'].parent['HEAP32'][$0] }, addr));
          } else {
            return Literal(EM_ASM_INT({ return Module['instance'].parent['HEAPU32'][$0] }, addr));
          }
        }
        abort();
      } else {
        if (load->bytes == 4) {
          return Literal(EM_ASM_DOUBLE({ return Module['instance'].parent['HEAPF32'][$0] }, addr));
        } else if (load->bytes == 8) {
          return Literal(EM_ASM_DOUBLE({ return Module['instance'].parent['HEAPF64'][$0] }, addr));
        }
        abort();
      }
    }

    void store(Store* store, Literal ptr, Literal value) override {
      size_t addr = ptr.geti32();
      assert(store->align == store->bytes);
      if (!store->float_) {
        if (store->bytes == 1) {
          EM_ASM_INT({ Module['instance'].parent['HEAP8'][$0] = $1 }, addr, value.geti32());
        } else if (store->bytes == 2) {
          EM_ASM_INT({ Module['instance'].parent['HEAP16'][$0] = $1 }, addr, value.geti32());
        } else if (store->bytes == 4) {
          EM_ASM_INT({ Module['instance'].parent['HEAP32'][$0] = $1 }, addr, value.geti32());
        }
        abort();
      } else {
        if (store->bytes == 4) {
          EM_ASM_DOUBLE({ Module['instance'].parent['HEAPF32'][$0] = $1 }, addr, value.getf64());
        } else if (store->bytes == 8) {
          EM_ASM_DOUBLE({ Module['instance'].parent['HEAPF64'][$0] = $1 }, addr, value.getf64());
        }
        abort();
      }
    }
  };

  return new ModuleInstance(*wasm, new JSExternalInterface());
}