| ... | @@ -6,7 +6,6 @@ const testing = std.testing; | ... | @@ -6,7 +6,6 @@ const testing = std.testing; |
| 6 | const leb = std.leb; | 6 | const leb = std.leb; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | const wasm = std.wasm; | 8 | const wasm = std.wasm; |
| 9 | const log = std.log.scoped(.codegen); | | |
| 10 | | 9 | |
| 11 | const Module = @import("../../Module.zig"); | 10 | const Module = @import("../../Module.zig"); |
| 12 | const Decl = Module.Decl; | 11 | const Decl = Module.Decl; |
| ... | @@ -551,7 +550,7 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, | ... | @@ -551,7 +550,7 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 551 | initial_stack_value: WValue = .none, | 550 | initial_stack_value: WValue = .none, |
| 552 | /// Arguments of this function declaration | 551 | /// Arguments of this function declaration |
| 553 | /// This will be set after `resolveCallingConventionValues` | 552 | /// This will be set after `resolveCallingConventionValues` |
| 554 | args: []WValue = &.{}, | 553 | args: []WValue = undefined, |
| 555 | /// This will only be `.none` if the function returns void, or returns an immediate. | 554 | /// This will only be `.none` if the function returns void, or returns an immediate. |
| 556 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated | 555 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| 557 | /// before this function returns its execution to the caller. | 556 | /// before this function returns its execution to the caller. |
| ... | @@ -563,6 +562,8 @@ const InnerError = error{ | ... | @@ -563,6 +562,8 @@ const InnerError = error{ |
| 563 | CodegenFail, | 562 | CodegenFail, |
| 564 | /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed | 563 | /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed |
| 565 | AnalysisFail, | 564 | AnalysisFail, |
| | 565 | /// Failed to emit MIR instructions to binary/textual representation. |
| | 566 | EmitFail, |
| 566 | /// Compiler implementation could not handle a large integer. | 567 | /// Compiler implementation could not handle a large integer. |
| 567 | Overflow, | 568 | Overflow, |
| 568 | }; | 569 | }; |
| ... | @@ -799,7 +800,7 @@ pub fn genFunc(self: *Self) InnerError!Result { | ... | @@ -799,7 +800,7 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 799 | emit.emitMir() catch |err| switch (err) { | 800 | emit.emitMir() catch |err| switch (err) { |
| 800 | error.EmitFail => { | 801 | error.EmitFail => { |
| 801 | self.err_msg = emit.error_msg.?; | 802 | self.err_msg = emit.error_msg.?; |
| 802 | return error.CodegenFail; | 803 | return error.EmitFail; |
| 803 | }, | 804 | }, |
| 804 | else => |e| return e, | 805 | else => |e| return e, |
| 805 | }; | 806 | }; |
| ... | @@ -808,34 +809,8 @@ pub fn genFunc(self: *Self) InnerError!Result { | ... | @@ -808,34 +809,8 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 808 | return Result.appended; | 809 | return Result.appended; |
| 809 | } | 810 | } |
| 810 | | 811 | |
| 811 | pub fn genDecl(self: *Self) InnerError!Result { | | |
| 812 | const decl = self.decl; | | |
| 813 | assert(decl.has_tv); | | |
| 814 | | | |
| 815 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val }); | | |
| 816 | | | |
| 817 | if (decl.val.castTag(.function)) |func_payload| { | | |
| 818 | _ = func_payload; | | |
| 819 | return self.fail("TODO wasm backend genDecl function pointer", .{}); | | |
| 820 | } else if (decl.val.castTag(.extern_fn)) |extern_fn| { | | |
| 821 | const ext_decl = extern_fn.data; | | |
| 822 | var func_type = try self.genFunctype(ext_decl.ty); | | |
| 823 | func_type.deinit(self.gpa); | | |
| 824 | ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); | | |
| 825 | return Result.appended; | | |
| 826 | } else { | | |
| 827 | const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { | | |
| 828 | break :init_val payload.data.init; | | |
| 829 | } else decl.val; | | |
| 830 | if (init_val.tag() != .unreachable_value) { | | |
| 831 | return try self.genTypedValue(decl.ty, init_val); | | |
| 832 | } | | |
| 833 | return Result.appended; | | |
| 834 | } | | |
| 835 | } | | |
| 836 | | | |
| 837 | /// Generates the wasm bytecode for the declaration belonging to `Context` | 812 | /// Generates the wasm bytecode for the declaration belonging to `Context` |
| 838 | fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | 813 | pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 839 | if (val.isUndef()) { | 814 | if (val.isUndef()) { |
| 840 | try self.code.appendNTimes(0xaa, @intCast(usize, ty.abiSize(self.target))); | 815 | try self.code.appendNTimes(0xaa, @intCast(usize, ty.abiSize(self.target))); |
| 841 | return Result.appended; | 816 | return Result.appended; |
| ... | @@ -847,16 +822,16 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -847,16 +822,16 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 847 | .function => val.castTag(.function).?.data.owner_decl, | 822 | .function => val.castTag(.function).?.data.owner_decl, |
| 848 | else => unreachable, | 823 | else => unreachable, |
| 849 | }; | 824 | }; |
| 850 | return try self.lowerDeclRef(ty, val, fn_decl); | 825 | return try self.lowerDeclRef(fn_decl); |
| 851 | }, | 826 | }, |
| 852 | .Optional => { | 827 | .Optional => { |
| 853 | var opt_buf: Type.Payload.ElemType = undefined; | 828 | var opt_buf: Type.Payload.ElemType = undefined; |
| 854 | const payload_type = ty.optionalChild(&opt_buf); | 829 | const payload_type = ty.optionalChild(&opt_buf); |
| 855 | if (ty.isPtrLikeOptional()) { | 830 | if (ty.isPtrLikeOptional()) { |
| 856 | if (val.castTag(.opt_payload)) |payload| { | 831 | if (val.castTag(.opt_payload)) |payload| { |
| 857 | return try self.genTypedValue(payload_type, payload.data); | 832 | return try self.genDecl(payload_type, payload.data); |
| 858 | } else if (!val.isNull()) { | 833 | } else if (!val.isNull()) { |
| 859 | return try self.genTypedValue(payload_type, val); | 834 | return try self.genDecl(payload_type, val); |
| 860 | } else { | 835 | } else { |
| 861 | try self.code.appendNTimes(0, @intCast(usize, ty.abiSize(self.target))); | 836 | try self.code.appendNTimes(0, @intCast(usize, ty.abiSize(self.target))); |
| 862 | return Result.appended; | 837 | return Result.appended; |
| ... | @@ -864,7 +839,7 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -864,7 +839,7 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 864 | } | 839 | } |
| 865 | // `null-tag` byte | 840 | // `null-tag` byte |
| 866 | try self.code.appendNTimes(@boolToInt(!val.isNull()), 4); | 841 | try self.code.appendNTimes(@boolToInt(!val.isNull()), 4); |
| 867 | const pl_result = try self.genTypedValue( | 842 | const pl_result = try self.genDecl( |
| 868 | payload_type, | 843 | payload_type, |
| 869 | if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), | 844 | if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), |
| 870 | ); | 845 | ); |
| ... | @@ -880,7 +855,7 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -880,7 +855,7 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 880 | if (ty.sentinel()) |sentinel| { | 855 | if (ty.sentinel()) |sentinel| { |
| 881 | try self.code.appendSlice(payload.data); | 856 | try self.code.appendSlice(payload.data); |
| 882 | | 857 | |
| 883 | switch (try self.genTypedValue(ty.childType(), sentinel)) { | 858 | switch (try self.genDecl(ty.childType(), sentinel)) { |
| 884 | .appended => return Result.appended, | 859 | .appended => return Result.appended, |
| 885 | .externally_managed => |data| { | 860 | .externally_managed => |data| { |
| 886 | try self.code.appendSlice(data); | 861 | try self.code.appendSlice(data); |
| ... | @@ -894,20 +869,22 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -894,20 +869,22 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 894 | const elem_vals = val.castTag(.array).?.data; | 869 | const elem_vals = val.castTag(.array).?.data; |
| 895 | const elem_ty = ty.elemType(); | 870 | const elem_ty = ty.elemType(); |
| 896 | for (elem_vals) |elem_val| { | 871 | for (elem_vals) |elem_val| { |
| 897 | switch (try self.genTypedValue(elem_ty, elem_val)) { | 872 | switch (try self.genDecl(elem_ty, elem_val)) { |
| 898 | .appended => {}, | 873 | .appended => {}, |
| 899 | .externally_managed => |data| try self.code.appendSlice(data), | 874 | .externally_managed => |data| { |
| | 875 | try self.code.appendSlice(data); |
| | 876 | }, |
| 900 | } | 877 | } |
| 901 | } | 878 | } |
| 902 | return Result.appended; | 879 | return Result.appended; |
| 903 | }, | 880 | }, |
| 904 | else => return self.fail("TODO implement genTypedValue for array type value: {s}", .{@tagName(val.tag())}), | 881 | else => return self.fail("TODO implement genDecl for array type value: {s}", .{@tagName(val.tag())}), |
| 905 | }, | 882 | }, |
| 906 | .Int => { | 883 | .Int => { |
| 907 | const info = ty.intInfo(self.target); | 884 | const info = ty.intInfo(self.target); |
| 908 | const abi_size = @intCast(usize, ty.abiSize(self.target)); | 885 | const abi_size = @intCast(usize, ty.abiSize(self.target)); |
| 909 | // todo: Implement integer sizes larger than 64bits | 886 | // todo: Implement integer sizes larger than 64bits |
| 910 | if (info.bits > 64) return self.fail("TODO: Implement genTypedValue for integer bit size: {d}", .{info.bits}); | 887 | if (info.bits > 64) return self.fail("TODO: Implement genDecl for integer bit size: {d}", .{info.bits}); |
| 911 | var buf: [8]u8 = undefined; | 888 | var buf: [8]u8 = undefined; |
| 912 | if (info.signedness == .unsigned) { | 889 | if (info.signedness == .unsigned) { |
| 913 | std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt()); | 890 | std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt()); |
| ... | @@ -929,7 +906,8 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -929,7 +906,8 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 929 | for (field_vals) |field_val, index| { | 906 | for (field_vals) |field_val, index| { |
| 930 | const field_ty = ty.structFieldType(index); | 907 | const field_ty = ty.structFieldType(index); |
| 931 | if (!field_ty.hasCodeGenBits()) continue; | 908 | if (!field_ty.hasCodeGenBits()) continue; |
| 932 | switch (try self.genTypedValue(field_ty, field_val)) { | 909 | |
| | 910 | switch (try self.genDecl(field_ty, field_val)) { |
| 933 | .appended => {}, | 911 | .appended => {}, |
| 934 | .externally_managed => |payload| try self.code.appendSlice(payload), | 912 | .externally_managed => |payload| try self.code.appendSlice(payload), |
| 935 | } | 913 | } |
| ... | @@ -945,21 +923,21 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -945,21 +923,21 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 945 | .Pointer => switch (val.tag()) { | 923 | .Pointer => switch (val.tag()) { |
| 946 | .variable => { | 924 | .variable => { |
| 947 | const decl = val.castTag(.variable).?.data.owner_decl; | 925 | const decl = val.castTag(.variable).?.data.owner_decl; |
| 948 | return try self.lowerDeclRef(ty, val, decl); | 926 | return try self.lowerDeclRef(decl); |
| 949 | }, | 927 | }, |
| 950 | .decl_ref => { | 928 | .decl_ref => { |
| 951 | const decl = val.castTag(.decl_ref).?.data; | 929 | const decl = val.castTag(.decl_ref).?.data; |
| 952 | return try self.lowerDeclRef(ty, val, decl); | 930 | return try self.lowerDeclRef(decl); |
| 953 | }, | 931 | }, |
| 954 | .slice => { | 932 | .slice => { |
| 955 | const slice = val.castTag(.slice).?.data; | 933 | const slice = val.castTag(.slice).?.data; |
| 956 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 934 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 957 | const ptr_ty = ty.slicePtrFieldType(&buf); | 935 | const ptr_ty = ty.slicePtrFieldType(&buf); |
| 958 | switch (try self.genTypedValue(ptr_ty, slice.ptr)) { | 936 | switch (try self.genDecl(ptr_ty, slice.ptr)) { |
| 959 | .externally_managed => |data| try self.code.appendSlice(data), | 937 | .externally_managed => |data| try self.code.appendSlice(data), |
| 960 | .appended => {}, | 938 | .appended => {}, |
| 961 | } | 939 | } |
| 962 | switch (try self.genTypedValue(Type.usize, slice.len)) { | 940 | switch (try self.genDecl(Type.usize, slice.len)) { |
| 963 | .externally_managed => |data| try self.code.appendSlice(data), | 941 | .externally_managed => |data| try self.code.appendSlice(data), |
| 964 | .appended => {}, | 942 | .appended => {}, |
| 965 | } | 943 | } |
| ... | @@ -971,25 +949,13 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -971,25 +949,13 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 971 | } | 949 | } |
| 972 | } | 950 | } |
| 973 | | 951 | |
| 974 | fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result { | 952 | fn lowerDeclRef(self: *Self, decl: *Module.Decl) InnerError!Result { |
| 975 | if (ty.isSlice()) { | 953 | decl.alive = true; |
| 976 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | | |
| 977 | const slice_ty = ty.slicePtrFieldType(&buf); | | |
| 978 | switch (try self.genTypedValue(slice_ty, val)) { | | |
| 979 | .appended => {}, | | |
| 980 | .externally_managed => |payload| try self.code.appendSlice(payload), | | |
| 981 | } | | |
| 982 | var slice_len: Value.Payload.U64 = .{ | | |
| 983 | .base = .{ .tag = .int_u64 }, | | |
| 984 | .data = val.sliceLen(), | | |
| 985 | }; | | |
| 986 | return try self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base)); | | |
| 987 | } | | |
| 988 | | 954 | |
| 989 | const offset = @intCast(u32, self.code.items.len); | 955 | const offset = @intCast(u32, self.code.items.len); |
| 990 | const atom = &self.decl.link.wasm; | 956 | const atom = &self.decl.link.wasm; |
| 991 | const target_sym_index = decl.link.wasm.sym_index; | 957 | const target_sym_index = decl.link.wasm.sym_index; |
| 992 | decl.alive = true; | 958 | |
| 993 | if (decl.ty.zigTypeTag() == .Fn) { | 959 | if (decl.ty.zigTypeTag() == .Fn) { |
| 994 | // We found a function pointer, so add it to our table, | 960 | // We found a function pointer, so add it to our table, |
| 995 | // as function pointers are not allowed to be stored inside the data section, | 961 | // as function pointers are not allowed to be stored inside the data section, |
| ... | @@ -1048,7 +1014,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1048,7 +1014,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1048 | | 1014 | |
| 1049 | const ret_ty = fn_ty.fnReturnType(); | 1015 | const ret_ty = fn_ty.fnReturnType(); |
| 1050 | switch (ret_ty.zigTypeTag()) { | 1016 | switch (ret_ty.zigTypeTag()) { |
| 1051 | .ErrorUnion, .Optional, .Pointer => result.return_value = try self.allocLocal(Type.initTag(.i32)), | 1017 | .ErrorUnion, .Optional => result.return_value = try self.allocLocal(Type.initTag(.i32)), |
| 1052 | .Int, .Float, .Bool, .Void, .NoReturn => {}, | 1018 | .Int, .Float, .Bool, .Void, .NoReturn => {}, |
| 1053 | else => return self.fail("TODO: Implement function return type {}", .{ret_ty}), | 1019 | else => return self.fail("TODO: Implement function return type {}", .{ret_ty}), |
| 1054 | } | 1020 | } |
| ... | @@ -1062,11 +1028,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1062,11 +1028,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1062 | }; | 1028 | }; |
| 1063 | | 1029 | |
| 1064 | try self.moveStack(offset, result.return_value.local); | 1030 | try self.moveStack(offset, result.return_value.local); |
| 1065 | | | |
| 1066 | // We want to make sure the return value's stack value doesn't get overwritten, | | |
| 1067 | // so set initial stack value to current's position instead. | | |
| 1068 | try self.addLabel(.global_get, 0); | | |
| 1069 | try self.addLabel(.local_set, self.initial_stack_value.local); | | |
| 1070 | } | 1031 | } |
| 1071 | }, | 1032 | }, |
| 1072 | else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), | 1033 | else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), |
| ... | @@ -1081,6 +1042,10 @@ fn initializeStack(self: *Self) !void { | ... | @@ -1081,6 +1042,10 @@ fn initializeStack(self: *Self) !void { |
| 1081 | assert(self.initial_stack_value == .none); | 1042 | assert(self.initial_stack_value == .none); |
| 1082 | // reserve space for immediate value | 1043 | // reserve space for immediate value |
| 1083 | // get stack pointer global | 1044 | // get stack pointer global |
| | 1045 | // TODO: For now, we hardcode the stack pointer to index '0', |
| | 1046 | // once the linker is further implemented, we can replace this by inserting |
| | 1047 | // a relocation and have the linker resolve the correct index to the stack pointer global. |
| | 1048 | // NOTE: relocations of the type GLOBAL_INDEX_LEB are 5-bytes big |
| 1084 | try self.addLabel(.global_get, 0); | 1049 | try self.addLabel(.global_get, 0); |
| 1085 | | 1050 | |
| 1086 | // Reserve a local to store the current stack pointer | 1051 | // Reserve a local to store the current stack pointer |
| ... | @@ -1109,6 +1074,8 @@ fn restoreStackPointer(self: *Self) !void { | ... | @@ -1109,6 +1074,8 @@ fn restoreStackPointer(self: *Self) !void { |
| 1109 | /// the result back into the stack pointer. | 1074 | /// the result back into the stack pointer. |
| 1110 | fn moveStack(self: *Self, offset: u32, local: u32) !void { | 1075 | fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 1111 | if (offset == 0) return; | 1076 | if (offset == 0) return; |
| | 1077 | // TODO: Rather than hardcode the stack pointer to position 0, |
| | 1078 | // have the linker resolve its relocation |
| 1112 | try self.addLabel(.global_get, 0); | 1079 | try self.addLabel(.global_get, 0); |
| 1113 | try self.addImm32(@bitCast(i32, offset)); | 1080 | try self.addImm32(@bitCast(i32, offset)); |
| 1114 | try self.addTag(.i32_sub); | 1081 | try self.addTag(.i32_sub); |
| ... | @@ -1116,13 +1083,6 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void { | ... | @@ -1116,13 +1083,6 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 1116 | try self.addLabel(.global_set, 0); | 1083 | try self.addLabel(.global_set, 0); |
| 1117 | } | 1084 | } |
| 1118 | | 1085 | |
| 1119 | /// From given zig bitsize, returns the wasm bitsize | | |
| 1120 | fn toWasmIntBits(bits: u16) ?u16 { | | |
| 1121 | return for ([_]u16{ 32, 64 }) |wasm_bits| { | | |
| 1122 | if (bits <= wasm_bits) return wasm_bits; | | |
| 1123 | } else null; | | |
| 1124 | } | | |
| 1125 | | | |
| 1126 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | 1086 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1127 | const air_tags = self.air.instructions.items(.tag); | 1087 | const air_tags = self.air.instructions.items(.tag); |
| 1128 | return switch (air_tags[inst]) { | 1088 | return switch (air_tags[inst]) { |
| ... | @@ -1169,15 +1129,11 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1169,15 +1129,11 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1169 | .load => self.airLoad(inst), | 1129 | .load => self.airLoad(inst), |
| 1170 | .loop => self.airLoop(inst), | 1130 | .loop => self.airLoop(inst), |
| 1171 | .not => self.airNot(inst), | 1131 | .not => self.airNot(inst), |
| 1172 | .optional_payload => self.airOptionalPayload(inst), | | |
| 1173 | .optional_payload_ptr => self.airOptionalPayload(inst), | | |
| 1174 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), | | |
| 1175 | .ret => self.airRet(inst), | 1132 | .ret => self.airRet(inst), |
| 1176 | .ret_ptr => self.airRetPtr(inst), | 1133 | .ret_ptr => self.airRetPtr(inst), |
| 1177 | .ret_load => self.airRetLoad(inst), | 1134 | .ret_load => self.airRetLoad(inst), |
| 1178 | .slice_len => self.airSliceLen(inst), | 1135 | .slice_len => self.airSliceLen(inst), |
| 1179 | .slice_elem_val => self.airSliceElemVal(inst), | 1136 | .slice_elem_val => self.airSliceElemVal(inst), |
| 1180 | .slice_ptr => self.airSlicePtr(inst), | | |
| 1181 | .store => self.airStore(inst), | 1137 | .store => self.airStore(inst), |
| 1182 | .struct_field_ptr => self.airStructFieldPtr(inst), | 1138 | .struct_field_ptr => self.airStructFieldPtr(inst), |
| 1183 | .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0), | 1139 | .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0), |
| ... | @@ -1186,14 +1142,16 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1186,14 +1142,16 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1186 | .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3), | 1142 | .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3), |
| 1187 | .struct_field_val => self.airStructFieldVal(inst), | 1143 | .struct_field_val => self.airStructFieldVal(inst), |
| 1188 | .switch_br => self.airSwitchBr(inst), | 1144 | .switch_br => self.airSwitchBr(inst), |
| 1189 | .trunc => self.airTrunc(inst), | | |
| 1190 | .unreach => self.airUnreachable(inst), | 1145 | .unreach => self.airUnreachable(inst), |
| 1191 | | | |
| 1192 | .wrap_optional => self.airWrapOptional(inst), | 1146 | .wrap_optional => self.airWrapOptional(inst), |
| | 1147 | |
| 1193 | .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), | 1148 | .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), |
| 1194 | .unwrap_errunion_err => self.airUnwrapErrUnionError(inst), | 1149 | .unwrap_errunion_err => self.airUnwrapErrUnionError(inst), |
| 1195 | .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), | 1150 | .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), |
| 1196 | .wrap_errunion_err => self.airWrapErrUnionErr(inst), | 1151 | |
| | 1152 | .optional_payload => self.airOptionalPayload(inst), |
| | 1153 | .optional_payload_ptr => self.airOptionalPayload(inst), |
| | 1154 | .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst), |
| 1197 | else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), | 1155 | else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 1198 | }; | 1156 | }; |
| 1199 | } | 1157 | } |
| ... | @@ -1235,15 +1193,15 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1235,15 +1193,15 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1235 | // local, containing the offset to the stack position | 1193 | // local, containing the offset to the stack position |
| 1236 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 | 1194 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 |
| 1237 | try self.moveStack(@intCast(u32, abi_size), local.local); | 1195 | try self.moveStack(@intCast(u32, abi_size), local.local); |
| | 1196 | |
| 1238 | return local; | 1197 | return local; |
| 1239 | } | 1198 | } |
| 1240 | | 1199 | |
| 1241 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1200 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1242 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1201 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1243 | const operand = self.resolveInst(un_op); | 1202 | const operand = self.resolveInst(un_op); |
| 1244 | | 1203 | const result = try self.load(operand, self.air.typeOf(un_op), 0); |
| 1245 | const result = try self.load(operand, self.air.typeOf(un_op).childType(), 0); | 1204 | try self.addLabel(.local_get, result.local); |
| 1246 | try self.emitWValue(result); | | |
| 1247 | try self.restoreStackPointer(); | 1205 | try self.restoreStackPointer(); |
| 1248 | try self.addTag(.@"return"); | 1206 | try self.addTag(.@"return"); |
| 1249 | return .none; | 1207 | return .none; |
| ... | @@ -1273,25 +1231,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1273,25 +1231,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1273 | }; | 1231 | }; |
| 1274 | | 1232 | |
| 1275 | for (args) |arg| { | 1233 | for (args) |arg| { |
| 1276 | const arg_ref = @intToEnum(Air.Inst.Ref, arg); | 1234 | const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg)); |
| 1277 | const arg_val = self.resolveInst(arg_ref); | 1235 | try self.emitWValue(arg_val); |
| 1278 | | | |
| 1279 | const arg_ty = self.air.typeOf(arg_ref); | | |
| 1280 | switch (arg_ty.zigTypeTag()) { | | |
| 1281 | .Struct, .Pointer, .Optional, .ErrorUnion => { | | |
| 1282 | // single pointer can be passed directly | | |
| 1283 | if (arg_ty.isSinglePointer() or arg_val != .constant) { | | |
| 1284 | try self.emitWValue(arg_val); | | |
| 1285 | continue; | | |
| 1286 | } | | |
| 1287 | const abi_size = arg_ty.abiSize(self.target); | | |
| 1288 | const arg_local = try self.allocLocal(Type.initTag(.i32)); | | |
| 1289 | try self.moveStack(@intCast(u32, abi_size), arg_local.local); | | |
| 1290 | try self.store(arg_local, arg_val, arg_ty, 0); | | |
| 1291 | try self.emitWValue(arg_local); | | |
| 1292 | }, | | |
| 1293 | else => try self.emitWValue(arg_val), | | |
| 1294 | } | | |
| 1295 | } | 1236 | } |
| 1296 | | 1237 | |
| 1297 | if (target) |direct| { | 1238 | if (target) |direct| { |
| ... | @@ -1301,11 +1242,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1301,11 +1242,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1301 | // so load its value onto the stack | 1242 | // so load its value onto the stack |
| 1302 | std.debug.assert(ty.zigTypeTag() == .Pointer); | 1243 | std.debug.assert(ty.zigTypeTag() == .Pointer); |
| 1303 | const operand = self.resolveInst(pl_op.operand); | 1244 | const operand = self.resolveInst(pl_op.operand); |
| 1304 | const offset = switch (operand) { | 1245 | try self.emitWValue(operand); |
| 1305 | .local_with_offset => |with_offset| with_offset.offset, | 1246 | const result = try self.load(operand, fn_ty, operand.local_with_offset.offset); |
| 1306 | else => @as(u32, 0), | | |
| 1307 | }; | | |
| 1308 | const result = try self.load(operand, fn_ty, offset); | | |
| 1309 | try self.addLabel(.local_get, result.local); | 1247 | try self.addLabel(.local_get, result.local); |
| 1310 | | 1248 | |
| 1311 | var fn_type = try self.genFunctype(fn_ty); | 1249 | var fn_type = try self.genFunctype(fn_ty); |
| ... | @@ -1316,12 +1254,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1316,12 +1254,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1316 | } | 1254 | } |
| 1317 | | 1255 | |
| 1318 | const ret_ty = fn_ty.fnReturnType(); | 1256 | const ret_ty = fn_ty.fnReturnType(); |
| 1319 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1257 | switch (ret_ty.zigTypeTag()) { |
| 1320 | | 1258 | .Void, .NoReturn => return WValue.none, |
| 1321 | const result_local = try self.allocLocal(ret_ty); | 1259 | else => { |
| 1322 | try self.addLabel(.local_set, result_local.local); | 1260 | const result_local = try self.allocLocal(ret_ty); |
| 1323 | // if the result was allocated on the virtual stack, we must load | 1261 | try self.addLabel(.local_set, result_local.local); |
| 1324 | return result_local; | 1262 | return result_local; |
| | 1263 | }, |
| | 1264 | } |
| 1325 | } | 1265 | } |
| 1326 | | 1266 | |
| 1327 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1267 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1338,6 +1278,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1338,6 +1278,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1338 | // local, containing the offset to the stack position | 1278 | // local, containing the offset to the stack position |
| 1339 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 | 1279 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 |
| 1340 | try self.moveStack(@intCast(u32, abi_size), local.local); | 1280 | try self.moveStack(@intCast(u32, abi_size), local.local); |
| | 1281 | |
| 1341 | return local; | 1282 | return local; |
| 1342 | } | 1283 | } |
| 1343 | | 1284 | |
| ... | @@ -1387,15 +1328,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1387,15 +1328,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1387 | return; | 1328 | return; |
| 1388 | }, | 1329 | }, |
| 1389 | .local => { | 1330 | .local => { |
| 1390 | if (payload_ty.hasCodeGenBits()) { | 1331 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1391 | try self.store(lhs, rhs, payload_ty, payload_offset); | 1332 | const tag_local = try self.load(rhs, tag_ty, 0); |
| 1392 | } | 1333 | const payload_local = try self.load(rhs, payload_ty, payload_offset); |
| 1393 | return try self.store(lhs, rhs, tag_ty, 0); | 1334 | |
| | 1335 | try self.store(lhs, tag_local, tag_ty, 0); |
| | 1336 | return try self.store(lhs, payload_local, payload_ty, payload_offset); |
| 1394 | }, | 1337 | }, |
| 1395 | .local_with_offset => |with_offset| { | 1338 | .local_with_offset => |with_offset| { |
| 1396 | const tag_local = try self.allocLocal(tag_ty); | 1339 | const tag_local = try self.allocLocal(tag_ty); |
| 1397 | try self.addImm32(0); | 1340 | try self.addImm32(0); |
| 1398 | try self.addLabel(.local_set, tag_local.local); | | |
| 1399 | try self.store(lhs, tag_local, tag_ty, 0); | 1341 | try self.store(lhs, tag_local, tag_ty, 0); |
| 1400 | | 1342 | |
| 1401 | return try self.store( | 1343 | return try self.store( |
| ... | @@ -1424,18 +1366,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1424,18 +1366,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1424 | } | 1366 | } |
| 1425 | return; | 1367 | return; |
| 1426 | }, | 1368 | }, |
| 1427 | .Pointer => { | | |
| 1428 | if (ty.isSlice() and rhs == .constant) { | | |
| 1429 | try self.emitWValue(rhs); | | |
| 1430 | const len_local = try self.allocLocal(Type.usize); | | |
| 1431 | const ptr_local = try self.allocLocal(Type.usize); | | |
| 1432 | try self.addLabel(.local_set, len_local.local); | | |
| 1433 | try self.addLabel(.local_set, ptr_local.local); | | |
| 1434 | try self.store(lhs, ptr_local, Type.usize, 0); | | |
| 1435 | try self.store(lhs, len_local, Type.usize, self.target.cpu.arch.ptrBitWidth() / 8); | | |
| 1436 | return; | | |
| 1437 | } | | |
| 1438 | }, | | |
| 1439 | else => {}, | 1369 | else => {}, |
| 1440 | } | 1370 | } |
| 1441 | try self.emitWValue(lhs); | 1371 | try self.emitWValue(lhs); |
| ... | @@ -1472,8 +1402,6 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1472,8 +1402,6 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1472 | const operand = self.resolveInst(ty_op.operand); | 1402 | const operand = self.resolveInst(ty_op.operand); |
| 1473 | const ty = self.air.getRefType(ty_op.ty); | 1403 | const ty = self.air.getRefType(ty_op.ty); |
| 1474 | | 1404 | |
| 1475 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; | | |
| 1476 | | | |
| 1477 | return switch (ty.zigTypeTag()) { | 1405 | return switch (ty.zigTypeTag()) { |
| 1478 | .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer | 1406 | .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer |
| 1479 | else => switch (operand) { | 1407 | else => switch (operand) { |
| ... | @@ -1487,10 +1415,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1487,10 +1415,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1487 | // load local's value from memory by its stack position | 1415 | // load local's value from memory by its stack position |
| 1488 | try self.emitWValue(operand); | 1416 | try self.emitWValue(operand); |
| 1489 | // Build the opcode with the right bitsize | 1417 | // Build the opcode with the right bitsize |
| 1490 | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt() or ty.zigTypeTag() == .ErrorSet) | 1418 | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed; |
| 1491 | .unsigned | | |
| 1492 | else | | |
| 1493 | .signed; | | |
| 1494 | // check if we should pass by pointer or value based on ABI size | 1419 | // check if we should pass by pointer or value based on ABI size |
| 1495 | // TODO: Implement a way to get ABI values from a given type, | 1420 | // TODO: Implement a way to get ABI values from a given type, |
| 1496 | // that is portable across the backend, rather than copying logic. | 1421 | // that is portable across the backend, rather than copying logic. |
| ... | @@ -1601,7 +1526,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1601,7 +1526,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1601 | } | 1526 | } |
| 1602 | | 1527 | |
| 1603 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | 1528 | fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1604 | if (val.isUndefDeep()) return self.emitUndefined(ty); | | |
| 1605 | switch (ty.zigTypeTag()) { | 1529 | switch (ty.zigTypeTag()) { |
| 1606 | .Int => { | 1530 | .Int => { |
| 1607 | const int_info = ty.intInfo(self.target); | 1531 | const int_info = ty.intInfo(self.target); |
| ... | @@ -1629,21 +1553,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1629,21 +1553,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1629 | } | 1553 | } |
| 1630 | }, | 1554 | }, |
| 1631 | .Pointer => { | 1555 | .Pointer => { |
| 1632 | if (val.castTag(.slice)) |slice| { | 1556 | if (val.castTag(.decl_ref)) |payload| { |
| 1633 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | | |
| 1634 | try self.emitConstant(slice.data.ptr, ty.slicePtrFieldType(&buf)); | | |
| 1635 | try self.emitConstant(slice.data.len, Type.usize); | | |
| 1636 | } else if (val.castTag(.decl_ref)) |payload| { | | |
| 1637 | const decl = payload.data; | 1557 | const decl = payload.data; |
| 1638 | decl.alive = true; | 1558 | decl.alive = true; |
| 1639 | // Function pointers use a table index, rather than a memory address | 1559 | try self.addLabel(.memory_address, decl.link.wasm.sym_index); |
| 1640 | if (decl.ty.zigTypeTag() == .Fn) { | | |
| 1641 | const target_sym_index = decl.link.wasm.sym_index; | | |
| 1642 | try self.bin_file.addTableFunction(target_sym_index); | | |
| 1643 | try self.addLabel(.function_index, target_sym_index); | | |
| 1644 | } else { | | |
| 1645 | try self.addLabel(.memory_address, decl.link.wasm.sym_index); | | |
| 1646 | } | | |
| 1647 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); | 1560 | } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()}); |
| 1648 | }, | 1561 | }, |
| 1649 | .Void => {}, | 1562 | .Void => {}, |
| ... | @@ -1718,22 +1631,6 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1718,22 +1631,6 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1718 | } | 1631 | } |
| 1719 | } | 1632 | } |
| 1720 | | 1633 | |
| 1721 | fn emitUndefined(self: *Self, ty: Type) InnerError!void { | | |
| 1722 | switch (ty.zigTypeTag()) { | | |
| 1723 | .Int => switch (ty.intInfo(self.target).bits) { | | |
| 1724 | 0...32 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))), | | |
| 1725 | 33...64 => try self.addImm64(0xaaaaaaaaaaaaaaaa), | | |
| 1726 | else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}), | | |
| 1727 | }, | | |
| 1728 | .Float => switch (ty.floatBits(self.target)) { | | |
| 1729 | 0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) } }), | | |
| 1730 | 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))), | | |
| 1731 | else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}), | | |
| 1732 | }, | | |
| 1733 | else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}), | | |
| 1734 | } | | |
| 1735 | } | | |
| 1736 | | | |
| 1737 | /// Returns a `Value` as a signed 32 bit value. | 1634 | /// Returns a `Value` as a signed 32 bit value. |
| 1738 | /// It's illegal to provide a value with a type that cannot be represented | 1635 | /// It's illegal to provide a value with a type that cannot be represented |
| 1739 | /// as an integer value. | 1636 | /// as an integer value. |
| ... | @@ -1884,7 +1781,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner | ... | @@ -1884,7 +1781,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 1884 | }); | 1781 | }); |
| 1885 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 1782 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1886 | | 1783 | |
| 1887 | const cmp_tmp = try self.allocLocal(Type.initTag(.i32)); // bool is always i32 | 1784 | const cmp_tmp = try self.allocLocal(lhs_ty); |
| 1888 | try self.addLabel(.local_set, cmp_tmp.local); | 1785 | try self.addLabel(.local_set, cmp_tmp.local); |
| 1889 | return cmp_tmp; | 1786 | return cmp_tmp; |
| 1890 | } | 1787 | } |
| ... | @@ -2184,25 +2081,9 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2184,25 +2081,9 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2184 | } | 2081 | } |
| 2185 | | 2082 | |
| 2186 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2083 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2187 | if (self.liveness.isUnused(inst)) return WValue.none; | | |
| 2188 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | | |
| 2189 | const operand = self.resolveInst(ty_op.operand); | | |
| 2190 | | | |
| 2191 | const op_ty = self.air.typeOf(ty_op.operand); | | |
| 2192 | if (!op_ty.hasCodeGenBits()) return WValue.none; | | |
| 2193 | const err_ty = self.air.getRefType(ty_op.ty); | | |
| 2194 | const offset = err_ty.errorUnionSet().abiSize(self.target); | | |
| 2195 | | | |
| 2196 | return WValue{ .local_with_offset = .{ | | |
| 2197 | .local = operand.local, | | |
| 2198 | .offset = @intCast(u32, offset), | | |
| 2199 | } }; | | |
| 2200 | } | | |
| 2201 | | | |
| 2202 | fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | | |
| 2203 | if (self.liveness.isUnused(inst)) return WValue.none; | | |
| 2204 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2084 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2205 | return self.resolveInst(ty_op.operand); | 2085 | _ = ty_op; |
| | 2086 | return self.fail("TODO: wasm airWrapErrUnionPayload", .{}); |
| 2206 | } | 2087 | } |
| 2207 | | 2088 | |
| 2208 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2089 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2211,26 +2092,20 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2211,26 +2092,20 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2211 | const operand = self.resolveInst(ty_op.operand); | 2092 | const operand = self.resolveInst(ty_op.operand); |
| 2212 | const ref_ty = self.air.typeOf(ty_op.operand); | 2093 | const ref_ty = self.air.typeOf(ty_op.operand); |
| 2213 | const ref_info = ref_ty.intInfo(self.target); | 2094 | const ref_info = ref_ty.intInfo(self.target); |
| 2214 | const wanted_info = ty.intInfo(self.target); | 2095 | const op_bits = ref_info.bits; |
| | 2096 | const wanted_bits = ty.intInfo(self.target).bits; |
| 2215 | | 2097 | |
| 2216 | const op_bits = toWasmIntBits(ref_info.bits) orelse | 2098 | if (op_bits > 32 and wanted_bits <= 32) { |
| 2217 | return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{ref_info.bits}); | | |
| 2218 | const wanted_bits = toWasmIntBits(wanted_info.bits) orelse | | |
| 2219 | return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{wanted_info.bits}); | | |
| 2220 | | | |
| 2221 | // hot path | | |
| 2222 | if (op_bits == wanted_bits) return operand; | | |
| 2223 | | | |
| 2224 | if (op_bits > 32 and wanted_bits == 32) { | | |
| 2225 | try self.emitWValue(operand); | 2099 | try self.emitWValue(operand); |
| 2226 | try self.addTag(.i32_wrap_i64); | 2100 | try self.addTag(.i32_wrap_i64); |
| 2227 | } else if (op_bits == 32 and wanted_bits > 32) { | 2101 | } else if (op_bits <= 32 and wanted_bits > 32) { |
| 2228 | try self.emitWValue(operand); | 2102 | try self.emitWValue(operand); |
| 2229 | try self.addTag(switch (ref_info.signedness) { | 2103 | try self.addTag(switch (ref_info.signedness) { |
| 2230 | .signed => .i64_extend_i32_s, | 2104 | .signed => .i64_extend_i32_s, |
| 2231 | .unsigned => .i64_extend_i32_u, | 2105 | .unsigned => .i64_extend_i32_u, |
| 2232 | }); | 2106 | }); |
| 2233 | } | 2107 | } else return operand; |
| | 2108 | |
| 2234 | const result = try self.allocLocal(ty); | 2109 | const result = try self.allocLocal(ty); |
| 2235 | try self.addLabel(.local_set, result.local); | 2110 | try self.addLabel(.local_set, result.local); |
| 2236 | return result; | 2111 | return result; |
| ... | @@ -2305,7 +2180,19 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2305,7 +2180,19 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2305 | const operand = self.resolveInst(ty_op.operand); | 2180 | const operand = self.resolveInst(ty_op.operand); |
| 2306 | const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8; | 2181 | const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8; |
| 2307 | | 2182 | |
| 2308 | return try self.load(operand, Type.usize, pointer_width); | 2183 | // Get pointer to slice |
| | 2184 | try self.emitWValue(operand); |
| | 2185 | // length of slice is stored after the pointer of the slice |
| | 2186 | const extra_index = try self.addExtra(Mir.MemArg{ |
| | 2187 | .offset = pointer_width, |
| | 2188 | .alignment = pointer_width, |
| | 2189 | }); |
| | 2190 | try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } }); |
| | 2191 | |
| | 2192 | const result = try self.allocLocal(Type.initTag(.i32)); // pointer is always i32 |
| | 2193 | // store slice length in local |
| | 2194 | try self.addLabel(.local_set, result.local); |
| | 2195 | return result; |
| 2309 | } | 2196 | } |
| 2310 | | 2197 | |
| 2311 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2198 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2319,8 +2206,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2319,8 +2206,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2319 | const elem_size = elem_ty.abiSize(self.target); | 2206 | const elem_size = elem_ty.abiSize(self.target); |
| 2320 | | 2207 | |
| 2321 | // load pointer onto stack | 2208 | // load pointer onto stack |
| 2322 | const slice_ptr = try self.load(slice, slice_ty, 0); | 2209 | try self.emitWValue(slice); |
| 2323 | try self.addLabel(.local_get, slice_ptr.local); | | |
| 2324 | | 2210 | |
| 2325 | // calculate index into slice | 2211 | // calculate index into slice |
| 2326 | try self.emitWValue(index); | 2212 | try self.emitWValue(index); |
| ... | @@ -2328,79 +2214,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2328,79 +2214,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2328 | try self.addTag(.i32_mul); | 2214 | try self.addTag(.i32_mul); |
| 2329 | try self.addTag(.i32_add); | 2215 | try self.addTag(.i32_add); |
| 2330 | | 2216 | |
| 2331 | const result = try self.allocLocal(elem_ty); | 2217 | const abi_size = if (elem_size < 8) |
| 2332 | try self.addLabel(.local_set, result.local); | 2218 | @intCast(u8, elem_size) |
| 2333 | return switch (elem_ty.zigTypeTag()) { | 2219 | else |
| 2334 | // pass as pointer | 2220 | @as(u8, 4); // elements larger than 8 bytes will be passed by pointer |
| 2335 | .Pointer, .Struct, .Optional => result, | | |
| 2336 | // pass by value | | |
| 2337 | else => try self.load(result, elem_ty, 0), | | |
| 2338 | }; | | |
| 2339 | } | | |
| 2340 | | | |
| 2341 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | | |
| 2342 | if (self.liveness.isUnused(inst)) return WValue.none; | | |
| 2343 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | | |
| 2344 | const operand = self.resolveInst(ty_op.operand); | | |
| 2345 | return try self.load(operand, Type.usize, 0); | | |
| 2346 | } | | |
| 2347 | | | |
| 2348 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | | |
| 2349 | if (self.liveness.isUnused(inst)) return WValue.none; | | |
| 2350 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | | |
| 2351 | const operand = self.resolveInst(ty_op.operand); | | |
| 2352 | const op_ty = self.air.typeOf(ty_op.operand); | | |
| 2353 | const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target); | | |
| 2354 | const wanted_bits = int_info.bits; | | |
| 2355 | const result = try self.allocLocal(self.air.getRefType(ty_op.ty)); | | |
| 2356 | const op_bits = op_ty.intInfo(self.target).bits; | | |
| 2357 | | | |
| 2358 | const wasm_bits = toWasmIntBits(wanted_bits) orelse | | |
| 2359 | return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits}); | | |
| 2360 | | | |
| 2361 | // Use wasm's instruction to wrap from 64bit to 32bit integer when possible | | |
| 2362 | if (op_bits == 64 and wanted_bits == 32) { | | |
| 2363 | try self.emitWValue(operand); | | |
| 2364 | try self.addTag(.i32_wrap_i64); | | |
| 2365 | try self.addLabel(.local_set, result.local); | | |
| 2366 | return result; | | |
| 2367 | } | | |
| 2368 | | 2221 | |
| 2369 | // Any other truncation must be done manually | 2222 | const extra_index = try self.addExtra(Mir.MemArg{ |
| 2370 | if (int_info.signedness == .unsigned) { | 2223 | .offset = 0, |
| 2371 | const mask = (@as(u65, 1) << @intCast(u7, wanted_bits)) - 1; | 2224 | .alignment = elem_ty.abiAlignment(self.target), |
| 2372 | try self.emitWValue(operand); | 2225 | }); |
| 2373 | switch (wasm_bits) { | 2226 | const signedness: std.builtin.Signedness = if (elem_ty.isUnsignedInt()) .unsigned else .signed; |
| 2374 | 32 => { | 2227 | const opcode = buildOpcode(.{ |
| 2375 | try self.addImm32(@bitCast(i32, @intCast(u32, mask))); | 2228 | .valtype1 = try self.typeToValtype(elem_ty), |
| 2376 | try self.addTag(.i32_and); | 2229 | .width = abi_size * 8, |
| 2377 | }, | 2230 | .op = .load, |
| 2378 | 64 => { | 2231 | .signedness = signedness, |
| 2379 | try self.addImm64(@intCast(u64, mask)); | 2232 | }); |
| 2380 | try self.addTag(.i64_and); | 2233 | try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = extra_index } }); |
| 2381 | }, | | |
| 2382 | else => unreachable, | | |
| 2383 | } | | |
| 2384 | } else { | | |
| 2385 | const shift_bits = wasm_bits - wanted_bits; | | |
| 2386 | try self.emitWValue(operand); | | |
| 2387 | switch (wasm_bits) { | | |
| 2388 | 32 => { | | |
| 2389 | try self.addImm32(@bitCast(i16, shift_bits)); | | |
| 2390 | try self.addTag(.i32_shl); | | |
| 2391 | try self.addImm32(@bitCast(i16, shift_bits)); | | |
| 2392 | try self.addTag(.i32_shr_s); | | |
| 2393 | }, | | |
| 2394 | 64 => { | | |
| 2395 | try self.addImm64(shift_bits); | | |
| 2396 | try self.addTag(.i64_shl); | | |
| 2397 | try self.addImm64(shift_bits); | | |
| 2398 | try self.addTag(.i64_shr_s); | | |
| 2399 | }, | | |
| 2400 | else => unreachable, | | |
| 2401 | } | | |
| 2402 | } | | |
| 2403 | | 2234 | |
| | 2235 | const result = try self.allocLocal(elem_ty); |
| 2404 | try self.addLabel(.local_set, result.local); | 2236 | try self.addLabel(.local_set, result.local); |
| 2405 | return result; | 2237 | return result; |
| 2406 | } | 2238 | } |