| ... | ... | @@ -27,8 +27,22 @@ const WValue = union(enum) { |
| 27 | 27 | none: void, |
| 28 | 28 | /// Index of the local variable |
| 29 | 29 | 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, |
| 32 | 46 | /// Used for types that contains of multiple areas within |
| 33 | 47 | /// a memory region in the stack. |
| 34 | 48 | /// The local represents the position in the stack, |
| ... | ... | @@ -588,24 +602,14 @@ fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError { |
| 588 | 602 | /// Resolves the `WValue` for the given instruction `inst` |
| 589 | 603 | /// When the given instruction has a `Value`, it returns a constant instead |
| 590 | 604 | fn 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.*; |
| 598 | 607 | |
| 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); |
| 609 | 613 | } |
| 610 | 614 | |
| 611 | 615 | /// 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 { |
| 730 | 734 | .none => {}, // no-op |
| 731 | 735 | .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local), |
| 732 | 736 | .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 |
| 734 | 743 | } |
| 735 | 744 | } |
| 736 | 745 | |
| ... | ... | @@ -929,7 +938,8 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 929 | 938 | return Result.appended; |
| 930 | 939 | }, |
| 931 | 940 | .Enum => { |
| 932 | | try self.emitConstant(val, ty); |
| 941 | const size = @intCast(usize, ty.abiSize(self.target)); |
| 942 | try self.code.appendNTimes(0xaa, size); |
| 933 | 943 | return Result.appended; |
| 934 | 944 | }, |
| 935 | 945 | .Bool => { |
| ... | ... | @@ -1207,6 +1217,10 @@ fn ptrSize(self: *const Self) u16 { |
| 1207 | 1217 | return @divExact(self.target.cpu.arch.ptrBitWidth(), 8); |
| 1208 | 1218 | } |
| 1209 | 1219 | |
| 1220 | fn arch(self: *const Self) std.Target.Cpu.Arch { |
| 1221 | return self.target.cpu.arch; |
| 1222 | } |
| 1223 | |
| 1210 | 1224 | /// For a given `Type`, will return true when the type will be passed |
| 1211 | 1225 | /// by reference, rather than by value. |
| 1212 | 1226 | fn isByRef(self: Self, ty: Type) bool { |
| ... | ... | @@ -1699,13 +1713,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1699 | 1713 | } |
| 1700 | 1714 | }, |
| 1701 | 1715 | .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); |
| 1709 | 1717 | }, |
| 1710 | 1718 | .Pointer => { |
| 1711 | 1719 | if (ty.isSlice() and rhs == .constant) { |
| ... | ... | @@ -1748,11 +1756,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1748 | 1756 | } |
| 1749 | 1757 | }, |
| 1750 | 1758 | .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 | | } |
| 1756 | 1759 | return try self.memCopy(ty, lhs, rhs); |
| 1757 | 1760 | }, |
| 1758 | 1761 | else => {}, |
| ... | ... | @@ -1933,7 +1936,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1933 | 1936 | return bin_local; |
| 1934 | 1937 | } |
| 1935 | 1938 | |
| 1936 | | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1939 | fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 1937 | 1940 | if (val.isUndefDeep()) return self.emitUndefined(ty); |
| 1938 | 1941 | switch (ty.zigTypeTag()) { |
| 1939 | 1942 | .Int => { |
| ... | ... | @@ -1941,16 +1944,16 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1941 | 1944 | // write constant |
| 1942 | 1945 | switch (int_info.signedness) { |
| 1943 | 1946 | .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()) }, |
| 1946 | 1949 | 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}), |
| 1948 | 1951 | }, |
| 1949 | 1952 | .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() }, |
| 1952 | 1955 | 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}), |
| 1954 | 1957 | }, |
| 1955 | 1958 | } |
| 1956 | 1959 | const result = try self.allocStack(ty); |
| ... | ... | @@ -1970,53 +1973,58 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1970 | 1973 | try self.addImm64(limb); |
| 1971 | 1974 | try self.addMemArg(.i64_store, .{ .offset = @intCast(u32, index * 8), .alignment = 8 }); |
| 1972 | 1975 | } |
| 1973 | | try self.addLabel(.local_get, result.local); |
| 1976 | return result; |
| 1974 | 1977 | }, |
| 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}), |
| 1983 | 1983 | }, |
| 1984 | | .Pointer => { |
| 1985 | | if (val.castTag(.slice)) |slice| { |
| 1984 | .Pointer => switch (val) { |
| 1985 | .slice => { |
| 1986 | const result = try self.allocStack(ty); |
| 1986 | 1987 | 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; |
| 1991 | 2006 | decl.markAlive(); |
| 1992 | | // Function pointers use a table index, rather than a memory address |
| 2007 | const target_sym_index = decl.link.wasm.sym_index; |
| 1993 | 2008 | if (decl.ty.zigTypeTag() == .Fn) { |
| 1994 | | const target_sym_index = decl.link.wasm.sym_index; |
| 1995 | 2009 | 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 | }, |
| 2009 | 2017 | .Enum => { |
| 2010 | 2018 | if (val.castTag(.enum_field_index)) |field_index| { |
| 2011 | 2019 | switch (ty.tag()) { |
| 2012 | | .enum_simple => try self.addImm32(@bitCast(i32, field_index.data)), |
| 2020 | .enum_simple => return WValue{ .imm32 = field_index.data }, |
| 2013 | 2021 | .enum_full, .enum_nonexhaustive => { |
| 2014 | 2022 | const enum_full = ty.cast(Type.Payload.EnumFull).?.data; |
| 2015 | 2023 | if (enum_full.values.count() != 0) { |
| 2016 | 2024 | 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); |
| 2018 | 2026 | } else { |
| 2019 | | try self.addImm32(@bitCast(i32, field_index.data)); |
| 2027 | return WValue{ .imm32 = field_index.data }; |
| 2020 | 2028 | } |
| 2021 | 2029 | }, |
| 2022 | 2030 | else => unreachable, |
| ... | ... | @@ -2024,55 +2032,61 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 2024 | 2032 | } else { |
| 2025 | 2033 | var int_tag_buffer: Type.Payload.Bits = undefined; |
| 2026 | 2034 | 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); |
| 2028 | 2036 | } |
| 2029 | 2037 | }, |
| 2030 | 2038 | .ErrorSet => { |
| 2031 | 2039 | const error_index = self.global_error_set.get(val.getError().?).?; |
| 2032 | | try self.addImm32(@bitCast(i32, error_index)); |
| 2040 | return WValue{ .imm32 = error_index }; |
| 2033 | 2041 | }, |
| 2034 | 2042 | .ErrorUnion => { |
| 2035 | 2043 | const error_type = ty.errorUnionSet(); |
| 2036 | 2044 | 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; |
| 2054 | 2050 | } |
| 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 { |
| 2057 | 2069 | var buf: Type.Payload.ElemType = undefined; |
| 2058 | 2070 | 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; |
| 2062 | 2075 | } |
| 2063 | 2076 | |
| 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; |
| 2076 | 2090 | }, |
| 2077 | 2091 | .Struct => { |
| 2078 | 2092 | const struct_data = val.castTag(.@"struct").?; |
| ... | ... | @@ -2083,16 +2097,15 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 2083 | 2097 | const offset = try self.copyLocal(result, ty); |
| 2084 | 2098 | for (fields.values()) |field, index| { |
| 2085 | 2099 | 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); |
| 2089 | 2102 | |
| 2090 | 2103 | // this prevents us from emitting useless instructions when we reached the end of the loop |
| 2091 | 2104 | if (index != (fields.count() - 1)) { |
| 2092 | 2105 | _ = try self.buildPointerOffset(offset, field.ty.abiSize(self.target), .modify); |
| 2093 | 2106 | } |
| 2094 | 2107 | } |
| 2095 | | try self.addLabel(.local_get, result.local); |
| 2108 | return result; |
| 2096 | 2109 | }, |
| 2097 | 2110 | .Array => { |
| 2098 | 2111 | const result = try self.allocStack(ty); |
| ... | ... | @@ -2108,7 +2121,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 2108 | 2121 | const tmp = try self.allocLocal(elem_ty); |
| 2109 | 2122 | const offset = try self.copyLocal(result, ty); |
| 2110 | 2123 | for (array.data) |value, index| { |
| 2111 | | try self.emitConstant(value, elem_ty); |
| 2124 | try self.lowerConstant(value, elem_ty); |
| 2112 | 2125 | try self.addLabel(.local_set, tmp.local); |
| 2113 | 2126 | try self.store(offset, tmp, elem_ty, 0); |
| 2114 | 2127 | |
| ... | ... | @@ -2129,9 +2142,9 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 2129 | 2142 | var index: u32 = 0; |
| 2130 | 2143 | while (index < len_with_sent) : (index += 1) { |
| 2131 | 2144 | if (sentinel != null and index == len) { |
| 2132 | | try self.emitConstant(sentinel.?, elem_ty); |
| 2145 | try self.lowerConstant(sentinel.?, elem_ty); |
| 2133 | 2146 | } else { |
| 2134 | | try self.emitConstant(value, elem_ty); |
| 2147 | try self.lowerConstant(value, elem_ty); |
| 2135 | 2148 | } |
| 2136 | 2149 | try self.addLabel(.local_set, tmp.local); |
| 2137 | 2150 | try self.store(offset, tmp, elem_ty, 0); |
| ... | ... | @@ -2144,26 +2157,26 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 2144 | 2157 | const elem_ty = ty.childType(); |
| 2145 | 2158 | const sent_val = ty.sentinel().?; |
| 2146 | 2159 | const tmp = try self.allocLocal(elem_ty); |
| 2147 | | try self.emitConstant(sent_val, elem_ty); |
| 2160 | try self.lowerConstant(sent_val, elem_ty); |
| 2148 | 2161 | try self.addLabel(.local_set, tmp.local); |
| 2149 | 2162 | try self.store(result, tmp, elem_ty, 0); |
| 2150 | 2163 | } else unreachable; |
| 2151 | | try self.addLabel(.local_get, result.local); |
| 2164 | return result; |
| 2152 | 2165 | }, |
| 2153 | 2166 | else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), |
| 2154 | 2167 | } |
| 2155 | 2168 | } |
| 2156 | 2169 | |
| 2157 | | fn emitUndefined(self: *Self, ty: Type) InnerError!void { |
| 2170 | fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { |
| 2158 | 2171 | switch (ty.zigTypeTag()) { |
| 2159 | 2172 | .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 }, |
| 2162 | 2175 | else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}), |
| 2163 | 2176 | }, |
| 2164 | 2177 | .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)) }, |
| 2167 | 2180 | else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}), |
| 2168 | 2181 | }, |
| 2169 | 2182 | .Array, .Struct => { |
| ... | ... | @@ -2172,18 +2185,18 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { |
| 2172 | 2185 | var offset: u32 = 0; |
| 2173 | 2186 | while (offset < abi_size) : (offset += 1) { |
| 2174 | 2187 | 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 }), |
| 2179 | 2192 | else => unreachable, |
| 2180 | 2193 | } |
| 2181 | 2194 | } |
| 2182 | | try self.addLabel(.local_get, result.local); |
| 2195 | return result; |
| 2183 | 2196 | }, |
| 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 }, |
| 2187 | 2200 | else => unreachable, |
| 2188 | 2201 | }, |
| 2189 | 2202 | 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 { |
| 2595 | 2608 | // for single value prong we can emit a simple if |
| 2596 | 2609 | if (case.values.len == 1) { |
| 2597 | 2610 | 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); |
| 2599 | 2613 | const opcode = buildOpcode(.{ |
| 2600 | 2614 | .valtype1 = try self.typeToValtype(target_ty), |
| 2601 | 2615 | .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 { |
| 2608 | 2622 | try self.startBlock(.block, blocktype); |
| 2609 | 2623 | for (case.values) |value| { |
| 2610 | 2624 | 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); |
| 2612 | 2627 | const opcode = buildOpcode(.{ |
| 2613 | 2628 | .valtype1 = try self.typeToValtype(target_ty), |
| 2614 | 2629 | .op = .eq, |