diff options
| author | Luuk de Gram <luuk@degram.dev> | 2021-12-30 23:17:10 +0100 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2022-01-01 12:59:43 +0100 |
| commit | 28cfc49c3e0f4f15a960d5e19ad30bc003d8a740 (patch) | |
| tree | f72d53208750b58ca1233a9bb66ebb4323a7f694 /src | |
| parent | b9a0401e2368894e135b1d1e870219c6b1543af2 (diff) | |
| download | zig-28cfc49c3e0f4f15a960d5e19ad30bc003d8a740.tar.gz zig-28cfc49c3e0f4f15a960d5e19ad30bc003d8a740.zip | |
wasm: Implement memCpy and get if behavior tests passing
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 108 |
1 files changed, 84 insertions, 24 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3ce64d4e89..edc06cc298 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -973,6 +973,41 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { }, else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), }, + .ErrorUnion => { + const error_ty = ty.errorUnionSet(); + const payload_ty = ty.errorUnionPayload(); + const is_pl = val.errorUnionIsPayload(); + + const err_val = if (!is_pl) val else Value.initTag(.zero); + switch (try self.genTypedValue(error_ty, err_val)) { + .externally_managed => |data| try self.code.appendSlice(data), + .appended => {}, + } + + if (payload_ty.hasCodeGenBits()) { + const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); + switch (try self.genTypedValue(payload_ty, pl_val)) { + .externally_managed => |data| try self.code.appendSlice(data), + .appended => {}, + } + } + + return Result.appended; + }, + .ErrorSet => { + switch (val.tag()) { + .@"error" => { + const name = val.castTag(.@"error").?.data.name; + const value = self.global_error_set.get(name).?; + try self.code.writer().writeIntLittle(u32, value); + }, + else => { + const abi_size = @intCast(usize, ty.abiSize(self.target)); + try self.code.appendNTimes(0, abi_size); + }, + } + return Result.appended; + }, else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), } } @@ -1149,6 +1184,26 @@ fn toWasmIntBits(bits: u16) ?u16 { } else null; } +/// Performs a copy of bytes for a given type. Copying all bytes +/// from rhs to lhs. +/// Asserts `lhs` and `rhs` have their active tag set to `local` +/// +/// TODO: Perform feature detection and when bulk_memory is available, +/// use wasm's mem.copy instruction. +fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void { + const abi_size = ty.abiSize(self.target); + var offset: u32 = 0; + while (offset < abi_size) : (offset += 1) { + // get lhs' address to store the result + try self.addLabel(.local_get, lhs.local); + // load byte from rhs' adress + try self.addLabel(.local_get, rhs.local); + try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 }); + // store the result in lhs (we already have its address on the stack) + try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }); + } +} + fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { const air_tags = self.air.instructions.items(.tag); return switch (air_tags[inst]) { @@ -1482,20 +1537,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro } }, .Struct => { - // we are copying a struct with its fields. - // Replace this with a wasm memcpy instruction once we support that feature. - const fields_len = ty.structFieldCount(); - var index: usize = 0; - while (index < fields_len) : (index += 1) { - const field_ty = ty.structFieldType(index); - if (!field_ty.hasCodeGenBits()) continue; - const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch { - return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); - }; - const field_local = try self.load(rhs, field_ty, field_offset); - try self.store(lhs, field_local, field_ty, field_offset); + if (rhs == .constant) { + try self.emitWValue(rhs); + try self.addLabel(.local_set, lhs.local); + return; } - return; + return try self.memCopy(ty, lhs, rhs); }, .Pointer => { if (ty.isSlice() and rhs == .constant) { @@ -2086,19 +2133,20 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr field_ty, }); }; - // field points to another struct, so retrieve that struct first - switch (struct_ptr) { - .local => return structFieldPtr(struct_ptr, offset), - .local_with_offset => |with_offset| { - const result = try self.load(struct_ptr, field_ty, with_offset.offset); - return structFieldPtr(result, offset); - }, - else => unreachable, - } + return structFieldPtr(struct_ptr, offset); } fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { - return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } }; + var final_offset = offset; + const local = switch (struct_ptr) { + .local => |local| local, + .local_with_offset => |with_offset| blk: { + final_offset += with_offset.offset; + break :blk with_offset.local; + }, + else => unreachable, + }; + return WValue{ .local_with_offset = .{ .local = local, .offset = final_offset } }; } fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2114,7 +2162,19 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); }; - return try self.load(operand, field_ty, offset); + + // TODO: Replace this check with some 'isByRef' function to de-duplicate logic + if (field_ty.zigTypeTag() == .Struct) { + return WValue{ .local_with_offset = .{ + .local = operand.local, + .offset = offset, + } }; + } + + switch (operand) { + .local_with_offset => |with_offset| return try self.load(operand, field_ty, offset + with_offset.offset), + else => return try self.load(operand, field_ty, offset), + } } fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
