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 {
483483 .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u,
484484 .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u,
485485 } 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 },
487492 .f32 => return .f32_trunc,
488493 .f64 => return .f64_trunc,
489494 },
......@@ -687,7 +692,10 @@ const InnerError = error{
687692};
688693
689694pub 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 }
691699 func.branches.deinit(func.gpa);
692700 func.blocks.deinit(func.gpa);
693701 func.locals.deinit(func.gpa);
......@@ -909,6 +917,13 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
909917 if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64;
910918 break :blk wasm.Valtype.i32; // represented as pointer to stack
911919 },
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 },
912927 else => wasm.Valtype.i32, // all represented as reference/immediate
913928 };
914929}
......@@ -1119,13 +1134,14 @@ fn genFunc(func: *CodeGen) InnerError!void {
11191134 try func.addTag(.dbg_prologue_end);
11201135
11211136 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 }
11221142 // Generate MIR for function body
11231143 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
11291145 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
11301146 // we emit an unreachable instruction to tell the stack validator that part will never be reached.
11311147 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:
13091325 assert(ty_classes[0] == .direct);
13101326 const scalar_type = abi.scalarType(ty, func.target);
13111327 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 });
13181328 try func.emitWValue(value);
1319 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
1320 .offset = value.offset(),
1321 .alignment = scalar_type.abiAlignment(func.target),
1322 });
1329
1330 // When the value lives in the virtual stack, we must load it onto the actual stack
1331 if (value != .imm32 and value != .imm64) {
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 }
13231343 },
13241344 .Int, .Float => {
13251345 if (ty_classes[1] == .none) {
......@@ -1472,12 +1492,15 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
14721492 // when the length is comptime-known, rather than a runtime value, we can optimize the generated code by having
14731493 // the loop during codegen, rather than inserting a runtime loop into the binary.
14741494 switch (len) {
1475 .imm32, .imm64 => {
1495 .imm32, .imm64 => blk: {
14761496 const length = switch (len) {
14771497 .imm32 => |val| val,
14781498 .imm64 => |val| val,
14791499 else => unreachable,
14801500 };
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;
14811504 var offset: u32 = 0;
14821505 const lhs_base = dst.offset();
14831506 const rhs_base = src.offset();
......@@ -1498,80 +1521,81 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
14981521 else => unreachable,
14991522 }
15001523 }
1524 return;
15011525 },
1502 else => {
1503 // TODO: We should probably lower this to a call to compiler_rt
1504 // But for now, we implement it manually
1505 var offset = try func.ensureAllocLocal(Type.usize); // local for counter
1506 defer offset.free(func);
1526 else => {},
1527 }
15071528
1508 // outer block to jump to when loop is done
1509 try func.startBlock(.block, wasm.block_empty);
1510 try func.startBlock(.loop, wasm.block_empty);
1529 // TODO: We should probably lower this to a call to compiler_rt
1530 // But for now, we implement it manually
1531 var offset = try func.ensureAllocLocal(Type.usize); // local for counter
1532 defer offset.free(func);
15111533
1512 // loop condition (offset == length -> break)
1513 {
1514 try func.emitWValue(offset);
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 }
1534 // outer block to jump to when loop is done
1535 try func.startBlock(.block, wasm.block_empty);
1536 try func.startBlock(.loop, wasm.block_empty);
15231537
1524 // get dst ptr
1525 {
1526 try func.emitWValue(dst);
1527 try func.emitWValue(offset);
1528 switch (func.arch()) {
1529 .wasm32 => try func.addTag(.i32_add),
1530 .wasm64 => try func.addTag(.i64_add),
1531 else => unreachable,
1532 }
1533 }
1538 // loop condition (offset == length -> break)
1539 {
1540 try func.emitWValue(offset);
1541 try func.emitWValue(len);
1542 switch (func.arch()) {
1543 .wasm32 => try func.addTag(.i32_eq),
1544 .wasm64 => try func.addTag(.i64_eq),
1545 else => unreachable,
1546 }
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 dst
1536 {
1537 try func.emitWValue(src);
1538 try func.emitWValue(offset);
1539 switch (func.arch()) {
1540 .wasm32 => {
1541 try func.addTag(.i32_add);
1542 try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1543 try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
1544 },
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 }
1550 // get dst ptr
1551 {
1552 try func.emitWValue(dst);
1553 try func.emitWValue(offset);
1554 switch (func.arch()) {
1555 .wasm32 => try func.addTag(.i32_add),
1556 .wasm64 => try func.addTag(.i64_add),
1557 else => unreachable,
1558 }
1559 }
15531560
1554 // increment loop counter
1555 {
1556 try func.emitWValue(offset);
1557 switch (func.arch()) {
1558 .wasm32 => {
1559 try func.addImm32(1);
1560 try func.addTag(.i32_add);
1561 },
1562 .wasm64 => {
1563 try func.addImm64(1);
1564 try func.addTag(.i64_add);
1565 },
1566 else => unreachable,
1567 }
1568 try func.addLabel(.local_set, offset.local.value);
1569 try func.addLabel(.br, 0); // jump to start of loop
1570 }
1571 try func.endBlock(); // close off loop block
1572 try func.endBlock(); // close off outer block
1573 },
1561 // get src value and also store in dst
1562 {
1563 try func.emitWValue(src);
1564 try func.emitWValue(offset);
1565 switch (func.arch()) {
1566 .wasm32 => {
1567 try func.addTag(.i32_add);
1568 try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1569 try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
1570 },
1571 .wasm64 => {
1572 try func.addTag(.i64_add);
1573 try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1574 try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
1575 },
1576 else => unreachable,
1577 }
15741578 }
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
15751599}
15761600
15771601fn ptrSize(func: *const CodeGen) u16 {
......@@ -1607,10 +1631,18 @@ fn isByRef(ty: Type, target: std.Target) bool {
16071631
16081632 .Array,
16091633 .Vector,
1610 .Struct,
16111634 .Frame,
16121635 .Union,
16131636 => 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 },
16141646 .Int => return ty.intInfo(target).bits > 64,
16151647 .Float => return ty.floatBits(target) > 64,
16161648 .ErrorUnion => {
......@@ -2100,9 +2132,48 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
21002132
21012133 const lhs = try func.resolveInst(bin_op.lhs);
21022134 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);
21062177 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
21072178}
21082179
......@@ -2134,7 +2205,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE
21342205 const len = @intCast(u32, ty.abiSize(func.target));
21352206 return func.memcpy(lhs, rhs, .{ .imm32 = len });
21362207 },
2137 .Struct, .Array, .Union, .Vector => {
2208 .Struct, .Array, .Union, .Vector => if (isByRef(ty, func.target)) {
21382209 const len = @intCast(u32, ty.abiSize(func.target));
21392210 return func.memcpy(lhs, rhs, .{ .imm32 = len });
21402211 },
......@@ -2190,6 +2261,8 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
21902261 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
21912262 const operand = try func.resolveInst(ty_op.operand);
21922263 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
21942267 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 {
22002273 break :result new_local;
22012274 }
22022275
2203 const stack_loaded = try func.load(operand, ty, 0);
2204 break :result try stack_loaded.toLocal(func, ty);
2276 if (ptr_info.host_size == 0) {
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);
22052300 };
22062301 func.finishAir(inst, result, &.{ty_op.operand});
22072302}
......@@ -2406,7 +2501,7 @@ fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr
24062501/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.
24072502fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
24082503 assert(ty.abiSize(func.target) <= 16);
2409 const bitsize = ty.intInfo(func.target).bits;
2504 const bitsize = @intCast(u16, ty.bitSize(func.target));
24102505 const wasm_bits = toWasmBits(bitsize) orelse {
24112506 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
24122507 };
......@@ -2462,18 +2557,21 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError
24622557 const parent_ptr = try func.lowerParentPtr(field_ptr.container_ptr, parent_ty);
24632558
24642559 const offset = switch (parent_ty.zigTypeTag()) {
2465 .Struct => blk: {
2466 const offset = parent_ty.structFieldOffset(field_ptr.field_index, func.target);
2467 break :blk offset;
2560 .Struct => switch (parent_ty.containerLayout()) {
2561 .Packed => parent_ty.packedStructFieldByteOffset(field_ptr.field_index, func.target),
2562 else => parent_ty.structFieldOffset(field_ptr.field_index, func.target),
24682563 },
2469 .Union => blk: {
2470 const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target);
2471 if (layout.payload_size == 0) break :blk 0;
2472 if (layout.payload_align > layout.tag_align) break :blk 0;
2473
2474 // tag is stored first so calculate offset from where payload starts
2475 const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align));
2476 break :blk offset;
2564 .Union => switch (parent_ty.containerLayout()) {
2565 .Packed => 0,
2566 else => blk: {
2567 const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target);
2568 if (layout.payload_size == 0) break :blk 0;
2569 if (layout.payload_align > layout.tag_align) break :blk 0;
2570
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 },
24772575 },
24782576 .Pointer => switch (parent_ty.ptrSize()) {
24792577 .Slice => switch (field_ptr.field_index) {
......@@ -2689,6 +2787,18 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
26892787 const is_pl = val.tag() == .opt_payload;
26902788 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
26912789 },
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 },
26922802 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
26932803 }
26942804}
......@@ -2723,6 +2833,11 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
27232833 .ErrorUnion => {
27242834 return WValue{ .imm32 = 0xaaaaaaaa };
27252835 },
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 },
27262841 else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),
27272842 }
27282843}
......@@ -3070,11 +3185,32 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
30703185 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
30713186 const result = if (!func.liveness.isUnused(inst)) result: {
30723187 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 }
30733194 break :result func.reuseOperand(ty_op.operand, operand);
30743195 } else WValue{ .none = {} };
30753196 func.finishAir(inst, result, &.{});
30763197}
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
30783214fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
30793215 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
30803216 const extra = func.air.extraData(Air.StructField, ty_pl.payload);
......@@ -3082,13 +3218,7 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
30823218
30833219 const struct_ptr = try func.resolveInst(extra.data.struct_operand);
30843220 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 {
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);
3221 const result = try func.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ty, extra.data.field_index);
30923222 func.finishAir(inst, result, &.{extra.data.struct_operand});
30933223}
30943224
......@@ -3097,21 +3227,40 @@ fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) Inne
30973227 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
30983228 const struct_ptr = try func.resolveInst(ty_op.operand);
30993229 const struct_ty = func.air.typeOf(ty_op.operand).childType();
3100 const field_ty = struct_ty.structFieldType(index);
3101 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, func.target)) orelse {
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);
3230
3231 const result = try func.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ty, index);
31083232 func.finishAir(inst, result, &.{ty_op.operand});
31093233}
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 }
31123261 switch (struct_ptr) {
31133262 .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 } };
31153264 },
31163265 else => return func.buildPointerOffset(struct_ptr, offset, .new),
31173266 }
......@@ -3128,24 +3277,75 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
31283277 const field_ty = struct_ty.structFieldType(field_index);
31293278 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 {
3132 const module = func.bin_file.base.options.module.?;
3133 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
3134 };
3135
3136 const result = result: {
3137 if (isByRef(field_ty, func.target)) {
3138 switch (operand) {
3139 .stack_offset => |stack_offset| {
3140 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
3141 },
3142 else => break :result try func.buildPointerOffset(operand, offset, .new),
3280 const result = switch (struct_ty.containerLayout()) {
3281 .Packed => switch (struct_ty.zigTypeTag()) {
3282 .Struct => result: {
3283 const struct_obj = struct_ty.castTag(.@"struct").?.data;
3284 assert(struct_obj.layout == .Packed);
3285 const offset = struct_obj.packedFieldBitOffset(func.target, field_index);
3286 const backing_ty = struct_obj.backing_int_ty;
3287 const wasm_bits = toWasmBits(backing_ty.intInfo(func.target).bits) orelse {
3288 return func.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{});
3289 };
3290 const const_wvalue = if (wasm_bits == 32)
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 }
31433343 }
3144 }
3145
3146 const field = try func.load(operand, field_ty, offset);
3147 break :result try field.toLocal(func, field_ty);
3344 const field = try func.load(operand, field_ty, offset);
3345 break :result try field.toLocal(func, field_ty);
3346 },
31483347 };
3348
31493349 func.finishAir(inst, result, &.{struct_field.struct_operand});
31503350}
31513351
......@@ -3492,13 +3692,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
34923692/// Asserts type's bitsize <= 128
34933693/// NOTE: May leave the result on the top of the stack.
34943694fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
3495 const given_info = given.intInfo(func.target);
3496 const wanted_info = wanted.intInfo(func.target);
3497 assert(given_info.bits <= 128);
3498 assert(wanted_info.bits <= 128);
3695 const given_bitsize = @intCast(u16, given.bitSize(func.target));
3696 const wanted_bitsize = @intCast(u16, wanted.bitSize(func.target));
3697 assert(given_bitsize <= 128);
3698 assert(wanted_bitsize <= 128);
34993699
3500 const op_bits = toWasmBits(given_info.bits).?;
3501 const wanted_bits = toWasmBits(wanted_info.bits).?;
3700 const op_bits = toWasmBits(given_bitsize).?;
3701 const wanted_bits = toWasmBits(wanted_bitsize).?;
35023702 if (op_bits == wanted_bits) return operand;
35033703
35043704 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
35063706 try func.addTag(.i32_wrap_i64);
35073707 } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) {
35083708 try func.emitWValue(operand);
3509 try func.addTag(switch (wanted_info.signedness) {
3510 .signed => .i64_extend_i32_s,
3511 .unsigned => .i64_extend_i32_u,
3512 });
3709 try func.addTag(if (wanted.isSignedInt()) .i64_extend_i32_s else .i64_extend_i32_u);
35133710 } else if (wanted_bits == 128) {
35143711 // for 128bit integers we store the integer in the virtual stack, rather than a local
35153712 const stack_ptr = try func.allocStack(wanted);
......@@ -3725,7 +3922,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
37253922 try func.addTag(.i32_mul);
37263923 try func.addTag(.i32_add);
37273924
3728 const result_ptr = try func.allocLocal(elem_ty);
3925 const result_ptr = try func.allocLocal(Type.usize);
37293926 try func.addLabel(.local_set, result_ptr.local.value);
37303927
37313928 const result = if (!isByRef(elem_ty, func.target)) result: {
......@@ -3777,19 +3974,25 @@ fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
37773974 const wanted_ty = func.air.getRefType(ty_op.ty);
37783975 const op_ty = func.air.typeOf(ty_op.operand);
37793976
3780 const int_info = op_ty.intInfo(func.target);
3781 if (toWasmBits(int_info.bits) == null) {
3782 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});
3977 const result = try func.trunc(operand, wanted_ty, op_ty);
3978 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
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});
37833987 }
37843988
3785 var result = try func.intcast(operand, op_ty, wanted_ty);
3786 const wanted_bits = wanted_ty.intInfo(func.target).bits;
3989 var result = try func.intcast(operand, given_ty, wanted_ty);
3990 const wanted_bits = @intCast(u16, wanted_ty.bitSize(func.target));
37873991 const wasm_bits = toWasmBits(wanted_bits).?;
37883992 if (wasm_bits != wanted_bits) {
37893993 result = try func.wrapOperand(result, wanted_ty);
37903994 }
3791
3792 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
3995 return result;
37933996}
37943997
37953998fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -4186,23 +4389,68 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41864389 }
41874390 break :result_value result;
41884391 },
4189 .Struct => {
4190 const result = try func.allocStack(result_ty);
4191 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset
4192 for (elements) |elem, elem_index| {
4193 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
4392 .Struct => switch (result_ty.containerLayout()) {
4393 .Packed => {
4394 if (isByRef(result_ty, func.target)) {
4395 return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});
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);
4196 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
4197 const value = try func.resolveInst(elem);
4198 try func.store(offset, value, elem_ty, 0);
4442 const elem_ty = result_ty.structFieldType(elem_index);
4443 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
4444 const value = try func.resolveInst(elem);
4445 try func.store(offset, value, elem_ty, 0);
41994446
4200 if (elem_index < elements.len - 1) {
4201 _ = try func.buildPointerOffset(offset, elem_size, .modify);
4447 if (elem_index < elements.len - 1) {
4448 _ = try func.buildPointerOffset(offset, elem_size, .modify);
4449 }
42024450 }
4203 }
42044451
4205 break :result_value result;
4452 break :result_value result;
4453 },
42064454 },
42074455 .Vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
42084456 else => unreachable,
......@@ -4444,8 +4692,8 @@ fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44444692
44454693 const dest_ty = func.air.typeOfIndex(inst);
44464694 const operand = try func.resolveInst(ty_op.operand);
4447 const trunc = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);
4448 const result = try trunc.toLocal(func, dest_ty);
4695 const truncated = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);
4696 const result = try truncated.toLocal(func, dest_ty);
44494697 func.finishAir(inst, result, &.{ty_op.operand});
44504698}
44514699
src/arch/wasm/abi.zig+16-6
......@@ -94,15 +94,25 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
9494pub fn scalarType(ty: Type, target: std.Target) Type {
9595 switch (ty.zigTypeTag()) {
9696 .Struct => {
97 std.debug.assert(ty.structFieldCount() == 1);
98 return scalarType(ty.structFieldType(0), target);
97 switch (ty.containerLayout()) {
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 }
99107 },
100108 .Union => {
101 const layout = ty.unionGetLayout(target);
102 if (layout.payload_size == 0 and layout.tag_size != 0) {
103 return scalarType(ty.unionTagTypeSafety().?, target);
109 if (ty.containerLayout() != .Packed) {
110 const layout = ty.unionGetLayout(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);
104115 }
105 std.debug.assert(ty.unionFields().count() == 1);
106116 return scalarType(ty.unionFields().values()[0].ty, target);
107117 },
108118 else => return ty,
src/codegen.zig+36-8
......@@ -556,14 +556,42 @@ pub fn generateSymbol(
556556 },
557557 .Struct => {
558558 if (typed_value.ty.containerLayout() == .Packed) {
559 return Result{
560 .fail = try ErrorMsg.create(
561 bin_file.allocator,
562 src_loc,
563 "TODO implement generateSymbol for packed struct",
564 .{},
565 ),
566 };
559 const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
560 const fields = struct_obj.fields.values();
561 const field_vals = typed_value.val.castTag(.aggregate).?.data;
562 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;
563 const current_pos = code.items.len;
564 const mod = bin_file.options.module.?;
565 try code.resize(current_pos + abi_size);
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 = {} };
567595 }
568596
569597 const struct_begin = code.items.len;
src/link/Wasm.zig+3-2
......@@ -1841,8 +1841,9 @@ fn setupStart(wasm: *Wasm) !void {
18411841/// Sets up the memory section of the wasm module, as well as the stack.
18421842fn setupMemory(wasm: *Wasm) !void {
18431843 log.debug("Setting up memory layout", .{});
1844 const page_size = 64 * 1024;
1845 const stack_size = wasm.base.options.stack_size_override orelse page_size * 1;
1844 const page_size = std.wasm.page_size; // 64kb
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;
18461847 const stack_alignment = 16; // wasm's stack alignment as specified by tool-convention
18471848 // Always place the stack at the start by default
18481849 // unless the user specified the global-base flag
test/behavior/bitcast.zig-6
......@@ -18,7 +18,6 @@ test "@bitCast iX -> uX (32, 64)" {
1818}
1919
2020test "@bitCast iX -> uX (8, 16, 128)" {
21 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
2221 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2322 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2423 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -160,7 +159,6 @@ test "@bitCast packed structs at runtime and comptime" {
160159 // stage1 gets the wrong answer for a lot of targets
161160 return error.SkipZigTest;
162161 }
163 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
164162 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
165163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
166164 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -187,7 +185,6 @@ test "@bitCast packed structs at runtime and comptime" {
187185}
188186
189187test "@bitCast extern structs at runtime and comptime" {
190 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
191188 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
192189
193190 const Full = extern struct {
......@@ -218,7 +215,6 @@ test "@bitCast extern structs at runtime and comptime" {
218215}
219216
220217test "bitcast packed struct to integer and back" {
221 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
222218 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
223219 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
224220 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -257,8 +253,6 @@ test "implicit cast to error union by returning" {
257253}
258254
259255test "bitcast packed struct literal to byte" {
260 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
261
262256 const Foo = packed struct {
263257 value: u8,
264258 };
test/behavior/bugs/12776.zig-1
......@@ -31,7 +31,6 @@ const CPU = packed struct {
3131test {
3232 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3333 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
34 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
3534 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
3635
3736 var ram = try RAM.new();
test/behavior/bugs/9584.zig-1
......@@ -44,7 +44,6 @@ pub fn b(x: *X) !void {
4444}
4545
4646test {
47 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
4847 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4948 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5049 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" {
11461146}
11471147
11481148test "switch on an extern enum with negative value" {
1149 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest;
1150
11511149 const Foo = enum(c_int) {
11521150 Bar = -1,
11531151 };
test/behavior/packed-struct.zig-9
......@@ -150,7 +150,6 @@ test "consistent size of packed structs" {
150150
151151test "correct sizeOf and offsets in packed structs" {
152152 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
153 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
154153 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
155154 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
156155 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -220,7 +219,6 @@ test "correct sizeOf and offsets in packed structs" {
220219
221220test "nested packed structs" {
222221 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
223 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
224222 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
225223 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
226224 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -267,7 +265,6 @@ test "nested packed structs" {
267265}
268266
269267test "regular in irregular packed struct" {
270 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
271268 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
272269 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
273270 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -289,7 +286,6 @@ test "regular in irregular packed struct" {
289286
290287test "byte-aligned field pointer offsets" {
291288 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
292 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
293289 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
294290 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
295291 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -395,7 +391,6 @@ test "load pointer from packed struct" {
395391 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
396392 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
397393 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
398 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
399394
400395 const A = struct {
401396 index: u16,
......@@ -413,7 +408,6 @@ test "load pointer from packed struct" {
413408}
414409
415410test "@ptrToInt on a packed struct field" {
416 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
417411 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
418412 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
419413 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -437,7 +431,6 @@ test "optional pointer in packed struct" {
437431 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
438432 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
439433 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
440 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
441434
442435 const T = packed struct { ptr: ?*const u8 };
443436 var n: u8 = 0;
......@@ -566,7 +559,6 @@ test "nested packed struct field access test" {
566559test "runtime init of unnamed packed struct type" {
567560 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
568561 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
569 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
570562 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
571563
572564 var z: u8 = 123;
......@@ -582,7 +574,6 @@ test "packed struct passed to callconv(.C) function" {
582574 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
583575 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
584576 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
585 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
586577 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
587578
588579 const S = struct {
test/behavior/struct.zig-12
......@@ -386,7 +386,6 @@ const APackedStruct = packed struct {
386386
387387test "packed struct" {
388388 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
389 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
390389 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
391390 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
392391 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -460,7 +459,6 @@ test "packed struct 24bits" {
460459
461460test "runtime struct initialization of bitfield" {
462461 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
463 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
464462 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
465463 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
466464 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -504,7 +502,6 @@ test "packed struct fields are ordered from LSB to MSB" {
504502 return error.SkipZigTest;
505503 }
506504 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
507 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
508505 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
509506 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
510507 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -525,7 +522,6 @@ test "packed struct fields are ordered from LSB to MSB" {
525522
526523test "implicit cast packed struct field to const ptr" {
527524 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
528 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
529525 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
530526 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
531527 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -546,7 +542,6 @@ test "implicit cast packed struct field to const ptr" {
546542}
547543
548544test "zero-bit field in packed struct" {
549 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
550545 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
551546 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
552547
......@@ -591,7 +586,6 @@ const bit_field_1 = BitField1{
591586
592587test "bit field access" {
593588 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
594 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
595589 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
596590 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
597591 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -647,7 +641,6 @@ test "default struct initialization fields" {
647641
648642test "packed array 24bits" {
649643 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
650 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
651644 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
652645 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
653646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
......@@ -735,7 +728,6 @@ const FooArrayOfAligned = packed struct {
735728};
736729
737730test "pointer to packed struct member in a stack variable" {
738 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
739731 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
740732 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
741733 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -754,7 +746,6 @@ test "pointer to packed struct member in a stack variable" {
754746}
755747
756748test "packed struct with u0 field access" {
757 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
758749 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
759750 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
760751
......@@ -864,7 +855,6 @@ test "non-packed struct with u128 entry in union" {
864855}
865856
866857test "packed struct field passed to generic function" {
867 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
868858 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
869859 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
870860 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -1298,7 +1288,6 @@ test "packed struct aggregate init" {
12981288 // stage1 fails this test on mips
12991289 return error.SkipZigTest;
13001290 }
1301 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
13021291 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
13031292 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13041293 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -1319,7 +1308,6 @@ test "packed struct aggregate init" {
13191308}
13201309
13211310test "packed struct field access via pointer" {
1322 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
13231311 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
13241312 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13251313 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO