Skip to content
Open
5 changes: 5 additions & 0 deletions src/settings_internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ var ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS = [];

var WARN_DEPRECATED = true;

// Enable fast math optimizations in wasm-opt when -ffast-math is passed.
// This enables aggressive floating-point optimizations that may violate
// IEEE 754 semantics but can improve performance.
var FAST_MATH = 0;

// WebGL 2 provides new garbage-free entry points to call to WebGL. Use
// those always when possible.
// We currently set this to false for certain browser when large memory sizes
Expand Down
27 changes: 27 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -15756,3 +15756,30 @@ def has_defined_function(file, func):
self.assertIn('main.cpp', out)
self.assertIn('foo.cpp', out)
self.assertIn('/emsdk/emscripten/system/lib/libc/musl/src/string/strcmp.c', out)

def test_fast_math_debug_output(self):
create_file('test.c', '''
#include <math.h>
int main() { return (int)(sin(1.0) * 100); }
''')

err = self.run_process([EMCC, 'test.c', '-v', '-O2', '-ffast-math'], stderr=PIPE).stderr
self.assertContained('--fast-math', err)

err_no_fast = self.run_process([EMCC, 'test.c', '-v', '-O2'], stderr=PIPE).stderr
self.assertNotContained('--fast-math', err_no_fast)

def test_fast_math_size_comparison(self):
create_file('math.c', '''
#include <math.h>
double f(double x) { return sin(x) * cos(x) + sqrt(x); }
int main() { return (int)f(1.5); }
''')

self.run_process([EMCC, 'math.c', '-O2', '-o', 'no_fast.wasm'])
no_fast_size = os.path.getsize('no_fast.wasm')

self.run_process([EMCC, 'math.c', '-O2', '-ffast-math', '-o', 'with_fast.wasm'])
with_fast_size = os.path.getsize('with_fast.wasm')

self.assertLessEqual(with_fast_size, no_fast_size)
7 changes: 6 additions & 1 deletion tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,12 @@ def minify_wasm_js(js_file, wasm_file, expensive_optimizations, debug_info):
# get the flags to pass into the very last binaryen tool invocation, that runs
# the final set of optimizations
def get_last_binaryen_opts():
return [f'--optimize-level={settings.OPT_LEVEL}',
opts = [f'--optimize-level={settings.OPT_LEVEL}',
f'--shrink-level={settings.SHRINK_LEVEL}',
'--optimize-stack-ir']
if settings.FAST_MATH:
opts.append('--fast-math')
return opts
Copy link
Member

@kripken kripken Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the wrong place for this: it is only sent into the very last binaryen tool invocation, as the comment says. We want to send this to every wasm-opt invocation, perhaps in run_wasm_opt

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about in get_binaryen_passes?



# run binaryen's wasm-metadce to dce both js and wasm
Expand Down Expand Up @@ -1258,6 +1261,8 @@ def run_binaryen_command(tool, infile, outfile=None, args=None, debug=False, std


def run_wasm_opt(infile, outfile=None, args=[], **kwargs): # noqa
if settings.FAST_MATH:
args.append('--fast-math')
return run_binaryen_command('wasm-opt', infile, outfile, args=args, **kwargs)


Expand Down
7 changes: 4 additions & 3 deletions tools/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,8 @@ def consume_arg_file():
settings.SHRINK_LEVEL = 0
settings.DEBUG_LEVEL = max(settings.DEBUG_LEVEL, 1)
elif requested_level == 'fast':
# TODO(https://github.com/emscripten-core/emscripten/issues/21497):
# If we ever map `-ffast-math` to `wasm-opt --fast-math` then
# then we should enable that too here.
# -Ofast typically includes -ffast-math semantics, so enable fast math optimizations
settings.FAST_MATH = 1
requested_level = 3
settings.SHRINK_LEVEL = 0
else:
Expand Down Expand Up @@ -545,6 +544,8 @@ def consume_arg_file():
settings.WASM_EXCEPTIONS = 1
elif arg == '-fignore-exceptions':
settings.DISABLE_EXCEPTION_CATCHING = 1
elif arg == '-ffast-math':
settings.FAST_MATH = 1
elif check_arg('--default-obj-ext'):
exit_with_error('--default-obj-ext is no longer supported by emcc')
elif arg.startswith('-fsanitize=cfi'):
Expand Down