mirror of
https://github.com/ziglang/zig.git
synced 2024-12-13 06:37:30 +00:00
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.
This commit is contained in:
parent
3832b58229
commit
29013220d9
@ -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()}),
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user