authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-12-30 23:17:10+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-01 12:59:43+01:00
log28cfc49c3e0f4f15a960d5e19ad30bc003d8a740
treef72d53208750b58ca1233a9bb66ebb4323a7f694
parentb9a0401e2368894e135b1d1e870219c6b1543af2
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Implement memCpy and get if behavior tests passing


2 files changed, 85 insertions(+), 25 deletions(-)

src/arch/wasm/CodeGen.zig+84-24
...@@ -973,6 +973,41 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {...@@ -973,6 +973,41 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {
973 },973 },
974 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),974 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
975 },975 },
976 .ErrorUnion => {
977 const error_ty = ty.errorUnionSet();
978 const payload_ty = ty.errorUnionPayload();
979 const is_pl = val.errorUnionIsPayload();
980
981 const err_val = if (!is_pl) val else Value.initTag(.zero);
982 switch (try self.genTypedValue(error_ty, err_val)) {
983 .externally_managed => |data| try self.code.appendSlice(data),
984 .appended => {},
985 }
986
987 if (payload_ty.hasCodeGenBits()) {
988 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
989 switch (try self.genTypedValue(payload_ty, pl_val)) {
990 .externally_managed => |data| try self.code.appendSlice(data),
991 .appended => {},
992 }
993 }
994
995 return Result.appended;
996 },
997 .ErrorSet => {
998 switch (val.tag()) {
999 .@"error" => {
1000 const name = val.castTag(.@"error").?.data.name;
1001 const value = self.global_error_set.get(name).?;
1002 try self.code.writer().writeIntLittle(u32, value);
1003 },
1004 else => {
1005 const abi_size = @intCast(usize, ty.abiSize(self.target));
1006 try self.code.appendNTimes(0, abi_size);
1007 },
1008 }
1009 return Result.appended;
1010 },
976 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),1011 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
977 }1012 }
978}1013}
...@@ -1149,6 +1184,26 @@ fn toWasmIntBits(bits: u16) ?u16 {...@@ -1149,6 +1184,26 @@ fn toWasmIntBits(bits: u16) ?u16 {
1149 } else null;1184 } else null;
1150}1185}
11511186
1187/// Performs a copy of bytes for a given type. Copying all bytes
1188/// from rhs to lhs.
1189/// Asserts `lhs` and `rhs` have their active tag set to `local`
1190///
1191/// TODO: Perform feature detection and when bulk_memory is available,
1192/// use wasm's mem.copy instruction.
1193fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void {
1194 const abi_size = ty.abiSize(self.target);
1195 var offset: u32 = 0;
1196 while (offset < abi_size) : (offset += 1) {
1197 // get lhs' address to store the result
1198 try self.addLabel(.local_get, lhs.local);
1199 // load byte from rhs' adress
1200 try self.addLabel(.local_get, rhs.local);
1201 try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 });
1202 // store the result in lhs (we already have its address on the stack)
1203 try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 });
1204 }
1205}
1206
1152fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {1207fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1153 const air_tags = self.air.instructions.items(.tag);1208 const air_tags = self.air.instructions.items(.tag);
1154 return switch (air_tags[inst]) {1209 return switch (air_tags[inst]) {
...@@ -1482,20 +1537,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1482,20 +1537,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1482 }1537 }
1483 },1538 },
1484 .Struct => {1539 .Struct => {
1485 // we are copying a struct with its fields.1540 if (rhs == .constant) {
1486 // Replace this with a wasm memcpy instruction once we support that feature.1541 try self.emitWValue(rhs);
1487 const fields_len = ty.structFieldCount();1542 try self.addLabel(.local_set, lhs.local);
1488 var index: usize = 0;1543 return;
1489 while (index < fields_len) : (index += 1) {
1490 const field_ty = ty.structFieldType(index);
1491 if (!field_ty.hasCodeGenBits()) continue;
1492 const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch {
1493 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
1494 };
1495 const field_local = try self.load(rhs, field_ty, field_offset);
1496 try self.store(lhs, field_local, field_ty, field_offset);
1497 }1544 }
1498 return;1545 return try self.memCopy(ty, lhs, rhs);
1499 },1546 },
1500 .Pointer => {1547 .Pointer => {
1501 if (ty.isSlice() and rhs == .constant) {1548 if (ty.isSlice() and rhs == .constant) {
...@@ -2086,19 +2133,20 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr...@@ -2086,19 +2133,20 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
2086 field_ty,2133 field_ty,
2087 });2134 });
2088 };2135 };
2089 // field points to another struct, so retrieve that struct first2136 return structFieldPtr(struct_ptr, offset);
2090 switch (struct_ptr) {
2091 .local => return structFieldPtr(struct_ptr, offset),
2092 .local_with_offset => |with_offset| {
2093 const result = try self.load(struct_ptr, field_ty, with_offset.offset);
2094 return structFieldPtr(result, offset);
2095 },
2096 else => unreachable,
2097 }
2098}2137}
20992138
2100fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {2139fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {
2101 return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } };2140 var final_offset = offset;
2141 const local = switch (struct_ptr) {
2142 .local => |local| local,
2143 .local_with_offset => |with_offset| blk: {
2144 final_offset += with_offset.offset;
2145 break :blk with_offset.local;
2146 },
2147 else => unreachable,
2148 };
2149 return WValue{ .local_with_offset = .{ .local = local, .offset = final_offset } };
2102}2150}
21032151
2104fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2152fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2114,7 +2162,19 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2114,7 +2162,19 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2114 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {2162 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {
2115 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});2163 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
2116 };2164 };
2117 return try self.load(operand, field_ty, offset);2165
2166 // TODO: Replace this check with some 'isByRef' function to de-duplicate logic
2167 if (field_ty.zigTypeTag() == .Struct) {
2168 return WValue{ .local_with_offset = .{
2169 .local = operand.local,
2170 .offset = offset,
2171 } };
2172 }
2173
2174 switch (operand) {
2175 .local_with_offset => |with_offset| return try self.load(operand, field_ty, offset + with_offset.offset),
2176 else => return try self.load(operand, field_ty, offset),
2177 }
2118}2178}
21192179
2120fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2180fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
test/behavior.zig+1-1
...@@ -29,6 +29,7 @@ test {...@@ -29,6 +29,7 @@ test {
29 _ = @import("behavior/fn_in_struct_in_comptime.zig");29 _ = @import("behavior/fn_in_struct_in_comptime.zig");
30 _ = @import("behavior/hasdecl.zig");30 _ = @import("behavior/hasdecl.zig");
31 _ = @import("behavior/hasfield.zig");31 _ = @import("behavior/hasfield.zig");
32 _ = @import("behavior/if.zig");
32 _ = @import("behavior/import.zig");33 _ = @import("behavior/import.zig");
33 _ = @import("behavior/incomplete_struct_param_tld.zig");34 _ = @import("behavior/incomplete_struct_param_tld.zig");
34 _ = @import("behavior/inttoptr.zig");35 _ = @import("behavior/inttoptr.zig");
...@@ -61,7 +62,6 @@ test {...@@ -61,7 +62,6 @@ test {
61 _ = @import("behavior/fn_in_struct_in_comptime.zig");62 _ = @import("behavior/fn_in_struct_in_comptime.zig");
62 _ = @import("behavior/for.zig");63 _ = @import("behavior/for.zig");
63 _ = @import("behavior/generics.zig");64 _ = @import("behavior/generics.zig");
64 _ = @import("behavior/if.zig");
65 _ = @import("behavior/int128.zig");65 _ = @import("behavior/int128.zig");
66 _ = @import("behavior/member_func.zig");66 _ = @import("behavior/member_func.zig");
67 _ = @import("behavior/null.zig");67 _ = @import("behavior/null.zig");