| ... | @@ -692,6 +692,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { | ... | @@ -692,6 +692,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 692 | .Struct, | 692 | .Struct, |
| 693 | .ErrorUnion, | 693 | .ErrorUnion, |
| 694 | .Optional, | 694 | .Optional, |
| | 695 | .Fn, |
| 695 | => wasm.Valtype.i32, | 696 | => wasm.Valtype.i32, |
| 696 | else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), | 697 | else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), |
| 697 | }; | 698 | }; |
| ... | @@ -809,23 +810,52 @@ pub fn genFunc(self: *Self) InnerError!Result { | ... | @@ -809,23 +810,52 @@ pub fn genFunc(self: *Self) InnerError!Result { |
| 809 | } | 810 | } |
| 810 | | 811 | |
| 811 | /// Generates the wasm bytecode for the declaration belonging to `Context` | 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 | switch (ty.zigTypeTag()) { | 818 | switch (ty.zigTypeTag()) { |
| 814 | .Fn => { | 819 | .Fn => { |
| 815 | if (val.tag() == .extern_fn) { | 820 | const fn_decl = switch (val.tag()) { |
| 816 | var func_type = try self.genFunctype(self.decl.ty); | 821 | .extern_fn => val.castTag(.extern_fn).?.data, |
| 817 | defer func_type.deinit(self.gpa); | 822 | .function => val.castTag(.function).?.data.owner_decl, |
| 818 | self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); | 823 | else => unreachable, |
| 819 | return Result.appended; // don't need code body for extern functions | 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 => { | 852 | .Array => switch (val.tag()) { |
| 824 | if (val.castTag(.bytes)) |payload| { | 853 | .bytes => { |
| | 854 | const payload = val.castTag(.bytes).?; |
| 825 | if (ty.sentinel()) |sentinel| { | 855 | if (ty.sentinel()) |sentinel| { |
| 826 | try self.code.appendSlice(payload.data); | 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 | .appended => return Result.appended, | 859 | .appended => return Result.appended, |
| 830 | .externally_managed => |data| { | 860 | .externally_managed => |data| { |
| 831 | try self.code.appendSlice(data); | 861 | try self.code.appendSlice(data); |
| ... | @@ -834,16 +864,33 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -834,16 +864,33 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 834 | } | 864 | } |
| 835 | } | 865 | } |
| 836 | return Result{ .externally_managed = payload.data }; | 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 | .Int => { | 883 | .Int => { |
| 840 | const info = ty.intInfo(self.target); | 884 | const info = ty.intInfo(self.target); |
| 841 | if (info.bits == 8 and info.signedness == .unsigned) { | 885 | const abi_size = ty.abiSize(self.target); |
| 842 | const int_byte = val.toUnsignedInt(); | 886 | // todo: Implement integer sizes larger than 64bits |
| 843 | try self.code.append(@intCast(u8, int_byte)); | 887 | if (info.bits > 64) return self.fail("TODO: Implement genDecl for integer bit size: {d}", .{info.bits}); |
| 844 | return Result.appended; | 888 | var buf: [8]u8 = undefined; |
| 845 | } | 889 | if (info.signedness == .unsigned) { |
| 846 | return self.fail("TODO: Implement codegen for int type: '{}'", .{ty}); | 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 | .Enum => { | 895 | .Enum => { |
| 849 | try self.emitConstant(val, ty); | 896 | try self.emitConstant(val, ty); |
| ... | @@ -855,15 +902,83 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -855,15 +902,83 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 855 | return Result.appended; | 902 | return Result.appended; |
| 856 | }, | 903 | }, |
| 857 | .Struct => { | 904 | .Struct => { |
| 858 | // TODO write the fields for real | 905 | const field_vals = val.castTag(.@"struct").?.data; |
| 859 | const abi_size = try std.math.cast(usize, ty.abiSize(self.target)); | 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 | try self.code.writer().writeByteNTimes(0xaa, abi_size); | 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 | else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), | 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 | const CallWValues = struct { | 982 | const CallWValues = struct { |
| 868 | args: []WValue, | 983 | args: []WValue, |
| 869 | return_value: WValue, | 984 | return_value: WValue, |
| ... | @@ -1015,6 +1130,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1015,6 +1130,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1015 | .loop => self.airLoop(inst), | 1130 | .loop => self.airLoop(inst), |
| 1016 | .not => self.airNot(inst), | 1131 | .not => self.airNot(inst), |
| 1017 | .ret => self.airRet(inst), | 1132 | .ret => self.airRet(inst), |
| | 1133 | .ret_ptr => self.airRetPtr(inst), |
| | 1134 | .ret_load => self.airRetLoad(inst), |
| 1018 | .slice_len => self.airSliceLen(inst), | 1135 | .slice_len => self.airSliceLen(inst), |
| 1019 | .slice_elem_val => self.airSliceElemVal(inst), | 1136 | .slice_elem_val => self.airSliceElemVal(inst), |
| 1020 | .store => self.airStore(inst), | 1137 | .store => self.airStore(inst), |
| ... | @@ -1029,6 +1146,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1029,6 +1146,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1029 | .wrap_optional => self.airWrapOptional(inst), | 1146 | .wrap_optional => self.airWrapOptional(inst), |
| 1030 | | 1147 | |
| 1031 | .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), | 1148 | .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), |
| | 1149 | .unwrap_errunion_err => self.airUnwrapErrUnionError(inst), |
| 1032 | .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), | 1150 | .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), |
| 1033 | | 1151 | |
| 1034 | .optional_payload => self.airOptionalPayload(inst), | 1152 | .optional_payload => self.airOptionalPayload(inst), |
| ... | @@ -1061,6 +1179,34 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1061,6 +1179,34 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1061 | return .none; | 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 | fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1210 | fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1065 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 1211 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1066 | const extra = self.air.extraData(Air.Call, pl_op.payload); | 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,6 +1242,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1096 | // so load its value onto the stack | 1242 | // so load its value onto the stack |
| 1097 | std.debug.assert(ty.zigTypeTag() == .Pointer); | 1243 | std.debug.assert(ty.zigTypeTag() == .Pointer); |
| 1098 | const operand = self.resolveInst(pl_op.operand); | 1244 | const operand = self.resolveInst(pl_op.operand); |
| | 1245 | try self.emitWValue(operand); |
| 1099 | const result = try self.load(operand, fn_ty, operand.local_with_offset.offset); | 1246 | const result = try self.load(operand, fn_ty, operand.local_with_offset.offset); |
| 1100 | try self.addLabel(.local_get, result.local); | 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,6 +1376,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1229 | // that is portable across the backend, rather than copying logic. | 1376 | // that is portable across the backend, rather than copying logic. |
| 1230 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) | 1377 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) |
| 1231 | @intCast(u8, ty.abiSize(self.target)) | 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 | else | 1381 | else |
| 1233 | @as(u8, 4); | 1382 | @as(u8, 4); |
| 1234 | const opcode = buildOpcode(.{ | 1383 | const opcode = buildOpcode(.{ |
| ... | @@ -1272,6 +1421,8 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | ... | @@ -1272,6 +1421,8 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1272 | // that is portable across the backend, rather than copying logic. | 1421 | // that is portable across the backend, rather than copying logic. |
| 1273 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) | 1422 | const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8) |
| 1274 | @intCast(u8, ty.abiSize(self.target)) | 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 | else | 1426 | else |
| 1276 | @as(u8, 4); | 1427 | @as(u8, 4); |
| 1277 | | 1428 | |
| ... | @@ -1920,6 +2071,15 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue | ... | @@ -1920,6 +2071,15 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 1920 | return try self.load(operand, payload_ty, offset); | 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 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2083 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1924 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2084 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1925 | _ = ty_op; | 2085 | _ = ty_op; |
| ... | @@ -1935,18 +2095,20 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1935,18 +2095,20 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1935 | const op_bits = ref_info.bits; | 2095 | const op_bits = ref_info.bits; |
| 1936 | const wanted_bits = ty.intInfo(self.target).bits; | 2096 | const wanted_bits = ty.intInfo(self.target).bits; |
| 1937 | | 2097 | |
| 1938 | try self.emitWValue(operand); | | |
| 1939 | if (op_bits > 32 and wanted_bits <= 32) { | 2098 | if (op_bits > 32 and wanted_bits <= 32) { |
| | 2099 | try self.emitWValue(operand); |
| 1940 | try self.addTag(.i32_wrap_i64); | 2100 | try self.addTag(.i32_wrap_i64); |
| 1941 | } else if (op_bits <= 32 and wanted_bits > 32) { | 2101 | } else if (op_bits <= 32 and wanted_bits > 32) { |
| | 2102 | try self.emitWValue(operand); |
| 1942 | try self.addTag(switch (ref_info.signedness) { | 2103 | try self.addTag(switch (ref_info.signedness) { |
| 1943 | .signed => .i64_extend_i32_s, | 2104 | .signed => .i64_extend_i32_s, |
| 1944 | .unsigned => .i64_extend_i32_u, | 2105 | .unsigned => .i64_extend_i32_u, |
| 1945 | }); | 2106 | }); |
| 1946 | } | 2107 | } else return operand; |
| 1947 | | 2108 | |
| 1948 | // other cases are no-op | 2109 | const result = try self.allocLocal(ty); |
| 1949 | return .none; | 2110 | try self.addLabel(.local_set, result.local); |
| | 2111 | return result; |
| 1950 | } | 2112 | } |
| 1951 | | 2113 | |
| 1952 | fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { | 2114 | fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |