authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-15 22:49:12+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-19 20:01:23+01:00
log38253a680d8a53723667eca2c1f9a56b183dea8c
tree5b7f64fc4426e9b88ffb183f7744d1dbb71c214a
parent7c6981e0c0f2412c49457a900e5a6a4db96be2e8
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Refactor emitConstant to lower to `WValue`


1 files changed, 148 insertions(+), 133 deletions(-)

src/arch/wasm/CodeGen.zig+148-133
......@@ -27,8 +27,22 @@ const WValue = union(enum) {
2727 none: void,
2828 /// Index of the local variable
2929 local: u32,
30 /// Holds a memoized typed value
31 constant: TypedValue,
30 /// Represents the index of a local that contains a pointer to its stack position
31 stack: u32,
32 /// An immediate 32bit value
33 imm32: u32,
34 /// An immediate 64bit value
35 imm64: u64,
36 /// A constant 32bit float value
37 float32: f32,
38 /// A constant 64bit float value
39 float64: f64,
40 /// A value that represents a pointer to the data section
41 memory: u64,
42 /// Represents a function pointer
43 /// In wasm function pointers are indexes into a function table,
44 /// rather than an address in the data section.
45 function_index: u32,
3246 /// Used for types that contains of multiple areas within
3347 /// a memory region in the stack.
3448 /// The local represents the position in the stack,
......@@ -588,24 +602,14 @@ fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
588602/// Resolves the `WValue` for the given instruction `inst`
589603/// When the given instruction has a `Value`, it returns a constant instead
590604fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue {
591 const inst_index = Air.refToIndex(ref) orelse {
592 const tv = Air.Inst.Ref.typed_value_map[@enumToInt(ref)];
593 if (!tv.ty.hasCodeGenBits()) {
594 return WValue.none;
595 }
596 return WValue{ .constant = tv };
597 };
605 const gop = try self.values.getOrPut(self.gpa, ref);
606 if (gop.found_existing) return gop.value_ptr.*;
598607
599 const inst_type = self.air.typeOfIndex(inst_index);
600 // It's allowed to have 0-bit integers
601 if (!inst_type.hasCodeGenBits() and !inst_type.isInt()) return WValue{ .none = {} };
602
603 if (self.air.instructions.items(.tag)[inst_index] == .constant) {
604 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
605 return WValue{ .constant = .{ .ty = inst_type, .val = self.air.values[ty_pl.payload] } };
606 }
607
608 return self.values.get(inst_index).?; // Instruction does not dominate all uses!
608 // when we did not find an existing instruction, it
609 // means we must generate it from a constant.
610 const val = self.air.value(ref).?;
611 const ty = self.air.typeOf(ref);
612 return self.lowerConstant(val, ty);
609613}
610614
611615/// Appends a MIR instruction and returns its index within the list of instructions
......@@ -730,7 +734,12 @@ fn emitWValue(self: *Self, val: WValue) InnerError!void {
730734 .none => {}, // no-op
731735 .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local),
732736 .local => |idx| try self.addLabel(.local_get, idx),
733 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack
737 .imm32 => |val| try self.addImm32(@bitCast(i32, val)),
738 .imm64 => |val| try self.addImm64(val),
739 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
740 .float64 => |val| try self.addFloat64(val),
741 .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation
742 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
734743 }
735744}
736745
......@@ -929,7 +938,8 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {
929938 return Result.appended;
930939 },
931940 .Enum => {
932 try self.emitConstant(val, ty);
941 const size = @intCast(usize, ty.abiSize(self.target));
942 try self.code.appendNTimes(0xaa, size);
933943 return Result.appended;
934944 },
935945 .Bool => {
......@@ -1207,6 +1217,10 @@ fn ptrSize(self: *const Self) u16 {
12071217 return @divExact(self.target.cpu.arch.ptrBitWidth(), 8);
12081218}
12091219
1220fn arch(self: *const Self) std.Target.Cpu.Arch {
1221 return self.target.cpu.arch;
1222}
1223
12101224/// For a given `Type`, will return true when the type will be passed
12111225/// by reference, rather than by value.
12121226fn isByRef(self: Self, ty: Type) bool {
......@@ -1699,13 +1713,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
16991713 }
17001714 },
17011715 .Struct, .Array => {
1702 const final_rhs = if (rhs == .constant) blk: {
1703 const tmp = try self.allocLocal(Type.usize);
1704 try self.emitWValue(rhs);
1705 try self.addLabel(.local_set, tmp.local);
1706 break :blk tmp;
1707 } else rhs;
1708 return try self.memCopy(ty, lhs, final_rhs);
1716 return try self.memCopy(ty, lhs, rhs);
17091717 },
17101718 .Pointer => {
17111719 if (ty.isSlice() and rhs == .constant) {
......@@ -1748,11 +1756,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17481756 }
17491757 },
17501758 .Int => if (ty.intInfo(self.target).bits > 64) {
1751 if (rhs == .constant) {
1752 try self.emitWValue(rhs);
1753 try self.addLabel(.local_set, lhs.local);
1754 return;
1755 }
17561759 return try self.memCopy(ty, lhs, rhs);
17571760 },
17581761 else => {},
......@@ -1933,7 +1936,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
19331936 return bin_local;
19341937}
19351938
1936fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1939fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
19371940 if (val.isUndefDeep()) return self.emitUndefined(ty);
19381941 switch (ty.zigTypeTag()) {
19391942 .Int => {
......@@ -1941,16 +1944,16 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
19411944 // write constant
19421945 switch (int_info.signedness) {
19431946 .signed => switch (int_info.bits) {
1944 0...32 => return try self.addImm32(@intCast(i32, val.toSignedInt())),
1945 33...64 => return try self.addImm64(@bitCast(u64, val.toSignedInt())),
1947 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) },
1948 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) },
19461949 65...128 => {},
1947 else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}),
1950 else => |bits| return self.fail("Wasm todo: lowerConstant for integer with {d} bits", .{bits}),
19481951 },
19491952 .unsigned => switch (int_info.bits) {
1950 0...32 => return try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))),
1951 33...64 => return try self.addImm64(val.toUnsignedInt()),
1953 0...32 => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1954 33...64 => return WValue{ .imm64 = val.toUnsignedInt() },
19521955 65...128 => {},
1953 else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}),
1956 else => |bits| return self.fail("Wasm TODO: lowerConstant for integer with {d} bits", .{bits}),
19541957 },
19551958 }
19561959 const result = try self.allocStack(ty);
......@@ -1970,53 +1973,58 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
19701973 try self.addImm64(limb);
19711974 try self.addMemArg(.i64_store, .{ .offset = @intCast(u32, index * 8), .alignment = 8 });
19721975 }
1973 try self.addLabel(.local_get, result.local);
1976 return result;
19741977 },
1975 .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())),
1976 .Float => {
1977 // write constant
1978 switch (ty.floatBits(self.target)) {
1979 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val.toFloat(f32) } }),
1980 64 => try self.addFloat64(val.toFloat(f64)),
1981 else => |bits| return self.fail("Wasm TODO: emitConstant for float with {d} bits", .{bits}),
1982 }
1978 .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1979 .Float => switch (ty.floatBits(self.target)) {
1980 0...32 => return WValue{ .float32 = val.toFloat(f32) },
1981 33...64 => return WValue{ .float64 = val.toFloat(f64) },
1982 else => |bits| return self.fail("Wasm TODO: emitConstant for floats with {d} bits", .{bits}),
19831983 },
1984 .Pointer => {
1985 if (val.castTag(.slice)) |slice| {
1984 .Pointer => switch (val) {
1985 .slice => {
1986 const result = try self.allocStack(ty);
19861987 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1987 try self.emitConstant(slice.data.ptr, ty.slicePtrFieldType(&buf));
1988 try self.emitConstant(slice.data.len, Type.usize);
1989 } else if (val.castTag(.decl_ref)) |payload| {
1990 const decl = payload.data;
1988 const ptr = try self.lowerConstant(val.slicePtr(), ty.slicePtrFieldType(&buf));
1989 const len = val.sliceLen();
1990 try self.store(result, ptr, Type.usize, 0);
1991 try self.addLabel(.local_get, result.local);
1992 switch (self.arch()) {
1993 .wasm32 => {
1994 try self.addImm32(@bitCast(i32, @intCast(u32, len)));
1995 try self.addMemArg(.i32_store, .{ .offset = self.ptrSize(), .alignment = self.ptrSize() });
1996 },
1997 .wasm64 => {
1998 try self.addImm64(len);
1999 try self.addMemArg(.i64_store, .{ .offset = self.ptrSize(), .alignment = self.ptrSize() });
2000 },
2001 }
2002 return result;
2003 },
2004 .decl_ref => {
2005 const decl = val.castTag(.decl_ref).?.data;
19912006 decl.markAlive();
1992 // Function pointers use a table index, rather than a memory address
2007 const target_sym_index = decl.link.wasm.sym_index;
19932008 if (decl.ty.zigTypeTag() == .Fn) {
1994 const target_sym_index = decl.link.wasm.sym_index;
19952009 try self.bin_file.addTableFunction(target_sym_index);
1996 try self.addLabel(.function_index, target_sym_index);
1997 } else {
1998 try self.addLabel(.memory_address, decl.link.wasm.sym_index);
1999 }
2000 } else if (val.castTag(.int_u64)) |int_ptr| {
2001 try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data)));
2002 } else if (val.tag() == .zero or val.tag() == .null_value) {
2003 try self.addImm32(0);
2004 } else if (val.tag() == .one) {
2005 try self.addImm32(1);
2006 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});
2007 },
2008 .Void => {},
2010 return WValue{ .function_index = target_sym_index };
2011 } else return WValue{ .memory = target_sym_index };
2012 },
2013 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
2014 .zero, .null_value => return WValue{ .imm32 = 0 },
2015 else => return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}),
2016 },
20092017 .Enum => {
20102018 if (val.castTag(.enum_field_index)) |field_index| {
20112019 switch (ty.tag()) {
2012 .enum_simple => try self.addImm32(@bitCast(i32, field_index.data)),
2020 .enum_simple => return WValue{ .imm32 = field_index.data },
20132021 .enum_full, .enum_nonexhaustive => {
20142022 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
20152023 if (enum_full.values.count() != 0) {
20162024 const tag_val = enum_full.values.keys()[field_index.data];
2017 try self.emitConstant(tag_val, enum_full.tag_ty);
2025 return self.lowerConstant(tag_val, enum_full.tag_ty);
20182026 } else {
2019 try self.addImm32(@bitCast(i32, field_index.data));
2027 return WValue{ .imm32 = field_index.data };
20202028 }
20212029 },
20222030 else => unreachable,
......@@ -2024,55 +2032,61 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
20242032 } else {
20252033 var int_tag_buffer: Type.Payload.Bits = undefined;
20262034 const int_tag_ty = ty.intTagType(&int_tag_buffer);
2027 try self.emitConstant(val, int_tag_ty);
2035 return self.lowerConstant(val, int_tag_ty);
20282036 }
20292037 },
20302038 .ErrorSet => {
20312039 const error_index = self.global_error_set.get(val.getError().?).?;
2032 try self.addImm32(@bitCast(i32, error_index));
2040 return WValue{ .imm32 = error_index };
20332041 },
20342042 .ErrorUnion => {
20352043 const error_type = ty.errorUnionSet();
20362044 const payload_type = ty.errorUnionPayload();
2037 if (val.castTag(.eu_payload)) |pl| {
2038 const payload_val = pl.data;
2039 // no error, so write a '0' const
2040 try self.addImm32(0);
2041
2042 if (payload_type.hasCodeGenBits()) {
2043 // after the error code, we emit the payload
2044 try self.emitConstant(payload_val, payload_type);
2045 }
2046 } else {
2047 // write the error val
2048 try self.emitConstant(val, error_type);
2049
2050 if (payload_type.hasCodeGenBits()) {
2051 // no payload, so write a '0' const
2052 try self.addImm32(0);
2053 }
2045 const is_pl = val.errorUnionIsPayload();
2046 const err_val = if (!is_pl) val else Value.initTag(.zero);
2047 const error_value = try self.lowerConstant(err_val, error_type);
2048 if (!payload_type.hasCodeGenBits()) {
2049 return error_value;
20542050 }
2055 },
2056 .Optional => {
2051
2052 const result = try self.allocStack(ty);
2053 try self.store(result, error_value, error_type, 0);
2054 const payload = try lowerConstant(
2055 if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
2056 payload_type,
2057 );
2058 const pl_ptr = if (self.isByRef(payload_type)) try self.buildPointerOffset(
2059 result,
2060 error_type.abiSize(self.target),
2061 .new,
2062 ) else result;
2063 try self.store(pl_ptr, payload, payload_type, 0);
2064 return result;
2065 },
2066 .Optional => if (ty.isPtrLikeOptional()) {
2067 return self.lowerConstant(val, payload_type);
2068 } else {
20572069 var buf: Type.Payload.ElemType = undefined;
20582070 const payload_type = ty.optionalChild(&buf);
2059 if (ty.isPtrLikeOptional()) {
2060 try self.emitConstant(val, payload_type);
2061 return;
2071 const is_pl = val.tag() == .opt_payload;
2072 const null_value = WValue{ .imm32 = if (is_pl) @as(u32, 0) else 1 };
2073 if (!payload_type.hasCodeGenBits()) {
2074 return null_value;
20622075 }
20632076
2064 // When constant has value 'null', set is_null local to '1'
2065 // and payload to '0'
2066 if (val.castTag(.opt_payload)) |payload| {
2067 try self.addImm32(1);
2068 if (payload_type.hasCodeGenBits())
2069 try self.emitConstant(payload.data, payload_type);
2070 } else {
2071 // set null-tag
2072 try self.addImm32(0);
2073 // null-tag is set, so write a '0' const
2074 try self.addImm32(0);
2075 }
2077 const result = try self.allocStack(ty);
2078 try self.store(result, null_value, Type.initTag(.u8), 0);
2079 const payload = try self.lowerConstant(
2080 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
2081 payload_type,
2082 );
2083
2084 const pl_ptr = if (self.isByRef(payload_type)) blk: {
2085 const offset = ty.abiSize(self.target) - payload_type.abiSize();
2086 break :blk try self.buildPointerOffset(result, offset, .new);
2087 } else result;
2088 try self.store(pl_ptr, payload, payload_type, 0);
2089 return result;
20762090 },
20772091 .Struct => {
20782092 const struct_data = val.castTag(.@"struct").?;
......@@ -2083,16 +2097,15 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
20832097 const offset = try self.copyLocal(result, ty);
20842098 for (fields.values()) |field, index| {
20852099 const tmp = try self.allocLocal(field.ty);
2086 try self.emitConstant(struct_data.data[index], field.ty);
2087 try self.addLabel(.local_set, tmp.local);
2088 try self.store(offset, tmp, field.ty, 0);
2100 const field_value = try self.lowerConstant(struct_data.data[index], field.ty);
2101 try self.store(offset, field_value, field.ty, 0);
20892102
20902103 // this prevents us from emitting useless instructions when we reached the end of the loop
20912104 if (index != (fields.count() - 1)) {
20922105 _ = try self.buildPointerOffset(offset, field.ty.abiSize(self.target), .modify);
20932106 }
20942107 }
2095 try self.addLabel(.local_get, result.local);
2108 return result;
20962109 },
20972110 .Array => {
20982111 const result = try self.allocStack(ty);
......@@ -2108,7 +2121,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
21082121 const tmp = try self.allocLocal(elem_ty);
21092122 const offset = try self.copyLocal(result, ty);
21102123 for (array.data) |value, index| {
2111 try self.emitConstant(value, elem_ty);
2124 try self.lowerConstant(value, elem_ty);
21122125 try self.addLabel(.local_set, tmp.local);
21132126 try self.store(offset, tmp, elem_ty, 0);
21142127
......@@ -2129,9 +2142,9 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
21292142 var index: u32 = 0;
21302143 while (index < len_with_sent) : (index += 1) {
21312144 if (sentinel != null and index == len) {
2132 try self.emitConstant(sentinel.?, elem_ty);
2145 try self.lowerConstant(sentinel.?, elem_ty);
21332146 } else {
2134 try self.emitConstant(value, elem_ty);
2147 try self.lowerConstant(value, elem_ty);
21352148 }
21362149 try self.addLabel(.local_set, tmp.local);
21372150 try self.store(offset, tmp, elem_ty, 0);
......@@ -2144,26 +2157,26 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
21442157 const elem_ty = ty.childType();
21452158 const sent_val = ty.sentinel().?;
21462159 const tmp = try self.allocLocal(elem_ty);
2147 try self.emitConstant(sent_val, elem_ty);
2160 try self.lowerConstant(sent_val, elem_ty);
21482161 try self.addLabel(.local_set, tmp.local);
21492162 try self.store(result, tmp, elem_ty, 0);
21502163 } else unreachable;
2151 try self.addLabel(.local_get, result.local);
2164 return result;
21522165 },
21532166 else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
21542167 }
21552168}
21562169
2157fn emitUndefined(self: *Self, ty: Type) InnerError!void {
2170fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
21582171 switch (ty.zigTypeTag()) {
21592172 .Int => switch (ty.intInfo(self.target).bits) {
2160 0...32 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
2161 33...64 => try self.addImm64(0xaaaaaaaaaaaaaaaa),
2173 0...32 => return WValue{ .imm32 = 0xaaaaaaaa },
2174 33...64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
21622175 else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}),
21632176 },
21642177 .Float => switch (ty.floatBits(self.target)) {
2165 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) } }),
2166 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),
2178 0...32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) },
2179 33...64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) },
21672180 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),
21682181 },
21692182 .Array, .Struct => {
......@@ -2172,18 +2185,18 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void {
21722185 var offset: u32 = 0;
21732186 while (offset < abi_size) : (offset += 1) {
21742187 try self.emitWValue(result);
2175 try self.addImm32(0xaa);
2176 switch (self.ptrSize()) {
2177 4 => try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }),
2178 8 => try self.addMemArg(.i64_store8, .{ .offset = offset, .alignment = 1 }),
2188 try self.addImm32(0xaaaaaaaa);
2189 switch (self.arch()) {
2190 .wasm32 => try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }),
2191 .wasm64 => try self.addMemArg(.i64_store8, .{ .offset = offset, .alignment = 1 }),
21792192 else => unreachable,
21802193 }
21812194 }
2182 try self.addLabel(.local_get, result.local);
2195 return result;
21832196 },
2184 .Pointer => switch (self.ptrSize()) {
2185 4 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
2186 8 => try self.addImm64(0xaaaaaaaaaaaaaaaa),
2197 .Pointer => switch (self.arch()) {
2198 .wasm32 => return WValue{ .imm32 = 0xaaaaaaaa },
2199 .wasm64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
21872200 else => unreachable,
21882201 },
21892202 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),
......@@ -2595,7 +2608,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
25952608 // for single value prong we can emit a simple if
25962609 if (case.values.len == 1) {
25972610 try self.emitWValue(target);
2598 try self.emitConstant(case.values[0].value, target_ty);
2611 const val = try self.lowerConstant(case.values[0].value, target_ty);
2612 try self.emitWValue(val);
25992613 const opcode = buildOpcode(.{
26002614 .valtype1 = try self.typeToValtype(target_ty),
26012615 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
......@@ -2608,7 +2622,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26082622 try self.startBlock(.block, blocktype);
26092623 for (case.values) |value| {
26102624 try self.emitWValue(target);
2611 try self.emitConstant(value.value, target_ty);
2625 const val = try self.lowerConstant(value.value, target_ty);
2626 try self.emitWValue(val);
26122627 const opcode = buildOpcode(.{
26132628 .valtype1 = try self.typeToValtype(target_ty),
26142629 .op = .eq,