| ... | @@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void { | ... | @@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void { |
| 644 | try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); | 644 | try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); |
| 645 | } | 645 | } |
| 646 | | 646 | |
| | 647 | /// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`. |
| | 648 | fn addMemArg(self: *Self, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void { |
| | 649 | const extra_index = try self.addExtra(mem_arg); |
| | 650 | try self.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } }); |
| | 651 | } |
| | 652 | |
| 647 | /// Appends entries to `mir_extra` based on the type of `extra`. | 653 | /// Appends entries to `mir_extra` based on the type of `extra`. |
| 648 | /// Returns the index into `mir_extra` | 654 | /// Returns the index into `mir_extra` |
| 649 | fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 { | 655 | fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 { |
| ... | @@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { | ... | @@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 692 | .ErrorUnion, | 698 | .ErrorUnion, |
| 693 | .Optional, | 699 | .Optional, |
| 694 | .Fn, | 700 | .Fn, |
| | 701 | .Array, |
| 695 | => wasm.Valtype.i32, | 702 | => wasm.Valtype.i32, |
| 696 | else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), | 703 | else => self.fail("TODO - Wasm typeToValtype for type '{}'", .{ty}), |
| 697 | }; | 704 | }; |
| 698 | } | 705 | } |
| 699 | | 706 | |
| ... | @@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { | ... | @@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { |
| 756 | switch (return_type.zigTypeTag()) { | 763 | switch (return_type.zigTypeTag()) { |
| 757 | .Void, .NoReturn => {}, | 764 | .Void, .NoReturn => {}, |
| 758 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), | 765 | .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}), |
| 759 | .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}), | | |
| 760 | else => try returns.append(try self.typeToValtype(return_type)), | 766 | else => try returns.append(try self.typeToValtype(return_type)), |
| 761 | } | 767 | } |
| 762 | | 768 | |
| ... | @@ -967,6 +973,41 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -967,6 +973,41 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 967 | }, | 973 | }, |
| 968 | else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), | 974 | else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), |
| 969 | }, | 975 | }, |
| | 976 | .ErrorUnion => { |
| | 977 | const error_ty = ty.errorUnionSet(); |
| | 978 | const payload_ty = ty.errorUnionPayload(); |
| | 979 | const is_pl = val.errorUnionIsPayload(); |
| | 980 | |
| | 981 | const err_val = if (!is_pl) val else Value.initTag(.zero); |
| | 982 | switch (try self.genTypedValue(error_ty, err_val)) { |
| | 983 | .externally_managed => |data| try self.code.appendSlice(data), |
| | 984 | .appended => {}, |
| | 985 | } |
| | 986 | |
| | 987 | if (payload_ty.hasCodeGenBits()) { |
| | 988 | const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); |
| | 989 | switch (try self.genTypedValue(payload_ty, pl_val)) { |
| | 990 | .externally_managed => |data| try self.code.appendSlice(data), |
| | 991 | .appended => {}, |
| | 992 | } |
| | 993 | } |
| | 994 | |
| | 995 | return Result.appended; |
| | 996 | }, |
| | 997 | .ErrorSet => { |
| | 998 | switch (val.tag()) { |
| | 999 | .@"error" => { |
| | 1000 | const name = val.castTag(.@"error").?.data.name; |
| | 1001 | const value = self.global_error_set.get(name).?; |
| | 1002 | try self.code.writer().writeIntLittle(u32, value); |
| | 1003 | }, |
| | 1004 | else => { |
| | 1005 | const abi_size = @intCast(usize, ty.abiSize(self.target)); |
| | 1006 | try self.code.appendNTimes(0, abi_size); |
| | 1007 | }, |
| | 1008 | } |
| | 1009 | return Result.appended; |
| | 1010 | }, |
| 970 | else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), | 1011 | else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), |
| 971 | } | 1012 | } |
| 972 | } | 1013 | } |
| ... | @@ -1047,10 +1088,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1047,10 +1088,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1047 | } | 1088 | } |
| 1048 | | 1089 | |
| 1049 | const ret_ty = fn_ty.fnReturnType(); | 1090 | const ret_ty = fn_ty.fnReturnType(); |
| 1050 | switch (ret_ty.zigTypeTag()) { | 1091 | if (isByRef(ret_ty)) { |
| 1051 | .ErrorUnion, .Optional, .Pointer => result.return_value = try self.allocLocal(Type.initTag(.i32)), | 1092 | result.return_value = try self.allocLocal(Type.initTag(.i32)); |
| 1052 | .Int, .Float, .Bool, .Void, .NoReturn => {}, | | |
| 1053 | else => return self.fail("TODO: Implement function return type {}", .{ret_ty}), | | |
| 1054 | } | 1093 | } |
| 1055 | | 1094 | |
| 1056 | // Check if we store the result as a pointer to the stack rather than | 1095 | // Check if we store the result as a pointer to the stack rather than |
| ... | @@ -1116,6 +1155,26 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void { | ... | @@ -1116,6 +1155,26 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 1116 | try self.addLabel(.global_set, 0); | 1155 | try self.addLabel(.global_set, 0); |
| 1117 | } | 1156 | } |
| 1118 | | 1157 | |
| | 1158 | /// From a given type, will create space on the virtual stack to store the value of such type. |
| | 1159 | /// This returns a `WValue` with its active tag set to `local`, containing the index to the local |
| | 1160 | /// that points to the position on the virtual stack. This function should be used instead of |
| | 1161 | /// moveStack unless a local was already created to store the point. |
| | 1162 | /// |
| | 1163 | /// Asserts Type has codegenbits |
| | 1164 | fn allocStack(self: *Self, ty: Type) !WValue { |
| | 1165 | assert(ty.hasCodeGenBits()); |
| | 1166 | |
| | 1167 | // calculate needed stack space |
| | 1168 | const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { |
| | 1169 | return self.fail("Given type '{}' too big to fit into stack frame", .{ty}); |
| | 1170 | }; |
| | 1171 | |
| | 1172 | // allocate a local using wasm's pointer size |
| | 1173 | const local = try self.allocLocal(Type.@"usize"); |
| | 1174 | try self.moveStack(abi_size, local.local); |
| | 1175 | return local; |
| | 1176 | } |
| | 1177 | |
| 1119 | /// From given zig bitsize, returns the wasm bitsize | 1178 | /// From given zig bitsize, returns the wasm bitsize |
| 1120 | fn toWasmIntBits(bits: u16) ?u16 { | 1179 | fn toWasmIntBits(bits: u16) ?u16 { |
| 1121 | return for ([_]u16{ 32, 64 }) |wasm_bits| { | 1180 | return for ([_]u16{ 32, 64 }) |wasm_bits| { |
| ... | @@ -1123,6 +1182,80 @@ fn toWasmIntBits(bits: u16) ?u16 { | ... | @@ -1123,6 +1182,80 @@ fn toWasmIntBits(bits: u16) ?u16 { |
| 1123 | } else null; | 1182 | } else null; |
| 1124 | } | 1183 | } |
| 1125 | | 1184 | |
| | 1185 | /// Performs a copy of bytes for a given type. Copying all bytes |
| | 1186 | /// from rhs to lhs. |
| | 1187 | /// Asserts `lhs` and `rhs` have their active tag set to `local` |
| | 1188 | /// |
| | 1189 | /// TODO: Perform feature detection and when bulk_memory is available, |
| | 1190 | /// use wasm's mem.copy instruction. |
| | 1191 | fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void { |
| | 1192 | const abi_size = ty.abiSize(self.target); |
| | 1193 | var offset: u32 = 0; |
| | 1194 | while (offset < abi_size) : (offset += 1) { |
| | 1195 | // get lhs' address to store the result |
| | 1196 | try self.addLabel(.local_get, lhs.local); |
| | 1197 | // load byte from rhs' adress |
| | 1198 | try self.addLabel(.local_get, rhs.local); |
| | 1199 | try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 }); |
| | 1200 | // store the result in lhs (we already have its address on the stack) |
| | 1201 | try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }); |
| | 1202 | } |
| | 1203 | } |
| | 1204 | |
| | 1205 | fn ptrSize(self: *const Self) u16 { |
| | 1206 | return @divExact(self.target.cpu.arch.ptrBitWidth(), 8); |
| | 1207 | } |
| | 1208 | |
| | 1209 | /// For a given `Type`, will return true when the type will be passed |
| | 1210 | /// by reference, rather than by value. |
| | 1211 | fn isByRef(ty: Type) bool { |
| | 1212 | switch (ty.zigTypeTag()) { |
| | 1213 | .Type, |
| | 1214 | .ComptimeInt, |
| | 1215 | .ComptimeFloat, |
| | 1216 | .EnumLiteral, |
| | 1217 | .Undefined, |
| | 1218 | .Null, |
| | 1219 | .BoundFn, |
| | 1220 | .Opaque, |
| | 1221 | => unreachable, |
| | 1222 | |
| | 1223 | .NoReturn, |
| | 1224 | .Void, |
| | 1225 | .Bool, |
| | 1226 | .Int, |
| | 1227 | .Float, |
| | 1228 | .ErrorSet, |
| | 1229 | .Fn, |
| | 1230 | .Enum, |
| | 1231 | .Vector, |
| | 1232 | .AnyFrame, |
| | 1233 | => return false, |
| | 1234 | |
| | 1235 | .Array, |
| | 1236 | .Struct, |
| | 1237 | .Frame, |
| | 1238 | .Union, |
| | 1239 | => return ty.hasCodeGenBits(), |
| | 1240 | .ErrorUnion => { |
| | 1241 | const has_tag = ty.errorUnionSet().hasCodeGenBits(); |
| | 1242 | const has_pl = ty.errorUnionPayload().hasCodeGenBits(); |
| | 1243 | if (!has_tag or !has_pl) return false; |
| | 1244 | return ty.hasCodeGenBits(); |
| | 1245 | }, |
| | 1246 | .Optional => { |
| | 1247 | if (ty.isPtrLikeOptional()) return false; |
| | 1248 | var buf: Type.Payload.ElemType = undefined; |
| | 1249 | return ty.optionalChild(&buf).hasCodeGenBits(); |
| | 1250 | }, |
| | 1251 | .Pointer => { |
| | 1252 | // Slices act like struct and will be passed by reference |
| | 1253 | if (ty.isSlice()) return ty.hasCodeGenBits(); |
| | 1254 | return false; |
| | 1255 | }, |
| | 1256 | } |
| | 1257 | } |
| | 1258 | |
| 1126 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | 1259 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1127 | const air_tags = self.air.instructions.items(.tag); | 1260 | const air_tags = self.air.instructions.items(.tag); |
| 1128 | return switch (air_tags[inst]) { | 1261 | return switch (air_tags[inst]) { |
| ... | @@ -1146,12 +1279,14 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1146,12 +1279,14 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1146 | .cmp_lt => self.airCmp(inst, .lt), | 1279 | .cmp_lt => self.airCmp(inst, .lt), |
| 1147 | .cmp_neq => self.airCmp(inst, .neq), | 1280 | .cmp_neq => self.airCmp(inst, .neq), |
| 1148 | | 1281 | |
| | 1282 | .array_to_slice => self.airArrayToSlice(inst), |
| 1149 | .alloc => self.airAlloc(inst), | 1283 | .alloc => self.airAlloc(inst), |
| 1150 | .arg => self.airArg(inst), | 1284 | .arg => self.airArg(inst), |
| 1151 | .bitcast => self.airBitcast(inst), | 1285 | .bitcast => self.airBitcast(inst), |
| 1152 | .block => self.airBlock(inst), | 1286 | .block => self.airBlock(inst), |
| 1153 | .breakpoint => self.airBreakpoint(inst), | 1287 | .breakpoint => self.airBreakpoint(inst), |
| 1154 | .br => self.airBr(inst), | 1288 | .br => self.airBr(inst), |
| | 1289 | .bool_to_int => self.airBoolToInt(inst), |
| 1155 | .call => self.airCall(inst), | 1290 | .call => self.airCall(inst), |
| 1156 | .cond_br => self.airCondBr(inst), | 1291 | .cond_br => self.airCondBr(inst), |
| 1157 | .constant => unreachable, | 1292 | .constant => unreachable, |
| ... | @@ -1172,11 +1307,17 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1172,11 +1307,17 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1172 | .optional_payload => self.airOptionalPayload(inst), | 1307 | .optional_payload => self.airOptionalPayload(inst), |
| 1173 | .optional_payload_ptr => self.airOptionalPayload(inst), | 1308 | .optional_payload_ptr => self.airOptionalPayload(inst), |
| 1174 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), | 1309 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), |
| | 1310 | .ptr_add => self.airPtrBinOp(inst, .add), |
| | 1311 | .ptr_sub => self.airPtrBinOp(inst, .sub), |
| | 1312 | .ptr_elem_ptr => self.airPtrElemPtr(inst), |
| | 1313 | .ptr_elem_val => self.airPtrElemVal(inst), |
| | 1314 | .ptrtoint => self.airPtrToInt(inst), |
| 1175 | .ret => self.airRet(inst), | 1315 | .ret => self.airRet(inst), |
| 1176 | .ret_ptr => self.airRetPtr(inst), | 1316 | .ret_ptr => self.airRetPtr(inst), |
| 1177 | .ret_load => self.airRetLoad(inst), | 1317 | .ret_load => self.airRetLoad(inst), |
| 1178 | .slice_len => self.airSliceLen(inst), | 1318 | .slice_len => self.airSliceLen(inst), |
| 1179 | .slice_elem_val => self.airSliceElemVal(inst), | 1319 | .slice_elem_val => self.airSliceElemVal(inst), |
| | 1320 | .slice_elem_ptr => self.airSliceElemPtr(inst), |
| 1180 | .slice_ptr => self.airSlicePtr(inst), | 1321 | .slice_ptr => self.airSlicePtr(inst), |
| 1181 | .store => self.airStore(inst), | 1322 | .store => self.airStore(inst), |
| 1182 | .struct_field_ptr => self.airStructFieldPtr(inst), | 1323 | .struct_field_ptr => self.airStructFieldPtr(inst), |
| ... | @@ -1229,21 +1370,24 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1229,21 +1370,24 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1229 | try self.initializeStack(); | 1370 | try self.initializeStack(); |
| 1230 | } | 1371 | } |
| 1231 | | 1372 | |
| 1232 | const abi_size = child_type.abiSize(self.target); | 1373 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1233 | if (abi_size == 0) return WValue{ .none = {} }; | | |
| 1234 | | 1374 | |
| 1235 | // local, containing the offset to the stack position | 1375 | return self.allocStack(child_type); |
| 1236 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 | | |
| 1237 | try self.moveStack(@intCast(u32, abi_size), local.local); | | |
| 1238 | return local; | | |
| 1239 | } | 1376 | } |
| 1240 | | 1377 | |
| 1241 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1378 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1242 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1379 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1243 | const operand = self.resolveInst(un_op); | 1380 | const operand = self.resolveInst(un_op); |
| | 1381 | const ret_ty = self.air.typeOf(un_op).childType(); |
| | 1382 | if (!ret_ty.hasCodeGenBits()) return WValue.none; |
| | 1383 | |
| | 1384 | if (isByRef(ret_ty)) { |
| | 1385 | try self.emitWValue(operand); |
| | 1386 | } else { |
| | 1387 | const result = try self.load(operand, ret_ty, 0); |
| | 1388 | try self.emitWValue(result); |
| | 1389 | } |
| 1244 | | 1390 | |
| 1245 | const result = try self.load(operand, self.air.typeOf(un_op).childType(), 0); | | |
| 1246 | try self.emitWValue(result); | | |
| 1247 | try self.restoreStackPointer(); | 1391 | try self.restoreStackPointer(); |
| 1248 | try self.addTag(.@"return"); | 1392 | try self.addTag(.@"return"); |
| 1249 | return .none; | 1393 | return .none; |
| ... | @@ -1277,20 +1421,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1277,20 +1421,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1277 | const arg_val = self.resolveInst(arg_ref); | 1421 | const arg_val = self.resolveInst(arg_ref); |
| 1278 | | 1422 | |
| 1279 | const arg_ty = self.air.typeOf(arg_ref); | 1423 | const arg_ty = self.air.typeOf(arg_ref); |
| 1280 | switch (arg_ty.zigTypeTag()) { | 1424 | if (!arg_ty.hasCodeGenBits()) continue; |
| 1281 | .Struct, .Pointer, .Optional, .ErrorUnion => { | 1425 | |
| 1282 | // single pointer can be passed directly | 1426 | // If we need to pass by reference, but the argument is a constant, |
| 1283 | if (arg_ty.isSinglePointer() or arg_val != .constant) { | 1427 | // we must first lower it before passing it. |
| 1284 | try self.emitWValue(arg_val); | 1428 | if (isByRef(arg_ty) and arg_val == .constant) { |
| 1285 | continue; | 1429 | const arg_local = try self.allocStack(arg_ty); |
| 1286 | } | 1430 | try self.store(arg_local, arg_val, arg_ty, 0); |
| 1287 | const abi_size = arg_ty.abiSize(self.target); | 1431 | try self.emitWValue(arg_local); |
| 1288 | const arg_local = try self.allocLocal(Type.initTag(.i32)); | 1432 | } else if (arg_val == .none) { |
| 1289 | try self.moveStack(@intCast(u32, abi_size), arg_local.local); | 1433 | // TODO: Remove this branch when zero-sized pointers do not generate |
| 1290 | try self.store(arg_local, arg_val, arg_ty, 0); | 1434 | // an argument. |
| 1291 | try self.emitWValue(arg_local); | 1435 | try self.addImm32(0); |
| 1292 | }, | 1436 | } else { |
| 1293 | else => try self.emitWValue(arg_val), | 1437 | try self.emitWValue(arg_val); |
| 1294 | } | 1438 | } |
| 1295 | } | 1439 | } |
| 1296 | | 1440 | |
| ... | @@ -1301,12 +1445,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1301,12 +1445,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1301 | // so load its value onto the stack | 1445 | // so load its value onto the stack |
| 1302 | std.debug.assert(ty.zigTypeTag() == .Pointer); | 1446 | std.debug.assert(ty.zigTypeTag() == .Pointer); |
| 1303 | const operand = self.resolveInst(pl_op.operand); | 1447 | const operand = self.resolveInst(pl_op.operand); |
| 1304 | const offset = switch (operand) { | 1448 | try self.emitWValue(operand); |
| 1305 | .local_with_offset => |with_offset| with_offset.offset, | | |
| 1306 | else => @as(u32, 0), | | |
| 1307 | }; | | |
| 1308 | const result = try self.load(operand, fn_ty, offset); | | |
| 1309 | try self.addLabel(.local_get, result.local); | | |
| 1310 | | 1449 | |
| 1311 | var fn_type = try self.genFunctype(fn_ty); | 1450 | var fn_type = try self.genFunctype(fn_ty); |
| 1312 | defer fn_type.deinit(self.gpa); | 1451 | defer fn_type.deinit(self.gpa); |
| ... | @@ -1318,9 +1457,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1318,9 +1457,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1318 | const ret_ty = fn_ty.fnReturnType(); | 1457 | const ret_ty = fn_ty.fnReturnType(); |
| 1319 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1458 | if (!ret_ty.hasCodeGenBits()) return WValue.none; |
| 1320 | | 1459 | |
| | 1460 | // TODO: Implement this for all aggregate types |
| | 1461 | if (ret_ty.isSlice()) { |
| | 1462 | // first load the values onto the regular stack, before we move the stack pointer |
| | 1463 | // to prevent overwriting the return value. |
| | 1464 | const tmp = try self.allocLocal(ret_ty); |
| | 1465 | try self.addLabel(.local_set, tmp.local); |
| | 1466 | const field_ty = Type.@"usize"; |
| | 1467 | const offset = @intCast(u32, field_ty.abiSize(self.target)); |
| | 1468 | const ptr_local = try self.load(tmp, field_ty, 0); |
| | 1469 | const len_local = try self.load(tmp, field_ty, offset); |
| | 1470 | |
| | 1471 | // As our values are now safe, we reserve space on the virtual stack and |
| | 1472 | // store the values there. |
| | 1473 | const result = try self.allocStack(ret_ty); |
| | 1474 | try self.store(result, ptr_local, field_ty, 0); |
| | 1475 | try self.store(result, len_local, field_ty, offset); |
| | 1476 | return result; |
| | 1477 | } |
| | 1478 | |
| 1321 | const result_local = try self.allocLocal(ret_ty); | 1479 | const result_local = try self.allocLocal(ret_ty); |
| 1322 | try self.addLabel(.local_set, result_local.local); | 1480 | try self.addLabel(.local_set, result_local.local); |
| 1323 | // if the result was allocated on the virtual stack, we must load | | |
| 1324 | return result_local; | 1481 | return result_local; |
| 1325 | } | 1482 | } |
| 1326 | | 1483 | |
| ... | @@ -1331,14 +1488,8 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1331,14 +1488,8 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1331 | if (self.initial_stack_value == .none) { | 1488 | if (self.initial_stack_value == .none) { |
| 1332 | try self.initializeStack(); | 1489 | try self.initializeStack(); |
| 1333 | } | 1490 | } |
| 1334 | | 1491 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1335 | const abi_size = child_type.abiSize(self.target); | 1492 | return self.allocStack(child_type); |
| 1336 | if (abi_size == 0) return WValue{ .none = {} }; | | |
| 1337 | | | |
| 1338 | // local, containing the offset to the stack position | | |
| 1339 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 | | |
| 1340 | try self.moveStack(@intCast(u32, abi_size), local.local); | | |
| 1341 | return local; | | |
| 1342 | } | 1493 | } |
| 1343 | | 1494 | |
| 1344 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1495 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1370,6 +1521,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1370,6 +1521,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1370 | | 1521 | |
| 1371 | switch (rhs) { | 1522 | switch (rhs) { |
| 1372 | .constant => { | 1523 | .constant => { |
| | 1524 | if (rhs.constant.val.castTag(.decl_ref)) |_| { |
| | 1525 | // retrieve values from memory instead |
| | 1526 | const mem_local = try self.allocLocal(Type.usize); |
| | 1527 | try self.emitWValue(rhs); |
| | 1528 | try self.addLabel(.local_set, mem_local.local); |
| | 1529 | try self.store(lhs, mem_local, ty, 0); |
| | 1530 | return; |
| | 1531 | } |
| 1373 | // constant will contain both tag and payload, | 1532 | // constant will contain both tag and payload, |
| 1374 | // so save those in 2 temporary locals before storing them | 1533 | // so save those in 2 temporary locals before storing them |
| 1375 | // in memory | 1534 | // in memory |
| ... | @@ -1396,6 +1555,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1396,6 +1555,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1396 | return try self.store(lhs, tag_local, tag_ty, 0); | 1555 | return try self.store(lhs, tag_local, tag_ty, 0); |
| 1397 | }, | 1556 | }, |
| 1398 | .local_with_offset => |with_offset| { | 1557 | .local_with_offset => |with_offset| { |
| | 1558 | // check if we're storing the payload, or the error |
| | 1559 | if (with_offset.offset == 0) { |
| | 1560 | try self.store(lhs, .{ .local = with_offset.local }, tag_ty, 0); |
| | 1561 | return; |
| | 1562 | } |
| 1399 | const tag_local = try self.allocLocal(tag_ty); | 1563 | const tag_local = try self.allocLocal(tag_ty); |
| 1400 | try self.addImm32(0); | 1564 | try self.addImm32(0); |
| 1401 | try self.addLabel(.local_set, tag_local.local); | 1565 | try self.addLabel(.local_set, tag_local.local); |
| ... | @@ -1412,30 +1576,43 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1412,30 +1576,43 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1412 | } | 1576 | } |
| 1413 | }, | 1577 | }, |
| 1414 | .Struct => { | 1578 | .Struct => { |
| 1415 | // we are copying a struct with its fields. | 1579 | if (rhs == .constant) { |
| 1416 | // Replace this with a wasm memcpy instruction once we support that feature. | 1580 | try self.emitWValue(rhs); |
| 1417 | const fields_len = ty.structFieldCount(); | 1581 | try self.addLabel(.local_set, lhs.local); |
| 1418 | var index: usize = 0; | 1582 | return; |
| 1419 | while (index < fields_len) : (index += 1) { | | |
| 1420 | const field_ty = ty.structFieldType(index); | | |
| 1421 | if (!field_ty.hasCodeGenBits()) continue; | | |
| 1422 | const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch { | | |
| 1423 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); | | |
| 1424 | }; | | |
| 1425 | const field_local = try self.load(rhs, field_ty, field_offset); | | |
| 1426 | try self.store(lhs, field_local, field_ty, field_offset); | | |
| 1427 | } | 1583 | } |
| 1428 | return; | 1584 | return try self.memCopy(ty, lhs, rhs); |
| 1429 | }, | 1585 | }, |
| 1430 | .Pointer => { | 1586 | .Pointer => { |
| 1431 | if (ty.isSlice() and rhs == .constant) { | 1587 | if (ty.isSlice() and rhs == .constant) { |
| 1432 | try self.emitWValue(rhs); | 1588 | try self.emitWValue(rhs); |
| | 1589 | |
| | 1590 | const val = rhs.constant.val; |
| 1433 | const len_local = try self.allocLocal(Type.usize); | 1591 | const len_local = try self.allocLocal(Type.usize); |
| 1434 | const ptr_local = try self.allocLocal(Type.usize); | 1592 | const ptr_local = try self.allocLocal(Type.usize); |
| | 1593 | const len_offset = self.ptrSize(); |
| | 1594 | if (val.castTag(.decl_ref)) |decl| { |
| | 1595 | // for decl references we also need to retrieve the length and the original decl's pointer |
| | 1596 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = Type.@"usize".abiAlignment(self.target) }); |
| | 1597 | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); |
| | 1598 | try self.addMemArg( |
| | 1599 | .i32_load, |
| | 1600 | .{ .offset = len_offset, .alignment = Type.@"usize".abiAlignment(self.target) }, |
| | 1601 | ); |
| | 1602 | } |
| 1435 | try self.addLabel(.local_set, len_local.local); | 1603 | try self.addLabel(.local_set, len_local.local); |
| 1436 | try self.addLabel(.local_set, ptr_local.local); | 1604 | try self.addLabel(.local_set, ptr_local.local); |
| 1437 | try self.store(lhs, ptr_local, Type.usize, 0); | 1605 | try self.store(lhs, ptr_local, Type.usize, 0); |
| 1438 | try self.store(lhs, len_local, Type.usize, self.target.cpu.arch.ptrBitWidth() / 8); | 1606 | try self.store(lhs, len_local, Type.usize, len_offset); |
| | 1607 | return; |
| | 1608 | } else if (ty.isSlice()) { |
| | 1609 | // store pointer first |
| | 1610 | const ptr_local = try self.load(rhs, Type.@"usize", 0); |
| | 1611 | try self.store(lhs, ptr_local, Type.@"usize", 0); |
| | 1612 | |
| | 1613 | // retrieve length from rhs, and store that alongside lhs as well |
| | 1614 | const len_local = try self.load(rhs, Type.@"usize", 4); |
| | 1615 | try self.store(lhs, len_local, Type.@"usize", 4); |
| 1439 | return; | 1616 | return; |
| 1440 | } | 1617 | } |
| 1441 | }, | 1618 | }, |
| ... | @@ -1447,12 +1624,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1447,12 +1624,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1447 | // check if we should pass by pointer or value based on ABI size | 1624 | // check if we should pass by pointer or value based on ABI size |
| 1448 | // TODO: Implement a way to get ABI values from a given type, | 1625 | // TODO: Implement a way to get ABI values from a given type, |
| 1449 | // that is portable across the backend, rather than copying logic. | 1626 | // that is portable across the backend, rather than copying logic. |
| 1450 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) | 1627 | const abi_size = switch (ty.zigTypeTag()) { |
| 1451 | @intCast(u8, ty.abiSize(self.target)) | 1628 | .Int, |
| 1452 | else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum) | 1629 | .Float, |
| 1453 | @intCast(u8, ty.abiSize(self.target)) | 1630 | .ErrorSet, |
| 1454 | else | 1631 | .Enum, |
| 1455 | @as(u8, 4); | 1632 | .Bool, |
| | 1633 | .ErrorUnion, |
| | 1634 | => @intCast(u8, ty.abiSize(self.target)), |
| | 1635 | else => @as(u8, 4), |
| | 1636 | }; |
| 1456 | const opcode = buildOpcode(.{ | 1637 | const opcode = buildOpcode(.{ |
| 1457 | .valtype1 = valtype, | 1638 | .valtype1 = valtype, |
| 1458 | .width = abi_size * 8, // use bitsize instead of byte size | 1639 | .width = abi_size * 8, // use bitsize instead of byte size |
| ... | @@ -1460,14 +1641,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1460,14 +1641,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1460 | }); | 1641 | }); |
| 1461 | | 1642 | |
| 1462 | // store rhs value at stack pointer's location in memory | 1643 | // store rhs value at stack pointer's location in memory |
| 1463 | const mem_arg_index = try self.addExtra(Mir.MemArg{ | 1644 | try self.addMemArg( |
| 1464 | .offset = offset, | 1645 | Mir.Inst.Tag.fromOpcode(opcode), |
| 1465 | .alignment = ty.abiAlignment(self.target), | 1646 | .{ .offset = offset, .alignment = ty.abiAlignment(self.target) }, |
| 1466 | }); | 1647 | ); |
| 1467 | try self.addInst(.{ | | |
| 1468 | .tag = Mir.Inst.Tag.fromOpcode(opcode), | | |
| 1469 | .data = .{ .payload = mem_arg_index }, | | |
| 1470 | }); | | |
| 1471 | } | 1648 | } |
| 1472 | | 1649 | |
| 1473 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1650 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1477,12 +1654,15 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1477,12 +1654,15 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1477 | | 1654 | |
| 1478 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; | 1655 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| 1479 | | 1656 | |
| 1480 | return switch (ty.zigTypeTag()) { | 1657 | if (isByRef(ty)) { |
| 1481 | .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer | 1658 | const new_local = try self.allocStack(ty); |
| 1482 | else => switch (operand) { | 1659 | try self.store(new_local, operand, ty, 0); |
| 1483 | .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset), | 1660 | return new_local; |
| 1484 | else => try self.load(operand, ty, 0), | 1661 | } |
| 1485 | }, | 1662 | |
| | 1663 | return switch (operand) { |
| | 1664 | .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset), |
| | 1665 | else => try self.load(operand, ty, 0), |
| 1486 | }; | 1666 | }; |
| 1487 | } | 1667 | } |
| 1488 | | 1668 | |
| ... | @@ -1494,15 +1674,18 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1494,15 +1674,18 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1494 | .unsigned | 1674 | .unsigned |
| 1495 | else | 1675 | else |
| 1496 | .signed; | 1676 | .signed; |
| 1497 | // check if we should pass by pointer or value based on ABI size | | |
| 1498 | // TODO: Implement a way to get ABI values from a given type, | 1677 | // TODO: Implement a way to get ABI values from a given type, |
| 1499 | // that is portable across the backend, rather than copying logic. | 1678 | // that is portable across the backend, rather than copying logic. |
| 1500 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) | 1679 | const abi_size = switch (ty.zigTypeTag()) { |
| 1501 | @intCast(u8, ty.abiSize(self.target)) | 1680 | .Int, |
| 1502 | else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum) | 1681 | .Float, |
| 1503 | @intCast(u8, ty.abiSize(self.target)) | 1682 | .ErrorSet, |
| 1504 | else | 1683 | .Enum, |
| 1505 | @as(u8, 4); | 1684 | .Bool, |
| | 1685 | .ErrorUnion, |
| | 1686 | => @intCast(u8, ty.abiSize(self.target)), |
| | 1687 | else => @as(u8, 4), |
| | 1688 | }; |
| 1506 | | 1689 | |
| 1507 | const opcode = buildOpcode(.{ | 1690 | const opcode = buildOpcode(.{ |
| 1508 | .valtype1 = try self.typeToValtype(ty), | 1691 | .valtype1 = try self.typeToValtype(ty), |
| ... | @@ -1511,14 +1694,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1511,14 +1694,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1511 | .signedness = signedness, | 1694 | .signedness = signedness, |
| 1512 | }); | 1695 | }); |
| 1513 | | 1696 | |
| 1514 | const mem_arg_index = try self.addExtra(Mir.MemArg{ | 1697 | try self.addMemArg( |
| 1515 | .offset = offset, | 1698 | Mir.Inst.Tag.fromOpcode(opcode), |
| 1516 | .alignment = ty.abiAlignment(self.target), | 1699 | .{ .offset = offset, .alignment = ty.abiAlignment(self.target) }, |
| 1517 | }); | 1700 | ); |
| 1518 | try self.addInst(.{ | | |
| 1519 | .tag = Mir.Inst.Tag.fromOpcode(opcode), | | |
| 1520 | .data = .{ .payload = mem_arg_index }, | | |
| 1521 | }); | | |
| 1522 | | 1701 | |
| 1523 | // store the result in a local | 1702 | // store the result in a local |
| 1524 | const result = try self.allocLocal(ty); | 1703 | const result = try self.allocLocal(ty); |
| ... | @@ -1647,6 +1826,12 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1647,6 +1826,12 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1647 | } else { | 1826 | } else { |
| 1648 | try self.addLabel(.memory_address, decl.link.wasm.sym_index); | 1827 | try self.addLabel(.memory_address, decl.link.wasm.sym_index); |
| 1649 | } | 1828 | } |
| | 1829 | } else if (val.castTag(.int_u64)) |int_ptr| { |
| | 1830 | try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data))); |
| | 1831 | } else if (val.tag() == .zero) { |
| | 1832 | try self.addImm32(0); |
| | 1833 | } else if (val.tag() == .one) { |
| | 1834 | try self.addImm32(1); |
| 1650 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); | 1835 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); |
| 1651 | }, | 1836 | }, |
| 1652 | .Void => {}, | 1837 | .Void => {}, |
| ... | @@ -1706,10 +1891,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1706,10 +1891,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1706 | | 1891 | |
| 1707 | // When constant has value 'null', set is_null local to '1' | 1892 | // When constant has value 'null', set is_null local to '1' |
| 1708 | // and payload to '0' | 1893 | // and payload to '0' |
| 1709 | if (val.castTag(.opt_payload)) |pl| { | 1894 | if (val.castTag(.opt_payload)) |payload| { |
| 1710 | const payload_val = pl.data; | | |
| 1711 | try self.addImm32(0); | 1895 | try self.addImm32(0); |
| 1712 | try self.emitConstant(payload_val, payload_type); | 1896 | if (payload_type.hasCodeGenBits()) |
| | 1897 | try self.emitConstant(payload.data, payload_type); |
| 1713 | } else { | 1898 | } else { |
| 1714 | // set null-tag | 1899 | // set null-tag |
| 1715 | try self.addImm32(1); | 1900 | try self.addImm32(1); |
| ... | @@ -1717,6 +1902,20 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1717,6 +1902,20 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1717 | try self.addImm32(0); | 1902 | try self.addImm32(0); |
| 1718 | } | 1903 | } |
| 1719 | }, | 1904 | }, |
| | 1905 | .Struct => { |
| | 1906 | const struct_data = val.castTag(.@"struct").?; |
| | 1907 | // in case of structs, we reserve stack space and store it there. |
| | 1908 | const result = try self.allocStack(ty); |
| | 1909 | |
| | 1910 | const fields = ty.structFields(); |
| | 1911 | for (fields.values()) |field, index| { |
| | 1912 | const tmp = try self.allocLocal(field.ty); |
| | 1913 | try self.emitConstant(struct_data.data[index], field.ty); |
| | 1914 | try self.addLabel(.local_set, tmp.local); |
| | 1915 | try self.store(result, tmp, field.ty, field.offset); |
| | 1916 | } |
| | 1917 | try self.addLabel(.local_get, result.local); |
| | 1918 | }, |
| 1720 | else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), | 1919 | else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), |
| 1721 | } | 1920 | } |
| 1722 | } | 1921 | } |
| ... | @@ -1733,6 +1932,14 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { | ... | @@ -1733,6 +1932,14 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { |
| 1733 | 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))), | 1932 | 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))), |
| 1734 | else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}), | 1933 | else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}), |
| 1735 | }, | 1934 | }, |
| | 1935 | // As arrays point to linear memory, we cannot use 0xaaaaaaaa as the wasm |
| | 1936 | // validator will not accept it due to out-of-bounds memory access); |
| | 1937 | .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaa))), |
| | 1938 | .Struct => { |
| | 1939 | // TODO: Write 0xaa to each field |
| | 1940 | const result = try self.allocStack(ty); |
| | 1941 | try self.addLabel(.local_get, result.local); |
| | 1942 | }, |
| 1736 | else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}), | 1943 | else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}), |
| 1737 | } | 1944 | } |
| 1738 | } | 1945 | } |
| ... | @@ -1946,7 +2153,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1946,7 +2153,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1946 | | 2153 | |
| 1947 | fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2154 | fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1948 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2155 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1949 | return self.resolveInst(ty_op.operand); | 2156 | const operand = self.resolveInst(ty_op.operand); |
| | 2157 | if (operand == .constant) { |
| | 2158 | const result = try self.allocLocal(self.air.typeOfIndex(inst)); |
| | 2159 | try self.emitWValue(operand); |
| | 2160 | try self.addLabel(.local_set, result.local); |
| | 2161 | return result; |
| | 2162 | } |
| | 2163 | return operand; |
| 1950 | } | 2164 | } |
| 1951 | | 2165 | |
| 1952 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2166 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1966,16 +2180,26 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr | ... | @@ -1966,16 +2180,26 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr |
| 1966 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2180 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1967 | const struct_ptr = self.resolveInst(ty_op.operand); | 2181 | const struct_ptr = self.resolveInst(ty_op.operand); |
| 1968 | const struct_ty = self.air.typeOf(ty_op.operand).childType(); | 2182 | const struct_ty = self.air.typeOf(ty_op.operand).childType(); |
| | 2183 | const field_ty = struct_ty.structFieldType(index); |
| 1969 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { | 2184 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { |
| 1970 | return self.fail("Field type '{}' too big to fit into stack frame", .{ | 2185 | return self.fail("Field type '{}' too big to fit into stack frame", .{ |
| 1971 | struct_ty.structFieldType(index), | 2186 | field_ty, |
| 1972 | }); | 2187 | }); |
| 1973 | }; | 2188 | }; |
| 1974 | return structFieldPtr(struct_ptr, offset); | 2189 | return structFieldPtr(struct_ptr, offset); |
| 1975 | } | 2190 | } |
| 1976 | | 2191 | |
| 1977 | fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { | 2192 | fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 1978 | return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } }; | 2193 | var final_offset = offset; |
| | 2194 | const local = switch (struct_ptr) { |
| | 2195 | .local => |local| local, |
| | 2196 | .local_with_offset => |with_offset| blk: { |
| | 2197 | final_offset += with_offset.offset; |
| | 2198 | break :blk with_offset.local; |
| | 2199 | }, |
| | 2200 | else => unreachable, |
| | 2201 | }; |
| | 2202 | return WValue{ .local_with_offset = .{ .local = local, .offset = final_offset } }; |
| 1979 | } | 2203 | } |
| 1980 | | 2204 | |
| 1981 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2205 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1991,7 +2215,15 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1991,7 +2215,15 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1991 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { | 2215 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { |
| 1992 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); | 2216 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); |
| 1993 | }; | 2217 | }; |
| 1994 | return try self.load(operand, field_ty, offset); | 2218 | |
| | 2219 | if (isByRef(field_ty)) { |
| | 2220 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } }; |
| | 2221 | } |
| | 2222 | |
| | 2223 | switch (operand) { |
| | 2224 | .local_with_offset => |with_offset| return try self.load(operand, field_ty, offset + with_offset.offset), |
| | 2225 | else => return try self.load(operand, field_ty, offset), |
| | 2226 | } |
| 1995 | } | 2227 | } |
| 1996 | | 2228 | |
| 1997 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2229 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2145,29 +2377,29 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2145,29 +2377,29 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2145 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { | 2377 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| 2146 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2378 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2147 | const operand = self.resolveInst(un_op); | 2379 | const operand = self.resolveInst(un_op); |
| 2148 | const err_ty = self.air.typeOf(un_op).errorUnionSet(); | 2380 | const err_ty = self.air.typeOf(un_op); |
| | 2381 | const pl_ty = err_ty.errorUnionPayload(); |
| 2149 | | 2382 | |
| 2150 | // load the error tag value | 2383 | // load the error tag value |
| 2151 | try self.emitWValue(operand); | 2384 | try self.emitWValue(operand); |
| 2152 | const mem_arg_index = try self.addExtra(Mir.MemArg{ | 2385 | if (pl_ty.hasCodeGenBits()) { |
| 2153 | .offset = 0, | 2386 | try self.addMemArg(.i32_load16_u, .{ |
| 2154 | .alignment = err_ty.abiAlignment(self.target), | 2387 | .offset = 0, |
| 2155 | }); | 2388 | .alignment = err_ty.errorUnionSet().abiAlignment(self.target), |
| 2156 | try self.addInst(.{ | 2389 | }); |
| 2157 | .tag = .i32_load16_u, | 2390 | } |
| 2158 | .data = .{ .payload = mem_arg_index }, | | |
| 2159 | }); | | |
| 2160 | | 2391 | |
| 2161 | // Compare the error value with '0' | 2392 | // Compare the error value with '0' |
| 2162 | try self.addImm32(0); | 2393 | try self.addImm32(0); |
| 2163 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2394 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2164 | | 2395 | |
| 2165 | const is_err_tmp = try self.allocLocal(err_ty); | 2396 | const is_err_tmp = try self.allocLocal(Type.initTag(.i32)); // result is always an i32 |
| 2166 | try self.addLabel(.local_set, is_err_tmp.local); | 2397 | try self.addLabel(.local_set, is_err_tmp.local); |
| 2167 | return is_err_tmp; | 2398 | return is_err_tmp; |
| 2168 | } | 2399 | } |
| 2169 | | 2400 | |
| 2170 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2401 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2402 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 2171 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2403 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2172 | const operand = self.resolveInst(ty_op.operand); | 2404 | const operand = self.resolveInst(ty_op.operand); |
| 2173 | const err_ty = self.air.typeOf(ty_op.operand); | 2405 | const err_ty = self.air.typeOf(ty_op.operand); |
| ... | @@ -2183,6 +2415,11 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2183,6 +2415,11 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2183 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2415 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2184 | const operand = self.resolveInst(ty_op.operand); | 2416 | const operand = self.resolveInst(ty_op.operand); |
| 2185 | const err_ty = self.air.typeOf(ty_op.operand); | 2417 | const err_ty = self.air.typeOf(ty_op.operand); |
| | 2418 | const payload_ty = err_ty.errorUnionPayload(); |
| | 2419 | if (!payload_ty.hasCodeGenBits()) { |
| | 2420 | return operand; |
| | 2421 | } |
| | 2422 | |
| 2186 | return try self.load(operand, err_ty.errorUnionSet(), 0); | 2423 | return try self.load(operand, err_ty.errorUnionSet(), 0); |
| 2187 | } | 2424 | } |
| 2188 | | 2425 | |
| ... | @@ -2192,20 +2429,43 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2192,20 +2429,43 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2192 | const operand = self.resolveInst(ty_op.operand); | 2429 | const operand = self.resolveInst(ty_op.operand); |
| 2193 | | 2430 | |
| 2194 | const op_ty = self.air.typeOf(ty_op.operand); | 2431 | const op_ty = self.air.typeOf(ty_op.operand); |
| 2195 | if (!op_ty.hasCodeGenBits()) return WValue.none; | 2432 | if (!op_ty.hasCodeGenBits()) return operand; |
| 2196 | const err_ty = self.air.getRefType(ty_op.ty); | 2433 | const err_ty = self.air.getRefType(ty_op.ty); |
| 2197 | const offset = err_ty.errorUnionSet().abiSize(self.target); | 2434 | const offset = err_ty.errorUnionSet().abiSize(self.target); |
| 2198 | | 2435 | |
| 2199 | return WValue{ .local_with_offset = .{ | 2436 | const err_union = try self.allocStack(err_ty); |
| 2200 | .local = operand.local, | 2437 | const to_store = switch (op_ty.zigTypeTag()) { |
| 2201 | .offset = @intCast(u32, offset), | 2438 | // for those types we must load the pointer and then store |
| 2202 | } }; | 2439 | // its value |
| | 2440 | .Pointer, .Optional => blk: { |
| | 2441 | if (!op_ty.isPtrLikeOptional()) { |
| | 2442 | return self.fail("TODO: airWrapErrUnionPayload for optional type {}", .{op_ty}); |
| | 2443 | } |
| | 2444 | break :blk try self.load(operand, op_ty, 0); |
| | 2445 | }, |
| | 2446 | .Int => operand, |
| | 2447 | else => return self.fail("TODO: airWrapErrUnionPayload for type {}", .{op_ty}), |
| | 2448 | }; |
| | 2449 | |
| | 2450 | try self.store(err_union, to_store, op_ty, @intCast(u32, offset)); |
| | 2451 | |
| | 2452 | // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. |
| | 2453 | const tmp_local = try self.allocLocal(err_ty.errorUnionSet()); // locals are '0' by default. |
| | 2454 | try self.store(err_union, tmp_local, err_ty.errorUnionSet(), 0); |
| | 2455 | |
| | 2456 | return err_union; |
| 2203 | } | 2457 | } |
| 2204 | | 2458 | |
| 2205 | fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2459 | fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2206 | if (self.liveness.isUnused(inst)) return WValue.none; | 2460 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 2207 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2461 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2208 | return self.resolveInst(ty_op.operand); | 2462 | const operand = self.resolveInst(ty_op.operand); |
| | 2463 | const err_ty = self.air.getRefType(ty_op.ty); |
| | 2464 | |
| | 2465 | const err_union = try self.allocStack(err_ty); |
| | 2466 | // TODO: Also write 'undefined' to the payload |
| | 2467 | try self.store(err_union, operand, err_ty.errorUnionSet(), 0); |
| | 2468 | return err_union; |
| 2209 | } | 2469 | } |
| 2210 | | 2470 | |
| 2211 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2471 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2243,13 +2503,11 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! | ... | @@ -2243,13 +2503,11 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! |
| 2243 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2503 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2244 | const operand = self.resolveInst(un_op); | 2504 | const operand = self.resolveInst(un_op); |
| 2245 | | 2505 | |
| 2246 | // load the null tag value | 2506 | const op_ty = self.air.typeOf(un_op); |
| 2247 | try self.emitWValue(operand); | 2507 | try self.emitWValue(operand); |
| 2248 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 }); | 2508 | if (!op_ty.isPtrLikeOptional()) { |
| 2249 | try self.addInst(.{ | 2509 | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); |
| 2250 | .tag = .i32_load8_u, | 2510 | } |
| 2251 | .data = .{ .payload = mem_arg_index }, | | |
| 2252 | }); | | |
| 2253 | | 2511 | |
| 2254 | // Compare the error value with '0' | 2512 | // Compare the error value with '0' |
| 2255 | try self.addImm32(0); | 2513 | try self.addImm32(0); |
| ... | @@ -2306,9 +2564,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2306,9 +2564,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2306 | | 2564 | |
| 2307 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2565 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2308 | const operand = self.resolveInst(ty_op.operand); | 2566 | const operand = self.resolveInst(ty_op.operand); |
| 2309 | const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8; | | |
| 2310 | | 2567 | |
| 2311 | return try self.load(operand, Type.usize, pointer_width); | 2568 | return try self.load(operand, Type.usize, self.ptrSize()); |
| 2312 | } | 2569 | } |
| 2313 | | 2570 | |
| 2314 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2571 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2333,12 +2590,36 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2333,12 +2590,36 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2333 | | 2590 | |
| 2334 | const result = try self.allocLocal(elem_ty); | 2591 | const result = try self.allocLocal(elem_ty); |
| 2335 | try self.addLabel(.local_set, result.local); | 2592 | try self.addLabel(.local_set, result.local); |
| 2336 | return switch (elem_ty.zigTypeTag()) { | 2593 | |
| 2337 | // pass as pointer | 2594 | if (isByRef(elem_ty)) { |
| 2338 | .Pointer, .Struct, .Optional => result, | 2595 | return result; |
| 2339 | // pass by value | 2596 | } |
| 2340 | else => try self.load(result, elem_ty, 0), | 2597 | return try self.load(result, elem_ty, 0); |
| 2341 | }; | 2598 | } |
| | 2599 | |
| | 2600 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2601 | if (self.liveness.isUnused(inst)) return WValue.none; |
| | 2602 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 2603 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 2604 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| | 2605 | const elem_ty = self.air.getRefType(ty_pl.ty).childType(); |
| | 2606 | const elem_size = elem_ty.abiSize(self.target); |
| | 2607 | |
| | 2608 | const slice = self.resolveInst(bin_op.lhs); |
| | 2609 | const index = self.resolveInst(bin_op.rhs); |
| | 2610 | |
| | 2611 | const slice_ptr = try self.load(slice, slice_ty, 0); |
| | 2612 | try self.addLabel(.local_get, slice_ptr.local); |
| | 2613 | |
| | 2614 | // calculate index into slice |
| | 2615 | try self.emitWValue(index); |
| | 2616 | try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); |
| | 2617 | try self.addTag(.i32_mul); |
| | 2618 | try self.addTag(.i32_add); |
| | 2619 | |
| | 2620 | const result = try self.allocLocal(Type.initTag(.i32)); |
| | 2621 | try self.addLabel(.local_set, result.local); |
| | 2622 | return result; |
| 2342 | } | 2623 | } |
| 2343 | | 2624 | |
| 2344 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2625 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2407,3 +2688,124 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2407,3 +2688,124 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2407 | try self.addLabel(.local_set, result.local); | 2688 | try self.addLabel(.local_set, result.local); |
| 2408 | return result; | 2689 | return result; |
| 2409 | } | 2690 | } |
| | 2691 | |
| | 2692 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2693 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| | 2694 | return self.resolveInst(un_op); |
| | 2695 | } |
| | 2696 | |
| | 2697 | fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2698 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 2699 | const operand = self.resolveInst(ty_op.operand); |
| | 2700 | const array_ty = self.air.typeOf(ty_op.operand).childType(); |
| | 2701 | const ty = Type.@"usize"; |
| | 2702 | const ptr_width = @intCast(u32, ty.abiSize(self.target)); |
| | 2703 | const slice_ty = self.air.getRefType(ty_op.ty); |
| | 2704 | |
| | 2705 | // create a slice on the stack |
| | 2706 | const slice_local = try self.allocStack(slice_ty); |
| | 2707 | |
| | 2708 | // store the array ptr in the slice |
| | 2709 | if (array_ty.hasCodeGenBits()) { |
| | 2710 | try self.store(slice_local, operand, ty, 0); |
| | 2711 | } |
| | 2712 | |
| | 2713 | // store the length of the array in the slice |
| | 2714 | const len = array_ty.arrayLen(); |
| | 2715 | try self.addImm32(@bitCast(i32, @intCast(u32, len))); |
| | 2716 | const len_local = try self.allocLocal(ty); |
| | 2717 | try self.addLabel(.local_set, len_local.local); |
| | 2718 | try self.store(slice_local, len_local, ty, ptr_width); |
| | 2719 | |
| | 2720 | return slice_local; |
| | 2721 | } |
| | 2722 | |
| | 2723 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2724 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2725 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| | 2726 | return self.resolveInst(un_op); |
| | 2727 | } |
| | 2728 | |
| | 2729 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2730 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2731 | |
| | 2732 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 2733 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| | 2734 | const pointer = self.resolveInst(bin_op.lhs); |
| | 2735 | const index = self.resolveInst(bin_op.rhs); |
| | 2736 | const elem_ty = ptr_ty.childType(); |
| | 2737 | const elem_size = elem_ty.abiSize(self.target); |
| | 2738 | |
| | 2739 | // load pointer onto the stack |
| | 2740 | if (ptr_ty.isSlice()) { |
| | 2741 | const ptr_local = try self.load(pointer, ptr_ty, 0); |
| | 2742 | try self.addLabel(.local_get, ptr_local.local); |
| | 2743 | } else { |
| | 2744 | try self.emitWValue(pointer); |
| | 2745 | } |
| | 2746 | |
| | 2747 | // calculate index into slice |
| | 2748 | try self.emitWValue(index); |
| | 2749 | try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); |
| | 2750 | try self.addTag(.i32_mul); |
| | 2751 | try self.addTag(.i32_add); |
| | 2752 | |
| | 2753 | const result = try self.allocLocal(elem_ty); |
| | 2754 | try self.addLabel(.local_set, result.local); |
| | 2755 | if (isByRef(elem_ty)) { |
| | 2756 | return result; |
| | 2757 | } |
| | 2758 | return try self.load(result, elem_ty, 0); |
| | 2759 | } |
| | 2760 | |
| | 2761 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2762 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2763 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 2764 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 2765 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| | 2766 | const elem_ty = self.air.getRefType(ty_pl.ty).childType(); |
| | 2767 | const elem_size = elem_ty.abiSize(self.target); |
| | 2768 | |
| | 2769 | const ptr = self.resolveInst(bin_op.lhs); |
| | 2770 | const index = self.resolveInst(bin_op.rhs); |
| | 2771 | |
| | 2772 | // load pointer onto the stack |
| | 2773 | if (ptr_ty.isSlice()) { |
| | 2774 | const ptr_local = try self.load(ptr, ptr_ty, 0); |
| | 2775 | try self.addLabel(.local_get, ptr_local.local); |
| | 2776 | } else { |
| | 2777 | try self.emitWValue(ptr); |
| | 2778 | } |
| | 2779 | |
| | 2780 | // calculate index into ptr |
| | 2781 | try self.emitWValue(index); |
| | 2782 | try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); |
| | 2783 | try self.addTag(.i32_mul); |
| | 2784 | try self.addTag(.i32_add); |
| | 2785 | |
| | 2786 | const result = try self.allocLocal(Type.initTag(.i32)); |
| | 2787 | try self.addLabel(.local_set, result.local); |
| | 2788 | return result; |
| | 2789 | } |
| | 2790 | |
| | 2791 | fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| | 2792 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2793 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 2794 | const ptr = self.resolveInst(bin_op.lhs); |
| | 2795 | const offset = self.resolveInst(bin_op.rhs); |
| | 2796 | const pointee_ty = self.air.typeOf(bin_op.lhs).childType(); |
| | 2797 | |
| | 2798 | const valtype = try self.typeToValtype(Type.usize); |
| | 2799 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); |
| | 2800 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); |
| | 2801 | |
| | 2802 | try self.emitWValue(ptr); |
| | 2803 | try self.emitWValue(offset); |
| | 2804 | try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target)))); |
| | 2805 | try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); |
| | 2806 | try self.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode)); |
| | 2807 | |
| | 2808 | const result = try self.allocLocal(Type.usize); |
| | 2809 | try self.addLabel(.local_set, result.local); |
| | 2810 | return result; |
| | 2811 | } |