From 0e2fcab334083d3cbc786e891be6c97e9fd81595 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 10 Feb 2022 21:06:16 +0100 Subject: [PATCH] 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`. --- src/arch/wasm/CodeGen.zig | 95 ++++++++++++++++++++++++++++----------- src/link/Wasm.zig | 6 +-- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b0c24be03b..bcad7cac19 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1106,6 +1106,20 @@ pub const DeclGen = struct { } return Result{ .appended = {} }; }, + .Float => { + const float_bits = ty.floatBits(self.target()); + if (float_bits > 64) { + return self.fail("Wasm TODO: Implement f80 and f128", .{}); + } + + switch (float_bits) { + 16, 32 => try writer.writeIntLittle(u32, @bitCast(u32, val.toFloat(f32))), + 64 => try writer.writeIntLittle(u64, @bitCast(u64, val.toFloat(f64))), + else => unreachable, + } + + return Result{ .appended = {} }; + }, .Enum => { var int_buffer: Value.Payload.U64 = undefined; const int_val = val.enumToInt(ty, &int_buffer); @@ -1334,10 +1348,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu defer self.gpa.free(param_types); fn_ty.fnParamTypes(param_types); var result: CallWValues = .{ - .args = try self.gpa.alloc(WValue, param_types.len), + .args = &.{}, .return_value = .none, }; - errdefer self.gpa.free(result.args); + var args = std.ArrayList(WValue).init(self.gpa); + defer args.deinit(); + const ret_ty = fn_ty.fnReturnType(); // Check if we store the result as a pointer to the stack rather than // by value @@ -1350,18 +1366,18 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu switch (cc) { .Naked => return result, .Unspecified, .C => { - for (param_types) |ty, ty_index| { + for (param_types) |ty| { if (!ty.hasRuntimeBits()) { - result.args[ty_index] = .{ .none = {} }; continue; } - result.args[ty_index] = .{ .local = self.local_index }; + try args.append(.{ .local = self.local_index }); self.local_index += 1; } }, else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), } + result.args = args.toOwnedSlice(); return result; } @@ -2060,6 +2076,26 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { if (val.isUndefDeep()) return self.emitUndefined(ty); + if (val.castTag(.decl_ref)) |decl_ref| { + const decl = decl_ref.data; + decl.markAlive(); + const target_sym_index = decl.link.wasm.sym_index; + if (ty.isSlice()) { + var slice_len: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = val.sliceLen(), + }; + var slice_val: Value.Payload.Slice = .{ + .base = .{ .tag = .slice }, + .data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) }, + }; + return self.lowerConstant(Value.initPayload(&slice_val.base), ty); + } else if (decl.ty.zigTypeTag() == .Fn) { + try self.bin_file.addTableFunction(target_sym_index); + return WValue{ .function_index = target_sym_index }; + } else return WValue{ .memory = target_sym_index }; + } + switch (ty.zigTypeTag()) { .Int => { const int_info = ty.intInfo(self.target); @@ -2084,25 +2120,6 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { else => unreachable, }, .Pointer => switch (val.tag()) { - .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; - decl.markAlive(); - const target_sym_index = decl.link.wasm.sym_index; - if (ty.isSlice()) { - var slice_len: Value.Payload.U64 = .{ - .base = .{ .tag = .int_u64 }, - .data = val.sliceLen(), - }; - var slice_val: Value.Payload.Slice = .{ - .base = .{ .tag = .slice }, - .data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) }, - }; - return self.lowerConstant(Value.initPayload(&slice_val.base), ty); - } else if (decl.ty.zigTypeTag() == .Fn) { - try self.bin_file.addTableFunction(target_sym_index); - return WValue{ .function_index = target_sym_index }; - } else return WValue{ .memory = target_sym_index }; - }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; const index = elem_ptr.index; @@ -2114,6 +2131,27 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { .offset = @intCast(u32, offset), } }; }, + .field_ptr => { + const field_ptr = val.castTag(.field_ptr).?.data; + const container = field_ptr.container_ptr; + const parent_ptr = try self.lowerConstant(container, ty); + + const offset = switch (container.tag()) { + .decl_ref => blk: { + const decl_ref = container.castTag(.decl_ref).?.data; + if (decl_ref.ty.castTag(.@"struct")) |_| { + const offset = decl_ref.ty.structFieldOffset(field_ptr.field_index, self.target); + break :blk offset; + } + return self.fail("Wasm TODO: field_ptr decl_ref for type '{}'", .{decl_ref.ty}); + }, + else => |tag| return self.fail("Wasm TODO: Implement field_ptr for value tag: '{s}'", .{tag}), + }; + return WValue{ .memory_offset = .{ + .pointer = parent_ptr.memory, + .offset = @intCast(u32, offset), + } }; + }, .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, .zero, .null_value => return WValue{ .imm32 = 0 }, 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 { }, .Optional => if (ty.isPtrLikeOptional()) { var buf: Type.Payload.ElemType = undefined; - return self.lowerConstant(val, ty.optionalChild(&buf)); + const pl_ty = ty.optionalChild(&buf); + if (val.castTag(.opt_payload)) |payload| { + return self.lowerConstant(payload.data, pl_ty); + } else if (val.isNull()) { + return WValue{ .imm32 = 0 }; + } else { + return self.lowerConstant(val, pl_ty); + } } else { const is_pl = val.tag() == .opt_payload; return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 }; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index d62f3a4201..81d77d5b66 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -428,7 +428,7 @@ pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void { fn mapFunctionTable(self: *Wasm) void { var it = self.function_table.valueIterator(); - var index: u32 = 0; + var index: u32 = 1; while (it.next()) |value_ptr| : (index += 1) { value_ptr.* = index; } @@ -821,7 +821,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { try leb.writeULEB128(writer, wasm.reftype(.funcref)); try emitLimits(writer, .{ - .min = @intCast(u32, self.function_table.count()), + .min = @intCast(u32, self.function_table.count()) + 1, .max = null, }); @@ -931,7 +931,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { var flags: u32 = 0x2; // Yes we have a table try leb.writeULEB128(writer, flags); try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols - try emitInit(writer, .{ .i32_const = 0 }); + try emitInit(writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid try leb.writeULEB128(writer, @as(u8, 0)); try leb.writeULEB128(writer, @intCast(u32, self.function_table.count())); var symbol_it = self.function_table.keyIterator();