Commit 0e2fcab334

Luuk de Gram <luuk@degram.dev>
2022-02-10 21:06:16
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`.
1 parent e139c41
Changed files (2)
src
arch
link
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 };
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();