| ... | @@ -1256,6 +1256,28 @@ fn isByRef(ty: Type) bool { | ... | @@ -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 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | 1281 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1260 | const air_tags = self.air.instructions.items(.tag); | 1282 | const air_tags = self.air.instructions.items(.tag); |
| 1261 | return switch (air_tags[inst]) { | 1283 | return switch (air_tags[inst]) { |
| ... | @@ -1296,16 +1318,16 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1296,16 +1318,16 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1296 | .is_err => self.airIsErr(inst, .i32_ne), | 1318 | .is_err => self.airIsErr(inst, .i32_ne), |
| 1297 | .is_non_err => self.airIsErr(inst, .i32_eq), | 1319 | .is_non_err => self.airIsErr(inst, .i32_eq), |
| 1298 | | 1320 | |
| 1299 | .is_null => self.airIsNull(inst, .i32_ne), | 1321 | .is_null => self.airIsNull(inst, .i32_eq, .value), |
| 1300 | .is_non_null => self.airIsNull(inst, .i32_eq), | 1322 | .is_non_null => self.airIsNull(inst, .i32_ne, .value), |
| 1301 | .is_null_ptr => self.airIsNull(inst, .i32_ne), | 1323 | .is_null_ptr => self.airIsNull(inst, .i32_eq, .ptr), |
| 1302 | .is_non_null_ptr => self.airIsNull(inst, .i32_eq), | 1324 | .is_non_null_ptr => self.airIsNull(inst, .i32_ne, .ptr), |
| 1303 | | 1325 | |
| 1304 | .load => self.airLoad(inst), | 1326 | .load => self.airLoad(inst), |
| 1305 | .loop => self.airLoop(inst), | 1327 | .loop => self.airLoop(inst), |
| 1306 | .not => self.airNot(inst), | 1328 | .not => self.airNot(inst), |
| 1307 | .optional_payload => self.airOptionalPayload(inst), | 1329 | .optional_payload => self.airOptionalPayload(inst), |
| 1308 | .optional_payload_ptr => self.airOptionalPayload(inst), | 1330 | .optional_payload_ptr => self.airOptionalPayloadPtr(inst), |
| 1309 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), | 1331 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), |
| 1310 | .ptr_add => self.airPtrBinOp(inst, .add), | 1332 | .ptr_add => self.airPtrBinOp(inst, .add), |
| 1311 | .ptr_sub => self.airPtrBinOp(inst, .sub), | 1333 | .ptr_sub => self.airPtrBinOp(inst, .sub), |
| ... | @@ -1482,14 +1504,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1482,14 +1504,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1482 | } | 1504 | } |
| 1483 | | 1505 | |
| 1484 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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 | // Initialize the stack | 1509 | // Initialize the stack |
| 1488 | if (self.initial_stack_value == .none) { | 1510 | if (self.initial_stack_value == .none) { |
| 1489 | try self.initializeStack(); | 1511 | try self.initializeStack(); |
| 1490 | } | 1512 | } |
| 1491 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; | 1513 | |
| 1492 | return self.allocStack(child_type); | 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 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,6 +1544,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1516 | const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8); | 1544 | const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8); |
| 1517 | const payload_offset = if (ty.zigTypeTag() == .ErrorUnion) | 1545 | const payload_offset = if (ty.zigTypeTag() == .ErrorUnion) |
| 1518 | @intCast(u32, tag_ty.abiSize(self.target)) | 1546 | @intCast(u32, tag_ty.abiSize(self.target)) |
| | 1547 | else if (ty.isPtrLikeOptional()) |
| | 1548 | @as(u32, 0) |
| 1519 | else | 1549 | else |
| 1520 | @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target)); | 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,6 +1558,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1528 | try self.addLabel(.local_set, mem_local.local); | 1558 | try self.addLabel(.local_set, mem_local.local); |
| 1529 | try self.store(lhs, mem_local, ty, 0); | 1559 | try self.store(lhs, mem_local, ty, 0); |
| 1530 | return; | 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 | // constant will contain both tag and payload, | 1566 | // constant will contain both tag and payload, |
| 1533 | // so save those in 2 temporary locals before storing them | 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,6 +1580,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1546 | return; | 1580 | return; |
| 1547 | }, | 1581 | }, |
| 1548 | .local => { | 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 | // Load values from `rhs` stack position and store in `lhs` instead | 1589 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1550 | const tag_local = try self.load(rhs, tag_ty, 0); | 1590 | const tag_local = try self.load(rhs, tag_ty, 0); |
| 1551 | if (payload_ty.hasCodeGenBits()) { | 1591 | if (payload_ty.hasCodeGenBits()) { |
| ... | @@ -1630,7 +1670,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1630,7 +1670,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1630 | .ErrorSet, | 1670 | .ErrorSet, |
| 1631 | .Enum, | 1671 | .Enum, |
| 1632 | .Bool, | 1672 | .Bool, |
| 1633 | .ErrorUnion, | | |
| 1634 | => @intCast(u8, ty.abiSize(self.target)), | 1673 | => @intCast(u8, ty.abiSize(self.target)), |
| 1635 | else => @as(u8, 4), | 1674 | else => @as(u8, 4), |
| 1636 | }; | 1675 | }; |
| ... | @@ -1684,6 +1723,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1684,6 +1723,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1684 | .Bool, | 1723 | .Bool, |
| 1685 | .ErrorUnion, | 1724 | .ErrorUnion, |
| 1686 | => @intCast(u8, ty.abiSize(self.target)), | 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 | else => @as(u8, 4), | 1730 | else => @as(u8, 4), |
| 1688 | }; | 1731 | }; |
| 1689 | | 1732 | |
| ... | @@ -1828,7 +1871,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1828,7 +1871,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1828 | } | 1871 | } |
| 1829 | } else if (val.castTag(.int_u64)) |int_ptr| { | 1872 | } else if (val.castTag(.int_u64)) |int_ptr| { |
| 1830 | try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data))); | 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 | try self.addImm32(0); | 1875 | try self.addImm32(0); |
| 1833 | } else if (val.tag() == .one) { | 1876 | } else if (val.tag() == .one) { |
| 1834 | try self.addImm32(1); | 1877 | try self.addImm32(1); |
| ... | @@ -1886,18 +1929,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1886,18 +1929,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1886 | var buf: Type.Payload.ElemType = undefined; | 1929 | var buf: Type.Payload.ElemType = undefined; |
| 1887 | const payload_type = ty.optionalChild(&buf); | 1930 | const payload_type = ty.optionalChild(&buf); |
| 1888 | if (ty.isPtrLikeOptional()) { | 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 | // When constant has value 'null', set is_null local to '1' | 1936 | // When constant has value 'null', set is_null local to '1' |
| 1893 | // and payload to '0' | 1937 | // and payload to '0' |
| 1894 | if (val.castTag(.opt_payload)) |payload| { | 1938 | if (val.castTag(.opt_payload)) |payload| { |
| 1895 | try self.addImm32(0); | 1939 | try self.addImm32(1); |
| 1896 | if (payload_type.hasCodeGenBits()) | 1940 | if (payload_type.hasCodeGenBits()) |
| 1897 | try self.emitConstant(payload.data, payload_type); | 1941 | try self.emitConstant(payload.data, payload_type); |
| 1898 | } else { | 1942 | } else { |
| 1899 | // set null-tag | 1943 | // set null-tag |
| 1900 | try self.addImm32(1); | 1944 | try self.addImm32(0); |
| 1901 | // null-tag is set, so write a '0' const | 1945 | // null-tag is set, so write a '0' const |
| 1902 | try self.addImm32(0); | 1946 | try self.addImm32(0); |
| 1903 | } | 1947 | } |
| ... | @@ -2065,23 +2109,34 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2065,23 +2109,34 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2065 | } | 2109 | } |
| 2066 | | 2110 | |
| 2067 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { | 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]; | 2112 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2069 | const lhs = self.resolveInst(data.bin_op.lhs); | 2113 | const lhs = self.resolveInst(bin_op.lhs); |
| 2070 | const rhs = self.resolveInst(data.bin_op.rhs); | 2114 | const rhs = self.resolveInst(bin_op.rhs); |
| 2071 | const lhs_ty = self.air.typeOf(data.bin_op.lhs); | 2115 | const operand_ty = self.air.typeOf(bin_op.lhs); |
| 2072 | | 2116 | |
| 2073 | try self.emitWValue(lhs); | 2117 | try self.emitWValue(lhs); |
| 2074 | try self.emitWValue(rhs); | 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 | const signedness: std.builtin.Signedness = blk: { | 2131 | const signedness: std.builtin.Signedness = blk: { |
| 2077 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 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 | // incase of an actual integer, we emit the correct signedness | 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 | const opcode: wasm.Opcode = buildOpcode(.{ | 2138 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2084 | .valtype1 = try self.typeToValtype(lhs_ty), | 2139 | .valtype1 = try self.typeToValtype(operand_ty), |
| 2085 | .op = switch (op) { | 2140 | .op = switch (op) { |
| 2086 | .lt => .lt, | 2141 | .lt => .lt, |
| 2087 | .lte => .le, | 2142 | .lte => .le, |
| ... | @@ -2173,7 +2228,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2173,7 +2228,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2173 | struct_ty.structFieldType(extra.data.field_index), | 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 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { | 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,10 +2241,10 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr |
| 2186 | field_ty, | 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 | var final_offset = offset; | 2248 | var final_offset = offset; |
| 2194 | const local = switch (struct_ptr) { | 2249 | const local = switch (struct_ptr) { |
| 2195 | .local => |local| local, | 2250 | .local => |local| local, |
| ... | @@ -2199,7 +2254,7 @@ fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { | ... | @@ -2199,7 +2254,7 @@ fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 2199 | }, | 2254 | }, |
| 2200 | else => unreachable, | 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 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,24 +2489,13 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2434 | const offset = err_ty.errorUnionSet().abiSize(self.target); | 2489 | const offset = err_ty.errorUnionSet().abiSize(self.target); |
| 2435 | | 2490 | |
| 2436 | const err_union = try self.allocStack(err_ty); | 2491 | const err_union = try self.allocStack(err_ty); |
| 2437 | const to_store = switch (op_ty.zigTypeTag()) { | 2492 | const payload_ptr = try self.buildPointerOffset(err_union, offset); |
| 2438 | // for those types we must load the pointer and then store | 2493 | try self.store(payload_ptr, operand, op_ty, 0); |
| 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 | | 2494 | |
| 2452 | // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. | 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. | 2496 | try self.addLabel(.local_get, err_union.local); |
| 2454 | try self.store(err_union, tmp_local, err_ty.errorUnionSet(), 0); | 2497 | try self.addImm32(0); |
| | 2498 | try self.addMemArg(.i32_store16, .{ .offset = 0, .alignment = 2 }); |
| 2455 | | 2499 | |
| 2456 | return err_union; | 2500 | return err_union; |
| 2457 | } | 2501 | } |
| ... | @@ -2499,64 +2543,122 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2499,64 +2543,122 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2499 | return result; | 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 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2547 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2504 | const operand = self.resolveInst(un_op); | 2548 | const operand = self.resolveInst(un_op); |
| 2505 | | 2549 | |
| 2506 | const op_ty = self.air.typeOf(un_op); | 2550 | const op_ty = self.air.typeOf(un_op); |
| | 2551 | const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; |
| 2507 | try self.emitWValue(operand); | 2552 | try self.emitWValue(operand); |
| 2508 | if (!op_ty.isPtrLikeOptional()) { | 2553 | if (!optional_ty.isPtrLikeOptional()) { |
| 2509 | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); | 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 | try self.addImm32(0); | 2564 | try self.addImm32(0); |
| 2514 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 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 | try self.addLabel(.local_set, is_null_tmp.local); | 2568 | try self.addLabel(.local_set, is_null_tmp.local); |
| 2518 | return is_null_tmp; | 2569 | return is_null_tmp; |
| 2519 | } | 2570 | } |
| 2520 | | 2571 | |
| 2521 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2572 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2573 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2522 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2574 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2523 | const operand = self.resolveInst(ty_op.operand); | 2575 | const operand = self.resolveInst(ty_op.operand); |
| 2524 | const opt_ty = self.air.typeOf(ty_op.operand); | 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 | 2581 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| 2527 | // loading its value | 2582 | |
| 2528 | if (opt_ty.zigTypeTag() == .Pointer) { | 2583 | if (isByRef(payload_ty)) { |
| 2529 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = 1 } }; | 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 | var buf: Type.Payload.ElemType = undefined; | 2597 | var buf: Type.Payload.ElemType = undefined; |
| 2535 | const child_ty = opt_ty.optionalChild(&buf); | 2598 | const payload_ty = opt_ty.optionalChild(&buf); |
| 2536 | const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target); | 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 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2607 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2542 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2608 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2543 | const operand = self.resolveInst(ty_op.operand); | 2609 | const operand = self.resolveInst(ty_op.operand); |
| 2544 | _ = operand; | 2610 | const opt_ty = self.air.typeOf(ty_op.operand).childType(); |
| 2545 | return self.fail("TODO - wasm codegen for optional_payload_ptr_set", .{}); | 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 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2632 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2633 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2634 | |
| 2549 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | const operand = self.resolveInst(ty_op.operand); | 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); | 2654 | // Create optional type, set the non-null bit, and store the operand inside the optional type |
| 2553 | const optional_ty = self.air.getRefType(ty_op.ty); | 2655 | const result = try self.allocStack(op_ty); |
| 2554 | const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target); | 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 = .{ | 2661 | return result; |
| 2557 | .local = operand.local, | | |
| 2558 | .offset = @intCast(u32, offset), | | |
| 2559 | } }; | | |
| 2560 | } | 2662 | } |
| 2561 | | 2663 | |
| 2562 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2664 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |