Skip to content

Commit

Permalink
[wasm] Add -wasm.page-size option (#272)
Browse files Browse the repository at this point in the history
  • Loading branch information
titzer authored Sep 6, 2024
1 parent f5101b9 commit 63d0b81
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 16 deletions.
2 changes: 2 additions & 0 deletions aeneas/src/main/CLOptions.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 10 additions & 2 deletions aeneas/src/wasm/WasmCodeGen.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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<int> => 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<Operand>, offsetOperand: int) {
w.putb(logAlign); // flags = alignment
emitInt(a[offsetOperand]); // offset
Expand Down
40 changes: 28 additions & 12 deletions aeneas/src/wasm/WasmTarget.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 10 additions & 2 deletions lib/util/DataWriter.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions lib/util/Ints.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
85 changes: 85 additions & 0 deletions test/core/big_mtable00.v3
Original file line number Diff line number Diff line change
@@ -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;
}
85 changes: 85 additions & 0 deletions test/core/big_mtable01.v3
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 63d0b81

Please sign in to comment.