From b2221e564490421265f9e3b2e398a89bbdfb0516 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 18 Nov 2021 21:33:10 +0100 Subject: [PATCH] wasm: Implement structs stored on the stack By calculating the abi size of the struct, we move the stack pointer and store each field depending on its size (i.e. a 1-byte field will use i32.store8). This commit adds all required opcodes to perform those stores and loads. This also gets rid of `mir_offset` as we now save results of binary operations into locals and emit its result onto the stack within condbr instead. This makes everything a lot simpler but also more robust. In the future, we could look into an algorithm to re-use such locals. For struct fields we use the new `local_with_offset` tag. This stores the struct's stack pointer as well as the field's offset from that stack pointer. `allocLocal` will now always allocate a single local, using a given type. --- src/arch/wasm/CodeGen.zig | 298 ++++++++++++++++++++------------------ src/arch/wasm/Emit.zig | 22 +++ src/arch/wasm/Mir.zig | 116 ++++++++++++++- 3 files changed, 296 insertions(+), 140 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index ea8611fbe3..175a0b6b71 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -28,16 +28,15 @@ const WValue = union(enum) { local: u32, /// Holds a memoized typed value constant: TypedValue, - /// Offset position in the list of MIR instructions - mir_offset: usize, - /// Used for variables that create multiple locals on the stack when allocated - /// such as structs and optionals. - multi_value: struct { - /// The index of the first local variable - index: u32, - /// The count of local variables this `WValue` consists of. - /// i.e. an ErrorUnion has a 'count' of 2. - count: u32, + /// Used for types that contains of multiple areas within + /// a memory region in the stack. + /// The local represents the position in the stack, + /// whereas the offset represents the offset from that position. + local_with_offset: struct { + /// Index of the local variable + local: u32, + /// The offset from the local's stack position + offset: u32, }, }; @@ -187,7 +186,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { }, 32 => switch (args.valtype1.?) { .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, - .i32, .f32, .f64 => unreachable, + .i32 => return .i32_load, + .f32, .f64 => unreachable, }, else => unreachable, } else switch (args.valtype1.?) { @@ -668,9 +668,10 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { .Bool, .Pointer, .ErrorSet, + .Struct, + .ErrorUnion, => wasm.Valtype.i32, - .Struct, .ErrorUnion, .Optional => unreachable, // Multi typed, must be handled individually. - else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}), + else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), }; } @@ -692,76 +693,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 { /// Writes the bytecode depending on the given `WValue` in `val` fn emitWValue(self: *Self, val: WValue) InnerError!void { switch (val) { - .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually - .none, .mir_offset => {}, // no-op + .none => {}, // no-op + .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local), .local => |idx| try self.addLabel(.local_get, idx), .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack } } -/// Creates one or multiple locals for a given `Type`. -/// Returns a corresponding `Wvalue` that can either be of tag -/// local or multi_value +/// Creates one locals for a given `Type`. +/// Returns a corresponding `Wvalue` with `local` as active tag fn allocLocal(self: *Self, ty: Type) InnerError!WValue { const initial_index = self.local_index; - switch (ty.zigTypeTag()) { - .Struct => { - // for each struct field, generate a local - const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; - const fields_len = @intCast(u32, struct_data.fields.count()); - try self.locals.ensureUnusedCapacity(self.gpa, fields_len); - for (struct_data.fields.values()) |*value| { - const val_type = try self.genValtype(value.ty); - self.locals.appendAssumeCapacity(val_type); - self.local_index += 1; - } - return WValue{ .multi_value = .{ - .index = initial_index, - .count = fields_len, - } }; - }, - .ErrorUnion => { - const payload_type = ty.errorUnionPayload(); - const val_type = try self.genValtype(payload_type); - - // we emit the error value as the first local, and the payload as the following. - // The first local is also used to find the index of the error and payload. - // - // TODO: Add support where the payload is a type that contains multiple locals such as a struct. - try self.locals.ensureUnusedCapacity(self.gpa, 2); - self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 - self.locals.appendAssumeCapacity(val_type); - self.local_index += 2; - - return WValue{ .multi_value = .{ - .index = initial_index, - .count = 2, - } }; - }, - .Optional => { - var opt_buf: Type.Payload.ElemType = undefined; - const child_type = ty.optionalChild(&opt_buf); - if (ty.isPtrLikeOptional()) { - return self.fail("TODO: wasm optional pointer", .{}); - } - - try self.locals.ensureUnusedCapacity(self.gpa, 2); - self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32 - self.locals.appendAssumeCapacity(try self.genValtype(child_type)); - self.local_index += 2; - - return WValue{ .multi_value = .{ - .index = initial_index, - .count = 2, - } }; - }, - else => { - const valtype = try self.genValtype(ty); - try self.locals.append(self.gpa, valtype); - self.local_index += 1; - return WValue{ .local = initial_index }; - }, - } + const valtype = try self.genValtype(ty); + try self.locals.append(self.gpa, valtype); + self.local_index += 1; + return WValue{ .local = initial_index }; } fn genFunctype(self: *Self) InnerError!void { @@ -857,7 +803,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { if (ty.sentinel()) |sentinel| { try self.code.appendSlice(payload.data); - switch (try self.gen(ty.elemType(), sentinel)) { + switch (try self.gen(ty.childType(), sentinel)) { .appended => return Result.appended, .externally_managed => |data| { try self.code.appendSlice(data); @@ -927,15 +873,8 @@ fn restoreStackPointer(self: *Self) !void { /// the result back into the stack pointer. fn moveStack(self: *Self, offset: u32, local: u32) !void { if (offset == 0) return; - // Generates the following code: - // - // global.get 0 - // i32.const [offset] - // i32.sub - // global.set 0 - // TODO: Rather than hardcode the stack pointer to position 0, - // have the linker resolve it. + // have the linker resolve its relocation try self.addLabel(.global_get, 0); try self.addImm32(@bitCast(i32, offset)); try self.addTag(.i32_sub); @@ -1053,17 +992,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - const elem_type = self.air.typeOfIndex(inst).elemType(); + const child_type = self.air.typeOfIndex(inst).childType(); // Initialize the stack if (self.initial_stack_value == .none) { try self.initializeStack(); } - const abi_size = elem_type.abiSize(self.target); + const abi_size = child_type.abiSize(self.target); if (abi_size == 0) return WValue{ .none = {} }; - const local = try self.allocLocal(elem_type); + // local, containing the offset to the stack position + const local = try self.allocLocal(child_type); try self.moveStack(@intCast(u32, abi_size), local.local); return local; @@ -1074,43 +1014,100 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const lhs = self.resolveInst(bin_op.lhs); const rhs = self.resolveInst(bin_op.rhs); + const ty = self.air.typeOf(bin_op.lhs).childType(); - // get lhs stack position + const offset: u32 = switch (lhs) { + .local_with_offset => |with_off| with_off.offset, + else => 0, + }; + + switch (ty.zigTypeTag()) { + .ErrorUnion, .Optional => { + var buf: Type.Payload.ElemType = undefined; + const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf); + const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.i8); + const payload_offset = @intCast(u32, tag_ty.abiSize(self.target) / 8); + if (rhs == .constant) { + // constant will contain both tag and payload, + // so save those in 2 temporary locals before storing them + // in memory + try self.emitWValue(rhs); + const tag_local = try self.allocLocal(Type.initTag(.i32)); + const payload_local = try self.allocLocal(payload_ty); + + try self.addLabel(.local_set, payload_local.local); + try self.addLabel(.local_set, tag_local.local); + + try self.store(lhs, tag_local, tag_ty, 0); + try self.store(lhs, payload_local, payload_ty, payload_offset); + } else if (offset == 0) { + // tag is being set + try self.store(lhs, rhs, tag_ty, 0); + } else { + // payload is being set + try self.store(lhs, rhs, payload_ty, payload_offset); + } + }, + else => try self.store(lhs, rhs, ty, offset), + } + + return .none; +} + +fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { try self.emitWValue(lhs); - // get rhs value try self.emitWValue(rhs); - - const ty = self.air.typeOf(bin_op.lhs); const valtype = try self.typeToValtype(ty); - const opcode = buildOpcode(.{ .valtype1 = valtype, .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size .op = .store, }); - // store rhs value at stack pointer's location in memory - const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); - try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } }); - return .none; + // store rhs value at stack pointer's location in memory + const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 }); + try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } }); } fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const lhs = self.resolveInst(ty_op.operand); + const operand = self.resolveInst(ty_op.operand); + const ty = self.air.getRefType(ty_op.ty); + return switch (ty.zigTypeTag()) { + .Struct, .ErrorUnion => operand, + else => self.load(operand, ty, 0), + }; +} + +fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { // load local's value from memory by its stack position - try self.emitWValue(lhs); - const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); - try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = mem_arg_index } }); - return .none; + try self.emitWValue(operand); + // Build the opcode with the right bitsize + const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed; + const opcode = buildOpcode(.{ + .valtype1 = try self.typeToValtype(ty), + .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size + .op = .load, + .signedness = signedness, + }); + + const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 }); + try self.addInst(.{ + .tag = Mir.Inst.Tag.fromOpcode(opcode), + .data = .{ .payload = mem_arg_index }, + }); + + // store the result in a local + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); + return result; } fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { _ = inst; - // arguments share the index with locals - defer self.local_index += 1; - return WValue{ .local = self.local_index }; + defer self.arg_index += 1; + return self.args[self.arg_index]; } fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { @@ -1388,14 +1385,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // insert blocks at the position of `offset` so // the condition can jump to it - const offset = switch (condition) { - .mir_offset => |offset| offset, - else => blk: { - const offset = self.mir_instructions.len; - try self.emitWValue(condition); - break :blk offset; - }, - }; + const offset = self.mir_instructions.len; + try self.emitWValue(condition); // result type is always noreturn, so use `block_empty` as type. try self.startBlock(.block, wasm.block_empty, offset); @@ -1415,10 +1406,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { - // save offset, so potential conditions can insert blocks in front of - // the comparison that we can later jump back to - const offset = self.mir_instructions.len; - const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; const lhs = self.resolveInst(data.bin_op.lhs); const rhs = self.resolveInst(data.bin_op.rhs); @@ -1447,7 +1434,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner .signedness = signedness, }); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - return WValue{ .mir_offset = offset }; + + const cmp_tmp = try self.allocLocal(lhs_ty); + try self.addLabel(.local_set, cmp_tmp.local); + return cmp_tmp; } fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1468,7 +1458,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const offset = self.mir_instructions.len; const operand = self.resolveInst(ty_op.operand); try self.emitWValue(operand); @@ -1478,7 +1467,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addImm32(0); try self.addTag(.i32_eq); - return WValue{ .mir_offset = offset }; + // save the result in the local + const not_tmp = try self.allocLocal(self.air.getRefType(ty_op.ty)); + try self.addLabel(.local_set, not_tmp.local); + return not_tmp; } fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1504,24 +1496,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.StructField, ty_pl.payload); const struct_ptr = self.resolveInst(extra.data.struct_operand); - return structFieldPtr(struct_ptr, extra.data.field_index); + const struct_ty = self.air.typeOf(extra.data.struct_operand).childType(); + const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch { + return self.fail("Field type '{}' too big to fit into stack frame", .{ + struct_ty.structFieldType(extra.data.field_index), + }); + }; + return structFieldPtr(struct_ptr, offset); } + fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const struct_ptr = self.resolveInst(ty_op.operand); - return structFieldPtr(struct_ptr, index); + const struct_ty = self.air.typeOf(ty_op.operand).childType(); + const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { + return self.fail("Field type '{}' too big to fit into stack frame", .{ + struct_ty.structFieldType(index), + }); + }; + return structFieldPtr(struct_ptr, offset); } -fn structFieldPtr(struct_ptr: WValue, index: u32) InnerError!WValue { - return WValue{ .local = struct_ptr.multi_value.index + index }; + +fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { + return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } }; } fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue.none; const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; - const struct_multivalue = self.resolveInst(extra.struct_operand).multi_value; - return WValue{ .local = struct_multivalue.index + extra.field_index }; + const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; + const struct_ty = self.air.typeOf(struct_field.struct_operand); + const operand = self.resolveInst(struct_field.struct_operand); + const field_index = struct_field.field_index; + const field_ty = struct_ty.structFieldType(field_index); + if (!field_ty.hasCodeGenBits()) return WValue.none; + const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { + return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); + }; + return try self.load(operand, field_ty, offset); } fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1675,25 +1688,32 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = self.resolveInst(un_op); - const offset = self.mir_instructions.len; + const err_ty = self.air.typeOf(un_op).errorUnionSet(); + + // load the error tag value + try self.emitWValue(operand); + const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); + try self.addInst(.{ + .tag = .i32_load, + .data = .{ .payload = mem_arg_index }, + }); - // load the error value which is positioned at multi_value's index - try self.emitWValue(.{ .local = operand.multi_value.index }); // Compare the error value with '0' try self.addImm32(0); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - return WValue{ .mir_offset = offset }; + const is_err_tmp = try self.allocLocal(err_ty); + try self.addLabel(.local_set, is_err_tmp.local); + return is_err_tmp; } fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = self.resolveInst(ty_op.operand); - // The index of multi_value contains the error code. To get the initial index of the payload we get - // the following index. Next, convert it to a `WValue.local` - // - // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct. - return WValue{ .local = operand.multi_value.index + 1 }; + const err_ty = self.air.typeOf(ty_op.operand); + const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target) / 8); + + return self.load(operand, self.air.getRefType(ty_op.ty), offset); } fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1728,8 +1748,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = self.resolveInst(un_op); - // load the null value which is positioned at multi_value's index - try self.emitWValue(.{ .local = operand.multi_value.index }); + // load the null value which is positioned at local_with_offset's index + try self.emitWValue(.{ .local = operand.local_with_offset.local }); try self.addImm32(0); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); @@ -1743,7 +1763,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = self.resolveInst(ty_op.operand); - return WValue{ .local = operand.multi_value.index + 1 }; + return WValue{ .local = operand.local_with_offset.local + 1 }; } fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 0b09a889f3..d70f0c5ee5 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -60,8 +60,30 @@ pub fn emitMir(emit: *Emit) InnerError!void { // memory instructions .i32_load => try emit.emitMemArg(tag, inst), + .i64_load => try emit.emitMemArg(tag, inst), + .f32_load => try emit.emitMemArg(tag, inst), + .f64_load => try emit.emitMemArg(tag, inst), + .i32_load8_s => try emit.emitMemArg(tag, inst), + .i32_load8_u => try emit.emitMemArg(tag, inst), + .i32_load16_s => try emit.emitMemArg(tag, inst), + .i32_load16_u => try emit.emitMemArg(tag, inst), + .i64_load8_s => try emit.emitMemArg(tag, inst), + .i64_load8_u => try emit.emitMemArg(tag, inst), + .i64_load16_s => try emit.emitMemArg(tag, inst), + .i64_load16_u => try emit.emitMemArg(tag, inst), + .i64_load32_s => try emit.emitMemArg(tag, inst), + .i64_load32_u => try emit.emitMemArg(tag, inst), .i32_store => try emit.emitMemArg(tag, inst), + .i64_store => try emit.emitMemArg(tag, inst), + .f32_store => try emit.emitMemArg(tag, inst), + .f64_store => try emit.emitMemArg(tag, inst), + .i32_store8 => try emit.emitMemArg(tag, inst), + .i32_store16 => try emit.emitMemArg(tag, inst), + .i64_store8 => try emit.emitMemArg(tag, inst), + .i64_store16 => try emit.emitMemArg(tag, inst), + .i64_store32 => try emit.emitMemArg(tag, inst), + // Instructions with an index that do not require relocations .local_get => try emit.emitLabel(tag, inst), .local_set => try emit.emitLabel(tag, inst), .local_tee => try emit.emitLabel(tag, inst), diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 5e20b64d39..dbe894f212 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -97,11 +97,125 @@ pub const Inst = struct { /// /// Uses `payload` of type `MemArg`. i32_load = 0x28, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i64_load = 0x29, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + f32_load = 0x2A, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + f64_load = 0x2B, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i32_load8_s = 0x2C, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i32_load8_u = 0x2D, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i32_load16_s = 0x2E, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i32_load16_u = 0x2F, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i64_load8_s = 0x30, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i64_load8_u = 0x31, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i64_load16_s = 0x32, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i64_load16_u = 0x33, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i64_load32_s = 0x34, + /// Loads a value from memory onto the stack, based on the signedness + /// and bitsize of the type. + /// + /// Uses `payload` with type `MemArg` + i64_load32_u = 0x35, /// Pops 2 values from the stack, where the first value represents the value to write into memory /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. /// /// Uses `payload` of type `MemArg`. i32_store = 0x36, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + i64_store = 0x37, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + f32_store = 0x38, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + f64_store = 0x39, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + i32_store8 = 0x3A, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + i32_store16 = 0x3B, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + i64_store8 = 0x3C, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + i64_store16 = 0x3D, + /// Pops 2 values from the stack, where the first value represents the value to write into memory + /// and the second value represents the offset into memory where the value must be written to. + /// This opcode is typed and expects the stack value's type to be equal to this opcode's type. + /// + /// Uses `Payload` with type `MemArg` + i64_store32 = 0x3E, /// Returns the memory size in amount of pages. /// /// Uses `nop` @@ -247,7 +361,7 @@ pub const Inst = struct { /// From a given wasm opcode, returns a MIR tag. pub fn fromOpcode(opcode: std.wasm.Opcode) Tag { - return @intToEnum(Tag, @enumToInt(opcode)); + return @intToEnum(Tag, @enumToInt(opcode)); // Given `Opcode` is not present as a tag for MIR yet } /// Returns a wasm opcode from a given MIR tag.