Commit 29013220d9

Luuk de Gram <luuk@degram.dev>
2022-01-30 15:24:03
wasm: Implement elem_ptr
This implements lowering elem_ptr for decl's and constants. To generate the correct pointer, we perform a relocation by using the addend that represents the offset. The offset is calculated by taking the element's size and multiplying that by the index. For constants this generates a single immediate instruction, and for decl's this generates a single pointer address.
1 parent 3832b58
Changed files (4)
src/arch/wasm/CodeGen.zig
@@ -39,6 +39,14 @@ const WValue = union(enum) {
     /// Note: The value contains the symbol index, rather than the actual address
     /// as we use this to perform the relocation.
     memory: u32,
+    /// A value that represents a parent pointer and an offset
+    /// from that pointer. i.e. when slicing with constant values.
+    memory_offset: struct {
+        /// The symbol of the parent pointer
+        pointer: u32,
+        /// Offset will be set as 'addend' when relocating
+        offset: u32,
+    },
     /// Represents a function pointer
     /// In wasm function pointers are indexes into a function table,
     /// rather than an address in the data section.
@@ -754,7 +762,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {
         .imm64 => |val| try self.addImm64(val),
         .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
         .float64 => |val| try self.addFloat64(val),
-        .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation
+        .memory => |ptr| {
+            const extra_index = try self.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
+            try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
+        },
+        .memory_offset => |mem_off| {
+            const extra_index = try self.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
+            try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
+        },
         .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
     }
 }
@@ -927,7 +942,7 @@ pub const DeclGen = struct {
                     .function => val.castTag(.function).?.data.owner_decl,
                     else => unreachable,
                 };
-                return try self.lowerDeclRef(ty, val, fn_decl);
+                return try self.lowerDeclRefValue(ty, val, fn_decl, 0);
             },
             .Optional => {
                 var opt_buf: Type.Payload.ElemType = undefined;
@@ -1115,11 +1130,11 @@ pub const DeclGen = struct {
             .Pointer => switch (val.tag()) {
                 .variable => {
                     const decl = val.castTag(.variable).?.data.owner_decl;
-                    return self.lowerDeclRef(ty, val, decl);
+                    return self.lowerDeclRefValue(ty, val, decl, 0);
                 },
                 .decl_ref => {
                     const decl = val.castTag(.decl_ref).?.data;
-                    return self.lowerDeclRef(ty, val, decl);
+                    return self.lowerDeclRefValue(ty, val, decl, writer, 0);
                 },
                 .slice => {
                     const slice = val.castTag(.slice).?.data;
@@ -1139,6 +1154,13 @@ pub const DeclGen = struct {
                     try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8));
                     return Result{ .appended = {} };
                 },
+                .elem_ptr => {
+                    const elem_ptr = val.castTag(.elem_ptr).?.data;
+                    const elem_size = ty.childType().abiSize(self.target());
+                    const offset = elem_ptr.index * elem_size;
+                    return self.lowerParentPtr(elem_ptr.array_ptr, writer, offset);
+                },
+                .int_u64 => return self.genTypedValue(Type.usize, val, writer),
                 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
             },
             .ErrorUnion => {
@@ -1179,7 +1201,36 @@ pub const DeclGen = struct {
         }
     }
 
-    fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result {
+    fn lowerParentPtr(self: *DeclGen, ptr_value: Value, offset: usize) InnerError!Result {
+        switch (ptr_value.tag()) {
+            .decl_ref => {
+                const decl = ptr_value.castTag(.decl_ref).?.data;
+                return self.lowerParentPtrDecl(ptr_value, decl, offset);
+            },
+            else => |tag| return self.fail("TODO: Implement lowerParentPtr for pointer value tag: {s}", .{tag}),
+        }
+    }
+
+    fn lowerParentPtrDecl(self: *DeclGen, ptr_val: Value, decl: *Module.Decl, offset: usize) InnerError!Result {
+        decl.markAlive();
+        var ptr_ty_payload: Type.Payload.ElemType = .{
+            .base = .{ .tag = .single_mut_pointer },
+            .data = decl.ty,
+        };
+        const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
+        return self.lowerDeclRefValue(ptr_ty, ptr_val, decl, offset);
+    }
+
+    fn lowerDeclRefValue(
+        self: *DeclGen,
+        ty: Type,
+        val: Value,
+        /// The target decl that is being pointed to
+        decl: *Module.Decl,
+        /// When lowering to an indexed pointer, we can specify the offset
+        /// which will then be used as 'addend' to the relocation.
+        offset: usize,
+    ) InnerError!Result {
         const writer = self.code.writer();
         if (ty.isSlice()) {
             var buf: Type.SlicePtrFieldTypeBuffer = undefined;
@@ -1202,6 +1253,7 @@ pub const DeclGen = struct {
             self.symbol_index, // source symbol index
             decl.link.wasm.sym_index, // target symbol index
             @intCast(u32, self.code.items.len), // offset
+            @intCast(u32, offset), // addend
         ));
         return Result{ .appended = {} };
     }
@@ -1974,6 +2026,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
                     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;
+                const offset = index * ty.childType().abiSize(self.target);
+                const array_ptr = try self.lowerConstant(elem_ptr.array_ptr, ty);
+
+                return WValue{ .memory_offset = .{
+                    .pointer = array_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()}),
src/arch/wasm/Emit.zig
@@ -326,25 +326,27 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
 }
 
 fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
-    const symbol_index = emit.mir.instructions.items(.data)[inst].label;
+    const extra_index = emit.mir.instructions.items(.data)[inst].payload;
+    const mem = emit.mir.extraData(Mir.Memory, extra_index).data;
     const mem_offset = emit.offset() + 1;
     const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;
     if (is_wasm32) {
         try emit.code.append(std.wasm.opcode(.i32_const));
         var buf: [5]u8 = undefined;
-        leb128.writeUnsignedFixed(5, &buf, symbol_index);
+        leb128.writeUnsignedFixed(5, &buf, mem.pointer);
         try emit.code.appendSlice(&buf);
     } else {
         try emit.code.append(std.wasm.opcode(.i64_const));
         var buf: [10]u8 = undefined;
-        leb128.writeUnsignedFixed(10, &buf, symbol_index);
+        leb128.writeUnsignedFixed(10, &buf, mem.pointer);
         try emit.code.appendSlice(&buf);
     }
 
     try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
         .offset = mem_offset,
-        .index = symbol_index,
+        .index = mem.pointer,
         .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
+        .addend = mem.offset,
     });
 }
 
src/arch/wasm/Mir.zig
@@ -546,3 +546,10 @@ pub const MemArg = struct {
     offset: u32,
     alignment: u32,
 };
+
+/// Represents a memory address, which holds both the pointer
+/// or the parent pointer and the offset to it.
+pub const Memory = struct {
+    pointer: u32,
+    offset: u32,
+};
src/link/Wasm.zig
@@ -345,10 +345,19 @@ pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32,
 
 /// For a given decl, find the given symbol index's atom, and create a relocation for the type.
 /// Returns the given pointer address
-pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32, target_symbol_index: u32, offset: u32) !u32 {
+pub fn getDeclVAddr(
+    self: *Wasm,
+    decl: *Module.Decl,
+    ty: Type,
+    symbol_index: u32,
+    target_symbol_index: u32,
+    offset: u32,
+    addend: u32,
+) !u32 {
     const atom = decl.link.wasm.symbolAtom(symbol_index);
     const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
     if (ty.zigTypeTag() == .Fn) {
+        std.debug.assert(addend == 0); // addend not allowed for function relocations
         // We found a function pointer, so add it to our table,
         // as function pointers are not allowed to be stored inside the data section.
         // They are instead stored in a function table which are called by index.
@@ -363,6 +372,7 @@ pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32
             .index = target_symbol_index,
             .offset = offset,
             .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
+            .addend = addend,
         });
     }
     // we do not know the final address at this point,