| ... | ... | @@ -8,6 +8,7 @@ const mem = std.mem; |
| 8 | 8 | const wasm = std.wasm; |
| 9 | 9 | const log = std.log.scoped(.codegen); |
| 10 | 10 | |
| 11 | const codegen = @import("../../codegen.zig"); |
| 11 | 12 | const Module = @import("../../Module.zig"); |
| 12 | 13 | const Decl = Module.Decl; |
| 13 | 14 | const Type = @import("../../type.zig").Type; |
| ... | ... | @@ -546,7 +547,7 @@ blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct { |
| 546 | 547 | value: WValue, |
| 547 | 548 | }) = .{}, |
| 548 | 549 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. |
| 549 | | code: ArrayList(u8), |
| 550 | code: *ArrayList(u8), |
| 550 | 551 | /// The index the next local generated will have |
| 551 | 552 | /// NOTE: arguments share the index with locals therefore the first variable |
| 552 | 553 | /// will have the index that comes after the last argument's index |
| ... | ... | @@ -566,9 +567,6 @@ locals: std.ArrayListUnmanaged(u8), |
| 566 | 567 | target: std.Target, |
| 567 | 568 | /// Represents the wasm binary file that is being linked. |
| 568 | 569 | bin_file: *link.File.Wasm, |
| 569 | | /// Reference to the Module that this decl is part of. |
| 570 | | /// Used to find the error value. |
| 571 | | module: *Module, |
| 572 | 570 | /// List of MIR Instructions |
| 573 | 571 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 574 | 572 | /// Contains extra data for MIR |
| ... | ... | @@ -611,7 +609,6 @@ pub fn deinit(self: *Self) void { |
| 611 | 609 | self.locals.deinit(self.gpa); |
| 612 | 610 | self.mir_instructions.deinit(self.gpa); |
| 613 | 611 | self.mir_extra.deinit(self.gpa); |
| 614 | | self.code.deinit(); |
| 615 | 612 | self.* = undefined; |
| 616 | 613 | } |
| 617 | 614 | |
| ... | ... | @@ -639,7 +636,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { |
| 639 | 636 | } |
| 640 | 637 | |
| 641 | 638 | // When we need to pass the value by reference (such as a struct), we will |
| 642 | | // leverage `genTypedValue` to lower the constant to bytes and emit it |
| 639 | // leverage `generateSymbol` to lower the constant to bytes and emit it |
| 643 | 640 | // to the 'rodata' section. We then return the index into the section as `WValue`. |
| 644 | 641 | // |
| 645 | 642 | // In the other cases, we will simply lower the constant to a value that fits |
| ... | ... | @@ -822,7 +819,40 @@ fn genFunctype(gpa: Allocator, fn_ty: Type, target: std.Target) !wasm.Type { |
| 822 | 819 | }; |
| 823 | 820 | } |
| 824 | 821 | |
| 825 | | pub fn genFunc(self: *Self) InnerError!void { |
| 822 | pub fn generate( |
| 823 | bin_file: *link.File, |
| 824 | src_loc: Module.SrcLoc, |
| 825 | func: *Module.Fn, |
| 826 | air: Air, |
| 827 | liveness: Liveness, |
| 828 | code: *std.ArrayList(u8), |
| 829 | debug_output: codegen.DebugInfoOutput, |
| 830 | ) codegen.GenerateSymbolError!codegen.FnResult { |
| 831 | _ = debug_output; // TODO |
| 832 | _ = src_loc; |
| 833 | var code_gen: Self = .{ |
| 834 | .gpa = bin_file.allocator, |
| 835 | .air = air, |
| 836 | .liveness = liveness, |
| 837 | .values = .{}, |
| 838 | .code = code, |
| 839 | .decl = func.owner_decl, |
| 840 | .err_msg = undefined, |
| 841 | .locals = .{}, |
| 842 | .target = bin_file.options.target, |
| 843 | .bin_file = bin_file.cast(link.File.Wasm).?, |
| 844 | }; |
| 845 | defer code_gen.deinit(); |
| 846 | |
| 847 | genFunc(&code_gen) catch |err| switch (err) { |
| 848 | error.CodegenFail => return codegen.FnResult{ .fail = code_gen.err_msg }, |
| 849 | else => |e| return e, |
| 850 | }; |
| 851 | |
| 852 | return codegen.FnResult{ .appended = {} }; |
| 853 | } |
| 854 | |
| 855 | fn genFunc(self: *Self) InnerError!void { |
| 826 | 856 | var func_type = try genFunctype(self.gpa, self.decl.ty, self.target); |
| 827 | 857 | defer func_type.deinit(self.gpa); |
| 828 | 858 | self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| ... | ... | @@ -889,7 +919,7 @@ pub fn genFunc(self: *Self) InnerError!void { |
| 889 | 919 | var emit: Emit = .{ |
| 890 | 920 | .mir = mir, |
| 891 | 921 | .bin_file = &self.bin_file.base, |
| 892 | | .code = &self.code, |
| 922 | .code = self.code, |
| 893 | 923 | .locals = self.locals.items, |
| 894 | 924 | .decl = self.decl, |
| 895 | 925 | }; |
| ... | ... | @@ -903,433 +933,6 @@ pub fn genFunc(self: *Self) InnerError!void { |
| 903 | 933 | }; |
| 904 | 934 | } |
| 905 | 935 | |
| 906 | | pub const DeclGen = struct { |
| 907 | | /// The decl we are generating code for. |
| 908 | | decl: *Decl, |
| 909 | | /// The symbol we're generating code for. |
| 910 | | /// This can either be the symbol of the Decl itself, |
| 911 | | /// or one of its locals. |
| 912 | | symbol_index: u32, |
| 913 | | gpa: Allocator, |
| 914 | | /// A reference to the linker, that will process the decl's |
| 915 | | /// code and create any relocations it deems neccesary. |
| 916 | | bin_file: *link.File.Wasm, |
| 917 | | /// This will be set when `InnerError` has been returned. |
| 918 | | /// In any other case, this will be 'undefined'. |
| 919 | | err_msg: *Module.ErrorMsg, |
| 920 | | /// Reference to the Module that is being compiled. |
| 921 | | /// Used to find the error value of an error. |
| 922 | | module: *Module, |
| 923 | | /// The list of bytes that have been generated so far, |
| 924 | | /// can be used to calculate the offset into a section. |
| 925 | | code: *std.ArrayList(u8), |
| 926 | | |
| 927 | | /// Sets `err_msg` on `DeclGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig |
| 928 | | fn fail(self: *DeclGen, comptime fmt: []const u8, args: anytype) InnerError { |
| 929 | | const src: LazySrcLoc = .{ .node_offset = 0 }; |
| 930 | | const src_loc = src.toSrcLoc(self.decl); |
| 931 | | self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args); |
| 932 | | return error.CodegenFail; |
| 933 | | } |
| 934 | | |
| 935 | | fn target(self: *const DeclGen) std.Target { |
| 936 | | return self.bin_file.base.options.target; |
| 937 | | } |
| 938 | | |
| 939 | | pub fn genDecl(self: *DeclGen) InnerError!Result { |
| 940 | | const decl = self.decl; |
| 941 | | assert(decl.has_tv); |
| 942 | | |
| 943 | | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val }); |
| 944 | | |
| 945 | | if (decl.val.castTag(.function)) |func_payload| { |
| 946 | | _ = func_payload; |
| 947 | | return self.fail("TODO wasm backend genDecl function pointer", .{}); |
| 948 | | } else if (decl.val.castTag(.extern_fn)) |extern_fn| { |
| 949 | | const ext_decl = extern_fn.data.owner_decl; |
| 950 | | var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target()); |
| 951 | | defer func_type.deinit(self.gpa); |
| 952 | | ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 953 | | return Result{ .appended = {} }; |
| 954 | | } else { |
| 955 | | const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { |
| 956 | | break :init_val payload.data.init; |
| 957 | | } else decl.val; |
| 958 | | if (init_val.tag() != .unreachable_value) { |
| 959 | | return self.genTypedValue(decl.ty, init_val); |
| 960 | | } |
| 961 | | return Result{ .appended = {} }; |
| 962 | | } |
| 963 | | } |
| 964 | | |
| 965 | | /// Generates the wasm bytecode for the declaration belonging to `Context` |
| 966 | | pub fn genTypedValue(self: *DeclGen, ty: Type, val: Value) InnerError!Result { |
| 967 | | log.debug("genTypedValue: ty = {}, val = {}", .{ ty, val }); |
| 968 | | |
| 969 | | const writer = self.code.writer(); |
| 970 | | if (val.isUndef()) { |
| 971 | | try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target()))); |
| 972 | | return Result{ .appended = {} }; |
| 973 | | } |
| 974 | | switch (ty.zigTypeTag()) { |
| 975 | | .Fn => { |
| 976 | | const fn_decl = switch (val.tag()) { |
| 977 | | .extern_fn => val.castTag(.extern_fn).?.data.owner_decl, |
| 978 | | .function => val.castTag(.function).?.data.owner_decl, |
| 979 | | else => unreachable, |
| 980 | | }; |
| 981 | | return try self.lowerDeclRefValue(ty, val, fn_decl, 0); |
| 982 | | }, |
| 983 | | .Optional => { |
| 984 | | var opt_buf: Type.Payload.ElemType = undefined; |
| 985 | | const payload_type = ty.optionalChild(&opt_buf); |
| 986 | | const is_pl = !val.isNull(); |
| 987 | | const abi_size = @intCast(usize, ty.abiSize(self.target())); |
| 988 | | const offset = abi_size - @intCast(usize, payload_type.abiSize(self.target())); |
| 989 | | |
| 990 | | if (!payload_type.hasRuntimeBits()) { |
| 991 | | try writer.writeByteNTimes(@boolToInt(is_pl), abi_size); |
| 992 | | return Result{ .appended = {} }; |
| 993 | | } |
| 994 | | |
| 995 | | if (ty.isPtrLikeOptional()) { |
| 996 | | if (val.castTag(.opt_payload)) |payload| { |
| 997 | | return self.genTypedValue(payload_type, payload.data); |
| 998 | | } else if (!val.isNull()) { |
| 999 | | return self.genTypedValue(payload_type, val); |
| 1000 | | } else { |
| 1001 | | try writer.writeByteNTimes(0, abi_size); |
| 1002 | | return Result{ .appended = {} }; |
| 1003 | | } |
| 1004 | | } |
| 1005 | | |
| 1006 | | // `null-tag` bytes |
| 1007 | | try writer.writeByteNTimes(@boolToInt(is_pl), offset); |
| 1008 | | switch (try self.genTypedValue( |
| 1009 | | payload_type, |
| 1010 | | if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), |
| 1011 | | )) { |
| 1012 | | .appended => {}, |
| 1013 | | .externally_managed => |payload| try writer.writeAll(payload), |
| 1014 | | } |
| 1015 | | return Result{ .appended = {} }; |
| 1016 | | }, |
| 1017 | | .Array => switch (val.tag()) { |
| 1018 | | .bytes => { |
| 1019 | | const payload = val.castTag(.bytes).?; |
| 1020 | | return Result{ .externally_managed = payload.data }; |
| 1021 | | }, |
| 1022 | | .array => { |
| 1023 | | const elem_vals = val.castTag(.array).?.data; |
| 1024 | | const elem_ty = ty.childType(); |
| 1025 | | for (elem_vals) |elem_val| { |
| 1026 | | switch (try self.genTypedValue(elem_ty, elem_val)) { |
| 1027 | | .appended => {}, |
| 1028 | | .externally_managed => |data| try writer.writeAll(data), |
| 1029 | | } |
| 1030 | | } |
| 1031 | | return Result{ .appended = {} }; |
| 1032 | | }, |
| 1033 | | .repeated => { |
| 1034 | | const array = val.castTag(.repeated).?.data; |
| 1035 | | const elem_ty = ty.childType(); |
| 1036 | | const sentinel = ty.sentinel(); |
| 1037 | | const len = ty.arrayLen(); |
| 1038 | | |
| 1039 | | var index: u32 = 0; |
| 1040 | | while (index < len) : (index += 1) { |
| 1041 | | switch (try self.genTypedValue(elem_ty, array)) { |
| 1042 | | .externally_managed => |data| try writer.writeAll(data), |
| 1043 | | .appended => {}, |
| 1044 | | } |
| 1045 | | } |
| 1046 | | if (sentinel) |sentinel_value| { |
| 1047 | | return self.genTypedValue(elem_ty, sentinel_value); |
| 1048 | | } |
| 1049 | | return Result{ .appended = {} }; |
| 1050 | | }, |
| 1051 | | .empty_array_sentinel => { |
| 1052 | | const elem_ty = ty.childType(); |
| 1053 | | const sent_val = ty.sentinel().?; |
| 1054 | | return self.genTypedValue(elem_ty, sent_val); |
| 1055 | | }, |
| 1056 | | else => unreachable, |
| 1057 | | }, |
| 1058 | | .Int => { |
| 1059 | | const info = ty.intInfo(self.target()); |
| 1060 | | const abi_size = @intCast(usize, ty.abiSize(self.target())); |
| 1061 | | if (info.bits <= 64) { |
| 1062 | | var buf: [8]u8 = undefined; |
| 1063 | | if (info.signedness == .unsigned) { |
| 1064 | | std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt()); |
| 1065 | | } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt()); |
| 1066 | | try writer.writeAll(buf[0..abi_size]); |
| 1067 | | return Result{ .appended = {} }; |
| 1068 | | } |
| 1069 | | var space: Value.BigIntSpace = undefined; |
| 1070 | | const bigint = val.toBigInt(&space); |
| 1071 | | const iterations = @divExact(abi_size, @sizeOf(usize)); |
| 1072 | | for (bigint.limbs) |_, index| { |
| 1073 | | const limb = bigint.limbs[bigint.limbs.len - index - 1]; |
| 1074 | | try writer.writeIntLittle(usize, limb); |
| 1075 | | } else if (bigint.limbs.len < iterations) { |
| 1076 | | // When the value is saved in less limbs than the required |
| 1077 | | // abi size, we fill the remaining parts with 0's. |
| 1078 | | var it_left = iterations - bigint.limbs.len; |
| 1079 | | while (it_left > 0) { |
| 1080 | | it_left -= 1; |
| 1081 | | try writer.writeIntLittle(usize, 0); |
| 1082 | | } |
| 1083 | | } |
| 1084 | | return Result{ .appended = {} }; |
| 1085 | | }, |
| 1086 | | .Float => { |
| 1087 | | const float_bits = ty.floatBits(self.target()); |
| 1088 | | if (float_bits > 64) { |
| 1089 | | return self.fail("Wasm TODO: Implement f80 and f128", .{}); |
| 1090 | | } |
| 1091 | | |
| 1092 | | switch (float_bits) { |
| 1093 | | 16, 32 => try writer.writeIntLittle(u32, @bitCast(u32, val.toFloat(f32))), |
| 1094 | | 64 => try writer.writeIntLittle(u64, @bitCast(u64, val.toFloat(f64))), |
| 1095 | | else => unreachable, |
| 1096 | | } |
| 1097 | | |
| 1098 | | return Result{ .appended = {} }; |
| 1099 | | }, |
| 1100 | | .Enum => { |
| 1101 | | var int_buffer: Value.Payload.U64 = undefined; |
| 1102 | | const int_val = val.enumToInt(ty, &int_buffer); |
| 1103 | | var buf: Type.Payload.Bits = undefined; |
| 1104 | | const int_ty = ty.intTagType(&buf); |
| 1105 | | return self.genTypedValue(int_ty, int_val); |
| 1106 | | }, |
| 1107 | | .Bool => { |
| 1108 | | try writer.writeByte(@boolToInt(val.toBool())); |
| 1109 | | return Result{ .appended = {} }; |
| 1110 | | }, |
| 1111 | | .Struct => { |
| 1112 | | const struct_obj = ty.castTag(.@"struct").?.data; |
| 1113 | | if (struct_obj.layout == .Packed) { |
| 1114 | | return self.fail("TODO: Packed structs for wasm", .{}); |
| 1115 | | } |
| 1116 | | |
| 1117 | | const struct_begin = self.code.items.len; |
| 1118 | | const field_vals = val.castTag(.@"struct").?.data; |
| 1119 | | for (field_vals) |field_val, index| { |
| 1120 | | const field_ty = ty.structFieldType(index); |
| 1121 | | if (!field_ty.hasRuntimeBits()) continue; |
| 1122 | | |
| 1123 | | switch (try self.genTypedValue(field_ty, field_val)) { |
| 1124 | | .appended => {}, |
| 1125 | | .externally_managed => |payload| try writer.writeAll(payload), |
| 1126 | | } |
| 1127 | | const unpadded_field_len = self.code.items.len - struct_begin; |
| 1128 | | |
| 1129 | | // Pad struct members if required |
| 1130 | | const padded_field_end = ty.structFieldOffset(index + 1, self.target()); |
| 1131 | | const padding = try std.math.cast(usize, padded_field_end - unpadded_field_len); |
| 1132 | | |
| 1133 | | if (padding > 0) { |
| 1134 | | try writer.writeByteNTimes(0, padding); |
| 1135 | | } |
| 1136 | | } |
| 1137 | | return Result{ .appended = {} }; |
| 1138 | | }, |
| 1139 | | .Union => { |
| 1140 | | const union_val = val.castTag(.@"union").?.data; |
| 1141 | | const layout = ty.unionGetLayout(self.target()); |
| 1142 | | |
| 1143 | | if (layout.payload_size == 0) { |
| 1144 | | return self.genTypedValue(ty.unionTagType().?, union_val.tag); |
| 1145 | | } |
| 1146 | | |
| 1147 | | // Check if we should store the tag first, in which case, do so now: |
| 1148 | | if (layout.tag_align >= layout.payload_align) { |
| 1149 | | switch (try self.genTypedValue(ty.unionTagType().?, union_val.tag)) { |
| 1150 | | .appended => {}, |
| 1151 | | .externally_managed => |payload| try writer.writeAll(payload), |
| 1152 | | } |
| 1153 | | } |
| 1154 | | |
| 1155 | | const union_ty = ty.cast(Type.Payload.Union).?.data; |
| 1156 | | const field_index = union_ty.tag_ty.enumTagFieldIndex(union_val.tag).?; |
| 1157 | | assert(union_ty.haveFieldTypes()); |
| 1158 | | const field_ty = union_ty.fields.values()[field_index].ty; |
| 1159 | | if (!field_ty.hasRuntimeBits()) { |
| 1160 | | try writer.writeByteNTimes(0xaa, @intCast(usize, layout.payload_size)); |
| 1161 | | } else { |
| 1162 | | switch (try self.genTypedValue(field_ty, union_val.val)) { |
| 1163 | | .appended => {}, |
| 1164 | | .externally_managed => |payload| try writer.writeAll(payload), |
| 1165 | | } |
| 1166 | | |
| 1167 | | // Unions have the size of the largest field, so we must pad |
| 1168 | | // whenever the active field has a smaller size. |
| 1169 | | const diff = layout.payload_size - field_ty.abiSize(self.target()); |
| 1170 | | if (diff > 0) { |
| 1171 | | try writer.writeByteNTimes(0xaa, @intCast(usize, diff)); |
| 1172 | | } |
| 1173 | | } |
| 1174 | | |
| 1175 | | if (layout.tag_size == 0) { |
| 1176 | | return Result{ .appended = {} }; |
| 1177 | | } |
| 1178 | | return self.genTypedValue(union_ty.tag_ty, union_val.tag); |
| 1179 | | }, |
| 1180 | | .Pointer => switch (val.tag()) { |
| 1181 | | .variable => { |
| 1182 | | const decl = val.castTag(.variable).?.data.owner_decl; |
| 1183 | | return self.lowerDeclRefValue(ty, val, decl, 0); |
| 1184 | | }, |
| 1185 | | .decl_ref => { |
| 1186 | | const decl = val.castTag(.decl_ref).?.data; |
| 1187 | | return self.lowerDeclRefValue(ty, val, decl, 0); |
| 1188 | | }, |
| 1189 | | .slice => { |
| 1190 | | const slice = val.castTag(.slice).?.data; |
| 1191 | | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 1192 | | const ptr_ty = ty.slicePtrFieldType(&buf); |
| 1193 | | switch (try self.genTypedValue(ptr_ty, slice.ptr)) { |
| 1194 | | .externally_managed => |data| try writer.writeAll(data), |
| 1195 | | .appended => {}, |
| 1196 | | } |
| 1197 | | switch (try self.genTypedValue(Type.usize, slice.len)) { |
| 1198 | | .externally_managed => |data| try writer.writeAll(data), |
| 1199 | | .appended => {}, |
| 1200 | | } |
| 1201 | | return Result{ .appended = {} }; |
| 1202 | | }, |
| 1203 | | .zero => { |
| 1204 | | try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8)); |
| 1205 | | return Result{ .appended = {} }; |
| 1206 | | }, |
| 1207 | | .elem_ptr => { |
| 1208 | | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| 1209 | | const elem_size = ty.childType().abiSize(self.target()); |
| 1210 | | const offset = elem_ptr.index * elem_size; |
| 1211 | | return self.lowerParentPtr(elem_ptr.array_ptr, @intCast(usize, offset)); |
| 1212 | | }, |
| 1213 | | .int_u64 => return self.genTypedValue(Type.usize, val), |
| 1214 | | else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), |
| 1215 | | }, |
| 1216 | | .ErrorUnion => { |
| 1217 | | const error_ty = ty.errorUnionSet(); |
| 1218 | | const payload_ty = ty.errorUnionPayload(); |
| 1219 | | const is_pl = val.errorUnionIsPayload(); |
| 1220 | | const abi_align = ty.abiAlignment(self.target()); |
| 1221 | | |
| 1222 | | { |
| 1223 | | const err_val = if (!is_pl) val else Value.initTag(.zero); |
| 1224 | | const start = self.code.items.len; |
| 1225 | | switch (try self.genTypedValue(error_ty, err_val)) { |
| 1226 | | .externally_managed => |data| try writer.writeAll(data), |
| 1227 | | .appended => {}, |
| 1228 | | } |
| 1229 | | const unpadded_end = self.code.items.len - start; |
| 1230 | | const padded_end = mem.alignForwardGeneric(usize, unpadded_end, abi_align); |
| 1231 | | const padding = padded_end - unpadded_end; |
| 1232 | | if (padding > 0) { |
| 1233 | | try writer.writeByteNTimes(0, padding); |
| 1234 | | } |
| 1235 | | } |
| 1236 | | |
| 1237 | | if (payload_ty.hasRuntimeBits()) { |
| 1238 | | const start = self.code.items.len; |
| 1239 | | const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); |
| 1240 | | switch (try self.genTypedValue(payload_ty, pl_val)) { |
| 1241 | | .externally_managed => |data| try writer.writeAll(data), |
| 1242 | | .appended => {}, |
| 1243 | | } |
| 1244 | | |
| 1245 | | const unpadded_end = self.code.items.len - start; |
| 1246 | | const padded_end = mem.alignForwardGeneric(usize, unpadded_end, abi_align); |
| 1247 | | const padding = padded_end - unpadded_end; |
| 1248 | | if (padding > 0) { |
| 1249 | | try writer.writeByteNTimes(0, padding); |
| 1250 | | } |
| 1251 | | } |
| 1252 | | |
| 1253 | | return Result{ .appended = {} }; |
| 1254 | | }, |
| 1255 | | .ErrorSet => { |
| 1256 | | switch (val.tag()) { |
| 1257 | | .@"error" => { |
| 1258 | | const name = val.castTag(.@"error").?.data.name; |
| 1259 | | const kv = try self.module.getErrorValue(name); |
| 1260 | | try writer.writeIntLittle(u32, kv.value); |
| 1261 | | }, |
| 1262 | | else => { |
| 1263 | | try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target()))); |
| 1264 | | }, |
| 1265 | | } |
| 1266 | | return Result{ .appended = {} }; |
| 1267 | | }, |
| 1268 | | else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), |
| 1269 | | } |
| 1270 | | } |
| 1271 | | |
| 1272 | | fn lowerParentPtr(self: *DeclGen, ptr_value: Value, offset: usize) InnerError!Result { |
| 1273 | | switch (ptr_value.tag()) { |
| 1274 | | .decl_ref => { |
| 1275 | | const decl = ptr_value.castTag(.decl_ref).?.data; |
| 1276 | | return self.lowerParentPtrDecl(ptr_value, decl, offset); |
| 1277 | | }, |
| 1278 | | else => |tag| return self.fail("TODO: Implement lowerParentPtr for pointer value tag: {s}", .{tag}), |
| 1279 | | } |
| 1280 | | } |
| 1281 | | |
| 1282 | | fn lowerParentPtrDecl(self: *DeclGen, ptr_val: Value, decl: *Module.Decl, offset: usize) InnerError!Result { |
| 1283 | | decl.markAlive(); |
| 1284 | | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 1285 | | .base = .{ .tag = .single_mut_pointer }, |
| 1286 | | .data = decl.ty, |
| 1287 | | }; |
| 1288 | | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| 1289 | | return self.lowerDeclRefValue(ptr_ty, ptr_val, decl, offset); |
| 1290 | | } |
| 1291 | | |
| 1292 | | fn lowerDeclRefValue( |
| 1293 | | self: *DeclGen, |
| 1294 | | ty: Type, |
| 1295 | | val: Value, |
| 1296 | | /// The target decl that is being pointed to |
| 1297 | | decl: *Module.Decl, |
| 1298 | | /// When lowering to an indexed pointer, we can specify the offset |
| 1299 | | /// which will then be used as 'addend' to the relocation. |
| 1300 | | offset: usize, |
| 1301 | | ) InnerError!Result { |
| 1302 | | const writer = self.code.writer(); |
| 1303 | | if (ty.isSlice()) { |
| 1304 | | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 1305 | | const slice_ty = ty.slicePtrFieldType(&buf); |
| 1306 | | switch (try self.genTypedValue(slice_ty, val)) { |
| 1307 | | .appended => {}, |
| 1308 | | .externally_managed => |payload| try writer.writeAll(payload), |
| 1309 | | } |
| 1310 | | var slice_len: Value.Payload.U64 = .{ |
| 1311 | | .base = .{ .tag = .int_u64 }, |
| 1312 | | .data = val.sliceLen(), |
| 1313 | | }; |
| 1314 | | return self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base)); |
| 1315 | | } |
| 1316 | | |
| 1317 | | decl.markAlive(); |
| 1318 | | if (decl.link.wasm.sym_index == 0) { |
| 1319 | | try writer.writeIntLittle(u32, 0); |
| 1320 | | } else { |
| 1321 | | try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr( |
| 1322 | | self.decl, // parent decl that owns the atom of the symbol |
| 1323 | | self.symbol_index, // source symbol index |
| 1324 | | decl, // target decl that contains the target symbol |
| 1325 | | @intCast(u32, self.code.items.len), // offset |
| 1326 | | @intCast(u32, offset), // addend |
| 1327 | | )); |
| 1328 | | } |
| 1329 | | return Result{ .appended = {} }; |
| 1330 | | } |
| 1331 | | }; |
| 1332 | | |
| 1333 | 936 | const CallWValues = struct { |
| 1334 | 937 | args: []WValue, |
| 1335 | 938 | return_value: WValue, |
| ... | ... | @@ -1809,8 +1412,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1809 | 1412 | |
| 1810 | 1413 | if (func_val.castTag(.function)) |func| { |
| 1811 | 1414 | break :blk func.data.owner_decl; |
| 1812 | | } else if (func_val.castTag(.extern_fn)) |ext_fn| { |
| 1813 | | break :blk ext_fn.data.owner_decl; |
| 1415 | } else if (func_val.castTag(.extern_fn)) |extern_fn| { |
| 1416 | const ext_decl = extern_fn.data.owner_decl; |
| 1417 | var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target); |
| 1418 | defer func_type.deinit(self.gpa); |
| 1419 | ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); |
| 1420 | break :blk ext_decl; |
| 1814 | 1421 | } else if (func_val.castTag(.decl_ref)) |decl_ref| { |
| 1815 | 1422 | break :blk decl_ref.data; |
| 1816 | 1423 | } |
| ... | ... | @@ -2184,7 +1791,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2184 | 1791 | }, |
| 2185 | 1792 | .ErrorSet => switch (val.tag()) { |
| 2186 | 1793 | .@"error" => { |
| 2187 | | const kv = try self.module.getErrorValue(val.getError().?); |
| 1794 | const kv = try self.bin_file.base.options.module.?.getErrorValue(val.getError().?); |
| 2188 | 1795 | return WValue{ .imm32 = kv.value }; |
| 2189 | 1796 | }, |
| 2190 | 1797 | else => return WValue{ .imm32 = 0 }, |
| ... | ... | @@ -2275,7 +1882,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 { |
| 2275 | 1882 | .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())), |
| 2276 | 1883 | }, |
| 2277 | 1884 | .ErrorSet => { |
| 2278 | | const kv = self.module.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function |
| 1885 | const kv = self.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function |
| 2279 | 1886 | return @bitCast(i32, kv.value); |
| 2280 | 1887 | }, |
| 2281 | 1888 | else => unreachable, // Programmer called this function for an illegal type |