authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2025-03-24 14:54:31+01:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2025-03-24 15:00:00+01:00
log33ad2c949e0355b680d93c2e7c3ba58a9b2c506c
tree010c43c1e04dea15126d899c238a5fab65cd889b
parent911f4527f0ad166d84d77887602a784b1e801421

stage2-wasm: packed store/load 128 bits


4 files changed, 85 insertions(+), 59 deletions(-)

src/arch/wasm/CodeGen.zig+85-53
...@@ -759,6 +759,16 @@ fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {...@@ -759,6 +759,16 @@ fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
759 return result;759 return result;
760}760}
761761
762fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue {
763 const zcu = cg.pt.zcu;
764 const ty = val.typeOf(zcu);
765
766 return if (isByRef(ty, zcu, cg.target))
767 .{ .uav_ref = .{ .ip_index = val.toIntern() } }
768 else
769 try cg.lowerConstant(val, ty);
770}
771
762/// NOTE: if result == .stack, it will be stored in .local772/// NOTE: if result == .stack, it will be stored in .local
763fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void {773fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void {
764 assert(operands.len <= Liveness.bpi - 1);774 assert(operands.len <= Liveness.bpi - 1);
...@@ -2319,39 +2329,56 @@ fn airStore(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {...@@ -2319,39 +2329,56 @@ fn airStore(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
2319 } else {2329 } else {
2320 // at this point we have a non-natural alignment, we must2330 // at this point we have a non-natural alignment, we must
2321 // load the value, and then shift+or the rhs into the result location.2331 // load the value, and then shift+or the rhs into the result location.
2322 const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8);2332 const host_size = ptr_info.packed_offset.host_size * 8;
2333 const host_ty = try pt.intType(.unsigned, host_size);
2334 const bit_size: u16 = @intCast(ty.bitSize(zcu));
2335 const bit_offset = ptr_info.packed_offset.bit_offset;
2336
2337 const mask_val = try cg.resolveValue(val: {
2338 const limbs = try cg.gpa.alloc(
2339 std.math.big.Limb,
2340 std.math.big.int.calcTwosCompLimbCount(host_size) + 1,
2341 );
2342 defer cg.gpa.free(limbs);
23232343
2324 if (isByRef(int_elem_ty, zcu, cg.target)) {2344 var mask_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2325 return cg.fail("TODO: airStore for pointers to bitfields with backing type larger than 64bits", .{});2345 mask_bigint.setTwosCompIntLimit(.max, .unsigned, host_size);
2326 }
23272346
2328 var mask = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(ty.bitSize(zcu)))) - 1));2347 if (bit_size != host_size) {
2329 mask <<= @as(u6, @intCast(ptr_info.packed_offset.bit_offset));2348 mask_bigint.shiftRight(mask_bigint.toConst(), host_size - bit_size);
2330 mask ^= ~@as(u64, 0);2349 }
2331 const shift_val: WValue = if (ptr_info.packed_offset.host_size <= 4)2350 if (bit_offset != 0) {
2332 .{ .imm32 = ptr_info.packed_offset.bit_offset }2351 mask_bigint.shiftLeft(mask_bigint.toConst(), bit_offset);
2352 }
2353 mask_bigint.bitNotWrap(mask_bigint.toConst(), .unsigned, host_size);
2354
2355 break :val try pt.intValue_big(host_ty, mask_bigint.toConst());
2356 });
2357
2358 const shift_val: WValue = if (33 <= host_size and host_size <= 64)
2359 .{ .imm64 = bit_offset }
2333 else2360 else
2334 .{ .imm64 = ptr_info.packed_offset.bit_offset };2361 .{ .imm32 = bit_offset };
2335 const mask_val: WValue = if (ptr_info.packed_offset.host_size <= 4)2362
2336 .{ .imm32 = @as(u32, @truncate(mask)) }2363 if (host_size <= 64) {
2364 try cg.emitWValue(lhs);
2365 }
2366 const loaded = if (host_size <= 64)
2367 try cg.load(lhs, host_ty, 0)
2337 else2368 else
2338 .{ .imm64 = mask };2369 lhs;
2339 const wrap_mask_val: WValue = if (ptr_info.packed_offset.host_size <= 4)2370 const anded = try cg.binOp(loaded, mask_val, host_ty, .@"and");
2340 .{ .imm32 = @truncate(~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu))) }2371 const extended_value = try cg.intcast(rhs, ty, host_ty);
2372 const shifted_value = if (bit_offset > 0)
2373 try cg.binOp(extended_value, shift_val, host_ty, .shl)
2341 else2374 else
2342 .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(zcu)) };2375 extended_value;
23432376 const result = try cg.binOp(anded, shifted_value, host_ty, .@"or");
2344 try cg.emitWValue(lhs);2377 if (host_size <= 64) {
2345 const loaded = try cg.load(lhs, int_elem_ty, 0);2378 try cg.store(.stack, result, host_ty, lhs.offset());
2346 const anded = try cg.binOp(loaded, mask_val, int_elem_ty, .@"and");2379 } else {
2347 const extended_value = try cg.intcast(rhs, ty, int_elem_ty);2380 try cg.store(lhs, result, host_ty, lhs.offset());
2348 const masked_value = try cg.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and");2381 }
2349 const shifted_value = if (ptr_info.packed_offset.bit_offset > 0) shifted: {
2350 break :shifted try cg.binOp(masked_value, shift_val, int_elem_ty, .shl);
2351 } else masked_value;
2352 const result = try cg.binOp(anded, shifted_value, int_elem_ty, .@"or");
2353 // lhs is still on the stack
2354 try cg.store(.stack, result, int_elem_ty, lhs.offset());
2355 }2382 }
23562383
2357 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });2384 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
...@@ -2494,22 +2521,30 @@ fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2494,22 +2521,30 @@ fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2494 }2521 }
24952522
2496 if (ptr_info.packed_offset.host_size == 0) {2523 if (ptr_info.packed_offset.host_size == 0) {
2497 break :result try cg.load(operand, ty, 0);2524 const loaded = try cg.load(operand, ty, 0);
2498 }2525 const ty_size = ty.abiSize(zcu);
24992526 if (ty.isAbiInt(zcu) and ty_size * 8 > ty.bitSize(zcu)) {
2500 // at this point we have a non-natural alignment, we must2527 const int_elem_ty = try pt.intType(.unsigned, @intCast(ty_size * 8));
2501 // shift the value to obtain the correct bit.2528 break :result try cg.trunc(loaded, ty, int_elem_ty);
2502 const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8);2529 } else {
2503 const shift_val: WValue = if (ptr_info.packed_offset.host_size <= 4)2530 break :result loaded;
2504 .{ .imm32 = ptr_info.packed_offset.bit_offset }2531 }
2505 else if (ptr_info.packed_offset.host_size <= 8)2532 } else {
2506 .{ .imm64 = ptr_info.packed_offset.bit_offset }2533 const int_elem_ty = try pt.intType(.unsigned, ptr_info.packed_offset.host_size * 8);
2507 else2534 const shift_val: WValue = if (ptr_info.packed_offset.host_size <= 4)
2508 return cg.fail("TODO: airLoad where ptr to bitfield exceeds 64 bits", .{});2535 .{ .imm32 = ptr_info.packed_offset.bit_offset }
2536 else if (ptr_info.packed_offset.host_size <= 8)
2537 .{ .imm64 = ptr_info.packed_offset.bit_offset }
2538 else
2539 .{ .imm32 = ptr_info.packed_offset.bit_offset };
25092540
2510 const stack_loaded = try cg.load(operand, int_elem_ty, 0);2541 const stack_loaded = if (ptr_info.packed_offset.host_size <= 8)
2511 const shifted = try cg.binOp(stack_loaded, shift_val, int_elem_ty, .shr);2542 try cg.load(operand, int_elem_ty, 0)
2512 break :result try cg.trunc(shifted, ty, int_elem_ty);2543 else
2544 operand;
2545 const shifted = try cg.binOp(stack_loaded, shift_val, int_elem_ty, .shr);
2546 break :result try cg.trunc(shifted, ty, int_elem_ty);
2547 }
2513 };2548 };
2514 return cg.finishAir(inst, result, &.{ty_op.operand});2549 return cg.finishAir(inst, result, &.{ty_op.operand});
2515}2550}
...@@ -3857,15 +3892,12 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3857,15 +3892,12 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3857 const packed_struct = zcu.typeToPackedStruct(struct_ty).?;3892 const packed_struct = zcu.typeToPackedStruct(struct_ty).?;
3858 const offset = pt.structPackedFieldBitOffset(packed_struct, field_index);3893 const offset = pt.structPackedFieldBitOffset(packed_struct, field_index);
3859 const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));3894 const backing_ty = Type.fromInterned(packed_struct.backingIntTypeUnordered(ip));
3860 const wasm_bits = toWasmBits(backing_ty.intInfo(zcu).bits) orelse {3895 const host_bits = backing_ty.intInfo(zcu).bits;
3861 return cg.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{});3896
3862 };3897 const const_wvalue: WValue = if (33 <= host_bits and host_bits <= 64)
3863 const const_wvalue: WValue = if (wasm_bits == 32)
3864 .{ .imm32 = offset }
3865 else if (wasm_bits == 64)
3866 .{ .imm64 = offset }3898 .{ .imm64 = offset }
3867 else3899 else
3868 return cg.fail("TODO: airStructFieldVal for packed structs larger than 64 bits", .{});3900 .{ .imm32 = offset };
38693901
3870 // for first field we don't require any shifting3902 // for first field we don't require any shifting
3871 const shifted_value = if (offset == 0)3903 const shifted_value = if (offset == 0)
...@@ -4043,7 +4075,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner...@@ -4043,7 +4075,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
4043 if (use_br_table) {4075 if (use_br_table) {
4044 const width = width_maybe.?;4076 const width = width_maybe.?;
40454077
4046 const br_value_original = try cg.binOp(target, try cg.resolveInst(Air.internedToRef(min.?.toIntern())), target_ty, .sub);4078 const br_value_original = try cg.binOp(target, try cg.resolveValue(min.?), target_ty, .sub);
4047 _ = try cg.intcast(br_value_original, target_ty, Type.u32);4079 _ = try cg.intcast(br_value_original, target_ty, Type.u32);
40484080
4049 const jump_table: Mir.JumpTable = .{ .length = width + 1 };4081 const jump_table: Mir.JumpTable = .{ .length = width + 1 };
...@@ -5232,7 +5264,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5232,7 +5264,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5232 }5264 }
5233 }5265 }
5234 if (sentinel) |s| {5266 if (sentinel) |s| {
5235 const val = try cg.resolveInst(Air.internedToRef(s.toIntern()));5267 const val = try cg.resolveValue(s);
5236 try cg.store(offset, val, elem_ty, 0);5268 try cg.store(offset, val, elem_ty, 0);
5237 }5269 }
5238 } else {5270 } else {
...@@ -5243,7 +5275,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5243,7 +5275,7 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5243 offset += elem_size;5275 offset += elem_size;
5244 }5276 }
5245 if (sentinel) |s| {5277 if (sentinel) |s| {
5246 const val = try cg.resolveInst(Air.internedToRef(s.toIntern()));5278 const val = try cg.resolveValue(s);
5247 try cg.store(result, val, elem_ty, offset);5279 try cg.store(result, val, elem_ty, offset);
5248 }5280 }
5249 }5281 }
test/behavior/bitcast.zig-2
...@@ -480,7 +480,6 @@ test "@bitCast of packed struct of bools all true" {...@@ -480,7 +480,6 @@ test "@bitCast of packed struct of bools all true" {
480 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO480 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
481 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO481 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
482 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO482 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
483 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
484 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO483 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
485484
486 const P = packed struct {485 const P = packed struct {
...@@ -501,7 +500,6 @@ test "@bitCast of packed struct of bools all false" {...@@ -501,7 +500,6 @@ test "@bitCast of packed struct of bools all false" {
501 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO500 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
502 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO501 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
503 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO502 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
504 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
505 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO503 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
506504
507 const P = packed struct {505 const P = packed struct {
test/behavior/packed-struct.zig-1
...@@ -1321,7 +1321,6 @@ test "packed struct with signed field" {...@@ -1321,7 +1321,6 @@ test "packed struct with signed field" {
1321test "assign packed struct initialized with RLS to packed struct literal field" {1321test "assign packed struct initialized with RLS to packed struct literal field" {
1322 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isWasm()) return error.SkipZigTest;1322 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isWasm()) return error.SkipZigTest;
1323 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1323 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1324 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1325 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1324 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13261325
1327 const Inner = packed struct { x: u17 };1326 const Inner = packed struct { x: u17 };
test/behavior/struct.zig-3
...@@ -421,9 +421,7 @@ const Foo96Bits = packed struct {...@@ -421,9 +421,7 @@ const Foo96Bits = packed struct {
421test "packed struct 24bits" {421test "packed struct 24bits" {
422 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;422 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
423 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;423 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
424 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
425 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO424 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
426 if (builtin.cpu.arch.isWasm()) return error.SkipZigTest; // TODO
427 if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO425 if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // TODO
428 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO426 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
429427
...@@ -763,7 +761,6 @@ const S0 = struct {...@@ -763,7 +761,6 @@ const S0 = struct {
763var g_foo: S0 = S0.init();761var g_foo: S0 = S0.init();
764762
765test "packed struct with fp fields" {763test "packed struct with fp fields" {
766 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
767 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO764 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
768 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO765 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
769 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO766 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO