Repository: dotnet/runtime
File: src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs (C#)

Code before:

        private protected override void EmitRelocations(int sectionIndex, List<SymbolicRelocation> relocationList)
        {
            // This is a no-op for now under Wasm
        }

        const int StackPointerGlobalIndex = 0;
        const int R2RStartGlobalIndex = 1;

        private WasmImport[] _defaultImports = new[]
        {
            null, // placeholder for memory, which is set up dynamically in WriteImports()
            new WasmImport("env", "__stack_pointer", WasmExternalKind.Global, new WasmGlobalType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex),
            new WasmImport("env", "__r2r_start", WasmExternalKind.Global, new WasmGlobalType(WasmValueType.I32, WasmMutabilityType.Const), index: R2RStartGlobalIndex),
        };

        private void WriteImports()
        {
            // Calculate the required memory encodeSize based on the combined data section encodeSize
            ulong contentSize = (ulong)SectionByName(WasmObjectNodeSection.CombinedDataSection.Name).ContentSize;
            uint dataPages = checked((uint)((contentSize + (1<<16) - 1) >> 16));
            uint numPages = Math.Max(dataPages, 1); // Ensure at least one page is allocated for the minimum

            _defaultImports[0] = new WasmImport("env", "memory", WasmExternalKind.Memory,
                new WasmMemoryType(WasmLimitType.HasMin, numPages)); // memory limits: flags (0 = only minimum)

            int[] assignedImportIndices = new int[(int)WasmExternalKind.Count];
            foreach (WasmImport import in _defaultImports)
            {
                if (import.Index.HasValue)
                {
                    int assigned = assignedImportIndices[(int)import.Kind];
                    if (assigned != import.Index.Value)
                    {
                        throw new InvalidOperationException($"Import {import.Module}.{import.Name} of kind {import.Kind} assigned index {assigned}, needs {import.Index.Value}");
                    }
                }
                assignedImportIndices[(int)import.Kind]++;
                WriteImport(import);
            }
        }

        private void WriteExports()
        {
            WriteTableExport("table", 0);
            string[] functionExports = _uniqueSymbols.Keys.ToArray();
            // TODO-WASM: Handle exports better (e.g., only export public methods, etc.)
            // Also, see if we could leverage definedSymbols for this instead of doing our own bookkeeping in _uniqueSymbols.
            foreach (string name in functionExports.OrderBy(name => name))
            {
                WriteFunctionExport(name, _uniqueSymbols[name]);

Review comment (suggestion):
The imported memory’s minimum page calculation is now based only on the combined data size. Previously the writer effectively reserved a page for the stack and ensured the module’s declared memory minimum covered the data placement base (via DataStartOffset). With data segments now placed at `__r2r_start + offset`, if `__r2r_start` is non-zero and the host allocates memory using the module’s declared minimum, instantiation can trap because the active data segments won’t fit. Consider incorporating the expected base (e.g., the old 64KiB stack reservation / `__r2r_start` convention) into the minimum-page calculation, or otherwise making the memory minimum conservative enough to cover the maximum possible `__r2r_start` you intend to use.

Code after:

        private protected override void EmitRelocations(int sectionIndex, List<SymbolicRelocation> relocationList)
        {
            // This is a no-op for now under Wasm
        }

        const int StackPointerGlobalIndex = 0;
        const int R2RStartGlobalIndex = 1;

        private WasmImport[] _defaultImports = new[]
        {
            null, // placeholder for memory, which is set up dynamically in WriteImports()
            new WasmImport("env", "__stack_pointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex),
            new WasmImport("env", "__r2r_start", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: R2RStartGlobalIndex),
        };

        private void WriteImports()
        {
            // Calculate the minimum required memory size based on the combined data section size
            ulong contentSize = (ulong)SectionByName(WasmObjectNodeSection.CombinedDataSection.Name).ContentSize;
            uint dataPages = checked((uint)((contentSize + (1<<16) - 1) >> 16));
            uint numPages = Math.Max(dataPages, 1); // Ensure at least one page is allocated for the minimum

            _defaultImports[0] = new WasmImport("env", "memory", import: new WasmMemoryImportType(WasmLimitType.HasMin, numPages)); // memory limits: flags (0 = only minimum)

            int[] assignedImportIndices = new int[(int)WasmExternalKind.Count];
            foreach (WasmImport import in _defaultImports)
            {
                if (import.Index.HasValue)
                {
                    int assigned = assignedImportIndices[(int)import.Kind];
                    if (assigned != import.Index.Value)
                    {
                        throw new InvalidOperationException($"Import {import.Module}.{import.Name} of kind {import.Kind} assigned index {assigned}, needs {import.Index.Value}");
                    }
                }
                assignedImportIndices[(int)import.Kind]++;
                WriteImport(import);
            }
        }

        private void WriteExports()
        {
            WriteTableExport("table", 0);
            string[] functionExports = _uniqueSymbols.Keys.ToArray();
            // TODO-WASM: Handle exports better (e.g., only export public methods, etc.)
            // Also, see if we could leverage definedSymbols for this instead of doing our own bookkeeping in _uniqueSymbols.
            foreach (string name in functionExports.OrderBy(name => name))
            {
                WriteFunctionExport(name, _uniqueSymbols[name]);
            }