authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-11-27 16:20:23+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-11-30 17:56:02+01:00
loga7ad1212cb1b127754c7e48ead8d80d66f0f6623
treee86d27adc503658c983714140842e39e24ca2ac6
parenta314e86772c539e803dfd51c34de52675189b34a
signaturelock-open Commit is signed but in an unrecognized format.

wasm: airAggregateInit - Support packed structs

This allows the Wasm backend to construct an instance of a packed struct during runtime. We first allocate a local, and then shift+or each field's value into the result local. We then finally return this result local as value. The commit also fixes a type-issue in `airElemVal` where we used the element type instead of a pointer type to store the value's address into.

1 files changed, 59 insertions(+), 14 deletions(-)

src/arch/wasm/CodeGen.zig+59-14
......@@ -3834,7 +3834,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38343834 try func.addTag(.i32_mul);
38353835 try func.addTag(.i32_add);
38363836
3837 const result_ptr = try func.allocLocal(elem_ty);
3837 const result_ptr = try func.allocLocal(Type.usize);
38383838 try func.addLabel(.local_set, result_ptr.local.value);
38393839
38403840 const result = if (!isByRef(elem_ty, func.target)) result: {
......@@ -4301,23 +4301,68 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43014301 }
43024302 break :result_value result;
43034303 },
4304 .Struct => {
4305 const result = try func.allocStack(result_ty);
4306 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset
4307 for (elements) |elem, elem_index| {
4308 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
4304 .Struct => switch (result_ty.containerLayout()) {
4305 .Packed => {
4306 if (isByRef(result_ty, func.target)) {
4307 return func.fail("TODO: airAggregateInit for packed structs larger than 64 bits", .{});
4308 }
4309 const struct_obj = result_ty.castTag(.@"struct").?.data;
4310 const fields = struct_obj.fields.values();
4311 const backing_type = struct_obj.backing_int_ty;
4312 // we ensure a new local is created so it's zero-initialized
4313 const result = try func.ensureAllocLocal(backing_type);
4314 var current_bit: u16 = 0;
4315 for (elements) |elem, elem_index| {
4316 const field = fields[elem_index];
4317 if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue;
4318
4319 const shift_val = if (struct_obj.backing_int_ty.bitSize(func.target) <= 32)
4320 WValue{ .imm32 = current_bit }
4321 else
4322 WValue{ .imm64 = current_bit };
4323
4324 const value = try func.resolveInst(elem);
4325 const value_bit_size = @intCast(u16, field.ty.bitSize(func.target));
4326 var int_ty_payload: Type.Payload.Bits = .{
4327 .base = .{ .tag = .int_unsigned },
4328 .data = value_bit_size,
4329 };
4330 const int_ty = Type.initPayload(&int_ty_payload.base);
4331
4332 // load our current result on stack so we can perform all transformations
4333 // using only stack values. Saving the cost of loads and stores.
4334 try func.emitWValue(result);
4335 const bitcasted = try func.bitcast(int_ty, field.ty, value);
4336 const extended_val = try func.intcast(bitcasted, int_ty, backing_type);
4337 // no need to shift any values when the current offset is 0
4338 const shifted = if (current_bit != 0) shifted: {
4339 break :shifted try func.binOp(extended_val, shift_val, backing_type, .shl);
4340 } else extended_val;
4341 // we ignore the result as we keep it on the stack to assign it directly to `result`
4342 _ = try func.binOp(.stack, shifted, backing_type, .@"or");
4343 try func.addLabel(.local_set, result.local.value);
4344 current_bit += value_bit_size;
4345 }
4346 break :result_value result;
4347 },
4348 else => {
4349 const result = try func.allocStack(result_ty);
4350 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset
4351 for (elements) |elem, elem_index| {
4352 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
43094353
4310 const elem_ty = result_ty.structFieldType(elem_index);
4311 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
4312 const value = try func.resolveInst(elem);
4313 try func.store(offset, value, elem_ty, 0);
4354 const elem_ty = result_ty.structFieldType(elem_index);
4355 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
4356 const value = try func.resolveInst(elem);
4357 try func.store(offset, value, elem_ty, 0);
43144358
4315 if (elem_index < elements.len - 1) {
4316 _ = try func.buildPointerOffset(offset, elem_size, .modify);
4359 if (elem_index < elements.len - 1) {
4360 _ = try func.buildPointerOffset(offset, elem_size, .modify);
4361 }
43174362 }
4318 }
43194363
4320 break :result_value result;
4364 break :result_value result;
4365 },
43214366 },
43224367 .Vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
43234368 else => unreachable,