authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-18 21:33:10+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-21 21:07:55+01:00
logb2221e564490421265f9e3b2e398a89bbdfb0516
treeddf654e63898ff97c88afa4c3c5201b6b2101f8f
parent261f13414b6bafbe075ce5066964b36a3a5b5e16
signaturelock-open Commit is signed but in an unrecognized format.

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.

3 files changed, 295 insertions(+), 139 deletions(-)

src/arch/wasm/CodeGen.zig+158-138
......@@ -28,16 +28,15 @@ const WValue = union(enum) {
2828 local: u32,
2929 /// Holds a memoized typed value
3030 constant: TypedValue,
31 /// Offset position in the list of MIR instructions
32 mir_offset: usize,
33 /// Used for variables that create multiple locals on the stack when allocated
34 /// such as structs and optionals.
35 multi_value: struct {
36 /// The index of the first local variable
37 index: u32,
38 /// The count of local variables this `WValue` consists of.
39 /// i.e. an ErrorUnion has a 'count' of 2.
40 count: u32,
31 /// Used for types that contains of multiple areas within
32 /// a memory region in the stack.
33 /// The local represents the position in the stack,
34 /// whereas the offset represents the offset from that position.
35 local_with_offset: struct {
36 /// Index of the local variable
37 local: u32,
38 /// The offset from the local's stack position
39 offset: u32,
4140 },
4241};
4342
......@@ -187,7 +186,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
187186 },
188187 32 => switch (args.valtype1.?) {
189188 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
190 .i32, .f32, .f64 => unreachable,
189 .i32 => return .i32_load,
190 .f32, .f64 => unreachable,
191191 },
192192 else => unreachable,
193193 } else switch (args.valtype1.?) {
......@@ -668,9 +668,10 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
668668 .Bool,
669669 .Pointer,
670670 .ErrorSet,
671 .Struct,
672 .ErrorUnion,
671673 => wasm.Valtype.i32,
672 .Struct, .ErrorUnion, .Optional => unreachable, // Multi typed, must be handled individually.
673 else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}),
674 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),
674675 };
675676}
676677
......@@ -692,76 +693,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 {
692693/// Writes the bytecode depending on the given `WValue` in `val`
693694fn emitWValue(self: *Self, val: WValue) InnerError!void {
694695 switch (val) {
695 .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually
696 .none, .mir_offset => {}, // no-op
696 .none => {}, // no-op
697 .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local),
697698 .local => |idx| try self.addLabel(.local_get, idx),
698699 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack
699700 }
700701}
701702
702/// Creates one or multiple locals for a given `Type`.
703/// Returns a corresponding `Wvalue` that can either be of tag
704/// local or multi_value
703/// Creates one locals for a given `Type`.
704/// Returns a corresponding `Wvalue` with `local` as active tag
705705fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
706706 const initial_index = self.local_index;
707 switch (ty.zigTypeTag()) {
708 .Struct => {
709 // for each struct field, generate a local
710 const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data;
711 const fields_len = @intCast(u32, struct_data.fields.count());
712 try self.locals.ensureUnusedCapacity(self.gpa, fields_len);
713 for (struct_data.fields.values()) |*value| {
714 const val_type = try self.genValtype(value.ty);
715 self.locals.appendAssumeCapacity(val_type);
716 self.local_index += 1;
717 }
718 return WValue{ .multi_value = .{
719 .index = initial_index,
720 .count = fields_len,
721 } };
722 },
723 .ErrorUnion => {
724 const payload_type = ty.errorUnionPayload();
725 const val_type = try self.genValtype(payload_type);
726
727 // we emit the error value as the first local, and the payload as the following.
728 // The first local is also used to find the index of the error and payload.
729 //
730 // TODO: Add support where the payload is a type that contains multiple locals such as a struct.
731 try self.locals.ensureUnusedCapacity(self.gpa, 2);
732 self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32
733 self.locals.appendAssumeCapacity(val_type);
734 self.local_index += 2;
735
736 return WValue{ .multi_value = .{
737 .index = initial_index,
738 .count = 2,
739 } };
740 },
741 .Optional => {
742 var opt_buf: Type.Payload.ElemType = undefined;
743 const child_type = ty.optionalChild(&opt_buf);
744 if (ty.isPtrLikeOptional()) {
745 return self.fail("TODO: wasm optional pointer", .{});
746 }
747
748 try self.locals.ensureUnusedCapacity(self.gpa, 2);
749 self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32
750 self.locals.appendAssumeCapacity(try self.genValtype(child_type));
751 self.local_index += 2;
752
753 return WValue{ .multi_value = .{
754 .index = initial_index,
755 .count = 2,
756 } };
757 },
758 else => {
759 const valtype = try self.genValtype(ty);
760 try self.locals.append(self.gpa, valtype);
761 self.local_index += 1;
762 return WValue{ .local = initial_index };
763 },
764 }
707 const valtype = try self.genValtype(ty);
708 try self.locals.append(self.gpa, valtype);
709 self.local_index += 1;
710 return WValue{ .local = initial_index };
765711}
766712
767713fn genFunctype(self: *Self) InnerError!void {
......@@ -857,7 +803,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {
857803 if (ty.sentinel()) |sentinel| {
858804 try self.code.appendSlice(payload.data);
859805
860 switch (try self.gen(ty.elemType(), sentinel)) {
806 switch (try self.gen(ty.childType(), sentinel)) {
861807 .appended => return Result.appended,
862808 .externally_managed => |data| {
863809 try self.code.appendSlice(data);
......@@ -927,15 +873,8 @@ fn restoreStackPointer(self: *Self) !void {
927873/// the result back into the stack pointer.
928874fn moveStack(self: *Self, offset: u32, local: u32) !void {
929875 if (offset == 0) return;
930 // Generates the following code:
931 //
932 // global.get 0
933 // i32.const [offset]
934 // i32.sub
935 // global.set 0
936
937876 // TODO: Rather than hardcode the stack pointer to position 0,
938 // have the linker resolve it.
877 // have the linker resolve its relocation
939878 try self.addLabel(.global_get, 0);
940879 try self.addImm32(@bitCast(i32, offset));
941880 try self.addTag(.i32_sub);
......@@ -1053,17 +992,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1053992}
1054993
1055994fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1056 const elem_type = self.air.typeOfIndex(inst).elemType();
995 const child_type = self.air.typeOfIndex(inst).childType();
1057996
1058997 // Initialize the stack
1059998 if (self.initial_stack_value == .none) {
1060999 try self.initializeStack();
10611000 }
10621001
1063 const abi_size = elem_type.abiSize(self.target);
1002 const abi_size = child_type.abiSize(self.target);
10641003 if (abi_size == 0) return WValue{ .none = {} };
10651004
1066 const local = try self.allocLocal(elem_type);
1005 // local, containing the offset to the stack position
1006 const local = try self.allocLocal(child_type);
10671007 try self.moveStack(@intCast(u32, abi_size), local.local);
10681008
10691009 return local;
......@@ -1074,43 +1014,100 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10741014
10751015 const lhs = self.resolveInst(bin_op.lhs);
10761016 const rhs = self.resolveInst(bin_op.rhs);
1017 const ty = self.air.typeOf(bin_op.lhs).childType();
10771018
1078 // get lhs stack position
1019 const offset: u32 = switch (lhs) {
1020 .local_with_offset => |with_off| with_off.offset,
1021 else => 0,
1022 };
1023
1024 switch (ty.zigTypeTag()) {
1025 .ErrorUnion, .Optional => {
1026 var buf: Type.Payload.ElemType = undefined;
1027 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);
1028 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.i8);
1029 const payload_offset = @intCast(u32, tag_ty.abiSize(self.target) / 8);
1030 if (rhs == .constant) {
1031 // constant will contain both tag and payload,
1032 // so save those in 2 temporary locals before storing them
1033 // in memory
1034 try self.emitWValue(rhs);
1035 const tag_local = try self.allocLocal(Type.initTag(.i32));
1036 const payload_local = try self.allocLocal(payload_ty);
1037
1038 try self.addLabel(.local_set, payload_local.local);
1039 try self.addLabel(.local_set, tag_local.local);
1040
1041 try self.store(lhs, tag_local, tag_ty, 0);
1042 try self.store(lhs, payload_local, payload_ty, payload_offset);
1043 } else if (offset == 0) {
1044 // tag is being set
1045 try self.store(lhs, rhs, tag_ty, 0);
1046 } else {
1047 // payload is being set
1048 try self.store(lhs, rhs, payload_ty, payload_offset);
1049 }
1050 },
1051 else => try self.store(lhs, rhs, ty, offset),
1052 }
1053
1054 return .none;
1055}
1056
1057fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
10791058 try self.emitWValue(lhs);
1080 // get rhs value
10811059 try self.emitWValue(rhs);
1082
1083 const ty = self.air.typeOf(bin_op.lhs);
10841060 const valtype = try self.typeToValtype(ty);
1085
10861061 const opcode = buildOpcode(.{
10871062 .valtype1 = valtype,
10881063 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size
10891064 .op = .store,
10901065 });
1066
10911067 // store rhs value at stack pointer's location in memory
1092 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 });
1068 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 });
10931069 try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } });
1094
1095 return .none;
10961070}
10971071
10981072fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10991073 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1100 const lhs = self.resolveInst(ty_op.operand);
1074 const operand = self.resolveInst(ty_op.operand);
1075 const ty = self.air.getRefType(ty_op.ty);
1076
1077 return switch (ty.zigTypeTag()) {
1078 .Struct, .ErrorUnion => operand,
1079 else => self.load(operand, ty, 0),
1080 };
1081}
11011082
1083fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
11021084 // load local's value from memory by its stack position
1103 try self.emitWValue(lhs);
1104 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 });
1105 try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = mem_arg_index } });
1106 return .none;
1085 try self.emitWValue(operand);
1086 // Build the opcode with the right bitsize
1087 const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed;
1088 const opcode = buildOpcode(.{
1089 .valtype1 = try self.typeToValtype(ty),
1090 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size
1091 .op = .load,
1092 .signedness = signedness,
1093 });
1094
1095 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 });
1096 try self.addInst(.{
1097 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1098 .data = .{ .payload = mem_arg_index },
1099 });
1100
1101 // store the result in a local
1102 const result = try self.allocLocal(ty);
1103 try self.addLabel(.local_set, result.local);
1104 return result;
11071105}
11081106
11091107fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
11101108 _ = inst;
1111 // arguments share the index with locals
1112 defer self.local_index += 1;
1113 return WValue{ .local = self.local_index };
1109 defer self.arg_index += 1;
1110 return self.args[self.arg_index];
11141111}
11151112
11161113fn 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 {
13881385
13891386 // insert blocks at the position of `offset` so
13901387 // the condition can jump to it
1391 const offset = switch (condition) {
1392 .mir_offset => |offset| offset,
1393 else => blk: {
1394 const offset = self.mir_instructions.len;
1395 try self.emitWValue(condition);
1396 break :blk offset;
1397 },
1398 };
1388 const offset = self.mir_instructions.len;
1389 try self.emitWValue(condition);
13991390
14001391 // result type is always noreturn, so use `block_empty` as type.
14011392 try self.startBlock(.block, wasm.block_empty, offset);
......@@ -1415,10 +1406,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14151406}
14161407
14171408fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
1418 // save offset, so potential conditions can insert blocks in front of
1419 // the comparison that we can later jump back to
1420 const offset = self.mir_instructions.len;
1421
14221409 const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
14231410 const lhs = self.resolveInst(data.bin_op.lhs);
14241411 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
14471434 .signedness = signedness,
14481435 });
14491436 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
1450 return WValue{ .mir_offset = offset };
1437
1438 const cmp_tmp = try self.allocLocal(lhs_ty);
1439 try self.addLabel(.local_set, cmp_tmp.local);
1440 return cmp_tmp;
14511441}
14521442
14531443fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1468,7 +1458,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14681458
14691459fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14701460 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1471 const offset = self.mir_instructions.len;
14721461
14731462 const operand = self.resolveInst(ty_op.operand);
14741463 try self.emitWValue(operand);
......@@ -1478,7 +1467,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14781467 try self.addImm32(0);
14791468 try self.addTag(.i32_eq);
14801469
1481 return WValue{ .mir_offset = offset };
1470 // save the result in the local
1471 const not_tmp = try self.allocLocal(self.air.getRefType(ty_op.ty));
1472 try self.addLabel(.local_set, not_tmp.local);
1473 return not_tmp;
14821474}
14831475
14841476fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1504,24 +1496,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15041496 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
15051497 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
15061498 const struct_ptr = self.resolveInst(extra.data.struct_operand);
1507 return structFieldPtr(struct_ptr, extra.data.field_index);
1499 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
1500 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch {
1501 return self.fail("Field type '{}' too big to fit into stack frame", .{
1502 struct_ty.structFieldType(extra.data.field_index),
1503 });
1504 };
1505 return structFieldPtr(struct_ptr, offset);
15081506}
1507
15091508fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {
15101509 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
15111510 const struct_ptr = self.resolveInst(ty_op.operand);
1512 return structFieldPtr(struct_ptr, index);
1511 const struct_ty = self.air.typeOf(ty_op.operand).childType();
1512 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
1513 return self.fail("Field type '{}' too big to fit into stack frame", .{
1514 struct_ty.structFieldType(index),
1515 });
1516 };
1517 return structFieldPtr(struct_ptr, offset);
15131518}
1514fn structFieldPtr(struct_ptr: WValue, index: u32) InnerError!WValue {
1515 return WValue{ .local = struct_ptr.multi_value.index + index };
1519
1520fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {
1521 return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } };
15161522}
15171523
15181524fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15191525 if (self.liveness.isUnused(inst)) return WValue.none;
15201526
15211527 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1522 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1523 const struct_multivalue = self.resolveInst(extra.struct_operand).multi_value;
1524 return WValue{ .local = struct_multivalue.index + extra.field_index };
1528 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
1529 const struct_ty = self.air.typeOf(struct_field.struct_operand);
1530 const operand = self.resolveInst(struct_field.struct_operand);
1531 const field_index = struct_field.field_index;
1532 const field_ty = struct_ty.structFieldType(field_index);
1533 if (!field_ty.hasCodeGenBits()) return WValue.none;
1534 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {
1535 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
1536 };
1537 return try self.load(operand, field_ty, offset);
15251538}
15261539
15271540fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1675,25 +1688,32 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16751688fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
16761689 const un_op = self.air.instructions.items(.data)[inst].un_op;
16771690 const operand = self.resolveInst(un_op);
1678 const offset = self.mir_instructions.len;
1691 const err_ty = self.air.typeOf(un_op).errorUnionSet();
1692
1693 // load the error tag value
1694 try self.emitWValue(operand);
1695 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 });
1696 try self.addInst(.{
1697 .tag = .i32_load,
1698 .data = .{ .payload = mem_arg_index },
1699 });
16791700
1680 // load the error value which is positioned at multi_value's index
1681 try self.emitWValue(.{ .local = operand.multi_value.index });
16821701 // Compare the error value with '0'
16831702 try self.addImm32(0);
16841703 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
16851704
1686 return WValue{ .mir_offset = offset };
1705 const is_err_tmp = try self.allocLocal(err_ty);
1706 try self.addLabel(.local_set, is_err_tmp.local);
1707 return is_err_tmp;
16871708}
16881709
16891710fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16901711 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
16911712 const operand = self.resolveInst(ty_op.operand);
1692 // The index of multi_value contains the error code. To get the initial index of the payload we get
1693 // the following index. Next, convert it to a `WValue.local`
1694 //
1695 // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct.
1696 return WValue{ .local = operand.multi_value.index + 1 };
1713 const err_ty = self.air.typeOf(ty_op.operand);
1714 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target) / 8);
1715
1716 return self.load(operand, self.air.getRefType(ty_op.ty), offset);
16971717}
16981718
16991719fn 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!
17281748 const un_op = self.air.instructions.items(.data)[inst].un_op;
17291749 const operand = self.resolveInst(un_op);
17301750
1731 // load the null value which is positioned at multi_value's index
1732 try self.emitWValue(.{ .local = operand.multi_value.index });
1751 // load the null value which is positioned at local_with_offset's index
1752 try self.emitWValue(.{ .local = operand.local_with_offset.local });
17331753 try self.addImm32(0);
17341754 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
17351755
......@@ -1743,7 +1763,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!
17431763fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
17441764 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
17451765 const operand = self.resolveInst(ty_op.operand);
1746 return WValue{ .local = operand.multi_value.index + 1 };
1766 return WValue{ .local = operand.local_with_offset.local + 1 };
17471767}
17481768
17491769fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
src/arch/wasm/Emit.zig+22
......@@ -60,8 +60,30 @@ pub fn emitMir(emit: *Emit) InnerError!void {
6060
6161 // memory instructions
6262 .i32_load => try emit.emitMemArg(tag, inst),
63 .i64_load => try emit.emitMemArg(tag, inst),
64 .f32_load => try emit.emitMemArg(tag, inst),
65 .f64_load => try emit.emitMemArg(tag, inst),
66 .i32_load8_s => try emit.emitMemArg(tag, inst),
67 .i32_load8_u => try emit.emitMemArg(tag, inst),
68 .i32_load16_s => try emit.emitMemArg(tag, inst),
69 .i32_load16_u => try emit.emitMemArg(tag, inst),
70 .i64_load8_s => try emit.emitMemArg(tag, inst),
71 .i64_load8_u => try emit.emitMemArg(tag, inst),
72 .i64_load16_s => try emit.emitMemArg(tag, inst),
73 .i64_load16_u => try emit.emitMemArg(tag, inst),
74 .i64_load32_s => try emit.emitMemArg(tag, inst),
75 .i64_load32_u => try emit.emitMemArg(tag, inst),
6376 .i32_store => try emit.emitMemArg(tag, inst),
77 .i64_store => try emit.emitMemArg(tag, inst),
78 .f32_store => try emit.emitMemArg(tag, inst),
79 .f64_store => try emit.emitMemArg(tag, inst),
80 .i32_store8 => try emit.emitMemArg(tag, inst),
81 .i32_store16 => try emit.emitMemArg(tag, inst),
82 .i64_store8 => try emit.emitMemArg(tag, inst),
83 .i64_store16 => try emit.emitMemArg(tag, inst),
84 .i64_store32 => try emit.emitMemArg(tag, inst),
6485
86 // Instructions with an index that do not require relocations
6587 .local_get => try emit.emitLabel(tag, inst),
6688 .local_set => try emit.emitLabel(tag, inst),
6789 .local_tee => try emit.emitLabel(tag, inst),
src/arch/wasm/Mir.zig+115-1
......@@ -97,11 +97,125 @@ pub const Inst = struct {
9797 ///
9898 /// Uses `payload` of type `MemArg`.
9999 i32_load = 0x28,
100 /// Loads a value from memory onto the stack, based on the signedness
101 /// and bitsize of the type.
102 ///
103 /// Uses `payload` with type `MemArg`
104 i64_load = 0x29,
105 /// Loads a value from memory onto the stack, based on the signedness
106 /// and bitsize of the type.
107 ///
108 /// Uses `payload` with type `MemArg`
109 f32_load = 0x2A,
110 /// Loads a value from memory onto the stack, based on the signedness
111 /// and bitsize of the type.
112 ///
113 /// Uses `payload` with type `MemArg`
114 f64_load = 0x2B,
115 /// Loads a value from memory onto the stack, based on the signedness
116 /// and bitsize of the type.
117 ///
118 /// Uses `payload` with type `MemArg`
119 i32_load8_s = 0x2C,
120 /// Loads a value from memory onto the stack, based on the signedness
121 /// and bitsize of the type.
122 ///
123 /// Uses `payload` with type `MemArg`
124 i32_load8_u = 0x2D,
125 /// Loads a value from memory onto the stack, based on the signedness
126 /// and bitsize of the type.
127 ///
128 /// Uses `payload` with type `MemArg`
129 i32_load16_s = 0x2E,
130 /// Loads a value from memory onto the stack, based on the signedness
131 /// and bitsize of the type.
132 ///
133 /// Uses `payload` with type `MemArg`
134 i32_load16_u = 0x2F,
135 /// Loads a value from memory onto the stack, based on the signedness
136 /// and bitsize of the type.
137 ///
138 /// Uses `payload` with type `MemArg`
139 i64_load8_s = 0x30,
140 /// Loads a value from memory onto the stack, based on the signedness
141 /// and bitsize of the type.
142 ///
143 /// Uses `payload` with type `MemArg`
144 i64_load8_u = 0x31,
145 /// Loads a value from memory onto the stack, based on the signedness
146 /// and bitsize of the type.
147 ///
148 /// Uses `payload` with type `MemArg`
149 i64_load16_s = 0x32,
150 /// Loads a value from memory onto the stack, based on the signedness
151 /// and bitsize of the type.
152 ///
153 /// Uses `payload` with type `MemArg`
154 i64_load16_u = 0x33,
155 /// Loads a value from memory onto the stack, based on the signedness
156 /// and bitsize of the type.
157 ///
158 /// Uses `payload` with type `MemArg`
159 i64_load32_s = 0x34,
160 /// Loads a value from memory onto the stack, based on the signedness
161 /// and bitsize of the type.
162 ///
163 /// Uses `payload` with type `MemArg`
164 i64_load32_u = 0x35,
100165 /// Pops 2 values from the stack, where the first value represents the value to write into memory
101166 /// and the second value represents the offset into memory where the value must be written to.
167 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
102168 ///
103169 /// Uses `payload` of type `MemArg`.
104170 i32_store = 0x36,
171 /// Pops 2 values from the stack, where the first value represents the value to write into memory
172 /// and the second value represents the offset into memory where the value must be written to.
173 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
174 ///
175 /// Uses `Payload` with type `MemArg`
176 i64_store = 0x37,
177 /// Pops 2 values from the stack, where the first value represents the value to write into memory
178 /// and the second value represents the offset into memory where the value must be written to.
179 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
180 ///
181 /// Uses `Payload` with type `MemArg`
182 f32_store = 0x38,
183 /// Pops 2 values from the stack, where the first value represents the value to write into memory
184 /// and the second value represents the offset into memory where the value must be written to.
185 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
186 ///
187 /// Uses `Payload` with type `MemArg`
188 f64_store = 0x39,
189 /// Pops 2 values from the stack, where the first value represents the value to write into memory
190 /// and the second value represents the offset into memory where the value must be written to.
191 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
192 ///
193 /// Uses `Payload` with type `MemArg`
194 i32_store8 = 0x3A,
195 /// Pops 2 values from the stack, where the first value represents the value to write into memory
196 /// and the second value represents the offset into memory where the value must be written to.
197 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
198 ///
199 /// Uses `Payload` with type `MemArg`
200 i32_store16 = 0x3B,
201 /// Pops 2 values from the stack, where the first value represents the value to write into memory
202 /// and the second value represents the offset into memory where the value must be written to.
203 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
204 ///
205 /// Uses `Payload` with type `MemArg`
206 i64_store8 = 0x3C,
207 /// Pops 2 values from the stack, where the first value represents the value to write into memory
208 /// and the second value represents the offset into memory where the value must be written to.
209 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
210 ///
211 /// Uses `Payload` with type `MemArg`
212 i64_store16 = 0x3D,
213 /// Pops 2 values from the stack, where the first value represents the value to write into memory
214 /// and the second value represents the offset into memory where the value must be written to.
215 /// This opcode is typed and expects the stack value's type to be equal to this opcode's type.
216 ///
217 /// Uses `Payload` with type `MemArg`
218 i64_store32 = 0x3E,
105219 /// Returns the memory size in amount of pages.
106220 ///
107221 /// Uses `nop`
......@@ -247,7 +361,7 @@ pub const Inst = struct {
247361
248362 /// From a given wasm opcode, returns a MIR tag.
249363 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {
250 return @intToEnum(Tag, @enumToInt(opcode));
364 return @intToEnum(Tag, @enumToInt(opcode)); // Given `Opcode` is not present as a tag for MIR yet
251365 }
252366
253367 /// Returns a wasm opcode from a given MIR tag.