diff --git a/emcc.py b/emcc.py index 52ee28c27547d..fcaf2b91e3ad2 100644 --- a/emcc.py +++ b/emcc.py @@ -384,6 +384,15 @@ def get_clang_flags(user_args): if '-mbulk-memory' not in user_args: flags.append('-mbulk-memory') + # In emscripten we currently disable bulk memory by default. + # This should be removed/updated when we als update the default browser targets. + if '-mbulk-memory' not in user_args and '-mno-bulk-memory' not in user_args: + # Bulk memory may be enabled via threads or directly via -s. + if not settings.BULK_MEMORY: + flags.append('-mno-bulk-memory') + if '-mnontrapping-fptoint' not in user_args and '-mno-nontrapping-fptoint' not in user_args: + flags.append('-mno-nontrapping-fptoint') + if settings.RELOCATABLE and '-fPIC' not in user_args: flags.append('-fPIC') diff --git a/test/test_other.py b/test/test_other.py index 61e13c20c037b..22802283c84d3 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -10268,6 +10268,51 @@ def test_wasm_features_section(self, args): self.run_process([EMCC, test_file('hello_world.c'), '-O2'] + args) self.verify_custom_sec_existence('a.out.wasm', 'target_features', False) + def test_wasm_features(self): + # Test that wasm features are explicitly enabled or disabled based on target engine version + def verify_features_sec(feature, expect_in, linked=False): + with webassembly.Module('a.out.wasm' if linked else 'hello_world.o') as module: + features = module.get_target_features() + if expect_in: + self.assertTrue(feature in features and + features[feature] == webassembly.TargetFeaturePrefix.USED, + f'{feature} missing from wasm file') + else: + self.assertFalse(feature in features, + f'{feature} unexpectedly found in wasm file') + + def verify_features_sec_linked(feature, expect_in): + return verify_features_sec(feature, expect_in, linked=True) + + def compile(flags): + self.run_process([EMCC, test_file('hello_world.c')] + flags) + + compile(['-c']) + verify_features_sec('bulk-memory', False) + verify_features_sec('nontrapping-fptoint', False) + verify_features_sec('sign-ext', True) + verify_features_sec('mutable-globals', True) + verify_features_sec('multivalue', True) + verify_features_sec('reference-types', True) + + compile(['-mnontrapping-fptoint', '-c']) + verify_features_sec('nontrapping-fptoint', True) + + # BIGINT causes binaryen to not run, and keeps the target_features section after link + # Setting this SAFARI_VERSION should enable bulk memory because it links in emscripten_memcpy_bulkmem + # However it does not enable nontrapping-fptoint yet because it has no effect at compile time and + # no libraries include nontrapping yet. + compile(['-sMIN_SAFARI_VERSION=150000', '-sWASM_BIGINT']) + verify_features_sec_linked('sign-ext', True) + verify_features_sec_linked('mutable-globals', True) + verify_features_sec_linked('multivalue', True) + verify_features_sec_linked('bulk-memory', True) + verify_features_sec_linked('nontrapping-fptoint', False) + + compile(['-sMIN_SAFARI_VERSION=150000', '-mno-bulk-memory', '-sWASM_BIGINT']) + # FIXME? -mno-bulk-memory at link time does not override MIN_SAFARI_VERSION. it probably should? + verify_features_sec_linked('bulk-memory', True) + def test_js_preprocess(self): # Use stderr rather than stdout here because stdout is redirected to the output JS file itself. create_file('lib.js', ''' diff --git a/tools/webassembly.py b/tools/webassembly.py index 69376b989ed4b..f8c9a3be38841 100644 --- a/tools/webassembly.py +++ b/tools/webassembly.py @@ -162,6 +162,12 @@ class DylinkType(IntEnum): IMPORT_INFO = 4 +class TargetFeaturePrefix(IntEnum): + USED = 0x2b + DISALLOWED = 0x2d + REQUIRED = 0x3d + + class InvalidWasmError(BaseException): pass @@ -561,6 +567,18 @@ def get_function_type(self, idx): func_type = self.get_function_types()[idx - self.num_imported_funcs()] return self.get_types()[func_type] + def get_target_features(self): + section = self.get_custom_section('target_features') + self.seek(section.offset) + assert self.read_string() == 'target_features' + features = {} + self.read_byte() # ignore feature count + while self.tell() < section.offset + section.size: + prefix = TargetFeaturePrefix(self.read_byte()) + feature = self.read_string() + features[feature] = prefix + return features + def parse_dylink_section(wasm_file): with Module(wasm_file) as module: