aboutsummaryrefslogtreecommitdiff
path: root/src/arch
diff options
context:
space:
mode:
authorLuuk de Gram <luuk@degram.dev>2021-11-28 12:49:04 +0100
committerLuuk de Gram <luuk@degram.dev>2021-11-28 12:49:04 +0100
commit7226ad2670f267b4d90b84d0e104fbb1fa41fe49 (patch)
tree2f690511466db3bc73e13b994fd3844079e321b6 /src/arch
parent9b5d61430fc7297b24d870adf42392ad113fa21b (diff)
downloadzig-7226ad2670f267b4d90b84d0e104fbb1fa41fe49.tar.gz
zig-7226ad2670f267b4d90b84d0e104fbb1fa41fe49.zip
wasm-link: Implement indirect function table
The function table contains all function pointers that are called by using call_indirect. During codegen, we create a relocation where the linker will resolve the correct index into the table and stores this value within the data section at the location of the pointer.
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/wasm/CodeGen.zig30
-rw-r--r--src/arch/wasm/Emit.zig8
-rw-r--r--src/arch/wasm/Mir.zig5
3 files changed, 39 insertions, 4 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index 3fac50ca7f..cbda80aee3 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1065,9 +1065,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const extra = self.air.extraData(Air.Call, pl_op.payload);
const args = self.air.extra[extra.end..][0..extra.data.args_len];
+ const ty = self.air.typeOf(pl_op.operand);
- const target: *Decl = blk: {
- const func_val = self.air.value(pl_op.operand).?;
+ const fn_ty = switch (ty.zigTypeTag()) {
+ .Fn => ty,
+ .Pointer => ty.childType(),
+ else => unreachable,
+ };
+
+ const target: ?*Decl = blk: {
+ const func_val = self.air.value(pl_op.operand) orelse break :blk null;
if (func_val.castTag(.function)) |func| {
break :blk func.data.owner_decl;
@@ -1082,9 +1089,24 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
try self.emitWValue(arg_val);
}
- try self.addLabel(.call, target.link.wasm.sym_index);
+ if (target) |direct| {
+ try self.addLabel(.call, direct.link.wasm.sym_index);
+ } else {
+ // in this case we call a function pointer
+ // so load its value onto the stack
+ std.debug.assert(ty.zigTypeTag() == .Pointer);
+ const operand = self.resolveInst(pl_op.operand);
+ const result = try self.load(operand, fn_ty, operand.local_with_offset.offset);
+ try self.addLabel(.local_get, result.local);
+
+ var fn_type = try self.genFunctype(fn_ty);
+ defer fn_type.deinit(self.gpa);
+
+ const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);
+ try self.addLabel(.call_indirect, fn_type_index);
+ }
- const ret_ty = target.ty.fnReturnType();
+ const ret_ty = fn_ty.fnReturnType();
switch (ret_ty.zigTypeTag()) {
.Void, .NoReturn => return WValue.none,
else => {
diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig
index d01f319b91..c4368ab943 100644
--- a/src/arch/wasm/Emit.zig
+++ b/src/arch/wasm/Emit.zig
@@ -47,6 +47,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
// relocatables
.call => try emit.emitCall(inst),
+ .call_indirect => try emit.emitCallIndirect(inst),
.global_get => try emit.emitGlobal(tag, inst),
.global_set => try emit.emitGlobal(tag, inst),
.memory_address => try emit.emitMemAddress(inst),
@@ -276,6 +277,13 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
});
}
+fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
+ const label = emit.mir.instructions.items(.data)[inst].label;
+ try emit.code.append(std.wasm.opcode(.call_indirect));
+ try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
+ try leb128.writeULEB128(emit.code.writer(), label);
+}
+
fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
const symbol_index = emit.mir.instructions.items(.data)[inst].label;
try emit.code.append(std.wasm.opcode(.i32_const));
diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig
index 97d8875984..5891893f33 100644
--- a/src/arch/wasm/Mir.zig
+++ b/src/arch/wasm/Mir.zig
@@ -69,6 +69,11 @@ pub const Inst = struct {
///
/// Uses `label`
call = 0x10,
+ /// Calls a function pointer by its function signature
+ /// and index into the function table.
+ ///
+ /// Uses `label`
+ call_indirect = 0x11,
/// Loads a local at given index onto the stack.
///
/// Uses `label`