Commit 96a4692f94

Luuk de Gram <luuk@degram.dev>
2021-12-01 11:31:47
wasm: Correctly load slice value on stack
1 parent 1777fb2
Changed files (2)
src
arch
link
src/arch/wasm/CodeGen.zig
@@ -6,6 +6,7 @@ const testing = std.testing;
 const leb = std.leb;
 const mem = std.mem;
 const wasm = std.wasm;
+const log = std.log.scoped(.codegen);
 
 const Module = @import("../../Module.zig");
 const Decl = Module.Decl;
@@ -562,8 +563,6 @@ const InnerError = error{
     CodegenFail,
     /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
     AnalysisFail,
-    /// Failed to emit MIR instructions to binary/textual representation.
-    EmitFail,
     /// Compiler implementation could not handle a large integer.
     Overflow,
 };
@@ -800,7 +799,7 @@ pub fn genFunc(self: *Self) InnerError!Result {
     emit.emitMir() catch |err| switch (err) {
         error.EmitFail => {
             self.err_msg = emit.error_msg.?;
-            return error.EmitFail;
+            return error.CodegenFail;
         },
         else => |e| return e,
     };
@@ -809,8 +808,34 @@ pub fn genFunc(self: *Self) InnerError!Result {
     return Result.appended;
 }
 
+pub fn genDecl(self: *Self) InnerError!Result {
+    const decl = self.decl;
+    assert(decl.has_tv);
+
+    log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });
+
+    if (decl.val.castTag(.function)) |func_payload| {
+        _ = func_payload;
+        return self.fail("TODO wasm backend genDecl function pointer", .{});
+    } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
+        const ext_decl = extern_fn.data;
+        var func_type = try self.genFunctype(ext_decl.ty);
+        func_type.deinit(self.gpa);
+        ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
+        return Result.appended;
+    } else {
+        const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
+            break :init_val payload.data.init;
+        } else decl.val;
+        if (init_val.tag() != .unreachable_value) {
+            return try self.genTypedValue(decl.ty, init_val);
+        }
+        return Result.appended;
+    }
+}
+
 /// Generates the wasm bytecode for the declaration belonging to `Context`
-pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
+fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {
     if (val.isUndef()) {
         try self.code.appendNTimes(0xaa, @intCast(usize, ty.abiSize(self.target)));
         return Result.appended;
@@ -822,16 +847,16 @@ pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
                 .function => val.castTag(.function).?.data.owner_decl,
                 else => unreachable,
             };
-            return try self.lowerDeclRef(fn_decl);
+            return try self.lowerDeclRef(ty, val, fn_decl);
         },
         .Optional => {
             var opt_buf: Type.Payload.ElemType = undefined;
             const payload_type = ty.optionalChild(&opt_buf);
             if (ty.isPtrLikeOptional()) {
                 if (val.castTag(.opt_payload)) |payload| {
-                    return try self.genDecl(payload_type, payload.data);
+                    return try self.genTypedValue(payload_type, payload.data);
                 } else if (!val.isNull()) {
-                    return try self.genDecl(payload_type, val);
+                    return try self.genTypedValue(payload_type, val);
                 } else {
                     try self.code.appendNTimes(0, @intCast(usize, ty.abiSize(self.target)));
                     return Result.appended;
@@ -839,7 +864,7 @@ pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
             }
             // `null-tag` byte
             try self.code.appendNTimes(@boolToInt(!val.isNull()), 4);
-            const pl_result = try self.genDecl(
+            const pl_result = try self.genTypedValue(
                 payload_type,
                 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
             );
@@ -855,7 +880,7 @@ pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
                 if (ty.sentinel()) |sentinel| {
                     try self.code.appendSlice(payload.data);
 
-                    switch (try self.genDecl(ty.childType(), sentinel)) {
+                    switch (try self.genTypedValue(ty.childType(), sentinel)) {
                         .appended => return Result.appended,
                         .externally_managed => |data| {
                             try self.code.appendSlice(data);
@@ -869,22 +894,20 @@ pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
                 const elem_vals = val.castTag(.array).?.data;
                 const elem_ty = ty.elemType();
                 for (elem_vals) |elem_val| {
-                    switch (try self.genDecl(elem_ty, elem_val)) {
+                    switch (try self.genTypedValue(elem_ty, elem_val)) {
                         .appended => {},
-                        .externally_managed => |data| {
-                            try self.code.appendSlice(data);
-                        },
+                        .externally_managed => |data| try self.code.appendSlice(data),
                     }
                 }
                 return Result.appended;
             },
-            else => return self.fail("TODO implement genDecl for array type value: {s}", .{@tagName(val.tag())}),
+            else => return self.fail("TODO implement genTypedValue for array type value: {s}", .{@tagName(val.tag())}),
         },
         .Int => {
             const info = ty.intInfo(self.target);
             const abi_size = @intCast(usize, ty.abiSize(self.target));
             // todo: Implement integer sizes larger than 64bits
-            if (info.bits > 64) return self.fail("TODO: Implement genDecl for integer bit size: {d}", .{info.bits});
+            if (info.bits > 64) return self.fail("TODO: Implement genTypedValue for integer bit size: {d}", .{info.bits});
             var buf: [8]u8 = undefined;
             if (info.signedness == .unsigned) {
                 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
@@ -906,8 +929,7 @@ pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
             for (field_vals) |field_val, index| {
                 const field_ty = ty.structFieldType(index);
                 if (!field_ty.hasCodeGenBits()) continue;
-
-                switch (try self.genDecl(field_ty, field_val)) {
+                switch (try self.genTypedValue(field_ty, field_val)) {
                     .appended => {},
                     .externally_managed => |payload| try self.code.appendSlice(payload),
                 }
@@ -923,21 +945,21 @@ pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
         .Pointer => switch (val.tag()) {
             .variable => {
                 const decl = val.castTag(.variable).?.data.owner_decl;
-                return try self.lowerDeclRef(decl);
+                return try self.lowerDeclRef(ty, val, decl);
             },
             .decl_ref => {
                 const decl = val.castTag(.decl_ref).?.data;
-                return try self.lowerDeclRef(decl);
+                return try self.lowerDeclRef(ty, val, decl);
             },
             .slice => {
                 const slice = val.castTag(.slice).?.data;
                 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                 const ptr_ty = ty.slicePtrFieldType(&buf);
-                switch (try self.genDecl(ptr_ty, slice.ptr)) {
+                switch (try self.genTypedValue(ptr_ty, slice.ptr)) {
                     .externally_managed => |data| try self.code.appendSlice(data),
                     .appended => {},
                 }
-                switch (try self.genDecl(Type.usize, slice.len)) {
+                switch (try self.genTypedValue(Type.usize, slice.len)) {
                     .externally_managed => |data| try self.code.appendSlice(data),
                     .appended => {},
                 }
@@ -949,13 +971,25 @@ pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
     }
 }
 
-fn lowerDeclRef(self: *Self, decl: *Module.Decl) InnerError!Result {
-    decl.alive = true;
+fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result {
+    if (ty.isSlice()) {
+        var buf: Type.SlicePtrFieldTypeBuffer = undefined;
+        const slice_ty = ty.slicePtrFieldType(&buf);
+        switch (try self.genTypedValue(slice_ty, val)) {
+            .appended => {},
+            .externally_managed => |payload| try self.code.appendSlice(payload),
+        }
+        var slice_len: Value.Payload.U64 = .{
+            .base = .{ .tag = .int_u64 },
+            .data = val.sliceLen(),
+        };
+        return try self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base));
+    }
 
     const offset = @intCast(u32, self.code.items.len);
     const atom = &self.decl.link.wasm;
     const target_sym_index = decl.link.wasm.sym_index;
-
+    decl.alive = true;
     if (decl.ty.zigTypeTag() == .Fn) {
         // We found a function pointer, so add it to our table,
         // as function pointers are not allowed to be stored inside the data section,
@@ -2225,7 +2259,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     const elem_size = elem_ty.abiSize(self.target);
 
     // load pointer onto stack
-    try self.emitWValue(slice);
+    const slice_ptr = try self.load(slice, slice_ty, 0);
+    try self.addLabel(.local_get, slice_ptr.local);
 
     // calculate index into slice
     try self.emitWValue(index);
@@ -2233,24 +2268,6 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     try self.addTag(.i32_mul);
     try self.addTag(.i32_add);
 
-    const abi_size = if (elem_size < 8)
-        @intCast(u8, elem_size)
-    else
-        @as(u8, 4); // elements larger than 8 bytes will be passed by pointer
-
-    const extra_index = try self.addExtra(Mir.MemArg{
-        .offset = 0,
-        .alignment = elem_ty.abiAlignment(self.target),
-    });
-    const signedness: std.builtin.Signedness = if (elem_ty.isUnsignedInt()) .unsigned else .signed;
-    const opcode = buildOpcode(.{
-        .valtype1 = try self.typeToValtype(elem_ty),
-        .width = abi_size * 8,
-        .op = .load,
-        .signedness = signedness,
-    });
-    try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = extra_index } });
-
     const result = try self.allocLocal(elem_ty);
     try self.addLabel(.local_set, result.local);
     return result;
src/link/Wasm.zig
@@ -277,7 +277,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
     defer codegen.deinit();
 
     // generate the 'code' section for the function declaration
-    const result = codegen.genDecl(decl.ty, decl.val) catch |err| switch (err) {
+    const result = codegen.genDecl() catch |err| switch (err) {
         error.CodegenFail => {
             decl.analysis = .codegen_failure;
             try module.failed_decls.put(module.gpa, decl, codegen.err_msg);
@@ -297,6 +297,7 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: CodeGen.Result, cod
 
     if (decl.isExtern()) {
         try self.addOrUpdateImport(decl);
+        return;
     }
 
     if (code.len == 0) return;