Emscripten stack simplification (#1870)

This takes advantage of the recent memory simplification in emscripten, where JS static allocation is done at compile time. That means we know the stack's initial location at compile time, and can apply it. This is the binaryen side of that:

* asm2wasm support for asm.js globals with an initial value var X = Y; where Y is not 0 (which is what the stack now is).
* wasm-emscripten-finalize support for a flag --initial-stack-pointer=X, and remove the old code to import the stack's initial location.
diff --git a/auto_update_tests.py b/auto_update_tests.py
index 9d15647..bef6a89 100755
--- a/auto_update_tests.py
+++ b/auto_update_tests.py
@@ -87,7 +87,7 @@
       if ext != '.out' and not os.path.exists(out_path):
         continue
       cmd = (WASM_EMSCRIPTEN_FINALIZE +
-             [wast_path, '-S', '--global-base=568'] + ext_args)
+             [wast_path, '-S', '--global-base=568', '--initial-stack-pointer=16384'] + ext_args)
       actual = run_command(cmd)
       with open(out_path, 'w') as o:
         o.write(actual)
diff --git a/scripts/test/lld.py b/scripts/test/lld.py
index 03a0733..03c96d6 100755
--- a/scripts/test/lld.py
+++ b/scripts/test/lld.py
@@ -38,7 +38,7 @@
         continue
 
       cmd = (WASM_EMSCRIPTEN_FINALIZE +
-             [wast_path, '-S', '--global-base=568'] + ext_args)
+             [wast_path, '-S', '--global-base=568', '--initial-stack-pointer=16384'] + ext_args)
       actual = run_command(cmd)
 
       if not os.path.exists(expected_file):
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index d958c4b..c193791 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -426,13 +426,16 @@
   std::map<IString, MappedGlobal> mappedGlobals;
 
 private:
-  void allocateGlobal(IString name, Type type) {
+  void allocateGlobal(IString name, Type type, Literal value=Literal()) {
     assert(mappedGlobals.find(name) == mappedGlobals.end());
+    if (value.type == none) {
+      value = Literal::makeZero(type);
+    }
     mappedGlobals.emplace(name, MappedGlobal(type));
     wasm.addGlobal(builder.makeGlobal(
       name,
       type,
-      LiteralUtils::makeZero(type, wasm),
+      builder.makeConst(value),
       Builder::Mutable
     ));
   }
@@ -986,8 +989,7 @@
         Ref value = pair[1];
         if (value->isNumber()) {
           // global int
-          assert(value->getNumber() == 0);
-          allocateGlobal(name, Type::i32);
+          allocateGlobal(name, Type::i32, Literal(int32_t(value->getInteger())));
         } else if (value[0] == BINARY) {
           // int import
           assert(value[1] == OR && value[3]->isNumber() && value[3]->getNumber() == 0);
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index 3d74138..3ea3629 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -36,6 +36,8 @@
 using namespace wasm;
 
 int main(int argc, const char *argv[]) {
+  const uint64_t INVALID_BASE = -1;
+
   std::string infile;
   std::string outfile;
   std::string inputSourceMapFilename;
@@ -46,7 +48,8 @@
   bool debugInfo = false;
   bool legalizeJavaScriptFFI = true;
   unsigned numReservedFunctionPointers = 0;
-  uint64_t globalBase;
+  uint64_t globalBase = INVALID_BASE;
+  uint64_t initialStackPointer = INVALID_BASE;
   Options options("wasm-emscripten-finalize",
                   "Performs Emscripten-specific transforms on .wasm files");
   options
@@ -75,11 +78,16 @@
                                           const std::string &argument) {
              numReservedFunctionPointers = std::stoi(argument);
            })
-      .add("--global-base", "", "Where lld started to place globals",
+      .add("--global-base", "", "The address at which static globals were placed",
            Options::Arguments::One,
            [&globalBase](Options*, const std::string&argument ) {
              globalBase = std::stoull(argument);
            })
+      .add("--initial-stack-pointer", "", "The initial location of the stack pointer",
+           Options::Arguments::One,
+           [&initialStackPointer](Options*, const std::string&argument ) {
+             initialStackPointer = std::stoull(argument);
+           })
 
       .add("--input-source-map", "-ism", "Consume source map from the specified file",
            Options::Arguments::One,
@@ -141,6 +149,12 @@
   uint32_t dataSize = 0;
 
   if (!isSideModule) {
+    if (globalBase == INVALID_BASE) {
+      Fatal() << "globalBase must be set";
+    }
+    if (initialStackPointer == INVALID_BASE) {
+      Fatal() << "initialStackPointer must be set";
+    }
     Export* dataEndExport = wasm.getExport("__data_end");
     if (dataEndExport == nullptr) {
       Fatal() << "__data_end export not found";
@@ -189,7 +203,7 @@
   } else {
     generator.generateRuntimeFunctions();
     generator.generateMemoryGrowthFunction();
-    generator.generateStackInitialization();
+    generator.generateStackInitialization(initialStackPointer);
     // emscripten calls this by default for side libraries so we only need
     // to include in as a static ctor for main module case.
     if (wasm.getExportOrNull("__post_instantiate")) {
diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h
index 2b626a7..d078caf 100644
--- a/src/wasm-emscripten.h
+++ b/src/wasm-emscripten.h
@@ -36,7 +36,7 @@
 
   void generateRuntimeFunctions();
   Function* generateMemoryGrowthFunction();
-  void generateStackInitialization();
+  void generateStackInitialization(Address addr);
 
   // Create thunks for use with emscripten Runtime.dynCall. Creates one for each
   // signature in the indirect function table.
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index b18fe0c..88e66ea 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -156,19 +156,9 @@
   return growFunction;
 }
 
-void EmscriptenGlueGenerator::generateStackInitialization() {
-  // Replace a global with a constant initial value with an imported
-  // initial value, which emscripten JS will send us.
-  // TODO: with mutable imported globals, we can avoid adding another
-  //       global for the import.
-  Builder builder(wasm);
-  auto* import = builder.makeGlobal(STACK_INIT, i32, nullptr, Builder::Immutable);
-  import->module = ENV;
-  import->base = STACKTOP;
-  wasm.addGlobal(import);
+void EmscriptenGlueGenerator::generateStackInitialization(Address addr) {
   auto* stackPointer = getStackPointerGlobal();
-  assert(stackPointer->init->is<Const>());
-  stackPointer->init = builder.makeGetGlobal(import->name, i32);
+  stackPointer->init->cast<Const>()->value = Literal(int32_t(addr));
 }
 
 static bool hasI64ResultOrParam(FunctionType* ft) {
diff --git a/test/lld/duplicate_imports.wast.out b/test/lld/duplicate_imports.wast.out
index 6d3417b..58f5846 100644
--- a/test/lld/duplicate_imports.wast.out
+++ b/test/lld/duplicate_imports.wast.out
@@ -9,7 +9,6 @@
  (type $legaltype$puts2 (func (param i32 i32) (result i32)))
  (type $legaltype$invoke_ffd (func (param i32 f64 f64) (result f64)))
  (type $legaltype$invoke_ffd2 (func (param i32 f64 f64) (result f64)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "puts" (func $puts1 (param i32) (result i32)))
  (import "env" "puts" (func $legalimport$puts2 (param i32 i32) (result i32)))
  (import "env" "invoke_ffd" (func $legalimport$invoke_ffd (param i32 f64 f64) (result f64)))
@@ -17,7 +16,7 @@
  (memory $0 2)
  (data (i32.const 568) "Hello, world\00")
  (table $0 1 1 funcref)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66128))
  (global $global$2 i32 (i32.const 581))
  (export "memory" (memory $0))
diff --git a/test/lld/em_asm.wast.out b/test/lld/em_asm.wast.out
index 009dedc..92b1ea7 100644
--- a/test/lld/em_asm.wast.out
+++ b/test/lld/em_asm.wast.out
@@ -7,14 +7,13 @@
  (type $FUNCSIG$ii (func (param i32) (result i32)))
  (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
  (type $FUNCSIG$iii (func (param i32 i32) (result i32)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "emscripten_asm_const_i" (func $emscripten_asm_const_i (param i32) (result i32)))
  (import "env" "emscripten_asm_const_iii" (func $emscripten_asm_const_iii (param i32 i32 i32) (result i32)))
  (import "env" "emscripten_asm_const_ii" (func $emscripten_asm_const_ii (param i32 i32) (result i32)))
  (memory $0 2)
  (data (i32.const 568) "{ Module.print(\"Hello world\"); }\00{ return $0 + $1; }\00{ Module.print(\"Got \" + $0); }\00")
  (table $0 1 1 funcref)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66192))
  (global $global$2 i32 (i32.const 652))
  (export "memory" (memory $0))
diff --git a/test/lld/em_asm_table.wast.out b/test/lld/em_asm_table.wast.out
index 35e593d..ae3675d 100644
--- a/test/lld/em_asm_table.wast.out
+++ b/test/lld/em_asm_table.wast.out
@@ -4,12 +4,11 @@
  (type $FUNCSIG$vii (func (param i32 i32)))
  (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
  (import "env" "memory" (memory $0 8192))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "emscripten_log" (func $fimport$0 (param i32 i32)))
  (import "env" "emscripten_asm_const_iii" (func $emscripten_asm_const_iii (param i32 i32 i32) (result i32)))
  (table $0 159609 funcref)
  (elem (i32.const 1) $fimport$0 $emscripten_asm_const_iii)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 1048))
  (export "__data_end" (global $global$1))
  (export "stackSave" (func $stackSave))
diff --git a/test/lld/hello_world.wast.mem.out b/test/lld/hello_world.wast.mem.out
index 078620b..66dafd4 100644
--- a/test/lld/hello_world.wast.mem.out
+++ b/test/lld/hello_world.wast.mem.out
@@ -3,11 +3,10 @@
  (type $1 (func (result i32)))
  (type $2 (func))
  (type $FUNCSIG$ii (func (param i32) (result i32)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "puts" (func $puts (param i32) (result i32)))
  (memory $0 2)
  (table $0 1 1 funcref)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66128))
  (global $global$2 i32 (i32.const 581))
  (export "memory" (memory $0))
diff --git a/test/lld/hello_world.wast.out b/test/lld/hello_world.wast.out
index df89a57..1b1e199 100644
--- a/test/lld/hello_world.wast.out
+++ b/test/lld/hello_world.wast.out
@@ -3,12 +3,11 @@
  (type $1 (func (result i32)))
  (type $2 (func))
  (type $FUNCSIG$ii (func (param i32) (result i32)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "puts" (func $puts (param i32) (result i32)))
  (memory $0 2)
  (data (i32.const 568) "Hello, world\00")
  (table $0 1 1 funcref)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66128))
  (global $global$2 i32 (i32.const 581))
  (export "memory" (memory $0))
diff --git a/test/lld/init.wast.out b/test/lld/init.wast.out
index b702fb3..dfff8d5 100644
--- a/test/lld/init.wast.out
+++ b/test/lld/init.wast.out
@@ -1,11 +1,10 @@
 (module
  (type $0 (func))
  (type $1 (func (result i32)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (memory $0 2)
  (data (i32.const 568) "\00\00\00\00\00\00\00\00")
  (table $0 1 1 funcref)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66112))
  (global $global$2 i32 (i32.const 576))
  (export "memory" (memory $0))
diff --git a/test/lld/recursive.wast.out b/test/lld/recursive.wast.out
index 1c1180e..e8f32d9 100644
--- a/test/lld/recursive.wast.out
+++ b/test/lld/recursive.wast.out
@@ -3,12 +3,11 @@
  (type $1 (func (result i32)))
  (type $2 (func))
  (type $FUNCSIG$iii (func (param i32 i32) (result i32)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "printf" (func $printf (param i32 i32) (result i32)))
  (memory $0 2)
  (data (i32.const 568) "%d:%d\n\00Result: %d\n\00")
  (table $0 1 1 funcref)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66128))
  (global $global$2 i32 (i32.const 587))
  (export "memory" (memory $0))
diff --git a/test/lld/reserved_func_ptr.wast.jscall.out b/test/lld/reserved_func_ptr.wast.jscall.out
index 6242c3b..bc9b677 100644
--- a/test/lld/reserved_func_ptr.wast.jscall.out
+++ b/test/lld/reserved_func_ptr.wast.jscall.out
@@ -18,7 +18,6 @@
  (type $FUNCSIG$v (func))
  (type $FUNCSIG$vii (func (param i32 i32)))
  (type $FUNCSIG$viiii (func (param i32 i32 i32 i32)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "_Z4atoiPKc" (func $_Z4atoiPKc (param i32) (result i32)))
  (import "env" "jsCall_ddi" (func $jsCall_ddi (param i32 f64 i32) (result f64)))
  (import "env" "jsCall_fffi" (func $jsCall_fffi (param i32 f32 f32 i32) (result f32)))
@@ -29,7 +28,7 @@
  (memory $0 2)
  (table $0 21 21 funcref)
  (elem (i32.const 1) $_Z18address_taken_funciii $_Z19address_taken_func2iii $jsCall_ddi_0 $jsCall_ddi_1 $jsCall_ddi_2 $jsCall_fffi_0 $jsCall_fffi_1 $jsCall_fffi_2 $jsCall_iii_0 $jsCall_iii_1 $jsCall_iii_2 $jsCall_v_0 $jsCall_v_1 $jsCall_v_2 $jsCall_vi_0 $jsCall_vi_1 $jsCall_vi_2 $jsCall_viii_0 $jsCall_viii_1 $jsCall_viii_2)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66112))
  (global $global$2 i32 (i32.const 568))
  (export "memory" (memory $0))
diff --git a/test/lld/reserved_func_ptr.wast.out b/test/lld/reserved_func_ptr.wast.out
index 393178a..e0d9ea1 100644
--- a/test/lld/reserved_func_ptr.wast.out
+++ b/test/lld/reserved_func_ptr.wast.out
@@ -8,12 +8,11 @@
  (type $6 (func (param i32) (result i32)))
  (type $FUNCSIG$ii (func (param i32) (result i32)))
  (type $FUNCSIG$viii (func (param i32 i32 i32)))
- (import "env" "STACKTOP" (global $stack$init i32))
  (import "env" "_Z4atoiPKc" (func $_Z4atoiPKc (param i32) (result i32)))
  (memory $0 2)
  (table $0 3 3 funcref)
  (elem (i32.const 1) $_Z18address_taken_funciii $_Z19address_taken_func2iii)
- (global $global$0 (mut i32) (global.get $stack$init))
+ (global $global$0 (mut i32) (i32.const 16384))
  (global $global$1 i32 (i32.const 66112))
  (global $global$2 i32 (i32.const 568))
  (export "memory" (memory $0))
diff --git a/test/unit.asm.js b/test/unit.asm.js
index 05827ce..b62c7ae 100644
--- a/test/unit.asm.js
+++ b/test/unit.asm.js
@@ -30,6 +30,8 @@
   var HEAPF32 = new global.Float32Array(buffer);
   var HEAPF64 = new global.Float64Array(buffer);
 
+  var nonZero = 1337;
+
   function big_negative() {
     var temp = 0.0;
     temp = +-2147483648;
@@ -785,6 +787,7 @@
     emterpretify_assertions_safeHeap();
     call_emscripten_log();
     mod_detectSign(1.0, 2.31, 9.78);
+    nonZero = nonZero + 1 | 0;
   }
 
   function v() {
diff --git a/test/unit.fromasm b/test/unit.fromasm
index b1a24bf..c9e20da 100644
--- a/test/unit.fromasm
+++ b/test/unit.fromasm
@@ -26,6 +26,7 @@
  (global $Int (mut i32) (i32.const 0))
  (global $Double (mut f64) (f64.const 0))
  (global $n (mut i32) (global.get $n$asm2wasm$import))
+ (global $nonZero (mut i32) (i32.const 1337))
  (global $exportedNumber i32 (i32.const 42))
  (export "big_negative" (func $big_negative))
  (export "pick" (func $big_negative))
@@ -1162,6 +1163,12 @@
     )
    )
   )
+  (global.set $nonZero
+   (i32.add
+    (global.get $nonZero)
+    (i32.const 1)
+   )
+  )
  )
  (func $vi (; 55 ;) (; has Stack IR ;) (param $0 i32)
   (nop)
diff --git a/test/unit.fromasm.clamp b/test/unit.fromasm.clamp
index 58cf67a..deec92f 100644
--- a/test/unit.fromasm.clamp
+++ b/test/unit.fromasm.clamp
@@ -24,6 +24,7 @@
  (global $Int (mut i32) (i32.const 0))
  (global $Double (mut f64) (f64.const 0))
  (global $n (mut i32) (global.get $n$asm2wasm$import))
+ (global $nonZero (mut i32) (i32.const 1337))
  (global $exportedNumber i32 (i32.const 42))
  (export "big_negative" (func $big_negative))
  (export "pick" (func $big_negative))
@@ -1238,6 +1239,12 @@
     )
    )
   )
+  (global.set $nonZero
+   (i32.add
+    (global.get $nonZero)
+    (i32.const 1)
+   )
+  )
  )
  (func $vi (; 57 ;) (; has Stack IR ;) (param $0 i32)
   (nop)
diff --git a/test/unit.fromasm.clamp.no-opts b/test/unit.fromasm.clamp.no-opts
index 7a30149..95cb543 100644
--- a/test/unit.fromasm.clamp.no-opts
+++ b/test/unit.fromasm.clamp.no-opts
@@ -31,6 +31,7 @@
  (global $tempDoublePtr (mut i32) (global.get $tempDoublePtr$asm2wasm$import))
  (global $n (mut i32) (global.get $n$asm2wasm$import))
  (global $STACKTOP (mut i32) (global.get $STACKTOP$asm2wasm$import))
+ (global $nonZero (mut i32) (i32.const 1337))
  (global $exportedNumber i32 (i32.const 42))
  (export "big_negative" (func $big_negative))
  (export "pick" (func $exportMe))
@@ -2215,6 +2216,12 @@
     (f64.const 9.78)
    )
   )
+  (global.set $nonZero
+   (i32.add
+    (global.get $nonZero)
+    (i32.const 1)
+   )
+  )
  )
  (func $v (; 83 ;)
   (nop)
diff --git a/test/unit.fromasm.imprecise b/test/unit.fromasm.imprecise
index 869873e..7ff7a4c 100644
--- a/test/unit.fromasm.imprecise
+++ b/test/unit.fromasm.imprecise
@@ -22,6 +22,7 @@
  (global $Int (mut i32) (i32.const 0))
  (global $Double (mut f64) (f64.const 0))
  (global $n (mut i32) (global.get $n$asm2wasm$import))
+ (global $nonZero (mut i32) (i32.const 1337))
  (global $exportedNumber i32 (i32.const 42))
  (export "big_negative" (func $big_negative))
  (export "pick" (func $big_negative))
@@ -1141,6 +1142,12 @@
     (f64.const 1)
    )
   )
+  (global.set $nonZero
+   (i32.add
+    (global.get $nonZero)
+    (i32.const 1)
+   )
+  )
  )
  (func $vi (; 54 ;) (; has Stack IR ;) (param $0 i32)
   (nop)
diff --git a/test/unit.fromasm.imprecise.no-opts b/test/unit.fromasm.imprecise.no-opts
index 74dd7ef..69d1ff2 100644
--- a/test/unit.fromasm.imprecise.no-opts
+++ b/test/unit.fromasm.imprecise.no-opts
@@ -31,6 +31,7 @@
  (global $tempDoublePtr (mut i32) (global.get $tempDoublePtr$asm2wasm$import))
  (global $n (mut i32) (global.get $n$asm2wasm$import))
  (global $STACKTOP (mut i32) (global.get $STACKTOP$asm2wasm$import))
+ (global $nonZero (mut i32) (i32.const 1337))
  (global $exportedNumber i32 (i32.const 42))
  (export "big_negative" (func $big_negative))
  (export "pick" (func $exportMe))
@@ -2100,6 +2101,12 @@
     (f64.const 9.78)
    )
   )
+  (global.set $nonZero
+   (i32.add
+    (global.get $nonZero)
+    (i32.const 1)
+   )
+  )
  )
  (func $v (; 78 ;)
   (nop)
diff --git a/test/unit.fromasm.no-opts b/test/unit.fromasm.no-opts
index 561062f..fb82b57 100644
--- a/test/unit.fromasm.no-opts
+++ b/test/unit.fromasm.no-opts
@@ -33,6 +33,7 @@
  (global $tempDoublePtr (mut i32) (global.get $tempDoublePtr$asm2wasm$import))
  (global $n (mut i32) (global.get $n$asm2wasm$import))
  (global $STACKTOP (mut i32) (global.get $STACKTOP$asm2wasm$import))
+ (global $nonZero (mut i32) (i32.const 1337))
  (global $exportedNumber i32 (i32.const 42))
  (export "big_negative" (func $big_negative))
  (export "pick" (func $exportMe))
@@ -2141,6 +2142,12 @@
     (f64.const 9.78)
    )
   )
+  (global.set $nonZero
+   (i32.add
+    (global.get $nonZero)
+    (i32.const 1)
+   )
+  )
  )
  (func $v (; 81 ;)
   (nop)