| ... | @@ -210,7 +210,12 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -210,7 +210,12 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 210 | }, | 210 | }, |
| 211 | 32 => switch (args.valtype1.?) { | 211 | 32 => switch (args.valtype1.?) { |
| 212 | .i64 => return .i64_store32, | 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 | else => unreachable, | 220 | else => unreachable, |
| 216 | } | 221 | } |
| ... | @@ -529,6 +534,9 @@ global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt), | ... | @@ -529,6 +534,9 @@ global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt), |
| 529 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | 534 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 530 | /// Contains extra data for MIR | 535 | /// Contains extra data for MIR |
| 531 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, | 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 | const InnerError = error{ | 541 | const InnerError = error{ |
| 534 | OutOfMemory, | 542 | OutOfMemory, |
| ... | @@ -686,9 +694,7 @@ fn emitWValue(self: *Self, val: WValue) InnerError!void { | ... | @@ -686,9 +694,7 @@ fn emitWValue(self: *Self, val: WValue) InnerError!void { |
| 686 | switch (val) { | 694 | switch (val) { |
| 687 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually | 695 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually |
| 688 | .none, .mir_offset => {}, // no-op | 696 | .none, .mir_offset => {}, // no-op |
| 689 | .local => |idx| { | 697 | .local => |idx| try self.addLabel(.local_get, idx), |
| 690 | try self.addLabel(.local_get, idx); | | |
| 691 | }, | | |
| 692 | .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack | 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,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 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | 946 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 888 | const air_tags = self.air.instructions.items(.tag); | 947 | const air_tags = self.air.instructions.items(.tag); |
| 889 | return switch (air_tags[inst]) { | 948 | return switch (air_tags[inst]) { |
| ... | @@ -963,6 +1022,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -963,6 +1022,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 963 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1022 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 964 | const operand = self.resolveInst(un_op); | 1023 | const operand = self.resolveInst(un_op); |
| 965 | try self.emitWValue(operand); | 1024 | try self.emitWValue(operand); |
| | 1025 | try self.restoreStackPointer(); |
| 966 | try self.addTag(.@"return"); | 1026 | try self.addTag(.@"return"); |
| 967 | return .none; | 1027 | return .none; |
| 968 | } | 1028 | } |
| ... | @@ -989,13 +1049,24 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -989,13 +1049,24 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 989 | } | 1049 | } |
| 990 | | 1050 | |
| 991 | try self.addLabel(.call, target.link.wasm.symbol_index); | 1051 | try self.addLabel(.call, target.link.wasm.symbol_index); |
| 992 | | | |
| 993 | return .none; | 1052 | return .none; |
| 994 | } | 1053 | } |
| 995 | | 1054 | |
| 996 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1055 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 997 | const elem_type = self.air.typeOfIndex(inst).elemType(); | 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 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,48 +1075,35 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1004 | const lhs = self.resolveInst(bin_op.lhs); | 1075 | const lhs = self.resolveInst(bin_op.lhs); |
| 1005 | const rhs = self.resolveInst(bin_op.rhs); | 1076 | const rhs = self.resolveInst(bin_op.rhs); |
| 1006 | | 1077 | |
| 1007 | switch (lhs) { | 1078 | // get lhs stack position |
| 1008 | .multi_value => |multi_value| switch (rhs) { | 1079 | try self.emitWValue(lhs); |
| 1009 | // When assigning a value to a multi_value such as a struct, | 1080 | // get rhs value |
| 1010 | // we simply assign the local_index to the rhs one. | 1081 | try self.emitWValue(rhs); |
| 1011 | // This allows us to update struct fields without having to individually | 1082 | |
| 1012 | // set each local as each field's index will be calculated off the struct's base index | 1083 | const ty = self.air.typeOf(bin_op.lhs); |
| 1013 | .multi_value => self.values.put(self.gpa, Air.refToIndex(bin_op.lhs).?, rhs) catch unreachable, // Instruction does not dominate all uses! | 1084 | const valtype = try self.typeToValtype(ty); |
| 1014 | .constant, .none => { | 1085 | |
| 1015 | // emit all values onto the stack if constant | 1086 | const opcode = buildOpcode(.{ |
| 1016 | try self.emitWValue(rhs); | 1087 | .valtype1 = valtype, |
| 1017 | | 1088 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| 1018 | // for each local, pop the stack value into the local | 1089 | .op = .store, |
| 1019 | // As the last element is on top of the stack, we must populate the locals | 1090 | }); |
| 1020 | // in reverse. | 1091 | // store rhs value at stack pointer's location in memory |
| 1021 | var i: u32 = multi_value.count; | 1092 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); |
| 1022 | while (i > 0) : (i -= 1) { | 1093 | try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } }); |
| 1023 | try self.addLabel(.local_set, multi_value.index + i - 1); | 1094 | |
| 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 | } | | |
| 1043 | return .none; | 1095 | return .none; |
| 1044 | } | 1096 | } |
| 1045 | | 1097 | |
| 1046 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1098 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1047 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,14 +1118,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1060 | const lhs = self.resolveInst(bin_op.lhs); | 1118 | const lhs = self.resolveInst(bin_op.lhs); |
| 1061 | const rhs = self.resolveInst(bin_op.rhs); | 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 | try self.emitWValue(lhs); | 1121 | try self.emitWValue(lhs); |
| 1072 | try self.emitWValue(rhs); | 1122 | try self.emitWValue(rhs); |
| 1073 | | 1123 | |
| ... | @@ -1078,7 +1128,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1078,7 +1128,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1078 | .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, | 1128 | .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, |
| 1079 | }); | 1129 | }); |
| 1080 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 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 | fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 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,14 +1140,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1086 | const lhs = self.resolveInst(bin_op.lhs); | 1140 | const lhs = self.resolveInst(bin_op.lhs); |
| 1087 | const rhs = self.resolveInst(bin_op.rhs); | 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 | try self.emitWValue(lhs); | 1143 | try self.emitWValue(lhs); |
| 1098 | try self.emitWValue(rhs); | 1144 | try self.emitWValue(rhs); |
| 1099 | | 1145 | |
| ... | @@ -1132,7 +1178,10 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1132,7 +1178,10 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1132 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); | 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 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | 1187 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |