aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-03-09 16:03:20 -0500
committerGitHub <noreply@github.com>2022-03-09 16:03:20 -0500
commit2aa4a32097392c869fe2ef58f372a960c0268859 (patch)
tree18a320927655ba151a658a7003693a825dde96f1 /src
parentb936fe0a5855872814c9f70f958363f64896217b (diff)
parent90f08a69aaf8b5d6c36faa92d605afcd5fde0002 (diff)
downloadzig-2aa4a32097392c869fe2ef58f372a960c0268859.tar.gz
zig-2aa4a32097392c869fe2ef58f372a960c0268859.zip
Merge pull request #11105 from Luukdegram/wasm-misc
stage2: wasm - miscellaneous improvements
Diffstat (limited to 'src')
-rw-r--r--src/arch/wasm/CodeGen.zig168
-rw-r--r--src/arch/wasm/Emit.zig30
-rw-r--r--src/arch/wasm/Mir.zig60
3 files changed, 226 insertions, 32 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index c3e8bb7864..a32e97adf0 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -212,7 +212,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
16 => switch (args.valtype1.?) {
.i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u,
.i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u,
- .f32, .f64 => unreachable,
+ .f32 => return .f32_load,
+ .f64 => unreachable,
},
32 => switch (args.valtype1.?) {
.i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
@@ -242,7 +243,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
16 => switch (args.valtype1.?) {
.i32 => return .i32_store16,
.i64 => return .i64_store16,
- .f32, .f64 => unreachable,
+ .f32 => return .f32_store,
+ .f64 => unreachable,
},
32 => switch (args.valtype1.?) {
.i64 => return .i64_store32,
@@ -1064,7 +1066,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {
}
/// From given zig bitsize, returns the wasm bitsize
-fn toWasmIntBits(bits: u16) ?u16 {
+fn toWasmBits(bits: u16) ?u16 {
return for ([_]u16{ 32, 64 }) |wasm_bits| {
if (bits <= wasm_bits) return wasm_bits;
} else null;
@@ -1120,11 +1122,11 @@ fn isByRef(ty: Type, target: std.Target) bool {
.ErrorSet,
.Fn,
.Enum,
- .Vector,
.AnyFrame,
=> return false,
.Array,
+ .Vector,
.Struct,
.Frame,
.Union,
@@ -1155,22 +1157,24 @@ fn isByRef(ty: Type, target: std.Target) bool {
/// local value to store the pointer. This allows for local re-use and improves binary size.
fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue {
// do not perform arithmetic when offset is 0.
- if (offset == 0 and ptr_value.offset() == 0) return ptr_value;
+ if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;
const result_ptr: WValue = switch (action) {
.new => try self.allocLocal(Type.usize),
.modify => ptr_value,
};
try self.emitWValue(ptr_value);
- switch (self.arch()) {
- .wasm32 => {
- try self.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset())));
- try self.addTag(.i32_add);
- },
- .wasm64 => {
- try self.addImm64(offset + ptr_value.offset());
- try self.addTag(.i64_add);
- },
- else => unreachable,
+ if (offset + ptr_value.offset() > 0) {
+ switch (self.arch()) {
+ .wasm32 => {
+ try self.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset())));
+ try self.addTag(.i32_add);
+ },
+ .wasm64 => {
+ try self.addImm64(offset + ptr_value.offset());
+ try self.addTag(.i64_add);
+ },
+ else => unreachable,
+ }
}
try self.addLabel(.local_set, result_ptr.local);
return result_ptr;
@@ -1218,6 +1222,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.cond_br => self.airCondBr(inst),
.dbg_stmt => WValue.none,
.intcast => self.airIntcast(inst),
+ .fptrunc => self.airFptrunc(inst),
+ .fpext => self.airFpext(inst),
.float_to_int => self.airFloatToInt(inst),
.get_union_tag => self.airGetUnionTag(inst),
@@ -1263,6 +1269,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2),
.struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3),
.struct_field_val => self.airStructFieldVal(inst),
+ .field_parent_ptr => self.airFieldParentPtr(inst),
.switch_br => self.airSwitchBr(inst),
.trunc => self.airTrunc(inst),
@@ -1273,6 +1280,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.unwrap_errunion_err => self.airUnwrapErrUnionError(inst),
.wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
.wrap_errunion_err => self.airWrapErrUnionErr(inst),
+ .errunion_payload_ptr_set => self.airErrUnionPayloadPtrSet(inst),
.wasm_memory_size => self.airWasmMemorySize(inst),
.wasm_memory_grow => self.airWasmMemoryGrow(inst),
@@ -1297,8 +1305,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.bit_reverse,
.is_err_ptr,
.is_non_err_ptr,
- .fptrunc,
- .fpext,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
@@ -1331,8 +1337,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.atomic_rmw,
.tag_name,
.error_name,
- .errunion_payload_ptr_set,
- .field_parent_ptr,
.mul_add,
// For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248
@@ -1370,7 +1374,10 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const child_type = self.air.typeOfIndex(inst).childType();
- if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
+
+ if (!child_type.isFnOrHasRuntimeBits()) {
+ return self.allocStack(Type.usize); // create pointer to void
+ }
if (isByRef(child_type, self.target)) {
return self.return_value;
@@ -1513,7 +1520,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
return self.memCopy(ty, lhs, rhs);
},
- .Struct, .Array, .Union => {
+ .Struct, .Array, .Union, .Vector => {
return self.memCopy(ty, lhs, rhs);
},
.Pointer => {
@@ -1886,6 +1893,8 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
const kv = self.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
return @bitCast(i32, kv.value);
},
+ .Bool => return @intCast(i32, val.toSignedInt()),
+ .Pointer => return @intCast(i32, val.toSignedInt()),
else => unreachable, // Programmer called this function for an illegal type
}
}
@@ -2164,8 +2173,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
self.gpa.free(case.values);
} else case_list.deinit();
- var lowest: i32 = 0;
- var highest: i32 = 0;
+ var lowest_maybe: ?i32 = null;
+ var highest_maybe: ?i32 = null;
while (case_i < switch_br.data.cases_len) : (case_i += 1) {
const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
@@ -2177,11 +2186,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
for (items) |ref, i| {
const item_val = self.air.value(ref).?;
const int_val = self.valueAsI32(item_val, target_ty);
- if (int_val < lowest) {
- lowest = int_val;
+ if (lowest_maybe == null or int_val < lowest_maybe.?) {
+ lowest_maybe = int_val;
}
- if (int_val > highest) {
- highest = int_val;
+ if (highest_maybe == null or int_val > highest_maybe.?) {
+ highest_maybe = int_val;
}
values[i] = .{ .integer = int_val, .value = item_val };
}
@@ -2190,6 +2199,9 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
try self.startBlock(.block, blocktype);
}
+ // When highest and lowest are null, we have no cases and can use a jump table
+ const lowest = lowest_maybe orelse 0;
+ const highest = highest_maybe orelse 0;
// When the highest and lowest values are seperated by '50',
// we define it as sparse and use an if/else-chain, rather than a jump table.
// When the target is an integer size larger than u32, we have no way to use the value
@@ -2215,6 +2227,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
// we put inside, are atleast 0.
try self.addImm32(lowest * -1);
try self.addTag(.i32_add);
+ } else if (lowest > 0) {
+ // make the index start from 0 by substracting the lowest value
+ try self.addImm32(lowest);
+ try self.addTag(.i32_sub);
}
// Account for default branch so always add '1'
@@ -2223,12 +2239,13 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const table_extra_index = try self.addExtra(jump_table);
try self.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
try self.mir_extra.ensureUnusedCapacity(self.gpa, depth);
- while (lowest <= highest) : (lowest += 1) {
+ var value = lowest;
+ while (value <= highest) : (value += 1) {
// idx represents the branch we jump to
const idx = blk: {
for (case_list.items) |case, idx| {
for (case.values) |case_value| {
- if (case_value.integer == lowest) break :blk @intCast(u32, idx);
+ if (case_value.integer == value) break :blk @intCast(u32, idx);
}
}
break :blk if (has_else_body) case_i else unreachable;
@@ -2398,9 +2415,9 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const ref_info = ref_ty.intInfo(self.target);
const wanted_info = ty.intInfo(self.target);
- const op_bits = toWasmIntBits(ref_info.bits) orelse
+ const op_bits = toWasmBits(ref_info.bits) orelse
return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{ref_info.bits});
- const wanted_bits = toWasmIntBits(wanted_info.bits) orelse
+ const wanted_bits = toWasmBits(wanted_info.bits) orelse
return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{wanted_info.bits});
// hot path
@@ -2641,7 +2658,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const result = try self.allocLocal(self.air.getRefType(ty_op.ty));
const op_bits = op_ty.intInfo(self.target).bits;
- const wasm_bits = toWasmIntBits(wanted_bits) orelse
+ const wasm_bits = toWasmBits(wanted_bits) orelse
return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits});
// Use wasm's instruction to wrap from 64bit to 32bit integer when possible
@@ -3172,3 +3189,90 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
} else @as(u32, 0);
return self.load(operand, tag_ty, offset);
}
+
+fn airFpext(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 dest_ty = self.air.typeOfIndex(inst);
+ const dest_bits = dest_ty.floatBits(self.target);
+ const src_bits = self.air.typeOf(ty_op.operand).floatBits(self.target);
+ const operand = try self.resolveInst(ty_op.operand);
+
+ if (dest_bits == 64 and src_bits == 32) {
+ const result = try self.allocLocal(dest_ty);
+ try self.emitWValue(operand);
+ try self.addTag(.f64_promote_f32);
+ try self.addLabel(.local_set, result.local);
+ return result;
+ } else {
+ // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2
+ return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{dest_bits});
+ }
+}
+
+fn airFptrunc(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 dest_ty = self.air.typeOfIndex(inst);
+ const dest_bits = dest_ty.floatBits(self.target);
+ const src_bits = self.air.typeOf(ty_op.operand).floatBits(self.target);
+ const operand = try self.resolveInst(ty_op.operand);
+
+ if (dest_bits == 32 and src_bits == 64) {
+ const result = try self.allocLocal(dest_ty);
+ try self.emitWValue(operand);
+ try self.addTag(.f32_demote_f64);
+ try self.addLabel(.local_set, result.local);
+ return result;
+ } else {
+ // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2
+ return self.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{dest_bits});
+ }
+}
+
+fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const err_set_ty = self.air.typeOf(ty_op.operand).childType();
+ const err_ty = err_set_ty.errorUnionSet();
+ const payload_ty = err_set_ty.errorUnionPayload();
+ const operand = try self.resolveInst(ty_op.operand);
+
+ // set error-tag to '0' to annotate error union is non-error
+ try self.store(operand, .{ .imm32 = 0 }, err_ty, 0);
+
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+
+ if (!payload_ty.hasRuntimeBits()) {
+ return operand;
+ }
+
+ const err_align = err_set_ty.abiAlignment(self.target);
+ const set_size = err_ty.abiSize(self.target);
+ const offset = mem.alignForwardGeneric(u64, set_size, err_align);
+
+ return self.buildPointerOffset(operand, @intCast(u32, offset), .new);
+}
+
+fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
+ const field_ptr = try self.resolveInst(extra.field_ptr);
+
+ const struct_ty = self.air.getRefType(ty_pl.ty).childType();
+ const field_offset = struct_ty.structFieldOffset(extra.field_index, self.target);
+
+ if (field_offset == 0) {
+ return field_ptr;
+ }
+
+ const base = try self.buildPointerOffset(field_ptr, 0, .new);
+ try self.addLabel(.local_get, base.local);
+ try self.addImm32(@bitCast(i32, @intCast(u32, field_offset)));
+ try self.addTag(.i32_sub);
+ try self.addLabel(.local_set, base.local);
+ return base;
+}
diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig
index 4432e19638..e0b40794b8 100644
--- a/src/arch/wasm/Emit.zig
+++ b/src/arch/wasm/Emit.zig
@@ -153,6 +153,34 @@ pub fn emitMir(emit: *Emit) InnerError!void {
.i64_shl => try emit.emitTag(tag),
.i64_shr_s => try emit.emitTag(tag),
.i64_shr_u => try emit.emitTag(tag),
+ .f32_abs => try emit.emitTag(tag),
+ .f32_neg => try emit.emitTag(tag),
+ .f32_ceil => try emit.emitTag(tag),
+ .f32_floor => try emit.emitTag(tag),
+ .f32_trunc => try emit.emitTag(tag),
+ .f32_nearest => try emit.emitTag(tag),
+ .f32_sqrt => try emit.emitTag(tag),
+ .f32_add => try emit.emitTag(tag),
+ .f32_sub => try emit.emitTag(tag),
+ .f32_mul => try emit.emitTag(tag),
+ .f32_div => try emit.emitTag(tag),
+ .f32_min => try emit.emitTag(tag),
+ .f32_max => try emit.emitTag(tag),
+ .f32_copysign => try emit.emitTag(tag),
+ .f64_abs => try emit.emitTag(tag),
+ .f64_neg => try emit.emitTag(tag),
+ .f64_ceil => try emit.emitTag(tag),
+ .f64_floor => try emit.emitTag(tag),
+ .f64_trunc => try emit.emitTag(tag),
+ .f64_nearest => try emit.emitTag(tag),
+ .f64_sqrt => try emit.emitTag(tag),
+ .f64_add => try emit.emitTag(tag),
+ .f64_sub => try emit.emitTag(tag),
+ .f64_mul => try emit.emitTag(tag),
+ .f64_div => try emit.emitTag(tag),
+ .f64_min => try emit.emitTag(tag),
+ .f64_max => try emit.emitTag(tag),
+ .f64_copysign => try emit.emitTag(tag),
.i32_wrap_i64 => try emit.emitTag(tag),
.i64_extend_i32_s => try emit.emitTag(tag),
.i64_extend_i32_u => try emit.emitTag(tag),
@@ -161,6 +189,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {
.i64_extend8_s => try emit.emitTag(tag),
.i64_extend16_s => try emit.emitTag(tag),
.i64_extend32_s => try emit.emitTag(tag),
+ .f32_demote_f64 => try emit.emitTag(tag),
+ .f64_promote_f32 => try emit.emitTag(tag),
.i32_reinterpret_f32 => try emit.emitTag(tag),
.i64_reinterpret_f64 => try emit.emitTag(tag),
.f32_reinterpret_i32 => try emit.emitTag(tag),
diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig
index 04edb09dca..b24af64ff5 100644
--- a/src/arch/wasm/Mir.zig
+++ b/src/arch/wasm/Mir.zig
@@ -369,6 +369,62 @@ pub const Inst = struct {
/// Uses `tag`
i64_shr_u = 0x88,
/// Uses `tag`
+ f32_abs = 0x8B,
+ /// Uses `tag`
+ f32_neg = 0x8C,
+ /// Uses `tag`
+ f32_ceil = 0x8D,
+ /// Uses `tag`
+ f32_floor = 0x8E,
+ /// Uses `tag`
+ f32_trunc = 0x8F,
+ /// Uses `tag`
+ f32_nearest = 0x90,
+ /// Uses `tag`
+ f32_sqrt = 0x91,
+ /// Uses `tag`
+ f32_add = 0x92,
+ /// Uses `tag`
+ f32_sub = 0x93,
+ /// Uses `tag`
+ f32_mul = 0x94,
+ /// Uses `tag`
+ f32_div = 0x95,
+ /// Uses `tag`
+ f32_min = 0x96,
+ /// Uses `tag`
+ f32_max = 0x97,
+ /// Uses `tag`
+ f32_copysign = 0x98,
+ /// Uses `tag`
+ f64_abs = 0x99,
+ /// Uses `tag`
+ f64_neg = 0x9A,
+ /// Uses `tag`
+ f64_ceil = 0x9B,
+ /// Uses `tag`
+ f64_floor = 0x9C,
+ /// Uses `tag`
+ f64_trunc = 0x9D,
+ /// Uses `tag`
+ f64_nearest = 0x9E,
+ /// Uses `tag`
+ f64_sqrt = 0x9F,
+ /// Uses `tag`
+ f64_add = 0xA0,
+ /// Uses `tag`
+ f64_sub = 0xA1,
+ /// Uses `tag`
+ f64_mul = 0xA2,
+ /// Uses `tag`
+ f64_div = 0xA3,
+ /// Uses `tag`
+ f64_min = 0xA4,
+ /// Uses `tag`
+ f64_max = 0xA5,
+ /// Uses `tag`
+ f64_copysign = 0xA6,
+ /// Uses `tag`
i32_wrap_i64 = 0xA7,
/// Uses `tag`
i32_trunc_f32_s = 0xA8,
@@ -391,6 +447,10 @@ pub const Inst = struct {
/// Uses `tag`
i64_trunc_f64_u = 0xB1,
/// Uses `tag`
+ f32_demote_f64 = 0xB6,
+ /// Uses `tag`
+ f64_promote_f32 = 0xBB,
+ /// Uses `tag`
i32_reinterpret_f32 = 0xBC,
/// Uses `tag`
i64_reinterpret_f64 = 0xBD,