| ... | ... | @@ -1256,6 +1256,28 @@ fn isByRef(ty: Type) bool { |
| 1256 | 1256 | } |
| 1257 | 1257 | } |
| 1258 | 1258 | |
| 1259 | /// Creates a new local for a pointer that points to memory with given offset. |
| 1260 | /// This can be used to get a pointer to a struct field, error payload, etc. |
| 1261 | fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64) InnerError!WValue { |
| 1262 | // do not perform arithmetic when offset is 0. |
| 1263 | if (offset == 0) return ptr_value; |
| 1264 | const result_ptr = try self.allocLocal(Type.usize); |
| 1265 | try self.emitWValue(ptr_value); |
| 1266 | switch (self.target.cpu.arch.ptrBitWidth()) { |
| 1267 | 32 => { |
| 1268 | try self.addImm32(@bitCast(i32, @intCast(u32, offset))); |
| 1269 | try self.addTag(.i32_add); |
| 1270 | }, |
| 1271 | 64 => { |
| 1272 | try self.addImm64(offset); |
| 1273 | try self.addTag(.i64_add); |
| 1274 | }, |
| 1275 | else => unreachable, |
| 1276 | } |
| 1277 | try self.addLabel(.local_set, result_ptr.local); |
| 1278 | return result_ptr; |
| 1279 | } |
| 1280 | |
| 1259 | 1281 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1260 | 1282 | const air_tags = self.air.instructions.items(.tag); |
| 1261 | 1283 | return switch (air_tags[inst]) { |
| ... | ... | @@ -1296,16 +1318,16 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1296 | 1318 | .is_err => self.airIsErr(inst, .i32_ne), |
| 1297 | 1319 | .is_non_err => self.airIsErr(inst, .i32_eq), |
| 1298 | 1320 | |
| 1299 | | .is_null => self.airIsNull(inst, .i32_ne), |
| 1300 | | .is_non_null => self.airIsNull(inst, .i32_eq), |
| 1301 | | .is_null_ptr => self.airIsNull(inst, .i32_ne), |
| 1302 | | .is_non_null_ptr => self.airIsNull(inst, .i32_eq), |
| 1321 | .is_null => self.airIsNull(inst, .i32_eq, .value), |
| 1322 | .is_non_null => self.airIsNull(inst, .i32_ne, .value), |
| 1323 | .is_null_ptr => self.airIsNull(inst, .i32_eq, .ptr), |
| 1324 | .is_non_null_ptr => self.airIsNull(inst, .i32_ne, .ptr), |
| 1303 | 1325 | |
| 1304 | 1326 | .load => self.airLoad(inst), |
| 1305 | 1327 | .loop => self.airLoop(inst), |
| 1306 | 1328 | .not => self.airNot(inst), |
| 1307 | 1329 | .optional_payload => self.airOptionalPayload(inst), |
| 1308 | | .optional_payload_ptr => self.airOptionalPayload(inst), |
| 1330 | .optional_payload_ptr => self.airOptionalPayloadPtr(inst), |
| 1309 | 1331 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), |
| 1310 | 1332 | .ptr_add => self.airPtrBinOp(inst, .add), |
| 1311 | 1333 | .ptr_sub => self.airPtrBinOp(inst, .sub), |
| ... | ... | @@ -1482,14 +1504,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1482 | 1504 | } |
| 1483 | 1505 | |
| 1484 | 1506 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1485 | | const child_type = self.air.typeOfIndex(inst).childType(); |
| 1507 | const pointee_type = self.air.typeOfIndex(inst).childType(); |
| 1486 | 1508 | |
| 1487 | 1509 | // Initialize the stack |
| 1488 | 1510 | if (self.initial_stack_value == .none) { |
| 1489 | 1511 | try self.initializeStack(); |
| 1490 | 1512 | } |
| 1491 | | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1492 | | return self.allocStack(child_type); |
| 1513 | |
| 1514 | if (!pointee_type.hasCodeGenBits()) { |
| 1515 | // when the pointee is zero-sized, we still want to create a pointer. |
| 1516 | // but instead use a default pointer type as storage. |
| 1517 | const zero_ptr = try self.allocStack(Type.usize); |
| 1518 | return zero_ptr; |
| 1519 | } |
| 1520 | return self.allocStack(pointee_type); |
| 1493 | 1521 | } |
| 1494 | 1522 | |
| 1495 | 1523 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1516,6 +1544,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1516 | 1544 | const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8); |
| 1517 | 1545 | const payload_offset = if (ty.zigTypeTag() == .ErrorUnion) |
| 1518 | 1546 | @intCast(u32, tag_ty.abiSize(self.target)) |
| 1547 | else if (ty.isPtrLikeOptional()) |
| 1548 | @as(u32, 0) |
| 1519 | 1549 | else |
| 1520 | 1550 | @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target)); |
| 1521 | 1551 | |
| ... | ... | @@ -1528,6 +1558,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1528 | 1558 | try self.addLabel(.local_set, mem_local.local); |
| 1529 | 1559 | try self.store(lhs, mem_local, ty, 0); |
| 1530 | 1560 | return; |
| 1561 | } else if (ty.isPtrLikeOptional()) { |
| 1562 | // set the address of rhs to lhs |
| 1563 | try self.store(lhs, rhs, Type.usize, 0); |
| 1564 | return; |
| 1531 | 1565 | } |
| 1532 | 1566 | // constant will contain both tag and payload, |
| 1533 | 1567 | // so save those in 2 temporary locals before storing them |
| ... | ... | @@ -1546,6 +1580,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1546 | 1580 | return; |
| 1547 | 1581 | }, |
| 1548 | 1582 | .local => { |
| 1583 | // When the optional is pointer-like, we simply store the pointer |
| 1584 | // instead. |
| 1585 | if (ty.isPtrLikeOptional()) { |
| 1586 | try self.store(lhs, rhs, Type.usize, 0); |
| 1587 | return; |
| 1588 | } |
| 1549 | 1589 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1550 | 1590 | const tag_local = try self.load(rhs, tag_ty, 0); |
| 1551 | 1591 | if (payload_ty.hasCodeGenBits()) { |
| ... | ... | @@ -1630,7 +1670,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1630 | 1670 | .ErrorSet, |
| 1631 | 1671 | .Enum, |
| 1632 | 1672 | .Bool, |
| 1633 | | .ErrorUnion, |
| 1634 | 1673 | => @intCast(u8, ty.abiSize(self.target)), |
| 1635 | 1674 | else => @as(u8, 4), |
| 1636 | 1675 | }; |
| ... | ... | @@ -1684,6 +1723,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1684 | 1723 | .Bool, |
| 1685 | 1724 | .ErrorUnion, |
| 1686 | 1725 | => @intCast(u8, ty.abiSize(self.target)), |
| 1726 | .Optional => blk: { |
| 1727 | if (ty.isPtrLikeOptional()) break :blk @intCast(u8, self.ptrSize()); |
| 1728 | break :blk @intCast(u8, ty.abiSize(self.target)); |
| 1729 | }, |
| 1687 | 1730 | else => @as(u8, 4), |
| 1688 | 1731 | }; |
| 1689 | 1732 | |
| ... | ... | @@ -1828,7 +1871,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1828 | 1871 | } |
| 1829 | 1872 | } else if (val.castTag(.int_u64)) |int_ptr| { |
| 1830 | 1873 | try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data))); |
| 1831 | | } else if (val.tag() == .zero) { |
| 1874 | } else if (val.tag() == .zero or val.tag() == .null_value) { |
| 1832 | 1875 | try self.addImm32(0); |
| 1833 | 1876 | } else if (val.tag() == .one) { |
| 1834 | 1877 | try self.addImm32(1); |
| ... | ... | @@ -1886,18 +1929,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1886 | 1929 | var buf: Type.Payload.ElemType = undefined; |
| 1887 | 1930 | const payload_type = ty.optionalChild(&buf); |
| 1888 | 1931 | if (ty.isPtrLikeOptional()) { |
| 1889 | | return self.fail("Wasm TODO: emitConstant for optional pointer", .{}); |
| 1932 | try self.emitConstant(val, payload_type); |
| 1933 | return; |
| 1890 | 1934 | } |
| 1891 | 1935 | |
| 1892 | 1936 | // When constant has value 'null', set is_null local to '1' |
| 1893 | 1937 | // and payload to '0' |
| 1894 | 1938 | if (val.castTag(.opt_payload)) |payload| { |
| 1895 | | try self.addImm32(0); |
| 1939 | try self.addImm32(1); |
| 1896 | 1940 | if (payload_type.hasCodeGenBits()) |
| 1897 | 1941 | try self.emitConstant(payload.data, payload_type); |
| 1898 | 1942 | } else { |
| 1899 | 1943 | // set null-tag |
| 1900 | | try self.addImm32(1); |
| 1944 | try self.addImm32(0); |
| 1901 | 1945 | // null-tag is set, so write a '0' const |
| 1902 | 1946 | try self.addImm32(0); |
| 1903 | 1947 | } |
| ... | ... | @@ -2065,23 +2109,34 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2065 | 2109 | } |
| 2066 | 2110 | |
| 2067 | 2111 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { |
| 2068 | | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; |
| 2069 | | const lhs = self.resolveInst(data.bin_op.lhs); |
| 2070 | | const rhs = self.resolveInst(data.bin_op.rhs); |
| 2071 | | const lhs_ty = self.air.typeOf(data.bin_op.lhs); |
| 2112 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2113 | const lhs = self.resolveInst(bin_op.lhs); |
| 2114 | const rhs = self.resolveInst(bin_op.rhs); |
| 2115 | const operand_ty = self.air.typeOf(bin_op.lhs); |
| 2072 | 2116 | |
| 2073 | 2117 | try self.emitWValue(lhs); |
| 2074 | 2118 | try self.emitWValue(rhs); |
| 2075 | 2119 | |
| 2120 | if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { |
| 2121 | var buf: Type.Payload.ElemType = undefined; |
| 2122 | const payload_ty = operand_ty.optionalChild(&buf); |
| 2123 | if (payload_ty.hasCodeGenBits()) { |
| 2124 | // When we hit this case, we must check the value of optionals |
| 2125 | // that are not pointers. This means first checking against non-null for |
| 2126 | // both lhs and rhs, as well as checking the payload are matching of lhs and rhs |
| 2127 | return self.fail("TODO: Implement airCmp for comparing optionals", .{}); |
| 2128 | } |
| 2129 | } |
| 2130 | |
| 2076 | 2131 | const signedness: std.builtin.Signedness = blk: { |
| 2077 | 2132 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2078 | | if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 2133 | if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 2079 | 2134 | |
| 2080 | 2135 | // incase of an actual integer, we emit the correct signedness |
| 2081 | | break :blk lhs_ty.intInfo(self.target).signedness; |
| 2136 | break :blk operand_ty.intInfo(self.target).signedness; |
| 2082 | 2137 | }; |
| 2083 | 2138 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2084 | | .valtype1 = try self.typeToValtype(lhs_ty), |
| 2139 | .valtype1 = try self.typeToValtype(operand_ty), |
| 2085 | 2140 | .op = switch (op) { |
| 2086 | 2141 | .lt => .lt, |
| 2087 | 2142 | .lte => .le, |
| ... | ... | @@ -2173,7 +2228,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2173 | 2228 | struct_ty.structFieldType(extra.data.field_index), |
| 2174 | 2229 | }); |
| 2175 | 2230 | }; |
| 2176 | | return structFieldPtr(struct_ptr, offset); |
| 2231 | return self.structFieldPtr(struct_ptr, offset); |
| 2177 | 2232 | } |
| 2178 | 2233 | |
| 2179 | 2234 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { |
| ... | ... | @@ -2186,10 +2241,10 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr |
| 2186 | 2241 | field_ty, |
| 2187 | 2242 | }); |
| 2188 | 2243 | }; |
| 2189 | | return structFieldPtr(struct_ptr, offset); |
| 2244 | return self.structFieldPtr(struct_ptr, offset); |
| 2190 | 2245 | } |
| 2191 | 2246 | |
| 2192 | | fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 2247 | fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 2193 | 2248 | var final_offset = offset; |
| 2194 | 2249 | const local = switch (struct_ptr) { |
| 2195 | 2250 | .local => |local| local, |
| ... | ... | @@ -2199,7 +2254,7 @@ fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 2199 | 2254 | }, |
| 2200 | 2255 | else => unreachable, |
| 2201 | 2256 | }; |
| 2202 | | return WValue{ .local_with_offset = .{ .local = local, .offset = final_offset } }; |
| 2257 | return self.buildPointerOffset(.{ .local = local }, final_offset); |
| 2203 | 2258 | } |
| 2204 | 2259 | |
| 2205 | 2260 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -2434,24 +2489,13 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2434 | 2489 | const offset = err_ty.errorUnionSet().abiSize(self.target); |
| 2435 | 2490 | |
| 2436 | 2491 | const err_union = try self.allocStack(err_ty); |
| 2437 | | const to_store = switch (op_ty.zigTypeTag()) { |
| 2438 | | // for those types we must load the pointer and then store |
| 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)); |
| 2492 | const payload_ptr = try self.buildPointerOffset(err_union, offset); |
| 2493 | try self.store(payload_ptr, operand, op_ty, 0); |
| 2451 | 2494 | |
| 2452 | 2495 | // 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); |
| 2496 | try self.addLabel(.local_get, err_union.local); |
| 2497 | try self.addImm32(0); |
| 2498 | try self.addMemArg(.i32_store16, .{ .offset = 0, .alignment = 2 }); |
| 2455 | 2499 | |
| 2456 | 2500 | return err_union; |
| 2457 | 2501 | } |
| ... | ... | @@ -2499,64 +2543,122 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2499 | 2543 | return result; |
| 2500 | 2544 | } |
| 2501 | 2545 | |
| 2502 | | fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| 2546 | fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue { |
| 2503 | 2547 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2504 | 2548 | const operand = self.resolveInst(un_op); |
| 2505 | 2549 | |
| 2506 | 2550 | const op_ty = self.air.typeOf(un_op); |
| 2551 | const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; |
| 2507 | 2552 | try self.emitWValue(operand); |
| 2508 | | if (!op_ty.isPtrLikeOptional()) { |
| 2509 | | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); |
| 2553 | if (!optional_ty.isPtrLikeOptional()) { |
| 2554 | var buf: Type.Payload.ElemType = undefined; |
| 2555 | const payload_ty = optional_ty.optionalChild(&buf); |
| 2556 | // When payload is zero-bits, we can treat operand as a value, rather than a |
| 2557 | // stack value |
| 2558 | if (payload_ty.hasCodeGenBits()) { |
| 2559 | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); |
| 2560 | } |
| 2510 | 2561 | } |
| 2511 | 2562 | |
| 2512 | | // Compare the error value with '0' |
| 2563 | // Compare the null value with '0' |
| 2513 | 2564 | try self.addImm32(0); |
| 2514 | 2565 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2515 | 2566 | |
| 2516 | | const is_null_tmp = try self.allocLocal(Type.initTag(.u8)); |
| 2567 | const is_null_tmp = try self.allocLocal(Type.initTag(.i32)); |
| 2517 | 2568 | try self.addLabel(.local_set, is_null_tmp.local); |
| 2518 | 2569 | return is_null_tmp; |
| 2519 | 2570 | } |
| 2520 | 2571 | |
| 2521 | 2572 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2573 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2522 | 2574 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2523 | 2575 | const operand = self.resolveInst(ty_op.operand); |
| 2524 | 2576 | const opt_ty = self.air.typeOf(ty_op.operand); |
| 2577 | const payload_ty = self.air.typeOfIndex(inst); |
| 2578 | if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| 2579 | if (opt_ty.isPtrLikeOptional()) return operand; |
| 2525 | 2580 | |
| 2526 | | // For pointers we simply return its stack address, rather than |
| 2527 | | // loading its value |
| 2528 | | if (opt_ty.zigTypeTag() == .Pointer) { |
| 2529 | | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = 1 } }; |
| 2581 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| 2582 | |
| 2583 | if (isByRef(payload_ty)) { |
| 2584 | return self.buildPointerOffset(operand, offset); |
| 2530 | 2585 | } |
| 2531 | 2586 | |
| 2532 | | if (opt_ty.isPtrLikeOptional()) return operand; |
| 2587 | return self.load(operand, payload_ty, @intCast(u32, offset)); |
| 2588 | } |
| 2589 | |
| 2590 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2591 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2592 | |
| 2593 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2594 | const operand = self.resolveInst(ty_op.operand); |
| 2595 | const opt_ty = self.air.typeOf(ty_op.operand).childType(); |
| 2533 | 2596 | |
| 2534 | 2597 | var buf: Type.Payload.ElemType = undefined; |
| 2535 | | const child_ty = opt_ty.optionalChild(&buf); |
| 2536 | | const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target); |
| 2598 | const payload_ty = opt_ty.optionalChild(&buf); |
| 2599 | if (!payload_ty.hasCodeGenBits() or opt_ty.isPtrLikeOptional()) { |
| 2600 | return operand; |
| 2601 | } |
| 2537 | 2602 | |
| 2538 | | return self.load(operand, child_ty, @intCast(u32, offset)); |
| 2603 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| 2604 | return self.buildPointerOffset(operand, offset); |
| 2539 | 2605 | } |
| 2540 | 2606 | |
| 2541 | 2607 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2542 | 2608 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2543 | 2609 | const operand = self.resolveInst(ty_op.operand); |
| 2544 | | _ = operand; |
| 2545 | | return self.fail("TODO - wasm codegen for optional_payload_ptr_set", .{}); |
| 2610 | const opt_ty = self.air.typeOf(ty_op.operand).childType(); |
| 2611 | var buf: Type.Payload.ElemType = undefined; |
| 2612 | const payload_ty = opt_ty.optionalChild(&buf); |
| 2613 | if (!payload_ty.hasCodeGenBits()) { |
| 2614 | return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty}); |
| 2615 | } |
| 2616 | |
| 2617 | if (opt_ty.isPtrLikeOptional()) { |
| 2618 | return operand; |
| 2619 | } |
| 2620 | |
| 2621 | const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { |
| 2622 | return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty}); |
| 2623 | }; |
| 2624 | |
| 2625 | try self.emitWValue(operand); |
| 2626 | try self.addImm32(1); |
| 2627 | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| 2628 | |
| 2629 | return self.buildPointerOffset(operand, offset); |
| 2546 | 2630 | } |
| 2547 | 2631 | |
| 2548 | 2632 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2633 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2634 | |
| 2549 | 2635 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2636 | const payload_ty = self.air.typeOf(ty_op.operand); |
| 2637 | if (!payload_ty.hasCodeGenBits()) { |
| 2638 | const non_null_bit = try self.allocStack(Type.initTag(.u1)); |
| 2639 | try self.addLabel(.local_get, non_null_bit.local); |
| 2640 | try self.addImm32(1); |
| 2641 | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| 2642 | return non_null_bit; |
| 2643 | } |
| 2644 | |
| 2550 | 2645 | const operand = self.resolveInst(ty_op.operand); |
| 2646 | const op_ty = self.air.typeOfIndex(inst); |
| 2647 | if (op_ty.isPtrLikeOptional()) { |
| 2648 | return operand; |
| 2649 | } |
| 2650 | const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { |
| 2651 | return self.fail("Optional type {} too big to fit into stack frame", .{op_ty}); |
| 2652 | }; |
| 2551 | 2653 | |
| 2552 | | const op_ty = self.air.typeOf(ty_op.operand); |
| 2553 | | const optional_ty = self.air.getRefType(ty_op.ty); |
| 2554 | | const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target); |
| 2654 | // Create optional type, set the non-null bit, and store the operand inside the optional type |
| 2655 | const result = try self.allocStack(op_ty); |
| 2656 | try self.addLabel(.local_get, result.local); |
| 2657 | try self.addImm32(1); |
| 2658 | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| 2659 | try self.store(result, operand, payload_ty, offset); |
| 2555 | 2660 | |
| 2556 | | return WValue{ .local_with_offset = .{ |
| 2557 | | .local = operand.local, |
| 2558 | | .offset = @intCast(u32, offset), |
| 2559 | | } }; |
| 2661 | return result; |
| 2560 | 2662 | } |
| 2561 | 2663 | |
| 2562 | 2664 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |