authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-10 21:06:16+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-10 21:40:06+01:00
log0e2fcab334083d3cbc786e891be6c97e9fd81595
tree7a9042737d071235a7cb17fc68365982f7a835f6
parente139c41fd8955f873615b2c2434d162585c0e44c
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Implement 'field_ptr' constants

This implements the `field_ptr` value for pointers. As the value only provides us with the index, we must calculate the offset from the container type using said index. (i.e. the offset from a struct field at index 2). Besides this, small miscellaneous fixes/updates were done to get remaining behavior tests passing: - We start the function table index at 1, so unresolved function pointers don't can be null-checked properly. - Implement genTypedValue for floats up to f64. - Fix zero-sized arguments by only creating `args` for non-zero-sized types. - lowerConstant now works for all decl_ref's. - lowerConstant properly lowers optional pointers, so `null` pointers are lowered to `0`.

2 files changed, 73 insertions(+), 28 deletions(-)

src/arch/wasm/CodeGen.zig+70-25
......@@ -1106,6 +1106,20 @@ pub const DeclGen = struct {
11061106 }
11071107 return Result{ .appended = {} };
11081108 },
1109 .Float => {
1110 const float_bits = ty.floatBits(self.target());
1111 if (float_bits > 64) {
1112 return self.fail("Wasm TODO: Implement f80 and f128", .{});
1113 }
1114
1115 switch (float_bits) {
1116 16, 32 => try writer.writeIntLittle(u32, @bitCast(u32, val.toFloat(f32))),
1117 64 => try writer.writeIntLittle(u64, @bitCast(u64, val.toFloat(f64))),
1118 else => unreachable,
1119 }
1120
1121 return Result{ .appended = {} };
1122 },
11091123 .Enum => {
11101124 var int_buffer: Value.Payload.U64 = undefined;
11111125 const int_val = val.enumToInt(ty, &int_buffer);
......@@ -1334,10 +1348,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
13341348 defer self.gpa.free(param_types);
13351349 fn_ty.fnParamTypes(param_types);
13361350 var result: CallWValues = .{
1337 .args = try self.gpa.alloc(WValue, param_types.len),
1351 .args = &.{},
13381352 .return_value = .none,
13391353 };
1340 errdefer self.gpa.free(result.args);
1354 var args = std.ArrayList(WValue).init(self.gpa);
1355 defer args.deinit();
1356
13411357 const ret_ty = fn_ty.fnReturnType();
13421358 // Check if we store the result as a pointer to the stack rather than
13431359 // by value
......@@ -1350,18 +1366,18 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
13501366 switch (cc) {
13511367 .Naked => return result,
13521368 .Unspecified, .C => {
1353 for (param_types) |ty, ty_index| {
1369 for (param_types) |ty| {
13541370 if (!ty.hasRuntimeBits()) {
1355 result.args[ty_index] = .{ .none = {} };
13561371 continue;
13571372 }
13581373
1359 result.args[ty_index] = .{ .local = self.local_index };
1374 try args.append(.{ .local = self.local_index });
13601375 self.local_index += 1;
13611376 }
13621377 },
13631378 else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}),
13641379 }
1380 result.args = args.toOwnedSlice();
13651381 return result;
13661382}
13671383
......@@ -2060,6 +2076,26 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
20602076
20612077fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
20622078 if (val.isUndefDeep()) return self.emitUndefined(ty);
2079 if (val.castTag(.decl_ref)) |decl_ref| {
2080 const decl = decl_ref.data;
2081 decl.markAlive();
2082 const target_sym_index = decl.link.wasm.sym_index;
2083 if (ty.isSlice()) {
2084 var slice_len: Value.Payload.U64 = .{
2085 .base = .{ .tag = .int_u64 },
2086 .data = val.sliceLen(),
2087 };
2088 var slice_val: Value.Payload.Slice = .{
2089 .base = .{ .tag = .slice },
2090 .data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) },
2091 };
2092 return self.lowerConstant(Value.initPayload(&slice_val.base), ty);
2093 } else if (decl.ty.zigTypeTag() == .Fn) {
2094 try self.bin_file.addTableFunction(target_sym_index);
2095 return WValue{ .function_index = target_sym_index };
2096 } else return WValue{ .memory = target_sym_index };
2097 }
2098
20632099 switch (ty.zigTypeTag()) {
20642100 .Int => {
20652101 const int_info = ty.intInfo(self.target);
......@@ -2084,25 +2120,6 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
20842120 else => unreachable,
20852121 },
20862122 .Pointer => switch (val.tag()) {
2087 .decl_ref => {
2088 const decl = val.castTag(.decl_ref).?.data;
2089 decl.markAlive();
2090 const target_sym_index = decl.link.wasm.sym_index;
2091 if (ty.isSlice()) {
2092 var slice_len: Value.Payload.U64 = .{
2093 .base = .{ .tag = .int_u64 },
2094 .data = val.sliceLen(),
2095 };
2096 var slice_val: Value.Payload.Slice = .{
2097 .base = .{ .tag = .slice },
2098 .data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) },
2099 };
2100 return self.lowerConstant(Value.initPayload(&slice_val.base), ty);
2101 } else if (decl.ty.zigTypeTag() == .Fn) {
2102 try self.bin_file.addTableFunction(target_sym_index);
2103 return WValue{ .function_index = target_sym_index };
2104 } else return WValue{ .memory = target_sym_index };
2105 },
21062123 .elem_ptr => {
21072124 const elem_ptr = val.castTag(.elem_ptr).?.data;
21082125 const index = elem_ptr.index;
......@@ -2114,6 +2131,27 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
21142131 .offset = @intCast(u32, offset),
21152132 } };
21162133 },
2134 .field_ptr => {
2135 const field_ptr = val.castTag(.field_ptr).?.data;
2136 const container = field_ptr.container_ptr;
2137 const parent_ptr = try self.lowerConstant(container, ty);
2138
2139 const offset = switch (container.tag()) {
2140 .decl_ref => blk: {
2141 const decl_ref = container.castTag(.decl_ref).?.data;
2142 if (decl_ref.ty.castTag(.@"struct")) |_| {
2143 const offset = decl_ref.ty.structFieldOffset(field_ptr.field_index, self.target);
2144 break :blk offset;
2145 }
2146 return self.fail("Wasm TODO: field_ptr decl_ref for type '{}'", .{decl_ref.ty});
2147 },
2148 else => |tag| return self.fail("Wasm TODO: Implement field_ptr for value tag: '{s}'", .{tag}),
2149 };
2150 return WValue{ .memory_offset = .{
2151 .pointer = parent_ptr.memory,
2152 .offset = @intCast(u32, offset),
2153 } };
2154 },
21172155 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
21182156 .zero, .null_value => return WValue{ .imm32 = 0 },
21192157 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),
......@@ -2160,7 +2198,14 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
21602198 },
21612199 .Optional => if (ty.isPtrLikeOptional()) {
21622200 var buf: Type.Payload.ElemType = undefined;
2163 return self.lowerConstant(val, ty.optionalChild(&buf));
2201 const pl_ty = ty.optionalChild(&buf);
2202 if (val.castTag(.opt_payload)) |payload| {
2203 return self.lowerConstant(payload.data, pl_ty);
2204 } else if (val.isNull()) {
2205 return WValue{ .imm32 = 0 };
2206 } else {
2207 return self.lowerConstant(val, pl_ty);
2208 }
21642209 } else {
21652210 const is_pl = val.tag() == .opt_payload;
21662211 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
src/link/Wasm.zig+3-3
......@@ -428,7 +428,7 @@ pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void {
428428
429429fn mapFunctionTable(self: *Wasm) void {
430430 var it = self.function_table.valueIterator();
431 var index: u32 = 0;
431 var index: u32 = 1;
432432 while (it.next()) |value_ptr| : (index += 1) {
433433 value_ptr.* = index;
434434 }
......@@ -821,7 +821,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
821821
822822 try leb.writeULEB128(writer, wasm.reftype(.funcref));
823823 try emitLimits(writer, .{
824 .min = @intCast(u32, self.function_table.count()),
824 .min = @intCast(u32, self.function_table.count()) + 1,
825825 .max = null,
826826 });
827827
......@@ -931,7 +931,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
931931 var flags: u32 = 0x2; // Yes we have a table
932932 try leb.writeULEB128(writer, flags);
933933 try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
934 try emitInit(writer, .{ .i32_const = 0 });
934 try emitInit(writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
935935 try leb.writeULEB128(writer, @as(u8, 0));
936936 try leb.writeULEB128(writer, @intCast(u32, self.function_table.count()));
937937 var symbol_it = self.function_table.keyIterator();