From 6a6bb771d6d7786064331afab47943d65b0d4fd6 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 13:09:29 -0700
Subject: [PATCH 01/61] add fasta_float test

---
 tests/test_core.py | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tests/test_core.py b/tests/test_core.py
index dd5b1e39ad380..74569c7f5c0f9 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -8305,6 +8305,12 @@ def test_fasta(self):
         src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
         self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
 
+      if Settings.ASM_JS:
+        # test float support in asm
+        i, j = results[-1]
+        src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', 'float')
+        self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'))
+
   def test_whets(self):
     if not Settings.ASM_JS: return self.skip('mainly a test for asm validation here')
     self.do_run(open(path_from_root('tests', 'whets.cpp')).read(), 'Single Precision C Whetstone Benchmark')

From 3ff3a402d58a8c33151d6ce6767d34cb5bc9cc29 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 13:15:44 -0700
Subject: [PATCH 02/61] move to FROUND option for Math.fround

---
 emscripten.py      |  2 +-
 src/parseTools.js  |  4 ++--
 src/preamble.js    | 10 ++++------
 src/settings.js    |  2 +-
 tests/test_core.py |  1 +
 5 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/emscripten.py b/emscripten.py
index dbea6eb22baaa..8829a50fb4c9e 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -451,7 +451,7 @@ def fix_item(item):
     math_envs = ['Math.min'] # TODO: move min to maths
     asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs])
 
-    if settings['TO_FLOAT32']: maths += ['Math.toFloat32']
+    if settings['FROUND']: maths += ['Math.fround']
 
     basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
     if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
diff --git a/src/parseTools.js b/src/parseTools.js
index dae386f179a57..2272c85167ef8 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2139,8 +2139,8 @@ function makeIsNaN(value) {
 }
 
 function makeFloat(value, type) {
-  if (TO_FLOAT32 && type == 'float') {
-    return 'Math_toFloat32(' + value + ')';
+  if (FROUND && type == 'float') {
+    return 'Math_fround(' + value + ')';
   }
   return value;
 }
diff --git a/src/preamble.js b/src/preamble.js
index ee273f6a36eda..f00e59e0e723b 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -1074,11 +1074,9 @@ Math['imul'] = function(a, b) {
 #endif
 Math.imul = Math['imul'];
 
-#if TO_FLOAT32
-if (!Math['toFloat32']) Math['toFloat32'] = function(x) {
-  return x;
-};
-Math.toFloat32 = Math['toFloat32'];
+#if FROUND
+if (!Math['fround']) Math['fround'] = function(x) { return x };
+Math.fround = Math['fround'];
 #endif
 
 var Math_abs = Math.abs;
@@ -1096,7 +1094,7 @@ var Math_ceil = Math.ceil;
 var Math_floor = Math.floor;
 var Math_pow = Math.pow;
 var Math_imul = Math.imul;
-var Math_toFloat32 = Math.toFloat32;
+var Math_fround = Math.fround;
 var Math_min = Math.min;
 
 // A counter of dependencies for calling run(). If we need to
diff --git a/src/settings.js b/src/settings.js
index d2b47dc88c38f..3ea513cb1409e 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -115,7 +115,7 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i
 var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full precision, which means it is
                          // correct even if the value exceeds the JS double-integer limit of ~52 bits (otherwise,
                          // rounding will occur above that range).
-var TO_FLOAT32 = 0; // Use Math.toFloat32
+var FROUND = 0; // Use Math.fround (polyfilling when necessary)
 
 var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
                              // compiler. This potentially lets closure optimize the code better.
diff --git a/tests/test_core.py b/tests/test_core.py
index 74569c7f5c0f9..88d1e34cae8c5 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -8307,6 +8307,7 @@ def test_fasta(self):
 
       if Settings.ASM_JS:
         # test float support in asm
+        Settings.FROUND = 1
         i, j = results[-1]
         src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', 'float')
         self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'))

From 96b514ca183befe9bd800ce16f33a61c5f971627 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 13:29:00 -0700
Subject: [PATCH 03/61] use fround in asm initializers and coercions

---
 src/parseTools.js | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 2272c85167ef8..14683bf617248 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1194,11 +1194,9 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
 
 function asmInitializer(type, impl) {
   if (type in Runtime.FLOAT_TYPES) {
-    if (RUNNING_JS_OPTS) {
-      return '+0';
-    } else {
-      return '.0';
-    }
+    var ret = RUNNING_JS_OPTS ? '+0' : '.0';
+    if (FROUND && type === 'float') ret = 'Math_fround(' + ret + ')';
+    return ret;
   } else {
     return '0';
   }
@@ -1219,7 +1217,11 @@ function asmCoercion(value, type, signedness) {
           value = '(' + value + ')|0';
         }
       }
-      return '(+(' + value + '))';
+      if (FROUND && type === 'float') {
+        return 'Math_fround(' + value + ')';
+      } else {
+        return '(+(' + value + '))';
+      }
     }
   } else {
     return '((' + value + ')|0)';

From 845a37cffb7d134291d6e9245b03ba3cb01b4133 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 13:48:05 -0700
Subject: [PATCH 04/61] fround on numerical constants, and fix some related
 parse issues

---
 src/parseTools.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 14683bf617248..134c6a83002f8 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -479,10 +479,13 @@ function parseParamTokens(params) {
         segment[0].text += segment[1].text;
         segment.splice(1, 1); // TODO: merge tokens nicely
       }
+      var num = isNumber(segment[1].text);
+      var ident = parseNumerical(segment[1].text, segment[0].text);
+      if (!num) ident = toNiceIdent(ident);
       ret.push({
         intertype: 'value',
         type: segment[0].text,
-        ident: toNiceIdent(parseNumerical(segment[1].text, segment[0].text))
+        ident: ident
       });
       Types.needAnalysis[removeAllPointing(ret[ret.length-1].type)] = 0;
     }
@@ -569,7 +572,7 @@ function finalizeParam(param) {
     if (param.type == 'i64' && USE_TYPED_ARRAYS == 2) {
       return parseI64Constant(param.ident);
     }
-    var ret = toNiceIdent(param.ident);
+    var ret = param.ident;
     if (ret in Variables.globals) {
       ret = makeGlobalUse(ret);
     }
@@ -1177,6 +1180,7 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
   if (!ASM_JS) return value;
   // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
   if (type in Runtime.FLOAT_TYPES && isNumber(value) && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
+    if (FROUND && type === 'float') return 'Math_fround(' + value + ')';
     if (RUNNING_JS_OPTS) {
       return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
     } else {

From cba6d823fc2db9400621325ea0a87c1a3f3a913f Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 13:52:27 -0700
Subject: [PATCH 05/61] fix int to float conversions to differentiate float
 from double

---
 src/parseTools.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 134c6a83002f8..77925876df351 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2445,7 +2445,7 @@ function processMathop(item) {
     case 'fdiv': return makeFloat(getFastValue(idents[0], '/', idents[1], item.type), item.type);
     case 'fmul': return makeFloat(getFastValue(idents[0], '*', idents[1], item.type), item.type);
     case 'frem': return makeFloat(getFastValue(idents[0], '%', idents[1], item.type), item.type);
-    case 'uitofp': case 'sitofp': return asmCoercion(idents[0], 'double', op[0]);
+    case 'uitofp': case 'sitofp': return asmCoercion(idents[0], item.type, op[0]);
     case 'fptoui': case 'fptosi': return makeRounding(idents[0], bitsLeft, op === 'fptosi', true);
 
     // TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking

From 2a8b87bcc174a17390f0d3799de8a6d2d86c2820 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 14:13:20 -0700
Subject: [PATCH 06/61] simplify fround initializer

---
 src/parseTools.js | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 77925876df351..a6f2e35e6997c 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1198,9 +1198,8 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
 
 function asmInitializer(type, impl) {
   if (type in Runtime.FLOAT_TYPES) {
-    var ret = RUNNING_JS_OPTS ? '+0' : '.0';
-    if (FROUND && type === 'float') ret = 'Math_fround(' + ret + ')';
-    return ret;
+    if (FROUND && type === 'float') return 'Math_fround(0)';
+    return RUNNING_JS_OPTS ? '+0' : '.0';
   } else {
     return '0';
   }

From 80df6c605bfba43a46eb71cdcaa6ceba50a05871 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 14:36:22 -0700
Subject: [PATCH 07/61] respect fround in fptrunc/ext

---
 src/parseTools.js | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index a6f2e35e6997c..a61b5035b4acd 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2491,8 +2491,15 @@ function processMathop(item) {
       }
       // otherwise, fall through
     }
-    case 'fpext': case 'sext': return idents[0];
-    case 'fptrunc': return idents[0];
+    case 'sext': return idents[0];
+    case 'fpext': {
+      if (FROUND) return '+(' + idents[0] + ')';
+      return idents[0];
+    }
+    case 'fptrunc': {
+      if (FROUND) return 'Math_fround(' + idents[0] + ')';
+      return idents[0];
+    }
     case 'select': return idents[0] + '?' + asmEnsureFloat(idents[1], item.type) + ':' + asmEnsureFloat(idents[2], item.type);
     case 'ptrtoint': case 'inttoptr': {
       var ret = '';

From 7cc79ee531f2c44e5cbf3d071b205924578acffb Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 14:44:15 -0700
Subject: [PATCH 08/61] add fpext and fptrunc to list of llvm conversions, so
 we differentiate float/double in them

---
 src/modules.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/modules.js b/src/modules.js
index 854575e076716..814817a3bb8e3 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -18,7 +18,7 @@ var LLVM = {
   PHI_REACHERS: set('branch', 'switch', 'invoke', 'indirectbr'),
   EXTENDS: set('sext', 'zext'),
   COMPS: set('icmp', 'fcmp'),
-  CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui'),
+  CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui', 'fpext', 'fptrunc'),
   INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2
 };
 LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));

From c777166191bfef6d5ed3da760722ea9028983e6f Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 16:02:35 -0700
Subject: [PATCH 09/61] properly apply fround to all float constants, even if
 they are fractions and look like doubles

---
 src/parseTools.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index a61b5035b4acd..8322fb74ce3d4 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1178,9 +1178,10 @@ function makeVarDef(js) {
 
 function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
   if (!ASM_JS) return value;
+  if (!isNumber(value)) return value;
+  if (FROUND && type === 'float') return 'Math_fround(' + value + ')';
   // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
-  if (type in Runtime.FLOAT_TYPES && isNumber(value) && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
-    if (FROUND && type === 'float') return 'Math_fround(' + value + ')';
+  if (type in Runtime.FLOAT_TYPES && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
     if (RUNNING_JS_OPTS) {
       return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
     } else {

From c92280c3557c9faf182ffac60de856f1de6770ac Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 16:19:46 -0700
Subject: [PATCH 10/61] emit tempFloat global

---
 emscripten.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/emscripten.py b/emscripten.py
index 8829a50fb4c9e..76056491923c0 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -585,7 +585,7 @@ def math_fix(g):
   var undef = 0;
   var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
 ''' + ''.join(['''
-  var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs + '''
+  var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + ['  var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('FROUND') else '0.0')] + ['''
 // EMSCRIPTEN_START_FUNCS
 function stackAlloc(size) {
   size = size|0;

From ca3f2f004d51268fbf3a9455d2f0c5569b15599d Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 16:22:41 -0700
Subject: [PATCH 11/61] use float temporary in makeIsNaN when relevant

---
 src/parseTools.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 8322fb74ce3d4..f8d7e6ac8e6d6 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2139,8 +2139,8 @@ function makeRounding(value, bits, signed, floatConversion) {
   }
 }
 
-function makeIsNaN(value) {
-  if (ASM_JS) return makeInlineCalculation('((VALUE) != (VALUE))', value, 'tempDouble');
+function makeIsNaN(value, type) {
+  if (ASM_JS) return makeInlineCalculation('((VALUE) != (VALUE))', value, type === 'float' ? 'tempFloat' : 'tempDouble');
   return 'isNaN(' + value + ')';
 }
 
@@ -2477,8 +2477,8 @@ function processMathop(item) {
         case 'ult': case 'olt': return idents[0] + '<' + idents[1];
         case 'une': case 'one': return idents[0] + '!=' + idents[1];
         case 'ueq': case 'oeq': return idents[0] + '==' + idents[1];
-        case 'ord': return '!' + makeIsNaN(idents[0]) + '&!' + makeIsNaN(idents[1]);
-        case 'uno': return makeIsNaN(idents[0]) + '|' + makeIsNaN(idents[1]);
+        case 'ord': return '!' + makeIsNaN(idents[0], paramTypes[0]) + '&!' + makeIsNaN(idents[1], paramTypes[0]);
+        case 'uno': return makeIsNaN(idents[0], paramTypes[0]) + '|' + makeIsNaN(idents[1], paramTypes[0]);
         case 'true': return '1';
         default: throw 'Unknown fcmp variant: ' + variant;
       }

From 5041ed44a66938588e674b9f7c03b9281b99c4e1 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 16:26:58 -0700
Subject: [PATCH 12/61] simplify auto-return adding code, and make it generic
 so it can handle floats as well

---
 src/jsifier.js    | 15 ++-------------
 src/parseTools.js |  2 +-
 2 files changed, 3 insertions(+), 14 deletions(-)

diff --git a/src/jsifier.js b/src/jsifier.js
index b36e11ed0d3cb..7a2fdde0a6433 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -756,14 +756,7 @@ function JSify(data, functionsOnly, givenFunctions) {
           if (func.setjmpTable && !ASM_JS) {
             ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
           }
-          if (ASM_JS && func.returnType !== 'void') {
-            // Add a return
-            if (func.returnType in Runtime.FLOAT_TYPES) {
-              ret += ' return +0;\n';
-            } else {
-              ret += ' return 0;\n';
-            }
-          }
+          if (ASM_JS && func.returnType !== 'void') ret += '  return ' + asmInitializer(func.returnType) + ';\n'; // Add a return
         } else {
           ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0]);
         }
@@ -833,11 +826,7 @@ function JSify(data, functionsOnly, givenFunctions) {
       var lastReturn = func.JS.lastIndexOf('return ');
       if ((lastCurly < 0 && lastReturn < 0) || // no control flow, no return
           (lastCurly >= 0 && lastReturn < lastCurly)) { // control flow, no return past last join
-        if (func.returnType in Runtime.FLOAT_TYPES) {
-          func.JS += ' return +0;\n';
-        } else {
-          func.JS += ' return 0;\n';
-        }
+        func.JS += '  return ' + asmInitializer(func.returnType) + ';\n';
       }
     }
     func.JS += '}\n';
diff --git a/src/parseTools.js b/src/parseTools.js
index f8d7e6ac8e6d6..3489964f21915 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1197,7 +1197,7 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
   }
 }
 
-function asmInitializer(type, impl) {
+function asmInitializer(type) {
   if (type in Runtime.FLOAT_TYPES) {
     if (FROUND && type === 'float') return 'Math_fround(0)';
     return RUNNING_JS_OPTS ? '+0' : '.0';

From 296cd5f54cb598fad7c9e58776bac906da1add02 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 16:51:18 -0700
Subject: [PATCH 13/61] support fround in function tables and asm module
 support functions

---
 emscripten.py   | 22 +++++++---------------
 src/jsifier.js  |  2 +-
 src/modules.js  | 41 +++++++++++++++++++++++++++--------------
 tools/shared.py | 22 ++++++++++++++++++++++
 4 files changed, 57 insertions(+), 30 deletions(-)

diff --git a/emscripten.py b/emscripten.py
index 76056491923c0..24e38f4499d3e 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -11,6 +11,7 @@
 
 import os, sys, json, optparse, subprocess, re, time, multiprocessing, string, logging
 
+from tools import shared
 from tools import jsrun, cache as cache_module, tempfiles
 from tools.response_file import read_response_file
 
@@ -25,7 +26,6 @@ def get_configuration():
   if hasattr(get_configuration, 'configuration'):
     return get_configuration.configuration
 
-  from tools import shared
   configuration = shared.Configuration(environ=os.environ)
   get_configuration.configuration = configuration
   return configuration
@@ -425,8 +425,8 @@ def make_table(sig, raw):
       Counter.i += 1
       bad = 'b' + str(i)
       params = ','.join(['p%d' % p for p in range(len(sig)-1)])
-      coercions = ';'.join(['p%d = %sp%d%s' % (p, '+' if sig[p+1] != 'i' else '', p, '' if sig[p+1] != 'i' else '|0') for p in range(len(sig)-1)]) + ';'
-      ret = '' if sig[0] == 'v' else ('return %s0' % ('+' if sig[0] != 'i' else ''))
+      coercions = ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';'
+      ret = '' if sig[0] == 'v' else ('return %s' % shared.JS.make_initializer(sig[0], settings))
       start = raw.index('[')
       end = raw.rindex(']')
       body = raw[start+1:end].split(',')
@@ -476,18 +476,14 @@ def fix_item(item):
 
     asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
     # function tables
-    def asm_coerce(value, sig):
-      if sig == 'v': return value
-      return ('+' if sig != 'i' else '') + value + ('|0' if sig == 'i' else '')
-
     function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']]
     function_tables_impls = []
 
     for sig in last_forwarded_json['Functions']['tables'].iterkeys():
       args = ','.join(['a' + str(i) for i in range(1, len(sig))])
-      arg_coercions = ' '.join(['a' + str(i) + '=' + asm_coerce('a' + str(i), sig[i]) + ';' for i in range(1, len(sig))])
-      coerced_args = ','.join([asm_coerce('a' + str(i), sig[i]) for i in range(1, len(sig))])
-      ret = ('return ' if sig[0] != 'v' else '') + asm_coerce('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0])
+      arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))])
+      coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))])
+      ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings)
       function_tables_impls.append('''
   function dynCall_%s(index%s%s) {
     index = index|0;
@@ -497,7 +493,7 @@ def asm_coerce(value, sig):
 ''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret))
 
       for i in range(settings['RESERVED_FUNCTION_POINTERS']):
-        jsret = ('return ' if sig[0] != 'v' else '') + asm_coerce('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0])
+        jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0], settings)
         function_tables_impls.append('''
   function jsCall_%s_%s(%s) {
     %s
@@ -505,7 +501,6 @@ def asm_coerce(value, sig):
   }
 
 ''' % (sig, i, args, arg_coercions, jsret))
-      from tools import shared
       shared.Settings.copy(settings)
       asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n'
       basic_funcs.append('invoke_%s' % sig)
@@ -727,14 +722,12 @@ def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBU
       relooper = cache.get_path('relooper.js')
     settings.setdefault('RELOOPER', relooper)
     if not os.path.exists(relooper):
-      from tools import shared
       shared.Building.ensure_relooper(relooper)
   
   settings.setdefault('STRUCT_INFO', cache.get_path('struct_info.compiled.json'))
   struct_info = settings.get('STRUCT_INFO')
   
   if not os.path.exists(struct_info):
-    from tools import shared
     shared.Building.ensure_struct_info(struct_info)
   
   emscript(args.infile, settings, args.outfile, libraries, compiler_engine=compiler_engine,
@@ -833,7 +826,6 @@ def _main(environ):
     temp_files = tempfiles.TempFiles(temp_dir)
 
   if keywords.compiler is None:
-    from tools import shared
     keywords.compiler = shared.COMPILER_ENGINE
 
   if keywords.verbose is None:
diff --git a/src/jsifier.js b/src/jsifier.js
index 7a2fdde0a6433..79ad9c0374884 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1579,7 +1579,7 @@ function JSify(data, functionsOnly, givenFunctions) {
       returnType = getReturnType(type);
       if (callIdent in Functions.implementedFunctions) {
         // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as
-        var trueType = Functions.getSignatureReturnType(Functions.implementedFunctions[callIdent]);
+        var trueType = Functions.getSignatureType(Functions.implementedFunctions[callIdent][0]);
         if (trueType !== returnType && !isIdenticallyImplemented(trueType, returnType)) {
           if (VERBOSE) warnOnce('Fixing function call based on return type from signature, on ' + [callIdent, returnType, trueType]);
           returnType = trueType;
diff --git a/src/modules.js b/src/modules.js
index 814817a3bb8e3..2e6edeba4b882 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -253,13 +253,32 @@ var Functions = {
 
   aliases: {}, // in shared modules (MAIN_MODULE or SHARED_MODULE), a list of aliases for functions that have them
 
+  getSignatureLetter: function(type) {
+    switch(type) {
+      case 'float': return 'f';
+      case 'double': return 'd';
+      case 'void': return 'v';
+      default: return 'i';
+    }
+  },
+
+  getSignatureType: function(letter) {
+    switch(letter) {
+      case 'v': return 'void';
+      case 'i': return 'i32';
+      case 'f': return 'float';
+      case 'd': return 'double';
+      default: throw 'what is this sig? ' + sig;
+    }
+  },
+
   getSignature: function(returnType, argTypes, hasVarArgs) {
-    var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f');
+    var sig = Functions.getSignatureLetter(returnType);
     for (var i = 0; i < argTypes.length; i++) {
       var type = argTypes[i];
       if (!type) break; // varargs
       if (type in Runtime.FLOAT_TYPES) {
-        sig += 'f';
+        sig += Functions.getSignatureLetter(type);
       } else {
         var chunks = getNumIntChunks(type);
         for (var j = 0; j < chunks; j++) sig += 'i';
@@ -269,15 +288,6 @@ var Functions = {
     return sig;
   },
 
-  getSignatureReturnType: function(sig) {
-    switch(sig[0]) {
-      case 'v': return 'void';
-      case 'i': return 'i32';
-      case 'f': return 'double';
-      default: throw 'what is this sig? ' + sig;
-    }
-  },
-
   // Mark a function as needing indexing. Python will coordinate them all
   getIndex: function(ident, sig) {
     var ret;
@@ -353,14 +363,17 @@ var Functions = {
                 if (t[0] == 'i') {
                   retPre = 'return ';
                   retPost = '|0';
-                } else {
+                } else if (t[0] === 'd') {
                   retPre = 'return +';
+                } else {
+                  retPre = 'return Math_fround(';
+                  retPost = ')';
                 }
               }
               for (var j = 1; j < t.length; j++) {
                 args += (j > 1 ? ',' : '') + 'a' + j;
-                arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32') + ';';
-                call += (j > 1 ? ',' : '') + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32');
+                arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, Functions.getSignatureType(t[j])) + ';';
+                call += (j > 1 ? ',' : '') + asmCoercion('a' + j, Functions.getSignatureType(t[j]));
               }
               call += ')';
               if (short == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things');
diff --git a/tools/shared.py b/tools/shared.py
index 108a48a48087e..a923f88a3f079 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -1510,6 +1510,28 @@ class JS:
   def to_nice_ident(ident): # limited version of the JS function toNiceIdent
     return ident.replace('%', '$').replace('@', '_')
 
+  @staticmethod
+  def make_initializer(sig, settings=None):
+    settings = settings or Settings
+    if sig == 'i':
+      return '0'
+    elif sig == 'f' and settings.get('FROUND'):
+      return 'Math_fround(0)'
+    else:
+      return '+0'
+
+  @staticmethod
+  def make_coercion(value, sig, settings=None):
+    settings = settings or Settings
+    if sig == 'i':
+      return value + '|0'
+    elif sig == 'f' and settings.get('FROUND'):
+      return 'Math_fround(' + value + ')'
+    elif sig == 'd' or sig == 'f':
+      return '+' + value
+    else:
+      return value
+
   @staticmethod
   def make_extcall(sig, named=True):
     args = ','.join(['a' + str(i) for i in range(1, len(sig))])

From 24f2b64781720ada9821bfd6eb8deddd72242dcb Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 17:45:50 -0700
Subject: [PATCH 14/61] do not mangle hex float constants in function calls

---
 src/parseTools.js | 2 +-
 src/utility.js    | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 3489964f21915..455cbe8b78dbe 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -479,7 +479,7 @@ function parseParamTokens(params) {
         segment[0].text += segment[1].text;
         segment.splice(1, 1); // TODO: merge tokens nicely
       }
-      var num = isNumber(segment[1].text);
+      var num = isNumber(segment[1].text) || (segment[1].text[0] === '0'); // handle 0x... as well
       var ident = parseNumerical(segment[1].text, segment[0].text);
       if (!num) ident = toNiceIdent(ident);
       ret.push({
diff --git a/src/utility.js b/src/utility.js
index ac821a89924f0..62adb807cefaf 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -222,6 +222,7 @@ function mergeInto(obj, other) {
 }
 
 function isNumber(x) {
+  // XXX this does not handle 0xabc123 etc
   return x == parseFloat(x) || (typeof x == 'string' && x.match(/^-?\d+$/));
 }
 

From c9cd4323d8091da25c1a2e84aa5e27033a97b590 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 18:00:19 -0700
Subject: [PATCH 15/61] fix splitI64 use of float instead of double

---
 src/parseTools.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 455cbe8b78dbe..90970e1ffcac8 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -801,8 +801,8 @@ function splitI64(value, floatConversion) {
   var high = makeInlineCalculation(
     asmCoercion('Math_abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' +
       '(VALUE > ' + asmEnsureFloat('0', 'double') + ' ? ' +
-               asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0' +
-               ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'float') + ')', 'double')) + '>>>0' + 
+               asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'double') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'double') + ')', 'i32') + '>>>0' +
+               ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'double') + ')', 'double')) + '>>>0' + 
       ')' +
     ' : 0',
     value,

From 866039dc1f1c880399d3819a1194350d78c12a33 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 18:09:47 -0700
Subject: [PATCH 16/61] do not send floats to ffis

---
 src/modules.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/modules.js b/src/modules.js
index 2e6edeba4b882..dd622bee351c8 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -372,8 +372,9 @@ var Functions = {
               }
               for (var j = 1; j < t.length; j++) {
                 args += (j > 1 ? ',' : '') + 'a' + j;
-                arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, Functions.getSignatureType(t[j])) + ';';
-                call += (j > 1 ? ',' : '') + asmCoercion('a' + j, Functions.getSignatureType(t[j]));
+                var type = Functions.getSignatureType(t[j]);
+                arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, type) + ';';
+                call += (j > 1 ? ',' : '') + asmCoercion('a' + j, type === 'float' ? 'double' : type); // ffi arguments must be doubles if they are floats
               }
               call += ')';
               if (short == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things');

From e0053d0115e7028a990aa3d189209c3cc22919c6 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 18:20:58 -0700
Subject: [PATCH 17/61] detect NaN as a number

---
 src/utility.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/utility.js b/src/utility.js
index 62adb807cefaf..bec0eb8f4c579 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -222,8 +222,8 @@ function mergeInto(obj, other) {
 }
 
 function isNumber(x) {
-  // XXX this does not handle 0xabc123 etc
-  return x == parseFloat(x) || (typeof x == 'string' && x.match(/^-?\d+$/));
+  // XXX this does not handle 0xabc123 etc. We should likely also do x == parseInt(x) (which handles that), and remove hack |// handle 0x... as well|
+  return x == parseFloat(x) || (typeof x == 'string' && x.match(/^-?\d+$/)) || x === 'NaN';
 }
 
 function isArray(x) {

From 3351bf7019d1bba9d62426a4ec77d098775e3f08 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 25 Oct 2013 18:28:24 -0700
Subject: [PATCH 18/61] send and receive only doubles through ffis, not floats

---
 src/jsifier.js    | 12 +++++++++---
 src/parseTools.js |  4 ++++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/jsifier.js b/src/jsifier.js
index 79ad9c0374884..329b06c63e878 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1501,8 +1501,9 @@ function JSify(data, functionsOnly, givenFunctions) {
 
     args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
     if (ASM_JS) {
-      if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) {
-        args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) });
+      var ffiCall = shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable;
+      if (ffiCall) {
+        args = args.map(function(arg, i) { return asmCoercion(arg, ensureValidFFIType(argsTypes[i])) });
       } else {
         args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) });
       }
@@ -1615,7 +1616,12 @@ function JSify(data, functionsOnly, givenFunctions) {
 
     var ret = callIdent + '(' + args.join(',') + ')';
     if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && simpleIdent in Functions.libraryFunctions) {
-      ret = asmCoercion(ret, returnType);
+      if (ffiCall) {
+        ret = asmCoercion(ret, ensureValidFFIType(returnType));
+        if (FROUND && returnType === 'float') ret = asmCoercion(ret, 'float'); // receive double, then float it
+      } else {
+        ret = asmCoercion(ret, returnType);
+      }
       if (simpleIdent == 'abort' && funcData.returnType != 'void') {
         ret += '; return ' + asmCoercion('0', funcData.returnType); // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return
       }
diff --git a/src/parseTools.js b/src/parseTools.js
index 90970e1ffcac8..803208268925f 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2691,3 +2691,7 @@ function ensureVector(ident, base) {
   return ident == 0 ? base + '32x4.zero()' : ident;
 }
 
+function ensureValidFFIType(type) {
+  return type === 'float' ? 'double' : type; // ffi does not tolerate float XXX
+}
+

From 76014aec3125274b975196103259018f37785451 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 14:20:25 -0700
Subject: [PATCH 19/61] use doubles in makeBigInt

---
 src/runtime.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/runtime.js b/src/runtime.js
index 5d5cb43b47783..ed624fbf76142 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -82,8 +82,8 @@ var RuntimeGenerator = {
   // Rounding is inevitable if the number is large. This is a particular problem for small negative numbers
   // (-1 will be rounded!), so handle negatives separately and carefully
   makeBigInt: function(low, high, unsigned) {
-    var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))';
-    var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))';
+    var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
+    var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
     if (typeof unsigned === 'string') return '(' + unsigned + ' ? ' + unsignedRet + ' : ' + signedRet + ')';
     return unsigned ? unsignedRet : signedRet;
   }

From 2f2f3ca9fbbe5906bb68aa56edf7eca192ac9dc1 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 14:36:15 -0700
Subject: [PATCH 20/61] use asm ffi coercing for floats in load abort calls

---
 src/jsifier.js    | 5 ++---
 src/parseTools.js | 7 +++++++
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/jsifier.js b/src/jsifier.js
index 329b06c63e878..669cec7368ac9 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1324,7 +1324,7 @@ function JSify(data, functionsOnly, givenFunctions) {
         if (isNumber(item.ident)) {
           // Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source
           if (ASM_JS) {
-            return asmCoercion('abort(' + item.ident + ')', item.type);
+            return asmFFICoercion('abort(' + item.ident + ')', item.type);
           } else {
             item.assignTo = null;
             return 'throw "fault on read from ' + item.ident + '";';
@@ -1617,8 +1617,7 @@ function JSify(data, functionsOnly, givenFunctions) {
     var ret = callIdent + '(' + args.join(',') + ')';
     if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && simpleIdent in Functions.libraryFunctions) {
       if (ffiCall) {
-        ret = asmCoercion(ret, ensureValidFFIType(returnType));
-        if (FROUND && returnType === 'float') ret = asmCoercion(ret, 'float'); // receive double, then float it
+        ret = asmFFICoercion(ret, returnType);
       } else {
         ret = asmCoercion(ret, returnType);
       }
diff --git a/src/parseTools.js b/src/parseTools.js
index 803208268925f..9089a9e6864ac 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2695,3 +2695,10 @@ function ensureValidFFIType(type) {
   return type === 'float' ? 'double' : type; // ffi does not tolerate float XXX
 }
 
+// FFI return values must arrive as doubles, and we can force them to floats afterwards
+function asmFFICoercion(value, type) {
+  value = asmCoercion(value, ensureValidFFIType(type));
+  if (FROUND && type === 'float') value = asmCoercion(value, 'float');
+  return value;
+}
+

From 7b44c1589112d5f3df207b1fee5d19c49dab9cc9 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 14:38:33 -0700
Subject: [PATCH 21/61] cast to double before float in function pointer
 wrappers

---
 src/modules.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/modules.js b/src/modules.js
index dd622bee351c8..b8301d71d1275 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -366,8 +366,8 @@ var Functions = {
                 } else if (t[0] === 'd') {
                   retPre = 'return +';
                 } else {
-                  retPre = 'return Math_fround(';
-                  retPost = ')';
+                  retPre = 'return Math_fround(+(';
+                  retPost = '))';
                 }
               }
               for (var j = 1; j < t.length; j++) {

From 7c909b0db1a699f9a4f71189a56e51c065557a41 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 14:48:40 -0700
Subject: [PATCH 22/61] use double for emscripten_get_now, to not lose
 precision from performance.now etc.

---
 system/include/emscripten/emscripten.h | 2 +-
 tests/emscripten_get_now.cpp           | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h
index d30620eca762f..dd1e01a43edb3 100644
--- a/system/include/emscripten/emscripten.h
+++ b/system/include/emscripten/emscripten.h
@@ -203,7 +203,7 @@ void emscripten_get_canvas_size(int *width, int *height, int *isFullscreen);
  * absolute time, and is only meaningful in comparison to
  * other calls to this function. The unit is ms.
  */
-float emscripten_get_now();
+double emscripten_get_now();
 
 /*
  * Simple random number generation in [0, 1), maps to Math.random().
diff --git a/tests/emscripten_get_now.cpp b/tests/emscripten_get_now.cpp
index 17aa7d32db814..5ededb230509e 100644
--- a/tests/emscripten_get_now.cpp
+++ b/tests/emscripten_get_now.cpp
@@ -14,10 +14,10 @@ int main() {
   // b) Values returned by emscripten_get_now() are strictly nondecreasing.
   // c) emscripten_get_now() is able to return sub-millisecond precision timer values.
   bool detected_good_timer_precision = false;
-  float smallest_delta = 0.f;
+  double smallest_delta = 0.f;
   for(int x = 0; x < 1000; ++x) { // Have several attempts to find a good small delta, i.e. give time to JS engine to warm up the code and so on.
-    float t = emscripten_get_now();
-    float t2 = emscripten_get_now();
+    double t = emscripten_get_now();
+    double t2 = emscripten_get_now();
     for(int i = 0; i < 100 && t == t2; ++i) {
       t2 = emscripten_get_now();
     }

From c03de3f080fc942ea29b403c37c538697d047b6e Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 16:46:03 -0700
Subject: [PATCH 23/61] simplify wrapper asm coercions

---
 src/modules.js | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/src/modules.js b/src/modules.js
index b8301d71d1275..13cca9775c5c3 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -360,15 +360,9 @@ var Functions = {
             if (!wrapped[curr]) {
               var args = '', arg_coercions = '', call = short + '(', retPre = '', retPost = '';
               if (t[0] != 'v') {
-                if (t[0] == 'i') {
-                  retPre = 'return ';
-                  retPost = '|0';
-                } else if (t[0] === 'd') {
-                  retPre = 'return +';
-                } else {
-                  retPre = 'return Math_fround(+(';
-                  retPost = '))';
-                }
+                var temp = asmFFICoercion('X', Functions.getSignatureType(t[0])).split('X');
+                retPre = 'return ' + temp[0];
+                retPost = temp[1];
               }
               for (var j = 1; j < t.length; j++) {
                 args += (j > 1 ? ',' : '') + 'a' + j;

From 048595a2580c75e8cbfb8fc93ca0ea7c9c21dc45 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 18:11:04 -0700
Subject: [PATCH 24/61] ensure a dot in large float32 constants

---
 src/parseTools.js | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 9089a9e6864ac..1f4847172af90 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1176,21 +1176,29 @@ function makeVarDef(js) {
   return js;
 }
 
+function ensureDot(value) {
+  value = value.toString();
+  if (value.indexOf('.') >= 0 || /[IN]/.test(value)) return value; // if already dotted, or Infinity or NaN, nothing to do here
+  var e = value.indexOf('e');
+  if (e < 0) return value + '.0';
+  return value.substr(0, e) + '.0' + value.substr(e);
+}
+
 function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
   if (!ASM_JS) return value;
   if (!isNumber(value)) return value;
-  if (FROUND && type === 'float') return 'Math_fround(' + value + ')';
+  if (FROUND && type === 'float') {
+    // normally ok to just emit Math_fround(0), but if the constant is large we may need a .0 (if it can't fit in an int)
+    if (value == 0) return 'Math_fround(0)';
+    value = ensureDot(value);
+    return 'Math_fround(' + value + ')';
+  }
   // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
   if (type in Runtime.FLOAT_TYPES && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
     if (RUNNING_JS_OPTS) {
       return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
     } else {
-      // ensure a .
-      value = value.toString();
-      if (value.indexOf('.') >= 0 || /[IN]/.test(value)) return value; // if already dotted, or Infinity or NaN, nothing to do here
-      var e = value.indexOf('e');
-      if (e < 0) return value + '.0';
-      return value.substr(0, e) + '.0' + value.substr(e);
+      return ensureDot(value);
     }
   } else {
     return value;

From eee7d81f248104d2bde5d64ed1f3f5111f78b93d Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 18:13:27 -0700
Subject: [PATCH 25/61] fix uitofp of i64 to float32

---
 src/parseTools.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 1f4847172af90..79a8837383783 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2271,7 +2271,7 @@ function processMathop(item) {
       case 'lshr': {
 				throw 'shifts should have been legalized!';
       }
-      case 'uitofp': case 'sitofp': return RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u');
+      case 'uitofp': case 'sitofp': return makeFloat(RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u'), item.type);
       case 'fptoui': case 'fptosi': return finish(splitI64(idents[0], true));
       case 'icmp': {
         switch (variant) {

From 2e50e7ca8ae5f6e18894c74fe4d33c90e404c6bc Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 18:17:02 -0700
Subject: [PATCH 26/61] improve fasta float32 testing

---
 tests/test_core.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/tests/test_core.py b/tests/test_core.py
index 88d1e34cae8c5..7a53098c32350 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -8305,9 +8305,12 @@ def test_fasta(self):
         src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
         self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
 
-      if Settings.ASM_JS:
+      assert 'asm1' in test_modes
+      if self.run_name == 'asm1':
         # test float support in asm
-        Settings.FROUND = 1
+        print 'was fround', Settings.FROUND
+        Settings.FROUND = 1 - Settings.FROUND
+        print 'now fround variant', Settings.FROUND
         i, j = results[-1]
         src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', 'float')
         self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'))

From 0154b1effe2cde0ad81a1adcbe83b7c9d018dbbd Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 18:38:52 -0700
Subject: [PATCH 27/61] add test for precise float32 support, allow 3 modes of
 precise float32 support, and rename option to PRECISE_F32 to be consistent
 with other precision options

---
 emscripten.py      |  4 ++--
 src/parseTools.js  | 14 +++++++-------
 src/preamble.js    |  7 ++++++-
 src/settings.js    |  8 +++++++-
 tests/test_core.py | 29 +++++++++++++++++++++++++++++
 tools/shared.py    |  4 ++--
 6 files changed, 53 insertions(+), 13 deletions(-)

diff --git a/emscripten.py b/emscripten.py
index 24e38f4499d3e..3383ca662b3ff 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -451,7 +451,7 @@ def fix_item(item):
     math_envs = ['Math.min'] # TODO: move min to maths
     asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs])
 
-    if settings['FROUND']: maths += ['Math.fround']
+    if settings['PRECISE_F32']: maths += ['Math.fround']
 
     basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
     if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
@@ -580,7 +580,7 @@ def math_fix(g):
   var undef = 0;
   var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
 ''' + ''.join(['''
-  var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + ['  var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('FROUND') else '0.0')] + ['''
+  var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + ['  var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('PRECISE_F32') else '0.0')] + ['''
 // EMSCRIPTEN_START_FUNCS
 function stackAlloc(size) {
   size = size|0;
diff --git a/src/parseTools.js b/src/parseTools.js
index 79a8837383783..4dbabbb019fa0 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1187,7 +1187,7 @@ function ensureDot(value) {
 function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
   if (!ASM_JS) return value;
   if (!isNumber(value)) return value;
-  if (FROUND && type === 'float') {
+  if (PRECISE_F32 && type === 'float') {
     // normally ok to just emit Math_fround(0), but if the constant is large we may need a .0 (if it can't fit in an int)
     if (value == 0) return 'Math_fround(0)';
     value = ensureDot(value);
@@ -1207,7 +1207,7 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
 
 function asmInitializer(type) {
   if (type in Runtime.FLOAT_TYPES) {
-    if (FROUND && type === 'float') return 'Math_fround(0)';
+    if (PRECISE_F32 && type === 'float') return 'Math_fround(0)';
     return RUNNING_JS_OPTS ? '+0' : '.0';
   } else {
     return '0';
@@ -1229,7 +1229,7 @@ function asmCoercion(value, type, signedness) {
           value = '(' + value + ')|0';
         }
       }
-      if (FROUND && type === 'float') {
+      if (PRECISE_F32 && type === 'float') {
         return 'Math_fround(' + value + ')';
       } else {
         return '(+(' + value + '))';
@@ -2153,7 +2153,7 @@ function makeIsNaN(value, type) {
 }
 
 function makeFloat(value, type) {
-  if (FROUND && type == 'float') {
+  if (PRECISE_F32 && type == 'float') {
     return 'Math_fround(' + value + ')';
   }
   return value;
@@ -2502,11 +2502,11 @@ function processMathop(item) {
     }
     case 'sext': return idents[0];
     case 'fpext': {
-      if (FROUND) return '+(' + idents[0] + ')';
+      if (PRECISE_F32) return '+(' + idents[0] + ')';
       return idents[0];
     }
     case 'fptrunc': {
-      if (FROUND) return 'Math_fround(' + idents[0] + ')';
+      if (PRECISE_F32) return 'Math_fround(' + idents[0] + ')';
       return idents[0];
     }
     case 'select': return idents[0] + '?' + asmEnsureFloat(idents[1], item.type) + ':' + asmEnsureFloat(idents[2], item.type);
@@ -2706,7 +2706,7 @@ function ensureValidFFIType(type) {
 // FFI return values must arrive as doubles, and we can force them to floats afterwards
 function asmFFICoercion(value, type) {
   value = asmCoercion(value, ensureValidFFIType(type));
-  if (FROUND && type === 'float') value = asmCoercion(value, 'float');
+  if (PRECISE_F32 && type === 'float') value = asmCoercion(value, 'float');
   return value;
 }
 
diff --git a/src/preamble.js b/src/preamble.js
index f00e59e0e723b..8ab6d6040f799 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -1074,8 +1074,13 @@ Math['imul'] = function(a, b) {
 #endif
 Math.imul = Math['imul'];
 
-#if FROUND
+#if PRECISE_F32
+#if PRECISE_F32 == 1
+var froundBuffer = new Float32Array(1);
+if (!Math['fround']) Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] };
+#else // 2
 if (!Math['fround']) Math['fround'] = function(x) { return x };
+#endif
 Math.fround = Math['fround'];
 #endif
 
diff --git a/src/settings.js b/src/settings.js
index 3ea513cb1409e..de9ab46c21f10 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -115,7 +115,13 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i
 var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full precision, which means it is
                          // correct even if the value exceeds the JS double-integer limit of ~52 bits (otherwise,
                          // rounding will occur above that range).
-var FROUND = 0; // Use Math.fround (polyfilling when necessary)
+var PRECISE_F32 = 0; // 0: Use JS numbers for floating-point values. These are 64-bit and do not model C++
+                     //    floats exactly, which are 32-bit.
+                     // 1: Model C++ floats precisely, using Math.fround, polyfilling when necessary. This
+                     //    can be slow if the polyfill is used on heavy float32 computation.
+                     // 2: Model C++ floats precisely using Math.fround if available in the JS engine, otherwise
+                     //    use an empty polyfill. This will have less of a speed penalty than using the full
+                     //    polyfill in cases where engine support is not present.
 
 var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
                              // compiler. This potentially lets closure optimize the code better.
diff --git a/tests/test_core.py b/tests/test_core.py
index 7a53098c32350..f2376b5333d44 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -882,6 +882,35 @@ def test_double_i64_conversion(self):
     '''
     self.do_run(src, 'OK!\n');
 
+  def test_float32_precise(self):
+    assert 'asm1' in test_modes
+    if self.run_name != 'asm1': return self.skip('todo')
+
+    Settings.PRECISE_F32 = 1
+
+    src = r'''
+      #include <stdio.h>
+
+      int main(int argc, char **argv) {
+        float x = 1.23456789123456789;
+        float y = 5.20456089123406709;
+        while (argc > 10 || argc % 19 == 15) {
+          // confuse optimizer
+          x /= y;
+          y = 2*y - 1;
+          argc--;
+        }
+        x = x - y;
+        y = 3*y - x/2;
+        x = x*y;
+        y += 0.000000000123123123123;
+        x -= y/7.654;
+        printf("\n%.20f, %.20f\n", x, y);
+        return 0;
+      }
+    '''
+    self.do_run(src, '\n-72.16590881347656250000, 17.59867858886718750000\n')
+
   def test_negative_zero(self):
     src = r'''
       #include <stdio.h>
diff --git a/tools/shared.py b/tools/shared.py
index a923f88a3f079..6ba6ef50c6fcd 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -1515,7 +1515,7 @@ def make_initializer(sig, settings=None):
     settings = settings or Settings
     if sig == 'i':
       return '0'
-    elif sig == 'f' and settings.get('FROUND'):
+    elif sig == 'f' and settings.get('PRECISE_F32'):
       return 'Math_fround(0)'
     else:
       return '+0'
@@ -1525,7 +1525,7 @@ def make_coercion(value, sig, settings=None):
     settings = settings or Settings
     if sig == 'i':
       return value + '|0'
-    elif sig == 'f' and settings.get('FROUND'):
+    elif sig == 'f' and settings.get('PRECISE_F32'):
       return 'Math_fround(' + value + ')'
     elif sig == 'd' or sig == 'f':
       return '+' + value

From 75af3c1ac8dba93b508b8b431bef9a35e6b054c3 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Tue, 29 Oct 2013 18:44:35 -0700
Subject: [PATCH 28/61] add asm1f test mode

---
 tests/runner.py    |  2 +-
 tests/test_core.py | 16 ++--------------
 2 files changed, 3 insertions(+), 15 deletions(-)

diff --git a/tests/runner.py b/tests/runner.py
index 867f71139e9a2..483df36e3f410 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -36,7 +36,7 @@ def path_from_root(*pathelems):
 
 # Core test runner class, shared between normal tests and benchmarks
 checked_sanity = False
-test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1']
+test_modes = ['default', 'o1', 'o2', 'asm1', 'asm1f', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1']
 test_index = 0
 
 class RunnerCore(unittest.TestCase):
diff --git a/tests/test_core.py b/tests/test_core.py
index f2376b5333d44..ca81102daf21c 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -883,10 +883,7 @@ def test_double_i64_conversion(self):
     self.do_run(src, 'OK!\n');
 
   def test_float32_precise(self):
-    assert 'asm1' in test_modes
-    if self.run_name != 'asm1': return self.skip('todo')
-
-    Settings.PRECISE_F32 = 1
+    if self.emcc_args == None or 'PRECISE_F32=1' not in self.emcc_args: return self.skip('needs precise float32')
 
     src = r'''
       #include <stdio.h>
@@ -8334,16 +8331,6 @@ def test_fasta(self):
         src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
         self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
 
-      assert 'asm1' in test_modes
-      if self.run_name == 'asm1':
-        # test float support in asm
-        print 'was fround', Settings.FROUND
-        Settings.FROUND = 1 - Settings.FROUND
-        print 'now fround variant', Settings.FROUND
-        i, j = results[-1]
-        src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', 'float')
-        self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'))
-
   def test_whets(self):
     if not Settings.ASM_JS: return self.skip('mainly a test for asm validation here')
     self.do_run(open(path_from_root('tests', 'whets.cpp')).read(), 'Single Precision C Whetstone Benchmark')
@@ -10611,6 +10598,7 @@ def setUp(self):
 
 # asm.js
 asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"])
+asm1f = make_run("asm1f", compiler=CLANG, emcc_args=["-O1", "-s", "PRECISE_F32=1"])
 asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
 asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "CHECK_HEAP_ALIGN=1"])
 asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"})

From 140ea9e81feb09d8f2559995c73d49a39424ef5d Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 1 Nov 2013 18:39:33 -0700
Subject: [PATCH 29/61] do not emit unnecessary coercions when calling a math
 builtin with a float

---
 src/jsifier.js     | 3 ++-
 src/parseTools.js  | 2 ++
 tests/test_core.py | 8 ++++++--
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/src/jsifier.js b/src/jsifier.js
index d5b87a773ea63..97317756d3027 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1503,7 +1503,8 @@ function JSify(data, functionsOnly, givenFunctions) {
 
     args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
     if (ASM_JS) {
-      var ffiCall = shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable;
+      var ffiCall = (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) &&
+                    !(simpleIdent in JS_MATH_BUILTINS);
       if (ffiCall) {
         args = args.map(function(arg, i) { return asmCoercion(arg, ensureValidFFIType(argsTypes[i])) });
       } else {
diff --git a/src/parseTools.js b/src/parseTools.js
index 6179e285f2fc8..34e064f0256ec 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -629,6 +629,8 @@ function cleanSegment(segment) {
 
 var MATHOPS = set(['add', 'sub', 'sdiv', 'udiv', 'mul', 'icmp', 'zext', 'urem', 'srem', 'fadd', 'fsub', 'fmul', 'fdiv', 'fcmp', 'frem', 'uitofp', 'sitofp', 'fpext', 'fptrunc', 'fptoui', 'fptosi', 'trunc', 'sext', 'select', 'shl', 'shr', 'ashl', 'ashr', 'lshr', 'lshl', 'xor', 'or', 'and', 'ptrtoint', 'inttoptr']);
 
+var JS_MATH_BUILTINS = set(['Math_sin', 'Math_cos', 'Math_tan', 'Math_asin', 'Math_acos', 'Math_atan', 'Math_ceil', 'Math_floor', 'Math_exp', 'Math_log', 'Math_sqrt']);
+
 var PARSABLE_LLVM_FUNCTIONS = set('getelementptr', 'bitcast');
 mergeInto(PARSABLE_LLVM_FUNCTIONS, MATHOPS);
 
diff --git a/tests/test_core.py b/tests/test_core.py
index a19cde109a66d..f3a106a2eef8a 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1516,7 +1516,7 @@ def test_math(self):
         #include <stdio.h>
         #include <stdlib.h>
         #include <cmath>
-        int main()
+        int main(int argc, char **argv)
         {
           printf("*%.2f,%.2f,%d", M_PI, -M_PI, (1/0.0) > 1e300); // could end up as infinity, or just a very very big number
           printf(",%d", isfinite(NAN) != 0);
@@ -1538,11 +1538,15 @@ def test_math(self):
           sincosf(0.0, &fsine, &fcosine);
           printf(",%1.1f", fsine);
           printf(",%1.1f", fcosine);
+          fsine = sinf(1.1 + argc - 1);
+          fcosine = cosf(1.1 + argc - 1);
+          printf(",%1.1f", fsine);
+          printf(",%1.1f", fcosine);
           printf("*\\n");
           return 0;
         }
       '''
-      self.do_run(src, '*3.14,-3.14,1,0,0,0,1,0,1,1,0,2,3,0.0,1.0,0.0,1.0*')
+      self.do_run(src, '*3.14,-3.14,1,0,0,0,1,0,1,1,0,2,3,0.0,1.0,0.0,1.0,0.9,0.5*')
 
   def test_erf(self):
       src = '''

From 24c8ebb2f0ee4717bc39f3236583f6a329cc6b64 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 8 Nov 2013 17:33:56 -0800
Subject: [PATCH 30/61] add asm2f test subsuite

---
 tests/test_core.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/test_core.py b/tests/test_core.py
index 53d16f9afde60..eec42c8a52113 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -10736,6 +10736,7 @@ def setUp(self):
 asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"])
 asm1f = make_run("asm1f", compiler=CLANG, emcc_args=["-O1", "-s", "PRECISE_F32=1"])
 asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
+asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1", "-g"]) # TODO: collapse into another test subsuite
 asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "CHECK_HEAP_ALIGN=1"])
 asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"})
 

From d041b0fe9f08069b5786d505cfe958899031b521 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 8 Nov 2013 18:01:46 -0800
Subject: [PATCH 31/61] initial work for float32 support in js optimizer

---
 tools/js-optimizer.js | 54 +++++++++++++++++++++++++------------------
 1 file changed, 31 insertions(+), 23 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 36244298431f9..cba3186115ef6 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1515,21 +1515,33 @@ function unVarify(vars, ret) { // transform var x=1, y=2 etc. into (x=1, y=2), i
 // annotations, plus explicit metadata) and denormalize (vice versa)
 var ASM_INT = 0;
 var ASM_DOUBLE = 1;
+var ASM_FLOAT = 2;
 
 function detectAsmCoercion(node, asmInfo) {
   // for params, +x vs x|0, for vars, 0.0 vs 0
   if (node[0] === 'num' && node[1].toString().indexOf('.') >= 0) return ASM_DOUBLE;
   if (node[0] === 'unary-prefix') return ASM_DOUBLE;
+  if (node[0] === 'call' && node[1][0] === 'name' && node[1][1] === 'Math_fround') return ASM_FLOAT;
   if (asmInfo && node[0] == 'name') return getAsmType(node[1], asmInfo);
   return ASM_INT;
 }
 
 function makeAsmCoercion(node, type) {
-  return type === ASM_INT ? ['binary', '|', node, ['num', 0]] : ['unary-prefix', '+', node];
+  switch (type) {
+    case ASM_INT: return ['binary', '|', node, ['num', 0]];
+    case ASM_DOUBLE: return ['unary-prefix', '+', node];
+    case ASM_FLOAT: return ['call', ['name', 'Math_fround'], [node]];
+    default: throw 'wha? ' + JSON.stringify([node, type]) + new Error().stack;
+  }
 }
 
 function makeAsmVarDef(v, type) {
-  return [v, type === ASM_INT ? ['num', 0] : ['unary-prefix', '+', ['num', 0]]];
+  switch (type) {
+    case ASM_INT: return [v, ['num', 0]];
+    case ASM_DOUBLE: return [v, ['unary-prefix', '+', ['num', 0]]];
+    case ASM_FLOAT: return [v, ['call', ['name', 'Math_fround'], [['num', 0]]]];
+    default: throw 'wha?';
+  }
 }
 
 function getAsmType(name, asmInfo) {
@@ -1568,7 +1580,8 @@ function normalizeAsm(func) {
       var name = v[0];
       var value = v[1];
       if (!(name in data.vars)) {
-        assert(value[0] === 'num' || (value[0] === 'unary-prefix' && value[2][0] === 'num')); // must be valid coercion no-op
+        assert(value[0] === 'num' || (value[0] === 'unary-prefix' && value[2][0] === 'num') // must be valid coercion no-op
+                                  || (value[0] === 'call' && value[1][0] === 'name' && value[1][1] === 'Math_fround'));
         data.vars[name] = detectAsmCoercion(value);
         v.length = 1; // make an un-assigning var
       } else {
@@ -3173,7 +3186,7 @@ function outline(ast) {
 
     var writes = {};
     var namings = {};
-    var hasReturn = false, hasReturnInt = false, hasReturnDouble = false, hasBreak = false, hasContinue = false;
+    var hasReturn = false, hasReturnType = {}, hasBreak = false, hasContinue = false;
     var breaks = {};    // set of labels we break or continue
     var continues = {}; // to (name -> id, just like labels)
     var breakCapturers = 0;
@@ -3193,10 +3206,8 @@ function outline(ast) {
       } else if (type == 'return') {
         if (!node[1]) {
           hasReturn = true;
-        } else if (detectAsmCoercion(node[1]) == ASM_INT) {
-          hasReturnInt = true;
         } else {
-          hasReturnDouble = true;
+          hasReturnType[detectAsmCoercion(node[1])] = true;
         }
       } else if (type == 'break') {
         var label = node[1] || 0;
@@ -3226,7 +3237,6 @@ function outline(ast) {
         continueCapturers--;
       }
     });
-    assert(hasReturn + hasReturnInt + hasReturnDouble <= 1);
 
     var reads = {};
     for (var v in namings) {
@@ -3234,7 +3244,7 @@ function outline(ast) {
       if (actualReads > 0) reads[v] = actualReads;
     }
 
-    return { writes: writes, reads: reads, hasReturn: hasReturn, hasReturnInt: hasReturnInt, hasReturnDouble: hasReturnDouble, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels };
+    return { writes: writes, reads: reads, hasReturn: hasReturn, hasReturnType: hasReturnType, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels };
   }
 
   function makeAssign(dst, src) {
@@ -3255,7 +3265,10 @@ function outline(ast) {
     return ['switch', value, cases];
   }
 
-  var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7;
+  var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7, CONTROL_RETURN_FLOAT = 8;
+  function controlFromAsmType(asmType) {
+    return CONTROL_RETURN_INT + (asmType | 0); // assumes ASM_INT starts at 0, and order of these two is identical!
+  }
 
   var sizeToOutline = null; // customized per function and as we make progress
   function calculateThreshold(func, asmData) {
@@ -3314,7 +3327,7 @@ function outline(ast) {
     });
 
     // Generate new function
-    if (codeInfo.hasReturn || codeInfo.hasReturnInt || codeInfo.hasReturnDouble || codeInfo.hasBreak || codeInfo.hasContinue) {
+    if (codeInfo.hasReturn || codeInfo.hasReturnType[ASM_INT] || codeInfo.hasReturnType[ASM_DOUBLE] || codeInfo.hasReturnType[ASM_FLOAT] || codeInfo.hasBreak || codeInfo.hasContinue) {
       // we need to capture all control flow using a top-level labeled one-time loop in the outlined function
       var breakCapturers = 0;
       var continueCapturers = 0;
@@ -3339,7 +3352,7 @@ function outline(ast) {
                 ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', CONTROL_RETURN_VOID])]);
               } else {
                 var type = detectAsmCoercion(node[1], asmData);
-                ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE])]);
+                ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', controlFromAsmType(type)])]);
                 ret.push(['stat', makeAssign(makeStackAccess(type, asmData.controlDataStackPos(outlineIndex)), node[1])]);
               }
               ret.push(['stat', ['break', 'OL']]);
@@ -3402,16 +3415,10 @@ function outline(ast) {
           [['stat', ['return']]]
         ));
       }
-      if (codeInfo.hasReturnInt) {
+      for (var returnType in codeInfo.hasReturnType) {
         reps.push(makeIf(
-          makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_INT]),
-          [['stat', ['return', makeAsmCoercion(['name', 'tempInt'], ASM_INT)]]]
-        ));
-      }
-      if (codeInfo.hasReturnDouble) {
-        reps.push(makeIf(
-          makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_DOUBLE]),
-          [['stat', ['return', makeAsmCoercion(['name', 'tempDouble'], ASM_DOUBLE)]]]
+          makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', controlFromAsmType(returnType)]),
+          [['stat', ['return', makeAsmCoercion(['name', 'tempInt'], returnType | 0)]]]
         ));
       }
       if (codeInfo.hasBreak) {
@@ -3490,8 +3497,9 @@ function outline(ast) {
     var last = getStatements(func)[getStatements(func).length-1];
     if (last[0] === 'stat') last = last[1];
     if (last[0] !== 'return') {
-      if (allCodeInfo.hasReturnInt || allCodeInfo.hasReturnDouble) {
-        getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], allCodeInfo.hasReturnInt ? ASM_INT : ASM_DOUBLE)]]);
+      for (var returnType in codeInfo.hasReturnType) {
+        getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], returnType | 0)]]);
+        break;
       }
     }
     outliningParents[newIdent] = func[1];

From eb3ec20f8b8c78932d659ffdc6a1f5092a39d777 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 8 Nov 2013 18:18:12 -0800
Subject: [PATCH 32/61] fix registerize pass for float32

---
 tools/js-optimizer.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index cba3186115ef6..c69f7362d8889 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1930,7 +1930,7 @@ function registerize(ast) {
     // we just use a fresh register to make sure we avoid this, but it could be
     // optimized to check for safe registers (free, and not used in this loop level).
     var varRegs = {}; // maps variables to the register they will use all their life
-    var freeRegsClasses = asm ? [[], []] : []; // two classes for asm, one otherwise
+    var freeRegsClasses = asm ? [[], [], []] : []; // two classes for asm, one otherwise XXX - hardcoded length
     var nextReg = 1;
     var fullNames = {};
     var loopRegs = {}; // for each loop nesting level, the list of bound variables

From 8d20a1424af064991bee3c66ef83ba0886b01180 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 8 Nov 2013 18:54:13 -0800
Subject: [PATCH 33/61] fix float/int bitcast optimization to recognize and
 preserve Math_fround

---
 tools/js-optimizer.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index c69f7362d8889..54cde9f532bb2 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -618,6 +618,7 @@ function simplifyExpressions(ast) {
 
     if (asm) {
       if (hasTempDoublePtr) {
+        var asmData = normalizeAsm(ast);
         traverse(ast, function(node, type) {
           if (type === 'assign') {
             if (node[1] === true && node[2][0] === 'sub' && node[2][1][0] === 'name' && node[2][1][1] === 'HEAP32') {
@@ -642,7 +643,7 @@ function simplifyExpressions(ast) {
                 node[2][0] !== 'seq') { // avoid (x, y, z) which can be used for tempDoublePtr on doubles for alignment fixes
               if (node[1][2][1][1] === 'HEAP32') {
                 node[1][3][1][1] = 'HEAPF32';
-                return ['unary-prefix', '+', node[1][3]];
+                return makeAsmCoercion(node[1][3], detectAsmCoercion(node[2]));
               } else {
                 node[1][3][1][1] = 'HEAP32';
                 return ['binary', '|', node[1][3], ['num', 0]];
@@ -686,7 +687,6 @@ function simplifyExpressions(ast) {
             }
           }
         });
-        var asmData = normalizeAsm(ast);
         for (var v in bitcastVars) {
           var info = bitcastVars[v];
           // good variables define only one type, use only one type, have definitions and uses, and define as a different type than they use

From 11418ef9f027c503e371da0bf82aff9387826594 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Fri, 8 Nov 2013 19:13:57 -0800
Subject: [PATCH 34/61] emit proper large float constants when js opts are to
 be run

---
 src/parseTools.js            |  7 ++-----
 tests/cases/storebigfloat.ll | 17 +++++++++++++++++
 2 files changed, 19 insertions(+), 5 deletions(-)
 create mode 100644 tests/cases/storebigfloat.ll

diff --git a/src/parseTools.js b/src/parseTools.js
index d25f9bd0c6a8e..088fea169ec0e 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1166,6 +1166,7 @@ function makeVarDef(js) {
 function ensureDot(value) {
   value = value.toString();
   if (value.indexOf('.') >= 0 || /[IN]/.test(value)) return value; // if already dotted, or Infinity or NaN, nothing to do here
+  if (RUNNING_JS_OPTS) return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
   var e = value.indexOf('e');
   if (e < 0) return value + '.0';
   return value.substr(0, e) + '.0' + value.substr(e);
@@ -1182,11 +1183,7 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
   }
   // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
   if (type in Runtime.FLOAT_TYPES && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
-    if (RUNNING_JS_OPTS) {
-      return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
-    } else {
-      return ensureDot(value);
-    }
+    return ensureDot(value);
   } else {
     return value;
   }
diff --git a/tests/cases/storebigfloat.ll b/tests/cases/storebigfloat.ll
new file mode 100644
index 0000000000000..c9995835bec70
--- /dev/null
+++ b/tests/cases/storebigfloat.ll
@@ -0,0 +1,17 @@
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+; [#uses=0]
+define i32 @main() {
+entry:
+  %retval = alloca i32, align 4                   ; [#uses=1 type=i32*]
+  %f = alloca float, align 4
+  store float 1.000000e+10, float* %f, align 4
+  store i32 0, i32* %retval
+  %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+  ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
+

From 8ccb00a3d0e8d0679553db1cdcc66027e59bc41c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Sat, 9 Nov 2013 18:02:57 +0200
Subject: [PATCH 35/61] Fix test_lua on Windows when built using mingw32-make.
 That tool got confused by a '#' comment at the end of a command line, so move
 the comment to its own separate line.

---
 tests/lua/src/Makefile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/lua/src/Makefile b/tests/lua/src/Makefile
index 401e73672a173..a9cf091105da1 100644
--- a/tests/lua/src/Makefile
+++ b/tests/lua/src/Makefile
@@ -59,8 +59,9 @@ o:	$(ALL_O)
 
 a:	$(ALL_A)
 
+# XXX EMSCRIPTEN: add AR_ARGS
 $(LUA_A): $(BASE_O)
-	$(AR) $(AR_ARGS) $@ $(BASE_O) # XXX EMSCRIPTEN: add AR_ARGS
+	$(AR) $(AR_ARGS) $@ $(BASE_O)
 	$(RANLIB) $@
 
 $(LUA_T): $(LUA_O) $(LUA_A)

From 7a3b033e450b94f5b8c32509fd9ec8b3ca539dfb Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 09:16:12 -0800
Subject: [PATCH 36/61] fix ensureDot logic

---
 src/parseTools.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index 088fea169ec0e..adb3f6e4ff4a3 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1165,7 +1165,9 @@ function makeVarDef(js) {
 
 function ensureDot(value) {
   value = value.toString();
-  if (value.indexOf('.') >= 0 || /[IN]/.test(value)) return value; // if already dotted, or Infinity or NaN, nothing to do here
+  // if already dotted, or Infinity or NaN, nothing to do here
+  // if smaller than 1 and running js opts, we always need to force a coercion (0.001 will turn into 1e-3, which has no .)
+  if ((value.indexOf('.') >= 0 || /[IN]/.test(value)) && (!RUNNING_JS_OPTS || Math.abs(value) >= 1)) return value;
   if (RUNNING_JS_OPTS) return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
   var e = value.indexOf('e');
   if (e < 0) return value + '.0';
@@ -1181,8 +1183,7 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
     value = ensureDot(value);
     return 'Math_fround(' + value + ')';
   }
-  // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
-  if (type in Runtime.FLOAT_TYPES && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
+  if (type in Runtime.FLOAT_TYPES) {
     return ensureDot(value);
   } else {
     return value;

From 7f5c2dec956c2d24b524583f80b23282d0c60353 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 09:21:25 -0800
Subject: [PATCH 37/61] optimize hasSideEffects

---
 tools/js-optimizer.js | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 54cde9f532bb2..20d4d3d233c74 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1145,10 +1145,12 @@ function simplifyNotComps(ast) {
 var NO_SIDE_EFFECTS = set('num', 'name');
 
 function hasSideEffects(node) { // this is 99% incomplete!
-  if (node[0] in NO_SIDE_EFFECTS) return false;
-  if (node[0] === 'unary-prefix') return hasSideEffects(node[2]);
-  if (node[0] === 'binary') return hasSideEffects(node[2]) || hasSideEffects(node[3]);
-  return true;
+  switch (node[0]) {
+    case 'num': case 'name': return false;
+    case 'unary-prefix': return hasSideEffects(node[2]);
+    case 'binary': return hasSideEffects(node[2]) || hasSideEffects(node[3]);
+    default: return true;
+  }
 }
 
 // Clear out empty ifs and blocks, and redundant blocks/stats and so forth

From e203c5b86c34c75e4b790bf2d22de926083d73ee Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 09:22:24 -0800
Subject: [PATCH 38/61] remove unneeded var

---
 tools/js-optimizer.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 20d4d3d233c74..3e1b2b7f7401b 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1142,8 +1142,6 @@ function simplifyNotComps(ast) {
   simplifyNotCompsPass = false;
 }
 
-var NO_SIDE_EFFECTS = set('num', 'name');
-
 function hasSideEffects(node) { // this is 99% incomplete!
   switch (node[0]) {
     case 'num': case 'name': return false;

From 16a5d12dc19b984cf3b55fc2682bd24f1d6834c4 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 09:29:34 -0800
Subject: [PATCH 39/61] consolidate side effect checking code

---
 tools/js-optimizer.js | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 3e1b2b7f7401b..8b05fc38e3baa 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1144,9 +1144,10 @@ function simplifyNotComps(ast) {
 
 function hasSideEffects(node) { // this is 99% incomplete!
   switch (node[0]) {
-    case 'num': case 'name': return false;
+    case 'num': case 'name': case 'string': return false;
     case 'unary-prefix': return hasSideEffects(node[2]);
     case 'binary': return hasSideEffects(node[2]) || hasSideEffects(node[3]);
+    case 'sub': return hasSideEffects(node[1]) || hasSideEffects(node[2]);
     default: return true;
   }
 }
@@ -2081,7 +2082,6 @@ function registerize(ast) {
 // can happen in ALLOW_MEMORY_GROWTH mode
 
 var ELIMINATION_SAFE_NODES = set('var', 'assign', 'call', 'if', 'toplevel', 'do', 'return', 'label', 'switch'); // do is checked carefully, however
-var NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS = set('name', 'num', 'string', 'binary', 'sub', 'unary-prefix');
 var IGNORABLE_ELIMINATOR_SCAN_NODES = set('num', 'toplevel', 'string', 'break', 'continue', 'dot'); // dot can only be STRING_TABLE.*
 var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', 'for', 'while', 'array', 'throw'); // we could handle some of these, TODO, but nontrivial (e.g. for while, the condition is hit multiple times after the body)
 
@@ -2173,7 +2173,7 @@ function eliminate(ast, memSafe) {
       if (definitions[name] === 1 && uses[name] === 1) {
         potentials[name] = 1;
       } else if (uses[name] === 0 && (!definitions[name] || definitions[name] <= 1)) { // no uses, no def or 1 def (cannot operate on phis, and the llvm optimizer will remove unneeded phis anyhow) (no definition means it is a function parameter, or a local with just |var x;| but no defining assignment)
-        var hasSideEffects = false;
+        var sideEffects = false;
         var value = values[name];
         if (value) {
           // TODO: merge with other side effect code
@@ -2183,15 +2183,10 @@ function eliminate(ast, memSafe) {
           if (!(value[0] === 'seq' && value[1][0] === 'assign' && value[1][2][0] === 'sub' && value[1][2][2][0] === 'binary' && value[1][2][2][1] === '>>' &&
                 value[1][2][2][2][0] === 'name' && value[1][2][2][2][1] === 'tempDoublePtr')) {
             // If not that, then traverse and scan normally.
-            traverse(value, function(node, type) {
-              if (!(type in NODES_WITHOUT_ELIMINATION_SIDE_EFFECTS)) {
-                hasSideEffects = true; // cannot remove this unused variable, constructing it has side effects
-                return true;
-              }
-            });
+            sideEffects = hasSideEffects(value);
           }
         }
-        if (!hasSideEffects) {
+        if (!sideEffects) {
           varsToRemove[name] = !definitions[name] ? 2 : 1; // remove it normally
           sideEffectFree[name] = true;
           // Each time we remove a variable with 0 uses, if its value has no

From d420405180ee7910511c6a89572ce9f823880f3a Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 09:55:18 -0800
Subject: [PATCH 40/61] Math.* have no side effects

---
 tools/eliminator/eliminator-test-output.js |  3 ++
 tools/eliminator/eliminator-test.js        | 10 ++++++-
 tools/js-optimizer.js                      | 32 ++++++++++++++++------
 3 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js
index 1a6506edd53d5..0171e99b12a8a 100644
--- a/tools/eliminator/eliminator-test-output.js
+++ b/tools/eliminator/eliminator-test-output.js
@@ -6119,4 +6119,7 @@ function intoCond() {
   HEAP32[$115 >> 2] = $NumWords;
  }
 }
+function math(a, b, c, d) {
+ print(Math_imul(d) + (Math_fround(c) + (a + Math_abs(b))));
+}
 
diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js
index ffad69ea5f06e..ef17b38892938 100644
--- a/tools/eliminator/eliminator-test.js
+++ b/tools/eliminator/eliminator-test.js
@@ -8852,5 +8852,13 @@ function intoCond() {
   HEAP32[$504 >> 2] = $503;
  }
 }
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU", "asm", "phi", "intoCond"]
+function math(a, b, c, d) {
+ var x, y, z, w;
+ x = a;
+ y = Math_abs(b);
+ z = Math_fround(c);
+ w = Math_imul(d);
+ print(x + y + z + w);
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU", "asm", "phi", "intoCond", "math"]
 
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 8b05fc38e3baa..d2bfcd9185d21 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1142,12 +1142,26 @@ function simplifyNotComps(ast) {
   simplifyNotCompsPass = false;
 }
 
+function callHasSideEffects(node) { // checks if the call itself (not the args) has side effects (or is not statically known)
+  return !(node[1][0] === 'name' && /^Math_/.test(node[1][1]));
+}
+
 function hasSideEffects(node) { // this is 99% incomplete!
   switch (node[0]) {
     case 'num': case 'name': case 'string': return false;
     case 'unary-prefix': return hasSideEffects(node[2]);
     case 'binary': return hasSideEffects(node[2]) || hasSideEffects(node[3]);
     case 'sub': return hasSideEffects(node[1]) || hasSideEffects(node[2]);
+    case 'call': {
+      if (callHasSideEffects(node)) return true;
+      // This is a statically known call, with no side effects. only args can side effect us
+      var args = node[2];
+      var num = args.length;
+      for (var i = 0; i < num; i++) {
+        if (hasSideEffects(args[i])) return true;
+      }
+      return false;
+    }
     default: return true;
   }
 }
@@ -2445,14 +2459,16 @@ function eliminate(ast, memSafe) {
           for (var i = 0; i < args.length; i++) {
             traverseInOrder(args[i]);
           }
-          // these two invalidations will also invalidate calls
-          if (!globalsInvalidated) {
-            invalidateGlobals();
-            globalsInvalidated = true;
-          }
-          if (!memoryInvalidated) {
-            invalidateMemory();
-            memoryInvalidated = true;
+          if (callHasSideEffects(node)) {
+            // these two invalidations will also invalidate calls
+            if (!globalsInvalidated) {
+              invalidateGlobals();
+              globalsInvalidated = true;
+            }
+            if (!memoryInvalidated) {
+              invalidateMemory();
+              memoryInvalidated = true;
+            }
           }
         } else if (type === 'if') {
           if (allowTracking) {

From c94fbc8db50b1bb8ba9c31bf6e93f2c733cac253 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 10:34:06 -0800
Subject: [PATCH 41/61] optimize fround polyfill a tiny bit

---
 src/preamble.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/preamble.js b/src/preamble.js
index 3238c99cb0ae2..3e76e5030454c 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -1076,8 +1076,10 @@ Math.imul = Math['imul'];
 
 #if PRECISE_F32
 #if PRECISE_F32 == 1
-var froundBuffer = new Float32Array(1);
-if (!Math['fround']) Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] };
+if (!Math['fround']) {
+  var froundBuffer = new Float32Array(1);
+  Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] };
+}
 #else // 2
 if (!Math['fround']) Math['fround'] = function(x) { return x };
 #endif

From d969c64b05697855d6455863fac62b74991dd9e7 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 10:34:35 -0800
Subject: [PATCH 42/61] test both float and double in fasta

---
 tests/test_core.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/tests/test_core.py b/tests/test_core.py
index eec42c8a52113..b7b2f57e0ea5c 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -8358,9 +8358,11 @@ def test_fasta(self):
       if self.emcc_args is None: return self.skip('requires emcc')
       results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''),
 (50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ]
-      for i, j in results:
-        src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
-        self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
+      for t in ['float', 'double']:
+        print t
+        src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', t)
+        for i, j in results:
+          self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
 
   def test_whets(self):
     if not Settings.ASM_JS: return self.skip('mainly a test for asm validation here')

From 23d8911d25700c246093849dac35377dc21a233f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Sat, 9 Nov 2013 17:29:38 +0200
Subject: [PATCH 43/61] Fix test_asm_pgo on Windows by using response files.
 The test failed by exceeding Windows command line length limitations, giving
 an error "WindowsError: [Error 87] The parameter is incorrect". Also extend
 the logging given by EM_BUILD_VERBOSE=3 to print out the compiler
 invokations.

---
 tools/response_file.py |  6 ++++++
 tools/shared.py        | 21 ++++++++++-----------
 2 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/tools/response_file.py b/tools/response_file.py
index f19cf8af1a173..7f916752429c2 100644
--- a/tools/response_file.py
+++ b/tools/response_file.py
@@ -1,4 +1,5 @@
 import tempfile, os, sys, shlex
+import shared
 
 # Routes the given cmdline param list in args into a new response file and returns the filename to it.
 # The returned filename has a suffix '.rsp'.
@@ -9,6 +10,11 @@ def create_response_file(args, directory):
   args = map(lambda p: p.replace('\\', '\\\\').replace('"', '\\"'), args)
   response_fd.write('"' + '" "'.join(args) + '"')
   response_fd.close()
+  
+  # Register the created .rsp file to be automatically cleaned up once this process finishes, so that
+  # caller does not have to remember to do it.
+  shared.configuration.get_temp_files().note(response_filename)
+  
   return response_filename
 
 # Reads a response file, and returns the list of cmdline params found in the file.
diff --git a/tools/shared.py b/tools/shared.py
index d38aef4cf1ae8..78d7e54ed4ca1 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -3,7 +3,7 @@
 from tempfile import mkstemp
 from distutils.spawn import find_executable
 import jsrun, cache, tempfiles
-from response_file import create_response_file
+import response_file
 import logging, platform
 
 def listify(x):
@@ -41,8 +41,8 @@ def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, st
     # emscripten.py supports reading args from a response file instead of cmdline.
     # Use .rsp to avoid cmdline length limitations on Windows.
     if len(args) >= 2 and args[1].endswith("emscripten.py"):
-      self.response_filename = create_response_file(args[2:], TEMP_DIR)
-      args = args[0:2] + ['@' + self.response_filename]
+      response_filename = response_file.create_response_file(args[2:], TEMP_DIR)
+      args = args[0:2] + ['@' + response_filename]
       
     try:
       # Call the process with fixed streams.
@@ -78,13 +78,6 @@ def poll(self):
   def kill(self):
     return self.process.kill()
   
-  def __del__(self):
-    try:
-      # Clean up the temporary response file that was used to spawn this process, so that we don't leave temp files around.
-      tempfiles.try_delete(self.response_filename)
-    except:
-      pass # Mute all exceptions in dtor, particularly if we didn't use a response file, self.response_filename doesn't exist.
-
 __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
 def path_from_root(*pathelems):
   return os.path.join(__rootpath__, *pathelems)
@@ -1214,7 +1207,13 @@ def emscripten(filename, append_ext=True, extra_args=[]):
     # Run Emscripten
     Settings.RELOOPER = Cache.get_path('relooper.js')
     settings = Settings.serialize()
-    compiler_output = jsrun.timeout_run(Popen([PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling')
+    args = settings + extra_args
+    if WINDOWS:
+      args = ['@' + response_file.create_response_file(args, TEMP_DIR)]
+    cmdline = [PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + args
+    if jsrun.TRACK_PROCESS_SPAWNS:
+      logging.info('Executing emscripten.py compiler with cmdline "' + ' '.join(cmdline) + '"')
+    compiler_output = jsrun.timeout_run(Popen(cmdline, stdout=PIPE), None, 'Compiling')
     #print compiler_output
 
     # Detect compilation crashes and errors

From c4114043e9b5dea715b4a8b4328ab360ad3fd687 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 11:43:09 -0800
Subject: [PATCH 44/61] make asm2f do full optimization, including minification

---
 tests/test_core.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_core.py b/tests/test_core.py
index b7b2f57e0ea5c..4b76a9db81f6c 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -10738,7 +10738,7 @@ def setUp(self):
 asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"])
 asm1f = make_run("asm1f", compiler=CLANG, emcc_args=["-O1", "-s", "PRECISE_F32=1"])
 asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
-asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1", "-g"]) # TODO: collapse into another test subsuite
+asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1"]) # TODO: collapse into another test subsuite
 asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "CHECK_HEAP_ALIGN=1"])
 asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"})
 

From d446e2b3c3b96474106b9bd47f35d901abb14f6d Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 16:18:31 -0800
Subject: [PATCH 45/61] support inline asm comments; fixes #1766

---
 src/intertyper.js  | 3 +++
 src/parseTools.js  | 6 ++++++
 tests/test_core.py | 7 +++++++
 3 files changed, 16 insertions(+)

diff --git a/src/intertyper.js b/src/intertyper.js
index fceeb38d92b4a..fa53c6520d980 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -680,6 +680,9 @@ function intertyper(lines, sidePass, baseLineNums) {
           args.push(ident);
         });
       }
+      item.ident = expandLLVMString(item.ident).replace(/(#[^\n]*)/g, function(m) {
+        return '/* ' + m.substr(1) + ' */'; // fix asm comments to js comments
+      });
       if (item.assignTo) item.ident = 'return ' + item.ident;
       item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');';
       return { ret: item, item: item };
diff --git a/src/parseTools.js b/src/parseTools.js
index c4f28184cb1cd..52727903a710a 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -981,6 +981,12 @@ function parseLLVMString(str) {
   return ret;
 }
 
+function expandLLVMString(str) {
+  return str.replace(/\\../g, function(m) {
+    return String.fromCharCode(parseInt(m.substr(1), '16'));
+  });
+}
+
 function getLabelIds(labels) {
   return labels.map(function(label) { return label.ident });
 }
diff --git a/tests/test_core.py b/tests/test_core.py
index b2147d4936961..3680a92d3866d 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -3821,6 +3821,10 @@ def test_inlinejs(self):
         double get() {
           double ret = 0;
           __asm __volatile__("Math.abs(-12/3.3)":"=r"(ret)); // write to a variable
+          asm("#comment1");
+          asm volatile("#comment2");
+          asm volatile("#comment3\n"
+                       "#comment4\n");
           return ret;
         }
 
@@ -3839,6 +3843,9 @@ def test_inlinejs(self):
         '''
 
       self.do_run(src, 'Inline JS is very cool\n3.64\n') # TODO 1\n2\n3\n1\n2\n3\n')
+      if self.emcc_args == []: # opts will eliminate the comments
+        out = open('src.cpp.o.js').read()
+        for i in range(1, 5): assert ('comment%d' % i) in out
 
   def test_inlinejs2(self):
       if not self.is_le32(): return self.skip('le32 needed for inline js')

From 159315a0edf8770305a1f042c30285fcc06d8663 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sat, 9 Nov 2013 19:34:30 -0800
Subject: [PATCH 46/61] add asm2f to ALL

---
 tests/runner.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/runner.py b/tests/runner.py
index 483df36e3f410..aed70d3511300 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -36,7 +36,7 @@ def path_from_root(*pathelems):
 
 # Core test runner class, shared between normal tests and benchmarks
 checked_sanity = False
-test_modes = ['default', 'o1', 'o2', 'asm1', 'asm1f', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1']
+test_modes = ['default', 'o1', 'o2', 'asm1', 'asm1f', 'asm2', 'asm2f', 'asm2g', 'asm2x86', 's_0_0', 's_0_1']
 test_index = 0
 
 class RunnerCore(unittest.TestCase):

From 39b138d4ddda29b535285bc1c5223641dcacce28 Mon Sep 17 00:00:00 2001
From: John Vilk <jvilk@cs.umass.edu>
Date: Wed, 6 Nov 2013 22:08:33 -0500
Subject: [PATCH 47/61] [SDL] Joystick API implementation using HTML5 Gamepad
 API

Works in browsers that implement the working draft of the standard (current Chrome / Firefox stable):
http://www.w3.org/TR/2012/WD-gamepad-20120529/#gamepad-interface

...and browsers that implement the editor's draft (current Firefox Nightly):
https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#idl-def-Gamepad

Contains unit tests for both event types.
---
 AUTHORS               |   1 +
 src/library_sdl.js    | 234 +++++++++++++++++++++++++++++++++++++++---
 src/struct_info.json  |  15 +++
 tests/sdl_joystick.c  | 123 ++++++++++++++++++++++
 tests/test_browser.py |  76 ++++++++++++++
 5 files changed, 435 insertions(+), 14 deletions(-)
 create mode 100644 tests/sdl_joystick.c

diff --git a/AUTHORS b/AUTHORS
index 0b8fdf3370558..6a28c2054491d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -107,3 +107,4 @@ a license to everyone to use it as detailed in LICENSE.)
 * Michael Tirado <icetooth333@gmail.com>
 * Ben Noordhuis <info@bnoordhuis.nl>
 * Bob Roberts <bobroberts177@gmail.com>
+* John Vilk <jvilk@cs.umass.edu>
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 5b43b7aba53fa..c46364ff76f82 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -75,6 +75,7 @@ var LibrarySDL = {
     textInput: false,
 
     startTime: null,
+    initFlags: 0, // The flags passed to SDL_Init
     buttonState: 0,
     modState: 0,
     DOMButtons: [0, 0, 0],
@@ -639,6 +640,21 @@ var LibrarySDL = {
           {{{ makeSetValue('ptr', C_STRUCTS.SDL_ResizeEvent.h, 'event.h', 'i32') }}};
           break;
         }
+        case 'joystick_button_up': case 'joystick_button_down': {
+          var state = event.type === 'joystick_button_up' ? 0 : 1;
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.which, 'event.index', 'i8') }}};
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.button, 'event.button', 'i8') }}};
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.state, 'state', 'i8') }}};
+          break;
+        }
+        case 'joystick_axis_motion': {
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.which, 'event.index', 'i8') }}};
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.axis, 'event.axis', 'i8') }}};
+          {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.value, 'SDL.joystickAxisValueConversion(event.value)', 'i32') }}};
+          break;
+        }
         default: throw 'Unhandled SDL event: ' + event.type;
       }
     },
@@ -695,7 +711,109 @@ var LibrarySDL = {
       for (var i = 0; i < num; i++) {
         console.log('   diagonal ' + i + ':' + [data[i*surfData.width*4 + i*4 + 0], data[i*surfData.width*4 + i*4 + 1], data[i*surfData.width*4 + i*4 + 2], data[i*surfData.width*4 + i*4 + 3]]);
       }
-    }
+    },
+
+    // Joystick helper methods and state
+
+    joystickEventState: 0,
+    lastJoystickState: {}, // Map from SDL_Joystick* to their last known state. Required to determine if a change has occurred.
+    // Maps Joystick names to pointers. Allows us to avoid reallocating memory for
+    // joystick names each time this function is called.
+    joystickNamePool: {},
+    recordJoystickState: function(joystick, state) {
+      // Standardize button state.
+      var buttons = new Array(state.buttons.length);
+      for (var i = 0; i < state.buttons.length; i++) {
+        buttons[i] = SDL.getJoystickButtonState(state.buttons[i]);
+      }
+
+      SDL.lastJoystickState[joystick] = {
+        buttons: buttons,
+        axes: state.axes.slice(0),
+        timestamp: state.timestamp,
+        index: state.index,
+        id: state.id
+      };
+    },
+    // Retrieves the button state of the given gamepad button.
+    // Abstracts away implementation differences.
+    // Returns 'true' if pressed, 'false' otherwise.
+    getJoystickButtonState: function(button) {
+      if (typeof button === 'object') {
+        // Current gamepad API editor's draft (Firefox Nightly)
+        // https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#idl-def-GamepadButton
+        return button.pressed;
+      } else {
+        // Current gamepad API working draft (Firefox / Chrome Stable)
+        // http://www.w3.org/TR/2012/WD-gamepad-20120529/#gamepad-interface
+        return button > 0;
+      }
+    },
+    // Queries for and inserts controller events into the SDL queue.
+    queryJoysticks: function() {
+      for (var joystick in SDL.lastJoystickState) {
+        var state = SDL.getGamepad(joystick - 1);
+        var prevState = SDL.lastJoystickState[joystick];
+        // Check only if the timestamp has differed.
+        // NOTE: Timestamp is not available in Firefox.
+        if (typeof state.timestamp !== 'number' || state.timestamp !== prevState.timestamp) {
+          var i;
+          for (i = 0; i < state.buttons.length; i++) {
+            var buttonState = SDL.getJoystickButtonState(state.buttons[i]);
+            // NOTE: The previous state already has a boolean representation of
+            //       its button, so no need to standardize its button state here.
+            if (buttonState !== prevState.buttons[i]) {
+              // Insert button-press event.
+              SDL.events.push({
+                type: buttonState ? 'joystick_button_down' : 'joystick_button_up',
+                joystick: joystick,
+                index: joystick - 1,
+                button: i
+              });
+            }
+          }
+          for (i = 0; i < state.axes.length; i++) {
+            if (state.axes[i] !== prevState.axes[i]) {
+              // Insert axes-change event.
+              SDL.events.push({
+                type: 'joystick_axis_motion',
+                joystick: joystick,
+                index: joystick - 1,
+                axis: i,
+                value: state.axes[i]
+              });
+            }
+          }
+
+          SDL.recordJoystickState(joystick, state);
+        }
+      }
+    },
+    // Converts the double-based browser axis value [-1, 1] into SDL's 16-bit
+    // value [-32768, 32767]
+    joystickAxisValueConversion: function(value) {
+      // Ensures that 0 is 0, 1 is 32767, and -1 is 32768.
+      return Math.ceil(((value+1) * 32767.5) - 32768);
+    },
+
+    getGamepads: function() {
+      var fcn = navigator.getGamepads || navigator.webkitGamepads || navigator.mozGamepads || navigator.gamepads || navigator.webkitGetGamepads;
+      if (fcn !== undefined) {
+        // The function must be applied on the navigator object.
+        return fcn.apply(navigator);
+      } else {
+        return [];
+      }
+    },
+
+    // Helper function: Returns the gamepad if available, or null if not.
+    getGamepad: function(deviceIndex) {
+      var gamepads = SDL.getGamepads();
+      if (gamepads.length > deviceIndex && deviceIndex >= 0) {
+        return gamepads[deviceIndex];
+      }
+      return null;
+    },
   },
 
   SDL_Linked_Version: function() {
@@ -708,8 +826,10 @@ var LibrarySDL = {
     return SDL.version;
   },
 
-  SDL_Init: function(what) {
+  SDL_Init: function(initFlags) {
     SDL.startTime = Date.now();
+    SDL.initFlags = initFlags;
+
     // capture all key events. we just keep down and up, but also capture press to prevent default actions
     if (!Module['doNotCaptureKeyboard']) {
       document.addEventListener("keydown", SDL.receiveEvent);
@@ -718,6 +838,15 @@ var LibrarySDL = {
       window.addEventListener("blur", SDL.receiveEvent);
       document.addEventListener("visibilitychange", SDL.receiveEvent);
     }
+
+    if (initFlags & 0x200) {
+      // SDL_INIT_JOYSTICK
+      // Firefox will not give us Joystick data unless we register this NOP
+      // callback.
+      // https://bugzilla.mozilla.org/show_bug.cgi?id=936104
+      addEventListener("gamepadconnected", function() {});
+    }
+
     window.addEventListener("unload", SDL.receiveEvent);
     SDL.keyboardState = _malloc(0x10000); // Our SDL needs 512, but 64K is safe for older SDLs
     _memset(SDL.keyboardState, 0, 0x10000);
@@ -730,6 +859,12 @@ var LibrarySDL = {
     SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
     SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
     SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
+    // These are not technically DOM events; the HTML gamepad API is poll-based.
+    // However, we define them here, as the rest of the SDL code assumes that
+    // all SDL events originate as DOM events.
+    SDL.DOMEventToSDLEvent['joystick_axis_motion'] = 0x600 /* SDL_JOYAXISMOTION */;
+    SDL.DOMEventToSDLEvent['joystick_button_down'] = 0x603 /* SDL_JOYBUTTONDOWN */;
+    SDL.DOMEventToSDLEvent['joystick_button_up'] = 0x604 /* SDL_JOYBUTTONUP */;
     return 0; // success
   },
 
@@ -1189,6 +1324,11 @@ var LibrarySDL = {
   },
 
   SDL_PollEvent: function(ptr) {
+    if (SDL.initFlags & 0x200 && SDL.joystickEventState) {
+      // If SDL_INIT_JOYSTICK was supplied AND the joystick system is configured
+      // to automatically query for events, query for joystick events.
+      SDL.queryJoysticks();
+    }
     if (SDL.events.length === 0) return 0;
     if (ptr) {
       SDL.makeCEvent(SDL.events.shift(), ptr);
@@ -2375,37 +2515,103 @@ var LibrarySDL = {
 
   // Joysticks
 
-  SDL_NumJoysticks: function() { return 0; },
+  SDL_NumJoysticks: function() {
+    var count = 0;
+    var gamepads = SDL.getGamepads();
+    // The length is not the number of gamepads; check which ones are defined.
+    for (var i = 0; i < gamepads.length; i++) {
+      if (gamepads[i] !== undefined) count++;
+    }
+    return count;
+  },
 
-  SDL_JoystickName: function(deviceIndex) { return 0; },
+  SDL_JoystickName: function(deviceIndex) {
+    var gamepad = SDL.getGamepad(deviceIndex);
+    if (gamepad) {
+      var name = gamepad.id;
+      if (SDL.joystickNamePool.hasOwnProperty(name)) {
+        return SDL.joystickNamePool[name];
+      }
+      return SDL.joystickNamePool[name] = allocate(intArrayFromString(name), 'i8', ALLOC_NORMAL);
+    }
+    return 0;
+  },
 
-  SDL_JoystickOpen: function(deviceIndex) { return 0; },
+  SDL_JoystickOpen: function(deviceIndex) {
+    var gamepad = SDL.getGamepad(deviceIndex);
+    if (gamepad) {
+      // Use this as a unique 'pointer' for this joystick.
+      var joystick = deviceIndex+1;
+      SDL.recordJoystickState(joystick, gamepad);
+      return joystick;
+    }
+    return 0;
+  },
 
-  SDL_JoystickOpened: function(deviceIndex) { return 0; },
+  SDL_JoystickOpened: function(deviceIndex) {
+    return SDL.lastJoystickState.hasOwnProperty(deviceIndex+1) ? 1 : 0;
+  },
 
-  SDL_JoystickIndex: function(joystick) { return 0; },
+  SDL_JoystickIndex: function(joystick) {
+    // joystick pointers are simply the deviceIndex+1.
+    return joystick - 1;
+  },
 
-  SDL_JoystickNumAxes: function(joystick) { return 0; },
+  SDL_JoystickNumAxes: function(joystick) {
+    var gamepad = SDL.getGamepad(joystick - 1);
+    if (gamepad) {
+      return gamepad.axes.length;
+    }
+    return 0;
+  },
 
   SDL_JoystickNumBalls: function(joystick) { return 0; },
 
   SDL_JoystickNumHats: function(joystick) { return 0; },
 
-  SDL_JoystickNumButtons: function(joystick) { return 0; },
+  SDL_JoystickNumButtons: function(joystick) {
+    var gamepad = SDL.getGamepad(joystick - 1);
+    if (gamepad) {
+      return gamepad.buttons.length;
+    }
+    return 0;
+  },
 
-  SDL_JoystickUpdate: function() {},
+  SDL_JoystickUpdate: function() {
+    SDL.queryJoysticks();
+  },
 
-  SDL_JoystickEventState: function(state) { return 0; },
+  SDL_JoystickEventState: function(state) {
+    if (state < 0) {
+      // SDL_QUERY: Return current state.
+      return SDL.joystickEventState;
+    }
+    return SDL.joystickEventState = state;
+  },
 
-  SDL_JoystickGetAxis: function(joystick, axis) { return 0; },
+  SDL_JoystickGetAxis: function(joystick, axis) {
+    var gamepad = SDL.getGamepad(joystick - 1);
+    if (gamepad && gamepad.axes.length > axis) {
+      return SDL.joystickAxisValueConversion(gamepad.axes[axis]);
+    }
+    return 0;
+  },
 
   SDL_JoystickGetHat: function(joystick, hat) { return 0; },
 
   SDL_JoystickGetBall: function(joystick, ball, dxptr, dyptr) { return -1; },
 
-  SDL_JoystickGetButton: function(joystick, button) { return 0; },
+  SDL_JoystickGetButton: function(joystick, button) {
+    var gamepad = SDL.getGamepad(joystick - 1);
+    if (gamepad && gamepad.buttons.length > button) {
+      return SDL.getJoystickButtonState(gamepad.buttons[button]) ? 1 : 0;
+    }
+    return 0;
+  },
 
-  SDL_JoystickClose: function(joystick) {},
+  SDL_JoystickClose: function(joystick) {
+    delete SDL.lastJoystickState[joystick];
+  },
 
   // Misc
 
diff --git a/src/struct_info.json b/src/struct_info.json
index 5b4726e8003ee..f6499295313d7 100644
--- a/src/struct_info.json
+++ b/src/struct_info.json
@@ -961,6 +961,21 @@
                 "x", 
                 "y"
             ],
+            "SDL_JoyAxisEvent": [
+                "type", 
+                "which", 
+                "axis", 
+                "padding1", 
+                "padding2", 
+                "value"
+            ],
+            "SDL_JoyButtonEvent": [
+                "type", 
+                "which", 
+                "button", 
+                "state", 
+                "padding1"
+            ],
             "SDL_ResizeEvent": [
                 "type", 
                 "w", 
diff --git a/tests/sdl_joystick.c b/tests/sdl_joystick.c
new file mode 100644
index 0000000000000..7035050f8d840
--- /dev/null
+++ b/tests/sdl_joystick.c
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_ttf.h>
+#include <assert.h>
+#include <string.h>
+#include <emscripten.h>
+
+int result = 1;
+
+void assertJoystickEvent(int expectedGamepad, int expectedType, int expectedIndex, int expectedValue) {
+  SDL_Event event;
+  while(1) {
+    // Loop ends either when assertion fails (we run out of events), or we find
+    // the event we're looking for.
+    assert(SDL_PollEvent(&event) == 1);
+    if (event.type != expectedType) {
+      continue;
+    }
+    switch(event.type) {
+      case SDL_JOYAXISMOTION: {
+        assert(event.jaxis.which == expectedGamepad);
+        assert(event.jaxis.axis == expectedIndex);
+        assert(event.jaxis.value == expectedValue);
+        break;
+      }
+      case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: {
+        assert(event.jbutton.which == expectedGamepad);
+        assert(event.jbutton.button == expectedIndex);
+        assert(event.jbutton.state == expectedValue);
+        break;
+      }
+    }
+    // Break out of while loop.
+    break;
+  }
+}
+
+void assertNoJoystickEvent() {
+  SDL_Event event;
+  while(SDL_PollEvent(&event)) {
+    switch(event.type) {
+      case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: case SDL_JOYAXISMOTION: {
+        // Fail.
+        assert(0);
+      }
+    }
+  }
+}
+
+void main_2(void* arg);
+
+int main() {
+  SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
+  SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
+  emscripten_async_call(main_2, NULL, 3000); // avoid startup delays and intermittent errors
+  return 0;
+}
+
+void main_2(void* arg) {
+  // TODO: At the moment, we only support joystick support through polling.
+  emscripten_run_script("window.addNewGamepad('Pad Thai', 4, 16)");
+  emscripten_run_script("window.addNewGamepad('Pad Kee Mao', 0, 4)");
+  // Check that the joysticks exist properly.
+  assert(SDL_NumJoysticks() == 2);
+  assert(!SDL_JoystickOpened(0));
+  assert(!SDL_JoystickOpened(1));
+  SDL_Joystick* pad1 = SDL_JoystickOpen(0);
+  assert(SDL_JoystickOpened(0));
+  assert(SDL_JoystickIndex(pad1) == 0);
+  assert(strncmp(SDL_JoystickName(0), "Pad Thai", 9) == 0);
+  assert(strncmp(SDL_JoystickName(1), "Pad Kee Mao", 12) == 0);
+  assert(SDL_JoystickNumAxes(pad1) == 4);
+  assert(SDL_JoystickNumButtons(pad1) == 16);
+
+  // Button events.
+  emscripten_run_script("window.simulateGamepadButtonDown(0, 1)");
+  // We didn't tell SDL to automatically update this joystick's state.
+  assertNoJoystickEvent();
+  SDL_JoystickUpdate();
+  assertJoystickEvent(0, SDL_JOYBUTTONDOWN, 1, SDL_PRESSED);
+  assert(SDL_JoystickGetButton(pad1, 1) == 1);
+  // Enable automatic updates.
+  SDL_JoystickEventState(SDL_ENABLE);
+  assert(SDL_JoystickEventState(SDL_QUERY) == SDL_ENABLE);
+  emscripten_run_script("window.simulateGamepadButtonUp(0, 1)");
+  assertJoystickEvent(0, SDL_JOYBUTTONUP, 1, SDL_RELEASED);
+  assert(SDL_JoystickGetButton(pad1, 1) == 0);
+  // No button change: Should not result in a new event.
+  emscripten_run_script("window.simulateGamepadButtonUp(0, 1)");
+  assertNoJoystickEvent();
+  // Joystick 1 is not opened; should not result in a new event.
+  emscripten_run_script("window.simulateGamepadButtonDown(1, 1)");
+  assertNoJoystickEvent();
+
+  // Joystick wiggling
+  emscripten_run_script("window.simulateAxisMotion(0, 0, 1)");
+  assertJoystickEvent(0, SDL_JOYAXISMOTION, 0, 32767);
+  assert(SDL_JoystickGetAxis(pad1, 0) == 32767);
+  emscripten_run_script("window.simulateAxisMotion(0, 0, 0)");
+  assertJoystickEvent(0, SDL_JOYAXISMOTION, 0, 0);
+  assert(SDL_JoystickGetAxis(pad1, 0) == 0);
+  emscripten_run_script("window.simulateAxisMotion(0, 1, -1)");
+  assertJoystickEvent(0, SDL_JOYAXISMOTION, 1, -32768);
+  assert(SDL_JoystickGetAxis(pad1, 1) == -32768);
+  emscripten_run_script("window.simulateAxisMotion(0, 1, -1)");
+  // No joystick change: Should not result in a new event.
+  assertNoJoystickEvent();
+  // Joystick 1 is not opened; should not result in a new event.
+  emscripten_run_script("window.simulateAxisMotion(1, 1, -1)");
+  assertNoJoystickEvent();
+
+  SDL_JoystickClose(pad1);
+  assert(!SDL_JoystickOpened(0));
+
+  // Joystick 0 is closed; we should not process any new gamepad events from it.
+  emscripten_run_script("window.simulateGamepadButtonDown(0, 1)");
+  assertNoJoystickEvent();
+
+  // End test.
+  result = 2;
+  printf("Test passed!\n");
+}
+
diff --git a/tests/test_browser.py b/tests/test_browser.py
index d52f109f478d1..1900e2cf91228 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -874,6 +874,82 @@ def test_glut_touchevents(self):
   def test_glut_wheelevents(self):
     self.btest('glut_wheelevents.c', '1')
 
+  def test_sdl_joystick_1(self):
+    # Generates events corresponding to the Working Draft of the HTML5 Gamepad API.
+    # http://www.w3.org/TR/2012/WD-gamepad-20120529/#gamepad-interface
+    open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
+      var gamepads = [];
+      // Spoof this function.
+      navigator['getGamepads'] = function() {
+        return gamepads;
+      };
+      window['addNewGamepad'] = function(id, numAxes, numButtons) {
+        var index = gamepads.length;
+        gamepads.push({
+          axes: new Array(numAxes),
+          buttons: new Array(numButtons),
+          id: id,
+          index: index
+        });
+        var i;
+        for (i = 0; i < numAxes; i++) gamepads[index].axes[i] = 0;
+        for (i = 0; i < numButtons; i++) gamepads[index].buttons[i] = 0;
+      };
+      window['simulateGamepadButtonDown'] = function (index, button) {
+        gamepads[index].buttons[button] = 1;
+      };
+      window['simulateGamepadButtonUp'] = function (index, button) {
+        gamepads[index].buttons[button] = 0;
+      };
+      window['simulateAxisMotion'] = function (index, axis, value) {
+        gamepads[index].axes[axis] = value;
+      };
+    ''')
+    open(os.path.join(self.get_dir(), 'sdl_joystick.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_joystick.c')).read()))
+
+    Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_joystick.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js']).communicate()
+    self.run_browser('page.html', '', '/report_result?2')
+
+  def test_sdl_joystick_2(self):
+    # Generates events corresponding to the Editor's Draft of the HTML5 Gamepad API.
+    # https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#idl-def-Gamepad
+    open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
+      var gamepads = [];
+      // Spoof this function.
+      navigator['getGamepads'] = function() {
+        return gamepads;
+      };
+      window['addNewGamepad'] = function(id, numAxes, numButtons) {
+        var index = gamepads.length;
+        gamepads.push({
+          axes: new Array(numAxes),
+          buttons: new Array(numButtons),
+          id: id,
+          index: index
+        });
+        var i;
+        for (i = 0; i < numAxes; i++) gamepads[index].axes[i] = 0;
+        // Buttons are objects
+        for (i = 0; i < numButtons; i++) gamepads[index].buttons[i] = { pressed: false, value: 0 };
+      };
+      // FF mutates the original objects.
+      window['simulateGamepadButtonDown'] = function (index, button) {
+        gamepads[index].buttons[button].pressed = true;
+        gamepads[index].buttons[button].value = 1;
+      };
+      window['simulateGamepadButtonUp'] = function (index, button) {
+        gamepads[index].buttons[button].pressed = false;
+        gamepads[index].buttons[button].value = 0;
+      };
+      window['simulateAxisMotion'] = function (index, axis, value) {
+        gamepads[index].axes[axis] = value;
+      };
+    ''')
+    open(os.path.join(self.get_dir(), 'sdl_joystick.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_joystick.c')).read()))
+
+    Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_joystick.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js']).communicate()
+    self.run_browser('page.html', '', '/report_result?2')
+
   def test_webgl_context_attributes(self):
     # Javascript code to check the attributes support we want to test in the WebGL implementation 
     # (request the attribute, create a context and check its value afterwards in the context attributes).

From 1c7c811c8ded27a733075529f736797953a5209b Mon Sep 17 00:00:00 2001
From: ToadKing <toadking@toadking.com>
Date: Sun, 10 Nov 2013 03:38:58 -0500
Subject: [PATCH 48/61] nanosleep - don't write to rmtp if it's NULL

---
 src/library.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/library.js b/src/library.js
index 31f531e931861..e08d63da6a393 100644
--- a/src/library.js
+++ b/src/library.js
@@ -6131,8 +6131,10 @@ LibraryManager.library = {
     // int nanosleep(const struct timespec  *rqtp, struct timespec *rmtp);
     var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}};
     var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}};
-    {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}
-    {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}
+    if (rmtp !== 0) {
+      {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}
+      {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}
+    }
     return _usleep((seconds * 1e6) + (nanoseconds / 1000));
   },
   // TODO: Implement these for real.

From 0d37bbee315eb06c9b326224d673bda0f4e9e338 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Sun, 10 Nov 2013 14:48:56 +0200
Subject: [PATCH 49/61] Fix Makefile in test_lua in the case when make is in a
 path that contains spaces (e.g. c:\program
 files\emscripten\...\mingw32-make).

---
 tests/lua/Makefile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/lua/Makefile b/tests/lua/Makefile
index bd9515fd84dad..9f0a2edde5a04 100644
--- a/tests/lua/Makefile
+++ b/tests/lua/Makefile
@@ -51,8 +51,9 @@ R= $V.1
 # Targets start here.
 all:	$(PLAT)
 
+# XXX Emscripten Added quotes to $(MAKE) to properly call make when the path contains spaces
 $(PLATS) clean:
-	cd src && $(MAKE) $@
+	cd src && "$(MAKE)" $@
 
 test:	dummy
 	src/lua -v

From 74e4c26aa1cdf71139b22ef79936a092706b5707 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= <jujjyl@gmail.com>
Date: Sun, 10 Nov 2013 17:09:51 +0200
Subject: [PATCH 50/61] Make GL library more spec conformant by not throwing
 exceptions on errors, but instead reporting them via glGetError() as the
 GLES2 spec says. Use the linker flag "-s GL_ASSERTIONS=1" to enable
 additional debug-time error logging from the GL layer for error reasons.

---
 src/library_gl.js | 80 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 62 insertions(+), 18 deletions(-)

diff --git a/src/library_gl.js b/src/library_gl.js
index ecb72f0f19e72..4b6ea579d5875 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -530,7 +530,11 @@ var LibraryGL = {
         ret = allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL);
         break;
       default:
-        throw 'Failure: Invalid glGetString value: ' + name_;
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glGetString: Unknown parameter ' + name_ + '!');
+#endif
+        return 0;
     }
     GL.stringCache[name_] = ret;
     return ret;
@@ -561,7 +565,11 @@ var LibraryGL = {
         {{{ makeSetValue('p', '0', 'result ? 1 : 0', 'i8') }}};
         break;
       case "string":
-        throw 'Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!';
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!');
+#endif
+        return;
       case "object":
         if (result === null) {
           {{{ makeSetValue('p', '0', '0', 'i32') }}};
@@ -583,13 +591,19 @@ var LibraryGL = {
         } else if (result instanceof WebGLTexture) {
           {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
         } else {
-          throw 'Unknown object returned from WebGL getParameter';
+          GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+          Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Unknown object returned from WebGL getParameter(' + name_ + ')!');
+#endif
+          return;
         }
         break;
-      case "undefined":
-        throw 'Native code calling glGetIntegerv(' + name_ + ') and it returns undefined';
       default:
-        throw 'Why did we hit the default case?';
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+        return;
     }
   },
 
@@ -607,7 +621,11 @@ var LibraryGL = {
           {{{ makeSetValue('p', '0', '0', 'float') }}};
       case "object":
         if (result === null) {
-          throw 'Native code calling glGetFloatv(' + name_ + ') and it returns null';
+          GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+          Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns null!');
+#endif
+          return;
         } else if (result instanceof Float32Array ||
                    result instanceof Uint32Array ||
                    result instanceof Int32Array ||
@@ -626,13 +644,19 @@ var LibraryGL = {
         } else if (result instanceof WebGLTexture) {
           {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
         } else {
-          throw 'Unknown object returned from WebGL getParameter';
+          GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+          Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+          return;
         }
         break;
-      case "undefined":
-        throw 'Native code calling glGetFloatv(' + name_ + ') and it returns undefined';
       default:
-        throw 'Why did we hit the default case?';
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+        return;
     }
   },
 
@@ -647,7 +671,11 @@ var LibraryGL = {
         {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}};
         break;
       case "string":
-        throw 'Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!';
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!');
+#endif
+        return;
       case "object":
         if (result === null) {
           {{{ makeSetValue('p', '0', '0', 'i8') }}};
@@ -665,13 +693,19 @@ var LibraryGL = {
                    result instanceof WebGLTexture) {
           {{{ makeSetValue('p', '0', '1', 'i8') }}}; // non-zero ID is always 1!
         } else {
-          throw 'Unknown object returned from WebGL getParameter';
+          GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+          Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Unknown object returned from WebGL getParameter(' + name_ + ')!');
+#endif
+          return;
         }
         break;
-      case "undefined":
-          throw 'Unknown object returned from WebGL getParameter';
       default:
-        throw 'Why did we hit the default case?';
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+        return;
     }
   },
 
@@ -759,7 +793,12 @@ var LibraryGL = {
       case 0x1908 /* GL_RGBA */:
         sizePerPixel = 4;
         break;
-      default: throw 'unsupported glReadPixels format';
+      default: 
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glReadPixels: Unsupported format ' + format + '!');
+#endif
+        return;
     }
     var totalSize = width*height*sizePerPixel;
     Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels, pixels + totalSize));
@@ -2191,7 +2230,12 @@ var LibraryGL = {
         attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
       case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
         attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break;
-      default: throw 'TODO: glGetPointerv for ' + name;
+      default:
+        GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+        Module.printErr('GL_INVALID_ENUM in glGetPointerv: Unsupported name ' + name + '!');
+#endif
+        return;
     }
     {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
   },

From eed2f8149d04b0fc991d3893f59a00f7f30442f0 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sun, 10 Nov 2013 11:34:32 -0800
Subject: [PATCH 51/61] more precise float testing

---
 tests/test_core.py | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/tests/test_core.py b/tests/test_core.py
index 4b76a9db81f6c..8038ea83c9520 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -8358,11 +8358,14 @@ def test_fasta(self):
       if self.emcc_args is None: return self.skip('requires emcc')
       results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''),
 (50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ]
-      for t in ['float', 'double']:
-        print t
-        src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', t)
-        for i, j in results:
-          self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
+      for precision in [0, 1, 2]:
+        Settings.PRECISE_F32 = precision
+        for t in ['float', 'double']:
+          print precision, t
+          src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', t)
+          for i, j in results:
+            self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1)
+          shutil.copyfile('src.cpp.o.js', '%d_%s.js' % (precision, t))
 
   def test_whets(self):
     if not Settings.ASM_JS: return self.skip('mainly a test for asm validation here')

From 37d0e6c55f9f48255b5ac34426196ddf6d5072a2 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Sun, 10 Nov 2013 17:45:06 -0800
Subject: [PATCH 52/61] use writeAsciiToMemory in library.js in more places

---
 src/library.js | 35 +++++++----------------------------
 1 file changed, 7 insertions(+), 28 deletions(-)

diff --git a/src/library.js b/src/library.js
index e08d63da6a393..7a9546629a230 100644
--- a/src/library.js
+++ b/src/library.js
@@ -847,10 +847,7 @@ LibraryManager.library = {
       ___setErrNo(ERRNO_CODES.ERANGE);
       return 0;
     } else {
-      for (var i = 0; i < cwd.length; i++) {
-        {{{ makeSetValue('buf', 'i', 'cwd.charCodeAt(i)', 'i8') }}}
-      }
-      {{{ makeSetValue('buf', 'i', '0', 'i8') }}}
+      writeAsciiToMemory(cwd, buf);
       return buf;
     }
   },
@@ -1293,10 +1290,7 @@ LibraryManager.library = {
     if (namesize < ret.length + 1) {
       return ___setErrNo(ERRNO_CODES.ERANGE);
     } else {
-      for (var i = 0; i < ret.length; i++) {
-        {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}}
-      }
-      {{{ makeSetValue('name', 'i', '0', 'i8') }}}
+      writeAsciiToMemory(ret, name);
       return 0;
     }
   },
@@ -2699,10 +2693,7 @@ LibraryManager.library = {
     var result = dir + '/' + name;
     if (!_tmpnam.buffer) _tmpnam.buffer = _malloc(256);
     if (!s) s = _tmpnam.buffer;
-    for (var i = 0; i < result.length; i++) {
-      {{{ makeSetValue('s', 'i', 'result.charCodeAt(i)', 'i8') }}};
-    }
-    {{{ makeSetValue('s', 'i', '0', 'i8') }}};
+    writeAsciiToMemory(result, s);
     return s;
   },
   tempnam__deps: ['tmpnam'],
@@ -3343,10 +3334,7 @@ LibraryManager.library = {
     var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}};
     for (var i = 0; i < strings.length; i++) {
       var line = strings[i];
-      for (var j = 0; j < line.length; j++) {
-        {{{ makeSetValue('poolPtr', 'j', 'line.charCodeAt(j)', 'i8') }}};
-      }
-      {{{ makeSetValue('poolPtr', 'j', '0', 'i8') }}};
+      writeAsciiToMemory(line, poolPtr);
       {{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}};
       poolPtr += line.length + 1;
     }
@@ -3976,10 +3964,7 @@ LibraryManager.library = {
         return ___setErrNo(ERRNO_CODES.ERANGE);
       } else {
         var msg = ERRNO_MESSAGES[errnum];
-        for (var i = 0; i < msg.length; i++) {
-          {{{ makeSetValue('strerrbuf', 'i', 'msg.charCodeAt(i)', 'i8') }}}
-        }
-        {{{ makeSetValue('strerrbuf', 'i', 0, 'i8') }}}
+        writeAsciiToMemory(msg, strerrbuf);
         return 0;
       }
     } else {
@@ -5067,10 +5052,7 @@ LibraryManager.library = {
     var layout = {{{ JSON.stringify(C_STRUCTS.utsname) }}};
     function copyString(element, value) {
       var offset = layout[element];
-      for (var i = 0; i < value.length; i++) {
-        {{{ makeSetValue('name', 'offset + i', 'value.charCodeAt(i)', 'i8') }}}
-      }
-      {{{ makeSetValue('name', 'offset + i', '0', 'i8') }}}
+      writeAsciiToMemory(value, name + offset);
     }
     if (name === 0) {
       return -1;
@@ -6574,10 +6556,7 @@ LibraryManager.library = {
 
     var me = _nl_langinfo;
     if (!me.ret) me.ret = _malloc(32);
-    for (var i = 0; i < result.length; i++) {
-      {{{ makeSetValue('me.ret', 'i', 'result.charCodeAt(i)', 'i8') }}}
-    }
-    {{{ makeSetValue('me.ret', 'i', '0', 'i8') }}}
+    writeAsciiToMemory(result, me.ret);
     return me.ret;
   },
 

From eb8172ed7872c99b6438c4ab0fa9f83372fc44b0 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 10:06:29 -0800
Subject: [PATCH 53/61] minify in asm var definitions that denormalizeAsm
 generates, to handle Math.fround calls

---
 tools/js-optimizer.js | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d2bfcd9185d21..0371b3a374b6c 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -2059,6 +2059,25 @@ function registerize(ast) {
         }
       }
       denormalizeAsm(fun, finalAsmData);
+      if (extraInfo && extraInfo.globals) {
+        // minify in asm var definitions, that denormalizeAsm just generated
+        var stats = fun[3];
+        for (var i = fun[2].length; i < stats.length; i++) {
+          var line = stats[i];
+          if (line[0] !== 'var') break;
+          var pairs = line[1];
+          for (var j = 0; j < pairs.length; j++) {
+            var value = pairs[j][1];
+            if (value && value[0] === 'call' && value[1][0] === 'name') {
+              var name = value[1][1];
+              var minified = extraInfo.globals[name];
+              if (minified) {
+                value[1][1] = minified;
+              }
+            }
+          }
+        }
+      }
     }
   });
 }

From e238b7e11fa973cd86721e5b1ed45b50b52e3c0d Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 10:23:40 -0800
Subject: [PATCH 54/61] minify in asm arg coercions that denormalizeAsm
 generates, to handle Math.fround calls

---
 tools/js-optimizer.js | 30 +++++++++++++++++++-----------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 0371b3a374b6c..57ce0071d5e6f 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -2061,19 +2061,27 @@ function registerize(ast) {
       denormalizeAsm(fun, finalAsmData);
       if (extraInfo && extraInfo.globals) {
         // minify in asm var definitions, that denormalizeAsm just generated
+        function minify(value) {
+          if (value && value[0] === 'call' && value[1][0] === 'name') {
+            var name = value[1][1];
+            var minified = extraInfo.globals[name];
+            if (minified) {
+              value[1][1] = minified;
+            }
+          }
+        }
         var stats = fun[3];
-        for (var i = fun[2].length; i < stats.length; i++) {
+        for (var i = 0; i < stats.length; i++) {
           var line = stats[i];
-          if (line[0] !== 'var') break;
-          var pairs = line[1];
-          for (var j = 0; j < pairs.length; j++) {
-            var value = pairs[j][1];
-            if (value && value[0] === 'call' && value[1][0] === 'name') {
-              var name = value[1][1];
-              var minified = extraInfo.globals[name];
-              if (minified) {
-                value[1][1] = minified;
-              }
+          if (i >= fun[2].length && line[0] !== 'var') break; // when we pass the arg and var coercions, break
+          if (line[0] === 'stat') {
+            assert(line[1][0] === 'assign');
+            minify(line[1][3]);
+          } else {
+            assert(line[0] === 'var');
+            var pairs = line[1];
+            for (var j = 0; j < pairs.length; j++) {
+              minify(pairs[j][1]);
             }
           }
         }

From fa0443ee8ef4306ee2bf848a6c18d189bafbac77 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 10:41:01 -0800
Subject: [PATCH 55/61] benchmark linpack in both float and double modes

---
 tests/test_benchmark.py | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py
index e9cfee52df082..1c5a2d71601ab 100644
--- a/tests/test_benchmark.py
+++ b/tests/test_benchmark.py
@@ -428,10 +428,15 @@ def test_life(self):
     src = open(path_from_root('tests', 'life.c'), 'r').read()
     self.do_benchmark('life', src, '''--------------------------------''', shared_args=['-std=c99'], force_c=True)
 
-  def test_linpack(self):
+  def test_linpack_double(self):
     def output_parser(output):
       return 100.0/float(re.search('Unrolled Double  Precision +([\d\.]+) Mflops', output).group(1))
-    self.do_benchmark('linpack', open(path_from_root('tests', 'linpack.c')).read(), '''Unrolled Double  Precision''', force_c=True, output_parser=output_parser)
+    self.do_benchmark('linpack_double', open(path_from_root('tests', 'linpack.c')).read(), '''Unrolled Double  Precision''', force_c=True, output_parser=output_parser)
+
+  def test_linpack_float(self):
+    def output_parser(output):
+      return 100.0/float(re.search('Unrolled Single  Precision +([\d\.]+) Mflops', output).group(1))
+    self.do_benchmark('linpack_float', open(path_from_root('tests', 'linpack.c')).read(), '''Unrolled Single  Precision''', force_c=True, output_parser=output_parser, shared_args=['-DSP'])
 
   def test_zzz_java_nbody(self): # tests xmlvm compiled java, including bitcasts of doubles, i64 math, etc.
     args = [path_from_root('tests', 'nbody-java', x) for x in os.listdir(path_from_root('tests', 'nbody-java')) if x.endswith('.c')] + \
@@ -504,4 +509,4 @@ def test_zzz_bullet(self): # Called thus so it runs late in the alphabetical cyc
     native_args = native_lib + ['-I' + path_from_root('tests', 'bullet', 'src'),
                                 '-I' + path_from_root('tests', 'bullet', 'Demos', 'Benchmarks')]
 
-    self.do_benchmark('bullet', src, '\nok.\n', emcc_args=emcc_args, native_args=native_args)
\ No newline at end of file
+    self.do_benchmark('bullet', src, '\nok.\n', emcc_args=emcc_args, native_args=native_args)

From 9d006256df96e6d81bf869958af28643720f14db Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 10:59:18 -0800
Subject: [PATCH 56/61] coerce to double before converting a float to an i64

---
 src/parseTools.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/parseTools.js b/src/parseTools.js
index adb3f6e4ff4a3..e806c86111b6b 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -2259,7 +2259,7 @@ function processMathop(item) {
 				throw 'shifts should have been legalized!';
       }
       case 'uitofp': case 'sitofp': return makeFloat(RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u'), item.type);
-      case 'fptoui': case 'fptosi': return finish(splitI64(idents[0], true));
+      case 'fptoui': case 'fptosi': return finish(splitI64(asmCoercion(idents[0], 'double'), true)); // coerce to double before conversion to i64
       case 'icmp': {
         switch (variant) {
           case 'uge': return '((' + high1 + '>>>0) >= (' + high2 + '>>>0)) & ((((' + high1 + '>>>0) >  ('  + high2 + '>>>0)) | ' +

From 0ba049d69dbcb0cafd339f33eaa9dc9aa292a7e1 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 10:59:55 -0800
Subject: [PATCH 57/61] add option for precise f32 in benchmarks

---
 tests/test_benchmark.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py
index 1c5a2d71601ab..943b133d63975 100644
--- a/tests/test_benchmark.py
+++ b/tests/test_benchmark.py
@@ -122,6 +122,7 @@ def process(filename):
                     '--llvm-lto', '3', '--memory-init-file', '0', '--js-transform', 'python hardcode.py',
                     '-s', 'TOTAL_MEMORY=128*1024*1024',
                     '--closure', '1',
+                    #'-s', 'PRECISE_F32=1',
                     #'-g',
                     '-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate()
     assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0]

From 2182170ca32698886e311b7e5edbb5f9ed30aa3a Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 11:29:47 -0800
Subject: [PATCH 58/61] test suite changes for f32 landing: enable
 float32_precise in all modes, disable asm1f (rely on testing in
 float32_precise and fasta), and mark asm2f as permanent

---
 tests/test_core.py | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/tests/test_core.py b/tests/test_core.py
index 8038ea83c9520..30b9c70f4d08f 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -883,7 +883,7 @@ def test_double_i64_conversion(self):
     self.do_run(src, 'OK!\n');
 
   def test_float32_precise(self):
-    if self.emcc_args == None or 'PRECISE_F32=1' not in self.emcc_args: return self.skip('needs precise float32')
+    Settings.PRECISE_F32 = 1
 
     src = r'''
       #include <stdio.h>
@@ -10739,9 +10739,8 @@ def setUp(self):
 
 # asm.js
 asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"])
-asm1f = make_run("asm1f", compiler=CLANG, emcc_args=["-O1", "-s", "PRECISE_F32=1"])
 asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"])
-asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1"]) # TODO: collapse into another test subsuite
+asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1"])
 asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "CHECK_HEAP_ALIGN=1"])
 asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"})
 

From 0ea99d4659ec686115c70f0b7e67fa829739c1e0 Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 11:47:05 -0800
Subject: [PATCH 59/61] remove asm1f from ALL

---
 tests/runner.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/runner.py b/tests/runner.py
index aed70d3511300..8c4a9abf448db 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -36,7 +36,7 @@ def path_from_root(*pathelems):
 
 # Core test runner class, shared between normal tests and benchmarks
 checked_sanity = False
-test_modes = ['default', 'o1', 'o2', 'asm1', 'asm1f', 'asm2', 'asm2f', 'asm2g', 'asm2x86', 's_0_0', 's_0_1']
+test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2f', 'asm2g', 'asm2x86', 's_0_0', 's_0_1']
 test_index = 0
 
 class RunnerCore(unittest.TestCase):

From 7e25db4555f3ce93014340a22b4901a2f379b0bc Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 15:52:34 -0800
Subject: [PATCH 60/61] remove unnecessary ltoing in benchmark suite

---
 tests/test_benchmark.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py
index 943b133d63975..d19afb8d6a89e 100644
--- a/tests/test_benchmark.py
+++ b/tests/test_benchmark.py
@@ -119,7 +119,7 @@ def process(filename):
     try_delete(final_filename)
     output = Popen([PYTHON, EMCC, filename, #'-O3',
                     '-O2', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0',
-                    '--llvm-lto', '3', '--memory-init-file', '0', '--js-transform', 'python hardcode.py',
+                    '--memory-init-file', '0', '--js-transform', 'python hardcode.py',
                     '-s', 'TOTAL_MEMORY=128*1024*1024',
                     '--closure', '1',
                     #'-s', 'PRECISE_F32=1',

From 9e17b54163e9888e31bf79cb2eee7df10e5ee06a Mon Sep 17 00:00:00 2001
From: Alon Zakai <alonzakai@gmail.com>
Date: Mon, 11 Nov 2013 17:40:43 -0800
Subject: [PATCH 61/61] version 1.7.3

---
 tools/shared.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/shared.py b/tools/shared.py
index a2654bef85eb0..03a22f789650b 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -307,7 +307,7 @@ def find_temp_directory():
 # we re-check sanity when the settings are changed)
 # We also re-check sanity and clear the cache when the version changes
 
-EMSCRIPTEN_VERSION = '1.7.2'
+EMSCRIPTEN_VERSION = '1.7.3'
 
 def generate_sanity():
   return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT