authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-21 22:09:47-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-21 22:09:47-05:00
log722c6b95671fcda0da1561205e487d169c046473
treead01478eafe15b93433213be7c6fddff6e7855e4
parent2991e3f66bd518e864a3f0878f280150c0d645c1
parentdeb8d0765b46f75546f4342ad9078dbe6ad7b9be
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10188 from Luukdegram/stage2-wasm-stack

stage2: wasm - implement the stack

6 files changed, 670 insertions(+), 214 deletions(-)

src/arch/wasm/CodeGen.zig+461-209
...@@ -28,16 +28,15 @@ const WValue = union(enum) {...@@ -28,16 +28,15 @@ const WValue = union(enum) {
28 local: u32,28 local: u32,
29 /// Holds a memoized typed value29 /// Holds a memoized typed value
30 constant: TypedValue,30 constant: TypedValue,
31 /// Offset position in the list of MIR instructions31 /// Used for types that contains of multiple areas within
32 mir_offset: usize,32 /// a memory region in the stack.
33 /// Used for variables that create multiple locals on the stack when allocated33 /// The local represents the position in the stack,
34 /// such as structs and optionals.34 /// whereas the offset represents the offset from that position.
35 multi_value: struct {35 local_with_offset: struct {
36 /// The index of the first local variable36 /// Index of the local variable
37 index: u32,37 local: u32,
38 /// The count of local variables this `WValue` consists of.38 /// The offset from the local's stack position
39 /// i.e. an ErrorUnion has a 'count' of 2.39 offset: u32,
40 count: u32,
41 },40 },
42};41};
4342
...@@ -187,7 +186,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -187,7 +186,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
187 },186 },
188 32 => switch (args.valtype1.?) {187 32 => switch (args.valtype1.?) {
189 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,188 .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 => return .f32_load,
191 .f64 => unreachable,
192 },
193 64 => switch (args.valtype1.?) {
194 .i64 => return .i64_load,
195 .f64 => return .f64_load,
196 else => unreachable,
191 },197 },
192 else => unreachable,198 else => unreachable,
193 } else switch (args.valtype1.?) {199 } else switch (args.valtype1.?) {
...@@ -210,7 +216,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -210,7 +216,14 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
210 },216 },
211 32 => switch (args.valtype1.?) {217 32 => switch (args.valtype1.?) {
212 .i64 => return .i64_store32,218 .i64 => return .i64_store32,
213 .i32, .f32, .f64 => unreachable,219 .i32 => return .i32_store,
220 .f32 => return .f32_store,
221 .f64 => unreachable,
222 },
223 64 => switch (args.valtype1.?) {
224 .i64 => return .i64_store,
225 .f64 => return .f64_store,
226 else => unreachable,
214 },227 },
215 else => unreachable,228 else => unreachable,
216 }229 }
...@@ -499,7 +512,10 @@ gpa: *mem.Allocator,...@@ -499,7 +512,10 @@ gpa: *mem.Allocator,
499/// Table to save `WValue`'s generated by an `Air.Inst`512/// Table to save `WValue`'s generated by an `Air.Inst`
500values: ValueTable,513values: ValueTable,
501/// Mapping from Air.Inst.Index to block ids514/// Mapping from Air.Inst.Index to block ids
502blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},515blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
516 label: u32,
517 value: WValue,
518}) = .{},
503/// `bytes` contains the wasm bytecode belonging to the 'code' section.519/// `bytes` contains the wasm bytecode belonging to the 'code' section.
504code: ArrayList(u8),520code: ArrayList(u8),
505/// Contains the generated function type bytecode for the current function521/// Contains the generated function type bytecode for the current function
...@@ -509,6 +525,9 @@ func_type_data: ArrayList(u8),...@@ -509,6 +525,9 @@ func_type_data: ArrayList(u8),
509/// NOTE: arguments share the index with locals therefore the first variable525/// NOTE: arguments share the index with locals therefore the first variable
510/// will have the index that comes after the last argument's index526/// will have the index that comes after the last argument's index
511local_index: u32 = 0,527local_index: u32 = 0,
528/// The index of the current argument.
529/// Used to track which argument is being referenced in `airArg`.
530arg_index: u32 = 0,
512/// If codegen fails, an error messages will be allocated and saved in `err_msg`531/// If codegen fails, an error messages will be allocated and saved in `err_msg`
513err_msg: *Module.ErrorMsg,532err_msg: *Module.ErrorMsg,
514/// Current block depth. Used to calculate the relative difference between a break533/// Current block depth. Used to calculate the relative difference between a break
...@@ -529,6 +548,16 @@ global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt),...@@ -529,6 +548,16 @@ global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt),
529mir_instructions: std.MultiArrayList(Mir.Inst) = .{},548mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
530/// Contains extra data for MIR549/// Contains extra data for MIR
531mir_extra: std.ArrayListUnmanaged(u32) = .{},550mir_extra: std.ArrayListUnmanaged(u32) = .{},
551/// When a function is executing, we store the the current stack pointer's value within this local.
552/// This value is then used to restore the stack pointer to the original value at the return of the function.
553initial_stack_value: WValue = .none,
554/// Arguments of this function declaration
555/// This will be set after `resolveCallingConventionValues`
556args: []WValue = undefined,
557/// This will only be `.none` if the function returns void, or returns an immediate.
558/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
559/// before this function returns its execution to the caller.
560return_value: WValue = .none,
532561
533const InnerError = error{562const InnerError = error{
534 OutOfMemory,563 OutOfMemory,
...@@ -662,9 +691,11 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {...@@ -662,9 +691,11 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
662 .Bool,691 .Bool,
663 .Pointer,692 .Pointer,
664 .ErrorSet,693 .ErrorSet,
694 .Struct,
695 .ErrorUnion,
696 .Optional,
665 => wasm.Valtype.i32,697 => wasm.Valtype.i32,
666 .Struct, .ErrorUnion, .Optional => unreachable, // Multi typed, must be handled individually.698 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),
667 else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}),
668 };699 };
669}700}
670701
...@@ -686,78 +717,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 {...@@ -686,78 +717,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 {
686/// Writes the bytecode depending on the given `WValue` in `val`717/// Writes the bytecode depending on the given `WValue` in `val`
687fn emitWValue(self: *Self, val: WValue) InnerError!void {718fn emitWValue(self: *Self, val: WValue) InnerError!void {
688 switch (val) {719 switch (val) {
689 .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually720 .none => {}, // no-op
690 .none, .mir_offset => {}, // no-op721 .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local),
691 .local => |idx| {722 .local => |idx| try self.addLabel(.local_get, idx),
692 try self.addLabel(.local_get, idx);
693 },
694 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack723 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack
695 }724 }
696}725}
697726
698/// Creates one or multiple locals for a given `Type`.727/// Creates one locals for a given `Type`.
699/// Returns a corresponding `Wvalue` that can either be of tag728/// Returns a corresponding `Wvalue` with `local` as active tag
700/// local or multi_value
701fn allocLocal(self: *Self, ty: Type) InnerError!WValue {729fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
702 const initial_index = self.local_index;730 const initial_index = self.local_index;
703 switch (ty.zigTypeTag()) {731 const valtype = try self.genValtype(ty);
704 .Struct => {732 try self.locals.append(self.gpa, valtype);
705 // for each struct field, generate a local733 self.local_index += 1;
706 const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data;734 return WValue{ .local = initial_index };
707 const fields_len = @intCast(u32, struct_data.fields.count());
708 try self.locals.ensureUnusedCapacity(self.gpa, fields_len);
709 for (struct_data.fields.values()) |*value| {
710 const val_type = try self.genValtype(value.ty);
711 self.locals.appendAssumeCapacity(val_type);
712 self.local_index += 1;
713 }
714 return WValue{ .multi_value = .{
715 .index = initial_index,
716 .count = fields_len,
717 } };
718 },
719 .ErrorUnion => {
720 const payload_type = ty.errorUnionPayload();
721 const val_type = try self.genValtype(payload_type);
722
723 // we emit the error value as the first local, and the payload as the following.
724 // The first local is also used to find the index of the error and payload.
725 //
726 // TODO: Add support where the payload is a type that contains multiple locals such as a struct.
727 try self.locals.ensureUnusedCapacity(self.gpa, 2);
728 self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32
729 self.locals.appendAssumeCapacity(val_type);
730 self.local_index += 2;
731
732 return WValue{ .multi_value = .{
733 .index = initial_index,
734 .count = 2,
735 } };
736 },
737 .Optional => {
738 var opt_buf: Type.Payload.ElemType = undefined;
739 const child_type = ty.optionalChild(&opt_buf);
740 if (ty.isPtrLikeOptional()) {
741 return self.fail("TODO: wasm optional pointer", .{});
742 }
743
744 try self.locals.ensureUnusedCapacity(self.gpa, 2);
745 self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32
746 self.locals.appendAssumeCapacity(try self.genValtype(child_type));
747 self.local_index += 2;
748
749 return WValue{ .multi_value = .{
750 .index = initial_index,
751 .count = 2,
752 } };
753 },
754 else => {
755 const valtype = try self.genValtype(ty);
756 try self.locals.append(self.gpa, valtype);
757 self.local_index += 1;
758 return WValue{ .local = initial_index };
759 },
760 }
761}735}
762736
763fn genFunctype(self: *Self) InnerError!void {737fn genFunctype(self: *Self) InnerError!void {
...@@ -786,17 +760,8 @@ fn genFunctype(self: *Self) InnerError!void {...@@ -786,17 +760,8 @@ fn genFunctype(self: *Self) InnerError!void {
786 .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)),760 .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)),
787 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),761 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
788 .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),762 .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
789 .ErrorUnion => {
790 const val_type = try self.genValtype(return_type.errorUnionPayload());
791
792 // write down the amount of return values
793 try leb.writeULEB128(writer, @as(u32, 2));
794 try writer.writeByte(wasm.valtype(.i32)); // error code is always an i32 integer.
795 try writer.writeByte(val_type);
796 },
797 else => {763 else => {
798 try leb.writeULEB128(writer, @as(u32, 1));764 try leb.writeULEB128(writer, @as(u32, 1));
799 // Can we maybe get the source index of the return type?
800 const val_type = try self.genValtype(return_type);765 const val_type = try self.genValtype(return_type);
801 try writer.writeByte(val_type);766 try writer.writeByte(val_type);
802 },767 },
...@@ -807,6 +772,12 @@ pub fn genFunc(self: *Self) InnerError!Result {...@@ -807,6 +772,12 @@ pub fn genFunc(self: *Self) InnerError!Result {
807 try self.genFunctype();772 try self.genFunctype();
808 // TODO: check for and handle death of instructions773 // TODO: check for and handle death of instructions
809774
775 var cc_result = try self.resolveCallingConventionValues(self.decl.ty);
776 defer cc_result.deinit(self.gpa);
777
778 self.args = cc_result.args;
779 self.return_value = cc_result.return_value;
780
810 // Generate MIR for function body781 // Generate MIR for function body
811 try self.genBody(self.air.getMainBody());782 try self.genBody(self.air.getMainBody());
812 // End of function body783 // End of function body
...@@ -853,7 +824,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {...@@ -853,7 +824,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {
853 if (ty.sentinel()) |sentinel| {824 if (ty.sentinel()) |sentinel| {
854 try self.code.appendSlice(payload.data);825 try self.code.appendSlice(payload.data);
855826
856 switch (try self.gen(ty.elemType(), sentinel)) {827 switch (try self.gen(ty.childType(), sentinel)) {
857 .appended => return Result.appended,828 .appended => return Result.appended,
858 .externally_managed => |data| {829 .externally_managed => |data| {
859 try self.code.appendSlice(data);830 try self.code.appendSlice(data);
...@@ -887,6 +858,110 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {...@@ -887,6 +858,110 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {
887 }858 }
888}859}
889860
861const CallWValues = struct {
862 args: []WValue,
863 return_value: WValue,
864
865 fn deinit(self: *CallWValues, gpa: *Allocator) void {
866 gpa.free(self.args);
867 self.* = undefined;
868 }
869};
870
871fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValues {
872 const cc = fn_ty.fnCallingConvention();
873 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
874 defer self.gpa.free(param_types);
875 fn_ty.fnParamTypes(param_types);
876 var result: CallWValues = .{
877 .args = try self.gpa.alloc(WValue, param_types.len),
878 .return_value = .none,
879 };
880 errdefer self.gpa.free(result.args);
881 switch (cc) {
882 .Naked => return result,
883 .Unspecified, .C => {
884 for (param_types) |ty, ty_index| {
885 if (!ty.hasCodeGenBits()) {
886 result.args[ty_index] = .{ .none = {} };
887 continue;
888 }
889
890 result.args[ty_index] = .{ .local = self.local_index };
891 self.local_index += 1;
892 }
893
894 const ret_ty = fn_ty.fnReturnType();
895 switch (ret_ty.zigTypeTag()) {
896 .ErrorUnion, .Optional => result.return_value = try self.allocLocal(Type.initTag(.i32)),
897 .Int, .Float, .Bool, .Void, .NoReturn => {},
898 else => return self.fail("TODO: Implement function return type {}", .{ret_ty}),
899 }
900
901 // Check if we store the result as a pointer to the stack rather than
902 // by value
903 if (result.return_value != .none) {
904 if (self.initial_stack_value == .none) try self.initializeStack();
905 const offset = std.math.cast(u32, ret_ty.abiSize(self.target)) catch {
906 return self.fail("Return type '{}' too big for stack frame", .{ret_ty});
907 };
908
909 try self.moveStack(offset, result.return_value.local);
910 }
911 },
912 else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}),
913 }
914 return result;
915}
916
917/// Retrieves the stack pointer's value from the global variable and stores
918/// it in a local
919/// Asserts `initial_stack_value` is `.none`
920fn initializeStack(self: *Self) !void {
921 assert(self.initial_stack_value == .none);
922 // reserve space for immediate value
923 // get stack pointer global
924 // TODO: For now, we hardcode the stack pointer to index '0',
925 // once the linker is further implemented, we can replace this by inserting
926 // a relocation and have the linker resolve the correct index to the stack pointer global.
927 // NOTE: relocations of the type GLOBAL_INDEX_LEB are 5-bytes big
928 try self.addLabel(.global_get, 0);
929
930 // Reserve a local to store the current stack pointer
931 // We can later use this local to set the stack pointer back to the value
932 // we have stored here.
933 self.initial_stack_value = try self.allocLocal(Type.initTag(.i32));
934
935 // save the value to the local
936 try self.addLabel(.local_set, self.initial_stack_value.local);
937}
938
939/// Reads the stack pointer from `Context.initial_stack_value` and writes it
940/// to the global stack pointer variable
941fn restoreStackPointer(self: *Self) !void {
942 // only restore the pointer if it was initialized
943 if (self.initial_stack_value == .none) return;
944 // Get the original stack pointer's value
945 try self.emitWValue(self.initial_stack_value);
946
947 // save its value in the global stack pointer
948 try self.addLabel(.global_set, 0);
949}
950
951/// Moves the stack pointer by given `offset`
952/// It does this by retrieving the stack pointer, subtracting `offset` and storing
953/// the result back into the stack pointer.
954fn moveStack(self: *Self, offset: u32, local: u32) !void {
955 if (offset == 0) return;
956 // TODO: Rather than hardcode the stack pointer to position 0,
957 // have the linker resolve its relocation
958 try self.addLabel(.global_get, 0);
959 try self.addImm32(@bitCast(i32, offset));
960 try self.addTag(.i32_sub);
961 try self.addLabel(.local_tee, local);
962 try self.addLabel(.global_set, 0);
963}
964
890fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {965fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
891 const air_tags = self.air.instructions.items(.tag);966 const air_tags = self.air.instructions.items(.tag);
892 return switch (air_tags[inst]) {967 return switch (air_tags[inst]) {
...@@ -965,7 +1040,15 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -965,7 +1040,15 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
965fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1040fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
966 const un_op = self.air.instructions.items(.data)[inst].un_op;1041 const un_op = self.air.instructions.items(.data)[inst].un_op;
967 const operand = self.resolveInst(un_op);1042 const operand = self.resolveInst(un_op);
968 try self.emitWValue(operand);1043 // result must be stored in the stack and we return a pointer
1044 // to the stack instead
1045 if (self.return_value != .none) {
1046 try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0);
1047 try self.emitWValue(self.return_value);
1048 } else {
1049 try self.emitWValue(operand);
1050 }
1051 try self.restoreStackPointer();
969 try self.addTag(.@"return");1052 try self.addTag(.@"return");
970 return .none;1053 return .none;
971}1054}
...@@ -993,12 +1076,33 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -993,12 +1076,33 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
9931076
994 try self.addLabel(.call, target.link.wasm.symbol_index);1077 try self.addLabel(.call, target.link.wasm.symbol_index);
9951078
996 return .none;1079 const ret_ty = target.ty.fnReturnType();
1080 switch (ret_ty.zigTypeTag()) {
1081 .Void, .NoReturn => return WValue.none,
1082 else => {
1083 const result_local = try self.allocLocal(ret_ty);
1084 try self.addLabel(.local_set, result_local.local);
1085 return result_local;
1086 },
1087 }
997}1088}
9981089
999fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1090fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1000 const elem_type = self.air.typeOfIndex(inst).elemType();1091 const child_type = self.air.typeOfIndex(inst).childType();
1001 return self.allocLocal(elem_type);1092
1093 // Initialize the stack
1094 if (self.initial_stack_value == .none) {
1095 try self.initializeStack();
1096 }
1097
1098 const abi_size = child_type.abiSize(self.target);
1099 if (abi_size == 0) return WValue{ .none = {} };
1100
1101 // local, containing the offset to the stack position
1102 const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32
1103 try self.moveStack(@intCast(u32, abi_size), local.local);
1104
1105 return local;
1002}1106}
10031107
1004fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1108fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1006,56 +1110,149 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1006,56 +1110,149 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10061110
1007 const lhs = self.resolveInst(bin_op.lhs);1111 const lhs = self.resolveInst(bin_op.lhs);
1008 const rhs = self.resolveInst(bin_op.rhs);1112 const rhs = self.resolveInst(bin_op.rhs);
1113 const ty = self.air.typeOf(bin_op.lhs).childType();
10091114
1010 switch (lhs) {1115 const offset: u32 = switch (lhs) {
1011 .multi_value => |multi_value| switch (rhs) {1116 .local_with_offset => |with_off| with_off.offset,
1012 // When assigning a value to a multi_value such as a struct,1117 else => 0,
1013 // we simply assign the local_index to the rhs one.1118 };
1014 // This allows us to update struct fields without having to individually1119
1015 // set each local as each field's index will be calculated off the struct's base index1120 try self.store(lhs, rhs, ty, offset);
1016 .multi_value => self.values.put(self.gpa, Air.refToIndex(bin_op.lhs).?, rhs) catch unreachable, // Instruction does not dominate all uses!1121 return .none;
1017 .constant, .none => {1122}
1018 // emit all values onto the stack if constant1123
1019 try self.emitWValue(rhs);1124fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
10201125 switch (ty.zigTypeTag()) {
1021 // for each local, pop the stack value into the local1126 .ErrorUnion, .Optional => {
1022 // As the last element is on top of the stack, we must populate the locals1127 var buf: Type.Payload.ElemType = undefined;
1023 // in reverse.1128 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);
1024 var i: u32 = multi_value.count;1129 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8);
1025 while (i > 0) : (i -= 1) {1130 const payload_offset = if (ty.zigTypeTag() == .ErrorUnion)
1026 try self.addLabel(.local_set, multi_value.index + i - 1);1131 @intCast(u32, tag_ty.abiSize(self.target))
1027 }1132 else
1028 },1133 @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target));
1029 .local => {1134
1030 // This can occur when we wrap a single value into a multi-value,1135 switch (rhs) {
1031 // such as wrapping a non-optional value into an optional.1136 .constant => {
1032 // This means we must zero the null-tag, and set the payload.1137 // constant will contain both tag and payload,
1033 assert(multi_value.count == 2);1138 // so save those in 2 temporary locals before storing them
1034 // set payload1139 // in memory
1035 try self.emitWValue(rhs);1140 try self.emitWValue(rhs);
1036 try self.addLabel(.local_set, multi_value.index + 1);1141 const tag_local = try self.allocLocal(tag_ty);
1037 },1142 const payload_local = try self.allocLocal(payload_ty);
1038 else => unreachable,1143
1144 try self.addLabel(.local_set, payload_local.local);
1145 try self.addLabel(.local_set, tag_local.local);
1146
1147 try self.store(lhs, tag_local, tag_ty, 0);
1148 return try self.store(lhs, payload_local, payload_ty, payload_offset);
1149 },
1150 .local => {
1151 // Load values from `rhs` stack position and store in `lhs` instead
1152 const tag_local = try self.load(rhs, tag_ty, 0);
1153 const payload_local = try self.load(rhs, payload_ty, payload_offset);
1154
1155 try self.store(lhs, tag_local, tag_ty, 0);
1156 return try self.store(lhs, payload_local, payload_ty, payload_offset);
1157 },
1158 .local_with_offset => |with_offset| {
1159 const tag_local = try self.allocLocal(tag_ty);
1160 try self.addImm32(0);
1161 try self.store(lhs, tag_local, tag_ty, 0);
1162
1163 return try self.store(
1164 lhs,
1165 .{ .local = with_offset.local },
1166 payload_ty,
1167 with_offset.offset,
1168 );
1169 },
1170 else => unreachable,
1171 }
1039 },1172 },
1040 .local => |local| {1173 .Struct => {
1041 try self.emitWValue(rhs);1174 // we are copying a struct with its fields.
1042 try self.addLabel(.local_set, local);1175 // Replace this with a wasm memcpy instruction once we support that feature.
1176 const fields_len = ty.structFieldCount();
1177 var index: usize = 0;
1178 while (index < fields_len) : (index += 1) {
1179 const field_ty = ty.structFieldType(index);
1180 if (!field_ty.hasCodeGenBits()) continue;
1181 const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch {
1182 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
1183 };
1184 const field_local = try self.load(rhs, field_ty, field_offset);
1185 try self.store(lhs, field_local, field_ty, field_offset);
1186 }
1187 return;
1043 },1188 },
1044 else => unreachable,1189 else => {},
1045 }1190 }
1046 return .none;1191 try self.emitWValue(lhs);
1192 try self.emitWValue(rhs);
1193 const valtype = try self.typeToValtype(ty);
1194 const opcode = buildOpcode(.{
1195 .valtype1 = valtype,
1196 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size
1197 .op = .store,
1198 });
1199
1200 // store rhs value at stack pointer's location in memory
1201 const mem_arg_index = try self.addExtra(Mir.MemArg{
1202 .offset = offset,
1203 .alignment = ty.abiAlignment(self.target),
1204 });
1205 try self.addInst(.{
1206 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1207 .data = .{ .payload = mem_arg_index },
1208 });
1047}1209}
10481210
1049fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1211fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1050 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1212 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1051 return self.resolveInst(ty_op.operand);1213 const operand = self.resolveInst(ty_op.operand);
1214 const ty = self.air.getRefType(ty_op.ty);
1215
1216 return switch (ty.zigTypeTag()) {
1217 .Struct, .ErrorUnion, .Optional => operand, // pass as pointer
1218 else => switch (operand) {
1219 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),
1220 else => try self.load(operand, ty, 0),
1221 },
1222 };
1223}
1224
1225fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1226 // load local's value from memory by its stack position
1227 try self.emitWValue(operand);
1228 // Build the opcode with the right bitsize
1229 const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed;
1230 const opcode = buildOpcode(.{
1231 .valtype1 = try self.typeToValtype(ty),
1232 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size
1233 .op = .load,
1234 .signedness = signedness,
1235 });
1236
1237 const mem_arg_index = try self.addExtra(Mir.MemArg{
1238 .offset = offset,
1239 .alignment = ty.abiAlignment(self.target),
1240 });
1241 try self.addInst(.{
1242 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1243 .data = .{ .payload = mem_arg_index },
1244 });
1245
1246 // store the result in a local
1247 const result = try self.allocLocal(ty);
1248 try self.addLabel(.local_set, result.local);
1249 return result;
1052}1250}
10531251
1054fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1252fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1055 _ = inst;1253 _ = inst;
1056 // arguments share the index with locals1254 defer self.arg_index += 1;
1057 defer self.local_index += 1;1255 return self.args[self.arg_index];
1058 return WValue{ .local = self.local_index };
1059}1256}
10601257
1061fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {1258fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
...@@ -1063,14 +1260,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1063,14 +1260,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1063 const lhs = self.resolveInst(bin_op.lhs);1260 const lhs = self.resolveInst(bin_op.lhs);
1064 const rhs = self.resolveInst(bin_op.rhs);1261 const rhs = self.resolveInst(bin_op.rhs);
10651262
1066 // it's possible for both lhs and/or rhs to return an offset as well,
1067 // in which case we return the first offset occurrence we find.
1068 const offset = blk: {
1069 if (lhs == .mir_offset) break :blk lhs.mir_offset;
1070 if (rhs == .mir_offset) break :blk rhs.mir_offset;
1071 break :blk self.mir_instructions.len;
1072 };
1073
1074 try self.emitWValue(lhs);1263 try self.emitWValue(lhs);
1075 try self.emitWValue(rhs);1264 try self.emitWValue(rhs);
10761265
...@@ -1081,7 +1270,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1081,7 +1270,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1081 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,1270 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
1082 });1271 });
1083 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1272 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
1084 return WValue{ .mir_offset = offset };1273
1274 // save the result in a temporary
1275 const bin_local = try self.allocLocal(bin_ty);
1276 try self.addLabel(.local_set, bin_local.local);
1277 return bin_local;
1085}1278}
10861279
1087fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {1280fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
...@@ -1089,14 +1282,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1089,14 +1282,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1089 const lhs = self.resolveInst(bin_op.lhs);1282 const lhs = self.resolveInst(bin_op.lhs);
1090 const rhs = self.resolveInst(bin_op.rhs);1283 const rhs = self.resolveInst(bin_op.rhs);
10911284
1092 // it's possible for both lhs and/or rhs to return an offset as well,
1093 // in which case we return the first offset occurrence we find.
1094 const offset = blk: {
1095 if (lhs == .mir_offset) break :blk lhs.mir_offset;
1096 if (rhs == .mir_offset) break :blk rhs.mir_offset;
1097 break :blk self.mir_instructions.len;
1098 };
1099
1100 try self.emitWValue(lhs);1285 try self.emitWValue(lhs);
1101 try self.emitWValue(rhs);1286 try self.emitWValue(rhs);
11021287
...@@ -1135,7 +1320,10 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {...@@ -1135,7 +1320,10 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1135 return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{});1320 return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{});
1136 }1321 }
11371322
1138 return WValue{ .mir_offset = offset };1323 // save the result in a temporary
1324 const bin_local = try self.allocLocal(bin_ty);
1325 try self.addLabel(.local_set, bin_local.local);
1326 return bin_local;
1139}1327}
11401328
1141fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {1329fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
...@@ -1176,7 +1364,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1176,7 +1364,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
11761364
1177 // memory instruction followed by their memarg immediate1365 // memory instruction followed by their memarg immediate
1178 // memarg ::== x:u32, y:u32 => {align x, offset y}1366 // memarg ::== x:u32, y:u32 => {align x, offset y}
1179 const extra_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 });1367 const extra_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 4 });
1180 try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });1368 try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });
1181 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});1369 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});
1182 },1370 },
...@@ -1289,21 +1477,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1289,21 +1477,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1289 const extra = self.air.extraData(Air.Block, ty_pl.payload);1477 const extra = self.air.extraData(Air.Block, ty_pl.payload);
1290 const body = self.air.extra[extra.end..][0..extra.data.body_len];1478 const body = self.air.extra[extra.end..][0..extra.data.body_len];
12911479
1292 try self.startBlock(.block, block_ty, null);1480 // if block_ty is non-empty, we create a register to store the temporary value
1481 const block_result: WValue = if (block_ty != wasm.block_empty)
1482 try self.allocLocal(self.air.getRefType(ty_pl.ty))
1483 else
1484 WValue.none;
1485
1486 try self.startBlock(.block, wasm.block_empty);
1293 // Here we set the current block idx, so breaks know the depth to jump1487 // Here we set the current block idx, so breaks know the depth to jump
1294 // to when breaking out.1488 // to when breaking out.
1295 try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);1489 try self.blocks.putNoClobber(self.gpa, inst, .{
1490 .label = self.block_depth,
1491 .value = block_result,
1492 });
1296 try self.genBody(body);1493 try self.genBody(body);
1297 try self.endBlock();1494 try self.endBlock();
12981495
1299 return .none;1496 return block_result;
1300}1497}
13011498
1302/// appends a new wasm block to the code section and increases the `block_depth` by 11499/// appends a new wasm block to the code section and increases the `block_depth` by 1
1303fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8, with_offset: ?usize) !void {1500fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8) !void {
1304 self.block_depth += 1;1501 self.block_depth += 1;
1305 const offset = with_offset orelse self.mir_instructions.len;1502 try self.addInst(.{
1306 try self.addInstAt(offset, .{
1307 .tag = Mir.Inst.Tag.fromOpcode(block_tag),1503 .tag = Mir.Inst.Tag.fromOpcode(block_tag),
1308 .data = .{ .block_type = valtype },1504 .data = .{ .block_type = valtype },
1309 });1505 });
...@@ -1322,7 +1518,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1322,7 +1518,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
13221518
1323 // result type of loop is always 'noreturn', meaning we can always1519 // result type of loop is always 'noreturn', meaning we can always
1324 // emit the wasm type 'block_empty'.1520 // emit the wasm type 'block_empty'.
1325 try self.startBlock(.loop, wasm.block_empty, null);1521 try self.startBlock(.loop, wasm.block_empty);
1326 try self.genBody(body);1522 try self.genBody(body);
13271523
1328 // breaking to the index of a loop block will continue the loop instead1524 // breaking to the index of a loop block will continue the loop instead
...@@ -1340,19 +1536,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1340,19 +1536,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1340 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];1536 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1341 // TODO: Handle death instructions for then and else body1537 // TODO: Handle death instructions for then and else body
13421538
1343 // insert blocks at the position of `offset` so
1344 // the condition can jump to it
1345 const offset = switch (condition) {
1346 .mir_offset => |offset| offset,
1347 else => blk: {
1348 const offset = self.mir_instructions.len;
1349 try self.emitWValue(condition);
1350 break :blk offset;
1351 },
1352 };
1353
1354 // result type is always noreturn, so use `block_empty` as type.1539 // result type is always noreturn, so use `block_empty` as type.
1355 try self.startBlock(.block, wasm.block_empty, offset);1540 try self.startBlock(.block, wasm.block_empty);
1541 // emit the conditional value
1542 try self.emitWValue(condition);
13561543
1357 // we inserted the block in front of the condition1544 // we inserted the block in front of the condition
1358 // so now check if condition matches. If not, break outside this block1545 // so now check if condition matches. If not, break outside this block
...@@ -1369,10 +1556,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1369,10 +1556,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1369}1556}
13701557
1371fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {1558fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
1372 // save offset, so potential conditions can insert blocks in front of
1373 // the comparison that we can later jump back to
1374 const offset = self.mir_instructions.len;
1375
1376 const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];1559 const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
1377 const lhs = self.resolveInst(data.bin_op.lhs);1560 const lhs = self.resolveInst(data.bin_op.lhs);
1378 const rhs = self.resolveInst(data.bin_op.rhs);1561 const rhs = self.resolveInst(data.bin_op.rhs);
...@@ -1401,20 +1584,28 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner...@@ -1401,20 +1584,28 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
1401 .signedness = signedness,1584 .signedness = signedness,
1402 });1585 });
1403 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1586 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
1404 return WValue{ .mir_offset = offset };1587
1588 const cmp_tmp = try self.allocLocal(lhs_ty);
1589 try self.addLabel(.local_set, cmp_tmp.local);
1590 return cmp_tmp;
1405}1591}
14061592
1407fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1593fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1408 const br = self.air.instructions.items(.data)[inst].br;1594 const br = self.air.instructions.items(.data)[inst].br;
1595 const block = self.blocks.get(br.block_inst).?;
14091596
1410 // if operand has codegen bits we should break with a value1597 // if operand has codegen bits we should break with a value
1411 if (self.air.typeOf(br.operand).hasCodeGenBits()) {1598 if (self.air.typeOf(br.operand).hasCodeGenBits()) {
1412 try self.emitWValue(self.resolveInst(br.operand));1599 try self.emitWValue(self.resolveInst(br.operand));
1600
1601 if (block.value != .none) {
1602 try self.addLabel(.local_set, block.value.local);
1603 }
1413 }1604 }
14141605
1415 // We map every block to its block index.1606 // We map every block to its block index.
1416 // We then determine how far we have to jump to it by subtracting it from current block depth1607 // We then determine how far we have to jump to it by subtracting it from current block depth
1417 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;1608 const idx: u32 = self.block_depth - block.label;
1418 try self.addLabel(.br, idx);1609 try self.addLabel(.br, idx);
14191610
1420 return .none;1611 return .none;
...@@ -1422,7 +1613,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1422,7 +1613,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14221613
1423fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1614fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1424 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1615 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1425 const offset = self.mir_instructions.len;
14261616
1427 const operand = self.resolveInst(ty_op.operand);1617 const operand = self.resolveInst(ty_op.operand);
1428 try self.emitWValue(operand);1618 try self.emitWValue(operand);
...@@ -1432,7 +1622,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1432,7 +1622,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1432 try self.addImm32(0);1622 try self.addImm32(0);
1433 try self.addTag(.i32_eq);1623 try self.addTag(.i32_eq);
14341624
1435 return WValue{ .mir_offset = offset };1625 // save the result in the local
1626 const not_tmp = try self.allocLocal(self.air.getRefType(ty_op.ty));
1627 try self.addLabel(.local_set, not_tmp.local);
1628 return not_tmp;
1436}1629}
14371630
1438fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1631fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1458,24 +1651,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1458,24 +1651,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1458 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1651 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1459 const extra = self.air.extraData(Air.StructField, ty_pl.payload);1652 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
1460 const struct_ptr = self.resolveInst(extra.data.struct_operand);1653 const struct_ptr = self.resolveInst(extra.data.struct_operand);
1461 return structFieldPtr(struct_ptr, extra.data.field_index);1654 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
1655 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch {
1656 return self.fail("Field type '{}' too big to fit into stack frame", .{
1657 struct_ty.structFieldType(extra.data.field_index),
1658 });
1659 };
1660 return structFieldPtr(struct_ptr, offset);
1462}1661}
1662
1463fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {1663fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {
1464 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1664 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1465 const struct_ptr = self.resolveInst(ty_op.operand);1665 const struct_ptr = self.resolveInst(ty_op.operand);
1466 return structFieldPtr(struct_ptr, index);1666 const struct_ty = self.air.typeOf(ty_op.operand).childType();
1667 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
1668 return self.fail("Field type '{}' too big to fit into stack frame", .{
1669 struct_ty.structFieldType(index),
1670 });
1671 };
1672 return structFieldPtr(struct_ptr, offset);
1467}1673}
1468fn structFieldPtr(struct_ptr: WValue, index: u32) InnerError!WValue {1674
1469 return WValue{ .local = struct_ptr.multi_value.index + index };1675fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {
1676 return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } };
1470}1677}
14711678
1472fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1679fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1473 if (self.liveness.isUnused(inst)) return WValue.none;1680 if (self.liveness.isUnused(inst)) return WValue.none;
14741681
1475 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1682 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1476 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;1683 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
1477 const struct_multivalue = self.resolveInst(extra.struct_operand).multi_value;1684 const struct_ty = self.air.typeOf(struct_field.struct_operand);
1478 return WValue{ .local = struct_multivalue.index + extra.field_index };1685 const operand = self.resolveInst(struct_field.struct_operand);
1686 const field_index = struct_field.field_index;
1687 const field_ty = struct_ty.structFieldType(field_index);
1688 if (!field_ty.hasCodeGenBits()) return WValue.none;
1689 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {
1690 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
1691 };
1692 return try self.load(operand, field_ty, offset);
1479}1693}
14801694
1481fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1695fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1521,7 +1735,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1521,7 +1735,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1521 }1735 }
15221736
1523 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });1737 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });
1524 try self.startBlock(.block, blocktype, null);1738 try self.startBlock(.block, blocktype);
1525 }1739 }
15261740
1527 // When the highest and lowest values are seperated by '50',1741 // When the highest and lowest values are seperated by '50',
...@@ -1534,7 +1748,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1534,7 +1748,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1534 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];1748 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
1535 const has_else_body = else_body.len != 0;1749 const has_else_body = else_body.len != 0;
1536 if (has_else_body) {1750 if (has_else_body) {
1537 try self.startBlock(.block, blocktype, null);1751 try self.startBlock(.block, blocktype);
1538 }1752 }
15391753
1540 if (!is_sparse) {1754 if (!is_sparse) {
...@@ -1542,7 +1756,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1542,7 +1756,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1542 // The value 'target' represents the index into the table.1756 // The value 'target' represents the index into the table.
1543 // Each index in the table represents a label to the branch1757 // Each index in the table represents a label to the branch
1544 // to jump to.1758 // to jump to.
1545 try self.startBlock(.block, blocktype, null);1759 try self.startBlock(.block, blocktype);
1546 try self.emitWValue(target);1760 try self.emitWValue(target);
1547 if (lowest < 0) {1761 if (lowest < 0) {
1548 // since br_table works using indexes, starting from '0', we must ensure all values1762 // since br_table works using indexes, starting from '0', we must ensure all values
...@@ -1598,7 +1812,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1598,7 +1812,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1598 try self.addLabel(.br_if, 0);1812 try self.addLabel(.br_if, 0);
1599 } else {1813 } else {
1600 // in multi-value prongs we must check if any prongs match the target value.1814 // in multi-value prongs we must check if any prongs match the target value.
1601 try self.startBlock(.block, blocktype, null);1815 try self.startBlock(.block, blocktype);
1602 for (case.values) |value| {1816 for (case.values) |value| {
1603 try self.emitWValue(target);1817 try self.emitWValue(target);
1604 try self.emitConstant(value.value, target_ty);1818 try self.emitConstant(value.value, target_ty);
...@@ -1629,30 +1843,40 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1629,30 +1843,40 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1629fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {1843fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
1630 const un_op = self.air.instructions.items(.data)[inst].un_op;1844 const un_op = self.air.instructions.items(.data)[inst].un_op;
1631 const operand = self.resolveInst(un_op);1845 const operand = self.resolveInst(un_op);
1632 const offset = self.mir_instructions.len;1846 const err_ty = self.air.typeOf(un_op).errorUnionSet();
1847
1848 // load the error tag value
1849 try self.emitWValue(operand);
1850 const mem_arg_index = try self.addExtra(Mir.MemArg{
1851 .offset = 0,
1852 .alignment = err_ty.abiAlignment(self.target),
1853 });
1854 try self.addInst(.{
1855 .tag = .i32_load16_u,
1856 .data = .{ .payload = mem_arg_index },
1857 });
16331858
1634 // load the error value which is positioned at multi_value's index
1635 try self.emitWValue(.{ .local = operand.multi_value.index });
1636 // Compare the error value with '0'1859 // Compare the error value with '0'
1637 try self.addImm32(0);1860 try self.addImm32(0);
1638 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1861 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
16391862
1640 return WValue{ .mir_offset = offset };1863 const is_err_tmp = try self.allocLocal(err_ty);
1864 try self.addLabel(.local_set, is_err_tmp.local);
1865 return is_err_tmp;
1641}1866}
16421867
1643fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1868fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1644 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1869 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1645 const operand = self.resolveInst(ty_op.operand);1870 const operand = self.resolveInst(ty_op.operand);
1646 // The index of multi_value contains the error code. To get the initial index of the payload we get1871 const err_ty = self.air.typeOf(ty_op.operand);
1647 // the following index. Next, convert it to a `WValue.local`1872 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
1648 //1873 return self.load(operand, err_ty.errorUnionPayload(), offset);
1649 // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct.
1650 return WValue{ .local = operand.multi_value.index + 1 };
1651}1874}
16521875
1653fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1876fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1654 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1877 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1655 return self.resolveInst(ty_op.operand);1878 _ = ty_op;
1879 return self.fail("TODO: wasm airWrapErrUnionPayload", .{});
1656}1880}
16571881
1658fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1882fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1682,22 +1906,41 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!...@@ -1682,22 +1906,41 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!
1682 const un_op = self.air.instructions.items(.data)[inst].un_op;1906 const un_op = self.air.instructions.items(.data)[inst].un_op;
1683 const operand = self.resolveInst(un_op);1907 const operand = self.resolveInst(un_op);
16841908
1685 // load the null value which is positioned at multi_value's index1909 // load the null tag value
1686 try self.emitWValue(.{ .local = operand.multi_value.index });1910 try self.emitWValue(operand);
1911 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 });
1912 try self.addInst(.{
1913 .tag = .i32_load8_u,
1914 .data = .{ .payload = mem_arg_index },
1915 });
1916
1917 // Compare the error value with '0'
1687 try self.addImm32(0);1918 try self.addImm32(0);
1688 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1919 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
16891920
1690 // we save the result in a new local1921 const is_null_tmp = try self.allocLocal(Type.initTag(.u8));
1691 const local = try self.allocLocal(Type.initTag(.i32));1922 try self.addLabel(.local_set, is_null_tmp.local);
1692 try self.addLabel(.local_set, local.local);1923 return is_null_tmp;
1693
1694 return local;
1695}1924}
16961925
1697fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1926fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1698 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1927 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1699 const operand = self.resolveInst(ty_op.operand);1928 const operand = self.resolveInst(ty_op.operand);
1700 return WValue{ .local = operand.multi_value.index + 1 };1929 const opt_ty = self.air.typeOf(ty_op.operand);
1930
1931 // For pointers we simply return its stack address, rather than
1932 // loading its value
1933 if (opt_ty.zigTypeTag() == .Pointer) {
1934 return WValue{ .local_with_offset = .{ .local = operand.local, .offset = 1 } };
1935 }
1936
1937 if (opt_ty.isPtrLikeOptional()) return operand;
1938
1939 var buf: Type.Payload.ElemType = undefined;
1940 const child_ty = opt_ty.optionalChild(&buf);
1941 const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target);
1942
1943 return self.load(operand, child_ty, @intCast(u32, offset));
1701}1944}
17021945
1703fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1946fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1709,5 +1952,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -1709,5 +1952,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
17091952
1710fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1953fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1711 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1954 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1712 return self.resolveInst(ty_op.operand);1955 const operand = self.resolveInst(ty_op.operand);
1956
1957 const op_ty = self.air.typeOf(ty_op.operand);
1958 const optional_ty = self.air.getRefType(ty_op.ty);
1959 const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target);
1960
1961 return WValue{ .local_with_offset = .{
1962 .local = operand.local,
1963 .offset = @intCast(u32, offset),
1964 } };
1713}1965}
src/arch/wasm/Emit.zig+26-1
...@@ -60,8 +60,30 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -60,8 +60,30 @@ pub fn emitMir(emit: *Emit) InnerError!void {
6060
61 // memory instructions61 // memory instructions
62 .i32_load => try emit.emitMemArg(tag, inst),62 .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),
63 .i32_store => try emit.emitMemArg(tag, inst),76 .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
65 .local_get => try emit.emitLabel(tag, inst),87 .local_get => try emit.emitLabel(tag, inst),
66 .local_set => try emit.emitLabel(tag, inst),88 .local_set => try emit.emitLabel(tag, inst),
67 .local_tee => try emit.emitLabel(tag, inst),89 .local_tee => try emit.emitLabel(tag, inst),
...@@ -229,7 +251,10 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -229,7 +251,10 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
229 const extra_index = emit.mir.instructions.items(.data)[inst].payload;251 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
230 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;252 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;
231 try emit.code.append(@enumToInt(tag));253 try emit.code.append(@enumToInt(tag));
232 try leb128.writeULEB128(emit.code.writer(), mem_arg.alignment);254
255 // wasm encodes alignment as power of 2, rather than natural alignment
256 const encoded_alignment = mem_arg.alignment >> 1;
257 try leb128.writeULEB128(emit.code.writer(), encoded_alignment);
233 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);258 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);
234}259}
235260
src/arch/wasm/Mir.zig+115-1
...@@ -97,11 +97,125 @@ pub const Inst = struct {...@@ -97,11 +97,125 @@ pub const Inst = struct {
97 ///97 ///
98 /// Uses `payload` of type `MemArg`.98 /// Uses `payload` of type `MemArg`.
99 i32_load = 0x28,99 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,
100 /// Pops 2 values from the stack, where the first value represents the value to write into memory165 /// Pops 2 values from the stack, where the first value represents the value to write into memory
101 /// and the second value represents the offset into memory where the value must be written to.166 /// 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.
102 ///168 ///
103 /// Uses `payload` of type `MemArg`.169 /// Uses `payload` of type `MemArg`.
104 i32_store = 0x36,170 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,
105 /// Returns the memory size in amount of pages.219 /// Returns the memory size in amount of pages.
106 ///220 ///
107 /// Uses `nop`221 /// Uses `nop`
...@@ -247,7 +361,7 @@ pub const Inst = struct {...@@ -247,7 +361,7 @@ pub const Inst = struct {
247361
248 /// From a given wasm opcode, returns a MIR tag.362 /// From a given wasm opcode, returns a MIR tag.
249 pub fn fromOpcode(opcode: std.wasm.Opcode) Tag {363 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
251 }365 }
252366
253 /// Returns a wasm opcode from a given MIR tag.367 /// Returns a wasm opcode from a given MIR tag.
src/link/Wasm.zig+32-2
...@@ -404,6 +404,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -404,6 +404,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
404 // The table contains all decl's with its corresponding offset into404 // The table contains all decl's with its corresponding offset into
405 // the 'data' section405 // the 'data' section
406 const offset_table_size = @intCast(u32, self.offset_table.items.len * ptr_width);406 const offset_table_size = @intCast(u32, self.offset_table.items.len * ptr_width);
407 // The size of the emulated stack
408 const stack_size = @intCast(u32, self.base.options.stack_size_override orelse std.wasm.page_size);
407409
408 // The size of the data, this together with `offset_table_size` amounts to the410 // The size of the data, this together with `offset_table_size` amounts to the
409 // total size of the 'data' section411 // total size of the 'data' section
...@@ -487,7 +489,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -487,7 +489,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
487 }489 }
488490
489 // Memory section491 // Memory section
490 if (data_size != 0) {492 {
491 const header_offset = try reserveVecSectionHeader(file);493 const header_offset = try reserveVecSectionHeader(file);
492 const writer = file.writer();494 const writer = file.writer();
493495
...@@ -498,7 +500,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -498,7 +500,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
498 writer,500 writer,
499 try std.math.divCeil(501 try std.math.divCeil(
500 u32,502 u32,
501 offset_table_size + data_size,503 offset_table_size + data_size + stack_size,
502 std.wasm.page_size,504 std.wasm.page_size,
503 ),505 ),
504 );506 );
...@@ -511,6 +513,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -511,6 +513,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
511 );513 );
512 }514 }
513515
516 // Global section (used to emit stack pointer)
517 {
518 // We emit the emulated stack at the end of the data section,
519 // 'growing' downwards towards the program memory.
520 // TODO: Have linker resolve the offset table, so we can emit the stack
521 // at the start so we can't overwrite program memory with the stack.
522 const sp_value = offset_table_size + data_size + std.wasm.page_size;
523 const mutable = true; // stack pointer MUST be mutable
524 const header_offset = try reserveVecSectionHeader(file);
525 const writer = file.writer();
526
527 try writer.writeByte(wasm.valtype(.i32));
528 try writer.writeByte(@boolToInt(mutable));
529
530 // set the initial value of the stack pointer to the data size + stack size
531 try writer.writeByte(wasm.opcode(.i32_const));
532 try leb.writeILEB128(writer, @bitCast(i32, sp_value));
533 try writer.writeByte(wasm.opcode(.end));
534
535 try writeVecSectionHeader(
536 file,
537 header_offset,
538 .global,
539 @intCast(u32, (try file.getPos()) - header_offset - header_size),
540 @as(u32, 1),
541 );
542 }
543
514 // Export section544 // Export section
515 if (self.base.options.module) |module| {545 if (self.base.options.module) |module| {
516 const header_offset = try reserveVecSectionHeader(file);546 const header_offset = try reserveVecSectionHeader(file);
src/print_air.zig+1-1
...@@ -234,7 +234,7 @@ const Writer = struct {...@@ -234,7 +234,7 @@ const Writer = struct {
234 fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {234 fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
235 const ty_str = w.air.instructions.items(.data)[inst].ty_str;235 const ty_str = w.air.instructions.items(.data)[inst].ty_str;
236 const name = w.zir.nullTerminatedString(ty_str.str);236 const name = w.zir.nullTerminatedString(ty_str.str);
237 try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), ty_str.ty });237 try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), w.air.getRefType(ty_str.ty) });
238 }238 }
239239
240 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {240 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
test/stage2/wasm.zig+35
...@@ -740,4 +740,39 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -740,4 +740,39 @@ pub fn addCases(ctx: *TestContext) !void {
740 \\}740 \\}
741 , "0\n");741 , "0\n");
742 }742 }
743
744 {
745 var case = ctx.exe("wasm pointers", wasi);
746
747 case.addCompareOutput(
748 \\pub export fn _start() u32 {
749 \\ var x: u32 = 0;
750 \\
751 \\ foo(&x);
752 \\ return x;
753 \\}
754 \\
755 \\fn foo(x: *u32)void {
756 \\ x.* = 2;
757 \\}
758 , "2\n");
759
760 case.addCompareOutput(
761 \\pub export fn _start() u32 {
762 \\ var x: u32 = 0;
763 \\
764 \\ foo(&x);
765 \\ bar(&x);
766 \\ return x;
767 \\}
768 \\
769 \\fn foo(x: *u32)void {
770 \\ x.* = 2;
771 \\}
772 \\
773 \\fn bar(x: *u32) void {
774 \\ x.* += 2;
775 \\}
776 , "4\n");
777 }
743}778}