diff --git a/aeneas/src/main/CLOptions.v3 b/aeneas/src/main/CLOptions.v3 index 5675e8136..3f35b722e 100644 --- a/aeneas/src/main/CLOptions.v3 +++ b/aeneas/src/main/CLOptions.v3 @@ -185,6 +185,8 @@ component CLOptions { "Generate code that uses Wasm multivalue."); def WASM_EXPORT_MEM = wasmOpt.newBoolOption("wasm.export-mem", true, "Export the Wasm memory from generated modules."); + def WASM_PAGE_SIZE = wasmOpt.newSizeOption("wasm.page-size", 65536, + "Specify the Wasm memory page size (must be a power of 2)."); def MAIN_EXPORT = wasmOpt.newStringOption("main-export", "main", "Specify the name of the main export from a generated Wasm module."); def ENTRY_EXPORT = wasmOpt.newStringOption("entry-export", "entry", diff --git a/aeneas/src/wasm/WasmCodeGen.v3 b/aeneas/src/wasm/WasmCodeGen.v3 index 3f1c03875..02255b6b9 100644 --- a/aeneas/src/wasm/WasmCodeGen.v3 +++ b/aeneas/src/wasm/WasmCodeGen.v3 @@ -577,7 +577,7 @@ class WasmCodeGen extends SsaMachGen { WasmOp.I64_CONST.opcode, WasmOp.I32_CONST.opcode => emitInt(a[a.length - 1]); WasmOp.CALL.opcode => { - emitInt(a[a.length - 1]); // function_index + emitUint(a[a.length - 1]); // function_index } WasmOp.F32_CONST.opcode => { var val = Operand.Immediate.!(a[0]).val; @@ -590,7 +590,7 @@ class WasmCodeGen extends SsaMachGen { else w.put_b64(i64.view(Float64Val.!(val).bits)); } WasmOp.CALL_INDIRECT.opcode => { - emitInt(a[a.length - 1]); // type_index + emitUint(a[a.length - 1]); // type_index w.putb(0); // reserved } } @@ -620,6 +620,14 @@ class WasmCodeGen extends SsaMachGen { _ => context.fail1("cannot convert immediate to int: %s", V3.renderVal(val)); } } + def emitUint(o: Operand) { + var val = Operand.Immediate.!(o).val; + match (val) { + null => w.putb(0); + x: Box => w.put_uleb32(u32.view(x.val)); + _ => context.fail1("cannot convert immediate to unsigned int: %s", V3.renderVal(val)); + } + } def asmls(logAlign: int, a: Array, offsetOperand: int) { w.putb(logAlign); // flags = alignment emitInt(a[offsetOperand]); // offset diff --git a/aeneas/src/wasm/WasmTarget.v3 b/aeneas/src/wasm/WasmTarget.v3 index 3aa554006..eb111d104 100644 --- a/aeneas/src/wasm/WasmTarget.v3 +++ b/aeneas/src/wasm/WasmTarget.v3 @@ -13,8 +13,9 @@ component WasmComponent { Aeneas.registerTarget(WasmTarget.new("wasm", false)); } } -def WASM_PAGE_SIZE = 65536; def WASM_MAGIC = 0x6d736100; +def MINIMUM_DATA_START_ADDR = 8u; +def MAXIMUM_DATA_START_ADDR = 0x8000_0000u; def WASM_VERSION = 0x1; def XXX: Terminal; @@ -123,6 +124,13 @@ class WasmTarget extends Target { def processIrClass(ic: IrClass) { } def emitWasmModule(context: SsaContext, rt: MachRuntime) -> DataWriter { + def pageSize = CLOptions.WASM_PAGE_SIZE.get(); + if (!Ints.isPowerOf2(pageSize)) context.fail1("expected -wasm.page-size option to be a power of 2, got %d", pageSize); + + var dataStartAddrOption = CLOptions.DATA_START_ADDR.get(); + if (dataStartAddrOption > MAXIMUM_DATA_START_ADDR) context.fail1("-data-start-addr too large for Wasm target, got 0x%x", dataStartAddrOption); + def dataStartAddr = if(dataStartAddrOption < MINIMUM_DATA_START_ADDR, MINIMUM_DATA_START_ADDR, u32.view(dataStartAddrOption)); + def prog = context.prog; // Lower to machine level from V3 level def config = MachLoweringConfig.new(); @@ -240,14 +248,21 @@ class WasmTarget extends Target { //-------------------------------------------------------------------------------- out.startSection(WasmSection.MEMORY); out.putb(1); // 1 memory - // add the size of initialized memory and runtime to the heap size - var initSize = u32.view(m.metaRegion.mark + m.dataRegion.mark); - var pages = 2u + u32.!((rt.heapSize + rt.shadowStackSize - + initSize + u32.view(WASM_PAGE_SIZE - 1)) / - u32.view(WASM_PAGE_SIZE)); - out.putb(1); // flags = contains maximum - out.put_uleb32(pages); // initial memory size - out.put_uleb32(pages); // maximum memory size + // Compute the maximum accessible data address + var maxDataAddr = dataStartAddr + + u32.!(m.metaRegion.mark) + + u32.!(m.dataRegion.mark) + + u32.!(rt.heapSize + rt.shadowStackSize); + + var pages = (maxDataAddr + pageSize - 1u) / pageSize; + var flags = 1; + if (pageSize != 65536) flags |= 1 << 3; // custom page size + out.putb(flags); // flags = contains maximum + out.put_uleb32(pages); // initial memory size + out.put_uleb32(pages); // maximum memory size + if (pageSize != 65536) { + out.put_uleb32(u32.!(Ints.log(pageSize))); // page size log + } out.endSection(); // (7) Emit the export section @@ -329,11 +344,11 @@ class WasmTarget extends Target { out.putb(1); // 1 data entry out.putb(0); // linear memory #0 out.putb(WasmOp.I32_CONST.opcode); - out.put_sleb32(WASM_PAGE_SIZE); // linear memory offset + out.put_sleb32(int.view(dataStartAddr)); // linear memory offset out.putb(WasmOp.END.opcode); var data_sizepos = out.skip_leb32(); // data size var data_start = out.pos; - out.startAddr = WASM_PAGE_SIZE - out.end(); + out.startAddr = int.view(dataStartAddr) - out.end(); // Encode meta region m.layoutMeta(out); // Encode runtime region @@ -405,7 +420,8 @@ class WasmTarget extends Target { .putx(abs) .outln(); } - w.overwrite_uleb32(abs); + // TODO,XXX: address immediates are in i32.const, which is a signed LEB. + w.overwrite_sleb32(abs); } def emitTestWrappers(context: SsaContext, rt: MachRuntime, w: DataWriter, e: ExecuteTestCase) { var prog = context.prog; diff --git a/lib/util/DataWriter.v3 b/lib/util/DataWriter.v3 index 1737071a3..e9974e812 100644 --- a/lib/util/DataWriter.v3 +++ b/lib/util/DataWriter.v3 @@ -136,14 +136,22 @@ class DataWriter { skipN(5); return oldpos; } - // Overwrite a 32-bit LEB at the current position. + // Overwrite a 32-bit unsigned LEB at the current position. def overwrite_uleb32(val: int) -> this { for (i < 4) { putb(val | 0x80); - val >>= 7; + val >>>= 7; } putb(val); } + // Overwrite a 32-bit signed LEB at the current position. + def overwrite_sleb32(val: int) -> this { + for (i < 4) { + putb(val | 0x80); + val >>= 7; + } + putb(val & 0x7F); + } // Set the current position to {npos}. def at(npos: int) -> this { if (pos > max) max = pos; // remember the maximum pos diff --git a/lib/util/Ints.v3 b/lib/util/Ints.v3 index e9f2ba7f7..23ead8065 100644 --- a/lib/util/Ints.v3 +++ b/lib/util/Ints.v3 @@ -175,6 +175,10 @@ component Ints { def abs(a: int) -> u32 { return u32.view(if(a < 0, 0 - a, a)); } + // Return {true} if {a} is a power of 2. + def isPowerOf2(a: u32) -> bool { + return (a & (a - 1)) == 0; + } } // Parse result for parsing integers and longs. diff --git a/test/core/big_mtable00.v3 b/test/core/big_mtable00.v3 new file mode 100644 index 000000000..329487143 --- /dev/null +++ b/test/core/big_mtable00.v3 @@ -0,0 +1,85 @@ +//@execute =1893 + +class A { def m() -> int { return 33; } } + +class B00 extends A { def m() -> int { return 0x00; } } +class B01 extends A { def m() -> int { return 0x01; } } +class B02 extends A { def m() -> int { return 0x02; } } +class B03 extends A { def m() -> int { return 0x03; } } +class B04 extends A { def m() -> int { return 0x04; } } +class B05 extends A { def m() -> int { return 0x05; } } +class B06 extends A { def m() -> int { return 0x06; } } +class B07 extends A { def m() -> int { return 0x07; } } +class B08 extends A { def m() -> int { return 0x08; } } +class B09 extends A { def m() -> int { return 0x09; } } +class B0A extends A { def m() -> int { return 0x0A; } } +class B0B extends A { def m() -> int { return 0x0B; } } +class B0C extends A { def m() -> int { return 0x0C; } } +class B0D extends A { def m() -> int { return 0x0D; } } +class B0E extends A { def m() -> int { return 0x0E; } } +class B0F extends A { def m() -> int { return 0x0F; } } + +class B10 extends A { def m() -> int { return 0x10; } } +class B11 extends A { def m() -> int { return 0x11; } } +class B12 extends A { def m() -> int { return 0x12; } } +class B13 extends A { def m() -> int { return 0x13; } } +class B14 extends A { def m() -> int { return 0x14; } } +class B15 extends A { def m() -> int { return 0x15; } } +class B16 extends A { def m() -> int { return 0x16; } } +class B17 extends A { def m() -> int { return 0x17; } } +class B18 extends A { def m() -> int { return 0x18; } } +class B19 extends A { def m() -> int { return 0x19; } } +class B1A extends A { def m() -> int { return 0x1A; } } +class B1B extends A { def m() -> int { return 0x1B; } } +class B1C extends A { def m() -> int { return 0x1C; } } +class B1D extends A { def m() -> int { return 0x1D; } } +class B1E extends A { def m() -> int { return 0x1E; } } +class B1F extends A { def m() -> int { return 0x1F; } } + +class B20 extends A { def m() -> int { return 0x20; } } +class B21 extends A { def m() -> int { return 0x21; } } +class B22 extends A { def m() -> int { return 0x22; } } +class B23 extends A { def m() -> int { return 0x23; } } +class B24 extends A { def m() -> int { return 0x24; } } +class B25 extends A { def m() -> int { return 0x25; } } +class B26 extends A { def m() -> int { return 0x26; } } +class B27 extends A { def m() -> int { return 0x27; } } +class B28 extends A { def m() -> int { return 0x28; } } +class B29 extends A { def m() -> int { return 0x29; } } +class B2A extends A { def m() -> int { return 0x2A; } } +class B2B extends A { def m() -> int { return 0x2B; } } +class B2C extends A { def m() -> int { return 0x2C; } } +class B2D extends A { def m() -> int { return 0x2D; } } +class B2E extends A { def m() -> int { return 0x2E; } } +class B2F extends A { def m() -> int { return 0x2F; } } + +class B30 extends A { def m() -> int { return 0x30; } } +class B31 extends A { def m() -> int { return 0x31; } } +class B32 extends A { def m() -> int { return 0x32; } } +class B33 extends A { def m() -> int { return 0x33; } } +class B34 extends A { def m() -> int { return 0x34; } } +class B35 extends A { def m() -> int { return 0x35; } } +class B36 extends A { def m() -> int { return 0x36; } } +class B37 extends A { def m() -> int { return 0x37; } } +class B38 extends A { def m() -> int { return 0x38; } } +class B39 extends A { def m() -> int { return 0x39; } } +class B3A extends A { def m() -> int { return 0x3A; } } +class B3B extends A { def m() -> int { return 0x3B; } } +class B3C extends A { def m() -> int { return 0x3C; } } +class B3D extends A { def m() -> int { return 0x3D; } } +class B3E extends A { def m() -> int { return 0x3E; } } +class B3F extends A { def m() -> int { return 0x3F; } } + +def array = [ + A.new(), + B00.new(), B01.new(), B02.new(), B03.new(), B04.new(), B05.new(), B06.new(), B07.new(), B08.new(), B09.new(), B0A.new(), B0B.new(), B0C.new(), B0D.new(), B0E.new(), + B10.new(), B11.new(), B12.new(), B13.new(), B14.new(), B15.new(), B16.new(), B17.new(), B18.new(), B19.new(), B1A.new(), B1B.new(), B1C.new(), B1D.new(), B1E.new(), + B20.new(), B21.new(), B22.new(), B23.new(), B24.new(), B25.new(), B26.new(), B27.new(), B28.new(), B29.new(), B2A.new(), B2B.new(), B2C.new(), B2D.new(), B2E.new(), + B30.new(), B31.new(), B32.new(), B33.new(), B34.new(), B35.new(), B36.new(), B37.new(), B38.new(), B39.new(), B3A.new(), B3B.new(), B3C.new(), B3D.new(), B3E.new() +]; + +def main() -> int { + var sum = 0; + for (o in array) sum += o.m(); + return sum; +} diff --git a/test/core/big_mtable01.v3 b/test/core/big_mtable01.v3 new file mode 100644 index 000000000..b1eef992f --- /dev/null +++ b/test/core/big_mtable01.v3 @@ -0,0 +1,85 @@ +//@execute =1893 + +class A { def m() -> int { return 33; } } + +class B00 extends A { def m() -> int { return 0x00; } } +class B01 extends A { def m() -> int { return 0x01; } } +class B02 extends A { def m() -> int { return 0x02; } } +class B03 extends A { def m() -> int { return 0x03; } } +class B04 extends A { def m() -> int { return 0x04; } } +class B05 extends A { def m() -> int { return 0x05; } } +class B06 extends A { def m() -> int { return 0x06; } } +class B07 extends A { def m() -> int { return 0x07; } } +class B08 extends A { def m() -> int { return 0x08; } } +class B09 extends A { def m() -> int { return 0x09; } } +class B0A extends A { def m() -> int { return 0x0A; } } +class B0B extends A { def m() -> int { return 0x0B; } } +class B0C extends A { def m() -> int { return 0x0C; } } +class B0D extends A { def m() -> int { return 0x0D; } } +class B0E extends A { def m() -> int { return 0x0E; } } +class B0F extends A { def m() -> int { return 0x0F; } } + +class B10 extends A { def m() -> int { return 0x10; } } +class B11 extends A { def m() -> int { return 0x11; } } +class B12 extends A { def m() -> int { return 0x12; } } +class B13 extends A { def m() -> int { return 0x13; } } +class B14 extends A { def m() -> int { return 0x14; } } +class B15 extends A { def m() -> int { return 0x15; } } +class B16 extends A { def m() -> int { return 0x16; } } +class B17 extends A { def m() -> int { return 0x17; } } +class B18 extends A { def m() -> int { return 0x18; } } +class B19 extends A { def m() -> int { return 0x19; } } +class B1A extends A { def m() -> int { return 0x1A; } } +class B1B extends A { def m() -> int { return 0x1B; } } +class B1C extends A { def m() -> int { return 0x1C; } } +class B1D extends A { def m() -> int { return 0x1D; } } +class B1E extends A { def m() -> int { return 0x1E; } } +class B1F extends A { def m() -> int { return 0x1F; } } + +class B20 extends A { def m() -> int { return 0x20; } } +class B21 extends A { def m() -> int { return 0x21; } } +class B22 extends A { def m() -> int { return 0x22; } } +class B23 extends A { def m() -> int { return 0x23; } } +class B24 extends A { def m() -> int { return 0x24; } } +class B25 extends A { def m() -> int { return 0x25; } } +class B26 extends A { def m() -> int { return 0x26; } } +class B27 extends A { def m() -> int { return 0x27; } } +class B28 extends A { def m() -> int { return 0x28; } } +class B29 extends A { def m() -> int { return 0x29; } } +class B2A extends A { def m() -> int { return 0x2A; } } +class B2B extends A { def m() -> int { return 0x2B; } } +class B2C extends A { def m() -> int { return 0x2C; } } +class B2D extends A { def m() -> int { return 0x2D; } } +class B2E extends A { def m() -> int { return 0x2E; } } +class B2F extends A { def m() -> int { return 0x2F; } } + +class B30 extends A { def m() -> int { return 0x30; } } +class B31 extends A { def m() -> int { return 0x31; } } +class B32 extends A { def m() -> int { return 0x32; } } +class B33 extends A { def m() -> int { return 0x33; } } +class B34 extends A { def m() -> int { return 0x34; } } +class B35 extends A { def m() -> int { return 0x35; } } +class B36 extends A { def m() -> int { return 0x36; } } +class B37 extends A { def m() -> int { return 0x37; } } +class B38 extends A { def m() -> int { return 0x38; } } +class B39 extends A { def m() -> int { return 0x39; } } +class B3A extends A { def m() -> int { return 0x3A; } } +class B3B extends A { def m() -> int { return 0x3B; } } +class B3C extends A { def m() -> int { return 0x3C; } } +class B3D extends A { def m() -> int { return 0x3D; } } +class B3E extends A { def m() -> int { return 0x3E; } } +class B3F extends A { def m() -> int { return 0x3F; } } + +def main() -> int { + def array = [ + A.new(), + B00.new(), B01.new(), B02.new(), B03.new(), B04.new(), B05.new(), B06.new(), B07.new(), B08.new(), B09.new(), B0A.new(), B0B.new(), B0C.new(), B0D.new(), B0E.new(), + B10.new(), B11.new(), B12.new(), B13.new(), B14.new(), B15.new(), B16.new(), B17.new(), B18.new(), B19.new(), B1A.new(), B1B.new(), B1C.new(), B1D.new(), B1E.new(), + B20.new(), B21.new(), B22.new(), B23.new(), B24.new(), B25.new(), B26.new(), B27.new(), B28.new(), B29.new(), B2A.new(), B2B.new(), B2C.new(), B2D.new(), B2E.new(), + B30.new(), B31.new(), B32.new(), B33.new(), B34.new(), B35.new(), B36.new(), B37.new(), B38.new(), B39.new(), B3A.new(), B3B.new(), B3C.new(), B3D.new(), B3E.new() + ]; + + var sum = 0; + for (o in array) sum += o.m(); + return sum; +}