aboutsummaryrefslogtreecommitdiff
path: root/src/arch/wasm/CodeGen.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-03-23 20:18:50 -0400
committerGitHub <noreply@github.com>2022-03-23 20:18:50 -0400
commite02ec8f7f5485b4e95ca8089b25a1c76d85c5834 (patch)
tree6f73e582246515b07220df133b440b55d426313d /src/arch/wasm/CodeGen.zig
parenta36f4ee290fa9f3f1515e8aa9bd2bb0f0117c505 (diff)
parent5cb16dfa59c6e1de1fbcbfcfdecdca1335690df0 (diff)
downloadzig-e02ec8f7f5485b4e95ca8089b25a1c76d85c5834.tar.gz
zig-e02ec8f7f5485b4e95ca8089b25a1c76d85c5834.zip
Merge pull request #11280 from Luukdegram/wasm-errors
stage2: wasm - `@errorName` and more
Diffstat (limited to 'src/arch/wasm/CodeGen.zig')
-rw-r--r--src/arch/wasm/CodeGen.zig128
1 files changed, 112 insertions, 16 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index eb4004fa2c..eb8d72a994 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1329,6 +1329,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.fptrunc => self.airFptrunc(inst),
.fpext => self.airFpext(inst),
.float_to_int => self.airFloatToInt(inst),
+ .int_to_float => self.airIntToFloat(inst),
.get_union_tag => self.airGetUnionTag(inst),
// TODO
@@ -1382,6 +1383,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.slice_elem_val => self.airSliceElemVal(inst),
.slice_elem_ptr => self.airSliceElemPtr(inst),
.slice_ptr => self.airSlicePtr(inst),
+ .ptr_slice_len_ptr => self.airPtrSliceFieldPtr(inst, self.ptrSize()),
+ .ptr_slice_ptr_ptr => self.airPtrSliceFieldPtr(inst, 0),
.store => self.airStore(inst),
.set_union_tag => self.airSetUnionTag(inst),
@@ -1398,11 +1401,14 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.unreach => self.airUnreachable(inst),
.wrap_optional => self.airWrapOptional(inst),
- .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst),
- .unwrap_errunion_err => self.airUnwrapErrUnionError(inst),
+ .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst, false),
+ .unwrap_errunion_payload_ptr => self.airUnwrapErrUnionPayload(inst, true),
+ .unwrap_errunion_err => self.airUnwrapErrUnionError(inst, false),
+ .unwrap_errunion_err_ptr => self.airUnwrapErrUnionError(inst, true),
.wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
.wrap_errunion_err => self.airWrapErrUnionErr(inst),
.errunion_payload_ptr_set => self.airErrUnionPayloadPtrSet(inst),
+ .error_name => self.airErrorName(inst),
.wasm_memory_size => self.airWasmMemorySize(inst),
.wasm_memory_grow => self.airWasmMemoryGrow(inst),
@@ -1428,8 +1434,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.bit_reverse,
.is_err_ptr,
.is_non_err_ptr,
- .unwrap_errunion_payload_ptr,
- .unwrap_errunion_err_ptr,
.sqrt,
.sin,
@@ -1445,9 +1449,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.round,
.trunc_float,
- .ptr_slice_len_ptr,
- .ptr_slice_ptr_ptr,
- .int_to_float,
.cmpxchg_weak,
.cmpxchg_strong,
.fence,
@@ -1458,7 +1459,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.atomic_store_seq_cst,
.atomic_rmw,
.tag_name,
- .error_name,
.mul_add,
// For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248
@@ -1886,6 +1886,23 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
.offset = @intCast(u32, offset),
} };
},
+ .opt_payload_ptr => {
+ const payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
+ const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty);
+ var buf: Type.Payload.ElemType = undefined;
+ const payload_ty = payload_ptr.container_ty.optionalChild(&buf);
+ if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) {
+ return parent_ptr;
+ }
+
+ const abi_size = payload_ptr.container_ty.abiSize(self.target);
+ const offset = abi_size - payload_ty.abiSize(self.target);
+
+ return WValue{ .memory_offset = .{
+ .pointer = parent_ptr.memory,
+ .offset = @intCast(u32, offset),
+ } };
+ },
else => |tag| return self.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
}
}
@@ -1948,7 +1965,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
else => unreachable,
},
.Pointer => switch (val.tag()) {
- .field_ptr, .elem_ptr => {
+ .field_ptr, .elem_ptr, .opt_payload_ptr => {
return self.lowerParentPtr(val, ty.childType());
},
.int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
@@ -2452,7 +2469,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (case_value.integer == value) break :blk @intCast(u32, idx);
}
}
- break :blk if (has_else_body) case_i else unreachable;
+ // error sets are almost always sparse so we use the default case
+ // for errors that are not present in any branch. This is fine as this default
+ // case will never be hit for those cases but we do save runtime cost and size
+ // by using a jump table for this instead of if-else chains.
+ break :blk if (has_else_body or target_ty.zigTypeTag() == .ErrorSet) case_i else unreachable;
};
self.mir_extra.appendAssumeCapacity(idx);
} else if (has_else_body) {
@@ -2539,30 +2560,32 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
return is_err_tmp;
}
-fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
- const err_ty = self.air.typeOf(ty_op.operand);
+ const op_ty = self.air.typeOf(ty_op.operand);
+ const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
const payload_ty = err_ty.errorUnionPayload();
if (!payload_ty.hasRuntimeBits()) return WValue{ .none = {} };
const err_align = err_ty.abiAlignment(self.target);
const set_size = err_ty.errorUnionSet().abiSize(self.target);
const offset = mem.alignForwardGeneric(u64, set_size, err_align);
- if (isByRef(payload_ty, self.target)) {
+ if (op_is_ptr or isByRef(payload_ty, self.target)) {
return self.buildPointerOffset(operand, offset, .new);
}
return self.load(operand, payload_ty, @intCast(u32, offset));
}
-fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
- const err_ty = self.air.typeOf(ty_op.operand);
+ const op_ty = self.air.typeOf(ty_op.operand);
+ const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
const payload_ty = err_ty.errorUnionPayload();
- if (!payload_ty.hasRuntimeBits()) {
+ if (op_is_ptr or !payload_ty.hasRuntimeBits()) {
return operand;
}
@@ -3210,6 +3233,28 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
return result;
}
+fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const operand = try self.resolveInst(ty_op.operand);
+ const dest_ty = self.air.typeOfIndex(inst);
+ const op_ty = self.air.typeOf(ty_op.operand);
+
+ try self.emitWValue(operand);
+ const op = buildOpcode(.{
+ .op = .convert,
+ .valtype1 = typeToValtype(dest_ty, self.target),
+ .valtype2 = typeToValtype(op_ty, self.target),
+ .signedness = if (op_ty.isSignedInt()) .signed else .unsigned,
+ });
+ try self.addTag(Mir.Inst.Tag.fromOpcode(op));
+
+ const result = try self.allocLocal(dest_ty);
+ try self.addLabel(.local_set, result.local);
+ return result;
+}
+
fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
@@ -3618,3 +3663,54 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
try self.addLabel(.local_set, result.local);
return result;
}
+
+fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const operand = try self.resolveInst(un_op);
+
+ // First retrieve the symbol index to the error name table
+ // that will be used to emit a relocation for the pointer
+ // to the error name table.
+ //
+ // Each entry to this table is a slice (ptr+len).
+ // The operand in this instruction represents the index within this table.
+ // This means to get the final name, we emit the base pointer and then perform
+ // pointer arithmetic to find the pointer to this slice and return that.
+ //
+ // As the names are global and the slice elements are constant, we do not have
+ // to make a copy of the ptr+value but can point towards them directly.
+ const error_table_symbol = try self.bin_file.getErrorTableSymbol();
+ const name_ty = Type.initTag(.const_slice_u8_sentinel_0);
+ const abi_size = name_ty.abiSize(self.target);
+
+ const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation
+ try self.emitWValue(error_name_value);
+ try self.emitWValue(operand);
+ switch (self.arch()) {
+ .wasm32 => {
+ try self.addImm32(@bitCast(i32, @intCast(u32, abi_size)));
+ try self.addTag(.i32_mul);
+ try self.addTag(.i32_add);
+ },
+ .wasm64 => {
+ try self.addImm64(abi_size);
+ try self.addTag(.i64_mul);
+ try self.addTag(.i64_add);
+ },
+ else => unreachable,
+ }
+
+ const result_ptr = try self.allocLocal(Type.usize);
+ try self.addLabel(.local_set, result_ptr.local);
+ return result_ptr;
+}
+
+fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const slice_ptr = try self.resolveInst(ty_op.operand);
+ return self.buildPointerOffset(slice_ptr, offset, .new);
+}