authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-30 15:24:03+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-03 21:42:48+01:00
log29013220d95f60669c4a181d157157aea9f137b5
tree0d56abd5a9d6eb7a037df535d47b9f900912fa1f
parent3832b582292d3065f600e7c7a8393c411e6cdb0a
signaturelock-open Commit is signed but in an unrecognized format.

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.

4 files changed, 92 insertions(+), 10 deletions(-)

src/arch/wasm/CodeGen.zig+68-5
...@@ -39,6 +39,14 @@ const WValue = union(enum) {...@@ -39,6 +39,14 @@ const WValue = union(enum) {
39 /// Note: The value contains the symbol index, rather than the actual address39 /// Note: The value contains the symbol index, rather than the actual address
40 /// as we use this to perform the relocation.40 /// as we use this to perform the relocation.
41 memory: u32,41 memory: u32,
42 /// A value that represents a parent pointer and an offset
43 /// from that pointer. i.e. when slicing with constant values.
44 memory_offset: struct {
45 /// The symbol of the parent pointer
46 pointer: u32,
47 /// Offset will be set as 'addend' when relocating
48 offset: u32,
49 },
42 /// Represents a function pointer50 /// Represents a function pointer
43 /// In wasm function pointers are indexes into a function table,51 /// In wasm function pointers are indexes into a function table,
44 /// rather than an address in the data section.52 /// rather than an address in the data section.
...@@ -754,7 +762,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {...@@ -754,7 +762,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {
754 .imm64 => |val| try self.addImm64(val),762 .imm64 => |val| try self.addImm64(val),
755 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),763 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
756 .float64 => |val| try self.addFloat64(val),764 .float64 => |val| try self.addFloat64(val),
757 .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation765 .memory => |ptr| {
766 const extra_index = try self.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
767 try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
768 },
769 .memory_offset => |mem_off| {
770 const extra_index = try self.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
771 try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
772 },
758 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation773 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
759 }774 }
760}775}
...@@ -927,7 +942,7 @@ pub const DeclGen = struct {...@@ -927,7 +942,7 @@ pub const DeclGen = struct {
927 .function => val.castTag(.function).?.data.owner_decl,942 .function => val.castTag(.function).?.data.owner_decl,
928 else => unreachable,943 else => unreachable,
929 };944 };
930 return try self.lowerDeclRef(ty, val, fn_decl);945 return try self.lowerDeclRefValue(ty, val, fn_decl, 0);
931 },946 },
932 .Optional => {947 .Optional => {
933 var opt_buf: Type.Payload.ElemType = undefined;948 var opt_buf: Type.Payload.ElemType = undefined;
...@@ -1115,11 +1130,11 @@ pub const DeclGen = struct {...@@ -1115,11 +1130,11 @@ pub const DeclGen = struct {
1115 .Pointer => switch (val.tag()) {1130 .Pointer => switch (val.tag()) {
1116 .variable => {1131 .variable => {
1117 const decl = val.castTag(.variable).?.data.owner_decl;1132 const decl = val.castTag(.variable).?.data.owner_decl;
1118 return self.lowerDeclRef(ty, val, decl);1133 return self.lowerDeclRefValue(ty, val, decl, 0);
1119 },1134 },
1120 .decl_ref => {1135 .decl_ref => {
1121 const decl = val.castTag(.decl_ref).?.data;1136 const decl = val.castTag(.decl_ref).?.data;
1122 return self.lowerDeclRef(ty, val, decl);1137 return self.lowerDeclRefValue(ty, val, decl, writer, 0);
1123 },1138 },
1124 .slice => {1139 .slice => {
1125 const slice = val.castTag(.slice).?.data;1140 const slice = val.castTag(.slice).?.data;
...@@ -1139,6 +1154,13 @@ pub const DeclGen = struct {...@@ -1139,6 +1154,13 @@ pub const DeclGen = struct {
1139 try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8));1154 try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8));
1140 return Result{ .appended = {} };1155 return Result{ .appended = {} };
1141 },1156 },
1157 .elem_ptr => {
1158 const elem_ptr = val.castTag(.elem_ptr).?.data;
1159 const elem_size = ty.childType().abiSize(self.target());
1160 const offset = elem_ptr.index * elem_size;
1161 return self.lowerParentPtr(elem_ptr.array_ptr, writer, offset);
1162 },
1163 .int_u64 => return self.genTypedValue(Type.usize, val, writer),
1142 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),1164 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
1143 },1165 },
1144 .ErrorUnion => {1166 .ErrorUnion => {
...@@ -1179,7 +1201,36 @@ pub const DeclGen = struct {...@@ -1179,7 +1201,36 @@ pub const DeclGen = struct {
1179 }1201 }
1180 }1202 }
11811203
1182 fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result {1204 fn lowerParentPtr(self: *DeclGen, ptr_value: Value, offset: usize) InnerError!Result {
1205 switch (ptr_value.tag()) {
1206 .decl_ref => {
1207 const decl = ptr_value.castTag(.decl_ref).?.data;
1208 return self.lowerParentPtrDecl(ptr_value, decl, offset);
1209 },
1210 else => |tag| return self.fail("TODO: Implement lowerParentPtr for pointer value tag: {s}", .{tag}),
1211 }
1212 }
1213
1214 fn lowerParentPtrDecl(self: *DeclGen, ptr_val: Value, decl: *Module.Decl, offset: usize) InnerError!Result {
1215 decl.markAlive();
1216 var ptr_ty_payload: Type.Payload.ElemType = .{
1217 .base = .{ .tag = .single_mut_pointer },
1218 .data = decl.ty,
1219 };
1220 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
1221 return self.lowerDeclRefValue(ptr_ty, ptr_val, decl, offset);
1222 }
1223
1224 fn lowerDeclRefValue(
1225 self: *DeclGen,
1226 ty: Type,
1227 val: Value,
1228 /// The target decl that is being pointed to
1229 decl: *Module.Decl,
1230 /// When lowering to an indexed pointer, we can specify the offset
1231 /// which will then be used as 'addend' to the relocation.
1232 offset: usize,
1233 ) InnerError!Result {
1183 const writer = self.code.writer();1234 const writer = self.code.writer();
1184 if (ty.isSlice()) {1235 if (ty.isSlice()) {
1185 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1236 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
...@@ -1202,6 +1253,7 @@ pub const DeclGen = struct {...@@ -1202,6 +1253,7 @@ pub const DeclGen = struct {
1202 self.symbol_index, // source symbol index1253 self.symbol_index, // source symbol index
1203 decl.link.wasm.sym_index, // target symbol index1254 decl.link.wasm.sym_index, // target symbol index
1204 @intCast(u32, self.code.items.len), // offset1255 @intCast(u32, self.code.items.len), // offset
1256 @intCast(u32, offset), // addend
1205 ));1257 ));
1206 return Result{ .appended = {} };1258 return Result{ .appended = {} };
1207 }1259 }
...@@ -1974,6 +2026,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {...@@ -1974,6 +2026,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
1974 return WValue{ .function_index = target_sym_index };2026 return WValue{ .function_index = target_sym_index };
1975 } else return WValue{ .memory = target_sym_index };2027 } else return WValue{ .memory = target_sym_index };
1976 },2028 },
2029 .elem_ptr => {
2030 const elem_ptr = val.castTag(.elem_ptr).?.data;
2031 const index = elem_ptr.index;
2032 const offset = index * ty.childType().abiSize(self.target);
2033 const array_ptr = try self.lowerConstant(elem_ptr.array_ptr, ty);
2034
2035 return WValue{ .memory_offset = .{
2036 .pointer = array_ptr.memory,
2037 .offset = @intCast(u32, offset),
2038 } };
2039 },
1977 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },2040 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1978 .zero, .null_value => return WValue{ .imm32 = 0 },2041 .zero, .null_value => return WValue{ .imm32 = 0 },
1979 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),2042 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),
src/arch/wasm/Emit.zig+6-4
...@@ -326,25 +326,27 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -326,25 +326,27 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
326}326}
327327
328fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {328fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
329 const symbol_index = emit.mir.instructions.items(.data)[inst].label;329 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
330 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;
330 const mem_offset = emit.offset() + 1;331 const mem_offset = emit.offset() + 1;
331 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;332 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;
332 if (is_wasm32) {333 if (is_wasm32) {
333 try emit.code.append(std.wasm.opcode(.i32_const));334 try emit.code.append(std.wasm.opcode(.i32_const));
334 var buf: [5]u8 = undefined;335 var buf: [5]u8 = undefined;
335 leb128.writeUnsignedFixed(5, &buf, symbol_index);336 leb128.writeUnsignedFixed(5, &buf, mem.pointer);
336 try emit.code.appendSlice(&buf);337 try emit.code.appendSlice(&buf);
337 } else {338 } else {
338 try emit.code.append(std.wasm.opcode(.i64_const));339 try emit.code.append(std.wasm.opcode(.i64_const));
339 var buf: [10]u8 = undefined;340 var buf: [10]u8 = undefined;
340 leb128.writeUnsignedFixed(10, &buf, symbol_index);341 leb128.writeUnsignedFixed(10, &buf, mem.pointer);
341 try emit.code.appendSlice(&buf);342 try emit.code.appendSlice(&buf);
342 }343 }
343344
344 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{345 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
345 .offset = mem_offset,346 .offset = mem_offset,
346 .index = symbol_index,347 .index = mem.pointer,
347 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,348 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
349 .addend = mem.offset,
348 });350 });
349}351}
350352
src/arch/wasm/Mir.zig+7
...@@ -546,3 +546,10 @@ pub const MemArg = struct {...@@ -546,3 +546,10 @@ pub const MemArg = struct {
546 offset: u32,546 offset: u32,
547 alignment: u32,547 alignment: u32,
548};548};
549
550/// Represents a memory address, which holds both the pointer
551/// or the parent pointer and the offset to it.
552pub const Memory = struct {
553 pointer: u32,
554 offset: u32,
555};
src/link/Wasm.zig+11-1
...@@ -345,10 +345,19 @@ pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32,...@@ -345,10 +345,19 @@ pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32,
345345
346/// For a given decl, find the given symbol index's atom, and create a relocation for the type.346/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
347/// Returns the given pointer address347/// Returns the given pointer address
348pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32, target_symbol_index: u32, offset: u32) !u32 {348pub fn getDeclVAddr(
349 self: *Wasm,
350 decl: *Module.Decl,
351 ty: Type,
352 symbol_index: u32,
353 target_symbol_index: u32,
354 offset: u32,
355 addend: u32,
356) !u32 {
349 const atom = decl.link.wasm.symbolAtom(symbol_index);357 const atom = decl.link.wasm.symbolAtom(symbol_index);
350 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;358 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
351 if (ty.zigTypeTag() == .Fn) {359 if (ty.zigTypeTag() == .Fn) {
360 std.debug.assert(addend == 0); // addend not allowed for function relocations
352 // We found a function pointer, so add it to our table,361 // We found a function pointer, so add it to our table,
353 // as function pointers are not allowed to be stored inside the data section.362 // as function pointers are not allowed to be stored inside the data section.
354 // They are instead stored in a function table which are called by index.363 // 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...@@ -363,6 +372,7 @@ pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32
363 .index = target_symbol_index,372 .index = target_symbol_index,
364 .offset = offset,373 .offset = offset,
365 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,374 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
375 .addend = addend,
366 });376 });
367 }377 }
368 // we do not know the final address at this point,378 // we do not know the final address at this point,