| ... | ... | @@ -210,7 +210,12 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 210 | 210 | }, |
| 211 | 211 | 32 => switch (args.valtype1.?) { |
| 212 | 212 | .i64 => return .i64_store32, |
| 213 | | .i32, .f32, .f64 => unreachable, |
| 213 | .i32 => return .i32_store, |
| 214 | .f32, .f64 => unreachable, |
| 215 | }, |
| 216 | 64 => switch (args.valtype1.?) { |
| 217 | .i64 => return .i64_store, |
| 218 | else => unreachable, |
| 214 | 219 | }, |
| 215 | 220 | else => unreachable, |
| 216 | 221 | } |
| ... | ... | @@ -529,6 +534,9 @@ global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt), |
| 529 | 534 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 530 | 535 | /// Contains extra data for MIR |
| 531 | 536 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 537 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 538 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 539 | initial_stack_value: WValue = .none, |
| 532 | 540 | |
| 533 | 541 | const InnerError = error{ |
| 534 | 542 | OutOfMemory, |
| ... | ... | @@ -686,9 +694,7 @@ fn emitWValue(self: *Self, val: WValue) InnerError!void { |
| 686 | 694 | switch (val) { |
| 687 | 695 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually |
| 688 | 696 | .none, .mir_offset => {}, // no-op |
| 689 | | .local => |idx| { |
| 690 | | try self.addLabel(.local_get, idx); |
| 691 | | }, |
| 697 | .local => |idx| try self.addLabel(.local_get, idx), |
| 692 | 698 | .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack |
| 693 | 699 | } |
| 694 | 700 | } |
| ... | ... | @@ -884,6 +890,59 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 884 | 890 | } |
| 885 | 891 | } |
| 886 | 892 | |
| 893 | /// Retrieves the stack pointer's value from the global variable and stores |
| 894 | /// it in a local |
| 895 | fn initializeStack(self: *Self) !void { |
| 896 | // reserve space for immediate value |
| 897 | // get stack pointer global |
| 898 | // TODO: For now, we hardcode the stack pointer to index '0', |
| 899 | // once the linker is further implemented, we can replace this by inserting |
| 900 | // a relocation and have the linker resolve the correct index to the stack pointer global. |
| 901 | // NOTE: relocations of the type GLOBAL_INDEX_LEB are 5-bytes big |
| 902 | try self.addLabel(.global_get, 0); |
| 903 | |
| 904 | // Reserve a local to store the current stack pointer |
| 905 | // We can later use this local to set the stack pointer back to the value |
| 906 | // we have stored here. |
| 907 | self.initial_stack_value = try self.allocLocal(Type.initTag(.i32)); |
| 908 | |
| 909 | // save the value to the local |
| 910 | try self.addLabel(.local_set, self.initial_stack_value.local); |
| 911 | } |
| 912 | |
| 913 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| 914 | /// to the global stack pointer variable |
| 915 | fn restoreStackPointer(self: *Self) !void { |
| 916 | // only restore the pointer if it was initialized |
| 917 | if (self.initial_stack_value == .none) return; |
| 918 | // Get the original stack pointer's value |
| 919 | try self.emitWValue(self.initial_stack_value); |
| 920 | |
| 921 | // save its value in the global stack pointer |
| 922 | try self.addLabel(.global_set, 0); |
| 923 | } |
| 924 | |
| 925 | /// Moves the stack pointer by given `offset` |
| 926 | /// It does this by retrieving the stack pointer, subtracting `offset` and storing |
| 927 | /// the result back into the stack pointer. |
| 928 | fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 929 | 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 | |
| 937 | // TODO: Rather than hardcode the stack pointer to position 0, |
| 938 | // have the linker resolve it. |
| 939 | try self.addLabel(.global_get, 0); |
| 940 | try self.addImm32(@bitCast(i32, offset)); |
| 941 | try self.addTag(.i32_sub); |
| 942 | try self.addLabel(.local_tee, local); |
| 943 | try self.addLabel(.global_set, 0); |
| 944 | } |
| 945 | |
| 887 | 946 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 888 | 947 | const air_tags = self.air.instructions.items(.tag); |
| 889 | 948 | return switch (air_tags[inst]) { |
| ... | ... | @@ -963,6 +1022,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 963 | 1022 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 964 | 1023 | const operand = self.resolveInst(un_op); |
| 965 | 1024 | try self.emitWValue(operand); |
| 1025 | try self.restoreStackPointer(); |
| 966 | 1026 | try self.addTag(.@"return"); |
| 967 | 1027 | return .none; |
| 968 | 1028 | } |
| ... | ... | @@ -989,13 +1049,24 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 989 | 1049 | } |
| 990 | 1050 | |
| 991 | 1051 | try self.addLabel(.call, target.link.wasm.symbol_index); |
| 992 | | |
| 993 | 1052 | return .none; |
| 994 | 1053 | } |
| 995 | 1054 | |
| 996 | 1055 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 997 | 1056 | const elem_type = self.air.typeOfIndex(inst).elemType(); |
| 998 | | return self.allocLocal(elem_type); |
| 1057 | |
| 1058 | // Initialize the stack |
| 1059 | if (self.initial_stack_value == .none) { |
| 1060 | try self.initializeStack(); |
| 1061 | } |
| 1062 | |
| 1063 | const abi_size = elem_type.abiSize(self.target); |
| 1064 | if (abi_size == 0) return WValue{ .none = {} }; |
| 1065 | |
| 1066 | const local = try self.allocLocal(elem_type); |
| 1067 | try self.moveStack(@intCast(u32, abi_size), local.local); |
| 1068 | |
| 1069 | return local; |
| 999 | 1070 | } |
| 1000 | 1071 | |
| 1001 | 1072 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1004,48 +1075,35 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1004 | 1075 | const lhs = self.resolveInst(bin_op.lhs); |
| 1005 | 1076 | const rhs = self.resolveInst(bin_op.rhs); |
| 1006 | 1077 | |
| 1007 | | switch (lhs) { |
| 1008 | | .multi_value => |multi_value| switch (rhs) { |
| 1009 | | // When assigning a value to a multi_value such as a struct, |
| 1010 | | // we simply assign the local_index to the rhs one. |
| 1011 | | // This allows us to update struct fields without having to individually |
| 1012 | | // set each local as each field's index will be calculated off the struct's base index |
| 1013 | | .multi_value => self.values.put(self.gpa, Air.refToIndex(bin_op.lhs).?, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 1014 | | .constant, .none => { |
| 1015 | | // emit all values onto the stack if constant |
| 1016 | | try self.emitWValue(rhs); |
| 1017 | | |
| 1018 | | // for each local, pop the stack value into the local |
| 1019 | | // As the last element is on top of the stack, we must populate the locals |
| 1020 | | // in reverse. |
| 1021 | | var i: u32 = multi_value.count; |
| 1022 | | while (i > 0) : (i -= 1) { |
| 1023 | | try self.addLabel(.local_set, multi_value.index + i - 1); |
| 1024 | | } |
| 1025 | | }, |
| 1026 | | .local => { |
| 1027 | | // This can occur when we wrap a single value into a multi-value, |
| 1028 | | // such as wrapping a non-optional value into an optional. |
| 1029 | | // This means we must zero the null-tag, and set the payload. |
| 1030 | | assert(multi_value.count == 2); |
| 1031 | | // set payload |
| 1032 | | try self.emitWValue(rhs); |
| 1033 | | try self.addLabel(.local_set, multi_value.index + 1); |
| 1034 | | }, |
| 1035 | | else => unreachable, |
| 1036 | | }, |
| 1037 | | .local => |local| { |
| 1038 | | try self.emitWValue(rhs); |
| 1039 | | try self.addLabel(.local_set, local); |
| 1040 | | }, |
| 1041 | | else => unreachable, |
| 1042 | | } |
| 1078 | // get lhs stack position |
| 1079 | try self.emitWValue(lhs); |
| 1080 | // get rhs value |
| 1081 | try self.emitWValue(rhs); |
| 1082 | |
| 1083 | const ty = self.air.typeOf(bin_op.lhs); |
| 1084 | const valtype = try self.typeToValtype(ty); |
| 1085 | |
| 1086 | const opcode = buildOpcode(.{ |
| 1087 | .valtype1 = valtype, |
| 1088 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| 1089 | .op = .store, |
| 1090 | }); |
| 1091 | // store rhs value at stack pointer's location in memory |
| 1092 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); |
| 1093 | try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } }); |
| 1094 | |
| 1043 | 1095 | return .none; |
| 1044 | 1096 | } |
| 1045 | 1097 | |
| 1046 | 1098 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1047 | 1099 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1048 | | return self.resolveInst(ty_op.operand); |
| 1100 | const lhs = self.resolveInst(ty_op.operand); |
| 1101 | |
| 1102 | // 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; |
| 1049 | 1107 | } |
| 1050 | 1108 | |
| 1051 | 1109 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1060,14 +1118,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1060 | 1118 | const lhs = self.resolveInst(bin_op.lhs); |
| 1061 | 1119 | const rhs = self.resolveInst(bin_op.rhs); |
| 1062 | 1120 | |
| 1063 | | // it's possible for both lhs and/or rhs to return an offset as well, |
| 1064 | | // in which case we return the first offset occurrence we find. |
| 1065 | | const offset = blk: { |
| 1066 | | if (lhs == .mir_offset) break :blk lhs.mir_offset; |
| 1067 | | if (rhs == .mir_offset) break :blk rhs.mir_offset; |
| 1068 | | break :blk self.mir_instructions.len; |
| 1069 | | }; |
| 1070 | | |
| 1071 | 1121 | try self.emitWValue(lhs); |
| 1072 | 1122 | try self.emitWValue(rhs); |
| 1073 | 1123 | |
| ... | ... | @@ -1078,7 +1128,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1078 | 1128 | .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, |
| 1079 | 1129 | }); |
| 1080 | 1130 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1081 | | return WValue{ .mir_offset = offset }; |
| 1131 | |
| 1132 | // save the result in a temporary |
| 1133 | const bin_local = try self.allocLocal(bin_ty); |
| 1134 | try self.addLabel(.local_set, bin_local.local); |
| 1135 | return bin_local; |
| 1082 | 1136 | } |
| 1083 | 1137 | |
| 1084 | 1138 | fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| ... | ... | @@ -1086,14 +1140,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1086 | 1140 | const lhs = self.resolveInst(bin_op.lhs); |
| 1087 | 1141 | const rhs = self.resolveInst(bin_op.rhs); |
| 1088 | 1142 | |
| 1089 | | // it's possible for both lhs and/or rhs to return an offset as well, |
| 1090 | | // in which case we return the first offset occurrence we find. |
| 1091 | | const offset = blk: { |
| 1092 | | if (lhs == .mir_offset) break :blk lhs.mir_offset; |
| 1093 | | if (rhs == .mir_offset) break :blk rhs.mir_offset; |
| 1094 | | break :blk self.mir_instructions.len; |
| 1095 | | }; |
| 1096 | | |
| 1097 | 1143 | try self.emitWValue(lhs); |
| 1098 | 1144 | try self.emitWValue(rhs); |
| 1099 | 1145 | |
| ... | ... | @@ -1132,7 +1178,10 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1132 | 1178 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); |
| 1133 | 1179 | } |
| 1134 | 1180 | |
| 1135 | | return WValue{ .mir_offset = offset }; |
| 1181 | // save the result in a temporary |
| 1182 | const bin_local = try self.allocLocal(bin_ty); |
| 1183 | try self.addLabel(.local_set, bin_local.local); |
| 1184 | return bin_local; |
| 1136 | 1185 | } |
| 1137 | 1186 | |
| 1138 | 1187 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |