| ... | ... | @@ -692,6 +692,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 692 | 692 | .Struct, |
| 693 | 693 | .ErrorUnion, |
| 694 | 694 | .Optional, |
| 695 | .Fn, |
| 695 | 696 | => wasm.Valtype.i32, |
| 696 | 697 | else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), |
| 697 | 698 | }; |
| ... | ... | @@ -809,23 +810,52 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 809 | 810 | } |
| 810 | 811 | |
| 811 | 812 | /// Generates the wasm bytecode for the declaration belonging to `Context` |
| 812 | | pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 813 | pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 814 | if (val.isUndef()) { |
| 815 | try self.code.appendNTimes(0xaa, ty.abiSize(self.target)); |
| 816 | return Result.appended; |
| 817 | } |
| 813 | 818 | switch (ty.zigTypeTag()) { |
| 814 | 819 | .Fn => { |
| 815 | | if (val.tag() == .extern_fn) { |
| 816 | | var func_type = try self.genFunctype(self.decl.ty); |
| 817 | | defer func_type.deinit(self.gpa); |
| 818 | | self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 819 | | return Result.appended; // don't need code body for extern functions |
| 820 | const fn_decl = switch (val.tag()) { |
| 821 | .extern_fn => val.castTag(.extern_fn).?.data, |
| 822 | .function => val.castTag(.function).?.data.owner_decl, |
| 823 | else => unreachable, |
| 824 | }; |
| 825 | return try self.lowerDeclRef(fn_decl); |
| 826 | }, |
| 827 | .Optional => { |
| 828 | var opt_buf: Type.Payload.ElemType = undefined; |
| 829 | const payload_type = ty.optionalChild(&opt_buf); |
| 830 | if (ty.isPtrLikeOptional()) { |
| 831 | if (val.castTag(.opt_payload)) |payload| { |
| 832 | return try self.genDecl(payload_type, payload.data); |
| 833 | } else if (!val.isNull()) { |
| 834 | return try self.genDecl(payload_type, val); |
| 835 | } else { |
| 836 | try self.code.appendNTimes(0, ty.abiSize(self.target)); |
| 837 | return Result.appended; |
| 838 | } |
| 839 | } |
| 840 | // `null-tag` byte |
| 841 | try self.code.appendNTimes(@boolToInt(!val.isNull()), 4); |
| 842 | const pl_result = try self.genDecl( |
| 843 | payload_type, |
| 844 | if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), |
| 845 | ); |
| 846 | switch (pl_result) { |
| 847 | .appended => {}, |
| 848 | .externally_managed => |payload| try self.code.appendSlice(payload), |
| 820 | 849 | } |
| 821 | | return self.fail("TODO implement wasm codegen for function pointers", .{}); |
| 850 | return Result.appended; |
| 822 | 851 | }, |
| 823 | | .Array => { |
| 824 | | if (val.castTag(.bytes)) |payload| { |
| 852 | .Array => switch (val.tag()) { |
| 853 | .bytes => { |
| 854 | const payload = val.castTag(.bytes).?; |
| 825 | 855 | if (ty.sentinel()) |sentinel| { |
| 826 | 856 | try self.code.appendSlice(payload.data); |
| 827 | 857 | |
| 828 | | switch (try self.gen(ty.childType(), sentinel)) { |
| 858 | switch (try self.genDecl(ty.childType(), sentinel)) { |
| 829 | 859 | .appended => return Result.appended, |
| 830 | 860 | .externally_managed => |data| { |
| 831 | 861 | try self.code.appendSlice(data); |
| ... | ... | @@ -834,16 +864,33 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 834 | 864 | } |
| 835 | 865 | } |
| 836 | 866 | return Result{ .externally_managed = payload.data }; |
| 837 | | } else return self.fail("TODO implement gen for more kinds of arrays", .{}); |
| 867 | }, |
| 868 | .array => { |
| 869 | const elem_vals = val.castTag(.array).?.data; |
| 870 | const elem_ty = ty.elemType(); |
| 871 | for (elem_vals) |elem_val| { |
| 872 | switch (try self.genDecl(elem_ty, elem_val)) { |
| 873 | .appended => {}, |
| 874 | .externally_managed => |data| { |
| 875 | try self.code.appendSlice(data); |
| 876 | }, |
| 877 | } |
| 878 | } |
| 879 | return Result.appended; |
| 880 | }, |
| 881 | else => return self.fail("TODO implement genDecl for array type value: {s}", .{@tagName(val.tag())}), |
| 838 | 882 | }, |
| 839 | 883 | .Int => { |
| 840 | 884 | const info = ty.intInfo(self.target); |
| 841 | | if (info.bits == 8 and info.signedness == .unsigned) { |
| 842 | | const int_byte = val.toUnsignedInt(); |
| 843 | | try self.code.append(@intCast(u8, int_byte)); |
| 844 | | return Result.appended; |
| 845 | | } |
| 846 | | return self.fail("TODO: Implement codegen for int type: '{}'", .{ty}); |
| 885 | const abi_size = ty.abiSize(self.target); |
| 886 | // todo: Implement integer sizes larger than 64bits |
| 887 | if (info.bits > 64) return self.fail("TODO: Implement genDecl for integer bit size: {d}", .{info.bits}); |
| 888 | var buf: [8]u8 = undefined; |
| 889 | if (info.signedness == .unsigned) { |
| 890 | std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt()); |
| 891 | } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt()); |
| 892 | try self.code.appendSlice(buf[0..abi_size]); |
| 893 | return Result.appended; |
| 847 | 894 | }, |
| 848 | 895 | .Enum => { |
| 849 | 896 | try self.emitConstant(val, ty); |
| ... | ... | @@ -855,15 +902,83 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 855 | 902 | return Result.appended; |
| 856 | 903 | }, |
| 857 | 904 | .Struct => { |
| 858 | | // TODO write the fields for real |
| 859 | | const abi_size = try std.math.cast(usize, ty.abiSize(self.target)); |
| 905 | const field_vals = val.castTag(.@"struct").?.data; |
| 906 | for (field_vals) |field_val, index| { |
| 907 | const field_ty = ty.structFieldType(index); |
| 908 | if (!field_ty.hasCodeGenBits()) continue; |
| 909 | |
| 910 | switch (try self.genDecl(field_ty, field_val)) { |
| 911 | .appended => {}, |
| 912 | .externally_managed => |payload| try self.code.appendSlice(payload), |
| 913 | } |
| 914 | } |
| 915 | return Result.appended; |
| 916 | }, |
| 917 | .Union => { |
| 918 | // TODO: Implement Union declarations |
| 919 | const abi_size = ty.abiSize(self.target); |
| 860 | 920 | try self.code.writer().writeByteNTimes(0xaa, abi_size); |
| 861 | | return Result{ .appended = {} }; |
| 921 | return Result.appended; |
| 922 | }, |
| 923 | .Pointer => switch (val.tag()) { |
| 924 | .variable => { |
| 925 | const decl = val.castTag(.variable).?.data.owner_decl; |
| 926 | return try self.lowerDeclRef(decl); |
| 927 | }, |
| 928 | .decl_ref => { |
| 929 | const decl = val.castTag(.decl_ref).?.data; |
| 930 | return try self.lowerDeclRef(decl); |
| 931 | }, |
| 932 | .slice => { |
| 933 | const slice = val.castTag(.slice).?.data; |
| 934 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 935 | const ptr_ty = ty.slicePtrFieldType(&buf); |
| 936 | switch (try self.genDecl(ptr_ty, slice.ptr)) { |
| 937 | .externally_managed => |data| try self.code.appendSlice(data), |
| 938 | .appended => {}, |
| 939 | } |
| 940 | switch (try self.genDecl(Type.usize, slice.len)) { |
| 941 | .externally_managed => |data| try self.code.appendSlice(data), |
| 942 | .appended => {}, |
| 943 | } |
| 944 | return Result.appended; |
| 945 | }, |
| 946 | else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), |
| 862 | 947 | }, |
| 863 | 948 | else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), |
| 864 | 949 | } |
| 865 | 950 | } |
| 866 | 951 | |
| 952 | fn lowerDeclRef(self: *Self, decl: *Module.Decl) InnerError!Result { |
| 953 | decl.alive = true; |
| 954 | |
| 955 | const offset = @intCast(u32, self.code.items.len); |
| 956 | const atom = &self.decl.link.wasm; |
| 957 | const target_sym_index = decl.link.wasm.sym_index; |
| 958 | |
| 959 | if (decl.ty.zigTypeTag() == .Fn) { |
| 960 | // We found a function pointer, so add it to our table, |
| 961 | // as function pointers are not allowed to be stored inside the data section, |
| 962 | // but rather in a function table which are called by index |
| 963 | try self.bin_file.addTableFunction(target_sym_index); |
| 964 | try atom.relocs.append(self.gpa, .{ |
| 965 | .index = target_sym_index, |
| 966 | .offset = offset, |
| 967 | .relocation_type = .R_WASM_TABLE_INDEX_I32, |
| 968 | }); |
| 969 | } else { |
| 970 | try atom.relocs.append(self.gpa, .{ |
| 971 | .index = target_sym_index, |
| 972 | .offset = offset, |
| 973 | .relocation_type = .R_WASM_MEMORY_ADDR_I32, |
| 974 | }); |
| 975 | } |
| 976 | const ptr_width = self.target.cpu.arch.ptrBitWidth() / 8; |
| 977 | try self.code.appendNTimes(0xaa, ptr_width); |
| 978 | |
| 979 | return Result.appended; |
| 980 | } |
| 981 | |
| 867 | 982 | const CallWValues = struct { |
| 868 | 983 | args: []WValue, |
| 869 | 984 | return_value: WValue, |
| ... | ... | @@ -1015,6 +1130,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1015 | 1130 | .loop => self.airLoop(inst), |
| 1016 | 1131 | .not => self.airNot(inst), |
| 1017 | 1132 | .ret => self.airRet(inst), |
| 1133 | .ret_ptr => self.airRetPtr(inst), |
| 1134 | .ret_load => self.airRetLoad(inst), |
| 1018 | 1135 | .slice_len => self.airSliceLen(inst), |
| 1019 | 1136 | .slice_elem_val => self.airSliceElemVal(inst), |
| 1020 | 1137 | .store => self.airStore(inst), |
| ... | ... | @@ -1029,6 +1146,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1029 | 1146 | .wrap_optional => self.airWrapOptional(inst), |
| 1030 | 1147 | |
| 1031 | 1148 | .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), |
| 1149 | .unwrap_errunion_err => self.airUnwrapErrUnionError(inst), |
| 1032 | 1150 | .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), |
| 1033 | 1151 | |
| 1034 | 1152 | .optional_payload => self.airOptionalPayload(inst), |
| ... | ... | @@ -1061,6 +1179,34 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1061 | 1179 | return .none; |
| 1062 | 1180 | } |
| 1063 | 1181 | |
| 1182 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1183 | const child_type = self.air.typeOfIndex(inst).childType(); |
| 1184 | |
| 1185 | // Initialize the stack |
| 1186 | if (self.initial_stack_value == .none) { |
| 1187 | try self.initializeStack(); |
| 1188 | } |
| 1189 | |
| 1190 | const abi_size = child_type.abiSize(self.target); |
| 1191 | if (abi_size == 0) return WValue{ .none = {} }; |
| 1192 | |
| 1193 | // local, containing the offset to the stack position |
| 1194 | const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32 |
| 1195 | try self.moveStack(@intCast(u32, abi_size), local.local); |
| 1196 | |
| 1197 | return local; |
| 1198 | } |
| 1199 | |
| 1200 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1201 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1202 | const operand = self.resolveInst(un_op); |
| 1203 | const result = try self.load(operand, self.air.typeOf(un_op), 0); |
| 1204 | try self.addLabel(.local_get, result.local); |
| 1205 | try self.restoreStackPointer(); |
| 1206 | try self.addTag(.@"return"); |
| 1207 | return .none; |
| 1208 | } |
| 1209 | |
| 1064 | 1210 | fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1065 | 1211 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1066 | 1212 | const extra = self.air.extraData(Air.Call, pl_op.payload); |
| ... | ... | @@ -1096,6 +1242,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1096 | 1242 | // so load its value onto the stack |
| 1097 | 1243 | std.debug.assert(ty.zigTypeTag() == .Pointer); |
| 1098 | 1244 | const operand = self.resolveInst(pl_op.operand); |
| 1245 | try self.emitWValue(operand); |
| 1099 | 1246 | const result = try self.load(operand, fn_ty, operand.local_with_offset.offset); |
| 1100 | 1247 | try self.addLabel(.local_get, result.local); |
| 1101 | 1248 | |
| ... | ... | @@ -1229,6 +1376,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1229 | 1376 | // that is portable across the backend, rather than copying logic. |
| 1230 | 1377 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) |
| 1231 | 1378 | @intCast(u8, ty.abiSize(self.target)) |
| 1379 | else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum) |
| 1380 | @intCast(u8, ty.abiSize(self.target)) |
| 1232 | 1381 | else |
| 1233 | 1382 | @as(u8, 4); |
| 1234 | 1383 | const opcode = buildOpcode(.{ |
| ... | ... | @@ -1272,6 +1421,8 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1272 | 1421 | // that is portable across the backend, rather than copying logic. |
| 1273 | 1422 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) |
| 1274 | 1423 | @intCast(u8, ty.abiSize(self.target)) |
| 1424 | else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum) |
| 1425 | @intCast(u8, ty.abiSize(self.target)) |
| 1275 | 1426 | else |
| 1276 | 1427 | @as(u8, 4); |
| 1277 | 1428 | |
| ... | ... | @@ -1920,6 +2071,15 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 1920 | 2071 | return try self.load(operand, payload_ty, offset); |
| 1921 | 2072 | } |
| 1922 | 2073 | |
| 2074 | fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2075 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 2076 | |
| 2077 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2078 | const operand = self.resolveInst(ty_op.operand); |
| 2079 | const err_ty = self.air.typeOf(ty_op.operand); |
| 2080 | return try self.load(operand, err_ty.errorUnionSet(), 0); |
| 2081 | } |
| 2082 | |
| 1923 | 2083 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1924 | 2084 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1925 | 2085 | _ = ty_op; |
| ... | ... | @@ -1935,18 +2095,20 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1935 | 2095 | const op_bits = ref_info.bits; |
| 1936 | 2096 | const wanted_bits = ty.intInfo(self.target).bits; |
| 1937 | 2097 | |
| 1938 | | try self.emitWValue(operand); |
| 1939 | 2098 | if (op_bits > 32 and wanted_bits <= 32) { |
| 2099 | try self.emitWValue(operand); |
| 1940 | 2100 | try self.addTag(.i32_wrap_i64); |
| 1941 | 2101 | } else if (op_bits <= 32 and wanted_bits > 32) { |
| 2102 | try self.emitWValue(operand); |
| 1942 | 2103 | try self.addTag(switch (ref_info.signedness) { |
| 1943 | 2104 | .signed => .i64_extend_i32_s, |
| 1944 | 2105 | .unsigned => .i64_extend_i32_u, |
| 1945 | 2106 | }); |
| 1946 | | } |
| 2107 | } else return operand; |
| 1947 | 2108 | |
| 1948 | | // other cases are no-op |
| 1949 | | return .none; |
| 2109 | const result = try self.allocLocal(ty); |
| 2110 | try self.addLabel(.local_set, result.local); |
| 2111 | return result; |
| 1950 | 2112 | } |
| 1951 | 2113 | |
| 1952 | 2114 | fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |