| ... | @@ -1088,10 +1088,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1088,10 +1088,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1088 | } | 1088 | } |
| 1089 | | 1089 | |
| 1090 | const ret_ty = fn_ty.fnReturnType(); | 1090 | const ret_ty = fn_ty.fnReturnType(); |
| 1091 | switch (ret_ty.zigTypeTag()) { | 1091 | if (isByRef(ret_ty)) { |
| 1092 | .ErrorUnion, .Optional, .Pointer => result.return_value = try self.allocLocal(Type.initTag(.i32)), | 1092 | result.return_value = try self.allocLocal(Type.initTag(.i32)); |
| 1093 | .Int, .Float, .Bool, .Void, .NoReturn => {}, | | |
| 1094 | else => return self.fail("TODO: Implement function return type {}", .{ret_ty}), | | |
| 1095 | } | 1093 | } |
| 1096 | | 1094 | |
| 1097 | // 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 |
| ... | @@ -1204,6 +1202,60 @@ fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void { | ... | @@ -1204,6 +1202,60 @@ fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void { |
| 1204 | } | 1202 | } |
| 1205 | } | 1203 | } |
| 1206 | | 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 | |
| 1207 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | 1259 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1208 | const air_tags = self.air.instructions.items(.tag); | 1260 | const air_tags = self.air.instructions.items(.tag); |
| 1209 | return switch (air_tags[inst]) { | 1261 | return switch (air_tags[inst]) { |
| ... | @@ -1255,6 +1307,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1255,6 +1307,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1255 | .optional_payload => self.airOptionalPayload(inst), | 1307 | .optional_payload => self.airOptionalPayload(inst), |
| 1256 | .optional_payload_ptr => self.airOptionalPayload(inst), | 1308 | .optional_payload_ptr => self.airOptionalPayload(inst), |
| 1257 | .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), |
| 1258 | .ptrtoint => self.airPtrToInt(inst), | 1314 | .ptrtoint => self.airPtrToInt(inst), |
| 1259 | .ret => self.airRet(inst), | 1315 | .ret => self.airRet(inst), |
| 1260 | .ret_ptr => self.airRetPtr(inst), | 1316 | .ret_ptr => self.airRetPtr(inst), |
| ... | @@ -1366,38 +1422,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1366,38 +1422,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1366 | | 1422 | |
| 1367 | const arg_ty = self.air.typeOf(arg_ref); | 1423 | const arg_ty = self.air.typeOf(arg_ref); |
| 1368 | if (!arg_ty.hasCodeGenBits()) continue; | 1424 | if (!arg_ty.hasCodeGenBits()) continue; |
| 1369 | // Passing constant function pointers must be turned into a stack pointer first. | 1425 | |
| 1370 | // This is because function pointers are stored as function table indexes, | 1426 | // If we need to pass by reference, but the argument is a constant, |
| 1371 | // Which means we would try to attempt to load a function pointer's value by reading | 1427 | // we must first lower it before passing it. |
| 1372 | // from the table index, rather than an address. | 1428 | if (isByRef(arg_ty) and arg_val == .constant) { |
| 1373 | var is_fn_ptr = false; | 1429 | const arg_local = try self.allocStack(arg_ty); |
| 1374 | if (arg_val == .constant) { | 1430 | try self.store(arg_local, arg_val, arg_ty, 0); |
| 1375 | if (arg_val.constant.val.castTag(.decl_ref)) |decl| { | 1431 | try self.emitWValue(arg_local); |
| 1376 | if (decl.data.ty.zigTypeTag() == .Fn) { | 1432 | } else if (arg_val == .none) { |
| 1377 | is_fn_ptr = true; | 1433 | // TODO: Remove this branch when zero-sized pointers do not generate |
| 1378 | } | 1434 | // an argument. |
| 1379 | } | 1435 | try self.addImm32(0); |
| 1380 | } | 1436 | } else { |
| 1381 | switch (arg_ty.zigTypeTag()) { | 1437 | try self.emitWValue(arg_val); |
| 1382 | .Struct, .Pointer, .Optional, .ErrorUnion => { | | |
| 1383 | // single pointer can be passed directly | | |
| 1384 | if ((arg_ty.isSinglePointer() and !is_fn_ptr) or arg_val != .constant) { | | |
| 1385 | if (arg_val == .none) { | | |
| 1386 | // when the argument is a 0-sized value, but the function | | |
| 1387 | // expects a non-zero typed value (such as a slice), we must emit an argument | | |
| 1388 | // as function calls are verified with the function signature in wasm. | | |
| 1389 | // In those cases we will emit a '0xaa' as address, meaning invalid memory. | | |
| 1390 | try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))); | | |
| 1391 | continue; | | |
| 1392 | } | | |
| 1393 | try self.emitWValue(arg_val); | | |
| 1394 | continue; | | |
| 1395 | } | | |
| 1396 | const arg_local = try self.allocStack(arg_ty); | | |
| 1397 | try self.store(arg_local, arg_val, arg_ty, 0); | | |
| 1398 | try self.emitWValue(arg_local); | | |
| 1399 | }, | | |
| 1400 | else => try self.emitWValue(arg_val), | | |
| 1401 | } | 1438 | } |
| 1402 | } | 1439 | } |
| 1403 | | 1440 | |
| ... | @@ -1408,12 +1445,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1408,12 +1445,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1408 | // so load its value onto the stack | 1445 | // so load its value onto the stack |
| 1409 | std.debug.assert(ty.zigTypeTag() == .Pointer); | 1446 | std.debug.assert(ty.zigTypeTag() == .Pointer); |
| 1410 | const operand = self.resolveInst(pl_op.operand); | 1447 | const operand = self.resolveInst(pl_op.operand); |
| 1411 | const offset = switch (operand) { | 1448 | try self.emitWValue(operand); |
| 1412 | .local_with_offset => |with_offset| with_offset.offset, | | |
| 1413 | else => @as(u32, 0), | | |
| 1414 | }; | | |
| 1415 | const result = try self.load(operand, fn_ty, offset); | | |
| 1416 | try self.addLabel(.local_get, result.local); | | |
| 1417 | | 1449 | |
| 1418 | var fn_type = try self.genFunctype(fn_ty); | 1450 | var fn_type = try self.genFunctype(fn_ty); |
| 1419 | defer fn_type.deinit(self.gpa); | 1451 | defer fn_type.deinit(self.gpa); |
| ... | @@ -1425,8 +1457,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1425,8 +1457,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1425 | const ret_ty = fn_ty.fnReturnType(); | 1457 | const ret_ty = fn_ty.fnReturnType(); |
| 1426 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1458 | if (!ret_ty.hasCodeGenBits()) return WValue.none; |
| 1427 | | 1459 | |
| 1428 | // slices are stored on the virtual stack, so we must pull out both ptr and len | 1460 | // TODO: Implement this for all aggregate types |
| 1429 | // to not overwrite the stack | | |
| 1430 | if (ret_ty.isSlice()) { | 1461 | if (ret_ty.isSlice()) { |
| 1431 | // first load the values onto the regular stack, before we move the stack pointer | 1462 | // first load the values onto the regular stack, before we move the stack pointer |
| 1432 | // to prevent overwriting the return value. | 1463 | // to prevent overwriting the return value. |
| ... | @@ -1551,7 +1582,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1551,7 +1582,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1551 | const val = rhs.constant.val; | 1582 | const val = rhs.constant.val; |
| 1552 | const len_local = try self.allocLocal(Type.usize); | 1583 | const len_local = try self.allocLocal(Type.usize); |
| 1553 | const ptr_local = try self.allocLocal(Type.usize); | 1584 | const ptr_local = try self.allocLocal(Type.usize); |
| 1554 | const len_offset = self.target.cpu.arch.ptrBitWidth() / 8; | 1585 | const len_offset = self.ptrSize(); |
| 1555 | if (val.castTag(.decl_ref)) |decl| { | 1586 | if (val.castTag(.decl_ref)) |decl| { |
| 1556 | // for decl references we also need to retrieve the length and the original decl's pointer | 1587 | // for decl references we also need to retrieve the length and the original decl's pointer |
| 1557 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = Type.@"usize".abiAlignment(self.target) }); | 1588 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = Type.@"usize".abiAlignment(self.target) }); |
| ... | @@ -1611,12 +1642,13 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1611,12 +1642,13 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1611 | | 1642 | |
| 1612 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; | 1643 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| 1613 | | 1644 | |
| 1614 | return switch (ty.zigTypeTag()) { | 1645 | if (isByRef(ty)) { |
| 1615 | .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer | 1646 | return operand; |
| 1616 | else => switch (operand) { | 1647 | } |
| 1617 | .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset), | 1648 | |
| 1618 | else => try self.load(operand, ty, 0), | 1649 | return switch (operand) { |
| 1619 | }, | 1650 | .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset), |
| | 1651 | else => try self.load(operand, ty, 0), |
| 1620 | }; | 1652 | }; |
| 1621 | } | 1653 | } |
| 1622 | | 1654 | |
| ... | @@ -1628,7 +1660,6 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1628,7 +1660,6 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1628 | .unsigned | 1660 | .unsigned |
| 1629 | else | 1661 | else |
| 1630 | .signed; | 1662 | .signed; |
| 1631 | // check if we should pass by pointer or value based on ABI size | | |
| 1632 | // TODO: Implement a way to get ABI values from a given type, | 1663 | // TODO: Implement a way to get ABI values from a given type, |
| 1633 | // that is portable across the backend, rather than copying logic. | 1664 | // that is portable across the backend, rather than copying logic. |
| 1634 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) | 1665 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) |
| ... | @@ -1779,6 +1810,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1779,6 +1810,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1779 | } | 1810 | } |
| 1780 | } else if (val.castTag(.int_u64)) |int_ptr| { | 1811 | } else if (val.castTag(.int_u64)) |int_ptr| { |
| 1781 | try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data))); | 1812 | try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data))); |
| | 1813 | } else if (val.tag() == .zero) { |
| | 1814 | try self.addImm32(0); |
| | 1815 | } else if (val.tag() == .one) { |
| | 1816 | try self.addImm32(1); |
| 1782 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); | 1817 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); |
| 1783 | }, | 1818 | }, |
| 1784 | .Void => {}, | 1819 | .Void => {}, |
| ... | @@ -2163,12 +2198,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2163,12 +2198,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2163 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); | 2198 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); |
| 2164 | }; | 2199 | }; |
| 2165 | | 2200 | |
| 2166 | // TODO: Replace this check with some 'isByRef' function to de-duplicate logic | 2201 | if (isByRef(field_ty)) { |
| 2167 | if (field_ty.zigTypeTag() == .Struct) { | 2202 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } }; |
| 2168 | return WValue{ .local_with_offset = .{ | | |
| 2169 | .local = operand.local, | | |
| 2170 | .offset = offset, | | |
| 2171 | } }; | | |
| 2172 | } | 2203 | } |
| 2173 | | 2204 | |
| 2174 | switch (operand) { | 2205 | switch (operand) { |
| ... | @@ -2328,20 +2359,23 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2328,20 +2359,23 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2328 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { | 2359 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| 2329 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2360 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2330 | const operand = self.resolveInst(un_op); | 2361 | const operand = self.resolveInst(un_op); |
| 2331 | const err_ty = self.air.typeOf(un_op).errorUnionSet(); | 2362 | const err_ty = self.air.typeOf(un_op); |
| | 2363 | const pl_ty = err_ty.errorUnionPayload(); |
| 2332 | | 2364 | |
| 2333 | // load the error tag value | 2365 | // load the error tag value |
| 2334 | try self.emitWValue(operand); | 2366 | try self.emitWValue(operand); |
| 2335 | try self.addMemArg( | 2367 | if (pl_ty.hasCodeGenBits()) { |
| 2336 | .i32_load16_u, | 2368 | try self.addMemArg(.i32_load16_u, .{ |
| 2337 | .{ .offset = 0, .alignment = err_ty.abiAlignment(self.target) }, | 2369 | .offset = 0, |
| 2338 | ); | 2370 | .alignment = err_ty.errorUnionSet().abiAlignment(self.target), |
| | 2371 | }); |
| | 2372 | } |
| 2339 | | 2373 | |
| 2340 | // Compare the error value with '0' | 2374 | // Compare the error value with '0' |
| 2341 | try self.addImm32(0); | 2375 | try self.addImm32(0); |
| 2342 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2376 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2343 | | 2377 | |
| 2344 | const is_err_tmp = try self.allocLocal(err_ty); | 2378 | const is_err_tmp = try self.allocLocal(Type.initTag(.i32)); // result is always an i32 |
| 2345 | try self.addLabel(.local_set, is_err_tmp.local); | 2379 | try self.addLabel(.local_set, is_err_tmp.local); |
| 2346 | return is_err_tmp; | 2380 | return is_err_tmp; |
| 2347 | } | 2381 | } |
| ... | @@ -2363,6 +2397,11 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2363,6 +2397,11 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2363 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2397 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2364 | const operand = self.resolveInst(ty_op.operand); | 2398 | const operand = self.resolveInst(ty_op.operand); |
| 2365 | const err_ty = self.air.typeOf(ty_op.operand); | 2399 | const err_ty = self.air.typeOf(ty_op.operand); |
| | 2400 | const payload_ty = err_ty.errorUnionPayload(); |
| | 2401 | if (!payload_ty.hasCodeGenBits()) { |
| | 2402 | return operand; |
| | 2403 | } |
| | 2404 | |
| 2366 | return try self.load(operand, err_ty.errorUnionSet(), 0); | 2405 | return try self.load(operand, err_ty.errorUnionSet(), 0); |
| 2367 | } | 2406 | } |
| 2368 | | 2407 | |
| ... | @@ -2507,9 +2546,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2507,9 +2546,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2507 | | 2546 | |
| 2508 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2547 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2509 | const operand = self.resolveInst(ty_op.operand); | 2548 | const operand = self.resolveInst(ty_op.operand); |
| 2510 | const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8; | | |
| 2511 | | 2549 | |
| 2512 | return try self.load(operand, Type.usize, pointer_width); | 2550 | return try self.load(operand, Type.usize, self.ptrSize()); |
| 2513 | } | 2551 | } |
| 2514 | | 2552 | |
| 2515 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2553 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2534,12 +2572,11 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2534,12 +2572,11 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2534 | | 2572 | |
| 2535 | const result = try self.allocLocal(elem_ty); | 2573 | const result = try self.allocLocal(elem_ty); |
| 2536 | try self.addLabel(.local_set, result.local); | 2574 | try self.addLabel(.local_set, result.local); |
| 2537 | return switch (elem_ty.zigTypeTag()) { | 2575 | |
| 2538 | // pass as pointer | 2576 | if (isByRef(elem_ty)) { |
| 2539 | .Pointer, .Struct, .Optional => result, | 2577 | return result; |
| 2540 | // pass by value | 2578 | } |
| 2541 | else => try self.load(result, elem_ty, 0), | 2579 | return try self.load(result, elem_ty, 0); |
| 2542 | }; | | |
| 2543 | } | 2580 | } |
| 2544 | | 2581 | |
| 2545 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2582 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2670,3 +2707,87 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2670,3 +2707,87 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2670 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2707 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2671 | return self.resolveInst(un_op); | 2708 | return self.resolveInst(un_op); |
| 2672 | } | 2709 | } |
| | 2710 | |
| | 2711 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2712 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2713 | |
| | 2714 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 2715 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| | 2716 | const pointer = self.resolveInst(bin_op.lhs); |
| | 2717 | const index = self.resolveInst(bin_op.rhs); |
| | 2718 | const elem_ty = ptr_ty.childType(); |
| | 2719 | const elem_size = elem_ty.abiSize(self.target); |
| | 2720 | |
| | 2721 | // load pointer onto the stack |
| | 2722 | if (ptr_ty.isSlice()) { |
| | 2723 | const ptr_local = try self.load(pointer, ptr_ty, 0); |
| | 2724 | try self.addLabel(.local_get, ptr_local.local); |
| | 2725 | } else { |
| | 2726 | try self.emitWValue(pointer); |
| | 2727 | } |
| | 2728 | |
| | 2729 | // calculate index into slice |
| | 2730 | try self.emitWValue(index); |
| | 2731 | try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); |
| | 2732 | try self.addTag(.i32_mul); |
| | 2733 | try self.addTag(.i32_add); |
| | 2734 | |
| | 2735 | const result = try self.allocLocal(elem_ty); |
| | 2736 | try self.addLabel(.local_set, result.local); |
| | 2737 | if (isByRef(elem_ty)) { |
| | 2738 | return result; |
| | 2739 | } |
| | 2740 | return try self.load(result, elem_ty, 0); |
| | 2741 | } |
| | 2742 | |
| | 2743 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2744 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2745 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 2746 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 2747 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| | 2748 | const elem_ty = self.air.getRefType(ty_pl.ty).childType(); |
| | 2749 | const elem_size = elem_ty.abiSize(self.target); |
| | 2750 | |
| | 2751 | const ptr = self.resolveInst(bin_op.lhs); |
| | 2752 | const index = self.resolveInst(bin_op.rhs); |
| | 2753 | |
| | 2754 | // load pointer onto the stack |
| | 2755 | if (ptr_ty.isSlice()) { |
| | 2756 | const ptr_local = try self.load(ptr, ptr_ty, 0); |
| | 2757 | try self.addLabel(.local_get, ptr_local.local); |
| | 2758 | } else { |
| | 2759 | try self.emitWValue(ptr); |
| | 2760 | } |
| | 2761 | |
| | 2762 | // calculate index into ptr |
| | 2763 | try self.emitWValue(index); |
| | 2764 | try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); |
| | 2765 | try self.addTag(.i32_mul); |
| | 2766 | try self.addTag(.i32_add); |
| | 2767 | |
| | 2768 | const result = try self.allocLocal(Type.initTag(.i32)); |
| | 2769 | try self.addLabel(.local_set, result.local); |
| | 2770 | return result; |
| | 2771 | } |
| | 2772 | |
| | 2773 | fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| | 2774 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2775 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 2776 | const ptr = self.resolveInst(bin_op.lhs); |
| | 2777 | const offset = self.resolveInst(bin_op.rhs); |
| | 2778 | const pointee_ty = self.air.typeOf(bin_op.lhs).childType(); |
| | 2779 | |
| | 2780 | const valtype = try self.typeToValtype(Type.usize); |
| | 2781 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); |
| | 2782 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); |
| | 2783 | |
| | 2784 | try self.emitWValue(ptr); |
| | 2785 | try self.emitWValue(offset); |
| | 2786 | try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target)))); |
| | 2787 | try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); |
| | 2788 | try self.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode)); |
| | 2789 | |
| | 2790 | const result = try self.allocLocal(Type.usize); |
| | 2791 | try self.addLabel(.local_set, result.local); |
| | 2792 | return result; |
| | 2793 | } |