diff options
| author | Luuk de Gram <luuk@degram.dev> | 2021-11-28 12:49:04 +0100 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2021-11-28 12:49:04 +0100 |
| commit | 7226ad2670f267b4d90b84d0e104fbb1fa41fe49 (patch) | |
| tree | 2f690511466db3bc73e13b994fd3844079e321b6 /src/arch | |
| parent | 9b5d61430fc7297b24d870adf42392ad113fa21b (diff) | |
| download | zig-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.zig | 30 | ||||
| -rw-r--r-- | src/arch/wasm/Emit.zig | 8 | ||||
| -rw-r--r-- | src/arch/wasm/Mir.zig | 5 |
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` |
