authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-03 19:46:54+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-04 17:46:58+01:00
logc519d9c80e5b207a84b9bd414727bd41cb4fadfa
treefb72dfc603307552b309585246b1eb14e566bac6
parent5c228765f1094d30e64d13c0077c67b2867ecd6a
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Implement (and fix) most optional instructions

Previously we were performing the wrapping and unwrapping operations incorrectly. We now correctly create the type and set its values. Besides this, we also set the null-byte to the incorrect value, which meant we were doing the opposite action of a is_null check. This is now fixed as well. While implementing this, I found a small bug in the wrapErrUnionPayload where we would load a pointer value and save that, rather than store the pointer with the error. This is now fixed as well, by copying the entire operand into the payload of the error union.

1 files changed, 164 insertions(+), 62 deletions(-)

src/arch/wasm/CodeGen.zig+164-62
...@@ -1256,6 +1256,28 @@ fn isByRef(ty: Type) bool {...@@ -1256,6 +1256,28 @@ fn isByRef(ty: Type) bool {
1256 }1256 }
1257}1257}
12581258
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.
1261fn 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
1259fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {1281fn 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),
12981320
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),
13031325
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}
14831505
1484fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1506fn 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();
14861508
1487 // Initialize the stack1509 // 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}
14941522
1495fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1523fn 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 else1549 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));
15211551
...@@ -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 them1567 // 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` instead1589 // 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 };
16891732
...@@ -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 }
18911935
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-tag1943 // set null-tag
1900 try self.addImm32(1);1944 try self.addImm32(0);
1901 // null-tag is set, so write a '0' const1945 // 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}
20662110
2067fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {2111fn 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);
20722116
2073 try self.emitWValue(lhs);2117 try self.emitWValue(lhs);
2074 try self.emitWValue(rhs);2118 try self.emitWValue(rhs);
20752119
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;
20792134
2080 // incase of an actual integer, we emit the correct signedness2135 // 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}
21782233
2179fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {2234fn 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}
21912246
2192fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {2247fn 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}
22042259
2205fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2260fn 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);
24352490
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 store2493 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));
24512494
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 });
24552499
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}
25012545
2502fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {2546fn 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);
25052549
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 }
25112562
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));
25152566
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}
25202571
2521fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2572fn 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;
25252580
2526 // For pointers we simply return its stack address, rather than2581 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
2527 // loading its value2582
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 }
25312586
2532 if (opt_ty.isPtrLikeOptional()) return operand;2587 return self.load(operand, payload_ty, @intCast(u32, offset));
2588}
2589
2590fn 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();
25332596
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 }
25372602
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}
25402606
2541fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2607fn 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}
25472631
2548fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2632fn 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 };
25512653
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);
25552660
2556 return WValue{ .local_with_offset = .{2661 return result;
2557 .local = operand.local,
2558 .offset = @intCast(u32, offset),
2559 } };
2560}2662}
25612663
2562fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2664fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {