authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-30 19:29:26-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-30 19:29:26-05:00
log94e751ad013915b7b2d8dbfdd0d708fe9f56f087
treecc9457e86fb969a9590e178ba865f46111c398fd
parent34fa6a1e0437ab7f08a2ccff2aff88aa77aeb037
parent090deae41dbb79bcbe6a06d0bea0413f21576881
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13718 from Luukdegram/wasm-packed

stage2: Wasm - Implement packed structs

10 files changed, 471 insertions(+), 215 deletions(-)

src/arch/wasm/CodeGen.zig+416-168
...@@ -483,7 +483,12 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -483,7 +483,12 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
483 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,483 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,
484 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,484 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,
485 } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32.485 } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32.
486 .i64 => unreachable,486 .i64 => switch (args.valtype2.?) {
487 .i32 => unreachable,
488 .i64 => unreachable,
489 .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u,
490 .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u,
491 },
487 .f32 => return .f32_trunc,492 .f32 => return .f32_trunc,
488 .f64 => return .f64_trunc,493 .f64 => return .f64_trunc,
489 },494 },
...@@ -687,7 +692,10 @@ const InnerError = error{...@@ -687,7 +692,10 @@ const InnerError = error{
687};692};
688693
689pub fn deinit(func: *CodeGen) void {694pub fn deinit(func: *CodeGen) void {
690 assert(func.branches.items.len == 0); // we should end with no branches left. Forgot a call to `branches.pop()`?695 // in case of an error and we still have branches
696 for (func.branches.items) |*branch| {
697 branch.deinit(func.gpa);
698 }
691 func.branches.deinit(func.gpa);699 func.branches.deinit(func.gpa);
692 func.blocks.deinit(func.gpa);700 func.blocks.deinit(func.gpa);
693 func.locals.deinit(func.gpa);701 func.locals.deinit(func.gpa);
...@@ -909,6 +917,13 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {...@@ -909,6 +917,13 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
909 if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64;917 if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64;
910 break :blk wasm.Valtype.i32; // represented as pointer to stack918 break :blk wasm.Valtype.i32; // represented as pointer to stack
911 },919 },
920 .Struct => switch (ty.containerLayout()) {
921 .Packed => {
922 const struct_obj = ty.castTag(.@"struct").?.data;
923 return typeToValtype(struct_obj.backing_int_ty, target);
924 },
925 else => wasm.Valtype.i32,
926 },
912 else => wasm.Valtype.i32, // all represented as reference/immediate927 else => wasm.Valtype.i32, // all represented as reference/immediate
913 };928 };
914}929}
...@@ -1119,13 +1134,14 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1119,13 +1134,14 @@ fn genFunc(func: *CodeGen) InnerError!void {
1119 try func.addTag(.dbg_prologue_end);1134 try func.addTag(.dbg_prologue_end);
11201135
1121 try func.branches.append(func.gpa, .{});1136 try func.branches.append(func.gpa, .{});
1137 // clean up outer branch
1138 defer {
1139 var outer_branch = func.branches.pop();
1140 outer_branch.deinit(func.gpa);
1141 }
1122 // Generate MIR for function body1142 // Generate MIR for function body
1123 try func.genBody(func.air.getMainBody());1143 try func.genBody(func.air.getMainBody());
11241144
1125 // clean up outer branch
1126 var outer_branch = func.branches.pop();
1127 outer_branch.deinit(func.gpa);
1128
1129 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)1145 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
1130 // we emit an unreachable instruction to tell the stack validator that part will never be reached.1146 // we emit an unreachable instruction to tell the stack validator that part will never be reached.
1131 if (func_type.returns.len != 0 and func.air.instructions.len > 0) {1147 if (func_type.returns.len != 0 and func.air.instructions.len > 0) {
...@@ -1309,17 +1325,21 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value:...@@ -1309,17 +1325,21 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value:
1309 assert(ty_classes[0] == .direct);1325 assert(ty_classes[0] == .direct);
1310 const scalar_type = abi.scalarType(ty, func.target);1326 const scalar_type = abi.scalarType(ty, func.target);
1311 const abi_size = scalar_type.abiSize(func.target);1327 const abi_size = scalar_type.abiSize(func.target);
1312 const opcode = buildOpcode(.{
1313 .op = .load,
1314 .width = @intCast(u8, abi_size),
1315 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
1316 .valtype1 = typeToValtype(scalar_type, func.target),
1317 });
1318 try func.emitWValue(value);1328 try func.emitWValue(value);
1319 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{1329
1320 .offset = value.offset(),1330 // When the value lives in the virtual stack, we must load it onto the actual stack
1321 .alignment = scalar_type.abiAlignment(func.target),1331 if (value != .imm32 and value != .imm64) {
1322 });1332 const opcode = buildOpcode(.{
1333 .op = .load,
1334 .width = @intCast(u8, abi_size),
1335 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
1336 .valtype1 = typeToValtype(scalar_type, func.target),
1337 });
1338 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
1339 .offset = value.offset(),
1340 .alignment = scalar_type.abiAlignment(func.target),
1341 });
1342 }
1323 },1343 },
1324 .Int, .Float => {1344 .Int, .Float => {
1325 if (ty_classes[1] == .none) {1345 if (ty_classes[1] == .none) {
...@@ -1472,12 +1492,15 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1472,12 +1492,15 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1472 // when the length is comptime-known, rather than a runtime value, we can optimize the generated code by having1492 // when the length is comptime-known, rather than a runtime value, we can optimize the generated code by having
1473 // the loop during codegen, rather than inserting a runtime loop into the binary.1493 // the loop during codegen, rather than inserting a runtime loop into the binary.
1474 switch (len) {1494 switch (len) {
1475 .imm32, .imm64 => {1495 .imm32, .imm64 => blk: {
1476 const length = switch (len) {1496 const length = switch (len) {
1477 .imm32 => |val| val,1497 .imm32 => |val| val,
1478 .imm64 => |val| val,1498 .imm64 => |val| val,
1479 else => unreachable,1499 else => unreachable,
1480 };1500 };
1501 // if the size (length) is more than 1024 bytes, we use a runtime loop instead to prevent
1502 // binary size bloat.
1503 if (length > 1024) break :blk;
1481 var offset: u32 = 0;1504 var offset: u32 = 0;
1482 const lhs_base = dst.offset();1505 const lhs_base = dst.offset();
1483 const rhs_base = src.offset();1506 const rhs_base = src.offset();
...@@ -1498,80 +1521,81 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1498,80 +1521,81 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1498 else => unreachable,1521 else => unreachable,
1499 }1522 }
1500 }1523 }
1524 return;
1501 },1525 },
1502 else => {1526 else => {},
1503 // TODO: We should probably lower this to a call to compiler_rt1527 }
1504 // But for now, we implement it manually
1505 var offset = try func.ensureAllocLocal(Type.usize); // local for counter
1506 defer offset.free(func);
15071528
1508 // outer block to jump to when loop is done1529 // TODO: We should probably lower this to a call to compiler_rt
1509 try func.startBlock(.block, wasm.block_empty);1530 // But for now, we implement it manually
1510 try func.startBlock(.loop, wasm.block_empty);1531 var offset = try func.ensureAllocLocal(Type.usize); // local for counter
1532 defer offset.free(func);
15111533
1512 // loop condition (offset == length -> break)1534 // outer block to jump to when loop is done
1513 {1535 try func.startBlock(.block, wasm.block_empty);
1514 try func.emitWValue(offset);1536 try func.startBlock(.loop, wasm.block_empty);
1515 try func.emitWValue(len);
1516 switch (func.arch()) {
1517 .wasm32 => try func.addTag(.i32_eq),
1518 .wasm64 => try func.addTag(.i64_eq),
1519 else => unreachable,
1520 }
1521 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
1522 }
15231537
1524 // get dst ptr1538 // loop condition (offset == length -> break)
1525 {1539 {
1526 try func.emitWValue(dst);1540 try func.emitWValue(offset);
1527 try func.emitWValue(offset);1541 try func.emitWValue(len);
1528 switch (func.arch()) {1542 switch (func.arch()) {
1529 .wasm32 => try func.addTag(.i32_add),1543 .wasm32 => try func.addTag(.i32_eq),
1530 .wasm64 => try func.addTag(.i64_add),1544 .wasm64 => try func.addTag(.i64_eq),
1531 else => unreachable,1545 else => unreachable,
1532 }1546 }
1533 }1547 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
1548 }
15341549
1535 // get src value and also store in dst1550 // get dst ptr
1536 {1551 {
1537 try func.emitWValue(src);1552 try func.emitWValue(dst);
1538 try func.emitWValue(offset);1553 try func.emitWValue(offset);
1539 switch (func.arch()) {1554 switch (func.arch()) {
1540 .wasm32 => {1555 .wasm32 => try func.addTag(.i32_add),
1541 try func.addTag(.i32_add);1556 .wasm64 => try func.addTag(.i64_add),
1542 try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });1557 else => unreachable,
1543 try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });1558 }
1544 },1559 }
1545 .wasm64 => {
1546 try func.addTag(.i64_add);
1547 try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1548 try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
1549 },
1550 else => unreachable,
1551 }
1552 }
15531560
1554 // increment loop counter1561 // get src value and also store in dst
1555 {1562 {
1556 try func.emitWValue(offset);1563 try func.emitWValue(src);
1557 switch (func.arch()) {1564 try func.emitWValue(offset);
1558 .wasm32 => {1565 switch (func.arch()) {
1559 try func.addImm32(1);1566 .wasm32 => {
1560 try func.addTag(.i32_add);1567 try func.addTag(.i32_add);
1561 },1568 try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1562 .wasm64 => {1569 try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
1563 try func.addImm64(1);1570 },
1564 try func.addTag(.i64_add);1571 .wasm64 => {
1565 },1572 try func.addTag(.i64_add);
1566 else => unreachable,1573 try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1567 }1574 try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
1568 try func.addLabel(.local_set, offset.local.value);1575 },
1569 try func.addLabel(.br, 0); // jump to start of loop1576 else => unreachable,
1570 }1577 }
1571 try func.endBlock(); // close off loop block
1572 try func.endBlock(); // close off outer block
1573 },
1574 }1578 }
1579
1580 // increment loop counter
1581 {
1582 try func.emitWValue(offset);
1583 switch (func.arch()) {
1584 .wasm32 => {
1585 try func.addImm32(1);
1586 try func.addTag(.i32_add);
1587 },
1588 .wasm64 => {
1589 try func.addImm64(1);
1590 try func.addTag(.i64_add);
1591 },
1592 else => unreachable,
1593 }
1594 try func.addLabel(.local_set, offset.local.value);
1595 try func.addLabel(.br, 0); // jump to start of loop
1596 }
1597 try func.endBlock(); // close off loop block
1598 try func.endBlock(); // close off outer block
1575}1599}
15761600
1577fn ptrSize(func: *const CodeGen) u16 {1601fn ptrSize(func: *const CodeGen) u16 {
...@@ -1607,10 +1631,18 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1607,10 +1631,18 @@ fn isByRef(ty: Type, target: std.Target) bool {
16071631
1608 .Array,1632 .Array,
1609 .Vector,1633 .Vector,
1610 .Struct,
1611 .Frame,1634 .Frame,
1612 .Union,1635 .Union,
1613 => return ty.hasRuntimeBitsIgnoreComptime(),1636 => return ty.hasRuntimeBitsIgnoreComptime(),
1637 .Struct => {
1638 if (ty.castTag(.@"struct")) |struct_ty| {
1639 const struct_obj = struct_ty.data;
1640 if (struct_obj.layout == .Packed and struct_obj.haveFieldTypes()) {
1641 return isByRef(struct_obj.backing_int_ty, target);
1642 }
1643 }
1644 return ty.hasRuntimeBitsIgnoreComptime();
1645 },
1614 .Int => return ty.intInfo(target).bits > 64,1646 .Int => return ty.intInfo(target).bits > 64,
1615 .Float => return ty.floatBits(target) > 64,1647 .Float => return ty.floatBits(target) > 64,
1616 .ErrorUnion => {1648 .ErrorUnion => {
...@@ -2100,9 +2132,48 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2100,9 +2132,48 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
21002132
2101 const lhs = try func.resolveInst(bin_op.lhs);2133 const lhs = try func.resolveInst(bin_op.lhs);
2102 const rhs = try func.resolveInst(bin_op.rhs);2134 const rhs = try func.resolveInst(bin_op.rhs);
2103 const ty = func.air.typeOf(bin_op.lhs).childType();2135 const ptr_ty = func.air.typeOf(bin_op.lhs);
2136 const ptr_info = ptr_ty.ptrInfo().data;
2137 const ty = ptr_ty.childType();
2138 if (ptr_info.host_size == 0) {
2139 try func.store(lhs, rhs, ty, 0);
2140 } else {
2141 // at this point we have a non-natural alignment, we must
2142 // load the value, and then shift+or the rhs into the result location.
2143 var int_ty_payload: Type.Payload.Bits = .{
2144 .base = .{ .tag = .int_unsigned },
2145 .data = ptr_info.host_size * 8,
2146 };
2147 const int_elem_ty = Type.initPayload(&int_ty_payload.base);
2148
2149 if (isByRef(int_elem_ty, func.target)) {
2150 return func.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{});
2151 }
2152
2153 var mask = @intCast(u64, (@as(u65, 1) << @intCast(u7, ty.bitSize(func.target))) - 1);
2154 mask <<= @intCast(u6, ptr_info.bit_offset);
2155 mask ^= ~@as(u64, 0);
2156 const shift_val = if (ptr_info.host_size <= 4)
2157 WValue{ .imm32 = ptr_info.bit_offset }
2158 else
2159 WValue{ .imm64 = ptr_info.bit_offset };
2160 const mask_val = if (ptr_info.host_size <= 4)
2161 WValue{ .imm32 = @truncate(u32, mask) }
2162 else
2163 WValue{ .imm64 = mask };
2164
2165 try func.emitWValue(lhs);
2166 const loaded = try func.load(lhs, int_elem_ty, 0);
2167 const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and");
2168 const extended_value = try func.intcast(rhs, ty, int_elem_ty);
2169 const shifted_value = if (ptr_info.bit_offset > 0) shifted: {
2170 break :shifted try func.binOp(extended_value, shift_val, int_elem_ty, .shl);
2171 } else extended_value;
2172 const result = try func.binOp(anded, shifted_value, int_elem_ty, .@"or");
2173 // lhs is still on the stack
2174 try func.store(.stack, result, int_elem_ty, lhs.offset());
2175 }
21042176
2105 try func.store(lhs, rhs, ty, 0);
2106 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });2177 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2107}2178}
21082179
...@@ -2134,7 +2205,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE...@@ -2134,7 +2205,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
2134 const len = @intCast(u32, ty.abiSize(func.target));2205 const len = @intCast(u32, ty.abiSize(func.target));
2135 return func.memcpy(lhs, rhs, .{ .imm32 = len });2206 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2136 },2207 },
2137 .Struct, .Array, .Union, .Vector => {2208 .Struct, .Array, .Union, .Vector => if (isByRef(ty, func.target)) {
2138 const len = @intCast(u32, ty.abiSize(func.target));2209 const len = @intCast(u32, ty.abiSize(func.target));
2139 return func.memcpy(lhs, rhs, .{ .imm32 = len });2210 return func.memcpy(lhs, rhs, .{ .imm32 = len });
2140 },2211 },
...@@ -2190,6 +2261,8 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2190,6 +2261,8 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2190 const ty_op = func.air.instructions.items(.data)[inst].ty_op;2261 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
2191 const operand = try func.resolveInst(ty_op.operand);2262 const operand = try func.resolveInst(ty_op.operand);
2192 const ty = func.air.getRefType(ty_op.ty);2263 const ty = func.air.getRefType(ty_op.ty);
2264 const ptr_ty = func.air.typeOf(ty_op.operand);
2265 const ptr_info = ptr_ty.ptrInfo().data;
21932266
2194 if (!ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{ty_op.operand});2267 if (!ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{ty_op.operand});
21952268
...@@ -2200,8 +2273,30 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2200,8 +2273,30 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2200 break :result new_local;2273 break :result new_local;
2201 }2274 }
22022275
2203 const stack_loaded = try func.load(operand, ty, 0);2276 if (ptr_info.host_size == 0) {
2204 break :result try stack_loaded.toLocal(func, ty);2277 const stack_loaded = try func.load(operand, ty, 0);
2278 break :result try stack_loaded.toLocal(func, ty);
2279 }
2280
2281 // at this point we have a non-natural alignment, we must
2282 // shift the value to obtain the correct bit.
2283 var int_ty_payload: Type.Payload.Bits = .{
2284 .base = .{ .tag = .int_unsigned },
2285 .data = ptr_info.host_size * 8,
2286 };
2287 const int_elem_ty = Type.initPayload(&int_ty_payload.base);
2288 const shift_val = if (ptr_info.host_size <= 4)
2289 WValue{ .imm32 = ptr_info.bit_offset }
2290 else if (ptr_info.host_size <= 8)
2291 WValue{ .imm64 = ptr_info.bit_offset }
2292 else
2293 return func.fail("TODO: airLoad where ptr to bitfield exceeds 64 bits", .{});
2294
2295 const stack_loaded = try func.load(operand, int_elem_ty, 0);
2296 const shifted = try func.binOp(stack_loaded, shift_val, int_elem_ty, .shr);
2297 const result = try func.trunc(shifted, ty, int_elem_ty);
2298 // const wrapped = try func.wrapOperand(shifted, ty);
2299 break :result try result.toLocal(func, ty);
2205 };2300 };
2206 func.finishAir(inst, result, &.{ty_op.operand});2301 func.finishAir(inst, result, &.{ty_op.operand});
2207}2302}
...@@ -2406,7 +2501,7 @@ fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr...@@ -2406,7 +2501,7 @@ fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr
2406/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.2501/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.
2407fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {2502fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
2408 assert(ty.abiSize(func.target) <= 16);2503 assert(ty.abiSize(func.target) <= 16);
2409 const bitsize = ty.intInfo(func.target).bits;2504 const bitsize = @intCast(u16, ty.bitSize(func.target));
2410 const wasm_bits = toWasmBits(bitsize) orelse {2505 const wasm_bits = toWasmBits(bitsize) orelse {
2411 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});2506 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
2412 };2507 };
...@@ -2462,18 +2557,21 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError...@@ -2462,18 +2557,21 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError
2462 const parent_ptr = try func.lowerParentPtr(field_ptr.container_ptr, parent_ty);2557 const parent_ptr = try func.lowerParentPtr(field_ptr.container_ptr, parent_ty);
24632558
2464 const offset = switch (parent_ty.zigTypeTag()) {2559 const offset = switch (parent_ty.zigTypeTag()) {
2465 .Struct => blk: {2560 .Struct => switch (parent_ty.containerLayout()) {
2466 const offset = parent_ty.structFieldOffset(field_ptr.field_index, func.target);2561 .Packed => parent_ty.packedStructFieldByteOffset(field_ptr.field_index, func.target),
2467 break :blk offset;2562 else => parent_ty.structFieldOffset(field_ptr.field_index, func.target),
2468 },2563 },
2469 .Union => blk: {2564 .Union => switch (parent_ty.containerLayout()) {
2470 const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target);2565 .Packed => 0,
2471 if (layout.payload_size == 0) break :blk 0;2566 else => blk: {
2472 if (layout.payload_align > layout.tag_align) break :blk 0;2567 const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target);
24732568 if (layout.payload_size == 0) break :blk 0;
2474 // tag is stored first so calculate offset from where payload starts2569 if (layout.payload_align > layout.tag_align) break :blk 0;
2475 const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align));2570
2476 break :blk offset;2571 // tag is stored first so calculate offset from where payload starts
2572 const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align));
2573 break :blk offset;
2574 },
2477 },2575 },
2478 .Pointer => switch (parent_ty.ptrSize()) {2576 .Pointer => switch (parent_ty.ptrSize()) {
2479 .Slice => switch (field_ptr.field_index) {2577 .Slice => switch (field_ptr.field_index) {
...@@ -2689,6 +2787,18 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {...@@ -2689,6 +2787,18 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
2689 const is_pl = val.tag() == .opt_payload;2787 const is_pl = val.tag() == .opt_payload;
2690 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };2788 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
2691 },2789 },
2790 .Struct => {
2791 const struct_obj = ty.castTag(.@"struct").?.data;
2792 assert(struct_obj.layout == .Packed);
2793 var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer
2794 val.writeToPackedMemory(ty, func.bin_file.base.options.module.?, &buf, 0);
2795 var payload: Value.Payload.U64 = .{
2796 .base = .{ .tag = .int_u64 },
2797 .data = std.mem.readIntLittle(u64, &buf),
2798 };
2799 const int_val = Value.initPayload(&payload.base);
2800 return func.lowerConstant(int_val, struct_obj.backing_int_ty);
2801 },
2692 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),2802 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
2693 }2803 }
2694}2804}
...@@ -2723,6 +2833,11 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {...@@ -2723,6 +2833,11 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
2723 .ErrorUnion => {2833 .ErrorUnion => {
2724 return WValue{ .imm32 = 0xaaaaaaaa };2834 return WValue{ .imm32 = 0xaaaaaaaa };
2725 },2835 },
2836 .Struct => {
2837 const struct_obj = ty.castTag(.@"struct").?.data;
2838 assert(struct_obj.layout == .Packed);
2839 return func.emitUndefined(struct_obj.backing_int_ty);
2840 },
2726 else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),2841 else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),
2727 }2842 }
2728}2843}
...@@ -3070,11 +3185,32 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3070,11 +3185,32 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3070 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3185 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3071 const result = if (!func.liveness.isUnused(inst)) result: {3186 const result = if (!func.liveness.isUnused(inst)) result: {
3072 const operand = try func.resolveInst(ty_op.operand);3187 const operand = try func.resolveInst(ty_op.operand);
3188 const wanted_ty = func.air.typeOfIndex(inst);
3189 const given_ty = func.air.typeOf(ty_op.operand);
3190 if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) {
3191 const bitcast_result = try func.bitcast(wanted_ty, given_ty, operand);
3192 break :result try bitcast_result.toLocal(func, wanted_ty);
3193 }
3073 break :result func.reuseOperand(ty_op.operand, operand);3194 break :result func.reuseOperand(ty_op.operand, operand);
3074 } else WValue{ .none = {} };3195 } else WValue{ .none = {} };
3075 func.finishAir(inst, result, &.{});3196 func.finishAir(inst, result, &.{});
3076}3197}
30773198
3199fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue {
3200 // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction
3201 if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand;
3202 assert((wanted_ty.isInt() and given_ty.isAnyFloat()) or (wanted_ty.isAnyFloat() and given_ty.isInt()));
3203
3204 const opcode = buildOpcode(.{
3205 .op = .reinterpret,
3206 .valtype1 = typeToValtype(wanted_ty, func.target),
3207 .valtype2 = typeToValtype(given_ty, func.target),
3208 });
3209 try func.emitWValue(operand);
3210 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3211 return WValue{ .stack = {} };
3212}
3213
3078fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3214fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3079 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;3215 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3080 const extra = func.air.extraData(Air.StructField, ty_pl.payload);3216 const extra = func.air.extraData(Air.StructField, ty_pl.payload);
...@@ -3082,13 +3218,7 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3082,13 +3218,7 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
30823218
3083 const struct_ptr = try func.resolveInst(extra.data.struct_operand);3219 const struct_ptr = try func.resolveInst(extra.data.struct_operand);
3084 const struct_ty = func.air.typeOf(extra.data.struct_operand).childType();3220 const struct_ty = func.air.typeOf(extra.data.struct_operand).childType();
3085 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, func.target)) orelse {3221 const result = try func.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ty, extra.data.field_index);
3086 const module = func.bin_file.base.options.module.?;
3087 return func.fail("Field type '{}' too big to fit into stack frame", .{
3088 struct_ty.structFieldType(extra.data.field_index).fmt(module),
3089 });
3090 };
3091 const result = try func.structFieldPtr(struct_ptr, offset);
3092 func.finishAir(inst, result, &.{extra.data.struct_operand});3222 func.finishAir(inst, result, &.{extra.data.struct_operand});
3093}3223}
30943224
...@@ -3097,21 +3227,40 @@ fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) Inne...@@ -3097,21 +3227,40 @@ fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) Inne
3097 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});3227 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
3098 const struct_ptr = try func.resolveInst(ty_op.operand);3228 const struct_ptr = try func.resolveInst(ty_op.operand);
3099 const struct_ty = func.air.typeOf(ty_op.operand).childType();3229 const struct_ty = func.air.typeOf(ty_op.operand).childType();
3100 const field_ty = struct_ty.structFieldType(index);3230
3101 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, func.target)) orelse {3231 const result = try func.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ty, index);
3102 const module = func.bin_file.base.options.module.?;
3103 return func.fail("Field type '{}' too big to fit into stack frame", .{
3104 field_ty.fmt(module),
3105 });
3106 };
3107 const result = try func.structFieldPtr(struct_ptr, offset);
3108 func.finishAir(inst, result, &.{ty_op.operand});3232 func.finishAir(inst, result, &.{ty_op.operand});
3109}3233}
31103234
3111fn structFieldPtr(func: *CodeGen, struct_ptr: WValue, offset: u32) InnerError!WValue {3235fn structFieldPtr(
3236 func: *CodeGen,
3237 inst: Air.Inst.Index,
3238 ref: Air.Inst.Ref,
3239 struct_ptr: WValue,
3240 struct_ty: Type,
3241 index: u32,
3242) InnerError!WValue {
3243 const result_ty = func.air.typeOfIndex(inst);
3244 const offset = switch (struct_ty.containerLayout()) {
3245 .Packed => switch (struct_ty.zigTypeTag()) {
3246 .Struct => offset: {
3247 if (result_ty.ptrInfo().data.host_size != 0) {
3248 break :offset @as(u32, 0);
3249 }
3250 break :offset struct_ty.packedStructFieldByteOffset(index, func.target);
3251 },
3252 .Union => 0,
3253 else => unreachable,
3254 },
3255 else => struct_ty.structFieldOffset(index, func.target),
3256 };
3257 // save a load and store when we can simply reuse the operand
3258 if (offset == 0) {
3259 return func.reuseOperand(ref, struct_ptr);
3260 }
3112 switch (struct_ptr) {3261 switch (struct_ptr) {
3113 .stack_offset => |stack_offset| {3262 .stack_offset => |stack_offset| {
3114 return WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };3263 return WValue{ .stack_offset = .{ .value = stack_offset.value + @intCast(u32, offset), .references = 1 } };
3115 },3264 },
3116 else => return func.buildPointerOffset(struct_ptr, offset, .new),3265 else => return func.buildPointerOffset(struct_ptr, offset, .new),
3117 }3266 }
...@@ -3128,24 +3277,75 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3128,24 +3277,75 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3128 const field_ty = struct_ty.structFieldType(field_index);3277 const field_ty = struct_ty.structFieldType(field_index);
3129 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{struct_field.struct_operand});3278 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{struct_field.struct_operand});
31303279
3131 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, func.target)) orelse {3280 const result = switch (struct_ty.containerLayout()) {
3132 const module = func.bin_file.base.options.module.?;3281 .Packed => switch (struct_ty.zigTypeTag()) {
3133 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});3282 .Struct => result: {
3134 };3283 const struct_obj = struct_ty.castTag(.@"struct").?.data;
31353284 assert(struct_obj.layout == .Packed);
3136 const result = result: {3285 const offset = struct_obj.packedFieldBitOffset(func.target, field_index);
3137 if (isByRef(field_ty, func.target)) {3286 const backing_ty = struct_obj.backing_int_ty;
3138 switch (operand) {3287 const wasm_bits = toWasmBits(backing_ty.intInfo(func.target).bits) orelse {
3139 .stack_offset => |stack_offset| {3288 return func.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{});
3140 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };3289 };
3141 },3290 const const_wvalue = if (wasm_bits == 32)
3142 else => break :result try func.buildPointerOffset(operand, offset, .new),3291 WValue{ .imm32 = offset }
3292 else if (wasm_bits == 64)
3293 WValue{ .imm64 = offset }
3294 else
3295 return func.fail("TODO: airStructFieldVal for packed structs larger than 64 bits", .{});
3296
3297 // for first field we don't require any shifting
3298 const shifted_value = if (offset == 0)
3299 operand
3300 else
3301 try func.binOp(operand, const_wvalue, backing_ty, .shr);
3302
3303 if (field_ty.zigTypeTag() == .Float) {
3304 var payload: Type.Payload.Bits = .{
3305 .base = .{ .tag = .int_unsigned },
3306 .data = @intCast(u16, field_ty.bitSize(func.target)),
3307 };
3308 const int_type = Type.initPayload(&payload.base);
3309 const truncated = try func.trunc(shifted_value, int_type, backing_ty);
3310 const bitcasted = try func.bitcast(field_ty, int_type, truncated);
3311 break :result try bitcasted.toLocal(func, field_ty);
3312 } else if (field_ty.isPtrAtRuntime() and struct_obj.fields.count() == 1) {
3313 // In this case we do not have to perform any transformations,
3314 // we can simply reuse the operand.
3315 break :result func.reuseOperand(struct_field.struct_operand, operand);
3316 } else if (field_ty.isPtrAtRuntime()) {
3317 var payload: Type.Payload.Bits = .{
3318 .base = .{ .tag = .int_unsigned },
3319 .data = @intCast(u16, field_ty.bitSize(func.target)),
3320 };
3321 const int_type = Type.initPayload(&payload.base);
3322 const truncated = try func.trunc(shifted_value, int_type, backing_ty);
3323 break :result try truncated.toLocal(func, field_ty);
3324 }
3325 const truncated = try func.trunc(shifted_value, field_ty, backing_ty);
3326 break :result try truncated.toLocal(func, field_ty);
3327 },
3328 .Union => return func.fail("TODO: airStructFieldVal for packed unions", .{}),
3329 else => unreachable,
3330 },
3331 else => result: {
3332 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, func.target)) orelse {
3333 const module = func.bin_file.base.options.module.?;
3334 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
3335 };
3336 if (isByRef(field_ty, func.target)) {
3337 switch (operand) {
3338 .stack_offset => |stack_offset| {
3339 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
3340 },
3341 else => break :result try func.buildPointerOffset(operand, offset, .new),
3342 }
3143 }3343 }
3144 }3344 const field = try func.load(operand, field_ty, offset);
31453345 break :result try field.toLocal(func, field_ty);
3146 const field = try func.load(operand, field_ty, offset);3346 },
3147 break :result try field.toLocal(func, field_ty);
3148 };3347 };
3348
3149 func.finishAir(inst, result, &.{struct_field.struct_operand});3349 func.finishAir(inst, result, &.{struct_field.struct_operand});
3150}3350}
31513351
...@@ -3492,13 +3692,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3492,13 +3692,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3492/// Asserts type's bitsize <= 1283692/// Asserts type's bitsize <= 128
3493/// NOTE: May leave the result on the top of the stack.3693/// NOTE: May leave the result on the top of the stack.
3494fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {3694fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
3495 const given_info = given.intInfo(func.target);3695 const given_bitsize = @intCast(u16, given.bitSize(func.target));
3496 const wanted_info = wanted.intInfo(func.target);3696 const wanted_bitsize = @intCast(u16, wanted.bitSize(func.target));
3497 assert(given_info.bits <= 128);3697 assert(given_bitsize <= 128);
3498 assert(wanted_info.bits <= 128);3698 assert(wanted_bitsize <= 128);
34993699
3500 const op_bits = toWasmBits(given_info.bits).?;3700 const op_bits = toWasmBits(given_bitsize).?;
3501 const wanted_bits = toWasmBits(wanted_info.bits).?;3701 const wanted_bits = toWasmBits(wanted_bitsize).?;
3502 if (op_bits == wanted_bits) return operand;3702 if (op_bits == wanted_bits) return operand;
35033703
3504 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {3704 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {
...@@ -3506,10 +3706,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -3506,10 +3706,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
3506 try func.addTag(.i32_wrap_i64);3706 try func.addTag(.i32_wrap_i64);
3507 } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) {3707 } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) {
3508 try func.emitWValue(operand);3708 try func.emitWValue(operand);
3509 try func.addTag(switch (wanted_info.signedness) {3709 try func.addTag(if (wanted.isSignedInt()) .i64_extend_i32_s else .i64_extend_i32_u);
3510 .signed => .i64_extend_i32_s,
3511 .unsigned => .i64_extend_i32_u,
3512 });
3513 } else if (wanted_bits == 128) {3710 } else if (wanted_bits == 128) {
3514 // for 128bit integers we store the integer in the virtual stack, rather than a local3711 // for 128bit integers we store the integer in the virtual stack, rather than a local
3515 const stack_ptr = try func.allocStack(wanted);3712 const stack_ptr = try func.allocStack(wanted);
...@@ -3725,7 +3922,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3725,7 +3922,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3725 try func.addTag(.i32_mul);3922 try func.addTag(.i32_mul);
3726 try func.addTag(.i32_add);3923 try func.addTag(.i32_add);
37273924
3728 const result_ptr = try func.allocLocal(elem_ty);3925 const result_ptr = try func.allocLocal(Type.usize);
3729 try func.addLabel(.local_set, result_ptr.local.value);3926 try func.addLabel(.local_set, result_ptr.local.value);
37303927
3731 const result = if (!isByRef(elem_ty, func.target)) result: {3928 const result = if (!isByRef(elem_ty, func.target)) result: {
...@@ -3777,19 +3974,25 @@ fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3777,19 +3974,25 @@ fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3777 const wanted_ty = func.air.getRefType(ty_op.ty);3974 const wanted_ty = func.air.getRefType(ty_op.ty);
3778 const op_ty = func.air.typeOf(ty_op.operand);3975 const op_ty = func.air.typeOf(ty_op.operand);
37793976
3780 const int_info = op_ty.intInfo(func.target);3977 const result = try func.trunc(operand, wanted_ty, op_ty);
3781 if (toWasmBits(int_info.bits) == null) {3978 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
3782 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});3979}
3980
3981/// Truncates a given operand to a given type, discarding any overflown bits.
3982/// NOTE: Resulting value is left on the stack.
3983fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue {
3984 const given_bits = @intCast(u16, given_ty.bitSize(func.target));
3985 if (toWasmBits(given_bits) == null) {
3986 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{given_bits});
3783 }3987 }
37843988
3785 var result = try func.intcast(operand, op_ty, wanted_ty);3989 var result = try func.intcast(operand, given_ty, wanted_ty);
3786 const wanted_bits = wanted_ty.intInfo(func.target).bits;3990 const wanted_bits = @intCast(u16, wanted_ty.bitSize(func.target));
3787 const wasm_bits = toWasmBits(wanted_bits).?;3991 const wasm_bits = toWasmBits(wanted_bits).?;
3788 if (wasm_bits != wanted_bits) {3992 if (wasm_bits != wanted_bits) {
3789 result = try func.wrapOperand(result, wanted_ty);3993 result = try func.wrapOperand(result, wanted_ty);
3790 }3994 }
37913995 return result;
3792 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
3793}3996}
37943997
3795fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3998fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -4186,23 +4389,68 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4186,23 +4389,68 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4186 }4389 }
4187 break :result_value result;4390 break :result_value result;
4188 },4391 },
4189 .Struct => {4392 .Struct => switch (result_ty.containerLayout()) {
4190 const result = try func.allocStack(result_ty);4393 .Packed => {
4191 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset4394 if (isByRef(result_ty, func.target)) {
4192 for (elements) |elem, elem_index| {4395 return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});
4193 if (result_ty.structFieldValueComptime(elem_index) != null) continue;4396 }
4397 const struct_obj = result_ty.castTag(.@"struct").?.data;
4398 const fields = struct_obj.fields.values();
4399 const backing_type = struct_obj.backing_int_ty;
4400 // we ensure a new local is created so it's zero-initialized
4401 const result = try func.ensureAllocLocal(backing_type);
4402 var current_bit: u16 = 0;
4403 for (elements) |elem, elem_index| {
4404 const field = fields[elem_index];
4405 if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue;
4406
4407 const shift_val = if (struct_obj.backing_int_ty.bitSize(func.target) <= 32)
4408 WValue{ .imm32 = current_bit }
4409 else
4410 WValue{ .imm64 = current_bit };
4411
4412 const value = try func.resolveInst(elem);
4413 const value_bit_size = @intCast(u16, field.ty.bitSize(func.target));
4414 var int_ty_payload: Type.Payload.Bits = .{
4415 .base = .{ .tag = .int_unsigned },
4416 .data = value_bit_size,
4417 };
4418 const int_ty = Type.initPayload(&int_ty_payload.base);
4419
4420 // load our current result on stack so we can perform all transformations
4421 // using only stack values. Saving the cost of loads and stores.
4422 try func.emitWValue(result);
4423 const bitcasted = try func.bitcast(int_ty, field.ty, value);
4424 const extended_val = try func.intcast(bitcasted, int_ty, backing_type);
4425 // no need to shift any values when the current offset is 0
4426 const shifted = if (current_bit != 0) shifted: {
4427 break :shifted try func.binOp(extended_val, shift_val, backing_type, .shl);
4428 } else extended_val;
4429 // we ignore the result as we keep it on the stack to assign it directly to `result`
4430 _ = try func.binOp(.stack, shifted, backing_type, .@"or");
4431 try func.addLabel(.local_set, result.local.value);
4432 current_bit += value_bit_size;
4433 }
4434 break :result_value result;
4435 },
4436 else => {
4437 const result = try func.allocStack(result_ty);
4438 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset
4439 for (elements) |elem, elem_index| {
4440 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
41944441
4195 const elem_ty = result_ty.structFieldType(elem_index);4442 const elem_ty = result_ty.structFieldType(elem_index);
4196 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));4443 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
4197 const value = try func.resolveInst(elem);4444 const value = try func.resolveInst(elem);
4198 try func.store(offset, value, elem_ty, 0);4445 try func.store(offset, value, elem_ty, 0);
41994446
4200 if (elem_index < elements.len - 1) {4447 if (elem_index < elements.len - 1) {
4201 _ = try func.buildPointerOffset(offset, elem_size, .modify);4448 _ = try func.buildPointerOffset(offset, elem_size, .modify);
4449 }
4202 }4450 }
4203 }
42044451
4205 break :result_value result;4452 break :result_value result;
4453 },
4206 },4454 },
4207 .Vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),4455 .Vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
4208 else => unreachable,4456 else => unreachable,
...@@ -4444,8 +4692,8 @@ fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4444,8 +4692,8 @@ fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44444692
4445 const dest_ty = func.air.typeOfIndex(inst);4693 const dest_ty = func.air.typeOfIndex(inst);
4446 const operand = try func.resolveInst(ty_op.operand);4694 const operand = try func.resolveInst(ty_op.operand);
4447 const trunc = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);4695 const truncated = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);
4448 const result = try trunc.toLocal(func, dest_ty);4696 const result = try truncated.toLocal(func, dest_ty);
4449 func.finishAir(inst, result, &.{ty_op.operand});4697 func.finishAir(inst, result, &.{ty_op.operand});
4450}4698}
44514699
src/arch/wasm/abi.zig+16-6
...@@ -94,15 +94,25 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {...@@ -94,15 +94,25 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
94pub fn scalarType(ty: Type, target: std.Target) Type {94pub fn scalarType(ty: Type, target: std.Target) Type {
95 switch (ty.zigTypeTag()) {95 switch (ty.zigTypeTag()) {
96 .Struct => {96 .Struct => {
97 std.debug.assert(ty.structFieldCount() == 1);97 switch (ty.containerLayout()) {
98 return scalarType(ty.structFieldType(0), target);98 .Packed => {
99 const struct_obj = ty.castTag(.@"struct").?.data;
100 return scalarType(struct_obj.backing_int_ty, target);
101 },
102 else => {
103 std.debug.assert(ty.structFieldCount() == 1);
104 return scalarType(ty.structFieldType(0), target);
105 },
106 }
99 },107 },
100 .Union => {108 .Union => {
101 const layout = ty.unionGetLayout(target);109 if (ty.containerLayout() != .Packed) {
102 if (layout.payload_size == 0 and layout.tag_size != 0) {110 const layout = ty.unionGetLayout(target);
103 return scalarType(ty.unionTagTypeSafety().?, target);111 if (layout.payload_size == 0 and layout.tag_size != 0) {
112 return scalarType(ty.unionTagTypeSafety().?, target);
113 }
114 std.debug.assert(ty.unionFields().count() == 1);
104 }115 }
105 std.debug.assert(ty.unionFields().count() == 1);
106 return scalarType(ty.unionFields().values()[0].ty, target);116 return scalarType(ty.unionFields().values()[0].ty, target);
107 },117 },
108 else => return ty,118 else => return ty,
src/codegen.zig+36-8
...@@ -556,14 +556,42 @@ pub fn generateSymbol(...@@ -556,14 +556,42 @@ pub fn generateSymbol(
556 },556 },
557 .Struct => {557 .Struct => {
558 if (typed_value.ty.containerLayout() == .Packed) {558 if (typed_value.ty.containerLayout() == .Packed) {
559 return Result{559 const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
560 .fail = try ErrorMsg.create(560 const fields = struct_obj.fields.values();
561 bin_file.allocator,561 const field_vals = typed_value.val.castTag(.aggregate).?.data;
562 src_loc,562 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;
563 "TODO implement generateSymbol for packed struct",563 const current_pos = code.items.len;
564 .{},564 const mod = bin_file.options.module.?;
565 ),565 try code.resize(current_pos + abi_size);
566 };566 var bits: u16 = 0;
567
568 for (field_vals) |field_val, index| {
569 const field_ty = fields[index].ty;
570 // pointer may point to a decl which must be marked used
571 // but can also result in a relocation. Therefore we handle those seperately.
572 if (field_ty.zigTypeTag() == .Pointer) {
573 const field_size = math.cast(usize, field_ty.abiSize(target)) orelse return error.Overflow;
574 var tmp_list = try std.ArrayList(u8).initCapacity(code.allocator, field_size);
575 defer tmp_list.deinit();
576 switch (try generateSymbol(bin_file, src_loc, .{
577 .ty = field_ty,
578 .val = field_val,
579 }, &tmp_list, debug_output, reloc_info)) {
580 .appended => {
581 mem.copy(u8, code.items[current_pos..], tmp_list.items);
582 },
583 .externally_managed => |external_slice| {
584 mem.copy(u8, code.items[current_pos..], external_slice);
585 },
586 .fail => |em| return Result{ .fail = em },
587 }
588 } else {
589 field_val.writeToPackedMemory(field_ty, mod, code.items[current_pos..], bits);
590 }
591 bits += @intCast(u16, field_ty.bitSize(target));
592 }
593
594 return Result{ .appended = {} };
567 }595 }
568596
569 const struct_begin = code.items.len;597 const struct_begin = code.items.len;
src/link/Wasm.zig+3-2
...@@ -1841,8 +1841,9 @@ fn setupStart(wasm: *Wasm) !void {...@@ -1841,8 +1841,9 @@ fn setupStart(wasm: *Wasm) !void {
1841/// Sets up the memory section of the wasm module, as well as the stack.1841/// Sets up the memory section of the wasm module, as well as the stack.
1842fn setupMemory(wasm: *Wasm) !void {1842fn setupMemory(wasm: *Wasm) !void {
1843 log.debug("Setting up memory layout", .{});1843 log.debug("Setting up memory layout", .{});
1844 const page_size = 64 * 1024;1844 const page_size = std.wasm.page_size; // 64kb
1845 const stack_size = wasm.base.options.stack_size_override orelse page_size * 1;1845 // Use the user-provided stack size or else we use 1MB by default
1846 const stack_size = wasm.base.options.stack_size_override orelse page_size * 16;
1846 const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention1847 const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention
1847 // Always place the stack at the start by default1848 // Always place the stack at the start by default
1848 // unless the user specified the global-base flag1849 // unless the user specified the global-base flag
test/behavior/bitcast.zig-6
...@@ -18,7 +18,6 @@ test "@bitCast iX -> uX (32, 64)" {...@@ -18,7 +18,6 @@ test "@bitCast iX -> uX (32, 64)" {
18}18}
1919
20test "@bitCast iX -> uX (8, 16, 128)" {20test "@bitCast iX -> uX (8, 16, 128)" {
21 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
22 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;21 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;22 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
24 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;23 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -160,7 +159,6 @@ test "@bitCast packed structs at runtime and comptime" {...@@ -160,7 +159,6 @@ test "@bitCast packed structs at runtime and comptime" {
160 // stage1 gets the wrong answer for a lot of targets159 // stage1 gets the wrong answer for a lot of targets
161 return error.SkipZigTest;160 return error.SkipZigTest;
162 }161 }
163 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
164 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;162 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
165 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
166 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;164 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -187,7 +185,6 @@ test "@bitCast packed structs at runtime and comptime" {...@@ -187,7 +185,6 @@ test "@bitCast packed structs at runtime and comptime" {
187}185}
188186
189test "@bitCast extern structs at runtime and comptime" {187test "@bitCast extern structs at runtime and comptime" {
190 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
191 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;188 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
192189
193 const Full = extern struct {190 const Full = extern struct {
...@@ -218,7 +215,6 @@ test "@bitCast extern structs at runtime and comptime" {...@@ -218,7 +215,6 @@ test "@bitCast extern structs at runtime and comptime" {
218}215}
219216
220test "bitcast packed struct to integer and back" {217test "bitcast packed struct to integer and back" {
221 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
222 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;218 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
223 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;219 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
224 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;220 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -257,8 +253,6 @@ test "implicit cast to error union by returning" {...@@ -257,8 +253,6 @@ test "implicit cast to error union by returning" {
257}253}
258254
259test "bitcast packed struct literal to byte" {255test "bitcast packed struct literal to byte" {
260 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
261
262 const Foo = packed struct {256 const Foo = packed struct {
263 value: u8,257 value: u8,
264 };258 };
test/behavior/bugs/12776.zig-1
...@@ -31,7 +31,6 @@ const CPU = packed struct {...@@ -31,7 +31,6 @@ const CPU = packed struct {
31test {31test {
32 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;32 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
33 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;33 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
34 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
35 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;34 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
3635
37 var ram = try RAM.new();36 var ram = try RAM.new();
test/behavior/bugs/9584.zig-1
...@@ -44,7 +44,6 @@ pub fn b(x: *X) !void {...@@ -44,7 +44,6 @@ pub fn b(x: *X) !void {
44}44}
4545
46test {46test {
47 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
48 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO47 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO48 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO49 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/enum.zig-2
...@@ -1146,8 +1146,6 @@ test "size of enum with only one tag which has explicit integer tag type" {...@@ -1146,8 +1146,6 @@ test "size of enum with only one tag which has explicit integer tag type" {
1146}1146}
11471147
1148test "switch on an extern enum with negative value" {1148test "switch on an extern enum with negative value" {
1149 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest;
1150
1151 const Foo = enum(c_int) {1149 const Foo = enum(c_int) {
1152 Bar = -1,1150 Bar = -1,
1153 };1151 };
test/behavior/packed-struct.zig-9
...@@ -150,7 +150,6 @@ test "consistent size of packed structs" {...@@ -150,7 +150,6 @@ test "consistent size of packed structs" {
150150
151test "correct sizeOf and offsets in packed structs" {151test "correct sizeOf and offsets in packed structs" {
152 if (builtin.zig_backend == .stage1) return error.SkipZigTest;152 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
153 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
154 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO153 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
155 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO154 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
156 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO155 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -220,7 +219,6 @@ test "correct sizeOf and offsets in packed structs" {...@@ -220,7 +219,6 @@ test "correct sizeOf and offsets in packed structs" {
220219
221test "nested packed structs" {220test "nested packed structs" {
222 if (builtin.zig_backend == .stage1) return error.SkipZigTest;221 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
223 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
224 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO222 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
225 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO223 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
226 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO224 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -267,7 +265,6 @@ test "nested packed structs" {...@@ -267,7 +265,6 @@ test "nested packed structs" {
267}265}
268266
269test "regular in irregular packed struct" {267test "regular in irregular packed struct" {
270 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
271 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;268 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
272 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;269 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
273 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;270 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -289,7 +286,6 @@ test "regular in irregular packed struct" {...@@ -289,7 +286,6 @@ test "regular in irregular packed struct" {
289286
290test "byte-aligned field pointer offsets" {287test "byte-aligned field pointer offsets" {
291 if (builtin.zig_backend == .stage1) return error.SkipZigTest;288 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
292 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
293 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;289 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
294 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;290 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
295 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;291 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -395,7 +391,6 @@ test "load pointer from packed struct" {...@@ -395,7 +391,6 @@ test "load pointer from packed struct" {
395 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;391 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
396 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;392 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
397 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;393 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
398 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
399394
400 const A = struct {395 const A = struct {
401 index: u16,396 index: u16,
...@@ -413,7 +408,6 @@ test "load pointer from packed struct" {...@@ -413,7 +408,6 @@ test "load pointer from packed struct" {
413}408}
414409
415test "@ptrToInt on a packed struct field" {410test "@ptrToInt on a packed struct field" {
416 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
417 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;411 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
418 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;412 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
419 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;413 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -437,7 +431,6 @@ test "optional pointer in packed struct" {...@@ -437,7 +431,6 @@ test "optional pointer in packed struct" {
437 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;431 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
438 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;432 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
439 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;433 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
440 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
441434
442 const T = packed struct { ptr: ?*const u8 };435 const T = packed struct { ptr: ?*const u8 };
443 var n: u8 = 0;436 var n: u8 = 0;
...@@ -566,7 +559,6 @@ test "nested packed struct field access test" {...@@ -566,7 +559,6 @@ test "nested packed struct field access test" {
566test "runtime init of unnamed packed struct type" {559test "runtime init of unnamed packed struct type" {
567 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;560 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
568 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;561 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
569 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
570 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;562 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
571563
572 var z: u8 = 123;564 var z: u8 = 123;
...@@ -582,7 +574,6 @@ test "packed struct passed to callconv(.C) function" {...@@ -582,7 +574,6 @@ test "packed struct passed to callconv(.C) function" {
582 if (builtin.zig_backend == .stage1) return error.SkipZigTest;574 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
583 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;575 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
584 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;576 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
585 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
586 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;577 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
587578
588 const S = struct {579 const S = struct {
test/behavior/struct.zig-12
...@@ -386,7 +386,6 @@ const APackedStruct = packed struct {...@@ -386,7 +386,6 @@ const APackedStruct = packed struct {
386386
387test "packed struct" {387test "packed struct" {
388 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;388 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
389 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
390 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO389 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
391 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO390 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
392 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO391 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -460,7 +459,6 @@ test "packed struct 24bits" {...@@ -460,7 +459,6 @@ test "packed struct 24bits" {
460459
461test "runtime struct initialization of bitfield" {460test "runtime struct initialization of bitfield" {
462 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;461 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
463 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
464 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO462 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
465 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO463 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
466 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO464 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -504,7 +502,6 @@ test "packed struct fields are ordered from LSB to MSB" {...@@ -504,7 +502,6 @@ test "packed struct fields are ordered from LSB to MSB" {
504 return error.SkipZigTest;502 return error.SkipZigTest;
505 }503 }
506 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO504 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
507 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
508 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO505 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
509 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO506 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
510 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO507 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -525,7 +522,6 @@ test "packed struct fields are ordered from LSB to MSB" {...@@ -525,7 +522,6 @@ test "packed struct fields are ordered from LSB to MSB" {
525522
526test "implicit cast packed struct field to const ptr" {523test "implicit cast packed struct field to const ptr" {
527 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;524 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
528 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
529 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO525 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
530 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO526 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
531 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO527 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -546,7 +542,6 @@ test "implicit cast packed struct field to const ptr" {...@@ -546,7 +542,6 @@ test "implicit cast packed struct field to const ptr" {
546}542}
547543
548test "zero-bit field in packed struct" {544test "zero-bit field in packed struct" {
549 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
550 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO545 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
551 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO546 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
552547
...@@ -591,7 +586,6 @@ const bit_field_1 = BitField1{...@@ -591,7 +586,6 @@ const bit_field_1 = BitField1{
591586
592test "bit field access" {587test "bit field access" {
593 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;588 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
594 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
595 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO589 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
596 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO590 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
597 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO591 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -647,7 +641,6 @@ test "default struct initialization fields" {...@@ -647,7 +641,6 @@ test "default struct initialization fields" {
647641
648test "packed array 24bits" {642test "packed array 24bits" {
649 if (builtin.zig_backend == .stage1) return error.SkipZigTest;643 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
650 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
651 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;644 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
652 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;645 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
653 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -735,7 +728,6 @@ const FooArrayOfAligned = packed struct {...@@ -735,7 +728,6 @@ const FooArrayOfAligned = packed struct {
735};728};
736729
737test "pointer to packed struct member in a stack variable" {730test "pointer to packed struct member in a stack variable" {
738 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
739 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO731 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
740 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO732 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
741 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO733 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -754,7 +746,6 @@ test "pointer to packed struct member in a stack variable" {...@@ -754,7 +746,6 @@ test "pointer to packed struct member in a stack variable" {
754}746}
755747
756test "packed struct with u0 field access" {748test "packed struct with u0 field access" {
757 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
758 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO749 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
759 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO750 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
760751
...@@ -864,7 +855,6 @@ test "non-packed struct with u128 entry in union" {...@@ -864,7 +855,6 @@ test "non-packed struct with u128 entry in union" {
864}855}
865856
866test "packed struct field passed to generic function" {857test "packed struct field passed to generic function" {
867 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
868 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO858 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
869 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO859 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
870 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO860 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -1298,7 +1288,6 @@ test "packed struct aggregate init" {...@@ -1298,7 +1288,6 @@ test "packed struct aggregate init" {
1298 // stage1 fails this test on mips1288 // stage1 fails this test on mips
1299 return error.SkipZigTest;1289 return error.SkipZigTest;
1300 }1290 }
1301 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1302 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1291 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1303 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1292 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1304 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1293 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -1319,7 +1308,6 @@ test "packed struct aggregate init" {...@@ -1319,7 +1308,6 @@ test "packed struct aggregate init" {
1319}1308}
13201309
1321test "packed struct field access via pointer" {1310test "packed struct field access via pointer" {
1322 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1323 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1311 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1324 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1312 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1325 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1313 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO